Wishlist For Home & Furniture — JS APIs

Extend the standard wishlist page with multi-list navigation, quantity controls, item notes, and a running total for home and furniture stores.

Wishlist Page — Home & Furniture Extensions

Home and furniture shoppers typically curate separate wishlists by room — Living Room, Bedroom, Kitchen, and so on. This guide extends the base Wishlist Page implementation with features suited to that browsing pattern:

  • Visual lists landing view — a grid of the shopper's wishlists, each tile showing product image previews and an item count, so they can jump directly to a room-specific list
  • Back navigation — a button that returns the shopper to the lists grid from any open list
  • Running total — a live price total for the current list, displayed alongside the back button and updated whenever quantities change
  • Quantity controls (+ / buttons) on each product card
  • Item notes (a textarea per card) for room dimensions, colour notes, or special delivery instructions
  • Add All to Cart button that adds every in-stock item at its current quantity in a single request
  • Dual-view layout — a structured table (default) and a card grid the shopper can toggle between; both views stay in sync when qty or notes change

Quantity and note changes are persisted back to the Swym list via swat.updateListItem().


Visual Lists Landing View


Wishlist UI adapted for Home & Furniture with Running Total, Add All To Cart, Per-item Quantities and Notes (Table/Row View)


Wishlist UI adapted for Home & Furniture with Running Total, Add All To Cart, Per-item Quantities and Notes (Grid/Column View)

What Changes

Base guideHome & Furniture extension
LiquidStandard single-list structureFull multi-list page: .swym-page-header / .swym-header-controls wrapper in the header; #swym-view-toggle, #swym-add-all-btn, and #swym-share-btn grouped in the controls area; #swym-lists-grid (landing view); #swym-wishlist-bar (back button + running total); table structure (#swym-wishlist-table-wrapper / #swym-wishlist-tbody); card grid (#swym-wishlist-grid); both grid and table start hidden
Module-level varsproductJSONCache, shareButtonHandlerAdds currentView (persists table/grid choice across renders) and SS_ACTIVE_LID_KEY (sessionStorage key for the last-selected list ID)
renderProductsAsync; fetches Shopify product JSON; renders cards into a gridAlso accepts lid; caches product JSON by variant ID (productJSONCache); sets data-epi and data-price on each card; renders qty controls and a note textarea per card; simultaneously builds a structured table row in #swym-wishlist-tbody; calls setupViewToggle and recalcTotal when done
initWishlistPageFetches lists; renders the first (or previously-active) list; calls setupListSwitcher for multi-list support (a <select> dropdown)On load, reads SS_ACTIVE_LID_KEY and SS_PRODUCTS_KEY from sessionStorage; if a matching cached list is found, renders immediately before swat.fetchLists resolves. swat.fetchLists then always re-renders from the authoritative API response. For multi-list users with no remembered selection, calls renderListsGrid (a visual tile grid, replacing the dropdown approach); for a remembered selection, renders the list directly and calls setupBackToLists.
(new) renderListsGridRenders the lists landing view: a grid of clickable tiles, one per wishlist, each showing up to three product image thumbnails and an item count; selecting a tile stores the chosen lid in SS_ACTIVE_LID_KEY and calls renderProducts
(new) setupBackToListsShows a "Back to all Lists" button; clicking it clears SS_ACTIVE_LID_KEY and SS_PRODUCTS_KEY, then calls renderListsGrid
(new) recalcTotalReads data-price and the qty display from every card in #swym-wishlist-grid and writes a formatted total to #swym-wishlist-total; called after every render, qty change, and remove
(new) setupViewToggleShows or hides the Table / Grid toggle; wires button clicks to flip currentView and swap visibility between table and grid
(new) syncQtyAcrossViewsKeeps qty displays in sync when the shopper changes qty in one view, preventing table and grid from diverging
(new) syncNoteAcrossViewsSame for note textareas
(new) scheduleItemUpdateDebounced call to swat.updateListItem() to persist qty + note changes (600 ms)
(new) setupAddAllButtonShows/hides the Add All to Cart button; hidden when all items are out of stock
(new) addAllToCartReads data-epi + .swym-wishlist-qty-value from every card and POSTs all in-stock items to /cart/add.js in one request

The share modal, save nudge, login nudge, and openShareModal are unchanged from the base guide.

Cache is a UX optimisation, not a data source. initWishlistPage renders immediately from sessionStorage if a matching cached list is found, then swat.fetchLists always re-renders from the authoritative API response. The cached render does not call setWishlistTitle or setupBackToLists -- both appear once the live render completes.

Wiring setupAddAllButton into initWishlistPage

If you are copying the Complete Implementation directly, setupAddAllButton is already wired at every call site — this section is a reference for integrating piecemeal into an existing custom build.

Call setupAddAllButton immediately after renderProducts in each place renderProducts is called — the initial fetchLists callback, the sw:removedfromwishlist listener, and the list tile click handler:

// In the fetchLists callbackFn, after renderProducts:
renderProducts(swat, lid, products, grid, countEl, empty);
setupShareButton(swat, lid, products.length);
setupAddAllButton(products);   // ← add this line

// In the sw:removedfromwishlist handler, after renderProducts:
renderProducts(swat, lid, updated, grid, countEl, empty);
setupShareButton(swat, lid, count);
setupAddAllButton(updated);    // ← add this line

Modified renderProducts

Replace renderProducts in assets/swym-wishlist-page.js with the version in the Complete Implementation below. The Home & Furniture version differs from the base guide in four ways:

  1. Product JSON cache — fetches are keyed by variant ID (product.epi) and stored in productJSONCache. Switching between wishlists re-uses cached data without hitting Shopify's product endpoint again.

  2. Dual-view output — each product renders into two places simultaneously: a card appended to #swym-wishlist-grid (same structure as the base guide, plus qty controls and a note textarea; carries data-epi and data-price), and a structured table row appended to #swym-wishlist-tbody (image, product name, variant title, qty stepper, live line price, note textarea, Add to Cart, Remove). Both elements carry data-epi so the sync helpers can find them by variant ID.

  3. Cross-view sync — qty and note change handlers call syncQtyAcrossViews and syncNoteAcrossViews so both the card and the table row always reflect the same value. The table row also updates the displayed line price (unit price × qty) as the shopper changes qty. Every qty change also calls recalcTotal to keep the running total current.

  4. View toggle and totalsetupViewToggle(true) is called after all rows are built to show the Table / Grid buttons and apply the current currentView state. recalcTotal() is called immediately after to initialize the running total in #swym-wishlist-total. When the wishlist is empty, setupViewToggle(false) hides the toggle and recalcTotal() clears the total.

Note on quantity: each per-card Add to Cart call reads the current qty display value rather than a hardcoded 1, so the quantity the shopper set carries through to the cart. addAllToCart does the same — reading from the DOM at click time.

New scheduleItemUpdate

Add this function anywhere in the IIFE, alongside the existing helpers. It debounces writes so rapid +/ taps or keystroke-by-keystroke note edits do not fire one API call per interaction. The complete function is in the Complete Implementation below.

Debounce interval: 600 ms works well for both quantity buttons and textarea input. Lower values increase API call volume; higher values make saves feel sluggish.

New setupViewToggle

Add this function alongside the existing helpers. renderProducts calls it at the end of every render pass — with true when products exist and false when the wishlist is empty.

When hasProducts is false, the toggle wrapper is hidden. When true, it reads currentView and applies visibility to either #swym-wishlist-table-wrapper or #swym-wishlist-grid, then re-wires the Table and Grid buttons using cloneNode to clear any listener attached in a previous render pass.

currentView is module-level — it persists across list switches and re-renders within the same page session, so the shopper's view choice is preserved when they navigate between lists.

New syncQtyAcrossViews and syncNoteAcrossViews

Add both functions alongside the existing helpers. They are called from the qty stepper and note input handlers in both the grid card and the table row.

Both functions query [data-epi] elements so they work regardless of which view is currently visible. The sourceEl guard prevents the triggering input from being overwritten with its own value, which would reset the cursor position in a textarea.

New renderListsGrid

Add this function alongside the existing helpers. initWishlistPage calls it when a multi-list user arrives with no remembered list selection.

The function hides the table wrapper, product grid, view toggle, Add All button, Share button, back button, and total display — everything that belongs to a single-list view — then builds a grid of <button> tiles in #swym-lists-grid. Each tile shows up to three product thumbnails (using the iu field from listcontents) and an item count. The first tile's first image loads with fetchpriority="high"; all others use loading="lazy".

Clicking a tile stores the selected lid in sessionStorage under SS_ACTIVE_LID_KEY, hides the lists grid, sets the page title to the list name, and calls renderProducts to load that list's items.

New setupBackToLists

