notify-me-wl 3.4.0 → 3.6.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/README.md CHANGED
@@ -18,8 +18,12 @@ related guides.
18
18
  - Adjust form copy and button text based on the widget type (`notify-me` or `coming-soon`).
19
19
  - Inline success/error feedback (no browser `alert`s); the submit button shows a "Sending…" loading state.
20
20
  - Closes on the × button, clicking the backdrop, or pressing `Esc`; locks background scroll while open and respects `prefers-reduced-motion`.
21
- - Optional email/SMS marketing opt-ins.
21
+ - Optional email/SMS marketing opt-ins, each shown only when its field is: "Email me store updates" with `email`, "Text me store updates" with `mobile`.
22
+ - Configurable from one JSON block, with the widget's main strings overridable — see [JSON configuration](#json-configuration) and [Copy](#copy).
23
+ - Optional product summary (image, name, size, now/was price) — see [Product summary](#product-summary).
24
+ - Theme integration through CSS custom properties and per-element theme classes — see [Styling and theme classes](#styling-and-theme-classes).
22
25
  - Two display modes: a centred modal (default) or a right-hand slide-in panel — see [Display modes](#display-modes).
26
+ - Opens from any element marked `data-twc-notify-me` — PLP size buttons, "Size out of stock?" links — for the product it belongs to, with the right size pre-selected. See [Opening from product listing pages](#opening-from-product-listing-pages).
23
27
  - Two auth modes: Shopify App Proxy (default) or a merchant-supplied token — see [Configuration](#configuration).
24
28
  - Optional "Shop similar styles" section under the form, showing personalised product recommendations for the customer.
25
29
 
@@ -40,6 +44,7 @@ npm install # install dev dependencies
40
44
  npm run build # emit build/notify-me-wl.min.js
41
45
  npm run dev # rebuild on change (watch mode)
42
46
  npm run typecheck # type-check without emitting
47
+ npm run selftest # build, then run the harness pages in headless Chrome
43
48
  ```
44
49
 
45
50
  > **v2.0.0 path change:** the distributed bundle moved from `index.js` to
@@ -47,15 +52,21 @@ npm run typecheck # type-check without emitting
47
52
 
48
53
  ### Usage
49
54
 
50
- Add a button to your HTML where you want the widget to appear. The button should have an `id` of `popup-open`, a `data-fields` attribute with the fields you want to include in the form (as a JSON string), and a `data-type` attribute to specify the widget type (`notify-me` or `coming-soon`).
55
+ Add `<div id="notification-widget"></div>` once per page and put the configuration attributes below on it (or, as older installs do, on a `<button id="popup-open">` — its attributes win where both carry one). Anything marked `data-twc-notify-me` opens the widget; the legacy `#popup-open` button does too. `data-fields` is a JSON string of the fields to include, and `data-type` is `notify-me` or `coming-soon`.
51
56
 
52
57
  ```html
53
58
  <div id="notification-widget"></div>
54
59
  <button id="popup-open" data-fields='["mobile", "firstName", "lastName"]' data-type="notify-me">Open Popup</button>
55
60
  ```
56
61
 
62
+ `window.currentProduct` supplies the product on a product page. Its `variants` may
63
+ include Shopify's `available` flag — the widget then lists only out-of-stock sizes
64
+ itself — or be pre-filtered in Liquid as older installs do.
65
+
57
66
  ### Configuration
58
67
 
68
+ Every attribute below can be set on `#notification-widget`, on `#popup-open`, or as a key in the JSON block (see [JSON configuration](#json-configuration)). Per setting, `#popup-open` wins, then `#notification-widget`, then the JSON block.
69
+
59
70
  - `data-fields`: A JSON array specifying the optional fields to include in the form. Possible values are `"email"`, `"mobile"`, `"firstName"`, `"lastName"`, `"countryCode"`, and `"provinceCode"`. Defaults to `["email"]`. (A required Size selector is always shown and populated from the product's variants.)
60
71
  - `data-type`: Specifies the type of the widget. Possible values are `"notify-me"` and `"coming-soon"`.
61
72
  - `data-display`: How the widget presents itself. `"modal"` (default) is a centred dialog; `"panel"` slides in from the right edge at full height. Any other value falls back to `"modal"`. See [Display modes](#display-modes).
@@ -72,6 +83,114 @@ Add a button to your HTML where you want the widget to appear. The button should
72
83
  - `data-currency`: Fallback ISO currency code (e.g. `"AUD"`) for formatting recommendation prices, used only when the storefront does not expose `window.Shopify.currency.active`. Without either, prices render as bare numbers rather than risking the wrong symbol.
73
84
  - `data-customer-email`: Overrides the customer email used to request recommendations. Intended for testing — in production the email comes from the Shopify customer context, or from the email the shopper submits.
74
85
 
86
+ ### JSON configuration
87
+
88
+ Instead of (or as well as) attributes, put a JSON block inside `#notification-widget`:
89
+
90
+ ```liquid
91
+ <div id="notification-widget" data-market-id="{{ localization.market.id }}">
92
+ <script type="application/json" data-twc-config>
93
+ {
94
+ "tenant": "your-tenant",
95
+ "type": "notify-me",
96
+ "display": "panel",
97
+ "fields": ["email"],
98
+ "currency": {{ cart.currency.iso_code | json }},
99
+ "productSummary": true,
100
+ "copy": { "title": {{ 'Notify me when available' | json }} },
101
+ "classes": { "submit": "Button Button--primary" }
102
+ }
103
+ </script>
104
+ </div>
105
+ ```
106
+
107
+ - Each attribute's key is its name without `data-`, camelCased: `data-proxy-app` → `proxyApp`, `data-recommendations-count` → `recommendationsCount`. Values use real JSON types — `"fields": ["email"]`, `"recommendations": false`, `"recommendationsCount": 6`.
108
+ - `productSummary`, `copy` and `classes` exist only in JSON.
109
+ - Per setting, an attribute on `#popup-open` wins, then one on `#notification-widget`, then the JSON block, then the default. Liquid-rendered values such as `data-market-id` can stay as attributes next to the block.
110
+ - Build values with Liquid's `| json` filter so quotes and apostrophes cannot break the block.
111
+ - `null` counts as not set. A value of the wrong type is ignored with a console warning and the next layer applies. Invalid JSON is reported in the console and the widget runs as if there were no block.
112
+ - The header row (`.twc-nm-header`, and so `--twc-nm-header-border`) is rendered only when a `data-twc-config` block is present.
113
+ - String settings accept numbers too, so `"marketId": {{ localization.market.id | json }}` works.
114
+
115
+ ### Copy
116
+
117
+ `copy` in the JSON block overrides the strings listed below. Everything is rendered as plain text. Status and validation messages ("Sending…", "Please select a size.", error and notice messages), the close button's aria-label, and the mobile, name, country and province labels are fixed for now.
118
+
119
+ | Key | Default |
120
+ |---|---|
121
+ | `title` | `"{product}"` — the product name |
122
+ | `intro` | "Register to receive a notification as soon as this item is back in stock" (notify-me) / "Register your interest to hear more about this item" (coming-soon) |
123
+ | `sizeLabel` | `"Size"` |
124
+ | `sizePlaceholder` | `"Select a size"` |
125
+ | `emailLabel` | `"Email"`; `false` removes the label (the input gets an `aria-label`) |
126
+ | `emailPlaceholder` | `"you@example.com"` |
127
+ | `emailOptIn` | `"Email me store updates"` (shown with the `email` field) |
128
+ | `smsOptIn` | `"Text me store updates"` (shown with the `mobile` field) |
129
+ | `submit` | `"Notify Me"` / `"Register Interest"` |
130
+ | `success` | `"You're on the list. We'll be in touch."` |
131
+ | `disclaimer` | none; when set, shown under the submit button |
132
+
133
+ `{product}` in `title` or `intro` is replaced by the product's name.
134
+
135
+ ### Product summary
136
+
137
+ `"productSummary": true` shows the product at the top of the form — image, name, the selected size and its price, with the compare-at price struck through. It follows the size select, so a size changed in the widget updates the summary and is the size registered.
138
+
139
+ It reads optional fields on the product data (`window.currentProduct` or `data-twc-product`). Prices are passed already formatted, so they match the theme:
140
+
141
+ ```liquid
142
+ {%- capture twc_product -%}
143
+ {"id":{{ product.id | json }},"title":{{ product.title | json }},
144
+ {%- if product.featured_image -%},"image":{{ product.featured_image | image_url: width: 300 | json }}{%- endif -%},"variants":[
145
+ {%- for v in product.variants -%}
146
+ {"id":{{ v.id | json }},"title":{{ v.title | json }},"available":{{ v.available | json }},
147
+ "price":{{ v.price | money_with_currency | json }}
148
+ {%- if v.compare_at_price > v.price -%},"compareAtPrice":{{ v.compare_at_price | money_with_currency | json }}{%- endif -%}
149
+ {%- if v.featured_image -%},"image":{{ v.featured_image | image_url: width: 300 | json }}{%- endif -%}}
150
+ {%- unless forloop.last -%},{%- endunless -%}
151
+ {%- endfor -%}]}
152
+ {%- endcapture -%}
153
+ ```
154
+
155
+ Emit it on a product page as `<script>window.currentProduct = {{ twc_product }};</script>`, or on a product card as `<div data-twc-product="{{ twc_product | escape }}">` (`| escape` is required inside the attribute).
156
+
157
+ A variant without an image falls back to the product image. Image URLs must be `https:`, `http:` or `//` URLs. With no size selected, the price shows only when every listed size has the same price.
158
+
159
+ ### Styling and theme classes
160
+
161
+ **CSS custom properties.** Set any of these (on `:root` or an ancestor, inside your own media queries if needed); unset ones keep the default look.
162
+
163
+ | Variable | Default |
164
+ |---|---|
165
+ | `--twc-nm-panel-width` | `420px` |
166
+ | `--twc-nm-panel-width-mobile` | `100%` (≤480px) |
167
+ | `--twc-nm-padding` | `24px` |
168
+ | `--twc-nm-overlay-bg` | `rgba(17, 17, 17, 0.55)` |
169
+ | `--twc-nm-panel-shadow` | `-10px 0 40px rgba(0, 0, 0, 0.18)` |
170
+ | `--twc-nm-transition` | `200ms ease` |
171
+ | `--twc-nm-bg` | `#fff` |
172
+ | `--twc-nm-text` | `#111827` |
173
+ | `--twc-nm-muted` | `#6b7280` |
174
+ | `--twc-nm-border` | `#e5e7eb` |
175
+ | `--twc-nm-font` | `inherit` |
176
+ | `--twc-nm-radius` | `8px` |
177
+ | `--twc-nm-header-border` | `none` |
178
+
179
+ **Theme classes.** `classes` in the JSON block adds your theme's classes to the widget's elements, so they pick up your existing button, input and heading styles:
180
+
181
+ ```json
182
+ "classes": {
183
+ "title": "Heading u-h4",
184
+ "input": "Form__Input",
185
+ "select": "Form__Input",
186
+ "submit": "Button Button--primary Button--full"
187
+ }
188
+ ```
189
+
190
+ Keys: `title`, `intro`, `label`, `input`, `select`, `checkbox`, `submit`, `close`, `disclaimer`, `summary`, `summaryImage`, `summaryName`, `summaryVariant`, `summaryPrice`, `summaryCompareAt`. `label`, `input` and `select` apply to every element of that kind.
191
+
192
+ An element given classes drops the widget's own colours, fonts, borders, padding and hover styles, so your theme's apply cleanly. Its layout (width, position, truncation) stays the widget's. The close button keeps its border, background and box-shadow reset and its positioning, and a themed select keeps the widget's arrow image (give it enough right padding, about 36px). Class names may contain letters, digits, `-` and `_`; anything else is dropped with a console warning.
193
+
75
194
  ### Auth modes
76
195
 
77
196
  Every TWC API call needs an `Authorization` header and an `X-Twc-Tenant` header.
@@ -91,7 +210,7 @@ continue." message; submission is blocked until a token is available.
91
210
 
92
211
  #### `"token"`
93
212
 
94
- Sends a token you supply, from `data-access-token` on the open button or
213
+ Sends a token you supply, from `data-access-token` on `#notification-widget` (or `#popup-open`) or
95
214
  `window.TWC_ACCESS_TOKEN`. The attribute wins if both are present, and the `Bearer `
96
215
  prefix is added if you omit it.
97
216
 
@@ -141,7 +260,7 @@ sliding.
141
260
 
142
261
  Market and country are captured on every submission so interest can be reported per
143
262
  market. The values come from Liquid, which renders server-side, so the theme passes
144
- them to the widget as attributes on the open button:
263
+ them to the widget as attributes on `#notification-widget` (or the legacy `#popup-open` button):
145
264
 
146
265
  ```html
147
266
  <button id="popup-open"
@@ -167,6 +286,83 @@ When `data-fields` includes `"countryCode"`, the country the shopper picks in th
167
286
  takes precedence over `data-country-code`. The dropdown is pre-selected to the market's
168
287
  country, so leaving it untouched sends the market country either way.
169
288
 
289
+ ### Opening from product listing pages
290
+
291
+ Three attributes connect a theme to the widget. No theme JavaScript is needed.
292
+
293
+ | Attribute | Where | Meaning |
294
+ | --- | --- | --- |
295
+ | `data-twc-product` | A product container, e.g. the card | JSON `{ id, title, variants: [{ id, title, available }] }` |
296
+ | `data-twc-notify-me` | Any clickable element | Clicking it opens the widget |
297
+ | `data-variant-id` | Optional, on the same element | Variant to pre-select |
298
+
299
+ The widget finds the product from the clicked element's closest `data-twc-product`,
300
+ falling back to `window.currentProduct`. It listens at page level in the capture phase,
301
+ so cards added later (filters, infinite scroll) work, and a theme handler that stops
302
+ propagation does not block it. It calls `preventDefault()` on the click — `href="#"`
303
+ does not jump — but lets the theme's own handlers run.
304
+
305
+ Build the product JSON with a compact capture rather than `product | json`, which is
306
+ too heavy to repeat on every card, and escape it into a double-quoted attribute so a
307
+ title with a quote or apostrophe cannot break the markup:
308
+
309
+ ```liquid
310
+ {%- capture twc_product -%}
311
+ {"id":{{ product.id | json }},"title":{{ product.title | json }},"variants":[
312
+ {%- for v in product.variants -%}
313
+ {"id":{{ v.id | json }},"title":{{ v.title | json }},"available":{{ v.available | json }}}
314
+ {%- unless forloop.last -%},{%- endunless -%}
315
+ {%- endfor -%}]}
316
+ {%- endcapture -%}
317
+ <div class="product-card" data-twc-product="{{ twc_product | escape }}">
318
+ {%- for variant in product.variants -%}
319
+ <button type="button" data-variant-id="{{ variant.id }}"
320
+ {% unless variant.available %}data-twc-notify-me{% endunless %}>{{ variant.title }}</button>
321
+ {%- endfor -%}
322
+ <a href="#" data-twc-notify-me>Size out of stock?</a>
323
+ </div>
324
+ ```
325
+
326
+ Out-of-stock size buttons must not carry the `disabled` attribute: browsers do not
327
+ dispatch clicks on disabled buttons. Style them with a class or `aria-disabled="true"`
328
+ instead.
329
+
330
+ Render `#notification-widget` with the configuration once per page (e.g. in
331
+ `theme.liquid`), and load the script with `defer` or no attribute:
332
+
333
+ ```liquid
334
+ <div id="notification-widget"
335
+ data-tenant="your-tenant"
336
+ data-type="notify-me"
337
+ data-fields='["email"]'
338
+ data-currency="{{ cart.currency.iso_code }}"
339
+ data-market-id="{{ localization.market.id }}"
340
+ data-market-handle="{{ localization.market.handle }}"
341
+ data-country-code="{{ localization.country.iso_code }}"></div>
342
+ <script src="https://cdn.jsdelivr.net/npm/notify-me-wl@3.5.0/build/notify-me-wl.min.js" defer></script>
343
+ ```
344
+
345
+ A page must have exactly one `#notification-widget`. A second, empty one earlier in
346
+ the page would be the one the widget reads its configuration from.
347
+
348
+ ### Size pre-selection
349
+
350
+ The size list shows only out-of-stock sizes. On open, the first rule that yields one of
351
+ them wins:
352
+
353
+ 1. `data-variant-id` on the clicked element.
354
+ 2. Product page only (the product came from `window.currentProduct`): `?variant=` in
355
+ the URL, then the value of the first `select[name="id"]` inside a
356
+ `form[action*="/cart/add"]`. Hidden `input[name="id"]` fields are ignored — some
357
+ themes keep a stale one in an installments form. Never applied to a card, so page
358
+ state cannot leak into a listing.
359
+ 3. Exactly one out-of-stock size — that size.
360
+ 4. Otherwise, "Select a size".
361
+
362
+ A product with no out-of-stock sizes shows "All sizes are currently in stock." instead
363
+ of the form. A missing or malformed product shows "Product information is unavailable.
364
+ Please refresh the page."
365
+
170
366
  ### Shop similar styles
171
367
 
172
368
  When enabled, the widget shows a horizontally scrolling row of recommended products
@@ -181,7 +377,7 @@ customer-interest call, and the tenant doubles as the retailer id in the path.
181
377
 
182
378
  The API needs a customer email, resolved in this order:
183
379
 
184
- 1. `data-customer-email` on the open button (testing).
380
+ 1. `data-customer-email` on `#notification-widget` (or `#popup-open`) (testing).
185
381
  2. `window.customer.email` — a logged-in Shopify customer. The section then appears
186
382
  as soon as the modal opens.
187
383
  3. `window.customerEmail` — the same address exposed as a bare global, which is how
@@ -248,7 +444,7 @@ The recommendation profile tag is resolved in this order, first match winning:
248
444
 
249
445
  1. `profileTag` on `websiteRecommendations`.
250
446
  2. `tags` on `websiteRecommendations`.
251
- 3. `data-recommendations-profile` on the open button.
447
+ 3. `data-recommendations-profile` on `#notification-widget` (or `#popup-open`).
252
448
  4. The bundled default, `similar`.
253
449
 
254
450
  The tenant config outranks the attribute deliberately — it is the intended home for this
@@ -308,11 +504,12 @@ simply not rendered. It never blocks or alters the notify-me submission.
308
504
 
309
505
  ### How It Works
310
506
 
311
- 1. **Initialization**: The script listens for the `DOMContentLoaded` event to initialize the widget.
507
+ 1. **Initialization**: The script initializes on `DOMContentLoaded` (or immediately, if the page has already loaded), reading configuration from `#notification-widget` and `#popup-open`.
312
508
  2. **Styles Injection**: It injects the modal/form styles, all namespaced under `#twc-nm-overlay` so they don't collide with the host theme.
313
509
  3. **Modal Creation**: Creates the modal structure and appends it to the `notification-widget` div.
314
510
  4. **Dynamic Content**: Based on the `data-type`, it sets the appropriate copy and button text, and builds the fields listed in `data-fields` plus the always-present Size selector.
315
- 5. **Form Submission**: On submit, the button enters a "Sending…" state, the configured auth token is resolved, and the form data is POSTed to the API. The result is shown inline (success message then auto-close, or an error message); the popup is never closed on failure.
511
+ 5. **Opening**: A click on any `data-twc-notify-me` element (or `#popup-open`) resolves the product, rebuilds the Size selector with that product's out-of-stock sizes, and pre-selects one per [Size pre-selection](#size-pre-selection).
512
+ 6. **Form Submission**: On submit, the button enters a "Sending…" state, the configured auth token is resolved, and the form data is POSTed to the API. The result is shown inline (success message then auto-close, or an error message); the popup is never closed on failure.
316
513
 
317
514
  ### API Request Example
318
515
 
@@ -1 +1 @@
1
- !function(){"use strict";const e=[{code:"AF",name:"Afghanistan"},{code:"AX",name:"Åland Islands"},{code:"AL",name:"Albania"},{code:"DZ",name:"Algeria"},{code:"AS",name:"American Samoa"},{code:"AD",name:"Andorra"},{code:"AO",name:"Angola"},{code:"AI",name:"Anguilla"},{code:"AG",name:"Antigua and Barbuda"},{code:"AR",name:"Argentina"},{code:"AM",name:"Armenia"},{code:"AW",name:"Aruba"},{code:"AU",name:"Australia"},{code:"AT",name:"Austria"},{code:"AZ",name:"Azerbaijan"},{code:"BS",name:"Bahamas"},{code:"BH",name:"Bahrain"},{code:"BD",name:"Bangladesh"},{code:"BB",name:"Barbados"},{code:"BY",name:"Belarus"},{code:"BE",name:"Belgium"},{code:"BZ",name:"Belize"},{code:"BJ",name:"Benin"},{code:"BM",name:"Bermuda"},{code:"BT",name:"Bhutan"},{code:"BO",name:"Bolivia"},{code:"BA",name:"Bosnia and Herzegovina"},{code:"BW",name:"Botswana"},{code:"BR",name:"Brazil"},{code:"BN",name:"Brunei"},{code:"BG",name:"Bulgaria"},{code:"BF",name:"Burkina Faso"},{code:"BI",name:"Burundi"},{code:"CV",name:"Cabo Verde"},{code:"KH",name:"Cambodia"},{code:"CM",name:"Cameroon"},{code:"CA",name:"Canada"},{code:"KY",name:"Cayman Islands"},{code:"CF",name:"Central African Republic"},{code:"TD",name:"Chad"},{code:"CL",name:"Chile"},{code:"CN",name:"China"},{code:"CO",name:"Colombia"},{code:"KM",name:"Comoros"},{code:"CG",name:"Congo"},{code:"CD",name:"Congo, DR"},{code:"CK",name:"Cook Islands"},{code:"CR",name:"Costa Rica"},{code:"HR",name:"Croatia"},{code:"CU",name:"Cuba"},{code:"CW",name:"Curaçao"},{code:"CY",name:"Cyprus"},{code:"CZ",name:"Czech Republic"},{code:"DK",name:"Denmark"},{code:"DJ",name:"Djibouti"},{code:"DM",name:"Dominica"},{code:"DO",name:"Dominican Republic"},{code:"EC",name:"Ecuador"},{code:"EG",name:"Egypt"},{code:"SV",name:"El Salvador"},{code:"GQ",name:"Equatorial Guinea"},{code:"ER",name:"Eritrea"},{code:"EE",name:"Estonia"},{code:"SZ",name:"Eswatini"},{code:"ET",name:"Ethiopia"},{code:"FO",name:"Faroe Islands"},{code:"FJ",name:"Fiji"},{code:"FI",name:"Finland"},{code:"FR",name:"France"},{code:"GF",name:"French Guiana"},{code:"PF",name:"French Polynesia"},{code:"GA",name:"Gabon"},{code:"GM",name:"Gambia"},{code:"GE",name:"Georgia"},{code:"DE",name:"Germany"},{code:"GH",name:"Ghana"},{code:"GI",name:"Gibraltar"},{code:"GR",name:"Greece"},{code:"GL",name:"Greenland"},{code:"GD",name:"Grenada"},{code:"GP",name:"Guadeloupe"},{code:"GU",name:"Guam"},{code:"GT",name:"Guatemala"},{code:"GN",name:"Guinea"},{code:"GW",name:"Guinea-Bissau"},{code:"GY",name:"Guyana"},{code:"HT",name:"Haiti"},{code:"HN",name:"Honduras"},{code:"HK",name:"Hong Kong"},{code:"HU",name:"Hungary"},{code:"IS",name:"Iceland"},{code:"IN",name:"India"},{code:"ID",name:"Indonesia"},{code:"IR",name:"Iran"},{code:"IQ",name:"Iraq"},{code:"IE",name:"Ireland"},{code:"IM",name:"Isle of Man"},{code:"IL",name:"Israel"},{code:"IT",name:"Italy"},{code:"JM",name:"Jamaica"},{code:"JP",name:"Japan"},{code:"JE",name:"Jersey"},{code:"JO",name:"Jordan"},{code:"KZ",name:"Kazakhstan"},{code:"KE",name:"Kenya"},{code:"KI",name:"Kiribati"},{code:"KR",name:"Korea, South"},{code:"XK",name:"Kosovo"},{code:"KW",name:"Kuwait"},{code:"KG",name:"Kyrgyzstan"},{code:"LA",name:"Laos"},{code:"LV",name:"Latvia"},{code:"LB",name:"Lebanon"},{code:"LS",name:"Lesotho"},{code:"LR",name:"Liberia"},{code:"LY",name:"Libya"},{code:"LI",name:"Liechtenstein"},{code:"LT",name:"Lithuania"},{code:"LU",name:"Luxembourg"},{code:"MO",name:"Macao"},{code:"MG",name:"Madagascar"},{code:"MW",name:"Malawi"},{code:"MY",name:"Malaysia"},{code:"MV",name:"Maldives"},{code:"ML",name:"Mali"},{code:"MT",name:"Malta"},{code:"MH",name:"Marshall Islands"},{code:"MQ",name:"Martinique"},{code:"MR",name:"Mauritania"},{code:"MU",name:"Mauritius"},{code:"MX",name:"Mexico"},{code:"FM",name:"Micronesia"},{code:"MD",name:"Moldova"},{code:"MC",name:"Monaco"},{code:"MN",name:"Mongolia"},{code:"ME",name:"Montenegro"},{code:"MS",name:"Montserrat"},{code:"MA",name:"Morocco"},{code:"MZ",name:"Mozambique"},{code:"MM",name:"Myanmar"},{code:"NA",name:"Namibia"},{code:"NR",name:"Nauru"},{code:"NP",name:"Nepal"},{code:"NL",name:"Netherlands"},{code:"NC",name:"New Caledonia"},{code:"NZ",name:"New Zealand"},{code:"NI",name:"Nicaragua"},{code:"NE",name:"Niger"},{code:"NG",name:"Nigeria"},{code:"NU",name:"Niue"},{code:"MK",name:"North Macedonia"},{code:"NO",name:"Norway"},{code:"OM",name:"Oman"},{code:"PK",name:"Pakistan"},{code:"PW",name:"Palau"},{code:"PS",name:"Palestine"},{code:"PA",name:"Panama"},{code:"PG",name:"Papua New Guinea"},{code:"PY",name:"Paraguay"},{code:"PE",name:"Peru"},{code:"PH",name:"Philippines"},{code:"PL",name:"Poland"},{code:"PT",name:"Portugal"},{code:"PR",name:"Puerto Rico"},{code:"QA",name:"Qatar"},{code:"RE",name:"Réunion"},{code:"RO",name:"Romania"},{code:"RW",name:"Rwanda"},{code:"KN",name:"Saint Kitts and Nevis"},{code:"LC",name:"Saint Lucia"},{code:"VC",name:"Saint Vincent and the Grenadines"},{code:"WS",name:"Samoa"},{code:"SM",name:"San Marino"},{code:"SA",name:"Saudi Arabia"},{code:"SN",name:"Senegal"},{code:"RS",name:"Serbia"},{code:"SC",name:"Seychelles"},{code:"SL",name:"Sierra Leone"},{code:"SG",name:"Singapore"},{code:"SK",name:"Slovakia"},{code:"SI",name:"Slovenia"},{code:"SB",name:"Solomon Islands"},{code:"SO",name:"Somalia"},{code:"ZA",name:"South Africa"},{code:"SS",name:"South Sudan"},{code:"ES",name:"Spain"},{code:"LK",name:"Sri Lanka"},{code:"SD",name:"Sudan"},{code:"SR",name:"Suriname"},{code:"SE",name:"Sweden"},{code:"CH",name:"Switzerland"},{code:"SY",name:"Syria"},{code:"TW",name:"Taiwan"},{code:"TJ",name:"Tajikistan"},{code:"TZ",name:"Tanzania"},{code:"TH",name:"Thailand"},{code:"TL",name:"Timor-Leste"},{code:"TG",name:"Togo"},{code:"TO",name:"Tonga"},{code:"TT",name:"Trinidad and Tobago"},{code:"TN",name:"Tunisia"},{code:"TR",name:"Turkey"},{code:"TM",name:"Turkmenistan"},{code:"TC",name:"Turks and Caicos Islands"},{code:"TV",name:"Tuvalu"},{code:"UG",name:"Uganda"},{code:"UA",name:"Ukraine"},{code:"AE",name:"United Arab Emirates"},{code:"GB",name:"United Kingdom"},{code:"US",name:"United States"},{code:"UY",name:"Uruguay"},{code:"UZ",name:"Uzbekistan"},{code:"VU",name:"Vanuatu"},{code:"VE",name:"Venezuela"},{code:"VN",name:"Vietnam"},{code:"VG",name:"Virgin Islands, British"},{code:"VI",name:"Virgin Islands, U.S."},{code:"YE",name:"Yemen"},{code:"ZM",name:"Zambia"},{code:"ZW",name:"Zimbabwe"}];let n=null;async function t(e){if("proxy"===e.mode){const t=await async function(e){if(n)return n;try{const t=await fetch(`/apps/${e}/auth/token`,{method:"GET",headers:{Accept:"application/json"}}),o=await t.json().catch(()=>null);return t.ok&&o&&o.success&&o.data&&o.data.access_token?(n=o.data.access_token,n):null}catch(e){return console.error("Error fetching proxy access token:",e),null}}(e.proxyApp);return t?`Bearer ${t}`:null}return e.accessToken?/^Bearer /i.test(e.accessToken)?e.accessToken:`Bearer ${e.accessToken}`:(console.error('notify-me-wl: data-auth="token" requires an access token. Set data-access-token on the open button or window.TWC_ACCESS_TOKEN, or use data-auth="proxy" to obtain one via the Shopify App Proxy.'),null)}const o=new Map;function a(e){return"string"==typeof e?e.trim():""}async function c(e,n,c){const r=`${e}|${c||""}`,i=o.get(r);if(void 0!==i)return i;const d=await t(n);if(!d)return null;try{const n=await fetch("https://api.au-aws.thewishlist.io/services/eventcollector/api/v1/custom/configs/public",{method:"GET",headers:{Accept:"application/json",Authorization:d,"X-Twc-Tenant":e}});if(!n.ok)return console.error("Tenant config request failed:",n.status),null;const t=function(e,n=null){if(!e||"object"!=typeof e)return null;const t=e.websiteRecommendations;if(!t||"object"!=typeof t)return null;const o=a(t.engine).toUpperCase();if("TWC"===o)return{engine:"TWC"};if("ATHOS"===o){const e=a(t.siteIdentifier);return e?{engine:"ATHOS",siteIdentifier:e,profileTag:a(t.profileTag)||a(t.tags)||a(n)||"similar"}:null}return null}(await n.json().catch(()=>null),c);return o.set(r,t),t}catch(e){return console.error("Error fetching tenant config:",e),null}}function r(e){return"string"==typeof e?e.trim():""}function i(e){if("number"==typeof e&&Number.isFinite(e))return e;if("string"==typeof e){const n=parseFloat(e);if(Number.isFinite(n))return n}return null}function d(e){return Boolean(e.name&&e.imageUrl&&e.productUrl)}function s(e,n){const t=new Map;return e.forEach(e=>{const n=t.get(e.product.ref);n&&n.score>=e.score||t.set(e.product.ref,e)}),Array.from(t.values()).sort((e,n)=>n.score-e.score).slice(0,n).map(e=>e.product)}async function m(e){const{email:n,tenant:o,auth:a,count:c}=e,m=await t(a);if(!m)return null;const l=Math.min(3*c,20),u=`https://api.au-aws.thewishlist.io/services/recommendations/api/v1/recommendations/${encodeURIComponent(o)}/${encodeURIComponent(n)}?n=${l}`;try{const e=await fetch(u,{method:"GET",headers:{Accept:"application/json",Authorization:m,"X-Twc-Tenant":o}});if(!e.ok)return console.error("Recommendations request failed:",e.status),null;return function(e,n){const t=e&&e.recommendations;if(!Array.isArray(t))return[];const o=[];return t.forEach(e=>{var n;const t=e&&e.product;if(!t)return;const a=r(t.name),c=r(t.image_url),s=r(t.product_url);if(!d({name:a,imageUrl:c,productUrl:s}))return;const m=r(t.product_ref)||r(t.product_id)||s;o.push({score:"number"==typeof e.score?e.score:0,product:{id:r(t.product_id)||m,ref:m,name:a,imageUrl:c,productUrl:s,price:null!==(n=i(t.price))&&void 0!==n?n:0,originalPrice:i(t.original_price)}})}),s(o,n)}(await e.json().catch(()=>null),c)}catch(e){return console.error("Error fetching recommendations:",e),null}}async function l(e){const{engine:n,email:t,count:o,productId:a}=e,c=encodeURIComponent(n.siteIdentifier),m=[`tags=${encodeURIComponent(n.profileTag)}`,`limits=${o}`];a&&m.push(`products=${encodeURIComponent(a)}`),t&&m.push(`shopper=${encodeURIComponent(t)}`);const l=`https://${c}.a.searchspring.io/boost/${c}/recommend?`+m.join("&");try{const e=await fetch(l,{method:"GET",headers:{Accept:"application/json"}});if(!e.ok)return console.error("Athos recommendations request failed:",e.status),null;return function(e,n){const t=Array.isArray(e)?e[0]:e,o=t&&t.results;if(!Array.isArray(o))return[];const a=[];return o.forEach((e,n)=>{var t;const c=e&&e.mappings&&e.mappings.core;if(!c)return;const s=r(c.name),m=r(c.imageUrl)||r(c.thumbnailImageUrl),l=r(c.url);if(!d({name:s,imageUrl:m,productUrl:l}))return;const u=r(c.uid)||r(c.sku)||l,p=null!==(t=i(c.price))&&void 0!==t?t:0,w=i(c.msrp);a.push({score:o.length-n,product:{id:r(c.uid)||u,ref:u,name:s,imageUrl:m,productUrl:l,price:p,originalPrice:null!==w&&w>p?w:null}})}),s(a,n)}(await e.json().catch(()=>null),o)}catch(e){return console.error("Error fetching Athos recommendations:",e),null}}const u=new Map;const p="twc-nm-recs",w="twc-nm-recs-title";function f(e,n){if(n)try{return new Intl.NumberFormat(void 0,{style:"currency",currency:n}).format(e)}catch(e){}return new Intl.NumberFormat(void 0,{minimumFractionDigits:0,maximumFractionDigits:2}).format(e)}function h(){const e=document.createElement("section");e.id=p,e.className="twc-nm-recs",e.setAttribute("aria-labelledby",w);const n=document.createElement("h3");n.id=w,n.className="twc-nm-recs-title",n.textContent="Shop similar styles",e.appendChild(n);const t=document.createElement("div");return t.className="twc-nm-recs-row",function(e){let n=0,t=0,o=!1,a=!1,c=!1;e.addEventListener("pointerdown",r=>{"mouse"===r.pointerType&&0===r.button&&(o=!0,a=!1,c=!1,n=r.clientX,t=e.scrollLeft,e.classList.add("twc-nm-recs-row--dragging"))}),e.addEventListener("pointermove",c=>{if(!o)return;const r=c.clientX-n;!a&&Math.abs(r)<5||(a=!0,e.scrollLeft=t-r,c.preventDefault())});const r=()=>{o&&(o=!1,c=a,e.classList.remove("twc-nm-recs-row--dragging"))};e.addEventListener("pointerup",r),e.addEventListener("pointercancel",r),e.addEventListener("pointerleave",r),e.addEventListener("click",e=>{c&&(c=!1,e.preventDefault(),e.stopPropagation())},!0),e.addEventListener("dragstart",e=>{o&&a&&e.preventDefault()})}(t),e.appendChild(t),{section:e,row:t}}function g(){const e=document.createElement("div");e.className="twc-nm-rec twc-nm-rec--skeleton",e.setAttribute("aria-hidden","true");const n=document.createElement("div");n.className="twc-nm-rec-imgbox twc-nm-skel",e.appendChild(n);const t=document.createElement("span");t.className="twc-nm-skel twc-nm-skel-line",e.appendChild(t);const o=document.createElement("span");return o.className="twc-nm-skel twc-nm-skel-line twc-nm-skel-line--short",e.appendChild(o),e}function y(e,n,t){const o=e.querySelector(".twc-nm-recs-row");if(!o)return;const a=(c=t,window.Shopify&&window.Shopify.currency&&window.Shopify.currency.active||c||null);var c;const r=document.createDocumentFragment();n.forEach(e=>{r.appendChild(function(e,n){const t=document.createElement("a");t.className="twc-nm-rec",t.href=e.productUrl,t.target="_blank",t.rel="noopener noreferrer";const o=document.createElement("div");o.className="twc-nm-rec-imgbox";const a=document.createElement("img");a.className="twc-nm-rec-img",a.src=e.imageUrl,a.alt=e.name,a.loading="lazy",a.decoding="async",o.appendChild(a),t.appendChild(o);const c=document.createElement("span");c.className="twc-nm-rec-name",c.textContent=e.name,t.appendChild(c);const r=document.createElement("span");if(r.className="twc-nm-rec-price",null!==e.originalPrice&&e.originalPrice>e.price){const t=document.createElement("s");t.className="twc-nm-rec-was",t.textContent=f(e.originalPrice,n),r.appendChild(t),r.appendChild(document.createTextNode(" "))}return r.appendChild(document.createTextNode(f(e.price,n))),t.appendChild(r),t}(e,a))}),o.textContent="",o.appendChild(r),e.classList.remove("twc-nm-recs--loading"),e.removeAttribute("aria-busy")}function b(){const e=document.getElementById(p);e&&e.parentNode&&e.parentNode.removeChild(e)}const v={"notify-me":{text:"Register to receive a notification as soon as this item is back in stock",buttonText:"Notify Me"},"coming-soon":{text:"Register your interest to hear more about this item",buttonText:"Register Interest"}};function x(n){const{wrapper:o,openButton:a,config:r,productData:i,localization:d}=n,{fields:s,type:w,display:f,auth:x,tenant:C,recommendationsEnabled:k,recommendationsCount:E,recommendationsProfile:S,currency:A,customerEmail:T}=r,M=document.createElement("div");M.innerHTML='\n <div id="twc-nm-overlay" role="dialog" aria-modal="true" aria-labelledby="popup-title">\n <div class="twc-nm-card">\n <button type="button" id="popup-close" class="twc-nm-close" aria-label="Close"><svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true" focusable="false"><path d="M3 3l10 10M13 3L3 13" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" fill="none"/></svg></button>\n <h2 id="popup-title" class="twc-nm-title"></h2>\n <p id="popup-text" class="twc-nm-text"></p>\n <form id="popup-form" class="twc-nm-form">\n <div class="twc-nm-field">\n <label class="twc-nm-label" for="twc-nm-size">Size</label>\n <select class="twc-nm-select" id="twc-nm-size" name="select-size" required>\n <option value="" disabled selected>Select a size</option>\n </select>\n </div>\n </form>\n </div>\n </div>\n ',o.appendChild(M);const N=document.getElementById("twc-nm-overlay"),I=document.getElementById("popup-close"),L=document.getElementById("popup-title"),B=document.getElementById("popup-text"),G=document.querySelector("select[name='select-size']"),P=document.getElementById("popup-form");if(!(N&&I&&L&&B&&G&&P))return;"panel"===f&&N.classList.add("twc-nm--panel"),B.innerText=v[w].text;const U=e.map(e=>`<option value="${e.code}"${e.code===d.countryCode?" selected":""}>${e.name}</option>`).join(""),z=(e,n,t)=>`<div class="twc-nm-field"><label class="twc-nm-label" for="${e}">${n}</label>${t}</div>`,R={email:z("twc-nm-email","Email",'<input class="twc-nm-input" id="twc-nm-email" name="email" type="email" placeholder="you@example.com" required />'),mobile:z("twc-nm-mobile","Mobile",'<input class="twc-nm-input" id="twc-nm-mobile" name="mobile" type="tel" placeholder="Mobile number" required />'),firstName:z("twc-nm-firstname","First name",'<input class="twc-nm-input" id="twc-nm-firstname" name="firstName" type="text" placeholder="First name" required />'),lastName:z("twc-nm-lastname","Last name",'<input class="twc-nm-input" id="twc-nm-lastname" name="lastName" type="text" placeholder="Last name" required />'),countryCode:z("twc-nm-country","Country",`<select class="twc-nm-select" id="twc-nm-country" name="countryCode"><option value="">Select country</option>${U}</select>`),provinceCode:z("twc-nm-province","State / Province",`<input class="twc-nm-input" id="twc-nm-province" name="provinceCode" type="text" placeholder="State / province code" value="${d.provinceCode||""}" />`)};if(s.forEach(e=>{R[e]&&P.insertAdjacentHTML("beforeend",R[e])}),P.insertAdjacentHTML("beforeend",'<div class="twc-nm-checks">\n <label class="twc-nm-check"><input type="checkbox" name="mailList" /> Email me store updates</label>\n <label class="twc-nm-check"><input type="checkbox" name="mailListSms" /> Text me store updates</label>\n </div>'),P.insertAdjacentHTML("beforeend",'<div id="popup-status" class="twc-nm-status" role="status" aria-live="polite" hidden></div>'),P.insertAdjacentHTML("beforeend",`<button type="submit" class="twc-nm-submit">${v[w].buttonText}</button>`),T){const e=P.querySelector("input[name='email']");e&&(e.value=T)}const $=document.getElementById("popup-status"),D=P.querySelector(".twc-nm-submit");if(!$||!D)return;function K(e,n){$.textContent=n,$.className=`twc-nm-status twc-nm-status--${e}`,$.hidden=!1}function H(){$.hidden=!0,$.textContent=""}const F=e=>{const n=P.querySelector(e);return n?n.value:""},j=e=>{const n=P.querySelector(e);return!!n&&n.checked},O=N.querySelector(".twc-nm-card");let q=null;async function V(e){const n=await function(e,n,t){return c(e,n,t)}(C,x,S);if(!n)return!1;if(!N.classList.contains("is-open"))return!1;const t=function(e){const{section:n,row:t}=h();n.classList.add("twc-nm-recs--loading"),n.setAttribute("aria-busy","true");for(let n=0;n<e;n++)t.appendChild(g());return n}(E);O.appendChild(t);const o=await async function(e,n){const{email:t,tenant:o,auth:a,count:c,productId:r}=n,i=`${e.engine}|${o}|${t}|${c}|${r||""}`,d=u.get(i);if(d)return d;let s=null;return s="ATHOS"===e.engine?await l({engine:e,email:t,count:c,productId:r}):await m({email:t,tenant:o,auth:a,count:c}),null===s?[]:(u.set(i,s),s)}(n,{email:e,tenant:C,auth:x,count:E,productId:i?String(i.id):null});return!!t.isConnected&&(o.length?(y(t,o,A),!0):(b(),!1))}function _(e){if(!k||!O)return Promise.resolve(!1);if(q)return q;if(document.getElementById(p))return Promise.resolve(!0);const n=V(e);return q=n,n.then(e=>(q===n&&(q=null),e),e=>(q===n&&(q=null),console.error("Error rendering recommendations:",e),!1))}let W=!1,Z=null;function J(e){"Escape"===e.key&&Y()}function Y(){N.classList.remove("is-open"),document.body.style.overflow="",document.removeEventListener("keydown",J),H(),b(),q=null,Z&&"function"==typeof Z.focus&&Z.focus()}a.addEventListener("click",function(){if(Z=document.activeElement,N.classList.add("is-open"),document.body.style.overflow="hidden",document.addEventListener("keydown",J),!i)return P.hidden=!0,void K("error","Product information is unavailable. Please refresh the page.");L.textContent=i.title,W||((i.variants||[]).forEach(e=>{const n=document.createElement("option");n.value=e.title,n.textContent=e.title,G.appendChild(n)}),W=!0),T&&_(T);const e=P.querySelector("select, input");e&&e.focus()}),I.addEventListener("click",Y),N.addEventListener("click",e=>{e.target===N&&Y()}),P.addEventListener("submit",async e=>{e.preventDefault();const n=((null==i?void 0:i.variants)||[]).find(e=>e.title===G.value);if(!n)return void K("error","Please select a size.");const o={variantRef:n.id,email:F("input[name='email']"),subscribe:j("input[name='mailList']"),subscribeSms:j("input[name='mailListSms']")};if("coming-soon"===w?o.comingSoon=!0:o.notifyMe=!0,s.includes("firstName")&&(o.firstName=F("input[name='firstName']")),s.includes("lastName")&&(o.lastName=F("input[name='lastName']")),s.includes("mobile")&&(o.mobile=F("input[name='mobile']")),s.includes("countryCode")){const e=F("select[name='countryCode']");e&&(o.countryCode=e)}else d.countryCode&&(o.countryCode=d.countryCode);if(s.includes("provinceCode")){const e=F("input[name='provinceCode']");e&&(o.provinceCode=e)}else d.provinceCode&&(o.provinceCode=d.provinceCode);d.marketId&&(o.marketId=d.marketId),d.marketHandle&&(o.marketHandle=d.marketHandle),H();const a=D.textContent||"";D.disabled=!0,D.textContent="Sending…";let c={ok:!1,reason:"error"};try{c=await async function(e,n,o){const a=await t(n);if(!a)return{ok:!1,reason:"auth"};try{return(await fetch("https://api.au-aws.thewishlist.io/services/wsservice/api/wishlist/items/customerInterest",{method:"POST",headers:{"Content-Type":"application/json",Authorization:a,"X-Twc-Tenant":o},body:JSON.stringify(e)})).ok?{ok:!0}:{ok:!1,reason:"error"}}catch(e){return console.error("Error submitting form:",e),{ok:!1,reason:"error"}}}(o,x,C)}finally{D.disabled=!1,D.textContent=a}if(c.ok){K("success","You're on the list. We'll be in touch.");const e=T||F("input[name='email']");!!e&&await _(e)||setTimeout(Y,1500)}else"auth"===c.reason?K("error","proxy"===x.mode?"Please log in to your account to continue.":"Something went wrong. Please try again."):K("error","Something went wrong. Please try again.")})}function C(e){var n;const t=(null!=e?e:"").trim();if(!t)return null;const o=null!==(n=t.split("?")[0].split("/").pop())&&void 0!==n?n:"";return/^\d+$/.test(o)?o:null}function k(e){const n=(null!=e?e:"").trim();return/^[a-z0-9_-]+$/i.test(n)?n:null}function E(e){const n=(null!=e?e:"").trim();return/^[a-z]{2}$/i.test(n)?n.toUpperCase():null}function S(e){const n=e.getAttribute("data-customer-email");if(n&&n.trim())return n.trim();const t=window.customer&&window.customer.email;if(t&&t.trim())return t.trim();const o=window.customerEmail;return"string"==typeof o&&o.trim()?o.trim():null}document.addEventListener("DOMContentLoaded",()=>{const e=document.getElementById("notification-widget");if(!e)return;const n=document.getElementById("popup-open");if(!n)return;!function(){const e=document.createElement("style");e.type="text/css",e.innerText="\n .notification-btn {\n display: inline-block;\n width: 100%;\n padding: 12px 24px;\n background: #fff;\n border: 1px solid #111827;\n border-radius: 8px;\n color: #111827;\n font: inherit;\n text-align: center;\n text-transform: uppercase;\n cursor: pointer;\n }\n .notification-btn:hover {\n background: #111827;\n color: #fff;\n }\n #twc-nm-overlay {\n position: fixed;\n inset: 0;\n z-index: 99999;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 16px;\n background: rgba(17, 17, 17, 0.55);\n opacity: 0;\n visibility: hidden;\n transition: opacity 200ms ease;\n }\n #twc-nm-overlay.is-open {\n opacity: 1;\n visibility: visible;\n }\n #twc-nm-overlay * {\n box-sizing: border-box;\n }\n #twc-nm-overlay .twc-nm-card {\n position: relative;\n width: 100%;\n max-width: 400px;\n max-height: calc(100vh - 32px);\n overflow-y: auto;\n padding: 24px;\n background: #fff;\n color: #111827;\n border-radius: 12px;\n box-shadow: 0 10px 40px rgba(0, 0, 0, 0.18);\n font: inherit;\n transform: translateY(8px);\n transition: transform 200ms ease;\n }\n #twc-nm-overlay.is-open .twc-nm-card {\n transform: translateY(0);\n }\n /* Panel mode: same blocking backdrop, docked to the right edge and\n full height instead of centred. Declared after the modal rules so\n the shared properties are overridden. */\n #twc-nm-overlay.twc-nm--panel {\n align-items: stretch;\n justify-content: flex-end;\n padding: 0;\n }\n #twc-nm-overlay.twc-nm--panel .twc-nm-card {\n width: 420px;\n max-width: 100%;\n max-height: none;\n border-radius: 0;\n box-shadow: -10px 0 40px rgba(0, 0, 0, 0.18);\n transform: translateX(100%);\n }\n /* Needs the extra class to beat both the .is-open rule above and the\n panel's own resting transform. */\n #twc-nm-overlay.twc-nm--panel.is-open .twc-nm-card {\n transform: translateX(0);\n }\n @media (max-width: 480px) {\n #twc-nm-overlay.twc-nm--panel .twc-nm-card {\n width: 100%;\n }\n }\n #twc-nm-overlay .twc-nm-close {\n position: absolute;\n top: 12px;\n right: 12px;\n width: 32px;\n height: 32px;\n min-width: 0;\n min-height: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0;\n padding: 0;\n border: none;\n background: transparent;\n box-shadow: none;\n border-radius: 6px;\n text-indent: 0;\n line-height: 1;\n color: #6b7280;\n opacity: 1;\n visibility: visible;\n cursor: pointer;\n }\n /* The icon is an inline SVG rather than a text glyph so host-theme\n button resets (font-size: 0, icon fonts, text-indent) can't hide it. */\n #twc-nm-overlay .twc-nm-close svg {\n display: block;\n width: 16px;\n height: 16px;\n flex: none;\n pointer-events: none;\n }\n #twc-nm-overlay .twc-nm-close::before,\n #twc-nm-overlay .twc-nm-close::after {\n content: none;\n }\n #twc-nm-overlay .twc-nm-close:hover {\n background: #f3f4f6;\n color: #111827;\n }\n #twc-nm-overlay .twc-nm-title {\n margin: 0 32px 4px 0;\n font-size: 18px;\n font-weight: 600;\n }\n #twc-nm-overlay .twc-nm-text {\n margin: 0 0 16px;\n font-size: 14px;\n line-height: 1.5;\n color: #6b7280;\n }\n #twc-nm-overlay .twc-nm-form {\n display: flex;\n flex-direction: column;\n gap: 14px;\n }\n #twc-nm-overlay .twc-nm-field {\n display: flex;\n flex-direction: column;\n gap: 6px;\n }\n #twc-nm-overlay .twc-nm-label {\n font-size: 13px;\n font-weight: 500;\n color: #374151;\n }\n #twc-nm-overlay .twc-nm-input,\n #twc-nm-overlay .twc-nm-select {\n width: 100%;\n padding: 10px 12px;\n font-size: 14px;\n font-family: inherit;\n color: #111827;\n background: #fff;\n border: 1px solid #e5e7eb;\n border-radius: 8px;\n }\n #twc-nm-overlay .twc-nm-input:focus,\n #twc-nm-overlay .twc-nm-select:focus {\n outline: none;\n border-color: #111827;\n box-shadow: 0 0 0 3px rgba(17, 24, 39, 0.08);\n }\n #twc-nm-overlay .twc-nm-select {\n appearance: none;\n -webkit-appearance: none;\n padding-right: 36px;\n cursor: pointer;\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2 4l4 4 4-4' stroke='%236b7280' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: right 12px center;\n }\n #twc-nm-overlay .twc-nm-checks {\n display: flex;\n flex-direction: column;\n gap: 10px;\n }\n #twc-nm-overlay .twc-nm-check {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n color: #374151;\n cursor: pointer;\n }\n #twc-nm-overlay .twc-nm-check input {\n width: 16px;\n height: 16px;\n margin: 0;\n accent-color: #111827;\n }\n #twc-nm-overlay .twc-nm-status {\n font-size: 13px;\n line-height: 1.5;\n padding: 10px 12px;\n border-radius: 8px;\n }\n #twc-nm-overlay .twc-nm-status--error {\n color: #b91c1c;\n background: #fef2f2;\n }\n #twc-nm-overlay .twc-nm-status--success {\n color: #15803d;\n background: #f0fdf4;\n }\n #twc-nm-overlay .twc-nm-submit {\n width: 100%;\n padding: 12px 16px;\n font-size: 14px;\n font-weight: 600;\n font-family: inherit;\n color: #fff;\n background: #111827;\n border: none;\n border-radius: 8px;\n cursor: pointer;\n transition: background 150ms ease;\n }\n #twc-nm-overlay .twc-nm-submit:hover {\n background: #374151;\n }\n #twc-nm-overlay .twc-nm-submit:disabled {\n opacity: 0.6;\n cursor: default;\n }\n #twc-nm-overlay .twc-nm-recs {\n margin-top: 20px;\n padding-top: 20px;\n border-top: 1px solid #e5e7eb;\n }\n #twc-nm-overlay .twc-nm-recs-title {\n margin: 0 0 12px;\n font-size: 14px;\n font-weight: 600;\n color: #111827;\n }\n #twc-nm-overlay .twc-nm-recs-row {\n display: flex;\n gap: 12px;\n overflow-x: auto;\n scroll-snap-type: x mandatory;\n -webkit-overflow-scrolling: touch;\n padding-bottom: 8px;\n scrollbar-width: thin;\n }\n /* Only where a real pointer exists — the affordance is meaningless\n on touch, which pans the row natively. */\n @media (hover: hover) {\n #twc-nm-overlay .twc-nm-recs-row {\n cursor: grab;\n }\n }\n #twc-nm-overlay .twc-nm-recs-row--dragging,\n #twc-nm-overlay .twc-nm-recs-row--dragging .twc-nm-rec {\n cursor: grabbing;\n }\n #twc-nm-overlay .twc-nm-recs-row--dragging {\n /* Snapping fights a manually driven scrollLeft and feels sticky. */\n scroll-snap-type: none;\n user-select: none;\n -webkit-user-select: none;\n }\n #twc-nm-overlay .twc-nm-rec {\n flex: 0 0 132px;\n scroll-snap-align: start;\n display: flex;\n flex-direction: column;\n gap: 6px;\n color: inherit;\n text-decoration: none;\n }\n #twc-nm-overlay .twc-nm-rec:focus-visible {\n outline: 2px solid #111827;\n outline-offset: 2px;\n border-radius: 8px;\n }\n #twc-nm-overlay .twc-nm-rec-imgbox {\n aspect-ratio: 3 / 4;\n overflow: hidden;\n border-radius: 8px;\n background: #f3f4f6;\n }\n #twc-nm-overlay .twc-nm-rec-img {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n transition: opacity 150ms ease;\n }\n #twc-nm-overlay .twc-nm-rec:hover .twc-nm-rec-img {\n opacity: 0.85;\n }\n #twc-nm-overlay .twc-nm-rec-name {\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n font-size: 13px;\n line-height: 1.35;\n color: #111827;\n }\n #twc-nm-overlay .twc-nm-rec-price {\n font-size: 13px;\n font-weight: 600;\n color: #111827;\n }\n #twc-nm-overlay .twc-nm-rec-was {\n margin-right: 4px;\n font-weight: 400;\n color: #9ca3af;\n }\n #twc-nm-overlay .twc-nm-rec--skeleton {\n pointer-events: none;\n }\n #twc-nm-overlay .twc-nm-skel {\n background-color: #f3f4f6;\n background-image: linear-gradient(\n 90deg,\n #f3f4f6 0%,\n #e8eaee 50%,\n #f3f4f6 100%\n );\n background-size: 200% 100%;\n border-radius: 4px;\n animation: twc-nm-shimmer 1.3s ease-in-out infinite;\n }\n #twc-nm-overlay .twc-nm-rec-imgbox.twc-nm-skel {\n border-radius: 8px;\n }\n #twc-nm-overlay .twc-nm-skel-line {\n height: 12px;\n width: 100%;\n }\n #twc-nm-overlay .twc-nm-skel-line--short {\n width: 45%;\n }\n @keyframes twc-nm-shimmer {\n from {\n background-position: 200% 0;\n }\n to {\n background-position: -200% 0;\n }\n }\n @media (prefers-reduced-motion: reduce) {\n #twc-nm-overlay,\n #twc-nm-overlay .twc-nm-card,\n #twc-nm-overlay .twc-nm-rec-img {\n transition: none;\n }\n #twc-nm-overlay .twc-nm-card,\n #twc-nm-overlay.twc-nm--panel .twc-nm-card {\n transform: none;\n }\n #twc-nm-overlay .twc-nm-skel {\n animation: none;\n }\n }\n ",document.head.appendChild(e)}();const t=JSON.parse(n.getAttribute("data-fields")||'["email"]'),o=n.getAttribute("data-type")||"notify-me",a="panel"===n.getAttribute("data-display")?"panel":"modal",c=n.getAttribute("data-auth")||"proxy",r=n.getAttribute("data-tenant")||"victoria-woods",i=n.getAttribute("data-proxy-app")||"twc-sdk",d=n.getAttribute("data-access-token")||window.TWC_ACCESS_TOKEN||null,s="false"!==n.getAttribute("data-recommendations"),m=parseInt(n.getAttribute("data-recommendations-count")||"",10),l=Number.isFinite(m)&&m>0?m:8,u=(n.getAttribute("data-recommendations-profile")||"").trim()||null,p=n.getAttribute("data-currency")||null,w=function(e){var n,t,o;const a={countryCode:E(e.getAttribute("data-country-code")),provinceCode:null,marketId:C(e.getAttribute("data-market-id")),marketHandle:k(e.getAttribute("data-market-handle"))},c=window.Shopify;if(c){const e=null===(n=c.markets)||void 0===n?void 0:n.currentMarket;if(e&&(!a.countryCode&&e.countryCode&&(a.countryCode=e.countryCode),a.marketId||(a.marketId=C(String(null!==(t=e.id)&&void 0!==t?t:"")))),a.countryCode||"string"!=typeof c.country||(a.countryCode=c.country),!a.countryCode&&"string"==typeof c.locale){const e=c.locale.split("-");e.length>1&&(a.countryCode=e[e.length-1].toUpperCase())}}if(!a.countryCode||!a.provinceCode){const e=null===(o=window.customer)||void 0===o?void 0:o.default_address;e&&(!a.countryCode&&e.country_code&&(a.countryCode=e.country_code),!a.provinceCode&&e.province_code&&(a.provinceCode=e.province_code))}if(!a.countryCode){const e=(navigator.language||"").split("-");e.length>1&&(a.countryCode=e[e.length-1].toUpperCase())}return a}(n);x({wrapper:e,openButton:n,config:{fields:t,type:o,display:a,auth:{mode:c,proxyApp:i,accessToken:d},tenant:r,recommendationsEnabled:s,recommendationsCount:l,recommendationsProfile:u,currency:p,customerEmail:S(n)},productData:window.currentProduct,localization:w})})}();
1
+ !function(){"use strict";const e="data-twc-product";function n(e){if(!e||"object"!=typeof e)return!1;const n=e;return("number"==typeof n.id||"string"==typeof n.id)&&"string"==typeof n.title&&(void 0===n.variants||Array.isArray(n.variants))}function t(){const e=new URLSearchParams(window.location.search).get("variant"),n=document.querySelector('form[action*="/cart/add"] select[name="id"]');return[e,n?n.value:null]}function o(e){return e.replace(/^data-/,"").replace(/-([a-z])/g,(e,n)=>n.toUpperCase())}function a(e,n){console.warn(`notify-me-wl: ignoring data-twc-config "${e}": expected ${n}`)}function r(e){return Array.isArray(e)&&e.every(e=>"string"==typeof e)}const c={title:".twc-nm-title",intro:".twc-nm-text",label:".twc-nm-label",input:".twc-nm-input",select:".twc-nm-select",checkbox:".twc-nm-check input",submit:".twc-nm-submit",close:".twc-nm-close",disclaimer:".twc-nm-disclaimer",summary:".twc-nm-summary",summaryImage:".twc-nm-summary-image",summaryName:".twc-nm-summary-name",summaryVariant:".twc-nm-summary-variant",summaryPrice:".twc-nm-summary-price",summaryCompareAt:".twc-nm-summary-compare"},i=/^[A-Za-z_-][A-Za-z0-9_-]*$/;function m(e){const n={};if(null==e)return n;if("object"!=typeof e||Array.isArray(e))return a("classes","an object"),n;const t=e;return Object.keys(c).forEach(e=>{const o=t[e];if(null==o)return;if("string"!=typeof o)return void a(`classes.${e}`,"a string");const r=o.split(/\s+/).filter(Boolean),c=r.filter(e=>i.test(e));c.length<r.length&&a(`classes.${e}`,'class names of letters, digits, "-" and "_"'),c.length&&(n[e]=c)}),n}const s=[{code:"AF",name:"Afghanistan"},{code:"AX",name:"Åland Islands"},{code:"AL",name:"Albania"},{code:"DZ",name:"Algeria"},{code:"AS",name:"American Samoa"},{code:"AD",name:"Andorra"},{code:"AO",name:"Angola"},{code:"AI",name:"Anguilla"},{code:"AG",name:"Antigua and Barbuda"},{code:"AR",name:"Argentina"},{code:"AM",name:"Armenia"},{code:"AW",name:"Aruba"},{code:"AU",name:"Australia"},{code:"AT",name:"Austria"},{code:"AZ",name:"Azerbaijan"},{code:"BS",name:"Bahamas"},{code:"BH",name:"Bahrain"},{code:"BD",name:"Bangladesh"},{code:"BB",name:"Barbados"},{code:"BY",name:"Belarus"},{code:"BE",name:"Belgium"},{code:"BZ",name:"Belize"},{code:"BJ",name:"Benin"},{code:"BM",name:"Bermuda"},{code:"BT",name:"Bhutan"},{code:"BO",name:"Bolivia"},{code:"BA",name:"Bosnia and Herzegovina"},{code:"BW",name:"Botswana"},{code:"BR",name:"Brazil"},{code:"BN",name:"Brunei"},{code:"BG",name:"Bulgaria"},{code:"BF",name:"Burkina Faso"},{code:"BI",name:"Burundi"},{code:"CV",name:"Cabo Verde"},{code:"KH",name:"Cambodia"},{code:"CM",name:"Cameroon"},{code:"CA",name:"Canada"},{code:"KY",name:"Cayman Islands"},{code:"CF",name:"Central African Republic"},{code:"TD",name:"Chad"},{code:"CL",name:"Chile"},{code:"CN",name:"China"},{code:"CO",name:"Colombia"},{code:"KM",name:"Comoros"},{code:"CG",name:"Congo"},{code:"CD",name:"Congo, DR"},{code:"CK",name:"Cook Islands"},{code:"CR",name:"Costa Rica"},{code:"HR",name:"Croatia"},{code:"CU",name:"Cuba"},{code:"CW",name:"Curaçao"},{code:"CY",name:"Cyprus"},{code:"CZ",name:"Czech Republic"},{code:"DK",name:"Denmark"},{code:"DJ",name:"Djibouti"},{code:"DM",name:"Dominica"},{code:"DO",name:"Dominican Republic"},{code:"EC",name:"Ecuador"},{code:"EG",name:"Egypt"},{code:"SV",name:"El Salvador"},{code:"GQ",name:"Equatorial Guinea"},{code:"ER",name:"Eritrea"},{code:"EE",name:"Estonia"},{code:"SZ",name:"Eswatini"},{code:"ET",name:"Ethiopia"},{code:"FO",name:"Faroe Islands"},{code:"FJ",name:"Fiji"},{code:"FI",name:"Finland"},{code:"FR",name:"France"},{code:"GF",name:"French Guiana"},{code:"PF",name:"French Polynesia"},{code:"GA",name:"Gabon"},{code:"GM",name:"Gambia"},{code:"GE",name:"Georgia"},{code:"DE",name:"Germany"},{code:"GH",name:"Ghana"},{code:"GI",name:"Gibraltar"},{code:"GR",name:"Greece"},{code:"GL",name:"Greenland"},{code:"GD",name:"Grenada"},{code:"GP",name:"Guadeloupe"},{code:"GU",name:"Guam"},{code:"GT",name:"Guatemala"},{code:"GN",name:"Guinea"},{code:"GW",name:"Guinea-Bissau"},{code:"GY",name:"Guyana"},{code:"HT",name:"Haiti"},{code:"HN",name:"Honduras"},{code:"HK",name:"Hong Kong"},{code:"HU",name:"Hungary"},{code:"IS",name:"Iceland"},{code:"IN",name:"India"},{code:"ID",name:"Indonesia"},{code:"IR",name:"Iran"},{code:"IQ",name:"Iraq"},{code:"IE",name:"Ireland"},{code:"IM",name:"Isle of Man"},{code:"IL",name:"Israel"},{code:"IT",name:"Italy"},{code:"JM",name:"Jamaica"},{code:"JP",name:"Japan"},{code:"JE",name:"Jersey"},{code:"JO",name:"Jordan"},{code:"KZ",name:"Kazakhstan"},{code:"KE",name:"Kenya"},{code:"KI",name:"Kiribati"},{code:"KR",name:"Korea, South"},{code:"XK",name:"Kosovo"},{code:"KW",name:"Kuwait"},{code:"KG",name:"Kyrgyzstan"},{code:"LA",name:"Laos"},{code:"LV",name:"Latvia"},{code:"LB",name:"Lebanon"},{code:"LS",name:"Lesotho"},{code:"LR",name:"Liberia"},{code:"LY",name:"Libya"},{code:"LI",name:"Liechtenstein"},{code:"LT",name:"Lithuania"},{code:"LU",name:"Luxembourg"},{code:"MO",name:"Macao"},{code:"MG",name:"Madagascar"},{code:"MW",name:"Malawi"},{code:"MY",name:"Malaysia"},{code:"MV",name:"Maldives"},{code:"ML",name:"Mali"},{code:"MT",name:"Malta"},{code:"MH",name:"Marshall Islands"},{code:"MQ",name:"Martinique"},{code:"MR",name:"Mauritania"},{code:"MU",name:"Mauritius"},{code:"MX",name:"Mexico"},{code:"FM",name:"Micronesia"},{code:"MD",name:"Moldova"},{code:"MC",name:"Monaco"},{code:"MN",name:"Mongolia"},{code:"ME",name:"Montenegro"},{code:"MS",name:"Montserrat"},{code:"MA",name:"Morocco"},{code:"MZ",name:"Mozambique"},{code:"MM",name:"Myanmar"},{code:"NA",name:"Namibia"},{code:"NR",name:"Nauru"},{code:"NP",name:"Nepal"},{code:"NL",name:"Netherlands"},{code:"NC",name:"New Caledonia"},{code:"NZ",name:"New Zealand"},{code:"NI",name:"Nicaragua"},{code:"NE",name:"Niger"},{code:"NG",name:"Nigeria"},{code:"NU",name:"Niue"},{code:"MK",name:"North Macedonia"},{code:"NO",name:"Norway"},{code:"OM",name:"Oman"},{code:"PK",name:"Pakistan"},{code:"PW",name:"Palau"},{code:"PS",name:"Palestine"},{code:"PA",name:"Panama"},{code:"PG",name:"Papua New Guinea"},{code:"PY",name:"Paraguay"},{code:"PE",name:"Peru"},{code:"PH",name:"Philippines"},{code:"PL",name:"Poland"},{code:"PT",name:"Portugal"},{code:"PR",name:"Puerto Rico"},{code:"QA",name:"Qatar"},{code:"RE",name:"Réunion"},{code:"RO",name:"Romania"},{code:"RW",name:"Rwanda"},{code:"KN",name:"Saint Kitts and Nevis"},{code:"LC",name:"Saint Lucia"},{code:"VC",name:"Saint Vincent and the Grenadines"},{code:"WS",name:"Samoa"},{code:"SM",name:"San Marino"},{code:"SA",name:"Saudi Arabia"},{code:"SN",name:"Senegal"},{code:"RS",name:"Serbia"},{code:"SC",name:"Seychelles"},{code:"SL",name:"Sierra Leone"},{code:"SG",name:"Singapore"},{code:"SK",name:"Slovakia"},{code:"SI",name:"Slovenia"},{code:"SB",name:"Solomon Islands"},{code:"SO",name:"Somalia"},{code:"ZA",name:"South Africa"},{code:"SS",name:"South Sudan"},{code:"ES",name:"Spain"},{code:"LK",name:"Sri Lanka"},{code:"SD",name:"Sudan"},{code:"SR",name:"Suriname"},{code:"SE",name:"Sweden"},{code:"CH",name:"Switzerland"},{code:"SY",name:"Syria"},{code:"TW",name:"Taiwan"},{code:"TJ",name:"Tajikistan"},{code:"TZ",name:"Tanzania"},{code:"TH",name:"Thailand"},{code:"TL",name:"Timor-Leste"},{code:"TG",name:"Togo"},{code:"TO",name:"Tonga"},{code:"TT",name:"Trinidad and Tobago"},{code:"TN",name:"Tunisia"},{code:"TR",name:"Turkey"},{code:"TM",name:"Turkmenistan"},{code:"TC",name:"Turks and Caicos Islands"},{code:"TV",name:"Tuvalu"},{code:"UG",name:"Uganda"},{code:"UA",name:"Ukraine"},{code:"AE",name:"United Arab Emirates"},{code:"GB",name:"United Kingdom"},{code:"US",name:"United States"},{code:"UY",name:"Uruguay"},{code:"UZ",name:"Uzbekistan"},{code:"VU",name:"Vanuatu"},{code:"VE",name:"Venezuela"},{code:"VN",name:"Vietnam"},{code:"VG",name:"Virgin Islands, British"},{code:"VI",name:"Virgin Islands, U.S."},{code:"YE",name:"Yemen"},{code:"ZM",name:"Zambia"},{code:"ZW",name:"Zimbabwe"}];let l=null;async function d(e){if("proxy"===e.mode){const n=await async function(e){if(l)return l;try{const n=await fetch(`/apps/${e}/auth/token`,{method:"GET",headers:{Accept:"application/json"}}),t=await n.json().catch(()=>null);return n.ok&&t&&t.success&&t.data&&t.data.access_token?(l=t.data.access_token,l):null}catch(e){return console.error("Error fetching proxy access token:",e),null}}(e.proxyApp);return n?`Bearer ${n}`:null}return e.accessToken?/^Bearer /i.test(e.accessToken)?e.accessToken:`Bearer ${e.accessToken}`:(console.error('notify-me-wl: data-auth="token" requires an access token. Set data-access-token on the open button or window.TWC_ACCESS_TOKEN, or use data-auth="proxy" to obtain one via the Shopify App Proxy.'),null)}const u=new Map;function p(e){return"string"==typeof e?e.trim():""}async function w(e,n,t){const o=`${e}|${t||""}`,a=u.get(o);if(void 0!==a)return a;const r=await d(n);if(!r)return null;try{const n=await fetch("https://api.au-aws.thewishlist.io/services/eventcollector/api/v1/custom/configs/public",{method:"GET",headers:{Accept:"application/json",Authorization:r,"X-Twc-Tenant":e}});if(!n.ok)return console.error("Tenant config request failed:",n.status),null;const a=function(e,n=null){if(!e||"object"!=typeof e)return null;const t=e.websiteRecommendations;if(!t||"object"!=typeof t)return null;const o=p(t.engine).toUpperCase();if("TWC"===o)return{engine:"TWC"};if("ATHOS"===o){const e=p(t.siteIdentifier);return e?{engine:"ATHOS",siteIdentifier:e,profileTag:p(t.profileTag)||p(t.tags)||p(n)||"similar"}:null}return null}(await n.json().catch(()=>null),t);return u.set(o,a),a}catch(e){return console.error("Error fetching tenant config:",e),null}}function f(e){return"string"==typeof e?e.trim():""}function y(e){if("number"==typeof e&&Number.isFinite(e))return e;if("string"==typeof e){const n=parseFloat(e);if(Number.isFinite(n))return n}return null}function h(e){return Boolean(e.name&&e.imageUrl&&e.productUrl)}function g(e,n){const t=new Map;return e.forEach(e=>{const n=t.get(e.product.ref);n&&n.score>=e.score||t.set(e.product.ref,e)}),Array.from(t.values()).sort((e,n)=>n.score-e.score).slice(0,n).map(e=>e.product)}async function v(e){const{email:n,tenant:t,auth:o,count:a}=e,r=await d(o);if(!r)return null;const c=Math.min(3*a,20),i=`https://api.au-aws.thewishlist.io/services/recommendations/api/v1/recommendations/${encodeURIComponent(t)}/${encodeURIComponent(n)}?n=${c}`;try{const e=await fetch(i,{method:"GET",headers:{Accept:"application/json",Authorization:r,"X-Twc-Tenant":t}});if(!e.ok)return console.error("Recommendations request failed:",e.status),null;return function(e,n){const t=e&&e.recommendations;if(!Array.isArray(t))return[];const o=[];return t.forEach(e=>{var n;const t=e&&e.product;if(!t)return;const a=f(t.name),r=f(t.image_url),c=f(t.product_url);if(!h({name:a,imageUrl:r,productUrl:c}))return;const i=f(t.product_ref)||f(t.product_id)||c;o.push({score:"number"==typeof e.score?e.score:0,product:{id:f(t.product_id)||i,ref:i,name:a,imageUrl:r,productUrl:c,price:null!==(n=y(t.price))&&void 0!==n?n:0,originalPrice:y(t.original_price)}})}),g(o,n)}(await e.json().catch(()=>null),a)}catch(e){return console.error("Error fetching recommendations:",e),null}}async function b(e){const{engine:n,email:t,count:o,productId:a}=e,r=encodeURIComponent(n.siteIdentifier),c=[`tags=${encodeURIComponent(n.profileTag)}`,`limits=${o}`];a&&c.push(`products=${encodeURIComponent(a)}`),t&&c.push(`shopper=${encodeURIComponent(t)}`);const i=`https://${r}.a.searchspring.io/boost/${r}/recommend?`+c.join("&");try{const e=await fetch(i,{method:"GET",headers:{Accept:"application/json"}});if(!e.ok)return console.error("Athos recommendations request failed:",e.status),null;return function(e,n){const t=Array.isArray(e)?e[0]:e,o=t&&t.results;if(!Array.isArray(o))return[];const a=[];return o.forEach((e,n)=>{var t;const r=e&&e.mappings&&e.mappings.core;if(!r)return;const c=f(r.name),i=f(r.imageUrl)||f(r.thumbnailImageUrl),m=f(r.url);if(!h({name:c,imageUrl:i,productUrl:m}))return;const s=f(r.uid)||f(r.sku)||m,l=null!==(t=y(r.price))&&void 0!==t?t:0,d=y(r.msrp);a.push({score:o.length-n,product:{id:f(r.uid)||s,ref:s,name:c,imageUrl:i,productUrl:m,price:l,originalPrice:null!==d&&d>l?d:null}})}),g(a,n)}(await e.json().catch(()=>null),o)}catch(e){return console.error("Error fetching Athos recommendations:",e),null}}const x=new Map;const C="twc-nm-recs",k="twc-nm-recs-title";function S(e,n){if(n)try{return new Intl.NumberFormat(void 0,{style:"currency",currency:n}).format(e)}catch(e){}return new Intl.NumberFormat(void 0,{minimumFractionDigits:0,maximumFractionDigits:2}).format(e)}function E(){const e=document.createElement("section");e.id=C,e.className="twc-nm-recs",e.setAttribute("aria-labelledby",k);const n=document.createElement("h3");n.id=k,n.className="twc-nm-recs-title",n.textContent="Shop similar styles",e.appendChild(n);const t=document.createElement("div");return t.className="twc-nm-recs-row",function(e){let n=0,t=0,o=!1,a=!1,r=!1;e.addEventListener("pointerdown",c=>{"mouse"===c.pointerType&&0===c.button&&(o=!0,a=!1,r=!1,n=c.clientX,t=e.scrollLeft,e.classList.add("twc-nm-recs-row--dragging"))}),e.addEventListener("pointermove",r=>{if(!o)return;const c=r.clientX-n;!a&&Math.abs(c)<5||(a=!0,e.scrollLeft=t-c,r.preventDefault())});const c=()=>{o&&(o=!1,r=a,e.classList.remove("twc-nm-recs-row--dragging"))};e.addEventListener("pointerup",c),e.addEventListener("pointercancel",c),e.addEventListener("pointerleave",c),e.addEventListener("click",e=>{r&&(r=!1,e.preventDefault(),e.stopPropagation())},!0),e.addEventListener("dragstart",e=>{o&&a&&e.preventDefault()})}(t),e.appendChild(t),{section:e,row:t}}function A(){const e=document.createElement("div");e.className="twc-nm-rec twc-nm-rec--skeleton",e.setAttribute("aria-hidden","true");const n=document.createElement("div");n.className="twc-nm-rec-imgbox twc-nm-skel",e.appendChild(n);const t=document.createElement("span");t.className="twc-nm-skel twc-nm-skel-line",e.appendChild(t);const o=document.createElement("span");return o.className="twc-nm-skel twc-nm-skel-line twc-nm-skel-line--short",e.appendChild(o),e}function L(e,n,t){const o=e.querySelector(".twc-nm-recs-row");if(!o)return;const a=(r=t,window.Shopify&&window.Shopify.currency&&window.Shopify.currency.active||r||null);var r;const c=document.createDocumentFragment();n.forEach(e=>{c.appendChild(function(e,n){const t=document.createElement("a");t.className="twc-nm-rec",t.href=e.productUrl,t.target="_blank",t.rel="noopener noreferrer";const o=document.createElement("div");o.className="twc-nm-rec-imgbox";const a=document.createElement("img");a.className="twc-nm-rec-img",a.src=e.imageUrl,a.alt=e.name,a.loading="lazy",a.decoding="async",o.appendChild(a),t.appendChild(o);const r=document.createElement("span");r.className="twc-nm-rec-name",r.textContent=e.name,t.appendChild(r);const c=document.createElement("span");if(c.className="twc-nm-rec-price",null!==e.originalPrice&&e.originalPrice>e.price){const t=document.createElement("s");t.className="twc-nm-rec-was",t.textContent=S(e.originalPrice,n),c.appendChild(t),c.appendChild(document.createTextNode(" "))}return c.appendChild(document.createTextNode(S(e.price,n))),t.appendChild(c),t}(e,a))}),o.textContent="",o.appendChild(c),e.classList.remove("twc-nm-recs--loading"),e.removeAttribute("aria-busy")}function T(){const e=document.getElementById(C);e&&e.parentNode&&e.parentNode.removeChild(e)}const N={"notify-me":{intro:"Register to receive a notification as soon as this item is back in stock",submit:"Notify Me"},"coming-soon":{intro:"Register your interest to hear more about this item",submit:"Register Interest"}},M=["title","intro","sizeLabel","sizePlaceholder","emailPlaceholder","emailOptIn","smsOptIn","submit","success","disclaimer"];function I(e,n){const t={title:"{product}",intro:N[n].intro,sizeLabel:"Size",sizePlaceholder:"Select a size",emailLabel:"Email",emailPlaceholder:"you@example.com",emailOptIn:"Email me store updates",smsOptIn:"Text me store updates",submit:N[n].submit,success:"You're on the list. We'll be in touch.",disclaimer:null};if(void 0===e)return t;if(!e||"object"!=typeof e||Array.isArray(e))return a("copy","an object"),t;const o=e;M.forEach(e=>{const n=o[e];null!=n&&("string"==typeof n?t[e]=n:a(`copy.${e}`,"a string"))});const r=o.emailLabel;return!1===r||"string"==typeof r?t.emailLabel=r:null!=r&&a("copy.emailLabel","a string or false"),t}function B(e,n){return e.split("{product}").join(n)}function P(e){return e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function $(e){if("string"!=typeof e)return null;const n=e.trim();return/^(https?:)?\/\//i.test(n)?n:null}function z(e){return"string"==typeof e?e.trim():""}function G(o){const{wrapper:a,config:r,localization:i}=o,{fields:m,type:l,display:u,auth:p,tenant:f,recommendationsEnabled:y,recommendationsCount:h,recommendationsProfile:g,currency:k,customerEmail:S,copy:N,productSummary:M,classes:I,jsonConfigured:G}=r,U='<button type="button" id="popup-close" class="twc-nm-close" aria-label="Close"><svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true" focusable="false"><path d="M3 3l10 10M13 3L3 13" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" fill="none"/></svg></button>',j='<h2 id="popup-title" class="twc-nm-title"></h2>',R=G?`<div class="twc-nm-header">${U}${j}</div>`:`${U}\n ${j}`,O=document.createElement("div");O.innerHTML=`\n <div id="twc-nm-overlay" role="dialog" aria-modal="true" aria-labelledby="popup-title">\n <div class="twc-nm-card">\n ${R}\n <p id="popup-text" class="twc-nm-text"></p>\n <div id="popup-notice" class="twc-nm-status" role="status" aria-live="polite" hidden></div>\n <form id="popup-form" class="twc-nm-form">\n <div class="twc-nm-field">\n <label class="twc-nm-label" for="twc-nm-size">${P(N.sizeLabel)}</label>\n <select class="twc-nm-select" id="twc-nm-size" name="select-size" required>\n <option value="" disabled selected>${P(N.sizePlaceholder)}</option>\n </select>\n </div>\n </form>\n </div>\n </div>\n `,a.appendChild(O);const H=document.getElementById("twc-nm-overlay"),F=document.getElementById("popup-close"),K=document.getElementById("popup-title"),D=document.getElementById("popup-text"),q=document.querySelector("select[name='select-size']"),_=document.getElementById("popup-form"),V=document.getElementById("popup-notice");if(!(H&&F&&K&&D&&q&&_&&V))return null;"panel"===u&&H.classList.add("twc-nm--panel");const J=M?function(){const e=document.createElement("div");return e.className="twc-nm-summary",e.innerHTML='<div class="twc-nm-summary-image"><img alt="" /></div><div class="twc-nm-summary-info"><p class="twc-nm-summary-name"></p><p class="twc-nm-summary-variant"></p><p class="twc-nm-summary-price"><span class="twc-nm-summary-now"></span> <s class="twc-nm-summary-compare"></s></p></div>',e}():null;J&&_.insertBefore(J,_.firstChild);const Z=s.map(e=>`<option value="${e.code}"${e.code===i.countryCode?" selected":""}>${e.name}</option>`).join(""),W=(e,n,t)=>`<div class="twc-nm-field"><label class="twc-nm-label" for="${e}">${n}</label>${t}</div>`,Y={email:!1===N.emailLabel?`<div class="twc-nm-field"><input class="twc-nm-input" id="twc-nm-email" name="email" type="email" placeholder="${P(N.emailPlaceholder)}" aria-label="${P(N.emailPlaceholder||"Email")}" required /></div>`:W("twc-nm-email",P(N.emailLabel),`<input class="twc-nm-input" id="twc-nm-email" name="email" type="email" placeholder="${P(N.emailPlaceholder)}" required />`),mobile:W("twc-nm-mobile","Mobile",'<input class="twc-nm-input" id="twc-nm-mobile" name="mobile" type="tel" placeholder="Mobile number" required />'),firstName:W("twc-nm-firstname","First name",'<input class="twc-nm-input" id="twc-nm-firstname" name="firstName" type="text" placeholder="First name" required />'),lastName:W("twc-nm-lastname","Last name",'<input class="twc-nm-input" id="twc-nm-lastname" name="lastName" type="text" placeholder="Last name" required />'),countryCode:W("twc-nm-country","Country",`<select class="twc-nm-select" id="twc-nm-country" name="countryCode"><option value="">Select country</option>${Z}</select>`),provinceCode:W("twc-nm-province","State / Province",`<input class="twc-nm-input" id="twc-nm-province" name="provinceCode" type="text" placeholder="State / province code" value="${i.provinceCode||""}" />`)};m.forEach(e=>{Y[e]&&_.insertAdjacentHTML("beforeend",Y[e])});const X=[m.includes("email")?`<label class="twc-nm-check"><input type="checkbox" name="mailList" /> ${P(N.emailOptIn)}</label>`:"",m.includes("mobile")?`<label class="twc-nm-check"><input type="checkbox" name="mailListSms" /> ${P(N.smsOptIn)}</label>`:""].join("");if(X&&_.insertAdjacentHTML("beforeend",`<div class="twc-nm-checks">${X}</div>`),_.insertAdjacentHTML("beforeend",'<div id="popup-status" class="twc-nm-status" role="status" aria-live="polite" hidden></div>'),_.insertAdjacentHTML("beforeend",`<button type="submit" class="twc-nm-submit">${P(N.submit)}</button>`),N.disclaimer&&_.insertAdjacentHTML("beforeend",`<p class="twc-nm-disclaimer">${P(N.disclaimer)}</p>`),function(e,n){Object.keys(n).forEach(t=>{const o=n[t];o&&e.querySelectorAll(c[t]).forEach(e=>{e.classList.add(...o,"twc-nm-themed")})})}(H,I),S){const e=_.querySelector("input[name='email']");e&&(e.value=S)}const Q=document.getElementById("popup-status"),ee=_.querySelector(".twc-nm-submit");if(!Q||!ee)return null;function ne(e,n){Q.textContent=n,Q.className=`twc-nm-status twc-nm-status--${e}`,Q.hidden=!1}function te(){Q.hidden=!0,Q.textContent=""}function oe(e,n){_.hidden=!0,V.textContent=n,V.className=`twc-nm-status twc-nm-status--${e}`,V.hidden=!1}const ae=e=>{const n=_.querySelector(e);return n?n.value:""},re=e=>{const n=_.querySelector(e);return!!n&&n.checked},ce=H.querySelector(".twc-nm-card");let ie=null,me=[],se=null;async function le(e){const n=await function(e,n,t){return w(e,n,t)}(f,p,g);if(!n)return!1;if(!H.classList.contains("is-open"))return!1;const t=function(e){const{section:n,row:t}=E();n.classList.add("twc-nm-recs--loading"),n.setAttribute("aria-busy","true");for(let n=0;n<e;n++)t.appendChild(A());return n}(h);ce.appendChild(t);const o=await async function(e,n){const{email:t,tenant:o,auth:a,count:r,productId:c}=n,i=`${e.engine}|${o}|${t}|${r}|${c||""}`,m=x.get(i);if(m)return m;let s=null;return s="ATHOS"===e.engine?await b({engine:e,email:t,count:r,productId:c}):await v({email:t,tenant:o,auth:a,count:r}),null===s?[]:(x.set(i,s),s)}(n,{email:e,tenant:f,auth:p,count:h,productId:ie?String(ie.id):null});return!!t.isConnected&&(o.length?(L(t,o,k),!0):(T(),!1))}function de(e){if(!y||!ce)return Promise.resolve(!1);if(se)return se;if(document.getElementById(C))return Promise.resolve(!0);const n=le(e);return se=n,n.then(e=>(se===n&&(se=null),e),e=>(se===n&&(se=null),console.error("Error rendering recommendations:",e),!1))}let ue=null,pe=0;function we(e){"Escape"===e.key&&fe()}function fe(){pe++,H.classList.remove("is-open"),document.body.style.overflow="",document.removeEventListener("keydown",we),te(),T(),se=null,ue&&"function"==typeof ue.focus&&ue.focus()}function ye(){if(!J||!ie)return;const e=me.find(e=>String(e.id)===q.value)||null;!function(e,n,t,o){const a=n=>e.querySelector(n),r=a(".twc-nm-summary-image"),c=a(".twc-nm-summary-image img"),i=t&&$(t.image)||$(n.image);i?(c.src=i,c.alt=n.title):c.removeAttribute("src"),r.hidden=!i,a(".twc-nm-summary-name").textContent=n.title;const m=a(".twc-nm-summary-variant");m.textContent=t?t.title:"",m.hidden=!t;let s="",l="";if(t)s=z(t.price),l=z(t.compareAtPrice);else{const e=o.map(e=>z(e.price));e.length&&e[0]&&e.every(n=>n===e[0])&&(s=e[0])}l===s&&(l=""),a(".twc-nm-summary-now").textContent=s;const d=a(".twc-nm-summary-compare");d.textContent=l,d.hidden=!l,a(".twc-nm-summary-price").hidden=!s}(J,ie,e,me)}return F.addEventListener("click",fe),q.addEventListener("change",ye),H.addEventListener("click",e=>{e.target===H&&fe()}),_.addEventListener("submit",async e=>{e.preventDefault();const n=me.find(e=>String(e.id)===q.value);if(!n)return void ne("error","Please select a size.");const t={variantRef:n.id,email:ae("input[name='email']"),subscribe:re("input[name='mailList']"),subscribeSms:re("input[name='mailListSms']")};if("coming-soon"===l?t.comingSoon=!0:t.notifyMe=!0,m.includes("firstName")&&(t.firstName=ae("input[name='firstName']")),m.includes("lastName")&&(t.lastName=ae("input[name='lastName']")),m.includes("mobile")&&(t.mobile=ae("input[name='mobile']")),m.includes("countryCode")){const e=ae("select[name='countryCode']");e&&(t.countryCode=e)}else i.countryCode&&(t.countryCode=i.countryCode);if(m.includes("provinceCode")){const e=ae("input[name='provinceCode']");e&&(t.provinceCode=e)}else i.provinceCode&&(t.provinceCode=i.provinceCode);i.marketId&&(t.marketId=i.marketId),i.marketHandle&&(t.marketHandle=i.marketHandle),te();const o=pe,a=ee.textContent||"";ee.disabled=!0,ee.textContent="Sending…";let r={ok:!1,reason:"error"};try{r=await async function(e,n,t){const o=await d(n);if(!o)return{ok:!1,reason:"auth"};try{return(await fetch("https://api.au-aws.thewishlist.io/services/wsservice/api/wishlist/items/customerInterest",{method:"POST",headers:{"Content-Type":"application/json",Authorization:o,"X-Twc-Tenant":t},body:JSON.stringify(e)})).ok?{ok:!0}:{ok:!1,reason:"error"}}catch(e){return console.error("Error submitting form:",e),{ok:!1,reason:"error"}}}(t,p,f)}finally{o===pe&&(ee.disabled=!1,ee.textContent=a)}if(o===pe)if(r.ok){ne("success",N.success);const e=S||ae("input[name='email']");!!e&&await de(e)||o!==pe||setTimeout(()=>{o===pe&&fe()},1500)}else"auth"===r.reason?ne("error","proxy"===p.mode?"Please log in to your account to continue.":"Something went wrong. Please try again."):ne("error","Something went wrong. Please try again.")}),{open:function(o){pe++,ee.disabled=!1,ee.textContent=N.submit,te(),_.hidden=!1,V.hidden=!0,V.textContent="",T(),se=null,H.classList.contains("is-open")||(ue=document.activeElement),H.classList.add("is-open"),document.body.style.overflow="hidden",document.addEventListener("keydown",we);const a=function(t){const o=t?t.closest(`[${e}]`):null;if(o){try{const t=JSON.parse(o.getAttribute(e)||"");if(n(t))return{ok:!0,product:t,source:"card"}}catch(e){}return console.error(`notify-me-wl: invalid ${e} JSON on`,o),{ok:!1}}const a=window.currentProduct;return n(a)?{ok:!0,product:a,source:"page"}:{ok:!1}}(o);if(!a.ok)return ie=null,me=[],K.textContent="",D.textContent=B(N.intro,""),void oe("error","Product information is unavailable. Please refresh the page.");if(ie=a.product,me=(ie.variants||[]).filter(e=>!!e&&("number"==typeof e.id||"string"==typeof e.id)&&"string"==typeof e.title&&!0!==e.available),K.textContent=B(N.title,ie.title),D.textContent=B(N.intro,ie.title),!me.length)return void oe("info","All sizes are currently in stock.");const r=o?o.getAttribute("data-variant-id"):null,c="page"===a.source?[r,...t()]:[r];var i,m;i=me,m=function(e,n){const t=e.map(e=>String(e.id));for(const e of n){const n=e?e.trim():"";if(n&&t.includes(n))return n}return 1===t.length?t[0]:null}(me,c),q.innerHTML=`<option value="" disabled selected>${P(N.sizePlaceholder)}</option>`,i.forEach(e=>{const n=document.createElement("option");n.value=String(e.id),n.textContent=e.title,q.appendChild(n)}),m&&(q.value=m),ye(),S&&de(S);const s=_.querySelector("select, input");s&&s.focus()}}}function U(e){var n;const t=(null!=e?e:"").trim();if(!t)return null;const o=null!==(n=t.split("?")[0].split("/").pop())&&void 0!==n?n:"";return/^\d+$/.test(o)?o:null}function j(e){const n=(null!=e?e:"").trim();return/^[a-z0-9_-]+$/i.test(n)?n:null}function R(e){const n=(null!=e?e:"").trim();return/^[a-z]{2}$/i.test(n)?n.toUpperCase():null}function O(e){var n,t,o;const a={countryCode:R(e("data-country-code")),provinceCode:null,marketId:U(e("data-market-id")),marketHandle:j(e("data-market-handle"))},r=window.Shopify;if(r){const e=null===(n=r.markets)||void 0===n?void 0:n.currentMarket;if(e&&(!a.countryCode&&e.countryCode&&(a.countryCode=e.countryCode),a.marketId||(a.marketId=U(String(null!==(t=e.id)&&void 0!==t?t:"")))),a.countryCode||"string"!=typeof r.country||(a.countryCode=r.country),!a.countryCode&&"string"==typeof r.locale){const e=r.locale.split("-");e.length>1&&(a.countryCode=e[e.length-1].toUpperCase())}}if(!a.countryCode||!a.provinceCode){const e=null===(o=window.customer)||void 0===o?void 0:o.default_address;e&&(!a.countryCode&&e.country_code&&(a.countryCode=e.country_code),!a.provinceCode&&e.province_code&&(a.provinceCode=e.province_code))}if(!a.countryCode){const e=(navigator.language||"").split("-");e.length>1&&(a.countryCode=e[e.length-1].toUpperCase())}return a}function H(e){const n=e("data-customer-email");if(n&&n.trim())return n.trim();const t=window.customer&&window.customer.email;if(t&&t.trim())return t.trim();const o=window.customerEmail;return"string"==typeof o&&o.trim()?o.trim():null}const F="[data-twc-notify-me]";function K(){const e=document.getElementById("notification-widget");if(!e)return;const n=document.getElementById("popup-open"),t=function(e){const n=e.querySelector('script[type="application/json"][data-twc-config]');if(!n)return null;try{const e=JSON.parse(n.textContent||"");if(e&&"object"==typeof e&&!Array.isArray(e))return e}catch(e){}return console.error("notify-me-wl: invalid data-twc-config JSON"),null}(e),c=function(e,n,t){const c=t=>(e?e.getAttribute(t):null)||n.getAttribute(t)||null,i=e=>null===t[e]?void 0:t[e],m=e=>{const n=i(e);return void 0===n?null:"boolean"==typeof n?n:(a(e,"true or false"),null)};return{string(e){const n=c(e);if(null!==n)return n;const t=o(e),r=i(t);return void 0===r?null:"string"==typeof r?r:"number"==typeof r&&Number.isFinite(r)?String(r):(a(t,"a string"),null)},boolean(e){const n=c(e);return null!==n?"false"!==n:m(o(e))},number(e){const n=c(e);if(null!==n){const e=parseInt(n,10);if(Number.isFinite(e))return e}const t=o(e),r=i(t);return void 0===r?null:"number"==typeof r&&Number.isFinite(r)?r:(a(t,"a number"),null)},stringArray(e){const n=c(e);if(null!==n){try{const e=JSON.parse(n);if(r(e))return e}catch(e){}console.warn(`notify-me-wl: ignoring invalid ${e} attribute`)}const t=o(e),m=i(t);return void 0===m?null:r(m)?m:(a(t,"an array of strings"),null)},json:i,jsonBoolean:m}}(n,e,t||{}),i=c.string;!function(){const e=document.createElement("style");e.type="text/css",e.innerText="\n .notification-btn {\n display: inline-block;\n width: 100%;\n padding: 12px 24px;\n background: #fff;\n border: 1px solid #111827;\n border-radius: 8px;\n color: #111827;\n font: inherit;\n text-align: center;\n text-transform: uppercase;\n cursor: pointer;\n }\n .notification-btn:hover {\n background: #111827;\n color: #fff;\n }\n #twc-nm-overlay {\n position: fixed;\n inset: 0;\n z-index: 99999;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 16px;\n background: var(--twc-nm-overlay-bg, rgba(17, 17, 17, 0.55));\n opacity: 0;\n visibility: hidden;\n transition: opacity var(--twc-nm-transition, 200ms ease);\n }\n #twc-nm-overlay.is-open {\n opacity: 1;\n visibility: visible;\n }\n #twc-nm-overlay * {\n box-sizing: border-box;\n }\n #twc-nm-overlay .twc-nm-card {\n position: relative;\n width: 100%;\n max-width: 400px;\n max-height: calc(100vh - 32px);\n overflow-y: auto;\n padding: var(--twc-nm-padding, 24px);\n background: var(--twc-nm-bg, #fff);\n color: var(--twc-nm-text, #111827);\n border-radius: 12px;\n box-shadow: 0 10px 40px rgba(0, 0, 0, 0.18);\n font: inherit;\n font-family: var(--twc-nm-font, inherit);\n transform: translateY(8px);\n transition: transform var(--twc-nm-transition, 200ms ease);\n }\n #twc-nm-overlay.is-open .twc-nm-card {\n transform: translateY(0);\n }\n /* Panel mode: same blocking backdrop, docked to the right edge and\n full height instead of centred. Declared after the modal rules so\n the shared properties are overridden. */\n #twc-nm-overlay.twc-nm--panel {\n align-items: stretch;\n justify-content: flex-end;\n padding: 0;\n }\n #twc-nm-overlay.twc-nm--panel .twc-nm-card {\n width: var(--twc-nm-panel-width, 420px);\n max-width: 100%;\n max-height: none;\n border-radius: 0;\n box-shadow: var(--twc-nm-panel-shadow, -10px 0 40px rgba(0, 0, 0, 0.18));\n transform: translateX(100%);\n }\n /* Needs the extra class to beat both the .is-open rule above and the\n panel's own resting transform. */\n #twc-nm-overlay.twc-nm--panel.is-open .twc-nm-card {\n transform: translateX(0);\n }\n @media (max-width: 480px) {\n #twc-nm-overlay.twc-nm--panel .twc-nm-card {\n width: var(--twc-nm-panel-width-mobile, 100%);\n }\n }\n /* Groups the title and close button so a theme can lay them out as a\n row. With no border it changes nothing: the close button stays\n positioned against the card. */\n #twc-nm-overlay .twc-nm-header {\n border-bottom: var(--twc-nm-header-border, none);\n }\n /* Layout below is never themed — it keeps the close button usable\n whatever classes a theme attaches. Cosmetics follow :not(themed). */\n #twc-nm-overlay .twc-nm-close {\n position: absolute;\n top: 12px;\n right: 12px;\n width: 32px;\n height: 32px;\n min-width: 0;\n min-height: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0;\n padding: 0;\n border: none;\n background: transparent;\n box-shadow: none;\n text-indent: 0;\n line-height: 1;\n opacity: 1;\n visibility: visible;\n cursor: pointer;\n }\n #twc-nm-overlay .twc-nm-close:where(:not(.twc-nm-themed)) {\n border-radius: 6px;\n color: var(--twc-nm-muted, #6b7280);\n }\n /* The icon is an inline SVG rather than a text glyph so host-theme\n button resets (font-size: 0, icon fonts, text-indent) can't hide it. */\n #twc-nm-overlay .twc-nm-close svg {\n display: block;\n width: 16px;\n height: 16px;\n flex: none;\n pointer-events: none;\n }\n #twc-nm-overlay .twc-nm-close::before,\n #twc-nm-overlay .twc-nm-close::after {\n content: none;\n }\n #twc-nm-overlay .twc-nm-close:where(:not(.twc-nm-themed)):hover {\n background: #f3f4f6;\n color: var(--twc-nm-text, #111827);\n }\n #twc-nm-overlay .twc-nm-title {\n margin: 0 32px 4px 0;\n }\n #twc-nm-overlay .twc-nm-title:where(:not(.twc-nm-themed)) {\n font-size: 18px;\n font-weight: 600;\n }\n #twc-nm-overlay .twc-nm-text {\n margin: 0 0 16px;\n }\n #twc-nm-overlay .twc-nm-text:where(:not(.twc-nm-themed)) {\n font-size: 14px;\n line-height: 1.5;\n color: var(--twc-nm-muted, #6b7280);\n }\n #twc-nm-overlay .twc-nm-form {\n display: flex;\n flex-direction: column;\n gap: 14px;\n }\n #twc-nm-overlay .twc-nm-field {\n display: flex;\n flex-direction: column;\n gap: 6px;\n }\n #twc-nm-overlay .twc-nm-label:where(:not(.twc-nm-themed)) {\n font-size: 13px;\n font-weight: 500;\n color: #374151;\n }\n #twc-nm-overlay .twc-nm-input,\n #twc-nm-overlay .twc-nm-select {\n width: 100%;\n }\n /* The shorthand resets every background sub-property, so host CSS\n can't leak into an unconfigured widget; the select's arrow is added\n by the rule after it. */\n #twc-nm-overlay .twc-nm-input:where(:not(.twc-nm-themed)),\n #twc-nm-overlay .twc-nm-select:where(:not(.twc-nm-themed)) {\n padding: 10px 12px;\n font-size: 14px;\n font-family: inherit;\n color: var(--twc-nm-text, #111827);\n background: #fff;\n border: 1px solid var(--twc-nm-border, #e5e7eb);\n border-radius: var(--twc-nm-radius, 8px);\n }\n #twc-nm-overlay .twc-nm-input:where(:not(.twc-nm-themed)):focus,\n #twc-nm-overlay .twc-nm-select:where(:not(.twc-nm-themed)):focus {\n outline: none;\n border-color: var(--twc-nm-text, #111827);\n box-shadow: 0 0 0 3px rgba(17, 24, 39, 0.08);\n }\n #twc-nm-overlay .twc-nm-select {\n appearance: none;\n -webkit-appearance: none;\n cursor: pointer;\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2 4l4 4 4-4' stroke='%236b7280' stroke-width='1.5' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: right 12px center;\n }\n /* After the shared padding above, which it must override. */\n #twc-nm-overlay .twc-nm-select:where(:not(.twc-nm-themed)) {\n padding-right: 36px;\n }\n #twc-nm-overlay .twc-nm-checks {\n display: flex;\n flex-direction: column;\n gap: 10px;\n }\n #twc-nm-overlay .twc-nm-check {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n color: #374151;\n cursor: pointer;\n }\n #twc-nm-overlay .twc-nm-check input {\n width: 16px;\n height: 16px;\n margin: 0;\n }\n #twc-nm-overlay .twc-nm-check input:where(:not(.twc-nm-themed)) {\n accent-color: var(--twc-nm-text, #111827);\n }\n #twc-nm-overlay .twc-nm-status {\n font-size: 13px;\n line-height: 1.5;\n padding: 10px 12px;\n border-radius: var(--twc-nm-radius, 8px);\n }\n #twc-nm-overlay .twc-nm-status--error {\n color: #b91c1c;\n background: #fef2f2;\n }\n #twc-nm-overlay .twc-nm-status--success {\n color: #15803d;\n background: #f0fdf4;\n }\n #twc-nm-overlay .twc-nm-status--info {\n color: #374151;\n background: #f3f4f6;\n }\n #twc-nm-overlay .twc-nm-submit {\n width: 100%;\n cursor: pointer;\n }\n #twc-nm-overlay .twc-nm-submit:where(:not(.twc-nm-themed)) {\n padding: 12px 16px;\n font-size: 14px;\n font-weight: 600;\n font-family: inherit;\n color: #fff;\n background: #111827;\n border: none;\n border-radius: var(--twc-nm-radius, 8px);\n transition: background 150ms ease;\n }\n #twc-nm-overlay .twc-nm-submit:where(:not(.twc-nm-themed)):hover {\n background: #374151;\n }\n #twc-nm-overlay .twc-nm-submit:disabled {\n opacity: 0.6;\n cursor: default;\n }\n #twc-nm-overlay .twc-nm-disclaimer {\n margin: 0;\n }\n #twc-nm-overlay .twc-nm-disclaimer:where(:not(.twc-nm-themed)) {\n font-size: 12px;\n line-height: 1.5;\n color: var(--twc-nm-muted, #6b7280);\n }\n #twc-nm-overlay .twc-nm-summary {\n display: flex;\n align-items: flex-start;\n }\n #twc-nm-overlay .twc-nm-summary:where(:not(.twc-nm-themed)) {\n gap: 16px;\n }\n /* Theme classes often set display; hidden parts must stay hidden. */\n #twc-nm-overlay .twc-nm-summary [hidden] {\n display: none !important;\n }\n #twc-nm-overlay .twc-nm-summary-image {\n flex: none;\n }\n #twc-nm-overlay .twc-nm-summary-image:where(:not(.twc-nm-themed)) {\n width: 96px;\n }\n #twc-nm-overlay .twc-nm-summary-image img {\n display: block;\n width: 100%;\n height: auto;\n }\n #twc-nm-overlay .twc-nm-summary-info {\n flex: 1 1 auto;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n #twc-nm-overlay .twc-nm-summary-info p {\n margin: 0;\n }\n #twc-nm-overlay .twc-nm-summary-name {\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n }\n #twc-nm-overlay .twc-nm-summary-name:where(:not(.twc-nm-themed)) {\n font-size: 14px;\n font-weight: 600;\n }\n #twc-nm-overlay .twc-nm-summary-variant:where(:not(.twc-nm-themed)) {\n font-size: 13px;\n color: var(--twc-nm-muted, #6b7280);\n }\n #twc-nm-overlay .twc-nm-summary-price:where(:not(.twc-nm-themed)) {\n font-size: 14px;\n }\n #twc-nm-overlay .twc-nm-summary-compare:where(:not(.twc-nm-themed)) {\n margin-left: 6px;\n color: #9ca3af;\n }\n #twc-nm-overlay .twc-nm-recs {\n margin-top: 20px;\n padding-top: 20px;\n border-top: 1px solid var(--twc-nm-border, #e5e7eb);\n }\n #twc-nm-overlay .twc-nm-recs-title {\n margin: 0 0 12px;\n font-size: 14px;\n font-weight: 600;\n color: #111827;\n }\n #twc-nm-overlay .twc-nm-recs-row {\n display: flex;\n gap: 12px;\n overflow-x: auto;\n scroll-snap-type: x mandatory;\n -webkit-overflow-scrolling: touch;\n padding-bottom: 8px;\n scrollbar-width: thin;\n }\n /* Only where a real pointer exists — the affordance is meaningless\n on touch, which pans the row natively. */\n @media (hover: hover) {\n #twc-nm-overlay .twc-nm-recs-row {\n cursor: grab;\n }\n }\n #twc-nm-overlay .twc-nm-recs-row--dragging,\n #twc-nm-overlay .twc-nm-recs-row--dragging .twc-nm-rec {\n cursor: grabbing;\n }\n #twc-nm-overlay .twc-nm-recs-row--dragging {\n /* Snapping fights a manually driven scrollLeft and feels sticky. */\n scroll-snap-type: none;\n user-select: none;\n -webkit-user-select: none;\n }\n #twc-nm-overlay .twc-nm-rec {\n flex: 0 0 132px;\n scroll-snap-align: start;\n display: flex;\n flex-direction: column;\n gap: 6px;\n color: inherit;\n text-decoration: none;\n }\n #twc-nm-overlay .twc-nm-rec:focus-visible {\n outline: 2px solid #111827;\n outline-offset: 2px;\n border-radius: 8px;\n }\n #twc-nm-overlay .twc-nm-rec-imgbox {\n aspect-ratio: 3 / 4;\n overflow: hidden;\n border-radius: 8px;\n background: #f3f4f6;\n }\n #twc-nm-overlay .twc-nm-rec-img {\n display: block;\n width: 100%;\n height: 100%;\n object-fit: cover;\n transition: opacity 150ms ease;\n }\n #twc-nm-overlay .twc-nm-rec:hover .twc-nm-rec-img {\n opacity: 0.85;\n }\n #twc-nm-overlay .twc-nm-rec-name {\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n font-size: 13px;\n line-height: 1.35;\n color: #111827;\n }\n #twc-nm-overlay .twc-nm-rec-price {\n font-size: 13px;\n font-weight: 600;\n color: #111827;\n }\n #twc-nm-overlay .twc-nm-rec-was {\n margin-right: 4px;\n font-weight: 400;\n color: #9ca3af;\n }\n #twc-nm-overlay .twc-nm-rec--skeleton {\n pointer-events: none;\n }\n #twc-nm-overlay .twc-nm-skel {\n background-color: #f3f4f6;\n background-image: linear-gradient(\n 90deg,\n #f3f4f6 0%,\n #e8eaee 50%,\n #f3f4f6 100%\n );\n background-size: 200% 100%;\n border-radius: 4px;\n animation: twc-nm-shimmer 1.3s ease-in-out infinite;\n }\n #twc-nm-overlay .twc-nm-rec-imgbox.twc-nm-skel {\n border-radius: 8px;\n }\n #twc-nm-overlay .twc-nm-skel-line {\n height: 12px;\n width: 100%;\n }\n #twc-nm-overlay .twc-nm-skel-line--short {\n width: 45%;\n }\n @keyframes twc-nm-shimmer {\n from {\n background-position: 200% 0;\n }\n to {\n background-position: -200% 0;\n }\n }\n @media (prefers-reduced-motion: reduce) {\n #twc-nm-overlay,\n #twc-nm-overlay .twc-nm-card,\n #twc-nm-overlay .twc-nm-rec-img {\n transition: none;\n }\n #twc-nm-overlay .twc-nm-card,\n #twc-nm-overlay.twc-nm--panel .twc-nm-card {\n transform: none;\n }\n #twc-nm-overlay .twc-nm-skel {\n animation: none;\n }\n }\n ",document.head.appendChild(e)}();const s=c.stringArray("data-fields")||["email"],l=i("data-type")||"notify-me",d="panel"===i("data-display")?"panel":"modal",u=i("data-auth")||"proxy",p=i("data-tenant")||"victoria-woods",w=i("data-proxy-app")||"twc-sdk",f=i("data-access-token")||window.TWC_ACCESS_TOKEN||null,y=!1!==c.boolean("data-recommendations"),h=c.number("data-recommendations-count"),g=G({wrapper:e,config:{fields:s,type:l,display:d,auth:{mode:u,proxyApp:w,accessToken:f},tenant:p,recommendationsEnabled:y,recommendationsCount:null!==h&&h>0?h:8,recommendationsProfile:(i("data-recommendations-profile")||"").trim()||null,currency:i("data-currency")||null,customerEmail:H(i),copy:I(c.json("copy"),l),productSummary:!0===c.jsonBoolean("productSummary"),classes:m(c.json("classes")),jsonConfigured:null!==t},localization:O(i)});g&&(document.addEventListener("click",e=>{const n=e.target,t=n instanceof Element?n.closest(F):null;t&&(e.preventDefault(),g.open(t))},!0),n&&!n.matches(F)&&n.addEventListener("click",()=>g.open(n)))}"loading"===document.readyState?document.addEventListener("DOMContentLoaded",K):K()}();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "notify-me-wl",
3
- "version": "3.4.0",
3
+ "version": "3.6.0",
4
4
  "description": "Coming Soon / Notify Me wishlist widget for retailer product pages.",
5
5
  "main": "build/notify-me-wl.min.js",
6
6
  "files": [
@@ -11,6 +11,8 @@
11
11
  "build": "rollup -c",
12
12
  "dev": "rollup -c -w",
13
13
  "typecheck": "tsc --noEmit",
14
+ "selftest": "npm run build && sh harness/run-selftest.sh",
15
+ "visual": "npm run build && sh harness/run-visual.sh",
14
16
  "test": "echo \"Error: no test specified\" && exit 1"
15
17
  },
16
18
  "author": "",