@khipu/design-system 0.3.5-alpha.3 → 0.3.5-alpha.31

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.
@@ -13,7 +13,7 @@
13
13
  *
14
14
  * AUTO-GENERATED FILE - DO NOT EDIT MANUALLY
15
15
  * Source: design-system/src/tokens/tokens.json
16
- * Generated: 2026-08-13T16:19:44.738Z
16
+ * Generated: 2026-10-07T15:44:56.536Z
17
17
  *
18
18
  * To regenerate:
19
19
  * cd design-system && npm run tokens:generate
@@ -397,13 +397,13 @@
397
397
  /* ============================================================================
398
398
  DARK MODE COLOR TOKENS
399
399
  Override only color variables; everything else inherits from :root.
400
- Activated via [data-theme="dark"] on <html>, <body>, or a wrapper —
401
- or via the BeerCSS-style body.dark class. Both selectors are emitted so
402
- a body.dark app gets the FULL dark theme (core + extended tokens); a
403
- single source flipping only partially is how mixed light/dark bugs arise.
400
+ Activated ONLY via [data-theme="dark"] on <html>, <body>, or a wrapper.
401
+ body.dark is deliberately NOT a trigger: BeerCSS adds that class on its
402
+ own when the OS reports a dark preference, so honouring it turns dark on
403
+ for consumers that never opted in. Dark stays opt-in until an app sets
404
+ data-theme explicitly.
404
405
  ============================================================================ */
405
406
 