Add this function alongside the existing helpers. initWishlistPage calls it (via renderListsGrid's tile click handler and directly after rendering a single list) for any multi-list user who is viewing a specific list.

The function shows #swym-back-to-lists and replaces the button node with cloneNode(true) to clear any listener from a previous render pass. Clicking the button removes SS_ACTIVE_LID_KEY and SS_PRODUCTS_KEY from sessionStorage and calls renderListsGrid, taking the shopper back to the landing view.

New recalcTotal

Add this function alongside the existing helpers. It is called at the end of every renderProducts pass, after every qty change (in both the card and the table row), and inside each card's remove handler.

The function queries every .swym-wishlist-card[data-price] inside #swym-wishlist-grid, multiplies each card's data-price by its current qty display value, and sums the results. If the total is greater than zero it writes a formatted string to #swym-wishlist-total and shows the element; otherwise it hides the element. Prices are stored in cents on data-price, matching Shopify's variant price format, so the same formatPrice helper used elsewhere formats them correctly.

Liquid

The Home & Furniture Liquid is a complete page template, not a small addition to the base guide's template. Copy sections/swym-wishlist-page.liquid from the Complete Implementation directly. Key structural differences from the base guide:

  • Header — a .swym-page-header flex wrapper separates .swym-header-title (the h1 and item count) from .swym-header-controls (the view toggle, Add All button, and Share button grouped together)
  • Lists landing view#swym-lists-grid renders the grid of list tiles; it starts hidden and is shown by renderListsGrid
  • Wishlist bar#swym-wishlist-bar sits below the header and holds #swym-back-to-lists (hidden until a list is selected) and #swym-wishlist-total (the running total, hidden until products are present)
  • Table and grid — both #swym-wishlist-table-wrapper and #swym-wishlist-grid start with display:none; setupViewToggle reveals whichever view matches currentView

New setupAddAllButton and addAllToCart

Add both functions to assets/swym-wishlist-page.js alongside the existing helpers. The complete functions are in the Complete Implementation below.

setupAddAllButton shows or hides #swym-add-all-btn based on whether at least one in-stock product exists. It uses cloneNode to replace the button and clear any listener from a previous render pass before attaching a fresh click handler.

addAllToCart reads data-epi and the qty display from every .swym-wishlist-card[data-epi] inside #swym-wishlist-grid at click time (not from the original products array), so any qty changes the shopper made are respected. It checks whether each card's cart button is disabled before including the item, skipping out-of-stock products silently.

Out-of-stock items are skipped. addAllToCart checks the per-card cart button's disabled state before including an item — so a wishlist with a mix of in-stock and out-of-stock products adds only the in-stock ones, without erroring on the rest.

Styling additions

The Complete Implementation includes a full CSS file. The selectors below cover the additions specific to this guide on top of the base guide's card and modal styles.

ElementPurpose
.swym-page-headerFlex wrapper for the header row — title on the left, controls on the right
.swym-header-titleLeft side of the header, holds the h1 and count
.swym-header-controlsRight side of the header, holds view toggle, Add All button, and Share button
#swym-wishlist-barBar below the header holding the back button (left) and running total (right)
#swym-back-to-lists"Back to all Lists" button; hidden when the shopper is on the landing view
#swym-wishlist-totalRunning price total for the current list; hidden when no priced items are present
#swym-lists-gridGrid of list tiles shown on the landing view
.swym-list-tileIndividual list tile button — card-like, with hover lift
.swym-list-tile-imagesImage strip inside the tile; shows up to 3 product thumbnails side-by-side
.swym-list-tile-images--1/2/3Modifier classes that control how many images fill the strip
.swym-list-tile-placeholderGradient fill shown when the list has no product images; flex container so the empty label centres
.swym-list-tile-empty-label"This list is empty!" label centred inside the placeholder
.swym-list-tile-infoName and item count below the image strip
.swym-list-tile-nameList name label
.swym-list-tile-countItem count label
#swym-add-all-btnAdd All to Cart button — hidden until 1+ in-stock items exist
#swym-view-toggleWrapper for the Table / Grid toggle buttons; hidden when wishlist is empty
.swym-view-btnTable and Grid toggle buttons
.swym-view-btn.is-activeActive view button — indicates the selected view
#swym-wishlist-table-wrapperOuter wrapper for the table view; hidden when grid view is active
.swym-table-headerColumn header row (Product, Qty, Price, Note, actions)
.swym-table-rowOne row per product in the table
.swym-table-col-*Individual column cells: img, product, qty, price, note, actions
.swym-table-variant-titleVariant name shown below the product title in the table
.swym-table-line-pricePer-row price that updates live as qty changes (unit price × qty)
.swym-table-actions-innerFlex wrapper for the cart and remove buttons inside .swym-table-col-actions
.swym-table-noteModifier on .swym-wishlist-note for compact table-row sizing
.swym-wishlist-qtyQuantity stepper wrapper ( / count / +) — shared between grid cards and table rows
.swym-wishlist-qty-valueQuantity number display — set a fixed min-width to prevent layout shift
.swym-wishlist-note-label"Note" label above the card textarea (grid view only)
.swym-wishlist-noteNote textarea — resize:vertical; shared class between grid and table
.swym-wishlist-cart-btnAdd to Cart button; [disabled] = out of stock
.swym-wishlist-remove-btnRemove button

As with all card styles, pull border colour, border radius, and font values from your theme's CSS variables rather than hardcoding them.

Product Fields Used (additions)

These fields are added on top of the ones documented in the base guide.

FieldUsage
iuItem image URL — used as the thumbnail source inside each list tile on the landing view (up to three images per tile, sourced from listcontents)
qtyQuantity stored on the wishlist item (defaults to 1 if not set). Read on render, written back via updateListItem
noteFree-text note stored on the wishlist item. Read on render, written back via updateListItem

Complete Implementation

Copy the files for your setup into the theme. The Liquid goes in sections/, the JS in assets/. Choose the variant that matches your Swym dashboard configuration.

sections/swym-wishlist-page.liquid

{% comment %} Swym Wishlist Page — Home n Furniture, Multiple Wishlists {% endcomment %}
<div id="swym-wishlist-page" class="page-width">

  <div class="swym-page-header">
    <div class="swym-header-title">
      <h1><span id="swym-wishlist-title">My Wishlists</span> <span id="swym-wishlist-count"></span></h1>
    </div>
    <div class="swym-header-controls">
      <div id="swym-view-toggle" style="display:none;">
        <button id="swym-view-table-btn" class="swym-view-btn is-active" aria-pressed="true" aria-label="Switch to table view">Table</button>
        <button id="swym-view-grid-btn" class="swym-view-btn" aria-pressed="false" aria-label="Switch to grid view">Grid</button>
      </div>
      <button id="swym-add-all-btn" style="display:none;" aria-label="Add all wishlist items to cart">Add All to Cart</button>
      <button id="swym-share-btn" style="display:none;" aria-label="Share wishlist">Share Wishlist</button>
    </div>
  </div>

  {%- comment -%} Share modal — rendered hidden; JS controls visibility {%- endcomment -%}
  <div id="swym-share-modal" role="dialog" aria-modal="true" aria-labelledby="swym-share-modal-title" style="display:none; position:fixed; inset:0; z-index:9999;">
    <div id="swym-share-modal-overlay" style="display:block; position:absolute; inset:0; background:rgba(0,0,0,0.5);"></div>
    <div id="swym-share-modal-content" style="position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); background:#fff; padding:2rem; max-width:480px; width:90%; border-radius:8px; max-height:90vh; overflow-y:auto;">
      <div id="swym-share-modal-header">
        <h2 id="swym-share-modal-title">Share Your Wishlist</h2>
        <button id="swym-share-modal-close" aria-label="Close share dialog">&times;</button>
      </div>

      {%- comment -%} Email share form {%- endcomment -%}
      <div class="swym-share-section">
        <label for="swym-share-name-input">Name</label>
        <input id="swym-share-name-input" type="text" placeholder="Your name" aria-label="Your name">

        <label for="swym-share-email-input">Email</label>
        <input id="swym-share-email-input" type="email" placeholder="[email protected]" aria-label="Recipient email address">

        <label for="swym-share-note-input">Message</label>
        <textarea id="swym-share-note-input" placeholder="Check out my wishlist!" aria-label="Message"></textarea>

        <button id="swym-share-email-btn" aria-label="Send wishlist by email">Share Via Email</button>
        <span id="swym-share-email-feedback" style="display:none;" aria-live="polite">Email sent!</span>
      </div>

      {%- comment -%} Social / copy {%- endcomment -%}
      <div class="swym-share-section swym-share-social-section">
        <p class="swym-share-divider-label">or Share via</p>
        <div class="swym-share-social-row">
          <button class="swym-share-social-btn" data-platform="facebook" aria-label="Share on Facebook">Facebook</button>
          <button class="swym-share-social-btn" data-platform="twitter" aria-label="Share on Twitter / X">X</button>
          <button id="swym-share-copy-btn" aria-label="Copy link">Copy Link</button>
        </div>
        <span id="swym-share-copy-feedback" style="display:none;" aria-live="polite">Copied!</span>
      </div>
    </div>
  </div>

  {%- comment -%} Login nudge for anonymous users {%- endcomment -%}
  <div id="swym-login-nudge" style="display:none;">
    <p>Sign in to save your wishlist across devices</p>
    <a href="/account/login?return_url=/pages/wishlist">Sign in</a>
  </div>

  {%- comment -%} Save nudge — shown after 3+ items for anonymous users {%- endcomment -%}
  <div id="swym-save-nudge" style="display:none;" aria-live="polite" aria-atomic="true">
    <p><strong>Don't lose your list!</strong> You've saved <span id="swym-save-nudge-count"></span> items. <a href="/account/login?return_url=/pages/wishlist">Sign in to keep them.</a></p>
    <button id="swym-save-nudge-dismiss" aria-label="Dismiss">&times;</button>
  </div>

  <div id="swym-wishlist-loading" aria-live="polite" aria-atomic="true">Loading your wishlist...</div>

  {%- comment -%} Lists selection grid — landing view when multiple wishlists exist {%- endcomment -%}
  <div id="swym-lists-grid" style="display:none;" aria-label="Your wishlists"></div>

  {%- comment -%} Bar: back button (left) + wishlist total (right) — shown when viewing a specific list {%- endcomment -%}
  <div id="swym-wishlist-bar">
    <button id="swym-back-to-lists" type="button" style="display:none;" aria-label="Back to all wishlists">< Back to all Lists</button>
    <div id="swym-wishlist-total" style="display:none;" aria-live="polite"></div>
  </div>

  <div id="swym-wishlist-table-wrapper" style="display:none;">
    <div id="swym-wishlist-table" role="region" aria-label="Wishlist products">
      <div class="swym-table-header">
        <div class="swym-table-col-img">&nbsp;</div>
        <div class="swym-table-col-product">Product</div>
        <div class="swym-table-col-qty">Qty</div>
        <div class="swym-table-col-price">Price</div>
        <div class="swym-table-col-note">Note</div>
        <div class="swym-table-col-actions">&nbsp;</div>
      </div>
      <div id="swym-wishlist-tbody"></div>
    </div>
  </div>
  <div id="swym-wishlist-grid" aria-busy="true" aria-label="Wishlist products" style="display:none;"></div>
  <div id="swym-wishlist-empty" style="display:none;">
    <p>Your wishlist is empty</p>
    <p>Start adding items you love</p>
    <a href="/collections">Browse products</a>
  </div>
</div>

<script src="{{ 'swym-wishlist-page.js' | asset_url }}" defer></script>

assets/swym-wishlist-page.js

(function() {
  'use strict';

  var SAVE_NUDGE_THRESHOLD = 3;
  var SS_PRODUCTS_KEY = 'swym-wishlist-products';
  var SS_SHARE_URL_KEY = 'swym-share-url';
  var SS_ACTIVE_LID_KEY = 'swym-active-lid';
  var productJSONCache = {};
  var shareButtonHandler = null;
  var currentView = 'table';

  function initWishlistPage(swat) {
    var isLoggedIn = swat.platform.isLoggedIn();
    var grid = document.getElementById('swym-wishlist-grid');
    var loading = document.getElementById('swym-wishlist-loading');
    var empty = document.getElementById('swym-wishlist-empty');
    var countEl = document.getElementById('swym-wishlist-count');

    if (!grid) return;

    if (!isLoggedIn) {
      var loginNudge = document.getElementById('swym-login-nudge');
      if (loginNudge) loginNudge.style.display = '';
    }

    // Only use cache when the user has explicitly selected a list in this session
    var activeLidCached = sessionStorage.getItem(SS_ACTIVE_LID_KEY);
    var cachedRaw = activeLidCached ? sessionStorage.getItem(SS_PRODUCTS_KEY) : null;
    var cachedData = null;
    if (cachedRaw) {
      try {
        cachedData = JSON.parse(cachedRaw);
        if (cachedData && cachedData.lid === activeLidCached) {
          if (loading) loading.style.display = 'none';
          renderProducts(swat, cachedData.lid, cachedData.products, grid, countEl, empty);
          if (countEl) countEl.textContent = '(' + cachedData.products.length + ' items)';
          setupShareButton(swat, cachedData.lid, cachedData.products.length);
          setupAddAllButton(cachedData.products);
        } else {
          cachedData = null;
        }
      } catch (e) {
        sessionStorage.removeItem(SS_PRODUCTS_KEY);
        cachedData = null;
      }
    }

    swat.fetchLists({
      callbackFn: function(lists) {
        if (loading) loading.style.display = 'none';

        if (!lists || lists.length === 0) {
          if (empty) empty.style.display = 'block';
          if (countEl) countEl.textContent = '(0 items)';
          return;
        }

        var multiList = swat.isCollectionsEnabled() && lists.length > 1;
        var rememberedLid = sessionStorage.getItem(SS_ACTIVE_LID_KEY);
        var activeList = rememberedLid
          ? lists.find(function(l) { return l.lid === rememberedLid; })
          : null;

        // Multi-list with no remembered selection: show the lists selection grid
        if (multiList && !activeList) {
          renderListsGrid(swat, lists, grid, countEl, empty);
          return;
        }

        // Single list or remembered selection: render list directly
        if (!activeList) activeList = lists[0];
        var lid = activeList.lid;
        var products = activeList.listcontents || [];

        sessionStorage.setItem(SS_PRODUCTS_KEY, JSON.stringify({ lid: lid, products: products }));

        if (products.length === 0) {
          if (empty) empty.style.display = 'block';
          if (countEl) countEl.textContent = '(0 items)';
        } else {
          if (countEl) countEl.textContent = '(' + products.length + ' items)';
          showSaveNudge(swat, products.length);
          renderProducts(swat, lid, products, grid, countEl, empty);
          setupShareButton(swat, lid, products.length);
          setupAddAllButton(products);
          sessionStorage.removeItem(SS_SHARE_URL_KEY);
        }

        setWishlistTitle(activeList.lname);
        if (multiList) setupBackToLists(swat, lists, lid, grid, countEl, empty);

        if (swat.evtLayer) {
          swat.evtLayer.addEventListener('sw:removedfromwishlist', function() {
            swat.fetchLists({
              callbackFn: function(freshLists) {
                var activeLid = sessionStorage.getItem(SS_ACTIVE_LID_KEY) || lid;
                var freshActive = freshLists && freshLists.length
                  ? (freshLists.find(function(l) { return l.lid === activeLid; }) || freshLists[0])
                  : null;
                var currentLid = freshActive ? freshActive.lid : null;
                var updated = freshActive ? (freshActive.listcontents || []) : [];
                var count = updated.length;

                sessionStorage.setItem(SS_PRODUCTS_KEY, JSON.stringify({ lid: currentLid, products: updated }));
                sessionStorage.removeItem(SS_SHARE_URL_KEY);
                renderProducts(swat, currentLid, updated, grid, countEl, empty);
                if (countEl) countEl.textContent = '(' + count + ' items)';
                setupShareButton(swat, currentLid, count);
                setupAddAllButton(updated);
                if (multiList) setupBackToLists(swat, freshLists || [], currentLid, grid, countEl, empty);
              }
            });
          });
        }
      },
      errorFn: function() {
        if (loading) loading.style.display = 'none';
      }
    });
  }

  // Renders the lists selection grid (landing view for multi-list users)
  function renderListsGrid(swat, lists, grid, countEl, emptyEl) {
    var listsGridEl = document.getElementById('swym-lists-grid');
    if (!listsGridEl) return;

    var tableWrapper = document.getElementById('swym-wishlist-table-wrapper');
    var viewToggle = document.getElementById('swym-view-toggle');
    var addAllBtn = document.getElementById('swym-add-all-btn');
    var shareBtn = document.getElementById('swym-share-btn');
    var backBtn = document.getElementById('swym-back-to-lists');
    var totalEl = document.getElementById('swym-wishlist-total');

    if (tableWrapper) tableWrapper.style.display = 'none';
    grid.style.display = 'none';
    if (viewToggle) viewToggle.style.display = 'none';
    if (addAllBtn) addAllBtn.style.display = 'none';
    if (shareBtn) shareBtn.style.display = 'none';
    if (backBtn) backBtn.style.display = 'none';
    if (totalEl) totalEl.style.display = 'none';
    if (emptyEl) emptyEl.style.display = 'none';
    if (countEl) countEl.textContent = '';
    setWishlistTitle('My Wishlists');

    listsGridEl.innerHTML = '';
    listsGridEl.style.display = '';

    lists.forEach(function(list, listIndex) {
      var images = (list.listcontents || [])
        .slice(0, 3)
        .map(function(item) { return item.iu || ''; })
        .filter(Boolean);

      var imgCount = images.length;
      var imagesHtml = imgCount === 0
        ? '<div class="swym-list-tile-placeholder"><span class="swym-list-tile-empty-label">This list is empty!</span></div>'
        : images.map(function(src, imgIdx) {
            var tileLoadAttr = (listIndex === 0 && imgIdx === 0) ? 'fetchpriority="high"' : 'loading="lazy"';
            return '<img src="' + esc(src) + '" alt="" ' + tileLoadAttr + '>';
          }).join('');

      var itemCount = (list.listcontents || []).length;
      var tile = document.createElement('button');
      tile.type = 'button';
      tile.className = 'swym-list-tile';
      tile.setAttribute('aria-label', 'Open wishlist: ' + list.lname);
      tile.dataset.lid = list.lid;
      tile.innerHTML =
        '<div class="swym-list-tile-images swym-list-tile-images--' + Math.max(1, imgCount) + '">' +
          imagesHtml +
        '</div>' +
        '<div class="swym-list-tile-info">' +
          '<span class="swym-list-tile-name">' + esc(list.lname) + '</span>' +
          '<span class="swym-list-tile-count">' + itemCount + ' ' + (itemCount === 1 ? 'item' : 'items') + '</span>' +
        '</div>';

      tile.addEventListener('click', function() {
        var products = list.listcontents || [];
        sessionStorage.setItem(SS_ACTIVE_LID_KEY, list.lid);
        sessionStorage.setItem(SS_PRODUCTS_KEY, JSON.stringify({ lid: list.lid, products: products }));
        sessionStorage.removeItem(SS_SHARE_URL_KEY);

        listsGridEl.style.display = 'none';
        setWishlistTitle(list.lname);
        if (countEl) countEl.textContent = '(' + products.length + ' items)';
        setupBackToLists(swat, lists, list.lid, grid, countEl, emptyEl);
        setupShareButton(swat, list.lid, products.length);
        setupAddAllButton(products);
        if (totalEl) totalEl.style.display = '';
        renderProducts(swat, list.lid, products, grid, countEl, emptyEl);
      });

      listsGridEl.appendChild(tile);
    });
  }

  // Wires the back-to-lists button for multi-list users viewing a specific list
  function setupBackToLists(swat, lists, activeLid, grid, countEl, emptyEl) {
    var backBtn = document.getElementById('swym-back-to-lists');
    if (!backBtn) return;

    var fresh = backBtn.cloneNode(true);
    backBtn.parentNode.replaceChild(fresh, backBtn);
    fresh.style.display = '';

    fresh.addEventListener('click', function() {
      sessionStorage.removeItem(SS_ACTIVE_LID_KEY);
      sessionStorage.removeItem(SS_PRODUCTS_KEY);
      renderListsGrid(swat, lists, grid, countEl, emptyEl);
    });
  }

  // Recalculates and updates the total from current grid card prices + quantities
  function recalcTotal() {
    var totalEl = document.getElementById('swym-wishlist-total');
    if (!totalEl) return;

    var total = 0;
    var hasAny = false;
    document.querySelectorAll('#swym-wishlist-grid .swym-wishlist-card[data-price]').forEach(function(card) {
      var price = parseFloat(card.dataset.price) || 0;
      if (price <= 0) return;
      var qtyEl = card.querySelector('.swym-wishlist-qty-value');
      var qty = qtyEl ? (parseInt(qtyEl.textContent, 10) || 1) : 1;
      total += price * qty;
      hasAny = true;
    });

    if (hasAny) {
      totalEl.textContent = 'Total: ' + formatPrice(total);
      totalEl.style.display = '';
    } else {
      totalEl.style.display = 'none';
    }
  }

  function showSaveNudge(swat, count) {
    if (swat.platform.isLoggedIn()) return;
    if (count < SAVE_NUDGE_THRESHOLD) return;
    if (sessionStorage.getItem('swym-save-nudge-dismissed')) return;

    var nudge = document.getElementById('swym-save-nudge');
    if (!nudge) return;

    var countSpan = document.getElementById('swym-save-nudge-count');
    if (countSpan) countSpan.textContent = count;
    nudge.style.display = '';

    var dismiss = document.getElementById('swym-save-nudge-dismiss');
    if (dismiss) {
      dismiss.addEventListener('click', function() {
        nudge.style.display = 'none';
        sessionStorage.setItem('swym-save-nudge-dismissed', 'true');
      });
    }
  }

  async function renderProducts(swat, lid, products, container, countEl, emptyEl) {
    container.setAttribute('aria-busy', 'true');
    container.innerHTML = '';
    var tableBody = document.getElementById('swym-wishlist-tbody');
    if (tableBody) tableBody.innerHTML = '';

    if (!products || products.length === 0) {
      if (emptyEl) emptyEl.style.display = 'block';
      container.setAttribute('aria-busy', 'false');
      setupViewToggle(false);
      recalcTotal();
      return;
    }

    if (emptyEl) emptyEl.style.display = 'none';

    var shopifyData = await Promise.all(products.map(function(product) {
      var key = String(product.epi);
      if (productJSONCache[key]) return Promise.resolve(productJSONCache[key]);
      return fetchProductJSON(product.du)
        .then(function(data) { productJSONCache[key] = data; return data; })
        .catch(function() { return null; });
    }));

    products.forEach(function(product, index) {
      var shopifyProduct = shopifyData[index];
      var allVariants = shopifyProduct ? shopifyProduct.variants : null;
      var variant = allVariants
        ? allVariants.find(function(v) { return String(v.id) === String(product.epi); })
        : null;
      var inStock = variant ? variant.available : product.stk !== 0;
      var displayPrice = variant ? variant.price : product.pr;

      var title = esc(shopifyProduct && shopifyProduct.title || product.dt || 'Product');
      var href = esc(shopifyProduct && shopifyProduct.url || product.du || '#');
      var imgSrc = esc(shopifyProduct && (shopifyProduct.featured_image || (shopifyProduct.images && shopifyProduct.images[0])) || product.iu || '');
      var noteId = 'swym-note-' + esc(String(product.epi));

      var imgLoadAttr = index === 0 ? 'fetchpriority="high"' : 'loading="lazy"';

      var card = document.createElement('div');
      card.className = 'swym-wishlist-card';
      card.dataset.epi = product.epi;
      if (displayPrice) card.dataset.price = displayPrice;
      card.innerHTML =
        '<a href="' + href + '">' +
          '<img src="' + imgSrc + '" alt="' + title + '" ' + imgLoadAttr + '>' +
        '</a>' +
        '<a href="' + href + '">' + title + '</a>' +
        (displayPrice ? '<span>' + esc(formatPrice(displayPrice)) + '</span>' : '') +
        '<div class="swym-wishlist-card-actions">' +
          '<div class="swym-wishlist-qty">' +
            '<button type="button" aria-label="Decrease quantity of ' + title + '">−</button>' +
            '<span class="swym-wishlist-qty-value" aria-live="polite">' + (product.qty || 1) + '</span>' +
            '<button type="button" aria-label="Increase quantity of ' + title + '">+</button>' +
          '</div>' +
          '<label class="swym-wishlist-note-label" for="' + noteId + '">Note</label>' +
          '<textarea id="' + noteId + '" class="swym-wishlist-note" rows="2"' +
            ' placeholder="Add a note (size, colour, special instructions…)"' +
            ' aria-label="Note for ' + title + '">' + esc(product.note || '') + '</textarea>' +
          '<button type="button" class="swym-wishlist-cart-btn"' + (!inStock ? ' disabled' : '') +
            ' aria-label="' + (inStock ? 'Add ' + title + ' to cart' : title + ' is out of stock') + '">' +
            (inStock ? 'Add to Cart' : 'Out of Stock') +
          '</button>' +
          '<button type="button" class="swym-wishlist-remove-btn" aria-label="Remove ' + title + ' from wishlist">×</button>' +
        '</div>';

      var decrementBtn = card.querySelector('.swym-wishlist-qty > button:first-of-type');
      var qtyDisplay = card.querySelector('.swym-wishlist-qty-value');
      var incrementBtn = card.querySelector('.swym-wishlist-qty > button:last-of-type');
      var noteInput = card.querySelector('.swym-wishlist-note');
      var cartBtn = card.querySelector('.swym-wishlist-cart-btn');
      var removeBtn = card.querySelector('.swym-wishlist-remove-btn');

      decrementBtn.addEventListener('click', function() {
        var qty = parseInt(qtyDisplay.textContent, 10);
        if (qty <= 1) return;
        qty -= 1;
        qtyDisplay.textContent = qty;
        product.qty = qty;
        syncQtyAcrossViews(product.epi, qty, qtyDisplay);
        scheduleItemUpdate(swat, lid, product, qty, noteInput.value.trim());
        recalcTotal();
      });

      incrementBtn.addEventListener('click', function() {
        var qty = parseInt(qtyDisplay.textContent, 10) + 1;
        qtyDisplay.textContent = qty;
        product.qty = qty;
        syncQtyAcrossViews(product.epi, qty, qtyDisplay);
        scheduleItemUpdate(swat, lid, product, qty, noteInput.value.trim());
        recalcTotal();
      });

      noteInput.addEventListener('input', function() {
        var qty = parseInt(qtyDisplay.textContent, 10);
        product.note = noteInput.value.trim();
        syncNoteAcrossViews(product.epi, noteInput.value, noteInput);
        scheduleItemUpdate(swat, lid, product, qty, noteInput.value.trim());
      });

      cartBtn.addEventListener('click', function() {
        if (cartBtn.disabled) return;
        var qty = parseInt(qtyDisplay.textContent, 10);
        cartBtn.textContent = 'Adding...';
        cartBtn.setAttribute('aria-label', 'Adding ' + title + ' to cart');
        cartBtn.disabled = true;
        fetch('/cart/add.js', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ items: [{ id: Number(product.epi), quantity: qty }] })
        })
        .then(function(response) {
          if (response.ok) {
            cartBtn.textContent = 'Added!';
            cartBtn.setAttribute('aria-label', title + ' added to cart');
            setTimeout(function() {
              cartBtn.textContent = 'Add to Cart';
              cartBtn.setAttribute('aria-label', 'Add ' + title + ' to cart');
              cartBtn.disabled = false;
            }, 2000);
          } else {
            cartBtn.textContent = 'Error';
            cartBtn.setAttribute('aria-label', 'Failed to add ' + title + ' to cart');
            cartBtn.disabled = false;
          }
        })
        .catch(function() {
          cartBtn.textContent = 'Error';
          cartBtn.setAttribute('aria-label', 'Failed to add ' + title + ' to cart');
          cartBtn.disabled = false;
        });
      });

      removeBtn.addEventListener('click', function() {
        swat.deleteFromList(
          lid,
          { epi: product.epi, empi: product.empi, du: product.du },
          function() {
            card.remove();
            var tableRow = document.querySelector('#swym-wishlist-table [data-epi="' + String(product.epi) + '"]');
            if (tableRow) tableRow.remove();
            var remaining = container.querySelectorAll('.swym-wishlist-card');
            if (countEl) countEl.textContent = '(' + remaining.length + ' items)';
            if (remaining.length === 0 && emptyEl) emptyEl.style.display = 'block';
            recalcTotal();
          },
          function() {}
        );
      });

      container.appendChild(card);

      if (tableBody) {
        var tableNoteId = 'swym-tnote-' + esc(String(product.epi));
        var variantTitle = variant ? esc(variant.title) : '';
        var initialQty = product.qty || 1;
        var initialLinePrice = displayPrice ? formatPrice(displayPrice * initialQty) : '';

        var row = document.createElement('div');
        row.className = 'swym-table-row';
        row.dataset.epi = product.epi;
        row.innerHTML =
          '<div class="swym-table-col-img">' +
            '<a href="' + href + '"><img src="' + imgSrc + '" alt="' + title + '" width="150" height="150" ' + imgLoadAttr + '></a>' +
          '</div>' +
          '<div class="swym-table-col-product">' +
            '<a href="' + href + '">' + title + '</a>' +
            (variantTitle ? '<span class="swym-table-variant-title">' + variantTitle + '</span>' : '') +
          '</div>' +
          '<div class="swym-table-col-qty">' +
            '<div class="swym-wishlist-qty">' +
              '<button type="button" aria-label="Decrease quantity of ' + title + '">−</button>' +
              '<span class="swym-wishlist-qty-value" aria-live="polite">' + initialQty + '</span>' +
              '<button type="button" aria-label="Increase quantity of ' + title + '">+</button>' +
            '</div>' +
          '</div>' +
          '<div class="swym-table-col-price">' +
            (displayPrice ? '<span class="swym-table-line-price">' + esc(initialLinePrice) + '</span>' : '') +
          '</div>' +
          '<div class="swym-table-col-note">' +
            '<textarea id="' + tableNoteId + '" class="swym-wishlist-note swym-table-note" rows="1"' +
              ' placeholder="Note…"' +
              ' aria-label="Note for ' + title + '">' + esc(product.note || '') + '</textarea>' +
          '</div>' +
          '<div class="swym-table-col-actions">' +
            '<div class="swym-table-actions-inner">' +
              '<button type="button" class="swym-wishlist-cart-btn"' + (!inStock ? ' disabled' : '') +
                ' aria-label="' + (inStock ? 'Add ' + title + ' to cart' : title + ' is out of stock') + '">' +
                (inStock ? 'Add to Cart' : 'Out of Stock') +
              '</button>' +
              '<button type="button" class="swym-wishlist-remove-btn" aria-label="Remove ' + title + ' from wishlist">\xd7</button>' +
            '</div>' +
          '</div>';

        var tDecrBtn = row.querySelector('.swym-table-col-qty .swym-wishlist-qty > button:first-of-type');
        var tQtyDisplay = row.querySelector('.swym-wishlist-qty-value');
        var tIncrBtn = row.querySelector('.swym-table-col-qty .swym-wishlist-qty > button:last-of-type');
        var tNoteInput = row.querySelector('.swym-wishlist-note');
        var tCartBtn = row.querySelector('.swym-wishlist-cart-btn');
        var tRemoveBtn = row.querySelector('.swym-wishlist-remove-btn');
        var tPriceEl = row.querySelector('.swym-table-line-price');

        tDecrBtn.addEventListener('click', function() {
          var qty = parseInt(tQtyDisplay.textContent, 10);
          if (qty <= 1) return;
          qty -= 1;
          tQtyDisplay.textContent = qty;
          product.qty = qty;
          syncQtyAcrossViews(product.epi, qty, tQtyDisplay);
          if (tPriceEl && displayPrice) tPriceEl.textContent = formatPrice(displayPrice * qty);
          scheduleItemUpdate(swat, lid, product, qty, tNoteInput.value.trim());
          recalcTotal();
        });

        tIncrBtn.addEventListener('click', function() {
          var qty = parseInt(tQtyDisplay.textContent, 10) + 1;
          tQtyDisplay.textContent = qty;
          product.qty = qty;
          syncQtyAcrossViews(product.epi, qty, tQtyDisplay);
          if (tPriceEl && displayPrice) tPriceEl.textContent = formatPrice(displayPrice * qty);
          scheduleItemUpdate(swat, lid, product, qty, tNoteInput.value.trim());
          recalcTotal();
        });

        tNoteInput.addEventListener('input', function() {
          var qty = parseInt(tQtyDisplay.textContent, 10);
          product.note = tNoteInput.value.trim();
          syncNoteAcrossViews(product.epi, tNoteInput.value, tNoteInput);
          scheduleItemUpdate(swat, lid, product, qty, tNoteInput.value.trim());
        });

        tCartBtn.addEventListener('click', function() {
          if (tCartBtn.disabled) return;
          var qty = parseInt(tQtyDisplay.textContent, 10);
          tCartBtn.textContent = 'Adding...';
          tCartBtn.disabled = true;
          fetch('/cart/add.js', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ items: [{ id: Number(product.epi), quantity: qty }] })
          })
          .then(function(response) {
            if (response.ok) {
              tCartBtn.textContent = 'Added!';
              setTimeout(function() {
                tCartBtn.textContent = 'Add to Cart';
                tCartBtn.disabled = false;
              }, 2000);
            } else {
              tCartBtn.textContent = 'Error';
              tCartBtn.disabled = false;
            }
          })
          .catch(function() {
            tCartBtn.textContent = 'Error';
            tCartBtn.disabled = false;
          });
        });

        tRemoveBtn.addEventListener('click', function() {
          swat.deleteFromList(
            lid,
            { epi: product.epi, empi: product.empi, du: product.du },
            function() {
              row.remove();
              var cardEl = container.querySelector('[data-epi="' + String(product.epi) + '"]');
              if (cardEl) cardEl.remove();
              var remaining = container.querySelectorAll('.swym-wishlist-card');
              if (countEl) countEl.textContent = '(' + remaining.length + ' items)';
              if (remaining.length === 0 && emptyEl) emptyEl.style.display = 'block';
              recalcTotal();
            },
            function() {}
          );
        });

        tableBody.appendChild(row);
      }
    });

    container.setAttribute('aria-busy', 'false');
    setupViewToggle(true);
    recalcTotal();
  }

  var _updateTimers = {};

  function scheduleItemUpdate(swat, lid, product, qty, note) {
    var key = String(product.epi);
    clearTimeout(_updateTimers[key]);
    _updateTimers[key] = setTimeout(function() {
      swat.updateListItem(
        lid,
        {
          epi: product.epi,
          empi: product.empi,
          du: product.du,
          qty: qty,
          note: note
        },
        function() {},
        function() {}
      );
    }, 600);
  }

  function syncQtyAcrossViews(epi, qty, sourceEl) {
    document.querySelectorAll('[data-epi="' + String(epi) + '"] .swym-wishlist-qty-value').forEach(function(el) {
      if (el !== sourceEl) el.textContent = qty;
    });
  }

  function syncNoteAcrossViews(epi, value, sourceEl) {
    document.querySelectorAll('[data-epi="' + String(epi) + '"] .swym-wishlist-note').forEach(function(el) {
      if (el !== sourceEl) el.value = value;
    });
  }

  function setupViewToggle(hasProducts) {
    var toggle = document.getElementById('swym-view-toggle');
    var tableWrapper = document.getElementById('swym-wishlist-table-wrapper');
    var gridEl = document.getElementById('swym-wishlist-grid');
    if (!toggle) return;

    toggle.style.display = hasProducts ? '' : 'none';
    if (!hasProducts) {
      if (tableWrapper) tableWrapper.style.display = 'none';
      return;
    }

    var tableBtn = document.getElementById('swym-view-table-btn');
    var gridBtn = document.getElementById('swym-view-grid-btn');
    if (!tableBtn || !gridBtn || !tableWrapper || !gridEl) return;

    if (currentView === 'table') {
      tableWrapper.style.display = '';
      gridEl.style.display = 'none';
    } else {
      tableWrapper.style.display = 'none';
      gridEl.style.display = '';
    }

    var freshTable = tableBtn.cloneNode(true);
    tableBtn.parentNode.replaceChild(freshTable, tableBtn);
    var freshGrid = gridBtn.cloneNode(true);
    gridBtn.parentNode.replaceChild(freshGrid, gridBtn);

    if (currentView === 'table') {
      freshTable.classList.add('is-active');
      freshTable.setAttribute('aria-pressed', 'true');
      freshGrid.classList.remove('is-active');
      freshGrid.setAttribute('aria-pressed', 'false');
    } else {
      freshGrid.classList.add('is-active');
      freshGrid.setAttribute('aria-pressed', 'true');
      freshTable.classList.remove('is-active');
      freshTable.setAttribute('aria-pressed', 'false');
    }

    freshTable.addEventListener('click', function() {
      currentView = 'table';
      tableWrapper.style.display = '';
      gridEl.style.display = 'none';
      freshTable.classList.add('is-active');
      freshTable.setAttribute('aria-pressed', 'true');
      freshGrid.classList.remove('is-active');
      freshGrid.setAttribute('aria-pressed', 'false');
    });

    freshGrid.addEventListener('click', function() {
      currentView = 'grid';
      gridEl.style.display = '';
      tableWrapper.style.display = 'none';
      freshGrid.classList.add('is-active');
      freshGrid.setAttribute('aria-pressed', 'true');
      freshTable.classList.remove('is-active');
      freshTable.setAttribute('aria-pressed', 'false');
    });
  }

  function setupAddAllButton(products) {
    var btn = document.getElementById('swym-add-all-btn');
    if (!btn) return;

    var hasInStock = products && products.some(function(p) { return p.stk !== 0; });
    if (!hasInStock) {
      btn.style.display = 'none';
      return;
    }
    btn.style.display = '';

    var fresh = btn.cloneNode(true);
    btn.parentNode.replaceChild(fresh, btn);
    fresh.addEventListener('click', function() { addAllToCart(fresh); });
  }

  function addAllToCart(btn) {
    var cards = document.querySelectorAll('#swym-wishlist-grid .swym-wishlist-card[data-epi]');
    var items = [];
    cards.forEach(function(card) {
      var epi = card.dataset.epi;
      var qtyEl = card.querySelector('.swym-wishlist-qty-value');
      var qty = qtyEl ? parseInt(qtyEl.textContent, 10) : 1;
      var cartBtn = card.querySelector('.swym-wishlist-cart-btn');
      if (epi && qty > 0 && cartBtn && !cartBtn.disabled) {
        items.push({ id: Number(epi), quantity: qty });
      }
    });

    if (!items.length) return;

    btn.disabled = true;
    btn.textContent = 'Adding...';
    btn.setAttribute('aria-label', 'Adding all items to cart');

    fetch('/cart/add.js', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ items: items })
    })
    .then(function(response) {
      if (response.ok) {
        btn.textContent = 'Added!';
        btn.setAttribute('aria-label', 'All items added to cart');
        setTimeout(function() {
          btn.textContent = 'Add All to Cart';
          btn.setAttribute('aria-label', 'Add all wishlist items to cart');
          btn.disabled = false;
        }, 2000);
      } else {
        btn.textContent = 'Error';
        btn.setAttribute('aria-label', 'Failed to add items to cart');
        btn.disabled = false;
      }
    })
    .catch(function() {
      btn.textContent = 'Error';
      btn.setAttribute('aria-label', 'Failed to add items to cart');
      btn.disabled = false;
    });
  }

  function bindShareUrlButtons(modal, copyBtn, url, swat, lid) {
    if (copyBtn) {
      copyBtn.disabled = false;
      var freshCopy = copyBtn.cloneNode(true);
      copyBtn.parentNode.replaceChild(freshCopy, copyBtn);
      freshCopy.addEventListener('click', function() { handleCopyLink(url); });
    }
    modal.querySelectorAll('.swym-share-social-btn').forEach(function(socialBtn) {
      var freshSocial = socialBtn.cloneNode(true);
      socialBtn.parentNode.replaceChild(freshSocial, socialBtn);
      freshSocial.addEventListener('click', function() {
        handleSocialShare(swat, lid, freshSocial.dataset.platform, url);
      });
    });
  }

  function setupShareButton(swat, lid, productCount) {
    var btn = document.getElementById('swym-share-btn');
    if (!btn || !lid) return;

    if (productCount === 0) {
      btn.style.display = 'none';
      return;
    }
    btn.style.display = '';

    if (shareButtonHandler) btn.removeEventListener('click', shareButtonHandler);
    shareButtonHandler = function() { openShareModal(swat, lid); };
    btn.addEventListener('click', shareButtonHandler);
  }

  function openShareModal(swat, lid) {
    var modal = document.getElementById('swym-share-modal');
    if (!modal) return;

    var nameInput = document.getElementById('swym-share-name-input');
    var emailInput = document.getElementById('swym-share-email-input');
    var noteInput = document.getElementById('swym-share-note-input');
    var copyBtn = document.getElementById('swym-share-copy-btn');
    if (nameInput) nameInput.value = '';
    if (emailInput) emailInput.value = '';
    if (noteInput) noteInput.value = '';
    if (copyBtn) copyBtn.disabled = true;
    modal.querySelectorAll('[id$="-feedback"]').forEach(function(el) { el.style.display = 'none'; });

    modal.style.display = '';
    document.body.style.overflow = 'hidden';
    if (nameInput) nameInput.focus();

    var cachedShareUrl = sessionStorage.getItem(SS_SHARE_URL_KEY);
    if (cachedShareUrl) {
      bindShareUrlButtons(modal, copyBtn, cachedShareUrl, swat, lid);
    } else {
      swat.markListPublic(lid, function() {
        swat.generateSharedListURL(lid, function(url) {
          sessionStorage.setItem(SS_SHARE_URL_KEY, url);
          bindShareUrlButtons(modal, copyBtn, url, swat, lid);
        });
      }, function() {
        if (copyBtn) copyBtn.disabled = true;
      });
    }

    var emailBtn = document.getElementById('swym-share-email-btn');
    if (emailBtn) {
      var freshEmail = emailBtn.cloneNode(true);
      emailBtn.parentNode.replaceChild(freshEmail, emailBtn);
      freshEmail.addEventListener('click', function() { handleEmailShare(swat, lid); });
    }

    var closeBtn = document.getElementById('swym-share-modal-close');
    var overlay = document.getElementById('swym-share-modal-overlay');
    if (closeBtn) {
      var freshClose = closeBtn.cloneNode(true);
      closeBtn.parentNode.replaceChild(freshClose, closeBtn);
      freshClose.addEventListener('click', closeShareModal);
    }
    if (overlay) {
      var freshOverlay = overlay.cloneNode(true);
      overlay.parentNode.replaceChild(freshOverlay, overlay);
      freshOverlay.addEventListener('click', closeShareModal);
    }
    document.addEventListener('keydown', handleShareModalKeydown);
  }

  function handleShareModalKeydown(e) {
    if (e.key === 'Escape') {
      closeShareModal();
      return;
    }
    if (e.key !== 'Tab') return;

    var modal = document.getElementById('swym-share-modal-content');
    if (!modal) return;
    var focusable = Array.prototype.slice.call(
      modal.querySelectorAll('a[href],button:not([disabled]),input,textarea,select,[tabindex]:not([tabindex="-1"])')
    ).filter(function(el) { return !el.disabled && el.offsetParent !== null; });
    if (!focusable.length) return;

    var first = focusable[0];
    var last = focusable[focusable.length - 1];
    if (e.shiftKey) {
      if (document.activeElement === first) { e.preventDefault(); last.focus(); }
    } else {
      if (document.activeElement === last) { e.preventDefault(); first.focus(); }
    }
  }

  function closeShareModal() {
    var modal = document.getElementById('swym-share-modal');
    if (modal) modal.style.display = 'none';
    document.body.style.overflow = '';
    document.removeEventListener('keydown', handleShareModalKeydown);
    var trigger = document.getElementById('swym-share-btn');
    if (trigger) trigger.focus();
  }

  function handleCopyLink(url) {
    var feedback = document.getElementById('swym-share-copy-feedback');
    var done = function() {
      showModalFeedback(feedback);
      setTimeout(closeShareModal, 800);
    };
    if (navigator.clipboard && navigator.clipboard.writeText) {
      navigator.clipboard.writeText(url).then(done).catch(function() {
        fallbackCopy(url, feedback);
        setTimeout(closeShareModal, 800);
      });
    } else {
      fallbackCopy(url, feedback);
      setTimeout(closeShareModal, 800);
    }
  }

  function handleEmailShare(swat, lid) {
    var nameInput = document.getElementById('swym-share-name-input');
    var emailInput = document.getElementById('swym-share-email-input');
    var noteInput = document.getElementById('swym-share-note-input');
    var emailBtn = document.getElementById('swym-share-email-btn');
    var feedback = document.getElementById('swym-share-email-feedback');
    if (!emailInput) return;

    var email = emailInput.value.trim();
    if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
      emailInput.focus();
      return;
    }

    if (emailBtn) emailBtn.disabled = true;
    swat.sendListViaEmail({
      toEmailId: email,
      note: noteInput ? noteInput.value.trim() : '',
      fromName: nameInput ? nameInput.value.trim() : '',
      lid: lid
    }, function() {
      if (emailBtn) emailBtn.disabled = false;
      if (nameInput) nameInput.value = '';
      emailInput.value = '';
      if (noteInput) noteInput.value = '';
      showModalFeedback(feedback);
      setTimeout(closeShareModal, 1000);
    }, function() {
      if (emailBtn) emailBtn.disabled = false;
    });
  }

  function handleSocialShare(swat, lid, platform, shareUrl) {
    var platformUrlTemplate = '';
    if (platform === 'facebook') platformUrlTemplate = 'https://www.facebook.com/sharer.php?u=' + encodeURIComponent(shareUrl) + '&t=';
    else if (platform === 'twitter') platformUrlTemplate = 'https://twitter.com/share?text=&url=' + encodeURIComponent(shareUrl);
    swat.shareListSocial(lid, '', platformUrlTemplate, platform, '', function() {});
    if (platformUrlTemplate) window.open(platformUrlTemplate, '_blank', 'noopener,noreferrer');
    closeShareModal();
  }

  function showModalFeedback(el) {
    if (!el) return;
    el.style.display = '';
    setTimeout(function() { el.style.display = 'none'; }, 2500);
  }

  function fallbackCopy(url, feedbackEl) {
    var ta = document.createElement('textarea');
    ta.value = url;
    ta.style.cssText = 'position:fixed;opacity:0;pointer-events:none;';
    document.body.appendChild(ta);
    ta.select();
    try { document.execCommand('copy'); showModalFeedback(feedbackEl); } catch (e) {}
    document.body.removeChild(ta);
  }

  function formatPrice(priceInCents) {
    return '$' + (priceInCents / 100).toFixed(2);
  }

  function esc(str) {
    return String(str)
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&#39;');
  }

  function fetchProductJSON(du) {
    var parts = du.split('/products/');
    if (parts.length < 2) return Promise.reject(new Error('invalid product URL'));
    var url = window.location.origin + window.Shopify.routes.root + 'products/' + parts[1].split('?')[0] + '.js';
    return fetch(url)
      .then(function(response) {
        if (!response.ok) throw new Error('product fetch failed');
        return response.json();
      });
  }

  function setWishlistTitle(name) {
    var titleEl = document.getElementById('swym-wishlist-title');
    if (titleEl) titleEl.textContent = name || 'My Wishlists';
  }

  window.SwymCallbacks = window.SwymCallbacks || [];
  window.SwymCallbacks.push(initWishlistPage);
})();

