{
  "version": 3,
  "sources": ["shared.js", "core.js"],
  "sourcesContent": ["/**\n * Shared helpers for the Crush.lu Alpine bundles (crush_lu/STYLE.md \u00A77).\n *\n * Imported by the bundle entries in this folder; esbuild inlines what each\n * bundle uses (`npm run build:js`). Keep these stateless: every bundle gets\n * its own copy in production, while DEBUG shares one ES module instance.\n */\n\n// =========================================================================\n// SHARED INTERACTIVITY MIXINS (Phase 5 \u2014 see crush_lu/STYLE.md \u00A77)\n//\n// These factories return plain state-and-method objects that named\n// Alpine.data components compose with the `mixin` helper below. They\n// are NOT Alpine.data registrations because the CSP build cannot pass\n// args from x-data; wrap them in a named component instead.\n//\n// IMPORTANT: use `mixin`, NOT `Object.assign` / spread. Both of those\n// *evaluate* getters on the source object during the copy (with `this`\n// bound to the bare source literal, which lacks the mixin methods),\n// turning a live `get isFoo()` into the literal value `undefined` \u2014\n// or a TypeError when the getter calls `this.somethingFromMixin()`.\n// `mixin` copies descriptors via Object.defineProperties, so accessor\n// properties stay live and resolve against the composed `this`.\n//\n// Usage:\n//   Alpine.data(\"myTabs\", () => mixin(\n//       makeTabs(\"upcoming\", [\"upcoming\", \"past\"]),\n//       { get isUpcoming() { return this.isTabActive(\"upcoming\"); },\n//         showUpcoming() { this.setTab(\"upcoming\"); } }\n//   ));\n// =========================================================================\n\nexport function mixin(target, source) {\n    Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));\n    return target;\n}\n\n// UX Wave 3 \u00B7 WP5 \u2014 route a failure message through the shared toast\n// store instead of a blocking alert(). Falls back to alert() only if\n// the store genuinely isn't registered (defensive; toasts ship on\n// every page via base.html).\nexport function notifyError(message) {\n    if (typeof Alpine !== \"undefined\" && Alpine.store && Alpine.store(\"toasts\")) {\n        Alpine.store(\"toasts\").add({ type: \"error\", message: message });\n    } else {\n        alert(message);\n    }\n}\n\nexport function makeTabs(initial, names) {\n    return {\n        activeTab: initial,\n        _tabNames: names || [],\n        setTab(name) {\n            this.activeTab = name;\n        },\n        isTabActive(name) {\n            return this.activeTab === name;\n        },\n    };\n}\n\nexport function makeConfirm(opts) {\n    var options = opts || {};\n    return {\n        confirming: !!options.initial,\n        get isConfirming() {\n            return this.confirming;\n        },\n        get isIdle() {\n            return !this.confirming;\n        },\n        request() {\n            this.confirming = true;\n        },\n        cancelConfirm() {\n            this.confirming = false;\n        },\n        proceed() {\n            this.confirming = false;\n            if (options.autoSubmit !== false) {\n                var form = this.$el && this.$el.closest(\"form\");\n                if (form) form.submit();\n            }\n        },\n    };\n}\n\nexport function makeModal(initiallyOpen) {\n    return {\n        open: !!initiallyOpen,\n        showModal() {\n            this.open = true;\n        },\n        hideModal() {\n            this.open = false;\n        },\n        toggleModal() {\n            this.open = !this.open;\n        },\n        get isModalOpen() {\n            return this.open;\n        },\n        get isModalClosed() {\n            return !this.open;\n        },\n    };\n}\n", "/**\n * Alpine.js CSP-Compatible Components for Crush.lu\n *\n * The Alpine.js CSP build cannot interpret inline method calls or complex expressions.\n * All interactive components must be registered with Alpine.data() to work properly.\n *\n * IMPORTANT: The CSP build does NOT support:\n * - Inline JavaScript expressions like @click=\"count++\" or @click=\"toggle()\"\n * - Passing parameters to Alpine.data() from x-data attributes\n *\n * Instead, use data attributes to pass initial values and read them in init().\n *\n * Core bundle: shared shell and site-wide components. Feature\n * components live in the sibling entries (coach.js, quiz.js,\n * journey.js, connect.js); pages load them via\n * crush_lu/partials/alpine_bundle.html. Build: `npm run build:js`.\n */\n\nimport { makeConfirm, makeTabs, mixin, notifyError } from \"./shared.js\";\n\ndocument.addEventListener(\"alpine:init\", function () {\n    // Global drawer store for the mobile side menu (partials/mobile_drawer.html).\n    // The drawer is a modal: while it is open the page behind it cannot be\n    // scrolled, Escape closes it, Tab stays inside it, and closing hands focus\n    // back to whatever opened it. Without that a keyboard or screen-reader\n    // user tabs straight out of the menu into a page they cannot see.\n    var DRAWER_FOCUSABLE =\n        'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\n    function drawerPanel() {\n        return document.getElementById(\"mobile-drawer-panel\");\n    }\n\n    function drawerFocusables() {\n        var panel = drawerPanel();\n        if (!panel) return [];\n        return Array.prototype.filter.call(\n            panel.querySelectorAll(DRAWER_FOCUSABLE),\n            function (el) {\n                // Skip anything x-show / hidden has taken out of the layout.\n                return el.getClientRects().length > 0;\n            },\n        );\n    }\n\n    // Kept outside the store: Alpine wraps store values in a reactive proxy,\n    // and a proxied element is not the element document.contains() knows.\n    var drawerOpener = null;\n\n    Alpine.store(\"drawer\", {\n        open: false,\n        init() {\n            var self = this;\n            document.addEventListener(\"keydown\", function (e) {\n                if (!self.open) return;\n                if (e.key === \"Escape\") {\n                    e.preventDefault();\n                    self.close();\n                    return;\n                }\n                if (e.key !== \"Tab\") return;\n                var items = drawerFocusables();\n                if (!items.length) return;\n                var first = items[0];\n                var last = items[items.length - 1];\n                var panel = drawerPanel();\n                var inside = panel && panel.contains(document.activeElement);\n                if (e.shiftKey && (!inside || document.activeElement === first)) {\n                    e.preventDefault();\n                    last.focus();\n                } else if (!e.shiftKey && (!inside || document.activeElement === last)) {\n                    e.preventDefault();\n                    first.focus();\n                }\n            });\n        },\n        show() {\n            if (this.open) return;\n            drawerOpener = document.activeElement;\n            this.open = true;\n            document.body.style.overflow = \"hidden\";\n            // The panel is display:none until Alpine has reacted; focus the\n            // close button once it can take focus.\n            var self = this;\n            setTimeout(function () {\n                // Closed again already (a quick Escape): the panel is still on\n                // screen while it slides out, and focusing into it now would\n                // strand focus in an element about to disappear.\n                if (!self.open) return;\n                var items = drawerFocusables();\n                if (items.length) items[0].focus();\n            }, 50);\n        },\n        toggle() {\n            if (this.open) {\n                this.close();\n            } else {\n                this.show();\n            }\n        },\n        close() {\n            if (!this.open) return;\n            this.open = false;\n            document.body.style.overflow = \"\";\n            var opener = drawerOpener;\n            drawerOpener = null;\n            if (opener && document.contains(opener) && opener.focus) {\n                opener.focus();\n            }\n        },\n    });\n\n    // Prompt queue (crush_lu/STYLE.md \u00A78): at most one of the cookie sheet,\n    // flash messages, the install card and the push prompt at a time, in\n    // that order. The cookie sheet (shared core partial, no Alpine) reports\n    // itself via `cookie-banner-toggle`; flash messages are never hidden,\n    // they only hold install/push back until dismissed or auto-hidden.\n    // Install/push wait for DOMContentLoaded, when the sheet has decided,\n    // and never show on a first visit: pwa-install.js (deferred, so it has\n    // run by then) counts visits in localStorage \"crush-pwa-sessions\".\n    var PROMPT_ORDER = [\"cookie\", \"messages\", \"install\", \"push\"];\n    Alpine.store(\"prompts\", {\n        ready: false,\n        returning: false,\n        cookie: false,\n        messages: 0,\n        install: false,\n        push: false,\n        init() {\n            var self = this;\n            var sheet = document.getElementById(\"cookie-consent-banner\");\n            this.cookie = !!sheet && sheet.style.display === \"block\";\n            document.addEventListener(\"cookie-banner-toggle\", function (e) {\n                self.cookie = !!(e.detail && e.detail.open);\n            });\n            function markReady() {\n                try {\n                    var visits = localStorage.getItem(\"crush-pwa-sessions\");\n                    self.returning = (parseInt(visits, 10) || 0) >= 2;\n                } catch (e) {\n                    self.returning = false; // storage blocked: never nag\n                }\n                self.ready = true;\n            }\n            if (document.readyState === \"complete\") {\n                markReady();\n            } else {\n                document.addEventListener(\"DOMContentLoaded\", markReady);\n                window.addEventListener(\"load\", markReady);\n            }\n        },\n        get active() {\n            for (var i = 0; i < PROMPT_ORDER.length; i++) {\n                var name = PROMPT_ORDER[i];\n                if (this[name]) {\n                    return i < 2 || (this.ready && this.returning) ? name : null;\n                }\n            }\n            return null;\n        },\n        isActive(name) {\n            return this.active === name;\n        },\n        set(name, on) {\n            this[name] = !!on;\n        },\n        // The banner and dashboard share one dismissal timestamp. A failed\n        // storage read suppresses an unsolicited offer, like sessionCount().\n        installCooldownActive() {\n            try {\n                var dismissed = localStorage.getItem(\"crush-pwa-install-dismissed\");\n                if (dismissed === null) return false;\n                var dismissedAt = Number(dismissed);\n                var now = Date.now();\n                if (\n                    /^\\d+$/.test(dismissed) &&\n                    Number.isSafeInteger(dismissedAt) &&\n                    dismissedAt > 0 &&\n                    dismissedAt <= now &&\n                    now - dismissedAt < 30 * 24 * 60 * 60 * 1000\n                ) {\n                    return true;\n                }\n                // Expired, future, malformed or legacy \"true\" values.\n                localStorage.removeItem(\"crush-pwa-install-dismissed\");\n                return false;\n            } catch (e) {\n                return true;\n            }\n        },\n        dismissInstall() {\n            try {\n                localStorage.setItem(\"crush-pwa-install-dismissed\", String(Date.now()));\n            } catch (e) {\n                // Still hide this visit's offer when storage is unavailable.\n            }\n            this.set(\"install\", false);\n        },\n        holdMessage() {\n            this.messages += 1;\n        },\n        releaseMessage() {\n            this.messages = Math.max(0, this.messages - 1);\n        },\n    });\n\n    // Legal pages' floating \"Back to contents\" pill. It sits in the corner the\n    // bottom prompts (cookie sheet, install card, push prompt) occupy, so it\n    // steps aside while one of them is the active prompt. Flash messages are\n    // not bottom-anchored and never hide it.\n    Alpine.data(\"legalBackToContents\", function () {\n        return {\n            get visible() {\n                var active = Alpine.store(\"prompts\").active;\n                return active !== \"cookie\" && active !== \"install\" && active !== \"push\";\n            },\n        };\n    });\n\n    // UX Wave 3 \u00B7 WP5 (finding 3-14) \u2014 client-side downscale/re-encode\n    // before a profile photo is uploaded. Keeps mobile uploads out of the\n    // 4-12MB range the coach review queue was seeing. Uses\n    // createImageBitmap({imageOrientation: 'from-image'}) so EXIF rotation\n    // is baked into the pixels instead of relying on <img> auto-rotation\n    // (which canvas drawImage does not inherit). Falls back to returning\n    // the original file untouched if the browser lacks canvas/bitmap\n    // support \u2014 server-side validation is unchanged either way.\n    function resizeImageForUpload(file, maxEdge, quality) {\n        maxEdge = maxEdge || 2048;\n        quality = quality || 0.85;\n        if (!file || typeof file.type !== \"string\" || file.type.indexOf(\"image/\") !== 0) {\n            return Promise.resolve(file);\n        }\n        if (typeof createImageBitmap !== \"function\" || typeof document.createElement(\"canvas\").getContext !== \"function\") {\n            return Promise.resolve(file);\n        }\n        return createImageBitmap(file, { imageOrientation: \"from-image\" })\n            .then(function (bitmap) {\n                var scale = Math.min(1, maxEdge / Math.max(bitmap.width, bitmap.height));\n                var shortEdge = Math.min(bitmap.width, bitmap.height);\n                // The final profile form requires both sides to be at least\n                // 200px. Keep an originally valid panorama valid even when\n                // that means its long edge remains above the usual target.\n                if (shortEdge >= 200 && Math.round(shortEdge * scale) < 200) {\n                    scale = Math.min(1, 200 / shortEdge);\n                }\n                var width = Math.max(1, Math.round(bitmap.width * scale));\n                var height = Math.max(1, Math.round(bitmap.height * scale));\n                var canvas = document.createElement(\"canvas\");\n                canvas.width = width;\n                canvas.height = height;\n                var ctx = canvas.getContext(\"2d\");\n                ctx.drawImage(bitmap, 0, 0, width, height);\n                if (bitmap.close) bitmap.close();\n                return new Promise(function (resolve) {\n                    canvas.toBlob(\n                        function (blob) {\n                            resolve(blob || file);\n                        },\n                        \"image/jpeg\",\n                        quality,\n                    );\n                });\n            })\n            .then(function (blob) {\n                if (!blob || blob === file) return file;\n                var name = (file.name || \"photo\").replace(/\\.[^.]+$/, \"\") + \".jpg\";\n                try {\n                    return new File([blob], name, { type: \"image/jpeg\" });\n                } catch (e) {\n                    // Older Safari lacks the File constructor's options form.\n                    return blob;\n                }\n            })\n            .catch(function () {\n                return file;\n            });\n    }\n\n    // UX Wave 3 \u00B7 WP5 (finding 3-14) \u2014 the profile wizard's Photos step\n    // (`photoUpload`, nested in its own x-data scope inside\n    // create_profile.html) resizes then uploads each photo asynchronously;\n    // the outer wizard's \"Continue\" button lives in a different scope and\n    // had no way to see that work in flight. A member who picks a large\n    // photo and taps Continue immediately used to advance to Review before\n    // the upload request was even created \u2014 the final page-unload could\n    // then abandon it, leaving a preview of a photo that was never saved.\n    // Tracked at module scope (same idiom as _photoUploadDeprecationLogged\n    // above) so the wizard component can await it without cross-scope\n    // Alpine wiring.\n    var _pendingPhotoUploads = [];\n    var _photoStepAdvancing = false;\n    function trackPendingPhotoUpload(promise) {\n        _pendingPhotoUploads.push(promise);\n        var settle = function () {\n            var idx = _pendingPhotoUploads.indexOf(promise);\n            if (idx !== -1) _pendingPhotoUploads.splice(idx, 1);\n        };\n        promise.then(settle, settle);\n    }\n    function waitForPendingPhotoUploads() {\n        if (!_pendingPhotoUploads.length) return Promise.resolve();\n        // A selection or removal may be added while the current batch is\n        // settling. Drain the live set before advancing the wizard.\n        return Promise.all(\n            _pendingPhotoUploads.slice().map(function (promise) {\n                return promise.then(function () {}, function () {});\n            }),\n        ).then(waitForPendingPhotoUploads);\n    }\n\n    // Push settings cards: navigator.serviceWorker.ready never settles when no\n    // worker is registered (failed registration, private mode, DEBUG's\n    // sw-unregister), so give up on the \"Checking\u2026\" spinner after 3 s and\n    // offer a Retry. A late answer still settles the card normally.\n    var PUSH_CHECK_TIMEOUT_MS = 3000;\n    // Platform-aware \"blocked\" copy (#1064): each OS has its own path back to\n    // the notification permission, so one generic sentence misleads. Shared by\n    // the member and the coach push cards (compose with mixin(), never spread).\n    function makeBlockedPlatform() {\n        return {\n            blockedPlatform: \"desktop\",\n            get blockedOnIos() {\n                return this.blockedPlatform === \"ios\";\n            },\n            get blockedOnAndroid() {\n                return this.blockedPlatform === \"android\";\n            },\n            get blockedOnDesktop() {\n                return this.blockedPlatform === \"desktop\";\n            },\n            _detectBlockedPlatform: function () {\n                // iPadOS Safari reports a desktop \"Macintosh\" UA; touch points tell it apart.\n                var ua = navigator.userAgent || \"\";\n                if (\n                    /iPhone|iPad|iPod/.test(ua) ||\n                    (/Macintosh/.test(ua) && navigator.maxTouchPoints > 1)\n                ) {\n                    this.blockedPlatform = \"ios\";\n                } else if (/Android/i.test(ua)) {\n                    this.blockedPlatform = \"android\";\n                }\n            },\n        };\n    }\n    function makePushStatusCheck() {\n        return {\n            checkTimedOut: false,\n            _checkRun: 0,\n            // \"Not supported\" and a browser-level \"blocked\" are the truthful\n            // answers (Retry cannot change either), so both win over Retry.\n            get showCheckTimedOut() {\n                return (\n                    !this.isLoading &&\n                    this.checkTimedOut &&\n                    this.isSupported &&\n                    !this.permissionDenied\n                );\n            },\n            // probe(finish) runs the component's own check and calls finish().\n            // An unsupported browser has nothing to probe: settle at once.\n            _runStatusCheck: function (probe) {\n                var self = this;\n                var run = ++this._checkRun;\n                this.isLoading = true;\n                this.checkTimedOut = false;\n                setTimeout(function () {\n                    if (run === self._checkRun && self.isLoading) {\n                        self.isLoading = false;\n                        self.checkTimedOut = true;\n                    }\n                }, PUSH_CHECK_TIMEOUT_MS);\n                var finish = function () {\n                    if (run !== self._checkRun) return;\n                    self.isLoading = false;\n                    self.checkTimedOut = false;\n                    self.$nextTick(function () {\n                        self._retryDeviceMatch();\n                    });\n                };\n                if (!this.isSupported) return finish();\n                probe(finish);\n            },\n            retryStatusCheck: function () {\n                this._checkStatus();\n            },\n        };\n    }\n\n    // Changelog search/filter toolbar. Category buttons are real submit\n    // buttons (the form submit does the filtering); this only keeps the\n    // pressed styling in step because the toolbar sits outside the swapped\n    // timeline. Initial state comes from data-* attributes (CSP build).\n    Alpine.data(\"changelogToolbar\", function () {\n        return {\n            q: \"\",\n            cat: \"\",\n\n            init: function () {\n                // Back/forward: HTMX swaps in a cached body snapshot whose\n                // button state can be stale, and fires htmx:historyRestore\n                // before this component exists. So the URL, not the snapshot,\n                // is the source of truth here; the server-rendered data-*\n                // values are the fallback for a URL with unknown values.\n                var params = new URLSearchParams(window.location.search);\n                var urlCat = params.get(\"category\") || \"\";\n                var known = Array.prototype.some.call(\n                    this.$el.querySelectorAll(\"[data-category-value]\"),\n                    function (btn) {\n                        return btn.getAttribute(\"data-category-value\") === urlCat;\n                    },\n                );\n                this.q = params.has(\"q\")\n                    ? params.get(\"q\")\n                    : this.$el.dataset.q || \"\";\n                this.cat = known ? urlCat : this.$el.dataset.category || \"\";\n                this._syncButtons();\n            },\n\n            selectCategory: function (event) {\n                var btn = event.currentTarget;\n                this.cat = btn.getAttribute(\"data-category-value\") || \"\";\n                this._syncButtons();\n            },\n\n            _syncButtons: function () {\n                var self = this;\n                var on = [\"bg-crush-purple\", \"text-white\", \"border-crush-purple\"];\n                var off = [\n                    \"bg-white\",\n                    \"dark:bg-gray-900\",\n                    \"text-crush-dark\",\n                    \"dark:text-gray-200\",\n                    \"border-gray-300\",\n                    \"dark:border-gray-700\",\n                    \"hover:border-crush-purple\",\n                ];\n                var buttons = this.$el.querySelectorAll(\"[data-category-value]\");\n                buttons.forEach(function (btn) {\n                    var active =\n                        (btn.getAttribute(\"data-category-value\") || \"\") === self.cat;\n                    btn.setAttribute(\"aria-pressed\", active ? \"true\" : \"false\");\n                    on.forEach(function (c) {\n                        btn.classList.toggle(c, active);\n                    });\n                    off.forEach(function (c) {\n                        btn.classList.toggle(c, !active);\n                    });\n                });\n            },\n        };\n    });\n\n    // Event ticket \"Add to Google Wallet\" button component\n    Alpine.data(\"eventTicketButton\", function () {\n        return {\n            loading: false,\n            error: false,\n            errorMsg: \"\",\n\n            get isLoading() {\n                return this.loading;\n            },\n            get hasError() {\n                return this.error;\n            },\n            get errorMessage() {\n                return this.errorMsg;\n            },\n\n            saveToWallet: function () {\n                var self = this;\n                var regId = this.$el\n                    .closest(\"[data-registration-id]\")\n                    .getAttribute(\"data-registration-id\");\n                if (!regId) return;\n\n                self.loading = true;\n                self.error = false;\n                self.errorMsg = \"\";\n\n                fetch(\"/wallet/google/event-ticket/\" + regId + \"/jwt/\")\n                    .then(function (r) {\n                        return r.json();\n                    })\n                    .then(function (data) {\n                        self.loading = false;\n                        if (data.jwt) {\n                            window.open(\n                                \"https://pay.google.com/gp/v/save/\" + data.jwt,\n                                \"_blank\",\n                            );\n                        } else {\n                            self.error = true;\n                            self.errorMsg = data.error || gettext(\"Failed to generate ticket.\");\n                        }\n                    })\n                    .catch(function (err) {\n                        self.loading = false;\n                        self.error = true;\n                        self.errorMsg = gettext(\"Network error. Please try again.\");\n                    });\n            },\n        };\n    });\n\n    // =========================================================================\n    // MOBILE NATIVE APP NAVIGATION COMPONENTS\n    // =========================================================================\n\n    // Bottom navigation bar - 4 tabs (5 for coaches) fixed at bottom on mobile\n    Alpine.data(\"bottomNav\", function () {\n        return {\n            currentPath: \"\",\n            eventsCount: 0,\n            requestsCount: 0,\n            screeningCount: 0,\n\n            init: function () {\n                // Strip i18n language prefix (e.g. /en/, /de/, /fr/) for route matching\n                var path = window.location.pathname;\n                var langPrefix = path.match(/^\\/[a-z]{2}(?:-[a-z]{2})?\\//);\n                this.currentPath = langPrefix\n                    ? path.substring(langPrefix[0].length - 1)\n                    : path;\n                this.eventsCount = parseInt(this.$el.dataset.eventsCount || \"0\", 10);\n                this.requestsCount = parseInt(\n                    this.$el.dataset.requestsCount || \"0\",\n                    10,\n                );\n                this.screeningCount = parseInt(\n                    this.$el.dataset.screeningCount || \"0\",\n                    10,\n                );\n            },\n\n            get isHomeActive() {\n                return (\n                    this.currentPath === \"/dashboard/\" ||\n                    this.currentPath.indexOf(\"/dashboard/\") === 0\n                );\n            },\n            get isEventsActive() {\n                // The tab points at /my-events/ for a member holding seats and\n                // at /events/ otherwise, so both paths must light it up.\n                return (\n                    this.currentPath === \"/events/\" ||\n                    this.currentPath.indexOf(\"/events/\") === 0 ||\n                    this.currentPath.indexOf(\"/my-events/\") === 0\n                );\n            },\n            get isConnectionsActive() {\n                return (\n                    this.currentPath === \"/connections/\" ||\n                    this.currentPath.indexOf(\"/connections/\") === 0\n                );\n            },\n            get isProfileActive() {\n                return this.currentPath.indexOf(\"/profile/\") === 0;\n            },\n            get isCoachActive() {\n                return this.currentPath.indexOf(\"/coach/\") === 0;\n            },\n            get isCrushConnectActive() {\n                return this.currentPath.indexOf(\"/crush-connect/\") === 0;\n            },\n\n            get homeActiveClass() {\n                return this.isHomeActive ? \"bottom-nav-item-active\" : \"\";\n            },\n            get eventsActiveClass() {\n                return this.isEventsActive ? \"bottom-nav-item-active\" : \"\";\n            },\n            get connectionsActiveClass() {\n                return this.isConnectionsActive ? \"bottom-nav-item-active\" : \"\";\n            },\n            get profileActiveClass() {\n                return this.isProfileActive ? \"bottom-nav-item-active\" : \"\";\n            },\n            get coachActiveClass() {\n                return this.isCoachActive ? \"bottom-nav-item-active\" : \"\";\n            },\n            get crushConnectActiveClass() {\n                return this.isCrushConnectActive ? \"bottom-nav-item-active\" : \"\";\n            },\n\n            get hasEventsBadge() {\n                return this.eventsCount > 0;\n            },\n            get hasRequestsBadge() {\n                return this.requestsCount > 0;\n            },\n            get hasScreeningBadge() {\n                return this.screeningCount > 0;\n            },\n\n            handleTap: function () {\n                // Haptic feedback (silent no-op where unsupported)\n                if (navigator.vibrate) {\n                    navigator.vibrate(10);\n                }\n            },\n\n            scrollToTop: function () {\n                window.scrollTo({ top: 0, behavior: \"smooth\" });\n            },\n        };\n    });\n\n    // Slim mobile top bar - logo or back arrow + title\n    Alpine.data(\"topBarMobile\", function () {\n        return {\n            pageTitle: \"\",\n            // Unread in-app notifications (not connection requests \u2014 those\n            // badge the bottom nav's Connections tab).\n            notificationCount: 0,\n            i18nNotifications: \"Notifications\",\n            i18nUnreadNotifications: \"Unread notifications: {count}\",\n\n            init: function () {\n                // Read page title from meta tag set by {% block mobile_page_title %}\n                var meta = document.querySelector('meta[name=\"mobile-page-title\"]');\n                this.pageTitle = meta ? meta.getAttribute(\"content\") : \"\";\n                this.notificationCount = parseInt(\n                    this.$el.dataset.notificationCount || \"0\",\n                    10,\n                );\n                if (this.$el.dataset.i18nNotifications) {\n                    this.i18nNotifications = this.$el.dataset.i18nNotifications;\n                }\n                if (this.$el.dataset.i18nUnreadNotifications) {\n                    this.i18nUnreadNotifications =\n                        this.$el.dataset.i18nUnreadNotifications;\n                }\n            },\n\n            get showBackButton() {\n                return this.pageTitle !== \"\";\n            },\n            get showLogo() {\n                return this.pageTitle === \"\";\n            },\n            get hasNotifications() {\n                return this.notificationCount > 0;\n            },\n            // Same \"9+\" cap as the desktop notificationBell badge.\n            get notificationBadgeText() {\n                if (this.notificationCount > 9) return \"9+\";\n                return String(this.notificationCount);\n            },\n            // The badge is aria-hidden; the count is spoken via the link's label.\n            get bellAriaLabel() {\n                if (!this.hasNotifications) return this.i18nNotifications;\n                return this.i18nUnreadNotifications.replace(\n                    \"{count}\",\n                    String(this.notificationCount),\n                );\n            },\n\n            // Bound via x-on:notif-unread-count.window \u2014 the desktop\n            // notificationBell re-fetches /api/notifications/ on load and on\n            // mark-read, and broadcasts each new unread count.\n            syncNotificationCount: function (event) {\n                var count = parseInt(event.detail, 10);\n                if (!isNaN(count) && count >= 0) {\n                    this.notificationCount = count;\n                }\n            },\n\n            goBack: function () {\n                if (navigator.vibrate) {\n                    navigator.vibrate(10);\n                }\n\n                // Hierarchical back for profile edit sub-sections\n                var params = new URLSearchParams(window.location.search);\n                if (window.location.pathname.indexOf('/profile/edit/') !== -1 && params.has('section')) {\n                    // Navigate to the parent edit profile overview\n                    window.location.href = window.location.pathname;\n                    return;\n                }\n\n                // Fallback to history.back(). If history length is 1 (direct load/no history),\n                // fall back to a safe location like dashboard.\n                if (window.history.length <= 1) {\n                    var prefix = \"/en\";\n                    var match = window.location.pathname.match(/^\\/([a-z]{2})\\//);\n                    if (match) {\n                        prefix = \"/\" + match[1];\n                    }\n                    window.location.href = prefix + '/dashboard/';\n                } else {\n                    window.history.back();\n                }\n            },\n\n            toggleDrawer: function () {\n                this.$store.drawer.toggle();\n            },\n        };\n    });\n\n    // Mobile navigation side drawer (partials/mobile_drawer.html). State lives\n    // in the global `drawer` store so the top bar's hamburger (a separate\n    // component) can toggle it; the CSP build cannot evaluate `$store.\u2026`\n    // expressions in templates, so these members wrap the store access.\n    Alpine.data(\"mobileDrawer\", function () {\n        return {\n            get drawerOpen() {\n                return this.$store.drawer.open;\n            },\n            closeDrawer: function () {\n                this.$store.drawer.close();\n            },\n        };\n    });\n\n    // Outermost Alpine scope, on <body>. It holds no state: page changes are\n    // full loads that the browser cross-fades (@view-transition in\n    // tailwind-input.css), so nothing here drives a transition.\n    Alpine.data(\"pageShell\", function () {\n        return {};\n    });\n\n    // =========================================================================\n    // SHARED FORM COMPONENTS (Phase 4)\n    // =========================================================================\n\n    // Event detail description collapse/expand (#4-03)\n    Alpine.data(\"eventDescriptionToggle\", function () {\n        return {\n            expanded: false,\n            // The template initially clamps long text so it cannot flash\n            // open before Alpine loads. The same gate renders the toggle.\n            collapsible: false,\n            init: function () {\n                this.collapsible = this.$el.dataset.collapsible === \"true\";\n            },\n            get collapsed() {\n                return !this.expanded;\n            },\n            // String form for :aria-expanded on the toggle button (#WP6-2):\n            // mirrors the getter idiom base.html uses for nav aria-expanded\n            // bindings under the CSP-safe Alpine build.\n            get expandedAria() {\n                return this.expanded ? \"true\" : \"false\";\n            },\n            toggle: function () {\n                this.expanded = !this.expanded;\n                if (this.collapsible) {\n                    this.$refs.description.classList.toggle(\n                        \"line-clamp-4\",\n                        !this.expanded,\n                    );\n                }\n            },\n        };\n    });\n\n    // Event detail mobile sticky CTA bar (#4-03). Mirrors whichever single\n    // btn-crush-primary registration anchor is already rendered inside\n    // #event-cta-panel, so it can never disagree with the in-page CTA \u2014 it\n    // reads the same DOM instead of re-deriving eligibility. Stays hidden\n    // when that panel has no such anchor (blocked / login states).\n    //\n    // A registered-but-unpaid member sees \"Pay with Card\" / \"Pay with Crush\n    // Credit\" as <button class=\"js-sumup-checkout-detail\"> elements instead\n    // (they trigger a fetch()-based SumUp checkout, not a navigation), so\n    // when no anchor is found this also falls back to that button and, on\n    // tap, re-dispatches a click to the real in-panel button so its Alpine\n    // checkout handler runs exactly as if the member had tapped it directly.\n    Alpine.data(\"eventStickyCta\", function () {\n        return {\n            visible: false,\n            ctaHref: \"\",\n            ctaLabel: \"\",\n            priceText: \"\",\n            factsText: \"\",\n            isPayment: false,\n            payMethod: \"\",\n            // Bare getter for the anchor branch's x-show (#WP6 fix): the CSP\n            // build only evaluates bare property/method names, not\n            // expressions like \"!isPayment\".\n            get isLink() {\n                return !this.isPayment;\n            },\n            init: function () {\n                this.priceText = this.$el.dataset.priceLabel || \"\";\n                this.factsText = this.$el.dataset.factsText || \"\";\n                var self = this;\n                // #WP6-1: while this bar is visible, push #toast-container's\n                // bottom offset above the bar's own rendered height so a\n                // toast never renders on top of the price/CTA. Reverts to\n                // its normal .toast-above-nav offset when the bar hides.\n                //\n                // `visible` (Alpine's own x-show flag) only ever changes from\n                // the IntersectionObserver below, so it is silent about the\n                // `md:hidden` breakpoint: rotating or resizing past 768px\n                // while `visible` stays true leaves this offset applied with\n                // no bar left to justify it, and resizing back can reuse a\n                // stale height (Codex review on #1062). Recompute on resize\n                // too, not just on the `visible` watcher, so the offset\n                // always matches what CSS is actually showing right now.\n                function syncToastOffset() {\n                    var toast = document.getElementById(\"toast-container\");\n                    if (!toast) {\n                        return;\n                    }\n                    // The bar is md:hidden, so at >=768px offsetHeight reads 0\n                    // regardless of `visible` (display:none from the media\n                    // query) \u2014 never write a bottom offset in that case, or\n                    // the desktop toast stack (lg:bottom-auto lg:top-4) picks\n                    // up an inline `bottom` it never had.\n                    var barHeight = self.visible ? self.$el.offsetHeight : 0;\n                    if (barHeight > 0) {\n                        toast.style.setProperty(\n                            \"bottom\",\n                            \"calc(var(--bottom-nav-height) + \" +\n                                barHeight +\n                                \"px + env(safe-area-inset-bottom, 0px))\",\n                        );\n                    } else {\n                        toast.style.removeProperty(\"bottom\");\n                    }\n                }\n                this.$watch(\"visible\", function () {\n                    // This watcher and the x-show effect both react to the\n                    // same `visible` change, but x-show always applies its\n                    // style mutation on a requestAnimationFrame callback\n                    // (even with no x-transition), which runs AFTER a plain\n                    // $nextTick's microtask \u2014 so offsetHeight below would\n                    // still read the pre-toggle 0. Wait two frames instead:\n                    // one for x-show's own rAF, one more so the resulting\n                    // layout has actually been computed before we read it.\n                    requestAnimationFrame(function () {\n                        requestAnimationFrame(syncToastOffset);\n                    });\n                });\n                // A plain `resize` listener fires on every pixel during a\n                // drag; debounce it so a rotation/resize settles once before\n                // reading layout, same cost profile as the watcher above.\n                var resizeTimer = null;\n                window.addEventListener(\"resize\", function () {\n                    if (resizeTimer) {\n                        clearTimeout(resizeTimer);\n                    }\n                    resizeTimer = setTimeout(syncToastOffset, 150);\n                });\n                var panel = document.getElementById(\"event-cta-panel\");\n                if (!panel || !(\"IntersectionObserver\" in window)) {\n                    return;\n                }\n                // A language-blocked member sees the registration (or\n                // payment) CTA rendered alongside the language-requirement\n                // warning even though event_register rejects them \u2014 skip\n                // the sticky bar entirely rather than advertise an action\n                // that cannot succeed (Codex review on #1062).\n                if (panel.querySelector(\"#event-language-blocked\")) {\n                    return;\n                }\n                var anchor = panel.querySelector(\"a.btn-crush-primary\");\n                // event_register rejects age-restricted sign-ups without a\n                // qualifying profile DOB. Keep a real payment button available\n                // for an existing unpaid registration, but never mirror a\n                // registration anchor that leads straight to that rejection.\n                if (anchor && panel.dataset.ageBlocked === \"true\") {\n                    return;\n                }\n                var target = anchor;\n                if (anchor) {\n                    this.ctaHref = anchor.getAttribute(\"href\") || \"\";\n                    this.ctaLabel = (anchor.textContent || \"\").trim();\n                } else {\n                    // Prefer the \"Pay with Card\" button: it's the one payment\n                    // option always rendered when a balance is due, whereas\n                    // \"Pay with Crush Credit\" only appears with sufficient\n                    // credit \u2014 so anchoring on \"card\" keeps target selection\n                    // stable across members.\n                    var payButton =\n                        panel.querySelector(\n                            '.js-sumup-checkout-detail[data-payment-method=\"card\"]',\n                        ) || panel.querySelector(\".js-sumup-checkout-detail\");\n                    if (!payButton) {\n                        return;\n                    }\n                    this.isPayment = true;\n                    this.payMethod =\n                        payButton.getAttribute(\"data-payment-method\") || \"card\";\n                    this.ctaLabel = (payButton.textContent || \"\").trim();\n                    target = payButton;\n                }\n                // Observe the CTA element itself, not the whole panel. Use\n                // the full viewport so the bar hides as soon as any part of\n                // the real CTA enters view, including near the bottom edge.\n                var observer = new IntersectionObserver(\n                    function (entries) {\n                        var entry = entries[0];\n                        self.visible = !!entry && !entry.isIntersecting;\n                    },\n                    { rootMargin: \"0px\" },\n                );\n                observer.observe(target);\n            },\n            onCtaClick: function (event) {\n                if (!this.isPayment) {\n                    return;\n                }\n                event.preventDefault();\n                var panel = document.getElementById(\"event-cta-panel\");\n                var real =\n                    panel &&\n                    panel.querySelector(\n                        '.js-sumup-checkout-detail[data-payment-method=\"' +\n                            this.payMethod +\n                            '\"]',\n                    );\n                if (real) {\n                    real.click();\n                }\n            },\n        };\n    });\n\n    // Calendar dropdown component\n    Alpine.data(\"calendarDropdown\", function () {\n        return {\n            open: false,\n\n            get isOpen() {\n                return this.open;\n            },\n            get isClosed() {\n                return !this.open;\n            },\n\n            toggle() {\n                this.open = !this.open;\n            },\n\n            close() {\n                this.open = false;\n            },\n        };\n    });\n\n    // Navbar component with dropdowns and mobile menu\n    Alpine.data(\"navbar\", function () {\n        return {\n            mobileMenuOpen: false,\n            coachToolsOpen: false,\n            myCrushOpen: false,\n            coachProfileOpen: false,\n            eventsOpen: false,\n            userMenuOpen: false,\n\n            init: function () {\n                // Close all dropdowns on Escape key\n                document.addEventListener(\"keydown\", (e) => {\n                    if (e.key === \"Escape\") {\n                        this.closeAllDropdowns();\n                    }\n                });\n            },\n\n            // Computed getters for CSP compatibility (avoid inline expressions)\n            get mobileMenuClosed() {\n                return !this.mobileMenuOpen;\n            },\n            get mobileMenuAriaExpanded() {\n                return this.mobileMenuOpen ? \"true\" : \"false\";\n            },\n            get coachToolsAriaExpanded() {\n                return this.coachToolsOpen ? \"true\" : \"false\";\n            },\n            get myCrushAriaExpanded() {\n                return this.myCrushOpen ? \"true\" : \"false\";\n            },\n            get coachProfileAriaExpanded() {\n                return this.coachProfileOpen ? \"true\" : \"false\";\n            },\n            get eventsAriaExpanded() {\n                return this.eventsOpen ? \"true\" : \"false\";\n            },\n            get userMenuAriaExpanded() {\n                return this.userMenuOpen ? \"true\" : \"false\";\n            },\n\n            toggleMobile: function () {\n                this.mobileMenuOpen = !this.mobileMenuOpen;\n            },\n            toggleCoachTools: function () {\n                this.coachToolsOpen = !this.coachToolsOpen;\n            },\n            toggleMyCrush: function () {\n                this.myCrushOpen = !this.myCrushOpen;\n            },\n            toggleCoachProfile: function () {\n                this.coachProfileOpen = !this.coachProfileOpen;\n            },\n            toggleEvents: function () {\n                this.eventsOpen = !this.eventsOpen;\n            },\n            toggleUserMenu: function () {\n                this.userMenuOpen = !this.userMenuOpen;\n            },\n            closeCoachTools: function () {\n                this.coachToolsOpen = false;\n            },\n            closeMyCrush: function () {\n                this.myCrushOpen = false;\n            },\n            closeCoachProfile: function () {\n                this.coachProfileOpen = false;\n            },\n            closeEvents: function () {\n                this.eventsOpen = false;\n            },\n            closeUserMenu: function () {\n                this.userMenuOpen = false;\n            },\n            closeAllDropdowns: function () {\n                this.mobileMenuOpen = false;\n                this.coachToolsOpen = false;\n                this.myCrushOpen = false;\n                this.coachProfileOpen = false;\n                this.eventsOpen = false;\n                this.userMenuOpen = false;\n            },\n        };\n    });\n\n    // Profile progress dropdown for incomplete profiles in navbar\n    Alpine.data(\"profileProgress\", function () {\n        return {\n            isOpen: false,\n\n            // CSP-compatible computed getters\n            get isClosed() {\n                return !this.isOpen;\n            },\n            get ariaExpanded() {\n                return this.isOpen ? \"true\" : \"false\";\n            },\n\n            toggle: function () {\n                this.isOpen = !this.isOpen;\n            },\n            close: function () {\n                this.isOpen = false;\n            },\n        };\n    });\n\n    // Dismissible alert/message component\n    function makeDismissible() {\n        return {\n            show: true,\n            init: function () {\n                this.startAutoDismiss();\n            },\n            startAutoDismiss: function () {\n                // Auto-dismiss only when the banner opts in via data-auto-dismiss\n                // (success/info confirmations). Errors and warnings omit the\n                // attribute so they persist until the user closes them.\n                var delay = parseInt(this.$el.dataset.autoDismiss, 10);\n                if (delay > 0) {\n                    var self = this;\n                    setTimeout(function () {\n                        self.show = false;\n                    }, delay);\n                }\n            },\n            dismiss: function () {\n                this.show = false;\n            },\n        };\n    }\n    Alpine.data(\"dismissible\", makeDismissible);\n\n    // base.html's Django flash messages: while one is visible it holds the\n    // lower-priority prompts (install, push) in Alpine.store(\"prompts\").\n    Alpine.data(\"flashMessage\", function () {\n        return mixin(makeDismissible(), {\n            init: function () {\n                var prompts = Alpine.store(\"prompts\");\n                prompts.holdMessage();\n                this.$watch(\"show\", function (visible) {\n                    if (!visible) prompts.releaseMessage();\n                });\n                this.startAutoDismiss();\n            },\n        });\n    });\n\n    // Inline connection request form (event attendees page).\n    // Suggestion chips carry their text in a data-prefill attribute; clicking\n    // one inserts it into the note textarea (x-ref=\"note\"). The CSP build\n    // passes the click event to the bare method reference @click=\"prefill\".\n    Alpine.data(\"connectionForm\", function () {\n        return {\n            prefill: function (event) {\n                var btn = event && event.currentTarget;\n                var text = btn ? btn.getAttribute(\"data-prefill\") : \"\";\n                var ta = this.$refs.note;\n                if (!ta || !text) return;\n                if (ta.value && ta.value.indexOf(text) !== 0) {\n                    ta.value = text + \" \" + ta.value;\n                } else {\n                    ta.value = text;\n                }\n                ta.focus();\n                ta.setSelectionRange(ta.value.length, ta.value.length);\n            },\n        };\n    });\n\n    // Tab navigation component (for auth page)\n    // Reads initial tab from data-initial-tab attribute\n    Alpine.data(\"tabNav\", function () {\n        return {\n            activeTab: \"login\",\n\n            // Computed getters for CSP compatibility\n            get isLoginTab() {\n                return this.activeTab === \"login\";\n            },\n            get isSignupTab() {\n                return this.activeTab === \"signup\";\n            },\n            // Alpine's CSP-friendly build can't evaluate an inline ternary\n            // (`isLoginTab ? 'true' : 'false'`) in x-bind:aria-selected \u2014\n            // it silently logs a console warning and never sets the\n            // attribute. These return the string directly so the binding\n            // stays a bare property name.\n            get loginAriaSelected() {\n                return this.isLoginTab ? \"true\" : \"false\";\n            },\n            get signupAriaSelected() {\n                return this.isSignupTab ? \"true\" : \"false\";\n            },\n            // Roving tabindex: only the active tab sits in the sequential\n            // tab order, per the ARIA tabs keyboard pattern. Arrow keys\n            // move focus between tabs (handled by onTabKeydown below).\n            get loginTabIndex() {\n                return this.isLoginTab ? \"0\" : \"-1\";\n            },\n            get signupTabIndex() {\n                return this.isSignupTab ? \"0\" : \"-1\";\n            },\n            get loginTabClass() {\n                return this.activeTab === \"login\"\n                    ? \"bg-gradient-to-r from-purple-500 to-pink-500 text-white shadow-md\"\n                    : \"text-gray-900 bg-white/50 hover:bg-white/80 dark:text-gray-300 dark:bg-transparent dark:hover:bg-white/10\";\n            },\n            get signupTabClass() {\n                return this.activeTab === \"signup\"\n                    ? \"bg-gradient-to-r from-purple-500 to-pink-500 text-white shadow-md\"\n                    : \"text-gray-900 bg-white/50 hover:bg-white/80 dark:text-gray-300 dark:bg-transparent dark:hover:bg-white/10\";\n            },\n\n            init: function () {\n                // Read initial tab from data attribute\n                var initialTab = this.$el.getAttribute(\"data-initial-tab\");\n                if (initialTab) {\n                    this.activeTab = initialTab;\n                }\n            },\n            setLogin: function () {\n                this.activeTab = \"login\";\n            },\n            setSignup: function () {\n                this.activeTab = \"signup\";\n            },\n            // ARIA tabs keyboard pattern: Left/Right/Home/End move both\n            // selection and focus between the two tabs (there are only\n            // ever two, so wrapping toggles). Other keys are left alone.\n            onTabKeydown: function (event) {\n                var key = event.key;\n                if (\n                    key !== \"ArrowLeft\" &&\n                    key !== \"ArrowRight\" &&\n                    key !== \"Home\" &&\n                    key !== \"End\"\n                ) {\n                    return;\n                }\n                event.preventDefault();\n                var next = this.isLoginTab ? \"signup\" : \"login\";\n                if (key === \"Home\") next = \"login\";\n                if (key === \"End\") next = \"signup\";\n                var nextId = next === \"login\" ? \"auth-tab-login\" : \"auth-tab-signup\";\n                var nextEl = document.getElementById(nextId);\n                if (!nextEl) return;\n                // Dispatch a real click rather than setting activeTab\n                // directly: the signup tab also carries a plain\n                // addEventListener click handler (funnel analytics in\n                // auth.html) that a direct state assignment would bypass,\n                // so an arrow-key switch to signup would silently miss\n                // the signup_page_viewed event.\n                nextEl.click();\n                nextEl.focus();\n            },\n        };\n    });\n\n    // Event list tabs (upcoming / past)\n    // Hero ghost eye-tracker.\n    // Finds the two <g class=\"ghost-eye\"> wrappers inside the hero ghost SVG\n    // and continuously eases their SVG `transform` attribute toward the cursor\n    // via a requestAnimationFrame lerp loop. mousemove only updates the target;\n    // a single rAF loop interpolates current \u2192 target each frame, which is much\n    // smoother than triggering a CSS transition on every mouse event.\n    // Disabled on `(any-pointer: coarse)` only setups and on prefers-reduced-motion.\n    Alpine.data(\"ghostEyes\", function () {\n        return {\n            init: function () {\n                var fineCursor = window.matchMedia(\"(any-pointer: fine)\").matches;\n                var reducedMotion = window.matchMedia(\n                    \"(prefers-reduced-motion: reduce)\",\n                ).matches;\n                if (!fineCursor || reducedMotion) return;\n\n                var hero = this.$el;\n                var eyes = hero.querySelectorAll(\".ghost-eye\");\n                var heart = hero.querySelector(\".ghost-heart\");\n                if (!eyes.length && !heart) return;\n\n                // Heart's resting position in the SVG (matches the original\n                // transform=\"translate(248,120)\" on the .ghost-heart element).\n                // JS rewrites this transform every frame as base + offset; the\n                // heartbeat SMIL on the same element uses additive=\"sum\" so\n                // its scale rides on top of our translate without clobbering it.\n                var heartBaseX = 248,\n                    heartBaseY = 120;\n\n                // Eyes drift subtly; heart drifts much more so it visibly\n                // follows the cursor and the eyes appear to chase it.\n                var eyeOffset = 11; // SVG user units (~16 CSS px)\n                var heartOffset = 130; // SVG user units \u2014 heart drifts well outside the ghost silhouette\n                var falloffRange = 320; // px from hero center \u2192 full deflection\n                var eyeEase = 0.22; // pupils react quickly\n                var heartEase = 0.08; // heart drifts lazily, like it's drawn along\n\n                var targetUx = 0,\n                    targetUy = 0; // unit vector \u00D7 falloff (shared direction)\n                var eyeX = 0,\n                    eyeY = 0;\n                var heartX = 0,\n                    heartY = 0;\n                var self = this;\n\n                this._handler = function (event) {\n                    var rect = hero.getBoundingClientRect();\n                    var cx = rect.left + rect.width / 2;\n                    var cy = rect.top + rect.height / 2;\n                    var dx = event.clientX - cx;\n                    var dy = event.clientY - cy;\n                    var dist = Math.sqrt(dx * dx + dy * dy) || 1;\n                    var falloff = Math.min(1, dist / falloffRange);\n                    targetUx = (dx / dist) * falloff;\n                    targetUy = (dy / dist) * falloff;\n                };\n\n                var tick = function () {\n                    var targetEyeX = targetUx * eyeOffset;\n                    var targetEyeY = targetUy * eyeOffset;\n                    var targetHeartX = targetUx * heartOffset;\n                    var targetHeartY = targetUy * heartOffset;\n\n                    eyeX += (targetEyeX - eyeX) * eyeEase;\n                    eyeY += (targetEyeY - eyeY) * eyeEase;\n                    heartX += (targetHeartX - heartX) * heartEase;\n                    heartY += (targetHeartY - heartY) * heartEase;\n\n                    var eyeT =\n                        \"translate(\" + eyeX.toFixed(2) + \" \" + eyeY.toFixed(2) + \")\";\n                    for (var i = 0; i < eyes.length; i++) {\n                        eyes[i].setAttribute(\"transform\", eyeT);\n                    }\n                    if (heart) {\n                        heart.setAttribute(\n                            \"transform\",\n                            \"translate(\" +\n                                (heartBaseX + heartX).toFixed(2) +\n                                \" \" +\n                                (heartBaseY + heartY).toFixed(2) +\n                                \")\",\n                        );\n                    }\n                    self._raf = requestAnimationFrame(tick);\n                };\n\n                window.addEventListener(\"mousemove\", this._handler, {\n                    passive: true,\n                });\n                this._raf = requestAnimationFrame(tick);\n            },\n            destroy: function () {\n                if (this._handler) {\n                    window.removeEventListener(\"mousemove\", this._handler);\n                }\n                if (this._raf) {\n                    cancelAnimationFrame(this._raf);\n                }\n            },\n        };\n    });\n\n    Alpine.data(\"eventTabs\", function () {\n        // Composes makeTabs (state + setTab/isTabActive) with the page-specific\n        // gradient class getters and showUpcoming/showPast aliases the template\n        // already references. Keep the template-facing API stable.\n        var activeClass =\n            \"bg-gradient-to-r from-purple-500 to-pink-500 text-white shadow-md\";\n        var inactiveClass =\n            \"text-gray-600 dark:text-gray-400 bg-white/50 dark:bg-gray-700/50 hover:bg-white/80 dark:hover:bg-gray-700/80\";\n        return mixin(makeTabs(\"upcoming\", [\"upcoming\", \"past\"]), {\n            get isUpcoming() {\n                return this.isTabActive(\"upcoming\");\n            },\n            get isPast() {\n                return this.isTabActive(\"past\");\n            },\n            get upcomingTabClass() {\n                return this.isUpcoming ? activeClass : inactiveClass;\n            },\n            get pastTabClass() {\n                return this.isPast ? activeClass : inactiveClass;\n            },\n            showUpcoming() {\n                this.setTab(\"upcoming\");\n            },\n            showPast() {\n                this.setTab(\"past\");\n            },\n            get upcomingAriaSelected() {\n                return this.isUpcoming ? \"true\" : \"false\";\n            },\n            get pastAriaSelected() {\n                return this.isPast ? \"true\" : \"false\";\n            },\n            get upcomingTabIndex() {\n                return this.isUpcoming ? \"0\" : \"-1\";\n            },\n            get pastTabIndex() {\n                return this.isPast ? \"0\" : \"-1\";\n            },\n            // WAI-ARIA APG tab keyboard behavior (round-2 finding): arrow\n            // keys move both selection and focus between the two tabs;\n            // Home/End jump to the first/last. $refs are set in the\n            // template (x-ref=\"tabUpcoming\" / \"tabPast\") on the same\n            // x-data root, so they're reachable from here.\n            focusTab(name) {\n                this.setTab(name);\n                var self = this;\n                this.$nextTick(function () {\n                    var target =\n                        name === \"upcoming\"\n                            ? self.$refs.tabUpcoming\n                            : self.$refs.tabPast;\n                    if (target) target.focus();\n                });\n            },\n            onTabKeydown(event) {\n                if (event.key === \"ArrowRight\" || event.key === \"ArrowLeft\") {\n                    event.preventDefault();\n                    this.focusTab(this.isUpcoming ? \"past\" : \"upcoming\");\n                } else if (event.key === \"Home\") {\n                    event.preventDefault();\n                    this.focusTab(\"upcoming\");\n                } else if (event.key === \"End\") {\n                    event.preventDefault();\n                    this.focusTab(\"past\");\n                }\n            },\n        });\n    });\n\n    // Event type filter (event_list.html, finding 1-13). Shared active-type\n    // state lives on an Alpine.store because directives here can't call a\n    // method with an argument (STYLE.md \u00A77) \u2014 each chip/card instead reads\n    // its own value from its element's own data-filter-type/data-event-type\n    // attribute in init(), and only ever calls no-arg methods/getters.\n    Alpine.store(\"eventTypeFilter\", { active: \"all\" });\n\n    Alpine.data(\"eventTypeFilterChip\", function () {\n        return {\n            type: \"all\",\n            init() {\n                this.type = this.$el.dataset.filterType || \"all\";\n            },\n            activate() {\n                Alpine.store(\"eventTypeFilter\").active = this.type;\n            },\n            get isActive() {\n                return Alpine.store(\"eventTypeFilter\").active === this.type;\n            },\n            get chipClass() {\n                return this.isActive\n                    ? \"bg-crush-purple text-white border-crush-purple\"\n                    : \"bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-200 border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700\";\n            },\n            // Round-2 finding: the chip's selected state was only visible as\n            // color, which a screen-reader user activating it can't see.\n            get ariaPressed() {\n                return this.isActive ? \"true\" : \"false\";\n            },\n        };\n    });\n\n    Alpine.data(\"eventTypeFilterCard\", function () {\n        return {\n            type: \"\",\n            init() {\n                this.type = this.$el.dataset.eventType || \"\";\n            },\n            get visible() {\n                var active = Alpine.store(\"eventTypeFilter\").active;\n                return active === \"all\" || active === this.type;\n            },\n        };\n    });\n\n    // Retired /account/settings/ (8-08): its 301 lands on the account\n    // drill-down overview, and the browser re-applies the old #anchor. Send\n    // the monolith's anchors to the sub-section that now holds them; an\n    // unknown anchor stays on the overview.\n    var LEGACY_SETTINGS_ANCHORS = {\n        \"email-notifications\": \"notifications\",\n        \"whatsapp-notifications\": \"notifications\",\n        \"push-notifications\": \"notifications\",\n    };\n    Alpine.data(\"legacySettingsAnchor\", function () {\n        return {\n            init: function () {\n                var anchor = window.location.hash.slice(1);\n                if (!anchor) {\n                    return;\n                }\n                if (!Object.prototype.hasOwnProperty.call(LEGACY_SETTINGS_ANCHORS, anchor)) {\n                    history.replaceState(null, \"\", window.location.pathname + window.location.search);\n                    return;\n                }\n                var params = new URLSearchParams(window.location.search);\n                params.set(\"section\", \"account\");\n                params.set(\"sub\", LEGACY_SETTINGS_ANCHORS[anchor]);\n                window.location.replace(\n                    window.location.pathname + \"?\" + params.toString() + \"#\" + anchor\n                );\n            },\n        };\n    });\n\n    // Email preferences component (account settings)\n    // Reads initial unsubscribe state from data-unsubscribed attribute\n    Alpine.data(\"emailPreferences\", function () {\n        return {\n            unsubscribeAll: false,\n            saving: false,\n            saveSuccess: false,\n            saveError: false,\n\n            get unsubscribeAllClass() {\n                return this.unsubscribeAll ? \"opacity-50 pointer-events-none\" : \"\";\n            },\n\n            get showSaving() {\n                return this.saving;\n            },\n\n            get showSuccess() {\n                return this.saveSuccess;\n            },\n\n            get showError() {\n                return this.saveError;\n            },\n\n            get showIdleMessage() {\n                return !this.saving && !this.saveSuccess && !this.saveError;\n            },\n\n            init: function () {\n                var self = this;\n                var unsubscribed = this.$el.getAttribute(\"data-unsubscribed\");\n                this.unsubscribeAll = unsubscribed === \"true\";\n\n                // Event delegation for all email preference toggles\n                this.$el.addEventListener(\"change\", function (event) {\n                    if (event.target.classList.contains(\"email-pref-toggle\")) {\n                        var prefKey = event.target.dataset.prefKey;\n                        self.updatePreference(\n                            prefKey,\n                            event.target.checked,\n                            event.target,\n                        );\n                    }\n                });\n            },\n            toggleUnsubscribe: function () {\n                this.unsubscribeAll = !this.unsubscribeAll;\n                this.updatePreference(\"unsubscribed_all\", this.unsubscribeAll, null);\n            },\n            getCsrfToken: function () {\n                var input = document.querySelector('input[name=\"csrfmiddlewaretoken\"]');\n                if (input && input.value) return input.value;\n                var cookie = document.cookie.split(\"; \").find(function (row) {\n                    return row.startsWith(\"csrftoken=\");\n                });\n                return cookie ? cookie.split(\"=\")[1] : \"\";\n            },\n            updatePreference: function (key, value, checkbox) {\n                var self = this;\n                self.saving = true;\n                self.saveSuccess = false;\n                self.saveError = false;\n\n                fetch(\"/api/email/preferences/\", {\n                    method: \"POST\",\n                    headers: {\n                        \"Content-Type\": \"application/json\",\n                        \"X-CSRFToken\": self.getCsrfToken(),\n                    },\n                    body: JSON.stringify({ key: key, value: value }),\n                })\n                    .then(function (response) {\n                        return response.json();\n                    })\n                    .then(function (data) {\n                        self.saving = false;\n                        if (data.success) {\n                            self.saveSuccess = true;\n                            setTimeout(function () {\n                                self.saveSuccess = false;\n                            }, 2000);\n                        } else {\n                            self.saveError = true;\n                            // Revert on error\n                            if (checkbox) {\n                                checkbox.checked = !value;\n                            }\n                            setTimeout(function () {\n                                self.saveError = false;\n                            }, 3000);\n                        }\n                    })\n                    .catch(function () {\n                        self.saving = false;\n                        self.saveError = true;\n                        // Revert on network error\n                        if (checkbox) {\n                            checkbox.checked = !value;\n                        }\n                        setTimeout(function () {\n                            self.saveError = false;\n                        }, 3000);\n                    });\n            },\n        };\n    });\n\n    // WhatsApp opt-in: saves the moment the switch flips and confirms with a\n    // toast. The surrounding <form> still posts as the no-JS fallback.\n    Alpine.data(\"whatsappPreference\", function () {\n        return {\n            // Tail of the write queue. Rapid flips must reach the server in\n            // click order, or an earlier \"on\" can land after a later \"off\".\n            queue: Promise.resolve(),\n            // #1147: each flip gets an increasing change id. Only the latest\n            // change may move the switch, and it moves it to the last value\n            // the server confirmed -- comparing checkbox.checked instead\n            // let an on -> off -> on run roll back a superseded failure.\n            seq: 0,\n            confirmed: null,\n            save: function (event) {\n                var self = this;\n                var checkbox = event.target;\n                var value = checkbox.checked;\n                var id = ++self.seq;\n                if (self.confirmed === null) self.confirmed = !value;\n                // $el is the switch inside an event handler, so go via the form.\n                var form = checkbox.closest(\"form\");\n                var csrf = form.querySelector('input[name=\"csrfmiddlewaretoken\"]');\n                var fail = function () {\n                    if (id === self.seq) checkbox.checked = self.confirmed;\n                    Alpine.store(\"toasts\").add({\n                        type: \"error\",\n                        message: form.getAttribute(\"data-error-message\"),\n                    });\n                };\n                var send = function () {\n                    return fetch(\"/api/email/preferences/\", {\n                        method: \"POST\",\n                        headers: {\n                            \"Content-Type\": \"application/json\",\n                            \"X-CSRFToken\": csrf ? csrf.value : \"\",\n                        },\n                        body: JSON.stringify({ key: \"whatsapp_opt_in\", value: value }),\n                    })\n                        .then(function (response) {\n                            return response.json();\n                        })\n                        .then(function (data) {\n                            if (!data.success) {\n                                fail();\n                                return;\n                            }\n                            self.confirmed = value;\n                            if (id === self.seq) checkbox.checked = value;\n                            Alpine.store(\"toasts\").add({\n                                type: \"success\",\n                                message: form.getAttribute(\"data-saved-message\"),\n                            });\n                        })\n                        .catch(fail);\n                };\n                this.queue = this.queue.then(send);\n            },\n        };\n    });\n\n    Alpine.data(\"profileSectionAutosave\", function () {\n        return {\n            saveUrl: \"\",\n            section: \"\",\n            saving: false,\n            saveSuccess: false,\n            saveError: false,\n            fieldErrors: {},\n            nonFieldErrors: [],\n            lastSavedData: {},\n            _debounceTimer: null,\n            _activeController: null,\n\n            init: function () {\n                var self = this;\n                this.saveUrl = this.$el.getAttribute(\"data-save-url\") || \"\";\n                this.section = this.$el.getAttribute(\"data-section\") || \"\";\n                this.lastSavedData = this.serializeForm();\n\n                this.$el.addEventListener(\"input\", function (event) {\n                    if (!self._shouldHandleTarget(event.target)) {\n                        return;\n                    }\n\n                    if (self._isDebouncedField(event.target)) {\n                        self.scheduleSave(event.target, 900);\n                    } else if (event.target.type === \"range\") {\n                        self.scheduleSave(event.target, 250);\n                    }\n                });\n\n                this.$el.addEventListener(\"change\", function (event) {\n                    if (!self._shouldHandleTarget(event.target)) {\n                        return;\n                    }\n\n                    if (event.target.type === \"range\") {\n                        self.save(event.target);\n                        return;\n                    }\n\n                    if (!self._isDebouncedField(event.target)) {\n                        self.save(event.target);\n                    }\n                });\n\n                this.$el.addEventListener(\"profile-autosave:trigger\", function () {\n                    self.save(null);\n                });\n            },\n\n            get showSaving() {\n                return this.saving;\n            },\n\n            get showSuccess() {\n                return this.saveSuccess;\n            },\n\n            get showError() {\n                return this.saveError;\n            },\n\n            get showIdleMessage() {\n                return !this.saving && !this.saveSuccess && !this.saveError;\n            },\n\n            scheduleSave: function (trigger, wait) {\n                var self = this;\n                clearTimeout(this._debounceTimer);\n                this._debounceTimer = setTimeout(function () {\n                    self.save(trigger);\n                }, wait || 800);\n            },\n\n            save: function (trigger) {\n                var self = this;\n                var payload;\n\n                if (!this.saveUrl || !this.section) {\n                    return;\n                }\n\n                clearTimeout(this._debounceTimer);\n\n                payload = this.serializeForm();\n                payload.section = this.section;\n\n                if (this._activeController) {\n                    this._activeController.abort();\n                }\n\n                this._activeController = new AbortController();\n                this.saving = true;\n                this.saveSuccess = false;\n                this.saveError = false;\n\n                fetch(this.saveUrl, {\n                    method: \"POST\",\n                    headers: {\n                        \"Content-Type\": \"application/json\",\n                        \"X-CSRFToken\": this.getCsrfToken(),\n                    },\n                    body: JSON.stringify(payload),\n                    signal: this._activeController.signal,\n                })\n                    .then(function (response) {\n                        return response.json().then(function (data) {\n                            return { ok: response.ok, data: data };\n                        });\n                    })\n                    .then(function (result) {\n                        self.saving = false;\n\n                        if (result.ok && result.data.success) {\n                            self._activeController = null;\n                            self.lastSavedData = self.serializeForm();\n                            self.clearErrors();\n                            self.saveSuccess = true;\n                            setTimeout(function () {\n                                self.saveSuccess = false;\n                            }, 1800);\n                            return;\n                        }\n\n                        self._activeController = null;\n                        self.saveError = true;\n                        self.applyErrors(\n                            result.data.errors || {},\n                            result.data.non_field_errors || [],\n                        );\n                        self.revertControlIfNeeded(trigger);\n                        setTimeout(function () {\n                            self.saveError = false;\n                        }, 3000);\n                    })\n                    .catch(function (error) {\n                        if (error && error.name === \"AbortError\") {\n                            return;\n                        }\n\n                        self.saving = false;\n                        self.saveError = true;\n                        self.revertControlIfNeeded(trigger);\n                        setTimeout(function () {\n                            self.saveError = false;\n                        }, 3000);\n                    });\n            },\n\n            serializeForm: function () {\n                var formData = new FormData(this.$el);\n                var data = {};\n                var checkboxGroups = {};\n                var inputs;\n                var i;\n\n                formData.forEach(function (value, key) {\n                    if (Object.prototype.hasOwnProperty.call(data, key)) {\n                        if (!Array.isArray(data[key])) {\n                            data[key] = [data[key]];\n                        }\n                        data[key].push(value);\n                    } else {\n                        data[key] = value;\n                    }\n                });\n\n                inputs = this.$el.querySelectorAll(\"input[type='checkbox'][name]\");\n                for (i = 0; i < inputs.length; i++) {\n                    checkboxGroups[inputs[i].name] =\n                        (checkboxGroups[inputs[i].name] || 0) + 1;\n                }\n\n                Object.keys(checkboxGroups).forEach(function (name) {\n                    if (!Object.prototype.hasOwnProperty.call(data, name)) {\n                        data[name] = checkboxGroups[name] > 1 ? [] : false;\n                    } else if (checkboxGroups[name] === 1) {\n                        data[name] = data[name] === true || data[name] === \"on\";\n                    }\n                });\n\n                return data;\n            },\n\n            applyErrors: function (fieldErrors, nonFieldErrors) {\n                var self = this;\n                this.fieldErrors = fieldErrors || {};\n                this.nonFieldErrors = nonFieldErrors || [];\n\n                this.$el.querySelectorAll(\"[data-error-for]\").forEach(function (node) {\n                    var field = node.getAttribute(\"data-error-for\");\n                    var messages = self.fieldErrors[field] || [];\n                    // Find or create a dedicated text span to avoid destroying child nodes (e.g. SVG icons)\n                    var span = node.querySelector(\"[data-error-text]\");\n                    if (!span) {\n                        span = document.createElement(\"span\");\n                        span.setAttribute(\"data-error-text\", \"\");\n                        node.appendChild(span);\n                    }\n                    span.textContent = messages.length ? messages[0] : \"\";\n                    node.classList.toggle(\"hidden\", messages.length === 0);\n                });\n\n                this.$el\n                    .querySelectorAll(\"[data-non-field-errors]\")\n                    .forEach(function (node) {\n                        node.textContent = \"\";\n                        if (self.nonFieldErrors.length) {\n                            self.nonFieldErrors.forEach(function (message) {\n                                var p = document.createElement(\"p\");\n                                p.textContent = message;\n                                node.appendChild(p);\n                            });\n                            node.classList.remove(\"hidden\");\n                        } else {\n                            node.classList.add(\"hidden\");\n                        }\n                    });\n            },\n\n            clearErrors: function () {\n                this.applyErrors({}, []);\n            },\n\n            revertControlIfNeeded: function (trigger) {\n                var savedValue;\n                var groupValues;\n                var radios;\n\n                if (!trigger || !trigger.name) {\n                    return;\n                }\n\n                savedValue = this.lastSavedData[trigger.name];\n\n                if (trigger.type === \"checkbox\") {\n                    groupValues = Array.isArray(savedValue) ? savedValue : null;\n\n                    if (groupValues) {\n                        this.$el\n                            .querySelectorAll(\n                                \"input[type='checkbox'][name='\" + trigger.name + \"']\",\n                            )\n                            .forEach(function (checkbox) {\n                                checkbox.checked =\n                                    groupValues.indexOf(checkbox.value) !== -1;\n                            });\n                        return;\n                    }\n\n                    trigger.checked = !!savedValue;\n                    return;\n                }\n\n                if (trigger.type === \"radio\") {\n                    radios = this.$el.querySelectorAll(\n                        \"input[type='radio'][name='\" + trigger.name + \"']\",\n                    );\n                    radios.forEach(function (radio) {\n                        radio.checked = radio.value === savedValue;\n                    });\n                    return;\n                }\n\n                if (savedValue !== undefined) {\n                    trigger.value = savedValue;\n                }\n            },\n\n            _shouldHandleTarget: function (target) {\n                return (\n                    target &&\n                    target.form === this.$el &&\n                    target.name &&\n                    target.type !== \"file\"\n                );\n            },\n\n            _isDebouncedField: function (target) {\n                return (\n                    target.tagName === \"TEXTAREA\" ||\n                    target.type === \"text\" ||\n                    target.type === \"tel\"\n                );\n            },\n\n            getCsrfToken: function () {\n                var input = document.querySelector('input[name=\"csrfmiddlewaretoken\"]');\n                if (input && input.value) return input.value;\n                var cookie = document.cookie.split(\"; \").find(function (row) {\n                    return row.startsWith(\"csrftoken=\");\n                });\n                return cookie ? cookie.split(\"=\")[1] : \"\";\n            },\n        };\n    });\n\n    // Push notification preferences component (account settings)\n    // Handles both enabling push and managing preferences\n    // Uses event delegation for CSP compliance - no inline event handlers\n    Alpine.data(\"pushPreferences\", function () {\n        return mixin(mixin(makePushStatusCheck(), makeBlockedPlatform()), {\n            subscriptions: [],\n            isSupported: false,\n            isSubscribed: false,\n            isCurrentDeviceSubscribed: false, // TRUE only if THIS device has push enabled\n            isEnabling: false,\n            isDisabling: false,\n            errorMessage: \"\",\n            permissionDenied: false,\n            isLoading: true,\n            currentEndpoint: null, // For identifying \"This device\" by endpoint\n            currentFingerprint: null, // Stable device fingerprint (fallback for endpoint)\n            endpointDetected: false, // Flag to trigger re-render when endpoint is detected\n            subscriptionHealth: {}, // Map of subscription ID -> health status\n            checkingHealth: false, // True while checking health\n            // i18n strings for time formatting (loaded from data attributes in init)\n            i18n: {\n                neverUsed: \"Never used\",\n                justNow: \"Just now\",\n                lastActive: \"Last active:\",\n                minutesAgo: \"m ago\",\n                hoursAgo: \"h ago\",\n                daysAgo: \"d ago\",\n                monthsAgo: \"mo ago\",\n                checking: \"Checking...\",\n                checkSubscriptionHealth: \"Check Subscription Health\",\n            },\n\n            // Computed getters for CSP compatibility\n            get hasSubscriptions() {\n                return this.subscriptions.length > 0;\n            },\n            get noSubscriptions() {\n                return this.subscriptions.length === 0;\n            },\n            get canEnable() {\n                return (\n                    this.isSupported &&\n                    !this.isCurrentDeviceSubscribed &&\n                    !this.isEnabling &&\n                    !this.permissionDenied\n                );\n            },\n            // Show enable button if current device is NOT subscribed (allows multi-device)\n            get showEnableButton() {\n                return (\n                    !this.isLoading &&\n                    !this.checkTimedOut &&\n                    this.isSupported &&\n                    !this.isCurrentDeviceSubscribed &&\n                    !this.permissionDenied\n                );\n            },\n            get showPermissionDenied() {\n                return !this.isLoading && this.permissionDenied;\n            },\n            get showNotSupported() {\n                return !this.isLoading && !this.isSupported;\n            },\n            get showPreferences() {\n                return !this.isLoading && !this.checkTimedOut && this.isSubscribed && this.hasSubscriptions;\n            },\n            get showLoading() {\n                return this.isLoading;\n            },\n            // Button text getters for CSP compatibility (replaces ternary expressions)\n            get enableButtonText() {\n                return this.isEnabling ? gettext(\"Enabling...\") : gettext(\"Enable Push Notifications\");\n            },\n            get disableButtonText() {\n                return this.isDisabling ? gettext(\"Disabling...\") : gettext(\"Disable\");\n            },\n            get showEnablingIcon() {\n                return this.isEnabling;\n            },\n            get showNotEnablingIcon() {\n                return !this.isEnabling;\n            },\n            // Health check state getters (CSP-safe)\n            get notCheckingHealth() {\n                return !this.checkingHealth;\n            },\n            get checkHealthButtonText() {\n                return this.checkingHealth\n                    ? this.i18n.checking || \"Checking...\"\n                    : this.i18n.checkSubscriptionHealth || \"Check Subscription Health\";\n            },\n\n            init: function () {\n                var self = this;\n\n                // Load i18n strings from data attributes\n                this._loadI18nStrings();\n\n                // Parse initial subscriptions from data attribute\n                var data = this.$el.getAttribute(\"data-subscriptions\");\n                if (data) {\n                    try {\n                        this.subscriptions = JSON.parse(data);\n                    } catch (e) {\n                        console.error(\"[Push] Failed to parse subscriptions:\", e);\n                    }\n                }\n\n                // Check if push is supported directly (don't rely on CrushPush being loaded)\n                // This is the same check as in push-notifications.js\n                this.isSupported =\n                    \"serviceWorker\" in navigator && \"PushManager\" in window;\n\n                // Check if permission was denied\n                if (\"Notification\" in window && Notification.permission === \"denied\") {\n                    this.permissionDenied = true;\n                }\n                this._detectBlockedPlatform();\n\n                // Detect current device endpoint for \"This device\" badge\n                this._detectCurrentEndpoint();\n\n                this._checkStatus();\n\n                // Event delegation for toggle changes\n                this.$el.addEventListener(\"change\", function (event) {\n                    if (event.target.classList.contains(\"push-pref-toggle\")) {\n                        var subId = parseInt(event.target.dataset.subscriptionId);\n                        var prefKey = event.target.dataset.prefKey;\n                        self.updatePreference(\n                            subId,\n                            prefKey,\n                            event.target.checked,\n                            event.target,\n                        );\n                    }\n                });\n\n                // Event delegation for button clicks\n                this.$el.addEventListener(\"click\", function (event) {\n                    if (event.target.closest(\".enable-push-btn\")) {\n                        self.enablePush();\n                    } else if (event.target.closest(\".disable-push-btn\")) {\n                        // Get subscription info from the button's container\n                        var btn = event.target.closest(\".disable-push-btn\");\n                        var container = btn.closest(\"[data-subscription-id]\");\n                        if (container) {\n                            var subscriptionId = container.dataset.subscriptionId;\n                            var endpoint = container.dataset.endpoint;\n                            // Check if this is the current device\n                            if (\n                                self.currentEndpoint &&\n                                endpoint === self.currentEndpoint\n                            ) {\n                                self.disablePush(); // Current device - use existing unsubscribe\n                            } else {\n                                self.disableRemoteSubscription(subscriptionId); // Other device\n                            }\n                        } else {\n                            // Fallback to current device unsubscribe\n                            self.disablePush();\n                        }\n                    }\n                });\n            },\n\n            // Load i18n strings from data attributes\n            _loadI18nStrings: function () {\n                var el = this.$el;\n                this.i18n.neverUsed =\n                    el.getAttribute(\"data-i18n-never-used\") || this.i18n.neverUsed;\n                this.i18n.justNow =\n                    el.getAttribute(\"data-i18n-just-now\") || this.i18n.justNow;\n                this.i18n.lastActive =\n                    el.getAttribute(\"data-i18n-last-active\") || this.i18n.lastActive;\n                this.i18n.minutesAgo =\n                    el.getAttribute(\"data-i18n-minutes-ago\") || this.i18n.minutesAgo;\n                this.i18n.hoursAgo =\n                    el.getAttribute(\"data-i18n-hours-ago\") || this.i18n.hoursAgo;\n                this.i18n.daysAgo =\n                    el.getAttribute(\"data-i18n-days-ago\") || this.i18n.daysAgo;\n                this.i18n.monthsAgo =\n                    el.getAttribute(\"data-i18n-months-ago\") || this.i18n.monthsAgo;\n                this.i18n.checking =\n                    el.getAttribute(\"data-i18n-checking\") || this.i18n.checking;\n                this.i18n.checkSubscriptionHealth =\n                    el.getAttribute(\"data-i18n-check-subscription-health\") ||\n                    this.i18n.checkSubscriptionHealth;\n            },\n\n            // Detect current device's push endpoint and fingerprint for \"This device\" identification\n            // Uses endpoint as primary identifier, fingerprint as fallback\n            // Sets isCurrentDeviceSubscribed based on whether THIS device is in the subscriptions list\n            _detectCurrentEndpoint: function () {\n                var self = this;\n\n                // Generate fingerprint immediately using our own implementation\n                // This ensures fingerprint is always available, regardless of CrushPush loading\n                self.currentFingerprint = self._generateFingerprint();\n\n                if (\"serviceWorker\" in navigator) {\n                    navigator.serviceWorker.ready\n                        .then(function (reg) {\n                            return reg.pushManager.getSubscription();\n                        })\n                        .then(function (sub) {\n                            self.currentEndpoint = sub ? sub.endpoint : null;\n                            self.endpointDetected = true; // Trigger Alpine reactivity\n\n                            // Strategy 1: Match by endpoint (most accurate when available)\n                            if (sub && sub.endpoint) {\n                                var subscriptionElements =\n                                    self.$el.querySelectorAll(\"[data-endpoint]\");\n                                for (var i = 0; i < subscriptionElements.length; i++) {\n                                    if (\n                                        subscriptionElements[i].dataset.endpoint ===\n                                        sub.endpoint\n                                    ) {\n                                        self.isCurrentDeviceSubscribed = true;\n                                        self._showThisDeviceBadge(\n                                            subscriptionElements[i],\n                                        );\n                                        break;\n                                    }\n                                }\n                            }\n\n                            // Strategy 2: Fallback to fingerprint if endpoint didn't match\n                            if (\n                                !self.isCurrentDeviceSubscribed &&\n                                self.currentFingerprint\n                            ) {\n                                self._matchByFingerprint();\n                            }\n                        })\n                        .catch(function () {\n                            self.endpointDetected = true; // Mark as done even on failure\n                            // Try fingerprint matching as fallback\n                            if (self.currentFingerprint) {\n                                self._matchByFingerprint();\n                            }\n                        });\n                } else {\n                    self.endpointDetected = true; // No service worker support\n                    // Still try fingerprint matching\n                    if (self.currentFingerprint) {\n                        self._matchByFingerprint();\n                    }\n                }\n            },\n\n            // Match device by fingerprint (fallback when endpoint doesn't match)\n            _matchByFingerprint: function () {\n                var self = this;\n                var subscriptionElements =\n                    self.$el.querySelectorAll(\"[data-fingerprint]\");\n                for (var i = 0; i < subscriptionElements.length; i++) {\n                    if (\n                        subscriptionElements[i].dataset.fingerprint ===\n                        self.currentFingerprint\n                    ) {\n                        self.isCurrentDeviceSubscribed = true;\n                        self._showThisDeviceBadge(subscriptionElements[i]);\n                        break;\n                    }\n                }\n            },\n\n            // Retry device matching after DOM is rendered (called after isLoading becomes false)\n            // This handles the race condition where _detectCurrentEndpoint runs before\n            // the subscription elements are rendered by Alpine's x-if\n            _retryDeviceMatch: function () {\n                var self = this;\n                if (self.isCurrentDeviceSubscribed) return; // Already matched\n\n                // Try endpoint matching first\n                if (self.currentEndpoint) {\n                    var subscriptionElements =\n                        self.$el.querySelectorAll(\"[data-endpoint]\");\n                    for (var i = 0; i < subscriptionElements.length; i++) {\n                        if (\n                            subscriptionElements[i].dataset.endpoint ===\n                            self.currentEndpoint\n                        ) {\n                            self.isCurrentDeviceSubscribed = true;\n                            self._showThisDeviceBadge(subscriptionElements[i]);\n                            return;\n                        }\n                    }\n                }\n\n                // Fall back to fingerprint matching\n                if (self.currentFingerprint) {\n                    self._matchByFingerprint();\n                }\n            },\n\n            // Show \"This device\" badge for a subscription container\n            _showThisDeviceBadge: function (container) {\n                var badges = container.querySelectorAll(\".this-device-badge\");\n                for (var i = 0; i < badges.length; i++) {\n                    badges[i].classList.remove(\"hidden\");\n                }\n            },\n\n            // Generate device fingerprint independently (same algorithm as push-notifications.js)\n            // This ensures fingerprint is always available even if CrushPush fails to load\n            _generateFingerprint: function () {\n                var components = [\n                    screen.width,\n                    screen.height,\n                    window.devicePixelRatio || 1,\n                    new Date().getTimezoneOffset(),\n                    navigator.language || \"\",\n                    navigator.platform || \"\",\n                    navigator.hardwareConcurrency || 0,\n                    navigator.deviceMemory || 0,\n                    \"ontouchstart\" in window ? 1 : 0,\n                    screen.colorDepth || 0,\n                    this._getCanvasFingerprint(),\n                ];\n                return this._simpleHash(components.join(\"|\"));\n            },\n\n            // Canvas-based fingerprint component (same as push-notifications.js)\n            _getCanvasFingerprint: function () {\n                try {\n                    var canvas = document.createElement(\"canvas\");\n                    canvas.width = 200;\n                    canvas.height = 50;\n                    var ctx = canvas.getContext(\"2d\");\n                    ctx.textBaseline = \"top\";\n                    ctx.font = \"14px Arial\";\n                    ctx.fillStyle = \"#f60\";\n                    ctx.fillRect(125, 1, 62, 20);\n                    ctx.fillStyle = \"#069\";\n                    ctx.fillText(\"Crush.lu PWA\", 2, 15);\n                    ctx.fillStyle = \"rgba(102, 204, 0, 0.7)\";\n                    ctx.fillText(\"Crush.lu PWA\", 4, 17);\n                    return canvas.toDataURL().slice(-50);\n                } catch (e) {\n                    return \"no-canvas\";\n                }\n            },\n\n            // Simple hash function (djb2 algorithm, same as push-notifications.js)\n            _simpleHash: function (str) {\n                var hash = 5381;\n                for (var i = 0; i < str.length; i++) {\n                    hash = (hash << 5) + hash + str.charCodeAt(i);\n                    hash = hash & hash;\n                }\n                return Math.abs(hash).toString(16).padStart(8, \"0\");\n            },\n\n            // Check if a subscription is from the current device\n            isCurrentDevice: function (subscription) {\n                // Check by endpoint first, then fingerprint\n                if (\n                    this.currentEndpoint &&\n                    subscription.endpoint === this.currentEndpoint\n                ) {\n                    return true;\n                }\n                if (\n                    this.currentFingerprint &&\n                    subscription.device_fingerprint === this.currentFingerprint\n                ) {\n                    return true;\n                }\n                return false;\n            },\n\n            // Format relative time for \"Last active\" display\n            formatRelativeTime: function (dateStr) {\n                if (!dateStr) return this.i18n.neverUsed;\n                var date = new Date(dateStr);\n                var now = new Date();\n                var diffMs = now - date;\n                var diffMins = Math.floor(diffMs / 60000);\n                if (diffMins < 1) return this.i18n.justNow;\n                if (diffMins < 60) return diffMins + \" \" + this.i18n.minutesAgo;\n                var diffHours = Math.floor(diffMins / 60);\n                if (diffHours < 24) return diffHours + \" \" + this.i18n.hoursAgo;\n                var diffDays = Math.floor(diffHours / 24);\n                if (diffDays < 30) return diffDays + \" \" + this.i18n.daysAgo;\n                var diffMonths = Math.floor(diffDays / 30);\n                return diffMonths + \" \" + this.i18n.monthsAgo;\n            },\n\n            enablePush: function () {\n                var self = this;\n                if (!this.isSupported || this.isEnabling) return;\n\n                this.isEnabling = true;\n                this.errorMessage = \"\";\n\n                window.CrushPush.subscribe()\n                    .then(function (result) {\n                        self.isEnabling = false;\n                        if (result.success) {\n                            self.isSubscribed = true;\n                            // Reload page to get fresh subscription data\n                            window.location.reload();\n                        } else {\n                            if (result.error === \"Permission denied\") {\n                                self.permissionDenied = true;\n                            } else {\n                                self.errorMessage =\n                                    result.error || \"Failed to enable notifications\";\n                            }\n                        }\n                    })\n                    .catch(function (err) {\n                        self.isEnabling = false;\n                        self.errorMessage = gettext(\"An error occurred. Please try again.\");\n                        console.error(\"[Push] Enable error:\", err);\n                    });\n            },\n\n            disablePush: function () {\n                var self = this;\n                if (!this.isSupported || this.isDisabling) return;\n\n                this.isDisabling = true;\n\n                window.CrushPush.unsubscribe()\n                    .then(function (result) {\n                        self.isDisabling = false;\n                        if (result.success) {\n                            self.isSubscribed = false;\n                            self.isCurrentDeviceSubscribed = false;\n                            self.subscriptions = [];\n                            // Reload to update UI\n                            window.location.reload();\n                        } else {\n                            self.errorMessage =\n                                result.error || \"Failed to disable notifications\";\n                        }\n                    })\n                    .catch(function (err) {\n                        self.isDisabling = false;\n                        self.errorMessage = gettext(\"An error occurred. Please try again.\");\n                        console.error(\"[Push] Disable error:\", err);\n                    });\n            },\n\n            // Disable push subscription for a remote device (not the current device)\n            disableRemoteSubscription: function (subscriptionId) {\n                var self = this;\n                if (this.isDisabling) return;\n\n                this.isDisabling = true;\n\n                fetch(\"/api/push/delete-subscription/\", {\n                    method: \"POST\",\n                    headers: {\n                        \"Content-Type\": \"application/json\",\n                        \"X-CSRFToken\": self.getCsrfToken(),\n                    },\n                    body: JSON.stringify({ subscription_id: subscriptionId }),\n                })\n                    .then(function (response) {\n                        return response.json();\n                    })\n                    .then(function (data) {\n                        self.isDisabling = false;\n                        if (data.success) {\n                            // Reload to update UI\n                            window.location.reload();\n                        } else {\n                            self.errorMessage =\n                                data.error || \"Failed to disable notifications\";\n                        }\n                    })\n                    .catch(function (err) {\n                        self.isDisabling = false;\n                        self.errorMessage = gettext(\"An error occurred. Please try again.\");\n                        console.error(\"[Push] Remote disable error:\", err);\n                    });\n            },\n\n            updatePreference: function (subscriptionId, prefKey, value, checkbox) {\n                var self = this;\n                var preferences = {};\n                preferences[prefKey] = value;\n\n                fetch(\"/api/push/preferences/\", {\n                    method: \"POST\",\n                    headers: {\n                        \"Content-Type\": \"application/json\",\n                        \"X-CSRFToken\": self.getCsrfToken(),\n                    },\n                    body: JSON.stringify({\n                        subscriptionId: subscriptionId,\n                        preferences: preferences,\n                    }),\n                })\n                    .then(function (response) {\n                        return response.json();\n                    })\n                    .then(function (data) {\n                        if (!data.success) {\n                            // Revert checkbox on error\n                            checkbox.checked = !value;\n                            console.error(\n                                \"[Push] Failed to update preference:\",\n                                data.error,\n                            );\n                        }\n                    })\n                    .catch(function (err) {\n                        // Revert checkbox on network error\n                        checkbox.checked = !value;\n                        console.error(\"[Push] Network error:\", err);\n                    });\n            },\n\n            getCsrfToken: function () {\n                // First try hidden form input (works with CSRF_COOKIE_HTTPONLY=True)\n                var input = document.querySelector('input[name=\"csrfmiddlewaretoken\"]');\n                if (input && input.value) return input.value;\n                // Fallback to cookie\n                var cookie = document.cookie.split(\"; \").find(function (row) {\n                    return row.startsWith(\"csrftoken=\");\n                });\n                return cookie ? cookie.split(\"=\")[1] : \"\";\n            },\n\n            // Wait for CrushPush, then ask it whether this device is subscribed\n            _checkStatus: function () {\n                var self = this;\n                this._runStatusCheck(function (finish) {\n                    self._waitForCrushPush(function () {\n                        if (!self.isSupported || !window.CrushPush.isSubscribed) {\n                            return finish();\n                        }\n                        window.CrushPush.isSubscribed()\n                            .then(function (subscribed) {\n                                self.isSubscribed = subscribed;\n                                finish();\n                            })\n                            .catch(finish);\n                    });\n                });\n            },\n\n            // Wait for CrushPush to be available (handles script load timing)\n            _waitForCrushPush: function (callback) {\n                var maxAttempts = 20; // 2 seconds max\n                var attempts = 0;\n\n                function check() {\n                    attempts++;\n                    if (window.CrushPush) {\n                        callback();\n                    } else if (attempts < maxAttempts) {\n                        setTimeout(check, 100);\n                    }\n                    // Otherwise CrushPush never loaded (script blocked or\n                    // failed): leave the check pending so the status-check\n                    // timeout offers Retry instead of a dead Enable button.\n                }\n\n                check();\n            },\n\n            // Check health of all subscriptions\n            checkAllSubscriptionsHealth: function () {\n                var self = this;\n                self.checkingHealth = true;\n\n                // Initialize subscriptionHealth if not exists\n                if (!self.subscriptionHealth) {\n                    self.subscriptionHealth = {};\n                }\n\n                var promises = self.subscriptions.map(function (sub) {\n                    return fetch(\"/api/push/validate-subscription/\", {\n                        method: \"POST\",\n                        headers: {\n                            \"Content-Type\": \"application/json\",\n                            \"X-CSRFToken\": self.getCsrfToken(),\n                        },\n                        body: JSON.stringify({ endpoint: sub.endpoint }),\n                    })\n                        .then(function (response) {\n                            if (response.ok) {\n                                return response.json();\n                            }\n                            throw new Error(\"Health check failed\");\n                        })\n                        .then(function (data) {\n                            self.subscriptionHealth[sub.id] = {\n                                valid: data.valid,\n                                warning: data.warning,\n                                reason: data.reason,\n                                age_days: data.age_days,\n                            };\n                        })\n                        .catch(function (error) {\n                            console.error(\n                                \"Health check failed for subscription\",\n                                sub.id,\n                                error,\n                            );\n                            self.subscriptionHealth[sub.id] = {\n                                valid: false,\n                                reason: \"check_failed\",\n                            };\n                        });\n                });\n\n                Promise.all(promises).then(function () {\n                    self.checkingHealth = false;\n                    // Force Alpine to update\n                    self.$nextTick(function () {\n                        // Trigger reactivity\n                    });\n                });\n            },\n\n            // Refresh a specific subscription\n            refreshSubscription: function (subscriptionId) {\n                var self = this;\n                var confirmed = confirm(\n                    \"Refresh this push notification subscription? You may need to grant permission again.\",\n                );\n                if (!confirmed) return;\n\n                // Use window.CrushPushNotifications.refresh from push-notifications.js\n                if (\n                    window.CrushPushNotifications &&\n                    window.CrushPushNotifications.refresh\n                ) {\n                    window.CrushPushNotifications.refresh()\n                        .then(function (success) {\n                            if (success) {\n                                // Show success and reload\n                                alert(gettext(\"Subscription refreshed successfully\"));\n                                window.location.reload();\n                            } else {\n                                alert(gettext(\"Failed to refresh subscription\"));\n                            }\n                        })\n                        .catch(function (error) {\n                            console.error(\"Error refreshing:\", error);\n                            alert(gettext(\"Error refreshing subscription\"));\n                        });\n                } else {\n                    alert(\n                        gettext(\"Push notification system not loaded. Please refresh the page and try again.\"),\n                    );\n                }\n            },\n\n            // Delete a subscription by ID\n            deleteSubscription: function (subscriptionId) {\n                var self = this;\n                var confirmed = confirm(\n                    \"Remove this subscription? You will stop receiving notifications on this device.\",\n                );\n                if (!confirmed) return;\n\n                self.disableRemoteSubscription(subscriptionId);\n            },\n\n            // CSP-safe helper: Check if health status exists for subscription\n            hasHealthStatus: function (subscriptionId) {\n                return !!this.subscriptionHealth[subscriptionId];\n            },\n\n            // CSP-safe helper: Check if subscription is healthy\n            isHealthy: function (subscriptionId) {\n                var health = this.subscriptionHealth[subscriptionId];\n                return health && health.valid && !health.warning;\n            },\n\n            // CSP-safe helper: Check if subscription has old_subscription warning\n            isOldSubscription: function (subscriptionId) {\n                var health = this.subscriptionHealth[subscriptionId];\n                return health && health.warning === \"old_subscription\";\n            },\n\n            // CSP-safe helper: Get age text for old subscription\n            getAgeText: function (subscriptionId) {\n                var health = this.subscriptionHealth[subscriptionId];\n                var ageDays = health && health.age_days ? health.age_days : 0;\n                return \"Subscription is \" + ageDays + \" days old\";\n            },\n\n            // CSP-safe helper: Check if subscription has high failure count\n            hasHighFailureCount: function (subscriptionId) {\n                var health = this.subscriptionHealth[subscriptionId];\n                return (\n                    health &&\n                    health.valid === false &&\n                    health.reason === \"high_failure_count\"\n                );\n            },\n\n            // CSP-safe helper: Check if subscription is not found\n            isNotFound: function (subscriptionId) {\n                var health = this.subscriptionHealth[subscriptionId];\n                return (\n                    health && health.valid === false && health.reason === \"not_found\"\n                );\n            },\n        });\n    });\n\n    // CSP-safe wrapper component for individual subscription health status\n    // Creates computed properties for a specific subscription ID\n    // CSP build: no call arguments in x-data, so the id comes from\n    // data-subscription-id on the element.\n    Alpine.data(\"subscriptionHealthStatus\", function () {\n        return {\n            subscriptionId: null,\n\n            init: function () {\n                this.subscriptionId = parseInt(\n                    this.$el.dataset.subscriptionId,\n                    10,\n                );\n            },\n\n            // The parent pushPreferences component. $root would be this\n            // element's own scope (it declares x-data), so read the nearest\n            // scope above it instead.\n            get parentComponent() {\n                return (\n                    (this.$el.parentElement &&\n                        Alpine.$data(this.$el.parentElement)) ||\n                    {}\n                );\n            },\n\n            // CSP-safe computed properties\n            get hasStatus() {\n                var parent = this.parentComponent;\n                return (\n                    parent.subscriptionHealth &&\n                    !!parent.subscriptionHealth[this.subscriptionId]\n                );\n            },\n\n            get healthyStatus() {\n                var parent = this.parentComponent;\n                var health = parent.subscriptionHealth\n                    ? parent.subscriptionHealth[this.subscriptionId]\n                    : null;\n                return health && health.valid && !health.warning;\n            },\n\n            get oldStatus() {\n                var parent = this.parentComponent;\n                var health = parent.subscriptionHealth\n                    ? parent.subscriptionHealth[this.subscriptionId]\n                    : null;\n                return health && health.warning === \"old_subscription\";\n            },\n\n            get ageText() {\n                var parent = this.parentComponent;\n                var health = parent.subscriptionHealth\n                    ? parent.subscriptionHealth[this.subscriptionId]\n                    : null;\n                var ageDays = health && health.age_days ? health.age_days : 0;\n                return \"Subscription is \" + ageDays + \" days old\";\n            },\n\n            get failureStatus() {\n                var parent = this.parentComponent;\n                var health = parent.subscriptionHealth\n                    ? parent.subscriptionHealth[this.subscriptionId]\n                    : null;\n                return (\n                    health &&\n                    health.valid === false &&\n                    health.reason === \"high_failure_count\"\n                );\n            },\n\n            get notFoundStatus() {\n                var parent = this.parentComponent;\n                var health = parent.subscriptionHealth\n                    ? parent.subscriptionHealth[this.subscriptionId]\n                    : null;\n                return (\n                    health && health.valid === false && health.reason === \"not_found\"\n                );\n            },\n\n            // Pass-through getters for parent properties (for disable button)\n            get isDisabling() {\n                var parent = this.parentComponent;\n                return parent.isDisabling || false;\n            },\n\n            get disableButtonText() {\n                var parent = this.parentComponent;\n                return parent.disableButtonText || \"Disable Notifications\";\n            },\n\n            // CSP-safe methods\n            refreshSubscriptionById: function () {\n                var parent = this.parentComponent;\n                if (parent.refreshSubscription) {\n                    parent.refreshSubscription(this.subscriptionId);\n                }\n            },\n\n            deleteSubscriptionById: function () {\n                var parent = this.parentComponent;\n                if (parent.deleteSubscription) {\n                    parent.deleteSubscription(this.subscriptionId);\n                }\n            },\n        };\n    });\n\n    // Coach push notification preferences component (account settings and coach dashboard)\n    // Separate from user push preferences - completely independent system\n    Alpine.data(\"coachPushPreferences\", function () {\n        return mixin(mixin(makePushStatusCheck(), makeBlockedPlatform()), {\n            subscriptions: [],\n            isSupported: false,\n            isSubscribed: false,\n            isCurrentDeviceSubscribed: false, // TRUE only if THIS device has push enabled\n            isEnabling: false,\n            isDisabling: false,\n            errorMessage: \"\",\n            permissionDenied: false,\n            isLoading: true,\n            currentEndpoint: null, // For identifying \"This device\" by endpoint\n            currentFingerprint: null, // Stable device fingerprint (fallback for endpoint)\n            endpointDetected: false, // Flag to trigger re-render when endpoint is detected\n            // i18n strings for time formatting (loaded from data attributes in init)\n            i18n: {\n                neverUsed: \"Never used\",\n                justNow: \"Just now\",\n                lastActive: \"Last active:\",\n                minutesAgo: \"m ago\",\n                hoursAgo: \"h ago\",\n                daysAgo: \"d ago\",\n                monthsAgo: \"mo ago\",\n                checking: \"Checking...\",\n                checkSubscriptionHealth: \"Check Subscription Health\",\n            },\n\n            get hasSubscriptions() {\n                return this.subscriptions.length > 0;\n            },\n            // Show enable button only if THIS device isn't subscribed (allows multi-device)\n            get showEnableButton() {\n                return (\n                    !this.isLoading &&\n                    !this.checkTimedOut &&\n                    this.isSupported &&\n                    !this.isCurrentDeviceSubscribed &&\n                    !this.permissionDenied\n                );\n            },\n            get showPermissionDenied() {\n                return !this.isLoading && this.permissionDenied;\n            },\n            get showNotSupported() {\n                return !this.isLoading && !this.isSupported;\n            },\n            get showPreferences() {\n                return !this.isLoading && !this.checkTimedOut && this.isSubscribed && this.hasSubscriptions;\n            },\n            get showLoading() {\n                return this.isLoading;\n            },\n            get showEnablingSpinner() {\n                return this.isEnabling;\n            },\n            get showNotEnablingIcon() {\n                return !this.isEnabling;\n            },\n            get showTestSpinner() {\n                return this.isSendingTest;\n            },\n            get enableButtonText() {\n                return this.isEnabling ? gettext(\"Enabling...\") : gettext(\"Enable Notifications\");\n            },\n            get testButtonText() {\n                return this.isSendingTest ? gettext(\"Sending...\") : gettext(\"Send Test\");\n            },\n            get disableButtonText() {\n                return this.isDisabling ? gettext(\"Disabling...\") : gettext(\"Disable Notifications\");\n            },\n            isSendingTest: false,\n\n            init: function () {\n                var self = this;\n\n                // Load i18n strings from data attributes\n                this._loadI18nStrings();\n\n                var data = this.$el.getAttribute(\"data-subscriptions\");\n                if (data) {\n                    try {\n                        this.subscriptions = JSON.parse(data);\n                    } catch (e) {\n                        console.error(\"[CoachPush] Failed to parse subscriptions:\", e);\n                    }\n                }\n\n                this.isSupported =\n                    \"serviceWorker\" in navigator && \"PushManager\" in window;\n                if (\"Notification\" in window && Notification.permission === \"denied\") {\n                    this.permissionDenied = true;\n                }\n                this._detectBlockedPlatform();\n\n                // Detect current device endpoint for \"This device\" badge\n                this._detectCurrentEndpoint();\n\n                this._checkStatus();\n\n                this.$el.addEventListener(\"change\", function (event) {\n                    if (event.target.classList.contains(\"coach-push-pref-toggle\")) {\n                        var subId = parseInt(event.target.dataset.subscriptionId);\n                        var prefKey = event.target.dataset.prefKey;\n                        self.updatePreference(\n                            subId,\n                            prefKey,\n                            event.target.checked,\n                            event.target,\n                        );\n                    }\n                });\n\n                this.$el.addEventListener(\"click\", function (event) {\n                    if (event.target.closest(\".enable-coach-push-btn\")) {\n                        self.enablePush();\n                    } else if (event.target.closest(\".disable-coach-push-btn\")) {\n                        // Identify which subscription to disable\n                        var btn = event.target.closest(\".disable-coach-push-btn\");\n                        var container = btn.closest(\"[data-subscription-id]\");\n                        if (container) {\n                            var subscriptionId = parseInt(\n                                container.dataset.subscriptionId,\n                            );\n                            var endpoint = container.dataset.endpoint;\n                            // Check if this is the current device\n                            if (\n                                self.currentEndpoint &&\n                                endpoint === self.currentEndpoint\n                            ) {\n                                self.disablePush(); // Current device - use existing unsubscribe\n                            } else {\n                                self.disableRemoteSubscription(subscriptionId); // Other device\n                            }\n                        } else {\n                            self.disablePush(); // Fallback to current device\n                        }\n                    } else if (event.target.closest(\".test-coach-push-btn\")) {\n                        self.sendTestNotification();\n                    }\n                });\n            },\n\n            // Load i18n strings from data attributes\n            _loadI18nStrings: function () {\n                var el = this.$el;\n                this.i18n.neverUsed =\n                    el.getAttribute(\"data-i18n-never-used\") || this.i18n.neverUsed;\n                this.i18n.justNow =\n                    el.getAttribute(\"data-i18n-just-now\") || this.i18n.justNow;\n                this.i18n.lastActive =\n                    el.getAttribute(\"data-i18n-last-active\") || this.i18n.lastActive;\n                this.i18n.minutesAgo =\n                    el.getAttribute(\"data-i18n-minutes-ago\") || this.i18n.minutesAgo;\n                this.i18n.hoursAgo =\n                    el.getAttribute(\"data-i18n-hours-ago\") || this.i18n.hoursAgo;\n                this.i18n.daysAgo =\n                    el.getAttribute(\"data-i18n-days-ago\") || this.i18n.daysAgo;\n                this.i18n.monthsAgo =\n                    el.getAttribute(\"data-i18n-months-ago\") || this.i18n.monthsAgo;\n                this.i18n.checking =\n                    el.getAttribute(\"data-i18n-checking\") || this.i18n.checking;\n                this.i18n.checkSubscriptionHealth =\n                    el.getAttribute(\"data-i18n-check-subscription-health\") ||\n                    this.i18n.checkSubscriptionHealth;\n            },\n\n            // Detect current device's push endpoint and fingerprint for \"This device\" identification\n            // Uses endpoint as primary identifier, fingerprint as fallback\n            // Sets isCurrentDeviceSubscribed to true if this device has an active subscription\n            _detectCurrentEndpoint: function () {\n                var self = this;\n\n                // Generate fingerprint immediately using our own implementation\n                // No dependency on CrushPush - works independently\n                self.currentFingerprint = self._generateFingerprint();\n\n                if (\"serviceWorker\" in navigator) {\n                    navigator.serviceWorker.ready\n                        .then(function (reg) {\n                            return reg.pushManager.getSubscription();\n                        })\n                        .then(function (sub) {\n                            self.currentEndpoint = sub ? sub.endpoint : null;\n                            self.endpointDetected = true; // Trigger Alpine reactivity\n\n                            // Strategy 1: Match by endpoint (most accurate when available)\n                            if (sub && sub.endpoint) {\n                                var subscriptionElements =\n                                    self.$el.querySelectorAll(\"[data-endpoint]\");\n                                for (var i = 0; i < subscriptionElements.length; i++) {\n                                    if (\n                                        subscriptionElements[i].dataset.endpoint ===\n                                        sub.endpoint\n                                    ) {\n                                        self.isCurrentDeviceSubscribed = true;\n                                        self._showThisDeviceBadge(\n                                            subscriptionElements[i],\n                                        );\n                                        break;\n                                    }\n                                }\n                            }\n\n                            // Strategy 2: Fallback to fingerprint if endpoint didn't match\n                            if (\n                                !self.isCurrentDeviceSubscribed &&\n                                self.currentFingerprint\n                            ) {\n                                self._matchByFingerprint();\n                            }\n                        })\n                        .catch(function () {\n                            self.endpointDetected = true; // Mark as done even on failure\n                            // Try fingerprint matching as fallback\n                            if (self.currentFingerprint) {\n                                self._matchByFingerprint();\n                            }\n                        });\n                } else {\n                    self.endpointDetected = true; // No service worker support\n                    // Still try fingerprint matching\n                    if (self.currentFingerprint) {\n                        self._matchByFingerprint();\n                    }\n                }\n            },\n\n            // Match device by fingerprint (fallback when endpoint doesn't match)\n            _matchByFingerprint: function () {\n                var self = this;\n                var subscriptionElements =\n                    self.$el.querySelectorAll(\"[data-fingerprint]\");\n                for (var i = 0; i < subscriptionElements.length; i++) {\n                    if (\n                        subscriptionElements[i].dataset.fingerprint ===\n                        self.currentFingerprint\n                    ) {\n                        self.isCurrentDeviceSubscribed = true;\n                        self._showThisDeviceBadge(subscriptionElements[i]);\n                        break;\n                    }\n                }\n            },\n\n            // Retry device matching after DOM is rendered (called after isLoading becomes false)\n            // This handles the race condition where _detectCurrentEndpoint runs before\n            // the subscription elements are rendered by Alpine's x-if\n            _retryDeviceMatch: function () {\n                var self = this;\n                if (self.isCurrentDeviceSubscribed) return; // Already matched\n\n                // Try endpoint matching first\n                if (self.currentEndpoint) {\n                    var subscriptionElements =\n                        self.$el.querySelectorAll(\"[data-endpoint]\");\n                    for (var i = 0; i < subscriptionElements.length; i++) {\n                        if (\n                            subscriptionElements[i].dataset.endpoint ===\n                            self.currentEndpoint\n                        ) {\n                            self.isCurrentDeviceSubscribed = true;\n                            self._showThisDeviceBadge(subscriptionElements[i]);\n                            return;\n                        }\n                    }\n                }\n\n                // Fall back to fingerprint matching\n                if (self.currentFingerprint) {\n                    self._matchByFingerprint();\n                }\n            },\n\n            // Show \"This device\" badge for a subscription container\n            _showThisDeviceBadge: function (container) {\n                var badges = container.querySelectorAll(\".this-device-badge\");\n                for (var i = 0; i < badges.length; i++) {\n                    badges[i].classList.remove(\"hidden\");\n                }\n            },\n\n            // Check if a subscription is from the current device\n            isCurrentDevice: function (subscription) {\n                // Check by endpoint first, then fingerprint\n                if (\n                    this.currentEndpoint &&\n                    subscription.endpoint === this.currentEndpoint\n                ) {\n                    return true;\n                }\n                if (\n                    this.currentFingerprint &&\n                    subscription.device_fingerprint === this.currentFingerprint\n                ) {\n                    return true;\n                }\n                return false;\n            },\n\n            // Format relative time for \"Last active\" display\n            formatRelativeTime: function (dateStr) {\n                if (!dateStr) return this.i18n.neverUsed;\n                var date = new Date(dateStr);\n                var now = new Date();\n                var diffMs = now - date;\n                var diffMins = Math.floor(diffMs / 60000);\n                if (diffMins < 1) return this.i18n.justNow;\n                if (diffMins < 60) return diffMins + \" \" + this.i18n.minutesAgo;\n                var diffHours = Math.floor(diffMins / 60);\n                if (diffHours < 24) return diffHours + \" \" + this.i18n.hoursAgo;\n                var diffDays = Math.floor(diffHours / 24);\n                if (diffDays < 30) return diffDays + \" \" + this.i18n.daysAgo;\n                var diffMonths = Math.floor(diffDays / 30);\n                return diffMonths + \" \" + this.i18n.monthsAgo;\n            },\n\n            enablePush: function () {\n                var self = this;\n                if (!this.isSupported || this.isEnabling) return;\n                this.isEnabling = true;\n                this.errorMessage = \"\";\n\n                Notification.requestPermission()\n                    .then(function (permission) {\n                        if (permission !== \"granted\") {\n                            self.isEnabling = false;\n                            self.permissionDenied = true;\n                            return;\n                        }\n                        navigator.serviceWorker.ready.then(function (registration) {\n                            fetch(\"/api/coach/push/vapid-public-key/\")\n                                .then(function (r) {\n                                    return r.json();\n                                })\n                                .then(function (data) {\n                                    if (!data.success) throw new Error(data.error);\n                                    return registration.pushManager.subscribe({\n                                        userVisibleOnly: true,\n                                        applicationServerKey:\n                                            self._urlBase64ToUint8Array(data.publicKey),\n                                    });\n                                })\n                                .then(function (subscription) {\n                                    // Get fingerprint for stable device identification\n                                    // Use our own implementation - no dependency on CrushPush\n                                    var fingerprint = self._generateFingerprint();\n                                    return fetch(\"/api/coach/push/subscribe/\", {\n                                        method: \"POST\",\n                                        headers: {\n                                            \"Content-Type\": \"application/json\",\n                                            \"X-CSRFToken\": self.getCsrfToken(),\n                                        },\n                                        body: JSON.stringify({\n                                            endpoint: subscription.endpoint,\n                                            keys: {\n                                                p256dh: btoa(\n                                                    String.fromCharCode.apply(\n                                                        null,\n                                                        new Uint8Array(\n                                                            subscription.getKey(\n                                                                \"p256dh\",\n                                                            ),\n                                                        ),\n                                                    ),\n                                                ),\n                                                auth: btoa(\n                                                    String.fromCharCode.apply(\n                                                        null,\n                                                        new Uint8Array(\n                                                            subscription.getKey(\"auth\"),\n                                                        ),\n                                                    ),\n                                                ),\n                                            },\n                                            userAgent: navigator.userAgent,\n                                            deviceName: self._getDeviceName(),\n                                            deviceFingerprint: fingerprint,\n                                        }),\n                                    });\n                                })\n                                .then(function (r) {\n                                    return r.json();\n                                })\n                                .then(function (data) {\n                                    self.isEnabling = false;\n                                    if (data.success) {\n                                        self.isSubscribed = true;\n                                        window.location.reload();\n                                    } else {\n                                        self.errorMessage =\n                                            data.error || \"Failed to enable\";\n                                    }\n                                })\n                                .catch(function (err) {\n                                    self.isEnabling = false;\n                                    self.errorMessage = \"Error occurred\";\n                                    console.error(\"[CoachPush]\", err);\n                                });\n                        });\n                    })\n                    .catch(function () {\n                        self.isEnabling = false;\n                        self.errorMessage = \"Permission denied\";\n                    });\n            },\n\n            disablePush: function () {\n                var self = this;\n                if (!this.isSupported || this.isDisabling) return;\n                this.isDisabling = true;\n\n                navigator.serviceWorker.ready\n                    .then(function (reg) {\n                        return reg.pushManager.getSubscription();\n                    })\n                    .then(function (sub) {\n                        if (!sub) {\n                            self.isDisabling = false;\n                            self.isSubscribed = false;\n                            self.isCurrentDeviceSubscribed = false;\n                            window.location.reload();\n                            return;\n                        }\n                        // Call API first to check if browser subscription should be kept\n                        // Include fingerprint so server can check for user subscriptions with different endpoint\n                        // Use our own implementation - no dependency on CrushPush\n                        var fingerprint = self._generateFingerprint();\n                        return fetch(\"/api/coach/push/unsubscribe/\", {\n                            method: \"POST\",\n                            headers: {\n                                \"Content-Type\": \"application/json\",\n                                \"X-CSRFToken\": self.getCsrfToken(),\n                            },\n                            body: JSON.stringify({\n                                endpoint: sub.endpoint,\n                                deviceFingerprint: fingerprint,\n                            }),\n                        })\n                            .then(function (response) {\n                                return response.json();\n                            })\n                            .then(function (data) {\n                                if (data.success) {\n                                    // Only unsubscribe browser if no other system needs it\n                                    if (!data.keep_browser_subscription) {\n                                        return sub.unsubscribe();\n                                    }\n                                }\n                            })\n                            .then(function () {\n                                self.isDisabling = false;\n                                self.isCurrentDeviceSubscribed = false;\n                                window.location.reload();\n                            });\n                    })\n                    .catch(function (err) {\n                        self.isDisabling = false;\n                        self.errorMessage = \"Failed\";\n                        console.error(\"[CoachPush]\", err);\n                    });\n            },\n\n            // Disable push subscription for a remote device (not the current device)\n            disableRemoteSubscription: function (subscriptionId) {\n                var self = this;\n                if (this.isDisabling) return;\n\n                this.isDisabling = true;\n\n                fetch(\"/api/coach/push/delete-subscription/\", {\n                    method: \"POST\",\n                    headers: {\n                        \"Content-Type\": \"application/json\",\n                        \"X-CSRFToken\": self.getCsrfToken(),\n                    },\n                    body: JSON.stringify({ subscription_id: subscriptionId }),\n                })\n                    .then(function (response) {\n                        return response.json();\n                    })\n                    .then(function (data) {\n                        self.isDisabling = false;\n                        if (data.success) {\n                            // Reload to update UI\n                            window.location.reload();\n                        } else {\n                            self.errorMessage =\n                                data.error || \"Failed to disable notifications\";\n                        }\n                    })\n                    .catch(function (err) {\n                        self.isDisabling = false;\n                        self.errorMessage = gettext(\"An error occurred. Please try again.\");\n                        console.error(\"[CoachPush] Remote disable error:\", err);\n                    });\n            },\n\n            sendTestNotification: function () {\n                var self = this;\n                if (this.isSendingTest) return;\n                this.isSendingTest = true;\n                fetch(\"/api/coach/push/test/\", {\n                    method: \"POST\",\n                    headers: { \"X-CSRFToken\": self.getCsrfToken() },\n                })\n                    .then(function (r) {\n                        return r.json();\n                    })\n                    .then(function (data) {\n                        self.isSendingTest = false;\n                    })\n                    .catch(function (err) {\n                        self.isSendingTest = false;\n                        console.error(\"[CoachPush] Test failed:\", err);\n                    });\n            },\n\n            updatePreference: function (subId, prefKey, value, checkbox) {\n                var self = this;\n                var prefs = {};\n                prefs[prefKey] = value;\n                fetch(\"/api/coach/push/preferences/\", {\n                    method: \"POST\",\n                    headers: {\n                        \"Content-Type\": \"application/json\",\n                        \"X-CSRFToken\": self.getCsrfToken(),\n                    },\n                    body: JSON.stringify({ subscriptionId: subId, preferences: prefs }),\n                })\n                    .then(function (r) {\n                        return r.json();\n                    })\n                    .then(function (data) {\n                        if (!data.success) checkbox.checked = !value;\n                    })\n                    .catch(function () {\n                        checkbox.checked = !value;\n                    });\n            },\n\n            getCsrfToken: function () {\n                // First try hidden form input (works with CSRF_COOKIE_HTTPONLY=True)\n                var input = document.querySelector('input[name=\"csrfmiddlewaretoken\"]');\n                if (input && input.value) return input.value;\n                // Fallback to cookie\n                var c = document.cookie.split(\"; \").find(function (r) {\n                    return r.startsWith(\"csrftoken=\");\n                });\n                return c ? c.split(\"=\")[1] : \"\";\n            },\n\n            _checkStatus: function () {\n                var self = this;\n                this._runStatusCheck(function (finish) {\n                    self._waitForServiceWorker(function () {\n                        if (self.isSupported && self.subscriptions.length > 0) {\n                            self.isSubscribed = true;\n                        }\n                        finish();\n                    });\n                });\n            },\n\n            _waitForServiceWorker: function (cb) {\n                var self = this;\n                if (\"serviceWorker\" in navigator) {\n                    navigator.serviceWorker.ready\n                        .then(function () {\n                            cb();\n                        })\n                        .catch(function () {\n                            self.isLoading = false;\n                            cb();\n                        });\n                } else {\n                    self.isLoading = false;\n                    cb();\n                }\n            },\n\n            _urlBase64ToUint8Array: function (base64) {\n                var padding = \"=\".repeat((4 - (base64.length % 4)) % 4);\n                var b64 = (base64 + padding).replace(/\\-/g, \"+\").replace(/_/g, \"/\");\n                var raw = window.atob(b64);\n                var out = new Uint8Array(raw.length);\n                for (var i = 0; i < raw.length; ++i) out[i] = raw.charCodeAt(i);\n                return out;\n            },\n\n            _getDeviceName: function () {\n                var ua = navigator.userAgent;\n                if (/Android/i.test(ua)) return \"Android Chrome\";\n                if (/iPhone|iPad|iPod/i.test(ua)) return \"iPhone Safari\";\n                if (/Windows/i.test(ua)) return \"Windows Desktop\";\n                if (/Macintosh/i.test(ua)) return \"Mac Desktop\";\n                if (/Linux/i.test(ua)) return \"Linux Desktop\";\n                return \"Unknown Device\";\n            },\n\n            // Generate a stable browser fingerprint from hardware/software characteristics\n            // Independent implementation - doesn't require CrushPush to be loaded\n            _generateFingerprint: function () {\n                var components = [\n                    screen.width,\n                    screen.height,\n                    window.devicePixelRatio || 1,\n                    new Date().getTimezoneOffset(),\n                    navigator.language || \"\",\n                    navigator.platform || \"\",\n                    navigator.hardwareConcurrency || 0,\n                    navigator.deviceMemory || 0,\n                    \"ontouchstart\" in window ? 1 : 0,\n                    screen.colorDepth || 0,\n                    this._getCanvasFingerprint(),\n                ];\n                return this._simpleHash(components.join(\"|\"));\n            },\n\n            _getCanvasFingerprint: function () {\n                try {\n                    var canvas = document.createElement(\"canvas\");\n                    canvas.width = 200;\n                    canvas.height = 50;\n                    var ctx = canvas.getContext(\"2d\");\n                    ctx.textBaseline = \"top\";\n                    ctx.font = \"14px Arial\";\n                    ctx.fillStyle = \"#f60\";\n                    ctx.fillRect(125, 1, 62, 20);\n                    ctx.fillStyle = \"#069\";\n                    ctx.fillText(\"Crush.lu PWA\", 2, 15);\n                    ctx.fillStyle = \"rgba(102, 204, 0, 0.7)\";\n                    ctx.fillText(\"Crush.lu PWA\", 4, 17);\n                    return canvas.toDataURL().slice(-50);\n                } catch (e) {\n                    return \"no-canvas\";\n                }\n            },\n\n            _simpleHash: function (str) {\n                var hash = 5381;\n                for (var i = 0; i < str.length; i++) {\n                    hash = (hash << 5) + hash + str.charCodeAt(i);\n                    hash = hash & hash;\n                }\n                return Math.abs(hash).toString(16).padStart(8, \"0\");\n            },\n        });\n    });\n\n    // Decline animation component (connection response)\n    // Shows briefly then fades out\n    Alpine.data(\"declineAnimation\", function () {\n        return {\n            show: true,\n            init: function () {\n                var self = this;\n                setTimeout(function () {\n                    self.show = false;\n                }, 2000);\n            },\n        };\n    });\n\n    // Character counter component\n    // Reads initial count from data-initial-count and max from data-max-length\n    Alpine.data(\"charCounter\", function () {\n        return {\n            charCount: 0,\n            maxLength: 500,\n            init: function () {\n                var initialCount = this.$el.getAttribute(\"data-initial-count\");\n                var maxLength = this.$el.getAttribute(\"data-max-length\");\n                this.charCount = initialCount ? parseInt(initialCount) : 0;\n                this.maxLength = maxLength ? parseInt(maxLength) : 500;\n            },\n            get charDisplay() {\n                return this.charCount + \"/\" + this.maxLength;\n            },\n            updateCount: function (event) {\n                this.charCount = event.target.value.length;\n            },\n        };\n    });\n\n    // Photo upload component for profile photos\n    // Reads initial photos from data attributes\n    // DEPRECATED \u2014 see crush_lu/STYLE.md \u00A77. New photo-upload UI should use\n    // the photoPicker component (HTMX, slot-based, no 3-photo hardcoding).\n    // photoUpload is preserved for the onboarding wizard while that flow\n    // remains out of scope for the visual-system refactor.\n    var _photoUploadDeprecationLogged = false;\n    Alpine.data(\"photoUpload\", function () {\n        if (!_photoUploadDeprecationLogged && typeof console !== \"undefined\") {\n            console.warn(\n                \"[crush_lu] Alpine.data('photoUpload') is deprecated; use photoPicker for new photo-upload UI.\",\n            );\n            _photoUploadDeprecationLogged = true;\n        }\n        // Serialize writes per slot: a server-side upload must finish before\n        // a later Remove deletes it, and a newer selection follows that delete.\n        var slotOperations = [Promise.resolve(), Promise.resolve(), Promise.resolve()];\n        return {\n            // focus/focusUrl: the coach-set object-position of the saved\n            // file and its URL; it applies only while that file is shown.\n            photos: [\n                { id: 1, hasImage: false, preview: \"\", uploadGeneration: 0, focus: \"\", focusUrl: \"\" },\n                { id: 2, hasImage: false, preview: \"\", uploadGeneration: 0, focus: \"\", focusUrl: \"\" },\n                { id: 3, hasImage: false, preview: \"\", uploadGeneration: 0, focus: \"\", focusUrl: \"\" },\n            ],\n\n            // Computed getters for CSP compatibility\n            get photo1NoImage() {\n                return !this.photos[0].hasImage;\n            },\n            get photo2NoImage() {\n                return !this.photos[1].hasImage;\n            },\n            get photo3NoImage() {\n                return !this.photos[2].hasImage;\n            },\n            get photo1HasImage() {\n                return this.photos[0].hasImage;\n            },\n            get photo1Preview() {\n                return this.photos[0].preview;\n            },\n            get photo2HasImage() {\n                return this.photos[1].hasImage;\n            },\n            get photo2Preview() {\n                return this.photos[1].preview;\n            },\n            get photo3HasImage() {\n                return this.photos[2].hasImage;\n            },\n            get photo3Preview() {\n                return this.photos[2].preview;\n            },\n            get photo1PreviewStyle() {\n                return this._previewStyle(0);\n            },\n            get photo2PreviewStyle() {\n                return this._previewStyle(1);\n            },\n            get photo3PreviewStyle() {\n                return this._previewStyle(2);\n            },\n            _previewStyle: function (index) {\n                var photo = this.photos[index];\n                return photo.focus && photo.preview === photo.focusUrl\n                    ? \"object-position: \" + photo.focus\n                    : \"\";\n            },\n\n            init: function () {\n                var el = this.$el;\n                var self = this;\n\n                // Read initial photo states from data attributes (from database)\n                for (var i = 1; i <= 3; i++) {\n                    var hasImage =\n                        el.getAttribute(\"data-photo-\" + i + \"-exists\") === \"true\";\n                    var preview = el.getAttribute(\"data-photo-\" + i + \"-url\") || \"\";\n                    this.photos[i - 1].hasImage = hasImage;\n                    this.photos[i - 1].preview = preview;\n                    this.photos[i - 1].uploadedUrl = preview;\n                    this.photos[i - 1].focus =\n                        el.getAttribute(\"data-photo-\" + i + \"-focus\") || \"\";\n                    this.photos[i - 1].focusUrl = preview;\n                }\n\n                // Also check draft for any newly uploaded photos not yet in database\n                fetch(\"/api/profile/draft/get/\")\n                    .then(function (response) {\n                        return response.json();\n                    })\n                    .then(function (result) {\n                        if (result.success && result.data && result.data.merged) {\n                            var draft = result.data.merged;\n\n                            // Check each photo URL in draft\n                            for (var i = 1; i <= 3; i++) {\n                                var photoUrlKey = \"photo_\" + i + \"_url\";\n                                if (draft[photoUrlKey]) {\n                                    self.photos[i - 1].preview = draft[photoUrlKey];\n                                    self.photos[i - 1].hasImage = true;\n                                    self.photos[i - 1].uploadedUrl = draft[photoUrlKey];\n                                }\n                            }\n                        }\n                    })\n                    .catch(function (err) {\n                        console.error(\n                            \"[PHOTO UPLOAD] Failed to load draft photos:\",\n                            err,\n                        );\n                    });\n            },\n            handleFile1: function (event) {\n                this._handleFileSelect(0, event);\n            },\n            handleFile2: function (event) {\n                this._handleFileSelect(1, event);\n            },\n            handleFile3: function (event) {\n                this._handleFileSelect(2, event);\n            },\n            _handleFileSelect: function (index, event) {\n                // Continue locks the photo step while its writes drain.\n                if (_photoStepAdvancing) {\n                    event.target.value = \"\";\n                    return;\n                }\n                var file = event.target.files[0];\n                if (file) {\n                    var self = this;\n                    var photoNumber = index + 1; // Convert 0-indexed to 1-indexed\n                    var generation = ++self.photos[index].uploadGeneration;\n                    var previousOperation = slotOperations[index];\n\n                    // Show preview immediately unless a later Remove or\n                    // selection has already invalidated this FileReader.\n                    var reader = new FileReader();\n                    reader.onload = function (e) {\n                        if (self.photos[index].uploadGeneration !== generation) return;\n                        self.photos[index].preview = e.target.result;\n                        self.photos[index].hasImage = true;\n                    };\n                    reader.readAsDataURL(file);\n\n                    // Downscale/re-encode client-side (max ~2048px long edge,\n                    // JPEG q=0.85) before the auto-save upload \u2014 server\n                    // validation is unchanged and still applies to whatever\n                    // arrives. Tracked as a pending upload (see\n                    // trackPendingPhotoUpload above) so the wizard's\n                    // Continue button can wait for it to settle instead of\n                    // advancing past a photo that was never saved.\n                    var uploadPromise = Promise.all([\n                        previousOperation,\n                        resizeImageForUpload(file, 2048, 0.85),\n                    ]).then(function (ready) {\n                        var uploadFile = ready[1];\n                        // The slot was removed (or replaced by a newer\n                        // selection) while this resize was running \u2014 do not\n                        // upload a file the member already deleted.\n                        if (self.photos[index].uploadGeneration !== generation) {\n                            return;\n                        }\n\n                        var formData = new FormData();\n                        formData.append(\"photo\", uploadFile, uploadFile.name || file.name);\n                        formData.append(\"photo_number\", photoNumber);\n\n                        // Get CSRF token\n                        var csrfToken = document.querySelector(\n                            \"[name=csrfmiddlewaretoken]\",\n                        );\n                        if (csrfToken) {\n                            formData.append(\"csrfmiddlewaretoken\", csrfToken.value);\n                        }\n\n                        return fetch(\"/api/profile/draft/upload-photo/\", {\n                            method: \"POST\",\n                            headers: {\n                                \"X-CSRFToken\": csrfToken ? csrfToken.value : \"\",\n                            },\n                            body: formData,\n                        })\n                            .then(function (response) {\n                                return response.json();\n                            })\n                            .then(function (result) {\n                                // Re-check after the round trip too: a\n                                // Remove tapped mid-upload must still win\n                                // over this response.\n                                if (self.photos[index].uploadGeneration !== generation) {\n                                    return;\n                                }\n                                if (result.success) {\n                                    self.photos[index].uploadedUrl = result.photo_url;\n                                    // The photo is saved on the profile now,\n                                    // so empty the native file input. The\n                                    // final form submit would otherwise send\n                                    // it a second time: a second stored file,\n                                    // and a second hit on the slot's daily\n                                    // upload limit. A failed upload keeps the\n                                    // file in the input as the fallback.\n                                    var input = document.getElementById(\n                                        \"photo\" + photoNumber,\n                                    );\n                                    if (input) input.value = \"\";\n                                } else {\n                                    console.error(\n                                        \"[PHOTO UPLOAD] \u274C Upload failed:\",\n                                        result.error,\n                                    );\n                                    notifyError(\n                                        gettext(\"Photo upload failed: \") + result.error,\n                                    );\n                                }\n                            })\n                            .catch(function (err) {\n                                console.error(\"[PHOTO UPLOAD] \u274C Network error:\", err);\n                                notifyError(\n                                    gettext(\"Photo upload failed. Please try again.\"),\n                                );\n                            });\n                    });\n                    slotOperations[index] = uploadPromise.then(function () {}, function () {});\n                    trackPendingPhotoUpload(uploadPromise);\n                }\n            },\n            removePhoto1: function () {\n                this._removePhoto(0);\n            },\n            removePhoto2: function () {\n                this._removePhoto(1);\n            },\n            removePhoto3: function () {\n                this._removePhoto(2);\n            },\n            _removePhoto: function (index) {\n                if (_photoStepAdvancing) return;\n                var self = this;\n                var photoNumber = index + 1;\n                var removalGeneration = ++self.photos[index].uploadGeneration;\n                var previousOperation = slotOperations[index];\n\n                var clearLocal = function () {\n                    self.photos[index].preview = \"\";\n                    self.photos[index].hasImage = false;\n                    self.photos[index].uploadedUrl = \"\";\n                    var input = document.getElementById(\"photo\" + photoNumber);\n                    if (input) input.value = \"\";\n                };\n\n                // An upload POST may already be running. Its generation\n                // check cannot undo a server write, so issue the delete only\n                // after that POST settles. Later selections queue behind it.\n                var deletePromise = previousOperation.then(function () {\n                    var csrfToken = document.querySelector(\"[name=csrfmiddlewaretoken]\");\n                    var formData = new FormData();\n                    formData.append(\"photo_number\", photoNumber);\n                    return fetch(\"/api/profile/draft/delete-photo/\", {\n                        method: \"POST\",\n                        headers: {\n                            \"X-CSRFToken\": csrfToken ? csrfToken.value : \"\",\n                        },\n                        body: formData,\n                    })\n                        .then(function (response) {\n                            return response.json();\n                        })\n                        .then(function (result) {\n                            if (result.success) {\n                                // A newer file may already have been selected.\n                                if (self.photos[index].uploadGeneration === removalGeneration) {\n                                    clearLocal();\n                                }\n                            } else {\n                                console.error(\"[PHOTO REMOVE] \u274C Delete failed:\", result.error);\n                                notifyError(gettext(\"Could not remove the photo: \") + result.error);\n                            }\n                        })\n                        .catch(function (err) {\n                            console.error(\"[PHOTO REMOVE] \u274C Network error:\", err);\n                            notifyError(gettext(\"Could not remove the photo. Please try again.\"));\n                        });\n                });\n                slotOperations[index] = deletePromise.then(function () {}, function () {});\n                trackPendingPhotoUpload(deletePromise);\n            },\n        };\n    });\n\n    // Profile creation wizard component\n    // Reads initial values from data attributes\n    Alpine.data(\"profileWizard\", function () {\n        return {\n            currentStep: 1,\n            totalSteps: 4,\n            isSubmitting: false,\n            phoneVerified: false,\n            showErrors: false,\n            errors: {},\n            isEditing: false,\n            step1Valid: false,\n            step2Valid: true,\n\n            // Step 1 required fields tracking\n            gender: \"\",\n            location: \"\",\n            locationName: \"\",\n\n            // Step 2 fields tracking\n\n            // Field-specific error messages\n            fieldErrors: {},\n\n            // Step saving state\n            isSaving: false,\n            saveError: \"\",\n\n            // Computed-like properties for CSP compatibility\n            // These avoid function calls in templates\n            get step1Completed() {\n                return this.currentStep > 1;\n            },\n            get step2Completed() {\n                return this.currentStep > 2;\n            },\n            get step3Completed() {\n                return this.currentStep > 3;\n            },\n            get step4Completed() {\n                return this.currentStep > 4;\n            },\n            // Progress bar for mobile wizard\n            get progressBarStyle() {\n                var pct = (this.currentStep / this.totalSteps) * 100;\n                return \"width:\" + pct + \"%\";\n            },\n            // Step labels and the \"Step X of Y \u2014 \" prefix arrive from the\n            // template as data attributes so Django's {% trans %} can\n            // translate them (DE/FR). The JS itself never hard-codes copy.\n            stepLabels: [],\n            stepLabelPrefix: \"Step \",\n            stepLabelOf: \" of \",\n            stepLabelSep: \" \u2014 \",\n            get stepLabel() {\n                var name = this.stepLabels[this.currentStep - 1] || \"\";\n                return (\n                    this.stepLabelPrefix +\n                    this.currentStep +\n                    this.stepLabelOf +\n                    this.totalSteps +\n                    this.stepLabelSep +\n                    name\n                );\n            },\n\n            get isStep1() {\n                return this.currentStep === 1;\n            },\n            get isStep2() {\n                return this.currentStep === 2;\n            },\n            get isStep3() {\n                return this.currentStep === 3;\n            },\n            get isStep4() {\n                return this.currentStep === 4;\n            },\n            get step1NotCompleted() {\n                return !this.step1Completed;\n            },\n            get step2NotCompleted() {\n                return !this.step2Completed;\n            },\n            get step3NotCompleted() {\n                return !this.step3Completed;\n            },\n            get step4NotCompleted() {\n                return !this.step4Completed;\n            },\n            get notPhoneVerified() {\n                return !this.phoneVerified;\n            },\n            get isNotEditing() {\n                return !this.isEditing;\n            },\n            get isNotSubmitting() {\n                return !this.isSubmitting;\n            },\n            get isSavingStep() {\n                return this.isSaving;\n            },\n            get isNotSaving() {\n                return !this.isSaving;\n            },\n            get hasSaveError() {\n                return this.saveError !== \"\";\n            },\n            get hasFieldErrors() {\n                return Object.keys(this.fieldErrors).length > 0;\n            },\n            get canContinueStep1() {\n                // Location is deliberately absent: optional since fast-track\n                // event verification (must match save-step1 API + model).\n                return (\n                    this.phoneVerified &&\n                    this.gender !== \"\" &&\n                    !this.isSaving\n                );\n            },\n            get cannotContinueStep1() {\n                return (\n                    !this.phoneVerified ||\n                    this.gender === \"\" ||\n                    this.isSaving\n                );\n            },\n            get canContinueStep2() {\n                return !this.isSaving;\n            },\n            get cannotContinueStep2() {\n                return this.isSaving;\n            },\n            get hasGenderError() {\n                return this.fieldErrors.gender !== undefined;\n            },\n            get hasLocationError() {\n                return this.fieldErrors.location !== undefined;\n            },\n            get genderErrorMessage() {\n                return this.fieldErrors.gender || \"\";\n            },\n            get locationErrorMessage() {\n                return this.fieldErrors.location || \"\";\n            },\n\n            // Step progress bar classes (avoid ternary expressions in templates)\n            get step1CircleClass() {\n                return this.currentStep >= 1\n                    ? \"bg-gradient-to-r from-purple-500 to-pink-500\"\n                    : \"bg-gray-300\";\n            },\n            get step1TextClass() {\n                return this.currentStep >= 1\n                    ? \"text-purple-600 font-medium\"\n                    : \"text-gray-400\";\n            },\n            get step2CircleClass() {\n                return this.currentStep >= 2\n                    ? \"bg-gradient-to-r from-purple-500 to-pink-500\"\n                    : \"bg-gray-300\";\n            },\n            get step2TextClass() {\n                return this.currentStep >= 2\n                    ? \"text-purple-600 font-medium\"\n                    : \"text-gray-400\";\n            },\n            get step3CircleClass() {\n                return this.currentStep >= 3\n                    ? \"bg-gradient-to-r from-purple-500 to-pink-500\"\n                    : \"bg-gray-300\";\n            },\n            get step3TextClass() {\n                return this.currentStep >= 3\n                    ? \"text-purple-600 font-medium\"\n                    : \"text-gray-400\";\n            },\n            get step4CircleClass() {\n                return this.currentStep >= 4\n                    ? \"bg-gradient-to-r from-purple-500 to-pink-500\"\n                    : \"bg-gray-300\";\n            },\n            get step4TextClass() {\n                return this.currentStep >= 4\n                    ? \"text-purple-600 font-medium\"\n                    : \"text-gray-400\";\n            },\n            get step1ConnectorClass() {\n                return this.step1Completed\n                    ? \"bg-gradient-to-r from-purple-500 to-pink-500\"\n                    : \"bg-gray-200\";\n            },\n            get step2ConnectorClass() {\n                return this.step2Completed\n                    ? \"bg-gradient-to-r from-purple-500 to-pink-500\"\n                    : \"bg-gray-200\";\n            },\n            get step3ConnectorClass() {\n                return this.step3Completed\n                    ? \"bg-gradient-to-r from-purple-500 to-pink-500\"\n                    : \"bg-gray-200\";\n            },\n            // Step navigation button classes (for breadcrumb quick navigation)\n            get step1ButtonClass() {\n                return this.isStep1\n                    ? \"bg-purple-100 text-purple-700 font-medium\"\n                    : \"text-gray-500 hover:text-purple-600 hover:bg-purple-50\";\n            },\n            get step2ButtonClass() {\n                return this.isStep2\n                    ? \"bg-purple-100 text-purple-700 font-medium\"\n                    : \"text-gray-500 hover:text-purple-600 hover:bg-purple-50\";\n            },\n            get step3ButtonClass() {\n                return this.isStep3\n                    ? \"bg-purple-100 text-purple-700 font-medium\"\n                    : \"text-gray-500 hover:text-purple-600 hover:bg-purple-50\";\n            },\n            get step4ButtonClass() {\n                return this.isStep4\n                    ? \"bg-purple-100 text-purple-700 font-medium\"\n                    : \"text-gray-500 hover:text-purple-600 hover:bg-purple-50\";\n            },\n\n            init: function () {\n                // Read initial values from data attributes\n                var el = this.$el;\n                var initialStep = el.getAttribute(\"data-initial-step\");\n                var phoneVerified = el.getAttribute(\"data-phone-verified\");\n                var isEditing = el.getAttribute(\"data-is-editing\");\n\n                // Translated step labels are injected by the template as a\n                // pipe-separated list so the JS stays copy-free.\n                var labelsAttr = el.getAttribute(\"data-step-labels\");\n                if (labelsAttr) {\n                    this.stepLabels = labelsAttr.split(\"|\");\n                }\n                var prefix = el.getAttribute(\"data-step-label-prefix\");\n                if (prefix) this.stepLabelPrefix = prefix;\n                var ofWord = el.getAttribute(\"data-step-label-of\");\n                if (ofWord) this.stepLabelOf = ofWord;\n                var sep = el.getAttribute(\"data-step-label-sep\");\n                if (sep) this.stepLabelSep = sep;\n\n                // Translated auto-save pill copy (same pattern as the step\n                // labels above).\n                var savedJustNow = el.getAttribute(\"data-saved-just-now\");\n                if (savedJustNow) this.savedJustNowLabel = savedJustNow;\n                var savedAgo = el.getAttribute(\"data-saved-ago\");\n                if (savedAgo) this.savedAgoLabel = savedAgo;\n                var savedEarlier = el.getAttribute(\"data-saved-earlier\");\n                if (savedEarlier) this.savedEarlierLabel = savedEarlier;\n\n                // Map DB completion_status values \u2192 wizard sub-step numbers.\n                // Wizard has 4 sub-steps: 1 Basic Info \u00B7 2 About You \u00B7 3 Photos\n                // \u00B7 4 Review. step4 is a legacy DB value (old Preferences step)\n                // \u2014 both step3 and step4 land users on Review.\n                var stepMap = {\n                    not_started: 1,\n                    step1: 2,\n                    step2: 3,\n                    step3: 4,\n                    step4: 4,\n                    submitted: 4,\n                };\n\n                if (initialStep && stepMap[initialStep]) {\n                    this.currentStep = stepMap[initialStep];\n                } else if (initialStep && !isNaN(parseInt(initialStep))) {\n                    this.currentStep = parseInt(initialStep);\n                }\n\n                this.phoneVerified = phoneVerified === \"true\";\n                this.isEditing = isEditing === \"true\";\n\n                // Set up HTMX listener\n                var self = this;\n                window.addEventListener(\"htmx:afterRequest\", function (event) {\n                    if (event.detail.successful) {\n                        var trigger = event.detail.xhr.getResponseHeader(\n                            \"HX-Trigger-After-Swap\",\n                        );\n                        if (trigger === \"step-valid\") {\n                            self.nextStep();\n                        }\n                    }\n                });\n\n                // Listen for phone verification event from nested component\n                window.addEventListener(\"phone-verified\", function () {\n                    self.phoneVerified = true;\n                });\n\n                // Listen for phone unverification (when user clicks Change)\n                this.$el.addEventListener(\"phone-unverified\", function () {\n                    self.phoneVerified = false;\n                });\n\n                // Initialize field values from DOM\n                self.initFieldTracking();\n\n                // Listen for custom events from canton map and gender selection\n                window.addEventListener(\"location-selected\", function (e) {\n                    if (e.detail && e.detail.location) {\n                        self.location = e.detail.location;\n                        self.locationName = e.detail.name || e.detail.location;\n                        self.fieldErrors.location = undefined;\n                        self.saveDraft();\n                    }\n                });\n\n                window.addEventListener(\"gender-selected\", function (e) {\n                    if (e.detail && e.detail.gender) {\n                        self.gender = e.detail.gender;\n                        self.fieldErrors.gender = undefined;\n                    }\n                });\n\n                // =========================================================================\n                // DRAFT AUTO-SAVE SETUP\n                // =========================================================================\n\n                // Load draft data on init (will populate form fields if draft exists)\n                self.loadDraft();\n\n                // Setup auto-save listeners\n                self.setupAutoSaveListeners();\n\n                // Setup periodic checkpoint every 60s\n                self.setupPeriodicCheckpoint();\n\n                // Warn before leaving with unsaved changes\n                self.setupUnloadWarning();\n\n                // Browser/Android back gesture support (finding 3-04): stamp\n                // the landing step as a history entry (replace, not push \u2014\n                // this is the page load, not a navigation) and honour a\n                // #step-N deep link within range. popstate then walks the\n                // wizard back/forward without re-pushing (would loop).\n                try {\n                    var hashMatch = /^#step-([1-4])$/.exec(window.location.hash);\n                    if (hashMatch) {\n                        self.currentStep = parseInt(hashMatch[1], 10);\n                    }\n                    // Codex review finding: seed history for resumed wizard\n                    // steps. A returning user can land directly on step 3 or\n                    // 4 (see stepMap above); a bare replaceState only ever\n                    // records that landing step, so the very first Back\n                    // press has no earlier wizard entry to land on and\n                    // leaves the page instead of walking to step 2/3. Push\n                    // one entry per preceding step first (this is still the\n                    // page load, not a user navigation \u2014 pushState here just\n                    // backfills the history stack the wizard would have\n                    // built had the user clicked through from step 1).\n                    // The landing entry itself becomes step 1 (replace), so\n                    // one Back past step 1 leaves the wizard. A reload lands\n                    // on an entry that already carries wizardStep: re-seeding\n                    // it would stack a second set of synthetic entries.\n                    var alreadySeeded =\n                        history.state && history.state.wizardStep;\n                    if (self.currentStep > 1 && !alreadySeeded) {\n                        history.replaceState({ wizardStep: 1 }, \"\", \"#step-1\");\n                        for (var seedStep = 2; seedStep <= self.currentStep; seedStep++) {\n                            history.pushState(\n                                { wizardStep: seedStep },\n                                \"\",\n                                \"#step-\" + seedStep,\n                            );\n                        }\n                    } else {\n                        history.replaceState(\n                            { wizardStep: self.currentStep },\n                            \"\",\n                            \"#step-\" + self.currentStep,\n                        );\n                    }\n                } catch (e) {\n                    // history API unavailable \u2014 steps still work without it.\n                }\n                window.addEventListener(\"popstate\", function (e) {\n                    var step =\n                        e.state && e.state.wizardStep\n                            ? e.state.wizardStep\n                            : self.currentStep;\n                    // Review can be reached directly from an edited Event\n                    // Identity step via browser Back. Persist those fields\n                    // before showing a summary that looks ready to submit.\n                    if (self.currentStep === 2 && step === self.totalSteps) {\n                        if (self.isSaving) {\n                            history.replaceState({ wizardStep: 2 }, \"\", \"#step-2\");\n                            return;\n                        }\n                        self.saveStep2().then(function (result) {\n                            // A second navigation during the request wins.\n                            if (\n                                self.currentStep !== 2 ||\n                                !history.state ||\n                                history.state.wizardStep !== step\n                            ) {\n                                return;\n                            }\n                            if (result.success) {\n                                self._setStep(step, false);\n                            } else {\n                                // Keep the editable step and its error visible.\n                                history.replaceState({ wizardStep: 2 }, \"\", \"#step-2\");\n                            }\n                        });\n                        return;\n                    }\n                    // A browser Back from edited Photos also reaches Review\n                    // directly. Use the same pending-write gate as Continue.\n                    if (self.currentStep === 3 && step === self.totalSteps) {\n                        if (self.isSaving) {\n                            history.replaceState({ wizardStep: 3 }, \"\", \"#step-3\");\n                            return;\n                        }\n                        self._completePhotoStep(false);\n                        return;\n                    }\n                    self._setStep(step, false);\n                });\n            },\n\n            // Initialize field tracking from DOM values\n            initFieldTracking: function () {\n                var self = this;\n\n                // Read initial gender value\n                var genderEl = document.querySelector('[name=\"gender\"]:checked');\n                if (genderEl) {\n                    self.gender = genderEl.value;\n                }\n\n                // Read initial location value and name\n                var locationEl = document.getElementById(\"id_location\");\n                if (locationEl && locationEl.value) {\n                    self.location = locationEl.value;\n                    // Try to get the display name from the canton map component's data attribute\n                    var cantonMapEl = document.querySelector('[x-data=\"cantonMap\"]');\n                    if (cantonMapEl) {\n                        self.locationName =\n                            cantonMapEl.getAttribute(\"data-initial-name\") ||\n                            locationEl.value;\n                    } else {\n                        self.locationName = locationEl.value;\n                    }\n                }\n\n                // Set up change listeners for gender radio buttons\n                var genderRadios = document.querySelectorAll('[name=\"gender\"]');\n                genderRadios.forEach(function (radio) {\n                    radio.addEventListener(\"change\", function (e) {\n                        self.gender = e.target.value;\n                        self.fieldErrors.gender = undefined;\n                        // Dispatch event for other components\n                        window.dispatchEvent(\n                            new CustomEvent(\"gender-selected\", {\n                                detail: { gender: e.target.value },\n                            }),\n                        );\n                    });\n                });\n\n                // Set up change listener for location (hidden input updated by canton map)\n                var locationInput = document.getElementById(\"id_location\");\n                if (locationInput) {\n                    // Use MutationObserver to detect value changes on hidden input\n                    var observer = new MutationObserver(function (mutations) {\n                        mutations.forEach(function (mutation) {\n                            if (\n                                mutation.type === \"attributes\" &&\n                                mutation.attributeName === \"value\"\n                            ) {\n                                self.location = locationInput.value;\n                                self.fieldErrors.location = undefined;\n                            }\n                        });\n                    });\n                    observer.observe(locationInput, { attributes: true });\n\n                    // Also listen for direct changes\n                    locationInput.addEventListener(\"change\", function (e) {\n                        self.location = e.target.value;\n                        self.fieldErrors.location = undefined;\n                    });\n                }\n            },\n\n            // Single choke point for every step transition (finding 3-04):\n            // nextStep/prevStep/goToStep/saveAndNextStep1-3 and the popstate\n            // handler all route through here so the Android/WebView back\n            // gesture always lands on the previous wizard section instead of\n            // exiting the page. pushHistory=false is for popstate itself\n            // (already a history entry) and the initial render.\n            _setStep: function (step, pushHistory) {\n                if (step < 1 || step > this.totalSteps) return;\n                this.currentStep = step;\n                // 3-05 follow-up: landing on Review via the back/forward\n                // gesture or a direct goToStep() must refresh the summary,\n                // the same way saveAndNextStep3 already does on the forward\n                // path \u2014 otherwise an edited field can show a stale value.\n                if (step === this.totalSteps) {\n                    this.updateReview();\n                }\n                window.scrollTo({ top: 0, behavior: \"smooth\" });\n                try {\n                    if (pushHistory) {\n                        history.pushState(\n                            { wizardStep: step },\n                            \"\",\n                            \"#step-\" + step,\n                        );\n                    } else {\n                        history.replaceState(\n                            { wizardStep: step },\n                            \"\",\n                            \"#step-\" + step,\n                        );\n                    }\n                } catch (e) {\n                    // history API unavailable (e.g. sandboxed preview) \u2014 the\n                    // step change above still works, just without deep-linking.\n                }\n                this.$nextTick(function () {\n                    var heading = document.querySelector(\n                        '[data-wizard-step=\"' + step + '\"] h3',\n                    );\n                    if (heading) {\n                        heading.setAttribute(\"tabindex\", \"-1\");\n                        heading.focus();\n                    }\n                });\n            },\n\n            nextStep: function () {\n                if (this.currentStep < this.totalSteps) {\n                    this._setStep(this.currentStep + 1, true);\n                }\n            },\n\n            // CSP-compatible method for conditional next step (requires phone verification)\n            nextStepIfVerified: function () {\n                if (this.phoneVerified) {\n                    this.nextStep();\n                }\n            },\n\n            prevStep: function () {\n                if (this.currentStep > 1) {\n                    this._setStep(this.currentStep - 1, true);\n                }\n            },\n\n            goToStep: function (step) {\n                this._setStep(step, true);\n            },\n\n            // CSP-compatible Review-step \"Edit\" links: reads the target step\n            // from the clicked element's own data attribute (same idiom as\n            // traitSelector.handleClick) instead of an inline goToStep(n)\n            // call, which the CSP build disallows.\n            editSection: function () {\n                var step = parseInt(this.$el.getAttribute(\"data-goto-step\"), 10);\n                if (step) {\n                    this.goToStep(step);\n                }\n            },\n\n            isStepCompleted: function (step) {\n                return step < this.currentStep;\n            },\n\n            isCurrentStep: function (step) {\n                return step === this.currentStep;\n            },\n\n            // Refresh the Review-step summary from the live form state.\n            // Fallback copy comes from each element's data-empty attribute\n            // (translated server-side); elements keep their server-rendered\n            // initial content until a fresher client-side value exists.\n            updateReview: function () {\n                var emptyLabel = function (el) {\n                    return el.getAttribute(\"data-empty\") || \"\";\n                };\n\n                var phone = document.querySelector(\"[name=phone_number]\");\n                var dobInput = document.querySelector(\"[name=date_of_birth]\");\n                var genderEl = document.querySelector(\"[name=gender]:checked\");\n\n                var reviewPhone = this.$refs.reviewPhone;\n                var reviewDob = this.$refs.reviewDob;\n                var reviewGender = this.$refs.reviewGender;\n                var reviewLocation = this.$refs.reviewLocation;\n                var reviewInterests = this.$refs.reviewInterests;\n                var reviewLanguages = this.$refs.reviewLanguages;\n                var reviewPhotos = this.$refs.reviewPhotos;\n\n                if (reviewPhone) {\n                    reviewPhone.textContent =\n                        (phone && phone.value) || emptyLabel(reviewPhone);\n                }\n                if (reviewDob) {\n                    // Native <input type=\"date\"> value is always YYYY-MM-DD.\n                    // Format it for display client-side (no hard-coded copy)\n                    // when it's set this session; otherwise keep the\n                    // server-rendered value (resume-on-Review case).\n                    if (dobInput && dobInput.value) {\n                        var dobDate = new Date(dobInput.value + \"T00:00:00\");\n                        reviewDob.textContent = dobDate.toLocaleDateString(\n                            document.documentElement.lang || \"en\",\n                            { day: \"numeric\", month: \"short\", year: \"numeric\" },\n                        );\n                    } else {\n                        reviewDob.textContent = emptyLabel(reviewDob);\n                    }\n                }\n                if (reviewGender) {\n                    var genderText = \"\";\n                    if (genderEl) {\n                        var label = genderEl.nextElementSibling;\n                        var genderLabel = label && label.querySelector(\".gender-label\");\n                        genderText = genderLabel ? genderLabel.textContent.trim() : \"\";\n                    }\n                    reviewGender.textContent = genderText || emptyLabel(reviewGender);\n                }\n                if (reviewLocation) {\n                    reviewLocation.textContent =\n                        this.locationName || emptyLabel(reviewLocation);\n                }\n\n                if (reviewInterests) {\n                    // Event Identity summary chips: the event vibe (if any) plus\n                    // the selected interest labels. Replaces the old bio excerpt.\n                    var chips = [];\n                    var vibeEl = document.querySelector('[name=\"event_vibe\"]:checked');\n                    if (vibeEl && vibeEl.value) {\n                        var vibeSpan = vibeEl.nextElementSibling;\n                        if (vibeSpan) chips.push(vibeSpan.textContent.trim());\n                    }\n                    document\n                        .querySelectorAll('[name=\"interests_new\"]:checked')\n                        .forEach(function (box) {\n                            var span = box.nextElementSibling;\n                            if (span) chips.push(span.textContent.trim());\n                        });\n\n                    reviewInterests.textContent = \"\";\n                    if (chips.length === 0) {\n                        var em = document.createElement(\"span\");\n                        em.className =\n                            \"text-muted-fg italic text-sm\";\n                        em.textContent = emptyLabel(reviewInterests);\n                        reviewInterests.appendChild(em);\n                    } else {\n                        chips.forEach(function (text) {\n                            var chip = document.createElement(\"span\");\n                            chip.className =\n                                \"inline-block px-2.5 py-1 rounded-full text-xs font-medium bg-purple-100 dark:bg-purple-900/40 text-purple-700 dark:text-purple-300\";\n                            chip.textContent = text;\n                            reviewInterests.appendChild(chip);\n                        });\n                    }\n                }\n\n                if (reviewLanguages) {\n                    var checked = document.querySelectorAll(\n                        '[name=\"event_languages\"]:checked',\n                    );\n                    var labels = [];\n                    for (var i = 0; i < checked.length; i++) {\n                        var span = checked[i].nextElementSibling;\n                        if (span) {\n                            labels.push(span.textContent.trim().replace(/\\s+/g, \" \"));\n                        }\n                    }\n                    reviewLanguages.textContent = labels.length\n                        ? labels.join(\" \u00B7 \")\n                        : emptyLabel(reviewLanguages);\n                }\n\n                if (reviewPhotos) {\n                    // Mirror the step-3 photo previews (server photos + any\n                    // picked this session; x-show hides empty slots).\n                    var srcs = [];\n                    var positions = [];\n                    var photoImgs = document.querySelectorAll(\n                        '[data-wizard-step=\"3\"] img',\n                    );\n                    for (var j = 0; j < photoImgs.length; j++) {\n                        var src = photoImgs[j].getAttribute(\"src\");\n                        var wrapper = photoImgs[j].parentElement;\n                        var hidden = wrapper && wrapper.style.display === \"none\";\n                        if (src && !hidden) {\n                            srcs.push(src);\n                            positions.push(photoImgs[j].style.objectPosition);\n                        }\n                    }\n                    reviewPhotos.innerHTML = \"\";\n                    if (srcs.length) {\n                        for (var k = 0; k < srcs.length; k++) {\n                            var img = document.createElement(\"img\");\n                            img.src = srcs[k];\n                            img.alt = \"\";\n                            img.className = \"h-14 w-14 rounded-lg object-cover object-face\";\n                            if (positions[k]) img.style.objectPosition = positions[k];\n                            reviewPhotos.appendChild(img);\n                        }\n                    } else {\n                        // Amber nudge, not a neutral \"No photos yet\" row \u2014 a\n                        // profile with no face is a real conversion/safety\n                        // cost at an events-first product (finding 3-05).\n                        var callout = document.createElement(\"div\");\n                        callout.className =\n                            \"flex items-center gap-2 rounded-lg border border-amber-200 bg-amber-50 px-3 py-2 dark:border-amber-700/50 dark:bg-amber-900/20\";\n                        var nudge = document.createElement(\"p\");\n                        nudge.className = \"text-sm text-amber-800 dark:text-amber-200\";\n                        nudge.textContent =\n                            reviewPhotos.getAttribute(\"data-empty-nudge\") ||\n                            emptyLabel(reviewPhotos);\n                        callout.appendChild(nudge);\n                        reviewPhotos.appendChild(callout);\n                    }\n                }\n            },\n\n            setSubmitting: function () {\n                this.isSubmitting = true;\n                // CRITICAL: Before form submission, ensure phone number input has full international number\n                // intlTelInput with separateDialCode=true stores only national number in input.value\n                // We need to set the full number so Django form receives it correctly\n                var phoneInput = document.querySelector('[name=\"phone_number\"]');\n                if (phoneInput) {\n                    if (window.itiInstance) {\n                        // Get full international number from intlTelInput (works for both\n                        // readOnly/verified and editable phones)\n                        var fullNumber = window.itiInstance.getNumber();\n                        if (fullNumber) {\n                            phoneInput.value = fullNumber;\n                        }\n                    }\n                    // Safety net: if value still lacks '+' prefix and looks like a\n                    // national number, the backend clean_phone_number will handle it\n                    // for verified phones by returning the DB value.\n                }\n            },\n\n            // Handle form submission - only allow on the final step (Review).\n            // This prevents Enter key in text inputs from submitting the form prematurely.\n            handleFormSubmit: function (e) {\n                // Only allow submission when on the final (Review) step.\n                if (this.currentStep !== this.totalSteps) {\n                    // Prevent form submission on non-final steps\n                    return;\n                }\n                // A photo still held by its file input was never saved: a\n                // successful auto-save upload empties the input, a failed one\n                // leaves the file there. Sending it with this native multipart\n                // submit is what reached the server with an empty body on iOS\n                // (no CSRF token, 403), so send the member back to Photos to\n                // choose it again or remove it instead.\n                if (this._hasUnsavedPhoto()) {\n                    this.saveError = gettext(\n                        \"A photo was not saved. Choose it again or remove it, then continue.\",\n                    );\n                    notifyError(this.saveError);\n                    this.goToStep(3);\n                    return;\n                }\n                // Prepare form data (phone number formatting)\n                this.setSubmitting();\n                // Refresh CSRF token before final submit to prevent stale-token errors\n                // (form may have been open for 15+ minutes, server may have rotated secrets)\n                fetch(\"/api/csrf-token/\", {\n                    method: \"GET\",\n                    credentials: \"same-origin\",\n                })\n                    .then(function (response) {\n                        return response.json();\n                    })\n                    .then(function (data) {\n                        if (data.csrfToken) {\n                            var inputs = document.querySelectorAll(\n                                'input[name=\"csrfmiddlewaretoken\"]',\n                            );\n                            for (var i = 0; i < inputs.length; i++) {\n                                inputs[i].value = data.csrfToken;\n                            }\n                        }\n                        var form = document.getElementById(\"profileForm\");\n                        if (form) form.submit();\n                    })\n                    .catch(function () {\n                        // If refresh fails, try submitting with existing token\n                        var form = document.getElementById(\"profileForm\");\n                        if (form) form.submit();\n                    });\n            },\n\n            _hasUnsavedPhoto: function () {\n                for (var n = 1; n <= 3; n++) {\n                    var input = document.getElementById(\"photo\" + n);\n                    if (input && input.files && input.files.length) return true;\n                }\n                return false;\n            },\n\n            nextStepAndReview: function () {\n                this.nextStep();\n                this.updateReview();\n            },\n\n            // Update ALL csrfmiddlewaretoken inputs in the DOM with a fresh token\n            updateAllCsrfTokens: function (token) {\n                var inputs = document.querySelectorAll(\n                    'input[name=\"csrfmiddlewaretoken\"]',\n                );\n                for (var i = 0; i < inputs.length; i++) {\n                    inputs[i].value = token;\n                }\n            },\n\n            // CSRF token helper for AJAX requests\n            // Reads from hidden form input (works with CSRF_COOKIE_HTTPONLY=True)\n            getCsrfToken: function () {\n                // First try the hidden form input (preferred when CSRF_COOKIE_HTTPONLY=True)\n                var input = document.querySelector('input[name=\"csrfmiddlewaretoken\"]');\n                if (input && input.value) {\n                    return input.value;\n                }\n                // Fallback to cookie (if CSRF_COOKIE_HTTPONLY=False)\n                var cookie = document.cookie.split(\"; \").find(function (row) {\n                    return row.startsWith(\"csrftoken=\");\n                });\n                return cookie ? cookie.split(\"=\")[1] : \"\";\n            },\n\n            // Collect Step 1 form data\n            collectStep1Data: function () {\n                var phoneEl = document.querySelector('[name=\"phone_number\"]');\n                var dobEl = document.querySelector('[name=\"date_of_birth\"]');\n                var genderEl = document.querySelector('[name=\"gender\"]:checked');\n                var locationEl = document.getElementById(\"id_location\");\n\n                // Get phone number: prefer intlTelInput's getNumber() for full international format\n                // intlTelInput with separateDialCode=true stores only national number in input.value\n                var phoneNumber = \"\";\n                if (window.itiInstance) {\n                    phoneNumber = window.itiInstance.getNumber() || \"\";\n                } else if (phoneEl) {\n                    phoneNumber = phoneEl.value || \"\";\n                }\n\n                return {\n                    phone_number: phoneNumber,\n                    date_of_birth: dobEl ? dobEl.value : \"\",\n                    gender: genderEl ? genderEl.value : \"\",\n                    location: locationEl ? locationEl.value : \"\",\n                };\n            },\n\n            // Collect Step 2 form data (Event Identity, 2026 redesign).\n            collectStep2Data: function () {\n                var interestsNew = [];\n                document\n                    .querySelectorAll('[name=\"interests_new\"]:checked')\n                    .forEach(function (b) {\n                        interestsNew.push(b.value);\n                    });\n\n                var askMeAbout = [];\n                document\n                    .querySelectorAll('[name=\"ask_me_about\"]:checked')\n                    .forEach(function (b) {\n                        askMeAbout.push(b.value);\n                    });\n\n                var vibeEl = document.querySelector('[name=\"event_vibe\"]:checked');\n                var qualitiesEl = document.querySelector('[name=\"qualities_ids\"]');\n                var defectsEl = document.querySelector('[name=\"defects_ids\"]');\n\n                return {\n                    interests_new: interestsNew,\n                    ask_me_about: askMeAbout,\n                    event_vibe: vibeEl ? vibeEl.value : \"\",\n                    qualities_ids: qualitiesEl ? qualitiesEl.value : \"\",\n                    defects_ids: defectsEl ? defectsEl.value : \"\",\n                };\n            },\n\n            // Collect Step 3 form data (privacy settings + event languages - photos handled by HTMX)\n            collectStep3Data: function () {\n                var showFullName = document.querySelector('[name=\"show_full_name\"]');\n                var showExactAge = document.querySelector('[name=\"show_exact_age\"]');\n\n                // Collect checked event language checkboxes\n                var langCheckboxes = document.querySelectorAll(\n                    '[name=\"event_languages\"]:checked',\n                );\n                var eventLanguages = [];\n                for (var i = 0; i < langCheckboxes.length; i++) {\n                    eventLanguages.push(langCheckboxes[i].value);\n                }\n\n                return {\n                    show_full_name: showFullName ? showFullName.checked : false,\n                    show_exact_age: showExactAge ? showExactAge.checked : true,\n                    event_languages: eventLanguages,\n                };\n            },\n\n            // Save Step 1 data to backend\n            saveStep1: function () {\n                var self = this;\n                self.isSaving = true;\n                self.saveError = \"\";\n                self.fieldErrors = {};\n\n                var data = self.collectStep1Data();\n\n                return fetch(\"/api/profile/save-step1/\", {\n                    method: \"POST\",\n                    headers: {\n                        \"Content-Type\": \"application/json\",\n                        \"X-CSRFToken\": self.getCsrfToken(),\n                    },\n                    body: JSON.stringify(data),\n                })\n                    .then(function (response) {\n                        return response.json().then(function (d) {\n                            return { ok: response.ok, data: d };\n                        });\n                    })\n                    .then(function (result) {\n                        self.isSaving = false;\n                        if (result.ok && result.data.success) {\n                            self.fieldErrors = {};\n                            if (result.data.csrfToken) {\n                                self.updateAllCsrfTokens(result.data.csrfToken);\n                            }\n                            return { success: true };\n                        } else {\n                            self.saveError =\n                                result.data.error ||\n                                gettext(\"Failed to save. Please try again.\");\n                            // Handle field-specific errors from backend\n                            if (result.data.errors) {\n                                self.fieldErrors = result.data.errors;\n                            }\n                            return {\n                                success: false,\n                                error: self.saveError,\n                                errors: result.data.errors,\n                            };\n                        }\n                    })\n                    .catch(function (err) {\n                        self.isSaving = false;\n                        self.saveError = gettext(\"Network error. Please check your connection.\");\n                        return { success: false, error: self.saveError };\n                    });\n            },\n\n            // Save Step 2 data to backend\n            saveStep2: function () {\n                var self = this;\n                self.isSaving = true;\n                self.saveError = \"\";\n                self.fieldErrors = {};\n\n                var data = self.collectStep2Data();\n\n                return fetch(\"/api/profile/save-step2/\", {\n                    method: \"POST\",\n                    headers: {\n                        \"Content-Type\": \"application/json\",\n                        \"X-CSRFToken\": self.getCsrfToken(),\n                    },\n                    body: JSON.stringify(data),\n                })\n                    .then(function (response) {\n                        return response.json().then(function (d) {\n                            return { ok: response.ok, data: d };\n                        });\n                    })\n                    .then(function (result) {\n                        self.isSaving = false;\n                        if (result.ok && result.data.success) {\n                            self.fieldErrors = {};\n                            if (result.data.csrfToken) {\n                                self.updateAllCsrfTokens(result.data.csrfToken);\n                            }\n                            return { success: true };\n                        } else {\n                            self.saveError =\n                                result.data.error ||\n                                gettext(\"Failed to save. Please try again.\");\n                            // Handle field-specific errors from backend\n                            if (result.data.errors) {\n                                self.fieldErrors = result.data.errors;\n                            }\n                            return {\n                                success: false,\n                                error: self.saveError,\n                                errors: result.data.errors,\n                            };\n                        }\n                    })\n                    .catch(function (err) {\n                        self.isSaving = false;\n                        self.saveError = gettext(\"Network error. Please check your connection.\");\n                        return { success: false, error: self.saveError };\n                    });\n            },\n\n            // Save Step 3 data to backend (privacy settings via FormData)\n            saveStep3: function () {\n                var self = this;\n                self.isSaving = true;\n                self.saveError = \"\";\n\n                var data = self.collectStep3Data();\n\n                // Use FormData to match backend expectation\n                var formData = new FormData();\n                if (data.show_full_name) formData.append(\"show_full_name\", \"on\");\n                if (data.show_exact_age) formData.append(\"show_exact_age\", \"on\");\n\n                // Append each selected event language\n                for (var i = 0; i < data.event_languages.length; i++) {\n                    formData.append(\"event_languages\", data.event_languages[i]);\n                }\n\n                return fetch(\"/api/profile/save-step3/\", {\n                    method: \"POST\",\n                    headers: {\n                        \"X-CSRFToken\": self.getCsrfToken(),\n                    },\n                    body: formData,\n                })\n                    .then(function (response) {\n                        return response.json().then(function (d) {\n                            return { ok: response.ok, data: d };\n                        });\n                    })\n                    .then(function (result) {\n                        self.isSaving = false;\n                        if (result.ok && result.data.success) {\n                            if (result.data.csrfToken) {\n                                self.updateAllCsrfTokens(result.data.csrfToken);\n                            }\n                            return { success: true };\n                        } else {\n                            self.saveError =\n                                result.data.error ||\n                                gettext(\"Failed to save. Please try again.\");\n                            return { success: false, error: self.saveError };\n                        }\n                    })\n                    .catch(function (err) {\n                        self.isSaving = false;\n                        self.saveError = gettext(\"Network error. Please check your connection.\");\n                        return { success: false, error: self.saveError };\n                    });\n            },\n\n            // Save Step 1 and advance if successful\n            saveAndNextStep1: function () {\n                var self = this;\n                if (!self.phoneVerified) return;\n\n                self.saveStep1().then(function (result) {\n                    if (result.success) {\n                        self.saveError = \"\";\n                        self._setStep(2, true);\n                    }\n                    // Error is already set in saveStep1\n                });\n            },\n\n            // Save Step 2 and advance if successful\n            saveAndNextStep2: function () {\n                var self = this;\n\n                self.saveStep2().then(function (result) {\n                    if (result.success) {\n                        self.saveError = \"\";\n                        self._setStep(3, true);\n                    }\n                });\n            },\n\n            // Save Step 3 (Photos) and advance to the Review step. Waits\n            // for any in-flight photo resize/upload first (finding 3-14):\n            // photoUpload lives in its own nested x-data scope, so without\n            // this the wizard had no way to know an upload was still in\n            // flight and would advance to Review immediately, leaving a\n            // preview of a photo that may never actually get saved.\n            saveAndNextStep3: function () {\n                this._completePhotoStep(true);\n            },\n            _completePhotoStep: function (pushHistory) {\n                var self = this;\n                if (self.isSaving) return;\n                self.isSaving = true;\n                _photoStepAdvancing = true;\n\n                waitForPendingPhotoUploads().then(function () {\n                    return self.saveStep3();\n                }).then(function (result) {\n                    _photoStepAdvancing = false;\n                    // The member may navigate elsewhere while the request\n                    // settles; an old completion must not pull them back.\n                    if (self.currentStep !== 3) return;\n                    if (!pushHistory && (!history.state || history.state.wizardStep !== 4)) return;\n                    if (result.success) {\n                        self.saveError = \"\";\n                        self._setStep(4, pushHistory);\n                    } else if (!pushHistory) {\n                        history.replaceState({ wizardStep: 3 }, \"\", \"#step-3\");\n                    }\n                }).catch(function () {\n                    _photoStepAdvancing = false;\n                    self.isSaving = false;\n                    self.saveError = gettext(\"Failed to save. Please try again.\");\n                    if (!pushHistory && self.currentStep === 3) {\n                        history.replaceState({ wizardStep: 3 }, \"\", \"#step-3\");\n                    }\n                });\n            },\n\n            // Clear save error (for dismissing error messages)\n            clearSaveError: function () {\n                this.saveError = \"\";\n            },\n\n            // =========================================================================\n            // DRAFT AUTO-SAVE FUNCTIONALITY\n            // =========================================================================\n\n            // Draft state\n            isDirty: false,\n            isAutoSaving: false,\n            lastSavedAt: null,\n            autoSaveTimer: null,\n            draftData: {},\n            draftRestored: false,\n\n            // Auto-save pill copy \u2014 overridden from data attributes in init()\n            // so Django's {% trans %} can translate it (JS stays copy-free).\n            savedJustNowLabel: \"Saved just now\",\n            savedAgoLabel: \"Saved %s ago\",\n            savedEarlierLabel: \"Saved earlier\",\n\n            // CSP-safe getters for auto-save UI\n            get showDraftRestored() {\n                return this.draftRestored;\n            },\n            dismissDraftNotice: function () {\n                this.draftRestored = false;\n            },\n            get showAutoSaving() {\n                return this.isAutoSaving;\n            },\n            // The pill flashes for a few seconds after each save instead of\n            // sitting in the corner forever (on mobile it overlaps the\n            // sticky Continue button). The restored-draft banner answers\n            // \"did my work survive?\" on page load.\n            savedPillVisible: false,\n            _savedPillTimer: null,\n            get showLastSaved() {\n                return this.savedPillVisible && this.lastSavedAt !== null;\n            },\n            _flashSavedPill: function () {\n                var self = this;\n                this.savedPillVisible = true;\n                clearTimeout(this._savedPillTimer);\n                this._savedPillTimer = setTimeout(function () {\n                    self.savedPillVisible = false;\n                }, 4000);\n            },\n            get lastSavedMessage() {\n                if (!this.lastSavedAt) return \"\";\n                var now = new Date();\n                var saved = new Date(this.lastSavedAt);\n                var diffMs = now - saved;\n                var diffSec = Math.floor(diffMs / 1000);\n\n                if (diffSec < 10) return this.savedJustNowLabel;\n                if (diffSec < 60) {\n                    return this.savedAgoLabel.replace(\"%s\", diffSec + \"s\");\n                }\n                var diffMin = Math.floor(diffSec / 60);\n                if (diffMin < 60) {\n                    return this.savedAgoLabel.replace(\"%s\", diffMin + \"m\");\n                }\n                return this.savedEarlierLabel;\n            },\n\n            // Load draft data on init\n            loadDraft: function () {\n                var self = this;\n\n                fetch(\"/api/profile/draft/get/\")\n                    .then(function (response) {\n                        return response.json();\n                    })\n                    .then(function (result) {\n                        if (result.success && result.data) {\n                            self.draftData = result.data.merged || {};\n                            self.lastSavedAt = result.data.last_saved;\n\n                            // Show the \"we restored your answers\" notice only\n                            // when actual UNSAVED draft input exists \u2014 the\n                            // merged blob is non-empty for any returning user.\n                            var rawDraft = result.data.draft || {};\n                            for (var stepKey in rawDraft) {\n                                var stepDraft = rawDraft[stepKey];\n                                if (\n                                    stepDraft &&\n                                    typeof stepDraft === \"object\" &&\n                                    Object.keys(stepDraft).length > 0\n                                ) {\n                                    self.draftRestored = true;\n                                    break;\n                                }\n                            }\n\n                            self.populateFieldsFromDraft();\n                        }\n                    })\n                    .catch(function (err) {\n                        console.error(\"[DRAFT LOAD] Failed to load draft:\", err);\n                    });\n            },\n\n            // Populate form fields from draft data\n            populateFieldsFromDraft: function () {\n                var self = this;\n                var form = this.$el.querySelector(\"form\");\n\n                if (!form) {\n                    return;\n                }\n\n                for (var key in this.draftData) {\n                    var value = this.draftData[key];\n\n                    // CRITICAL: Skip file inputs (photos) - cannot be set programmatically for security\n                    if (key === \"photo_1\" || key === \"photo_2\" || key === \"photo_3\") {\n                        continue;\n                    }\n\n                    // NEVER restore a persisted CSRF token (old drafts may\n                    // contain one): it would clobber the fresh token and\n                    // 403 every save after a re-login.\n                    if (key === \"csrfmiddlewaretoken\") {\n                        continue;\n                    }\n\n                    // Handle checkbox arrays (like event_languages)\n                    if (Array.isArray(value)) {\n                        var checkboxes = form.querySelectorAll('[name=\"' + key + '\"]');\n\n                        for (var i = 0; i < checkboxes.length; i++) {\n                            var checkbox = checkboxes[i];\n                            var shouldCheck = value.indexOf(checkbox.value) !== -1;\n                            checkbox.checked = shouldCheck;\n                        }\n                        continue;\n                    }\n\n                    var input = form.querySelector('[name=\"' + key + '\"]');\n\n                    if (input) {\n                        if (input.type === \"checkbox\") {\n                            // Handle single checkbox - convert string 'true'/'false' or Python 'True'/'False' to boolean\n                            var shouldCheck =\n                                value === true ||\n                                value === \"true\" ||\n                                value === \"1\" ||\n                                value === \"True\";\n                            input.checked = shouldCheck;\n                        } else if (input.type === \"radio\") {\n                            // For radio buttons, find the one with matching value\n                            var radios = form.querySelectorAll('[name=\"' + key + '\"]');\n\n                            for (var i = 0; i < radios.length; i++) {\n                                if (radios[i].value === value) {\n                                    radios[i].checked = true;\n                                    break;\n                                }\n                            }\n                        } else if (input.type !== \"file\") {\n                            // Only set value for non-file inputs\n                            input.value = value || \"\";\n                        }\n                    }\n                }\n\n                // Update component state from draft data\n                if (this.draftData.phone_number) {\n                    this.phoneNumber = this.draftData.phone_number;\n                }\n                if (this.draftData.date_of_birth) {\n                    this.dateOfBirth = this.draftData.date_of_birth;\n                }\n                if (this.draftData.gender) {\n                    this.gender = this.draftData.gender;\n                }\n                if (this.draftData.location) {\n                    this.location = this.draftData.location;\n                    // Try to get the display name from the map\n                    var locationInput = form.querySelector('[name=\"location\"]');\n                    if (locationInput) {\n                        var mapContainer = document.querySelector(\n                            '[x-data*=\"cantonMap\"]',\n                        );\n                        if (mapContainer) {\n                            var regionPath = document.getElementById(\n                                this.draftData.location,\n                            );\n                            if (regionPath) {\n                                this.locationName =\n                                    regionPath.getAttribute(\"data-region-name\") ||\n                                    this.draftData.location;\n                            } else {\n                                this.locationName = this.draftData.location;\n                            }\n                            // Tell the cantonMap so the SVG highlight and\n                            // selection label reflect the restored region\n                            // instead of the placeholder.\n                            mapContainer.dispatchEvent(\n                                new CustomEvent(\"location-restore\", {\n                                    detail: { location: this.draftData.location },\n                                }),\n                            );\n                        } else {\n                            this.locationName = this.draftData.location;\n                        }\n                    }\n                }\n\n                // Re-hydrate the Event Identity sub-components: the generic loop\n                // above checked their boxes/updated hidden inputs, but those\n                // components ran init() before the draft arrived, so their\n                // counters, caps and chip visibility are stale until told.\n                var eventIdentityEl = form.querySelector('[x-data=\"eventIdentity\"]');\n                if (eventIdentityEl) {\n                    eventIdentityEl.dispatchEvent(new CustomEvent(\"event-identity-restore\"));\n                }\n                [\"qualities_ids\", \"defects_ids\"].forEach(function (fieldName) {\n                    var hidden = form.querySelector('[name=\"' + fieldName + '\"]');\n                    var raw = self.draftData[fieldName];\n                    if (!hidden || raw === undefined || raw === null) {\n                        return;\n                    }\n                    var container = hidden.closest('[x-data]');\n                    if (!container) {\n                        return;\n                    }\n                    var ids = String(raw)\n                        .split(\",\")\n                        .map(function (x) {\n                            return parseInt(x, 10);\n                        })\n                        .filter(function (n) {\n                            return !isNaN(n);\n                        });\n                    container.dispatchEvent(\n                        new CustomEvent(\"trait-restore\", { detail: { ids: ids } }),\n                    );\n                });\n\n                // Update the review display after populating fields so Step 4 (Review)\n                // shows the correct data when the page is refreshed mid-wizard.\n                setTimeout(function () {\n                    self.updateReview();\n                }, 100);\n            },\n\n            // Setup auto-save event listeners\n            setupAutoSaveListeners: function () {\n                var self = this;\n                var form = this.$el.querySelector(\"form\");\n                if (!form) return;\n\n                // Text inputs and textareas: debounced save (2 seconds after typing stops)\n                var textInputs = form.querySelectorAll(\n                    'input[type=\"text\"], input[type=\"tel\"], input[type=\"date\"], textarea',\n                );\n                for (var i = 0; i < textInputs.length; i++) {\n                    textInputs[i].addEventListener(\"input\", function () {\n                        self.scheduleAutoSave();\n                    });\n                }\n\n                // Dropdowns, radios, and checkboxes: immediate save\n                var immediateInputs = form.querySelectorAll(\n                    'select, input[type=\"radio\"], input[type=\"checkbox\"]',\n                );\n                for (var j = 0; j < immediateInputs.length; j++) {\n                    immediateInputs[j].addEventListener(\"change\", function () {\n                        self.saveDraft();\n                    });\n                }\n\n                // Trait chips are <button>s, so they fire no input/change event \u2014\n                // traitSelector dispatches \"profile-autosave:trigger\" instead\n                // (the edit card's profileSectionAutosave listens for it, but the\n                // wizard has no such component). Without this, a member who picks\n                // qualities/defects and leaves before Continue loses them.\n                form.addEventListener(\"profile-autosave:trigger\", function () {\n                    self.saveDraft();\n                });\n            },\n\n            // Schedule auto-save with debounce (2 seconds)\n            scheduleAutoSave: function () {\n                clearTimeout(this.autoSaveTimer);\n                this.isDirty = true;\n\n                var self = this;\n                this.autoSaveTimer = setTimeout(function () {\n                    self.saveDraft();\n                }, 2000); // 2-second debounce\n            },\n\n            // Save current step data to draft (no validation)\n            saveDraft: function () {\n                var self = this;\n                self.isAutoSaving = true;\n\n                var stepData = self.gatherCurrentStepData();\n\n                var payload = {\n                    step: self.currentStep,\n                    data: stepData,\n                };\n                var jsonPayload = JSON.stringify(payload);\n\n                fetch(\"/api/profile/draft/save/\", {\n                    method: \"POST\",\n                    headers: {\n                        \"Content-Type\": \"application/json\",\n                        \"X-CSRFToken\": self.getCsrfToken(),\n                    },\n                    body: jsonPayload,\n                })\n                    .then(function (response) {\n                        return response.json();\n                    })\n                    .then(function (result) {\n                        self.isAutoSaving = false;\n                        self.isDirty = false;\n                        if (result.success) {\n                            self.lastSavedAt = result.saved_at;\n                            self._flashSavedPill();\n                        } else {\n                            console.error(\"[DRAFT SAVE] Save failed:\", result.error);\n                        }\n                    })\n                    .catch(function (err) {\n                        self.isAutoSaving = false;\n                        console.error(\"[DRAFT SAVE] \u274C Network error:\", err);\n                    });\n            },\n\n            // Gather current step form data.\n            // Scoped to the ACTIVE step's container ([data-wizard-step=\"N\"]):\n            // a full-form snapshot stored under one step key would survive\n            // that other step's official save (which only clears its own key)\n            // and silently override newer profile data on the next restore.\n            gatherCurrentStepData: function () {\n                var form = this.$el.querySelector(\"form\");\n                if (!form) {\n                    console.warn(\"[GATHER] No form found\");\n                    return {};\n                }\n\n                var container =\n                    form.querySelector(\n                        '[data-wizard-step=\"' + this.currentStep + '\"]',\n                    ) || form;\n\n                var data = {};\n                var checkboxGroups = {}; // Track checkbox arrays\n\n                var fields = container.querySelectorAll(\"input, textarea, select\");\n                for (var i = 0; i < fields.length; i++) {\n                    var field = fields[i];\n                    var key = field.name;\n                    if (!key) continue;\n\n                    // Skip file inputs (photos) - they're uploaded separately\n                    if (field.type === \"file\") continue;\n\n                    // NEVER persist the CSRF token: it is session-scoped, and\n                    // restoring a stale one after re-login would overwrite the\n                    // fresh token and 403 every subsequent save.\n                    if (key === \"csrfmiddlewaretoken\") continue;\n\n                    if (field.type === \"checkbox\") {\n                        // Collected per-group below (FormData-style iteration\n                        // would overwrite instead of building an array)\n                        if (!checkboxGroups[key]) {\n                            checkboxGroups[key] = [];\n                        }\n                        checkboxGroups[key].push(field);\n                        continue;\n                    }\n\n                    if (field.type === \"radio\") {\n                        if (field.checked) {\n                            data[key] = field.value;\n                        }\n                        continue;\n                    }\n\n                    // Only add non-empty values, so a partial save doesn't\n                    // clobber another field. EXCEPT the trait CSV hidden inputs:\n                    // draft saves are merged server-side (draft_data[step].update),\n                    // so omitting an emptied qualities_ids/defects_ids would leave\n                    // its stale value behind \u2014 deselecting the last chip would then\n                    // resurrect the trait on resume. Their empty state must be sent\n                    // so the merge overwrites it.\n                    if (\n                        field.value !== \"\" ||\n                        key === \"qualities_ids\" ||\n                        key === \"defects_ids\"\n                    ) {\n                        data[key] = field.value;\n                    }\n                }\n\n                // Single checkboxes (privacy settings) are stored as boolean;\n                // multiple checkboxes with same name (event_languages) as array\n                for (var name in checkboxGroups) {\n                    var group = checkboxGroups[name];\n\n                    if (group.length === 1) {\n                        data[name] = group[0].checked;\n                    } else {\n                        var checkedValues = [];\n                        for (var j = 0; j < group.length; j++) {\n                            if (group[j].checked) {\n                                checkedValues.push(group[j].value);\n                            }\n                        }\n                        data[name] = checkedValues;\n                    }\n                }\n\n                return data;\n            },\n\n            // Setup periodic checkpoint (every 60 seconds if dirty)\n            setupPeriodicCheckpoint: function () {\n                var self = this;\n                setInterval(function () {\n                    if (self.isDirty) {\n                        self.saveDraft();\n                    }\n                }, 60000); // 60 seconds\n            },\n\n            // Warn before leaving with unsaved changes\n            setupUnloadWarning: function () {\n                var self = this;\n                window.addEventListener(\"beforeunload\", function (e) {\n                    if (self.isDirty) {\n                        e.preventDefault();\n                        e.returnValue = \"\";\n                    }\n                });\n            },\n        };\n    });\n\n    // Canton Map component for location selection\n    // Interactive SVG map for selecting Luxembourg cantons and border regions\n    Alpine.data(\"cantonMap\", function () {\n        return {\n            // State\n            selectedRegion: \"\",\n            selectedRegionName: \"\",\n            hoveredRegion: \"\",\n            hoveredRegionName: \"\",\n            showFallbackDropdown: false,\n            focusedIndex: -1,\n\n            // Region data for keyboard navigation\n            regions: [],\n\n            // Computed getters for CSP compatibility\n            get hasSelection() {\n                return this.selectedRegion !== \"\";\n            },\n            get noSelection() {\n                return this.selectedRegion === \"\";\n            },\n            get isHovering() {\n                return this.hoveredRegion !== \"\";\n            },\n            get selectionLabel() {\n                if (this.selectedRegionName) {\n                    return this.selectedRegionName;\n                }\n                return (\n                    this.$el.getAttribute(\"data-placeholder\") ||\n                    \"Click the map to select your region\"\n                );\n            },\n            get hoverLabel() {\n                return this.hoveredRegionName || \"\";\n            },\n            get fallbackDropdownVisible() {\n                return this.showFallbackDropdown;\n            },\n            get selectedClass() {\n                return this.hasSelection ? \"has-selection\" : \"\";\n            },\n\n            init: function () {\n                var self = this;\n\n                // Read initial value from data attributes\n                var initialValue = this.$el.getAttribute(\"data-initial-value\");\n                var initialName = this.$el.getAttribute(\"data-initial-name\");\n\n                if (initialValue && initialValue !== \"\") {\n                    this.selectedRegion = initialValue;\n                    this.selectedRegionName = initialName || initialValue;\n                    // Highlight the initially selected region\n                    this.$nextTick(function () {\n                        // Safety check: ensure the function exists before calling\n                        if (typeof self._highlightRegion === \"function\") {\n                            self._highlightRegion(initialValue);\n                        }\n                    });\n                }\n\n                // Build regions array for keyboard navigation\n                this.$nextTick(function () {\n                    self._buildRegionsArray();\n                    self._setupEventDelegation();\n                    self._setupKeyboardNavigation();\n                });\n\n                // Restore from a draft value written into the hidden input\n                // AFTER init (profileWizard.populateFieldsFromDraft) \u2014 the\n                // map would otherwise show the placeholder while the hidden\n                // field already holds a region.\n                this.$el.addEventListener(\"location-restore\", function (e) {\n                    var regionId = e.detail && e.detail.location;\n                    if (!regionId) return;\n                    var path = document.getElementById(regionId);\n                    var name =\n                        (path && path.getAttribute(\"data-region-name\")) || regionId;\n                    self.selectRegion(regionId, name);\n                });\n\n                // Check for reduced motion preference\n                if (\n                    window.matchMedia &&\n                    window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n                ) {\n                    this.$el.classList.add(\"reduce-motion\");\n                }\n            },\n\n            _buildRegionsArray: function () {\n                var svg = this.$el.querySelector(\"svg\");\n                if (!svg) return;\n\n                var paths = svg.querySelectorAll(\"[data-region-id]\");\n                this.regions = [];\n\n                for (var i = 0; i < paths.length; i++) {\n                    this.regions.push({\n                        id: paths[i].getAttribute(\"data-region-id\"),\n                        name: paths[i].getAttribute(\"data-region-name\"),\n                        element: paths[i],\n                    });\n                }\n            },\n\n            _getBorderRegionAtPoint: function (svgX, svgY) {\n                // France - South side (narrow bottom strip)\n                if (svgY > 850) {\n                    return {\n                        id: \"border-france\",\n                        name: \"France (Thionville/Metz area)\",\n                    };\n                }\n                // Belgium - West side (left strip)\n                if (svgX < 350) {\n                    return { id: \"border-belgium\", name: \"Belgium (Arlon area)\" };\n                }\n                // Germany - East side (right strip)\n                if (svgX > 350) {\n                    return {\n                        id: \"border-germany\",\n                        name: \"Germany (Trier/Saarland area)\",\n                    };\n                }\n                return null;\n            },\n\n            _screenToSVGCoords: function (svg, screenX, screenY) {\n                var point = svg.createSVGPoint();\n                point.x = screenX;\n                point.y = screenY;\n                var ctm = svg.getScreenCTM();\n                if (ctm) {\n                    return point.matrixTransform(ctm.inverse());\n                }\n                return point;\n            },\n\n            _setupEventDelegation: function () {\n                var self = this;\n                var svg = this.$el.querySelector(\"svg\");\n                if (!svg) return;\n\n                // Click handler\n                svg.addEventListener(\"click\", function (e) {\n                    var path = e.target.closest(\"[data-region-id]\");\n\n                    if (path && path.classList.contains(\"lux-canton\")) {\n                        self.selectRegion(\n                            path.getAttribute(\"data-region-id\"),\n                            path.getAttribute(\"data-region-name\"),\n                        );\n                        return;\n                    }\n\n                    if (path && path.classList.contains(\"border-region\")) {\n                        self.selectRegion(\n                            path.getAttribute(\"data-region-id\"),\n                            path.getAttribute(\"data-region-name\"),\n                        );\n                        return;\n                    }\n\n                    if (\n                        e.target.classList.contains(\"map-background\") ||\n                        e.target.tagName === \"svg\"\n                    ) {\n                        var svgPoint = self._screenToSVGCoords(\n                            svg,\n                            e.clientX,\n                            e.clientY,\n                        );\n                        var borderRegion = self._getBorderRegionAtPoint(\n                            svgPoint.x,\n                            svgPoint.y,\n                        );\n                        if (borderRegion) {\n                            self.selectRegion(borderRegion.id, borderRegion.name);\n                        }\n                    }\n                });\n\n                // Mouseover handler\n                svg.addEventListener(\"mouseover\", function (e) {\n                    var path = e.target.closest(\"[data-region-id]\");\n                    if (path) {\n                        self.hoverRegion(\n                            path.getAttribute(\"data-region-id\"),\n                            path.getAttribute(\"data-region-name\"),\n                        );\n                    }\n                });\n\n                // Mouseout handler\n                svg.addEventListener(\"mouseout\", function (e) {\n                    var path = e.target.closest(\"[data-region-id]\");\n                    if (path) {\n                        self.clearHover();\n                    }\n                });\n\n                // Touch events for mobile\n                svg.addEventListener(\n                    \"touchstart\",\n                    function (e) {\n                        var path = e.target.closest(\"[data-region-id]\");\n                        if (path) {\n                            self.hoverRegion(\n                                path.getAttribute(\"data-region-id\"),\n                                path.getAttribute(\"data-region-name\"),\n                            );\n                        }\n                    },\n                    { passive: true },\n                );\n\n                svg.addEventListener(\"touchend\", function (e) {\n                    var path = e.target.closest(\"[data-region-id]\");\n                    if (path) {\n                        self.selectRegion(\n                            path.getAttribute(\"data-region-id\"),\n                            path.getAttribute(\"data-region-name\"),\n                        );\n                        self.clearHover();\n                    }\n                });\n            },\n\n            _setupKeyboardNavigation: function () {\n                var self = this;\n                var svg = this.$el.querySelector(\"svg\");\n                if (!svg) return;\n\n                svg.addEventListener(\"keydown\", function (e) {\n                    var key = e.key;\n                    if (key === \"ArrowDown\" || key === \"ArrowRight\") {\n                        e.preventDefault();\n                        self._navigateNext();\n                    } else if (key === \"ArrowUp\" || key === \"ArrowLeft\") {\n                        e.preventDefault();\n                        self._navigatePrevious();\n                    } else if (key === \"Enter\" || key === \" \") {\n                        e.preventDefault();\n                        self._selectFocused();\n                    } else if (key === \"Home\") {\n                        e.preventDefault();\n                        self._focusFirst();\n                    } else if (key === \"End\") {\n                        e.preventDefault();\n                        self._focusLast();\n                    }\n                });\n\n                svg.addEventListener(\"focus\", function () {\n                    if (self.regions.length === 0) {\n                        return;\n                    }\n                    if (self.focusedIndex < 0) {\n                        var selectedIndex = self._getSelectedIndex();\n                        self.focusedIndex = selectedIndex >= 0 ? selectedIndex : 0;\n                    }\n                    // blur clears the active descendant but keeps the index, so\n                    // re-apply on every focus to restore it when the user tabs back.\n                    self._applyFocus();\n                });\n\n                svg.addEventListener(\"blur\", function () {\n                    self._clearFocus();\n                });\n            },\n\n            _navigateNext: function () {\n                if (this.regions.length === 0) return;\n                this.focusedIndex = (this.focusedIndex + 1) % this.regions.length;\n                this._applyFocus();\n            },\n\n            _navigatePrevious: function () {\n                if (this.regions.length === 0) return;\n                this.focusedIndex =\n                    (this.focusedIndex - 1 + this.regions.length) % this.regions.length;\n                this._applyFocus();\n            },\n\n            _focusFirst: function () {\n                if (this.regions.length === 0) return;\n                this.focusedIndex = 0;\n                this._applyFocus();\n            },\n\n            _focusLast: function () {\n                if (this.regions.length === 0) return;\n                this.focusedIndex = this.regions.length - 1;\n                this._applyFocus();\n            },\n\n            _selectFocused: function () {\n                if (this.focusedIndex >= 0 && this.focusedIndex < this.regions.length) {\n                    var region = this.regions[this.focusedIndex];\n                    this.selectRegion(region.id, region.name);\n                }\n            },\n\n            _getSelectedIndex: function () {\n                for (var i = 0; i < this.regions.length; i++) {\n                    if (this.regions[i].id === this.selectedRegion) {\n                        return i;\n                    }\n                }\n                return -1;\n            },\n\n            _applyFocus: function () {\n                this._clearFocus();\n                if (this.focusedIndex >= 0 && this.focusedIndex < this.regions.length) {\n                    var region = this.regions[this.focusedIndex];\n                    region.element.classList.add(\"region-focused\");\n                    // DOM focus stays on the listbox <svg>, so point it at the active\n                    // option; otherwise screen readers never announce the new region.\n                    var svg = this.$el.querySelector(\"svg\");\n                    if (svg) {\n                        svg.setAttribute(\n                            \"aria-activedescendant\",\n                            region.element.id || region.id\n                        );\n                    }\n                    this.hoverRegion(region.id, region.name);\n                }\n            },\n\n            _clearFocus: function () {\n                var svg = this.$el.querySelector(\"svg\");\n                if (!svg) return;\n                svg.removeAttribute(\"aria-activedescendant\");\n                var focusedElements = svg.querySelectorAll(\".region-focused\");\n                for (var i = 0; i < focusedElements.length; i++) {\n                    focusedElements[i].classList.remove(\"region-focused\");\n                }\n            },\n\n            selectRegion: function (regionId, regionName) {\n                if (this.selectedRegion) {\n                    this._unhighlightRegion(this.selectedRegion);\n                }\n                this.selectedRegion = regionId;\n                this.selectedRegionName = regionName;\n                this._highlightRegion(regionId);\n\n                var hiddenInput = document.getElementById(\"id_location\");\n                if (hiddenInput) {\n                    hiddenInput.value = regionId;\n                    hiddenInput.dispatchEvent(new Event(\"change\", { bubbles: true }));\n                }\n\n                // Dispatch global event for profileWizard validation tracking\n                window.dispatchEvent(\n                    new CustomEvent(\"location-selected\", {\n                        detail: { location: regionId, name: regionName },\n                    }),\n                );\n\n                this.$dispatch(\"region-selected\", { id: regionId, name: regionName });\n            },\n\n            hoverRegion: function (regionId, regionName) {\n                this.hoveredRegion = regionId;\n                this.hoveredRegionName = regionName;\n                var path = document.getElementById(regionId);\n                if (path && !path.classList.contains(\"region-selected\")) {\n                    path.classList.add(\"region-hover\");\n                }\n            },\n\n            clearHover: function () {\n                if (this.hoveredRegion) {\n                    var prevPath = document.getElementById(this.hoveredRegion);\n                    if (prevPath) {\n                        prevPath.classList.remove(\"region-hover\");\n                    }\n                }\n                this.hoveredRegion = \"\";\n                this.hoveredRegionName = \"\";\n            },\n\n            toggleFallbackDropdown: function () {\n                this.showFallbackDropdown = !this.showFallbackDropdown;\n            },\n\n            handleFallbackSelect: function (event) {\n                var select = event.target;\n                var option = select.options[select.selectedIndex];\n                if (option && option.value) {\n                    this.selectRegion(option.value, option.text);\n                }\n            },\n\n            _highlightRegion: function (regionId) {\n                var path = document.getElementById(regionId);\n                if (path) {\n                    path.classList.add(\"region-selected\");\n                    path.classList.remove(\"region-hover\");\n                    path.setAttribute(\"aria-selected\", \"true\");\n                }\n            },\n\n            _unhighlightRegion: function (regionId) {\n                var path = document.getElementById(regionId);\n                if (path) {\n                    path.classList.remove(\"region-selected\");\n                    path.setAttribute(\"aria-selected\", \"false\");\n                }\n            },\n        };\n    });\n\n    // Date of Birth Picker component - stepped selection for better UX\n    // 4 steps: 1) Age range chips, 2) Year selection, 3) Month selection, 4) Day selection\n    // CSP-compatible: Uses DOM manipulation for dynamic content (x-for not CSP-safe)\n    // Reads initial value from hidden input with name=\"date_of_birth\"\n    Alpine.data(\"dobPicker\", function () {\n        return {\n            // State\n            step: 1, // 1=age range, 2=year, 3=month, 4=day\n            selectedAgeRange: \"\",\n            selectedYear: null,\n            selectedMonth: null,\n            selectedDay: null,\n            _translatedMonths: null,\n            _clsUnselected:\n                \"border-gray-200 bg-white dark:bg-gray-800 dark:border-gray-600 text-gray-700 dark:text-gray-200 hover:border-purple-300 hover:bg-purple-50 dark:hover:bg-purple-900/30\",\n            _clsSelected:\n                \"border-purple-500 bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:border-purple-400 dark:text-purple-300\",\n\n            // Age range definitions (computed from current year)\n            get ageRanges() {\n                var y = new Date().getFullYear();\n                return [\n                    {\n                        label: \"18-25\",\n                        emoji: \"\\u{1F331}\",\n                        minYear: y - 25,\n                        maxYear: y - 18,\n                    },\n                    {\n                        label: \"26-35\",\n                        emoji: \"\\u{1F33F}\",\n                        minYear: y - 35,\n                        maxYear: y - 26,\n                    },\n                    {\n                        label: \"36-45\",\n                        emoji: \"\\u{1F333}\",\n                        minYear: y - 45,\n                        maxYear: y - 36,\n                    },\n                    {\n                        label: \"46-55\",\n                        emoji: \"\\u{1F342}\",\n                        minYear: y - 55,\n                        maxYear: y - 46,\n                    },\n                    {\n                        label: \"56-65\",\n                        emoji: \"\\u{1F341}\",\n                        minYear: y - 65,\n                        maxYear: y - 56,\n                    },\n                    {\n                        label: \"66+\",\n                        emoji: \"\\u{1F31F}\",\n                        minYear: y - 100,\n                        maxYear: y - 66,\n                    },\n                ];\n            },\n\n            // CSP-safe computed getters\n            get isStep1() {\n                return this.step === 1;\n            },\n            get isStep2() {\n                return this.step === 2;\n            },\n            get isStep3() {\n                return this.step === 3;\n            },\n            get isStep4() {\n                return this.step === 4;\n            },\n            get hasAgeRange() {\n                return this.selectedAgeRange !== \"\";\n            },\n            get hasYear() {\n                return this.selectedYear !== null;\n            },\n            get hasMonth() {\n                return this.selectedMonth !== null;\n            },\n            get hasDay() {\n                return this.selectedDay !== null;\n            },\n            get isComplete() {\n                return this.hasYear && this.hasMonth && this.hasDay;\n            },\n            get notComplete() {\n                return !this.isComplete;\n            },\n\n            // Breadcrumb display text getters\n            get yearBreadcrumbText() {\n                return this.selectedYear ? String(this.selectedYear) : \"\";\n            },\n            get monthBreadcrumbText() {\n                return this.selectedMonthName || \"\";\n            },\n            get dayBreadcrumbText() {\n                return this.selectedDay ? String(this.selectedDay) : \"\";\n            },\n\n            get months() {\n                if (this._translatedMonths) return this._translatedMonths;\n                // Default English month names (will be overridden by data-months attribute)\n                return [\n                    { num: 1, name: \"January\" },\n                    { num: 2, name: \"February\" },\n                    { num: 3, name: \"March\" },\n                    { num: 4, name: \"April\" },\n                    { num: 5, name: \"May\" },\n                    { num: 6, name: \"June\" },\n                    { num: 7, name: \"July\" },\n                    { num: 8, name: \"August\" },\n                    { num: 9, name: \"September\" },\n                    { num: 10, name: \"October\" },\n                    { num: 11, name: \"November\" },\n                    { num: 12, name: \"December\" },\n                ];\n            },\n\n            get isoDate() {\n                if (!this.isComplete) return \"\";\n                var m =\n                    this.selectedMonth < 10\n                        ? \"0\" + this.selectedMonth\n                        : this.selectedMonth;\n                var d =\n                    this.selectedDay < 10 ? \"0\" + this.selectedDay : this.selectedDay;\n                return this.selectedYear + \"-\" + m + \"-\" + d;\n            },\n\n            get formattedDate() {\n                if (!this.isComplete) return \"\";\n                var monthObj = this._findMonth(this.selectedMonth);\n                var monthName = monthObj ? monthObj.name : this.selectedMonth;\n                return this.selectedDay + \" \" + monthName + \" \" + this.selectedYear;\n            },\n\n            get selectedMonthName() {\n                if (!this.selectedMonth) return \"\";\n                var monthObj = this._findMonth(this.selectedMonth);\n                return monthObj ? monthObj.name : \"\";\n            },\n\n            // Breadcrumb button class getters (with dark mode)\n            get step1ButtonClass() {\n                return this.isStep1\n                    ? \"bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300 font-medium\"\n                    : \"text-gray-500 dark:text-gray-400 hover:text-purple-600 dark:hover:text-purple-400 hover:bg-purple-50 dark:hover:bg-purple-900/20\";\n            },\n            get step2ButtonClass() {\n                return this.isStep2\n                    ? \"bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300 font-medium\"\n                    : \"text-gray-500 dark:text-gray-400 hover:text-purple-600 dark:hover:text-purple-400 hover:bg-purple-50 dark:hover:bg-purple-900/20\";\n            },\n            get step3ButtonClass() {\n                return this.isStep3\n                    ? \"bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300 font-medium\"\n                    : \"text-gray-500 dark:text-gray-400 hover:text-purple-600 dark:hover:text-purple-400 hover:bg-purple-50 dark:hover:bg-purple-900/20\";\n            },\n            get step4ButtonClass() {\n                return this.isStep4\n                    ? \"bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300 font-medium\"\n                    : \"text-gray-500 dark:text-gray-400 hover:text-purple-600 dark:hover:text-purple-400 hover:bg-purple-50 dark:hover:bg-purple-900/20\";\n            },\n\n            // Breadcrumb visibility getters\n            get showAgeRangeBreadcrumb() {\n                return this.step > 1;\n            },\n            get showYearBreadcrumb() {\n                return this.step > 2;\n            },\n            get showMonthBreadcrumb() {\n                return this.step > 3;\n            },\n\n            init: function () {\n                var self = this;\n\n                // Load translated month names from data attribute\n                var monthsData = this.$el.getAttribute(\"data-months\");\n                if (monthsData) {\n                    try {\n                        this._translatedMonths = JSON.parse(monthsData);\n                    } catch (e) {\n                        console.warn(\"dobPicker: Could not parse months data\", e);\n                    }\n                }\n\n                // Restore state from data-initial-dob BEFORE the first render\n                // so x-show=\"isStepN\" picks the right step on the first pass.\n                // This mutates `this.step` and the selected* fields, so when\n                // Alpine evaluates the getters right after init() returns,\n                // they already reflect the prefilled value.\n                this._parseInitialDate();\n\n                // Render the server-empty containers (age ranges, and if we\n                // prefilled, the year/month/day grids too). _parseInitialDate\n                // handled the renders for the prefilled path; this covers the\n                // blank-profile path where we still need the age chips.\n                this.$nextTick(function () {\n                    if (!self.selectedAgeRange) {\n                        self._renderAgeRanges();\n                    }\n                });\n\n                // Restore from a draft value written into the hidden input\n                // AFTER init (profileWizard.populateFieldsFromDraft) \u2014\n                // without this the picker UI stays on the empty age-range\n                // step while the hidden field already holds a date.\n                this.$el.addEventListener(\"dob-restore\", function (e) {\n                    var value = (e.detail && e.detail.value) || \"\";\n                    if (value) {\n                        self._parseInitialDate(value);\n                    }\n                });\n\n                // Listen for external resets if needed\n                this.$el.addEventListener(\"dob-reset\", function () {\n                    self.step = 1;\n                    self.selectedAgeRange = \"\";\n                    self.selectedYear = null;\n                    self.selectedMonth = null;\n                    self.selectedDay = null;\n                    self._updateHiddenInput();\n                    self._renderAgeRanges();\n                });\n            },\n\n            _findMonth: function (num) {\n                var months = this.months;\n                for (var i = 0; i < months.length; i++) {\n                    if (months[i].num === num) return months[i];\n                }\n                return null;\n            },\n\n            _getDaysInMonth: function (year, month) {\n                // Month is 1-based, Date uses 0-based months\n                // Using day 0 of next month gives last day of current month\n                return new Date(year, month, 0).getDate();\n            },\n\n            _validateSelectedDay: function () {\n                if (this.selectedDay && this.selectedMonth && this.selectedYear) {\n                    var maxDay = this._getDaysInMonth(\n                        this.selectedYear,\n                        this.selectedMonth,\n                    );\n                    if (this.selectedDay > maxDay) {\n                        this.selectedDay = null;\n                        this._updateCompletionSummary();\n                    }\n                }\n            },\n\n            _parseInitialDate: function (explicitValue) {\n                // Prefer an explicit value (draft restore), then the\n                // data-initial-dob attribute on the component root (set by\n                // the server template) so we don't depend on when Alpine\n                // decides to scan the inner hidden input during $nextTick.\n                // Fall back to the hidden input for callers that run after\n                // the user picks a date.\n                var initialValue =\n                    explicitValue || this.$el.getAttribute(\"data-initial-dob\") || \"\";\n                if (!initialValue) {\n                    var hiddenInput = this.$el.querySelector(\n                        'input[name=\"date_of_birth\"]',\n                    );\n                    initialValue = (hiddenInput && hiddenInput.value) || \"\";\n                }\n                if (!initialValue) return;\n\n                var parts = initialValue.split(\"-\");\n                if (parts.length !== 3) return;\n\n                var year = parseInt(parts[0], 10);\n                var month = parseInt(parts[1], 10);\n                var day = parseInt(parts[2], 10);\n\n                if (isNaN(year) || isNaN(month) || isNaN(day)) return;\n\n                // Find matching age range\n                var self = this;\n                var matchingRange = null;\n                for (var i = 0; i < this.ageRanges.length; i++) {\n                    var r = this.ageRanges[i];\n                    if (year >= r.minYear && year <= r.maxYear) {\n                        matchingRange = r;\n                        break;\n                    }\n                }\n\n                if (matchingRange) {\n                    this.selectedAgeRange = matchingRange.label;\n                    this.selectedYear = year;\n                    this.selectedMonth = month;\n                    this.selectedDay = day;\n                    this.step = 4; // Show completion state\n                    // Re-render with initial values\n                    this._renderAgeRanges();\n                    this._renderYears();\n                    this._renderMonths();\n                    this._renderDays();\n                    this._updateCompletionSummary();\n                }\n            },\n\n            _updateHiddenInput: function () {\n                var hiddenInput = this.$el.querySelector('input[name=\"date_of_birth\"]');\n                if (hiddenInput) {\n                    hiddenInput.value = this.isoDate;\n                    hiddenInput.dispatchEvent(new Event(\"change\", { bubbles: true }));\n                }\n            },\n\n            _dispatchDateSelected: function () {\n                window.dispatchEvent(\n                    new CustomEvent(\"dob-selected\", {\n                        detail: {\n                            iso: this.isoDate,\n                            formatted: this.formattedDate,\n                            year: this.selectedYear,\n                            month: this.selectedMonth,\n                            day: this.selectedDay,\n                        },\n                    }),\n                );\n            },\n\n            // DOM rendering methods (CSP-safe alternative to x-for)\n            _renderAgeRanges: function () {\n                var container = this.$el.querySelector(\"[data-dob-age-ranges]\");\n                if (!container) return;\n\n                var self = this;\n                container.innerHTML = \"\";\n\n                this.ageRanges.forEach(function (range) {\n                    var btn = document.createElement(\"button\");\n                    btn.type = \"button\";\n                    btn.className =\n                        \"w-full h-full min-h-[3.5rem] flex items-center justify-center gap-2 px-3 py-3 rounded-xl text-sm font-medium border-2 transition-all duration-200\";\n                    btn.className +=\n                        self.selectedAgeRange === range.label\n                            ? \" \" + self._clsSelected\n                            : \" \" + self._clsUnselected;\n                    btn.innerHTML =\n                        '<span class=\"text-xl flex-shrink-0\">' +\n                        range.emoji +\n                        '</span><span class=\"whitespace-nowrap\">' +\n                        range.label +\n                        \"</span>\";\n                    btn.addEventListener(\"click\", function () {\n                        self.selectAgeRange(range.label);\n                    });\n                    container.appendChild(btn);\n                });\n            },\n\n            _renderYears: function () {\n                var container = this.$el.querySelector(\"[data-dob-years]\");\n                if (!container) return;\n\n                var self = this;\n                container.innerHTML = \"\";\n\n                if (!this.selectedAgeRange) return;\n\n                // Find the selected age range\n                var range = null;\n                for (var i = 0; i < this.ageRanges.length; i++) {\n                    if (this.ageRanges[i].label === this.selectedAgeRange) {\n                        range = this.ageRanges[i];\n                        break;\n                    }\n                }\n                if (!range) return;\n\n                for (var y = range.maxYear; y >= range.minYear; y--) {\n                    (function (year) {\n                        var btn = document.createElement(\"button\");\n                        btn.type = \"button\";\n                        btn.className =\n                            \"px-3 py-2 rounded-lg text-sm font-medium border-2 transition-all duration-150\";\n                        btn.className +=\n                            self.selectedYear === year\n                                ? \" \" + self._clsSelected\n                                : \" \" + self._clsUnselected;\n                        btn.textContent = year;\n                        btn.addEventListener(\"click\", function () {\n                            self.selectYear(year);\n                        });\n                        container.appendChild(btn);\n                    })(y);\n                }\n            },\n\n            _renderMonths: function () {\n                var container = this.$el.querySelector(\"[data-dob-months]\");\n                if (!container) return;\n\n                var self = this;\n                container.innerHTML = \"\";\n\n                this.months.forEach(function (month) {\n                    var btn = document.createElement(\"button\");\n                    btn.type = \"button\";\n                    btn.className =\n                        \"px-3 py-2.5 rounded-lg text-sm font-medium border-2 transition-all duration-150\";\n                    btn.className +=\n                        self.selectedMonth === month.num\n                            ? \" \" + self._clsSelected\n                            : \" \" + self._clsUnselected;\n                    btn.textContent = month.name;\n                    btn.addEventListener(\"click\", function () {\n                        self.selectMonth(month.num);\n                    });\n                    container.appendChild(btn);\n                });\n            },\n\n            _renderDays: function () {\n                var container = this.$el.querySelector(\"[data-dob-days]\");\n                if (!container) return;\n\n                var self = this;\n                container.innerHTML = \"\";\n\n                if (!this.selectedMonth || !this.selectedYear) return;\n\n                var daysInMonth = this._getDaysInMonth(\n                    this.selectedYear,\n                    this.selectedMonth,\n                );\n\n                for (var d = 1; d <= daysInMonth; d++) {\n                    (function (day) {\n                        var btn = document.createElement(\"button\");\n                        btn.type = \"button\";\n                        btn.className =\n                            \"w-full aspect-square flex items-center justify-center rounded-lg text-sm font-medium border-2 transition-all duration-150\";\n                        btn.className +=\n                            self.selectedDay === day\n                                ? \" \" + self._clsSelected\n                                : \" \" + self._clsUnselected;\n                        btn.textContent = day;\n                        btn.addEventListener(\"click\", function () {\n                            self.selectDay(day);\n                        });\n                        container.appendChild(btn);\n                    })(d);\n                }\n            },\n\n            _updateCompletionSummary: function () {\n                var summary = this.$el.querySelector(\"[data-dob-summary]\");\n                if (summary) {\n                    if (this.isComplete) {\n                        summary.classList.remove(\"hidden\");\n                        var dateDisplay = summary.querySelector(\"[data-dob-formatted]\");\n                        if (dateDisplay) {\n                            dateDisplay.textContent = this.formattedDate;\n                        }\n                    } else {\n                        summary.classList.add(\"hidden\");\n                    }\n                }\n            },\n\n            // Selection methods\n            selectAgeRange: function (label) {\n                this.selectedAgeRange = label;\n                this.selectedYear = null;\n                this.selectedMonth = null;\n                this.selectedDay = null;\n                this.step = 2;\n                this._updateHiddenInput();\n                this._renderAgeRanges();\n                this._renderYears();\n            },\n\n            selectYear: function (year) {\n                this.selectedYear = year;\n                this._validateSelectedDay();\n                this.selectedMonth = null;\n                this.selectedDay = null;\n                this.step = 3;\n                this._updateHiddenInput();\n                this._renderYears();\n                this._renderMonths();\n            },\n\n            selectMonth: function (month) {\n                this.selectedMonth = month;\n                this._validateSelectedDay();\n                this.selectedDay = null;\n                this.step = 4;\n                this._updateHiddenInput();\n                this._renderMonths();\n                this._renderDays();\n            },\n\n            selectDay: function (day) {\n                this.selectedDay = day;\n                this._updateHiddenInput();\n                this._renderDays();\n                this._updateCompletionSummary();\n                this._dispatchDateSelected();\n            },\n\n            // Navigation methods for breadcrumb\n            goToStep1: function () {\n                this.step = 1;\n                this._renderAgeRanges();\n            },\n\n            goToStep2: function () {\n                if (this.hasAgeRange) {\n                    this.step = 2;\n                    this._renderYears();\n                }\n            },\n\n            goToStep3: function () {\n                if (this.hasYear) {\n                    this.step = 3;\n                    this._renderMonths();\n                }\n            },\n\n            goToStep4: function () {\n                if (this.hasMonth) {\n                    this._validateSelectedDay();\n                    this.step = 4;\n                    this._renderDays();\n                    this._updateCompletionSummary();\n                }\n            },\n        };\n    });\n\n    // Phone verification component for the phone input field\n    // Used in profile creation - wraps the phone input with verification logic\n    // Reads initial state from data attributes: data-verified, data-phone-input-id\n    Alpine.data(\"phoneVerificationComponent\", function () {\n        return {\n            verified: false,\n            canVerify: false,\n            errorMessage: \"\",\n            iti: null,\n            phoneInputId: \"\",\n            failureCount: 0,\n\n            // Computed getters for CSP compatibility\n            get notVerified() {\n                return !this.verified;\n            },\n            // The resting (unverified, no failed attempt yet) state reads as a\n            // neutral status, not an error \u2014 a red \"Verification Required\"\n            // pill before the member has typed anything reads as \"you already\n            // did something wrong\". Only a failed verify attempt earns red.\n            get notVerifiedNeutral() {\n                return !this.verified && this.failureCount === 0;\n            },\n            get notVerifiedFailed() {\n                return !this.verified && this.failureCount > 0;\n            },\n            get showSupportContact() {\n                return this.failureCount >= 2;\n            },\n            get cannotVerify() {\n                return !this.canVerify;\n            },\n            get verifiedValue() {\n                return this.verified ? \"true\" : \"false\";\n            },\n            get verifyButtonPulseClass() {\n                return this.canVerify ? \"animate-pulse-subtle\" : \"\";\n            },\n            get phoneHintClass() {\n                return !this.verified ? \"text-purple-600 font-medium\" : \"text-gray-500\";\n            },\n\n            init: function () {\n                var self = this;\n\n                // Read initial state from data attributes\n                var verifiedAttr = this.$el.getAttribute(\"data-verified\");\n                this.verified = verifiedAttr === \"true\";\n\n                // data-keep-iti=\"true\" preserves the flag/dial-code display after\n                // verification. Use this on pages without a <form> (e.g. onboarding)\n                // where destroying the instance would leave a plain text field.\n                this.keepIti = this.$el.getAttribute(\"data-keep-iti\") === \"true\";\n\n                this.phoneInputId =\n                    this.$el.getAttribute(\"data-phone-input-id\") || \"id_phone_number\";\n\n                var phoneInput = document.getElementById(this.phoneInputId);\n                if (\n                    phoneInput &&\n                    typeof window.intlTelInput === \"function\" &&\n                    !this.verified\n                ) {\n                    var prefilledValue = phoneInput.value;\n                    phoneInput.value = \"\";\n\n                    try {\n                        this.iti = window.intlTelInput(phoneInput, {\n                            initialCountry: \"lu\",\n                            preferredCountries: [\"lu\", \"de\", \"fr\", \"be\"],\n                            onlyCountries: [\n                                \"lu\",\n                                \"de\",\n                                \"fr\",\n                                \"be\",\n                                \"nl\",\n                                \"ch\",\n                                \"at\",\n                                \"it\",\n                                \"es\",\n                                \"pt\",\n                                \"gb\",\n                                \"ie\",\n                                \"us\",\n                                \"ca\",\n                                \"se\",\n                                \"dz\",\n                            ],\n                            separateDialCode: true,\n                            nationalMode: false,\n                            formatOnDisplay: true,\n                            autoPlaceholder: \"aggressive\",\n                            utilsScript:\n                                \"https://cdn.jsdelivr.net/npm/intl-tel-input@18.5.3/build/js/utils.js\",\n                        });\n\n                        window.itiInstance = this.iti;\n\n                        this.iti.promise\n                            .then(function () {\n                                if (prefilledValue) {\n                                    var num = prefilledValue.trim().replace(/\\s/g, \"\");\n                                    if (!num.startsWith(\"+\")) {\n                                        if (num.startsWith(\"00\"))\n                                            num = \"+\" + num.slice(2);\n                                        else num = \"+352\" + num.replace(/^0+/, \"\");\n                                    }\n                                    self.iti.setNumber(num);\n                                }\n                            })\n                            .catch(function (err) {\n                                console.warn(\n                                    \"intl-tel-input: Utils loading error\",\n                                    err,\n                                );\n                            });\n                    } catch (err) {\n                        console.error(\"intl-tel-input: Initialization error\", err);\n                    }\n                }\n\n                // WhatsApp \u2192 SMS fallback: the modal dispatches this when the\n                // number isn't on WhatsApp so we run the normal SMS flow (which\n                // reuses the open modal, reCAPTCHA, and Firebase send).\n                window.addEventListener(\"start-sms-verification\", function (e) {\n                    self._doStartVerification(e.detail);\n                });\n\n                // Listen for verification success event\n                window.addEventListener(\"phone-verified\", function (e) {\n                    self.verified = true;\n                    var phoneInput = document.getElementById(self.phoneInputId);\n                    if (phoneInput && e.detail) {\n                        if (self.iti) {\n                            // Use setNumber() to keep intl-tel-input in sync, then\n                            // destroy the instance so it can't strip the dial code later\n                            self.iti.setNumber(e.detail);\n                        }\n                        phoneInput.readOnly = true;\n                        if (self.keepIti && self.iti) {\n                            // No form submission on this page \u2014 keep the iti instance so\n                            // the country flag and dial code remain visible after verification.\n                            self.iti.setNumber(e.detail);\n                        } else {\n                            // Set the raw input value to full E.164 number as a safety net\n                            phoneInput.value = e.detail;\n                            // Destroy intl-tel-input instance - phone is now verified and locked,\n                            // we don't want the library stripping the dial code on form submit\n                            if (self.iti) {\n                                try {\n                                    self.iti.destroy();\n                                } catch (err) {\n                                    console.warn(\n                                        \"intl-tel-input: Could not destroy after verification\",\n                                        err,\n                                    );\n                                }\n                                self.iti = null;\n                                window.itiInstance = null;\n                            }\n                        }\n                    }\n                });\n            },\n\n            // Cleanup intl-tel-input when component is destroyed (prevents memory leaks)\n            destroy: function () {\n                if (this.iti) {\n                    try {\n                        this.iti.destroy();\n                    } catch (e) {\n                        console.warn(\"intl-tel-input: Could not destroy instance\", e);\n                    }\n                    this.iti = null;\n                    window.itiInstance = null;\n                }\n            },\n\n            // CSP-compatible: no event parameter needed, uses this.$el\n            onPhoneInput: function () {\n                if (this.iti) {\n                    this.canVerify =\n                        this.iti.isValidNumber() || this.iti.getNumber().length >= 8;\n                } else {\n                    // Get value from the phone input element\n                    var phoneInput = document.getElementById(this.phoneInputId);\n                    this.canVerify = phoneInput && phoneInput.value.trim().length >= 6;\n                }\n                this.errorMessage = \"\";\n            },\n\n            startVerification: function () {\n                var self = this;\n                if (this.iti && !this.iti.isValidNumber()) {\n                    this.errorMessage = \"Please enter a valid phone number\";\n                    return;\n                }\n\n                // Get phone number\n                var phoneNumber = this.iti\n                    ? this.iti.getNumber()\n                    : document.getElementById(this.phoneInputId).value;\n\n                // Check if phone is already taken BEFORE sending SMS\n                var csrfToken = document.querySelector(\n                    'input[name=\"csrfmiddlewaretoken\"]',\n                );\n                fetch(\"/api/phone/check-available/\", {\n                    method: \"POST\",\n                    headers: {\n                        \"Content-Type\": \"application/json\",\n                        Accept: \"application/json\",\n                        \"X-CSRFToken\": csrfToken ? csrfToken.value : \"\",\n                    },\n                    credentials: \"same-origin\",\n                    body: JSON.stringify({ phone_number: phoneNumber }),\n                })\n                    .then(function (response) {\n                        if (response.status === 429) {\n                            return response\n                                .json()\n                                .catch(function () {\n                                    return null;\n                                })\n                                .then(function (data) {\n                                    self.errorMessage =\n                                        (data && data.error) ||\n                                        gettext(\n                                            \"Too many attempts. Please wait a few minutes before trying again.\",\n                                        );\n                                    return null;\n                                });\n                        }\n                        if (!response.ok) {\n                            // Non-fatal \u2014 proceed; backend will still catch duplicates at verify time\n                            self._doStartVerification(phoneNumber);\n                            return null;\n                        }\n                        return response.json();\n                    })\n                    .then(function (data) {\n                        if (data === null) {\n                            // 429 already handled, or non-fatal non-OK already dispatched above\n                            return;\n                        }\n                        if (!data.available) {\n                            self.errorMessage =\n                                data.error || \"This phone number is already in use\";\n                            return;\n                        }\n                        // Phone is available - open modal and send SMS\n                        self._doStartVerification(phoneNumber);\n                    })\n                    .catch(function () {\n                        // Network failure \u2014 proceed; backend will still catch duplicates\n                        self._doStartVerification(phoneNumber);\n                    });\n            },\n\n            _doStartVerification: function (phoneNumber) {\n                var self = this;\n\n                // Dispatch event to open modal\n                window.dispatchEvent(new CustomEvent(\"open-phone-modal\"));\n\n                // Initialize phone verification\n                if (window.phoneVerification) {\n                    window.phoneVerification\n                        .sendVerificationCode(phoneNumber)\n                        .then(function (result) {\n                            if (result.success) {\n                                self.failureCount = 0;\n                            } else {\n                                self.failureCount =\n                                    window.phoneVerification.getFailureCount();\n                                self.errorMessage = result.error;\n                                // Update modal's failure count and show error there too\n                                var modal = document.querySelector(\n                                    '[x-data=\"phoneVerificationModal\"]',\n                                );\n                                if (\n                                    modal &&\n                                    modal._x_dataStack &&\n                                    modal._x_dataStack[0]\n                                ) {\n                                    var modalData = modal._x_dataStack[0];\n                                    modalData.failureCount = self.failureCount;\n                                    modalData.error = result.error;\n                                    modalData.step = \"code\";\n                                }\n                            }\n                        });\n                }\n            },\n\n            // WhatsApp channel: hand the number to the OTP modal, which owns the\n            // send/verify against the api/phone/whatsapp/ endpoints. Unlike SMS\n            // there is no Firebase pre-flight or reCAPTCHA \u2014 the backend send\n            // already rejects in-use numbers (409), so no check-available call.\n            startWhatsAppVerification: function () {\n                if (this.iti && !this.iti.isValidNumber()) {\n                    this.errorMessage = gettext(\"Please enter a valid phone number\");\n                    return;\n                }\n                var phoneNumber = this.iti\n                    ? this.iti.getNumber()\n                    : document.getElementById(this.phoneInputId).value;\n                this.errorMessage = \"\";\n                window.dispatchEvent(\n                    new CustomEvent(\"open-whatsapp-modal\", { detail: phoneNumber }),\n                );\n            },\n\n            resetVerification: function () {\n                this.verified = false;\n                this.canVerify = false;\n                var phoneInput = document.getElementById(this.phoneInputId);\n                if (phoneInput) {\n                    phoneInput.readOnly = false;\n                    phoneInput.value = \"\";\n                    phoneInput.focus();\n                }\n                // Update parent Alpine state\n                this.$dispatch(\"phone-unverified\");\n            },\n        };\n    });\n\n    // Language switcher dropdown component (desktop navbar)\n    // CSP-compatible component for changing site language\n    Alpine.data(\"languageSwitcher\", function () {\n        return {\n            langOpen: false,\n\n            // Computed getters for CSP compatibility\n            get isOpen() {\n                return this.langOpen;\n            },\n            get isClosed() {\n                return !this.langOpen;\n            },\n            get ariaExpanded() {\n                return this.langOpen ? \"true\" : \"false\";\n            },\n            get chevronClass() {\n                return this.langOpen ? \"rotate-180\" : \"\";\n            },\n\n            toggle: function () {\n                this.langOpen = !this.langOpen;\n            },\n            close: function () {\n                this.langOpen = false;\n            },\n        };\n    });\n\n    // Auto-submit language select on change (mobile version)\n    // Uses event delegation for CSP compliance\n    // Also updates the 'next' URL to use the correct language prefix\n    (function () {\n        document.addEventListener(\"change\", function (event) {\n            if (event.target.classList.contains(\"lang-select-auto-submit\")) {\n                var form = event.target.closest(\"form\");\n                if (form) {\n                    // Update the 'next' hidden input with the correct localized URL\n                    var nextInput = form.querySelector('input[name=\"next\"]');\n                    var currentPath =\n                        form.dataset.currentPath || window.location.pathname;\n                    var selectedLang = event.target.value;\n\n                    if (nextInput && currentPath) {\n                        // Replace language prefix in path (e.g., /en/about/ -> /de/about/)\n                        // Pattern matches /xx/ at the start where xx is a 2-letter language code\n                        var newPath = currentPath.replace(\n                            /^\\/[a-z]{2}\\//,\n                            \"/\" + selectedLang + \"/\",\n                        );\n                        // If path didn't have a language prefix, add one\n                        if (\n                            newPath === currentPath &&\n                            !currentPath.match(/^\\/[a-z]{2}\\//)\n                        ) {\n                            newPath = \"/\" + selectedLang + currentPath;\n                        }\n                        // Preserve query string (e.g., ?section=account)\n                        if (window.location.search) {\n                            newPath = newPath + window.location.search;\n                        }\n                        nextInput.value = newPath;\n                    }\n                    form.submit();\n                }\n            }\n        });\n    })();\n\n    // Phone verification modal component\n    // Used in profile creation for SMS verification with Firebase\n    // Reads phone input ID from data attribute: data-phone-input-id\n    Alpine.data(\"phoneVerificationModal\", function () {\n        return {\n            isOpen: false,\n            step: \"sending\", // sending, code, verifying, success, fallback\n            // \"sms\" (Firebase) or \"whatsapp\" (Meta). Drives which send/verify\n            // path verifyCode()/resendCode() take and the channel UI hints.\n            channel: \"sms\",\n            // Full E.164 number \u2014 WhatsApp send/verify has no Firebase\n            // confirmationResult to carry it, so the modal keeps it for resend.\n            phoneNumber: \"\",\n            // CSP-compatible: individual properties instead of array (array index access requires eval)\n            otp0: \"\",\n            otp1: \"\",\n            otp2: \"\",\n            otp3: \"\",\n            otp4: \"\",\n            otp5: \"\",\n            error: \"\",\n            maskedPhone: \"\",\n            resendCountdown: 60,\n            resendTimer: null,\n            failureCount: 0,\n            phoneAlreadyInUse: false,\n            dialogElement: null,\n            opener: null,\n            previousBodyOverflow: \"\",\n            usesNativeDialog: false,\n            isNativeModal: false,\n            hiddenBackground: [],\n            openSmsHandler: null,\n            openWhatsAppHandler: null,\n            fallbackKeyHandler: null,\n            fallbackFocusHandler: null,\n            flowGeneration: 0,\n            successTimer: null,\n\n            // Computed getters for CSP compatibility\n            get showPhoneInUseError() {\n                return this.phoneAlreadyInUse;\n            },\n            get isWhatsAppChannel() {\n                return this.channel === \"whatsapp\";\n            },\n            get isSendingStep() {\n                return this.step === \"sending\";\n            },\n            get isAwaitingSms() {\n                return this.channel === \"sms\" && this.step === \"sending\";\n            },\n            get modalAriaState() {\n                return this.isAwaitingSms ? \"false\" : \"true\";\n            },\n            get isCodeStep() {\n                return this.step === \"code\";\n            },\n            get isFallbackStep() {\n                return this.step === \"fallback\";\n            },\n            get canFallbackToSms() {\n                // SMS won't help when the number is already on another account.\n                return !this.phoneAlreadyInUse;\n            },\n            get isVerifyingStep() {\n                return this.step === \"verifying\";\n            },\n            get isSuccessStep() {\n                return this.step === \"success\";\n            },\n            get canResend() {\n                return this.resendCountdown === 0;\n            },\n            get cannotResend() {\n                return this.resendCountdown > 0;\n            },\n            get hasError() {\n                return Boolean(this.error);\n            },\n            get showSupportContact() {\n                return this.failureCount >= 2;\n            },\n            // CSP-compatible: getter combines individual OTP fields\n            get otpCode() {\n                return (\n                    this.otp0 +\n                    this.otp1 +\n                    this.otp2 +\n                    this.otp3 +\n                    this.otp4 +\n                    this.otp5\n                );\n            },\n            get isCodeComplete() {\n                return this.otpCode.length === 6;\n            },\n            get isCodeIncomplete() {\n                return this.otpCode.length !== 6;\n            },\n\n            // CSP-compatible: combined update + navigation handlers (no $event in template)\n            // Each method updates its field and handles focus navigation\n            handleOtp0Input: function () {\n                var el = this.$refs.otp0;\n                this.otp0 = el.value;\n                if (el.value.length === 1) {\n                    this.$refs.otp1.focus();\n                }\n            },\n            handleOtp1Input: function () {\n                var el = this.$refs.otp1;\n                this.otp1 = el.value;\n                if (el.value.length === 1) {\n                    this.$refs.otp2.focus();\n                }\n            },\n            handleOtp2Input: function () {\n                var el = this.$refs.otp2;\n                this.otp2 = el.value;\n                if (el.value.length === 1) {\n                    this.$refs.otp3.focus();\n                }\n            },\n            handleOtp3Input: function () {\n                var el = this.$refs.otp3;\n                this.otp3 = el.value;\n                if (el.value.length === 1) {\n                    this.$refs.otp4.focus();\n                }\n            },\n            handleOtp4Input: function () {\n                var el = this.$refs.otp4;\n                this.otp4 = el.value;\n                if (el.value.length === 1) {\n                    this.$refs.otp5.focus();\n                }\n            },\n            handleOtp5Input: function () {\n                var el = this.$refs.otp5;\n                this.otp5 = el.value;\n                if (el.value.length === 1) {\n                    this.verifyCode();\n                }\n            },\n\n            init: function () {\n                var self = this;\n                this.dialogElement = this.$el;\n                this.usesNativeDialog = typeof this.dialogElement.showModal === \"function\";\n                this.$watch(\"step\", function () {\n                    self.$nextTick(function () {\n                        self.syncDialog();\n                        self.focusCurrentStep();\n                    });\n                });\n                this.openSmsHandler = function () {\n                    self.channel = \"sms\";\n                    self.open();\n                };\n                window.addEventListener(\"open-phone-modal\", this.openSmsHandler);\n\n                // WhatsApp: open the modal and immediately kick off the send.\n                this.openWhatsAppHandler = function (e) {\n                    self.channel = \"whatsapp\";\n                    self.open();\n                    self.sendWhatsApp(e.detail);\n                };\n                window.addEventListener(\"open-whatsapp-modal\", this.openWhatsAppHandler);\n\n                // Older WebViews lack showModal(). Keep their overlay modal for\n                // keyboard and assistive technology users as well.\n                this.fallbackKeyHandler = function (e) {\n                    if (!self.isOpen) return;\n                    if (e.key === \"Escape\" && !self.isNativeModal) {\n                        e.preventDefault();\n                        self.close();\n                    } else if (e.key === \"Tab\" && !self.isAwaitingSms) {\n                        self.trapTab(e);\n                    }\n                };\n                this.fallbackFocusHandler = function (e) {\n                    if (self.isOpen && !self.isNativeModal && !self.isAwaitingSms &&\n                        !self.dialogElement.contains(e.target)) {\n                        self.focusCurrentStep();\n                    }\n                };\n                document.addEventListener(\"keydown\", this.fallbackKeyHandler);\n                document.addEventListener(\"focusin\", this.fallbackFocusHandler);\n            },\n\n            destroy: function () {\n                this.close();\n                window.removeEventListener(\"open-phone-modal\", this.openSmsHandler);\n                window.removeEventListener(\"open-whatsapp-modal\", this.openWhatsAppHandler);\n                document.removeEventListener(\"keydown\", this.fallbackKeyHandler);\n                document.removeEventListener(\"focusin\", this.fallbackFocusHandler);\n            },\n\n            focusCurrentStep: function (retried) {\n                if (!this.isOpen || !this.dialogElement.hasAttribute(\"open\")) return;\n                var target = this.$refs.closeButton;\n                if (this.step === \"code\") {\n                    target = this.$refs.otp0;\n                } else if (this.step === \"fallback\" && this.canFallbackToSms) {\n                    target = this.$refs.smsFallback;\n                } else if (this.step === \"success\") {\n                    target = this.$refs.successTitle;\n                }\n                if (target && target.getClientRects().length) {\n                    target.focus();\n                } else if (target && !retried) {\n                    // A fast response can change sending/verifying to the next\n                    // step before Alpine has displayed that step's controls.\n                    var self = this;\n                    var step = this.step;\n                    requestAnimationFrame(function () {\n                        if (self.step === step) self.focusCurrentStep(true);\n                    });\n                }\n            },\n\n            syncDialog: function () {\n                if (!this.isOpen) return;\n                // Firebase's invisible reCAPTCHA can require an interactive\n                // challenge outside this partial. A native modal would make its\n                // iframe inert and cover it even at Google's high z-index.\n                if (this.isAwaitingSms) {\n                    if (this.isNativeModal) this.dialogElement.close();\n                    this.isNativeModal = false;\n                    this.restoreFallbackBackground();\n                    this.dialogElement.setAttribute(\"open\", \"\");\n                } else if (this.usesNativeDialog && !this.isNativeModal) {\n                    this.restoreFallbackBackground();\n                    this.dialogElement.removeAttribute(\"open\");\n                    this.dialogElement.showModal();\n                    this.isNativeModal = true;\n                } else if (!this.usesNativeDialog && !this.dialogElement.hasAttribute(\"open\")) {\n                    this.dialogElement.setAttribute(\"open\", \"\");\n                }\n                if (!this.usesNativeDialog && !this.isAwaitingSms &&\n                    !this.hiddenBackground.length) {\n                    this.focusCurrentStep();\n                    this.hideFallbackBackground();\n                }\n            },\n\n            trapTab: function (event) {\n                var controls = Array.from(this.dialogElement.querySelectorAll(\n                    'button, a[href], input, select, textarea, [tabindex]',\n                )).filter(function (element) {\n                    return !element.disabled && element.tabIndex >= 0 &&\n                        element.getClientRects().length;\n                });\n                var first = controls[0];\n                var last = controls[controls.length - 1];\n                if (!first) {\n                    event.preventDefault();\n                    this.focusCurrentStep();\n                } else if (controls.indexOf(document.activeElement) === -1) {\n                    event.preventDefault();\n                    (event.shiftKey ? last : first).focus();\n                } else if (event.shiftKey && document.activeElement === first) {\n                    event.preventDefault();\n                    last.focus();\n                } else if (!event.shiftKey && document.activeElement === last) {\n                    event.preventDefault();\n                    first.focus();\n                }\n            },\n\n            hideFallbackBackground: function () {\n                var branch = this.dialogElement;\n                // The partial can be nested inside a page section. Hide siblings\n                // at each level without hiding an ancestor of the dialog itself.\n                while (branch.parentElement) {\n                    var siblings = branch.parentElement.children;\n                    for (var i = 0; i < siblings.length; i++) {\n                        var sibling = siblings[i];\n                        if (sibling === branch) continue;\n                        this.hiddenBackground.push({\n                            element: sibling,\n                            inert: sibling.getAttribute(\"inert\"),\n                            ariaHidden: sibling.getAttribute(\"aria-hidden\"),\n                        });\n                        sibling.setAttribute(\"inert\", \"\");\n                        sibling.setAttribute(\"aria-hidden\", \"true\");\n                    }\n                    branch = branch.parentElement;\n                    if (branch === document.body) break;\n                }\n            },\n\n            restoreFallbackBackground: function () {\n                this.hiddenBackground.forEach(function (entry) {\n                    if (entry.inert === null) entry.element.removeAttribute(\"inert\");\n                    else entry.element.setAttribute(\"inert\", entry.inert);\n                    if (entry.ariaHidden === null) {\n                        entry.element.removeAttribute(\"aria-hidden\");\n                    } else {\n                        entry.element.setAttribute(\"aria-hidden\", entry.ariaHidden);\n                    }\n                });\n                this.hiddenBackground = [];\n            },\n\n            restoreOpenerFocus: function () {\n                var candidates = [\n                    this.opener,\n                    document.querySelector('[x-data=\"onboardingPhoneContinue\"] a'),\n                ].concat(Array.from(document.querySelectorAll(\n                    '[x-data=\"phoneVerificationComponent\"] input[type=\"tel\"]',\n                )));\n                for (var i = 0; i < candidates.length; i++) {\n                    var candidate = candidates[i];\n                    if (candidate && candidate.isConnected && !candidate.disabled &&\n                        candidate.getClientRects().length && !candidate.closest(\"[inert]\")) {\n                        candidate.focus();\n                        break;\n                    }\n                }\n                this.opener = null;\n            },\n\n            open: function () {\n                // A repeated event must not replace the real opener with a\n                // digit/close control that already has focus inside the dialog.\n                if (!this.isOpen) {\n                    this.opener = document.activeElement;\n                    this.previousBodyOverflow = document.body.style.overflow;\n                }\n                this.flowGeneration++;\n                clearTimeout(this.successTimer);\n                this.successTimer = null;\n                // Clear any existing timer before opening (prevents memory leak from reopening)\n                if (this.resendTimer) {\n                    clearInterval(this.resendTimer);\n                    this.resendTimer = null;\n                }\n                this.isOpen = true;\n                this.step = \"sending\";\n                this.error = \"\";\n                this.phoneAlreadyInUse = false;\n                // CSP-compatible: reset individual OTP fields\n                this.otp0 = \"\";\n                this.otp1 = \"\";\n                this.otp2 = \"\";\n                this.otp3 = \"\";\n                this.otp4 = \"\";\n                this.otp5 = \"\";\n                this.resendCountdown = 0;\n\n                // Prevent body scroll when modal is open\n                document.body.style.overflow = \"hidden\";\n                var self = this;\n                this.$nextTick(function () {\n                    if (!self.isOpen) return;\n                    self.syncDialog();\n                    self.focusCurrentStep();\n                });\n            },\n\n            close: function () {\n                if (!this.isOpen) return;\n                this.isOpen = false;\n                this.flowGeneration++;\n                clearTimeout(this.successTimer);\n                this.successTimer = null;\n                // Clear timer on close\n                if (this.resendTimer) {\n                    clearInterval(this.resendTimer);\n                    this.resendTimer = null;\n                }\n\n                if (this.isNativeModal && this.dialogElement.open) this.dialogElement.close();\n                else this.dialogElement.removeAttribute(\"open\");\n                this.isNativeModal = false;\n                this.restoreFallbackBackground();\n                document.body.style.overflow = this.previousBodyOverflow;\n                this.restoreOpenerFocus();\n            },\n\n            onDialogClose: function () {\n                if (this.isOpen && !this.dialogElement.open) this.close();\n            },\n\n            finishVerification: function (phone, generation) {\n                // Verification still updates the page when the user cancelled\n                // while it was in flight, but must not take over a newer dialog.\n                window.dispatchEvent(new CustomEvent(\"phone-verified\", { detail: phone }));\n                if (!this.isOpen || generation !== this.flowGeneration) return;\n                this.step = \"success\";\n                var self = this;\n                this.successTimer = setTimeout(function () {\n                    if (generation === self.flowGeneration) self.close();\n                }, 2000);\n            },\n\n            showCodeStep: function (phone, channel) {\n                // The public one-argument callback belongs to Firebase SMS.\n                // Ignore it after cancel or after switching to WhatsApp.\n                if (!this.isOpen || this.channel !== (channel || \"sms\")) return;\n                this.step = \"code\";\n                this.maskedPhone = phone.slice(0, 7) + \"***\" + phone.slice(-2);\n                this.startResendTimer();\n            },\n\n            // CSP-compatible: get index from data-index attribute on element\n            handleOtpInput: function (event) {\n                var el = event.target || this.dialogElement;\n                var index = parseInt(el.dataset.index, 10);\n                var value = el.value;\n                if (value.length === 1 && index < 5) {\n                    var nextRef = this.$refs[\"otp\" + (index + 1)];\n                    if (nextRef) nextRef.focus();\n                }\n                if (index === 5 && value.length === 1) {\n                    this.verifyCode();\n                }\n            },\n\n            // CSP-compatible: get index from data-index attribute on element\n            handleOtpBackspace: function (event) {\n                var el = event.target || this.dialogElement;\n                var index = parseInt(el.dataset.index, 10);\n                if (!el.value && index > 0) {\n                    var prevRef = this.$refs[\"otp\" + (index - 1)];\n                    if (prevRef) prevRef.focus();\n                }\n            },\n\n            // CSP-compatible: no parameter needed\n            handleOtpPaste: function (event) {\n                event.preventDefault();\n                var paste = (event.clipboardData || window.clipboardData).getData(\n                    \"text\",\n                );\n                var digits = paste.replace(/\\D/g, \"\").slice(0, 6).split(\"\");\n                // CSP-compatible: set individual OTP fields\n                this.otp0 = digits[0] || \"\";\n                this.otp1 = digits[1] || \"\";\n                this.otp2 = digits[2] || \"\";\n                this.otp3 = digits[3] || \"\";\n                this.otp4 = digits[4] || \"\";\n                this.otp5 = digits[5] || \"\";\n                if (digits.length === 6) this.verifyCode();\n            },\n\n            verifyCode: function () {\n                var self = this;\n                var generation = this.flowGeneration;\n                var code = this.otpCode;\n                if (code.length !== 6) {\n                    this.error = gettext(\"Please enter the 6-digit code\");\n                    return;\n                }\n\n                this.step = \"verifying\";\n                this.error = \"\";\n\n                if (this.channel === \"whatsapp\") {\n                    this.verifyWhatsApp(code);\n                    return;\n                }\n\n                if (window.phoneVerification) {\n                    window.phoneVerification.verifyCode(code).then(function (result) {\n                        if (result.success) {\n                            self.finishVerification(result.phone_number, generation);\n                            return;\n                        }\n                        if (!self.isOpen || generation !== self.flowGeneration) return;\n                        if (result.error_code === \"phone_already_in_use\") {\n                            self.phoneAlreadyInUse = true;\n                            self.step = \"code\";\n                            self.error = result.error;\n                        } else {\n                            self.step = \"code\";\n                            self.error = result.error;\n                            // CSP-compatible: reset individual OTP fields\n                            self.otp0 = \"\";\n                            self.otp1 = \"\";\n                            self.otp2 = \"\";\n                            self.otp3 = \"\";\n                            self.otp4 = \"\";\n                            self.otp5 = \"\";\n                        }\n                    });\n                }\n            },\n\n            resendCode: function () {\n                var self = this;\n                var generation = this.flowGeneration;\n                this.step = \"sending\";\n                this.error = \"\";\n                if (this.channel === \"whatsapp\") {\n                    // sendWhatsApp() drives showCodeStep()/fallback on its own.\n                    this.sendWhatsApp(this.phoneNumber);\n                    return;\n                }\n                if (window.phoneVerification) {\n                    window.phoneVerification\n                        .sendVerificationCode()\n                        .then(function (result) {\n                            if (!self.isOpen || generation !== self.flowGeneration) return;\n                            if (result.success) {\n                                self.failureCount = 0;\n                                self.step = \"code\";\n                                self.startResendTimer();\n                            } else {\n                                self.failureCount =\n                                    window.phoneVerification.getFailureCount();\n                                self.error =\n                                    result.error || gettext(\"Failed to resend code\");\n                                self.step = \"code\";\n                            }\n                        });\n                }\n            },\n\n            // \u2500\u2500 WhatsApp OTP channel (Meta delivery, server-side verify) \u2500\u2500\u2500\u2500\u2500\u2500\n            getCsrfToken: function () {\n                // The SMS instance already carries the freshest token (it gets\n                // rotated server-side on POST); fall back to a form input, then\n                // the cookie, so the modal works on pages without a <form>.\n                if (window.phoneVerification && window.phoneVerification.csrfToken) {\n                    return window.phoneVerification.csrfToken;\n                }\n                var input = document.querySelector(\n                    'input[name=\"csrfmiddlewaretoken\"]',\n                );\n                if (input) {\n                    return input.value;\n                }\n                var match = document.cookie.match(/csrftoken=([^;]+)/);\n                return match ? match[1] : \"\";\n            },\n\n            updateCsrf: function (token) {\n                if (!token) return;\n                if (window.phoneVerification) {\n                    window.phoneVerification.csrfToken = token;\n                }\n                var inputs = document.querySelectorAll(\n                    'input[name=\"csrfmiddlewaretoken\"]',\n                );\n                for (var i = 0; i < inputs.length; i++) {\n                    inputs[i].value = token;\n                }\n            },\n\n            clearOtp: function () {\n                this.otp0 = \"\";\n                this.otp1 = \"\";\n                this.otp2 = \"\";\n                this.otp3 = \"\";\n                this.otp4 = \"\";\n                this.otp5 = \"\";\n            },\n\n            sendWhatsApp: function (phone) {\n                var self = this;\n                var generation = this.flowGeneration;\n                this.phoneNumber = phone;\n                this.step = \"sending\";\n                this.error = \"\";\n                this.phoneAlreadyInUse = false;\n                fetch(\"/api/phone/whatsapp/send/\", {\n                    method: \"POST\",\n                    headers: {\n                        \"Content-Type\": \"application/json\",\n                        Accept: \"application/json\",\n                        \"X-CSRFToken\": this.getCsrfToken(),\n                    },\n                    credentials: \"same-origin\",\n                    body: JSON.stringify({ phone_number: phone }),\n                })\n                    .then(function (response) {\n                        return response.json().catch(function () {\n                            return {};\n                        });\n                    })\n                    .then(function (data) {\n                        if (data.success && data.already_verified) {\n                            self.finishVerification(data.phone_number, generation);\n                            return;\n                        }\n                        if (!self.isOpen || generation !== self.flowGeneration) return;\n                        if (data.success) {\n                            self.clearOtp();\n                            self.showCodeStep(phone, \"whatsapp\");\n                            return;\n                        }\n                        // Failed send: route everything to the fallback step.\n                        // not_on_whatsapp / transient errors offer SMS; an\n                        // in-use number doesn't (SMS would fail the same way).\n                        self.phoneAlreadyInUse =\n                            data.error_code === \"phone_already_in_use\";\n                        self.error =\n                            data.error ||\n                            gettext(\n                                \"Could not send the WhatsApp code. Please try again.\",\n                            );\n                        self.step = \"fallback\";\n                    })\n                    .catch(function () {\n                        if (!self.isOpen || generation !== self.flowGeneration) return;\n                        self.error = gettext(\n                            \"Could not send the WhatsApp code. Please try again.\",\n                        );\n                        self.step = \"fallback\";\n                    });\n            },\n\n            verifyWhatsApp: function (code) {\n                var self = this;\n                var generation = this.flowGeneration;\n                fetch(\"/api/phone/whatsapp/verify/\", {\n                    method: \"POST\",\n                    headers: {\n                        \"Content-Type\": \"application/json\",\n                        Accept: \"application/json\",\n                        \"X-CSRFToken\": this.getCsrfToken(),\n                    },\n                    credentials: \"same-origin\",\n                    body: JSON.stringify({ code: code }),\n                })\n                    .then(function (response) {\n                        return response.json().catch(function () {\n                            return {};\n                        });\n                    })\n                    .then(function (data) {\n                        self.updateCsrf(data.csrfToken);\n                        if (data.success && data.phone_verified) {\n                            self.finishVerification(data.phone_number, generation);\n                            return;\n                        }\n                        if (!self.isOpen || generation !== self.flowGeneration) return;\n                        if (data.error_code === \"phone_already_in_use\") {\n                            self.phoneAlreadyInUse = true;\n                            self.step = \"code\";\n                            self.error = data.error;\n                        } else {\n                            self.step = \"code\";\n                            self.error =\n                                data.error ||\n                                gettext(\"Incorrect code. Please try again.\");\n                            self.clearOtp();\n                        }\n                    })\n                    .catch(function () {\n                        if (!self.isOpen || generation !== self.flowGeneration) return;\n                        self.step = \"code\";\n                        self.error = gettext(\"Verification failed. Please try again.\");\n                        self.clearOtp();\n                    });\n            },\n\n            switchToSms: function () {\n                // Hand off to the SMS flow; the component re-opens this modal in\n                // \"sms\" mode (resetting channel/step) and starts the Firebase send.\n                window.dispatchEvent(\n                    new CustomEvent(\"start-sms-verification\", {\n                        detail: this.phoneNumber,\n                    }),\n                );\n            },\n\n            startResendTimer: function () {\n                var self = this;\n                this.resendCountdown = 60;\n                if (this.resendTimer) clearInterval(this.resendTimer);\n                this.resendTimer = setInterval(function () {\n                    self.resendCountdown--;\n                    if (self.resendCountdown <= 0) {\n                        clearInterval(self.resendTimer);\n                    }\n                }, 1000);\n            },\n        };\n    });\n\n    // PWA Install Button component for membership page\n    // CSP-compatible with computed getters\n    Alpine.data(\"pwaInstallButton\", function () {\n        return {\n            deferredPrompt: null,\n            canInstall: false,\n            isInstalled: false,\n            showInstructions: false,\n            showFallback: false,\n            instructions: \"\",\n\n            // Computed getters for CSP compatibility\n            get isInstalledVisible() {\n                return this.isInstalled;\n            },\n            get showFallbackVisible() {\n                return this.showFallback;\n            },\n            // The install offer is the \"install\" prompt (STYLE.md \u00A78): queued\n            // behind the cookie sheet and flash messages, and one at a time\n            // with the push prompt. base.html leaves the global install\n            // banner out on the dashboard, so this card is the only offer.\n            get installOffered() {\n                return this.canInstall || this.showInstructions;\n            },\n            get installQueued() {\n                return Alpine.store(\"prompts\").isActive(\"install\");\n            },\n            get canInstallVisible() {\n                return this.canInstall && this.installQueued;\n            },\n            get showInstructionsVisible() {\n                return this.showInstructions && this.installQueued;\n            },\n            // Whether the dashboard card has anything worth showing at all.\n            // Deliberately excludes showFallback: that state (desktop\n            // Firefox and other non-Chromium browsers with no install path)\n            // used to render an \"Open crush.lu in Chrome or Safari\" card\n            // after a 2s timer \u2014 a card that was blank until then and only\n            // ever offered advice nobody there could act on. The card just\n            // doesn't render for that visitor now; the sibling Membership\n            // card expands to the full row instead, through\n            // membershipSpanClass below.\n            get cardVisible() {\n                return this.isInstalled || (this.installOffered && this.installQueued);\n            },\n            // Bound as a bare name on the Membership card: the CSP build\n            // can't evaluate an object literal with an inline negation.\n            get membershipSpanClass() {\n                return this.cardVisible ? \"\" : \"md:col-span-2\";\n            },\n\n            init: function () {\n                var self = this;\n                this.$watch(\"installOffered\", function (on) {\n                    Alpine.store(\"prompts\").set(\"install\", on);\n                });\n\n                if (document.documentElement.hasAttribute(\"data-native-app\")) return;\n\n                // Check if already installed (standalone mode)\n                if (\n                    window.matchMedia(\"(display-mode: standalone)\").matches ||\n                    window.navigator.standalone === true\n                ) {\n                    self.isInstalled = true;\n                    return;\n                }\n\n                // Check CrushPWA if available\n                if (window.CrushPWA && window.CrushPWA.isStandalone) {\n                    self.isInstalled = true;\n                    return;\n                }\n\n                window.addEventListener(\"pwa-hide-install\", function () {\n                    self.canInstall = false;\n                    self.showInstructions = false;\n                    self.showFallback = false;\n                    self.deferredPrompt = null;\n                    Alpine.store(\"prompts\").set(\"install\", false);\n                });\n\n                // Installed status remains visible above; only the offer and\n                // iOS instructions obey the shared 30-day cooldown.\n                if (Alpine.store(\"prompts\").installCooldownActive()) return;\n\n                // iOS-specific instructions (no beforeinstallprompt on iOS).\n                // iPadOS Safari reports a desktop \"Macintosh\" UA, so detect it\n                // the way pwa-install.js does (MacIntel + touch points).\n                var isIOS =\n                    (/iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream) ||\n                    (navigator.platform === \"MacIntel\" && navigator.maxTouchPoints > 1);\n                if (isIOS) {\n                    self.showInstructions = true;\n                    self.instructions = 'Tap Share, then \"Add to Home Screen\"';\n                    return;\n                }\n\n                // Listen for beforeinstallprompt event (Chrome, Edge, Samsung)\n                window.addEventListener(\"beforeinstallprompt\", function (e) {\n                    e.preventDefault();\n                    if (Alpine.store(\"prompts\").installCooldownActive()) return;\n                    self.deferredPrompt = e;\n                    self.canInstall = true;\n                    self.showFallback = false;\n                });\n\n                // Listen for appinstalled event\n                window.addEventListener(\"appinstalled\", function () {\n                    self.isInstalled = true;\n                    self.canInstall = false;\n                    self.showFallback = false;\n                    self.deferredPrompt = null;\n                });\n\n                // Show fallback after 2s if no beforeinstallprompt fires\n                // (covers desktop Firefox, non-Chromium browsers)\n                setTimeout(function () {\n                    if (\n                        !self.canInstall &&\n                        !self.isInstalled &&\n                        !self.showInstructions\n                    ) {\n                        self.showFallback = true;\n                    }\n                }, 2000);\n            },\n\n            install: function () {\n                var self = this;\n                if (!self.deferredPrompt) return;\n\n                self.deferredPrompt.prompt();\n                self.deferredPrompt.userChoice.then(function (result) {\n                    if (result.outcome === \"accepted\") {\n                        self.isInstalled = true;\n                    } else if (result.outcome === \"dismissed\") {\n                        Alpine.store(\"prompts\").dismissInstall();\n                        window.dispatchEvent(new CustomEvent(\"pwa-hide-install\"));\n                    }\n                    // A used prompt can't be shown again, so a dismissed\n                    // one must not leave a dead Install button behind. The\n                    // installOffered watcher then frees the prompts queue.\n                    self.canInstall = false;\n                    self.deferredPrompt = null;\n                });\n            },\n        };\n    });\n\n    // PWA Install Banner component\n    // Listens for custom events from pwa-install.js\n    Alpine.data(\"pwaInstallBanner\", function () {\n        return {\n            show: false,\n            platform: \"other\",\n            showGuide: false,\n            guideStep: 1,\n\n            get isIos() {\n                return this.platform === \"ios\";\n            },\n\n            // Queued behind the cookie sheet and flash messages.\n            get visible() {\n                return this.show && Alpine.store(\"prompts\").isActive(\"install\");\n            },\n\n            get isStepOne() {\n                return this.guideStep === 1;\n            },\n\n            get isStepTwo() {\n                return this.guideStep === 2;\n            },\n\n            get isStepThree() {\n                return this.guideStep === 3;\n            },\n\n            get isFirstStep() {\n                return this.guideStep === 1;\n            },\n\n            get isLastStep() {\n                return this.guideStep === 3;\n            },\n\n            get hasPrevStep() {\n                return this.guideStep > 1;\n            },\n\n            get hasNextStep() {\n                return this.guideStep < 3;\n            },\n\n            get stepTwoIndicatorClass() {\n                return this.guideStep >= 2\n                    ? \"bg-purple-500 w-8\"\n                    : \"bg-gray-200 dark:bg-slate-600 w-4\";\n            },\n\n            get stepThreeIndicatorClass() {\n                return this.guideStep >= 3\n                    ? \"bg-purple-500 w-8\"\n                    : \"bg-gray-200 dark:bg-slate-600 w-4\";\n            },\n\n            init: function () {\n                var self = this;\n                // Listen for show/hide events from pwa-install.js\n                this.$watch(\"show\", function (on) {\n                    Alpine.store(\"prompts\").set(\"install\", on);\n                });\n                window.addEventListener(\"pwa-show-install\", function (e) {\n                    self.show = true;\n                    if (e.detail && e.detail.platform) {\n                        self.platform = e.detail.platform;\n                    }\n                });\n                window.addEventListener(\"pwa-hide-install\", function () {\n                    self.show = false;\n                });\n                // iOS guide modal\n                window.addEventListener(\"pwa-show-ios-guide\", function () {\n                    self.showGuide = true;\n                    self.guideStep = 1;\n                });\n            },\n\n            dismiss: function () {\n                this.show = false;\n                // Trigger the dismiss handler in pwa-install.js\n                window.dispatchEvent(new CustomEvent(\"pwa-dismiss-install\"));\n            },\n\n            openGuide: function () {\n                this.showGuide = true;\n                this.guideStep = 1;\n            },\n\n            closeGuide: function () {\n                this.showGuide = false;\n                this.guideStep = 1;\n            },\n\n            nextStep: function () {\n                if (this.guideStep < 3) {\n                    this.guideStep++;\n                }\n            },\n\n            prevStep: function () {\n                if (this.guideStep > 1) {\n                    this.guideStep--;\n                }\n            },\n        };\n    });\n\n    // PWA Success Toast component\n    // Shows success message after PWA install, auto-hides after 5 seconds\n    Alpine.data(\"pwaSuccessToast\", function () {\n        return {\n            show: false,\n\n            init: function () {\n                var self = this;\n                window.addEventListener(\"pwa-install-success\", function () {\n                    self.showToast();\n                });\n            },\n\n            showToast: function () {\n                var self = this;\n                this.show = true;\n                // Auto-hide after 5 seconds\n                setTimeout(function () {\n                    self.show = false;\n                }, 5000);\n            },\n\n            close: function () {\n                this.show = false;\n            },\n        };\n    });\n\n    // Event Registration Form - Dynamic form behavior with contextual questions\n    Alpine.data(\"eventRegistration\", function () {\n        return {\n            // State\n            bringingGuest: false,\n            isSubmitting: false,\n\n            // CSP-safe getters for button text visibility\n            get showSubmitText() {\n                return !this.isSubmitting;\n            },\n\n            get showProcessingText() {\n                return this.isSubmitting;\n            },\n\n            // Actions\n            toggleGuest: function () {\n                this.bringingGuest = !this.bringingGuest;\n            },\n\n            handleSubmit: function (event) {\n                this.isSubmitting = true;\n                // Let native form submission or HTMX continue\n                if (!event.target.getAttribute(\"hx-post\")) {\n                    event.target.submit();\n                }\n            },\n        };\n    });\n\n    /**\n     * Theme Toggle Component\n     *\n     * Provides dark mode toggle with system preference detection.\n     * Integrates with window.themeManager (from theme-manager.js).\n     *\n     * Features:\n     * - Automatic system preference detection\n     * - Manual theme toggle\n     * - localStorage persistence\n     * - Smooth transitions\n     */\n    // [data-label-<key>, English fallback]; keys match theme_toggle_labels.html.\n    var THEME_TOGGLE_LABELS = [\n        [\"system-dark\", \"System (Dark)\"],\n        [\"system-light\", \"System (Light)\"],\n        [\"dark-mode\", \"Dark Mode\"],\n        [\"light-mode\", \"Light Mode\"],\n        [\"switch-light-mode\", \"Switch to light mode\"],\n        [\"switch-dark-mode\", \"Switch to dark mode\"],\n        [\"dark\", \"Dark\"],\n        [\"light\", \"Light\"],\n        [\"switch-light\", \"Switch to Light\"],\n        [\"switch-dark\", \"Switch to Dark\"],\n    ];\n\n    function makeThemeToggle() {\n        return {\n            currentTheme: \"light\",\n            // \"light\" | \"dark\" | \"system\" (no manual choice saved)\n            preference: \"system\",\n            systemPreference: \"light\",\n            // Journey / gift pages are always dark (data-theme-lock on <html>):\n            // the toggle is disabled and says why, in the page language.\n            lockedLabel: \"\",\n\n            // Status / aria copy, rendered with {% trans %} as data-label-*\n            // attributes by components/theme_toggle_labels.html (English\n            // fallback where a mount does not include it).\n            labels: {},\n\n            init: function () {\n                this.lockedLabel = this.$el.getAttribute(\"data-locked-label\") || \"\";\n                var el = this.$el;\n                var labels = {};\n                THEME_TOGGLE_LABELS.forEach(function (pair) {\n                    labels[pair[0]] = el.getAttribute(\"data-label-\" + pair[0]) || pair[1];\n                });\n                this.labels = labels;\n                // Initialize from themeManager, and stay in step when another\n                // toggle (navbar / drawer) or the OS changes the theme.\n                this._syncFromManager();\n                window.addEventListener(\n                    \"crush:themechange\",\n                    this._syncFromManager.bind(this),\n                );\n\n                // Detect system preference\n                if (window.matchMedia) {\n                    this.systemPreference = window.matchMedia(\n                        \"(prefers-color-scheme: dark)\",\n                    ).matches\n                        ? \"dark\"\n                        : \"light\";\n\n                    // Listen for system preference changes\n                    var mediaQuery = window.matchMedia(\"(prefers-color-scheme: dark)\");\n                    var self = this;\n\n                    var handleChange = function (e) {\n                        self.systemPreference = e.matches ? \"dark\" : \"light\";\n                    };\n\n                    // Modern browsers\n                    if (mediaQuery.addEventListener) {\n                        mediaQuery.addEventListener(\"change\", handleChange);\n                    }\n                    // Legacy browsers\n                    else if (mediaQuery.addListener) {\n                        mediaQuery.addListener(handleChange);\n                    }\n                }\n            },\n\n            // Getters for CSP compliance (no inline expressions in templates)\n            get isLocked() {\n                return document.documentElement.hasAttribute(\"data-theme-lock\");\n            },\n\n            get lockedTitle() {\n                return this.isLocked ? this.lockedLabel : null;\n            },\n\n            get isDark() {\n                return this.currentTheme === \"dark\";\n            },\n\n            get isLight() {\n                return this.currentTheme === \"light\";\n            },\n\n            get isSystemDark() {\n                return this.systemPreference === \"dark\";\n            },\n\n            get toggleButtonClass() {\n                if (this.isLocked) {\n                    return \"bg-gray-700 text-yellow-400 cursor-not-allowed\";\n                }\n                return this.isDark\n                    ? \"bg-gray-700 text-yellow-400 hover:bg-gray-600\"\n                    : \"bg-gray-100 text-gray-700 hover:bg-gray-200\";\n            },\n\n            get sunIconClass() {\n                return this.isLight ? \"opacity-100\" : \"opacity-40\";\n            },\n\n            get moonIconClass() {\n                return this.isDark ? \"opacity-100\" : \"opacity-40\";\n            },\n\n            get statusText() {\n                if (this.isLocked) {\n                    return this.lockedLabel;\n                }\n                // Read the reactive preference (kept in step by\n                // crush:themechange), not localStorage, which Alpine cannot track.\n                if (this.preference === \"system\") {\n                    return this.isSystemDark\n                        ? this.labels[\"system-dark\"]\n                        : this.labels[\"system-light\"];\n                }\n                return this.isDark ? this.labels[\"dark-mode\"] : this.labels[\"light-mode\"];\n            },\n\n            get ariaLabel() {\n                if (this.isLocked) {\n                    return this.lockedLabel;\n                }\n                return this.isDark\n                    ? this.labels[\"switch-light-mode\"]\n                    : this.labels[\"switch-dark-mode\"];\n            },\n\n            get themeLabel() {\n                if (this.preference === \"system\") {\n                    return this.isSystemDark\n                        ? this.labels[\"system-dark\"]\n                        : this.labels[\"system-light\"];\n                }\n                return this.isDark ? this.labels.dark : this.labels.light;\n            },\n\n            get themeButtonLabel() {\n                return this.isDark\n                    ? this.labels[\"switch-light\"]\n                    : this.labels[\"switch-dark\"];\n            },\n\n            // Methods\n            cycleTheme: function () {\n                this.toggleTheme();\n            },\n\n            toggleTheme: function () {\n                if (this.isLocked) {\n                    return;\n                }\n                if (window.themeManager) {\n                    window.themeManager.toggleTheme();\n                    this.currentTheme = window.themeManager.getTheme();\n                }\n            },\n\n            setTheme: function (theme) {\n                if (this.isLocked) {\n                    return;\n                }\n                if (window.themeManager) {\n                    window.themeManager.setTheme(theme);\n                    this._syncFromManager();\n                }\n            },\n\n            useSystemPreference: function () {\n                if (this.isLocked) {\n                    return;\n                }\n                if (window.themeManager) {\n                    window.themeManager.useSystemTheme();\n                    this._syncFromManager();\n                }\n            },\n\n            _syncFromManager: function () {\n                if (window.themeManager) {\n                    this.currentTheme = window.themeManager.getTheme();\n                    this.preference = window.themeManager.getPreference();\n                }\n            },\n        };\n    }\n\n    Alpine.data(\"themeToggle\", makeThemeToggle);\n\n    // Light / Dark / System segmented control (mobile drawer). Same state and\n    // theme lock as themeToggle; on a theme-locked page every option is\n    // aria-disabled and the locked label explains why.\n    Alpine.data(\"themeChoice\", function () {\n        var selected =\n            \"cursor-pointer bg-[var(--color-surface-card)] text-gray-900 shadow-sm dark:bg-gray-700 dark:text-white\";\n        var idle = \"cursor-pointer text-gray-700 dark:text-gray-300\";\n        // Locked (always-dark) page: no option reads as pressed or clickable.\n        var locked = \"text-gray-700 dark:text-gray-300 opacity-60 cursor-not-allowed\";\n        return mixin(makeThemeToggle(), {\n            _optionClass: function (chosen) {\n                if (this.isLocked) {\n                    return locked;\n                }\n                return chosen ? selected : idle;\n            },\n            // aria-pressed: a locked (always-dark) page has no pressed option.\n            get isLightChosen() {\n                return !this.isLocked && this.preference === \"light\";\n            },\n            get isDarkChosen() {\n                return !this.isLocked && this.preference === \"dark\";\n            },\n            get isSystemChosen() {\n                return !this.isLocked && this.preference === \"system\";\n            },\n            get lightOptionClass() {\n                return this._optionClass(this.isLightChosen);\n            },\n            get darkOptionClass() {\n                return this._optionClass(this.isDarkChosen);\n            },\n            get systemOptionClass() {\n                return this._optionClass(this.isSystemChosen);\n            },\n            chooseLight: function () {\n                this.setTheme(\"light\");\n            },\n            chooseDark: function () {\n                this.setTheme(\"dark\");\n            },\n            chooseSystem: function () {\n                this.useSystemPreference();\n            },\n        });\n    });\n\n    // ============================================================\n    // Ghost Story Slideshow - auto-playing, no controls\n    // (includes/ghost-story-compact.html on profile_submitted.html)\n    // ============================================================\n    Alpine.data(\"ghostStory\", function () {\n        return {\n            currentScene: 0,\n            totalScenes: 6,\n            isPaused: false,\n            autoAdvanceTimer: null,\n            sceneDurations: [4000, 4000, 5000, 5000, 5000, 6000],\n\n            // Scene visibility getters (CSP-safe)\n            get isScene0() {\n                return this.currentScene === 0;\n            },\n            get isScene1() {\n                return this.currentScene === 1;\n            },\n            get isScene2() {\n                return this.currentScene === 2;\n            },\n            get isScene3() {\n                return this.currentScene === 3;\n            },\n            get isScene4() {\n                return this.currentScene === 4;\n            },\n            get isScene5() {\n                return this.currentScene === 5;\n            },\n\n            // Scene container class getters (CSP-safe, no ternary in template)\n            get scene0Class() {\n                return this.currentScene === 0\n                    ? \"ghost-story-scene ghost-story-scene-active ghost-story-scene-0\"\n                    : \"ghost-story-scene ghost-story-scene-0\";\n            },\n            get scene1Class() {\n                return this.currentScene === 1\n                    ? \"ghost-story-scene ghost-story-scene-active ghost-story-scene-1\"\n                    : \"ghost-story-scene ghost-story-scene-1\";\n            },\n            get scene2Class() {\n                return this.currentScene === 2\n                    ? \"ghost-story-scene ghost-story-scene-active ghost-story-scene-2\"\n                    : \"ghost-story-scene ghost-story-scene-2\";\n            },\n            get scene3Class() {\n                return this.currentScene === 3\n                    ? \"ghost-story-scene ghost-story-scene-active ghost-story-scene-3\"\n                    : \"ghost-story-scene ghost-story-scene-3\";\n            },\n            get scene4Class() {\n                return this.currentScene === 4\n                    ? \"ghost-story-scene ghost-story-scene-active ghost-story-scene-4\"\n                    : \"ghost-story-scene ghost-story-scene-4\";\n            },\n            get scene5Class() {\n                return this.currentScene === 5\n                    ? \"ghost-story-scene ghost-story-scene-active ghost-story-scene-5\"\n                    : \"ghost-story-scene ghost-story-scene-5\";\n            },\n\n            init: function () {\n                var self = this;\n                this.startAutoAdvance();\n\n                // Pause on hover\n                this.$el.addEventListener(\"mouseenter\", function () {\n                    if (!self.isPaused) {\n                        self._hoverPaused = true;\n                        self.clearTimer();\n                    }\n                });\n                this.$el.addEventListener(\"mouseleave\", function () {\n                    if (self._hoverPaused) {\n                        self._hoverPaused = false;\n                        if (!self.isPaused) {\n                            self.startAutoAdvance();\n                        }\n                    }\n                });\n            },\n\n            destroy: function () {\n                this.clearTimer();\n            },\n\n            clearTimer: function () {\n                if (this.autoAdvanceTimer) {\n                    clearTimeout(this.autoAdvanceTimer);\n                    this.autoAdvanceTimer = null;\n                }\n            },\n\n            startAutoAdvance: function () {\n                var self = this;\n                this.clearTimer();\n                if (this.isPaused) return;\n                var duration = this.sceneDurations[this.currentScene] || 5000;\n                this.autoAdvanceTimer = setTimeout(function () {\n                    self.nextScene();\n                }, duration);\n            },\n\n            nextScene: function () {\n                if (this.currentScene < this.totalScenes - 1) {\n                    this.currentScene++;\n                } else {\n                    this.currentScene = 0;\n                }\n                this.startAutoAdvance();\n            },\n        };\n    });\n\n    // =========================================================================\n    // Field Validator - Real-time form field validation (CSP-safe)\n    // =========================================================================\n    // Usage: <div x-data=\"fieldValidator\"\n    //             data-required=\"true\"\n    //             data-min-length=\"2\"\n    //             data-max-length=\"100\"\n    //             data-validation-type=\"text\"\n    //             data-error-required=\"This field is required\"\n    //             data-error-min-length=\"Must be at least 2 characters\"\n    //             data-error-max-length=\"Must be under 100 characters\"\n    //             data-error-email=\"Please enter a valid email\">\n    //   <input type=\"text\" @input=\"handleInput\" @blur=\"handleBlur\" />\n    //   <p x-show=\"hasError\" x-text=\"errorMessage\" class=\"text-red-600 text-sm mt-1\"></p>\n    // </div>\n    Alpine.data(\"fieldValidator\", function () {\n        return {\n            value: \"\",\n            error: \"\",\n            isValid: true,\n            touched: false,\n            _debounceTimer: null,\n\n            // Config from data attributes\n            _required: false,\n            _minLength: 0,\n            _maxLength: 0,\n            _validationType: \"text\",\n            _errorRequired: \"This field is required\",\n            _errorMinLength: \"\",\n            _errorMaxLength: \"\",\n            _errorEmail: \"Please enter a valid email address\",\n\n            init: function () {\n                var el = this.$el;\n                this._required = el.getAttribute(\"data-required\") === \"true\";\n                this._minLength = parseInt(\n                    el.getAttribute(\"data-min-length\") || \"0\",\n                    10,\n                );\n                this._maxLength = parseInt(\n                    el.getAttribute(\"data-max-length\") || \"0\",\n                    10,\n                );\n                this._validationType =\n                    el.getAttribute(\"data-validation-type\") || \"text\";\n                this._errorRequired =\n                    el.getAttribute(\"data-error-required\") || this._errorRequired;\n                this._errorMinLength =\n                    el.getAttribute(\"data-error-min-length\") ||\n                    \"Must be at least \" + this._minLength + \" characters\";\n                this._errorMaxLength =\n                    el.getAttribute(\"data-error-max-length\") ||\n                    \"Must be under \" + this._maxLength + \" characters\";\n                this._errorEmail =\n                    el.getAttribute(\"data-error-email\") || this._errorEmail;\n            },\n\n            get hasError() {\n                return this.touched && this.error.length > 0;\n            },\n\n            get errorMessage() {\n                return this.error;\n            },\n\n            get fieldClass() {\n                if (!this.touched) return \"\";\n                return this.isValid ? \"border-green-500\" : \"border-red-500\";\n            },\n\n            _validate: function () {\n                var val = this.value.trim();\n\n                if (this._required && val.length === 0) {\n                    this.error = this._errorRequired;\n                    this.isValid = false;\n                    return;\n                }\n\n                if (\n                    val.length > 0 &&\n                    this._minLength > 0 &&\n                    val.length < this._minLength\n                ) {\n                    this.error = this._errorMinLength;\n                    this.isValid = false;\n                    return;\n                }\n\n                if (this._maxLength > 0 && val.length > this._maxLength) {\n                    this.error = this._errorMaxLength;\n                    this.isValid = false;\n                    return;\n                }\n\n                if (this._validationType === \"email\" && val.length > 0) {\n                    // Basic email pattern check\n                    var atIdx = val.indexOf(\"@\");\n                    var dotIdx = val.lastIndexOf(\".\");\n                    if (atIdx < 1 || dotIdx < atIdx + 2 || dotIdx >= val.length - 1) {\n                        this.error = this._errorEmail;\n                        this.isValid = false;\n                        return;\n                    }\n                }\n\n                this.error = \"\";\n                this.isValid = true;\n            },\n\n            handleInput: function () {\n                var input = this.$el.querySelector(\"input, textarea, select\");\n                if (input) {\n                    this.value = input.value;\n                }\n                // Debounce validation on input (500ms)\n                var self = this;\n                clearTimeout(this._debounceTimer);\n                this._debounceTimer = setTimeout(function () {\n                    if (self.touched) {\n                        self._validate();\n                    }\n                }, 500);\n            },\n\n            handleBlur: function () {\n                var input = this.$el.querySelector(\"input, textarea, select\");\n                if (input) {\n                    this.value = input.value;\n                }\n                this.touched = true;\n                this._validate();\n            },\n        };\n    });\n\n    // =========================================================================\n    // CRUSH SPARK COMPONENTS\n    // =========================================================================\n\n    /**\n     * sparkRequest - Description form with character counter\n     * Used on spark_request.html\n     */\n    Alpine.data(\"sparkRequest\", function () {\n        return {\n            description: \"\",\n            maxLength: 1000,\n\n            get charCount() {\n                return this.description.length;\n            },\n\n            get charsRemaining() {\n                return this.maxLength - this.description.length;\n            },\n\n            get isValid() {\n                return this.description.length >= 10;\n            },\n\n            get isOverLimit() {\n                return this.description.length > this.maxLength;\n            },\n        };\n    });\n\n    // Event-cancel double-submit guard (event_cancel.html). The first submit\n    // enters the confirming state and goes through natively; any further\n    // submit is swallowed so a double tap cannot post twice. Replaces an\n    // inline onsubmit handler the nonce-based CSP blocked.\n    Alpine.data(\"eventCancelForm\", function () {\n        return mixin(makeConfirm({ autoSubmit: false }), {\n            guardSubmit(event) {\n                if (this.isConfirming) {\n                    event.preventDefault();\n                    return;\n                }\n                this.request();\n            },\n            // Back/forward cache restores the page as it was left: re-arm it.\n            resetGuard(event) {\n                if (event && event.persisted) this.cancelConfirm();\n            },\n        });\n    });\n\n    // Spark confirm inline component (replaces browser confirm dialog)\n    Alpine.data(\"sparkConfirm\", function () {\n        // Composes makeConfirm with the template-facing API the spark\n        // partials expect (isInitial / showConfirm / cancel). autoSubmit\n        // is OFF \u2014 the spark confirmation panel makes its own HTMX call,\n        // it does NOT submit a parent form.\n        return mixin(makeConfirm({ autoSubmit: false }), {\n            get isInitial() {\n                return this.isIdle;\n            },\n            showConfirm() {\n                this.request();\n            },\n            cancel() {\n                this.cancelConfirm();\n            },\n        });\n    });\n\n    // Voting demo success state component\n    Alpine.data(\"votingDemoSuccess\", function () {\n        return {\n            submitted: false,\n            presChoice: \"\",\n            twistChoice: \"\",\n\n            get isSubmitted() {\n                return this.submitted;\n            },\n            get isNotSubmitted() {\n                return !this.submitted;\n            },\n            get presChoiceText() {\n                return this.presChoice;\n            },\n            get twistChoiceText() {\n                return this.twistChoice;\n            },\n\n            submitDemo() {\n                var presRadio = document.querySelector(\n                    'input[name=\"demo_presentation\"]:checked',\n                );\n                var twistRadio = document.querySelector(\n                    'input[name=\"demo_twist\"]:checked',\n                );\n                if (!presRadio || !twistRadio) return;\n\n                // Get label text for the selected options\n                var presLabel =\n                    presRadio.closest(\"label\") ||\n                    presRadio.parentElement.querySelector(\"label\") ||\n                    presRadio.closest(\".variant-option-item\").querySelector(\"strong\");\n                var twistLabel =\n                    twistRadio.closest(\"label\") ||\n                    twistRadio.parentElement.querySelector(\"label\") ||\n                    twistRadio.closest(\".variant-option-item\").querySelector(\"strong\");\n\n                this.presChoice = presLabel\n                    ? presLabel.querySelector(\"strong\").textContent.trim()\n                    : presRadio.value;\n                this.twistChoice = twistLabel\n                    ? twistLabel.querySelector(\"strong\").textContent.trim()\n                    : twistRadio.value;\n                this.submitted = true;\n\n                // Auto-scroll to results section after 3 seconds\n                var self = this;\n                setTimeout(function () {\n                    var resultsSection = document.getElementById(\"step-results\");\n                    if (resultsSection) {\n                        resultsSection.scrollIntoView({\n                            behavior: \"smooth\",\n                            block: \"start\",\n                        });\n                    }\n                }, 3000);\n            },\n        };\n    });\n\n    // Event Poll Voting component\n    // CSP-safe: all logic in methods/getters, no inline expressions.\n    // The ballot is a real <form> of native radios/checkboxes (name=\"option_ids\")\n    // that also works without JS; this component posts it as JSON instead, shows\n    // errors inline, and swaps in the server-rendered results on success.\n    Alpine.data(\"eventPollVoting\", function () {\n        return {\n            selectedCount: 0,\n            isSubmitting: false,\n            hasVoted: false,\n            errorMessage: \"\",\n\n            _textSubmit: gettext(\"Submit Vote\"),\n            _textSubmitting: gettext(\"Submitting...\"),\n            _textSubmitted: gettext(\"Vote Submitted\"),\n            _textError: gettext(\"Failed to submit vote\"),\n            _textNetworkError: gettext(\"Network error. Please try again.\"),\n            _textRateLimited: gettext(\n                \"Too many attempts. Take a breath and try again in a minute.\",\n            ),\n            _textSuccess: gettext(\"Thanks, your vote is in!\"),\n\n            init() {\n                var el = this.$el;\n                var keys = [\n                    \"Submit\",\n                    \"Submitting\",\n                    \"Submitted\",\n                    \"Error\",\n                    \"NetworkError\",\n                    \"RateLimited\",\n                    \"Success\",\n                ];\n                for (var i = 0; i < keys.length; i++) {\n                    var value = el.dataset[\"text\" + keys[i]];\n                    if (value) this[\"_text\" + keys[i]] = value;\n                }\n                // A browser may restore a checked option after reload/back.\n                this.updateSelection();\n            },\n\n            get submitButtonText() {\n                if (this.isSubmitting) return this._textSubmitting;\n                if (this.hasVoted) return this._textSubmitted;\n                return this._textSubmit;\n            },\n\n            get isDisabled() {\n                return this.isSubmitting || this.hasVoted || this.selectedCount === 0;\n            },\n\n            get isBallotShown() {\n                return !this.hasVoted;\n            },\n\n            updateSelection: function () {\n                this.selectedCount = this._selectedIds().length;\n                this.errorMessage = \"\";\n            },\n\n            _selectedIds: function () {\n                var checked = this.$root.querySelectorAll(\n                    'input[name=\"option_ids\"]:checked',\n                );\n                return Array.from(checked).map(function (input) {\n                    return parseInt(input.value, 10);\n                });\n            },\n\n            submitVote: function () {\n                var ids = this._selectedIds();\n                if (!ids.length || this.isSubmitting || this.hasVoted) return;\n\n                var self = this;\n                var form = this.$root.querySelector(\"form\");\n                var token = form.querySelector(\"[name=csrfmiddlewaretoken]\");\n                // The vote URL is language-neutral: reply in the page's language.\n                var lang = form.querySelector('input[name=\"lang\"]');\n                var payload = { option_ids: ids, lang: lang ? lang.value : \"\" };\n                // Optional \"I am...\" answer, rendered only for voters without a profile gender\n                var gender = form.querySelector('input[name=\"voter_gender\"]:checked');\n                if (gender) payload.gender = gender.value;\n\n                self.isSubmitting = true;\n                self.errorMessage = \"\";\n                fetch(form.action, {\n                    method: \"POST\",\n                    credentials: \"same-origin\",\n                    headers: {\n                        \"Content-Type\": \"application/json\",\n                        Accept: \"application/json\",\n                        \"X-CSRFToken\": token ? token.value : \"\",\n                    },\n                    body: JSON.stringify(payload),\n                })\n                    .then(function (r) {\n                        if (r.status === 429) return self._fail(self._textRateLimited);\n                        var type = r.headers.get(\"Content-Type\") || \"\";\n                        // A CSRF 403 page or a login redirect is HTML, not JSON.\n                        if (type.indexOf(\"application/json\") === -1) {\n                            return self._fail(self._textError);\n                        }\n                        return r.json().then(function (data) {\n                            if (r.ok && data.success) return self._succeed(data);\n                            self._fail(data.error || self._textError);\n                        });\n                    })\n                    .catch(function () {\n                        self._fail(self._textNetworkError);\n                    });\n            },\n\n            _fail: function (message) {\n                this.isSubmitting = false;\n                this.errorMessage = message;\n            },\n\n            _succeed: function (data) {\n                this.isSubmitting = false;\n                this.hasVoted = true;\n                var results = this.$refs.results;\n                if (results && data.results_html) {\n                    results.innerHTML = data.results_html;\n                    results.focus();\n                }\n                // The header count sits outside this component; update it from\n                // the same response that renders the results.\n                var total = document.querySelector(\"[data-poll-total-votes]\");\n                if (total && data.total_votes_label) {\n                    total.textContent = data.total_votes_label;\n                }\n                window.dispatchEvent(\n                    new CustomEvent(\"show-toast\", {\n                        detail: { type: \"success\", message: this._textSuccess },\n                    }),\n                );\n            },\n        };\n    });\n\n    // =========================================================================\n    // Trait Selector Component (Matching System)\n    // =========================================================================\n    // CSP-safe chip-based selector for qualities, defects, and sought qualities.\n    // Traits are server-rendered as buttons; this component tracks selection state.\n    //\n    // Usage (container):\n    //   <div x-data=\"traitSelector\" data-max=\"5\" data-initial=\"[1,2,3]\">\n    //\n    // Usage (each chip button, server-rendered via Django {% for %}):\n    //   <button type=\"button\" data-trait-id=\"42\" @click=\"handleClick\" ...>\n    //\n    Alpine.data(\"traitSelector\", function () {\n        return {\n            selected: [],\n            maxItems: 5,\n\n            init: function () {\n                var self = this;\n                this.maxItems = parseInt(this.$el.getAttribute(\"data-max\") || \"5\", 10);\n                var initialStr = this.$el.getAttribute(\"data-initial\");\n                if (initialStr) {\n                    try {\n                        this.selected = JSON.parse(initialStr);\n                    } catch (e) {\n                        this.selected = [];\n                    }\n                }\n                // Apply initial visual state to all chip buttons\n                this._syncAllChips();\n                // Wizard draft restore: data-initial is server-rendered from the\n                // saved profile, which is empty for a draft that hasn't hit\n                // Continue yet. When the wizard re-hydrates, adopt the persisted\n                // ids so the chips (and the x-bound hidden input) match \u2014 without\n                // this the reactive hiddenValue would overwrite the restored value\n                // back to empty.\n                this.$el.addEventListener(\"trait-restore\", function (e) {\n                    if (e.detail && Array.isArray(e.detail.ids)) {\n                        self.selected = e.detail.ids.slice();\n                        self._syncAllChips();\n                    }\n                });\n            },\n\n            get counterText() {\n                return this.selected.length + \"/\" + this.maxItems;\n            },\n\n            get hiddenValue() {\n                return this.selected.join(\",\");\n            },\n\n            handleClick: function () {\n                // Read trait ID from the clicked button's data attribute\n                var btn = this.$el;\n                if (!btn.hasAttribute(\"data-trait-id\")) {\n                    // Clicked on container, find closest button\n                    return;\n                }\n                var id = parseInt(btn.getAttribute(\"data-trait-id\"), 10);\n                var idx = this.selected.indexOf(id);\n                if (idx > -1) {\n                    this.selected.splice(idx, 1);\n                } else if (this.selected.length < this.maxItems) {\n                    this.selected.push(id);\n                }\n                this._syncAllChips();\n                this.$nextTick(\n                    function () {\n                        this.$dispatch(\"profile-autosave:trigger\");\n                    }.bind(this),\n                );\n            },\n\n            _syncAllChips: function () {\n                var self = this;\n                var container = this.$el.closest(\"[x-data]\");\n                if (!container) return;\n                var buttons = container.querySelectorAll(\"[data-trait-id]\");\n                var isMax = self.selected.length >= self.maxItems;\n                var accentColor = container.getAttribute(\"data-accent\") || \"purple\";\n                for (var i = 0; i < buttons.length; i++) {\n                    var btn = buttons[i];\n                    var traitId = parseInt(btn.getAttribute(\"data-trait-id\"), 10);\n                    var isSelected = self.selected.indexOf(traitId) > -1;\n                    var isDisabled = isMax && !isSelected;\n\n                    // Toggle-button semantics for assistive tech (the chips are\n                    // <button type=\"button\">, so aria-pressed carries the state).\n                    btn.setAttribute(\"aria-pressed\", isSelected ? \"true\" : \"false\");\n\n                    // Remove all state classes\n                    btn.classList.remove(\n                        \"border-purple-500\",\n                        \"bg-purple-100\",\n                        \"dark:bg-purple-900/40\",\n                        \"text-purple-700\",\n                        \"dark:text-purple-300\",\n                        \"border-pink-500\",\n                        \"bg-pink-100\",\n                        \"dark:bg-pink-900/40\",\n                        \"text-pink-700\",\n                        \"dark:text-pink-300\",\n                        \"border-green-500\",\n                        \"bg-green-100\",\n                        \"dark:bg-green-900/40\",\n                        \"text-green-700\",\n                        \"dark:text-green-300\",\n                        \"border-gray-200\",\n                        \"dark:border-gray-600\",\n                        \"dark:border-gray-700\",\n                        \"bg-white\",\n                        \"dark:bg-gray-800\",\n                        \"bg-gray-100\",\n                        \"text-gray-700\",\n                        \"dark:text-gray-200\",\n                        \"text-gray-400\",\n                        \"dark:text-gray-600\",\n                        \"cursor-not-allowed\",\n                        \"hover:border-purple-300\",\n                        \"hover:bg-purple-50\",\n                        \"dark:hover:bg-purple-900/30\",\n                        \"hover:border-pink-300\",\n                        \"hover:bg-pink-50\",\n                        \"dark:hover:bg-pink-900/30\",\n                        \"hover:border-green-300\",\n                        \"hover:bg-green-50\",\n                        \"dark:hover:bg-green-900/30\",\n                    );\n\n                    if (isSelected) {\n                        btn.classList.add(\n                            \"border-\" + accentColor + \"-500\",\n                            \"bg-\" + accentColor + \"-100\",\n                            \"dark:bg-\" + accentColor + \"-900/40\",\n                            \"text-\" + accentColor + \"-700\",\n                            \"dark:text-\" + accentColor + \"-300\",\n                        );\n                    } else if (isDisabled) {\n                        btn.classList.add(\n                            \"border-gray-200\",\n                            \"dark:border-gray-700\",\n                            \"bg-gray-100\",\n                            \"dark:bg-gray-800\",\n                            \"text-gray-400\",\n                            \"dark:text-gray-600\",\n                            \"cursor-not-allowed\",\n                        );\n                    } else {\n                        btn.classList.add(\n                            \"border-gray-200\",\n                            \"dark:border-gray-600\",\n                            \"bg-white\",\n                            \"dark:bg-gray-800\",\n                            \"text-gray-700\",\n                            \"dark:text-gray-200\",\n                        );\n                        btn.classList.add(\n                            \"hover:border-\" + accentColor + \"-300\",\n                            \"hover:bg-\" + accentColor + \"-50\",\n                            \"dark:hover:bg-\" + accentColor + \"-900/30\",\n                        );\n                    }\n                }\n            },\n        };\n    });\n\n    // =========================================================================\n    // Astro Toggle Component (Matching System)\n    // =========================================================================\n    Alpine.data(\"astroToggle\", function () {\n        return {\n            enabled: true,\n\n            init: function () {\n                this.enabled = this.$el.getAttribute(\"data-initial\") === \"true\";\n                this._syncVisual();\n            },\n\n            get isEnabled() {\n                return this.enabled;\n            },\n\n            get isDisabled() {\n                return !this.enabled;\n            },\n\n            get toggleBgClass() {\n                return this.enabled ? \"bg-purple-500\" : \"bg-gray-300 dark:bg-gray-600\";\n            },\n\n            get toggleKnobClass() {\n                return this.enabled ? \"translate-x-6\" : \"translate-x-1\";\n            },\n\n            toggle: function () {\n                this.enabled = !this.enabled;\n                this._syncVisual();\n                this.$nextTick(\n                    function () {\n                        this.$dispatch(\"profile-autosave:trigger\");\n                    }.bind(this),\n                );\n            },\n\n            _syncVisual: function () {\n                // Sync toggle button visual state\n                var toggleBtn = this.$el.querySelector(\"[data-toggle-btn]\");\n                var knob = this.$el.querySelector(\"[data-toggle-knob]\");\n                if (toggleBtn) {\n                    toggleBtn.classList.remove(\n                        \"bg-purple-500\",\n                        \"bg-gray-300\",\n                        \"dark:bg-gray-600\",\n                    );\n                    if (this.enabled) {\n                        toggleBtn.classList.add(\"bg-purple-500\");\n                    } else {\n                        toggleBtn.classList.add(\"bg-gray-300\", \"dark:bg-gray-600\");\n                    }\n                }\n                if (knob) {\n                    knob.classList.remove(\"translate-x-6\", \"translate-x-1\");\n                    knob.classList.add(\n                        this.enabled ? \"translate-x-6\" : \"translate-x-1\",\n                    );\n                }\n            },\n        };\n    });\n\n    // \u2500\u2500 Submission Status (profile_submitted.html pending state) \u2500\u2500\n    Alpine.data(\"submissionStatus\", function () {\n        return {\n            // State from server (initialized via data-* attributes)\n            status: \"\",\n            queuePosition: 0,\n            totalPending: 0,\n            hoursWaiting: 0,\n            waitStatus: \"\",\n            progressPercent: 0,\n\n            // Note state\n            noteText: \"\",\n            noteSending: false,\n            noteSent: false,\n            noteError: \"\",\n\n            // UI toggles\n            showCallPrep: false,\n            showNoteForm: false,\n\n            // Polling\n            pollInterval: null,\n\n            init: function () {\n                var el = this.$el;\n                this.status = el.dataset.status || \"pending\";\n                this.queuePosition = parseInt(el.dataset.queuePosition || \"0\", 10);\n                this.totalPending = parseInt(el.dataset.totalPending || \"0\", 10);\n                this.hoursWaiting = parseFloat(el.dataset.hoursWaiting || \"0\");\n                this.waitStatus = el.dataset.waitStatus || \"fresh\";\n                this.progressPercent = parseInt(el.dataset.progressPercent || \"0\", 10);\n                this.noteSent = el.dataset.hasNote === \"true\";\n\n                if (this.status === \"pending\") {\n                    this.startPolling();\n                }\n            },\n\n            startPolling: function () {\n                var self = this;\n                self.pollInterval = setInterval(function () {\n                    self.checkStatus();\n                }, 60000);\n            },\n\n            checkStatus: function () {\n                var self = this;\n                fetch(\"/api/submission/status/\", { credentials: \"same-origin\" })\n                    .then(function (r) {\n                        return r.json();\n                    })\n                    .then(function (data) {\n                        if (data.status !== self.status) {\n                            window.location.reload();\n                            return;\n                        }\n                        self.queuePosition = data.queue_position;\n                        self.hoursWaiting = data.hours_waiting;\n                        self.waitStatus = data.wait_status;\n                    })\n                    .catch(function () {\n                        // Silently ignore polling errors\n                    });\n            },\n\n            sendNote: function () {\n                if (!this.isNoteValid || this.noteSending) return;\n                var self = this;\n                self.noteSending = true;\n                self.noteError = \"\";\n\n                var csrfToken = \"\";\n                var csrfEl = document.querySelector('[name=\"csrfmiddlewaretoken\"]');\n                if (csrfEl) csrfToken = csrfEl.value;\n\n                fetch(\"/api/submission/note/\", {\n                    method: \"POST\",\n                    credentials: \"same-origin\",\n                    headers: {\n                        \"Content-Type\": \"application/json\",\n                        \"X-CSRFToken\": csrfToken,\n                    },\n                    body: JSON.stringify({ note: self.noteText }),\n                })\n                    .then(function (r) {\n                        return r.json().then(function (data) {\n                            return { ok: r.ok, data: data };\n                        });\n                    })\n                    .then(function (result) {\n                        self.noteSending = false;\n                        if (result.ok) {\n                            self.noteSent = true;\n                            self.showNoteForm = false;\n                        } else {\n                            self.noteError =\n                                result.data.error || \"Something went wrong\";\n                        }\n                    })\n                    .catch(function () {\n                        self.noteSending = false;\n                        self.noteError = gettext(\"Network error. Please try again.\");\n                    });\n            },\n\n            toggleCallPrep: function () {\n                this.showCallPrep = !this.showCallPrep;\n            },\n\n            toggleNoteForm: function () {\n                this.showNoteForm = !this.showNoteForm;\n            },\n\n            updateNoteText: function (e) {\n                this.noteText = e.target.value;\n            },\n\n            // CSP-safe getters\n            get isPending() {\n                return this.status === \"pending\";\n            },\n            get isNoteSending() {\n                return this.noteSending;\n            },\n            get isNoteNotSending() {\n                return !this.noteSending;\n            },\n            get isNoteSent() {\n                return this.noteSent;\n            },\n            get isNoteNotSent() {\n                return !this.noteSent;\n            },\n            get hasNoteError() {\n                return this.noteError !== \"\";\n            },\n            get isNoteValid() {\n                return this.noteText.length >= 10 && this.noteText.length <= 500;\n            },\n            get noteCharCount() {\n                return this.noteText.length + \"/500\";\n            },\n            get isCallPrepOpen() {\n                return this.showCallPrep;\n            },\n            get isNoteFormOpen() {\n                return this.showNoteForm && !this.noteSent;\n            },\n            get showQueuePosition() {\n                return this.queuePosition > 0;\n            },\n            get waitBadgeClass() {\n                if (this.waitStatus === \"fresh\")\n                    return \"bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300\";\n                if (this.waitStatus === \"normal\")\n                    return \"bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300\";\n                if (this.waitStatus === \"extended\")\n                    return \"bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300\";\n                return \"bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-300\";\n            },\n            get waitBadgeText() {\n                if (this.waitStatus === \"fresh\") return \"Just submitted\";\n                if (this.waitStatus === \"normal\") return \"In progress\";\n                if (this.waitStatus === \"extended\") return \"Taking a bit longer\";\n                return \"Extended wait\";\n            },\n            get noteIconBgClass() {\n                return this.noteSent\n                    ? \"bg-green-100 dark:bg-green-900/30\"\n                    : \"bg-blue-100 dark:bg-blue-900/30\";\n            },\n            get noteButtonClass() {\n                return this.isNoteValid && !this.noteSending\n                    ? \"bg-crush-purple hover:bg-purple-700 text-white\"\n                    : \"bg-gray-300 dark:bg-gray-700 text-gray-500 cursor-not-allowed\";\n            },\n\n            destroy: function () {\n                if (this.pollInterval) clearInterval(this.pollInterval);\n            },\n        };\n    });\n\n    // Photo slot picker popover (profile edit \"Photos\" section)\n    // Reads slot number from data-slot on the root element.\n    // Imports a social photo via POST /api/profile/import-social-photo/ and\n    // swaps the returned HTML into #photo-card-{slot}.\n    // Photo editor Back link. With a same-app `next`, Back returns there only\n    // once a main photo exists; photo uploads and deletes swap\n    // #photo-card-1 over HTMX without re-rendering this link, so keep its\n    // href in step after each swap.\n    Alpine.data(\"photoEditorBack\", function () {\n        return {\n            _onSwap: null,\n            init: function () {\n                var el = this.$el;\n                this._onSwap = function (event) {\n                    var target = event.detail && event.detail.target;\n                    if (!target || target.id !== \"photo-card-1\") return;\n                    var hasPhoto = !!target.querySelector(\n                        \".photo-preview-container.has-photo\",\n                    );\n                    el.setAttribute(\n                        \"href\",\n                        hasPhoto ? el.dataset.next : el.dataset.fallback,\n                    );\n                };\n                document.body.addEventListener(\"htmx:afterSwap\", this._onSwap);\n            },\n            destroy: function () {\n                document.body.removeEventListener(\"htmx:afterSwap\", this._onSwap);\n            },\n        };\n    });\n\n    Alpine.data(\"photoPicker\", function () {\n        return {\n            slot: 0,\n            isOpen: false,\n            pending: false,\n            error: \"\",\n\n            get isClosed() {\n                return !this.isOpen;\n            },\n            get hasError() {\n                return this.error.length > 0;\n            },\n            get errorMessage() {\n                return this.error;\n            },\n            get triggerDisabled() {\n                return this.pending;\n            },\n            get ariaExpanded() {\n                return this.isOpen ? \"true\" : \"false\";\n            },\n\n            init: function () {\n                this.slot = parseInt(this.$el.getAttribute(\"data-slot\")) || 0;\n            },\n\n            toggle: function () {\n                if (this.pending) return;\n                this.isOpen = !this.isOpen;\n                if (!this.isOpen) this.error = \"\";\n            },\n\n            close: function () {\n                this.isOpen = false;\n                this.error = \"\";\n            },\n\n            importFromProvider: function (event) {\n                var self = this;\n                var btn = event.currentTarget;\n                var accountId = parseInt(btn.getAttribute(\"data-account-id\"));\n                if (!accountId || !this.slot) return;\n\n                self.pending = true;\n                self.error = \"\";\n\n                var csrfEl = document.querySelector(\"[name=csrfmiddlewaretoken]\");\n                var csrfToken = csrfEl ? csrfEl.value : \"\";\n                var importUrl = \"/api/profile/import-social-photo/\";\n\n                fetch(importUrl, {\n                    method: \"POST\",\n                    headers: {\n                        \"Content-Type\": \"application/json\",\n                        \"X-CSRFToken\": csrfToken,\n                    },\n                    body: JSON.stringify({\n                        social_account_id: accountId,\n                        photo_slot: self.slot,\n                    }),\n                })\n                    .then(function (response) {\n                        return response.json();\n                    })\n                    .then(function (data) {\n                        if (data.success) {\n                            var targetSlot = data.photo_slot || self.slot;\n                            var card = document.getElementById(\n                                \"photo-card-\" + targetSlot,\n                            );\n                            if (card && data.html) {\n                                card.innerHTML = data.html;\n                                if (window.htmx) window.htmx.process(card);\n                            }\n                            self.close();\n                        } else {\n                            self.error = data.error || gettext(\"Import failed.\");\n                        }\n                    })\n                    .catch(function () {\n                        self.error = gettext(\"Network error. Please try again.\");\n                    })\n                    .finally(function () {\n                        self.pending = false;\n                    });\n            },\n        };\n    });\n\n    // Onboarding step-2 \"Continue\" gate.\n    // Shows the Continue button once the phone-verified window event fires\n    // (dispatched by phoneVerificationComponent on successful SMS verify).\n    // Reads initial state from the element's data-initial-verified attribute\n    // so a user who already has phone_verified=True sees the button right\n    // away without having to re-verify.\n    Alpine.data(\"onboardingPhoneContinue\", function () {\n        return {\n            verified: false,\n\n            get isVerified() {\n                return this.verified;\n            },\n            get isNotVerified() {\n                return !this.verified;\n            },\n\n            init: function () {\n                var self = this;\n                var initial = this.$el.getAttribute(\"data-initial-verified\");\n                this.verified = initial === \"true\";\n                window.addEventListener(\"phone-verified\", function () {\n                    self.verified = true;\n                });\n            },\n        };\n    });\n\n    // Notification bell \u2014 fetches recent notifications, shows unread count,\n    // marks rows read on click. Polls lazily (on open) \u2014 no background poll.\n    Alpine.data(\"notificationBell\", function () {\n        return {\n            isOpen: false,\n            unreadCount: 0,\n            unreadBroadcastWired: false,\n            items: [],\n            loaded: false,\n\n            get hasUnread() {\n                return this.unreadCount > 0;\n            },\n            get hasItems() {\n                return this.items.length > 0;\n            },\n            get hasNoItems() {\n                return this.items.length === 0;\n            },\n            get badgeText() {\n                if (this.unreadCount > 9) return \"9+\";\n                return String(this.unreadCount);\n            },\n\n            init: function () {\n                var self = this;\n                // The CSP Alpine build can't iterate nested item props via x-for,\n                // so the list is rendered imperatively whenever items change.\n                this.$watch(\"items\", function () {\n                    self.renderItems();\n                });\n                // Seed from the server-rendered count (the same helper the API\n                // uses) so this bell and the mobile top bar start equal, then\n                // broadcast every change so the mobile badge follows. init runs\n                // twice here (x-data auto-init + x-init=\"init\"): wire the\n                // broadcast once.\n                this.unreadCount = parseInt(\n                    this.$el.getAttribute(\"data-unread-count\") || \"0\",\n                    10,\n                ) || 0;\n                if (!this.unreadBroadcastWired) {\n                    this.unreadBroadcastWired = true;\n                    this.$watch(\"unreadCount\", function (count) {\n                        window.dispatchEvent(\n                            new CustomEvent(\"notif-unread-count\", {\n                                detail: count,\n                            }),\n                        );\n                    });\n                }\n                // Fetch unread count on mount so the badge appears immediately\n                this.refresh();\n            },\n\n            renderItems: function () {\n                var list = this.$refs.notiflist;\n                if (!list) return;\n                var self = this;\n                list.replaceChildren();\n                this.items.forEach(function (item) {\n                    var li = document.createElement(\"li\");\n                    li.className =\n                        \"px-4 py-3 hover:bg-gray-50 dark:hover:bg-gray-700/50\";\n\n                    var a = document.createElement(\"a\");\n                    a.href = item.link_url || \"#\";\n                    a.className = \"block\";\n                    // Synchronous guard so a rapid double-click can't fire two\n                    // mark-read POSTs (which would double-decrement the badge).\n                    var marking = false;\n                    a.addEventListener(\"click\", function () {\n                        if (!item.is_unread || marking) return;\n                        marking = true;\n                        self.markRead(item.id).catch(function (err) {\n                            // Release the lock so a failed request can be retried.\n                            marking = false;\n                            console.warn(\"notificationBell.markRead:\", err);\n                        });\n                    });\n\n                    var row = document.createElement(\"div\");\n                    row.className = \"flex items-start gap-2\";\n\n                    if (item.is_unread) {\n                        var dot = document.createElement(\"span\");\n                        dot.className =\n                            \"flex-shrink-0 mt-1.5 w-2 h-2 rounded-full bg-crush-purple\";\n                        row.appendChild(dot);\n                    }\n\n                    var content = document.createElement(\"div\");\n                    content.className = \"flex-1 min-w-0\";\n\n                    var title = document.createElement(\"p\");\n                    title.className =\n                        \"text-sm font-medium text-gray-900 dark:text-white mb-0 truncate\";\n                    title.textContent = item.title || \"\";\n                    content.appendChild(title);\n\n                    var body = document.createElement(\"p\");\n                    body.className =\n                        \"text-xs text-gray-500 dark:text-gray-400 mb-0 line-clamp-2\";\n                    body.textContent = item.body || \"\";\n                    content.appendChild(body);\n\n                    var time = document.createElement(\"p\");\n                    time.className =\n                        \"text-[11px] text-gray-400 dark:text-gray-500 mt-1\";\n                    time.textContent = item.relative_time || \"\";\n                    content.appendChild(time);\n\n                    row.appendChild(content);\n                    a.appendChild(row);\n                    li.appendChild(a);\n                    list.appendChild(li);\n                });\n            },\n\n            toggle: function () {\n                this.isOpen = !this.isOpen;\n                if (this.isOpen && !this.loaded) {\n                    this.refresh();\n                }\n            },\n\n            close: function () {\n                this.isOpen = false;\n            },\n\n            refresh: function () {\n                var self = this;\n                fetch(\"/api/notifications/\", {\n                    credentials: \"same-origin\",\n                    headers: { Accept: \"application/json\" },\n                })\n                    .then(function (r) {\n                        if (!r.ok) throw new Error(\"fetch failed\");\n                        return r.json();\n                    })\n                    .then(function (data) {\n                        self.unreadCount = data.unread_count || 0;\n                        self.items = (data.items || []).map(function (it) {\n                            return Object.assign({}, it, {\n                                relative_time: self.formatRelativeTime(it.created_at),\n                            });\n                        });\n                        self.loaded = true;\n                    })\n                    .catch(function (err) {\n                        console.warn(\"notificationBell.refresh:\", err);\n                    });\n            },\n\n            markRead: function (id) {\n                var self = this;\n                var token = self.getCsrfToken();\n                // Returns the promise so callers can react to failure; rejects on\n                // non-ok responses (fetch only rejects on network errors).\n                return fetch(\"/api/notifications/\" + id + \"/read/\", {\n                    method: \"POST\",\n                    credentials: \"same-origin\",\n                    headers: { \"X-CSRFToken\": token, Accept: \"application/json\" },\n                }).then(function (r) {\n                    if (!r.ok) throw new Error(\"mark read failed\");\n                    // Update local state \u2014 server is source of truth on next open\n                    self.items = self.items.map(function (it) {\n                        if (it.id === id) {\n                            return Object.assign({}, it, { is_unread: false });\n                        }\n                        return it;\n                    });\n                    self.unreadCount = Math.max(0, self.unreadCount - 1);\n                });\n            },\n\n            markAllRead: function () {\n                var self = this;\n                var token = self.getCsrfToken();\n                fetch(\"/api/notifications/mark-all-read/\", {\n                    method: \"POST\",\n                    credentials: \"same-origin\",\n                    headers: { \"X-CSRFToken\": token, Accept: \"application/json\" },\n                })\n                    .then(function () {\n                        self.unreadCount = 0;\n                        self.items = self.items.map(function (it) {\n                            return Object.assign({}, it, { is_unread: false });\n                        });\n                    })\n                    .catch(function (err) {\n                        console.warn(\"notificationBell.markAllRead:\", err);\n                    });\n            },\n\n            markReadFromEvent: function (event) {\n                var id = event.detail;\n                this.unreadCount = Math.max(0, this.unreadCount - 1);\n                this.items = this.items.map(function (it) {\n                    if (it.id === id)\n                        return Object.assign({}, it, { is_unread: false });\n                    return it;\n                });\n            },\n\n            formatRelativeTime: function (iso) {\n                if (!iso) return \"\";\n                var then = new Date(iso);\n                var diffMs = Date.now() - then.getTime();\n                var diffMin = Math.floor(diffMs / 60000);\n                if (diffMin < 1) return \"just now\";\n                if (diffMin < 60) return diffMin + \"m ago\";\n                var diffHr = Math.floor(diffMin / 60);\n                if (diffHr < 24) return diffHr + \"h ago\";\n                var diffDay = Math.floor(diffHr / 24);\n                if (diffDay < 7) return diffDay + \"d ago\";\n                return then.toLocaleDateString();\n            },\n\n            getCsrfToken: function () {\n                var name = \"csrftoken=\";\n                var parts = (document.cookie || \"\").split(\";\");\n                for (var i = 0; i < parts.length; i++) {\n                    var c = parts[i].trim();\n                    if (c.indexOf(name) === 0) return c.substring(name.length);\n                }\n                var hidden = document.querySelector(\n                    'input[name=\"csrfmiddlewaretoken\"]',\n                );\n                return hidden ? hidden.value : \"\";\n            },\n        };\n    });\n\n    // Event feedback NPS slider \u2014 live label as user drags\n    Alpine.data(\"feedbackNpsSlider\", function () {\n        return {\n            score: 8,\n            label: \"\",\n\n            init: function () {\n                var self = this;\n                var input = this.$el.querySelector('input[type=\"range\"]');\n                if (input) {\n                    self.score = parseInt(input.value, 10) || 8;\n                    self.updateLabel();\n                    input.addEventListener(\"input\", function () {\n                        self.score = parseInt(input.value, 10) || 0;\n                        self.updateLabel();\n                    });\n                }\n            },\n\n            updateLabel: function () {\n                if (this.score >= 9) {\n                    this.label = \"I'd strongly recommend\";\n                } else if (this.score >= 7) {\n                    this.label = \"I'd recommend\";\n                } else if (this.score >= 4) {\n                    this.label = \"Mixed\";\n                } else {\n                    this.label = \"Wouldn't recommend\";\n                }\n            },\n        };\n    });\n\n    // Event Identity edit section + wizard step 2 (2026 redesign): keeps the\n    // interest cap (max 8) and the \"Ask me about\u2026\" cap (max 3) live, and gates\n    // the ask-me-about chips to the currently-selected interests. CSP-friendly:\n    // no expressions in markup, all logic here, queries via $root (the methods\n    // are invoked from child @change handlers where $el is the input itself).\n    Alpine.data(\"eventIdentity\", function () {\n        return {\n            interestMax: 8,\n            askMeMax: 3,\n            interestCount: 0,\n            askMeCount: 0,\n\n            init: function () {\n                var self = this;\n                this.interestMax = parseInt(\n                    this.$root.getAttribute(\"data-interest-max\") || \"8\",\n                    10,\n                );\n                this.askMeMax = parseInt(\n                    this.$root.getAttribute(\"data-askme-max\") || \"3\",\n                    10,\n                );\n                this._syncInterests();\n                this._syncAskMe();\n                // Wizard drafts hydrate asynchronously, after this init has run\n                // (profileWizard.populateFieldsFromDraft programmatically checks\n                // the boxes without firing change events). Re-sync when it signals\n                // so restored counters, caps and \"Ask me about\" chip visibility\n                // are correct \u2014 mirrors the dobPicker/cantonMap restore pattern.\n                this.$root.addEventListener(\"event-identity-restore\", function () {\n                    self._syncInterests();\n                    self._syncAskMe();\n                });\n            },\n\n            onInterestChange: function () {\n                // A deselected interest must not stay an \"Ask me about\" target.\n                var selected = {};\n                this.$root\n                    .querySelectorAll('input[name=\"interests_new\"]')\n                    .forEach(function (b) {\n                        if (b.checked) selected[b.value] = true;\n                    });\n                this.$root\n                    .querySelectorAll('input[name=\"ask_me_about\"]')\n                    .forEach(function (a) {\n                        if (!selected[a.value]) a.checked = false;\n                    });\n                this._syncInterests();\n                this._syncAskMe();\n            },\n\n            onAskMeChange: function () {\n                this._syncAskMe();\n            },\n\n            _syncInterests: function () {\n                var boxes = this.$root.querySelectorAll(\n                    'input[name=\"interests_new\"]',\n                );\n                var checked = 0;\n                var selected = {};\n                boxes.forEach(function (b) {\n                    if (b.checked) {\n                        checked++;\n                        selected[b.value] = true;\n                    }\n                });\n                this.interestCount = checked;\n                var atCap = checked >= this.interestMax;\n                boxes.forEach(function (b) {\n                    b.disabled = atCap && !b.checked;\n                });\n                // Reveal ask-me-about chips only for the selected interests.\n                this.$root\n                    .querySelectorAll(\"[data-askme-wrap]\")\n                    .forEach(function (wrap) {\n                        var id = wrap.getAttribute(\"data-interest-id\");\n                        if (selected[id]) {\n                            wrap.classList.remove(\"hidden\");\n                        } else {\n                            wrap.classList.add(\"hidden\");\n                        }\n                    });\n                var emptyHint = this.$root.querySelector(\"[data-askme-empty]\");\n                if (emptyHint) {\n                    if (checked > 0) {\n                        emptyHint.classList.add(\"hidden\");\n                    } else {\n                        emptyHint.classList.remove(\"hidden\");\n                    }\n                }\n            },\n\n            _syncAskMe: function () {\n                var boxes = this.$root.querySelectorAll(\n                    'input[name=\"ask_me_about\"]',\n                );\n                var checked = 0;\n                boxes.forEach(function (b) {\n                    if (b.checked) checked++;\n                });\n                this.askMeCount = checked;\n                var atCap = checked >= this.askMeMax;\n                boxes.forEach(function (b) {\n                    b.disabled = atCap && !b.checked;\n                });\n            },\n        };\n    });\n\n    // Auto-redirect countdown shown on the profile-approved state of profile_submitted.html.\n    // Reads the destination URL from data-dashboard-url to stay language-prefix\u2013safe.\n    Alpine.data(\"approvedCountdown\", () => ({\n        countdown: 5,\n        init() {\n            const url = this.$el.dataset.dashboardUrl;\n            const t = setInterval(() => {\n                if (this.countdown <= 1) {\n                    clearInterval(t);\n                    window.location.href = url;\n                } else {\n                    this.countdown--;\n                }\n            }, 1000);\n        },\n    }));\n\n    // ========================================================================\n    // Verify-email resend cooldown (account/verification_sent_crush.html)\n    // ========================================================================\n\n    // The server's cooldown is per address (#1059), so a corrected address in\n    // \"Use a different address\" re-enables the button while the countdown for\n    // the cooled address keeps running. The page only knows that address as a\n    // SHA-256 hash of its trimmed, lower-cased form.\n    Alpine.data(\"resendCooldown\", () => ({\n        coolingDown: false,\n        otherAddress: false,\n        cooldownHash: \"\",\n        inputSeq: 0,\n        remaining: 0,\n        get disabled() {\n            return this.coolingDown && !this.otherAddress;\n        },\n        get enabled() {\n            return !this.disabled;\n        },\n        init() {\n            this.cooldownHash = this.$el.dataset.cooldownHash || \"\";\n            const cooldownUntil = parseInt(this.$el.dataset.cooldownUntil, 10) || 0;\n            const serverNow = parseInt(this.$el.dataset.serverNow, 10) || 0;\n            this.remaining = Math.max(0, cooldownUntil - serverNow);\n            if (this.remaining <= 0) {\n                return;\n            }\n            this.coolingDown = true;\n            const t = setInterval(() => {\n                this.remaining--;\n                if (this.remaining <= 0) {\n                    clearInterval(t);\n                    this.coolingDown = false;\n                }\n            }, 1000);\n        },\n        onEmailInput(event) {\n            const value = (event.target.value || \"\").trim().toLowerCase();\n            const seq = ++this.inputSeq;\n            if (!value) {\n                this.otherAddress = false;\n                return;\n            }\n            // Without the hash or SubtleCrypto (plain-http dev hosts) any typed\n            // address counts as different; the server still no-ops a repeat.\n            if (!this.cooldownHash || !window.crypto || !window.crypto.subtle) {\n                this.otherAddress = true;\n                return;\n            }\n            window.crypto.subtle\n                .digest(\"SHA-256\", new TextEncoder().encode(value))\n                .then((buf) => {\n                    if (seq !== this.inputSeq) {\n                        return;\n                    }\n                    const hex = Array.from(new Uint8Array(buf))\n                        .map((b) => b.toString(16).padStart(2, \"0\"))\n                        .join(\"\");\n                    this.otherAddress = hex !== this.cooldownHash;\n                });\n        },\n    }));\n\n    // Community Supporter donation card (components/support_card.html).\n    //\n    // Every user-visible string arrives on data- attributes rather than\n    // gettext() here: the card's strings were written as {% trans %} in the\n    // template and so live in the `django` catalog, while gettext() in this\n    // file reads `djangojs`. Passing them in keeps one copy of each string,\n    // in the catalog the translators already have.\n    //\n    // The card is only ever rendered with commerce enabled -- the template\n    // withholds this whole half inside a native shell -- so there is no\n    // \"nothing to wire up\" guard to port.\n    Alpine.data(\"donationCard\", function () {\n        return {\n            min: 2,\n            max: 500,\n            endpoint: \"\",\n            labelSupport: \"\",\n            msgMin: \"\",\n            msgMax: \"\",\n            msgPreparing: \"\",\n            msgUnavailable: \"\",\n            msgFailed: \"\",\n\n            // The amount shown on the button. Distinct from `activeTier`: a\n            // typed amount equal to a preset still leaves every preset\n            // unhighlighted, so the two cannot be derived from each other.\n            amount: NaN,\n            activeTier: null,\n            busy: false,\n            errorMessage: \"\",\n\n            init: function () {\n                var el = this.$root;\n                this.min = parseFloat(el.getAttribute(\"data-min\") || \"2\");\n                this.max = parseFloat(el.getAttribute(\"data-max\") || \"500\");\n                this.endpoint = el.getAttribute(\"data-endpoint\") || \"\";\n                this.labelSupport = el.getAttribute(\"data-label-support\") || \"\";\n                this.msgMin = el.getAttribute(\"data-msg-min\") || \"\";\n                this.msgMax = el.getAttribute(\"data-msg-max\") || \"\";\n                this.msgPreparing = el.getAttribute(\"data-msg-preparing\") || \"\";\n                this.msgUnavailable = el.getAttribute(\"data-msg-unavailable\") || \"\";\n                this.msgFailed = el.getAttribute(\"data-msg-failed\") || \"\";\n\n                // data-default names the tier the template already rendered\n                // active, so this re-asserts the server's state rather than\n                // changing it -- no flash, and the card still reads correctly\n                // if Alpine never boots.\n                this.activeTier = el.getAttribute(\"data-default\");\n                this.amount = this.toCents(this.activeTier || \"\");\n                this.syncTiers();\n            },\n\n            get hasError() {\n                return this.errorMessage !== \"\";\n            },\n\n            get buttonLabel() {\n                if (this.busy) {\n                    return this.msgPreparing;\n                }\n                if (!isNaN(this.amount) && this.amount > 0) {\n                    // \"Support \u00B7 \u20AC10\" (#4-10), not \"Support the Project (\u20AC10.00)\":\n                    // the full-sentence label wrapped to two lines on a 390px\n                    // pill and lost its side padding. Whole euros drop the\n                    // \".00\" so the common presets stay short; a genuinely\n                    // fractional custom amount still shows its cents.\n                    var amountText =\n                        this.amount % 1 === 0\n                            ? this.amount.toFixed(0)\n                            : this.amount.toFixed(2);\n                    return this.labelSupport + \" \u00B7 \u20AC\" + amountText;\n                }\n                return this.labelSupport;\n            },\n\n            // Round the decimal the member typed, not its binary float, so this\n            // agrees with the server's Decimal ROUND_HALF_UP on every input.\n            //\n            // toFixed() rounds the float and gets 2.355 wrong (\u20AC2.35 shown, \u20AC2.36\n            // charged). Nudging by Number.EPSILON first looks like it fixes that\n            // and does for 2.355 -- but it is still float arithmetic and diverges\n            // on 4.6% of half-cent amounts: toCents(10.075) gave 10.07 where the\n            // server says 10.08. Digits are the only representation both sides\n            // agree on, so parse them.\n            //\n            // Half-up needs only \"is the remainder >= 0.005\", which is true\n            // exactly when the third decimal digit is 5-9 -- later digits cannot\n            // change that, so they are ignored rather than examined.\n            toCents: function (value) {\n                var text = String(value).trim();\n                if (!/^\\d+(\\.\\d*)?$|^\\.\\d+$/.test(text)) {\n                    return NaN;   // scientific notation, signs, junk \u2014 let the caller reject it\n                }\n                var parts = text.split(\".\");\n                var whole = parseInt(parts[0] || \"0\", 10);\n                var frac = parts[1] || \"\";\n                var cents = whole * 100 + parseInt(frac.slice(0, 2).padEnd(2, \"0\"), 10);\n                if (frac.length > 2 && frac.charCodeAt(2) >= 53) {\n                    cents += 1;\n                }\n                return cents / 100;\n            },\n\n            selectTier: function () {\n                // $el, not $root: the handler sits on the clicked button, which\n                // is what carries the amount.\n                var btn = this.$el;\n                if (this.$refs.custom) this.$refs.custom.value = \"\";\n                this.activeTier = btn.getAttribute(\"data-amount\");\n                this.amount = this.toCents(this.activeTier || \"\");\n                this.syncTiers();\n            },\n\n            onCustomInput: function () {\n                var raw = this.customRaw();\n                var cents = this.toCents(raw);\n                // Only a usable amount moves the button and clears the presets.\n                // Half-typed input (\"1\", \"\") leaves the last selection showing;\n                // submit() is where a non-empty custom field takes over\n                // regardless.\n                if (!isNaN(cents) && cents >= this.min) {\n                    this.activeTier = null;\n                    this.amount = cents;\n                    this.syncTiers();\n                }\n            },\n\n            submit: function () {\n                var self = this;\n                var final = this.effectiveAmount();\n\n                if (isNaN(final) || final < this.min) {\n                    this.errorMessage = this.msgMin;\n                    return;\n                }\n\n                // Mirrors DONATION_MAX_EUR in views_payments.py. The server is\n                // what actually enforces this; checking here only saves a round\n                // trip on an obvious slipped decimal point.\n                if (final > this.max) {\n                    this.errorMessage = this.msgMax;\n                    return;\n                }\n\n                this.errorMessage = \"\";\n                this.busy = true;\n\n                // The endpoint comes in as an absolute path from the template,\n                // like every other checkout call site. Reversing it by name\n                // would tie the partial to which urlconf is active and turn a\n                // NoReverseMatch into a 500 on the whole page.\n                //\n                // CSRF comes from the hidden input base.html renders --\n                // CSRF_COOKIE_HTTPONLY is on, so there is no cookie to read.\n                fetch(this.endpoint, {\n                    method: \"POST\",\n                    headers: {\n                        \"Content-Type\": \"application/json\",\n                        \"X-CSRFToken\": (document.querySelector(\"[name=csrfmiddlewaretoken]\") || {}).value || \"\"\n                    },\n                    body: JSON.stringify({ amount: final })\n                })\n                    .then(function (res) { return res.json(); })\n                    .then(function (data) {\n                        // SumUp answers with its widget page, Mollie with its\n                        // hosted checkout (views_mollie).\n                        var target = data.checkout_url || data.widget_url;\n                        if (data.success && target) {\n                            window.location.href = target;\n                        } else {\n                            throw new Error(data.error || self.msgUnavailable);\n                        }\n                    })\n                    .catch(function (err) {\n                        self.busy = false;\n                        // Put the attempted figure back on the button; only an\n                        // amount that already passed both bounds gets this far.\n                        self.amount = self.toCents(String(final));\n                        self.errorMessage = err.message || self.msgFailed;\n                    });\n            },\n\n            customRaw: function () {\n                return this.$refs.custom ? (this.$refs.custom.value || \"\").trim() : \"\";\n            },\n\n            // A non-empty custom field is authoritative, valid or not. Taking it\n            // only when it already passed the minimum meant typing \"1\" left the\n            // amount on the last preset, so the checks in submit() saw a valid\n            // \u20AC10 and opened a \u20AC10 checkout for someone who asked for \u20AC1. The\n            // input is not in a form, so its `min` attribute never intervenes.\n            // Resolved to the cent from the typed digits, so the figure checked,\n            // the figure on the button and the figure SumUp charges are one.\n            effectiveAmount: function () {\n                var custom = this.customRaw();\n                return custom !== \"\" ? this.toCents(custom) : this.amount;\n            },\n\n            // Toggles only the classes that differ between the two states, so\n            // the shared ones stay where the template put them instead of being\n            // restated (and kept in sync) here.\n            syncTiers: function () {\n                var active = this.activeTier;\n                var buttons = this.$root.querySelectorAll(\"[data-amount]\");\n                buttons.forEach(function (btn) {\n                    var on = btn.getAttribute(\"data-amount\") === active;\n\n                    btn.classList.toggle(\"border-2\", on);\n                    btn.classList.toggle(\"border-pink-500\", on);\n                    // pink-700: white / pink-100 on pink-500 was 2.7 / 2.2:1\n                    btn.classList.toggle(\"bg-pink-700\", on);\n                    btn.classList.toggle(\"shadow-md\", on);\n                    btn.classList.toggle(\"border\", !on);\n                    btn.classList.toggle(\"border-gray-200\", !on);\n                    btn.classList.toggle(\"dark:border-white/20\", !on);\n                    btn.classList.toggle(\"bg-gray-50\", !on);\n                    btn.classList.toggle(\"dark:bg-white/10\", !on);\n                    btn.classList.toggle(\"hover:bg-pink-50\", !on);\n                    btn.classList.toggle(\"dark:hover:bg-pink-600/30\", !on);\n\n                    var amountSpan = btn.querySelector(\".tier-amount\");\n                    if (amountSpan) {\n                        amountSpan.classList.toggle(\"text-white\", on);\n                        amountSpan.classList.toggle(\"text-gray-900\", !on);\n                        amountSpan.classList.toggle(\"dark:text-white\", !on);\n                    }\n\n                    var labelSpan = btn.querySelector(\".tier-label\");\n                    if (labelSpan) {\n                        labelSpan.classList.toggle(\"text-pink-100\", on);\n                        labelSpan.classList.toggle(\"text-gray-600\", !on);\n                        labelSpan.classList.toggle(\"dark:text-purple-200\", !on);\n                    }\n                });\n            },\n        };\n    });\n\n    // Named component behind the SumUp \"Pay with Card\" / \"Pay with Crush\n    // Credit\" buttons on event_detail.html, _event_registration_success.html\n    // and my_events.html (UX Wave 3 WP8 \u00B7 finding 4-05). The three templates\n    // used to carry byte-identical <script> blocks with a click-delegation\n    // listener; a double tap fired two checkouts (no disabled state) and a\n    // failure showed an untranslated alert(), which reads as a scam dialog\n    // inside the iOS/Android WebView. One Alpine.data component fixes both:\n    // isLoading disables the button for the duration of the fetch, and a\n    // failure goes to Alpine.store(\"toasts\") (the documented public API in\n    // toast-component.js) instead of alert().\n    //\n    // Config comes in as data-* attributes (CSP build: x-data cannot take\n    // arguments), same convention as donationCard above. data-label /\n    // data-label-loading are pre-rendered so the resting state matches the\n    // server-rendered fallback content exactly, and the loading state names\n    // itself instead of just spinning.\n    Alpine.data(\"sumupCheckoutButton\", function () {\n        return {\n            registrationId: \"\",\n            paymentMethod: \"card\",\n            restingLabel: \"\",\n            loadingLabel: \"\",\n            errorMessage: \"\",\n            isLoading: false,\n\n            init: function () {\n                var el = this.$el;\n                this.registrationId = el.getAttribute(\"data-sumup-reg-id\") || \"\";\n                this.paymentMethod = el.getAttribute(\"data-payment-method\") || \"card\";\n                this.restingLabel = el.getAttribute(\"data-label\") || \"\";\n                this.loadingLabel = el.getAttribute(\"data-label-loading\") || \"\";\n                this.errorMessage = el.getAttribute(\"data-msg-error\") || \"\";\n\n                // start() leaves isLoading true while navigating to\n                // widget_url so the button stays disabled during the\n                // redirect. If the browser instead restores this page from\n                // the back/forward cache (e.g. the member backs out of the\n                // SumUp checkout on mobile Safari/Chrome), that redirect\n                // never completes and the button would stay stuck on the\n                // loading label until a hard reload. pageshow with\n                // event.persisted fires on a bfcache restore (never on a\n                // normal load), so clear the stale loading state here.\n                var self = this;\n                window.addEventListener(\"pageshow\", function (event) {\n                    if (event.persisted) {\n                        self.isLoading = false;\n                    }\n                });\n            },\n\n            get label() {\n                return this.isLoading ? this.loadingLabel : this.restingLabel;\n            },\n\n            // Named getter, not \"x-show=\\\"!isLoading\\\"\" in the templates:\n            // the CSP build evaluates bare property/method names only.\n            get idle() {\n                return !this.isLoading;\n            },\n\n            // CSRF_COOKIE_HTTPONLY is True, so document.cookie can't see\n            // csrftoken. Read the hidden input base.html renders instead\n            // (same as HTMX does).\n            getCsrfToken: function () {\n                var input = document.querySelector('input[name=\"csrfmiddlewaretoken\"]');\n                return input ? input.value : \"\";\n            },\n\n            start: function () {\n                if (this.isLoading || !this.registrationId) return;\n                this.isLoading = true;\n                var self = this;\n                fetch(\"/payments/sumup/create-event-checkout/\" + this.registrationId + \"/\", {\n                    method: \"POST\",\n                    headers: {\n                        \"X-CSRFToken\": this.getCsrfToken(),\n                        \"Content-Type\": \"application/json\",\n                    },\n                    body: JSON.stringify({ payment_method: this.paymentMethod }),\n                })\n                    .then(function (res) {\n                        return res.json().catch(function () {\n                            throw new Error(\"HTTP \" + res.status);\n                        });\n                    })\n                    .then(function (data) {\n                        if (data.success && data.widget_url) {\n                            // Navigating away \u2014 leave isLoading true so the\n                            // button stays disabled during the redirect.\n                            window.location.href = data.widget_url;\n                            return;\n                        }\n                        self.isLoading = false;\n                        Alpine.store(\"toasts\").add({\n                            type: \"error\",\n                            message: data.error || self.errorMessage,\n                        });\n                    })\n                    .catch(function () {\n                        self.isLoading = false;\n                        Alpine.store(\"toasts\").add({\n                            type: \"error\",\n                            message: self.errorMessage,\n                        });\n                    });\n            },\n        };\n    });\n\n    // =========================================================================\n    // UX Wave 3 \u00B7 WP5 (finding 3-13) \u2014 screening-call self-booking\n    // (crush_lu/templates/crush_lu/book_screening.html)\n    // =========================================================================\n\n    // One instance per coach block. Slots are plain radio inputs (styled via\n    // Tailwind `peer-checked`, no per-item Alpine expression needed \u2014 the CSP\n    // build only allows bare method/property names), this component just\n    // tracks whether something is picked and mirrors the chosen slot's\n    // ISO datetimes into the two hidden fields the form actually submits.\n    // Picking a slot then pressing the sticky \"Confirm\" button is itself the\n    // two-step confirmation the finding asked for \u2014 no second dialog needed.\n    Alpine.data(\"bookingSlotPicker\", function () {\n        return {\n            selectedStart: \"\",\n            selectedEnd: \"\",\n            selectedLabel: \"\",\n            showAll: false,\n\n            get hasSelection() {\n                return !!this.selectedStart;\n            },\n\n            // The CSP-friendly Alpine build forbids expressions (e.g.\n            // \"!hasSelection\") inside x-bind, so the negation needs its own\n            // bare-name getter \u2014 see connectOnboarding.notShowSecondStory\n            // for the same pattern elsewhere in this file.\n            get hasNoSelection() {\n                return !this.hasSelection;\n            },\n\n            get isShowAllHidden() {\n                return !this.showAll;\n            },\n\n            pickSlot: function (event) {\n                var el = event.currentTarget || event.target;\n                this.selectedStart = el.dataset.start || \"\";\n                this.selectedEnd = el.dataset.end || \"\";\n                this.selectedLabel = el.dataset.label || \"\";\n            },\n\n            toggleShowAll: function () {\n                this.showAll = !this.showAll;\n            },\n        };\n    });\n\n    // Cancel-booking confirm, composed from the same makeConfirm mixin\n    // sparkConfirm uses \u2014 idle \"Cancel booking\" link, then an inline\n    // \"Yes, cancel / No, keep it\" choice before the form actually submits.\n    Alpine.data(\"bookingCancelConfirm\", function () {\n        return mixin(makeConfirm(), {\n            get isInitial() {\n                return this.isIdle;\n            },\n            showConfirm: function () {\n                this.request();\n            },\n            cancel: function () {\n                this.cancelConfirm();\n            },\n        });\n    });\n});\n\n// =============================================================================\n// REDUCED MOTION: SVG (SMIL) ANIMATIONS\n// The stylesheet's prefers-reduced-motion rule stops CSS animations and\n// transitions, but it cannot reach <animate> / <animateTransform> inside an\n// SVG, and the ghost in the navbar and top bar animates that way on every\n// page. Freeze those here for members who asked for reduced motion: on load,\n// after an HTMX swap brings in new artwork, and when the setting changes.\n// =============================================================================\n(function () {\n    var query = window.matchMedia\n        ? window.matchMedia(\"(prefers-reduced-motion: reduce)\")\n        : null;\n    if (!query) return;\n\n    function apply(root) {\n        // pauseAnimations() lives on the outermost <svg> of each drawing.\n        var scope = root || document;\n        if (!scope.querySelectorAll) return;\n        var svgs = scope.querySelectorAll(\"svg\");\n        for (var i = 0; i < svgs.length; i++) {\n            var svg = svgs[i];\n            if (svg.ownerSVGElement || !svg.pauseAnimations) continue;\n            if (query.matches) {\n                svg.pauseAnimations();\n            } else if (svg.animationsPaused && svg.animationsPaused()) {\n                svg.unpauseAnimations();\n            }\n        }\n    }\n\n    function applyAll() {\n        apply(document);\n    }\n\n    if (document.readyState === \"loading\") {\n        document.addEventListener(\"DOMContentLoaded\", applyAll);\n    } else {\n        applyAll();\n    }\n    document.addEventListener(\"htmx:afterSettle\", function (evt) {\n        if (query.matches) apply(evt.target && evt.target.parentNode);\n    });\n    if (query.addEventListener) {\n        query.addEventListener(\"change\", applyAll);\n    }\n})();\n"],
  "mappings": "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",
  "names": ["mixin", "target", "source", "notifyError", "message", "makeTabs", "initial", "names", "name", "makeConfirm", "opts", "options", "form", "DRAWER_FOCUSABLE", "drawerPanel", "drawerFocusables", "panel", "el", "drawerOpener", "self", "e", "items", "first", "last", "inside", "opener", "PROMPT_ORDER", "sheet", "markReady", "visits", "i", "name", "on", "dismissed", "dismissedAt", "now", "active", "resizeImageForUpload", "file", "maxEdge", "quality", "bitmap", "scale", "shortEdge", "width", "height", "canvas", "ctx", "resolve", "blob", "_pendingPhotoUploads", "_photoStepAdvancing", "trackPendingPhotoUpload", "promise", "settle", "idx", "waitForPendingPhotoUploads", "PUSH_CHECK_TIMEOUT_MS", "makeBlockedPlatform", "ua", "makePushStatusCheck", "probe", "run", "finish", "params", "urlCat", "known", "btn", "event", "off", "buttons", "c", "regId", "r", "data", "err", "path", "langPrefix", "meta", "count", "prefix", "match", "syncToastOffset", "toast", "barHeight", "resizeTimer", "anchor", "target", "payButton", "observer", "entries", "entry", "real", "makeDismissible", "delay", "mixin", "prompts", "visible", "text", "ta", "initialTab", "key", "next", "nextId", "nextEl", "fineCursor", "reducedMotion", "hero", "eyes", "heart", "heartBaseX", "heartBaseY", "eyeOffset", "heartOffset", "falloffRange", "eyeEase", "heartEase", "targetUx", "targetUy", "eyeX", "eyeY", "heartX", "heartY", "rect", "cx", "cy", "dx", "dy", "dist", "falloff", "tick", "targetEyeX", "targetEyeY", "targetHeartX", "targetHeartY", "eyeT", "activeClass", "inactiveClass", "makeTabs", "LEGACY_SETTINGS_ANCHORS", "unsubscribed", "prefKey", "input", "cookie", "row", "value", "checkbox", "response", "id", "form", "csrf", "fail", "send", "trigger", "wait", "payload", "result", "error", "formData", "checkboxGroups", "inputs", "fieldErrors", "nonFieldErrors", "node", "field", "messages", "span", "message", "p", "savedValue", "groupValues", "radios", "radio", "subId", "container", "subscriptionId", "endpoint", "reg", "sub", "subscriptionElements", "badges", "components", "str", "hash", "subscription", "dateStr", "date", "diffMs", "diffMins", "diffHours", "diffDays", "diffMonths", "preferences", "subscribed", "callback", "maxAttempts", "attempts", "check", "promises", "confirmed", "success", "health", "ageDays", "parent", "permission", "registration", "fingerprint", "prefs", "cb", "base64", "padding", "b64", "raw", "out", "initialCount", "maxLength", "_photoUploadDeprecationLogged", "slotOperations", "index", "photo", "hasImage", "preview", "draft", "photoUrlKey", "photoNumber", "generation", "previousOperation", "reader", "uploadPromise", "ready", "uploadFile", "csrfToken", "notifyError", "removalGeneration", "clearLocal", "deletePromise", "pct", "initialStep", "phoneVerified", "isEditing", "labelsAttr", "ofWord", "sep", "savedJustNow", "savedAgo", "savedEarlier", "stepMap", "hashMatch", "alreadySeeded", "seedStep", "step", "genderEl", "locationEl", "cantonMapEl", "genderRadios", "locationInput", "mutations", "mutation", "pushHistory", "heading", "emptyLabel", "phone", "dobInput", "reviewPhone", "reviewDob", "reviewGender", "reviewLocation", "reviewInterests", "reviewLanguages", "reviewPhotos", "dobDate", "genderText", "label", "genderLabel", "chips", "vibeEl", "vibeSpan", "box", "em", "chip", "checked", "labels", "srcs", "positions", "photoImgs", "j", "src", "wrapper", "hidden", "k", "img", "callout", "nudge", "phoneInput", "fullNumber", "n", "token", "phoneEl", "dobEl", "phoneNumber", "interestsNew", "b", "askMeAbout", "qualitiesEl", "defectsEl", "showFullName", "showExactAge", "langCheckboxes", "eventLanguages", "d", "saved", "diffSec", "diffMin", "rawDraft", "stepKey", "stepDraft", "checkboxes", "shouldCheck", "mapContainer", "regionPath", "eventIdentityEl", "fieldName", "ids", "x", "textInputs", "immediateInputs", "stepData", "jsonPayload", "fields", "group", "checkedValues", "initialValue", "initialName", "regionId", "svg", "paths", "svgX", "svgY", "screenX", "screenY", "point", "ctm", "svgPoint", "borderRegion", "selectedIndex", "region", "focusedElements", "regionName", "hiddenInput", "prevPath", "select", "option", "y", "m", "monthObj", "monthName", "monthsData", "num", "months", "year", "month", "maxDay", "explicitValue", "parts", "day", "matchingRange", "range", "daysInMonth", "summary", "dateDisplay", "verifiedAttr", "prefilledValue", "modal", "modalData", "nextInput", "currentPath", "selectedLang", "newPath", "retried", "controls", "element", "branch", "siblings", "sibling", "candidates", "candidate", "channel", "nextRef", "prevRef", "paste", "digits", "code", "isIOS", "THEME_TOGGLE_LABELS", "makeThemeToggle", "pair", "mediaQuery", "handleChange", "theme", "selected", "idle", "locked", "chosen", "duration", "val", "atIdx", "dotIdx", "makeConfirm", "presRadio", "twistRadio", "presLabel", "twistLabel", "resultsSection", "keys", "lang", "gender", "type", "results", "total", "initialStr", "isMax", "accentColor", "traitId", "isSelected", "isDisabled", "toggleBtn", "knob", "csrfEl", "hasPhoto", "accountId", "importUrl", "targetSlot", "card", "initial", "list", "item", "li", "a", "marking", "dot", "content", "title", "body", "time", "it", "iso", "then", "diffHr", "diffDay", "boxes", "atCap", "wrap", "emptyHint", "url", "cooldownUntil", "serverNow", "t", "seq", "buf", "hex", "amountText", "whole", "frac", "cents", "final", "res", "custom", "amountSpan", "labelSpan", "query", "apply", "root", "scope", "svgs", "applyAll", "evt"]
}
