ultimate-jekyll-manager 0.0.96 → 0.0.98

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.
Files changed (71) hide show
  1. package/.playwright-mcp/page-2025-10-22T19-11-27-666Z.png +0 -0
  2. package/.playwright-mcp/page-2025-10-22T19-11-57-357Z.png +0 -0
  3. package/CLAUDE.md +42 -0
  4. package/README.md +66 -0
  5. package/_backup/checkout copy 2.html +392 -0
  6. package/_backup/checkout copy 3.html +376 -0
  7. package/_backup/checkout copy 4.html +365 -0
  8. package/_backup/checkout copy.html +331 -0
  9. package/_backup/checkout-semi.html +331 -0
  10. package/_backup/cover-old.html +55 -0
  11. package/dist/assets/css/core/bindings.scss +7 -2
  12. package/dist/assets/css/core/utilities.scss +9 -1
  13. package/dist/assets/css/pages/payment/checkout/index.scss +52 -7
  14. package/dist/assets/js/core/complete.js +56 -0
  15. package/dist/assets/js/core/initialize.js +11 -0
  16. package/dist/assets/js/pages/app/index.js +82 -43
  17. package/dist/assets/js/pages/download/index.js +0 -6
  18. package/dist/assets/js/pages/payment/checkout/index.js +58 -52
  19. package/dist/assets/js/pages/payment/checkout/modules/discount-bindings.js +51 -0
  20. package/dist/assets/js/pages/payment/checkout/modules/pricing.js +55 -30
  21. package/dist/assets/js/pages/payment/checkout/modules/state.js +68 -14
  22. package/dist/assets/js/pages/payment/checkout/modules/ui-bindings.js +160 -0
  23. package/dist/assets/js/pages/payment/checkout/modules/ui.js +27 -42
  24. package/dist/assets/js/pages/payment/confirmation/index.js +58 -53
  25. package/dist/assets/js/pages/payment/confirmation/modules/bindings.js +28 -0
  26. package/dist/assets/js/pages/payment/confirmation/modules/state.js +19 -0
  27. package/dist/assets/js/ultimate-jekyll-manager.js +6 -2
  28. package/dist/assets/themes/classy/css/base/_spacing.scss +27 -0
  29. package/dist/defaults/dist/{redirects/authentication/helpers/well-known-change-password.html → .well-known/change-password.html} +4 -1
  30. package/dist/defaults/dist/.well-known/security.txt +15 -0
  31. package/dist/defaults/dist/_includes/core/body.html +31 -0
  32. package/dist/defaults/dist/_includes/core/foot.html +35 -0
  33. package/dist/defaults/dist/_layouts/blueprint/app.html +1 -1
  34. package/dist/defaults/dist/_layouts/blueprint/payment/checkout.html +6 -1
  35. package/dist/defaults/dist/_layouts/blueprint/payment/confirmation.html +6 -1
  36. package/dist/defaults/dist/_layouts/core/root.html +1 -0
  37. package/dist/defaults/dist/_layouts/modules/utilities/redirect.html +42 -31
  38. package/dist/defaults/dist/_layouts/themes/classy/backend/core/base.html +0 -35
  39. package/dist/defaults/dist/_layouts/themes/classy/frontend/core/base.html +0 -34
  40. package/dist/defaults/dist/_layouts/themes/classy/frontend/core/cover.html +6 -9
  41. package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/404.html +35 -44
  42. package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/account/index.html +1 -1
  43. package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/app.html +83 -0
  44. package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/auth/oauth2.html +42 -44
  45. package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/auth/reset.html +57 -59
  46. package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/auth/signin.html +67 -69
  47. package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/auth/signup.html +76 -78
  48. package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/download.html +1 -4
  49. package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/payment/checkout.html +291 -240
  50. package/dist/defaults/dist/_layouts/themes/classy/frontend/pages/payment/confirmation.html +43 -47
  51. package/dist/defaults/dist/humans.txt +25 -0
  52. package/dist/defaults/dist/opensearch.xml +26 -0
  53. package/dist/defaults/dist/pages/test/account/dashboard.html +3 -0
  54. package/dist/defaults/dist/pages/test/index.md +2 -2
  55. package/dist/defaults/dist/pages/test/libraries/ads.html +3 -0
  56. package/dist/defaults/dist/pages/test/libraries/bootstrap-components.html +11 -2
  57. package/dist/defaults/dist/pages/test/libraries/cover.html +47 -0
  58. package/dist/defaults/dist/pages/test/libraries/error.html +3 -0
  59. package/dist/defaults/dist/pages/test/libraries/lazy-loading.html +3 -0
  60. package/dist/defaults/dist/pages/test/redirect/external.md +3 -0
  61. package/dist/defaults/dist/pages/test/redirect/internal.md +3 -0
  62. package/dist/defaults/dist/pages/test/translation/index.md +2 -2
  63. package/dist/defaults/dist/redirects/authentication/helpers/change-password.html +1 -1
  64. package/dist/defaults/dist/redirects/authentication/helpers/forgot.html +1 -1
  65. package/dist/defaults/dist/redirects/authentication/helpers/recover.html +1 -1
  66. package/dist/defaults/dist/redirects/authentication/helpers/reset-password.html +1 -1
  67. package/dist/defaults/dist/sitemap.html +112 -28
  68. package/dist/defaults/dist/sitemap.xml +6 -1
  69. package/firebase-debug.log +388 -0
  70. package/package.json +1 -1
  71. package/dist/assets/js/core/init.js +0 -36