assets/swym-wishlist-page.css

/* ============================================================
   Swym Wishlist Page
   ============================================================ */

/* --- Page layout ------------------------------------------ */
#swym-wishlist-page {
  padding: 2rem 1rem 4rem;
}

/* --- Page header ------------------------------------------ */
.swym-page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin-bottom: 1.5rem;
}

.swym-header-title {
  display: flex;
  align-items: center;
}

.swym-header-title h1 {
  margin: 0;
  font-size: 2rem;
  font-weight: 700;
  line-height: 1.2;
}

.swym-header-controls {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
}

@media (max-width: 640px) {
  .swym-header-title h1 {
    font-size: 1.375rem;
  }
}

/* --- Wishlist bar (back button + total) ------------------- */
#swym-wishlist-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin: 0.75rem 0 0.25rem;
}

/* --- Back to lists button --------------------------------- */
#swym-back-to-lists {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 9px 18px;
  background: #fff;
  border: 1px solid #d1d5db;
  border-radius: 6px;
  font-size: 1.125rem;
  font-weight: 500;
  color: #374151;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}

#swym-back-to-lists:hover {
  background: #f3f4f6;
  border-color: #9ca3af;
}

#swym-wishlist-count {
  font-size: 1.25rem;
  font-weight: 400;
  color: #6b7280;
}

