PageSourceSearch

https://ardene.com/cdn/shop/t/239/assets/snippet.braze-inbox.js?v=70479139155257703581790728904

js ardene.com collected 2026-10-02 04:08:30 UTC 89,840 bytes, 2 lines download raw bytes

1(function(){"use strict";var __webpack_modules__={
1"./src/snippets/braze/inbox-history.js":function(__unused_webpack___webpack_module__,__webpack_exports__,__webpack_require__){eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   HISTORY_ITEMS_PER_PAGE: () => (/* binding */ HISTORY_ITEMS_PER_PAGE),\n/* harmony export */   HISTORY_SELECTOR: () => (/* binding */ HISTORY_SELECTOR),\n/* harmony export */   initHistoryFilterListeners: () => (/* binding */ initHistoryFilterListeners),\n/* harmony export */   paginate: () => (/* binding */ paginate),\n/* harmony export */   renderHistory: () => (/* binding */ renderHistory)\n/* harmony export */ });\n/* harmony import */ var _loyalty_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./loyalty.js */ \"./src/snippets/braze/loyalty.js\");\n\n\nconst HISTORY_SELECTOR = '[data-history]';\n\n/**\n * Rows per page, shared by the rewards, purchase and activity grids.\n *\n * Kept low so the pager actually appears: it only renders above one page, and\n * a typical account has well under a dozen rows, so a larger page size hid it\n * from most shoppers. The purchase grid sets the ceiling \u2014 each of its rows\n * carries an expandable line-item panel, so a page there is twice the height\n * the count suggests.\n */\nconst HISTORY_ITEMS_PER_PAGE = 5;\n\n/** @type {{ rewards: Array, purchases: Array, activities: Array, locale: string }} */\nlet historyDataCache = { rewards: [], purchases: [], activities: [], locale: 'en' };\n\n/**\n * @param {HTMLElement|null|undefined} el\n * @param {boolean} isHidden\n */\nfunction setHidden(el, isHidden) {\n  if (!el) return;\n  el.hidden = isHidden;\n  el.classList.toggle('hidden', isHidden);\n}\n\n/**\n * @param {HTMLElement} container\n * @returns {{ orderNumber: string, totalPoints: string }}\n */\nfunction historyLabels(container) {\n  return {\n    orderNumber: container?.dataset?.i18nOrderNumber || 'Order Number',\n    totalPoints: container?.dataset?.i18nTotalPoints || 'Total Points',\n    statusFulfilled: container?.dataset?.i18nStatusFulfilled || '',\n    statusPending: container?.dataset?.i18nStatusPending || '',\n  };\n}\n\n/**\n * @param {string} label\n * @param {boolean} disabled\n * @param {() => void} onClick\n * @returns {HTMLButtonElement}\n */\nfunction createPageNavButton(label, disabled, onClick) {\n  const button = document.createElement('button');\n  button.type = 'button';\n  button.className = 'braze-inbox__history-page-button';\n  button.textContent = label;\n  button.disabled = disabled;\n  if (disabled) button.classList.add('braze-inbox__history-page-button--disabled');\n  button.addEventListener('click', onClick);\n  return button;\n}\n\n/**\n * @param {number} currentPage\n * @param {number} totalPages\n * @returns {[number, number]}\n */\nfunction visiblePageRange(currentPage, totalPages) {\n  const maxVisiblePages = 5;\n  let startPage = Math.max(1, currentPage - Math.floor(maxVisiblePages / 2));\n  let endPage = Math.min(totalPages, startPage + maxVisiblePages - 1);\n  if (endPage - startPage < maxVisiblePages - 1) {\n    startPage = Math.max(1, endPage - maxVisiblePages + 1);\n  }\n  return [startPage, endPage];\n}\n\n/**\n * @param {number} totalItems\n * @param {number} currentPage\n * @param {number} itemsPerPage\n * @param {Function} onPageChange\n * @returns {DocumentFragment}\n */\nfunction createPagination(totalItems, currentPage, itemsPerPage, onPageChange) {\n  const fragment = document.createDocumentFragment();\n  const totalPages = Math.ceil(totalItems / itemsPerPage);\n\n  const prev = createPageNavButton('<', currentPage === 1, () => {\n    if (currentPage > 1) onPageChange(currentPage - 1);\n  });\n  prev.setAttribute('aria-label', 'Previous page');\n  fragment.appendChild(prev);\n\n  const [startPage, endPage] = visiblePageRange(currentPage, totalPages);\n  for (let i = startPage; i <= endPage; i++) {\n    const pageButton = createPageNavButton(String(i), false, () => onPageChange(i));\n    pageButton.setAttribute('aria-label', `Page ${i}`);\n    pageButton.setAttribute('aria-current', i === currentPage ? 'page' : 'false');\n    if (i === currentPage) pageButton.classList.add('braze-inbox__history-page-button--active');\n    fragment.appendChild(pageButton);\n  }\n\n  const next = createPageNavButton('>', currentPage === totalPages, () => {\n    if (currentPage < totalPages) onPageChange(currentPage + 1);\n  });\n  next.setAttribute('aria-label', 'Next page');\n  fragment.appendChild(next);\n\n  return fragment;\n}\n\n/**\n * @param {HTMLElement} section\n * @param {boolean} isEmpty\n */\nfunction setSectionEmpty(section, isEmpty) {\n  const loadingEl = section.querySelector('[data-history-state=\"loading\"]');\n  const loadedEl = section.querySelector('[data-history-state=\"loaded\"]');\n  const emptyEl = section.querySelector('[data-history-state=\"empty\"]');\n  section.setAttribute('data-state', isEmpty ? 'empty' : 'loaded');\n  setHidden(loadingEl, true);\n  setHidden(loadedEl, isEmpty);\n  setHidden(emptyEl, !isEmpty);\n}\n\n/**\n * @param {HTMLElement|null} paginationEl\n * @param {number} totalItems\n * @param {number} safePage\n * @param {(page: number) => void} onPageChange\n */\nfunction renderPagination(paginationEl, totalItems, safePage, onPageChange) {\n  if (!paginationEl) return;\n  paginationEl.replaceChildren();\n  const totalPages = Math.max(1, Math.ceil(totalItems / HISTORY_ITEMS_PER_PAGE));\n  if (totalPages > 1) {\n    paginationEl.appendChild(\n      createPagination(totalItems, safePage, HISTORY_ITEMS_PER_PAGE, onPageChange),\n    );\n  }\n}\n\n/**\n * Resolves a requested page against a row set.\n *\n * Shared by all three history grids, so a page number out of range clamps\n * rather than rendering an empty grid.\n *\n * @param {Array} items\n * @param {number} page\n * @returns {{ safePage: number, pageItems: Array }}\n */\nfunction paginate(items, page) {\n  const totalPages = Math.max(1, Math.ceil(items.length / HISTORY_ITEMS_PER_PAGE));\n  const safePage = Math.min(Math.max(page, 1), totalPages);\n  const pageItems = items.slice(\n    (safePage - 1) * HISTORY_ITEMS_PER_PAGE,\n    safePage * HISTORY_ITEMS_PER_PAGE,\n  );\n  return { safePage, pageItems };\n}\n\n/**\n * @param {object} reward\n * @returns {object}\n */\nfunction normalizeReward(reward) {\n  return {\n    ...reward,\n    date: (0,_loyalty_js__WEBPACK_IMPORTED_MODULE_0__.pickHistoryDate)(reward, [\n      'date',\n      'dateAwarded',\n      'awardedDate',\n      'createdAt',\n      'timestamp',\n      'redemptionDate',\n      'issuedDate',\n    ]),\n    title: reward.title || reward.name || reward.rewardName || reward.rewardTitle || '',\n    status: reward.status || reward.rewardStatus || reward.state || '',\n    points: reward.points ?? reward.pointsSpent ?? reward.pointAmount ?? reward.numPoints,\n  };\n}\n\n/**\n * @param {Object} purchase\n * @returns {Array}\n */\nfunction getPurchaseLineItems(purchase) {\n  if (Array.isArray(purchase.items) && purchase.items.length) return purchase.items;\n  if (Array.isArray(purchase.skus) && purchase.skus.length) return purchase.skus;\n  if (Array.isArray(purchase.lineItems) && purchase.lineItems.length) return purchase.lineItems;\n  if (Array.isArray(purchase.line_items) && purchase.line_items.length) return purchase.line_items;\n  return [];\n}\n\n/**\n * @param {object} purchase\n * @returns {object}\n */\nfunction normalizePurchase(purchase) {\n  return {\n    ...purchase,\n    date: (0,_loyalty_js__WEBPACK_IMPORTED_MODULE_0__.pickHistoryDate)(purchase, [\n      'date',\n      'purchaseDate',\n      'orderDate',\n      'createdAt',\n      'timestamp',\n      'transactionDate',\n    ]),\n    orderNumber: (0,_loyalty_js__WEBPACK_IMPORTED_MODULE_0__.getPurchaseOrderNumber)(purchase),\n    total: purchase.total ?? purchase.amount ?? purchase.orderTotal ?? purchase.totalAmount,\n    pointsAwarded:\n      purchase.pointsAwarded ?? purchase.totalPoints ?? purchase.points ?? purchase.numPoints,\n    pointsNote: purchase.pointsNote || purchase.pointsStatus || purchase.refundNote || '',\n    items: getPurchaseLineItems(purchase),\n  };\n}\n\n/**\n * @param {object} activity\n * @returns {object}\n */\nfunction normalizeActivity(activity) {\n  return {\n    ...activity,\n    date: (0,_loyalty_js__WEBPACK_IMPORTED_MODULE_0__.pickHistoryDate)(activity, [\n      'date',\n      'completedDate',\n      'activityDate',\n      'createdAt',\n      'timestamp',\n    ]),\n    title: activity.activityName || activity.title || activity.name || activity.description || '',\n    points: activity.points ?? activity.numPoints ?? activity.pointsAwarded ?? activity.pointValue,\n  };\n}\n\n/**\n * @param {object} reward\n * @param {{ statusFulfilled: string, statusPending: string }} labels\n * @returns {HTMLElement}
1\n */\nfunction createRewardRow(reward, labels) {\n  const row = document.createElement('div');\n  row.className = 'braze-inbox__history-row braze-inbox__history-row--rewards';\n\n  const dateCell = document.createElement('div');\n  dateCell.className = 'braze-inbox__history-cell';\n  dateCell.textContent = (0,_loyalty_js__WEBPACK_IMPORTED_MODULE_0__.formatHistoryDateUtc)(reward.date);\n\n  const rewardCell = document.createElement('div');\n  rewardCell.className = 'braze-inbox__history-cell';\n  rewardCell.textContent = reward.title || '';\n\n  const statusCell = document.createElement('div');\n  statusCell.className = 'braze-inbox__history-cell';\n  statusCell.textContent = (0,_loyalty_js__WEBPACK_IMPORTED_MODULE_0__.formatRewardStatus)(reward.status, labels);\n\n  const pointsCell = document.createElement('div');\n  pointsCell.className = 'braze-inbox__history-cell braze-inbox__history-cell--align-end';\n  if (reward.points == null || reward.points === '') {\n    pointsCell.textContent = '';\n  } else {\n    const points = Number(reward.points);\n    pointsCell.textContent = Number.isFinite(points) ? String(points) : String(reward.points);\n  }\n\n  row.append(dateCell, rewardCell, statusCell, pointsCell);\n  return row;\n}\n\n/**\n * @param {string} className\n * @param {string} text\n * @returns {HTMLElement}\n */\nfunction historyCell(className, text) {\n  const cell = document.createElement('div');\n  cell.className = className;\n  cell.textContent = text;\n  return cell;\n}\n\n/**\n * @param {object} purchase\n * @param {{ orderNumber: string, totalPoints: string }} labels\n * @param {string} locale\n * @returns {{ row: HTMLElement, expandWrap: HTMLElement|null }}\n */\nfunction createPurchaseRow(purchase, labels, locale) {\n  const lineItems = purchase.items || [];\n  const expandable = lineItems.length > 0;\n  const orderLabel = purchase.orderNumber\n    ? `${labels.orderNumber} ${purchase.orderNumber}`\n    : labels.orderNumber;\n\n  const row = document.createElement(expandable ? 'button' : 'div');\n  if (expandable) {\n    row.type = 'button';\n    row.setAttribute('data-trigger', 'purchase-expand');\n    row.setAttribute('aria-expanded', 'false');\n  }\n  row.className = 'braze-inbox__history-row braze-inbox__history-row--purchases';\n\n  const total = (0,_loyalty_js__WEBPACK_IMPORTED_MODULE_0__.formatLoyaltyCurrency)(purchase.total, locale);\n  const detailsCell = historyCell(\n    'braze-inbox__history-cell',\n    expandable ? `\u25BE ${orderLabel}` : orderLabel,\n  );\n  row.append(\n    historyCell('braze-inbox__history-cell', (0,_loyalty_js__WEBPACK_IMPORTED_MODULE_0__.formatHistoryDateUtc)(purchase.date)),\n    detailsCell,\n    historyCell('braze-inbox__history-cell', total || '-'),\n    historyCell(\n      'braze-inbox__history-cell braze-inbox__history-cell--align-end',\n      formatPurchasePoints(purchase, labels.totalPoints),\n    ),\n  );\n\n  if (!expandable) return { row, expandWrap: null };
1\n\n  const expandWrap = buildPurchaseExpandPanel(lineItems, locale);\n  row.addEventListener('click', () => {\n    const open = row.getAttribute('aria-expanded') === 'true';\n    row.setAttribute('aria-expanded', open ? 'false' : 'true');\n    expandWrap.hidden = open;\n    detailsCell.textContent = `${open ? '\u25BE' : '\u25B4'} ${orderLabel}`;\n  });\n  return { row, expandWrap };\n}\n\n/**\n * @param {object} purchase\n * @param {string} totalPointsLabel\n * @returns {string}\n */\nfunction formatPurchasePoints(purchase, totalPointsLabel) {\n  const pts = purchase.pointsAwarded;\n  if (pts == null || pts === '') return '';\n  const numeric = Number(pts);\n  const signed = Number.isFinite(numeric)\n    ? numeric >= 0\n      ? `+${numeric}`\n      : String(numeric)\n    : String(pts);\n  const note = purchase.pointsNote ? ` (${purchase.pointsNote})` : '';\n  const alreadyNoted = /partial refund/i.test(String(pts));\n  return alreadyNoted ? `${totalPointsLabel} ${pts}` : `${totalPointsLabel} ${signed}${note}`;\n}\n\n/**\n * @param {Array} lineItems\n * @param {string} locale\n * @returns {HTMLElement}\n */\nfunction buildPurchaseExpandPanel(lineItems, locale) {\n  const expandWra
1p = document.createElement('div');\n  expandWrap.className = 'braze-inbox__history-expand';\n  expandWrap.hidden = true;\n  expandWrap.setAttribute('data-purchase-expand-panel', 'true');\n\n  lineItems.forEach((item) => {\n    const skuRow = document.createElement('div');\n    skuRow.className = 'braze-inbox__history-row braze-inbox__history-row--sku';\n\n    // A line item has no date of its own, so the Date column stays empty and the\n    // SKU sits under \"Purchase Details\" beside the order number it breaks down.\n    const dateCell = document.createElement('div');\n    dateCell.className = 'braze-inbox__history-cell';\n\n    const detailsCell = document.createElement('div');\n    detailsCell.className = 'braze-inbox__history-cell';\n    detailsCell.textContent = (0,_loyalty_js__WEBPACK_IMPORTED_MODULE_0__.formatLineItemDetails)(item);\n\n    const amountCell = document.createElement('div');\n    amountCell.className = 'braze-inbox__history-cell';\n    const amount = item.amount ?? item.price ?? item.total;\n    amountCell.textContent = (0,_loyalty_js__WEBPACK_IMPORTED_MODULE_0__.formatLoyaltyCurrency)(amount, locale);\n\n    // Fourth column is \"Pts awarded\" \u2014 blank until QA asked for per-item points\n    // now that these rows expand. Aligned like the purchase row above it.\n    const pointsCell = document.createElement('div');\n    pointsCell.className = 'braze-inbox__history-cell braze-inbox__history-cell--align-end';\n    pointsCell.textContent = (0,_loyalty_js__WEBPACK_IMPORTED_MODULE_0__.formatLineItemPoints)(item);\n\n    skuRow.append(dateCell, detailsCell, amountCell, pointsCell);
1\n    expandWrap.appendChild(skuRow);\n  });\n\n  return expandWrap;\n}\n\n/**\n * @param {object} activity\n * @returns {HTMLElement}\n */\nfunction createActivityRow(activity) {\n  const row = document.createElement('div');\n  row.className = 'braze-inbox__history-row braze-inbox__history-row--activities';\n\n  const dateCell = document.createElement('div');\n  dateCell.className = 'braze-inbox__history-cell';\n  dateCell.textContent = (0,_loyalty_js__WEBPACK_IMPORTED_MODULE_0__.formatHistoryDateUtc)(activity.date);\n\n  const activityCell = document.createElement('div');\n  activityCell.className = 'braze-inbox__history-cell';\n  activityCell.textContent = activity.title || '';\n\n  const pointsCell = document.createElement('div');\n  pointsCell.className = 'braze-inbox__history-cell braze-inbox__history-cell--align-end';\n  const points = Number(activity.points) || 0;\n  pointsCell.textContent = points >= 0 ? `+${points}` : String(points);\n\n  row.append(dateCell, activityCell, pointsCell);\n  return row;\n}\n\n/**\n * @param {HTMLElement} section\n * @param {Array} rewards\n * @param {string} locale\n * @param {{ statusFulfilled: string, statusPending: string }} labels\n * @param {number} page\n */\nfunction renderRewardsHistory(section, rewards, locale, labels, page = 1) {\n  const bodyEl = section.querySelector('[data-history-body=\"rewards\"]');\n  const paginationEl = section.querySelector('[data-history-pagination=\"rewards\"]');\n\n  if (!rewards || rewards.length === 0) {\n    setSectionEmpty(section, true);\n    if (paginationEl) paginationEl.replaceChildren();\n    return;\n  }\n\n  const { safePage, pageItems } = paginate(rewards, page);\n  bodyEl.replaceChildren();\n  pageItems.forEach((reward) => bodyEl.appendChild(createRewardRow(reward, labels)));\n  renderPagination(paginationEl, rewards.length, safePage, (newPage) => {\n    renderRewardsHistory(section, rewards, locale, labels, newPage);\n  });\n  setSectionEmpty(section, false);\n}\n\n/**\n * @param {HTMLElement} section\n * @param {Array} purchases\n * @param {string} locale\n * @param {HTMLElement} container\n * @param {number} page\n */\nfunction renderPurchaseHistory(section, purchases, locale, container, page = 1) {\n  const bodyEl = section.querySelector('[data-history-body=\"purchases\"]');\n  const paginationEl = section.querySelector('[data-history-pagination=\"purchases\"]');\n  const labels = historyLabels(container);\n\n  if (!purchases || purchases.length === 0) {\n    setSectionEmpty(section, true);\n    if (paginationEl) paginationEl.replaceChildren();\n    return;\n  }\n\n  const { safePage, pageItems } = paginate(purchases, page);\n  bodyEl.replaceChildren();\n  pageItems.forEach((purchase) => {\n    const { row, expandWrap } = createPurchaseRow(purchase, labels, locale);\n    bodyEl.appendChild(row);\n    if (expandWrap) bodyEl.appendChild(expandWrap);\n  });\n  renderPagination(paginationEl, purchases.length, safePage, (newPage) => {\n    renderPurchaseHistory(section, purchases, locale, container, newPage);\n  });\n  setSectionEmpty(section, false);\n}\n\n/**\n * @param {HTMLElement} section\n * @param {Array} activities\n * @param {string} locale\n * @param {number} page\n */\nfunction renderActivityHistory(section, activities, locale, page = 1) {\n  const bodyEl = section.querySelector('[data-history-body=\"activities\"]');\n  const paginationEl = section.querySelector('[data-history-pagination=\"activities\"]');\n\n  if (!activities || activities.length === 0) {\n    setSectionEmpty(section, true);\n    if (paginationEl) paginationEl.replaceChildren();\n    return;\n  }\n\n  const { safePage, pageItems } = paginate(activities, page);\n  bodyEl.replaceChildren();\n  pageItems.forEach((activity) => bodyEl.appendChild(createActivityRow(activity)));\n  renderPagination(paginationEl, activities.length, safePage, (newPage) => {\n    renderActivityHistory(section, activities, locale, newPage);\n  });\n  setSectionEmpty(section, false);\n}\n\n/**\n * @param {HTMLElement} container\n */\nfunction refreshHistorySections(container) {\n  const locale = historyDataCache.locale;\n  const rewardsSection = container.querySelector('[data-history-section=\"rewards\"]');\n  const purchasesSection = container.querySelector('[data-history-section=\"purchases\"]');\n  const activitiesSection = container.querySelector('[data-history-section=\"activities\"]');\n\n  if (rewardsSection) {\n    const filter = rewardsSection.querySelector('[data-history-filter=\"rewards\"]')?.value || 'all';\n    renderRewardsHistory(\n      rewardsSection,\n      historyDataCache.rewards.filter((row) => (0,_loyalty_js__WEBPACK_IMPORTED_MODULE_0__.isWithinDays)(row.date, filter)),\n      locale,\n      historyLabels(container),\n    );\n  }\n\n  if (purchasesSection) {\n    const filter =\n      purchasesSection.querySelector('[data-history-filter=\"purchases\"]')?.value || 'all';\n    renderPurchaseHistory(\n      purchasesSection,\n      historyDataCache.purchases.filter((row) => (0,_loyalty_js__WEBPACK_IMPORTED_MODULE_0__.isWithinDays)(row.date, filter)),\n      locale,\n      container,\n    );\n  }\n\n  if (activitiesSection) {\n    const filter =\n      activitiesSection.querySelector('[data-history-filter=\"activities\"]')?.value || 'all';\n    renderActivityHistory(\n      activitiesSection,\n      historyDataCache.activities.filter((row) => (0,_loyalty_js__WEBPACK_IMPORTED_MODULE_0__.isWithinDays)(row.date, filter)),\n      locale,\n    );\n  }\n}\n\n/**\n * Main render function for History tab.\n * @param {HTMLElement} container\n * @param {Object} data\n * @param {string} locale\n */\nfunction renderHistory(container, data, locale) {\n  if (!data || !data.loggedIn) return;\n\n  historyDataCache = {\n    rewards: (Array.isArray(data.history?.rewards) ? data.history.rewards : []).map(\n      normalizeReward,\n    ),\n    purchases: (Array.isArray(data.history?.purchases) ? data.history.purchases : []).map(\n      normalizePurchase,\n    ),\n    activities: (Array.isArray(data.history?.activities) ? data.history.activities : []).map(\n      normalizeActivity,\n    ),\n    locale: locale || 'en',\n  };\n\n  refreshHistorySections(container);\n  container.setAttribute('data-state', 'loaded');\n}\n\n/**\n * Wires history filter change listener once.\n */\nfunction initHistoryFilterListeners() {\n  document.addEventListener('change', (e) => {\n    const filter = e.target.closest('[data-history-filter]');\n    if (!filter) return;\n    const container = filter.closest(HISTORY_SELECTOR);\n    if (container) refreshHistorySections(container);\n  });\n}\n\n\n//# sourceURL=webpack://ardene-theme/./src/snippets/braze/inbox-history.js?\n}")},
1"./src/snippets/braze/inbox-offers.js":function(__unused_webpack___webpack_module__,__webpack_exports__,__webpack_require__){eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   OFFER_DETAILS_CLOSE_SELECTOR: () => (/* binding */ OFFER_DETAILS_CLOSE_SELECTOR),\n/* harmony export */   closeOfferDetails: () => (/* binding */ closeOfferDetails),\n/* harmony export */   createOfferTitle: () => (/* binding */ createOfferTitle),\n/* harmony export */   initOfferDetailsListeners: () => (/* binding */ initOfferDetailsListeners),\n/* harmony export */   openOfferDetails: () => (/* binding */ openOfferDetails),\n/* harmony export */   renderOffers: () => (/* binding */ renderOffers)\n/* harmony export */ });\n/* harmony import */ var _loyalty_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./loyalty.js */ \"./src/snippets/braze/loyalty.js\");\n/* harmony import */ var _safe_url_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./safe-url.js */ \"./src/snippets/braze/safe-url.js\");\n\n\n\nconst OFFER_DETAILS_SELECTOR = '[data-offer-details]';\nconst REWARDS_OFFERS_SELECTOR = '[data-rewards-offers]';\n\n/**\n * The details overlay's close control.\n *\n * Deliberately not the theme's usual `data-close`: drawer-component closes\n * itself on any `[data-close]` it contains, so sharing that attribute dismissed\n * the whole rewards drawer along with the overlay. Both click listeners are on\n * `document`, so stopping propagation would not have helped \u2014 only a selector\n * the drawer does not claim does.\n */\nconst OFFER_DETAILS_CLOSE_SELECTOR = '[data-offer-details-close]';\n\nlet offerDetailsPreviousFocus = null;\n\n/**\n * @param {HTMLElement|null|undefined} el\n * @param {boolean} isHidden\n */\nfunction setHidden(el, isHidden) {\n  if (!el) return;\n  el.hidden = isHidden;\n  el.classList.toggle('hidden', isHidden);\n}\n\n/**\n * Static padlock SVG for offer cards (trusted markup, not API content).\n * @returns {string}\n */\nfunction getOfferIcon() {\n  return `<svg width=\"40\" height=\"40\" viewBox=\"0 0 40 40\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n    <rect x=\"12\" y=\"18\" width=\"16\" height=\"14\" rx=\"2\" stroke=\"#000000\" stroke-width=\"1.5\" fill=\"none\"/>\n    <path d=\"M15 18V14C15 11.2386 17.2386 9 20 9C22.7614 9 25 11.2386 25 14V18\" stroke=\"#000000\" stroke-width=\"1.5\" stroke-linecap=\"round\" fill=\"none\"/>\n    <circle cx=\"20\" cy=\"25\" r=\"1.5\" fill=\"#000000\"/>\n  </svg>`;\n}\n\n/**\n * Reads a label off the offers container, falling back to English.\n *\n * @param {string} datasetKey\n * @param {string} fallback\n * @returns {string}\n */\nfunction offersLabel(datasetKey, fallback) {\n  return document.querySelector(REWARDS_OFFERS_SELECTOR)?.dataset[datasetKey] || fallback;\n}\n\n/**\n * Builds the \"Expires: March 1, 2027\" line shared by the card and the overlay.\n *\n * @param {number|string|null|undefined} expiresTimestamp\n * @param {string} locale\n * @returns {string} empty when the offer has no expiry\n */\nfunction formatExpiryLine(expiresTimestamp, locale) {\n  if (!expiresTimestamp) return '';\n  const expiryDate = (0,_loyalty_js__WEBPACK_IMPORTED_MODULE_0__.formatLoyaltyDate)(expiresTimestamp, locale, {\n    year: 'numeric',\n    month: 'long',\n    day: 'numeric',\n  });\n  return `${offersLabel('i18nExpires', 'Expires')}: ${expiryDate}`;\n}\n\n/**\n * Opens the in-drawer offer details overlay.\n * @param {Object} offer\n * @param {string} locale\n */\nfunction openOfferDetails(offer, locale) {\n  const panel = document.querySelector(OFFER_DETAILS_SELECTOR);\n  if (!panel) return;\n\n  const titleEl = panel.querySelector('[data-offer-details-title]');\n  const expiryEl = panel.querySelector('[data-offer-details-expiry]');\n  const descEl = panel.querySelector('[data-offer-details-description]');\n\n  if (titleEl) titleEl.textContent = offer.title || offer.name || '';\n\n  if (expiryEl) {\n    expiryEl.textContent = formatExpiryLine(offer.expiresTimestamp, locale);\n    expiryEl.hidden = !expiryEl.textContent;\n  }\n\n  if (descEl) {\n    descEl.textContent = offer.description || offer.terms || '';\n    descEl.hidden = !descEl.textContent;\n  }\n\n  offerDetailsPreviousFocus = document.activeElement;\n  panel.setAttribute('data-active', 'true');\n  panel.setAttribute('aria-hidden', 'false');\n  panel.hidden = false;
1\n\n  panel.querySelector(OFFER_DETAILS_CLOSE_SELECTOR)?.focus();\n}\n\n/**\n * Closes the in-drawer offer details overlay.\n */\nfunction closeOfferDetails() {\n  const panel = document.querySelector(OFFER_DETAILS_SELECTOR);\n  if (!panel) return;\n\n  panel.setAttribute('data-active', 'false');\n  panel.setAttribute('aria-hidden', 'true');\n  panel.hidden = true;\n\n  if (offerDetailsPreviousFocus && typeof offerDetailsPreviousFocus.focus === 'function') {\n    offerDetailsPreviousFocus.focus();\n  }\n  offerDetailsPreviousFocus = null;\n}\n\n/**\n * Creates a single offer card.\n *\n * The card used to be one big button that opened the details overlay, which\n * meant a reward's configured CrowdTwist URL was unreachable \u2014 clicking the\n * title opened the overlay instead of the destination, unlike production. The\n * card is now a plain container with two distinct controls: the title links to\n * the reward URL, and Details opens the overlay. Keeping them separate is also\n * what makes the title a real link rather than a button that happens to\n * navigate.\n *\n * A reward with no usable URL keeps a non-interactive title; the overlay is\n * still reachable through Details, so no offer becomes a dead end.\n *\n * @param {Object} offer\n * @param {string} locale\n * @returns {HTMLElement}\n */\nfunction createOfferCard(offer, locale) {\n  const card = document.createElement('div');\n  card.className = 'braze-inbox__offer-card';\n\n  const icon = document.createElement('div');\n  icon.className = 'braze-inbox__offer-icon';\n  icon.innerHTML = getOfferIcon();\n  icon.setAttribute('aria-hidden', 'true');\n\n  const content = document.createElement('div');\n  content.className = 'braze-inbox__offer-content';\n  content.appendChild(createOfferTitle(offer));\n\n  const expiryLine = formatExpiryLine(offer.expiresTimestamp, locale);\n  if (expiryLine) {\n    const expiry = document.createElement('p');\n    expiry.className = 'braze-inbox__offer-expiry';\n    expiry.textContent = expiryLine;\n    content.appendChild(expiry);\n  }\n\n  content.appendChild(createOfferDetailsButton(offer, locale));\n\n  card.appendChild(icon);\n  card.appendChild(content);\n\n  return card;\n}\n\n/**\n * The reward title: a link to its configured URL, or inert copy without one.\n *\n * @param {Object} offer\n * @returns {HTMLElement}\n */\nfunction createOfferTitle(offer) {\n  const offerUrl = offer.url || '';\n  const hasUrl = (0,_safe_url_js__WEBPACK_IMPORTED_MODULE_1__.isSafeUrl)(offerUrl);\n\n  const title = document.createElement(hasUrl ? 'a' : 'p');\n  title.className = 'braze-inbox__offer-title';\n  title.textContent = offer.title || offer.name || '';\n  if (hasUrl) {\n    title.href = offerUrl;\n    title.setAttribute('data-actionable', 'true');\n  }\n  return title;\n}\n\n/**\n * The Details control, which opens the overlay rather than navigating.\n *\n * @param {Object} offer\n * @param {string} locale\n * @returns {HTMLButtonElement}\n */\nfunction createOfferDetailsButton(offer, locale) {\n  const label = offersLabel('i18nDetails', 'Details');\n  const titleText = offer.title || offer.name || '';\n\n  const details = document.createElement('button');\n  details.type = 'button';\n  details.className = 'braze-inbox__offer-details';\n  details.setAttribute('data-trigger', 'offer-details');\n  details.textContent = label;\n  details.setAttribute('aria-label', titleText ? `${label}: ${titleText}` : label);\n  details.addEventListener('click', (e) => {\n    e.preventDefault();\n    openOfferDetails(offer, locale);\n  });\n  return details;\n}\n\n/**\n * Renders offers into the offers container.\n * @param {HTMLElement} container\n * @param {Object} data\n * @param {string} locale\n */\nfunction renderOffers(container, data, locale) {\n  const offersLoaded = container.querySelector('[data-offers-state=\"loaded\"]');\n  const offersEmpty = container.querySelector('[data-offers-state=\"empty\"]');\n  const offersLoading = container.querySelector('[data-offers-state=\"loading\"]');\n\n  if (!data?.loggedIn || !data.offers || data.offers.length === 0) {\n    container.setAttribute('data-state', 'empty');\n    setHidden(offersEmpty, false);\n    setHidden(offersLoading, true);\n    return;\n  }\n\n  const offersList = document.createElement('div');\n  offersList.className = 'braze-inbox__offers-list';
1\n\n  data.offers.forEach((offer) => {\n    offersList.appendChild(createOfferCard(offer, locale));\n  });\n\n  offersLoaded.replaceChildren(offersList);\n\n  container.setAttribute('data-state', 'loaded');\n  setHidden(offersLoaded, false);\n  setHidden(offersLoading, true);\n}\n\n/**\n * Wires offer-details overlay close / focus trap listeners once.\n *\n * The close control is marked with `data-offer-details-close`, not the theme's\n * usual `data-close`: drawer-component closes itself on any `[data-close]` it\n * contains, so sharing that attribute dismissed the whole rewards drawer along\n * with the overlay. Both listeners are on `document`, so stopping propagation\n * would not have helped \u2014 only a selector the drawer does not claim does.\n */\nfunction initOfferDetailsListeners() {\n  document.addEventListener('click', (e) => {\n    const closeBtn = e.target.closest(OFFER_DETAILS_CLOSE_SELECTOR);\n    if (closeBtn) {\n      e.preventDefault();\n      closeOfferDetails();\n      return;\n    }\n    const panel = document.querySelector(OFFER_DETAILS_SELECTOR);\n    if (panel?.getAttribute('data-active') === 'true' && e.target === panel) {\n      closeOfferDetails();\n    }\n  });\n\n  document.addEventListener('keydown', (e) => {\n    const panel = document.querySelector(OFFER_DETAILS_SELECTOR);\n    if (!panel || panel.getAttribute('data-active') !== 'true') return;\n\n    if (e.key === 'Escape') {\n      e.preventDefault();\n      closeOfferDetails();\n      return;\n    }\n\n    if (e.key !== 'Tab') return;\n    const focusable = panel.querySelectorAll(\n      'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])',\n    );\n    if (!focusable.length) return;\n    const first = focusable[0];\n    const last = focusable[focusable.length - 1];\n    if (e.shiftKey && document.activeElement === first) {\n      e.preventDefault();\n      last.focus();\n    } else if (!e.shiftKey && document.activeElement === last) {\n      e.preventDefault();\n      first.focus();\n    }\n  });\n}\n\n\n//# sourceURL=webpack://ardene-theme/./src/snippets/braze/inbox-offers.js?\n}")},
1"./src/snippets/braze/inbox-profile.js":function(__unused_webpack___webpack_module__,__webpack_exports__,__webpack_require__){eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   renderProfile: () => (/* binding */ renderProfile)\n/* harmony export */ });\n/* harmony import */ var _loyalty_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./loyalty.js */ \"./src/snippets/braze/loyalty.js\");\n\n\n/**\n * @param {HTMLElement|null|undefined} el\n * @param {boolean} isHidden\n */\nfunction setHidden(el, isHidden) {\n  if (!el) return;\n  el.hidden = isHidden;\n  el.classList.toggle('hidden', isHidden);\n}\n\n/**\n * @param {HTMLElement} parent\n * @param {string} className\n * @param {string} text\n */\nfunction appendText(parent, className, text) {\n  if (!text) return;\n  const el = document.createElement('p');\n  el.className = className;\n  el.textContent = text;\n  parent.appendChild(el);\n}\n\n/**\n * @param {string} levelTitle\n * @returns {string}\n */\nfunction levelFromTitle(levelTitle) {\n  const match = String(levelTitle || '').match(/(\\d+)/);\n  const n = match ? Number(match[1]) : 1;\n  if (n === 2 || n === 3) return String(n);\n  return '1';\n}\n\n/**\n * Resolves the profile header's spend line for the member's current state.\n *\n * @param {DOMStringMap} i18n\n * @param {number|null} spendNeeded\n * @param {string} expirationDate already-formatted, empty when absent\n * @param {boolean} isMaintenance\n * @param {string} locale\n * @returns {string}\n */\nfunction resolveSpendText(i18n, spendNeeded, expirationDate, isMaintenance, locale) {\n  const variant = (0,_loyalty_js__WEBPACK_IMPORTED_MODULE_0__.spendMessageVariant)(spendNeeded, {\n    isMaintenance,\n    hasExpiry: Boolean(expirationDate),\n  });\n  const templates = {\n    qualified:\n      i18n.i18nSpendQualified ||\n      \"You're all set! {{amount}} left to spend \u2014 your level status is secured until {{date}}.\",\n    maintain:\n      i18n.i18nSpendMaintain || 'Spend {{amount}} by {{date}} to maintain your level status!',\n    levelUp: i18n.i18nSpendLevelUp || 'Spend {{amount}} by {{date}} to level up!',\n    fallback: i18n.i18nSpendFallback || 'Keep shopping to maintain or level up your status!',\n  };\n\n  return (0,_loyalty_js__WEBPACK_IMPORTED_MODULE_0__.fillTemplate)(templates[variant], {\n    amount: (0,_loyalty_js__WEBPACK_IMPORTED_MODULE_0__.formatSpendAmount)(spendNeeded, locale),\n    date: expirationDate,\n  });\n}\n\n/**\n * @param {DOMStringMap} i18n\n * @param {object} data\n * @param {string} locale\n */\nfunction buildProfileViewModel(i18n, data, locale) {\n  const memberName = [data.member?.firstName, data.member?.lastName].filter(Boolean).join(' ');\n  const memberSince = data.member?.memberSinceTimestamp\n    ? (0,_loyalty_js__WEBPACK_IMPORTED_MODULE_0__.formatLoyaltyDate)(data.member.memberSinceTimestamp, locale, {\n        year: 'numeric',\n        month: 'short',\n        day: 'numeric',\n      })\n    : '';\n  const levelTitle = data.tier?.currentLevel?.title || 'Level 1';\n  const spendNeeded = (0,_loyalty_js__WEBPACK_IMPORTED_MODULE_0__.resolveSpendNeeded)(data.tier?.spendNeeded);\n  const expirationDate = data.tier?.expirationDate\n    ? (0,_loyalty_js__WEBPACK_IMPORTED_MODULE_0__.formatLoyaltyDate)(data.tier.expirationDate, locale, {\n        year: 'numeric',\n        month: 'short',\n        day: 'numeric',\n      })\n    : '';\n  const points = data.points?.redeemable || 0;\n  const neededForReward = (0,_loyalty_js__WEBPACK_IMPORTED_MODULE_0__.neededPointsForReward)(points, data.points?.neededForReward);\n\n  return {\n    memberName,\n    memberSinceLine: memberSince ? `${i18n.i18nMemberSince || 'Member since'} ${memberSince}` : '',\n    levelTitle,\n    levelKey: levelFromTitle(levelTitle),\n    spendText: resolveSpendText(\n      i18n,\n      spendNeeded,\n      expirationDate,\n      Boolean(data.tier?.isMaintenance),\n      locale,\n    ),\n    points,\n    pointsAwayText: (0,_loyalty_js__WEBPACK_IMPORTED_MODULE_0__.fillTemplate)(\n      i18n.i18nPointsAway || '{{count}} points away from a {{value}} reward',\n      {\n        count: neededForReward,\n        value: (0,_loyalty_js__WEBPACK_IMPORTED_MODULE_0__.formatLoyaltyCurrency)(data.points?.rewardValue || 5, locale, { decimals: 0 }),\n      },\n    ),\n    showPointsAway: !(neededForReward === 0 && points >= 100),\n    pointsLabel: i18n.i18nPoints || 'Points',\n  };\n}\n\n/**\n * @param {ReturnType<typeof buildProfileViewModel>} view\n * @returns {{ info: HTMLElement, pointsWrap: HTMLElement }}\n */\nfunction buildProfileDom(view) {\n  const info = document.createElement('div');\n  info.className = 'braze-inbox__profile-info';\n  appendText(info, 'braze-inbox__profile-name', view.memberName);\n  appendText(info, 'braze-inbox__profile-member-since', view.memberSinceLine);\n  appendText(info, 'braze-inbox__profile-level', view.levelTitle);\n  appendText(info, 'braze-inbox__profile-progress', view.spendText);\n\n  const pointsWrap = document.createElement('div');\n  pointsWrap.className = 'braze-inbox__profile-points';\n  const circle = document.createElement('div');\n  circle.className = 'braze-inbox__points-circle';\n  circle.setAttribute('data-level', view.levelKey);
1\n  appendText(circle, 'braze-inbox__points-count', String(view.points));\n  appendText(circle, 'braze-inbox__points-label', view.pointsLabel);\n  pointsWrap.appendChild(circle);\n  if (view.showPointsAway) {\n    appendText(pointsWrap, 'braze-inbox__points-away', view.pointsAwayText);\n  }\n\n  return { info, pointsWrap };\n}\n\n/**\n * Renders profile data into the profile container using textContent only.\n * @param {HTMLElement} container\n * @param {Object} data\n * @param {string} locale\n */\nfunction renderProfile(container, data, locale) {\n  const loadedDiv = container.querySelector('[data-profile-state=\"loaded\"]');\n  const loadingDiv = container.querySelector('[data-profile-state=\"loading\"]');\n\n  if (!data?.loggedIn || data.error) {\n    container.setAttribute('data-state', 'error');\n    loadedDiv.replaceChildren();\n    const err = document.createElement('p');\n    err.style.textAlign = 'center';\n    err.style.padding = '20px';\n    err.textContent = 'Unable to load profile';\n    loadedDiv.appendChild(err);\n    setHidden(loadedDiv, false);\n    setHidden(loadingDiv, true);\n    return;\n  }\n\n  const view = buildProfileViewModel(container.dataset, data, locale);\n  const { info, pointsWrap } = buildProfileDom(view);\n  loadedDiv.replaceChildren(info, pointsWrap);\n  container.setAttribute('data-state', 'loaded');\n  setHidden(loadedDiv, false);\n  setHidden(loadingDiv, true);\n}\n\n\n//# sourceURL=webpack://ardene-theme/./src/snippets/braze/inbox-profile.js?\n}")},
1"./src/snippets/braze/inbox-ways-to-earn.js":function(__unused_webpack___webpack_module__,__webpack_exports__,__webpack_require__){eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   WAYS_TO_EARN_SELECTOR: () => (/* binding */ WAYS_TO_EARN_SELECTOR),\n/* harmony export */   fetchRewardsData: () => (/* binding */ fetchRewardsData),\n/* harmony export */   renderWaysToEarn: () => (/* binding */ renderWaysToEarn)\n/* harmony export */ });\n/* harmony import */ var _loyalty_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./loyalty.js */ \"./src/snippets/braze/loyalty.js\");\n/* harmony import */ var _safe_url_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./safe-url.js */ \"./src/snippets/braze/safe-url.js\");\n/* harmony import */ var _inbox_profile_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./inbox-profile.js */ \"./src/snippets/braze/inbox-profile.js\");\n\n\n\n\nconst WAYS_TO_EARN_SELECTOR = '[data-ways-to-earn]';\nconst REWARDS_PROFILE_SELECTOR = '[data-rewards-profile]';\n\n/**\n * @param {HTMLElement|null|undefined} el\n * @param {boolean} isHidden\n */\nfunction setHidden(el, isHidden) {\n  if (!el) return;\n  el.hidden = isHidden;\n  el.classList.toggle('hidden', isHidden);\n}\n\n/**\n * @param {string} customerId\n * @param {string} locale\n * @returns {Promise<Object|null>}\n */\nasync function fetchRewardsData(customerId, locale) {\n  try {\n    const res = await fetch(\n      `/apps/tools/api/rewards-summary?logged_in_customer_id=${customerId}&locale=${locale}`,\n    );\n    if (!res.ok) throw new Error(`HTTP ${res.status}`);\n    return await res.json();\n  } catch (error) {\n    console.error('[Braze Inbox] Failed to fetch rewards data:', error);\n    return null;\n  }\n}\n\n/**\n * @param {HTMLElement} container\n * @returns {string}\n */\nfunction weburlSuffixFrom(container) {\n  return container?.dataset?.weburlSuffix || 'en';\n}\n\n/**\n * @param {HTMLElement} card\n */\nfunction restoreActivityCard(card) {\n  card.disabled = false;\n  card.removeAttribute('aria-disabled');\n  delete card.dataset.activityState;\n}\n\n/**\n * @param {HTMLElement} card\n * @param {HTMLElement} container\n * @param {string} locale\n */\nfunction markActivityCompleted(card, container, locale) {\n  delete card.dataset.activityState;\n  card.removeAttribute('data-trigger');\n  card.removeAttribute('role');\n  card.removeAttribute('tabindex');\n  card.disabled = true;\n\n  const titleEl = card.querySelector('.braze-inbox__activity-name');\n  if (titleEl) {\n    titleEl.removeAttribute('data-actionable');\n    titleEl.style.textDecoration = 'none';\n  }\n\n  const content = card.querySelector('.braze-inbox__activity-content');\n  if (content && !content.querySelector('.braze-inbox__activity-date')) {\n    const dateEl = document.createElement('p');\n    dateEl.className = 'braze-inbox__activity-date';\n    const completedLabel = container.dataset.i18nCompleted || 'Completed';\n    const date = (0,_loyalty_js__WEBPACK_IMPORTED_MODULE_0__.formatLoyaltyDate)(Math.floor(Date.now() / 1000), locale, {\n      month: 'short',\n      day: 'numeric',\n      year: 'numeric',\n    });\n    dateEl.textContent = `${completedLabel} ${date}`;\n    content.appendChild(dateEl);\n  }\n}\n\n/**\n * @param {Object} activity\n * @param {string} activityUrl\n * @param {HTMLElement} card\n * @param {HTMLElement} container\n */\nasync function handleActivityClick(activity, activityUrl, card, container) {\n  if (card.disabled) return;\n  card.disabled = true;\n  card.setAttribute('aria-disabled', 'true');\n  card.dataset.activityState = 'loading';\n  if ((0,_safe_url_js__WEBPACK_IMPORTED_MODULE_1__.isSafeUrl)(activityUrl)) window.open(activityUrl, '_blank', 'noopener,noreferrer');\n\n  const profileContainer = document.querySelector(REWARDS_PROFILE_SELECTOR);\n  const customerId = profileContainer?.dataset.customerId;\n  const locale = profileContainer?.dataset.locale || 'en';\n  if (!customerId) {\n    console.error('[Ways to Earn] Customer ID n
1ot found');\n    restoreActivityCard(card);\n    return;\n  }\n\n  try {\n    const response = await fetch(\n      `/apps/tools/api/rewards-summary?logged_in_customer_id=${customerId}&locale=${locale}`,\n      {\n        method: 'POST',\n        headers: { 'Content-Type': 'application/json' },\n        body: JSON.stringify({ activityId: activity.activityId }),\n      },\n    );\n    const result = await response.json();\n    if (!result.success) {\n      restoreActivityCard(card);\n      console.error('[Ways to Earn]', result.error || 'Failed to earn points');\n      return;\n    }\n    markActivityCompleted(card, container, locale);\n    if (profileContainer) {\n      const profileData = await fetchRewardsData(customerId, locale);\n      if (profileData) (0,_inbox_profile_js__WEBPACK_IMPORTED_MODULE_2__.renderProfile)(profileContainer, profileData, locale);\n    }\n  } catch (error) {\n    console.error('[Ways to Earn] Error crediting activity:', error);\n    restoreActivityCard(card);\n  }\n}\n\n/**\n * @param {number|string|null} points\n * @param {string} ptsLabel\n * @returns {HTMLElement}\n */\nfunction createActivityBadge(points, ptsLabel) {\n  const badge = document.createElement('div');\n  badge.className = 'braze-inbox__activity-badge';\n\n  if (typeof points === 'number') {\n    const pointsEl = document.createElement('span');\n    pointsEl.className = 'braze-inbox__activity-points';\n    pointsEl.textContent = String(points);\n    const ptsEl = document.createElement('span');\n    ptsEl.className = 'braze-inbox__activity-pts';\n    ptsEl.textContent = ptsLabel;\n    badge.append(pointsEl, ptsEl);\n  } else if (points != null && points !== '') {\n    const pointsEl = document.createElement('span');\n    pointsEl.className = 'braze-inbox__activity-points';\n    pointsEl.textContent = String(points);\n    badge.appendChild(pointsEl);\n  } else {\n    const ptsEl = document.createElement('span');\n    ptsEl.className = 'braze-inbox__activity-pts';\n    ptsEl.textContent = ptsLabel;\n    badge.appendChild(ptsEl);\n  }\n\n  return badge;\n}\n\n/**\n * @param {Object} activity\n * @param {string} locale\n * @param {HTMLElement} container\n * @returns {HTMLElement}\n */\nfunction createWaysToEarnCard(activity, locale, container) {\n  const ptsLabel = container.dataset.i18nPts || 'Pts';\n  const activityName = activity.activityName || activity.title || activity.name || '';\n  const activityUrl = (0,_loyalty_js__WEBPACK_IMPORTED_MODULE_0__.getActivityWebUrl)(activity, weburlSuffixFrom(container));\n  const isActionable = (0,_loyalty_js__WEBPACK_IMPORTED_MODULE_0__.isActivityActionable)(activity, activityUrl, _safe_url_js__WEBPACK_IMPORTED_MODULE_1__.isSafeUrl);\n\n  const card = document.createElement(isActionable ? 'button' : 'div');\n  if (isActionable) {\n    card.type = 'button';\n    card.setAttribute('data-trigger', 'activity-earn');\n  }\n  card.className = 'braze-inbox__activity-card';\n  card.dataset.activityId = activity.activityId;\n  card.appendChild(createActivityBadge((0,_loyalty_js__WEBPACK_IMPORTED_MODULE_0__.getActivityPoints)(activity), ptsLabel));\n\n  const content = document.createElement('div');\n  content.className = 'braze-inbox__activity-content';\n  const title = document.createElement(isActionable ? 'span' : 'p');\n  title.className = 'braze-inbox__activity-name';\n  if (isActionable) title.setAttribute('data-actionable', 'true');\n  title.textContent = activityName;\n  content.appendChild(title);\n\n  if (!isActionable && activity.date) {\n    const dateEl = document.createElement('p');\n    dateEl.className = 'braze-inbox__activity-date';\n    dateEl.textContent = `${container.dataset.i18nCompleted || 'Completed'} ${(0,_loyalty_js__WEBPACK_IMPORTED_MODULE_0__.formatLoyaltyDate)(\n      activity.date,\n      locale,\n      { month: 'short', day: 'numeric', year: 'numeric' },\n    )}`;\n    content.appendChild(dateEl);\n  }\n  card.appendChild(content);\n\n  if (isActionable) {\n    card.addEventListener('click', (e) => {\n      e.preventDefault();\n      handleActivityClick(activity, activityUrl, card, container);\n    });\n  }\n  return card;\n}\n\n/**\n * @param {HTMLElement} container\n * @param {Object} data\n * @param {string} locale\n */\nfunction renderWaysToEarn(container, data, locale) {\n  const loadedEl = container.querySelector('[data-ways-to-earn-state=\"loaded\"]');\n  const emptyEl = container.querySelector('[data-ways-to-earn-state=\"empty\"]');\n  const loadingEl = container.querySelector('[data-ways-to-earn-state=\"loading\"]');\n  const activities = (0,_loyalty_js__WEBPACK_IMPORTED_MODULE_0__.getWaysToEarnActivities)(data);\n\n  if (!data?.loggedIn || activities.length === 0) {\n    container.setAttribute('data-state', 'empty');\n    setHidden(emptyEl, false);\n    setHidden(loadingEl, true);\n    setHidden(loadedEl, true);\n    return;\n  }\n\n  const activitiesGrid = document.createElement('div');\n  activitiesGrid.className = 'braze-inbox__activities-grid';
1\n  activities.forEach((activity) => {\n    activitiesGrid.appendChild(createWaysToEarnCard(activity, locale, container));\n  });\n\n  loadedEl.replaceChildren(activitiesGrid);\n  container.setAttribute('data-state', 'loaded');\n  setHidden(loadedEl, false);\n  setHidden(loadingEl, true);\n  setHidden(emptyEl, true);\n}\n\n\n\n\n//# sourceURL=webpack://ardene-theme/./src/snippets/braze/inbox-ways-to-earn.js?\n}")},
1"./src/snippets/braze/loyalty.js":function(__unused_webpack___webpack_module__,__webpack_exports__,__webpack_require__){eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   REWARD_POINT_THRESHOLD: () => (/* binding */ REWARD_POINT_THRESHOLD),\n/* harmony export */   fillTemplate: () => (/* binding */ fillTemplate),\n/* harmony export */   formatHistoryDateUtc: () => (/* binding */ formatHistoryDateUtc),\n/* harmony export */   formatLineItemDetails: () => (/* binding */ formatLineItemDetails),\n/* harmony export */   formatLineItemPoints: () => (/* binding */ formatLineItemPoints),\n/* harmony export */   formatLoyaltyCurrency: () => (/* binding */ formatLoyaltyCurrency),\n/* harmony export */   formatLoyaltyDate: () => (/* binding */ formatLoyaltyDate),\n/* harmony export */   formatRewardStatus: () => (/* binding */ formatRewardStatus),\n/* harmony export */   formatSpendAmount: () => (/* binding */ formatSpendAmount),\n/* harmony export */   getActivityPoints: () => (/* binding */ getActivityPoints),\n/* harmony export */   getActivityWebUrl: () => (/* binding */ getActivityWebUrl),\n/* harmony export */   getPurchaseOrderNumber: () => (/* binding */ getPurchaseOrderNumber),\n/* harmony export */   getWaysToEarnActivities: () => (/* binding */ getWaysToEarnActivities),\n/* harmony export */   isActivityActionable: () => (/* binding */ isActivityActionable),\n/* harmony export */   isWithinDays: () => (/* binding */ isWithinDays),\n/* harmony export */   neededPointsForReward: () => (/* binding */ neededPointsForReward),\n/* harmony export */   pickHistoryDate: () => (/* binding */ pickHistoryDate),\n/* harmony export */   resolveSpendNeeded: () => (/* binding */ resolveSpendNeeded),\n/* harmony export */   spendMessageVariant: () => (/* binding */ spendMessageVariant),\n/* harmony export */   toUtcCalendarDate: () => (/* binding */ toUtcCalendarDate)\n/* harmony export */ });\n/**\n * Loyalty helpers shared by the Braze inbox rewards UI.\n *\n * Dates from CrowdTwist / rewards-summary are often UTC midnight. Formatting\n * them in the shopper's local timezone shifts the calendar day back by one\n * in Americas timezones \u2014 always format the UTC calendar date instead.\n */\n\nconst REWARD_POINT_THRESHOLD = 100;\n\n/**\n * Points still needed for the next $5 reward.\n *\n * Prefers a positive API `neededForReward`. Otherwise derives from redeemable\n * points using the 100-point reward rule so we never show a false \"0 points away\".\n *\n * @param {number} redeemable\n * @param {number} [apiNeeded]\n * @param {number} [threshold=100]\n * @returns {number}\n */\nfunction neededPointsForReward(redeemable, apiNeeded, threshold = REWARD_POINT_THRESHOLD) {\n  const points = Number(redeemable) || 0;\n  const api = Number(apiNeeded);\n  if (Number.isFinite(api) && api > 0) return api;\n\n  if (points <= 0) return threshold;\n  const remainder = points % threshold;\n  return remainder === 0 ? 0 : threshold - remainder;\n}\n\n/**\n * Turns a reward-history status token into drawer copy.\n *\n * The app proxy sends a machine value (`fulfilled` / `pending`) rather than a\n * label, because this drawer is bilingual and owns its own strings. Anything\n * unrecognised passes through unchanged: a status the proxy adds later should\n * show up as something visible rather than silently blanking the cell.\n *\n * @param {string|null|undefined} status\n * @param {{ statusFulfilled?: string, statusPending?: string }} labels\n * @returns {string}\n */\nfunction formatRewardStatus(status, labels = {}) {\n  if (!status) return '';\n  if (status === 'fulfilled') return labels.statusFulfilled || status;\n  if (status === 'pending') return labels.statusPending || status;\n  return String(status);\n}\n\n/**\n * Resolves a tier spend requirement, preserving a genuine zero.\n *\n * A member who has already met their threshold has a `spendNeeded` of 0, and\n * that is a real amount the profile header should state. Coercing with\n * `Number(x) || 0` made zero indistinguishable from a missing field, so a\n * `> 0` guard hid both behind generic copy and the amount was never shown \u2014\n * the same class of false-zero this module already guards against in\n * neededPointsForReward.\n *\n * @param {unknown} spendNeeded\n * @returns {number|null} the amount, or null when there is nothing to show\n */\nfunction resolveSpendNeeded(spendNeeded) {\n  if (spendNeeded === null || spendNeeded === undefined || spendNeeded === '') return null;\n  const amount = Number(spendNeeded);\n  return Number.isFinite(amount) ? amount : null;\n}\n\n/**\n * Formats a loyalty dollar amount for the locale the drawer renders in.\n *\n * French Canadian puts the sign after the amount and separates decimals with a\n * comma (`30,90 $`); English keeps `$30.90`. The drawer used to assemble\n * `'$' + n.toFixed(2)` by hand while the French copy carried its own `$`\n * prefix, so every amount read as English on the French storefront. Intl owns\n * the symbol, its placement and both separators now, and the copy interpolates\n * an already-formatted amount.\n *\n * 
1Loyalty amounts are CrowdTwist programme dollars rather than cart money, so\n * the currency is a single default instead of being read off the cart.\n *\n * @param {number|string|null|undefined} value\n * @param {string} [locale='en']\n * @param {{ decimals?: number, currency?: string }} [options]\n * @returns {string} the formatted amount, or '' when there is none to state\n */\nfunction formatLoyaltyCurrency(value, locale = 'en', options = {}) {\n  if (value === null || value === undefined || value === '') return '';\n  const amount = Number(value);\n  if (!Number.isFinite(amount)) return '';\n\n  const { decimals = 2, currency = 'CAD' } = options;\n  const format = (tag) =>\n    new Intl.NumberFormat(tag, {\n      style: 'currency',\n      currency,\n      currencyDisplay: 'narrowSymbol',\n      minimumFractionDigits: decimals,\n      maximumFractionDigits: decimals,\n    }).format(amount);\n\n  try {\n    return format(locale || 'en');\n  } catch {\n    // Intl throws on a malformed language tag. A bad `request.locale.iso_code`\n    // should cost the member their formatting, not their balance.\n    return format('en');\n  }\n}\n\n/**\n * Formats a resolved spend amount for the profile copy.\n *\n * Every spend message names the amount now, including the qualified one, so the\n * formatted value has to keep a genuine zero (that is the member's real \"left to\n * spend\") while refusing a negative one: a member who overshot their threshold\n * still has $0 left to spend, not -$40.\n *\n * @param {number|null|undefined} spendNeeded resolved amount, or null when absent\n * @param {string} [locale='en']\n * @returns {string} a localized amount, or '' when there is nothing to state\n */\nfunction formatSpendAmount(spendNeeded, locale = 'en') {\n  if (spendNeeded === null || spendNeeded === undefined) return '';\n  return formatLoyaltyCurrency(Math.max(spendNeeded, 0), locale);\n}\n\n/**\n * Picks which spend message the profile header should show.\n *\n * Showing the amount is the point \u2014 a member asked why theirs was missing \u2014 but\n * a member who has already met their threshold has 0 left to spend, and\n * \"Spend $0.00 by Dec 14 to maintain your level status!\" reads worse than the\n * generic fallback it replaced. Zero (or an overshoot) is its own state: they\n * have qualified, and the only useful fact left is the date it holds until.\n *\n * @param {number|null|undefined} spendNeeded resolved amount, or null when absent\n * @param {{ isMaintenance?: boolean, hasExpiry?: boolean }} context\n * @returns {'qualified'|'maintain'|'levelUp'|'fallback'}\n */\nfunction spendMessageVariant(\n  spendNeeded,\n  { isMaintenance = false, hasExpiry = false } = {},\n) {\n  // Every non-fallback message names a date, so no date means no message.\n  if (spendNeeded === null || spendNeeded === undefined || !hasExpiry) return 'fallback';\n  if (spendNeeded <= 0) return 'qualified';\n  return isMaintenance ? 'maintain' : 'levelUp';\n}\n\n/**\n * Parses a loyalty timestamp into a Date representing the UTC calendar day.\n *\n * @param {number|string|null|undefined} timestamp Unix seconds or ISO / YYYY-MM-DD\n * @returns {Date|null}\n */\nfunction toUtcCalendarDate(timestamp) {\n  if (timestamp == null || timestamp === '') return null;\n\n  if (typeof timestamp === 'string') {\n    const ymd = timestamp.match(/^(\\d{4})-(\\d{2})-(\\d{2})/);\n    if (ymd) {\n      return new Date(Date.UTC(Number(ymd[1]), Number(ymd[2]) - 1, Number(ymd[3])));\n    }\n    const parsed = new Date(timestamp);\n    if (Number.isNaN(parsed.getTime())) return null;\n    return new Date(Date.UTC(parsed.getUTCFullYear(), parsed.getUTCMonth(), parsed.getUTCDate()));\n  }\n\n  const numeric = Number(timestamp);\n  if (!Number.isFinite(numeric)) return null;\n  // Heuristic: values under 1e12 are Unix seconds.\n  const ms = numeric < 1e12 ? numeric * 1000 : numeric;\n  const parsed = new Date(ms);\n  if (Number.isNaN(parsed.getTime())) return null;\n  return new Date(Date.UTC(parsed.getUTCFullYear(), parsed.getUTCMonth(), parsed.getUTCDate()));\n}\n\n/**\n * Formats a loyalty date using UTC calendar parts so expiry is not off by one day.\n *\n * @param {number|string|null|undefined} timestamp\n * @param {string} [locale='en']\n * @param {Intl.DateTimeFormatOptions} [options]\n * @returns {string}\n */\nfunction formatLoyaltyDate(\n  timestamp,\n  locale = 'en',\n  options = { year: 'numeric', month: 'long', day: 'numeric' },\n) {\n  const date = toUtcCalendarDate(timestamp);\n  if (!date) return '';\n  return date.toLocaleDateString(locale, { ...options, timeZone: 'UTC' });\n}\n\n/**\n * History table date as M/D/YYYY in UTC (matches live loyalty dashboard).\n *\n * @param {number|string|null|undefined} timestamp\n * @returns {string}
1\n */\nfunction formatHistoryDateUtc(timestamp) {\n  const date = toUtcCalendarDate(timestamp);\n  if (!date) return '';\n  const month = date.getUTCMonth() + 1;\n  const day = date.getUTCDate();\n  const year = date.getUTCFullYear();\n  return `${month}/${day}/${year}`;\n}\n\n/**\n * Whether a history row falls within the last N days (client-side filter).\n *\n * @param {number|string|null|undefined} timestamp\n * @param {string|number} daysFilter 'all' | 30 | 90 | 180 | 270 | 365\n * @returns {boolean}\n */\nfunction isWithinDays(timestamp, daysFilter) {\n  if (daysFilter === 'all' || daysFilter == null || daysFilter === '') return true;\n  const days = Number(daysFilter);\n  if (!Number.isFinite(days) || days <= 0) return true;\n\n  const date = toUtcCalendarDate(timestamp);\n  if (!date) return false;\n\n  const cutoff = Date.now() - days * 24 * 60 * 60 * 1000;\n  return date.getTime() >= cutoff;\n}\n\n/**\n * Resolves a CrowdTwist activity web URL from additional data points.\n * Merchants configure `weburlen` / `weburlfr` / `weburlus` (and casing variants).\n *\n * @param {object|null|undefined} activity\n * @param {string}
1 [weburlSuffix='en'] `en` | `fr` | `us`\n * @returns {string}\n */\nfunction getActivityWebUrl(activity, weburlSuffix = 'en') {\n  if (!activity || typeof activity !== 'object') return '';\n\n  const suffix = String(weburlSuffix || 'en').toLowerCase();\n  const bags = [\n    activity.additionalData,\n    activity.extraData,\n    activity.userData,\n    activity.data,\n    activity.customData,\n    activity,\n  ].filter((bag) => bag && typeof bag === 'object' && !Array.isArray(bag));\n\n  const keyVariants = [\n    `weburl${suffix}`,\n    `webUrl${suffix}`,\n    `web_url_${suffix}`,\n    `WEBURL${suffix.toUpperCase()}`,\n  ];\n\n  for (const bag of bags) {\n    for (const key of keyVariants) {\n      const value = bag[key];\n      if (typeof value === 'string' && value.trim()) return value.trim();\n    }\n  }\n\n  for (const bag of bags) {\n    for (const key of ['url', 'weburl', 'webUrl', 'web_url', 'link']) {\n      const value = bag[key];\n      if (typeof value === 'string' && value.trim()) return value.trim();\n    }\n  }\n\n  return '';\n}\n\n/**\n * \"Purchase Details\" text for one purchase line item.\n *\n * The SKU belongs in the details column beside the order number it breaks\n * down, not in the Date column. Quantity comes along because it has nowhere\n * else to sit once the SKU takes that column, and it is named only when it is\n * more than one \u2014 \"x 1\" on every row is noise, not information.\n *\n * @param {object|null|undefined} item\n * @returns {string} empty when the item names nothing\n */\nfunction formatLineItemDetails(item) {\n  if (!item || typeof item !== 'object') return '';\n\n  const label = item.sku || item.name || item.title || '';\n  if (!label) return '';\n\n  const quantity = Number(item.quantity);\n  return Number.isFinite(quantity) && quantity > 1 ? `${label} \\u00d7 ${quantity}` : label;\n}\n\n/**\n * Signed points for one purchase line item, for the expanded SKU rows.\n *\n * An item that genuinely earned nothing reads \"+0\" \u2014 an excluded item is a real\n * case and the shopper should see it \u2014 while an item whose points field is\n * absent stays blank rather than claiming zero. Same false-zero distinction as\n * neededPointsForReward and resolveSpendNeeded.\n *\n * @param {object|null|undefined} item\n * @returns {string} signed points, or '' when the item carries none\n */\nfunction formatLineItemPoints(item) {\n  if (!item || typeof item !== 'object') return '';\n\n  const raw = item.points ?? item.pointsAwarded ?? item.points_awarded ?? item.pointsEarned;\n  if (raw == null || raw === '') return '';\n\n  const numeric = Number(raw);\n  if (!Number.isFinite(numeric)) return String(raw);\n  return numeric >= 0 ? `+${numeric}` : String(numeric);\n}\n\n/**\n * Points value for a ways-to-earn activity (defensive field names).\n *\n * @param {object|null|undefined} activity\n * @returns {number|string|null}\n */\nfunction getActivityPoints(activity) {\n  if (!activity || typeof activity !== 'object') return null;\n  const raw =\n    activity.points ??\n    activity.numPoints ??\n    activity.pointValue ??\n    activity.pointsAwarded ??\n    activity.bonusPoints ??\n    activity.pointAmount;\n  if (raw == null || raw === '') return null;\n  if (typeof raw === 'number') return raw;\n  const asNumber = Number(raw);\n  return Number.isFinite(asNumber) ? asNumber : String(raw);\n}\n\n/**\n * First usable timestamp from common CrowdTwist / proxy field names.\n *\n * @param {object|null|undefined} row\n * @param {string[]} keys\n * @returns {number|string|null|undefined}\n */\nfunction pickHistoryDate(row, keys = []) {\n  if (!row || typeof row !== 'object') return null;\n  for (const key of keys) {\n    if (row[key] != null && row[key] !== '') return row[key];\n  }\n  return null;\n}\n\n/**\n * Order number for purchase history \u2014 never fall back to a generic `id`.\n *\n * @param {object|null|undefined} purchase\n * @returns {string}\n */\nfunction getPurchaseOrderNumber(purchase) {\n  if (!purchase || typeof purchase !== 'object') return '';\n  const raw =\n    purchase.orderNumber ||\n    purchase.order_number ||\n    purchase.orderId ||\n    purchase.order_id ||\n    purchase.details ||\n    purchase.title ||\n    '';\n  return raw != null && raw !== '' ? String(raw) : '';\n}\n\n/**\n * Whether a ways-to-earn catalog activity should be clickable.\n * Catalog lists never include history rows; still respect explicit completed/blocked flags.\n *\n * @param {object|null|undefined} activity\n * @param {string} activityUrl\n * @param {(url: unknown) => boolean} isSafeUrlFn\n * @returns {boolean}\n */\nfunction isActivityActionable(activity, activityUrl, isSafeUrlFn) {\n  if (!activity || typeof isSafeUrlFn !== 'function') return false;\n  if (!isSafeUrlFn(activityUrl)) return false;\n  if (activity.canEarnAgain === false) return false;\n  if (activity.completed === true || activity.isCompleted === true) return false;\n  return true;\n}\n\n/**\n * Available ways-to-earn activities from the summary payload.\n * Never falls back to history (completed) activities.\n *\n * @param {object|null|undefined} data\n * @returns {Array}
1\n */\nfunction getWaysToEarnActivities(data) {\n  if (!data || typeof data !== 'object') return [];\n  if (Array.isArray(data.waysToEarn) && data.waysToEarn.length) return data.waysToEarn;\n  if (Array.isArray(data.activities) && data.activities.length) return data.activities;\n  if (Array.isArray(data.availableActivities) && data.availableActivities.length) {\n    return data.availableActivities;\n  }\n  return [];\n}\n\n/**\n * Interpolates `{{key}}` placeholders in a Liquid `t` template string.\n *\n * @param {string} template\n * @param {Record<string, string|number>} vars\n * @returns {string}\n */\nfunction fillTemplate(template, vars = {}) {\n  if (!template) return '';\n  return String(template).replace(/\\{\\{\\s*(\\w+)\\s*\\}\\}/g, (_, key) =>\n    vars[key] != null ? String(vars[key]) : '',\n  );\n}\n\n\n//# sourceURL=webpack://ardene-theme/./src/snippets/braze/loyalty.js?\n}")},
1"./src/snippets/braze/safe-url.js":function(__unused_webpack___webpack_module__,__webpack_exports__,__webpack_require__){eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */   isSafeUrl: () => (/* binding */ isSafeUrl)\n/* harmony export */ });\n/**\n * URL scheme validation for Braze-authored content.\n *\n * Card and banner URLs are authored in the Braze dashboard, not by the theme.\n * Assigning one straight to `element.href` lets a `javascript:` URL execute in\n * the storefront origin the moment a shopper clicks the card. `data:` and\n * `vbscript:` are the same class of problem.\n *\n * Braze's own `allowUserSuppliedJavascript` initialization flag exists to stop\n * exactly this, which is the clearest signal that dashboard-authored content is\n * not to be trusted with a raw href.\n */\n\n/** Absolute http(s) \u2014 the only remote schemes a card may link to. */\nconst ABSOLUTE_HTTP = /^https?:\\/\\//i;\n\n/**\n * Returns true when a URL is safe to assign to an href or src.\n *\n * Accepts absolute http(s) and root-relative paths. Protocol-relative URLs\n * (`//host`) are rejected: they are absolute links to another origin wearing a\n * relative disguise.\n *\n * @param {unknown} url\n * @returns {boolean}\n */\nfunction isSafeUrl(url) {\n  if (typeof url !== 'string') return false;\n\n  const trimmed = url.trim();\n  if (!trimmed) return false;\n\n  if (trimmed.startsWith('//')) return false;\n  if (trimmed.startsWith('/')) return true;\n\n  return ABSOLUTE_HTTP.test(trimmed);\n}\n\n\n//# sourceURL=webpack://ardene-theme/./src/snippets/braze/safe-url.js?\n}")},
1"./src/snippets/snippet.braze-inbox.css":function(__unused_webpack_module,__webpack_exports__,__webpack_require__){eval("{__webpack_require__.r(__webpack_exports__);\n// extracted by mini-css-extract-plugin\n\n\n//# sourceURL=webpack://ardene-theme/./src/snippets/snippet.braze-inbox.css?\n}")},
1"./src/snippets/snippet.braze-inbox.js":function(__unused_webpack___webpack_module__,__webpack_exports__,__webpack_require__){eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _braze_inbox_profile_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./braze/inbox-profile.js */ \"./src/snippets/braze/inbox-profile.js\");\n/* harmony import */ var _braze_inbox_offers_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./braze/inbox-offers.js */ \"./src/snippets/braze/inbox-offers.js\");\n/* harmony import */ var _braze_inbox_ways_to_earn_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./braze/inbox-ways-to-earn.js */ \"./src/snippets/braze/inbox-ways-to-earn.js\");\n/* harmony import */ var _braze_inbox_history_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./braze/inbox-history.js */ \"./src/snippets/braze/inbox-history.js\");\n\n\n\n\n\nconst FEED_SELECTOR = '[data-braze-inbox-feed]';\nconst EMPTY_SELECTOR = '[data-braze-inbox-empty]';\nconst TAB_SELECTOR = '[data-braze-inbox-tab]';\nconst BADGE_SELECTOR = '[data-braze-inbox-count]';\nconst ACTIVE_TAB_CLASS = 'braze-inbox__tab--active';\n\nlet currentCards = [];\nlet activeCategory = 'all';\nlet activeObserver = null;\n\n/** @returns {{ logContentCardImpressions: Function, logContentCardClick: Function, logCardDismissal: Function }} */\nfunction sdk() {\n  return window.BRAZE_SDK ?? {};\n}\n\n/**\n * Returns inbox cards filtered from the full card pool.\n * @param {Array} cards\n * @returns {Array}\n */\nfunction getInboxCards(cards) {\n  return cards.filter(c => {\n    const extras = c.extras ?? {};\n    return extras.placement === 'inbox' && !c.dismissed && !c.isControl;\n  });\n}\n\n/**\n * Returns cards matching the active category tab, or all inbox cards if \"all\".\n * @param {Array} inboxCards\n * @returns {Array}\n */\nfunction filterByCategory(inboxCards) {\n  if (activeCategory === 'all') return inboxCards;\n  return inboxCards.filter(c => {\n    const extras = c.extras ?? {};\n    return extras.category === activeCategory;\n  });\n}\n\n/**\n * Updates all badge elements with the unviewed count.\n * @param {Array} inboxCards\n */\nfunction updateBadge(inboxCards) {\n  const unviewed = inboxCards.filter(c => !c.viewed).length;\n  document.querySelectorAll(BADGE_SELECTOR).forEach(el => {\n    el.textContent = unviewed > 0 ? String(unviewed) : '';\n    el.style.display = unviewed > 0 ? '' : 'none';\n  });\n}\n\n/**\n * Builds a single inbox card DOM element.\n * @param {Object} card\n * @returns {HTMLElement}\n */\nfunction buildInboxCard(card) {\n  const extras = card.extras ?? {};\n  const article = document.createElement('article');\n  article.className = 'braze-inbox__card';\n  if (!card.viewed) {\n    article.classList.add('braze-inbox__card--unread');\n  }\n  article.dataset.brazeCardId = card.id;\n\n  if (card.imageUrl) {\n    const img = document.createElement('img');\n    img.src = card.imageUrl;\n    img.alt = card.title || '';\n    img.className = 'braze-inbox__card-image';\n    img.loading = 'lazy';\n    article.appendChild(img);\n  }\n\n  const body = document.createElement('div');\n  body.className = 'braze-inbox__card-body';\n\n  if (card.title) {\n    const h3 = document.createElement('h3');\n    h3.className = 'braze-inbox__card-title';\n    h3.textContent = card.title;\n    body.appendChild(h3);\n  }\n\n  if (card.description) {\n    const p = document.createElement('p');\n    p.className = 'braze-inbox__card-description';\n    p.textContent = card.description;\n    body.appendChild(p);\n  }\n\n  if (extras.category) {\n    const tag = document.createElement('span');\n    tag.className = 'braze-inbox__card-tag';\n    tag.textContent = extras.category;\n    body.appendChild(tag);\n  }\n\n  article.appendChild(body);\n\n  if (card.dismissible) {\n    const btn = document.createElement('button');\n    btn.type = 'button';\n    btn.className = 'braze-inbox__card-dismiss';\n    btn.setAttribute('aria-label', 'Dismiss');\n    btn.textContent = '\\u00d7';\n    btn.addEventListener('click', (e) => {\n      e.preventDefault();\n      e.stopPropagation();
1\n      sdk().logCardDismissal?.(card);\n      article.remove();\n      currentCards = currentCards.filter(c => c.id !== card.id);\n      const inboxCards = getInboxCards(currentCards);\n      updateBadge(inboxCards);\n      if (filterByCategory(inboxCards).length === 0) {\n        showEmpty(true);\n      }\n    });\n    article.appendChild(btn);\n  }\n\n  // Card click \u2014 log and navigate (only allow http/https URLs)\n  if (card.url && /^https?:\\/\\//.test(card.url)) {\n    article.style.cursor = 'pointer';\n    article.addEventListener('click', () => {\n      sdk().logContentCardClick?.(card);\n      window.location.href = card.url;\n    });\n  }\n\n  return article;\n}\n\n/**\n * Shows or hides the empty state.\n * @param {boolean} show\n */\nfunction showEmpty(show) {\n  const emptyEl = document.querySelector(EMPTY_SELECTOR);\n  if (emptyEl) {\n    emptyEl.classList.toggle('hidden', !show);\n  }\n}\n\n/**\n * Renders the inbox feed with the current filter state.\n */\nfunction renderInbox() {\n  const feed = document.querySelector(FEED_SELECTOR);\n  if (!feed) return;\n\n  // Disconnect previous observer before re-rendering\n  if (activeObserver) {\n    activeObserver.disconnect();\n    activeObserver = null;\n  }\n\n  // Keep the empty-state element, clear rendered cards\n  feed.querySelectorAll('.braze-inbox__card').forEach(el => el.remove());\n\n  const inboxCards = getInboxCards(currentCards);\n  const visible = filterByCategory(inboxCards);\n\n  updateBadge(inboxCards);\n\n  if (visible.length === 0) {\n    showEmpty(true);\n    return;\n  }\n\n  showEmpty(false);\n\n  activeObserver = new IntersectionObserver((entries) => {\n    entries.forEach(entry => {\n      if (!entry.isIntersecting) return;\n      const cardId = entry.target.dataset.brazeCardId;\n      const card = inboxCards.find(c => c.id === cardId);\n      if (card) {\n        sdk().logContentCardImpressions?.([card]);\n        activeObserver.unobserve(entry.target);\n      }\n    });\n  }, { threshold: 0.5 });\n\n  visible.forEach(card => {\n    const el = buildInboxCard(card);\n    activeObserver.observe(el);\n    feed.appendChild(el);\n  });\n}\n\n// --- Tab handling ---\n\nconst PANEL_ID = 'braze-inbox-panel';\nconst REWARDS_PANEL_ID = 'braze-inbox-rewards-panel';\nconst REWARDS_PANEL_ATTR = 'data-active-panel';\n\n/**\n * Shows the Braze feed panel and hides the rewards panel.\n */\nfunction showBrazePanel() {\n  const brazePanel = document.getElementById(PANEL_ID);\n  const rewardsPanel = document.getElementById(REWARDS_PANEL_ID);\n  const wrapper = brazePanel?.closest('.braze-inbox__panels');\n\n  if (brazePanel) brazePanel.classList.remove('hidden');\n  if (rewardsPanel) rewardsPanel.classList.add('hidden');\n  if (wrapper) wrapper.setAttribute(REWARDS_PANEL_ATTR, 'braze');\n}\n\n/**\n * Shows the rewards widget panel and hides the Braze feed panel.\n */\nfunction showRewardsPanel() {\n  const brazePanel = document.getElementById(PANEL_ID);\n  const rewardsPanel = document.getElementById(REWARDS_PANEL_ID);\n  const wrapper = brazePanel?.closest('.braze-inbox__panels');\n\n  if (brazePanel) brazePanel.classList.add('hidden');\n  if (rewardsPanel) rewardsPanel.classList.remove('hidden');\n  if (wrapper) wrapper.setAttribute(REWARDS_PANEL_ATTR, 'rewards');\n\n  document.dispatchEvent(new CustomEvent('rewards-widget:activate'));\n}\n\nfunction activateTab(tab) {\n  const isRewardsTab = tab.dataset.rewardsTab === 'rewards';\n\n  // Update tab button states\n  document.querySelectorAll(TAB_SELECTOR).forEach(t => {\n    t.classList.remove(ACTIVE_TAB_CLASS);\n    t.setAttribute('aria-selected', 'false');\n    t.setAttribute('tabindex', '-1');\n  });\n  tab.classList.add(ACTIVE_TAB_CLASS);\n  tab.setAttribute('aria-selected', 'true');\n  tab.setAttribute('tabindex', '0');\n\n  if (isRewardsTab) {\n    showRewardsPanel();\n    // Do not update activeCategory or call renderInbox \u2014 rewards widget handles its own panel\n    return;\n  }\n\n  // Non-rewards tab: show Braze feed and filter by category\n  showBrazePanel();\n  activeCategory = tab.dataset.brazeInboxTab;\n\n  const panel = document.getElementById(PANEL_ID);\n  if (panel) {\n    panel.setAttribute('aria-labelledby', tab.id);\n  }\n\n  renderInbox();\n}\n\ndocument.addEventListener('click', (e) => {\n  const tab = e.target.closest(TAB_SELECTOR);\n  if (!tab) return;\n  activateTab(tab);\n});\n\ndocument.addEventListener('keydown', (e) => {\n  const tab = e.target.closest(TAB_SELECTOR);\n  if (!tab) return;\n\n  const tabs = Array.from(document.querySelectorAll(TAB_SELECTOR));\n  const idx = tabs.indexOf(tab);\n  if (idx === -1) return;\n\n  let next;\n  if (e.key === 'ArrowRight') {\n    next = tabs[(idx + 1) % tabs.length];\n  } else if (e.key === 'ArrowLeft') {\n    next = tabs[(idx - 1 + tabs.length) % tabs.length];\n  } else if (e.key === 'Home') {\n    next = tabs[0];\n  } else if (e.key === 'End') {\n    next = tabs[tabs.length - 1];\n  }\n\n  if (next) {\n    e.preventDefault();\n    next.focus();\n    activateTab(next);\n  }\n});\n\n// --- Listen for card updates from snippet.braze.js ---\ndocument.addEventListener('braze:content-cards-updated', 
1(e) => {\n  currentCards = e.detail?.cards ?? [];\n  renderInbox();\n});\n\n// --- Pick up cached cards if they arrived before this script loaded ---\nconst cached = sdk().getCachedContentCards?.();\nif (cached?.cards?.length) {\n  currentCards = cached.cards;\n  renderInbox();\n}\n\n// ========================================\n// Promotional Tabs (Not Logged In)\n// ========================================\n\nconst PROMO_TAB_SELECTOR = '[data-promo-tab]';\nconst PROMO_PANEL_SELECTOR = '[data-promo-panel]';\nconst PROMO_ACTIVE_TAB_CLASS = 'braze-inbox__tab--active';\n\n/**\n * Activates a promotional tab and shows its panel.\n * @param {HTMLElement} tab\n */\nfunction activatePromoTab(tab) {\n  const targetPanel = tab.dataset.promoTab;\n  \n  // Update all tab states\n  document.querySelectorAll(PROMO_TAB_SELECTOR).forEach(t => {\n    t.classList.remove(PROMO_ACTIVE_TAB_CLASS);\n    t.setAttribute('aria-selected', 'false');\n    t.setAttribute('tabindex', '-1');\n  });\n  \n  // Activate clicked tab\n  tab.classList.add(PROMO_ACTIVE_TAB_CLASS);\n  tab.setAttribute('aria-selected', 'true');\n  tab.setAttribute('tabindex', '0');\n  \n  // Hide all panels\n  document.querySelectorAll(PROMO_PANEL_SELECTOR).forEach(panel => {\n    panel.classList.add('hidden');\n  });\n  \n  // Show target panel\n  const panel = document.querySelector(`[data-
1promo-panel=\"${targetPanel}\"]`);\n  if (panel) {\n    panel.classList.remove('hidden');\n    // Scroll tab into view (with smooth snap behavior)\n    tab.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' });\n  }\n}\n\n// Click handler for promotional tabs\ndocument.addEventListener('click', (e) => {\n  const promoTab = e.target.closest(PROMO_TAB_SELECTOR);\n  if (!promoTab) return;\n  activatePromoTab(promoTab);\n});\n\n// Keyboard navigation for promotional tabs\ndocument.addEventListener('keydown', (e) => {\n  const promoTab = e.target.closest(PROMO_TAB_SELECTOR);\n  if (!promoTab) return;\n  \n  const promoTabs = Array.from(document.querySelectorAll(PROMO_TAB_SELECTOR));\n  const idx = promoTabs.indexOf(promoTab);\n  if (idx === -1) return;\n  \n  let next;\n  if (e.key === 'ArrowRight') {\n    next = promoTabs[(idx + 1) % promoTabs.length];\n  } else if (e.key === 'ArrowLeft') {\n    next = promoTabs[(idx - 1 + promoTabs.length) % promoTabs.length];\n  } else if (e.key === 'Home') {\n    next = promoTabs[0];\n  } else if (e.key === 'End') {\n    next = promoTabs[promoTabs.length - 1];\n  }\n  \n  if (next) {\n    e.preventDefault();\n    next.focus();\n    activatePromoTab(next);\n  }\n});\n\n// ========================================\n// Perks Tab - Level Switching\n// ========================================\n\nconst LEVEL_TAB_SELECTOR = '[data-level-tab]';\nconst LEVEL_VALUE_SELECTOR = '[data-level-value]';\nconst LEVEL_DISPLAY_SELECTOR = '[data-level-display]';\n\n/**\n * Activates a level tab and shows corresponding values in the table.\n * @param {HTMLElement} tab\n */\nfunction activateLevelTab(tab) {\n  const targetLevel = tab.dataset.levelTab;\n  const perksRoot = tab.closest('.braze-perks') || document;\n\n  // Update all tab states within this perks table\n  perksRoot.querySelectorAll(LEVEL_TAB_SELECTOR).forEach(t => {\n    t.setAttribute('aria-selected', 'false');\n    t.setAttribute('tabindex', '-1');\n    t.setAttribute('data-active', 'false');\n  });\n\n  tab.setAttribute('aria-selected', 'true');\n  tab.setAttribute('tabindex', '0');\n  tab.setAttribute('data-active', 'true');\n  perksRoot.setAttribute('data-active-level', targetLevel);\n\n  // Drive visibility with the HTML `hidden` attribute (values use `hidden`, not `.hidden`)\n  perksRoot.querySelectorAll(LEVEL_VALUE_SELECTOR).forEach(val => {\n    const isActive = val.dataset.levelValue === targetLevel;\n    val.hidden = !isActive;\n    val.setAttribute('data-active', isActive ? 'true' : 'false');\n  });\n\n  const levelDisplay = perksRoot.querySelector(LEVEL_DISPLAY_SELECTOR);\n  if (levelDisplay) {\n    levelDisplay.textContent = tab.textContent.trim();\n  }\n}\n\n// Click handler for level tabs\ndocument.addEventListener('click', (e) => {\n  const levelTab = e.target.closest(LEVEL_TAB_SELECTOR);\n  if (!levelTab) return;\n  activateLevelTab(levelTab);\n});\n\n// Keyboard navigation for level tabs\ndocument.addEventListener('keydown', (e) => {\n  const levelTab = e.target.closest(LEVEL_TAB_SELECTOR);\n  if (!levelTab) return;\n  \n  const levelTabs = Array.from(document.querySelectorAll(LEVEL_TAB_SELECTOR));\n  const idx = levelTabs.indexOf(levelTab);\n  if (idx === -1) return;\n  \n  let next;\n  if (e.key === 'ArrowRight') {\n    next = levelTabs[(idx + 1) % levelTabs.length];\n  } else if (e.key === 'ArrowLeft') {\n    next = levelTabs[(idx - 1 + levelTabs.length) % levelTabs.length];\n  } else if (e.key === 'Home') {\n    next = levelTabs[0];\n  } else if (e.key === 'End') {\n    next = levelTabs[levelTabs.length - 1];\n  }\n  \n  if (next) {\n    e.preventDefault();\n    next.focus();\n    activateLevelTab(next);\n  }\n});\n\n// ========================================\n// FAQ Tab - Accordion Toggle\n// ========================================\n\nconst FAQ_TOGGLE_SELECTOR = '[data-faq-toggle]';\nconst FAQ_CONTENT_SELECTOR = '[data-faq-content]';\n\n/**\n * Toggles FAQ accordion item open/closed.\n * @param {HTMLElement} button\n */\nfunction toggleFaqItem(button) {\n  const isExpanded = button.getAttribute('aria-expanded') === 'true';\n  const content = button.nextElementSibling;\n  \n  if (!content || !content.matches(FAQ_CONTENT_SELECTOR)) return;\n  \n  button.setAttribute('aria-expanded', String(!isExpanded));\n  content.hidden = isExpanded;\n}\n\n// Click handler for FAQ toggles\ndocument.addEventListener('click', (e) => {\n  const faqToggle = e.target.closest(FAQ_TOGGLE_SELECTOR);\n  if (!faqToggle) return;\n  toggleFaqItem(faqToggle);\n});\n\n// Keyboard handler for FAQ toggles (Enter/Space)\ndocument.addEventListener('keydown', (e) => {\n  if (e.key !== 'Enter' && e.key !== ' ') return;\n  \n  const faqToggle = e.target.closest(FAQ_TOGGLE_SELECTOR);\n  if (!faqToggle) return;\n  \n  e.preventDefault();\n  toggleFaqItem(faqToggle);\n});\n\n\n// ========================================\n// Logged-In User Experience\n// ========================================\n\nconst LOGGED_IN_TAB_SELECTOR = '[data-logged-in-tab]';\nconst LOGGED_IN_PANEL_SELECTOR = '[data-logged-in-panel]';\nconst REWARDS_PROFILE_SELECTOR = '[data-rewards-profile]';\nconst REWARDS_OFFERS_SELECTOR = '[data-rewards-offers]';\n\n/**\n * Activates a logged-in tab and shows its panel.\n * @param {HTMLElement} tabToActivate\n */\nfunction activateLoggedInTab(tabToActivate) {\n  const tabs = document.querySelectorAll(LOGGED_IN_TAB_SELECTOR);\n  const panels = document.querySelectorAll(LOGGED_IN_PANEL_SELECTOR);\n  const targetId = tabToActivate.dataset.loggedInTab;
1\n\n  tabs.forEach((tab) => {\n    const isActive = tab === tabToActivate;\n    tab.classList.toggle('braze-inbox__tab--active', isActive);\n    tab.setAttribute('aria-selected', String(isActive));\n    tab.setAttribute('tabindex', isActive ? '0' : '-1');\n  });\n\n  panels.forEach((panel) => {\n    const shouldShow = panel.dataset.loggedInPanel === targetId;\n    panel.hidden = !shouldShow;\n    panel.classList.toggle('hidden', !shouldShow);\n  });\n\n  tabToActivate.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' });\n}\n\ndocument.addEventListener('click', (e) => {\n  const tab = e.target.closest(LOGGED_IN_TAB_SELECTOR);\n  if (tab) {\n    e.preventDefault();\n    activateLoggedInTab(tab);\n  }\n});\n\ndocument.addEventListener('keydown', (e) => {\n  const tab = e.target.closest(LOGGED_IN_TAB_SELECTOR);\n  if (!tab) return;\n\n  const tabs = Array.from(document.querySelectorAll(LOGGED_IN_TAB_SELECTOR));\n  const currentIndex = tabs.indexOf(tab);\n  let nextIndex;\n\n  switch (e.key) {\n    case 'ArrowLeft':\n      nextIndex = currentIndex > 0 ? currentIndex - 1 : tabs.length - 1;\n      tabs[nextIndex].focus();\n      activateLoggedInTab(tabs[nextIndex]);\n      e.preventDefault();\n      break;\n    case 'ArrowRight':\n      nextIndex = currentIndex < tabs.length - 1 ? currentIndex + 1 : 0;\n      tabs[nextIndex].focus();\n      activateLoggedInTab(tabs[nextIndex]);\n      e.preventDefault();\n      break;\n    case 'Home':\n      tabs[0].focus();\n      activateLoggedInTab(tabs[0]);\n      e.preventDefault();\n      break;\n    case 'End':\n      tabs[tabs.length - 1].focus();\n      activateLoggedInTab(tabs[tabs.length - 1]);\n      e.preventDefault();\n      break;\n  }\n});\n\n(0,_braze_inbox_offers_js__WEBPACK_IMPORTED_MODULE_1__.initOfferDetailsListeners)();\n(0,_braze_inbox_history_js__WEBPACK_IMPORTED_MODULE_3__.initHistoryFilterListeners)();\n\ndocument.addEventListener('DOMContentLoaded', () => {\n  const profileContainer = document.querySelector(REWARDS_PROFILE_SELECTOR);\n  const offersContainer = document.querySelector(REWARDS_OFFERS_SELECTOR);\n  const waysToEarnContainer = document.querySelector(_braze_inbox_ways_to_earn_js__WEBPACK_IMPORTED_MODULE_2__.WAYS_TO_EARN_SELECTOR);\n  const historyContainer = document.querySelector(_braze_inbox_history_js__WEBPACK_IMPORTED_MODULE_3__.HISTORY_SELECTOR);\n\n  if (!profileContainer || !offersContainer) return;\n\n  const customerId = profileContainer.dataset.customerId;\n  const locale = profileContainer.dataset.locale || 'en';\n  if (!customerId) return;\n\n  profileContainer.setAttribute('data-state', 'loading');\n  const profileLoading = profileContainer.querySelector('[data-profile-state=\"loading\"]');\n  if (profileLoading) profileLoading.hidden = false;\n\n  offersContainer.setAttribute('data-state', 'loading');\n  const offersLoading = offersContainer.querySelector('[data-offers-state=\"loading\"]');\n  if (offersLoading) offersLoading.hidden = false;\n\n  if (waysToEarnContainer) {\n    waysToEarnContainer.setAttribute('data-state', 'loading');\n    const wLoading = waysToEarnContainer.querySelector('[data-ways-to-earn-state=\"loading\"]');\n    if (wLoading) wLoading.hidden = false;\n  }\n\n  if (historyContainer) {\n    historyContainer.setAttribute('data-state', 'loading');\n    historyContainer.querySelectorAll('[data-history-state=\"loading\"]').forEach((el) => {\n      el.hidden = false;\n    });\n  }\n\n  (0,_braze_inbox_ways_to_earn_js__WEBPACK_IMPORTED_MODULE_2__.fetchRewardsData)(customerId, locale).then((data) => {\n    if (data) {\n      (0,_braze_inbox_profile_js__WEBPACK_IMPORTED_MODULE_0__.renderProfile)(profileContainer, data, locale);\n      (0,_braze_inbox_offers_js__WEBPACK_IMPORTED_MODULE_1__.renderOffers)(offersContainer, data, locale);\n      if (waysToEarnContainer) (0,_braze_inbox_ways_to_earn_js__WEBPACK_IMPORTED_MODULE_2__.renderWaysToEarn)(waysToEarnContainer, data, locale);\n      if (historyContainer) (0,_braze_inbox_history_js__WEBPACK_IMPORTED_MODULE_3__.renderHistory)(historyContainer, data, locale);\n      return;\n    }\n\n    profileContainer.setAttribute('data-state', 'error');\n    offersContainer.setAttribute('data-state', 'error');\n    if (waysToEarnContainer) waysToEarnContainer.setAttribute('data-state', 'error');\n    if (historyContainer) historyContainer.setAttribute('data-state', 'error');\n  });\n});\n\n\n//# sourceURL=webpack://ardene-theme/./src/snippets/snippet.braze-inbox.js?\n}")}
1},__webpack_module_cache__={};function __webpack_require__(moduleId){var cachedModule=__webpack_module_cache__[moduleId];if(cachedModule!==void 0)return cachedModule.exports;var module=__webpack_module_cache__[moduleId]={exports:{}};return __webpack_modules__[moduleId](module,module.exports,__webpack_require__),module.exports}(function(){__webpack_require__.d=function(exports,definition){for(var key in definition)__webpack_require__.o(definition,key)&&!__webpack_require__.o(exports,key)&&Object.defineProperty(exports,key,{enumerable:!0,get:definition[key]})}})(),(function(){__webpack_require__.o=function(obj,prop){return Object.prototype.hasOwnProperty.call(obj,prop)}})(),(function(){__webpack_require__.r=function(exports){typeof Symbol!="undefined"&&Symbol.toStringTag&&Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(exports,"__esModule",{value:!0})}})(),__webpack_require__("./src/snippets/snippet.braze-inbox.js");var __webpack_exports__=__webpack_require__("./src/snippets/snippet.braze-inbox.css")})();
2//# sourceMappingURL=/cdn/shop/t/239/assets/snippet.braze-inbox.js.map?v=70479139155257703581790728904

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.