@@ -0,0 +1,55 @@
1
+ ---
2
+ ### FULLSCREEN LAYOUT ###
3
+ layout: themes/[ site.theme.id ]/frontend/core/base
4
+
5
+ ### FULLSCREEN PAGES ###
6
+ theme:
7
+ nav:
8
+ enabled: false
9
+ main:
10
+ class: "d-flex align-items-{{ page.resolved.theme.main.align | default: 'center' }} justify-content-center_ pt-0"
11
+ footer:
12
+ enabled: false
13
+ ### CORE CONFIG ###
14
+ # core:
15
+ # cover:
16
+ # class: "align-top" # USED TO HAVE {{ page.resolved.theme.core.cover.class }} IN THE TOP LEVEL CLASS WITH w-100
17
+ ---
18
+
19
+ <!-- Critical inline CSS to prevent FOUC on cover pages -->
20
+ <style>
21
+ main.d-flex {
22
+ display: flex !important;
23
+ min-height: calc(100vh);
24
+ }
25
+
26
+ main.d-flex.align-items-center {
27
+ align-items: center !important;
28
+ }
29
+
30
+ main.d-flex.justify-content-center {
31
+ justify-content: center !important;
32
+ }
33
+ </style>
34
+
35
+ <!-- <div class="w-100">
36
+ <div class="row justify-content-center">
37
+ <div class="col-12 text-center">
38
+ </div>
39
+ </div>
40
+ </div> -->
41
+
42
+ <!-- <div class="w-100">
43
+ <div class="row justify-content-center">
44
+ <div class="col-12 text-center px-0">
45
+ </div>
46
+ </div>
47
+ </div> -->
48
+
49
+ <div class="w-100">
50
+ <div class="d-flex justify-content-center">
51
+ {{ content | uj_content_format }}
52
+ </div>
53
+ </div>
54
+
55
+
@@ -24,6 +24,11 @@
24
24
 
25
25
  // Subtle color overlay for text elements
26
26
  color: transparent !important;
27
+ * {
28
+ // color: transparent !important;
29
+ // z-index: -1 !important;
30
+ opacity: 0 !important;
31
+ }
27
32
 
28
33
  // Prevent text selection during loading
29
34
  user-select: none;
@@ -31,8 +36,8 @@
31
36
 
32
37
  // Minimum dimensions for empty elements
33
38
  min-width: 50px;