/* --- View toggle ------------------------------------------ */
#swym-view-toggle {
  display: flex;
  border: 1px solid #d1d5db;
  border-radius: 6px;
  overflow: hidden;
}

.swym-view-btn {
  padding: 7px 16px;
  background: #fff;
  border: none;
  border-left: 1px solid #d1d5db;
  font-size: 1.0625rem;
  font-weight: 500;
  cursor: pointer;
  color: #6b7280;
  transition: background 0.15s, color 0.15s;
}

.swym-view-btn:first-child {
  border-left: none;
}

.swym-view-btn.is-active {
  background: #111827;
  color: #fff;
}

.swym-view-btn:hover:not(.is-active) {
  background: #f3f4f6;
  color: #374151;
}

/* --- Share button ----------------------------------------- */
#swym-share-btn {
  padding: 9px 18px;
  border: 1px solid #d1d5db;
  border-radius: 6px;
  background: #fff;
  font-size: 1.125rem;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}

#swym-share-btn:hover {
  background: #f3f4f6;
  border-color: #9ca3af;
}

/* --- Lists selection grid --------------------------------- */
#swym-lists-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 1.5rem;
  margin-top: 0.5rem;
}

@media (max-width: 640px) {
  #swym-lists-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 1rem;
  }
}

/* --- List tile -------------------------------------------- */
.swym-list-tile {
  display: flex;
  flex-direction: column;
  border: 1px solid #e5e7eb;
  border-radius: 12px;
  overflow: hidden;
  background: #fff;
  cursor: pointer;
  text-align: left;
  padding: 0;
  transition: box-shadow 0.2s, transform 0.2s;
}

