@khipu/design-system 0.2.0-alpha.9 → 0.2.0-alpha.90

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-15T15:18:22.603Z
14
+ * Generated: 2026-07-02T21:31:49.446Z
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;
@@ -1732,9 +1808,6 @@ a.kds-btn.kds-btn-md {
1732
1808
 
1733
1809
  .snackbar {
1734
1810
  position: fixed;
1735
- bottom: var(--kds-spacing-3);
1736
- left: 50%;
1737
- transform: translateX(-50%);
1738
1811
  z-index: var(--kds-z-index-snackbar);
1739
1812
  min-width: 344px;
1740
1813
  max-width: var(--kds-snackbar-max-width);
@@ -2032,6 +2105,221 @@ a.kds-btn-google:disabled {
2032
2105
  height: var(--kds-spacing-button-icon-size);
2033
2106
  }
2034
2107
 
2108
+ /* ========================================
2109
+ KDS LINEAR PROGRESS
2110
+ Native <progress> (KdsLinearProgress). Additive over BeerCSS `progress`,
2111
+ scoped to .kds-progress so other consumers are unaffected.
2112
+ ======================================== */
2113
+
2114
+ /* `progress` is inline-block by default → leaves a baseline (descender) gap
2115
+ below the bar. Block removes that gap so it sits flush with siblings. */
2116
+ .kds-progress {
2117
+ display: block;
2118
+ }
2119
+
2120
+ /* Animate the filled value when it changes (e.g. advancing a step). BeerCSS
2121
+ sets the value background but no transition, so the width jumps abruptly. */
2122
+ .kds-progress::-webkit-progress-value {
2123
+ transition: width var(--kds-transition-standard) var(--kds-easing-standard);
2124
+ }
2125
+
2126
+ .kds-progress::-moz-progress-bar {
2127
+ transition: width var(--kds-transition-standard) var(--kds-easing-standard);
2128
+ }
2129
+
2130
+ /* ========================================
2131
+ KDS SECURE LOADER
2132
+ Loader de espera: anillo circular + candado centrado + frases opcionales.
2133
+ El anillo reutiliza el spinner circular de BeerCSS, escalado.
2134
+ ======================================== */
2135
+
2136
+ .kds-secure-loader {
2137
+ display: flex;
2138
+ flex-direction: column;
2139
+ align-items: center;
2140
+ justify-content: center;
2141
+ gap: var(--kds-spacing-4);
2142
+ text-align: center;
2143
+ /* Llena y centra dentro de contenedores flex (pantalla de carga); en flujo
2144
+ normal (contenedor no-flex) se reduce a su contenido. */
2145
+ flex: 1 1 auto;
2146
+ }
2147
+
2148
+ .kds-secure-loader-text {
2149
+ display: flex;
2150
+ flex-direction: column;
2151
+ gap: var(--kds-spacing-0-25);
2152
+ }
2153
+
2154
+ .kds-secure-loader-title {
2155
+ margin: 0;
2156
+ font-family: var(--kds-font-family-primary);
2157
+ font-size: var(--kds-font-size-h6);
2158
+ font-weight: var(--kds-font-weight-semibold);
2159
+ line-height: var(--kds-line-height-snug);
2160
+ color: var(--kds-color-text-primary);
2161
+ }
2162
+
2163
+ .kds-secure-loader-message {
2164
+ margin: 0;
2165
+ font-family: var(--kds-font-family-secondary);
2166
+ font-size: var(--kds-font-size-body1);
2167
+ line-height: var(--kds-line-height-normal);
2168
+ color: var(--kds-color-primary-main);
2169
+ }
2170
+
2171
+ /* Caja del spinner con tamaño fijo: la animación no altera el layout.
2172
+ 6.25rem (100px) = tamaño del loader original (CircularProgress size=100). */
2173
+ .kds-secure-loader-spinner {
2174
+ position: relative;
2175
+ display: inline-grid;
2176
+ place-items: center;
2177
+ inline-size: var(--kds-secure-loader-size, 6.25rem);
2178
+ block-size: var(--kds-secure-loader-size, 6.25rem);
2179
+ }
2180
+
2181
+ /* Anillo: SVG nativo con arco vía stroke-dasharray, rotando. Replica el
2182
+ CircularProgress original (viewBox 44u, thickness 2, arco ~63%, disableShrink). */
2183
+ .kds-secure-loader-ring {
2184
+ inline-size: var(--kds-secure-loader-size, 6.25rem);
2185
+ block-size: var(--kds-secure-loader-size, 6.25rem);
2186
+ animation: kds-secure-loader-spin 1.2s linear infinite;
2187
+ }
2188
+
2189
+ .kds-secure-loader-ring circle {
2190
+ fill: none;
2191
+ stroke: var(--kds-color-primary-main);
2192
+ stroke-width: 2;
2193
+ stroke-linecap: round;
2194
+ /* circunferencia ≈ 2π·20.2 ≈ 127; arco visible ≈ 80 (~63%) */
2195
+ stroke-dasharray: 80 127;
2196
+ }
2197
+
2198
+ @keyframes kds-secure-loader-spin {
2199
+ to {
2200
+ transform: rotate(360deg);
2201
+ }
2202
+ }
2203
+
2204
+ /* Candado centrado sobre el anillo (~0.3 del diámetro). Centrado explícito
2205
+ (top/left 50% + translate) para que no dependa del grid ni se recorte. */
2206
+ .kds-secure-loader-lock {
2207
+ position: absolute;
2208
+ top: 50%;
2209
+ left: 50%;
2210
+ transform: translate(-50%, -50%);
2211
+ color: var(--kds-color-text-secondary);
2212
+ font-size: calc(var(--kds-secure-loader-size, 6.25rem) * 0.3);
2213
+ line-height: 1;
2214
+ pointer-events: none;
2215
+ }
2216
+
2217
+ /* ========================================
2218
+ KDS COPY BUTTON
2219
+ Chip compacto que muestra un valor + icono copy y lo copia al click.
2220
+ Acción inline (distinto de KdsCopyRow). Look del copy button de khenshin.
2221
+ ======================================== */
2222
+
2223
+ .kds-copy-button {
2224
+ display: inline-flex;
2225
+ align-items: center;
2226
+ gap: var(--kds-spacing-1);
2227
+ padding: var(--kds-spacing-0-25) var(--kds-spacing-1-5);
2228
+ /* No usar el alto de botón táctil de BeerCSS (~44px): es un chip compacto,
2229
+ su alto lo define el contenido + padding. */
2230
+ block-size: auto;
2231
+ min-block-size: 0;
2232
+ border: none;
2233
+ border-radius: var(--kds-radius-button);
2234
+ background: var(--kds-alert-info-bg);
2235
+ color: var(--kds-color-info-dark);
2236
+ font-family: var(--kds-font-family-secondary);
2237
+ font-size: var(--kds-font-size-sm);
2238
+ font-weight: var(--kds-font-weight-medium);
2239
+ line-height: var(--kds-line-height-normal);
2240
+ cursor: pointer;
2241
+ transition: background-color var(--kds-transition-shorter) var(--kds-easing-standard),
2242
+ color var(--kds-transition-shorter) var(--kds-easing-standard);
2243
+ }
2244
+
2245
+ /* active = hover: sin el aro de focus/active (outline morado de BeerCSS). */
2246
+ .kds-copy-button:focus,
2247
+ .kds-copy-button:focus-visible,
2248
+ .kds-copy-button:active {
2249
+ outline: none;
2250
+ }
2251
+
2252
+ /* value y copiedText se apilan en la MISMA celda de grid: la celda toma el
2253
+ ancho del texto más largo, por lo que el botón conserva su ancho al alternar
2254
+ entre "valor" y "copiado" (sin reflow). */
2255
+ .kds-copy-button-label {
2256
+ display: inline-grid;
2257
+ }
2258
+
2259
+ .kds-copy-button-value,
2260
+ .kds-copy-button-copied {
2261
+ grid-area: 1 / 1;
2262
+ white-space: nowrap;
2263
+ }
2264
+
2265
+ /* Estado por defecto: visible el valor, oculto (pero ocupando espacio) el copiado */
2266
+ .kds-copy-button-copied {
2267
+ visibility: hidden;
2268
+ }
2269
+
2270
+ /* Al copiar se invierte la visibilidad; el ancho no cambia */
2271
+ .kds-copy-button.copied .kds-copy-button-value {
2272
+ visibility: hidden;
2273
+ }
2274
+
2275
+ .kds-copy-button.copied .kds-copy-button-copied {
2276
+ visibility: visible;
2277
+ }
2278
+
2279
+ .kds-copy-button .material-symbols-outlined {
2280
+ font-size: 1rem;
2281
+ color: var(--kds-color-info-main);
2282
+ flex-shrink: 0;
2283
+ }
2284
+
2285
+ .kds-copy-button.copied {
2286
+ background: var(--kds-alert-success-bg);
2287
+ color: var(--kds-alert-success-text);
2288
+ animation: kds-copy-button-pulse var(--kds-transition-short) var(--kds-easing-standard);
2289
+ }
2290
+
2291
+ .kds-copy-button.copied .material-symbols-outlined {
2292
+ color: var(--kds-color-success-main);
2293
+ /* el check entra con un pop al copiar */
2294
+ animation: kds-copy-button-pop var(--kds-transition-short) var(--kds-easing-standard);
2295
+ }
2296
+
2297
+ @keyframes kds-copy-button-pulse {
2298
+ 0% {
2299
+ transform: scale(1);
2300
+ }
2301
+ 50% {
2302
+ transform: scale(1.04);
2303
+ }
2304
+ 100% {
2305
+ transform: scale(1);
2306
+ }
2307
+ }
2308
+
2309
+ @keyframes kds-copy-button-pop {
2310
+ 0% {
2311
+ transform: scale(0.3);
2312
+ opacity: 0;
2313
+ }
2314
+ 60% {
2315
+ transform: scale(1.25);
2316
+ opacity: 1;
2317
+ }
2318
+ 100% {
2319
+ transform: scale(1);
2320
+ }
2321
+ }
2322
+
2035
2323
  /* ========================================
2036
2324
  Link Styles
2037
2325
  ======================================== */
@@ -2133,6 +2421,16 @@ footer.primary a:hover {
2133
2421
  margin-right: var(--kds-spacing-2);
2134
2422
  }
2135
2423
 
2424
+ /* Ícono(s) en color primary. Para íconos MUI/SVG dentro del scope que, por el reset
2425
+ `.kds-theme-root svg { fill: currentColor }`, terminan heredando el color de texto
2426
+ (#333) en vez de su color propio — el `color="primary"` de MUI no le gana al contexto.
2427
+ Aplica al contenedor y a sus i/svg hijos. */
2428
+ .kds-icon-primary,
2429
+ .kds-icon-primary i,
2430
+ .kds-icon-primary svg {
2431
+ color: var(--kds-color-primary-main);
2432
+ }
2433
+
2136
2434
  /* Section title icon */
2137
2435
  .kds-stage-section-title i {
2138
2436
  vertical-align: middle;
@@ -2164,6 +2462,25 @@ body.dark {
2164
2462
  transition: border-color 0.2s, box-shadow 0.2s, outline-color 0.2s;
2165
2463
  }
2166
2464
 
2465
+ /* === TEXTAREA FIX === */
2466
+ /* BeerCSS base sets minimal block padding; add breathing room for textarea */
2467
+ .field > textarea {
2468
+ padding-block: 0.75rem;
2469
+ resize: vertical;
2470
+ }
2471
+
2472
+ .field.label > textarea {
2473
+ padding-block-start: 1.5rem;
2474
+ }
2475
+
2476
+ /* Label en reposo (estado "abajo", como placeholder): font-size menor al 1rem de BeerCSS
2477
+ para que un label largo no estorbe con un ícono (prefix/suffix) del input. Token sm
2478
+ (13–14px, siempre < 1rem del input incluso en desktop). Al flotar, BeerCSS lo reduce a
2479
+ .75rem con mayor especificidad, así que esto solo afecta el reposo. */
2480
+ .field.label > label {
2481
+ font-size: var(--kds-font-size-sm);
2482
+ }
2483
+
2167
2484
  /* ==========================================
2168
2485
  NOTCH VISIBILITY (opacity)
2169
2486
  ========================================== */
@@ -2285,6 +2602,23 @@ body.dark {
2285
2602
  color: var(--error); /* Rojo error */
2286
2603
  }
2287
2604
 
2605
+ /* ==========================================
2606
+ PASSWORD REVEAL TOGGLE (KdsTextField `revealable`)
2607
+ El <a role="button"> reusa el posicionamiento de icono de BeerCSS
2608
+ (.field > a, pointer-events: all). Aquí solo: color del icono
2609
+ (on-surface-variant) y un focus ring accesible para el toggle
2610
+ navegable por teclado (BeerCSS hace `outline: inherit` en <a>).
2611
+ ========================================== */
2612
+ .field > a.kds-field-reveal {
2613
+ color: var(--on-surface-variant);
2614
+ }
2615
+
2616
+ .field > a.kds-field-reveal:focus-visible {
2617
+ outline: 2px solid var(--primary);
2618
+ outline-offset: 2px;
2619
+ border-radius: 50%;
2620
+ }
2621
+
2288
2622
  /* ==========================================
2289
2623
  LABEL COLOR
2290
2624
  Mantener sincronizado con border
@@ -2392,6 +2726,71 @@ body.dark {
2392
2726
  display: inline-flex;
2393
2727
  }
2394
2728
 
2729
+ /* ========================================
2730
+ Radix Tooltip (.kds-tooltip)
2731
+ Espejo de `.tooltip` pero para popovers de Radix:
2732
+ bg oscuro, texto blanco, sombra + animación fade/slide direccional.
2733
+ ======================================== */
2734
+
2735
+ .kds-tooltip {
2736
+ background-color: var(--kds-color-text-primary);
2737
+ color: var(--kds-color-background-paper);
2738
+ font-family: var(--kds-font-family-primary);
2739
+ font-size: var(--kds-font-size-xs);
2740
+ font-weight: var(--kds-font-weight-medium);
2741
+ line-height: var(--kds-line-height-normal);
2742
+ border-radius: var(--kds-radius-sm);
2743
+ padding: var(--kds-spacing-0-75) var(--kds-spacing-1-5);
2744
+ box-shadow: var(--kds-shadow-4);
2745
+ max-width: 280px;
2746
+ z-index: var(--kds-z-index-tooltip, 1500);
2747
+ user-select: none;
2748
+ /* Animación direccional según Radix data-side — coordinada con copy-row
2749
+ (cubic-bezier 0.2, 0.8, 0.2, 1) para un feel consistente del DS. */
2750
+ animation-duration: 240ms;
2751
+ animation-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1);
2752
+ will-change: transform, opacity;
2753
+ }
2754
+
2755
+ .kds-tooltip[data-state='closed'] {
2756
+ animation-duration: 140ms;
2757
+ animation-timing-function: cubic-bezier(0.4, 0, 0.6, 1);
2758
+ }
2759
+
2760
+ /* Direccional según data-side (Radix añade el attr en runtime).
2761
+ Cada lado entra desde su dirección con ~8px de offset (más visible que 4px). */
2762
+ .kds-tooltip[data-side='top'][data-state='delayed-open'],
2763
+ .kds-tooltip[data-side='top'][data-state='instant-open'] { animation-name: kds-tooltip-in-top; }
2764
+ .kds-tooltip[data-side='bottom'][data-state='delayed-open'],
2765
+ .kds-tooltip[data-side='bottom'][data-state='instant-open'] { animation-name: kds-tooltip-in-bottom; }
2766
+ .kds-tooltip[data-side='left'][data-state='delayed-open'],
2767
+ .kds-tooltip[data-side='left'][data-state='instant-open'] { animation-name: kds-tooltip-in-left; }
2768
+ .kds-tooltip[data-side='right'][data-state='delayed-open'],
2769
+ .kds-tooltip[data-side='right'][data-state='instant-open'] { animation-name: kds-tooltip-in-right; }
2770
+
2771
+ /* Fallback cuando no hay data-side resuelto aún */
2772
+ .kds-tooltip[data-state='delayed-open']:not([data-side]),
2773
+ .kds-tooltip[data-state='instant-open']:not([data-side]) {
2774
+ animation-name: kds-tooltip-in-top;
2775
+ }
2776
+
2777
+ .kds-tooltip[data-state='closed'] { animation-name: kds-tooltip-out; }
2778
+
2779
+ @keyframes kds-tooltip-in-top { from { opacity: 0; transform: translateY(8px) scale(0.96); } to { opacity: 1; transform: none; } }
2780
+ @keyframes kds-tooltip-in-bottom { from { opacity: 0; transform: translateY(-8px) scale(0.96); } to { opacity: 1; transform: none; } }
2781
+ @keyframes kds-tooltip-in-left { from { opacity: 0; transform: translateX(8px) scale(0.96); } to { opacity: 1; transform: none; } }
2782
+ @keyframes kds-tooltip-in-right { from { opacity: 0; transform: translateX(-8px) scale(0.96); } to { opacity: 1; transform: none; } }
2783
+
2784
+ @keyframes kds-tooltip-out {
2785
+ from { opacity: 1; transform: none; }
2786
+ to { opacity: 0; transform: scale(0.96); }
2787
+ }
2788
+
2789
+ /* Arrow (SVG path generado por Radix) */
2790
+ .kds-tooltip-arrow {
2791
+ fill: var(--kds-color-text-primary);
2792
+ }
2793
+
2395
2794
  /* ========================================
2396
2795
  Material Symbols Icon Sizing
2397
2796
  ======================================== */
@@ -2426,6 +2825,16 @@ body.dark {
2426
2825
  width: 100%;
2427
2826
  }
2428
2827
 
2828
+ /* Truncado en una línea con ellipsis. Incluye min-width:0 para que funcione
2829
+ dentro de contenedores flex/grid (el ítem puede encogerse por debajo de su
2830
+ contenido). Útil para nombres/asuntos largos en headers y filas. */
2831
+ .kds-truncate {
2832
+ min-width: 0;
2833
+ overflow: hidden;
2834
+ text-overflow: ellipsis;
2835
+ white-space: nowrap;
2836
+ }
2837
+
2429
2838
  .kds-max-w-sm {
2430
2839
  max-width: 400px;
2431
2840
  }
@@ -2469,6 +2878,11 @@ body.dark {
2469
2878
  display: flex;
2470
2879
  flex-direction: column;
2471
2880
  gap: var(--kds-spacing-3);
2881
+ /* Reset del fieldset nativo: sin borde/padding/margin por defecto del navegador. */
2882
+ margin: 0;
2883
+ padding: 0;
2884
+ border: 0;
2885
+ min-inline-size: 0;
2472
2886
  }
2473
2887
 
2474
2888
  /* Checkbox Label - BeerCSS override */
@@ -2480,7 +2894,7 @@ label.radio {
2480
2894
  border-radius: 8px;
2481
2895
  cursor: pointer;
2482
2896
  transition: background-color 0.2s;
2483
- font-size: 16px;
2897
+ font-size: var(--kds-font-size-sm);
2484
2898
  line-height: 1.5;
2485
2899
  color: var(--kds-color-text-primary);
2486
2900
  }
@@ -2519,20 +2933,56 @@ label.radio:has(input:disabled) {
2519
2933
  }
2520
2934
 
2521
2935
  /* ========================================
2522
- ONBOARDING COMPONENTS
2523
- Multi-stage onboarding flow with form validation
2936
+ RADIO GROUP — variante card (.kds-radio-group--card)
2937
+ Cada opción como fila con borde (estilo selección de banco/cuenta),
2938
+ manteniendo el control radio. Hover + opción seleccionada resaltada.
2524
2939
  ======================================== */
2525
-
2526
- /* Stage Layout - Individual stage wrapper */
2527
- .kds-stage {
2528
- max-width: var(--kds-onboarding-max-width);
2529
- margin: 0 auto;
2530
- width: 100%;
2940
+ .kds-radio-group--card {
2941
+ gap: var(--kds-spacing-2);
2531
2942
  }
2532
2943
 
2533
- /* Stage Header - Title and subtitle */
2534
- .kds-stage-header {
2535
- display: flex;
2944
+ .kds-radio-group--card label.radio {
2945
+ gap: var(--kds-spacing-1-5);
2946
+ /* Vertical con un poco más de aire; horizontal reducido para pegar el radio a la izquierda. */
2947
+ padding: var(--kds-spacing-2) var(--kds-spacing-1-5);
2948
+ /* Bordes con los valores del bank selector. */
2949
+ border: 1px solid var(--kds-color-border-subtle);
2950
+ border-radius: var(--kds-radius-card);
2951
+ background: var(--kds-color-surface);
2952
+ transition: border-color 0.2s, background 0.2s;
2953
+ }
2954
+
2955
+ /* Leve separación extra entre el radio y el texto de la opción. */
2956
+ .kds-radio-group--card label.radio span {
2957
+ margin-left: var(--kds-spacing-0-5);
2958
+ }
2959
+
2960
+ .kds-radio-group--card label.radio:hover {
2961
+ border-color: var(--kds-color-border-default);
2962
+ }
2963
+
2964
+ /* Seleccionada: poco color primario — solo un fondo faint sutil + borde gris
2965
+ (el punto del radio ya comunica la selección). */
2966
+ .kds-radio-group--card label.radio:has(input:checked) {
2967
+ border-color: var(--kds-color-border-default);
2968
+ background: var(--kds-color-primary-faint);
2969
+ }
2970
+
2971
+ /* ========================================
2972
+ ONBOARDING COMPONENTS
2973
+ Multi-stage onboarding flow with form validation
2974
+ ======================================== */
2975
+
2976
+ /* Stage Layout - Individual stage wrapper */
2977
+ .kds-stage {
2978
+ max-width: var(--kds-onboarding-max-width);
2979
+ margin: 0 auto;
2980
+ width: 100%;
2981
+ }
2982
+
2983
+ /* Stage Header - Title and subtitle */
2984
+ .kds-stage-header {
2985
+ display: flex;
2536
2986
  flex-direction: column;
2537
2987
  align-items: flex-start;
2538
2988
  gap: var(--kds-spacing-1);
@@ -3015,19 +3465,15 @@ footer > .kds-container-center {
3015
3465
  display: flex;
3016
3466
  align-items: center;
3017
3467
  gap: var(--kds-spacing-1);
3018
- padding: var(--kds-spacing-2);
3468
+ /* Padding compacto: vertical spacing-1 (8px), horizontal spacing-2 (16px). */
3469
+ padding: var(--kds-spacing-1) var(--kds-spacing-2);
3019
3470
  border-radius: var(--kds-radius-md);
3020
3471
  border: 1px solid;
3021
3472
  font-family: var(--kds-font-family-primary);
3022
3473
  min-width: 0;
3023
- flex: 1 1 100%;
3024
- }
3025
-
3026
- @media (min-width: 768px) {
3027
- .kds-alert {
3028
- padding: var(--kds-spacing-2);
3029
- flex: 1 1 auto;
3030
- }
3474
+ /* Bloque full-width (display:flex ya llena el ancho); sin flex-grow para no estirarse
3475
+ verticalmente cuando es hijo de un contenedor flex-column. */
3476
+ flex: 0 1 auto;
3031
3477
  }
3032
3478
 
3033
3479
  .kds-alert:last-child {
@@ -3053,7 +3499,9 @@ footer > .kds-container-center {
3053
3499
  font-size: var(--kds-font-size-sm);
3054
3500
  font-weight: var(--kds-font-weight-semibold);
3055
3501
  line-height: var(--kds-line-height-normal);
3056
- margin-bottom: var(--kds-spacing-0-5);
3502
+ /* Solo margin-bottom: el padding del alert da el espaciado. Reset del resto para
3503
+ evitar el margin-block que BeerCSS aplica a <p> dentro del scope. */
3504
+ margin: 0 0 var(--kds-spacing-0-5);
3057
3505
  word-break: normal;
3058
3506
  overflow-wrap: break-word;
3059
3507
  }
@@ -3063,6 +3511,9 @@ footer > .kds-container-center {
3063
3511
  font-size: var(--kds-font-size-sm);
3064
3512
  font-weight: var(--kds-font-weight-regular);
3065
3513
  line-height: var(--kds-line-height-relaxed);
3514
+ /* Sin margen propio: el padding del alert es el único espaciado (evita el
3515
+ margin-block de <p> de BeerCSS dentro del scope). */
3516
+ margin: 0;
3066
3517
  word-break: normal;
3067
3518
  overflow-wrap: break-word;
3068
3519
  }
@@ -3108,6 +3559,32 @@ footer > .kds-container-center {
3108
3559
  color: var(--kds-color-error-dark);
3109
3560
  }
3110
3561
 
3562
+ /* Close button — icon-only, discreto, no usa kds-btn para no romper el flex layout del alert */
3563
+ .kds-alert-close {
3564
+ flex-shrink: 0;
3565
+ display: inline-flex;
3566
+ align-items: center;
3567
+ justify-content: center;
3568
+ width: 28px;
3569
+ height: 28px;
3570
+ padding: 0;
3571
+ background: transparent;
3572
+ border: 0;
3573
+ border-radius: 50%;
3574
+ color: inherit;
3575
+ cursor: pointer;
3576
+ transition: background-color 0.15s ease;
3577
+ }
3578
+
3579
+ .kds-alert-close:hover {
3580
+ background: rgba(0, 0, 0, 0.06);
3581
+ }
3582
+
3583
+ .kds-alert-close i {
3584
+ font-size: 18px;
3585
+ line-height: 1;
3586
+ }
3587
+
3111
3588
  /* Lists inside alerts */
3112
3589
  .kds-alert ul {
3113
3590
  padding-left: var(--kds-spacing-2);
@@ -4104,6 +4581,14 @@ dialog#surveyModal button.circle {
4104
4581
  text-align: center;
4105
4582
  }
4106
4583
 
4584
+ .kds-text-left {
4585
+ text-align: left;
4586
+ }
4587
+
4588
+ .kds-text-right {
4589
+ text-align: right;
4590
+ }
4591
+
4107
4592
  .kds-text-underline {
4108
4593
  text-decoration: underline;
4109
4594
  }
@@ -4120,6 +4605,54 @@ dialog#surveyModal button.circle {
4120
4605
  font-size: var(--kds-font-size-sm);
4121
4606
  }
4122
4607
 
4608
+ /* ── Font-size scale utilities (additive — opt-in companions to .kds-text-sm) ── */
4609
+ .kds-text-base {
4610
+ font-size: var(--kds-font-size-base);
4611
+ }
4612
+
4613
+ .kds-text-lg {
4614
+ font-size: var(--kds-font-size-lg);
4615
+ }
4616
+
4617
+ .kds-text-xl {
4618
+ font-size: var(--kds-font-size-xl);
4619
+ }
4620
+
4621
+ .kds-text-2xl {
4622
+ font-size: var(--kds-font-size-2xl);
4623
+ }
4624
+
4625
+ /* ── Font-weight utilities ── */
4626
+ .kds-fw-regular {
4627
+ font-weight: var(--kds-font-weight-regular);
4628
+ }
4629
+
4630
+ .kds-fw-medium {
4631
+ font-weight: var(--kds-font-weight-medium);
4632
+ }
4633
+
4634
+ .kds-fw-semibold {
4635
+ font-weight: var(--kds-font-weight-semibold);
4636
+ }
4637
+
4638
+ .kds-fw-bold {
4639
+ font-weight: var(--kds-font-weight-bold);
4640
+ }
4641
+
4642
+ /* ── Text-transform utilities ── */
4643
+ .kds-text-transform-none {
4644
+ text-transform: none;
4645
+ }
4646
+
4647
+ /* ── Soft background utilities (semantic muted backgrounds for callouts) ── */
4648
+ .kds-bg-warning-soft {
4649
+ background-color: var(--kds-color-warning-soft);
4650
+ }
4651
+
4652
+ .kds-bg-info-soft {
4653
+ background-color: var(--kds-color-info-soft);
4654
+ }
4655
+
4123
4656
  /* ── Typography variants ── */
4124
4657
  .kds-text-display1 {
4125
4658
  font-family: var(--kds-font-family-primary, 'Public Sans', sans-serif);
@@ -4197,6 +4730,35 @@ dialog#surveyModal button.circle {
4197
4730
  text-transform: uppercase;
4198
4731
  }
4199
4732
 
4733
+ /* Value — dato/valor destacado (ej. nombre del comercio en el header del cobro). */
4734
+ .kds-text-value {
4735
+ font-family: var(--kds-font-family-primary, 'Public Sans', sans-serif);
4736
+ font-size: var(--kds-font-size-sm);
4737
+ font-weight: var(--kds-font-weight-semibold);
4738
+ line-height: var(--kds-line-height-relaxed);
4739
+ color: var(--kds-color-gray-800);
4740
+ }
4741
+
4742
+ /* Code — texto tipo código/referencia (mono, uppercase, gris), como el "CÓDIGO" del header. */
4743
+ .kds-text-code {
4744
+ font-family: var(--kds-font-family-mono);
4745
+ font-size: var(--kds-font-size-xs);
4746
+ font-weight: var(--kds-font-weight-regular);
4747
+ letter-spacing: var(--kds-letter-spacing-wider);
4748
+ font-variant-numeric: tabular-nums;
4749
+ color: var(--kds-color-gray-400);
4750
+ text-transform: uppercase;
4751
+ }
4752
+
4753
+ /* Caption — texto muy chico y liviano para pie de página (ej. número de versión). */
4754
+ .kds-text-caption {
4755
+ font-family: var(--kds-font-family-primary, 'Public Sans', sans-serif);
4756
+ font-size: var(--kds-font-size-2xs);
4757
+ font-weight: var(--kds-font-weight-regular);
4758
+ line-height: var(--kds-line-height-normal);
4759
+ color: var(--kds-color-gray-400);
4760
+ }
4761
+
4200
4762
  .kds-text-link {
4201
4763
  font-family: var(--kds-font-family-primary, 'Public Sans', sans-serif);
4202
4764
  font-size: inherit;
@@ -4210,6 +4772,12 @@ dialog#surveyModal button.circle {
4210
4772
  color: var(--kds-color-primary-dark, #5B3179);
4211
4773
  }
4212
4774
 
4775
+ /* Inline emphasis: bold weight only, inherits size/color/family from context so it
4776
+ works as a highlighted fragment inside any other variant (body, heading, etc.). */
4777
+ .kds-text-strong {
4778
+ font-weight: var(--kds-font-weight-bold, 700);
4779
+ }
4780
+
4213
4781
  /* Display utilities */
4214
4782
  .kds-hidden {
4215
4783
  display: none;
@@ -4330,7 +4898,9 @@ dialog#surveyModal button.circle {
4330
4898
  ======================================== */
4331
4899
 
4332
4900
  .kds-badge {
4333
- display: inline-block;
4901
+ display: inline-flex;
4902
+ align-items: center;
4903
+ gap: 4px;
4334
4904
  padding: 2px 8px;
4335
4905
  border-radius: var(--kds-radius-sm);
4336
4906
  font-size: var(--kds-font-size-xs);
@@ -4338,6 +4908,40 @@ dialog#surveyModal button.circle {
4338
4908
  line-height: 1.5;
4339
4909
  }
4340
4910
 
4911
+ /* Icon prefix inside chip — small inline icon */
4912
+ .kds-badge > i.material-symbols-outlined {
4913
+ font-size: 14px;
4914
+ line-height: 1;
4915
+ }
4916
+
4917
+ /* Deletable chip close button — discreto, sin kds-btn (eso lo agrandaba ridículo) */
4918
+ .kds-badge-close {
4919
+ display: inline-flex;
4920
+ align-items: center;
4921
+ justify-content: center;
4922
+ width: 16px;
4923
+ height: 16px;
4924
+ margin-right: -2px;
4925
+ padding: 0;
4926
+ background: transparent;
4927
+ border: 0;
4928
+ border-radius: 50%;
4929
+ color: inherit;
4930
+ cursor: pointer;
4931
+ opacity: 0.7;
4932
+ transition: opacity 0.15s, background-color 0.15s;
4933
+ }
4934
+
4935
+ .kds-badge-close:hover {
4936
+ opacity: 1;
4937
+ background: rgba(0, 0, 0, 0.1);
4938
+ }
4939
+
4940
+ .kds-badge-close i {
4941
+ font-size: 14px;
4942
+ line-height: 1;
4943
+ }
4944
+
4341
4945
  .kds-badge.success {
4342
4946
  background-color: var(--kds-color-success-container);
4343
4947
  color: var(--kds-color-success-dark);
@@ -4549,21 +5153,46 @@ dialog#surveyModal button.circle {
4549
5153
  flex-shrink: 0;
4550
5154
  }
4551
5155
 
4552
- /* Merchant tile with logo variant */
5156
+ /* Merchant tile with logo variant — el logo (avatar del comercio) llena TODO el tile.
5157
+ `display: block` evita el desborde del auto-grid; `overflow: hidden` recorta la imagen
5158
+ a las esquinas redondeadas del tile. La imagen va CUADRADA (sin border-radius propio):
5159
+ solo el contenedor recorta, evitando el hairline sub-pixel del doble redondeo. El fondo
5160
+ se deja transparente bajo el logo como respaldo (no debe verse púrpura en los bordes). */
4553
5161
  .kds-merchant-tile.logo {
4554
- padding: var(--kds-spacing-0-5);
5162
+ padding: 0;
5163
+ display: block;
5164
+ overflow: hidden;
5165
+ background: transparent;
4555
5166
  }
4556
5167
 
4557
5168
  .kds-merchant-tile.logo img {
4558
5169
  width: 100%;
4559
5170
  height: 100%;
4560
5171
  object-fit: cover;
4561
- border-radius: var(--kds-radius-md);
5172
+ border-radius: 0;
5173
+ display: block;
5174
+ }
5175
+
5176
+ /* Variante para logos que traen su propio marco/forma (logos de comercio/servicio,
5177
+ no fotos): `contain` + padding para que el logo entre completo sin recortar sus
5178
+ esquinas. Opt-in: pasar className="kds-merchant-tile--contain". */
5179
+ .kds-merchant-tile.logo.kds-merchant-tile--contain {
5180
+ padding: var(--kds-spacing-1);
5181
+ }
5182
+ .kds-merchant-tile.logo.kds-merchant-tile--contain img {
5183
+ object-fit: contain;
4562
5184
  }
4563
5185
 
4564
5186
  .kds-merchant-meta {
4565
5187
  display: flex;
4566
5188
  flex-direction: column;
5189
+ gap: var(--kds-spacing-0-5);
5190
+ }
5191
+
5192
+ /* El meta controla su propio espaciado: anula el margin sibling de BeerCSS
5193
+ (`… + :is(p,h*,…) { margin-block-start: 1rem }`) que separaba de más las líneas. */
5194
+ .kds-merchant-meta > * {
5195
+ margin-block: 0 !important;
4567
5196
  }
4568
5197
 
4569
5198
  .kds-merchant-label {
@@ -4616,6 +5245,11 @@ dialog#surveyModal button.circle {
4616
5245
  color: var(--kds-color-text-primary);
4617
5246
  }
4618
5247
 
5248
+ /* Opt-in color modifier — switches the value to the LigoPay/khipu-blue tone */
5249
+ .kds-amount-value--info {
5250
+ color: var(--kds-color-info-blue); /* #5A5FE0 */
5251
+ }
5252
+
4619
5253
  .kds-amount-value small {
4620
5254
  font-size: 18px;
4621
5255
  font-weight: 500;
@@ -4901,6 +5535,144 @@ dialog#surveyModal button.circle {
4901
5535
  box-shadow: 0 -4px 16px rgba(16,24,40,.08);
4902
5536
  }
4903
5537
 
5538
+ /* ========================================
5539
+ 08b - FAB (.kds-fab)
5540
+ Circular, icon-only floating action button with an animated hide state.
5541
+ Presentational: drive `.kds-fab--hidden` from useHideOnScroll for scroll-away.
5542
+ ======================================== */
5543
+
5544
+ .kds-fab {
5545
+ display: inline-flex;
5546
+ align-items: center;
5547
+ justify-content: center;
5548
+ width: var(--kds-spacing-5);
5549
+ height: var(--kds-spacing-5);
5550
+ padding: 0;
5551
+ border: none;
5552
+ border-radius: var(--kds-radius-full);
5553
+ background: var(--kds-color-background-paper);
5554
+ color: var(--kds-color-text-primary);
5555
+ box-shadow: var(--kds-shadow-elevated);
5556
+ cursor: pointer;
5557
+ z-index: 100;
5558
+ /* Neutralize native button rendering so the pressed state doesn't flash a square. */
5559
+ -webkit-appearance: none;
5560
+ appearance: none;
5561
+ -webkit-tap-highlight-color: transparent;
5562
+ transition: opacity var(--kds-transition-short) ease, transform var(--kds-transition-short) ease,
5563
+ background-color var(--kds-transition-shorter) ease;
5564
+ }
5565
+
5566
+ /* Keep the circle in EVERY interaction state. Uses `button.kds-fab` (element + class) to
5567
+ out-specify BeerCSS's base `button`/`button:active` rules — same trick as `.kds-btn`. */
5568
+ button.kds-fab,
5569
+ button.kds-fab:hover,
5570
+ button.kds-fab:active,
5571
+ button.kds-fab:focus,
5572
+ button.kds-fab:focus-visible {
5573
+ border-radius: var(--kds-radius-full);
5574
+ background: var(--kds-color-background-paper);
5575
+ }
5576
+
5577
+ button.kds-fab:hover:not(:disabled) {
5578
+ background: var(--kds-color-background-muted);
5579
+ }
5580
+
5581
+ /* Subtle press feedback that preserves the round shape. */
5582
+ button.kds-fab:active:not(:disabled) {
5583
+ transform: translateY(var(--kds-spacing-0-25));
5584
+ }
5585
+
5586
+ button.kds-fab:focus-visible {
5587
+ outline: var(--kds-spacing-0-25) solid var(--kds-color-primary-main);
5588
+ outline-offset: var(--kds-spacing-0-25);
5589
+ }
5590
+
5591
+ .kds-fab > i.material-symbols-outlined {
5592
+ font-size: var(--kds-spacing-button-icon-size);
5593
+ line-height: 1;
5594
+ }
5595
+
5596
+ /* Pinned corners — fixed to the viewport (stays visible on scroll-up, iframe-friendly).
5597
+ Horizontally the top-right FAB hugs the right edge of the centered content column set via
5598
+ `--kds-fab-column-width` (default 100% → viewport edge). The consumer sets that var to its
5599
+ widget width so on desktop the FAB overlaps the header's top-right corner instead of drifting
5600
+ to the far viewport edge. `max(...)` keeps at least a token inset on narrow (mobile) viewports. */
5601
+ .kds-fab--top-right,
5602
+ .kds-fab--top-left,
5603
+ .kds-fab--bottom-right,
5604
+ .kds-fab--bottom-left {
5605
+ position: fixed;
5606
+ }
5607
+
5608
+ .kds-fab--top-right {
5609
+ top: var(--kds-spacing-2);
5610
+ right: max(var(--kds-spacing-2), calc((100% - var(--kds-fab-column-width, 100%)) / 2 + var(--kds-spacing-2)));
5611
+ }
5612
+
5613
+ .kds-fab--top-left {
5614
+ top: var(--kds-spacing-2);
5615
+ left: max(var(--kds-spacing-2), calc((100% - var(--kds-fab-column-width, 100%)) / 2 + var(--kds-spacing-2)));
5616
+ }
5617
+
5618
+ .kds-fab--bottom-right {
5619
+ bottom: var(--kds-spacing-2);
5620
+ right: max(var(--kds-spacing-2), calc((100% - var(--kds-fab-column-width, 100%)) / 2 + var(--kds-spacing-2)));
5621
+ }
5622
+
5623
+ .kds-fab--bottom-left {
5624
+ bottom: var(--kds-spacing-2);
5625
+ left: max(var(--kds-spacing-2), calc((100% - var(--kds-fab-column-width, 100%)) / 2 + var(--kds-spacing-2)));
5626
+ }
5627
+
5628
+ /* Desktop: nudge the top-right FAB up so it sits partially over the header's top edge. */
5629
+ @media (min-width: 768px) {
5630
+ .kds-fab--top-right {
5631
+ top: var(--kds-spacing-1);
5632
+ }
5633
+ }
5634
+
5635
+ /* Corner-overlap: mount the FAB centered OVER the top-right corner of the content column
5636
+ (`--kds-fab-column-width`), overlapping outward — vs `--top-right` which insets inside the
5637
+ column. Mobile keeps a small inset from the viewport via the `--kds-fab-inset` token. */
5638
+ .kds-fab--corner {
5639
+ --kds-fab-inset: 5px;
5640
+ position: fixed;
5641
+ top: var(--kds-fab-inset);
5642
+ right: var(--kds-fab-inset);
5643
+ }
5644
+
5645
+ @media (min-width: 768px) {
5646
+ .kds-fab--corner {
5647
+ right: max(
5648
+ var(--kds-fab-inset),
5649
+ calc((100% - var(--kds-fab-column-width, 100%)) / 2 - var(--kds-spacing-5) / 2)
5650
+ );
5651
+ }
5652
+ }
5653
+
5654
+ /* Hidden (scroll-away) state — fade + slide toward the nearest edge */
5655
+ .kds-fab--hidden {
5656
+ opacity: 0;
5657
+ transform: translateY(calc(-1 * var(--kds-spacing-1))) scale(0.9);
5658
+ pointer-events: none;
5659
+ }
5660
+
5661
+ .kds-fab--bottom-right.kds-fab--hidden,
5662
+ .kds-fab--bottom-left.kds-fab--hidden {
5663
+ transform: translateY(var(--kds-spacing-1)) scale(0.9);
5664
+ }
5665
+
5666
+ @media (prefers-reduced-motion: reduce) {
5667
+ .kds-fab {
5668
+ transition: opacity var(--kds-transition-short) ease;
5669
+ }
5670
+
5671
+ .kds-fab--hidden {
5672
+ transform: none;
5673
+ }
5674
+ }
5675
+
4904
5676
  /* ========================================
4905
5677
  09 - SUCCESS MARK (.kds-success-mark)
4906
5678
  Large success icon for completion screens
@@ -4973,29 +5745,29 @@ dialog#surveyModal button.circle {
4973
5745
  display: flex;
4974
5746
  align-items: center;
4975
5747
  justify-content: center;
4976
- gap: var(--kds-spacing-1);
5748
+ gap: var(--kds-spacing-0-5);
4977
5749
  color: var(--kds-color-gray-400);
4978
5750
  font-size: var(--kds-font-size-xs);
4979
5751
  letter-spacing: var(--kds-letter-spacing-wide);
4980
5752
  padding: var(--kds-spacing-1) 0;
4981
5753
  }
4982
5754
 
4983
- /* Desktop: footer inside card hidden; Mobile: footer outside card hidden */
5755
+ /* Footer SIEMPRE fuera del card body (desktop y mobile): el footer interno
5756
+ (variant="inside") queda oculto en todos los breakpoints; se muestra el
5757
+ externo, hermano directo del card bajo `.kds-screen`. */
4984
5758
  .kds-card-elevated > .kds-secure-footer {
4985
5759
  display: none;
4986
5760
  }
4987
5761
 
4988
- @media (max-width: 767px) {
4989
- .kds-card-elevated > .kds-secure-footer {
4990
- display: flex;
4991
- }
4992
- .kds-screen > .kds-secure-footer {
4993
- display: none;
4994
- }
5762
+ .kds-screen > form {
5763
+ margin-block-start: 0 !important;
5764
+ }
4995
5765
 
4996
- .kds-screen > form {
4997
- margin-block-start: 0 !important;
4998
- }
5766
+ /* Footer externo adyacente a la card: cancela el gap del .kds-screen para que
5767
+ quede pegado debajo de la card y no flotando lejos. Las dos cards
5768
+ (header/body) sí conservan su separación. */
5769
+ .kds-screen > .kds-secure-footer {
5770
+ margin-top: calc(-1 * var(--kds-spacing-3));
4999
5771
  }
5000
5772
 
5001
5773
  .kds-secure-footer-lock {
@@ -5017,6 +5789,32 @@ dialog#surveyModal button.circle {
5017
5789
  display: block;
5018
5790
  object-fit: contain;
5019
5791
  overflow: visible;
5792
+ /* Corrección óptica: la masa visible del wordmark (el cuerpo de "Khipu") queda sobre el
5793
+ centro geométrico de su viewBox (45x15) porque el descendente de la "p" ocupa la parte
5794
+ baja. Al centrar por caja (align-items:center) el logo se ve ~1px alto respecto al texto,
5795
+ así que se baja 0.75px para alinear el centro óptico. Alto fijo (11px) → nudge consistente. */
5796
+ position: relative;
5797
+ top: 0.75px;
5798
+ }
5799
+
5800
+ /* Divisor entre el logo de Khipu y el del PSP ("Khipu | klap") */
5801
+ .kds-secure-footer-sep {
5802
+ width: 1px;
5803
+ height: 0.85em;
5804
+ background: currentColor;
5805
+ opacity: 0.35;
5806
+ flex: 0 0 auto;
5807
+ }
5808
+
5809
+ /* Logo del PSP (proveedor de pagos): lo provee el consumidor, lo alineamos al mismo
5810
+ alto que el mark de Khipu. Sirve para <img> o un elemento con .kds-psp-mark. */
5811
+ .kds-secure-footer img,
5812
+ .kds-secure-footer .kds-psp-mark {
5813
+ height: 11px;
5814
+ width: auto;
5815
+ flex: 0 0 auto;
5816
+ display: block;
5817
+ object-fit: contain;
5020
5818
  }
5021
5819
 
5022
5820
  .kds-secure-footer-code {
@@ -5032,25 +5830,45 @@ dialog#surveyModal button.circle {
5032
5830
  background: none;
5033
5831
  border: 0;
5034
5832
  padding: 0;
5833
+ /* Reset del bare `button { height: 2.5rem }` de BeerCSS (scoped en .kds-theme-root):
5834
+ sin esto el toggle quedaba en una caja de 40px con el texto centrado → espacio
5835
+ irregular arriba (pegado al resumen) y abajo (~26px hasta el panel). Con height
5836
+ auto el botón ajusta a su contenido y el ritmo lo gobiernan los márgenes. */
5837
+ height: auto;
5035
5838
  color: var(--kds-color-primary-main);
5036
5839
  font-weight: var(--kds-font-weight-medium);
5037
5840
  font-size: var(--kds-font-size-sm);
5038
5841
  cursor: pointer;
5039
5842
  display: inline-flex;
5040
5843
  align-items: center;
5041
- gap: var(--kds-spacing-0-75);
5844
+ gap: 0;
5042
5845
  }
5043
5846
 
5044
- .kds-expand-toggle:hover {
5847
+ .kds-expand-toggle:hover,
5848
+ .kds-expand-toggle:focus,
5849
+ .kds-expand-toggle:active {
5045
5850
  background: transparent;
5851
+ outline: none;
5852
+ box-shadow: none;
5046
5853
  }
5047
5854
 
5048
- .kds-expand-toggle:hover span {
5855
+ .kds-expand-toggle:hover span,
5856
+ .kds-expand-toggle:focus span,
5857
+ .kds-expand-toggle:active span {
5049
5858
  text-decoration: underline;
5050
5859
  }
5051
5860
 
5052
5861
  .kds-expand-toggle i {
5053
5862
  font-size: var(--kds-font-size-base);
5863
+ /* BeerCSS estiliza el `i` bare como caja flex de 30px (height/width/min: 30px), que
5864
+ inflaba el toggle y dejaba aire vertical de más alrededor del label. La achicamos a
5865
+ 1.25em MANTENIENDO el centrado flex de BeerCSS (display:flex/align/justify) — así el
5866
+ caret queda compacto Y ópticamente centrado con el texto (no usar height:auto ni
5867
+ line-height:1: descentran el glifo). */
5868
+ width: 1.25em;
5869
+ height: 1.25em;
5870
+ min-width: 0;
5871
+ min-height: 0;
5054
5872
  transition: transform var(--kds-transition-shorter, 0.2s);
5055
5873
  }
5056
5874
 
@@ -5070,7 +5888,10 @@ dialog#surveyModal button.circle {
5070
5888
  }
5071
5889
 
5072
5890
  .kds-expand-panel.open {
5073
- max-height: 800px;
5891
+ /* Fallback cap for no-JS consumers. With JS (React useExpandToggle / vanilla
5892
+ initExpandToggle) the panel's inline max-height is set to its exact scrollHeight,
5893
+ which overrides this and prevents clipping regardless of content length. */
5894
+ max-height: 2000px;
5074
5895
  margin-top: 0;
5075
5896
  }
5076
5897
 
@@ -5188,13 +6009,17 @@ dialog#surveyModal button.circle {
5188
6009
 
5189
6010
  .kds-status-block {
5190
6011
  text-align: center;
5191
- padding: var(--kds-spacing-6) 0;
6012
+ /* Padding vertical estándar: spacing-2 (16px), el mismo rhythm inter-elementos
6013
+ que usan kds-btn-stack y kds-hr-dashed. NO usar spacing-6 (48px): genera
6014
+ demasiado espacio cuando el bloque se compone con header/divider alrededor. */
6015
+ padding: var(--kds-spacing-2) 0;
5192
6016
  }
5193
6017
 
5194
6018
  .kds-status-block-icon {
5195
6019
  width: var(--kds-status-icon-size);
5196
6020
  height: var(--kds-status-icon-size);
5197
- margin: 0 auto var(--kds-spacing-3);
6021
+ /* spacing-2 (16px) icono→título: rhythm estándar inter-elementos. */
6022
+ margin: 0 auto var(--kds-spacing-2);
5198
6023
  border-radius: var(--kds-radius-full);
5199
6024
  display: grid;
5200
6025
  place-items: center;
@@ -5225,11 +6050,11 @@ dialog#surveyModal button.circle {
5225
6050
  color: var(--kds-color-success-main);
5226
6051
  }
5227
6052
 
5228
- /* Variant: pending (animated spinner) */
6053
+ /* Variant: pending (animated blue spinner, sin icono interno) */
5229
6054
  .kds-status-block[data-status="pending"] .kds-status-block-icon {
5230
6055
  background: transparent;
5231
6056
  border: 3px solid var(--kds-color-divider);
5232
- border-top-color: var(--kds-color-primary-main);
6057
+ border-top-color: var(--kds-color-info-main);
5233
6058
  animation: kds-spin 1s linear infinite;
5234
6059
  }
5235
6060
 
@@ -5247,12 +6072,21 @@ dialog#surveyModal button.circle {
5247
6072
  color: var(--kds-color-error-main);
5248
6073
  }
5249
6074
 
6075
+ /* Variant: info — usar SIEMPRE con icon "info_i" (i minúscula sin glyph circular)
6076
+ para que solo se vea nuestro círculo decorativo. Iconos como "info" tienen
6077
+ su propio círculo built-in → causarían doble círculo. */
6078
+ .kds-status-block[data-status="info"] .kds-status-block-icon {
6079
+ background: var(--kds-color-info-soft);
6080
+ border: var(--kds-border-width-md) solid var(--kds-color-info-main);
6081
+ color: var(--kds-color-info-main);
6082
+ }
6083
+
5250
6084
  /* Inline variant: icon left + text right */
5251
6085
  .kds-status-block.inline {
5252
6086
  text-align: left;
5253
6087
  display: flex;
5254
6088
  align-items: center;
5255
- gap: var(--kds-spacing-3);
6089
+ gap: var(--kds-spacing-2);
5256
6090
  padding: var(--kds-spacing-1) 0;
5257
6091
  }
5258
6092
 
@@ -5314,6 +6148,40 @@ dialog#surveyModal button.circle {
5314
6148
  background: var(--kds-color-divider);
5315
6149
  }
5316
6150
 
6151
+ /* Close button — top-right discreto, NO usa kds-btn (rompía el flex layout) */
6152
+ .kds-bottom-sheet-close {
6153
+ position: absolute;
6154
+ top: var(--kds-spacing-1);
6155
+ right: var(--kds-spacing-1);
6156
+ width: 32px;
6157
+ height: 32px;
6158
+ display: inline-flex;
6159
+ align-items: center;
6160
+ justify-content: center;
6161
+ padding: 0;
6162
+ background: transparent;
6163
+ border: 0;
6164
+ border-radius: 50%;
6165
+ color: var(--kds-color-text-secondary);
6166
+ cursor: pointer;
6167
+ transition: background-color 0.15s ease;
6168
+ }
6169
+
6170
+ .kds-bottom-sheet-close:hover {
6171
+ background: rgba(0, 0, 0, 0.06);
6172
+ }
6173
+
6174
+ .kds-bottom-sheet-close i {
6175
+ font-size: 20px;
6176
+ line-height: 1;
6177
+ }
6178
+
6179
+ /* Body container — separa el content del title/actions */
6180
+ .kds-bottom-sheet-body {
6181
+ text-align: left;
6182
+ margin-top: var(--kds-spacing-1);
6183
+ }
6184
+
5317
6185
  .kds-bottom-sheet-icon {
5318
6186
  font-size: var(--kds-font-size-4xl);
5319
6187
  }
@@ -5547,6 +6415,30 @@ dialog#surveyModal button.circle {
5547
6415
  /* No max-height — clip-path on parent handles visual collapse without layout changes */
5548
6416
  .kds-invoice-sticky .kds-invoice-collapsible {
5549
6417
  opacity: calc(1 - var(--collapse-progress) * 1.5);
6418
+
6419
+ /* Scroll interno discreto (mobile): cuando el detalle abierto excede el viewport, el
6420
+ collapsible scrollea internamente (monto/merchant quedan fijos arriba) para poder leer
6421
+ todo el detalle. Al tocar el fondo, `overscroll-behavior: auto` ENCADENA el gesto al
6422
+ scroll del body → dispara useStickyInvoiceCollapse → colapsa el header y aparece el form
6423
+ (flujo natural). Se auto-desactiva con el detalle cerrado (contenido más corto que el
6424
+ max-height → sin scroll ni efecto visual). */
6425
+ max-height: calc(100vh - var(--kds-spacing-12) - var(--kds-spacing-4));
6426
+ max-height: calc(100dvh - var(--kds-spacing-12) - var(--kds-spacing-4));
6427
+ overflow-y: auto;
6428
+ overflow-x: hidden;
6429
+ overscroll-behavior-y: auto;
6430
+ scrollbar-width: thin;
6431
+ scrollbar-color: var(--kds-color-gray-300) transparent;
6432
+ }
6433
+ .kds-invoice-sticky .kds-invoice-collapsible::-webkit-scrollbar {
6434
+ width: 4px;
6435
+ }
6436
+ .kds-invoice-sticky .kds-invoice-collapsible::-webkit-scrollbar-thumb {
6437
+ background: var(--kds-color-gray-300);
6438
+ border-radius: var(--kds-radius-full);
6439
+ }
6440
+ .kds-invoice-sticky .kds-invoice-collapsible::-webkit-scrollbar-track {
6441
+ background: transparent;
5550
6442
  }
5551
6443
 
5552
6444
  /* Amount: 30px → 24px (progressive) */
@@ -5618,6 +6510,14 @@ dialog#surveyModal button.circle {
5618
6510
  margin-top: var(--kds-spacing-1);
5619
6511
  }
5620
6512
 
6513
+ /* Ritmo uniforme alrededor del toggle: spacing-2 arriba (resumen↔"Ver detalle")
6514
+ iguala el padding-top spacing-2 que la .kds-detail-list da debajo
6515
+ ("Ver detalle"↔primer label del panel). Antes el alto fijo del bare button
6516
+ (40px) hacía que el espacio de arriba y de abajo fueran distintos. */
6517
+ .kds-invoice-collapsible > .kds-expand-toggle {
6518
+ margin-top: var(--kds-spacing-2);
6519
+ }
6520
+
5621
6521
  /* -- Invoice Summary -- */
5622
6522
  .kds-invoice-summary {
5623
6523
  display: flex;
@@ -5641,6 +6541,22 @@ div.kds-invoice-header,
5641
6541
  padding: 0;
5642
6542
  border-radius: 0;
5643
6543
  align-content: initial;
6544
+ /* Override BeerCSS `header { min-height: ... }` — el header debe ajustarse a su
6545
+ contenido (sigue el alto del merchant tile: 64px→50px al colapsar). Sin esto
6546
+ quedaba un min-height fijo de 64px → hueco muerto debajo al colapsar en mobile. */
6547
+ min-height: 0;
6548
+ }
6549
+
6550
+ /* Mobile: center-align el header (merchant tile vs bloque monto+código) tanto
6551
+ expandido como colapsado. Al quitar el logo del header, el bloque de texto quedaba
6552
+ más bajo que el merchant tile y con `flex-start` dejaba hueco abajo; centrado los
6553
+ alinea. CSS-only (no depende de la clase .is-collapsed, que el JS de collapse no setea). */
6554
+ @media (max-width: 767px) {
6555
+ header.kds-invoice-header,
6556
+ div.kds-invoice-header,
6557
+ .kds-invoice-header {
6558
+ align-items: center;
6559
+ }
5644
6560
  }
5645
6561
 
5646
6562
  /* Reset BeerCSS sibling margin on <p> inside invoice header — specificity (0,4,1) ties BeerCSS, wins by source order */
@@ -5650,7 +6566,7 @@ div.kds-invoice-header,
5650
6566
  }
5651
6567
 
5652
6568
  .kds-invoice-amount {
5653
- font-weight: var(--kds-font-weight-bold);
6569
+ font-weight: var(--kds-font-weight-semibold);
5654
6570
  font-size: var(--kds-font-size-3xl);
5655
6571
  line-height: var(--kds-line-height-tight);
5656
6572
  letter-spacing: var(--kds-letter-spacing-wide);
@@ -5671,10 +6587,16 @@ div.kds-invoice-header,
5671
6587
  color: var(--kds-color-gray-400);
5672
6588
  text-transform: uppercase;
5673
6589
  margin: 0;
5674
- margin-top: var(--kds-spacing-1);
6590
+ margin-block: unset;
6591
+ display: flex;
6592
+ gap: var(--kds-spacing-1-5);
5675
6593
  }
5676
6594
 
5677
6595
  .kds-invoice-code-value {
6596
+ color: var(--kds-color-gray-400);
6597
+ }
6598
+
6599
+ .kds-invoice-code-value--lowercase {
5678
6600
  text-transform: lowercase;
5679
6601
  }
5680
6602
 
@@ -5687,6 +6609,9 @@ div.kds-invoice-header,
5687
6609
  place-items: center;
5688
6610
  color: #fff;
5689
6611
  flex-shrink: 0;
6612
+ /* Clip the slightly-oversized logo (see `.kds-invoice-merchant img`) to the
6613
+ rounded square so it can fully cover the deep-purple background. */
6614
+ overflow: hidden;
5690
6615
  }
5691
6616
 
5692
6617
  .kds-invoice-merchant i {
@@ -5706,28 +6631,43 @@ div.kds-invoice-header,
5706
6631
  }
5707
6632
 
5708
6633
  .kds-invoice-merchant img {
5709
- width: 100%;
5710
- height: 100%;
6634
+ /* Slightly larger than the container (+2px, centered by the grid) so the
6635
+ logo overflows under the parent's rounded clip and the deep-purple
6636
+ background never peeks through at the corners when the logo shrinks. */
6637
+ width: calc(100% + 2px);
6638
+ height: calc(100% + 2px);
5711
6639
  object-fit: cover;
5712
6640
  border-radius: var(--kds-radius-md);
5713
6641
  }
5714
6642
 
5715
6643
  /* -- Card Title -- */
5716
6644
  .kds-card-title {
5717
- font-weight: 700;
6645
+ font-weight: var(--kds-font-weight-semibold);
5718
6646
  font-size: var(--kds-font-size-base);
5719
6647
  line-height: 1.5;
5720
6648
  letter-spacing: -0.31px;
5721
6649
  color: var(--kds-color-text-primary);
5722
- margin-bottom: var(--kds-spacing-2) !important;
6650
+ /* sin !important: no hay regla de margin-bottom para h1 en BeerCSS que competir, y así un
6651
+ consumidor puede anularlo (p.ej. FormTitle dentro de un Stack que ya da el espacio). */
6652
+ margin-bottom: var(--kds-spacing-2);
6653
+ }
6654
+
6655
+ /* Opt-in size modifiers (additive — do NOT change default 16/600) */
6656
+ .kds-card-title--lg {
6657
+ font-size: var(--kds-font-size-xl); /* 20px */
6658
+ font-weight: var(--kds-font-weight-semibold);
6659
+ }
6660
+
6661
+ .kds-card-title--xl {
6662
+ font-size: var(--kds-font-size-2xl); /* 24px */
6663
+ font-weight: var(--kds-font-weight-semibold);
5723
6664
  }
5724
6665
 
5725
6666
  /* -- Key-Value Grid -- */
5726
6667
  .kds-kv {
5727
6668
  display: grid;
5728
6669
  grid-template-columns: auto 1fr;
5729
- row-gap: var(--kds-spacing-1);
5730
- column-gap: var(--kds-spacing-1-5);
6670
+ column-gap: var(--kds-spacing-1);
5731
6671
  align-items: baseline;
5732
6672
  }
5733
6673
 
@@ -5744,7 +6684,7 @@ div.kds-invoice-header,
5744
6684
  margin: 0;
5745
6685
  font-size: var(--kds-font-size-sm);
5746
6686
  line-height: var(--kds-line-height-relaxed);
5747
- font-weight: var(--kds-font-weight-bold);
6687
+ font-weight: var(--kds-font-weight-semibold);
5748
6688
  letter-spacing: normal;
5749
6689
  color: var(--kds-color-gray-800);
5750
6690
  min-width: 0;
@@ -5772,16 +6712,28 @@ div.kds-invoice-header,
5772
6712
  .kds-detail-list {
5773
6713
  display: flex;
5774
6714
  flex-direction: column;
5775
- gap: var(--kds-spacing-1-5);
6715
+ /* spacing-2 entre grupos del detalle (separación estándar entre piezas de info
6716
+ distintas, ej. "Emisor cobro" vs "Descripción"). El padding-top da la misma
6717
+ separación spacing-2 arriba del primer grupo (Asunto↔Emisor cobro). */
6718
+ gap: var(--kds-spacing-2);
5776
6719
  margin: 0;
5777
- padding: 0 0 var(--kds-spacing-1);
6720
+ padding: var(--kds-spacing-2) 0 0;
5778
6721
  }
5779
6722
 
5780
- .kds-detail-group {
5781
- margin-bottom: var(--kds-spacing-1-5);
6723
+ @media (max-width: 767px) {
6724
+ .kds-detail-list {
6725
+ padding-bottom: var(--kds-spacing-2);
6726
+ }
5782
6727
  }
5783
6728
 
5784
- .kds-detail-group:last-child {
6729
+ .kds-detail-group {
6730
+ /* El espacio ENTRE grupos lo da el `gap` de .kds-detail-list (spacing-2).
6731
+ Dentro del grupo, label↔value llevan un gap chico (spacing-0-5) para un
6732
+ ritmo parejo: antes el dt iba pegado al dd (0px) y se veía desigual frente
6733
+ a los 16px entre los demás elementos del panel. */
6734
+ display: flex;
6735
+ flex-direction: column;
6736
+ gap: var(--kds-spacing-0-5);
5785
6737
  margin-bottom: 0;
5786
6738
  }
5787
6739
 
@@ -5790,7 +6742,7 @@ div.kds-invoice-header,
5790
6742
  letter-spacing: var(--kds-letter-spacing-wide);
5791
6743
  color: var(--kds-color-text-hint);
5792
6744
  text-transform: uppercase;
5793
- margin: 0 0 var(--kds-spacing-0-5);
6745
+ margin: 0;
5794
6746
  }
5795
6747
 
5796
6748
  .kds-detail-group dd {
@@ -5800,6 +6752,19 @@ div.kds-invoice-header,
5800
6752
  color: var(--kds-color-text-primary);
5801
6753
  }
5802
6754
 
6755
+ /* Imagen adjunta del cobro dentro del panel de detalle (ej. comprobante/producto).
6756
+ Reemplaza los inline styles del antiguo modal MerchantInfo: ancho contenido, borde
6757
+ y radio del DS. Va como un grupo más de la .kds-detail-list (hereda su gap). */
6758
+ .kds-detail-image {
6759
+ display: block;
6760
+ max-width: 100%;
6761
+ width: 160px;
6762
+ height: auto;
6763
+ object-fit: contain;
6764
+ border-radius: var(--kds-radius-md);
6765
+ border: 1px solid var(--kds-color-border);
6766
+ }
6767
+
5803
6768
  /* -- Amount Summary Row -- */
5804
6769
  .kds-amount-row {
5805
6770
  display: flex;
@@ -5859,16 +6824,16 @@ div.kds-invoice-header,
5859
6824
  font-size: var(--kds-font-size-sm);
5860
6825
  }
5861
6826
 
5862
- .kds-recap-list .k {
6827
+ .kds-recap-list .kds-key {
5863
6828
  color: var(--kds-color-text-secondary);
5864
6829
  }
5865
6830
 
5866
- .kds-recap-list .v {
6831
+ .kds-recap-list .kds-value {
5867
6832
  color: var(--kds-color-text-primary);
5868
6833
  font-weight: 500;
5869
6834
  }
5870
6835
 
5871
- .kds-recap-list .v.placeholder {
6836
+ .kds-recap-list .kds-value.placeholder {
5872
6837
  color: var(--kds-color-text-hint);
5873
6838
  }
5874
6839
 
@@ -5895,6 +6860,72 @@ a.kds-btn-success:disabled {
5895
6860
  box-shadow: none;
5896
6861
  }
5897
6862
 
6863
+ /* -- Button Warning (amber CTA) -- */
6864
+ button.kds-btn-warning,
6865
+ a.kds-btn-warning {
6866
+ background: var(--kds-color-warning-main);
6867
+ color: var(--kds-color-warning-contrast);
6868
+ border: none;
6869
+ }
6870
+
6871
+ button.kds-btn-warning:hover:not(:disabled),
6872
+ a.kds-btn-warning:hover:not(:disabled) {
6873
+ background: var(--kds-color-warning-dark);
6874
+ }
6875
+
6876
+ button.kds-btn-warning:active:not(:disabled),
6877
+ button.kds-btn-warning:focus-visible:not(:disabled),
6878
+ a.kds-btn-warning:active:not(:disabled),
6879
+ a.kds-btn-warning:focus-visible:not(:disabled) {
6880
+ background: var(--kds-color-warning-dark);
6881
+ outline: 2px solid var(--kds-color-warning-main);
6882
+ outline-offset: 2px;
6883
+ }
6884
+
6885
+ button.kds-btn-warning:disabled,
6886
+ a.kds-btn-warning:disabled {
6887
+ background-color: var(--kds-color-action-disabled-bg);
6888
+ color: var(--kds-color-action-disabled);
6889
+ box-shadow: none;
6890
+ }
6891
+
6892
+ button.kds-btn-warning::after {
6893
+ display: none;
6894
+ }
6895
+
6896
+ /* -- Button Error (red / destructive CTA) -- */
6897
+ button.kds-btn-error,
6898
+ a.kds-btn-error {
6899
+ background: var(--kds-color-error-main);
6900
+ color: var(--kds-color-error-contrast);
6901
+ border: none;
6902
+ }
6903
+
6904
+ button.kds-btn-error:hover:not(:disabled),
6905
+ a.kds-btn-error:hover:not(:disabled) {
6906
+ background: var(--kds-color-error-dark);
6907
+ }
6908
+
6909
+ button.kds-btn-error:active:not(:disabled),
6910
+ button.kds-btn-error:focus-visible:not(:disabled),
6911
+ a.kds-btn-error:active:not(:disabled),
6912
+ a.kds-btn-error:focus-visible:not(:disabled) {
6913
+ background: var(--kds-color-error-dark);
6914
+ outline: 2px solid var(--kds-color-error-main);
6915
+ outline-offset: 2px;
6916
+ }
6917
+
6918
+ button.kds-btn-error:disabled,
6919
+ a.kds-btn-error:disabled {
6920
+ background-color: var(--kds-color-action-disabled-bg);
6921
+ color: var(--kds-color-action-disabled);
6922
+ box-shadow: none;
6923
+ }
6924
+
6925
+ button.kds-btn-error::after {
6926
+ display: none;
6927
+ }
6928
+
5898
6929
  /* -- Field Helper Text -- */
5899
6930
  .kds-field-helper {
5900
6931
  font-size: var(--kds-font-size-xs);
@@ -5937,6 +6968,8 @@ button.kds-btn-success::after {
5937
6968
  /* -- Alert inline (borderless, used inside cards) -- */
5938
6969
  .kds-alert-inline {
5939
6970
  border: none;
6971
+ padding: var(--kds-spacing-1) var(--kds-spacing-1-5);
6972
+ margin-bottom: var(--kds-spacing-1-5);
5940
6973
  }
5941
6974
 
5942
6975
  /* -- Card dimmed (behind overlays) -- */
@@ -6038,17 +7071,42 @@ button.kds-btn-success::after {
6038
7071
  }
6039
7072
 
6040
7073
  .kds-bill-description-picture {
6041
- width: 120px;
6042
- height: 120px;
7074
+ max-width: 100px;
6043
7075
  object-fit: contain;
6044
7076
  background: var(--kds-color-background-paper);
6045
- padding: var(--kds-spacing-1-25);
6046
- border-radius: var(--kds-radius-md);
6047
- margin-top: var(--kds-spacing-1);
7077
+ margin-top: var(--kds-spacing-1-25);
6048
7078
  display: block;
6049
7079
  box-sizing: content-box;
6050
7080
  }
6051
7081
 
7082
+ /* -- Bill Attachments -- */
7083
+ .kds-bill-attachments {
7084
+ display: flex;
7085
+ flex-direction: column;
7086
+ gap: var(--kds-spacing-0-75);
7087
+ }
7088
+
7089
+ .kds-bill-attachment {
7090
+ display: inline-flex;
7091
+ align-items: center;
7092
+ justify-content: flex-start;
7093
+ width: fit-content;
7094
+ color: var(--kds-color-info-main);
7095
+ font-size: var(--kds-font-size-sm);
7096
+ font-weight: var(--kds-font-weight-medium);
7097
+ text-decoration: none;
7098
+ }
7099
+
7100
+ .kds-bill-attachment:hover span {
7101
+ text-decoration: underline;
7102
+ }
7103
+
7104
+ .kds-bill-attachment i {
7105
+ font-size: var(--kds-font-size-lg);
7106
+ min-width: unset;
7107
+ justify-content: flex-start;
7108
+ }
7109
+
6052
7110
  /* -- Share Card (mustContinue) -- */
6053
7111
  .kds-share-card {
6054
7112
  margin-top: var(--kds-spacing-2);
@@ -6174,12 +7232,43 @@ button.kds-btn-success::after {
6174
7232
  padding-bottom: 0;
6175
7233
  }
6176
7234
 
6177
- /* Payment stage full-viewport centered wrapper */
7235
+ /* Body card: alto mínimo estable para que NO salte entre estados de distinta altura
7236
+ (loader/progress ↔ formulario). Solo la body card = hijo directo de `.kds-screen`
7237
+ (los headers van dentro de `.kds-invoice-sticky-wrap`, no la afecta).
7238
+ Flex-column para que el contenido quede arriba y el footer interno (versión / secure
7239
+ footer) pueda pinearse al fondo con `margin-top: auto` aunque el contenido sea corto. */
7240
+ .kds-screen > .kds-card-elevated {
7241
+ min-height: 36rem;
7242
+ display: flex;
7243
+ flex-direction: column;
7244
+ /* mismo radio que el header (invoice-sticky usa radius-card); el base card-elevated
7245
+ es radius-md (8px) y quedaba distinto al header en desktop. */
7246
+ border-radius: var(--kds-radius-card);
7247
+ }
7248
+
7249
+ /* Variante sin radio superior: pantallas de resultado (éxito/fallo/timeout) que van
7250
+ SIN header arriba — la body card va sola y su tope queda plano (flush) en vez de
7251
+ con esquinas redondeadas sueltas. El padding del card sigue manejando el inset. */
7252
+ /* Tope plano SOLO en mobile (< 768px): la body card se pega al borde superior del widget
7253
+ (igual que el header sticky, que también aplana el tope solo en mobile). En desktop la card
7254
+ va centrada y separada, así que conserva el border-radius superior redondeado. */
7255
+ @media (max-width: 767px) {
7256
+ .kds-screen > .kds-card-elevated.kds-card-elevated--flush-top,
7257
+ .kds-card-elevated.kds-card-elevated--flush-top {
7258
+ border-top-left-radius: 0;
7259
+ border-top-right-radius: 0;
7260
+ }
7261
+ }
7262
+
7263
+ /* Payment stage — full-viewport centered wrapper.
7264
+ Fondo muted (gris) para que las cards blancas (header + body) y el gap entre
7265
+ ellas se distingan; sin esto, card blanca sobre fondo blanco se funden. */
6178
7266
  .kds-payment-stage {
6179
7267
  min-height: 100vh;
6180
7268
  display: flex;
6181
7269
  justify-content: center;
6182
7270
  padding: var(--kds-spacing-2-5) var(--kds-spacing-2) var(--kds-spacing-8);
7271
+ background: var(--kds-color-background-muted);
6183
7272
  }
6184
7273
 
6185
7274
  /* Payment flow — narrow column */
@@ -6218,10 +7307,16 @@ button.kds-btn-success::after {
6218
7307
  padding-bottom: var(--kds-spacing-2);
6219
7308
  }
6220
7309
 
6221
- /* Invoice sticky card: progressive padding-top (expanded 20px → collapsed 8px).
6222
- Overrides base rule at same specificity; source order wins here. */
7310
+ /* Invoice sticky card padding (mobile).
7311
+ Top: estático 12px. Bottom: progresivo 16px (expandido, = desktop) → 8px (colapsado).
7312
+ El padding-bottom animado se reduce al colapsar — sin depender de que el clip-path
7313
+ corte el padding abruptamente. */
6223
7314
  .kds-payment-flow .kds-card-elevated.kds-invoice-sticky {
6224
7315
  padding-top: var(--kds-spacing-1-5);
7316
+ padding-bottom: calc(
7317
+ var(--kds-spacing-2)
7318
+ - var(--kds-spacing-1) * var(--collapse-progress, 0)
7319
+ );
6225
7320
  }
6226
7321
 
6227
7322
 
@@ -6480,7 +7575,7 @@ button.kds-btn-success::after {
6480
7575
  inset: 0;
6481
7576
  background: var(--kds-modal-backdrop);
6482
7577
  display: none;
6483
- align-items: center;
7578
+ align-items: flex-start;
6484
7579
  justify-content: center;
6485
7580
  z-index: var(--kds-z-index-modal, 50);
6486
7581
  animation: kds-fade 0.2s ease both;
@@ -6508,7 +7603,7 @@ button.kds-btn-success::after {
6508
7603
  display: flex;
6509
7604
  align-items: center;
6510
7605
  justify-content: space-between;
6511
- padding: var(--kds-spacing-2) var(--kds-spacing-2) var(--kds-spacing-1);
7606
+ padding: var(--kds-spacing-1) var(--kds-spacing-2) var(--kds-spacing-1);
6512
7607
  }
6513
7608
 
6514
7609
  .kds-bank-modal-header h3 {
@@ -6533,11 +7628,8 @@ button.kds-btn-success::after {
6533
7628
  display: none;
6534
7629
  }
6535
7630
 
6536
- .kds-bank-modal-search {
6537
- padding: 0 var(--kds-spacing-2) var(--kds-spacing-1-5);
6538
- }
6539
-
6540
- .kds-bank-modal-search input {
7631
+ /* Search field reutilizable (standalone o dentro del KdsBankModal). */
7632
+ .kds-search-field {
6541
7633
  width: 100%;
6542
7634
  padding: var(--kds-spacing-1-25) var(--kds-spacing-1-5);
6543
7635
  border: 1px solid var(--kds-color-divider);
@@ -6549,15 +7641,57 @@ button.kds-btn-success::after {
6549
7641
  background: var(--kds-color-surface);
6550
7642
  }
6551
7643
 
6552
- .kds-bank-modal-search input::placeholder {
7644
+ .kds-search-field::placeholder {
6553
7645
  color: var(--kds-color-text-hint);
6554
7646
  }
6555
7647
 
6556
- .kds-bank-modal-search input:focus {
7648
+ .kds-search-field:focus {
6557
7649
  border-color: var(--kds-color-primary-main);
6558
7650
  box-shadow: 0 0 0 2px var(--kds-color-primary-faint);
6559
7651
  }
6560
7652
 
7653
+ /* Variante con ícono (withIcon): lupa posicionada DENTRO del input, a la derecha. */
7654
+ .kds-search-field-wrapper {
7655
+ position: relative;
7656
+ width: 100%;
7657
+ }
7658
+
7659
+ .kds-search-field-icon {
7660
+ position: absolute;
7661
+ right: var(--kds-spacing-1-5);
7662
+ top: 50%;
7663
+ transform: translateY(-50%);
7664
+ font-size: 20px;
7665
+ color: var(--kds-color-text-hint);
7666
+ pointer-events: none;
7667
+ }
7668
+
7669
+ /* Espacio a la derecha del texto para no solaparse con la lupa. */
7670
+ .kds-search-field-wrapper .kds-search-field {
7671
+ padding-right: calc(var(--kds-spacing-1-5) + var(--kds-spacing-3));
7672
+ }
7673
+
7674
+ /* Variante angosta del field (prop `narrow` de KdsTextField): ancho reducido y texto
7675
+ centrado, para inputs de pocos caracteres dispuestos en fila (ej. segmentos de
7676
+ coordenadas o código). No crece ni se encoge dentro de un flex row. */
7677
+ .field.kds-field--narrow {
7678
+ width: 6rem;
7679
+ flex: 0 0 auto;
7680
+ /* En fila horizontal no aplica el margin vertical que BeerCSS da a fields
7681
+ hermanos (regla de "flow" `... + :is(.field,...)`, specificity ~0,3,1);
7682
+ sin el reset los siguientes quedan desalineados hacia abajo. `!important`
7683
+ para ganarle a esa regla de framework. */
7684
+ margin-block: 0 !important;
7685
+ }
7686
+
7687
+ .field.kds-field--narrow > input {
7688
+ text-align: center;
7689
+ }
7690
+
7691
+ .kds-bank-modal-search {
7692
+ padding: 0 var(--kds-spacing-2) var(--kds-spacing-1-5);
7693
+ }
7694
+
6561
7695
  .kds-bank-modal-body {
6562
7696
  flex: 1;
6563
7697
  overflow-y: auto;
@@ -6655,7 +7789,7 @@ button.kds-btn-success::after {
6655
7789
  min-width: 0;
6656
7790
  }
6657
7791
 
6658
- .kds-qr-text .title {
7792
+ .kds-qr-text .kds-qr-title {
6659
7793
  font-family: var(--kds-font-family-primary);
6660
7794
  font-weight: 600;
6661
7795
  font-size: var(--kds-font-size-sm);
@@ -6663,10 +7797,15 @@ button.kds-btn-success::after {
6663
7797
  color: var(--kds-color-text-primary);
6664
7798
  }
6665
7799
 
6666
- .kds-qr-text .sub {
7800
+ .kds-qr-text .kds-qr-subtitle {
6667
7801
  font-family: var(--kds-font-family-primary);
6668
7802
  font-size: var(--kds-font-size-caption);
6669
7803
  color: var(--kds-color-text-secondary);
7804
+ /* Trunca con ellipsis en lugar de wrap a 2 líneas — evita apretar el row
7805
+ cuando el contenedor es angosto (mobile o decorator constrained). */
7806
+ overflow: hidden;
7807
+ text-overflow: ellipsis;
7808
+ white-space: nowrap;
6670
7809
  }
6671
7810
 
6672
7811
  /* QR Badge - "Rápido" indicator */
@@ -6689,46 +7828,26 @@ button.kds-btn-success::after {
6689
7828
  font-size: 20px;
6690
7829
  }
6691
7830
 
6692
- /* Mobile responsive: Ajustes para pantallas pequeñas */
7831
+ /* Mobile responsive (≤480px): respira más mantenemos padding/gap/avatar igual
7832
+ que desktop y solo agregamos truncation al subtitle y `flex-shrink: 0` en los
7833
+ ítems que deben mantener su tamaño (avatar, badge, chevron). */
6693
7834
  @media (max-width: 480px) {
6694
- .kds-qr-row {
6695
- gap: var(--kds-spacing-1-5);
6696
- padding: var(--kds-spacing-1-5);
6697
- }
6698
-
6699
7835
  .kds-qr-avatar {
6700
- width: var(--kds-spacing-4-5);
6701
- height: var(--kds-spacing-4-5);
6702
7836
  flex-shrink: 0;
6703
7837
  }
6704
7838
 
6705
- .kds-qr-avatar .material-symbols-outlined,
6706
- .kds-qr-avatar .material-icons-round {
6707
- font-size: var(--kds-font-size-lg);
6708
- }
6709
-
6710
- .kds-qr-text .title {
6711
- font-size: var(--kds-font-size-xs);
6712
- line-height: var(--kds-line-height-tight);
6713
- }
6714
-
6715
7839
  .kds-qr-text .sub {
6716
- font-size: var(--kds-font-size-caption);
6717
- line-height: var(--kds-line-height-tight);
6718
7840
  overflow: hidden;
6719
7841
  text-overflow: ellipsis;
6720
7842
  white-space: nowrap;
6721
7843
  }
6722
7844
 
6723
7845
  .kds-qr-badge {
6724
- font-size: var(--kds-font-size-caption);
6725
- padding: var(--kds-spacing-0-25) var(--kds-spacing-0-75);
6726
7846
  flex-shrink: 0;
6727
7847
  }
6728
7848
 
6729
7849
  .kds-qr-row .material-symbols-outlined:last-child,
6730
7850
  .kds-qr-row .material-icons-round:last-child {
6731
- font-size: var(--kds-font-size-base);
6732
7851
  flex-shrink: 0;
6733
7852
  }
6734
7853
  }
@@ -6757,7 +7876,11 @@ button.kds-btn-success::after {
6757
7876
  border-radius: var(--kds-spacing-0-75);
6758
7877
  font-size: var(--kds-font-size-sm);
6759
7878
  cursor: pointer;
6760
- transition: background 0.12s ease;
7879
+ /* Transición más amplia y suave: bg + color + border ease-out */
7880
+ transition:
7881
+ background-color 280ms cubic-bezier(0.2, 0.8, 0.2, 1),
7882
+ color 280ms cubic-bezier(0.2, 0.8, 0.2, 1),
7883
+ border-top-color 280ms ease-out;
6761
7884
  position: relative;
6762
7885
  user-select: none;
6763
7886
  }
@@ -6766,19 +7889,21 @@ button.kds-btn-success::after {
6766
7889
  border-top: 0;
6767
7890
  }
6768
7891
 
6769
- .kds-copyable-table-row:hover {
7892
+ /* Hover NO aplica mientras la row esté en `.copied` o `.settling`
7893
+ (evita el flicker del hover state al desaparecer el verde de "copiado") */
7894
+ .kds-copyable-table-row:not(.copied):not(.settling):hover {
6770
7895
  background: var(--kds-color-primary-faint);
6771
7896
  }
6772
7897
 
6773
- .kds-copyable-table-row:hover + .kds-copyable-table-row {
7898
+ .kds-copyable-table-row:not(.copied):not(.settling):hover + .kds-copyable-table-row {
6774
7899
  border-top-color: transparent;
6775
7900
  }
6776
7901
 
6777
- .kds-copyable-table-row:active {
7902
+ .kds-copyable-table-row:not(.copied):not(.settling):active {
6778
7903
  background: var(--kds-color-primary-selected);
6779
7904
  }
6780
7905
 
6781
- .kds-copyable-table-row .k {
7906
+ .kds-copyable-table-row .kds-key {
6782
7907
  color: var(--kds-color-text-secondary);
6783
7908
  font-size: var(--kds-font-size-caption);
6784
7909
  letter-spacing: 0.3px;
@@ -6787,7 +7912,7 @@ button.kds-btn-success::after {
6787
7912
  flex: 0 0 auto;
6788
7913
  }
6789
7914
 
6790
- .kds-copyable-table-row .v {
7915
+ .kds-copyable-table-row .kds-value {
6791
7916
  color: var(--kds-color-text-primary);
6792
7917
  font-family: 'JetBrains Mono', 'SF Mono', ui-monospace, Menlo, Consolas, monospace;
6793
7918
  font-weight: var(--kds-font-weight-medium);
@@ -6803,17 +7928,18 @@ button.kds-btn-success::after {
6803
7928
  flex: 1 1 auto;
6804
7929
  }
6805
7930
 
6806
- .kds-copyable-table-row .v::after {
7931
+ .kds-copyable-table-row .kds-value::after {
6807
7932
  content: 'content_copy';
6808
7933
  font-family: 'Material Symbols Outlined';
6809
7934
  font-size: var(--kds-font-size-sm);
6810
7935
  color: var(--kds-color-primary-main);
6811
7936
  opacity: 0;
6812
- transition: opacity 0.12s ease;
7937
+ /* Transición coordinada con la row */
7938
+ transition: opacity 280ms cubic-bezier(0.2, 0.8, 0.2, 1);
6813
7939
  flex: 0 0 auto;
6814
7940
  }
6815
7941
 
6816
- .kds-copyable-table-row:hover .v::after {
7942
+ .kds-copyable-table-row:not(.copied):not(.settling):hover .kds-value::after {
6817
7943
  opacity: 0.7;
6818
7944
  }
6819
7945
 
@@ -6821,22 +7947,76 @@ button.kds-btn-success::after {
6821
7947
  background: var(--kds-color-success-soft);
6822
7948
  }
6823
7949
 
6824
- .kds-copyable-table-row.copied .v {
7950
+ .kds-copyable-table-row.copied .kds-value {
6825
7951
  color: var(--kds-color-success-dark);
6826
7952
  }
6827
7953
 
6828
- .kds-copyable-table-row.copied .v::after {
7954
+ .kds-copyable-table-row.copied .kds-value::after {
6829
7955
  content: 'check';
6830
7956
  font-family: 'Material Symbols Outlined';
6831
7957
  color: var(--kds-color-success-dark);
6832
7958
  opacity: 1;
6833
7959
  }
6834
7960
 
7961
+ /* `.settling`: estado de "recién copiado" — la row vuelve al normal sin
7962
+ activar hover. Dura lo mismo que la transición (~300ms) y se aplica
7963
+ desde React tras quitar `.copied`. */
7964
+ .kds-copyable-table-row.settling .kds-value::after {
7965
+ /* Mantén el check con opacity 0 mientras settling, fade out suave */
7966
+ opacity: 0;
7967
+ }
7968
+
6835
7969
  /* Disable BeerCSS ripple on copyable table rows */
6836
7970
  .kds-copyable-table-row::after {
6837
7971
  display: none;
6838
7972
  }
6839
7973
 
7974
+ /* ========================================
7975
+ GRID VARIANT (.kds-copyable-table--grid)
7976
+ Read-only grid of N text cells per row. No copy,
7977
+ no role=button, no "copy all". Cells share the row
7978
+ width equally. Used inside radio options, etc.
7979
+ ======================================== */
7980
+
7981
+ /* La grilla es solo columnas de texto (como el DataTable original): sin caja,
7982
+ borde, fondo ni sombra del contenedor copyable. */
7983
+ .kds-copyable-table--grid {
7984
+ border: 0;
7985
+ border-radius: 0;
7986
+ background: none;
7987
+ padding: 0;
7988
+ box-shadow: none;
7989
+ }
7990
+
7991
+ .kds-copyable-table-row--grid {
7992
+ display: flex;
7993
+ align-items: center;
7994
+ gap: var(--kds-spacing-1-5);
7995
+ padding: 0;
7996
+ /* Sin el divisor border-top que trae la fila base del copyable. */
7997
+ border-top: 0;
7998
+ cursor: default;
7999
+ user-select: text;
8000
+ }
8001
+
8002
+ /* Separación entre filas de la grilla (sin divisores). */
8003
+ .kds-copyable-table-row--grid + .kds-copyable-table-row--grid {
8004
+ margin-top: var(--kds-spacing-0-5);
8005
+ }
8006
+
8007
+ .kds-grid-cell {
8008
+ flex: 1 1 0;
8009
+ min-width: 0;
8010
+ font-size: var(--kds-font-size-sm);
8011
+ font-weight: var(--kds-font-weight-medium);
8012
+ color: var(--kds-color-text-primary);
8013
+ word-break: break-word;
8014
+ }
8015
+
8016
+ .kds-grid-cell--disabled {
8017
+ color: var(--kds-color-text-disabled);
8018
+ }
8019
+
6840
8020
  /* ========================================
6841
8021
  COPY-ALL BUTTON (.kds-copy-all-btn)
6842
8022
  Modifier for kds-btn kds-btn-outlined that adds
@@ -6894,6 +8074,14 @@ a.kds-copy-all-btn.copied:hover {
6894
8074
  min-height: unset;
6895
8075
  }
6896
8076
 
8077
+ /* Severity variants — default stays warning (backward-compatible) */
8078
+ .kds-section-note.kds-info { color: var(--kds-color-info-dark); }
8079
+ .kds-section-note.kds-info > i { color: var(--kds-color-info-dark); }
8080
+ .kds-section-note.kds-success { color: var(--kds-color-success-dark); }
8081
+ .kds-section-note.kds-success > i { color: var(--kds-color-success-dark); }
8082
+ .kds-section-note.kds-error { color: var(--kds-color-error-dark); }
8083
+ .kds-section-note.kds-error > i { color: var(--kds-color-error-dark); }
8084
+
6897
8085
  /* ========================================
6898
8086
  INFO TOOLTIP (.kds-info-tip)
6899
8087
  Inline info button with hover/click tooltip bubble.
@@ -6989,7 +8177,7 @@ a.kds-copy-all-btn.copied:hover {
6989
8177
  .kds-monto-row {
6990
8178
  display: flex;
6991
8179
  justify-content: space-between;
6992
- align-items: flex-end;
8180
+ align-items: flex-start;
6993
8181
  padding: var(--kds-spacing-1-75) 0 var(--kds-spacing-1);
6994
8182
  border-top: 1px dashed var(--kds-color-divider);
6995
8183
  margin-top: var(--kds-spacing-1-75);
@@ -7013,3 +8201,191 @@ a.kds-copy-all-btn.copied:hover {
7013
8201
  color: var(--kds-color-text-primary);
7014
8202
  }
7015
8203
 
8204
+ /* ========================================
8205
+ PAYMENT TOTAL (.kds-payment-total)
8206
+ Bloque "monto a pagar" para QR view y email templates.
8207
+ Portado desde paylink-ligopay → <kh:paymentTotal> + materialize-config.css.
8208
+ Variantes:
8209
+ - default: amount grande (3rem) en color primario Khipu.
8210
+ - email: amount compacto (1.5rem) en color texto primario.
8211
+ Responsive (default): mobile (≤ 1024px) centra y oculta titleMobile.
8212
+ ======================================== */
8213
+
8214
+ .kds-payment-total {
8215
+ /* Component-scoped tokens — overrideables sin tocar el CSS global. */
8216
+ --kds-payment-total-title-size: var(--kds-font-size-2xl); /* 24px */
8217
+ --kds-payment-total-label-size: var(--kds-font-size-xl); /* 20px */
8218
+ --kds-payment-amount-size: 3rem; /* 48px — destacado */
8219
+ --kds-payment-amount-line-height: var(--kds-line-height-snug);
8220
+ --kds-payment-amount-color: var(--kds-color-primary-main);
8221
+ --kds-payment-amount-weight: var(--kds-font-weight-medium);
8222
+
8223
+ text-align: left;
8224
+ }
8225
+
8226
+ .kds-payment-total-title,
8227
+ .kds-payment-total-title-mobile {
8228
+ font-size: var(--kds-payment-total-title-size);
8229
+ font-weight: var(--kds-font-weight-semibold);
8230
+ color: var(--kds-color-text-primary);
8231
+ margin-bottom: var(--kds-spacing-4);
8232
+ }
8233
+
8234
+ .kds-payment-total-title-mobile {
8235
+ display: none;
8236
+ }
8237
+
8238
+ .kds-payment-total .kds-payment-label {
8239
+ font-size: var(--kds-payment-total-label-size);
8240
+ font-weight: var(--kds-font-weight-semibold);
8241
+ color: var(--kds-color-text-primary);
8242
+ margin-bottom: var(--kds-spacing-1);
8243
+ padding-top: 0;
8244
+ }
8245
+
8246
+ .kds-payment-total .kds-payment-amount {
8247
+ font-size: var(--kds-payment-amount-size);
8248
+ font-weight: var(--kds-payment-amount-weight);
8249
+ line-height: var(--kds-payment-amount-line-height);
8250
+ color: var(--kds-payment-amount-color);
8251
+ margin-bottom: var(--kds-spacing-4);
8252
+ }
8253
+
8254
+ .kds-payment-total .kds-payment-amount .kds-payment-total-decimal-sup,
8255
+ .kds-invoice-amount .kds-payment-total-decimal-sup {
8256
+ font-size: var(--kds-font-size-decimal-sup);
8257
+ font-weight: var(--kds-font-weight-semibold);
8258
+ position: relative;
8259
+ top: 0;
8260
+ }
8261
+
8262
+ /* Variante email: amount compacto en color texto primario, alineación izquierda fija */
8263
+ .kds-payment-total--email {
8264
+ --kds-payment-amount-size: var(--kds-font-size-2xl); /* 24px */
8265
+ --kds-payment-amount-line-height: var(--kds-line-height-snug);
8266
+ --kds-payment-amount-color: var(--kds-color-text-primary);
8267
+ --kds-payment-amount-weight: var(--kds-font-weight-semibold);
8268
+ }
8269
+
8270
+ /* Tone modifier: swaps the amount color to the khipu-blue / LigoPay info blue.
8271
+ Combinable with --email or with the default variant. */
8272
+ .kds-payment-total--tone-info {
8273
+ --kds-payment-amount-color: var(--kds-color-info-blue); /* #5A5FE0 */
8274
+ }
8275
+
8276
+ /* Centered modifier: removes the asymmetric desktop padding (which exists for
8277
+ LigoPay QR view) and centers all internal text. Composable with --email and
8278
+ --tone-info. */
8279
+ .kds-payment-total--centered {
8280
+ padding-left: 0;
8281
+ padding-top: 0;
8282
+ text-align: center;
8283
+ }
8284
+ .kds-payment-total--centered .kds-payment-total-title,
8285
+ .kds-payment-total--centered .kds-payment-total-title-mobile,
8286
+ .kds-payment-total--centered .kds-payment-label,
8287
+ .kds-payment-total--centered .kds-payment-amount {
8288
+ text-align: center;
8289
+ }
8290
+
8291
+ /* Responsive: mobile ≤ 1024px — centra y conmuta los títulos (sólo variante default) */
8292
+ @media (max-width: 1024px) {
8293
+ .kds-payment-total {
8294
+ text-align: center;
8295
+ }
8296
+
8297
+ .kds-payment-total .kds-payment-total-title {
8298
+ display: none;
8299
+ }
8300
+
8301
+ .kds-payment-total .kds-payment-total-title-mobile {
8302
+ display: block;
8303
+ text-align: center;
8304
+ }
8305
+
8306
+ .kds-payment-total .kds-payment-label,
8307
+ .kds-payment-total .kds-payment-amount {
8308
+ text-align: center;
8309
+ }
8310
+
8311
+ /* Email mantiene alineación izquierda incluso en mobile (matchea email clients) */
8312
+ .kds-payment-total--email,
8313
+ .kds-payment-total--email .kds-payment-label,
8314
+ .kds-payment-total--email .kds-payment-amount {
8315
+ text-align: left;
8316
+ }
8317
+ }
8318
+
8319
+ /* ==========================================================================
8320
+ GRID UTILITIES — semantic aliases for BeerCSS .grid / .s* / .m* / .l*
8321
+
8322
+ Estas utilidades son ALIAS de las clases cortas de BeerCSS para que
8323
+ consumers (payment, LigoPay, Grails legacy) puedan escribir markup con
8324
+ nombres descriptivos sin renunciar al grid de 12 columnas de BeerCSS.
8325
+
8326
+ Convención (estilo Bootstrap responsive):
8327
+ - kds-grid → .grid (12-col CSS grid + 1rem gap)
8328
+ - kds-col-sm-{1..12} → .s{1..12} (mobile, siempre aplica)
8329
+ - kds-col-md-{1..12} → .m{1..12} (>=601px)
8330
+ - kds-col-lg-{1..12} → .l{1..12} (>=993px)
8331
+
8332
+ Las definiciones originales de BeerCSS NO se tocan; estas son aditivas.
8333
+ ========================================================================== */
8334
+
8335
+ .kds-grid {
8336
+ --_gap: 1rem;
8337
+ display: grid;
8338
+ grid-template-columns: repeat(12, 1fr);
8339
+ gap: var(--_gap);
8340
+ block-size: auto;
8341
+ }
8342
+
8343
+ .kds-grid.kds-grid-no-space { --_gap: 0rem; }
8344
+ .kds-grid.kds-grid-medium-space { --_gap: 1.5rem; }
8345
+ .kds-grid.kds-grid-large-space { --_gap: 2rem; }
8346
+
8347
+ .kds-grid > * { margin: 0; }
8348
+
8349
+ .kds-col-sm-1 { grid-area: auto/span 1; }
8350
+ .kds-col-sm-2 { grid-area: auto/span 2; }
8351
+ .kds-col-sm-3 { grid-area: auto/span 3; }
8352
+ .kds-col-sm-4 { grid-area: auto/span 4; }
8353
+ .kds-col-sm-5 { grid-area: auto/span 5; }
8354
+ .kds-col-sm-6 { grid-area: auto/span 6; }
8355
+ .kds-col-sm-7 { grid-area: auto/span 7; }
8356
+ .kds-col-sm-8 { grid-area: auto/span 8; }
8357
+ .kds-col-sm-9 { grid-area: auto/span 9; }
8358
+ .kds-col-sm-10 { grid-area: auto/span 10; }
8359
+ .kds-col-sm-11 { grid-area: auto/span 11; }
8360
+ .kds-col-sm-12 { grid-area: auto/span 12; }
8361
+
8362
+ @media only screen and (min-width: 601px) {
8363
+ .kds-col-md-1 { grid-area: auto/span 1; }
8364
+ .kds-col-md-2 { grid-area: auto/span 2; }
8365
+ .kds-col-md-3 { grid-area: auto/span 3; }
8366
+ .kds-col-md-4 { grid-area: auto/span 4; }
8367
+ .kds-col-md-5 { grid-area: auto/span 5; }
8368
+ .kds-col-md-6 { grid-area: auto/span 6; }
8369
+ .kds-col-md-7 { grid-area: auto/span 7; }
8370
+ .kds-col-md-8 { grid-area: auto/span 8; }
8371
+ .kds-col-md-9 { grid-area: auto/span 9; }
8372
+ .kds-col-md-10 { grid-area: auto/span 10; }
8373
+ .kds-col-md-11 { grid-area: auto/span 11; }
8374
+ .kds-col-md-12 { grid-area: auto/span 12; }
8375
+ }
8376
+
8377
+ @media only screen and (min-width: 993px) {
8378
+ .kds-col-lg-1 { grid-area: auto/span 1; }
8379
+ .kds-col-lg-2 { grid-area: auto/span 2; }
8380
+ .kds-col-lg-3 { grid-area: auto/span 3; }
8381
+ .kds-col-lg-4 { grid-area: auto/span 4; }
8382
+ .kds-col-lg-5 { grid-area: auto/span 5; }
8383
+ .kds-col-lg-6 { grid-area: auto/span 6; }
8384
+ .kds-col-lg-7 { grid-area: auto/span 7; }
8385
+ .kds-col-lg-8 { grid-area: auto/span 8; }
8386
+ .kds-col-lg-9 { grid-area: auto/span 9; }
8387
+ .kds-col-lg-10 { grid-area: auto/span 10; }
8388
+ .kds-col-lg-11 { grid-area: auto/span 11; }
8389
+ .kds-col-lg-12 { grid-area: auto/span 12; }
8390
+ }
8391
+