34
- min-height: 1em;
35
- display: inline-block;
39
+ min-height: 1lh;
40
+ // display: inline-block;
36
41
  }
37
42
 
38
43
  // Dark theme overrides
@@ -12,7 +12,9 @@
12
12
 
13
13
  .disabled,
14
14
  [disabled],
15
- :disabled {
15
+ :disabled,
16
+ [data-page-loading] button.btn,
17
+ [data-page-loading] .btn-action {
16
18
  cursor: not-allowed !important;
17
19
  pointer-events: all !important;
18
20
 
@@ -23,6 +25,12 @@
23
25
  }
24
26
  }
25
27
 
28
+ [data-page-loading] button.btn,
29
+ [data-page-loading] .btn-action {
30
+ opacity: var(--bs-btn-disabled-opacity);
31
+ color: var(--bs-btn-disabled-color);
32
+ }
33
+
26
34
  // Prevent pointer events on all children inside buttons and links
27
35
  // This helps fix event bubbling issues for click listeners
28
36
  button *, a * {
@@ -1,15 +1,21 @@
1
- // Checkout Page Styles - Minimal custom CSS
1
+ // Checkout Page Styles
2
2
 
3
- // Order summary sticky behavior (can't be done with Bootstrap)
3
+ // ============================================
4
+ // Order Summary Sticky Positioning
5
+ // ============================================
4
6
  .order-summary-sticky {
5
7
  position: sticky;
6
8
  top: 2rem;
7
9
  }
8
10
 
9
- // Product thumbnail size (specific dimension needed)
11
+ // ============================================
12
+ // Product Thumbnail
13
+ // ============================================
10
14
  .product-thumbnail {
11
15
  width: 60px;
12
16
  height: 60px;
17
+ min-width: 60px;
18
+ min-height: 60px;
13
19
  }
14
20
 
15
21
  // Save badge positioning adjustment
@@ -62,8 +68,47 @@
62
68
  #pay-with-paypal img {
63
69
  filter: brightness(0.9);
64
70
  }
65
- // .btn-check:checked + .btn {
66
- // color: var(--bs-btn-active-color);
67
- // background-color: var(--bs-btn-active-bg);
68
- // border-color: var(--bs-btn-active-border-color);
71
+
72
+ // ============================================
73
+ // Loading States Enhancement for Bindings
74
+ // ============================================
75
+ // Smooth transitions when bindings update
76
+ // [data-wm-bind] {
77
+ // transition: opacity 0.3s ease-in-out;
78
+ // }
79
+
80
+ // Animation for elements appearing via bindings
81
+ // [data-wm-bind*="@show"]:not([hidden]) {
82
+ // animation: fadeIn 0.3s ease-in-out;
83
+ // }
84
+
85
+ // @keyframes fadeIn {
86
+ // from {
87
+ // opacity: 0;
88
+ // transform: translateY(-5px);
89
+ // }
90
+ // to {
91
+ // opacity: 1;
92
+ // transform: translateY(0);
93
+ // }
94
+ // }
95
+
96
+ // ============================================
97
+ // Discount Messages Animation
98
+ // ============================================
99
+ // [data-wm-bind*="checkout.discount"] {
100
+ // &:not([hidden]) {
101
+ // animation: slideDown 0.2s ease-out;
102
+ // }
103
+ // }
104
+
105
+ // @keyframes slideDown {
106
+ // from {
107
+ // opacity: 0;
108
+ // max-height: 0;
109
+ // }
110
+ // to {
111
+ // opacity: 1;
112
+ // max-height: 50px;
113
+ // }
69
114
  // }