.swym-list-tile:hover {
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.1);
  transform: translateY(-3px);
}

.swym-list-tile-images {
  display: flex;
  height: 200px;
  overflow: hidden;
  background: #f3f4f6;
}

.swym-list-tile-images img {
  flex: 1;
  width: 0; /* flex sizing trick: all images share space equally */
  height: 100%;
  object-fit: cover;
  display: block;
}

.swym-list-tile-images img + img {
  border-left: 2px solid #fff;
}

.swym-list-tile-placeholder {
  flex: 1;
  background: linear-gradient(135deg, #f3f4f6 0%, #e5e7eb 100%);
  display: flex;
  align-items: center;
  justify-content: center;
}

.swym-list-tile-empty-label {
  font-size: 0.8125rem;
  color: #9ca3af;
  text-align: center;
  padding: 0 12px;
}

.swym-list-tile-info {
  padding: 16px 18px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.swym-list-tile-name {
  font-size: 1.125rem;
  font-weight: 600;
  color: #111827;
  display: block;
}

.swym-list-tile-count {
  font-size: 1rem;
  color: #6b7280;
  display: block;
}

@media (max-width: 640px) {
  .swym-list-tile-images {
    height: 140px;
  }
}

/* --- Wishlist total --------------------------------------- */
#swym-wishlist-total {
  font-size: 1.125rem;
  font-weight: 600;
  color: #111827;
  letter-spacing: 0.01em;
  text-align: right;
  margin-left: auto;
}

/* --- Loading / empty states ------------------------------- */
#swym-wishlist-loading {
  padding: 3rem 0;
  text-align: center;
  color: #6b7280;
  font-size: 1.1875rem;
}