406
- body.dark,
407
407
  [data-theme="dark"] {
408
408
 
409
409
  /* Primary palette - Khipu brand */
@@ -879,11 +879,10 @@ body.dark,
879
879
  values so selected states / rows / hovers adapt. Additive: the light
880
880
  :root / body.light values above are unchanged. Tokens already defined as
881
881
  var() refs (warning-soft, danger-soft) adapt on their own and are omitted.
882
- body.dark is included on purpose: the light group above also matches
883
- body.dark (specificity 0,1,1), so a bare [data-theme] override (0,1,0)
884
- would lose on a body that carries the transitional dark class.
882
+ body.dark is deliberately NOT a trigger: BeerCSS adds that class on its own
883
+ when the OS reports a dark preference, so honouring it turns dark on for
884
+ consumers that never opted in. Dark stays opt-in via data-theme.
885
885
  ======================================== */
886
- body.dark,
887
886
  [data-theme="dark"] {
888
887
  --kds-color-primary-faint: #1E1030; /* dark brand-tinted subtle surface (selected rows/cards) */
889
888
  --kds-color-info-soft: var(--kds-alert-info-bg); /* dark translucent blue */
@@ -917,7 +916,7 @@ body.dark,
917
916
  opt-out for Chrome/WebView "Auto Dark Theme" on Android: without it, an
918
917
  OS-dark device heuristically darkens SOME elements of a light page (mixed
919
918
  light/dark rendering in production apps). Dark is reached only by an
920
- explicit [data-theme="dark"] / body.dark opt-in. */
919
+ explicit [data-theme="dark"] opt-in. */
921
920
  :root,
922
921
  html,
923
922
  body {
@@ -936,8 +935,7 @@ body {
936
935
  }
937
936
 
938
937
  /* Explicit dark theme: let native UI (form controls, scrollbars) render dark. */
939
- [data-theme="dark"],
940
- body.dark {
938
+ [data-theme="dark"] {
941
939
  color-scheme: dark;
942
940
  }
943
941
 
@@ -999,6 +997,20 @@ footer {
999
997
  background-color: transparent !important;
1000
998
  }
1001
999
 
1000
+ /* Familia base del arbol del DS.
1001
+ BeerCSS la pone en `body { font-family: var(--font) }`, y `--font` es un alias
1002
+ declarado en :root. Una custom property se sustituye en el elemento que la declara,
1003
+ asi que ese alias se congela con la familia de :root: una marca que define
1004
+ --kds-font-family-primary mas abajo en el arbol no lo mueve, y el texto que solo
1005
+ hereda de body se queda con la familia por defecto.
1006
+
1007
+ Resolviendo el token aca, en la raiz del arbol del DS, hereda todo lo de adentro con
1008
+ la familia vigente en ese punto. Sin marca definida el valor es el mismo de siempre,
1009
+ asi que no cambia nada para quien no la configura. */
1010
+ .kds-theme-root {
1011
+ font-family: var(--kds-font-family-primary);
1012
+ }
1013
+
1002
1014
  /* ========================================
1003
1015
  TEXT UTILITIES OVERRIDE
1004
1016
  Fix BeerCSS classes that use wrong color tokens
@@ -1210,6 +1222,29 @@ a.kds-btn-outlined-white:hover:not(:disabled) {
1210
1222
  opacity: 0.9;
1211
1223
  }
1212
1224
 
1225
+ /* Outlined success — compone con .kds-btn-outlined, igual que .kds-btn-outlined-white:
1226
+ solo cambia superficie y color, la caja la aporta la variante base. */
1227
+ button.kds-btn-outlined-success,
1228
+ a.kds-btn-outlined-success {
1229
+ background: transparent;
1230
+ color: var(--kds-color-success-main);
1231
+ border-color: var(--kds-color-success-main);
1232
+ }
1233
+
1234
+ button.kds-btn-outlined-success:hover:not(:disabled),
1235
+ a.kds-btn-outlined-success:hover:not(:disabled) {
1236
+ background: var(--kds-color-success-soft);
1237
+ border-color: var(--kds-color-success-dark);
1238
+ color: var(--kds-color-success-dark);
1239
+ }
1240
+
1241
+ button.kds-btn-outlined-success:disabled,
1242
+ a.kds-btn-outlined-success:disabled {
1243
+ background: transparent;
1244
+ color: var(--kds-color-action-disabled);
1245
+ border-color: var(--kds-color-action-disabled);
1246
+ }
1247
+
1213
1248
  /* Text button — mismo tamaño que el resto de variantes (hereda min-height/padding
1214
1249
  del base): solo cambia la superficie, no la caja. */
1215
1250
  button.kds-btn-text,
@@ -1901,6 +1936,9 @@ a.kds-btn.kds-btn-md {
1901
1936
  .kds-mt-4 { margin-top: var(--kds-spacing-4); }
1902
1937
  .kds-mt-6 { margin-top: var(--kds-spacing-6); }
1903
1938
  .kds-mt-8 { margin-top: var(--kds-spacing-8); }
1939
+ /* Absorbe el espacio libre de un contenedor flex-column: ancla el elemento al fondo
1940
+ (p.ej. las acciones de un form dentro de .kds-card-elevated--action). */
1941
+ .kds-mt-auto { margin-top: auto; }
1904
1942
 
1905
1943
  .kds-mb-0 { margin-bottom: 0; }
1906
1944
  .kds-mb-0-5 { margin-bottom: var(--kds-spacing-0-5); }
@@ -2354,28 +2392,35 @@ button.kds-locale-switcher-toggle:hover,
2354
2392
  }
2355
2393
 
2356
2394
  /* ========================================
2357
- Google Sign-In Button
2358
- Per Google Identity branding guidelines:
2395
+ SSO Button (federated sign-in: Google, Microsoft, ...)
2396
+ Neutral white button shared by every identity provider, styled per
2397
+ Google Identity branding guidelines, which Microsoft's also satisfy:
2359
2398
  https://developers.google.com/identity/branding-guidelines
2399
+ `.kds-btn-google` / `.kds-google-icon` are kept as aliases for
2400
+ consumers pinned to earlier versions.
2401
+ Colors go through --kds-btn-sso-* so the dark theme swaps all of them
2402
+ together (background alone flipping left the label unreadable).
2360
2403
  ======================================== */
2361
2404
 
2405
+ button.kds-btn-sso,
2406
+ a.kds-btn-sso,
2362
2407
  button.kds-btn-google,
2363
2408
  a.kds-btn-google {
2364
2409
  display: inline-flex;
2365
2410
  align-items: center;
2366
2411
  justify-content: center;
2367
- gap: var(--kds-spacing-1-5);
2412
+ gap: var(--kds-spacing-1-25);
2368
2413
  width: 100%;
2369
- background: var(--kds-color-background-paper);
2370
- color: #1F1F1F;
2371
- border: 1px solid #747775;
2414
+ background: var(--kds-btn-sso-bg, #FFFFFF);
2415
+ color: var(--kds-btn-sso-color, #1F1F1F);
2416
+ border: 1px solid var(--kds-btn-sso-border, #747775);
2372
2417
  border-radius: var(--kds-radius-button);
2373
2418
  padding: var(--kds-spacing-1) var(--kds-spacing-1-5);
2374
2419
  min-height: var(--kds-spacing-button-min-height);
2375
- font-family: "Roboto", var(--kds-font-family-secondary);
2420
+ font-family: var(--kds-font-family-secondary);
2376
2421
  font-weight: var(--kds-font-weight-medium);
2377
2422
  font-size: var(--kds-font-size-sm);
2378
- line-height: var(--kds-line-height-normal);
2423
+ line-height: 20px; /* Google 14/20; no line-height token matches */
2379
2424
  letter-spacing: var(--kds-letter-spacing-normal);
2380
2425
  text-transform: none;
2381
2426
  cursor: pointer;
@@ -2385,17 +2430,34 @@ a.kds-btn-google {
2385
2430
  box-shadow: none;
2386
2431
  }
2387
2432
 
2433
+ button.kds-btn-sso:hover,
2434
+ a.kds-btn-sso:hover,
2388
2435
  button.kds-btn-google:hover,
2389
2436
  a.kds-btn-google:hover {
2390
- background: #F2F2F2;
2437
+ background: var(--kds-btn-sso-bg-hover, #F2F2F2);
2391
2438
  box-shadow: var(--kds-shadow-1);
2392
2439
  }
2393
2440
 
2441
+ button.kds-btn-sso:active,
2442
+ a.kds-btn-sso:active,
2394
2443
  button.kds-btn-google:active,
2395
2444
  a.kds-btn-google:active {
2396
- background: #E8E8E8;
2445
+ background: var(--kds-btn-sso-bg-active, #E8E8E8);
2446
+ }
2447
+
2448
+ /* Dark theme per Google Identity guidelines; hover/active are 8% / 12%
2449
+ state layers of the label color over the fill. */
2450
+ [data-theme="dark"] .kds-btn-sso,
2451
+ [data-theme="dark"] .kds-btn-google {
2452
+ --kds-btn-sso-bg: #131314;
2453
+ --kds-btn-sso-color: #E3E3E3;
2454
+ --kds-btn-sso-border: #8E918F;
2455
+ --kds-btn-sso-bg-hover: #242425;
2456
+ --kds-btn-sso-bg-active: #2C2C2D;
2397
2457
  }
2398
2458
 
2459
+ button.kds-btn-sso:disabled,
2460
+ a.kds-btn-sso:disabled,
2399
2461
  button.kds-btn-google:disabled,
2400
2462
  a.kds-btn-google:disabled {
2401
2463
  background: var(--kds-color-action-disabled-bg);
@@ -2405,7 +2467,8 @@ a.kds-btn-google:disabled {
2405
2467
  box-shadow: none;
2406
2468
  }
2407
2469
 
2408
- /* Google G icon container - white background per guidelines */
2470
+ /* Provider logo container */
2471
+ .kds-btn-sso .kds-sso-icon,
2409
2472
  .kds-btn-google .kds-google-icon {
2410
2473
  display: flex;
2411
2474
  align-items: center;
@@ -2413,6 +2476,7 @@ a.kds-btn-google:disabled {
2413
2476
  flex-shrink: 0;
2414
2477
  }
2415
2478
 
2479
+ .kds-btn-sso .kds-sso-icon svg,
2416
2480
  .kds-btn-google .kds-google-icon svg {
2417
2481
  width: var(--kds-spacing-button-icon-size);
2418
2482
  height: var(--kds-spacing-button-icon-size);
@@ -2456,12 +2520,24 @@ a.kds-btn-google:disabled {
2456
2520
  /* Llena y centra dentro de contenedores flex (pantalla de carga); en flujo
2457
2521
  normal (contenedor no-flex) se reduce a su contenido. */
2458
2522
  flex: 1 1 auto;
2459
- /* Alto propio: el loader es contenido minimo por definicion, asi que sin esto la
2460
- card queda del alto del spinner y se lee como un recorte. Ocupa el body completo
2461
- de un telefono para que la pantalla de carga se lea como una pantalla y no como
2462
- una franja. Va aca y no en la card para que el minimo aplique solo a la carga:
2463
- el resto de las pantallas crece con su contenido. */
2464
- min-height: 36rem;
2523
+ /* Alto propio: el loader es contenido minimo por definicion, asi que sin esto la card
2524
+ queda del alto del spinner y se lee como un recorte. Va aca y no en la card para que
2525
+ el minimo aplique solo a la carga: el resto de las pantallas crece con su contenido.
2526
+ El valor de escritorio es moderado a proposito: el minimo de telefono (36rem) dejaba
2527
+ la card de la carga mucho mas alta que cualquier otra pantalla, y al entrar y salir
2528
+ del loader el widget crecia y volvia a encoger.
2529
+ .kds-card-elevated--action espeja este valor (y el de telefono): cambiarlos juntos. */
2530
+ min-height: 18rem;
2531
+ }
2532
+
2533
+ /* En telefono la carga es mas alta: ahi la card va a ancho completo y un loader corto se
2534
+ lee como una franja perdida en el medio. 22rem (card de 400px con su padding) en vez de
2535
+ los 36rem de antes (624px): con el header arriba, la pantalla entera cabe en el alto util
2536
+ de la mayoria de los telefonos (KTUF-388). */
2537
+ @media (max-width: 767px) {
2538
+ .kds-secure-loader {
2539
+ min-height: 22rem;
2540
+ }
2465
2541
  }
2466
2542
 
2467
2543
  .kds-secure-loader-text {
@@ -3271,12 +3347,33 @@ label.radio span {
3271
3347
  cursor: pointer;
3272
3348
  }
3273
3349
 
3274
- /* Checked state */
3350
+ /* Checked state.
3351
+ accent-color aplica al input nativo, que BeerCSS deja con opacity 0: no se ve.
3352
+ Se mantiene igual por si algun consumidor no usa el markup de BeerCSS. */
3275
3353
  label.checkbox input[type="checkbox"]:checked,
3276
3354
  label.radio input[type="radio"]:checked {
3277
3355
  accent-color: var(--kds-color-accent, var(--primary));
3278
3356
  }
3279
3357
 
3358
+ /* El control que SI se ve lo dibuja BeerCSS como una ligadura de Material Symbols en
3359
+ `> span::before`, coloreada con var(--primary). Estas dos reglas reproducen sus
3360
+ selectores para que ese pseudo-elemento siga al rol accent.
3361
+
3362
+ Misma especificidad que el original y sin !important: este archivo va ultimo en el
3363
+ bundle, asi que gana por orden. El fallback deja el valor anterior intacto cuando la
3364
+ marca no define accent, que es el caso de todo consumidor actual.
3365
+
3366
+ No se toca `.switch`: es un control de encendido/apagado, no de seleccion, y el
3367
+ contrato de marca no lo incluye. */
3368
+ :is(.checkbox, .radio) > span::before {
3369
+ color: var(--kds-color-accent, var(--primary));
3370
+ }
3371
+
3372
+ :is(.checkbox, .radio) > input:checked + span::before,
3373
+ :is(.checkbox, .radio).icon > input:checked + span > i {
3374
+ color: var(--kds-color-accent, var(--primary));
3375
+ }
3376
+
3280
3377
  /* Disabled state */
3281
3378
  label.checkbox input[type="checkbox"]:disabled,
3282
3379
  label.radio input[type="radio"]:disabled {
@@ -4330,14 +4427,16 @@ footer > .kds-container-center {
4330
4427
 
4331
4428
  /* Code block for displaying code snippets */
4332
4429
  .kds-code-block {
4333
- background: var(--kds-color-background-elevated);
4334
- padding: var(--kds-spacing-4);
4430
+ background: var(--kds-color-background-muted);
4431
+ border: 1px solid var(--kds-color-divider);
4432
+ padding: var(--kds-spacing-2);
4335
4433
  border-radius: var(--kds-radius-md);
4336
4434
  overflow-x: auto;
4337
- font-family: 'Courier New', monospace;
4435
+ font-family: var(--kds-font-family-mono);
4338
4436
  font-size: var(--kds-font-size-sm);
4339
4437
  line-height: var(--kds-line-height-relaxed);
4340
4438
  color: var(--kds-color-text-primary);
4439
+ tab-size: 4;
4341
4440
  }
4342
4441
 
4343
4442
  /* ============================================
@@ -6279,13 +6378,39 @@ button.kds-fab:active:not(:disabled) {
6279
6378
  alto que el mark de Khipu. Sirve para <img> o un elemento con .kds-psp-mark. */
6280
6379
  .kds-secure-footer img,
6281
6380
  .kds-secure-footer .kds-psp-mark {
6282
- height: 11px;
6283
6381
  width: auto;
6284
6382
  flex: 0 0 auto;
6285
6383
  display: block;
6286
6384
  object-fit: contain;
6287
6385
  }
6288
6386
 
6387
+ /* Logo del PSP: dos perillas, las dos calibradas contra el asset real que sube un PSP
6388
+ hoy (klap.png, 60x30, con 2px de aire transparente arriba y abajo).
6389
+
6390
+ Se nivela por LINEA DE BASE, no por caja ni por borde inferior de la tinta: los dos
6391
+ wordmarks tienen descendente -la "p" de Khipu baja un 20% de su caja- asi que igualar
6392
+ los bordes de abajo deja al logo del PSP flotando sobre la linea de Khipu.
6393
+
6394
+ - --kds-psp-mark-height: el alto de la caja. El ojo lee la tinta, no la caja, y el
6395
+ aire del PNG se come parte del alto: a 11px la tinta de klap medía 9.5px contra los
6396
+ 11px de Khipu y se veia mas chico. A 15px la tinta llega a 13px y se leen iguales.
6397
+ - --kds-psp-mark-top: cuanto baja para que su linea de base calce con la de Khipu.
6398
+ Va como valor y no como formula a proposito: depende del aire y de la anatomia del
6399
+ asset de cada PSP, y se termina de ajustar mirando la pantalla. */
6400
+ .kds-secure-footer {
6401
+ --kds-psp-mark-height: 15px;
6402
+ --kds-psp-mark-top: 1px;
6403
+ }
6404
+
6405
+ .kds-secure-footer img,
6406
+ .kds-secure-footer .kds-psp-mark {
6407
+ height: var(--kds-psp-mark-height);
6408
+ position: relative;
6409
+ top: var(--kds-psp-mark-top);
6410
+ }
6411
+
6412
+
6413
+
6289
6414
  .kds-secure-footer-code {
6290
6415
  color: var(--kds-color-text-hint);
6291
6416
  }
@@ -6541,14 +6666,37 @@ button.kds-fab:active:not(:disabled) {
6541
6666
  color: var(--kds-color-success-main);
6542
6667
  }
6543
6668
 
6544
- /* Variant: pending (animated blue spinner, sin icono interno) */
6669
+ /* Variant: pending (animated spinner, sin icono interno).
6670
+ El acento sale de --kds-status-pending-accent, con el azul de info como
6671
+ fallback: el valor por defecto no cambia, y una pantalla puede pedir otro
6672
+ acento sin redefinir la regla ni recurrir a estilos inline. */
6545
6673
  .kds-status-block[data-status="pending"] .kds-status-block-icon {
6546
6674
  background: transparent;
6547
6675
  border: 3px solid var(--kds-color-divider);
6548
- border-top-color: var(--kds-color-info-main);
6676
+ border-top-color: var(--kds-status-pending-accent, var(--kds-color-info-main));
6549
6677
  animation: kds-spin 1s linear infinite;
6550
6678
  }
6551
6679
 
6680
+ /* Spinner neutro, para esperas que acompañan al contenido sin reclamar
6681
+ atención: el azul de info lee como un aviso y compite con la información
6682
+ de la pantalla. */
6683
+ .kds-status-block.kds-accent-neutral {
6684
+ --kds-status-pending-accent: var(--kds-color-text-primary);
6685
+ }
6686
+
6687
+ /* Spinner fino, para bandas de una línea donde el trazo de 3px pesa demasiado.
6688
+ El tamaño se pide por token y no por regla sobre el icono: así no compite en
6689
+ especificidad con `.kds-status-block.inline .kds-status-block-icon`, que
6690
+ marca igual y viene después. */
6691
+ .kds-status-block.kds-spinner-sm {
6692
+ --kds-status-icon-size: var(--kds-spacing-3);
6693
+ --kds-status-icon-size-sm: var(--kds-spacing-3);
6694
+ }
6695
+
6696
+ .kds-status-block.kds-spinner-sm .kds-status-block-icon {
6697
+ border-width: 2px;
6698
+ }
6699
+
6552
6700
  /* Variant: warn */
6553
6701
  .kds-status-block[data-status="warn"] .kds-status-block-icon {
6554
6702
  background: var(--kds-color-warning-soft);
@@ -6592,6 +6740,60 @@ button.kds-fab:active:not(:disabled) {
6592
6740
  font-size: var(--kds-font-size-xl);
6593
6741
  }
6594
6742
 
6743
+ /* Inline without description: the title is the only thing next to the icon. Its
6744
+ margin-bottom (spacing-1, which separates it from the description in the centered
6745
+ variant) made the column taller, so align-items:center left the text ~4px above the
6746
+ icon's center. */
6747
+ .kds-status-block.inline .kds-status-block-title:last-child {
6748
+ margin-bottom: 0;
6749
+ --kds-typography-h2-margin-block-end: 0;
6750
+ }
6751
+
6752
+ /* Inline with description (KdsStatusBlock inline + description): the description is a
6753
+ full-width paragraph below the icon+title row, spacing-1 under it. The block drops its
6754
+ bottom padding and owns the gap to what follows (spacing-2), so consumers never add
6755
+ margins around the description. kds-btn-stack and kds-share-card already carry that
6756
+ top margin themselves. */
6757
+ .kds-status-block.inline.kds-status-block--described {
6758
+ flex-wrap: wrap;
6759
+ row-gap: var(--kds-spacing-1);
6760
+ padding-bottom: 0;
6761
+ }
6762
+
6763
+ .kds-status-block--described > .kds-status-block-description {
6764
+ flex-basis: 100%;
6765
+ }
6766
+
6767
+ .kds-status-block--described + .kds-alert,
6768
+ .kds-status-block--described + .kds-copy-row {
6769
+ margin-top: var(--kds-spacing-2);
6770
+ }
6771
+
6772
+ /* Banda: el bloque de estado sobre una superficie sutil, para cuando acompaña
6773
+ al contenido en lugar de encabezar la pantalla. Va después de `.inline`
6774
+ porque marca igual en especificidad y necesita ganarle el padding. */
6775
+ .kds-status-block.kds-banded {
6776
+ /* 0.75rem = 12px, el valor que --kds-font-size-xs toma desde tablet. Va plano y no
6777
+ sigue la escala responsiva a proposito: a 11px el mensaje de espera se parte en dos
6778
+ lineas y el diseno lo quiere en una. Como custom property de la variante, siguiendo
6779
+ el patron de kds-spinner-sm con --kds-status-icon-size. */
6780
+ --kds-status-band-font-size: 0.75rem;
6781
+ background: var(--kds-color-background-muted);
6782
+ border-radius: var(--kds-radius-md);
6783
+ padding: var(--kds-spacing-1-5) var(--kds-spacing-2);
6784
+ }
6785
+
6786
+ /* La banda de espera lleva su propia tipografia, por spec de Figma: 11px en bold.
6787
+ Va scopeada a la variante y no a .kds-status-block-description, que la comparten
6788
+ los bloques de resultado, donde 11px seria ilegible. El color se deja en el token
6789
+ heredado: un #000 fijo se volveria invisible bajo modo oscuro, donde text-primary
6790
+ pasa a blanco. */
6791
+ .kds-status-block.kds-banded .kds-status-block-description {
6792
+ font-size: var(--kds-status-band-font-size);
6793
+ font-weight: var(--kds-font-weight-bold);
6794
+ line-height: normal;
6795
+ }
6796
+
6595
6797
  @keyframes kds-spin {
6596
6798
  to { transform: rotate(360deg); }
6597
6799
  }
@@ -6678,6 +6880,48 @@ button.kds-fab:active:not(:disabled) {
6678
6880
  min-height: 0;
6679
6881
  }
6680
6882
 
6883
+ /* Variante alta (size="full"): contenido largo (documentos, textos legales) usa todo el alto
6884
+ máximo del sheet y el body se estira para que un hijo lo llene. Los .kds-theme-root son
6885
+ envoltorios transparentes que el consumidor puede meter entre el body y el contenido (p. ej.
6886
+ un KdsRoot dentro de un div display:contents), así que se estiran a cualquier profundidad. */
6887
+ .kds-bottom-sheet-full {
6888
+ height: 90vh;
6889
+ height: 90dvh;
6890
+ }
6891
+
6892
+ .kds-bottom-sheet-full .kds-bottom-sheet-body,
6893
+ .kds-bottom-sheet-full .kds-bottom-sheet-body .kds-theme-root {
6894
+ display: flex;
6895
+ flex: 1;
6896
+ flex-direction: column;
6897
+ }
6898
+
6899
+ /* ========================================
6900
+ DOCUMENT FRAME (.kds-document-frame)
6901
+ Documento embebido (iframe) que llena su contenedor. --kds-document-frame-scale achica
6902
+ documentos pensados para un viewport de escritorio: el iframe se arma más ancho y se
6903
+ escala, así letra, títulos y márgenes bajan juntos.
6904
+ ======================================== */
6905
+
6906
+ .kds-document-frame {
6907
+ --kds-document-frame-scale: 1;
6908
+ position: relative;
6909
+ flex: 1;
6910
+ min-height: var(--kds-document-frame-min-height, 12rem);
6911
+ overflow: hidden;
6912
+ }
6913
+
6914
+ .kds-document-frame > iframe {
6915
+ position: absolute;
6916
+ top: 0;
6917
+ left: 0;
6918
+ width: calc(100% / var(--kds-document-frame-scale));
6919
+ height: calc(100% / var(--kds-document-frame-scale));
6920
+ border: 0;
6921
+ transform: scale(var(--kds-document-frame-scale));
6922
+ transform-origin: 0 0;
6923
+ }
6924
+
6681
6925
  .kds-bottom-sheet-icon {
6682
6926
  font-size: var(--kds-font-size-4xl);
6683
6927
  }
@@ -6713,6 +6957,14 @@ button.kds-fab:active:not(:disabled) {
6713
6957
  color: var(--kds-color-primary-contrast);
6714
6958
  }
6715
6959
 
6960
+ /* El reset `.kds-bottom-sheet-actions button { border: 0 }` borra la caja de las
6961
+ variantes outlined. Se restituye solo grosor y estilo: el color queda en
6962
+ currentColor, que ya es el de cada variante (primary, success, etc.). */
6963
+ .kds-bottom-sheet-actions button.kds-btn-outlined {
6964
+ border-style: solid;
6965
+ border-width: var(--kds-border-width-sm, 1px);
6966
+ }
6967
+
6716
6968
  .kds-bottom-sheet-actions .kds-btn-ghost {
6717
6969
  background: transparent;
6718
6970
  color: var(--kds-color-text-secondary);
@@ -6833,6 +7085,15 @@ button.kds-fab:active:not(:disabled) {
6833
7085
  }
6834
7086
 
6835
7087
  /* Auto-managed tab panels: hidden attribute must win over any display rule */
7088
+ /* `hidden` es semantica de plataforma: si el atributo esta, el elemento no se
7089
+ renderiza. El UA lo aplica con display:none, pero cualquier regla de autor con
7090
+ display lo pisa — .kds-btn y .kds-btn-stack son flex, asi que un boton con
7091
+ hidden seguia ocupando lugar. El !important es deliberado: ninguna regla de
7092
+ componente deberia poder anular el atributo. */
7093
+ [hidden] {
7094
+ display: none !important;
7095
+ }
7096
+
6836
7097
  [data-kds-tab-panel][hidden] {
6837
7098
  display: none;
6838
7099
  }
@@ -7223,6 +7484,57 @@ div.kds-invoice-header,
7223
7484
  padding: var(--kds-spacing-0-5);
7224
7485
  }
7225
7486
 
7487
+ /* -- Skeleton (KTUF-388) --
7488
+ Gris pulsante que reserva el lugar de un contenido que aún no llega. La base solo pinta:
7489
+ sobre un elemento que ya tiene tamaño (p.ej. .kds-invoice-merchant) conserva sus medidas,
7490
+ radio y colapso, y solo cambia el fondo. */
7491
+ .kds-skeleton {
7492
+ background: var(--kds-color-gray-200);
7493
+ animation: kds-skeleton-pulse 1.4s ease-in-out infinite;
7494
+ }
7495
+
7496
+ /* Texto: un texto de relleno con la tinta transparente, puesto DENTRO del elemento real
7497
+ (p.kds-invoice-amount, dd, ...). Usa la fuente, el tamaño y el alto de línea del slot, así
7498
+ que ancho y alto calzan con el contenido que reemplaza (también dentro de contenedores
7499
+ flex, donde una caja de alto fijo perdía el alto de línea) y el cambio no desplaza nada. */
7500
+ .kds-skeleton--text {
7501
+ color: transparent;
7502
+ border-radius: var(--kds-radius-sm);
7503
+ -webkit-box-decoration-break: clone;
7504
+ box-decoration-break: clone;
7505
+ user-select: none;
7506
+ /* Inline, el gris cubre la altura de la letra; se recorta arriba y abajo para que las
7507
+ barras de líneas vecinas no se toquen. Solo visual: el layout no cambia. Va inline
7508
+ también dentro de un flex (envuelto en un span): como item de flex cubriría todo el
7509
+ alto de línea y quedaría más grueso que el resto. */
7510
+ -webkit-clip-path: inset(0.2em 0 round var(--kds-radius-sm));
7511
+ clip-path: inset(0.2em 0 round var(--kds-radius-sm));
7512
+ }
7513
+
7514
+ [data-theme="dark"] .kds-skeleton {
7515
+ background: var(--kds-color-gray-700);
7516
+ }
7517
+
7518
+ @keyframes kds-skeleton-pulse {
7519
+ 0%, 100% { opacity: 1; }
7520
+ 50% { opacity: 0.45; }
7521
+ }
7522
+
7523
+ @media (prefers-reduced-motion: reduce) {
7524
+ .kds-skeleton {
7525
+ animation: none;
7526
+ }
7527
+ }
7528
+
7529
+ /* Header de la factura mientras carga (KdsInvoiceHeaderSkeleton): mismo markup que el
7530
+ header real. El "Detalle del cobro" es decorativo, no un botón: replica lo que da el alto
7531
+ al toggle real (line-height normal de button + caret de 1.25em de font-size-base). */
7532
+ .kds-invoice-skeleton .kds-expand-toggle {
7533
+ cursor: default;
7534
+ line-height: normal;
7535
+ min-height: calc(var(--kds-font-size-base) * 1.25);
7536
+ }
7537
+
7226
7538
  /* Titulo del body de la card: alineado a la izquierda, sin importar que el consumidor
7227
7539
  traiga .kds-text-center. Va con especificidad de contexto (screen + card) a proposito:
7228
7540
  el centrado se venia pidiendo app por app y el resultado era inconsistente entre
@@ -7907,6 +8219,53 @@ svg[fill='none'] {
7907
8219
  border-radius: var(--kds-radius-card);
7908
8220
  }
7909
8221
 
8222
+ /* Body card con acción (opt-in, KTUF-388): mismo alto mínimo que el card de carga, para que
8223
+ el card no salte de alto entre el loader, los pasos de progreso y los formularios, y la
8224
+ acción (submit) quede anclada al fondo con `.kds-mt-auto`. No cambia el default de arriba:
8225
+ solo lo toma quien pone el modificador (khenshin-web).
8226
+ - Espeja el min-height de .kds-secure-loader (18rem / 22rem en teléfono) más el padding
8227
+ vertical del card en el flujo (spacing-3 x2). Si cambia el del loader, cambia acá.
8228
+ - Valores fijos, sin vh/dvh: en un iframe (KTUF-239) el viewport es el propio iframe, cuyo
8229
+ alto lo da el RESIZE del contenido; un mínimo relativo al viewport se realimenta.
8230
+ - El contenido directo (form) crece para que la acción baje dentro de él y no compita
8231
+ con el `margin-top: auto` de .kds-card-version. */
8232
+ .kds-screen > .kds-card-elevated.kds-card-elevated--action {
8233
+ min-height: calc(18rem + 2 * var(--kds-spacing-3));
8234
+ }
8235
+
8236
+ @media (max-width: 767px) {
8237
+ .kds-screen > .kds-card-elevated.kds-card-elevated--action {
8238
+ min-height: calc(22rem + 2 * var(--kds-spacing-3));
8239
+ }
8240
+ }
8241
+
8242
+ .kds-card-elevated--action > form {
8243
+ flex-grow: 1;
8244
+ display: flex;
8245
+ flex-direction: column;
8246
+ }
8247
+
8248
+ /* Pantallas cuyas acciones van en un .kds-btn-stack hijo directo del card (resultados:
8249
+ éxito, fallo, timeout, pago manual): el stack se ancla al fondo como el submit de un form.
8250
+ Su margen de siempre (spacing-2) pasa a padding para conservar el respiro mínimo, y la
8251
+ versión que le sigue deja de absorber espacio; si no, el libre se repartía entre los dos
8252
+ márgenes automáticos y el botón quedaba a media altura. */
8253
+ .kds-card-elevated--action > .kds-btn-stack {
8254
+ margin-top: auto;
8255
+ padding-top: var(--kds-spacing-2);
8256
+ }
8257
+
8258
+ .kds-card-elevated--action > .kds-btn-stack ~ .kds-card-version {
8259
+ margin-top: 0;
8260
+ }
8261
+
8262
+ /* Un loader dentro del card (pasos de progreso) no suma su propio mínimo: ya lo da el card.
8263
+ Si no, el card crecía el alto de la línea de versión en cada paso de carga y volvía a
8264
+ encoger al llegar el formulario. El loader se centra en el alto que el card le deja. */
8265
+ .kds-card-elevated--action .kds-secure-loader {
8266
+ min-height: 0;
8267
+ }
8268
+
7910
8269
  /* Variante sin radio superior: pantallas de resultado (éxito/fallo/timeout) que van
7911
8270
  SIN header arriba — la body card va sola y su tope queda plano (flush) en vez de
7912
8271
  con esquinas redondeadas sueltas. El padding del card sigue manejando el inset. */
@@ -7923,13 +8282,20 @@ svg[fill='none'] {
7923
8282
 
7924
8283
  /* Payment stage — full-viewport centered wrapper.
7925
8284
  Fondo muted (gris) para que las cards blancas (header + body) y el gap entre
7926
- ellas se distingan; sin esto, card blanca sobre fondo blanco se funden. */
8285
+ ellas se distingan; sin esto, card blanca sobre fondo blanco se funden.
8286
+
8287
+ --kds-color-background-body es el fondo de la pantalla, y es lo que un comercio
8288
+ cambia con su backgroundColor. Se resuelve aca, con muted como fallback, y no
8289
+ como alias en :root: un alias se evalua en :root y queda con el valor de :root,
8290
+ asi que la variable que el widget setea en body nunca llegaria a la etapa. Los
8291
+ otros usos de muted (tabs, bloque de codigo, hover del FAB) no se tocan: son
8292
+ superficies de componentes, no el fondo de la pantalla. */
7927
8293
  .kds-payment-stage {
7928
8294
  min-height: 100vh;
7929
8295
  display: flex;
7930
8296
  justify-content: center;
7931
8297
  padding: var(--kds-spacing-2-5) var(--kds-spacing-2) var(--kds-spacing-8);
7932
- background: var(--kds-color-background-muted);
8298
+ background: var(--kds-color-background-body, var(--kds-color-background-muted));
7933
8299
  }
7934
8300
 
7935
8301
  /* Payment flow — narrow column */
@@ -7998,6 +8364,18 @@ svg[fill='none'] {
7998
8364
  @media (hover: hover) and (pointer: fine) {
7999
8365
  .kds-card-elevated.kds-invoice-sticky {
8000
8366
  border-radius: var(--kds-radius-card);
8367
+ /* El clip-path del colapso recorta con `round 0 0 R R` y eso PISA al
8368
+ border-radius: aunque la card declare las cuatro esquinas, el recorte las
8369
+ deja rectas arriba. Hay que redondearlas también acá o el fix no se ve. */
8370
+ clip-path: inset(
8371
+ 0 0
8372
+ calc(
8373
+ var(--collapse-collapsible-h) * var(--collapse-progress)
8374
+ + var(--kds-spacing-1) * var(--collapse-progress)
8375
+ )
8376
+ 0
8377
+ round var(--kds-radius-card)
8378
+ );
8001
8379
  }
8002
8380
 
8003
8381
  .kds-screen > .kds-card-elevated.kds-card-elevated--flush-top,
@@ -8336,8 +8714,11 @@ svg[fill='none'] {
8336
8714
  background: var(--kds-color-action-hover);
8337
8715
  }
8338
8716
 
8339
- /* Search field reutilizable (standalone o dentro del KdsBankModal). */
8340
- .kds-search-field {
8717
+ /* Search field reutilizable (standalone o dentro del KdsBankModal).
8718
+ El selector de descendiente se mantiene por compatibilidad con los consumidores
8719
+ que renderizan el input sin clase (payment lo arma a mano en GSP). */
8720
+ .kds-search-field,
8721
+ .kds-bank-modal-search input {
8341
8722
  width: 100%;
8342
8723
  padding: var(--kds-spacing-1-25) var(--kds-spacing-1-5);
8343
8724
  border: 1px solid var(--kds-color-divider);
@@ -8349,11 +8730,13 @@ svg[fill='none'] {
8349
8730
  background: var(--kds-color-surface);
8350
8731
  }
8351
8732
 
8352
- .kds-search-field::placeholder {
8733
+ .kds-search-field::placeholder,
8734
+ .kds-bank-modal-search input::placeholder {
8353
8735
  color: var(--kds-color-text-hint);
8354
8736
  }
8355
8737
 
8356
- .kds-search-field:focus {
8738
+ .kds-search-field:focus,
8739
+ .kds-bank-modal-search input:focus {
8357
8740
  border-color: var(--kds-color-primary-main);
8358
8741
  box-shadow: 0 0 0 2px var(--kds-color-primary-faint);
8359
8742
  }