@@ -0,0 +1,56 @@
1
+ // Page Loader Module - Handles page loading state indicator
2
+ export default function (Manager, options) {
3
+ // Shortcuts
4
+ const { webManager } = Manager;
5
+ let removed = false;
6
+
7
+ // Log
8
+ console.log('Running complete()');
9
+
10
+ // Remove page loading state indicator
11
+ const removeLoadingState = (source) => {
12
+ // Check if already removed
13
+ if (removed) return;
14
+ removed = true;
15
+
16
+ // Log
17
+ console.log(`Removing page loading state (source: ${source})`);
18
+
19
+ // Use requestAnimationFrame for smooth transition
20
+ requestAnimationFrame(() => {
21
+ document.documentElement.removeAttribute('data-page-loading');
22
+ document.documentElement.setAttribute('aria-busy', 'false');
23
+ });
24
+ };
25
+
26
+ // Check document ready state
27
+ console.log('Document readyState:', document.readyState);
28
+
29
+ // For interactive state, we need to wait a bit for resources
30
+ // Since window.load is unreliable with async scripts, use a hybrid approach
31
+
32
+ // Immediately remove if already complete
33
+ if (document.readyState === 'complete') {
34
+ removeLoadingState('Complete');
35
+ return;
36
+ }
37
+
38
+ // Strategy 1: Try window load event (might not fire)
39
+ window.addEventListener('load', () => {
40
+ removeLoadingState('Load');
41
+ }, { once: true });
42
+
43
+ // Strategy 2: Poll for complete state
44
+ const pollInterval = setInterval(() => {
45
+ if (document.readyState === 'complete') {
46
+ clearInterval(pollInterval);
47
+ removeLoadingState('Polling');
48
+ }
49
+ }, 50);
50
+
51
+ // Strategy 3: Timeout fallback (max 3 seconds)
52
+ setTimeout(() => {
53
+ clearInterval(pollInterval);
54
+ removeLoadingState('Timeout');
55
+ }, 3000);
56
+ }
@@ -0,0 +1,11 @@
1
+ // Page Loader Module - Initialization
2
+ export default function (Manager, options) {
3
+ // Shortcuts
4
+ const { webManager } = Manager;
5
+
6
+ // Log
7
+ console.log('Running initialize()');
8
+
9
+ // The click prevention logic has been moved to an inline script in base.html
10
+ // for immediate execution to prevent race conditions
11
+ }
@@ -2,7 +2,6 @@
2
2
  let webManager = null;
3
3
 
4
4
  // Global variables
5
- let hasLostFocus = false;
6
5
  let launchTimeout;
7
6
  let errorTimeout;
8
7
 
@@ -10,6 +9,7 @@ let errorTimeout;
10
9
  let $launchButton;
11
10
  let $downloadButton;
12
11
  let $errorAlert;
12
+ let $spinner;
13
13
 
14
14
  // Module
