@nevios/storefront-kit 0.1.0

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.
package/dist/index.cjs ADDED
@@ -0,0 +1,3057 @@
1
+ 'use strict';
2
+
3
+ var React10 = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+ var storefrontJs = require('@nevios/storefront-js');
6
+
7
+ function _interopNamespace(e) {
8
+ if (e && e.__esModule) return e;
9
+ var n = Object.create(null);
10
+ if (e) {
11
+ Object.keys(e).forEach(function (k) {
12
+ if (k !== 'default') {
13
+ var d = Object.getOwnPropertyDescriptor(e, k);
14
+ Object.defineProperty(n, k, d.get ? d : {
15
+ enumerable: true,
16
+ get: function () { return e[k]; }
17
+ });
18
+ }
19
+ });
20
+ }
21
+ n.default = e;
22
+ return Object.freeze(n);
23
+ }
24
+
25
+ var React10__namespace = /*#__PURE__*/_interopNamespace(React10);
26
+
27
+ // src/provider.tsx
28
+ var StorefrontContext = React10__namespace.createContext(null);
29
+ function NeviosProvider({ client, children }) {
30
+ return /* @__PURE__ */ jsxRuntime.jsx(StorefrontContext.Provider, { value: client, children });
31
+ }
32
+ function useStorefront() {
33
+ const client = React10__namespace.useContext(StorefrontContext);
34
+ if (!client) {
35
+ throw new Error(
36
+ "useStorefront must be used inside <NeviosProvider>. Wrap your app root with it."
37
+ );
38
+ }
39
+ return client;
40
+ }
41
+ function useStorefrontOptional() {
42
+ return React10__namespace.useContext(StorefrontContext);
43
+ }
44
+ var DEFAULTS = {
45
+ brand: "#111111",
46
+ brandForeground: "#ffffff",
47
+ background: "#ffffff",
48
+ foreground: "#111111",
49
+ muted: "#6b7280",
50
+ border: "rgba(0,0,0,0.1)",
51
+ radius: "12px",
52
+ fontFamily: "system-ui, sans-serif"
53
+ };
54
+ var TOKEN_VAR = {
55
+ brand: "--nv-brand",
56
+ brandForeground: "--nv-brand-foreground",
57
+ background: "--nv-bg",
58
+ foreground: "--nv-fg",
59
+ muted: "--nv-muted",
60
+ border: "--nv-border",
61
+ radius: "--nv-radius",
62
+ fontFamily: "--nv-font"
63
+ };
64
+ function themeToCssVars(theme = {}) {
65
+ const merged = { ...DEFAULTS, ...theme };
66
+ const vars = {};
67
+ for (const key of Object.keys(TOKEN_VAR)) {
68
+ vars[TOKEN_VAR[key]] = merged[key];
69
+ }
70
+ return vars;
71
+ }
72
+ function ThemeProvider({
73
+ theme,
74
+ children,
75
+ as: Tag = "div"
76
+ }) {
77
+ const style = themeToCssVars(theme);
78
+ return React10__namespace.createElement(
79
+ Tag,
80
+ { "data-nevios-theme": "", style },
81
+ children
82
+ );
83
+ }
84
+
85
+ // src/i18n/en.ts
86
+ var en = {
87
+ // common
88
+ "common.free": "Free",
89
+ "common.close": "Close",
90
+ "common.loadingShort": "\u2026",
91
+ // catalog
92
+ "catalog.addToCart": "Add to cart",
93
+ "catalog.noProducts": "No products yet.",
94
+ "catalog.soldOut": "Sold out",
95
+ "catalog.addError": "Couldn't add to cart. Please try again.",
96
+ "catalog.inStock": "In stock",
97
+ "catalog.preorder": "Pre-order",
98
+ // catalog — filters + sort
99
+ "catalog.filters.sort": "Sort by",
100
+ "catalog.filters.sortNewest": "Newest",
101
+ "catalog.filters.sortOldest": "Oldest",
102
+ "catalog.filters.sortTitleAsc": "Name: A\u2013Z",
103
+ "catalog.filters.sortTitleDesc": "Name: Z\u2013A",
104
+ "catalog.filters.sortPriceAsc": "Price: low to high",
105
+ "catalog.filters.sortPriceDesc": "Price: high to low",
106
+ "catalog.filters.priceRange": "Price",
107
+ "catalog.filters.min": "Min",
108
+ "catalog.filters.max": "Max",
109
+ "catalog.filters.tags": "Tags",
110
+ "catalog.filters.inStockOnly": "In stock only",
111
+ "catalog.filters.clear": "Clear filters",
112
+ // catalog — back in stock
113
+ "catalog.backInStock.prompt": "Out of stock \u2014 we'll email you when it's back.",
114
+ "catalog.backInStock.email": "Email",
115
+ "catalog.backInStock.notify": "Notify me",
116
+ "catalog.backInStock.success": "Done \u2014 we'll email you when it's back in stock.",
117
+ "catalog.backInStock.error": "Couldn't subscribe. Please try again.",
118
+ // cart
119
+ "cart.title": "Cart",
120
+ "cart.empty": "Your cart is empty.",
121
+ "cart.remove": "Remove",
122
+ "cart.increaseQty": "Increase quantity",
123
+ "cart.decreaseQty": "Decrease quantity",
124
+ "cart.checkout": "Checkout",
125
+ "cart.subtotal": "Subtotal",
126
+ "cart.tax": "Tax",
127
+ "cart.total": "Total",
128
+ // shared totals (checkout + order)
129
+ "totals.subtotal": "Subtotal",
130
+ "totals.discount": "Discount",
131
+ "totals.shipping": "Shipping",
132
+ "totals.paymentFee": "Payment fee",
133
+ "totals.taxIncluded": "Tax included",
134
+ "totals.total": "Total",
135
+ // checkout — steps + states
136
+ "checkout.stepAddress": "Address",
137
+ "checkout.stepShipping": "Shipping",
138
+ "checkout.stepPayment": "Payment",
139
+ "checkout.loading": "Loading checkout\u2026",
140
+ "checkout.empty": "Your checkout is empty.",
141
+ // checkout — address form
142
+ "checkout.shippingAddress": "Shipping address",
143
+ "checkout.continueToShipping": "Continue to shipping",
144
+ "checkout.email": "Email",
145
+ "checkout.firstName": "First name",
146
+ "checkout.lastName": "Last name",
147
+ "checkout.company": "Company (optional)",
148
+ "checkout.address": "Address",
149
+ "checkout.apartment": "Apartment, suite, etc. (optional)",
150
+ "checkout.city": "City",
151
+ "checkout.postalCode": "Postal code",
152
+ "checkout.country": "Country",
153
+ "checkout.phone": "Phone (optional)",
154
+ // checkout — shipping
155
+ "checkout.shippingMethod": "Shipping method",
156
+ "checkout.continueToPayment": "Continue to payment",
157
+ "checkout.loadingShipping": "Loading shipping options\u2026",
158
+ "checkout.noShipping": "No shipping options for this address.",
159
+ // checkout — payment
160
+ "checkout.payment": "Payment",
161
+ "checkout.loadingPayment": "Loading payment options\u2026",
162
+ "checkout.noPayment": "No payment methods available.",
163
+ "checkout.payNow": "Pay now",
164
+ "checkout.placeOrder": "Place order",
165
+ "checkout.gatewayRedirectNote": "You'll be redirected to complete payment securely.",
166
+ // checkout — confirmation
167
+ "checkout.thankYou": "Thank you for your order!",
168
+ "checkout.orderPlacedBody": "Your order {order} has been placed. A confirmation has been sent to {email}.",
169
+ // shipping estimate (pluralized)
170
+ "shipping.days.one": "{count} day",
171
+ "shipping.days.few": "{count} days",
172
+ "shipping.days.many": "{count} days",
173
+ "shipping.days.other": "{count} days",
174
+ "shipping.daysRange": "{min}\u2013{max} days",
175
+ // order — status page + confirmation
176
+ "order.title": "Order {number}",
177
+ "order.placedOn": "Placed {date}",
178
+ "order.loading": "Loading order\u2026",
179
+ "order.loadError": "Couldn't load this order.",
180
+ "order.notFound": "Order not found.",
181
+ "order.confirmLoading": "Loading your order\u2026",
182
+ "order.confirmLoadError": "Couldn't load your order.",
183
+ "order.confirmed": "Order {number} is confirmed.",
184
+ "order.items": "Items",
185
+ "order.shipping": "Shipping",
186
+ "order.shipsTo": "Ships to",
187
+ "order.tracking": "Tracking",
188
+ "order.payment": "Payment",
189
+ "order.method": "Method",
190
+ "order.status": "Status",
191
+ "order.downloadInvoice": "Download invoice",
192
+ "order.contactSupport": "Contact support",
193
+ "order.trackOrder": "Track your order",
194
+ // order — happy-path milestones
195
+ "order.phase.placed": "Placed",
196
+ "order.phase.paid": "Paid",
197
+ "order.phase.fulfilled": "Fulfilled",
198
+ "order.phase.shipped": "Shipped",
199
+ "order.phase.delivered": "Delivered",
200
+ // order — terminal banners
201
+ "order.terminal.cancelled": "This order was cancelled.",
202
+ "order.terminal.refunded": "This order was refunded.",
203
+ "order.terminal.returned": "This order was returned.",
204
+ // error pages
205
+ "error.notFound.title": "Page not found",
206
+ "error.notFound.body": "This page doesn't exist or isn't available in your region.",
207
+ "error.generic.title": "Something went wrong",
208
+ "error.generic.body": "Please try again in a moment.",
209
+ "error.home": "Back to home",
210
+ // account
211
+ "account.title": "Your account",
212
+ "account.signIn": "Sign in",
213
+ "account.signInBlurb": "Enter your email and we'll send you a sign-in link.",
214
+ "account.email": "Email",
215
+ "account.sendLink": "Send sign-in link",
216
+ "account.checkEmail": "Check your email for a sign-in link.",
217
+ "account.signOut": "Sign out",
218
+ "account.greeting": "Signed in as {email}",
219
+ "account.orders": "Your orders",
220
+ "account.noOrders": "No orders yet.",
221
+ "account.addresses": "Saved addresses",
222
+ "account.noAddresses": "No saved addresses yet.",
223
+ "account.viewOrder": "View order",
224
+ "account.verifying": "Signing you in\u2026",
225
+ "account.verifyError": "This sign-in link is invalid or has expired.",
226
+ "account.openDevLink": "Open sign-in link (dev)",
227
+ // account — profile + address editing + reorder (write ops)
228
+ "account.profile": "Profile",
229
+ "account.editProfile": "Edit profile",
230
+ "account.firstName": "First name",
231
+ "account.lastName": "Last name",
232
+ "account.phone": "Phone",
233
+ "account.acceptsMarketing": "Email me about news and offers",
234
+ "account.save": "Save",
235
+ "account.cancel": "Cancel",
236
+ "account.saved": "Saved.",
237
+ "account.saveError": "Couldn't save. Please try again.",
238
+ "account.addAddress": "Add address",
239
+ "account.editAddress": "Edit",
240
+ "account.deleteAddress": "Delete",
241
+ "account.confirmDelete": "Delete this address?",
242
+ "account.setDefault": "Set as default",
243
+ "account.default": "Default",
244
+ "account.company": "Company",
245
+ "account.address": "Address",
246
+ "account.addressLine2": "Apartment, suite, etc.",
247
+ "account.city": "City",
248
+ "account.postalCode": "Postal code",
249
+ "account.country": "Country (2-letter code)",
250
+ "account.reorder": "Buy again",
251
+ "account.reorderDone": "Added {added} item(s) to your cart.",
252
+ "account.reorderPartial": "Added {added}, skipped {skipped} unavailable item(s).",
253
+ "account.reorderError": "Couldn't reorder. Please try again."
254
+ };
255
+
256
+ // src/i18n/cs.ts
257
+ var cs = {
258
+ "common.free": "Zdarma",
259
+ "common.close": "Zav\u0159\xEDt",
260
+ "common.loadingShort": "\u2026",
261
+ "catalog.addToCart": "P\u0159idat do ko\u0161\xEDku",
262
+ "catalog.noProducts": "Zat\xEDm \u017E\xE1dn\xE9 produkty.",
263
+ "catalog.soldOut": "Vyprod\xE1no",
264
+ "catalog.addError": "Nepoda\u0159ilo se p\u0159idat do ko\u0161\xEDku. Zkuste to znovu.",
265
+ "catalog.inStock": "Skladem",
266
+ "catalog.preorder": "P\u0159edobjedn\xE1vka",
267
+ "catalog.filters.sort": "\u0158adit podle",
268
+ "catalog.filters.sortNewest": "Nejnov\u011Bj\u0161\xED",
269
+ "catalog.filters.sortOldest": "Nejstar\u0161\xED",
270
+ "catalog.filters.sortTitleAsc": "N\xE1zev: A\u2013Z",
271
+ "catalog.filters.sortTitleDesc": "N\xE1zev: Z\u2013A",
272
+ "catalog.filters.sortPriceAsc": "Cena: od nejni\u017E\u0161\xED",
273
+ "catalog.filters.sortPriceDesc": "Cena: od nejvy\u0161\u0161\xED",
274
+ "catalog.filters.priceRange": "Cena",
275
+ "catalog.filters.min": "Od",
276
+ "catalog.filters.max": "Do",
277
+ "catalog.filters.tags": "\u0160t\xEDtky",
278
+ "catalog.filters.inStockOnly": "Pouze skladem",
279
+ "catalog.filters.clear": "Zru\u0161it filtry",
280
+ "catalog.backInStock.prompt": "Vyprod\xE1no \u2014 d\xE1me v\xE1m e-mailem v\u011Bd\u011Bt, a\u017E bude zp\u011Bt.",
281
+ "catalog.backInStock.email": "E-mail",
282
+ "catalog.backInStock.notify": "Upozornit m\u011B",
283
+ "catalog.backInStock.success": "Hotovo \u2014 d\xE1me v\xE1m v\u011Bd\u011Bt, a\u017E bude zbo\u017E\xED skladem.",
284
+ "catalog.backInStock.error": "P\u0159ihl\xE1\u0161en\xED se nezda\u0159ilo. Zkuste to znovu.",
285
+ "cart.title": "Ko\u0161\xEDk",
286
+ "cart.empty": "V\xE1\u0161 ko\u0161\xEDk je pr\xE1zdn\xFD.",
287
+ "cart.remove": "Odebrat",
288
+ "cart.increaseQty": "Zv\xFD\u0161it mno\u017Estv\xED",
289
+ "cart.decreaseQty": "Sn\xED\u017Eit mno\u017Estv\xED",
290
+ "cart.checkout": "K pokladn\u011B",
291
+ "cart.subtotal": "Mezisou\u010Det",
292
+ "cart.tax": "DPH",
293
+ "cart.total": "Celkem",
294
+ "totals.subtotal": "Mezisou\u010Det",
295
+ "totals.discount": "Sleva",
296
+ "totals.shipping": "Doprava",
297
+ "totals.paymentFee": "Poplatek za platbu",
298
+ "totals.taxIncluded": "V\u010Detn\u011B DPH",
299
+ "totals.total": "Celkem",
300
+ "checkout.stepAddress": "Adresa",
301
+ "checkout.stepShipping": "Doprava",
302
+ "checkout.stepPayment": "Platba",
303
+ "checkout.loading": "Na\u010D\xEDt\xE1n\xED pokladny\u2026",
304
+ "checkout.empty": "Va\u0161e pokladna je pr\xE1zdn\xE1.",
305
+ "checkout.shippingAddress": "Dodac\xED adresa",
306
+ "checkout.continueToShipping": "Pokra\u010Dovat k doprav\u011B",
307
+ "checkout.email": "E-mail",
308
+ "checkout.firstName": "Jm\xE9no",
309
+ "checkout.lastName": "P\u0159\xEDjmen\xED",
310
+ "checkout.company": "Firma (nepovinn\xE9)",
311
+ "checkout.address": "Adresa",
312
+ "checkout.apartment": "Byt, patro apod. (nepovinn\xE9)",
313
+ "checkout.city": "M\u011Bsto",
314
+ "checkout.postalCode": "PS\u010C",
315
+ "checkout.country": "Zem\u011B",
316
+ "checkout.phone": "Telefon (nepovinn\xE9)",
317
+ "checkout.shippingMethod": "Zp\u016Fsob dopravy",
318
+ "checkout.continueToPayment": "Pokra\u010Dovat k platb\u011B",
319
+ "checkout.loadingShipping": "Na\u010D\xEDt\xE1n\xED mo\u017Enost\xED dopravy\u2026",
320
+ "checkout.noShipping": "Pro tuto adresu nejsou \u017E\xE1dn\xE9 mo\u017Enosti dopravy.",
321
+ "checkout.payment": "Platba",
322
+ "checkout.loadingPayment": "Na\u010D\xEDt\xE1n\xED platebn\xEDch metod\u2026",
323
+ "checkout.noPayment": "\u017D\xE1dn\xE9 platebn\xED metody nejsou k dispozici.",
324
+ "checkout.payNow": "Zaplatit",
325
+ "checkout.placeOrder": "Odeslat objedn\xE1vku",
326
+ "checkout.gatewayRedirectNote": "Budete p\u0159esm\u011Brov\xE1ni k bezpe\u010Dn\xE9mu dokon\u010Den\xED platby.",
327
+ "checkout.thankYou": "D\u011Bkujeme za va\u0161i objedn\xE1vku!",
328
+ "checkout.orderPlacedBody": "Va\u0161e objedn\xE1vka {order} byla p\u0159ijata. Potvrzen\xED jsme odeslali na {email}.",
329
+ "shipping.days.one": "{count} den",
330
+ "shipping.days.few": "{count} dny",
331
+ "shipping.days.many": "{count} dne",
332
+ "shipping.days.other": "{count} dn\xED",
333
+ "shipping.daysRange": "{min}\u2013{max} dn\xED",
334
+ "order.title": "Objedn\xE1vka {number}",
335
+ "order.placedOn": "Vytvo\u0159eno {date}",
336
+ "order.loading": "Na\u010D\xEDt\xE1n\xED objedn\xE1vky\u2026",
337
+ "order.loadError": "Objedn\xE1vku se nepoda\u0159ilo na\u010D\xEDst.",
338
+ "order.notFound": "Objedn\xE1vka nenalezena.",
339
+ "order.confirmLoading": "Na\u010D\xEDt\xE1n\xED va\u0161\xED objedn\xE1vky\u2026",
340
+ "order.confirmLoadError": "Va\u0161i objedn\xE1vku se nepoda\u0159ilo na\u010D\xEDst.",
341
+ "order.confirmed": "Objedn\xE1vka {number} je potvrzena.",
342
+ "order.items": "Polo\u017Eky",
343
+ "order.shipping": "Doprava",
344
+ "order.shipsTo": "Doru\u010Den\xED do",
345
+ "order.tracking": "Sledov\xE1n\xED",
346
+ "order.payment": "Platba",
347
+ "order.method": "Metoda",
348
+ "order.status": "Stav",
349
+ "order.downloadInvoice": "St\xE1hnout fakturu",
350
+ "order.contactSupport": "Kontaktovat podporu",
351
+ "order.trackOrder": "Sledovat objedn\xE1vku",
352
+ "order.phase.placed": "P\u0159ijato",
353
+ "order.phase.paid": "Zaplaceno",
354
+ "order.phase.fulfilled": "P\u0159ipraveno",
355
+ "order.phase.shipped": "Odesl\xE1no",
356
+ "order.phase.delivered": "Doru\u010Deno",
357
+ "order.terminal.cancelled": "Tato objedn\xE1vka byla zru\u0161ena.",
358
+ "order.terminal.refunded": "Tato objedn\xE1vka byla refundov\xE1na.",
359
+ "order.terminal.returned": "Tato objedn\xE1vka byla vr\xE1cena.",
360
+ "error.notFound.title": "Str\xE1nka nenalezena",
361
+ "error.notFound.body": "Tato str\xE1nka neexistuje nebo nen\xED ve va\u0161em regionu dostupn\xE1.",
362
+ "error.generic.title": "N\u011Bco se pokazilo",
363
+ "error.generic.body": "Zkuste to pros\xEDm za chv\xEDli znovu.",
364
+ "error.home": "Zp\u011Bt na \xFAvod",
365
+ "account.title": "V\xE1\u0161 \xFA\u010Det",
366
+ "account.signIn": "P\u0159ihl\xE1sit se",
367
+ "account.signInBlurb": "Zadejte e-mail a po\u0161leme v\xE1m p\u0159ihla\u0161ovac\xED odkaz.",
368
+ "account.email": "E-mail",
369
+ "account.sendLink": "Poslat p\u0159ihla\u0161ovac\xED odkaz",
370
+ "account.checkEmail": "Zkontrolujte e-mail s p\u0159ihla\u0161ovac\xEDm odkazem.",
371
+ "account.signOut": "Odhl\xE1sit se",
372
+ "account.greeting": "P\u0159ihl\xE1\u0161en jako {email}",
373
+ "account.orders": "Va\u0161e objedn\xE1vky",
374
+ "account.noOrders": "Zat\xEDm \u017E\xE1dn\xE9 objedn\xE1vky.",
375
+ "account.addresses": "Ulo\u017Een\xE9 adresy",
376
+ "account.noAddresses": "Zat\xEDm \u017E\xE1dn\xE9 ulo\u017Een\xE9 adresy.",
377
+ "account.viewOrder": "Zobrazit objedn\xE1vku",
378
+ "account.verifying": "P\u0159ihla\u0161ov\xE1n\xED\u2026",
379
+ "account.verifyError": "P\u0159ihla\u0161ovac\xED odkaz je neplatn\xFD nebo vypr\u0161el.",
380
+ "account.openDevLink": "Otev\u0159\xEDt p\u0159ihla\u0161ovac\xED odkaz (dev)",
381
+ "account.profile": "Profil",
382
+ "account.editProfile": "Upravit profil",
383
+ "account.firstName": "Jm\xE9no",
384
+ "account.lastName": "P\u0159\xEDjmen\xED",
385
+ "account.phone": "Telefon",
386
+ "account.acceptsMarketing": "Pos\xEDlejte mi novinky a nab\xEDdky e-mailem",
387
+ "account.save": "Ulo\u017Eit",
388
+ "account.cancel": "Zru\u0161it",
389
+ "account.saved": "Ulo\u017Eeno.",
390
+ "account.saveError": "Nepoda\u0159ilo se ulo\u017Eit. Zkuste to znovu.",
391
+ "account.addAddress": "P\u0159idat adresu",
392
+ "account.editAddress": "Upravit",
393
+ "account.deleteAddress": "Smazat",
394
+ "account.confirmDelete": "Smazat tuto adresu?",
395
+ "account.setDefault": "Nastavit jako v\xFDchoz\xED",
396
+ "account.default": "V\xFDchoz\xED",
397
+ "account.company": "Spole\u010Dnost",
398
+ "account.address": "Adresa",
399
+ "account.addressLine2": "Byt, patro apod.",
400
+ "account.city": "M\u011Bsto",
401
+ "account.postalCode": "PS\u010C",
402
+ "account.country": "Zem\u011B (dvoup\xEDsmenn\xFD k\xF3d)",
403
+ "account.reorder": "Koupit znovu",
404
+ "account.reorderDone": "P\u0159id\xE1no {added} polo\u017Eek do ko\u0161\xEDku.",
405
+ "account.reorderPartial": "P\u0159id\xE1no {added}, p\u0159esko\u010Deno {skipped} nedostupn\xFDch polo\u017Eek.",
406
+ "account.reorderError": "Objedn\xE1vku se nepoda\u0159ilo zopakovat. Zkuste to znovu."
407
+ };
408
+ var BUNDLED = { en, cs };
409
+ function langOf(locale) {
410
+ return (locale ?? "en").toLowerCase().replace(/_/g, "-").split("-")[0] || "en";
411
+ }
412
+ function bundledMessages(locale) {
413
+ return BUNDLED[langOf(locale)] ?? en;
414
+ }
415
+ var warned = /* @__PURE__ */ new Set();
416
+ function devWarn(message) {
417
+ let dev = false;
418
+ try {
419
+ const proc = globalThis.process;
420
+ dev = !!proc?.env && proc.env.NODE_ENV !== "production";
421
+ } catch {
422
+ dev = false;
423
+ }
424
+ if (dev && !warned.has(message)) {
425
+ warned.add(message);
426
+ console.warn(`[nevios i18n] ${message}`);
427
+ }
428
+ }
429
+ function interpolate(template, vars) {
430
+ if (!vars) return template;
431
+ return template.replace(/\{(\w+)\}/g, (m, k) => k in vars ? String(vars[k]) : m);
432
+ }
433
+ function makeI18n(locale, messages) {
434
+ let plural;
435
+ try {
436
+ plural = new Intl.PluralRules(locale);
437
+ } catch {
438
+ plural = new Intl.PluralRules("en");
439
+ }
440
+ const dict = messages;
441
+ const t = (key, vars) => {
442
+ let template;
443
+ if (vars && typeof vars.count === "number") {
444
+ const cat = plural.select(vars.count);
445
+ template = dict[`${key}.${cat}`] ?? dict[`${key}.other`] ?? dict[key];
446
+ } else {
447
+ template = dict[key];
448
+ }
449
+ if (template == null) {
450
+ devWarn(`missing message key "${String(key)}"`);
451
+ return String(key);
452
+ }
453
+ return interpolate(template, vars);
454
+ };
455
+ return { locale, messages, t };
456
+ }
457
+ var DEFAULT_I18N = makeI18n("en", en);
458
+ var I18nContext = React10__namespace.createContext(null);
459
+ function I18nProvider({
460
+ locale,
461
+ messages,
462
+ children
463
+ }) {
464
+ const value = React10__namespace.useMemo(() => {
465
+ const lang = langOf(locale);
466
+ if (locale && !(lang in BUNDLED) && !messages) {
467
+ devWarn(
468
+ `no bundled pack for "${locale}" and no \`messages\` override \u2014 the UI falls back to English under <html lang="${locale}">. Pass messages={...}.`
469
+ );
470
+ }
471
+ const base = bundledMessages(locale);
472
+ const merged = messages ? { ...base, ...messages } : base;
473
+ return makeI18n(locale ?? "en", merged);
474
+ }, [locale, messages]);
475
+ return /* @__PURE__ */ jsxRuntime.jsx(I18nContext.Provider, { value, children });
476
+ }
477
+ function useI18n() {
478
+ return React10__namespace.useContext(I18nContext) ?? DEFAULT_I18N;
479
+ }
480
+ function useT() {
481
+ return useI18n().t;
482
+ }
483
+ var defaultLabel = (m) => m.label ?? (m.name ? `${m.name}${m.currency ? ` (${m.currency})` : ""}` : m.handle);
484
+ function MarketSwitcher({
485
+ markets,
486
+ current,
487
+ hrefFor,
488
+ onSelect,
489
+ renderLabel = defaultLabel,
490
+ "aria-label": ariaLabel = "Market",
491
+ className,
492
+ style
493
+ }) {
494
+ if (!markets || markets.length <= 1) return null;
495
+ function handleChange(e) {
496
+ const m = markets.find((x) => x.handle === e.target.value);
497
+ if (!m) return;
498
+ if (onSelect) {
499
+ onSelect(m);
500
+ } else if (hrefFor && typeof window !== "undefined") {
501
+ window.location.assign(hrefFor(m));
502
+ }
503
+ }
504
+ return /* @__PURE__ */ jsxRuntime.jsxs(
505
+ "select",
506
+ {
507
+ "aria-label": ariaLabel,
508
+ value: current ?? "",
509
+ onChange: handleChange,
510
+ className,
511
+ style: {
512
+ font: "inherit",
513
+ color: "var(--nv-fg)",
514
+ background: "var(--nv-bg)",
515
+ border: "1px solid var(--nv-border)",
516
+ borderRadius: "var(--nv-radius)",
517
+ padding: "0.4rem 0.6rem",
518
+ cursor: "pointer",
519
+ ...style
520
+ },
521
+ children: [
522
+ current == null ? /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true }) : null,
523
+ markets.map((m) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: m.handle, children: renderLabel(m) }, m.handle))
524
+ ]
525
+ }
526
+ );
527
+ }
528
+ function formatCents(cents, currency, locale) {
529
+ return new Intl.NumberFormat(locale, { style: "currency", currency }).format(cents / 100);
530
+ }
531
+ function formatMoney(money, locale) {
532
+ return formatCents(money.amount_cents, money.currency, locale);
533
+ }
534
+ function useFormatLocale(locale) {
535
+ const { locale: ctxLocale } = useI18n();
536
+ return locale ?? ctxLocale;
537
+ }
538
+ function PriceMoney({
539
+ money,
540
+ showCompareAt = true,
541
+ locale
542
+ }) {
543
+ const loc = useFormatLocale(locale);
544
+ const onSale = showCompareAt && money.compare_at_cents != null && money.compare_at_cents > money.amount_cents;
545
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "inline-flex", alignItems: "baseline", gap: "0.5rem" }, children: [
546
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 500, color: onSale ? "var(--nv-brand)" : "inherit" }, children: formatMoney(money, loc) }),
547
+ onSale ? /* @__PURE__ */ jsxRuntime.jsx(
548
+ "span",
549
+ {
550
+ style: {
551
+ color: "var(--nv-muted)",
552
+ textDecoration: "line-through",
553
+ fontSize: "0.875em"
554
+ },
555
+ children: formatMoney({ ...money, amount_cents: money.compare_at_cents }, loc)
556
+ }
557
+ ) : null
558
+ ] });
559
+ }
560
+ var CARD_WIDTHS = [200, 400, 600];
561
+ var CARD_SIZES = "(min-width: 1024px) 25vw, (min-width: 640px) 33vw, 50vw";
562
+ function ProductCard({ product, href, locale }) {
563
+ const image = product.media[0];
564
+ const price = product.variants[0]?.price ?? null;
565
+ const imageBase = useStorefrontOptional()?.imageBase;
566
+ const responsive = image ? storefrontJs.imageSrcSet(image, { base: imageBase, widths: CARD_WIDTHS }) : null;
567
+ const inner = /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
568
+ /* @__PURE__ */ jsxRuntime.jsx(
569
+ "div",
570
+ {
571
+ style: {
572
+ aspectRatio: "1 / 1",
573
+ overflow: "hidden",
574
+ borderRadius: "var(--nv-radius)",
575
+ background: "color-mix(in oklab, var(--nv-muted) 12%, transparent)"
576
+ },
577
+ children: image && responsive ? (
578
+ // eslint-disable-next-line @next/next/no-img-element
579
+ /* @__PURE__ */ jsxRuntime.jsx(
580
+ "img",
581
+ {
582
+ src: responsive.src || image.url,
583
+ srcSet: responsive.srcSet || void 0,
584
+ sizes: responsive.srcSet ? CARD_SIZES : void 0,
585
+ alt: image.alt ?? product.title,
586
+ loading: "lazy",
587
+ style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
588
+ }
589
+ )
590
+ ) : null
591
+ }
592
+ ),
593
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontWeight: 500, lineHeight: 1.3 }, children: product.title }),
594
+ price ? /* @__PURE__ */ jsxRuntime.jsx(PriceMoney, { money: price, locale }) : null
595
+ ] });
596
+ if (!href) return inner;
597
+ return /* @__PURE__ */ jsxRuntime.jsx("a", { href, style: { color: "inherit", textDecoration: "none", display: "block" }, children: inner });
598
+ }
599
+ var MAIN_WIDTHS = [600, 800, 1e3];
600
+ var THUMB_WIDTHS = [56, 112];
601
+ function ProductGallery({ media, alt }) {
602
+ const [active, setActive] = React10__namespace.useState(0);
603
+ const imageBase = useStorefrontOptional()?.imageBase;
604
+ if (media.length === 0) {
605
+ return /* @__PURE__ */ jsxRuntime.jsx(
606
+ "div",
607
+ {
608
+ style: {
609
+ aspectRatio: "1 / 1",
610
+ borderRadius: "var(--nv-radius)",
611
+ background: "color-mix(in oklab, var(--nv-muted) 12%, transparent)"
612
+ }
613
+ }
614
+ );
615
+ }
616
+ const main = media[Math.min(active, media.length - 1)];
617
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.75rem" }, children: [
618
+ /* @__PURE__ */ jsxRuntime.jsx(
619
+ "div",
620
+ {
621
+ style: {
622
+ aspectRatio: "1 / 1",
623
+ overflow: "hidden",
624
+ borderRadius: "var(--nv-radius)",
625
+ background: "color-mix(in oklab, var(--nv-muted) 12%, transparent)"
626
+ },
627
+ children: (() => {
628
+ const r = storefrontJs.imageSrcSet(main, { base: imageBase, widths: MAIN_WIDTHS });
629
+ return (
630
+ // eslint-disable-next-line @next/next/no-img-element
631
+ /* @__PURE__ */ jsxRuntime.jsx(
632
+ "img",
633
+ {
634
+ src: r.src || main.url,
635
+ srcSet: r.srcSet || void 0,
636
+ sizes: r.srcSet ? "(min-width: 768px) 600px, 100vw" : void 0,
637
+ alt: main.alt ?? alt ?? "",
638
+ style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
639
+ }
640
+ )
641
+ );
642
+ })()
643
+ }
644
+ ),
645
+ media.length > 1 ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", gap: "0.5rem", flexWrap: "wrap" }, children: media.map((m, i) => /* @__PURE__ */ jsxRuntime.jsx(
646
+ "button",
647
+ {
648
+ type: "button",
649
+ onClick: () => setActive(i),
650
+ "aria-label": `Image ${i + 1}`,
651
+ "aria-pressed": i === active,
652
+ style: {
653
+ width: 56,
654
+ height: 56,
655
+ padding: 0,
656
+ cursor: "pointer",
657
+ overflow: "hidden",
658
+ borderRadius: "calc(var(--nv-radius) - 4px)",
659
+ background: "transparent",
660
+ border: i === active ? "2px solid var(--nv-brand)" : "1px solid var(--nv-border)"
661
+ },
662
+ children: (() => {
663
+ const r = storefrontJs.imageSrcSet(m, { base: imageBase, widths: THUMB_WIDTHS });
664
+ return (
665
+ // eslint-disable-next-line @next/next/no-img-element
666
+ /* @__PURE__ */ jsxRuntime.jsx(
667
+ "img",
668
+ {
669
+ src: r.src || m.url,
670
+ srcSet: r.srcSet || void 0,
671
+ sizes: r.srcSet ? "56px" : void 0,
672
+ alt: m.alt ?? "",
673
+ loading: "lazy",
674
+ style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
675
+ }
676
+ )
677
+ );
678
+ })()
679
+ },
680
+ m.url + i
681
+ )) }) : null
682
+ ] });
683
+ }
684
+ function matchVariant(product, selected) {
685
+ return product.variants.find(
686
+ (v) => v.options.length === selected.length && v.options.every((o, i) => o === selected[i])
687
+ ) ?? null;
688
+ }
689
+ function VariantPicker({ product, onChange }) {
690
+ const [selected, setSelected] = React10__namespace.useState(
691
+ () => product.variants[0]?.options ?? []
692
+ );
693
+ React10__namespace.useEffect(() => {
694
+ onChange?.(matchVariant(product, selected));
695
+ }, [product, selected]);
696
+ if (product.options.length === 0) return null;
697
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: "1rem" }, children: product.options.map((opt, optIndex) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
698
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 500, fontSize: "0.875em" }, children: opt.name }),
699
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", gap: "0.5rem", flexWrap: "wrap" }, children: opt.values.map((value) => {
700
+ const isActive = selected[optIndex] === value;
701
+ return /* @__PURE__ */ jsxRuntime.jsx(
702
+ "button",
703
+ {
704
+ type: "button",
705
+ "aria-pressed": isActive,
706
+ onClick: () => setSelected((prev) => {
707
+ const next = [...prev];
708
+ next[optIndex] = value;
709
+ return next;
710
+ }),
711
+ style: {
712
+ cursor: "pointer",
713
+ padding: "0.4rem 0.85rem",
714
+ borderRadius: "var(--nv-radius)",
715
+ font: "inherit",
716
+ background: isActive ? "var(--nv-brand)" : "transparent",
717
+ color: isActive ? "var(--nv-brand-foreground)" : "var(--nv-fg)",
718
+ border: isActive ? "1px solid var(--nv-brand)" : "1px solid var(--nv-border)"
719
+ },
720
+ children: value
721
+ },
722
+ value
723
+ );
724
+ }) })
725
+ ] }, opt.name)) });
726
+ }
727
+ function BackInStockForm({
728
+ variant,
729
+ defaultEmail = "",
730
+ onSuccess
731
+ }) {
732
+ const t = useT();
733
+ const client = useStorefront();
734
+ const [email, setEmail] = React10__namespace.useState(defaultEmail);
735
+ const [state, setState] = React10__namespace.useState("idle");
736
+ if (!variant || variant.availability?.status !== "out_of_stock") return null;
737
+ async function submit(e) {
738
+ e.preventDefault();
739
+ if (!variant || !email.trim()) return;
740
+ setState("submitting");
741
+ try {
742
+ await client.catalog.inventory.subscribe(variant.id, email.trim());
743
+ setState("success");
744
+ onSuccess?.();
745
+ } catch {
746
+ setState("error");
747
+ }
748
+ }
749
+ if (state === "success") {
750
+ return /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: 0, color: "var(--nv-muted)", fontSize: "0.875rem" }, children: t("catalog.backInStock.success") });
751
+ }
752
+ return /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: submit, style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
753
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: 0, color: "var(--nv-muted)", fontSize: "0.875rem" }, children: t("catalog.backInStock.prompt") }),
754
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "0.5rem", flexWrap: "wrap" }, children: [
755
+ /* @__PURE__ */ jsxRuntime.jsx(
756
+ "input",
757
+ {
758
+ type: "email",
759
+ required: true,
760
+ value: email,
761
+ onChange: (e) => setEmail(e.target.value),
762
+ placeholder: t("catalog.backInStock.email"),
763
+ "aria-label": t("catalog.backInStock.email"),
764
+ style: {
765
+ font: "inherit",
766
+ flex: 1,
767
+ minWidth: "12rem",
768
+ padding: "0.6rem 0.75rem",
769
+ borderRadius: "var(--nv-radius)",
770
+ border: "1px solid var(--nv-border)",
771
+ background: "var(--nv-bg)",
772
+ color: "var(--nv-fg)"
773
+ }
774
+ }
775
+ ),
776
+ /* @__PURE__ */ jsxRuntime.jsx(
777
+ "button",
778
+ {
779
+ type: "submit",
780
+ disabled: state === "submitting",
781
+ style: {
782
+ font: "inherit",
783
+ fontWeight: 500,
784
+ cursor: state === "submitting" ? "default" : "pointer",
785
+ padding: "0.6rem 1.1rem",
786
+ borderRadius: "var(--nv-radius)",
787
+ border: "1px solid var(--nv-border)",
788
+ background: "transparent",
789
+ color: "var(--nv-fg)",
790
+ opacity: state === "submitting" ? 0.6 : 1
791
+ },
792
+ children: state === "submitting" ? "\u2026" : t("catalog.backInStock.notify")
793
+ }
794
+ )
795
+ ] }),
796
+ state === "error" ? /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: 0, color: "#b3261e", fontSize: "0.8125rem" }, children: t("catalog.backInStock.error") }) : null
797
+ ] });
798
+ }
799
+ function StockBadge({ variant, hideInStock = false }) {
800
+ const t = useT();
801
+ const status = variant?.availability?.status;
802
+ if (!status) return null;
803
+ if (status === "in_stock" && hideInStock) return null;
804
+ const { key, color, bg } = {
805
+ in_stock: { key: "catalog.inStock", color: "#0a7c3a", bg: "rgba(10,124,58,0.12)" },
806
+ preorder: { key: "catalog.preorder", color: "var(--nv-fg)", bg: "color-mix(in oklab, var(--nv-muted) 16%, transparent)" },
807
+ out_of_stock: { key: "catalog.soldOut", color: "var(--nv-muted)", bg: "color-mix(in oklab, var(--nv-muted) 16%, transparent)" }
808
+ }[status];
809
+ return /* @__PURE__ */ jsxRuntime.jsxs(
810
+ "span",
811
+ {
812
+ style: {
813
+ display: "inline-flex",
814
+ alignItems: "center",
815
+ gap: "0.35rem",
816
+ fontSize: "0.75rem",
817
+ fontWeight: 600,
818
+ padding: "0.2rem 0.55rem",
819
+ borderRadius: "999px",
820
+ color,
821
+ background: bg
822
+ },
823
+ children: [
824
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, style: { width: 6, height: 6, borderRadius: "50%", background: "currentColor" } }),
825
+ t(key)
826
+ ]
827
+ }
828
+ );
829
+ }
830
+ function ProductDetailPage({
831
+ product,
832
+ locale,
833
+ addToCart
834
+ }) {
835
+ const [variant, setVariant] = React10__namespace.useState(
836
+ product.variants[0] ?? null
837
+ );
838
+ return /* @__PURE__ */ jsxRuntime.jsxs(
839
+ "div",
840
+ {
841
+ style: {
842
+ display: "grid",
843
+ gap: "2rem",
844
+ gridTemplateColumns: "repeat(auto-fit, minmax(min(100%, 320px), 1fr))",
845
+ fontFamily: "var(--nv-font)",
846
+ color: "var(--nv-fg)"
847
+ },
848
+ children: [
849
+ /* @__PURE__ */ jsxRuntime.jsx(ProductGallery, { media: product.media, alt: product.title }),
850
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "1.25rem" }, children: [
851
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
852
+ product.vendor ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "var(--nv-muted)", fontSize: "0.875em" }, children: product.vendor }) : null,
853
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { style: { margin: "0.25rem 0 0", fontSize: "1.6rem", fontWeight: 500 }, children: product.title })
854
+ ] }),
855
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.75rem", flexWrap: "wrap" }, children: [
856
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "1.25rem" }, children: variant ? /* @__PURE__ */ jsxRuntime.jsx(PriceMoney, { money: variant.price, locale }) : product.price_range ? /* @__PURE__ */ jsxRuntime.jsx(
857
+ PriceMoney,
858
+ {
859
+ money: {
860
+ amount_cents: product.price_range.min_cents,
861
+ with_tax_cents: product.price_range.min_cents,
862
+ without_tax_cents: product.price_range.min_cents,
863
+ compare_at_cents: null,
864
+ currency: product.price_range.currency,
865
+ includes_tax: product.market.prices_include_tax
866
+ },
867
+ locale
868
+ }
869
+ ) : null }),
870
+ /* @__PURE__ */ jsxRuntime.jsx(StockBadge, { variant, hideInStock: true })
871
+ ] }),
872
+ /* @__PURE__ */ jsxRuntime.jsx(VariantPicker, { product, onChange: setVariant }),
873
+ addToCart ? /* @__PURE__ */ jsxRuntime.jsx("div", { children: addToCart(variant) }) : null,
874
+ /* @__PURE__ */ jsxRuntime.jsx(BackInStockForm, { variant }),
875
+ product.description_html ? /* @__PURE__ */ jsxRuntime.jsx(
876
+ "div",
877
+ {
878
+ style: { lineHeight: 1.6 },
879
+ dangerouslySetInnerHTML: { __html: product.description_html }
880
+ }
881
+ ) : null
882
+ ] })
883
+ ]
884
+ }
885
+ );
886
+ }
887
+ function CollectionPage({
888
+ collection,
889
+ productHref,
890
+ locale
891
+ }) {
892
+ const t = useT();
893
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontFamily: "var(--nv-font)", color: "var(--nv-fg)" }, children: [
894
+ /* @__PURE__ */ jsxRuntime.jsxs("header", { style: { marginBottom: "1.5rem" }, children: [
895
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { style: { margin: 0, fontSize: "1.6rem", fontWeight: 500 }, children: collection.title }),
896
+ collection.description_html ? /* @__PURE__ */ jsxRuntime.jsx(
897
+ "div",
898
+ {
899
+ style: { marginTop: "0.5rem", color: "var(--nv-muted)", lineHeight: 1.6 },
900
+ dangerouslySetInnerHTML: { __html: collection.description_html }
901
+ }
902
+ ) : null
903
+ ] }),
904
+ collection.products.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "var(--nv-muted)" }, children: t("catalog.noProducts") }) : /* @__PURE__ */ jsxRuntime.jsx(
905
+ "div",
906
+ {
907
+ style: {
908
+ display: "grid",
909
+ gap: "1.5rem",
910
+ gridTemplateColumns: "repeat(auto-fill, minmax(min(100%, 220px), 1fr))"
911
+ },
912
+ children: collection.products.map((product) => /* @__PURE__ */ jsxRuntime.jsx(
913
+ ProductCard,
914
+ {
915
+ product,
916
+ href: productHref?.(product),
917
+ locale
918
+ },
919
+ product.id
920
+ ))
921
+ }
922
+ )
923
+ ] });
924
+ }
925
+ var SORTS = [
926
+ "newest",
927
+ "oldest",
928
+ "title-asc",
929
+ "title-desc",
930
+ "price-asc",
931
+ "price-desc"
932
+ ];
933
+ var SORT_LABEL = {
934
+ newest: "catalog.filters.sortNewest",
935
+ oldest: "catalog.filters.sortOldest",
936
+ "title-asc": "catalog.filters.sortTitleAsc",
937
+ "title-desc": "catalog.filters.sortTitleDesc",
938
+ "price-asc": "catalog.filters.sortPriceAsc",
939
+ "price-desc": "catalog.filters.sortPriceDesc"
940
+ };
941
+ var inputStyle = {
942
+ font: "inherit",
943
+ padding: "0.45rem 0.6rem",
944
+ borderRadius: "var(--nv-radius)",
945
+ border: "1px solid var(--nv-border)",
946
+ background: "var(--nv-bg)",
947
+ color: "var(--nv-fg)"
948
+ };
949
+ var labelStyle = {
950
+ fontSize: "0.8125rem",
951
+ color: "var(--nv-muted)"
952
+ };
953
+ function SortSelect({ value = "newest", onChange }) {
954
+ const t = useT();
955
+ return /* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "inline-flex", alignItems: "center", gap: "0.5rem", font: "inherit" }, children: [
956
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: labelStyle, children: t("catalog.filters.sort") }),
957
+ /* @__PURE__ */ jsxRuntime.jsx(
958
+ "select",
959
+ {
960
+ value,
961
+ onChange: (e) => onChange(e.target.value),
962
+ style: inputStyle,
963
+ children: SORTS.map((s) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: s, children: t(SORT_LABEL[s]) }, s))
964
+ }
965
+ )
966
+ ] });
967
+ }
968
+ function AvailabilityFilter({
969
+ checked = false,
970
+ onChange
971
+ }) {
972
+ const t = useT();
973
+ return /* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "inline-flex", alignItems: "center", gap: "0.5rem", cursor: "pointer", font: "inherit" }, children: [
974
+ /* @__PURE__ */ jsxRuntime.jsx("input", { type: "checkbox", checked, onChange: (e) => onChange(e.target.checked) }),
975
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.875rem" }, children: t("catalog.filters.inStockOnly") })
976
+ ] });
977
+ }
978
+ function PriceRangeFilter({
979
+ minCents,
980
+ maxCents,
981
+ currency,
982
+ onChange,
983
+ locale
984
+ }) {
985
+ const t = useT();
986
+ const loc = useFormatLocale(locale);
987
+ const toMajor = (c) => c == null ? "" : String(Math.round(c / 100));
988
+ const fromMajor = (v) => {
989
+ const n = parseInt(v, 10);
990
+ return Number.isFinite(n) && v.trim() !== "" ? n * 100 : void 0;
991
+ };
992
+ return /* @__PURE__ */ jsxRuntime.jsxs("fieldset", { style: { border: "none", margin: 0, padding: 0, display: "flex", flexDirection: "column", gap: "0.35rem" }, children: [
993
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: labelStyle, children: [
994
+ t("catalog.filters.priceRange"),
995
+ " (",
996
+ formatCents(0, currency, loc).replace(/[\d.,\s]/g, "").trim() || currency,
997
+ ")"
998
+ ] }),
999
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.4rem" }, children: [
1000
+ /* @__PURE__ */ jsxRuntime.jsx(
1001
+ "input",
1002
+ {
1003
+ type: "number",
1004
+ inputMode: "numeric",
1005
+ min: 0,
1006
+ placeholder: t("catalog.filters.min"),
1007
+ "aria-label": t("catalog.filters.min"),
1008
+ value: toMajor(minCents),
1009
+ onChange: (e) => onChange(fromMajor(e.target.value), maxCents),
1010
+ style: { ...inputStyle, width: "5.5rem" }
1011
+ }
1012
+ ),
1013
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--nv-muted)" }, children: "\u2013" }),
1014
+ /* @__PURE__ */ jsxRuntime.jsx(
1015
+ "input",
1016
+ {
1017
+ type: "number",
1018
+ inputMode: "numeric",
1019
+ min: 0,
1020
+ placeholder: t("catalog.filters.max"),
1021
+ "aria-label": t("catalog.filters.max"),
1022
+ value: toMajor(maxCents),
1023
+ onChange: (e) => onChange(minCents, fromMajor(e.target.value)),
1024
+ style: { ...inputStyle, width: "5.5rem" }
1025
+ }
1026
+ )
1027
+ ] })
1028
+ ] });
1029
+ }
1030
+ function TagFilter({ tags, selected = [], onChange }) {
1031
+ const t = useT();
1032
+ if (tags.length === 0) return null;
1033
+ const set = new Set(selected);
1034
+ const toggle = (tag) => {
1035
+ const next = new Set(set);
1036
+ if (next.has(tag)) next.delete(tag);
1037
+ else next.add(tag);
1038
+ onChange([...next]);
1039
+ };
1040
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.35rem" }, children: [
1041
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: labelStyle, children: t("catalog.filters.tags") }),
1042
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", gap: "0.4rem", flexWrap: "wrap" }, children: tags.map((tag) => {
1043
+ const active = set.has(tag);
1044
+ return /* @__PURE__ */ jsxRuntime.jsx(
1045
+ "button",
1046
+ {
1047
+ type: "button",
1048
+ "aria-pressed": active,
1049
+ onClick: () => toggle(tag),
1050
+ style: {
1051
+ font: "inherit",
1052
+ fontSize: "0.8125rem",
1053
+ cursor: "pointer",
1054
+ padding: "0.3rem 0.7rem",
1055
+ borderRadius: "999px",
1056
+ background: active ? "var(--nv-brand)" : "transparent",
1057
+ color: active ? "var(--nv-brand-foreground)" : "var(--nv-fg)",
1058
+ border: active ? "1px solid var(--nv-brand)" : "1px solid var(--nv-border)"
1059
+ },
1060
+ children: tag
1061
+ },
1062
+ tag
1063
+ );
1064
+ }) })
1065
+ ] });
1066
+ }
1067
+ var CartContext = React10__namespace.createContext(null);
1068
+ function CartProvider({ children }) {
1069
+ const client = useStorefront();
1070
+ const [cart, setCart] = React10__namespace.useState(null);
1071
+ const [loading, setLoading] = React10__namespace.useState(false);
1072
+ const run = React10__namespace.useCallback(
1073
+ async (op) => {
1074
+ setLoading(true);
1075
+ try {
1076
+ const next = await op();
1077
+ setCart(next);
1078
+ return next;
1079
+ } finally {
1080
+ setLoading(false);
1081
+ }
1082
+ },
1083
+ []
1084
+ );
1085
+ const refresh = React10__namespace.useCallback(async () => {
1086
+ setLoading(true);
1087
+ try {
1088
+ setCart(await client.cart.get());
1089
+ } finally {
1090
+ setLoading(false);
1091
+ }
1092
+ }, [client]);
1093
+ React10__namespace.useEffect(() => {
1094
+ void refresh();
1095
+ }, [refresh]);
1096
+ const value = React10__namespace.useMemo(
1097
+ () => ({
1098
+ cart,
1099
+ loading,
1100
+ itemCount: cart?.totals.item_count ?? 0,
1101
+ addLine: (input) => run(() => client.cart.addLine(input)),
1102
+ updateLine: (lineId, quantity) => run(() => client.cart.updateLine(lineId, quantity)),
1103
+ removeLine: (lineId) => run(() => client.cart.removeLine(lineId)),
1104
+ refresh
1105
+ }),
1106
+ [cart, loading, client, run, refresh]
1107
+ );
1108
+ return /* @__PURE__ */ jsxRuntime.jsx(CartContext.Provider, { value, children });
1109
+ }
1110
+ function useCart() {
1111
+ const ctx = React10__namespace.useContext(CartContext);
1112
+ if (!ctx) {
1113
+ throw new Error("useCart must be used inside <CartProvider>.");
1114
+ }
1115
+ return ctx;
1116
+ }
1117
+ function AddToCart({
1118
+ variant,
1119
+ quantity = 1,
1120
+ children,
1121
+ onAdded
1122
+ }) {
1123
+ const t = useT();
1124
+ const { addLine } = useCart();
1125
+ const [busy, setBusy] = React10__namespace.useState(false);
1126
+ const [error, setError] = React10__namespace.useState(null);
1127
+ const disabled = variant == null || busy;
1128
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
1129
+ /* @__PURE__ */ jsxRuntime.jsx(
1130
+ "button",
1131
+ {
1132
+ type: "button",
1133
+ disabled,
1134
+ onClick: async () => {
1135
+ if (variant == null) return;
1136
+ setBusy(true);
1137
+ setError(null);
1138
+ try {
1139
+ await addLine({ variant_id: variant.id, quantity });
1140
+ onAdded?.();
1141
+ } catch (err) {
1142
+ const code = err?.code;
1143
+ setError(code === "variant_out_of_stock" ? t("catalog.soldOut") : t("catalog.addError"));
1144
+ } finally {
1145
+ setBusy(false);
1146
+ }
1147
+ },
1148
+ style: {
1149
+ font: "inherit",
1150
+ fontWeight: 500,
1151
+ cursor: disabled ? "default" : "pointer",
1152
+ padding: "0.7rem 1.5rem",
1153
+ borderRadius: "var(--nv-radius)",
1154
+ border: "none",
1155
+ background: "var(--nv-brand)",
1156
+ color: "var(--nv-brand-foreground)",
1157
+ opacity: disabled ? 0.55 : 1
1158
+ },
1159
+ children: busy ? t("common.loadingShort") : children ?? t("catalog.addToCart")
1160
+ }
1161
+ ),
1162
+ error ? /* @__PURE__ */ jsxRuntime.jsx("p", { role: "alert", style: { margin: 0, fontSize: "0.8125rem", color: "var(--nv-danger, #b91c1c)" }, children: error }) : null
1163
+ ] });
1164
+ }
1165
+ function CartLineItem({ line, locale }) {
1166
+ const t = useT();
1167
+ const { updateLine, removeLine } = useCart();
1168
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "0.85rem", alignItems: "flex-start", padding: "0.75rem 0" }, children: [
1169
+ /* @__PURE__ */ jsxRuntime.jsx(
1170
+ "div",
1171
+ {
1172
+ style: {
1173
+ width: 64,
1174
+ height: 64,
1175
+ flex: "0 0 auto",
1176
+ borderRadius: "calc(var(--nv-radius) - 4px)",
1177
+ overflow: "hidden",
1178
+ background: "color-mix(in oklab, var(--nv-muted) 12%, transparent)"
1179
+ },
1180
+ children: line.image_url ? (
1181
+ // eslint-disable-next-line @next/next/no-img-element
1182
+ /* @__PURE__ */ jsxRuntime.jsx(
1183
+ "img",
1184
+ {
1185
+ src: line.image_url,
1186
+ alt: line.title ?? "",
1187
+ style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
1188
+ }
1189
+ )
1190
+ ) : null
1191
+ }
1192
+ ),
1193
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [
1194
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontWeight: 500, lineHeight: 1.3 }, children: line.title }),
1195
+ line.variant_title ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "var(--nv-muted)", fontSize: "0.875em" }, children: line.variant_title }) : null,
1196
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.75rem", marginTop: "0.5rem" }, children: [
1197
+ /* @__PURE__ */ jsxRuntime.jsxs(
1198
+ "div",
1199
+ {
1200
+ style: {
1201
+ display: "inline-flex",
1202
+ alignItems: "center",
1203
+ border: "1px solid var(--nv-border)",
1204
+ borderRadius: "var(--nv-radius)"
1205
+ },
1206
+ children: [
1207
+ /* @__PURE__ */ jsxRuntime.jsx(
1208
+ StepBtn,
1209
+ {
1210
+ label: "\u2212",
1211
+ onClick: () => line.quantity <= 1 ? removeLine(line.id) : updateLine(line.id, line.quantity - 1)
1212
+ }
1213
+ ),
1214
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { minWidth: 28, textAlign: "center", fontVariantNumeric: "tabular-nums" }, children: line.quantity }),
1215
+ /* @__PURE__ */ jsxRuntime.jsx(StepBtn, { label: "+", onClick: () => updateLine(line.id, line.quantity + 1) })
1216
+ ]
1217
+ }
1218
+ ),
1219
+ /* @__PURE__ */ jsxRuntime.jsx(
1220
+ "button",
1221
+ {
1222
+ type: "button",
1223
+ onClick: () => removeLine(line.id),
1224
+ style: {
1225
+ font: "inherit",
1226
+ fontSize: "0.8125em",
1227
+ background: "transparent",
1228
+ border: "none",
1229
+ color: "var(--nv-muted)",
1230
+ cursor: "pointer",
1231
+ padding: 0
1232
+ },
1233
+ children: t("cart.remove")
1234
+ }
1235
+ )
1236
+ ] })
1237
+ ] }),
1238
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontWeight: 500, whiteSpace: "nowrap" }, children: formatCents(line.line_total_cents, line.unit_price.currency, locale) })
1239
+ ] });
1240
+ }
1241
+ function StepBtn({ label, onClick }) {
1242
+ const t = useT();
1243
+ return /* @__PURE__ */ jsxRuntime.jsx(
1244
+ "button",
1245
+ {
1246
+ type: "button",
1247
+ "aria-label": label === "+" ? t("cart.increaseQty") : t("cart.decreaseQty"),
1248
+ onClick,
1249
+ style: {
1250
+ font: "inherit",
1251
+ width: 30,
1252
+ height: 30,
1253
+ lineHeight: 1,
1254
+ background: "transparent",
1255
+ border: "none",
1256
+ cursor: "pointer",
1257
+ color: "var(--nv-fg)"
1258
+ },
1259
+ children: label
1260
+ }
1261
+ );
1262
+ }
1263
+ function CartSummary({
1264
+ checkoutLabel,
1265
+ checkoutHref,
1266
+ onCheckout,
1267
+ locale
1268
+ }) {
1269
+ const tr = useT();
1270
+ const { cart } = useCart();
1271
+ if (cart == null) return null;
1272
+ const t = cart.totals;
1273
+ const empty = t.item_count === 0;
1274
+ const cta = checkoutLabel ?? tr("cart.checkout");
1275
+ const ctaStyle = {
1276
+ display: "block",
1277
+ textAlign: "center",
1278
+ font: "inherit",
1279
+ fontWeight: 500,
1280
+ cursor: empty ? "default" : "pointer",
1281
+ padding: "0.75rem 1.5rem",
1282
+ borderRadius: "var(--nv-radius)",
1283
+ border: "none",
1284
+ background: "var(--nv-brand)",
1285
+ color: "var(--nv-brand-foreground)",
1286
+ textDecoration: "none",
1287
+ opacity: empty ? 0.55 : 1
1288
+ };
1289
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.75rem" }, children: [
1290
+ /* @__PURE__ */ jsxRuntime.jsx(Row, { label: tr("cart.subtotal"), value: formatCents(t.subtotal_with_tax_cents, t.currency, locale) }),
1291
+ t.tax_cents > 0 ? /* @__PURE__ */ jsxRuntime.jsx(Row, { label: tr("cart.tax"), value: formatCents(t.tax_cents, t.currency, locale), muted: true }) : null,
1292
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: 1, background: "var(--nv-border)" } }),
1293
+ /* @__PURE__ */ jsxRuntime.jsx(Row, { label: tr("cart.total"), value: formatCents(t.total_cents, t.currency, locale), strong: true }),
1294
+ checkoutHref && !empty ? /* @__PURE__ */ jsxRuntime.jsx("a", { href: checkoutHref, style: ctaStyle, children: cta }) : /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", disabled: empty, onClick: onCheckout, style: ctaStyle, children: cta })
1295
+ ] });
1296
+ }
1297
+ function Row({
1298
+ label,
1299
+ value,
1300
+ strong,
1301
+ muted
1302
+ }) {
1303
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1304
+ "div",
1305
+ {
1306
+ style: {
1307
+ display: "flex",
1308
+ justifyContent: "space-between",
1309
+ fontWeight: strong ? 500 : 400,
1310
+ color: muted ? "var(--nv-muted)" : "inherit",
1311
+ fontSize: strong ? "1.05em" : "1em"
1312
+ },
1313
+ children: [
1314
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: label }),
1315
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontVariantNumeric: "tabular-nums" }, children: value })
1316
+ ]
1317
+ }
1318
+ );
1319
+ }
1320
+ function CartDrawer({
1321
+ open,
1322
+ onClose,
1323
+ title,
1324
+ ...summary
1325
+ }) {
1326
+ const t = useT();
1327
+ const { cart, loading } = useCart();
1328
+ const heading = title ?? t("cart.title");
1329
+ if (!open) return null;
1330
+ const lines = cart?.line_items ?? [];
1331
+ return /* @__PURE__ */ jsxRuntime.jsx(
1332
+ "div",
1333
+ {
1334
+ role: "dialog",
1335
+ "aria-label": heading,
1336
+ onClick: onClose,
1337
+ style: {
1338
+ position: "fixed",
1339
+ inset: 0,
1340
+ zIndex: 1e3,
1341
+ background: "rgba(0,0,0,0.4)",
1342
+ display: "flex",
1343
+ justifyContent: "flex-end"
1344
+ },
1345
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
1346
+ "div",
1347
+ {
1348
+ onClick: (e) => e.stopPropagation(),
1349
+ style: {
1350
+ width: "min(420px, 100%)",
1351
+ height: "100%",
1352
+ background: "var(--nv-bg)",
1353
+ color: "var(--nv-fg)",
1354
+ fontFamily: "var(--nv-font)",
1355
+ display: "flex",
1356
+ flexDirection: "column",
1357
+ boxShadow: "-8px 0 24px rgba(0,0,0,0.12)"
1358
+ },
1359
+ children: [
1360
+ /* @__PURE__ */ jsxRuntime.jsxs(
1361
+ "header",
1362
+ {
1363
+ style: {
1364
+ display: "flex",
1365
+ justifyContent: "space-between",
1366
+ alignItems: "center",
1367
+ padding: "1rem 1.25rem",
1368
+ borderBottom: "1px solid var(--nv-border)"
1369
+ },
1370
+ children: [
1371
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { style: { fontWeight: 500 }, children: heading }),
1372
+ /* @__PURE__ */ jsxRuntime.jsx(
1373
+ "button",
1374
+ {
1375
+ type: "button",
1376
+ "aria-label": t("common.close"),
1377
+ onClick: onClose,
1378
+ style: { font: "inherit", background: "transparent", border: "none", cursor: "pointer", fontSize: "1.25rem" },
1379
+ children: "\xD7"
1380
+ }
1381
+ )
1382
+ ]
1383
+ }
1384
+ ),
1385
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, overflowY: "auto", padding: "0 1.25rem" }, children: lines.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "var(--nv-muted)", padding: "2rem 0", textAlign: "center" }, children: loading ? t("common.loadingShort") : t("cart.empty") }) : lines.map((line, i) => /* @__PURE__ */ jsxRuntime.jsx(
1386
+ "div",
1387
+ {
1388
+ style: i > 0 ? { borderTop: "1px solid var(--nv-border)" } : void 0,
1389
+ children: /* @__PURE__ */ jsxRuntime.jsx(CartLineItem, { line, locale: summary.locale })
1390
+ },
1391
+ line.id
1392
+ )) }),
1393
+ lines.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "1.25rem", borderTop: "1px solid var(--nv-border)" }, children: /* @__PURE__ */ jsxRuntime.jsx(CartSummary, { ...summary }) }) : null
1394
+ ]
1395
+ }
1396
+ )
1397
+ }
1398
+ );
1399
+ }
1400
+ var CheckoutContext = React10__namespace.createContext(null);
1401
+ function CheckoutProvider({
1402
+ children
1403
+ }) {
1404
+ const client = useStorefront();
1405
+ const [checkout, setCheckout] = React10__namespace.useState(null);
1406
+ const [loading, setLoading] = React10__namespace.useState(false);
1407
+ const [error, setError] = React10__namespace.useState(null);
1408
+ const run = React10__namespace.useCallback(
1409
+ async (op, store) => {
1410
+ setLoading(true);
1411
+ try {
1412
+ const result = await op();
1413
+ const next = store(result);
1414
+ if (next) setCheckout(next);
1415
+ setError(null);
1416
+ return result;
1417
+ } catch (err) {
1418
+ setError(err instanceof Error ? err : new Error(String(err)));
1419
+ throw err;
1420
+ } finally {
1421
+ setLoading(false);
1422
+ }
1423
+ },
1424
+ []
1425
+ );
1426
+ const refresh = React10__namespace.useCallback(async () => {
1427
+ setLoading(true);
1428
+ try {
1429
+ setCheckout(await client.checkout.get());
1430
+ setError(null);
1431
+ } finally {
1432
+ setLoading(false);
1433
+ }
1434
+ }, [client]);
1435
+ React10__namespace.useEffect(() => {
1436
+ void refresh();
1437
+ }, [refresh]);
1438
+ const pay = React10__namespace.useCallback(
1439
+ async (opts = {}) => {
1440
+ const co = checkout;
1441
+ if (!co) throw new Error("pay() called before a checkout exists");
1442
+ const idempotencyKey = opts.idempotencyKey ?? co.token;
1443
+ if (co.payment_category === "gateway") {
1444
+ const start = await run(
1445
+ () => client.checkout.startPayment({ return_url: opts.returnUrl, cancel_url: opts.cancelUrl }),
1446
+ () => null
1447
+ );
1448
+ if (!start.redirect_url) throw new Error("gateway returned no redirect_url");
1449
+ const shouldRedirect = opts.redirect !== false && typeof window !== "undefined";
1450
+ if (shouldRedirect) window.location.assign(start.redirect_url);
1451
+ return { kind: "redirect", url: start.redirect_url };
1452
+ }
1453
+ const done = await run(
1454
+ () => client.checkout.complete({ idempotencyKey }),
1455
+ (r) => r
1456
+ );
1457
+ return { kind: "order", checkout: done };
1458
+ },
1459
+ [checkout, client, run]
1460
+ );
1461
+ const value = React10__namespace.useMemo(
1462
+ () => ({
1463
+ checkout,
1464
+ loading,
1465
+ error,
1466
+ create: (cartToken, input) => run(() => client.checkout.create(cartToken, input), (r) => r),
1467
+ setAddress: (input) => run(() => client.checkout.setAddress(input), (r) => r),
1468
+ shippingQuotes: () => run(() => client.checkout.shippingQuotes(), () => null),
1469
+ setShippingMethod: (methodId, rateId) => run(() => client.checkout.setShippingMethod(methodId, rateId), (r) => r),
1470
+ applyDiscount: (code) => run(() => client.checkout.applyDiscount(code), (r) => r),
1471
+ removeDiscount: (code) => run(() => client.checkout.removeDiscount(code), (r) => r),
1472
+ paymentMethods: () => run(() => client.checkout.paymentMethods(), () => null),
1473
+ setPaymentMethod: (method) => run(() => client.checkout.setPaymentMethod(method), (r) => r),
1474
+ pay,
1475
+ refresh,
1476
+ clear: () => {
1477
+ client.checkout.clear();
1478
+ setCheckout(null);
1479
+ }
1480
+ }),
1481
+ [checkout, loading, error, client, run, pay, refresh]
1482
+ );
1483
+ return /* @__PURE__ */ jsxRuntime.jsx(CheckoutContext.Provider, { value, children });
1484
+ }
1485
+ function useCheckout() {
1486
+ const ctx = React10__namespace.useContext(CheckoutContext);
1487
+ if (!ctx) {
1488
+ throw new Error("useCheckout must be used inside <CheckoutProvider>.");
1489
+ }
1490
+ return ctx;
1491
+ }
1492
+ function Field({ label, style, ...input }) {
1493
+ return /* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "flex", flexDirection: "column", gap: "0.35rem", flex: 1 }, children: [
1494
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.8125rem", color: "var(--nv-muted)" }, children: label }),
1495
+ /* @__PURE__ */ jsxRuntime.jsx(
1496
+ "input",
1497
+ {
1498
+ ...input,
1499
+ style: {
1500
+ font: "inherit",
1501
+ padding: "0.6rem 0.75rem",
1502
+ borderRadius: "var(--nv-radius)",
1503
+ border: "1px solid var(--nv-border)",
1504
+ background: "var(--nv-bg)",
1505
+ color: "var(--nv-fg)",
1506
+ width: "100%",
1507
+ boxSizing: "border-box",
1508
+ ...style
1509
+ }
1510
+ }
1511
+ )
1512
+ ] });
1513
+ }
1514
+ function FieldRow({ children }) {
1515
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", gap: "0.75rem", flexWrap: "wrap" }, children });
1516
+ }
1517
+ function Button({
1518
+ busy,
1519
+ disabled,
1520
+ children,
1521
+ style,
1522
+ ...rest
1523
+ }) {
1524
+ const off = disabled || busy;
1525
+ return /* @__PURE__ */ jsxRuntime.jsx(
1526
+ "button",
1527
+ {
1528
+ type: "button",
1529
+ disabled: off,
1530
+ ...rest,
1531
+ style: {
1532
+ font: "inherit",
1533
+ fontWeight: 500,
1534
+ cursor: off ? "default" : "pointer",
1535
+ padding: "0.75rem 1.5rem",
1536
+ borderRadius: "var(--nv-radius)",
1537
+ border: "none",
1538
+ background: "var(--nv-brand)",
1539
+ color: "var(--nv-brand-foreground)",
1540
+ opacity: off ? 0.55 : 1,
1541
+ ...style
1542
+ },
1543
+ children: busy ? "\u2026" : children
1544
+ }
1545
+ );
1546
+ }
1547
+ function SelectRow({
1548
+ selected,
1549
+ onSelect,
1550
+ children,
1551
+ trailing,
1552
+ disabled
1553
+ }) {
1554
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1555
+ "button",
1556
+ {
1557
+ type: "button",
1558
+ role: "radio",
1559
+ "aria-checked": selected,
1560
+ disabled,
1561
+ onClick: onSelect,
1562
+ style: {
1563
+ display: "flex",
1564
+ alignItems: "center",
1565
+ justifyContent: "space-between",
1566
+ gap: "0.75rem",
1567
+ width: "100%",
1568
+ textAlign: "left",
1569
+ font: "inherit",
1570
+ cursor: disabled ? "default" : "pointer",
1571
+ padding: "0.85rem 1rem",
1572
+ borderRadius: "var(--nv-radius)",
1573
+ border: `1px solid ${selected ? "var(--nv-brand)" : "var(--nv-border)"}`,
1574
+ boxShadow: selected ? "0 0 0 1px var(--nv-brand) inset" : "none",
1575
+ background: "var(--nv-bg)",
1576
+ color: "var(--nv-fg)",
1577
+ opacity: disabled ? 0.55 : 1
1578
+ },
1579
+ children: [
1580
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "flex", alignItems: "center", gap: "0.6rem" }, children: [
1581
+ /* @__PURE__ */ jsxRuntime.jsx(
1582
+ "span",
1583
+ {
1584
+ "aria-hidden": true,
1585
+ style: {
1586
+ width: "1rem",
1587
+ height: "1rem",
1588
+ borderRadius: "50%",
1589
+ border: `1px solid ${selected ? "var(--nv-brand)" : "var(--nv-border)"}`,
1590
+ background: selected ? "radial-gradient(var(--nv-brand) 0 45%, transparent 50%)" : "transparent",
1591
+ flexShrink: 0
1592
+ }
1593
+ }
1594
+ ),
1595
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children })
1596
+ ] }),
1597
+ trailing != null ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontVariantNumeric: "tabular-nums" }, children: trailing }) : null
1598
+ ]
1599
+ }
1600
+ );
1601
+ }
1602
+ function Section({
1603
+ title,
1604
+ children
1605
+ }) {
1606
+ return /* @__PURE__ */ jsxRuntime.jsxs("section", { style: { display: "flex", flexDirection: "column", gap: "0.85rem" }, children: [
1607
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: { margin: 0, fontSize: "1.05rem", fontWeight: 600 }, children: title }),
1608
+ children
1609
+ ] });
1610
+ }
1611
+ var EMPTY = {
1612
+ email: "",
1613
+ first_name: "",
1614
+ last_name: "",
1615
+ company: "",
1616
+ address1: "",
1617
+ address2: "",
1618
+ city: "",
1619
+ postal_code: "",
1620
+ country_code: "",
1621
+ phone: ""
1622
+ };
1623
+ function seed(email, addr) {
1624
+ return {
1625
+ ...EMPTY,
1626
+ email: email ?? "",
1627
+ first_name: addr?.first_name ?? "",
1628
+ last_name: addr?.last_name ?? "",
1629
+ company: addr?.company ?? "",
1630
+ address1: addr?.address1 ?? "",
1631
+ address2: addr?.address2 ?? "",
1632
+ city: addr?.city ?? "",
1633
+ postal_code: addr?.postal_code ?? "",
1634
+ country_code: addr?.country_code ?? "",
1635
+ phone: addr?.phone ?? ""
1636
+ };
1637
+ }
1638
+ function AddressForm({
1639
+ title,
1640
+ submitLabel,
1641
+ onComplete
1642
+ }) {
1643
+ const t = useT();
1644
+ const { checkout, setAddress, loading } = useCheckout();
1645
+ const [form, setForm] = React10__namespace.useState(
1646
+ () => seed(checkout?.email ?? null, checkout?.shipping_address ?? null)
1647
+ );
1648
+ const set = (key) => (e) => setForm((f) => ({ ...f, [key]: e.target.value }));
1649
+ async function submit(e) {
1650
+ e.preventDefault();
1651
+ const { email, ...rest } = form;
1652
+ await setAddress({
1653
+ email,
1654
+ shipping_address: rest,
1655
+ same_as_shipping: true
1656
+ });
1657
+ onComplete?.();
1658
+ }
1659
+ return /* @__PURE__ */ jsxRuntime.jsx("form", { onSubmit: submit, children: /* @__PURE__ */ jsxRuntime.jsxs(Section, { title: title ?? t("checkout.shippingAddress"), children: [
1660
+ /* @__PURE__ */ jsxRuntime.jsx(Field, { label: t("checkout.email"), type: "email", required: true, value: form.email, onChange: set("email"), autoComplete: "email" }),
1661
+ /* @__PURE__ */ jsxRuntime.jsxs(FieldRow, { children: [
1662
+ /* @__PURE__ */ jsxRuntime.jsx(Field, { label: t("checkout.firstName"), required: true, value: form.first_name, onChange: set("first_name"), autoComplete: "given-name" }),
1663
+ /* @__PURE__ */ jsxRuntime.jsx(Field, { label: t("checkout.lastName"), required: true, value: form.last_name, onChange: set("last_name"), autoComplete: "family-name" })
1664
+ ] }),
1665
+ /* @__PURE__ */ jsxRuntime.jsx(Field, { label: t("checkout.company"), value: form.company, onChange: set("company"), autoComplete: "organization" }),
1666
+ /* @__PURE__ */ jsxRuntime.jsx(Field, { label: t("checkout.address"), required: true, value: form.address1, onChange: set("address1"), autoComplete: "address-line1" }),
1667
+ /* @__PURE__ */ jsxRuntime.jsx(Field, { label: t("checkout.apartment"), value: form.address2, onChange: set("address2"), autoComplete: "address-line2" }),
1668
+ /* @__PURE__ */ jsxRuntime.jsxs(FieldRow, { children: [
1669
+ /* @__PURE__ */ jsxRuntime.jsx(Field, { label: t("checkout.city"), required: true, value: form.city, onChange: set("city"), autoComplete: "address-level2" }),
1670
+ /* @__PURE__ */ jsxRuntime.jsx(Field, { label: t("checkout.postalCode"), required: true, value: form.postal_code, onChange: set("postal_code"), autoComplete: "postal-code" }),
1671
+ /* @__PURE__ */ jsxRuntime.jsx(
1672
+ Field,
1673
+ {
1674
+ label: t("checkout.country"),
1675
+ required: true,
1676
+ value: form.country_code,
1677
+ onChange: set("country_code"),
1678
+ placeholder: "CZ",
1679
+ autoComplete: "country",
1680
+ maxLength: 2,
1681
+ style: { textTransform: "uppercase", maxWidth: "6rem" }
1682
+ }
1683
+ )
1684
+ ] }),
1685
+ /* @__PURE__ */ jsxRuntime.jsx(Field, { label: t("checkout.phone"), type: "tel", value: form.phone, onChange: set("phone"), autoComplete: "tel" }),
1686
+ /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "submit", busy: loading, children: submitLabel ?? t("checkout.continueToShipping") }) })
1687
+ ] }) });
1688
+ }
1689
+ function ShippingPicker({
1690
+ title,
1691
+ submitLabel,
1692
+ onComplete,
1693
+ locale
1694
+ }) {
1695
+ const t = useT();
1696
+ const { checkout, shippingQuotes, setShippingMethod, loading } = useCheckout();
1697
+ const heading = title ?? t("checkout.shippingMethod");
1698
+ const [quotes, setQuotes] = React10__namespace.useState(null);
1699
+ const [selected, setSelected] = React10__namespace.useState(null);
1700
+ const [fetching, setFetching] = React10__namespace.useState(true);
1701
+ const addressKey = checkout?.shipping_address ? `${checkout.shipping_address.country_code}:${checkout.shipping_address.postal_code}` : null;
1702
+ React10__namespace.useEffect(() => {
1703
+ let live = true;
1704
+ setFetching(true);
1705
+ shippingQuotes().then((qs) => {
1706
+ if (!live) return;
1707
+ setQuotes(qs);
1708
+ const savedLabel = checkout?.shipping_method?.rate_display_name;
1709
+ const match = savedLabel ? qs.find((q) => q.label === savedLabel) : void 0;
1710
+ setSelected(match?.rate_id ?? (qs.length === 1 ? qs[0].rate_id : null));
1711
+ }).catch(() => live && setQuotes([])).finally(() => live && setFetching(false));
1712
+ return () => {
1713
+ live = false;
1714
+ };
1715
+ }, [addressKey]);
1716
+ async function submit() {
1717
+ const q = quotes?.find((x) => x.rate_id === selected);
1718
+ if (!q) return;
1719
+ await setShippingMethod(q.method_id, q.rate_id);
1720
+ onComplete?.();
1721
+ }
1722
+ if (fetching && quotes == null) {
1723
+ return /* @__PURE__ */ jsxRuntime.jsx(Section, { title: heading, children: /* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "var(--nv-muted)", margin: 0 }, children: t("checkout.loadingShipping") }) });
1724
+ }
1725
+ if (!quotes || quotes.length === 0) {
1726
+ return /* @__PURE__ */ jsxRuntime.jsx(Section, { title: heading, children: /* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "var(--nv-muted)", margin: 0 }, children: t("checkout.noShipping") }) });
1727
+ }
1728
+ return /* @__PURE__ */ jsxRuntime.jsxs(Section, { title: heading, children: [
1729
+ /* @__PURE__ */ jsxRuntime.jsx("div", { role: "radiogroup", style: { display: "flex", flexDirection: "column", gap: "0.6rem" }, children: quotes.map((q) => {
1730
+ const days = estDays(q, t);
1731
+ return /* @__PURE__ */ jsxRuntime.jsx(
1732
+ SelectRow,
1733
+ {
1734
+ selected: selected === q.rate_id,
1735
+ onSelect: () => setSelected(q.rate_id),
1736
+ trailing: q.is_free ? t("common.free") : formatCents(q.price_cents, q.currency, locale),
1737
+ children: /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "flex", flexDirection: "column" }, children: [
1738
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 500 }, children: q.label }),
1739
+ days ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.8125rem", color: "var(--nv-muted)" }, children: days }) : null
1740
+ ] })
1741
+ },
1742
+ q.rate_id
1743
+ );
1744
+ }) }),
1745
+ /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(Button, { onClick: submit, busy: loading, disabled: selected == null, children: submitLabel ?? t("checkout.continueToPayment") }) })
1746
+ ] });
1747
+ }
1748
+ function estDays(q, t) {
1749
+ const { estimated_days_min: lo, estimated_days_max: hi } = q;
1750
+ if (lo == null && hi == null) return null;
1751
+ if (lo != null && hi != null) {
1752
+ return lo === hi ? t("shipping.days", { count: lo }) : t("shipping.daysRange", { min: lo, max: hi });
1753
+ }
1754
+ const n = lo ?? hi;
1755
+ return t("shipping.days", { count: n });
1756
+ }
1757
+ function PaymentSection({
1758
+ title,
1759
+ returnUrl,
1760
+ cancelUrl,
1761
+ onOrderPlaced,
1762
+ locale
1763
+ }) {
1764
+ const t = useT();
1765
+ const heading = title ?? t("checkout.payment");
1766
+ const { checkout, paymentMethods, setPaymentMethod, pay, loading } = useCheckout();
1767
+ const [methods, setMethods] = React10__namespace.useState(null);
1768
+ const [fetching, setFetching] = React10__namespace.useState(true);
1769
+ const selected = checkout?.payment_method ?? null;
1770
+ React10__namespace.useEffect(() => {
1771
+ let live = true;
1772
+ setFetching(true);
1773
+ paymentMethods().then((ms) => {
1774
+ if (!live) return;
1775
+ setMethods(ms);
1776
+ if (ms.length === 1 && checkout?.payment_method == null) {
1777
+ void setPaymentMethod(ms[0].method);
1778
+ }
1779
+ }).catch(() => live && setMethods([])).finally(() => live && setFetching(false));
1780
+ return () => {
1781
+ live = false;
1782
+ };
1783
+ }, []);
1784
+ const selectedMethod = methods?.find((m) => m.method === selected) ?? null;
1785
+ const isGateway = selectedMethod?.category === "gateway";
1786
+ async function placeOrder() {
1787
+ const result = await pay({ returnUrl, cancelUrl });
1788
+ if (result.kind === "order") onOrderPlaced?.(result.checkout);
1789
+ }
1790
+ if (fetching && methods == null) {
1791
+ return /* @__PURE__ */ jsxRuntime.jsx(Section, { title: heading, children: /* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "var(--nv-muted)", margin: 0 }, children: t("checkout.loadingPayment") }) });
1792
+ }
1793
+ if (!methods || methods.length === 0) {
1794
+ return /* @__PURE__ */ jsxRuntime.jsx(Section, { title: heading, children: /* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "var(--nv-muted)", margin: 0 }, children: t("checkout.noPayment") }) });
1795
+ }
1796
+ return /* @__PURE__ */ jsxRuntime.jsxs(Section, { title: heading, children: [
1797
+ /* @__PURE__ */ jsxRuntime.jsx("div", { role: "radiogroup", style: { display: "flex", flexDirection: "column", gap: "0.6rem" }, children: methods.map((m) => /* @__PURE__ */ jsxRuntime.jsx(
1798
+ SelectRow,
1799
+ {
1800
+ selected: selected === m.method,
1801
+ onSelect: () => void setPaymentMethod(m.method),
1802
+ trailing: m.surcharge_cents > 0 && checkout ? `+${formatCents(m.surcharge_cents, checkout.totals.currency, locale)}` : void 0,
1803
+ children: /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "flex", flexDirection: "column" }, children: [
1804
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 500 }, children: m.display_label }),
1805
+ m.display_description ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.8125rem", color: "var(--nv-muted)" }, children: m.display_description }) : null
1806
+ ] })
1807
+ },
1808
+ m.method
1809
+ )) }),
1810
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1811
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { onClick: placeOrder, busy: loading, disabled: selected == null, children: isGateway ? t("checkout.payNow") : t("checkout.placeOrder") }),
1812
+ isGateway ? /* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: "0.8125rem", color: "var(--nv-muted)", marginTop: "0.6rem" }, children: t("checkout.gatewayRedirectNote") }) : null
1813
+ ] })
1814
+ ] });
1815
+ }
1816
+ function OrderSummary({
1817
+ checkout: override,
1818
+ totalsOnly = false,
1819
+ locale
1820
+ }) {
1821
+ const tr = useT();
1822
+ const ctx = useCheckout();
1823
+ const checkout = override ?? ctx.checkout;
1824
+ if (checkout == null) return null;
1825
+ const t = checkout.totals;
1826
+ const c = t.currency;
1827
+ const money = (cents) => formatCents(cents, c, locale);
1828
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "1rem" }, children: [
1829
+ !totalsOnly && checkout.line_items.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "flex", flexDirection: "column", gap: "0.75rem" }, children: checkout.line_items.map((li) => /* @__PURE__ */ jsxRuntime.jsxs("li", { style: { display: "flex", gap: "0.75rem", alignItems: "center" }, children: [
1830
+ li.image_url ? /* @__PURE__ */ jsxRuntime.jsx(
1831
+ "img",
1832
+ {
1833
+ src: li.image_url,
1834
+ alt: "",
1835
+ width: 48,
1836
+ height: 48,
1837
+ style: { borderRadius: "var(--nv-radius)", objectFit: "cover", flexShrink: 0 }
1838
+ }
1839
+ ) : null,
1840
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { flex: 1, minWidth: 0 }, children: [
1841
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { display: "block", fontWeight: 500 }, children: li.title }),
1842
+ li.variant_title ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: { display: "block", fontSize: "0.8125rem", color: "var(--nv-muted)" }, children: li.variant_title }) : null,
1843
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontSize: "0.8125rem", color: "var(--nv-muted)" }, children: [
1844
+ "\xD7",
1845
+ li.quantity
1846
+ ] })
1847
+ ] }),
1848
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontVariantNumeric: "tabular-nums" }, children: money(li.line_total_cents) })
1849
+ ] }, li.id)) }) : null,
1850
+ !totalsOnly ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: 1, background: "var(--nv-border)" } }) : null,
1851
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
1852
+ /* @__PURE__ */ jsxRuntime.jsx(Row2, { label: tr("totals.subtotal"), value: money(t.subtotal_with_tax_cents) }),
1853
+ t.discount_cents > 0 ? /* @__PURE__ */ jsxRuntime.jsx(Row2, { label: tr("totals.discount"), value: `\u2212${money(t.discount_cents)}`, muted: true }) : null,
1854
+ checkout.shipping_method != null || t.shipping_cents > 0 ? /* @__PURE__ */ jsxRuntime.jsx(Row2, { label: tr("totals.shipping"), value: t.shipping_cents > 0 ? money(t.shipping_cents) : tr("common.free"), muted: true }) : null,
1855
+ t.surcharge_cents > 0 ? /* @__PURE__ */ jsxRuntime.jsx(Row2, { label: tr("totals.paymentFee"), value: money(t.surcharge_cents), muted: true }) : null,
1856
+ t.tax_cents > 0 ? /* @__PURE__ */ jsxRuntime.jsx(Row2, { label: tr("totals.taxIncluded"), value: money(t.tax_cents), muted: true }) : null,
1857
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: 1, background: "var(--nv-border)" } }),
1858
+ /* @__PURE__ */ jsxRuntime.jsx(Row2, { label: tr("totals.total"), value: money(t.total_cents), strong: true })
1859
+ ] })
1860
+ ] });
1861
+ }
1862
+ function Row2({
1863
+ label,
1864
+ value,
1865
+ strong,
1866
+ muted
1867
+ }) {
1868
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1869
+ "div",
1870
+ {
1871
+ style: {
1872
+ display: "flex",
1873
+ justifyContent: "space-between",
1874
+ fontWeight: strong ? 600 : 400,
1875
+ color: muted ? "var(--nv-muted)" : "inherit",
1876
+ fontSize: strong ? "1.1em" : "1em"
1877
+ },
1878
+ children: [
1879
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: label }),
1880
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontVariantNumeric: "tabular-nums" }, children: value })
1881
+ ]
1882
+ }
1883
+ );
1884
+ }
1885
+ var STEPS = ["address", "shipping", "payment"];
1886
+ var STEP_KEY = {
1887
+ address: "checkout.stepAddress",
1888
+ shipping: "checkout.stepShipping",
1889
+ payment: "checkout.stepPayment"
1890
+ };
1891
+ function reachedStep(checkout) {
1892
+ if (!checkout) return "address";
1893
+ if (checkout.shipping_method != null) return "payment";
1894
+ if (checkout.shipping_address != null) return "shipping";
1895
+ return "address";
1896
+ }
1897
+ function CheckoutFlow({
1898
+ cartToken,
1899
+ onOrderPlaced,
1900
+ emptyState,
1901
+ renderConfirmation,
1902
+ returnUrl,
1903
+ cancelUrl,
1904
+ locale
1905
+ }) {
1906
+ const t = useT();
1907
+ const { checkout, loading, create } = useCheckout();
1908
+ const [step, setStep] = React10__namespace.useState("address");
1909
+ const [bootstrapped, setBootstrapped] = React10__namespace.useState(false);
1910
+ React10__namespace.useEffect(() => {
1911
+ if (checkout || !cartToken || bootstrapped) return;
1912
+ setBootstrapped(true);
1913
+ void create(cartToken).catch(() => {
1914
+ });
1915
+ }, [checkout, cartToken, bootstrapped, create]);
1916
+ const serverStep = reachedStep(checkout);
1917
+ React10__namespace.useEffect(() => {
1918
+ setStep((cur) => rank(serverStep) > rank(cur) ? serverStep : cur);
1919
+ }, [serverStep]);
1920
+ if (checkout?.order_id) {
1921
+ return /* @__PURE__ */ jsxRuntime.jsx(Shell, { summary: /* @__PURE__ */ jsxRuntime.jsx(OrderSummary, { locale }), children: renderConfirmation ? renderConfirmation(checkout) : /* @__PURE__ */ jsxRuntime.jsx(DefaultConfirmation, { checkout }) });
1922
+ }
1923
+ if (!checkout) {
1924
+ if (loading || cartToken && !bootstrapped) {
1925
+ return /* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "var(--nv-muted)" }, children: t("checkout.loading") });
1926
+ }
1927
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: emptyState ?? /* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "var(--nv-muted)" }, children: t("checkout.empty") }) });
1928
+ }
1929
+ return /* @__PURE__ */ jsxRuntime.jsxs(Shell, { summary: /* @__PURE__ */ jsxRuntime.jsx(OrderSummary, { locale }), children: [
1930
+ /* @__PURE__ */ jsxRuntime.jsx(Steps, { current: step, reached: serverStep, onJump: setStep }),
1931
+ step === "address" ? /* @__PURE__ */ jsxRuntime.jsx(AddressForm, { onComplete: () => setStep("shipping") }) : step === "shipping" ? /* @__PURE__ */ jsxRuntime.jsx(ShippingPicker, { onComplete: () => setStep("payment"), locale }) : /* @__PURE__ */ jsxRuntime.jsx(
1932
+ PaymentSection,
1933
+ {
1934
+ returnUrl,
1935
+ cancelUrl,
1936
+ onOrderPlaced,
1937
+ locale
1938
+ }
1939
+ )
1940
+ ] });
1941
+ }
1942
+ function rank(step) {
1943
+ return STEPS.indexOf(step);
1944
+ }
1945
+ function Shell({
1946
+ children,
1947
+ summary
1948
+ }) {
1949
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1950
+ "div",
1951
+ {
1952
+ style: {
1953
+ display: "grid",
1954
+ gridTemplateColumns: "minmax(0, 1fr) minmax(0, 22rem)",
1955
+ gap: "2.5rem",
1956
+ alignItems: "start",
1957
+ fontFamily: "var(--nv-font)",
1958
+ color: "var(--nv-fg)"
1959
+ },
1960
+ children: [
1961
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: "1.5rem", minWidth: 0 }, children }),
1962
+ /* @__PURE__ */ jsxRuntime.jsx(
1963
+ "aside",
1964
+ {
1965
+ style: {
1966
+ position: "sticky",
1967
+ top: "1.5rem",
1968
+ padding: "1.25rem",
1969
+ borderRadius: "var(--nv-radius)",
1970
+ border: "1px solid var(--nv-border)",
1971
+ background: "var(--nv-bg)"
1972
+ },
1973
+ children: summary
1974
+ }
1975
+ )
1976
+ ]
1977
+ }
1978
+ );
1979
+ }
1980
+ function Steps({
1981
+ current,
1982
+ reached,
1983
+ onJump
1984
+ }) {
1985
+ const t = useT();
1986
+ return /* @__PURE__ */ jsxRuntime.jsx(
1987
+ "ol",
1988
+ {
1989
+ style: {
1990
+ listStyle: "none",
1991
+ display: "flex",
1992
+ gap: "0.5rem",
1993
+ margin: 0,
1994
+ padding: 0,
1995
+ fontSize: "0.875rem"
1996
+ },
1997
+ children: STEPS.map((s, i) => {
1998
+ const active = s === current;
1999
+ const visitable = rank(s) <= rank(reached);
2000
+ return /* @__PURE__ */ jsxRuntime.jsxs("li", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
2001
+ /* @__PURE__ */ jsxRuntime.jsxs(
2002
+ "button",
2003
+ {
2004
+ type: "button",
2005
+ disabled: !visitable,
2006
+ onClick: () => visitable && onJump(s),
2007
+ style: {
2008
+ font: "inherit",
2009
+ fontWeight: active ? 600 : 400,
2010
+ color: active ? "var(--nv-fg)" : "var(--nv-muted)",
2011
+ background: "none",
2012
+ border: "none",
2013
+ padding: 0,
2014
+ cursor: visitable && !active ? "pointer" : "default"
2015
+ },
2016
+ children: [
2017
+ i + 1,
2018
+ ". ",
2019
+ t(STEP_KEY[s])
2020
+ ]
2021
+ }
2022
+ ),
2023
+ i < STEPS.length - 1 ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--nv-muted)" }, children: "\u2192" }) : null
2024
+ ] }, s);
2025
+ })
2026
+ }
2027
+ );
2028
+ }
2029
+ function DefaultConfirmation({ checkout }) {
2030
+ const t = useT();
2031
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.75rem" }, children: [
2032
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: { margin: 0, fontSize: "1.3rem", fontWeight: 600 }, children: t("checkout.thankYou") }),
2033
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: 0, color: "var(--nv-muted)" }, children: t("checkout.orderPlacedBody", { order: `#${checkout.order_id}`, email: checkout.email ?? "" }) })
2034
+ ] });
2035
+ }
2036
+ function useOrderStatus(statusToken, initial) {
2037
+ const client = useStorefront();
2038
+ const [order, setOrder] = React10__namespace.useState(initial ?? null);
2039
+ const [loading, setLoading] = React10__namespace.useState(false);
2040
+ const [error, setError] = React10__namespace.useState(null);
2041
+ const load = React10__namespace.useCallback(async () => {
2042
+ if (!statusToken) return;
2043
+ setLoading(true);
2044
+ try {
2045
+ setOrder(await client.order.byStatusToken(statusToken));
2046
+ setError(null);
2047
+ } catch (e) {
2048
+ setError(e instanceof Error ? e : new Error(String(e)));
2049
+ } finally {
2050
+ setLoading(false);
2051
+ }
2052
+ }, [client, statusToken]);
2053
+ React10__namespace.useEffect(() => {
2054
+ if (initial) return;
2055
+ void load();
2056
+ }, [load, initial]);
2057
+ return { order, loading, error, reload: () => void load() };
2058
+ }
2059
+ var PHASE_RANK = {
2060
+ placed: 0,
2061
+ paid: 1,
2062
+ fulfilled: 2,
2063
+ shipped: 3,
2064
+ out_for_delivery: 4,
2065
+ delivered: 5,
2066
+ // terminal — never reached on the timeline
2067
+ returned: -1,
2068
+ refunded: -1,
2069
+ cancelled: -1
2070
+ };
2071
+ var MILESTONES = [
2072
+ { key: "order.phase.placed", rank: 0 },
2073
+ { key: "order.phase.paid", rank: 1 },
2074
+ { key: "order.phase.fulfilled", rank: 2 },
2075
+ { key: "order.phase.shipped", rank: 3 },
2076
+ { key: "order.phase.delivered", rank: 5 }
2077
+ ];
2078
+ var TERMINAL = {
2079
+ cancelled: { key: "order.terminal.cancelled", tone: "var(--nv-muted)" },
2080
+ refunded: { key: "order.terminal.refunded", tone: "var(--nv-muted)" },
2081
+ returned: { key: "order.terminal.returned", tone: "var(--nv-muted)" }
2082
+ };
2083
+ function isTerminal(phase) {
2084
+ return phase in TERMINAL;
2085
+ }
2086
+ function PhaseTimeline({ phase }) {
2087
+ const t = useT();
2088
+ if (isTerminal(phase)) return null;
2089
+ const current = PHASE_RANK[phase];
2090
+ return /* @__PURE__ */ jsxRuntime.jsx(
2091
+ "ol",
2092
+ {
2093
+ style: {
2094
+ listStyle: "none",
2095
+ display: "flex",
2096
+ margin: 0,
2097
+ padding: 0,
2098
+ gap: "0.25rem"
2099
+ },
2100
+ children: MILESTONES.map((m, i) => {
2101
+ const done = current >= m.rank;
2102
+ const active = m.rank === current || i === MILESTONES.length - 1 && current >= m.rank;
2103
+ return /* @__PURE__ */ jsxRuntime.jsxs("li", { style: { flex: 1, display: "flex", flexDirection: "column", gap: "0.4rem" }, children: [
2104
+ /* @__PURE__ */ jsxRuntime.jsx(
2105
+ "span",
2106
+ {
2107
+ "aria-hidden": true,
2108
+ style: {
2109
+ height: "0.35rem",
2110
+ borderRadius: "999px",
2111
+ background: done ? "var(--nv-brand)" : "var(--nv-border)"
2112
+ }
2113
+ }
2114
+ ),
2115
+ /* @__PURE__ */ jsxRuntime.jsx(
2116
+ "span",
2117
+ {
2118
+ style: {
2119
+ fontSize: "0.8125rem",
2120
+ fontWeight: active ? 600 : 400,
2121
+ color: done ? "var(--nv-fg)" : "var(--nv-muted)"
2122
+ },
2123
+ children: t(m.key)
2124
+ }
2125
+ )
2126
+ ] }, m.key);
2127
+ })
2128
+ }
2129
+ );
2130
+ }
2131
+ function StatusBanner({ phase }) {
2132
+ const t = useT();
2133
+ const meta = TERMINAL[phase];
2134
+ if (!meta) return null;
2135
+ return /* @__PURE__ */ jsxRuntime.jsx(
2136
+ "div",
2137
+ {
2138
+ style: {
2139
+ padding: "0.85rem 1rem",
2140
+ borderRadius: "var(--nv-radius)",
2141
+ border: "1px solid var(--nv-border)",
2142
+ background: "var(--nv-bg)",
2143
+ color: meta.tone,
2144
+ fontWeight: 500
2145
+ },
2146
+ children: t(meta.key)
2147
+ }
2148
+ );
2149
+ }
2150
+ function OrderLines({
2151
+ order,
2152
+ locale
2153
+ }) {
2154
+ return /* @__PURE__ */ jsxRuntime.jsxs("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "flex", flexDirection: "column", gap: "0.75rem" }, children: [
2155
+ order.line_items.map((li) => /* @__PURE__ */ jsxRuntime.jsxs("li", { style: { display: "flex", gap: "0.75rem", alignItems: "center" }, children: [
2156
+ li.image_url ? /* @__PURE__ */ jsxRuntime.jsx(
2157
+ "img",
2158
+ {
2159
+ src: li.image_url,
2160
+ alt: "",
2161
+ width: 48,
2162
+ height: 48,
2163
+ style: { borderRadius: "var(--nv-radius)", objectFit: "cover", flexShrink: 0 }
2164
+ }
2165
+ ) : null,
2166
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { flex: 1, minWidth: 0 }, children: [
2167
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { display: "block", fontWeight: 500 }, children: li.title }),
2168
+ li.variant_title ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: { display: "block", fontSize: "0.8125rem", color: "var(--nv-muted)" }, children: li.variant_title }) : null,
2169
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontSize: "0.8125rem", color: "var(--nv-muted)" }, children: [
2170
+ "\xD7",
2171
+ li.quantity
2172
+ ] })
2173
+ ] }),
2174
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontVariantNumeric: "tabular-nums" }, children: formatCents(li.total_with_tax_cents, order.currency, locale) })
2175
+ ] }, li.id)),
2176
+ order.fee_lines.map((f, i) => /* @__PURE__ */ jsxRuntime.jsxs("li", { style: { display: "flex", gap: "0.75rem", alignItems: "center" }, children: [
2177
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { flex: 1, minWidth: 0 }, children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: { display: "block", fontWeight: 500 }, children: f.title }) }),
2178
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontVariantNumeric: "tabular-nums" }, children: formatCents(f.total_with_tax_cents, order.currency, locale) })
2179
+ ] }, `fee-${i}`))
2180
+ ] });
2181
+ }
2182
+ function OrderTotals({
2183
+ order,
2184
+ locale
2185
+ }) {
2186
+ const tr = useT();
2187
+ const t = order.totals;
2188
+ const c = order.currency;
2189
+ const money = (cents) => formatCents(cents, c, locale);
2190
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.5rem" }, children: [
2191
+ /* @__PURE__ */ jsxRuntime.jsx(Row3, { label: tr("totals.subtotal"), value: money(t.subtotal_cents) }),
2192
+ t.discount_cents > 0 ? /* @__PURE__ */ jsxRuntime.jsx(Row3, { label: tr("totals.discount"), value: `\u2212${money(t.discount_cents)}`, muted: true }) : null,
2193
+ t.shipping_cents > 0 ? /* @__PURE__ */ jsxRuntime.jsx(Row3, { label: tr("totals.shipping"), value: money(t.shipping_cents), muted: true }) : null,
2194
+ t.surcharge_cents > 0 ? /* @__PURE__ */ jsxRuntime.jsx(Row3, { label: tr("totals.paymentFee"), value: money(t.surcharge_cents), muted: true }) : null,
2195
+ t.tax_cents > 0 ? /* @__PURE__ */ jsxRuntime.jsx(Row3, { label: tr("totals.taxIncluded"), value: money(t.tax_cents), muted: true }) : null,
2196
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: 1, background: "var(--nv-border)" } }),
2197
+ /* @__PURE__ */ jsxRuntime.jsx(Row3, { label: tr("totals.total"), value: money(t.total_cents), strong: true })
2198
+ ] });
2199
+ }
2200
+ function Row3({
2201
+ label,
2202
+ value,
2203
+ strong,
2204
+ muted
2205
+ }) {
2206
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2207
+ "div",
2208
+ {
2209
+ style: {
2210
+ display: "flex",
2211
+ justifyContent: "space-between",
2212
+ fontWeight: strong ? 600 : 400,
2213
+ color: muted ? "var(--nv-muted)" : "inherit",
2214
+ fontSize: strong ? "1.1em" : "1em"
2215
+ },
2216
+ children: [
2217
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: label }),
2218
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontVariantNumeric: "tabular-nums" }, children: value })
2219
+ ]
2220
+ }
2221
+ );
2222
+ }
2223
+ function OrderConfirmation({
2224
+ statusToken,
2225
+ order: initial,
2226
+ statusHref,
2227
+ title,
2228
+ locale
2229
+ }) {
2230
+ const t = useT();
2231
+ const { order, loading, error } = useOrderStatus(statusToken ?? null, initial);
2232
+ if (!order) {
2233
+ return /* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "var(--nv-muted)" }, children: loading ? t("order.confirmLoading") : error ? t("order.confirmLoadError") : t("order.notFound") });
2234
+ }
2235
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2236
+ "div",
2237
+ {
2238
+ style: {
2239
+ display: "flex",
2240
+ flexDirection: "column",
2241
+ gap: "1.25rem",
2242
+ fontFamily: "var(--nv-font)",
2243
+ color: "var(--nv-fg)",
2244
+ maxWidth: "34rem"
2245
+ },
2246
+ children: [
2247
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.4rem" }, children: [
2248
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: { margin: 0, fontSize: "1.4rem", fontWeight: 600 }, children: title ?? t("checkout.thankYou") }),
2249
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: 0, color: "var(--nv-muted)" }, children: t("order.confirmed", { number: order.order_number }) })
2250
+ ] }),
2251
+ /* @__PURE__ */ jsxRuntime.jsxs(
2252
+ "div",
2253
+ {
2254
+ style: {
2255
+ display: "flex",
2256
+ flexDirection: "column",
2257
+ gap: "1rem",
2258
+ padding: "1.25rem",
2259
+ borderRadius: "var(--nv-radius)",
2260
+ border: "1px solid var(--nv-border)",
2261
+ background: "var(--nv-bg)"
2262
+ },
2263
+ children: [
2264
+ /* @__PURE__ */ jsxRuntime.jsx(OrderLines, { order, locale }),
2265
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: 1, background: "var(--nv-border)" } }),
2266
+ /* @__PURE__ */ jsxRuntime.jsx(OrderTotals, { order, locale })
2267
+ ]
2268
+ }
2269
+ ),
2270
+ statusHref ? /* @__PURE__ */ jsxRuntime.jsx(
2271
+ "a",
2272
+ {
2273
+ href: statusHref,
2274
+ style: {
2275
+ alignSelf: "flex-start",
2276
+ font: "inherit",
2277
+ fontWeight: 500,
2278
+ textDecoration: "none",
2279
+ padding: "0.7rem 1.4rem",
2280
+ borderRadius: "var(--nv-radius)",
2281
+ background: "var(--nv-brand)",
2282
+ color: "var(--nv-brand-foreground)"
2283
+ },
2284
+ children: t("order.trackOrder")
2285
+ }
2286
+ ) : null
2287
+ ]
2288
+ }
2289
+ );
2290
+ }
2291
+ function OrderStatusPage({
2292
+ statusToken,
2293
+ order: initial,
2294
+ onContactSupport,
2295
+ locale
2296
+ }) {
2297
+ const t = useT();
2298
+ const { order, loading, error } = useOrderStatus(statusToken ?? null, initial);
2299
+ if (!order) {
2300
+ return /* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "var(--nv-muted)" }, children: loading ? t("order.loading") : error ? t("order.loadError") : t("order.notFound") });
2301
+ }
2302
+ const placed = new Date(order.placed_at);
2303
+ const placedLabel = isNaN(placed.getTime()) ? order.placed_at : placed.toLocaleDateString(locale, { year: "numeric", month: "long", day: "numeric" });
2304
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2305
+ "div",
2306
+ {
2307
+ style: {
2308
+ display: "flex",
2309
+ flexDirection: "column",
2310
+ gap: "1.5rem",
2311
+ fontFamily: "var(--nv-font)",
2312
+ color: "var(--nv-fg)",
2313
+ maxWidth: "40rem"
2314
+ },
2315
+ children: [
2316
+ /* @__PURE__ */ jsxRuntime.jsxs("header", { style: { display: "flex", flexDirection: "column", gap: "0.3rem" }, children: [
2317
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { style: { margin: 0, fontSize: "1.5rem", fontWeight: 600 }, children: t("order.title", { number: order.order_number }) }),
2318
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: 0, color: "var(--nv-muted)" }, children: t("order.placedOn", { date: placedLabel }) })
2319
+ ] }),
2320
+ isTerminal(order.public_phase) ? /* @__PURE__ */ jsxRuntime.jsx(StatusBanner, { phase: order.public_phase }) : /* @__PURE__ */ jsxRuntime.jsx(PhaseTimeline, { phase: order.public_phase }),
2321
+ /* @__PURE__ */ jsxRuntime.jsxs(Card, { title: t("order.items"), children: [
2322
+ /* @__PURE__ */ jsxRuntime.jsx(OrderLines, { order, locale }),
2323
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: 1, background: "var(--nv-border)" } }),
2324
+ /* @__PURE__ */ jsxRuntime.jsx(OrderTotals, { order, locale })
2325
+ ] }),
2326
+ order.shipping_method || order.tracking_numbers.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(Card, { title: t("order.shipping"), children: [
2327
+ order.shipping_method ? /* @__PURE__ */ jsxRuntime.jsx(
2328
+ Line,
2329
+ {
2330
+ label: order.shipping_method.name ?? t("order.shipping"),
2331
+ value: order.shipping_method.pickup_point_name ?? order.shipping_method.carrier ?? ""
2332
+ }
2333
+ ) : null,
2334
+ /* @__PURE__ */ jsxRuntime.jsx(
2335
+ Line,
2336
+ {
2337
+ label: t("order.shipsTo"),
2338
+ value: [order.shipping_address_summary.city, order.shipping_address_summary.country].filter(Boolean).join(", ")
2339
+ }
2340
+ ),
2341
+ order.tracking_numbers.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(Line, { label: t("order.tracking"), value: order.tracking_numbers.join(", ") }) : null
2342
+ ] }) : null,
2343
+ /* @__PURE__ */ jsxRuntime.jsxs(Card, { title: t("order.payment"), children: [
2344
+ /* @__PURE__ */ jsxRuntime.jsx(Line, { label: t("order.method"), value: order.payment.method ?? "\u2014" }),
2345
+ /* @__PURE__ */ jsxRuntime.jsx(Line, { label: t("order.status"), value: order.payment.status }),
2346
+ order.payment.invoice_available && order.payment.invoice_url ? /* @__PURE__ */ jsxRuntime.jsx("a", { href: order.payment.invoice_url, style: { color: "var(--nv-brand)" }, children: t("order.downloadInvoice") }) : null
2347
+ ] }),
2348
+ order.actions.can_contact_support && onContactSupport ? /* @__PURE__ */ jsxRuntime.jsx(
2349
+ "button",
2350
+ {
2351
+ type: "button",
2352
+ onClick: onContactSupport,
2353
+ style: {
2354
+ alignSelf: "flex-start",
2355
+ font: "inherit",
2356
+ fontWeight: 500,
2357
+ cursor: "pointer",
2358
+ padding: "0.65rem 1.3rem",
2359
+ borderRadius: "var(--nv-radius)",
2360
+ border: "1px solid var(--nv-border)",
2361
+ background: "var(--nv-bg)",
2362
+ color: "var(--nv-fg)"
2363
+ },
2364
+ children: t("order.contactSupport")
2365
+ }
2366
+ ) : null
2367
+ ]
2368
+ }
2369
+ );
2370
+ }
2371
+ function Card({ title, children }) {
2372
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2373
+ "section",
2374
+ {
2375
+ style: {
2376
+ display: "flex",
2377
+ flexDirection: "column",
2378
+ gap: "0.85rem",
2379
+ padding: "1.25rem",
2380
+ borderRadius: "var(--nv-radius)",
2381
+ border: "1px solid var(--nv-border)",
2382
+ background: "var(--nv-bg)"
2383
+ },
2384
+ children: [
2385
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: { margin: 0, fontSize: "1rem", fontWeight: 600 }, children: title }),
2386
+ children
2387
+ ]
2388
+ }
2389
+ );
2390
+ }
2391
+ function Line({ label, value }) {
2392
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", gap: "1rem" }, children: [
2393
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--nv-muted)" }, children: label }),
2394
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { textAlign: "right" }, children: value })
2395
+ ] });
2396
+ }
2397
+ var AccountContext = React10__namespace.createContext(null);
2398
+ function AccountProvider({ children }) {
2399
+ const client = useStorefront();
2400
+ const [customer, setCustomer] = React10__namespace.useState(null);
2401
+ const [loading, setLoading] = React10__namespace.useState(true);
2402
+ const refresh = React10__namespace.useCallback(async () => {
2403
+ setLoading(true);
2404
+ try {
2405
+ setCustomer(await client.account.me());
2406
+ } finally {
2407
+ setLoading(false);
2408
+ }
2409
+ }, [client]);
2410
+ React10__namespace.useEffect(() => {
2411
+ void refresh();
2412
+ }, [refresh]);
2413
+ const value = React10__namespace.useMemo(
2414
+ () => ({
2415
+ customer,
2416
+ signedIn: customer != null,
2417
+ loading,
2418
+ requestMagicLink: (email) => client.account.requestMagicLink(email),
2419
+ verify: async (token) => {
2420
+ const c = await client.account.verify(token);
2421
+ setCustomer(c);
2422
+ return c;
2423
+ },
2424
+ logout: async () => {
2425
+ await client.account.logout();
2426
+ setCustomer(null);
2427
+ },
2428
+ refresh
2429
+ }),
2430
+ [customer, loading, client, refresh]
2431
+ );
2432
+ return /* @__PURE__ */ jsxRuntime.jsx(AccountContext.Provider, { value, children });
2433
+ }
2434
+ function useAccount() {
2435
+ const ctx = React10__namespace.useContext(AccountContext);
2436
+ if (!ctx) {
2437
+ throw new Error("useAccount must be used inside <AccountProvider>.");
2438
+ }
2439
+ return ctx;
2440
+ }
2441
+ function useMagicLinkVerify(token) {
2442
+ const { verify } = useAccount();
2443
+ const [status, setStatus] = React10__namespace.useState("idle");
2444
+ const done = React10__namespace.useRef(false);
2445
+ React10__namespace.useEffect(() => {
2446
+ if (!token || done.current) return;
2447
+ done.current = true;
2448
+ setStatus("verifying");
2449
+ verify(token).then(() => setStatus("success")).catch(() => setStatus("error"));
2450
+ }, [token, verify]);
2451
+ return { status };
2452
+ }
2453
+ function LoginForm({ verifyPath = "/account/verify", onSent }) {
2454
+ const t = useT();
2455
+ const { requestMagicLink } = useAccount();
2456
+ const [email, setEmail] = React10__namespace.useState("");
2457
+ const [busy, setBusy] = React10__namespace.useState(false);
2458
+ const [sent, setSent] = React10__namespace.useState(false);
2459
+ const [devLink, setDevLink] = React10__namespace.useState(null);
2460
+ async function submit(e) {
2461
+ e.preventDefault();
2462
+ setBusy(true);
2463
+ try {
2464
+ const r = await requestMagicLink(email);
2465
+ setSent(true);
2466
+ if (r.devToken) setDevLink(`${verifyPath}?token=${encodeURIComponent(r.devToken)}`);
2467
+ onSent?.(email);
2468
+ } finally {
2469
+ setBusy(false);
2470
+ }
2471
+ }
2472
+ if (sent) {
2473
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.75rem", maxWidth: "24rem" }, children: [
2474
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: 0, color: "var(--nv-fg)" }, children: t("account.checkEmail") }),
2475
+ devLink ? /* @__PURE__ */ jsxRuntime.jsx("a", { href: devLink, style: { color: "var(--nv-brand)", fontSize: "0.875rem" }, children: t("account.openDevLink") }) : null
2476
+ ] });
2477
+ }
2478
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2479
+ "form",
2480
+ {
2481
+ onSubmit: submit,
2482
+ style: { display: "flex", flexDirection: "column", gap: "0.75rem", maxWidth: "24rem", fontFamily: "var(--nv-font)" },
2483
+ children: [
2484
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: 0, color: "var(--nv-muted)" }, children: t("account.signInBlurb") }),
2485
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "flex", flexDirection: "column", gap: "0.35rem", fontSize: "0.875rem" }, children: [
2486
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: t("account.email") }),
2487
+ /* @__PURE__ */ jsxRuntime.jsx(
2488
+ "input",
2489
+ {
2490
+ type: "email",
2491
+ required: true,
2492
+ value: email,
2493
+ onChange: (e) => setEmail(e.target.value),
2494
+ autoComplete: "email",
2495
+ style: {
2496
+ font: "inherit",
2497
+ padding: "0.6rem 0.75rem",
2498
+ borderRadius: "var(--nv-radius)",
2499
+ border: "1px solid var(--nv-border)",
2500
+ background: "var(--nv-bg)",
2501
+ color: "var(--nv-fg)"
2502
+ }
2503
+ }
2504
+ )
2505
+ ] }),
2506
+ /* @__PURE__ */ jsxRuntime.jsx(
2507
+ "button",
2508
+ {
2509
+ type: "submit",
2510
+ disabled: busy,
2511
+ style: {
2512
+ font: "inherit",
2513
+ fontWeight: 500,
2514
+ cursor: busy ? "default" : "pointer",
2515
+ padding: "0.65rem 1.3rem",
2516
+ borderRadius: "var(--nv-radius)",
2517
+ border: "none",
2518
+ background: "var(--nv-brand)",
2519
+ color: "var(--nv-brand-foreground)",
2520
+ opacity: busy ? 0.6 : 1
2521
+ },
2522
+ children: busy ? t("common.loadingShort") : t("account.sendLink")
2523
+ }
2524
+ )
2525
+ ]
2526
+ }
2527
+ );
2528
+ }
2529
+ function AccountDashboard({ children }) {
2530
+ const t = useT();
2531
+ const { customer, logout } = useAccount();
2532
+ if (customer == null) return null;
2533
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "1.5rem", fontFamily: "var(--nv-font)", color: "var(--nv-fg)" }, children: [
2534
+ /* @__PURE__ */ jsxRuntime.jsxs("header", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", gap: "1rem" }, children: [
2535
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--nv-muted)" }, children: t("account.greeting", { email: customer.email }) }),
2536
+ /* @__PURE__ */ jsxRuntime.jsx(
2537
+ "button",
2538
+ {
2539
+ type: "button",
2540
+ onClick: () => void logout(),
2541
+ style: {
2542
+ font: "inherit",
2543
+ cursor: "pointer",
2544
+ padding: "0.45rem 1rem",
2545
+ borderRadius: "var(--nv-radius)",
2546
+ border: "1px solid var(--nv-border)",
2547
+ background: "var(--nv-bg)",
2548
+ color: "var(--nv-fg)"
2549
+ },
2550
+ children: t("account.signOut")
2551
+ }
2552
+ )
2553
+ ] }),
2554
+ children
2555
+ ] });
2556
+ }
2557
+ function OrderHistory({
2558
+ statusHref = (token) => `/order/${token}`,
2559
+ cartHref = "/cart",
2560
+ onReorder,
2561
+ locale
2562
+ }) {
2563
+ const t = useT();
2564
+ const loc = useFormatLocale(locale);
2565
+ const { signedIn } = useAccount();
2566
+ const client = useStorefront();
2567
+ const [orders, setOrders] = React10__namespace.useState(null);
2568
+ const [reordering, setReordering] = React10__namespace.useState(null);
2569
+ const [notice, setNotice] = React10__namespace.useState(null);
2570
+ React10__namespace.useEffect(() => {
2571
+ let live = true;
2572
+ if (!signedIn) {
2573
+ setOrders([]);
2574
+ return;
2575
+ }
2576
+ client.account.orders().then((o) => live && setOrders(o)).catch(() => live && setOrders([]));
2577
+ return () => {
2578
+ live = false;
2579
+ };
2580
+ }, [signedIn, client]);
2581
+ async function reorder(statusToken) {
2582
+ setReordering(statusToken);
2583
+ setNotice(null);
2584
+ try {
2585
+ const result = await client.account.reorder(statusToken);
2586
+ if (onReorder) {
2587
+ onReorder(result);
2588
+ } else {
2589
+ const text = result.skipped > 0 ? t("account.reorderPartial", { added: result.added, skipped: result.skipped }) : t("account.reorderDone", { added: result.added });
2590
+ setNotice({ token: statusToken, text });
2591
+ if (typeof window !== "undefined") window.location.assign(cartHref);
2592
+ }
2593
+ } catch {
2594
+ setNotice({ token: statusToken, text: t("account.reorderError"), error: true });
2595
+ } finally {
2596
+ setReordering(null);
2597
+ }
2598
+ }
2599
+ return /* @__PURE__ */ jsxRuntime.jsxs("section", { style: { display: "flex", flexDirection: "column", gap: "0.75rem" }, children: [
2600
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: { margin: 0, fontSize: "1.1rem", fontWeight: 600 }, children: t("account.orders") }),
2601
+ orders == null ? /* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "var(--nv-muted)", margin: 0 }, children: t("common.loadingShort") }) : orders.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "var(--nv-muted)", margin: 0 }, children: t("account.noOrders") }) : /* @__PURE__ */ jsxRuntime.jsx("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "flex", flexDirection: "column", gap: "0.5rem" }, children: orders.map((o) => /* @__PURE__ */ jsxRuntime.jsxs(
2602
+ "li",
2603
+ {
2604
+ style: {
2605
+ display: "flex",
2606
+ flexDirection: "column",
2607
+ gap: "0.5rem",
2608
+ padding: "0.85rem 1rem",
2609
+ border: "1px solid var(--nv-border)",
2610
+ borderRadius: "var(--nv-radius)"
2611
+ },
2612
+ children: [
2613
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", gap: "1rem" }, children: [
2614
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "flex", flexDirection: "column" }, children: [
2615
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { style: { fontWeight: 600 }, children: o.number }),
2616
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { fontSize: "0.8125rem", color: "var(--nv-muted)" }, children: [
2617
+ formatCents(o.total_cents, o.currency, loc),
2618
+ o.placed_at ? ` \xB7 ${new Date(o.placed_at).toLocaleDateString(loc)}` : ""
2619
+ ] })
2620
+ ] }),
2621
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "flex", gap: "1rem", alignItems: "center", whiteSpace: "nowrap" }, children: [
2622
+ /* @__PURE__ */ jsxRuntime.jsx(
2623
+ "button",
2624
+ {
2625
+ type: "button",
2626
+ onClick: () => void reorder(o.status_token),
2627
+ disabled: reordering === o.status_token,
2628
+ style: {
2629
+ font: "inherit",
2630
+ fontSize: "0.875rem",
2631
+ cursor: reordering === o.status_token ? "default" : "pointer",
2632
+ background: "none",
2633
+ border: "none",
2634
+ color: "var(--nv-brand)",
2635
+ padding: 0,
2636
+ opacity: reordering === o.status_token ? 0.6 : 1
2637
+ },
2638
+ children: reordering === o.status_token ? "\u2026" : t("account.reorder")
2639
+ }
2640
+ ),
2641
+ /* @__PURE__ */ jsxRuntime.jsx("a", { href: statusHref(o.status_token), style: { color: "var(--nv-brand)", fontSize: "0.875rem" }, children: t("account.viewOrder") })
2642
+ ] })
2643
+ ] }),
2644
+ notice && notice.token === o.status_token ? /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: 0, fontSize: "0.8125rem", color: notice.error ? "#b3261e" : "var(--nv-muted)" }, children: notice.text }) : null
2645
+ ]
2646
+ },
2647
+ o.status_token
2648
+ )) })
2649
+ ] });
2650
+ }
2651
+ function AddressForm2({ initial, onSubmit, onCancel, busy }) {
2652
+ const t = useT();
2653
+ const [form, setForm] = React10__namespace.useState(() => ({
2654
+ first_name: initial?.first_name ?? "",
2655
+ last_name: initial?.last_name ?? "",
2656
+ company: initial?.company ?? "",
2657
+ address1: initial?.address1 ?? "",
2658
+ address2: initial?.address2 ?? "",
2659
+ city: initial?.city ?? "",
2660
+ zip: initial?.zip ?? "",
2661
+ country_code: initial?.country_code ?? "",
2662
+ phone: initial?.phone ?? "",
2663
+ is_default: Boolean(initial?.is_default)
2664
+ }));
2665
+ const set = (k) => (e) => setForm((f) => ({ ...f, [k]: e.target.value }));
2666
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2667
+ "form",
2668
+ {
2669
+ onSubmit: (e) => {
2670
+ e.preventDefault();
2671
+ void onSubmit(form);
2672
+ },
2673
+ style: { display: "flex", flexDirection: "column", gap: "0.75rem" },
2674
+ children: [
2675
+ /* @__PURE__ */ jsxRuntime.jsxs(FieldRow, { children: [
2676
+ /* @__PURE__ */ jsxRuntime.jsx(Field, { label: t("account.firstName"), value: form.first_name ?? "", onChange: set("first_name") }),
2677
+ /* @__PURE__ */ jsxRuntime.jsx(Field, { label: t("account.lastName"), value: form.last_name ?? "", onChange: set("last_name") })
2678
+ ] }),
2679
+ /* @__PURE__ */ jsxRuntime.jsx(Field, { label: t("account.company"), value: form.company ?? "", onChange: set("company") }),
2680
+ /* @__PURE__ */ jsxRuntime.jsx(Field, { label: t("account.address"), required: true, value: form.address1 ?? "", onChange: set("address1") }),
2681
+ /* @__PURE__ */ jsxRuntime.jsx(Field, { label: t("account.addressLine2"), value: form.address2 ?? "", onChange: set("address2") }),
2682
+ /* @__PURE__ */ jsxRuntime.jsxs(FieldRow, { children: [
2683
+ /* @__PURE__ */ jsxRuntime.jsx(Field, { label: t("account.city"), required: true, value: form.city ?? "", onChange: set("city") }),
2684
+ /* @__PURE__ */ jsxRuntime.jsx(Field, { label: t("account.postalCode"), required: true, value: form.zip ?? "", onChange: set("zip") }),
2685
+ /* @__PURE__ */ jsxRuntime.jsx(Field, { label: t("account.country"), required: true, value: form.country_code ?? "", onChange: set("country_code") })
2686
+ ] }),
2687
+ /* @__PURE__ */ jsxRuntime.jsx(Field, { label: t("account.phone"), value: form.phone ?? "", onChange: set("phone") }),
2688
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "inline-flex", alignItems: "center", gap: "0.5rem", cursor: "pointer", fontSize: "0.875rem" }, children: [
2689
+ /* @__PURE__ */ jsxRuntime.jsx(
2690
+ "input",
2691
+ {
2692
+ type: "checkbox",
2693
+ checked: Boolean(form.is_default),
2694
+ onChange: (e) => setForm((f) => ({ ...f, is_default: e.target.checked }))
2695
+ }
2696
+ ),
2697
+ t("account.setDefault")
2698
+ ] }),
2699
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "0.5rem" }, children: [
2700
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "submit", busy, children: t("account.save") }),
2701
+ onCancel ? /* @__PURE__ */ jsxRuntime.jsx(
2702
+ "button",
2703
+ {
2704
+ type: "button",
2705
+ onClick: onCancel,
2706
+ style: {
2707
+ font: "inherit",
2708
+ cursor: "pointer",
2709
+ padding: "0.75rem 1.25rem",
2710
+ borderRadius: "var(--nv-radius)",
2711
+ border: "1px solid var(--nv-border)",
2712
+ background: "transparent",
2713
+ color: "var(--nv-fg)"
2714
+ },
2715
+ children: t("account.cancel")
2716
+ }
2717
+ ) : null
2718
+ ] })
2719
+ ]
2720
+ }
2721
+ );
2722
+ }
2723
+ function formatAddress(a) {
2724
+ const name = [a.first_name, a.last_name].filter(Boolean).join(" ");
2725
+ const line = [a.address1, a.address2].filter(Boolean).join(", ");
2726
+ const place = [a.zip, a.city, a.country_code].filter(Boolean).join(" ");
2727
+ return [name, line, place].filter(Boolean).join(" \xB7 ");
2728
+ }
2729
+ var rowStyle = {
2730
+ padding: "0.85rem 1rem",
2731
+ border: "1px solid var(--nv-border)",
2732
+ borderRadius: "var(--nv-radius)",
2733
+ color: "var(--nv-fg)",
2734
+ display: "flex",
2735
+ flexDirection: "column",
2736
+ gap: "0.5rem"
2737
+ };
2738
+ var linkBtn = {
2739
+ font: "inherit",
2740
+ fontSize: "0.8125rem",
2741
+ cursor: "pointer",
2742
+ background: "none",
2743
+ border: "none",
2744
+ color: "var(--nv-brand)",
2745
+ padding: 0
2746
+ };
2747
+ function AddressBook() {
2748
+ const t = useT();
2749
+ const { signedIn } = useAccount();
2750
+ const client = useStorefront();
2751
+ const [addresses, setAddresses] = React10__namespace.useState(null);
2752
+ const [mode, setMode] = React10__namespace.useState({ kind: "list" });
2753
+ const [busy, setBusy] = React10__namespace.useState(false);
2754
+ const load = React10__namespace.useCallback(() => {
2755
+ if (!signedIn) {
2756
+ setAddresses([]);
2757
+ return;
2758
+ }
2759
+ client.account.addresses().then(setAddresses).catch(() => setAddresses([]));
2760
+ }, [signedIn, client]);
2761
+ React10__namespace.useEffect(() => {
2762
+ load();
2763
+ }, [load]);
2764
+ async function run(fn) {
2765
+ setBusy(true);
2766
+ try {
2767
+ await fn();
2768
+ setMode({ kind: "list" });
2769
+ load();
2770
+ } finally {
2771
+ setBusy(false);
2772
+ }
2773
+ }
2774
+ const create = (data) => run(() => client.account.addAddress(data));
2775
+ const update = (id, data) => run(() => client.account.updateAddress(id, data));
2776
+ const remove = (id) => {
2777
+ if (typeof window !== "undefined" && !window.confirm(t("account.confirmDelete"))) return;
2778
+ void run(() => client.account.deleteAddress(id));
2779
+ };
2780
+ return /* @__PURE__ */ jsxRuntime.jsxs("section", { style: { display: "flex", flexDirection: "column", gap: "0.75rem" }, children: [
2781
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", gap: "1rem" }, children: [
2782
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: { margin: 0, fontSize: "1.1rem", fontWeight: 600 }, children: t("account.addresses") }),
2783
+ mode.kind === "list" ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => setMode({ kind: "add" }), style: { ...linkBtn, fontSize: "0.875rem" }, children: t("account.addAddress") }) : null
2784
+ ] }),
2785
+ mode.kind === "add" ? /* @__PURE__ */ jsxRuntime.jsx(AddressForm2, { onSubmit: create, onCancel: () => setMode({ kind: "list" }), busy }) : null,
2786
+ addresses == null ? /* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "var(--nv-muted)", margin: 0 }, children: t("common.loadingShort") }) : addresses.length === 0 && mode.kind === "list" ? /* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "var(--nv-muted)", margin: 0 }, children: t("account.noAddresses") }) : /* @__PURE__ */ jsxRuntime.jsx("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "flex", flexDirection: "column", gap: "0.5rem" }, children: addresses.map((a, i) => {
2787
+ const id = a.id ?? String(i);
2788
+ const isEditing = mode.kind === "edit" && mode.id === id;
2789
+ return /* @__PURE__ */ jsxRuntime.jsx("li", { style: rowStyle, children: isEditing ? /* @__PURE__ */ jsxRuntime.jsx(
2790
+ AddressForm2,
2791
+ {
2792
+ initial: a,
2793
+ onSubmit: (data) => update(id, data),
2794
+ onCancel: () => setMode({ kind: "list" }),
2795
+ busy
2796
+ }
2797
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2798
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", gap: "1rem", alignItems: "baseline" }, children: [
2799
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatAddress(a) }),
2800
+ a.is_default ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.7rem", fontWeight: 600, color: "var(--nv-muted)", textTransform: "uppercase" }, children: t("account.default") }) : null
2801
+ ] }),
2802
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "1rem" }, children: [
2803
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => setMode({ kind: "edit", id }), style: linkBtn, children: t("account.editAddress") }),
2804
+ !a.is_default ? /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => void update(id, { is_default: true }), style: linkBtn, children: t("account.setDefault") }) : null,
2805
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => remove(id), style: { ...linkBtn, color: "#b3261e" }, children: t("account.deleteAddress") })
2806
+ ] })
2807
+ ] }) }, id);
2808
+ }) })
2809
+ ] });
2810
+ }
2811
+ function ProfileEditor() {
2812
+ const t = useT();
2813
+ const client = useStorefront();
2814
+ const { customer, refresh } = useAccount();
2815
+ const [editing, setEditing] = React10__namespace.useState(false);
2816
+ const [busy, setBusy] = React10__namespace.useState(false);
2817
+ const [error, setError] = React10__namespace.useState(false);
2818
+ const [form, setForm] = React10__namespace.useState({});
2819
+ if (!customer) return null;
2820
+ function startEdit() {
2821
+ setForm({
2822
+ first_name: customer.first_name ?? "",
2823
+ last_name: customer.last_name ?? "",
2824
+ phone: customer.phone ?? "",
2825
+ accepts_marketing: customer.accepts_marketing
2826
+ });
2827
+ setError(false);
2828
+ setEditing(true);
2829
+ }
2830
+ async function save() {
2831
+ setBusy(true);
2832
+ setError(false);
2833
+ try {
2834
+ await client.account.updateProfile(form);
2835
+ await refresh();
2836
+ setEditing(false);
2837
+ } catch {
2838
+ setError(true);
2839
+ } finally {
2840
+ setBusy(false);
2841
+ }
2842
+ }
2843
+ const name = [customer.first_name, customer.last_name].filter(Boolean).join(" ");
2844
+ return /* @__PURE__ */ jsxRuntime.jsxs("section", { style: { display: "flex", flexDirection: "column", gap: "0.75rem" }, children: [
2845
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", gap: "1rem" }, children: [
2846
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: { margin: 0, fontSize: "1.1rem", fontWeight: 600 }, children: t("account.profile") }),
2847
+ !editing ? /* @__PURE__ */ jsxRuntime.jsx(
2848
+ "button",
2849
+ {
2850
+ type: "button",
2851
+ onClick: startEdit,
2852
+ style: { font: "inherit", cursor: "pointer", background: "none", border: "none", color: "var(--nv-brand)", fontSize: "0.875rem" },
2853
+ children: t("account.editProfile")
2854
+ }
2855
+ ) : null
2856
+ ] }),
2857
+ !editing ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.25rem", color: "var(--nv-fg)" }, children: [
2858
+ name ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 500 }, children: name }) : null,
2859
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--nv-muted)", fontSize: "0.9rem" }, children: customer.email }),
2860
+ customer.phone ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--nv-muted)", fontSize: "0.9rem" }, children: customer.phone }) : null
2861
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: (e) => {
2862
+ e.preventDefault();
2863
+ void save();
2864
+ }, style: { display: "flex", flexDirection: "column", gap: "0.75rem" }, children: [
2865
+ /* @__PURE__ */ jsxRuntime.jsx(Field, { label: t("account.firstName"), value: form.first_name ?? "", onChange: (e) => setForm((f) => ({ ...f, first_name: e.target.value })) }),
2866
+ /* @__PURE__ */ jsxRuntime.jsx(Field, { label: t("account.lastName"), value: form.last_name ?? "", onChange: (e) => setForm((f) => ({ ...f, last_name: e.target.value })) }),
2867
+ /* @__PURE__ */ jsxRuntime.jsx(Field, { label: t("account.phone"), value: form.phone ?? "", onChange: (e) => setForm((f) => ({ ...f, phone: e.target.value })) }),
2868
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "inline-flex", alignItems: "center", gap: "0.5rem", cursor: "pointer", fontSize: "0.875rem" }, children: [
2869
+ /* @__PURE__ */ jsxRuntime.jsx(
2870
+ "input",
2871
+ {
2872
+ type: "checkbox",
2873
+ checked: Boolean(form.accepts_marketing),
2874
+ onChange: (e) => setForm((f) => ({ ...f, accepts_marketing: e.target.checked }))
2875
+ }
2876
+ ),
2877
+ t("account.acceptsMarketing")
2878
+ ] }),
2879
+ error ? /* @__PURE__ */ jsxRuntime.jsx("p", { style: { margin: 0, color: "#b3261e", fontSize: "0.8125rem" }, children: t("account.saveError") }) : null,
2880
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "0.5rem" }, children: [
2881
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "submit", busy, children: t("account.save") }),
2882
+ /* @__PURE__ */ jsxRuntime.jsx(
2883
+ "button",
2884
+ {
2885
+ type: "button",
2886
+ onClick: () => setEditing(false),
2887
+ style: { font: "inherit", cursor: "pointer", padding: "0.75rem 1.25rem", borderRadius: "var(--nv-radius)", border: "1px solid var(--nv-border)", background: "transparent", color: "var(--nv-fg)" },
2888
+ children: t("account.cancel")
2889
+ }
2890
+ )
2891
+ ] })
2892
+ ] })
2893
+ ] });
2894
+ }
2895
+ function serializeJsonLd(data) {
2896
+ return JSON.stringify(data).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026");
2897
+ }
2898
+ function stripHtml(html) {
2899
+ if (!html) return void 0;
2900
+ const text = html.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
2901
+ return text || void 0;
2902
+ }
2903
+ function absoluteUrl(url, base) {
2904
+ if (!url) return url;
2905
+ if (/^https?:\/\//i.test(url) || url.startsWith("data:")) return url;
2906
+ if (!base) return url;
2907
+ return `${base.replace(/\/+$/, "")}/${url.replace(/^\/+/, "")}`;
2908
+ }
2909
+ function compact(obj) {
2910
+ const out = {};
2911
+ for (const [k, v] of Object.entries(obj)) {
2912
+ if (v !== null && v !== void 0) out[k] = v;
2913
+ }
2914
+ return out;
2915
+ }
2916
+ function JsonLd({ data }) {
2917
+ return /* @__PURE__ */ jsxRuntime.jsx(
2918
+ "script",
2919
+ {
2920
+ type: "application/ld+json",
2921
+ dangerouslySetInnerHTML: { __html: serializeJsonLd(data) }
2922
+ }
2923
+ );
2924
+ }
2925
+ var SCHEMA_AVAILABILITY = {
2926
+ in_stock: "https://schema.org/InStock",
2927
+ preorder: "https://schema.org/PreOrder",
2928
+ out_of_stock: "https://schema.org/OutOfStock"
2929
+ };
2930
+ function ProductJsonLd({
2931
+ product,
2932
+ url,
2933
+ baseUrl,
2934
+ brandName
2935
+ }) {
2936
+ const origin = baseUrl ?? safeOrigin(url);
2937
+ const images = product.media.map((m) => absoluteUrl(m.url, origin));
2938
+ const brand = brandName ?? product.vendor;
2939
+ const offers = product.variants.map(
2940
+ (v) => compact({
2941
+ "@type": "Offer",
2942
+ price: (v.price.amount_cents / 100).toFixed(2),
2943
+ priceCurrency: v.price.currency,
2944
+ availability: SCHEMA_AVAILABILITY[v.availability?.status ?? "in_stock"],
2945
+ sku: v.sku ?? void 0,
2946
+ url
2947
+ })
2948
+ );
2949
+ const data = compact({
2950
+ "@context": "https://schema.org",
2951
+ "@type": "Product",
2952
+ name: product.title,
2953
+ description: stripHtml(product.description_html),
2954
+ image: images.length ? images : void 0,
2955
+ sku: product.variants[0]?.sku ?? void 0,
2956
+ brand: brand ? { "@type": "Brand", name: brand } : void 0,
2957
+ offers: offers.length === 1 ? offers[0] : offers
2958
+ });
2959
+ return /* @__PURE__ */ jsxRuntime.jsx(JsonLd, { data });
2960
+ }
2961
+ function safeOrigin(url) {
2962
+ try {
2963
+ return new URL(url).origin;
2964
+ } catch {
2965
+ return void 0;
2966
+ }
2967
+ }
2968
+ function BreadcrumbJsonLd({ items }) {
2969
+ if (!items.length) return null;
2970
+ const data = {
2971
+ "@context": "https://schema.org",
2972
+ "@type": "BreadcrumbList",
2973
+ itemListElement: items.map((it, i) => ({
2974
+ "@type": "ListItem",
2975
+ position: i + 1,
2976
+ name: it.name,
2977
+ item: it.url
2978
+ }))
2979
+ };
2980
+ return /* @__PURE__ */ jsxRuntime.jsx(JsonLd, { data });
2981
+ }
2982
+ function OrganizationJsonLd({
2983
+ name,
2984
+ url,
2985
+ logo,
2986
+ sameAs
2987
+ }) {
2988
+ const data = compact({
2989
+ "@context": "https://schema.org",
2990
+ "@type": "Organization",
2991
+ name,
2992
+ url,
2993
+ logo,
2994
+ sameAs: sameAs && sameAs.length ? sameAs : void 0
2995
+ });
2996
+ return /* @__PURE__ */ jsxRuntime.jsx(JsonLd, { data });
2997
+ }
2998
+
2999
+ exports.AccountDashboard = AccountDashboard;
3000
+ exports.AccountProvider = AccountProvider;
3001
+ exports.AddToCart = AddToCart;
3002
+ exports.AddressBook = AddressBook;
3003
+ exports.AddressForm = AddressForm;
3004
+ exports.AvailabilityFilter = AvailabilityFilter;
3005
+ exports.BackInStockForm = BackInStockForm;
3006
+ exports.BreadcrumbJsonLd = BreadcrumbJsonLd;
3007
+ exports.CartDrawer = CartDrawer;
3008
+ exports.CartLineItem = CartLineItem;
3009
+ exports.CartProvider = CartProvider;
3010
+ exports.CartSummary = CartSummary;
3011
+ exports.CheckoutFlow = CheckoutFlow;
3012
+ exports.CheckoutProvider = CheckoutProvider;
3013
+ exports.CollectionPage = CollectionPage;
3014
+ exports.I18nProvider = I18nProvider;
3015
+ exports.JsonLd = JsonLd;
3016
+ exports.LoginForm = LoginForm;
3017
+ exports.MarketSwitcher = MarketSwitcher;
3018
+ exports.NeviosProvider = NeviosProvider;
3019
+ exports.OrderConfirmation = OrderConfirmation;
3020
+ exports.OrderHistory = OrderHistory;
3021
+ exports.OrderStatusPage = OrderStatusPage;
3022
+ exports.OrderSummary = OrderSummary;
3023
+ exports.OrganizationJsonLd = OrganizationJsonLd;
3024
+ exports.PaymentSection = PaymentSection;
3025
+ exports.PriceMoney = PriceMoney;
3026
+ exports.PriceRangeFilter = PriceRangeFilter;
3027
+ exports.ProductCard = ProductCard;
3028
+ exports.ProductDetailPage = ProductDetailPage;
3029
+ exports.ProductGallery = ProductGallery;
3030
+ exports.ProductJsonLd = ProductJsonLd;
3031
+ exports.ProfileEditor = ProfileEditor;
3032
+ exports.ShippingPicker = ShippingPicker;
3033
+ exports.SortSelect = SortSelect;
3034
+ exports.StockBadge = StockBadge;
3035
+ exports.TagFilter = TagFilter;
3036
+ exports.ThemeProvider = ThemeProvider;
3037
+ exports.VariantPicker = VariantPicker;
3038
+ exports.absoluteUrl = absoluteUrl;
3039
+ exports.bundledMessages = bundledMessages;
3040
+ exports.cs = cs;
3041
+ exports.en = en;
3042
+ exports.formatCents = formatCents;
3043
+ exports.formatMoney = formatMoney;
3044
+ exports.serializeJsonLd = serializeJsonLd;
3045
+ exports.stripHtml = stripHtml;
3046
+ exports.themeToCssVars = themeToCssVars;
3047
+ exports.useAccount = useAccount;
3048
+ exports.useCart = useCart;
3049
+ exports.useCheckout = useCheckout;
3050
+ exports.useI18n = useI18n;
3051
+ exports.useMagicLinkVerify = useMagicLinkVerify;
3052
+ exports.useOrderStatus = useOrderStatus;
3053
+ exports.useStorefront = useStorefront;
3054
+ exports.useStorefrontOptional = useStorefrontOptional;
3055
+ exports.useT = useT;
3056
+ //# sourceMappingURL=index.cjs.map
3057
+ //# sourceMappingURL=index.cjs.map