#swym-wishlist-empty {
  padding: 4rem 0;
  text-align: center;
}

#swym-wishlist-empty p {
  margin: 0 0 0.5rem;
  color: #374151;
}

#swym-wishlist-empty p:first-child {
  font-size: 1.375rem;
  font-weight: 600;
}

#swym-wishlist-empty p:last-of-type {
  color: #6b7280;
  font-size: 1.125rem;
  margin-bottom: 1.5rem;
}

#swym-wishlist-empty a {
  display: inline-block;
  padding: 11px 22px;
  background: #111827;
  color: #fff;
  border-radius: 6px;
  font-size: 1.125rem;
  font-weight: 500;
  text-decoration: none;
  transition: background 0.15s;
}

#swym-wishlist-empty a:hover {
  background: #374151;
}

/* --- Nudges ----------------------------------------------- */
#swym-login-nudge {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 13px 18px;
  margin-bottom: 1.5rem;
  background: #f0f9ff;
  border: 1px solid #bae6fd;
  border-radius: 8px;
  font-size: 1.125rem;
  color: #0369a1;
}

#swym-login-nudge p {
  margin: 0;
  flex: 1;
}

#swym-login-nudge a {
  font-weight: 600;
  color: #0369a1;
  text-decoration: underline;
}

#swym-save-nudge {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 13px 18px;
  margin-bottom: 1.5rem;
  background: #fffbeb;
  border: 1px solid #fde68a;
  border-radius: 8px;
  font-size: 1.125rem;
  color: #92400e;
}