15
15
  export default (Manager, options) => {
@@ -33,33 +33,16 @@ function setupPage() {
33
33
  $launchButton = document.getElementById('launch-button');
34
34
  $downloadButton = document.getElementById('download-button');
35
35
  $errorAlert = document.getElementById('error-alert');
36
- const urlParams = window.location.search;
36
+ $spinner = document.querySelector('.spinner-border');
37
37
 
38
- // Enable both buttons
39
- [$launchButton, $downloadButton].forEach(button => {
40
- button.removeAttribute('disabled');
41
- button.classList.remove('disabled');
42
- });
38
+ // Hide spinner initially (will be shown again on launch)
39
+ $spinner.setAttribute('hidden', true);
43
40
 
44
- // window.addEventListener('blur', () => {
45
- // hasLostFocus = true;
46
- // if (launchTimeout) {
47
- // clearTimeout(launchTimeout);
48
- // }
49
- // });
50
-
51
- // window.addEventListener('focus', () => {
52
- // if (hasLostFocus) {
53
- // window.close();
54
- // }
55
- // });
56
-
57
- // Build app URL with parameters
58
- const baseUrl = $launchButton.getAttribute('href');
59
- const appUrl = `${baseUrl}${urlParams}`;
41
+ // Build deep link URL
42
+ const appUrl = buildDeepLinkUrl();
60
43
  $launchButton.setAttribute('href', appUrl);
61
44
 
62
- // Handle launch button click
45
+ // Launch app immediately on page load
63
46
  launchApp(appUrl);
64
47
 
65
48
  // Handle launch button click
@@ -67,32 +50,88 @@ function setupPage() {
67
50
  event.preventDefault();
68
51
  launchApp(appUrl);
69
52
  });
53
+ }
54
+
55
+ // Build deep link URL with path extraction and query passthrough
56
+ function buildDeepLinkUrl() {
57
+ const baseUrl = $launchButton.getAttribute('href'); // e.g., "myapp://"
58
+ const urlParams = new URLSearchParams(window.location.search);
59
+
60
+ // Define the special parameter name for the app path
61
+ const PATH_PARAM_NAMES = ['path']
70
62
 
71
- // Close window after 5 seconds regardless
72
- // launchTimeout = setTimeout(() => {
73
- // if (!hasLostFocus) {
74
- // console.log('App did not launch, closing window');
75
- // }
76
- // window.close();
77
- // }, 5000)
63
+ // Find which path parameter is being used (if any)
64
+ let appPath = '';
65
+ let pathParamUsed = null;
66
+
67
+ for (const paramName of PATH_PARAM_NAMES) {
68
+ const value = urlParams.get(paramName);
69
+ if (value !== null) {
70
+ appPath = value;
71
+ pathParamUsed = paramName;
72
+ break;
73
+ }
74
+ }
75
+
76
+ // Build the query string for the deep link
77
+ // This includes all parameters EXCEPT the special path parameter
78
+ const deepLinkParams = new URLSearchParams();
79
+
80
+ for (const [key, value] of urlParams.entries()) {
81
+ // Skip the special path parameter
82
+ if (!PATH_PARAM_NAMES.includes(key)) {
83
+ deepLinkParams.append(key, value);
84
+ }
85
+ }
86
+
87
+ // Construct the final deep link URL
88
+ // Remove leading slash from path if present to avoid double slashes
89
+ const cleanPath = appPath.startsWith('/') ? appPath.substring(1) : appPath;
90
+
91
+ // Build the final URL
92
+ let finalUrl = baseUrl;
93
+ if (cleanPath) {
94
+ finalUrl += cleanPath;
95
+ }
96
+
97
+ // Add query parameters if there are any
98
+ const queryString = deepLinkParams.toString();
99
+ if (queryString) {
100
+ // Add ? or & depending on whether the path already has query params
101
+ const separator = cleanPath.includes('?') ? '&' : '?';
102
+ finalUrl += separator + queryString;
103
+ }
104
+
105
+ console.log('Deep link constructed:', {
106
+ baseUrl: baseUrl,
107
+ pathParam: pathParamUsed,
108
+ appPath: appPath,
109
+ passedParams: queryString,
110
+ finalUrl: finalUrl
111
+ });
112
+
113
+ return finalUrl;
78
114
  }
79
115
 
80
116
  function launchApp(appUrl) {
81
- // Attempt to launch app
82
- const launchTime = Date.now();
83
- window.location.href = appUrl;
117
+ // Show spinner when attempting to launch
118
+ $spinner.removeAttribute('hidden');
84
119
 
85
- // Clear any previous error timeout
120
+ // Clear any previous error timeout and hide error alert
86
121
  clearTimeout(errorTimeout);
87
122
  $errorAlert.setAttribute('hidden', true);
88
123
 
89
- // Check if deep link failed after 2 seconds
124
+ // Attempt to launch app
125
+ window.location.href = appUrl;
126
+
127
+ // Check if deep link failed after a delay
128
+ // Since browser prompts don't trigger blur, we can't reliably detect if app launched
129
+ // We'll show download options after a delay - user can dismiss if app actually launched
90
130
  errorTimeout = setTimeout(() => {
91
- if (hasLostFocus) {
92
- console.log('Deep link launched app successfully');
93
- } else {
94
- console.log('Deep link failed to launch app');
95
- $errorAlert.removeAttribute('hidden');
96
- }
97
- }, 1000);
131
+ // Hide spinner
132
+ $spinner.setAttribute('hidden', true);
133
+
134
+ // Show download options
135
+ $errorAlert.removeAttribute('hidden');
136
+ }, 2000);
98
137
  }
