@khipu/design-system 0.1.0-alpha.50 → 0.1.0-alpha.52

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-04-27T13:24:37.477Z
14
+ * Generated: 2026-04-28T15:56:42.052Z
15
15
  *
16
16
  * To regenerate:
17
17
  * cd design-system && npm run tokens:generate
@@ -66,7 +66,7 @@
66
66
  --kds-color-background-default: #FFFFFF;
67
67
  --kds-color-background-paper: #FFFFFF;
68
68
  --kds-color-background-elevated: #FAFAFA;
69
- --kds-color-background-muted: #F5F5F5;
69
+ --kds-color-background-muted: #F4F4F7;
70
70
 
71
71
  /* Action colors */
72
72
  --kds-color-action-active: rgba(0, 0, 0, 0.56);
@@ -650,7 +650,7 @@ body.dark {
650
650
  --kds-color-hover-bg: #FAFAFA; /* Ultra-subtle hover background for rows */
651
651
 
652
652
  /* Layout colors */
653
- --kds-color-shell: #F5F5F5; /* Neutral page background */
653
+ --kds-color-shell: #F4F4F7; /* Neutral page background */
654
654
 
655
655
  /* Card specific */
656
656
  --kds-radius-card: 14px; /* Payment card radius (between lg and xl) */
@@ -4758,6 +4758,12 @@ dialog#surveyModal button.circle {
4758
4758
  font-size: var(--kds-font-size-sm);
4759
4759
  }
4760
4760
 
4761
+ /* Inside a card: push to bottom with auto margin, subtle top spacing */
4762
+ .kds-secure-footer.inside {
4763
+ margin-top: auto;
4764
+ padding-top: var(--kds-spacing-3);
4765
+ }
4766
+
4761
4767
  .kds-secure-footer-code {
4762
4768
  color: var(--kds-color-text-hint);
4763
4769
  }
@@ -5174,64 +5180,157 @@ dialog#surveyModal button.circle {
5174
5180
  ======================================== */
5175
5181
 
5176
5182
  /* -- Sticky Invoice Card (.kds-invoice-sticky) -- */