#swym-save-nudge p {
  margin: 0;
  flex: 1;
  line-height: 1.5;
}

#swym-save-nudge a {
  color: #92400e;
  font-weight: 600;
}

#swym-save-nudge-dismiss {
  flex-shrink: 0;
  padding: 0 4px;
  background: none;
  border: none;
  font-size: 1.375rem;
  line-height: 1;
  cursor: pointer;
  color: #92400e;
  opacity: 0.7;
}

#swym-save-nudge-dismiss:hover {
  opacity: 1;
}

/* --- Product grid ----------------------------------------- */
#swym-wishlist-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  align-items: start;
  gap: 1.5rem;
  margin-top: 2rem;
}

#swym-wishlist-grid[aria-busy="true"] {
  min-height: 600px;
}

@media (max-width: 640px) {
  #swym-wishlist-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 1rem;
  }
}

/* --- Product card ----------------------------------------- */
.swym-wishlist-card {
  display: flex;
  flex-direction: column;
  border: 1px solid #e5e7eb;
  border-radius: 10px;
  overflow: hidden;
  background: #fff;
  transition: box-shadow 0.15s;
}

.swym-wishlist-card:hover {
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}

.swym-wishlist-card > a:first-child {
  display: block;
  aspect-ratio: 1 / 1;
  overflow: hidden;
  background: #f9fafb;
}

.swym-wishlist-card > a:first-child img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 0.2s;
}

.swym-wishlist-card:hover > a:first-child img {
  transform: scale(1.03);
}

.swym-wishlist-card > a:nth-child(2) {
  display: block;
  padding: 14px 14px 4px;
  font-size: 1.125rem;
  font-weight: 500;
  color: #111827;
  text-decoration: none;
  line-height: 1.4;
}

.swym-wishlist-card > a:nth-child(2):hover {
  text-decoration: underline;
}

.swym-wishlist-card > span {
  padding: 4px 14px 0;
  font-size: 1.1875rem;
  font-weight: 600;
  color: #111827;
}

/* --- Card actions ----------------------------------------- */
.swym-wishlist-card-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 12px 14px 14px;
  margin-top: auto;
}

.swym-wishlist-cart-btn {
  flex: 1;
  padding: 9px 12px;
  background: #111827;
  color: #fff;
  border: none;
  border-radius: 6px;
  font-size: 1.0625rem;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.15s;
  white-space: nowrap;
}

.swym-wishlist-cart-btn:hover:not(:disabled) {
  background: #374151;
}

.swym-wishlist-cart-btn:disabled {
  background: #9ca3af;
  cursor: not-allowed;
}

.swym-wishlist-remove-btn {
  flex-shrink: 0;
  width: 38px;
  height: 38px;
  padding: 0;
  background: none;
  border: 1px solid #e5e7eb;
  border-radius: 6px;
  font-size: 1.375rem;
  line-height: 1;
  cursor: pointer;
  color: #6b7280;
  transition: color 0.15s, border-color 0.15s;
}

.swym-wishlist-remove-btn:hover {
  color: #ef4444;
  border-color: #fca5a5;
}

/* --- Add All to Cart button -------------------------------- */
#swym-add-all-btn {
  padding: 9px 18px;
  background: #111827;
  color: #fff;
  border: none;
  border-radius: 6px;
  font-size: 1.125rem;
  font-weight: 500;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.15s;
}

#swym-add-all-btn:hover:not(:disabled) {
  background: #374151;
}

#swym-add-all-btn:disabled {
  background: #9ca3af;
  cursor: not-allowed;
}

/* --- Quantity stepper ------------------------------------- */
.swym-wishlist-qty {
  display: flex;
  align-items: center;
  flex-basis: 100%;
  gap: 0;
  border: 1px solid #d1d5db;
  border-radius: 6px;
  overflow: hidden;
  height: 36px;
}

.swym-wishlist-qty button {
  flex-shrink: 0;
  width: 36px;
  height: 100%;
  padding: 0;
  background: #f9fafb;
  border: none;
  font-size: 1.25rem;
  line-height: 1;
  cursor: pointer;
  color: #374151;
  transition: background 0.15s;
}

