{
  "version": 3,
  "sources": ["../../assets/js/main.js"],
  "sourcesContent": ["// StrangeBooks JavaScript\n\ndocument.addEventListener('DOMContentLoaded', () => {\n    initCookieBanner();\n    initMobileNav();\n    initMobileSearchToggle();\n    initFilterAutoSubmit();\n    initCartButtons();\n    initReviewToggles();\n    initReviewScrollDots();\n    initStickyHeaderOffset();\n    initContactForm();\n    initContactPanelPosition();\n    initSurpriseTracking();\n});\n\n// Pushes to window.dataLayer, which both gtag.js and GTM containers read from \u2014\n// whichever analytics tag is pasted into SiteSettings.head_scripts/body_scripts\n// (see pages/admin.py) picks these up without any extra wiring here.\nfunction pushAnalyticsEvent(eventName, extraParams) {\n    window.dataLayer = window.dataLayer || [];\n    window.dataLayer.push(Object.assign({ event: eventName }, extraParams));\n}\n\n// \"Surprise Me\" hero button (any plain link to /surprise/), and the genre/\"Change it\"\n// pills in the \"Try another\" bar shown on a book page reached via /surprise/\n// (see templates/catalog/copy_detail.html) \u2014 each pill gets its own distinct event.\nfunction initSurpriseTracking() {\n    document.querySelectorAll('a[href=\"/surprise/\"]:not(.try-another-pill)').forEach((link) => {\n        link.addEventListener('click', () => pushAnalyticsEvent('surprise_click'));\n    });\n\n    document.querySelectorAll('.try-another-pill--random').forEach((link) => {\n        link.addEventListener('click', () => pushAnalyticsEvent('surprise_try_another'));\n    });\n\n    document.querySelectorAll('[data-surprise-genre]').forEach((link) => {\n        link.addEventListener('click', () =>\n            pushAnalyticsEvent('surprise_same_genre', { genre: link.dataset.surpriseGenre })\n        );\n    });\n}\n\n// Contact page \"What's it about?\" dropdown: shows/hides the order/book/authors fields\n// that only apply to the matching topic, relabels the message field for Author alert\n// (optional there, required everywhere else), and swaps which .contact-panel-option is\n// shown in the #author-alerts sidebar. Never touches the Turnstile widget \u2014 it has no\n// data-topic-fields attribute, so it's outside every selector this function uses, and\n// stays visible/required for every topic. Without JS all fields stay visible, the General\n// question panel is shown (it's the only one without `hidden` in the server-rendered\n// markup unless ?subject= picked another topic), and the server applies the same\n// per-topic rules in ContactForm.clean().\nfunction initContactForm() {\n    const topicSelect = document.getElementById('id_topic');\n    if (!topicSelect) return;\n\n    const groups = document.querySelectorAll('[data-topic-fields]');\n    const panels = document.querySelectorAll('[data-panel-topic]');\n    const messageLabel = document.querySelector('[data-message-label]');\n    const messageField = document.getElementById('id_message');\n\n    function applyTopic() {\n        const topic = topicSelect.value;\n        groups.forEach((group) => {\n            group.hidden = group.dataset.topicFields !== topic;\n        });\n        panels.forEach((panel) => {\n            panel.hidden = panel.dataset.panelTopic !== topic;\n        });\n\n        const isAuthorAlert = topic === 'author-alert';\n        if (messageLabel) {\n            messageLabel.textContent = isAuthorAlert ? 'Anything else? (optional)' : 'Your message';\n        }\n        if (messageField) {\n            messageField.required = !isAuthorAlert;\n        }\n    }\n\n    topicSelect.addEventListener('change', applyTopic);\n    applyTopic();\n}\n\n// On the contact page, the topic panel (#author-alerts) lives in the desktop sidebar grid\n// by default (see .contact-panel in style.css). Below the same breakpoint that grid uses,\n// this moves that single node to sit right after the \"What's it about?\" dropdown instead,\n// so the visitor sees the panel change as they pick a topic, and moves it back above that\n// width \u2014 there's only ever one copy, so the topic-switching logic in initContactForm and\n// the aria-live region both keep working on whichever copy is currently in the DOM.\nfunction initContactPanelPosition() {\n    const panel = document.getElementById('author-alerts');\n    const topicGroup = document.getElementById('topic-field-group');\n    if (!panel || !topicGroup) return;\n\n    const anchor = document.createComment('contact-panel-anchor');\n    panel.before(anchor);\n\n    function applyPosition(isMobile) {\n        if (isMobile) {\n            topicGroup.after(panel);\n        } else {\n            anchor.after(panel);\n        }\n    }\n\n    const mql = window.matchMedia('(max-width: 700px)');\n    applyPosition(mql.matches);\n    mql.addEventListener('change', (e) => applyPosition(e.matches));\n}\n\n// .site-top (header + announcement bar + category nav, etc.) is sticky at top:0 and its\n// height varies \u2014 with/without the announcement bar, mobile search/nav open, breakpoint.\n// Keeps anchor-link targets (e.g. a hero button scrolling to an in-page section) landing\n// below it instead of hidden underneath, on every resize/open/close, not just page load.\nfunction initStickyHeaderOffset() {\n    const siteTop = document.querySelector('.site-top');\n    if (!siteTop) return;\n\n    function applyOffset() {\n        document.documentElement.style.scrollPaddingTop = `${siteTop.offsetHeight}px`;\n    }\n\n    applyOffset();\n    if (window.ResizeObserver) {\n        new ResizeObserver(applyOffset).observe(siteTop);\n    } else {\n        window.addEventListener('resize', applyOffset);\n    }\n}\n\n// Mobile search toggle: tapping the search icon reveals the search bar below the header\nfunction initMobileSearchToggle() {\n    const toggle = document.querySelector('.search-toggle');\n    const form = document.querySelector('.search-form');\n    const closeBtn = document.querySelector('.search-close');\n    const nav = document.querySelector('.nav');\n    const navToggle = document.querySelector('.nav-toggle');\n    if (!toggle || !form) return;\n\n    function closeSearch() {\n        form.classList.remove('open');\n        toggle.classList.remove('active');\n        toggle.setAttribute('aria-expanded', 'false');\n    }\n\n    function openSearch() {\n        form.classList.add('open');\n        toggle.classList.add('active');\n        toggle.setAttribute('aria-expanded', 'true');\n        // Only one panel open at a time on mobile\n        if (nav && navToggle) {\n            nav.classList.remove('open');\n            navToggle.classList.remove('active');\n            navToggle.setAttribute('aria-expanded', 'false');\n            document.body.classList.remove('menu-open');\n        }\n        const input = form.querySelector('.search-input');\n        if (input) input.focus();\n    }\n\n    toggle.addEventListener('click', () => {\n        if (form.classList.contains('open')) {\n            closeSearch();\n        } else {\n            openSearch();\n        }\n    });\n\n    if (closeBtn) {\n        closeBtn.addEventListener('click', closeSearch);\n    }\n\n    // Only one panel open at a time on mobile\n    if (navToggle) {\n        navToggle.addEventListener('click', closeSearch);\n    }\n\n    document.addEventListener('click', (e) => {\n        if (!e.target.closest('.header')) closeSearch();\n    });\n\n    document.addEventListener('keydown', (e) => {\n        if (e.key === 'Escape') closeSearch();\n    });\n}\n\n// Mobile navigation\nfunction initMobileNav() {\n    const toggle = document.querySelector('.nav-toggle');\n    const nav = document.querySelector('.nav');\n    if (!toggle || !nav) return;\n\n    const mobileQuery = window.matchMedia('(max-width: 768px)');\n\n    // Hamburger toggle\n    toggle.addEventListener('click', () => {\n        const isOpen = nav.classList.toggle('open');\n        toggle.classList.toggle('active', isOpen);\n        toggle.setAttribute('aria-expanded', String(isOpen));\n        document.body.classList.toggle('menu-open', isOpen);\n    });\n\n    // Dropdown tap handling (Explore menu)\n    const dropdownToggles = nav.querySelectorAll('.nav-dropdown-toggle');\n    dropdownToggles.forEach((dt) => {\n        dt.addEventListener('click', (e) => {\n            if (!mobileQuery.matches) return;\n            e.preventDefault();\n            const dropdown = dt.closest('.nav-dropdown');\n            const isOpen = dropdown.classList.toggle('open');\n            dt.setAttribute('aria-expanded', String(isOpen));\n\n            // Close other dropdowns\n            dropdownToggles.forEach((other) => {\n                if (other !== dt) {\n                    const otherDropdown = other.closest('.nav-dropdown');\n                    otherDropdown.classList.remove('open');\n                    other.setAttribute('aria-expanded', 'false');\n                }\n            });\n        });\n    });\n\n    // Mobile category accordion (inside hamburger)\n    const mobileCatToggles = nav.querySelectorAll('.mobile-cat-toggle');\n    mobileCatToggles.forEach((btn) => {\n        btn.addEventListener('click', () => {\n            const group = btn.closest('.mobile-cat-group');\n            const isOpen = group.classList.toggle('open');\n            btn.setAttribute('aria-expanded', String(isOpen));\n\n            // Close other open groups\n            mobileCatToggles.forEach((other) => {\n                if (other !== btn) {\n                    const otherGroup = other.closest('.mobile-cat-group');\n                    otherGroup.classList.remove('open');\n                    other.setAttribute('aria-expanded', 'false');\n                }\n            });\n        });\n    });\n\n    // Category submenu tap handling\n    const categoryItems = document.querySelectorAll('.category-list > li > a');\n    categoryItems.forEach((link) => {\n        const subcategoryList = link.nextElementSibling;\n        if (!subcategoryList || !subcategoryList.classList.contains('subcategory-list')) return;\n\n        link.addEventListener('click', (e) => {\n            if (!mobileQuery.matches) return;\n            if (!subcategoryList.classList.contains('open')) {\n                // First tap: open submenu, prevent navigation\n                e.preventDefault();\n                // Close other subcategory lists\n                document.querySelectorAll('.subcategory-list.open').forEach((openList) => {\n                    if (openList !== subcategoryList) openList.classList.remove('open');\n                });\n                subcategoryList.classList.add('open');\n            }\n            // Second tap: allow navigation (default behavior)\n        });\n    });\n\n    // Close on outside tap\n    document.addEventListener('click', (e) => {\n        if (!mobileQuery.matches) return;\n        // Close nav dropdowns if clicking outside\n        if (!e.target.closest('.nav-dropdown')) {\n            nav.querySelectorAll('.nav-dropdown.open').forEach((d) => {\n                d.classList.remove('open');\n                const dt = d.querySelector('.nav-dropdown-toggle');\n                if (dt) dt.setAttribute('aria-expanded', 'false');\n            });\n        }\n        // Close subcategories if clicking outside category nav\n        if (!e.target.closest('.category-list')) {\n            document.querySelectorAll('.subcategory-list.open').forEach((s) => {\n                s.classList.remove('open');\n            });\n        }\n        // Close hamburger menu if clicking outside header\n        if (!e.target.closest('.header')) {\n            nav.classList.remove('open');\n            toggle.classList.remove('active');\n            toggle.setAttribute('aria-expanded', 'false');\n            document.body.classList.remove('menu-open');\n        }\n    });\n\n    // Close on escape key\n    document.addEventListener('keydown', (e) => {\n        if (e.key !== 'Escape') return;\n        nav.classList.remove('open');\n        toggle.classList.remove('active');\n        toggle.setAttribute('aria-expanded', 'false');\n        document.body.classList.remove('menu-open');\n        nav.querySelectorAll('.nav-dropdown.open').forEach((d) => {\n            d.classList.remove('open');\n            const dt = d.querySelector('.nav-dropdown-toggle');\n            if (dt) dt.setAttribute('aria-expanded', 'false');\n        });\n        document.querySelectorAll('.subcategory-list.open').forEach((s) => {\n            s.classList.remove('open');\n        });\n        nav.querySelectorAll('.mobile-cat-group.open').forEach((g) => {\n            g.classList.remove('open');\n            const btn = g.querySelector('.mobile-cat-toggle');\n            if (btn) btn.setAttribute('aria-expanded', 'false');\n        });\n    });\n\n    // Reset state when resizing past breakpoint\n    mobileQuery.addEventListener('change', () => {\n        if (!mobileQuery.matches) {\n            nav.classList.remove('open');\n            toggle.classList.remove('active');\n            toggle.setAttribute('aria-expanded', 'false');\n            document.body.classList.remove('menu-open');\n            nav.querySelectorAll('.nav-dropdown.open').forEach((d) => {\n                d.classList.remove('open');\n                const dt = d.querySelector('.nav-dropdown-toggle');\n                if (dt) dt.setAttribute('aria-expanded', 'false');\n            });\n            document.querySelectorAll('.subcategory-list.open').forEach((s) => {\n                s.classList.remove('open');\n            });\n            nav.querySelectorAll('.mobile-cat-group.open').forEach((g) => {\n                g.classList.remove('open');\n                const btn = g.querySelector('.mobile-cat-toggle');\n                if (btn) btn.setAttribute('aria-expanded', 'false');\n            });\n        }\n    });\n}\n\n// Auto-submit filter/sort dropdowns (CSP-safe alternative to inline onchange).\n// A select with [data-navigate] (e.g. the Category select on the products list,\n// now that categories are real paths) navigates straight to its selected\n// option's URL instead of submitting the form.\nfunction initFilterAutoSubmit() {\n    document.querySelectorAll('.filters select').forEach((select) => {\n        select.addEventListener('change', () => {\n            if (select.dataset.navigate) {\n                window.location.href = select.value;\n            } else {\n                select.form.submit();\n            }\n        });\n    });\n}\n\n// AJAX cart buttons \u2014 progressive enhancement for add/remove/increment/decrement\nconst CART_ACTION_RE = /\\/cart\\/(add|remove|increment|decrement)\\//;\n// Same URLs, but also captures product type + ref so cart rows and\n// recommendation cards for the same product can be matched up client-side.\nconst CART_URL_RE = /\\/cart\\/(add|remove|increment|decrement)\\/([^/]+)\\/([^/]+)\\//;\n\nlet pendingCartForm = null;\nlet pendingCartAction = null;\nlet cartTurnstileWidgetId = null;\nlet cartTurnstilePassed = false;\n\nfunction initCartButtons() {\n    document.addEventListener('submit', (e) => {\n        const form = e.target;\n        if (!form.action || !CART_ACTION_RE.test(form.action)) return;\n\n        const match = form.action.match(CART_ACTION_RE);\n        if (!match) return;\n\n        e.preventDefault();\n        handleCartSubmit(form, match[1]);\n    });\n\n    initCartTurnstileModal();\n\n    // Added after the initial paint so the fill's width transition only ever\n    // animates live updates, never the server-rendered first-load width.\n    const fill = document.querySelector('[data-shipping-fill]');\n    if (fill) fill.classList.add('shipping-fill--animated');\n}\n\n// Review card \"Read more\" toggle\nfunction initReviewToggles() {\n    document.addEventListener('click', (e) => {\n        const btn = e.target.closest('.review-card-toggle');\n        if (!btn) return;\n\n        const comment = btn.closest('.review-card-comment');\n        const preview = comment.querySelector('.review-card-comment-preview');\n        const full = comment.querySelector('.review-card-comment-full');\n        const expanding = full.hidden;\n\n        full.hidden = !expanding;\n        preview.hidden = expanding;\n        btn.textContent = expanding ? 'Read less' : 'Read more';\n    });\n}\n\n// Dots under the homepage's mobile reviews swipe row, synced to scroll position\nfunction initReviewScrollDots() {\n    const track = document.querySelector('.review-grid[data-scroll-dots]');\n    if (!track) return;\n\n    const cards = Array.from(track.children);\n    if (cards.length <= 1) return;\n\n    const dotsContainer = document.createElement('div');\n    dotsContainer.className = 'review-scroll-dots';\n    dotsContainer.setAttribute('role', 'tablist');\n    dotsContainer.setAttribute('aria-label', 'Review position');\n\n    cards.forEach((card, i) => {\n        const dot = document.createElement('button');\n        dot.type = 'button';\n        dot.className = 'review-scroll-dot' + (i === 0 ? ' active' : '');\n        dot.setAttribute('role', 'tab');\n        dot.setAttribute('aria-selected', i === 0 ? 'true' : 'false');\n        dot.setAttribute('aria-label', `Show review ${i + 1} of ${cards.length}`);\n        dot.addEventListener('click', () => {\n            track.scrollTo({ left: card.offsetLeft - track.offsetLeft, behavior: 'smooth' });\n        });\n        dotsContainer.appendChild(dot);\n    });\n\n    track.insertAdjacentElement('afterend', dotsContainer);\n\n    let scrollTimeout;\n    track.addEventListener('scroll', () => {\n        clearTimeout(scrollTimeout);\n        scrollTimeout = setTimeout(() => {\n            let closestIndex = 0;\n            let closestDistance = Infinity;\n            cards.forEach((card, i) => {\n                const distance = Math.abs(card.offsetLeft - track.scrollLeft - track.offsetLeft);\n                if (distance < closestDistance) {\n                    closestDistance = distance;\n                    closestIndex = i;\n                }\n            });\n            dotsContainer.querySelectorAll('.review-scroll-dot').forEach((dot, i) => {\n                dot.classList.toggle('active', i === closestIndex);\n                dot.setAttribute('aria-selected', i === closestIndex ? 'true' : 'false');\n            });\n        }, 100);\n    });\n}\n\nfunction getCsrfToken() {\n    const input = document.querySelector('[name=csrfmiddlewaretoken]');\n    if (input) return input.value;\n    const cookie = document.cookie.match(/csrftoken=([^;]+)/);\n    return cookie ? cookie[1] : '';\n}\n\nfunction setCartTurnstileToken(form, token) {\n    let input = form.querySelector('input[name=\"cf-turnstile-response\"]');\n    if (!input) {\n        input = document.createElement('input');\n        input.type = 'hidden';\n        input.name = 'cf-turnstile-response';\n        form.appendChild(input);\n    }\n    input.value = token || '';\n}\n\nfunction initCartTurnstileModal() {\n    const modal = document.getElementById('cart-turnstile-modal');\n    if (!modal) return;\n\n    cartTurnstilePassed = modal.getAttribute('data-required') === 'false';\n\n    modal.querySelectorAll('[data-cart-turnstile-dismiss]').forEach((el) => {\n        el.addEventListener('click', () => hideCartTurnstileModal(true));\n    });\n\n    document.addEventListener('keydown', (e) => {\n        if (e.key === 'Escape' && !modal.hidden) {\n            hideCartTurnstileModal(true);\n        }\n    });\n}\n\nfunction showCartTurnstileModal(form, action) {\n    const modal = document.getElementById('cart-turnstile-modal');\n    if (!modal) {\n        // Fallback: non-AJAX submit so server can redirect with a message\n        form.submit();\n        return;\n    }\n\n    pendingCartForm = form;\n    pendingCartAction = action;\n\n    const errorEl = document.getElementById('cart-turnstile-error');\n    if (errorEl) {\n        errorEl.hidden = true;\n        errorEl.textContent = '';\n    }\n\n    modal.hidden = false;\n    document.body.classList.add('cart-turnstile-open');\n\n    const sitekey = modal.getAttribute('data-sitekey');\n    const widgetHost = document.getElementById('cart-turnstile-widget');\n    if (!widgetHost || !sitekey) return;\n\n    const renderWidget = () => {\n        if (typeof turnstile === 'undefined') return false;\n        widgetHost.innerHTML = '';\n        cartTurnstileWidgetId = turnstile.render(widgetHost, {\n            sitekey,\n            callback: (token) => {\n                if (!pendingCartForm) return;\n                setCartTurnstileToken(pendingCartForm, token);\n                const formToSubmit = pendingCartForm;\n                const actionToRun = pendingCartAction;\n                hideCartTurnstileModal(false);\n                handleCartSubmit(formToSubmit, actionToRun);\n            },\n            'error-callback': () => {\n                if (errorEl) {\n                    errorEl.textContent = 'Verification failed. Please try again.';\n                    errorEl.hidden = false;\n                }\n            },\n            'expired-callback': () => {\n                if (pendingCartForm) setCartTurnstileToken(pendingCartForm, '');\n            },\n        });\n        return true;\n    };\n\n    if (!renderWidget()) {\n        // Script still loading \u2014 poll briefly\n        let attempts = 0;\n        const timer = setInterval(() => {\n            attempts += 1;\n            if (renderWidget() || attempts > 40) clearInterval(timer);\n        }, 100);\n    }\n}\n\nfunction hideCartTurnstileModal(cancelled) {\n    const modal = document.getElementById('cart-turnstile-modal');\n    if (modal) {\n        modal.hidden = true;\n    }\n    document.body.classList.remove('cart-turnstile-open');\n\n    if (typeof turnstile !== 'undefined' && cartTurnstileWidgetId !== null) {\n        try {\n            turnstile.remove(cartTurnstileWidgetId);\n        } catch {\n            /* ignore */\n        }\n        cartTurnstileWidgetId = null;\n    }\n\n    if (cancelled && pendingCartForm) {\n        const btn = pendingCartForm.querySelector('button[type=\"submit\"]');\n        if (btn) {\n            btn.disabled = false;\n            btn.classList.remove('btn-loading');\n        }\n    }\n\n    if (cancelled) {\n        pendingCartForm = null;\n        pendingCartAction = null;\n    }\n}\n\nasync function handleCartSubmit(form, action) {\n    const btn = form.querySelector('button[type=\"submit\"]');\n    if (!btn || btn.disabled) return;\n\n    // Double-click prevention\n    btn.disabled = true;\n    btn.classList.add('btn-loading');\n    const originalText = btn.textContent;\n\n    try {\n        const res = await fetch(form.action, {\n            method: 'POST',\n            headers: {\n                'X-CSRFToken': getCsrfToken(),\n                'X-Requested-With': 'XMLHttpRequest',\n            },\n            body: new FormData(form),\n        });\n\n        if (!res.ok) throw new Error(`HTTP ${res.status}`);\n        const data = await res.json();\n\n        if (!data.success && data.turnstile_required) {\n            // Clear spent/invalid token and prompt for a fresh challenge\n            setCartTurnstileToken(form, '');\n            btn.classList.remove('btn-loading');\n            btn.disabled = false;\n            showCartTurnstileModal(form, action);\n            return;\n        }\n\n        if (!data.success && data.reserved_by_other) {\n            btn.textContent = 'Reserved';\n            btn.classList.remove('btn-loading', 'btn-primary');\n            btn.classList.add('btn-secondary');\n            // Leave btn.disabled = true (set above), skip finally re-enable\n            updateCartHeader(data);\n            return;\n        }\n\n        // Successful cart mutation (or already-in-cart) clears the need for another challenge\n        if (data.success || !data.turnstile_required) {\n            cartTurnstilePassed = true;\n            pendingCartForm = null;\n            pendingCartAction = null;\n        }\n\n        updateCartHeader(data);\n        updateAnnouncementBar(data);\n\n        const isCartPage = document.querySelector('.cart-table') !== null;\n\n        if (isCartPage && action === 'remove') {\n            handleCartPageRemove(form, data);\n        } else if (isCartPage && action === 'add') {\n            handleCartPageAdd(form, data);\n        } else if (isCartPage && (action === 'increment' || action === 'decrement')) {\n            // Reload for +/- on cart page (server computes coupon/promo totals)\n            window.location.reload();\n        } else if (action === 'add') {\n            toggleButtonState(form, 'add');\n        } else if (action === 'remove') {\n            toggleButtonState(form, 'remove');\n        }\n\n        btn.classList.remove('btn-loading');\n        btn.disabled = false;\n    } catch (err) {\n        // Restore button on network/server error so user can retry\n        btn.textContent = originalText;\n        btn.classList.remove('btn-loading');\n        btn.disabled = false;\n    }\n}\n\nfunction updateCartHeader(data) {\n    if (data.cart_count === undefined || !data.total) return;\n    const span = document.querySelector('.nav .cart-link span');\n    if (span) {\n        span.textContent = `${data.cart_count} | ${data.total}`;\n    }\n    const mobileLink = document.querySelector('.cart-link--mobile');\n    if (mobileLink) {\n        const mobileCount = mobileLink.querySelector('.cart-link-count');\n        if (mobileCount) {\n            mobileCount.textContent = data.cart_count;\n        }\n        mobileLink.setAttribute('aria-label', `Basket, ${data.cart_count} item${data.cart_count === 1 ? '' : 's'}`);\n    }\n}\n\n// Mirrors pages.context_processors.announcement_bar's delivery-message logic \u2014 keep the two\n// in sync if either changes. Every mutation that can change the cart total (add, remove,\n// increment, decrement) sends shipping_progress; a response that doesn't (already-in-cart,\n// cart-full, reservation-denied) means the total didn't change, so this leaves the bar's\n// current tight/not-tight state untouched instead of guessing.\nfunction updateAnnouncementBar(data) {\n    const bar = document.querySelector('[data-announcement-bar]');\n    if (!bar || data.cart_count === undefined) return;\n\n    const progress = data.shipping_progress;\n    const deliveryItem = bar.querySelector('[data-announcement-delivery]');\n    if (!deliveryItem || !progress) return;\n\n    const qualifies = data.cart_count > 0 && progress.applied;\n    let tight = false;\n    let text;\n    if (data.cart_count === 0) {\n        text = deliveryItem.dataset.announcementEmptyText;\n    } else if (qualifies) {\n        text = 'Your order qualifies for free UK delivery';\n    } else {\n        text = `You're ${progress.remaining} away from free UK delivery`;\n        tight = true;\n    }\n\n    const textEl = deliveryItem.querySelector('[data-announcement-text]');\n    if (textEl && text) textEl.textContent = text;\n\n    const truckIcon = deliveryItem.querySelector('[data-announcement-icon=\"truck\"]');\n    if (truckIcon) truckIcon.hidden = qualifies;\n    const tickIcon = deliveryItem.querySelector('[data-announcement-icon=\"tick\"]');\n    if (tickIcon) tickIcon.hidden = !qualifies;\n\n    bar.classList.toggle('announcement-bar--tight', tight);\n}\n\nfunction toggleButtonState(form, completedAction) {\n    const btn = form.querySelector('button[type=\"submit\"]');\n    if (!btn) return;\n\n    if (completedAction === 'add') {\n        // Replace form with a \"View Cart\" link\n        const link = document.createElement('a');\n        link.href = '/cart/';\n        link.className = 'btn btn-secondary btn-block';\n        link.textContent = 'View Cart';\n        form.replaceWith(link);\n    } else if (completedAction === 'remove') {\n        // Switch to \"Add to Cart\" / add mode\n        btn.textContent = 'Add to Cart';\n        btn.classList.remove('btn-secondary');\n        btn.classList.add('btn-primary');\n        form.action = form.action.replace('/cart/remove/', '/cart/add/');\n    }\n}\n\nfunction handleCartPageRemove(form, data) {\n    const row = form.closest('tr');\n    if (!row) return;\n\n    updateCartTotals(data);\n    setRecommendationCardState(toAddUrl(form.action), false);\n\n    row.style.transition = 'opacity 0.3s ease';\n    row.style.opacity = '0';\n    setTimeout(() => {\n        row.remove();\n        // If cart is now empty, reload to show empty state\n        const remaining = document.querySelectorAll('.cart-table tbody tr');\n        if (remaining.length === 0) {\n            window.location.reload();\n        }\n    }, 300);\n}\n\nfunction handleCartPageAdd(form, data) {\n    if (!data.success) return;\n\n    updateCartTotals(data);\n    setRecommendationCardState(toAddUrl(form.action), true);\n\n    const tbody = document.querySelector('.cart-table tbody');\n    if (!tbody || !data.row_html) return;\n\n    tbody.insertAdjacentHTML('beforeend', data.row_html);\n    const newRow = tbody.lastElementChild;\n    if (newRow) {\n        newRow.style.opacity = '0';\n        newRow.style.transition = 'opacity 0.3s ease';\n        requestAnimationFrame(() => {\n            newRow.style.opacity = '1';\n        });\n    }\n}\n\n// Swaps a \"More by These Authors\" recommendation card between its active\n// Add to Cart form and a non-interactive \"In your cart\" state. Cards carry\n// their own add-to-cart URL as data-cart-add-url regardless of which state\n// they're currently showing, so this works both right after a successful\n// add and when a remove elsewhere on the page frees the book back up.\nfunction setRecommendationCardState(addUrl, inCart) {\n    if (!addUrl) return;\n    const card = document.querySelector(`.book-card[data-cart-add-url=\"${addUrl}\"]`);\n    if (!card) return;\n    const footer = card.querySelector('.book-card-footer');\n    if (!footer) return;\n\n    if (inCart) {\n        footer.innerHTML = '<span class=\"btn btn-secondary btn-block btn-disabled\" aria-disabled=\"true\">In your cart</span>';\n    } else {\n        footer.innerHTML =\n            `<form action=\"${addUrl}\" method=\"post\">` +\n            `<input type=\"hidden\" name=\"csrfmiddlewaretoken\" value=\"${getCsrfToken()}\">` +\n            '<button type=\"submit\" class=\"btn btn-primary btn-block\">Add to Cart</button>' +\n            '</form>';\n    }\n}\n\nfunction toAddUrl(url) {\n    const match = url.match(CART_URL_RE);\n    return match ? `/cart/add/${match[2]}/${match[3]}/` : null;\n}\n\nfunction updateCartTotals(data) {\n    const subtotalEl = document.querySelector('.cart-subtotal-value');\n    if (subtotalEl && data.subtotal) subtotalEl.textContent = data.subtotal;\n\n    const finalTotalEl = document.querySelector('.cart-final-total-value');\n    if (finalTotalEl && data.final_total) finalTotalEl.textContent = data.final_total;\n\n    updateShippingProgress(data.shipping_progress);\n}\n\nfunction updateShippingProgress(shippingProgress) {\n    const cta = document.querySelector('[data-shipping-cta]');\n    if (!cta || !shippingProgress) return;\n\n    // The cream text is nested inside the fill element and clipped by the\n    // fill's own overflow:hidden (see static/css/style.css), so this is the\n    // only width in the whole component \u2014 nothing else can fall out of sync\n    // with it.\n    const fill = cta.querySelector('[data-shipping-fill]');\n    if (fill) fill.style.width = `${shippingProgress.pct}%`;\n\n    cta.querySelectorAll('[data-shipping-remaining-amount]').forEach((el) => {\n        if (shippingProgress.remaining) el.textContent = shippingProgress.remaining;\n    });\n    cta.querySelectorAll('[data-shipping-remaining-msg]').forEach((el) => {\n        el.hidden = shippingProgress.applied;\n    });\n    cta.querySelectorAll('[data-shipping-complete-msg]').forEach((el) => {\n        el.hidden = !shippingProgress.applied;\n    });\n}\n\n// Cookie consent banner\nfunction initCookieBanner() {\n    const banner = document.getElementById('cookie-banner');\n    const acceptBtn = document.getElementById('cookie-accept-btn');\n\n    if (!banner || !acceptBtn) return;\n\n    // Check if user has already accepted cookies\n    if (localStorage.getItem('cookieConsent') === 'accepted') {\n        banner.style.display = 'none';\n        return;\n    }\n\n    // Show banner\n    banner.style.display = 'block';\n\n    // Handle accept button click\n    acceptBtn.addEventListener('click', () => {\n        localStorage.setItem('cookieConsent', 'accepted');\n        banner.classList.add('hidden');\n        // Remove from DOM after animation\n        setTimeout(() => {\n            banner.style.display = 'none';\n        }, 300);\n    });\n}\n"],
  "mappings": "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",
  "names": ["initCookieBanner", "initMobileNav", "initMobileSearchToggle", "initFilterAutoSubmit", "initCartButtons", "initReviewToggles", "initReviewScrollDots", "initStickyHeaderOffset", "initContactForm", "initContactPanelPosition", "initSurpriseTracking", "pushAnalyticsEvent", "eventName", "extraParams", "link", "topicSelect", "groups", "panels", "messageLabel", "messageField", "applyTopic", "topic", "group", "panel", "isAuthorAlert", "topicGroup", "anchor", "applyPosition", "isMobile", "mql", "e", "siteTop", "applyOffset", "toggle", "form", "closeBtn", "nav", "navToggle", "closeSearch", "openSearch", "input", "mobileQuery", "isOpen", "dropdownToggles", "dt", "other", "mobileCatToggles", "btn", "subcategoryList", "openList", "d", "s", "g", "select", "CART_ACTION_RE", "CART_URL_RE", "pendingCartForm", "pendingCartAction", "cartTurnstileWidgetId", "cartTurnstilePassed", "match", "handleCartSubmit", "initCartTurnstileModal", "fill", "comment", "preview", "full", "expanding", "track", "cards", "dotsContainer", "card", "i", "dot", "scrollTimeout", "closestIndex", "closestDistance", "distance", "getCsrfToken", "cookie", "setCartTurnstileToken", "token", "modal", "el", "hideCartTurnstileModal", "showCartTurnstileModal", "action", "errorEl", "sitekey", "widgetHost", "renderWidget", "formToSubmit", "actionToRun", "attempts", "timer", "cancelled", "originalText", "res", "data", "updateCartHeader", "updateAnnouncementBar", "isCartPage", "handleCartPageRemove", "handleCartPageAdd", "toggleButtonState", "span", "mobileLink", "mobileCount", "bar", "progress", "deliveryItem", "qualifies", "tight", "text", "textEl", "truckIcon", "tickIcon", "completedAction", "row", "updateCartTotals", "setRecommendationCardState", "toAddUrl", "tbody", "newRow", "addUrl", "inCart", "footer", "url", "subtotalEl", "finalTotalEl", "updateShippingProgress", "shippingProgress", "cta", "banner", "acceptBtn"]
}