5177
- /* Collapses detail rows on scroll via .collapsed class (JS toggle) */
5183
+ /* MOBILE ONLY: Progressive scroll-linked collapse (0-150px) via --collapse-progress CSS variable */
5184
+ /* Desktop: Mantiene cajitas normales sin sticky behavior */
5185
+
5186
+ /* Mobile only (< 768px): Sticky positioning and progressive collapse behavior */
5187
+ /* Animated properties match source prototype (Payment Flow.html lines 133-165) */
5188
+ @media (max-width: 767px) {
5189
+ /* Scroll-linked collapse variables — defaults on screen, overridden by JS inline style.
5190
+ Set on .kds-screen so both sticky card and siblings (via translateY) can inherit them. */
5191
+ .kds-screen {
5192
+ --collapse-progress: 0;
5193
+ --collapse-collapsible-h: 0px;
5194
+ }
5178
5195
 
5179
- .kds-invoice-sticky {
5180
- position: sticky;
5181
- top: 0;
5182
- z-index: 10;
5183
- transition: box-shadow 0.25s ease, border-radius 0.25s ease;
5184
- }
5196
+ /* Wrapper: carries sticky positioning + shadow (outside clip-path) */
5197
+ .kds-invoice-sticky-wrap {
5198
+ position: sticky;
5199
+ top: 0;
5200
+ z-index: 10;
5201
+
5202
+ /* Shadow rendered on wrapper so clip-path on child doesn't clip it.
5203
+ drop-shadow traces the child's clipped alpha boundary → follows rounded shape. */
5204
+ -webkit-filter: drop-shadow(
5205
+ 0 calc(4px * var(--collapse-progress, 0))
5206
+ calc(12px * var(--collapse-progress, 0))
5207
+ rgba(16, 24, 40, calc(0.12 * var(--collapse-progress, 0)))
5208
+ );
5209
+ filter: drop-shadow(
5210
+ 0 calc(4px * var(--collapse-progress, 0))
5211
+ calc(12px * var(--collapse-progress, 0))
5212
+ rgba(16, 24, 40, calc(0.12 * var(--collapse-progress, 0)))
5213
+ );
5214
+ }
5185
5215
 
5186
- .kds-invoice-sticky.collapsed {
5187
- box-shadow: var(--kds-shadow-2);
5188
- border-radius: 0 0 var(--kds-radius-card) var(--kds-radius-card);
5189
- }
5216
+ .kds-card-elevated.kds-invoice-sticky {
5217
+ /* Symmetric padding */
5218
+ padding: var(--kds-spacing-2);
5190
5219
 
5191
- .kds-invoice-sticky .kds-invoice-collapsible {
5192
- overflow: hidden;
5193
- max-height: 300px;
5194
- opacity: 1;
5195
- transition: max-height 0.3s ease, opacity 0.2s ease;
5196
- }
5220
+ /* Static border-radius on all corners */
5221
+ border-radius: var(--kds-radius-card);
5222
+
5223
+ /* Override .kds-card-elevated transition — scroll drives the animation, not CSS transitions */
5224
+ transition: none;
5225
+
5226
+ /* GPU acceleration */
5227
+ -webkit-font-smoothing: antialiased;
5228
+ -moz-osx-font-smoothing: grayscale;
5229
+
5230
+ /* clip-path clips the card from the bottom to hide the collapsible space.
5231
+ This is VISUAL ONLY — no layout change — prevents scroll feedback loop.
5232
+ Safari 13.1+ compatible (inset + round)
5233
+ When collapsed (progress=1): bottom corners get border-radius,
5234
+ top stays flush with screen edge. */
5235
+ /* Bottom inset = collapsible height + half the card padding to tighten
5236
+ the gap between "Código" and the visible bottom edge when collapsed. */
5237
+ clip-path: inset(
5238
+ 0 0
5239
+ calc(
5240
+ var(--collapse-collapsible-h) * var(--collapse-progress)
5241
+ + var(--kds-spacing-1) * var(--collapse-progress)
5242
+ )
5243
+ 0
5244
+ round var(--kds-radius-card)
5245
+ );
5246
+
5247
+ /* Suppress card's own shadow — wrapper handles it */
5248
+ box-shadow: none !important;
5249
+ }
5197
5250
 
5198
- .kds-invoice-sticky.collapsed .kds-invoice-collapsible {
5199
- max-height: 0;
5200
- opacity: 0;
5201
- }
5251
+ /* Brand inner: shrink logo + reduce margin on collapse */
5252
+ .kds-invoice-sticky .kds-brand-inner {
5253
+ margin-bottom: calc(var(--kds-spacing-1) * (1 - var(--collapse-progress)) + var(--kds-spacing-0-5) * var(--collapse-progress));
5254
+ }
5255
+ .kds-invoice-sticky .kds-brand-inner svg {
5256
+ height: calc(22px - 5px * var(--collapse-progress));
5257
+ }
5202
5258
 
5203
- .kds-invoice-sticky .kds-invoice-amount {
5204
- transition: font-size 0.25s ease;
5205
- }
5259
+ /* Collapsible content (kv-rows, detail-panel): Fade out only */
5260
+ /* No max-height clip-path on parent handles visual collapse without layout changes */
5261
+ .kds-invoice-sticky .kds-invoice-collapsible {
5262
+ opacity: calc(1 - var(--collapse-progress) * 1.5);
5263
+ }
5206
5264
 
5207
- .kds-invoice-sticky.collapsed .kds-invoice-amount {
5208
- font-size: var(--kds-font-size-xl);
5209
- }
5265
+ /* Amount: NO font-size change (source keeps 30px always) */
5266
+
5267
+ /* Merchant tile: 64×64px → 50×50px, radius 8px → 6px (progressive) */
5268
+ .kds-invoice-sticky .kds-invoice-merchant {
5269
+ width: calc(
5270
+ var(--kds-merchant-size) -
5271
+ (var(--kds-merchant-size) - var(--kds-merchant-size-collapsed)) * var(--collapse-progress)
5272
+ );
5273
+ height: calc(
5274
+ var(--kds-merchant-size) -
5275
+ (var(--kds-merchant-size) - var(--kds-merchant-size-collapsed)) * var(--collapse-progress)
5276
+ );
5277
+ border-radius: calc(
5278
+ var(--kds-merchant-radius) -
5279
+ (var(--kds-merchant-radius) - var(--kds-merchant-radius-collapsed)) * var(--collapse-progress)
5280
+ );
5281
+ }
5210
5282
 
5211
- .kds-invoice-sticky .kds-invoice-merchant {
5212
- transition: width 0.25s ease, height 0.25s ease;
5213
- }
5283
+ /* Merchant icon: 30px → 24px (progressive) */
5284
+ .kds-invoice-sticky .kds-invoice-merchant i {
5285
+ font-size: calc(
5286
+ var(--kds-merchant-icon-size) -
5287
+ (var(--kds-merchant-icon-size) - var(--kds-merchant-icon-size-collapsed)) * var(--collapse-progress)
5288
+ );
5289
+ }
5214
5290
 
5215
- .kds-invoice-sticky.collapsed .kds-invoice-merchant {
5216
- width: var(--kds-status-icon-size-sm);
5217
- height: var(--kds-status-icon-size-sm);
5218
- }
5291
+ /* Code: margin-top 4px → 0 (progressive). Font-size stays 12px. */
5292
+ .kds-invoice-sticky .kds-invoice-code {
5293
+ margin-top: calc(var(--kds-spacing-1) * (1 - var(--collapse-progress)));
5294
+ margin-bottom: 0;
5295
+ }
5296
+
5297
+ /* Pull all siblings after sticky wrapper up to fill clipped gap.
5298
+ transform: translateY is VISUAL ONLY — no layout change, no scroll feedback loop.
5299
+ --collapse-progress and --collapse-collapsible-h are set on the .kds-screen parent by JS. */
5300
+ .kds-invoice-sticky-wrap ~ * {
5301
+ transform: translateY(calc(
5302
+ -1 * (var(--collapse-collapsible-h, 0px) + var(--kds-spacing-1))
5303
+ * var(--collapse-progress, 0)
5304
+ ));
5305
+ }
5219
5306
 
5220
- .kds-invoice-sticky .kds-invoice-merchant i {
5221
- transition: font-size 0.25s ease;
5222
5307
  }
5223
5308
 
5224
- .kds-invoice-sticky.collapsed .kds-invoice-merchant i {
5225
- font-size: var(--kds-font-size-xl);
5309
+ /* Desktop (>= 768px): Regular card behavior, no sticky */
5310
+ @media (min-width: 768px) {
5311
+ .kds-invoice-sticky {
5312
+ position: relative;
5313
+ }
5226
5314
  }
5227
5315
 
5228
- .kds-invoice-sticky .kds-invoice-code {
5229
- transition: margin-top 0.25s ease, font-size 0.25s ease;
5316
+ /* -- Invoice Collapsible (kv rows below header) -- */
5317
+ .kds-invoice-collapsible {
5318
+ margin-top: var(--kds-spacing-1-5);
5230
5319
  }
5231
5320
 
5232
- .kds-invoice-sticky.collapsed .kds-invoice-code {
5233
- margin-top: 0;
5234
- font-size: var(--kds-font-size-caption);
5321
+ /* -- Invoice Summary (kv + expand toggle side by side) -- */
5322
+ .kds-invoice-summary {
5323
+ display: flex;
5324
+ align-items: flex-end;
5325
+ gap: var(--kds-spacing-2);
5326
+ }
5327
+ .kds-invoice-summary .kds-kv {
5328
+ flex: 1;
5329
+ min-width: 0;
5330
+ }
5331
+ .kds-invoice-summary .kds-expand-toggle {
5332
+ flex-shrink: 0;
5333
+ white-space: nowrap;
5235
5334
  }
5236
5335
 
5237
5336
  /* -- Invoice Header (amount + merchant tile) -- */
@@ -5242,6 +5341,12 @@ dialog#surveyModal button.circle {
5242
5341
  gap: var(--kds-spacing-2);
5243
5342
  }
5244
5343
 
5344
+ /* Reset BeerCSS sibling margin on <p> inside invoice header — specificity (0,4,1) ties BeerCSS, wins by source order */
5345
+ .kds-card-elevated.kds-invoice-sticky .kds-invoice-header p.kds-invoice-amount,
5346
+ .kds-card-elevated.kds-invoice-sticky .kds-invoice-header p.kds-invoice-code {
5347
+ margin-block: 0;
5348
+ }
5349
+
5245
5350
  .kds-invoice-amount {
5246
5351
  font-weight: 700;
5247
5352
  font-size: 30px;
@@ -5258,7 +5363,8 @@ dialog#surveyModal button.circle {
5258
5363
  .kds-invoice-code {
5259
5364
  font-size: var(--kds-font-size-xs);
5260
5365
  color: var(--kds-color-text-hint);
5261
- margin-top: var(--kds-spacing-0-5);
5366
+ margin: 0;
5367
+ margin-top: var(--kds-spacing-1);
5262
5368
  }
5263
5369
 
5264
5370
  .kds-invoice-merchant {
@@ -5596,7 +5702,30 @@ button.kds-btn-success::after {
5596
5702
  gap: var(--kds-spacing-3);
5597
5703
  }
5598
5704
 
5599
- /* Brand logo row */
5705
+ /* Mobile: Cards full-width with rounded corners */
5706
+ @media (max-width: 767px) {
5707
+ .kds-payment-stage {
5708
+ padding-left: 0;
5709
+ padding-right: 0;
5710
+ }
5711
+
5712
+ .kds-payment-flow {
5713
+ max-width: none;
5714
+ padding: 0;
5715
+ }
5716
+
5717
+ /* Cards: rounded corners, full width within padded stage */
5718
+ .kds-card-elevated,
5719
+ .khipu-card-elevated {
5720
+ margin-left: 0;
5721
+ margin-right: 0;
5722
+ border-radius: var(--kds-radius-card);
5723
+ width: 100%;
5724
+ }
5725
+
5726
+ }
5727
+
5728
+ /* Brand logo row (external, outside cards) */
5600
5729
  .kds-brand-row {
5601
5730
  padding: 0 var(--kds-spacing-0-5);
5602
5731
  display: flex;
@@ -5607,6 +5736,27 @@ button.kds-btn-success::after {
5607
5736
  display: block;
5608
5737
  }
5609
5738
 
5739
+ /* Brand inner (inside invoice card, hidden on desktop) */
5740
+ .kds-brand-inner {
5741
+ display: none;
5742
+ align-items: center;
5743
+ margin-bottom: var(--kds-spacing-1);
5744
+ }
5745
+ .kds-brand-inner svg {
5746
+ height: 22px;
5747
+ display: block;
5748
+ }
5749
+
5750
+ /* Mobile: hide external brand, show inner brand */
5751
+ @media (max-width: 767px) {
5752
+ .kds-brand-row {
5753
+ display: none;
5754
+ }
5755
+ .kds-brand-inner {
5756
+ display: flex;
5757
+ }
5758
+ }
5759
+
5610
5760
  /* Screen transitions (multi-step flows) */
5611
5761
  .kds-screen {
5612
5762
  display: none;
@@ -5922,3 +6072,145 @@ button.kds-btn-success::after {
5922
6072
  .kds-bank-modal-empty.visible {
5923
6073
  display: block;
5924
6074
  }
6075
+ /* ========================================
6076
+ QR FEATURED BUTTON (.kds-qr-row)
6077
+ Featured payment option with gradient background
6078
+ and badge for highlighting quick payment methods
6079
+ ======================================== */
6080
+
6081
+ .kds-qr-row {
6082
+ display: flex;
6083
+ align-items: center;
6084
+ gap: var(--kds-spacing-1-75);
6085
+ background: var(--kds-qr-bg-gradient);
6086
+ border: 1px solid var(--kds-qr-border);
6087
+ border-radius: var(--kds-radius-card);
6088
+ padding: var(--kds-spacing-1-75);
6089
+ cursor: pointer;
6090
+ text-align: left;
6091
+ font: inherit;
6092
+ color: inherit;
6093
+ transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.05s ease;
6094
+ box-shadow: var(--kds-qr-shadow);
6095
+ position: relative;
6096
+ }
6097
+
6098
+ .kds-qr-row:hover {
6099
+ border-color: var(--kds-qr-border-hover);
6100
+ box-shadow: var(--kds-qr-shadow-hover);
6101
+ }
6102
+
6103
+ .kds-qr-row:focus-visible {
6104
+ outline: 2px solid var(--kds-qr-focus-ring);
6105
+ outline-offset: 2px;
6106
+ }
6107
+
6108
+ .kds-qr-row:active {
6109
+ transform: translateY(1px);
6110
+ }
6111
+
6112
+ /* QR Avatar - Icon container */
6113
+ .kds-qr-avatar {
6114
+ width: var(--kds-status-icon-size-sm);
6115
+ height: var(--kds-status-icon-size-sm);
6116
+ border-radius: var(--kds-qr-avatar-radius);
6117
+ background: var(--kds-qr-avatar-bg);
6118
+ display: grid;
6119
+ place-items: center;
6120
+ color: var(--kds-color-primary-main);
6121
+ flex: 0 0 auto;
6122
+ border: 1px solid var(--kds-qr-avatar-border);
6123
+ }
6124
+
6125
+ .kds-qr-avatar .material-symbols-outlined,
6126
+ .kds-qr-avatar .material-icons-round {
6127
+ font-size: 24px;
6128
+ }
6129
+
6130
+ /* QR Text content */
6131
+ .kds-qr-text {
6132
+ flex: 1;
6133
+ display: flex;
6134
+ flex-direction: column;
6135
+ gap: 2px;
6136
+ min-width: 0;
6137
+ }
6138
+
6139
+ .kds-qr-text .title {
6140
+ font-family: var(--kds-font-family-primary);
6141
+ font-weight: 600;
6142
+ font-size: var(--kds-font-size-sm);
6143
+ letter-spacing: -0.15px;
6144
+ color: var(--kds-color-text-primary);
6145
+ }
6146
+
6147
+ .kds-qr-text .sub {
6148
+ font-family: var(--kds-font-family-primary);
6149
+ font-size: var(--kds-font-size-caption);
6150
+ color: var(--kds-color-text-secondary);
6151
+ }
6152
+
6153
+ /* QR Badge - "Rápido" indicator */
6154
+ .kds-qr-badge {
6155
+ font-family: var(--kds-font-family-primary);
6156
+ font-weight: 600;
6157
+ font-size: 10px;
6158
+ letter-spacing: 0.4px;
6159
+ text-transform: uppercase;
6160
+ color: var(--kds-qr-badge-text);
6161
+ background: var(--kds-qr-badge-bg);
6162
+ padding: 3px 7px;
6163
+ border-radius: 9999px;
6164
+ }
6165
+
6166
+ /* Chevron icon */
6167
+ .kds-qr-row .material-symbols-outlined:last-child,
6168
+ .kds-qr-row .material-icons-round:last-child {
6169
+ color: var(--kds-color-primary-main);
6170
+ font-size: 20px;
6171
+ }
6172
+
6173
+ /* Mobile responsive: Ajustes para pantallas pequeñas */
6174
+ @media (max-width: 480px) {
6175
+ .kds-qr-row {
6176
+ gap: var(--kds-spacing-1-5);
6177
+ padding: var(--kds-spacing-1-5);
6178
+ }
6179
+
6180
+ .kds-qr-avatar {
6181
+ width: var(--kds-spacing-4-5);
6182
+ height: var(--kds-spacing-4-5);
6183
+ flex-shrink: 0;
6184
+ }
6185
+
6186
+ .kds-qr-avatar .material-symbols-outlined,
6187
+ .kds-qr-avatar .material-icons-round {
6188
+ font-size: var(--kds-font-size-lg);
6189
+ }
6190
+
6191
+ .kds-qr-text .title {
6192
+ font-size: var(--kds-font-size-xs);
6193
+ line-height: var(--kds-line-height-tight);
6194
+ }
6195
+
6196
+ .kds-qr-text .sub {
6197
+ font-size: var(--kds-font-size-caption);
6198
+ line-height: var(--kds-line-height-tight);
6199
+ overflow: hidden;
6200
+ text-overflow: ellipsis;
6201
+ white-space: nowrap;
6202
+ }
6203
+
6204
+ .kds-qr-badge {
6205
+ font-size: var(--kds-font-size-caption);
6206
+ padding: var(--kds-spacing-0-25) var(--kds-spacing-0-75);
6207
+ flex-shrink: 0;
6208
+ }
6209
+
6210
+ .kds-qr-row .material-symbols-outlined:last-child,
6211
+ .kds-qr-row .material-icons-round:last-child {
6212
+ font-size: var(--kds-font-size-base);
6213
+ flex-shrink: 0;
6214
+ }
6215
+ }
6216
+
@@ -1646,6 +1646,8 @@ function H(e){return e<0?-1:e===0?0:1}function nt(e,t,r){return(1-r)*e+r*t}funct
1646
1646
  * - Segmented tabs
1647
1647
  * - Bank modal (search and selection)
1648
1648
  * - Sticky invoice card (collapse on scroll)
1649
+ * - Brand inner (logo inside sticky card on mobile)
1650
+ * - Secure footer (moved inside last card per screen)
1649
1651
  */
1650
1652
 
1651
1653
  (function() {
@@ -1679,6 +1681,8 @@ function H(e){return e<0?-1:e===0?0:1}function nt(e,t,r){return(1-r)*e+r*t}funct
1679
1681
  initModals();
1680
1682
 
1681
1683
  // Initialize payment flow components
1684
+ initBrandInner();
1685
+ initSecureFooterInside();
1682
1686
  initExpandToggle();
1683
1687
  initCopyRow();
1684
1688
  initCountdown();
@@ -1742,6 +1746,62 @@ function H(e){return e<0?-1:e===0?0:1}function nt(e,t,r){return(1-r)*e+r*t}funct
1742
1746
  };
1743
1747
  }
1744
1748
 
1749
+ /**
1750
+ * Inject .kds-brand-inner logo into each .kds-invoice-sticky card
1751
+ * On mobile the external .kds-brand-row is hidden via CSS; the inner brand
1752
+ * keeps the logo visible inside the sticky card as the user scrolls.
1753
+ * Clones the SVG from the nearest preceding .kds-brand-row so the logo
1754
+ * markup is defined once in HTML (single source of truth).
1755
+ * @param {Element} root - Root element to scope queries (default: document)
1756
+ */
1757
+ function initBrandInner(root) {
1758
+ root = root || document;
1759
+ var cards = root.querySelectorAll('.kds-invoice-sticky');
1760
+
1761
+ cards.forEach(function(card) {
1762
+ // Skip if already injected
1763
+ if (card.querySelector('.kds-brand-inner')) return;
1764
+
1765
+ // Find the brand-row logo for this screen (sibling before the sticky wrap)
1766
+ var screen = card.closest('.kds-screen') || card.parentElement;
1767
+ var brandRow = screen ? screen.querySelector('.kds-brand-row') : null;
1768
+ var logoSource = brandRow ? brandRow.querySelector('svg, img') : null;
1769
+
1770
+ if (!logoSource) return;
1771
+
1772
+ var inner = document.createElement('div');
1773
+ inner.className = 'kds-brand-inner';
1774
+ inner.appendChild(logoSource.cloneNode(true));
1775
+ card.insertBefore(inner, card.firstChild);
1776
+ });
1777
+ }
1778
+
1779
+ /**
1780
+ * Move .kds-secure-footer inside the last card of each screen
1781
+ * In the design, the "Pago seguro procesado por Khipu" line lives
1782
+ * inside the bottom card, not as a standalone element.
1783
+ * @param {Element} root - Root element to scope queries (default: document)
1784
+ */
1785
+ function initSecureFooterInside(root) {
1786
+ root = root || document;
1787
+ var screens = root.querySelectorAll('.kds-screen');
1788
+
1789
+ screens.forEach(function(screen) {
1790
+ var footer = screen.querySelector('.kds-secure-footer');
1791
+ if (!footer) return;
1792
+
1793
+ // Find the last non-sticky card in the screen
1794
+ var cards = screen.querySelectorAll('.kds-card-elevated:not(.kds-invoice-sticky)');
1795
+ if (!cards.length) return;
1796
+
1797
+ var lastCard = cards[cards.length - 1];
1798
+
1799
+ // Move (not clone) the footer inside the last card
1800
+ footer.classList.add('inside');
1801
+ lastCard.appendChild(footer);
1802
+ });
1803
+ }
1804
+
1745
1805
  /**
1746
1806
  * Initialize expand toggle panels
1747
1807
  * Delegated click on [data-expand-toggle] toggles aria-expanded and .open on target panel
@@ -1999,41 +2059,94 @@ function H(e){return e<0?-1:e===0?0:1}function nt(e,t,r){return(1-r)*e+r*t}funct
1999
2059
  }
2000
2060
 
2001
2061
  /**
2002
- * Initialize sticky invoice card collapse on scroll
2003
- * Toggles .collapsed class on .kds-invoice-sticky when user scrolls past threshold
2062
+ * Initialize sticky invoice card progressive collapse on scroll
2063
+ * MOBILE ONLY - Desktop mantiene cajitas normales
2064
+ * Uses scroll-linked animation (0-150px) for smooth collapse/expand
2065
+ * Updates CSS custom property --collapse-progress (0 to 1) for GPU-accelerated animations
2004
2066
  * Works with multiple screens - targets sticky element in currently active screen
2005
- * Uses hysteresis (different thresholds for collapse/expand) to prevent oscillation
2067
+ * Safari-compatible: uses native CSS custom properties, calc(), and requestAnimationFrame
2006
2068
  * @param {Element} root - Root element to scope queries (default: document)
2007
2069
  */
2008
2070
  function initStickyInvoice(root) {
2009
2071
  root = root || document;
2010
2072
 
2011
- var collapseAt = 60; // px scrolled to collapse
2012
- var expandAt = 20; // px scrolled to expand (lower = hysteresis)
2013
- var collapsedStates = {}; // Track collapsed state per screen
2073
+ // Progressive collapse range: 0px (expanded) to 150px (collapsed)
2074
+ var COLLAPSE_START = 0;
2075
+ var COLLAPSE_END = 150;
2076
+
2077
+ var lastScrollY = 0;
2078
+ var ticking = false;
2079
+ var MOBILE_BREAKPOINT = 768;
2080
+
2081
+ function isMobile() {
2082
+ return window.innerWidth < MOBILE_BREAKPOINT;
2083
+ }
2014
2084
 
2015
2085
  function onScroll() {
2016
- // Find sticky element in currently active screen
2086
+ // Find active screen first (needed for both desktop and mobile)
2017
2087
  var activeScreen = root.querySelector('.kds-screen.active');
2018
2088
  if (!activeScreen) return;
2019
2089
 
2020
2090
  var sticky = activeScreen.querySelector('.kds-invoice-sticky');
2021
2091
  if (!sticky) return;
2022
2092
 
2023
- var screenId = activeScreen.id || 'default';
2024
- var isCollapsed = collapsedStates[screenId] || false;
2025
- var scrollY = window.scrollY || window.pageYOffset;
2026
-
2027
- if (!isCollapsed && scrollY > collapseAt) {
2028
- sticky.classList.add('collapsed');
2029
- collapsedStates[screenId] = true;
2030
- } else if (isCollapsed && scrollY < expandAt) {
2031
- sticky.classList.remove('collapsed');
2032
- collapsedStates[screenId] = false;
2093
+ // Desktop: Sin animación, siempre expandido
2094
+ if (!isMobile()) {
2095
+ activeScreen.style.removeProperty('--collapse-progress');
2096
+ activeScreen.style.removeProperty('--collapse-collapsible-h');
2097
+ return;
2033
2098
  }
2099
+
2100
+ // Mobile: Animación progresiva scroll-linked
2101
+ if (ticking) return;
2102
+ ticking = true;
2103
+
2104
+ requestAnimationFrame(function() {
2105
+ ticking = false;
2106
+
2107
+ // Re-query inside rAF (screen may have changed)
2108
+ var currentScreen = root.querySelector('.kds-screen.active');
2109
+ if (!currentScreen) return;
2110
+ var currentSticky = currentScreen.querySelector('.kds-invoice-sticky');
2111
+ if (!currentSticky) return;
2112
+
2113
+ var scrollY = window.scrollY || window.pageYOffset;
2114
+
2115
+ // Calcular progress: 0 (inicio) a 1 (totalmente colapsado)
2116
+ var progress = Math.min(Math.max((scrollY - COLLAPSE_START) / (COLLAPSE_END - COLLAPSE_START), 0), 1);
2117
+
2118
+ // Cache collapsible height once for clip-path + translateY calculations
2119
+ // Set on screen so both sticky card and its siblings can access the variable
2120
+ if (!currentScreen.style.getPropertyValue('--collapse-collapsible-h')) {
2121
+ var collapsible = currentSticky.querySelector('.kds-invoice-collapsible');
2122
+ if (collapsible) {
2123
+ currentScreen.style.setProperty('--collapse-collapsible-h', collapsible.offsetHeight + 'px');
2124
+ }
2125
+ }
2126
+
2127
+ // Single DOM write per frame — set on screen (parent) so it cascades to sticky + siblings
2128
+ currentScreen.style.setProperty('--collapse-progress', progress);
2129
+
2130
+ lastScrollY = scrollY;
2131
+ });
2034
2132
  }
2035
2133
 
2134
+ // Handle resize - cleanup on desktop
2135
+ window.addEventListener('resize', function() {
2136
+ if (!isMobile()) {
2137
+ // Desktop: cleanup all collapsed state from screen elements
2138
+ var screens = root.querySelectorAll('.kds-screen');
2139
+ screens.forEach(function(screen) {
2140
+ screen.style.removeProperty('--collapse-progress');
2141
+ screen.style.removeProperty('--collapse-collapsible-h');
2142
+ });
2143
+ }
2144
+ });
2145
+
2036
2146
  window.addEventListener('scroll', onScroll, { passive: true });
2147
+
2148
+ // Init: Set initial state on page load
2149
+ onScroll();
2037
2150
  }
2038
2151
 
2039
2152
  /**
@@ -2086,6 +2199,8 @@ function H(e){return e<0?-1:e===0?0:1}function nt(e,t,r){return(1-r)*e+r*t}funct
2086
2199
  }
2087
2200
  window.Khipu.showSnackbar = showSnackbar;
2088
2201
  window.Khipu.closeModal = window.closeModal;
2202
+ window.Khipu.initBrandInner = initBrandInner;
2203
+ window.Khipu.initSecureFooterInside = initSecureFooterInside;
2089
2204
  window.Khipu.initExpandToggle = initExpandToggle;
2090
2205
  window.Khipu.initCopyRow = initCopyRow;
2091
2206
  window.Khipu.initCountdown = initCountdown;