@@ -48,12 +48,6 @@ function setupPlatformDetection() {
48
48
 
49
49
  // Show loading state initially, then switch to detected platform
50
50
  setTimeout(() => {
51
- // Enable all platform buttons
52
- const $platformButtons = document.querySelectorAll('.platform-btn');
53
- $platformButtons.forEach($btn => {
54
- $btn.disabled = false;
55
- });
56
-
57
51
  // Activate the detected platform tab using Bootstrap's tab API
58
52
  const $detectedTab = document.querySelector(`#${detectedPlatform}-tab`);
59
53
  if ($detectedTab) {
@@ -1,21 +1,22 @@
1
1
  // Libraries
2
- import { state } from './modules/state.js';
2
+ import { raw } from './modules/state.js';
3
3
  import { fetchProductDetails, fetchTrialEligibility, warmupServer } from './modules/api.js';
4
4
  import { initializeRecaptcha } from './modules/recaptcha.js';
5
5
  import {
6
6
  updateAllUI,
7
7
  handleBillingCycleChange,
8
8
  showError,
9
- hidePreloader,
10
- updatePaymentButtonVisibility
11
- } from './modules/ui.js';
12
- import { applyDiscountCode, autoApplyWelcomeCoupon } from './modules/discount.js';
9
+ updatePaymentButtonVisibility,
10
+ initializeCheckoutUI
11
+ } from './modules/ui-bindings.js';
12
+ import { applyDiscountCode, autoApplyWelcomeCoupon } from './modules/discount-bindings.js';
13
13
  import { paymentManager } from './modules/processors-main.js';
14
14
  import { FormManager } from '__main_assets__/js/libs/form-manager.js';
15
15
  import {
16
16
  generateCheckoutId,
17
17
  buildPaymentIntentData,
18
18
  } from './modules/session.js';
19
+ import { calculatePrices } from './modules/pricing.js';
19
20
  let webManager = null;
20
21
  let formManager = null;
21
22
 
@@ -134,7 +135,7 @@ function setupEventListeners() {
134
135
 
135
136
  // Handle billing cycle changes
136
137
  if (fieldName === 'billing-cycle') {
137
- handleBillingCycleChange(fieldValue);
138
+ handleBillingCycleChange(fieldValue, webManager);
138
139
  }
139
140
  });
140
141
 
@@ -143,7 +144,7 @@ function setupEventListeners() {
143
144
  const { action } = event.detail;
144
145
 
145
146
  if (action === 'apply-discount') {
146
- applyDiscountCode();
147
+ applyDiscountCode(webManager);
147
148
  }
148
149
  });
149
150
 
@@ -165,15 +166,15 @@ function setupEventListeners() {
165
166
  return;
166
167
  }
167
168
 
168
- // Set payment method in state
169
- state.paymentMethod = paymentMethod;
169
+ // Set payment method in raw
170
+ raw.paymentMethod = paymentMethod;
170
171
 
171
172
  // Track add_payment_info event when payment method is selected
172
- const basePrice = state.isSubscription
173
- ? (state.billingCycle === 'monthly' ? state.product.price_monthly : state.product.price_annually)
174
- : state.product.price;
173
+ const basePrice = raw.product.is_subscription
174
+ ? (raw.billingCycle === 'monthly' ? raw.product.price_monthly : raw.product.price_annually)
175
+ : raw.product.price;
175
176
 
176
- trackAddPaymentInfo(state.product, state.total || basePrice, state.billingCycle, paymentMethod);
177
+ trackAddPaymentInfo(raw.product, basePrice, raw.billingCycle, paymentMethod);
177
178
  console.log('Tracked add_payment_info event:', paymentMethod);
178
179
 
179
180
  // Process the payment
@@ -203,8 +204,8 @@ async function completePurchase() {
203
204
  // Get form data from FormManager
204
205
  const formData = formManager.getData();
205
206
 
206
- // Store form data in state
207
- state.formData = formData;
207
+ // Store form data in raw
208
+ raw.formData = formData;
208
209
 
209
210
  // Wait 1 second to simulate processing time
210
211
  if (webManager.isDevelopment()) {
@@ -212,7 +213,7 @@ async function completePurchase() {
212
213
  }
213
214
 
214
215
  // Add custom validation if needed
215
- if (!state.paymentMethod) {
216
+ if (!raw.paymentMethod) {
216
217
  throw new Error('Please select a payment method');
217
218
  }
218
219
 
@@ -222,14 +223,17 @@ async function completePurchase() {
222
223
  // Log the structured payment data
223
224
  console.log('🟢 Payment intent data:', paymentIntentData);
224
225
 
226
+ // Calculate pricing for analytics
227
+ const prices = calculatePrices(raw);
228
+
225
229
  // Store pre-payment analytics data
226
230
  const analyticsData = {
227
- transaction_id: state.checkoutId,
228
- value: state.total,
231
+ transaction_id: raw.checkoutId,
232
+ value: prices.total,
229
233
  currency: 'USD',
230
234
  items: [{
231
- item_name: state.product.name,
232
- price: state.total,
235
+ item_name: raw.product.name,
236
+ price: prices.total,
233
237
  quantity: 1
234
238
  }]
235
239
  };
@@ -241,15 +245,15 @@ async function completePurchase() {
241
245
 
242
246
  // Store order data for confirmation page (legacy format for now)
243
247
  const orderData = {
244
- orderId: state.checkoutId,
245
- product: state.product.name,
246
- productId: state.product.id,
247
- total: state.total,
248
- subtotal: state.subtotal,
249
- discountPercent: state.discountPercent,
250
- billingCycle: state.billingCycle,
251
- hasFreeTrial: state.hasFreeTrial && state.isSubscription,
252
- paymentMethod: state.paymentMethod,
248
+ orderId: raw.checkoutId,
249
+ product: raw.product.name,
250
+ productId: raw.product.id,
251
+ total: prices.total,
252
+ subtotal: prices.subtotal,
253
+ discountPercent: raw.discountPercent,
254
+ billingCycle: raw.billingCycle,
255
+ hasFreeTrial: raw.product.has_free_trial,
256
+ paymentMethod: raw.paymentMethod,
253
257
  email: webManager.auth().getUser().email,
254
258
  timestamp: new Date().toISOString(),
255
259
  formData: formData
@@ -257,11 +261,11 @@ async function completePurchase() {
257
261
 
258
262
  sessionStorage.setItem('pendingOrder', JSON.stringify(orderData));
259
263
 
260
- // Store the payment intent data in state for processors to use
261
- state.paymentIntentData = paymentIntentData;
264
+ // Store the payment intent data in raw for processors to use
265
+ raw.paymentIntentData = paymentIntentData;
262
266
 
263
267
  // Process payment with selected processor (will redirect)
264
- await paymentManager.processPayment(state.paymentMethod);
268
+ await paymentManager.processPayment(raw.paymentMethod);
265
269
 
266
270
  // This code won't run if redirect is successful
267
271
  // If we get here, something went wrong
@@ -283,9 +287,12 @@ async function completePurchase() {
283
287
  // Initialize checkout with parallel API calls
284
288
  async function initializeCheckout() {
285
289
  try {
290
+ // Initialize UI with loading states (replaces fullscreen loader)
291
+ initializeCheckoutUI();
292
+
286
293
  // Generate or retrieve checkout session ID
287
- state.checkoutId = generateCheckoutId();
288
- console.log('Checkout session ID:', state.checkoutId);
294
+ raw.checkoutId = generateCheckoutId();
295
+ console.log('Checkout session ID:', raw.checkoutId);
289
296
 
290
297
  // Get product ID from URL params
291
298
  const urlParams = new URLSearchParams(window.location.search);
@@ -318,9 +325,7 @@ async function initializeCheckout() {
318
325
  }
319
326
 
320
327
  // Set product data
321
- state.product = productData.value;
322
- state.isSubscription = state.product.is_subscription;
323
- state.hasFreeTrial = state.product.has_free_trial;
328
+ raw.product = productData.value;
324
329
 
325
330
  // Create mutable trial eligibility result for testing
326
331
  let trialEligibilityResult = trialEligible;
@@ -336,12 +341,12 @@ async function initializeCheckout() {
336
341
 
337
342
  // Apply trial eligibility with server/test response
338
343
  if (trialEligibilityResult.status === 'fulfilled') {
339
- state.hasFreeTrial = trialEligibilityResult.value && state.product.has_free_trial;
344
+ raw.product.has_free_trial = trialEligibilityResult.value && raw.product.has_free_trial;
340
345
  }
341
346
 
342
347
  // Initialize payment processors with API keys
343
- if (state.apiKeys) {
344
- paymentManager.initialize(state.apiKeys, webManager);
348
+ if (raw.apiKeys) {
349
+ paymentManager.initialize(raw.apiKeys, webManager);
345
350
  }
346
351
 
347
352
  // Update payment button visibility based on available processors
@@ -356,7 +361,7 @@ async function initializeCheckout() {
356
361
 
357
362
  if (availableMethods.length === 0) {
358
363
  console.error('No payment methods available! Check API keys configuration.');
359
- showError('No payment methods are currently available. Please contact support for assistance.');
364
+ showError('No payment methods are currently available. Please contact support for assistance.', webManager);
360
365
  return; // Stop initialization since we can't proceed without payment methods
361
366
  }
362
367
 
@@ -369,12 +374,12 @@ async function initializeCheckout() {
369
374
 
370
375
  // Set billing cycle from URL parameter (before UI updates)
371
376
  if (frequency === 'monthly' || frequency === 'annually') {
372
- state.billingCycle = frequency;
377
+ raw.billingCycle = frequency;
373
378
  console.log('Setting billing cycle from URL:', frequency);
374
379
  }
375
380
 
376
381
  // Update UI with product details
377
- updateAllUI();
382
+ updateAllUI(webManager);
378
383
 
379
384
  // Set up event listeners and FormManager
380
385
  setupEventListeners();
@@ -383,23 +388,24 @@ async function initializeCheckout() {
383
388
  formManager.setFormState('ready');
384
389
 
385
390
  // Track begin_checkout event on page load
386
- const basePrice = state.isSubscription
387
- ? (state.billingCycle === 'monthly' ? state.product.price_monthly : state.product.price_annually)
388
- : state.product.price;
391
+ const basePrice = raw.product.is_subscription
392
+ ? (raw.billingCycle === 'monthly' ? raw.product.price_monthly : raw.product.price_annually)
393
+ : raw.product.price;
389
394
 
390
- trackBeginCheckout(state.product, basePrice, state.billingCycle);
391
- console.log('Tracked begin_checkout event for:', state.product.id);
395
+ trackBeginCheckout(raw.product, basePrice, raw.billingCycle);
396
+ console.log('Tracked begin_checkout event for:', raw.product.id);
392
397
 
393
398
  // Auto-apply welcome coupon
394
- autoApplyWelcomeCoupon();
399
+ autoApplyWelcomeCoupon(webManager);
395
400
 
396
401
  } catch (error) {
397
402
  console.error('Checkout initialization failed:', error);
398
- showError(error.message || 'Failed to load checkout. Please refresh the page and try again.');
403
+ showError(error.message || 'Failed to load checkout. Please refresh the page and try again.', webManager);
399
404
  } finally {
400
- // Hide preloader once everything is loaded (success or failure)
405
+ // The bindings system handles loading states, no need for a separate preloader
406
+ // Auth listener is still useful for other purposes
401
407
  webManager.auth().listen({}, () => {
402
- hidePreloader();
408
+ console.log('Auth state updated');
403
409
  });
404
410
  }
405
411
  }