.swym-wishlist-qty button:hover {
  background: #f3f4f6;
}

.swym-wishlist-qty-value {
  flex: 1;
  text-align: center;
  font-size: 1.0625rem;
  font-weight: 500;
  color: #111827;
  border-left: 1px solid #d1d5db;
  border-right: 1px solid #d1d5db;
  line-height: 36px;
  min-width: 32px;
  user-select: none;
}

/* --- Item note -------------------------------------------- */
.swym-wishlist-note-label {
  flex-basis: 100%;
  font-size: 1rem;
  font-weight: 500;
  color: #374151;
  margin-bottom: -2px;
}

.swym-wishlist-note {
  flex: 1 0 100%;
  padding: 8px 10px;
  border: 1px solid #d1d5db;
  border-radius: 6px;
  font-size: 1.0625rem;
  color: #111827;
  background: #fff;
  resize: vertical;
  min-height: 60px;
  font-family: inherit;
  line-height: 1.4;
}

.swym-wishlist-note:focus {
  outline: 2px solid #6366f1;
  outline-offset: 2px;
}

.swym-wishlist-note::placeholder {
  color: #9ca3af;
}

/* --- Wishlist table --------------------------------------- */
#swym-wishlist-table-wrapper {
  margin-top: 2rem;
}

/* Columns: img | product | qty | price | note | actions */
.swym-table-header,
.swym-table-row {
  display: grid;
  grid-template-columns: 160px 2fr 120px 90px 1fr 160px;
  align-items: center;
}

.swym-table-header {
  border-bottom: 2px solid #e5e7eb;
}

.swym-table-header > div {
  display: block; /* prevent theme :empty rules from collapsing empty cells */
  padding: 8px 12px;
  font-size: 0.9375rem;
  font-weight: 600;
  color: #6b7280;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
}

.swym-table-row {
  border-bottom: 1px solid #f3f4f6;
  transition: background 0.1s;
}

.swym-table-row:hover {
  background: #f9fafb;
}

.swym-table-col-img,
.swym-table-col-product,
.swym-table-col-qty,
.swym-table-col-price,
.swym-table-col-note,
.swym-table-col-actions {
  padding: 12px;
}

.swym-table-col-img img {
  width: 150px;
  height: 150px;
  object-fit: cover;
  border-radius: 6px;
  display: block;
}

.swym-table-col-product a {
  font-weight: 500;
  color: #111827;
  text-decoration: none;
  display: block;
}

.swym-table-col-product a:hover {
  text-decoration: underline;
}

.swym-table-variant-title {
  display: block;
  font-size: 0.9375rem;
  color: #6b7280;
  margin-top: 3px;
}

.swym-table-line-price {
  font-size: 1.0625rem;
  font-weight: 600;
  color: #111827;
  white-space: nowrap;
}

.swym-table-note {
  min-height: 38px;
}

.swym-table-actions-inner {
  display: flex;
  gap: 6px;
  align-items: center;
}

.swym-table-actions-inner .swym-wishlist-cart-btn {
  flex: 1;
  padding: 12px;
  font-size: 1.0625rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}

@media (max-width: 768px) {
  .swym-table-header {
    display: none;
  }

  /* Layout per row:
     [img 33%]  Title        [×]
     [img     ] [-] qty [+]  $price
     [img     ] [Add to Cart ──────] */
  .swym-table-row {
    grid-template-columns: 33% 1fr auto;
    grid-template-rows: auto auto auto;
    gap: 8px 12px;
    padding: 12px 0;
    align-items: start;
  }

  .swym-table-col-img {
    grid-column: 1;
    grid-row: 1 / 4;
    align-self: stretch;
    padding: 0;
  }

  .swym-table-col-img img {
    width: 100%;
    height: auto;
    aspect-ratio: 1 / 1;
    object-fit: cover;
    border-radius: 6px;
  }

  .swym-table-col-product {
    grid-column: 2;
    grid-row: 1;
    padding: 0;
    align-self: center;
  }

  .swym-table-col-qty {
    grid-column: 2;
    grid-row: 2;
    padding: 0;
  }

  .swym-table-col-qty .swym-wishlist-qty {
    height: 30px;
  }

  .swym-table-col-qty .swym-wishlist-qty-value {
    line-height: 30px;
  }

  .swym-table-col-qty .swym-wishlist-qty button {
    width: 30px;
  }

  .swym-table-col-price {
    grid-column: 3;
    grid-row: 2;
    padding: 0;
    text-align: right;
    align-self: center;
  }

  .swym-table-col-note {
    display: none;
  }

  /* Dissolve the actions wrapper so each button becomes a direct grid item */
  .swym-table-col-actions,
  .swym-table-col-actions .swym-table-actions-inner {
    display: contents;
  }

  .swym-table-col-actions .swym-wishlist-remove-btn {
    grid-column: 3;
    grid-row: 1;
    align-self: start;
    padding: 2px 6px;
    font-size: 1.125rem;
    line-height: 1;
  }

  .swym-table-col-actions .swym-wishlist-cart-btn {
    grid-column: 2 / 4;
    grid-row: 3;
    width: 100%;
    padding: 12px;
    font-size: 1.0625rem;
    font-weight: 600;
    letter-spacing: 0.02em;
  }
}

/* --- Share modal ------------------------------------------ */
#swym-share-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 1.25rem;
}

#swym-share-modal-title {
  margin: 0;
  font-size: 1.375rem;
  font-weight: 600;
  color: #111827;
}

#swym-share-modal-close {
  padding: 4px 8px;
  background: none;
  border: none;
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
  color: #6b7280;
  border-radius: 4px;
  transition: background 0.15s, color 0.15s;
}

#swym-share-modal-close:hover {
  background: #f3f4f6;
  color: #111827;
}

.swym-share-section {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-bottom: 1.25rem;
}

.swym-share-section label {
  font-size: 1.0625rem;
  font-weight: 500;
  color: #374151;
}

.swym-share-section input,
.swym-share-section textarea {
  padding: 9px 12px;
  border: 1px solid #d1d5db;
  border-radius: 6px;
  font-size: 1.125rem;
  color: #111827;
  background: #fff;
  resize: vertical;
}

.swym-share-section input:focus,
.swym-share-section textarea:focus {
  outline: 2px solid #6366f1;
  outline-offset: 2px;
}

.swym-share-section textarea {
  min-height: 72px;
}

#swym-share-email-btn {
  align-self: flex-start;
  padding: 10px 20px;
  background: #111827;
  color: #fff;
  border: none;
  border-radius: 6px;
  font-size: 1.125rem;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.15s;
}

#swym-share-email-btn:hover:not(:disabled) {
  background: #374151;
}

#swym-share-email-btn:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

#swym-share-email-feedback,
#swym-share-copy-feedback {
  font-size: 1.0625rem;
  color: #16a34a;
  font-weight: 500;
}

.swym-share-divider-label {
  margin: 0 0 8px;
  font-size: 1.0625rem;
  color: #9ca3af;
  text-align: center;
  position: relative;
}

.swym-share-divider-label::before,
.swym-share-divider-label::after {
  content: '';
  position: absolute;
  top: 50%;
  width: 38%;
  height: 1px;
  background: #e5e7eb;
}

.swym-share-divider-label::before { left: 0; }
.swym-share-divider-label::after  { right: 0; }

.swym-share-social-row {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

.swym-share-social-btn,
#swym-share-copy-btn {
  flex: 1;
  padding: 9px 14px;
  border: 1px solid #d1d5db;
  border-radius: 6px;
  background: #fff;
  font-size: 1.0625rem;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
  white-space: nowrap;
}

.swym-share-social-btn:hover,
#swym-share-copy-btn:hover:not(:disabled) {
  background: #f3f4f6;
  border-color: #9ca3af;
}

#swym-share-copy-btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

.swym-wishlist-collections-v2-container {
  display: none !important;
}

The selectors used in this implementation are:

SelectorPurpose
.swym-page-headerFlex wrapper for the page header
.swym-header-titleTitle and count side of the header
.swym-header-controlsControls side of the header (view toggle, Add All, Share)
#swym-lists-gridGrid of list tiles on the landing view
.swym-list-tileIndividual list tile button
.swym-list-tile-imagesImage strip inside a tile
.swym-list-tile-infoName and count inside a tile
#swym-wishlist-barBar holding the back button and running total
#swym-back-to-lists"Back to all Lists" button
#swym-wishlist-totalRunning total display
#swym-add-all-btnAdd All to Cart button in the header controls
#swym-view-toggleWrapper for the Table / Grid toggle buttons
.swym-view-btnTable and Grid toggle buttons
.swym-wishlist-cardIndividual product card
.swym-wishlist-card-actionsActions container inside each card
.swym-wishlist-qtyWrapper for decrease button, quantity display, increase button
.swym-wishlist-qty-valueQuantity number display
.swym-wishlist-note-label"Note" label above the textarea
.swym-wishlist-noteNote textarea
.swym-wishlist-cart-btnAdd to Cart button; [disabled] state = out of stock
.swym-wishlist-remove-btnRemove from wishlist button
#swym-share-btnShare Wishlist button in the header controls
#swym-share-modalShare modal overlay wrapper
#swym-share-modal-contentShare modal panel
#swym-share-modal-headerShare modal title row
.swym-share-sectionShare modal content section (email form, social row)
.swym-share-social-rowRow of social share buttons
.swym-share-social-btnIndividual social share button
.swym-share-divider-label"or Share via" label between sections
#swym-login-nudgeSign-in prompt for anonymous users
#swym-save-nudge"Don't lose your list" banner for anonymous users with 3+ items