{"release_id":"01a1998d-9802-0000-b0f7-33a23307f4ed","chunk_id":"01a1998d-9968-7f60-8009-e1ecf1999b33","file":"0.BrLhZpga.mjs","ignoreList":[],"mappings":"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","names":["files","__vitePreload","AREAS","PAGE_AREAS","namespacesFor","path","first","clean","a","loadMessages","locale","namespaces","parts","ns","load","m","cookieLocale","LOCALE_COOKIE","isLocale","data","url","prefixed","splitLocalePath","fromServer","DEFAULT_LOCALE","isLocalizedPath","messages","STUDIO_PATH","STUDIO_HOP","studioHopFor","e","origin","routeStudioLinks","signedIn","go","to","isStudioHost","onClick","userId","onInbox","page","badgeTotal","$unreadNotifications","$.get","$unreadChat","open","$.state","items","loading","loadError","settingsOpen","setPref","key","value","notificationPrefs","p","next","refreshUnreadCount","res","body","unreadNotifications","unreadChat","$.user_effect","onVisible","loadItems","$.set","fireShownEvents","list","n","trackChallengeNotificationShown","toggle","trackNotificationDropdownOpened","root","onPointerDown","realtime","event","handleClick","trackNotificationClicked","trackChallengeNotificationClicked","x","c","target","resolveTarget","goto","markAllRead","code","tag","slug","nickname","sessionId","postId","creatorNickname","nick","threadId","albumId","sid","t","animId","summary","name","title","buyer","amount","payoutUsd","amt","xp","who","cn","f","score","count","tail","room","badge","h","table","tier","avatar","pathKey","nickKey","getAvatarUrl","typeIcon","type","ago","iso","ms","min","d","onKey","div","root_1","button","$.child","node","$.sibling","span","root_2","text","$.reset","$.template_effect","$.set_text","$.append","$$anchor","$$render","consequent","node_1","div_1","root_3","div_2","p_1","div_3","button_1","root_4","text_2","$0","$.delegated","consequent_1","button_2","node_2","div_4","root_5","p_2","div_5","$$index","$$array","checked","$.derived","$notificationPrefs","label","root_6","span_1","button_3","span_2","classes","text_4","$.set_attribute","$.set_class","text_3","consequent_2","div_6","node_3","button_4","root_7","div_7","p_3","p_4","consequent_3","p_5","root_8","text_7","p_6","root_9","text_8","p_7","root_10","span_3","text_9","ul","root_11","av","li","root_12","button_5","node_6","img","root_13","span_4","root_14","text_11","span_5","root_15","svg","path_1","consequent_7","consequent_8","alternate","div_8","p_8","p_9","consequent_4","consequent_5","consequent_6","alternate_1","$2","consequent_9","$$value","navMenuOpen","writable","hamburger","$navMenuOpen","scrolled","onScroll","isAuthed","userEmail","initial","avatarUrl","storeMode","activeMode","v","pathname","seg","prefix","isArena","isArenaDeck","navHidden","isStudioPage","navHiddenMobileOnly","isFeed","isChatInbox","isBetSection","feedTab","pickMode","store","pageMode","accent","$accentOverride","getModeAccent","ACCENT_CLASSES","cls","dotColor","btnBg","btnHover","btnText","nav","a_1","a_2","text_1","a_3","a_4","text_5","a_5","text_6","a_6","a_7","a_8","$.first_child","fragment_1","NotificationBell","a_9","node_4","a_10","fragment_3","text_12","node_5","openAuthSheet","a_11","fragment_4","text_13","a_12","text_14","node_7","$1","a_13","fragment_6","text_15","a_14","text_16","node_8","fragment_8","node_9","groupCreateOpen","text_17","$9","consequent_10","BG","$activeMode","barH","dismiss","$$props","$.bind_element_size","isRater","queueOpen","myFeaturedDuels","feedActive","duelActive","arenaActive","beatActive","messagesActive","navPath","navigating","feedPending","duelPending","arenaPending","beatPending","messagesPending","hidden","showQueueDot","showNotifDot","showChatDot","onMessagesClick","span_6","span_7","span_8","span_9","svg_1","span_10","span_11","span_12","classes_1","classes_2","classes_3","classes_4","$6","navRow","r","$.noop","on","isActive","close","albumsRow","b","fragment","small","myAlbums","b_1","small_1","sellsAlbums","accentInk","tokens","level","clanName","clanTag","clanAvatarPath","isSubscribed","previewTier","devPreviewTier","subscriptionTier","isCreator","isVerified","isAmbassador","isAffiliate","isAdmin","isModerator","RING_C","xpPct","floor","xpForLevel","showAuth","raterStatus","showRaterConsole","queueLive","queueBusy","toggleQueue","ok","deserialize","invalidateAll","stats","liveContest","contestEnds","days","superseded","isMobile","onMount","mq","onChange","PLAY","DISCOVER","match","onKeydown","signOut","createSupabaseBrowserClient","resetTracking","prev","circle","DuelAvatar","UserBadge","ClanEmblem","span_13","span_14","b_2","span_15","span_16","span_17","b_3","small_2","PRO_FROM_PER_MONTH","PRO_MONTHLY_TOKENS","span_18","b_4","small_3","text_19","PLUS_FROM_PER_MONTH","PLUS_MONTHLY_TOKENS","node_10","span_19","TokenCoin","span_20","span_21","span_22","span_23","fragment_5","span_24","span_25","span_26","span_27","span_28","span_29","span_30","span_31","node_13","root_16","span_32","b_5","small_4","node_14","alternate_2","div_9","span_33","span_34","span_35","text_32","alternate_3","div_10","span_36","$.each","node_15","item","div_11","span_37","node_16","root_20","node_17","span_38","text_36","root_21","text_38","$installed","div_12","node_18","LanguagePicker","node_19","a_15","fragment_9","consequent_11","fade","fly","cubicOut","consequent_12","AuthSheet","node_21","chatCount","badgeLabel","progress","visible","timer","$.set_style","loggedIn","isStandalone","analytics","receiveHandoff","replaceState","started","start","events","fallbackTimer","afterNavigate","$.head","HEARTBEAT_MS","mounted","ping","sendOffline","onVisibility","VISIBLE_MS","current","lastShownId","dismissTimer","$latestToast","tap","href","quickMute","applyMuteFromToast","RECONNECTING_DELAY_MS","OFFLINE_DELAY_MS","STARTUP_SUPPRESS_MS","startedAt","visibleState","delayTimer","$connectionState","pending","delay","heart","bubble","userPlus","svg_2","tapRow","icon","GAP_MS","LS_KEY","closing","recap","last","now","sinceIso","qs","revealed","locked","plural","s","FAKE_AVATARS","h2","shown","fragment_2","$.index","g","CookieConsent","studioRoute","STUDIO_ROUTE","studioPage","basePath","pageLocale","LOCALE_INFO","announcement","announcementDismissed","barHidden","showBar","realtimeUserId","disposeGlobalRealtime","initGlobalRealtime","onStudioHost","pornpilot","siteOrigin","studioOrigin","SITE_URL","translated","canonicalPath","studioCanonicalPath","localizePath","seo","getStudioSeo","getSeo","alternates","LOCALES","l","canonical","canonicalUrl","noindex","robotsNoindex","ogImage","DEFAULT_OG_IMAGE","showFooter","jsonLdTag","orgJsonLd","ORGANIZATION_SCHEMA","siteJsonLd","WEBSITE_SCHEMA","studioJsonLd","studioOrganizationSchema","studioWebsiteSchema","studioAppSchema","markHydrated","unsubscribe","initPwa","hashParams","searchParams","isQueryAuthError","params","errorCode","error","isSpentHandshake","cleanHash","reason","description","supabase","subscription","session","replayAutoplayVideos","onPageShow","mediaEvents","onMediaReady","onUserGesture","ev","closeAuthSheet","replay","meta","link","alt","link_1","link_2","meta_1","meta_2","EXPLICIT_UGC_PATH","meta_3","meta_4","meta_5","meta_6","meta_7","meta_8","meta_9","meta_10","meta_11","meta_12","meta_13","meta_14","STUDIO_BRAND","SITE_NAME","NavigationProgress","node_11","SuspendedBanner","AnnouncementBar","Navbar","main","BottomNav","NavMenu","$authSheet","ChatFab","Footer","CookieConsent_1","PushPromptRedirect","node_22","RaterPresenceHeartbeat","Toast","ConnectionIndicator","consequent_13","WelcomeBackModal","consequent_14","LazyAnalytics","node_27"],"sources":["../../../../../../src/lib/i18n/messages.ts","../../../../../../src/routes/+layout.ts","../../../../../../src/lib/studio-link.ts","../../../../../../src/lib/components/NotificationBell.svelte","../../../../../../src/lib/stores/navMenu.ts","../../../../../../src/lib/components/Navbar.svelte","../../../../../../src/lib/components/AnnouncementBar.svelte","../../../../../../src/lib/components/SuspendedBanner.svelte","../../../../../../src/lib/components/BottomNav.svelte","../../../../../../src/lib/components/NavMenu.svelte","../../../../../../src/lib/components/ChatFab.svelte","../../../../../../src/lib/components/NavigationProgress.svelte","../../../../../../src/lib/components/PushPromptRedirect.svelte","../../../../../../src/lib/components/LazyAnalytics.svelte","../../../../../../src/lib/components/rater/RaterPresenceHeartbeat.svelte","../../../../../../src/lib/components/realtime/Toast.svelte","../../../../../../src/lib/components/realtime/ConnectionIndicator.svelte","../../../../../../src/lib/components/WelcomeBackModal.svelte","../../../../../../src/routes/+layout.svelte"],"sourcesContent":["import type { Locale } from './locales';\nimport type { Messages } from './translate';\n\n/**\n * One JSON file per language and area (`messages/de/games.json`). Each is its\n * own chunk, so a page only downloads its language and its areas, and English\n * downloads nothing.\n */\nconst files = import.meta.glob<{ default: Messages }>('./messages/*/*.json');\n\n/** Areas a path needs on top of `common`. Keyed by the first path segment. */\nconst AREAS: Record<string, string[]> = {\n\t'': ['home'],\n\tgames: ['games'],\n\ttools: ['tools'],\n\tstats: ['tools'],\n\trate: ['rate'],\n\t// Epic R: the uploader /rate shares, and on two of them beat me's lobby\n\t'rate-my-boobs': ['rate', 'duel'],\n\t'rate-my-ass': ['rate', 'duel'],\n\t'rate-my-nudes': ['rate'],\n\tr: ['raters'],\n\t// RAT-595/596: the long-form guide pages, kept out of the shared areas\n\tsph: ['guides'],\n\t'dick-rating-sites': ['guides'],\n\td: ['duel'],\n\tb: ['duel'],\n\tpricing: ['pricing'],\n\ttokens: ['pricing'],\n\tauth: ['auth'],\n\tf: ['app'],\n\tc: ['app'],\n\ta: ['app'],\n\tn: ['app'],\n\tu: ['app'],\n\tp: ['app'],\n\tk: ['app'],\n\tl: ['app'],\n\tpost: ['app'],\n\tnotifications: ['app'],\n\tprofile: ['app'],\n\tonboard: ['app'],\n\talbums: ['app'],\n\talbum: ['app'],\n\troleplay: ['app'],\n\t// the live table deals cards from the deck, shown in each player's language\n\ttod: ['games', 'decks'],\n\tconquest: ['games'],\n\tpoker: ['games']\n};\n\n/** Pages that need one more area than their section: the party decks are\n *  big, so only the pages that deal them load them. */\nconst PAGE_AREAS: Record<string, string[]> = {\n\t'/games/tod/generator': ['decks'],\n\t'/games/would-you-rather': ['decks'],\n\t'/games/never-have-i-ever': ['decks']\n};\n\n/** `app` holds the shared components (posts, chat, duels, the paywall…), which\n *  turn up on every kind of page, so it loads everywhere; a section adds its\n *  own area on top. */\nexport function namespacesFor(path: string): string[] {\n\tconst first = path.split('/')[1] ?? '';\n\tconst clean = path.replace(/\\/$/, '');\n\tconst section = (AREAS[first] ?? []).filter((a) => a !== 'app');\n\treturn ['common', 'app', ...section, ...(PAGE_AREAS[clean] ?? [])];\n}\n\nexport async function loadMessages(locale: Locale, namespaces: string[]): Promise<Messages> {\n\tif (locale === 'en') return {};\n\tconst parts = await Promise.all(\n\t\tnamespaces.map((ns) => {\n\t\t\tconst load = files[`./messages/${locale}/${ns}.json`];\n\t\t\treturn load ? load().then((m) => m.default) : Promise.resolve({});\n\t\t})\n\t);\n\treturn Object.assign({}, ...parts);\n}\n","import { browser } from '$app/environment';\nimport { loadMessages, namespacesFor } from '$lib/i18n/messages';\nimport {\n\tDEFAULT_LOCALE,\n\tLOCALE_COOKIE,\n\tisLocale,\n\tsplitLocalePath,\n\ttype Locale\n} from '$lib/i18n/locales';\nimport { isLocalizedPath } from '$lib/i18n/localized-paths';\nimport type { LayoutLoad } from './$types';\n\n/** The language cookie as the browser has it now: the picker can change it\n *  after the server's data was loaded. */\nfunction cookieLocale(): Locale | null {\n\tconst m = new RegExp(`(?:^|;\\\\s*)${LOCALE_COOKIE}=([a-z]{2})`).exec(document.cookie);\n\treturn m && isLocale(m[1]) ? m[1] : null;\n}\n\n/**\n * RAT-573: the page's language, and its strings. Same rule as the server hook:\n * a translated page is in the language of its address (no prefix = English);\n * every other page follows the visitor's cookie. Worked out here as well as on\n * the server because the server's layout data is not reloaded on a client-side\n * navigation — /de/games/tod → /es/games/tod would keep German.\n *\n * English needs no strings (its text is the key); the others load one small\n * JSON chunk per area.\n */\nexport const load: LayoutLoad = async ({ data, url }) => {\n\tconst { locale: prefixed, path } = splitLocalePath(url.pathname);\n\tconst fromServer = isLocale(data.locale) ? data.locale : DEFAULT_LOCALE;\n\tconst locale: Locale = isLocalizedPath(path)\n\t\t? (prefixed ?? DEFAULT_LOCALE)\n\t\t: browser\n\t\t\t? (cookieLocale() ?? fromServer)\n\t\t\t: fromServer;\n\tconst messages = locale === DEFAULT_LOCALE ? {} : await loadMessages(locale, namespacesFor(path));\n\t// `i18n`, not `messages`: chat and session pages load their own `messages`,\n\t// and a page's data shadows the layout's — t() then read the chat array\n\treturn { ...data, locale, i18n: messages };\n};\n","/** A signed-in member's click on a studio link (RAT-526). Client-safe.\n *\n *  Studio links stay `/studio` in the markup. That is the address search\n *  engines follow, and its 301 to the studio's domain is the permanent move\n *  they should go on seeing. A browser, though, keeps a 301 for good: after one\n *  click it goes straight to the studio's domain and never asks ratemyd again,\n *  so a member who had followed it once could never be handed their sign-in.\n *  So their click goes by /go/studio, a per-visit 302 that carries it. Nothing\n *  changes for anyone signed out, for a new tab, or on the studio's own domain. */\nimport { isStudioHost } from '$lib/studio-host';\n\nexport const STUDIO_PATH = '/studio';\nexport const STUDIO_HOP = '/go/studio';\n\n/** Where this click should go instead, or null to leave it alone. */\nexport function studioHopFor(e: MouseEvent, origin: string): string | null {\n\tif (e.defaultPrevented || e.button !== 0) return null;\n\tif (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return null;\n\tconst a = (e.target as Element | null)?.closest?.('a[href]') as HTMLAnchorElement | null;\n\tif (!a || (a.target && a.target !== '_self') || a.hasAttribute('download')) return null;\n\tconst url = new URL(a.getAttribute('href') ?? '', origin);\n\tif (url.origin !== origin) return null;\n\tif (url.pathname !== STUDIO_PATH && url.pathname !== `${STUDIO_PATH}/`) return null;\n\treturn `${STUDIO_HOP}${url.search}`;\n}\n\n/** Listen, before SvelteKit's own link handling, for a signed-in click on a\n *  studio link. Returns the cleanup. */\nexport function routeStudioLinks(\n\tsignedIn: () => boolean,\n\tgo: (to: string) => void = (to) => location.assign(to)\n): () => void {\n\tif (isStudioHost(location.hostname)) return () => {};\n\tconst onClick = (e: MouseEvent) => {\n\t\tif (!signedIn()) return;\n\t\tconst to = studioHopFor(e, location.origin);\n\t\tif (!to) return;\n\t\te.preventDefault();\n\t\tgo(to);\n\t};\n\twindow.addEventListener('click', onClick, true);\n\treturn () => window.removeEventListener('click', onClick, true);\n}\n","<script lang=\"ts\">\n\timport { t } from '$lib/i18n';\n\timport { payoutUsd } from '$lib/payout-rate';\n\timport { goto } from '$app/navigation';\n\timport { page } from '$app/state';\n\timport { getAvatarUrl } from '$lib/avatar';\n\timport { unreadNotifications, unreadChat } from '$lib/stores/unreadNotifications';\n\timport { realtime } from '$lib/realtime/client';\n\timport { notificationPrefs, type NotificationPrefs } from '$lib/stores/global-realtime';\n\timport {\n\t\ttrackNotificationDropdownOpened,\n\t\ttrackNotificationClicked,\n\t\ttrackChallengeNotificationShown,\n\t\ttrackChallengeNotificationClicked\n\t} from '$lib/analytics';\n\n\tinterface Props {\n\t\tuserId?: string | null;\n\t}\n\n\tlet { userId = null }: Props = $props();\n\n\t// RAT-360 (F4): badge counts come from the shared stores that\n\t// `$lib/stores/global-realtime.ts` bumps live off the root-layout\n\t// `user:${userId}` subscription. The bell reads them directly — no\n\t// duplicate sub, no 5s polling. Initial fetch on mount + a\n\t// visibility-change refresh cover the cold start and the cross-tab\n\t// \"I read these elsewhere\" reconcile; the broadcast pathway covers\n\t// everything in between.\n\t//\n\t// On the inbox itself the chat half is left out, of the badge and of the\n\t// dropdown. The page under the bell is that list, and the dropdown's\n\t// messages row linked back to it: a tap changed nothing and the badge\n\t// stayed up. The bottom nav's messages dot still shows chat unread there.\n\tconst onInbox = $derived(page.url.pathname === '/c');\n\tconst badgeTotal = $derived($unreadNotifications + (onInbox ? 0 : $unreadChat));\n\n\ttype NotificationItem = {\n\t\tid: string;\n\t\ttype: string;\n\t\tpayload: Record<string, unknown>;\n\t\treadAt: string | null;\n\t\tcreatedAt: string;\n\t};\n\n\tlet open = $state(false);\n\tlet items = $state<NotificationItem[] | null>(null);\n\tlet loading = $state(false);\n\tlet loadError = $state('');\n\n\t// RAT-365: in-dropdown mute settings panel toggle.\n\tlet settingsOpen = $state(false);\n\n\t/** RAT-365 / RAT-368: the bell dropdown only handles the boolean\n\t *  category toggles. Per-conversation mute (mutedThreadIds /\n\t *  mutedRoomIds, RAT-366) goes through the chat / group settings\n\t *  panels, not here — narrowing the key type keeps that boundary\n\t *  honest at compile time. */\n\ttype BellMuteKey = 'muteAll' | 'muteDMs' | 'muteGroups' | 'muteNotifications';\n\n\t/** Optimistic update + non-blocking POST. Failure path leaves the store\n\t *  as-is; the next invalidateAll will reconcile against the server row. */\n\tasync function setPref(key: BellMuteKey, value: boolean) {\n\t\tnotificationPrefs.update((p) => {\n\t\t\tconst next: NotificationPrefs = { ...p };\n\t\t\tif (value) next[key] = true;\n\t\t\telse delete next[key];\n\t\t\treturn next;\n\t\t});\n\t\ttry {\n\t\t\tawait fetch('/api/profile/notification-prefs', {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: { 'content-type': 'application/json' },\n\t\t\t\tbody: JSON.stringify({ [key]: value })\n\t\t\t});\n\t\t} catch {\n\t\t\t// optimistic — server reconciles on next load\n\t\t}\n\t}\n\n\tasync function refreshUnreadCount() {\n\t\tif (!userId) return;\n\t\ttry {\n\t\t\tconst res = await fetch('/api/notifications/unread-count');\n\t\t\tif (!res.ok) return;\n\t\t\t// RAT-300: endpoint returns { count, notifications, chat }.\n\t\t\t// Server is the authoritative source; write straight to the\n\t\t\t// shared stores so a cross-tab read or a missed broadcast\n\t\t\t// reconciles cleanly.\n\t\t\tconst body = (await res.json()) as {\n\t\t\t\tcount?: number;\n\t\t\t\tnotifications?: number;\n\t\t\t\tchat?: number;\n\t\t\t};\n\t\t\tif (typeof body.notifications === 'number') unreadNotifications.set(body.notifications);\n\t\t\telse if (typeof body.count === 'number') unreadNotifications.set(body.count);\n\t\t\tif (typeof body.chat === 'number') unreadChat.set(body.chat);\n\t\t} catch {\n\t\t\t// transient network blip — next visibility tick re-tries. Don't\n\t\t\t// reset the badge on failure.\n\t\t}\n\t}\n\n\t// Initial fetch + visibility-change refresh. The 5s polling that used\n\t// to live here (RAT-247) is retired now that the layout-level realtime\n\t// sub bumps the counters live.\n\t$effect(() => {\n\t\tif (!userId) return;\n\t\trefreshUnreadCount();\n\t\tconst onVisible = () => {\n\t\t\tif (document.visibilityState === 'visible') refreshUnreadCount();\n\t\t};\n\t\tdocument.addEventListener('visibilitychange', onVisible);\n\t\treturn () => {\n\t\t\tdocument.removeEventListener('visibilitychange', onVisible);\n\t\t};\n\t});\n\n\tasync function loadItems() {\n\t\tloading = true;\n\t\tloadError = '';\n\t\ttry {\n\t\t\tconst res = await fetch('/api/notifications?limit=20');\n\t\t\tif (!res.ok) {\n\t\t\t\tloadError = res.status === 401 ? 'auth' : 'failed';\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst body = (await res.json()) as {\n\t\t\t\tnotifications: NotificationItem[];\n\t\t\t\tunreadCount: number;\n\t\t\t};\n\t\t\titems = body.notifications;\n\t\t\tfireShownEvents(body.notifications);\n\t\t} catch {\n\t\t\tloadError = 'network';\n\t\t} finally {\n\t\t\tloading = false;\n\t\t}\n\t}\n\n\t// RAT-9: type-specific shown event for duel challenges (alongside the generic\n\t// notification_dropdown_opened). Deduped per notification id via sessionStorage\n\t// so opening the dropdown twice doesn't double-count.\n\tfunction fireShownEvents(list: NotificationItem[]) {\n\t\tfor (const n of list) {\n\t\t\tif (n.type !== 'duel_challenge') continue;\n\t\t\tconst key = `ph_challenge_notif_shown_${n.id}`;\n\t\t\ttry {\n\t\t\t\tif (sessionStorage.getItem(key)) continue;\n\t\t\t\tsessionStorage.setItem(key, '1');\n\t\t\t} catch {\n\t\t\t\t// privacy mode — fire anyway, accept duplicates\n\t\t\t}\n\t\t\tconst p = n.payload ?? {};\n\t\t\ttrackChallengeNotificationShown({\n\t\t\t\tnotificationId: n.id,\n\t\t\t\tduelCode: typeof p.duelCode === 'string' ? p.duelCode : null,\n\t\t\t\tchallengerUserId:\n\t\t\t\t\ttypeof p.challengerUserId === 'string' ? p.challengerUserId : null\n\t\t\t});\n\t\t}\n\t}\n\n\tfunction toggle() {\n\t\tif (open) {\n\t\t\topen = false;\n\t\t\treturn;\n\t\t}\n\t\topen = true;\n\t\ttrackNotificationDropdownOpened({ unreadCount: $unreadNotifications });\n\t\t// Always reload — items cached from a previous open can be stale even\n\t\t// when realtime is wired (channel hiccups, tab focus loss, etc.).\n\t\tloadItems();\n\t}\n\n\t// A press anywhere outside the bell closes the dropdown, and the press goes\n\t// on to whatever it landed on. This used to be a transparent full-screen\n\t// button that ate the tap: the next thing people tried — a conversation, the\n\t// live ticker under the header — took a second tap, and the rage clicks on\n\t// that button came from 230 sessions on /c alone. (On desktop the nav's\n\t// backdrop blur also made it the containing block for that `fixed` button,\n\t// so it only ever covered the nav, and a click on the page didn't close the\n\t// dropdown at all.) Capture phase and pointerdown, so it runs before any\n\t// handler below it can stop the event, and before the click it precedes.\n\tlet root = $state<HTMLDivElement | null>(null);\n\t$effect(() => {\n\t\tif (!open) return;\n\t\tconst onPointerDown = (e: PointerEvent) => {\n\t\t\tif (e.target instanceof Node && root?.contains(e.target)) return;\n\t\t\topen = false;\n\t\t};\n\t\tdocument.addEventListener('pointerdown', onPointerDown, true);\n\t\treturn () => document.removeEventListener('pointerdown', onPointerDown, true);\n\t});\n\n\t// RAT-360 (F4): the badge counter is bumped centrally by global-realtime.\n\t// We still need a per-bell sub to keep the dropdown's `items` array fresh\n\t// when a new notification arrives WITH the dropdown already loaded —\n\t// otherwise the badge clicks open onto a stale list. Subscribes to the\n\t// same `user:${userId}` channel as the global handler; the RealtimeClient\n\t// primitive refcounts SDK channels so this is free (no extra socket).\n\t$effect(() => {\n\t\tif (!userId) return;\n\t\tconst unsub = realtime.subscribe<{\n\t\t\tnotificationId?: string;\n\t\t\ttype?: string;\n\t\t\tpayload?: Record<string, unknown>;\n\t\t\tcreatedAt?: string;\n\t\t}>(`user:${userId}`, (event) => {\n\t\t\tif (event.type !== 'notification_new') return;\n\t\t\tconst p = event.payload;\n\t\t\tif (!p.notificationId || !p.type || !p.createdAt) return;\n\t\t\tif (!items) return; // dropdown never opened — nothing to prepend\n\t\t\tif (items.some((n) => n.id === p.notificationId)) return; // dedup\n\t\t\titems = [\n\t\t\t\t{\n\t\t\t\t\tid: p.notificationId,\n\t\t\t\t\ttype: p.type,\n\t\t\t\t\tpayload: p.payload ?? {},\n\t\t\t\t\treadAt: null,\n\t\t\t\t\tcreatedAt: p.createdAt\n\t\t\t\t},\n\t\t\t\t...items\n\t\t\t];\n\t\t});\n\t\treturn unsub;\n\t});\n\n\tasync function handleClick(n: NotificationItem) {\n\t\ttrackNotificationClicked({ type: n.type, notificationId: n.id });\n\t\tif (n.type === 'duel_challenge') {\n\t\t\tconst p = n.payload ?? {};\n\t\t\ttrackChallengeNotificationClicked({\n\t\t\t\tnotificationId: n.id,\n\t\t\t\tduelCode: typeof p.duelCode === 'string' ? p.duelCode : null,\n\t\t\t\tchallengerUserId:\n\t\t\t\t\ttypeof p.challengerUserId === 'string' ? p.challengerUserId : null\n\t\t\t});\n\t\t}\n\n\t\t// RAT-231: reading deletes the row server-side, so remove it from the\n\t\t// dropdown optimistically. The list now only shows unread by definition.\n\t\titems = items?.filter((x) => x.id !== n.id) ?? items;\n\t\t// Decrement the shared badge store locally — next visibility-change\n\t\t// refresh reconciles against the authoritative server count if anything\n\t\t// drifted. Floor at 0 in case the badge was already stale.\n\t\tif (!n.readAt) unreadNotifications.update((c) => Math.max(0, c - 1));\n\t\tfetch(`/api/notifications/${encodeURIComponent(n.id)}/read`, { method: 'POST' }).catch(\n\t\t\t() => {}\n\t\t);\n\n\t\tconst target = resolveTarget(n);\n\t\topen = false;\n\t\tif (target) await goto(target);\n\t}\n\n\tasync function markAllRead() {\n\t\tconst res = await fetch('/api/notifications/read-all', { method: 'POST' });\n\t\tif (!res.ok) return;\n\t\t// RAT-231: mark-all-read deletes server-side. Clear the local list too.\n\t\titems = [];\n\t\tunreadNotifications.set(0);\n\t\tunreadChat.set(0);\n\t}\n\n\tfunction resolveTarget(n: NotificationItem): string | null {\n\t\tconst p = n.payload ?? {};\n\t\tif (n.type === 'duel_challenge') {\n\t\t\tconst code = p.duelCode;\n\t\t\treturn typeof code === 'string' && code ? `/d/${encodeURIComponent(code)}` : null;\n\t\t}\n\t\tif (n.type === 'duel_xp') {\n\t\t\tconst code = p.code;\n\t\t\treturn typeof code === 'string' && code ? `/d/${encodeURIComponent(code)}` : null;\n\t\t}\n\t\tif (n.type === 'clan_join_request') {\n\t\t\tconst tag = p.clanTag;\n\t\t\treturn typeof tag === 'string' && tag ? `/k/${encodeURIComponent(tag)}/manage` : '/k';\n\t\t}\n\t\t// Clan wars: every war notification lands on the war page.\n\t\tif (n.type === 'niche_post_removed') {\n\t\t\tconst slug = p.slug;\n\t\t\treturn typeof slug === 'string' && slug ? `/n/${encodeURIComponent(slug)}` : '/f';\n\t\t}\n\t\tif (n.type === 'niche_role') {\n\t\t\tconst slug = p.slug;\n\t\t\treturn typeof slug === 'string' && slug ? `/n/${encodeURIComponent(slug)}` : '/f';\n\t\t}\n\t\tif (n.type.startsWith('clan_war_')) {\n\t\t\tconst code = p.code;\n\t\t\treturn typeof code === 'string' && code ? `/k/war/${encodeURIComponent(code)}` : '/k';\n\t\t}\n\t\tif (n.type.startsWith('tod_')) {\n\t\t\tconst code = p.code;\n\t\t\treturn typeof code === 'string' && code ? `/tod/${encodeURIComponent(code)}` : '/tod';\n\t\t}\n\t\tif (n.type === 'new_follower') {\n\t\t\tconst nickname = p.followerNickname;\n\t\t\treturn typeof nickname === 'string' && nickname\n\t\t\t\t? `/u/${encodeURIComponent(nickname)}`\n\t\t\t\t: null;\n\t\t}\n\t\t// RAT-83: rater got a new request → land them on the queue tab.\n\t\tif (n.type === 'rate_request_queued') {\n\t\t\treturn '/p?tab=rater';\n\t\t}\n\t\t// RAT-84: requester gets pinged when the session starts → straight in.\n\t\tif (n.type === 'rate_session_started') {\n\t\t\tconst sessionId = p.sessionId;\n\t\t\treturn typeof sessionId === 'string' && sessionId\n\t\t\t\t? `/rate/session/${encodeURIComponent(sessionId)}`\n\t\t\t\t: null;\n\t\t}\n\t\t// RAT-203 / RAT-231 fix: missing target → bell click was a no-op.\n\t\tif (n.type === 'new_post') {\n\t\t\tconst postId = p.postId;\n\t\t\tif (typeof postId === 'string' && postId) return `/post/${encodeURIComponent(postId)}`;\n\t\t\tconst creatorNickname = p.creatorNickname;\n\t\t\treturn typeof creatorNickname === 'string' && creatorNickname\n\t\t\t\t? `/u/${encodeURIComponent(creatorNickname)}`\n\t\t\t\t: null;\n\t\t}\n\t\t// RAT-533: the reset ping → the arena, admirers first.\n\t\tif (n.type === 'arena_admirers') return '/a?admirers=first';\n\t\t// RAT-231: aggregated beat comment ping → land on the duel page.\n\t\tif (n.type === 'beat_new_comment' || n.type === 'beat_accepted' || n.type === 'beat_closed') {\n\t\t\tconst code = p.challengeCode;\n\t\t\treturn typeof code === 'string' && code ? `/b/${encodeURIComponent(code)}` : null;\n\t\t}\n\t\t// Comments 2.0: comment / reply on a feed post → the post page.\n\t\tif (n.type === 'post_comment' || n.type === 'post_comment_reply') {\n\t\t\tconst postId = p.postId;\n\t\t\treturn typeof postId === 'string' && postId ? `/post/${encodeURIComponent(postId)}` : '/f';\n\t\t}\n\t\t// RAT-350: @mention in chat. DM → /c/<sender>; group → /g/<inviteCode>.\n\t\tif (n.type === 'chat_mention') {\n\t\t\tif (p.chatKind === 'group') {\n\t\t\t\tconst code = p.inviteCode;\n\t\t\t\treturn typeof code === 'string' && code ? `/g/${encodeURIComponent(code)}` : null;\n\t\t\t}\n\t\t\tconst nick = p.senderNickname;\n\t\t\treturn typeof nick === 'string' && nick ? `/c/${encodeURIComponent(nick)}` : null;\n\t\t}\n\t\t// arena mutual match → land them in the 1:1 chat with the match (or the\n\t\t// inbox matches rail if the nickname is missing).\n\t\tif (n.type === 'arena_match') {\n\t\t\tconst nick = p.matchNickname;\n\t\t\treturn typeof nick === 'string' && nick ? `/c/${encodeURIComponent(nick)}` : '/c';\n\t\t}\n\t\t// RAT-412: super emote → open the arena deck, where the sender is surfaced first.\n\t\tif (n.type === 'arena_super_vote') {\n\t\t\treturn '/a';\n\t\t}\n\t\t// RAT-310: peer rate request → open the DM with the requester to rate it.\n\t\t// RAT-176 follow-up: route by the STABLE threadId in the payload, not the\n\t\t// nickname snapshot baked in at send time. The nickname goes stale on a\n\t\t// rename and 404s outright once the requester deletes — `/c/t/<threadId>`\n\t\t// resolves the requester's current handle server-side (and falls back to\n\t\t// the inbox if the thread is gone). Legacy notifications without a threadId\n\t\t// keep the nickname path.\n\t\tif (n.type === 'peer_rate_request') {\n\t\t\tconst threadId = p.threadId;\n\t\t\tif (typeof threadId === 'string' && threadId) return `/c/t/${encodeURIComponent(threadId)}`;\n\t\t\tconst nick = p.requesterNickname;\n\t\t\treturn typeof nick === 'string' && nick ? `/c/${encodeURIComponent(nick)}` : '/c';\n\t\t}\n\t\tif (n.type === 'achievement_earned') {\n\t\t\treturn '/p?tab=achievements';\n\t\t}\n\t\tif (n.type === 'album_sold' || n.type === 'payout_update') {\n\t\t\treturn '/p?tab=cashout';\n\t\t}\n\t\tif (n.type === 'album_new') {\n\t\t\tconst albumId = p.albumId;\n\t\t\treturn typeof albumId === 'string' && albumId ? `/album/${encodeURIComponent(albumId)}` : '/albums';\n\t\t}\n\t\tif (n.type === 'creator_live') {\n\t\t\tconst sid = p.sessionId;\n\t\t\treturn typeof sid === 'string' && sid ? `/livecam/${encodeURIComponent(sid)}` : '/livecam';\n\t\t}\n\t\tif (n.type === 'profile_viewed') {\n\t\t\t// Straight to the activity tab — the \"who viewed you\" card lives\n\t\t\t// there; landing on the profile root made the user hunt for it.\n\t\t\treturn '/p?tab=activity';\n\t\t}\n\t\tif (n.type === 'subscription_gifted' || n.type === 'tokens_gifted') {\n\t\t\treturn '/p';\n\t\t}\n\t\tif (n.type === 'rp_character_message') {\n\t\t\tconst slug = p.slug;\n\t\t\tif (typeof slug !== 'string' || !slug) return null;\n\t\t\tconst t = typeof p.threadNo === 'number' && p.threadNo > 1 ? `&t=${p.threadNo}` : '';\n\t\t\treturn `/roleplay/c/${encodeURIComponent(slug)}?from=chat${t}`;\n\t\t}\n\t\t// Tier list rank + feed mentions all land on the post itself.\n\t\tif (n.type === 'tierlist_included' || n.type === 'post_mention' || n.type === 'comment_mention') {\n\t\t\tconst postId = p.postId;\n\t\t\treturn typeof postId === 'string' && postId ? `/post/${encodeURIComponent(postId)}` : '/f';\n\t\t}\n\t\t// Animation clips land on their own page (player + comments + unlock).\n\t\t// Partner feature: request lands you in the DM (the tile is there),\n\t\t// accept → their profile (the chip is live), end → your /p profile tab.\n\t\tif (n.type === 'partner_request') {\n\t\t\tconst nick = p.nickname;\n\t\t\treturn typeof nick === 'string' && nick ? `/c/${encodeURIComponent(nick)}` : '/c';\n\t\t}\n\t\tif (n.type === 'partner_accepted') {\n\t\t\tconst nick = p.nickname;\n\t\t\treturn typeof nick === 'string' && nick ? `/u/${encodeURIComponent(nick)}` : '/f';\n\t\t}\n\t\tif (n.type === 'partner_ended') {\n\t\t\treturn '/p?tab=profile';\n\t\t}\n\t\tif (n.type === 'animation_ready' || n.type === 'animation_failed') {\n\t\t\tconst animId = p.animationId;\n\t\t\tif (typeof animId === 'string' && animId) return `/animations/${encodeURIComponent(animId)}`;\n\t\t\tconst postId = p.postId;\n\t\t\treturn typeof postId === 'string' && postId ? `/post/${encodeURIComponent(postId)}` : '/f';\n\t\t}\n\t\treturn null;\n\t}\n\n\tfunction summary(n: NotificationItem): string {\n\t\tconst p = n.payload ?? {};\n\t\tif (n.type === 'album_new') {\n\t\t\tconst name = typeof p.creatorNickname === 'string' ? p.creatorNickname : '';\n\t\t\tconst title = typeof p.albumTitle === 'string' ? p.albumTitle : '';\n\t\t\tif (name && title) return t('@{name} posted a new album: “{title}”', { name, title });\n\t\t\treturn p.reason === 'follow'\n\t\t\t\t? t('new album from a creator you follow')\n\t\t\t\t: t('new album from a creator you bought from');\n\t\t}\n\t\tif (n.type === 'album_sold') {\n\t\t\tconst buyer = typeof p.buyerNickname === 'string' && p.buyerNickname ? p.buyerNickname : t('someone');\n\t\t\tconst title = typeof p.albumTitle === 'string' && p.albumTitle ? p.albumTitle : t('your album');\n\t\t\tconst amount = payoutUsd(typeof p.tokens === 'number' ? p.tokens : 0);\n\t\t\t// several albums in one purchase (the creator bundle)\n\t\t\tif (typeof p.albumCount === 'number' && p.albumCount > 1)\n\t\t\t\treturn t('{buyer} bought {n} of your albums · +{amount}', { buyer, n: p.albumCount, amount });\n\t\t\treturn t('{buyer} bought \"{title}\" · +{amount}', { buyer, title, amount });\n\t\t}\n\t\tif (n.type === 'payout_update') {\n\t\t\tconst amt = typeof p.amountCents === 'number' ? `$${(p.amountCents / 100).toFixed(2)}` : '';\n\t\t\tif (p.status === 'paid') return t('your cash out {amount} was sent 💸', { amount: amt });\n\t\t\tif (p.status === 'rejected') return t('cash out rejected — tokens refunded');\n\t\t\treturn t('cash out requested {amount} · we wire it within a few business days', { amount: amt });\n\t\t}\n\t\tif (n.type === 'duel_challenge') {\n\t\t\tconst name = typeof p.challengerNickname === 'string' ? p.challengerNickname : '';\n\t\t\treturn name ? t('{name} challenged you to a duel', { name }) : t('someone challenged you to a duel');\n\t\t}\n\t\tif (n.type === 'duel_xp') {\n\t\t\tconst xp = typeof p.xp === 'number' ? p.xp : 0;\n\t\t\treturn t('you earned +{xp} XP from your duel →', { xp });\n\t\t}\n\t\tif (n.type === 'clan_join_request') {\n\t\t\tconst who = typeof p.applicantNickname === 'string' ? p.applicantNickname : '';\n\t\t\tconst cn = typeof p.clanName === 'string' ? p.clanName : t('your clan');\n\t\t\treturn who\n\t\t\t\t? t('@{name} applied to {clan} · review →', { name: who, clan: cn })\n\t\t\t\t: t('someone applied to {clan} · review →', { clan: cn });\n\t\t}\n\t\tif (n.type === 'new_follower') {\n\t\t\tconst name = typeof p.followerNickname === 'string' ? p.followerNickname : '';\n\t\t\treturn name ? t('{name} started following you', { name }) : t('someone started following you');\n\t\t}\n\t\tif (n.type === 'rate_request_queued') {\n\t\t\tconst name = typeof p.requesterNickname === 'string' ? p.requesterNickname : '';\n\t\t\treturn name\n\t\t\t\t? t('@{name} wants a rate · start it →', { name })\n\t\t\t\t: t('someone wants a rate · start it →');\n\t\t}\n\t\tif (n.type === 'rate_session_started') {\n\t\t\treturn t('your rate is live · join now →');\n\t\t}\n\t\tif (n.type === 'new_post') {\n\t\t\tconst name = typeof p.creatorNickname === 'string' ? p.creatorNickname : '';\n\t\t\treturn name ? t('@{name} posted', { name }) : t('new post from a creator you follow');\n\t\t}\n\t\t// RAT-527\n\t\tif (n.type === 'beat_accepted') return t('someone took your beat · the vote is live →');\n\t\tif (n.type === 'beat_closed') {\n\t\t\tconst f = typeof p.votesFor === 'number' ? p.votesFor : 0;\n\t\t\tconst a = typeof p.votesAgainst === 'number' ? p.votesAgainst : 0;\n\t\t\tconst score = `${f}–${a}`;\n\t\t\tif (p.result === 'won') return t('you won your beat, {score} →', { score });\n\t\t\tif (p.result === 'tie') return t('your beat ended in a tie, {score} →', { score });\n\t\t\treturn t('your beat is over, {score} · see it →', { score });\n\t\t}\n\t\tif (n.type === 'arena_admirers') {\n\t\t\tconst c = typeof p.count === 'number' ? p.count : 1;\n\t\t\treturn c === 1\n\t\t\t\t? t('1 person liked you · like them back →')\n\t\t\t\t: t('{n} people liked you · like them back →', { n: c });\n\t\t}\n\t\tif (n.type === 'beat_new_comment') {\n\t\t\tconst count = typeof p.commentCount === 'number' ? p.commentCount : 1;\n\t\t\treturn count > 1\n\t\t\t\t? t('{n} new comments on your beat duel', { n: count })\n\t\t\t\t: t('new comment on your beat duel');\n\t\t}\n\t\tif (n.type === 'post_comment') {\n\t\t\tconst name = typeof p.commenterNickname === 'string' ? p.commenterNickname : '';\n\t\t\tconst count = typeof p.count === 'number' ? p.count : 1;\n\t\t\tif (count > 1) {\n\t\t\t\treturn name\n\t\t\t\t\t? t('@{name} and others · {n} new comments on your post →', { name, n: count })\n\t\t\t\t\t: t('{n} new comments on your post →', { n: count });\n\t\t\t}\n\t\t\treturn name ? t('@{name} commented on your post →', { name }) : t('new comment on your post →');\n\t\t}\n\t\tif (n.type === 'post_comment_reply') {\n\t\t\tconst name = typeof p.commenterNickname === 'string' ? p.commenterNickname : '';\n\t\t\tconst tail = typeof p.preview === 'string' && p.preview ? ` · \"${p.preview.slice(0, 60)}\"` : '';\n\t\t\treturn (name ? t('@{name} replied to your comment', { name }) : t('someone replied to your comment')) + tail;\n\t\t}\n\t\t// RAT-350: @mention. Slightly different copy for DM vs group so the\n\t\t// landing context is unambiguous when the user opens the notification.\n\t\tif (n.type === 'chat_mention') {\n\t\t\tconst name = typeof p.senderNickname === 'string' ? p.senderNickname : '';\n\t\t\tconst room = p.chatKind === 'group' && typeof p.roomName === 'string' && p.roomName ? p.roomName : '';\n\t\t\tconst tail = typeof p.bodyExcerpt === 'string' && p.bodyExcerpt ? ` · \"${p.bodyExcerpt}\"` : '';\n\t\t\tconst head =\n\t\t\t\tp.chatKind === 'group'\n\t\t\t\t\t? room\n\t\t\t\t\t\t? name\n\t\t\t\t\t\t\t? t('@{name} mentioned you in {room}', { name, room })\n\t\t\t\t\t\t\t: t('someone mentioned you in {room}', { room })\n\t\t\t\t\t\t: name\n\t\t\t\t\t\t\t? t('@{name} mentioned you in a group chat', { name })\n\t\t\t\t\t\t\t: t('someone mentioned you in a group chat')\n\t\t\t\t\t: name\n\t\t\t\t\t\t? t('@{name} mentioned you', { name })\n\t\t\t\t\t\t: t('someone mentioned you');\n\t\t\treturn head + tail;\n\t\t}\n\t\tif (n.type === 'arena_match') {\n\t\t\tconst name = typeof p.matchNickname === 'string' ? p.matchNickname : '';\n\t\t\treturn name ? t('you matched with @{name} · say hi →', { name }) : t('new match · say hi →');\n\t\t}\n\t\tif (n.type === 'arena_super_vote') {\n\t\t\tconst name = typeof p.senderNickname === 'string' ? p.senderNickname : '';\n\t\t\treturn name ? t('@{name} super-emoted you ⭐ · see →', { name }) : t('someone super-emoted you ⭐ · see →');\n\t\t}\n\t\tif (n.type === 'peer_rate_request') {\n\t\t\tconst name = typeof p.requesterNickname === 'string' ? p.requesterNickname : '';\n\t\t\treturn name ? t('@{name} wants you to rate their pic →', { name }) : t('someone wants you to rate their pic →');\n\t\t}\n\t\tif (n.type === 'achievement_earned') {\n\t\t\tconst badge = typeof p.title === 'string' ? p.title : '';\n\t\t\treturn badge ? t('achievement unlocked · {badge} →', { badge }) : t('achievement unlocked →');\n\t\t}\n\t\tif (n.type === 'creator_live') {\n\t\t\tconst name = typeof p.raterNickname === 'string' ? p.raterNickname : '';\n\t\t\treturn name ? t('@{name} is live now 🔴 · watch →', { name }) : t('someone you follow is live 🔴');\n\t\t}\n\t\tif (n.type === 'profile_viewed') {\n\t\t\treturn t('someone viewed your profile 👀 · see who →');\n\t\t}\n\t\tif (n.type === 'subscription_gifted') {\n\t\t\tconst name = typeof p.gifterNickname === 'string' ? p.gifterNickname : '';\n\t\t\treturn name ? t('@{name} gifted you Pro 🎁', { name }) : t('you got gifted Pro 🎁');\n\t\t}\n\t\tif (n.type === 'tokens_gifted') {\n\t\t\tconst name = typeof p.gifterNickname === 'string' ? p.gifterNickname : '';\n\t\t\tconst amt = typeof p.amount === 'number' ? p.amount : 30;\n\t\t\treturn name\n\t\t\t\t? t('@{name} gifted you {n} tokens 🎁', { name, n: amt })\n\t\t\t\t: t('you got gifted {n} tokens 🎁', { n: amt });\n\t\t}\n\t\tif (n.type === 'rp_character_message') {\n\t\t\tconst who = typeof p.characterTitle === 'string' ? p.characterTitle : t('your character');\n\t\t\treturn t('{name} texted you first · read it →', { name: who });\n\t\t}\n\t\tif (n.type === 'niche_post_removed') {\n\t\t\tconst slug = typeof p.slug === 'string' ? p.slug : '?';\n\t\t\treturn `${t('a moderator of /n/{slug} removed your post', { slug })}${typeof p.reason === 'string' && p.reason ? ` · ${p.reason}` : ''}`;\n\t\t}\n\t\tif (n.type === 'niche_role') {\n\t\t\tconst slug = typeof p.slug === 'string' ? p.slug : '?';\n\t\t\treturn p.role === 'owner'\n\t\t\t\t? t(\"you're now an owner of /n/{slug} →\", { slug })\n\t\t\t\t: t(\"you're now a moderator of /n/{slug} →\", { slug });\n\t\t}\n\t\tif (n.type === 'tod_invite') {\n\t\t\tconst h = typeof p.hostNickname === 'string' ? p.hostNickname : t('someone');\n\t\t\treturn t('@{name} wants you at the truth or dare table · take a seat →', { name: h });\n\t\t}\n\t\tconst table = typeof p.name === 'string' ? p.name : t('your table');\n\t\tif (n.type === 'tod_started') return t('{name} just started · get in there →', { name: table });\n\t\tif (n.type === 'tod_turn') return t('the bottle landed on you · truth or dare →');\n\t\tif (n.type === 'tod_over')\n\t\t\treturn typeof p.xp === 'number'\n\t\t\t\t? t('{name} is over · {xp} xp →', { name: table, xp: `${p.xp > 0 ? '+' : ''}${p.xp}` })\n\t\t\t\t: t('{name} is over · see the board →', { name: table });\n\t\tif (n.type === 'tod_can_start') return t('3 at the table · {name} starts in 10 min →', { name: table });\n\t\tif (n.type === 'tod_table_full') return t('{name} is full · start it →', { name: table });\n\t\tif (n.type === 'tod_player_joined')\n\t\t\treturn typeof p.count === 'number'\n\t\t\t\t? t('{n} at the table · {name} →', { n: p.count, name: table })\n\t\t\t\t: t('someone sat down · {name} →', { name: table });\n\t\tif (n.type === 'clan_war_raid') {\n\t\t\tconst tag = typeof p.targetTag === 'string' ? p.targetTag : '?';\n\t\t\treturn t('raid on #{tag} is open · apply with a photo →', { tag });\n\t\t}\n\t\tif (n.type === 'clan_war_request') {\n\t\t\tconst tag = typeof p.attackerTag === 'string' ? p.attackerTag : '?';\n\t\t\treturn typeof p.stake === 'number' && p.stake > 0\n\t\t\t\t? t('#{tag} declared war on your clan · {n} tokens on the line →', { tag, n: p.stake })\n\t\t\t\t: t('#{tag} declared war on your clan →', { tag });\n\t\t}\n\t\tif (n.type === 'clan_war_accepted') {\n\t\t\tconst tag = typeof p.otherTag === 'string' ? p.otherTag : '?';\n\t\t\treturn t('war vs #{tag} is on · the fight is scheduled →', { tag });\n\t\t}\n\t\tif (n.type === 'clan_war_declined') {\n\t\t\tconst tag = typeof p.defenderTag === 'string' ? p.defenderTag : '?';\n\t\t\treturn t('#{tag} declined the war · stake refunded', { tag });\n\t\t}\n\t\tif (n.type === 'clan_war_forfeit') {\n\t\t\tconst tag = typeof p.otherTag === 'string' ? p.otherTag : '?';\n\t\t\tif (p.self) return t('your clan missed the war vs #{tag} · no-show mark until you win one', { tag });\n\t\t\treturn p.dodged\n\t\t\t\t? t('#{tag} dodged · you win by default →', { tag })\n\t\t\t\t: t(\"#{tag} didn't show · you win by default →\", { tag });\n\t\t}\n\t\tif (n.type === 'clan_war_result') {\n\t\t\tconst tag = typeof p.otherTag === 'string' ? p.otherTag : '?';\n\t\t\tconst score = typeof p.score === 'string' ? ` ${p.score}` : '';\n\t\t\treturn p.won\n\t\t\t\t? t('war won · beat #{tag}{score} · replay →', { tag, score })\n\t\t\t\t: t('war lost · fell to #{tag}{score} · replay →', { tag, score });\n\t\t}\n\t\tif (n.type === 'partner_request') {\n\t\t\tconst name = typeof p.nickname === 'string' ? p.nickname : '';\n\t\t\treturn name ? t('@{name} wants you as their partner →', { name }) : t('someone wants you as their partner →');\n\t\t}\n\t\tif (n.type === 'partner_accepted') {\n\t\t\tconst name = typeof p.nickname === 'string' ? p.nickname : '';\n\t\t\treturn name ? t(\"@{name} said yes. you're taken.\", { name }) : t(\"they said yes. you're taken.\");\n\t\t}\n\t\tif (n.type === 'partner_ended') {\n\t\t\tconst name = typeof p.nickname === 'string' ? p.nickname : '';\n\t\t\treturn name ? t('@{name} ended the relationship', { name }) : t('your partner ended the relationship');\n\t\t}\n\t\tif (n.type === 'tierlist_included') {\n\t\t\tconst name = typeof p.authorNickname === 'string' ? p.authorNickname : '';\n\t\t\tconst tier = typeof p.tier === 'string' ? p.tier : '';\n\t\t\tif (name) {\n\t\t\t\treturn tier\n\t\t\t\t\t? t('@{name} put you in {tier} tier on their tier list · see →', { name, tier })\n\t\t\t\t\t: t('@{name} put you on their tier list · see →', { name });\n\t\t\t}\n\t\t\treturn tier\n\t\t\t\t? t('you got ranked in {tier} tier on a tier list · see →', { tier })\n\t\t\t\t: t('you got ranked on a tier list · see →');\n\t\t}\n\t\tif (n.type === 'post_mention') {\n\t\t\tconst name = typeof p.authorNickname === 'string' ? p.authorNickname : '';\n\t\t\tconst tail = typeof p.preview === 'string' && p.preview ? ` · \"${p.preview.slice(0, 60)}\"` : '';\n\t\t\treturn (name ? t('@{name} mentioned you in a post', { name }) : t('someone mentioned you in a post')) + tail;\n\t\t}\n\t\tif (n.type === 'animation_ready') {\n\t\t\treturn t('your animation is ready · watch →');\n\t\t}\n\t\tif (n.type === 'animation_failed') {\n\t\t\treturn t('animation failed — your tokens are refunded');\n\t\t}\n\t\tif (n.type === 'comment_mention') {\n\t\t\tconst name = typeof p.commenterNickname === 'string' ? p.commenterNickname : '';\n\t\t\tconst tail = typeof p.preview === 'string' && p.preview ? ` · \"${p.preview.slice(0, 60)}\"` : '';\n\t\t\treturn (\n\t\t\t\t(name ? t('@{name} mentioned you in a comment', { name }) : t('someone mentioned you in a comment')) + tail\n\t\t\t);\n\t\t}\n\t\treturn n.type.replace(/_/g, ' ');\n\t}\n\n\t// RAT-231: pick the right avatar for a notification — actor (the user who\n\t// triggered it) when relevant, otherwise null = the type icon fallback.\n\tfunction avatar(n: NotificationItem): { src: string | null; initial: string } {\n\t\tconst p = n.payload ?? {};\n\t\tconst pathKey =\n\t\t\tn.type === 'duel_challenge'\n\t\t\t\t? 'challengerAvatarPath'\n\t\t\t\t: n.type === 'new_follower'\n\t\t\t\t\t? 'followerAvatarPath'\n\t\t\t\t\t: n.type === 'rate_request_queued'\n\t\t\t\t\t\t? 'requesterAvatarPath'\n\t\t\t\t\t\t: n.type === 'rate_session_started'\n\t\t\t\t\t\t\t? 'raterAvatarPath'\n\t\t\t\t\t\t\t: n.type === 'new_post' || n.type === 'album_new'\n\t\t\t\t\t\t\t\t? 'creatorAvatarPath'\n\t\t\t\t\t\t\t\t: n.type === 'post_comment' || n.type === 'post_comment_reply' || n.type === 'comment_mention'\n\t\t\t\t\t\t\t\t\t? 'commenterAvatarPath'\n\t\t\t\t\t\t\t\t\t: n.type === 'chat_mention' || n.type === 'arena_super_vote'\n\t\t\t\t\t\t\t\t\t? 'senderAvatarPath'\n\t\t\t\t\t\t\t\t\t: n.type === 'tierlist_included' || n.type === 'post_mention'\n\t\t\t\t\t\t\t\t\t? 'authorAvatarPath'\n\t\t\t\t\t\t\t\t\t: n.type === 'arena_match'\n\t\t\t\t\t\t\t\t\t\t? 'matchAvatarPath'\n\t\t\t\t\t\t\t\t\t\t: n.type === 'peer_rate_request'\n\t\t\t\t\t\t\t\t\t\t\t? 'requesterAvatarPath'\n\t\t\t\t\t\t\t\t\t\t\t: n.type === 'creator_live'\n\t\t\t\t\t\t\t\t\t\t\t\t? 'raterAvatarPath'\n\t\t\t\t\t\t\t\t\t\t\t\t: n.type === 'partner_request' || n.type === 'partner_accepted' || n.type === 'partner_ended'\n\t\t\t\t\t\t\t\t\t\t\t\t\t? 'avatarPath'\n\t\t\t\t\t\t\t\t\t\t\t\t\t: null;\n\t\tconst nickKey =\n\t\t\tn.type === 'duel_challenge'\n\t\t\t\t? 'challengerNickname'\n\t\t\t\t: n.type === 'new_follower'\n\t\t\t\t\t? 'followerNickname'\n\t\t\t\t\t: n.type === 'rate_request_queued'\n\t\t\t\t\t\t? 'requesterNickname'\n\t\t\t\t\t\t: n.type === 'rate_session_started'\n\t\t\t\t\t\t\t? 'raterNickname'\n\t\t\t\t\t\t\t: n.type === 'new_post' || n.type === 'album_new'\n\t\t\t\t\t\t\t\t? 'creatorNickname'\n\t\t\t\t\t\t\t\t: n.type === 'post_comment' || n.type === 'post_comment_reply' || n.type === 'comment_mention'\n\t\t\t\t\t\t\t\t\t? 'commenterNickname'\n\t\t\t\t\t\t\t\t\t: n.type === 'chat_mention' || n.type === 'arena_super_vote'\n\t\t\t\t\t\t\t\t\t? 'senderNickname'\n\t\t\t\t\t\t\t\t\t: n.type === 'tierlist_included' || n.type === 'post_mention'\n\t\t\t\t\t\t\t\t\t? 'authorNickname'\n\t\t\t\t\t\t\t\t\t: n.type === 'arena_match'\n\t\t\t\t\t\t\t\t\t\t? 'matchNickname'\n\t\t\t\t\t\t\t\t\t\t: n.type === 'peer_rate_request'\n\t\t\t\t\t\t\t\t\t\t\t? 'requesterNickname'\n\t\t\t\t\t\t\t\t\t\t\t: n.type === 'creator_live'\n\t\t\t\t\t\t\t\t\t\t\t\t? 'raterNickname'\n\t\t\t\t\t\t\t\t\t\t\t\t: n.type === 'partner_request' || n.type === 'partner_accepted' || n.type === 'partner_ended'\n\t\t\t\t\t\t\t\t\t\t\t\t\t? 'nickname'\n\t\t\t\t\t\t\t\t\t\t\t\t\t: null;\n\t\tconst path = pathKey && typeof p[pathKey] === 'string' ? (p[pathKey] as string) : null;\n\t\tconst nickname = nickKey && typeof p[nickKey] === 'string' ? (p[nickKey] as string) : '';\n\t\treturn {\n\t\t\tsrc: getAvatarUrl(path),\n\t\t\tinitial: nickname ? nickname.charAt(0).toUpperCase() : '?'\n\t\t};\n\t}\n\n\tfunction typeIcon(type: string): string {\n\t\t// SVG path strings, rendered inside a 20x20 viewBox in the markup.\n\t\tif (type === 'duel_challenge')\n\t\t\treturn 'M5 3l4 7-4 7M19 3l-4 7 4 7M9 10h6'; // crossed swords-ish\n\t\tif (type === 'beat_new_comment') return 'M4 5h12v8H6l-2 3z'; // speech bubble\n\t\tif (type === 'beat_accepted' || type === 'beat_closed')\n\t\t\treturn 'M5 3l4 7-4 7M19 3l-4 7 4 7M9 10h6'; // crossed swords\n\t\tif (type === 'rp_character_message') return 'M4 5h12v8H6l-2 3z'; // speech bubble\n\t\tif (type === 'arena_admirers') return 'M10 16s-6-4-6-8a3 3 0 016-1 3 3 0 016 1c0 4-6 8-6 8z'; // heart\n\t\tif (type === 'arena_match') return 'M10 16s-6-4-6-8a3 3 0 016-1 3 3 0 016 1c0 4-6 8-6 8z'; // heart\n\t\tif (type === 'partner_request' || type === 'partner_accepted' || type === 'partner_ended')\n\t\t\treturn 'M10 16s-6-4-6-8a3 3 0 016-1 3 3 0 016 1c0 4-6 8-6 8z'; // heart\n\t\tif (type.startsWith('clan_war_')) return 'M5 3l4 7-4 7M19 3l-4 7 4 7M9 10h6'; // crossed swords (clan wars)\n\t\tif (type === 'arena_super_vote')\n\t\t\treturn 'M10 1l2.6 6.1 6.6.5-5 4.3 1.5 6.4L10 15.4l-5.7 3.9 1.5-6.4-5-4.3 6.6-.5z'; // star\n\t\treturn 'M10 3a7 7 0 017 7v3l1 2H2l1-2v-3a7 7 0 017-7zM8 17a2 2 0 004 0'; // bell\n\t}\n\n\tfunction ago(iso: string): string {\n\t\tconst ms = Date.now() - new Date(iso).getTime();\n\t\tconst min = Math.floor(ms / 60000);\n\t\tif (min < 1) return t('now');\n\t\tif (min < 60) return t('{n}m', { n: min });\n\t\tconst h = Math.floor(min / 60);\n\t\tif (h < 24) return t('{n}h', { n: h });\n\t\tconst d = Math.floor(h / 24);\n\t\treturn t('{n}d', { n: d });\n\t}\n\n\tfunction onKey(e: KeyboardEvent) {\n\t\tif (e.key === 'Escape' && open) open = false;\n\t}\n</script>\n\n<svelte:window onkeydown={onKey} />\n\n<div class=\"relative\" bind:this={root}>\n\t<button\n\t\ttype=\"button\"\n\t\tonclick={toggle}\n\t\taria-label={t('Notifications')}\n\t\taria-expanded={open}\n\t\tclass=\"relative flex h-9 w-9 items-center justify-center rounded-full text-muted transition-colors hover:bg-surface hover:text-text\"\n\t>\n\t\t<svg class=\"h-5 w-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"1.8\">\n\t\t\t<path\n\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\td=\"M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0\"\n\t\t\t/>\n\t\t</svg>\n\t\t{#if badgeTotal > 0}\n\t\t\t<!-- RAT-300: badge sums notification table unread + chat unread\n\t\t\t\t (chat left out on /c — see onInbox). The dropdown below splits\n\t\t\t\t them into two surfaces (messages row → /c, notifications list\n\t\t\t\t below). -->\n\t\t\t<span\n\t\t\t\tclass=\"absolute -right-0.5 -top-0.5 flex min-w-[1.1rem] items-center justify-center rounded-full bg-coral px-1 text-[10px] font-bold leading-[1.1rem] text-white\"\n\t\t\t>\n\t\t\t\t{badgeTotal > 99 ? '99+' : badgeTotal}\n\t\t\t</span>\n\t\t{/if}\n\t</button>\n\n\t{#if open}\n\t\t<div\n\t\t\tclass=\"absolute right-0 z-50 mt-2 w-[min(22rem,calc(100vw-4rem))] origin-top-right overflow-hidden rounded-xl border border-border bg-bg shadow-2xl\"\n\t\t\trole=\"dialog\"\n\t\t\taria-label={t('Notifications')}\n\t\t>\n\t\t\t<div class=\"flex items-center justify-between border-b border-border/60 px-4 py-3\">\n\t\t\t\t<p class=\"font-display text-sm font-bold tracking-tight\">{t('notifications')}</p>\n\t\t\t\t<div class=\"flex items-center gap-3\">\n\t\t\t\t\t{#if $unreadNotifications > 0 || $unreadChat > 0}\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tclass=\"text-[11px] font-semibold text-muted transition-colors hover:text-text\"\n\t\t\t\t\t\t\tonclick={markAllRead}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{t('mark all read')}\n\t\t\t\t\t\t</button>\n\t\t\t\t\t{/if}\n\t\t\t\t\t<!-- RAT-365: open the inline mute-toggle panel. -->\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclass=\"flex h-5 w-5 items-center justify-center text-muted transition-colors hover:text-text\"\n\t\t\t\t\t\tonclick={() => (settingsOpen = !settingsOpen)}\n\t\t\t\t\t\taria-label={t('notification settings')}\n\t\t\t\t\t\taria-expanded={settingsOpen}\n\t\t\t\t\t>\n\t\t\t\t\t\t<svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n\t\t\t\t\t\t\t<circle cx=\"12\" cy=\"12\" r=\"3\" />\n\t\t\t\t\t\t\t<path d=\"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z\" />\n\t\t\t\t\t\t</svg>\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{#if settingsOpen}\n\t\t\t\t<!-- RAT-365: per-user mute toggles. Optimistic write to the store +\n\t\t\t\t\t non-blocking POST. muteAll wins over per-channel flags. -->\n\t\t\t\t<div class=\"border-b border-border/60 bg-surface/40 px-4 py-3\">\n\t\t\t\t\t<p class=\"mb-2 text-[11px] font-semibold uppercase tracking-wide text-muted\">{t('notification settings')}</p>\n\t\t\t\t\t<div class=\"flex flex-col gap-2\">\n\t\t\t\t\t\t{#each [\n\t\t\t\t\t\t\t{ key: 'muteAll' as const, label: t('mute everything') },\n\t\t\t\t\t\t\t{ key: 'muteDMs' as const, label: t('mute DMs') },\n\t\t\t\t\t\t\t{ key: 'muteGroups' as const, label: t('mute groups') },\n\t\t\t\t\t\t\t{ key: 'muteNotifications' as const, label: t('mute pop-ups') }\n\t\t\t\t\t\t] as toggle (toggle.key)}\n\t\t\t\t\t\t\t{@const checked = !!$notificationPrefs[toggle.key]}\n\t\t\t\t\t\t\t<label class=\"flex cursor-pointer items-center justify-between gap-3 text-[13px] text-text\">\n\t\t\t\t\t\t\t\t<span class:opacity-40={$notificationPrefs.muteAll && toggle.key !== 'muteAll'}>{toggle.label}</span>\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\trole=\"switch\"\n\t\t\t\t\t\t\t\t\taria-checked={checked}\n\t\t\t\t\t\t\t\t\taria-label={toggle.label}\n\t\t\t\t\t\t\t\t\tclass=\"relative h-5 w-9 shrink-0 rounded-full border border-border transition-colors {checked ? 'bg-coral' : 'bg-surface'}\"\n\t\t\t\t\t\t\t\t\tonclick={() => setPref(toggle.key, !checked)}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<span class=\"absolute top-[1px] left-[1px] block h-[16px] w-[16px] rounded-full bg-white transition-transform {checked ? 'translate-x-[16px]' : ''}\"></span>\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t{/each}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t{/if}\n\n\t\t\t<div class=\"bell-scroll max-h-[60vh] overflow-y-auto\">\n\t\t\t\t<!-- RAT-300: messages row. Only renders when there's chat unread;\n\t\t\t\t\t tapping closes the dropdown and routes to /c where the\n\t\t\t\t\t inbox markThreadRead on open will zero out the badge on the\n\t\t\t\t\t next 5s tick. Sits above the notification list because chat\n\t\t\t\t\t = synchronous-ish interaction, notifications = passive. Not\n\t\t\t\t\t on /c, which is where it leads (see onInbox). -->\n\t\t\t\t{#if $unreadChat > 0 && !onInbox}\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclass=\"flex w-full items-center gap-3 border-b border-border/30 bg-coral/[.04] px-4 py-3 text-left transition-colors hover:bg-surface/50\"\n\t\t\t\t\t\tonclick={() => { open = false; goto('/c'); }}\n\t\t\t\t\t>\n\t\t\t\t\t\t<span class=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-coral/15 text-coral\" aria-hidden=\"true\">\n\t\t\t\t\t\t\t<svg viewBox=\"0 0 24 24\" class=\"h-4 w-4\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n\t\t\t\t\t\t\t\t<path d=\"M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z\"/>\n\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<div class=\"min-w-0 flex-1\">\n\t\t\t\t\t\t\t<p class=\"text-[13px] font-semibold text-text\">{t('messages')}</p>\n\t\t\t\t\t\t\t<p class=\"text-[11px] text-muted\">\n\t\t\t\t\t\t\t\t{$unreadChat === 1 ? t('1 new message') : t('{n} new messages', { n: $unreadChat })}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<svg viewBox=\"0 0 24 24\" class=\"h-4 w-4 shrink-0 text-muted\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n\t\t\t\t\t\t\t<path d=\"M9 5l7 7-7 7\"/>\n\t\t\t\t\t\t</svg>\n\t\t\t\t\t</button>\n\t\t\t\t{/if}\n\t\t\t\t{#if loading && !items}\n\t\t\t\t\t<p class=\"px-4 py-8 text-center text-xs text-muted\">{t('loading…')}</p>\n\t\t\t\t{:else if loadError}\n\t\t\t\t\t<p class=\"px-4 py-8 text-center text-xs text-coral/70\">\n\t\t\t\t\t\t{t(\"couldn't load notifications. try again.\")}\n\t\t\t\t\t</p>\n\t\t\t\t{:else if !items || items.length === 0}\n\t\t\t\t\t<p class=\"px-4 py-8 text-center text-xs text-muted\">\n\t\t\t\t\t\t{t('no notifications yet.')}<br /><span class=\"text-muted/60\"\n\t\t\t\t\t\t\t>{t('this is where duel challenges and follows show up.')}</span\n\t\t\t\t\t\t>\n\t\t\t\t\t</p>\n\t\t\t\t{:else}\n\t\t\t\t\t<ul class=\"flex flex-col\">\n\t\t\t\t\t\t{#each items as n (n.id)}\n\t\t\t\t\t\t\t{@const av = avatar(n)}\n\t\t\t\t\t\t\t<li class=\"border-b border-border/30 last:border-b-0\">\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tonclick={() => handleClick(n)}\n\t\t\t\t\t\t\t\t\tclass=\"flex w-full items-start gap-3 bg-coral/[.04] px-4 py-3 text-left transition-colors hover:bg-surface/50\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{#if av.src}\n\t\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\t\tsrc={av.src}\n\t\t\t\t\t\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\t\t\t\t\t\tclass=\"h-9 w-9 shrink-0 rounded-full object-cover\"\n\t\t\t\t\t\t\t\t\t\t\tloading=\"lazy\"\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t{:else if av.initial !== '?'}\n\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\tclass=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-surface text-[13px] font-bold text-text\"\n\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t>{av.initial}</span>\n\t\t\t\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\tclass=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-surface text-muted\"\n\t\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\t\t\t\t\tviewBox=\"0 0 20 20\"\n\t\t\t\t\t\t\t\t\t\t\t\tclass=\"h-4 w-4\"\n\t\t\t\t\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\tstroke-width=\"1.6\"\n\t\t\t\t\t\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t<path d={typeIcon(n.type)} />\n\t\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t\t<div class=\"min-w-0 flex-1\">\n\t\t\t\t\t\t\t\t\t\t<p class=\"text-[13px] leading-snug text-text\">{summary(n)}</p>\n\t\t\t\t\t\t\t\t\t\t<p class=\"mt-0.5 text-[10px] uppercase tracking-wider text-muted/60\">\n\t\t\t\t\t\t\t\t\t\t\t{ago(n.createdAt)}\n\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t{/each}\n\t\t\t\t\t</ul>\n\t\t\t\t{/if}\n\t\t\t</div>\n\t\t</div>\n\t{/if}\n</div>\n\n<style>\n\t/* desktop scrollbar was browser-default white — a thin grey one on the dark panel */\n\t.bell-scroll {\n\t\tscrollbar-width: thin;\n\t\tscrollbar-color: rgba(255, 255, 255, 0.14) transparent;\n\t}\n\t.bell-scroll::-webkit-scrollbar { width: 6px; }\n\t.bell-scroll::-webkit-scrollbar-track { background: transparent; }\n\t.bell-scroll::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.14); border-radius: 3px; }\n\t.bell-scroll::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.24); }\n</style>\n","import { writable } from 'svelte/store';\n\n/**\n * Open state of the NavMenu drawer.\n *\n * A store rather than a bindable prop because two separate chrome surfaces\n * open the same drawer — the desktop Navbar hamburger and the mobile\n * BottomNav \"menu\" slot — while the drawer itself is mounted once in the\n * root layout, so there's no common parent to bind through.\n */\nexport const navMenuOpen = writable(false);\n","<script lang=\"ts\">\n\timport { t } from '$lib/i18n';\n\timport { page } from '$app/state';\n\timport { isStudioPage } from '$lib/studio-host';\n\timport { activeMode, getModeAccent, accentOverride, type ActiveMode } from '$lib/stores/activeMode';\n\timport { getAvatarUrl } from '$lib/avatar';\n\timport NotificationBell from '$lib/components/NotificationBell.svelte';\n\timport { groupCreateOpen } from '$lib/stores/chatUi';\n\timport { navMenuOpen } from '$lib/stores/navMenu';\n\timport { openAuthSheet } from '$lib/stores/authSheet';\n\n\t// RAT-272: mobile menu + rater queue toggle previously lived here moved\n\t// into BottomNav.svelte (mobile chrome) + /profile (rater settings card).\n\t// The old desktop \"get rated\" action picker was replaced by the nav\n\t// drawer; the button now links straight to /rate.\n\tlet scrolled = $state(false);\n\n\t$effect(() => {\n\t\tconst onScroll = () => {\n\t\t\tscrolled = window.scrollY > 10;\n\t\t};\n\t\tonScroll();\n\t\twindow.addEventListener('scroll', onScroll, { passive: true });\n\t\treturn () => window.removeEventListener('scroll', onScroll);\n\t});\n\n\t// Auth gate: prefer page.data.user over page.data.session.\n\t// Some +page.server.ts files (e.g. /rate, /d/[code] notFound) return an\n\t// explicit `session: null` for unauth fallbacks, which overrides the layout\n\t// session via SvelteKit's shallow data merge — even for authenticated users\n\t// landing on those pages. user is only set by the layout, never overridden,\n\t// so it's the reliable signal that the viewer is authed. (RAT-23)\n\tconst isAuthed = $derived(!!page.data.user);\n\tconst liveContest = $derived(\n\t\tpage.data.liveContest as {\n\t\t\tslug: string;\n\t\t\tweekNumber: number;\n\t\t\tkind: 'photo' | 'rater';\n\t\t\tprizeTiers: string[];\n\t\t} | null\n\t);\n\tconst tokens = $derived((page.data.tokens as number) ?? 0);\n\tconst lowTokens = $derived(tokens < 10);\n\tconst userEmail = $derived(page.data.user?.email ?? '');\n\tconst initial = $derived(userEmail.charAt(0).toUpperCase() || '?');\n\tconst avatarUrl = $derived(getAvatarUrl(page.data.avatarPath as string | null));\n\n\t// Subscribe to writable store reactively for /rate-mode (roast/hype) and other\n\t// in-page mode flips. Route-based modes (duel/arena) are derived directly from\n\t// the URL — relying on the activeMode store there caused timing bugs where the\n\t// navbar rendered with default styling on /a until a layout effect fired.\n\tlet storeMode = $state<string | null>(null);\n\t$effect(() => {\n\t\tconst unsub = activeMode.subscribe((v) => {\n\t\t\tstoreMode = v;\n\t\t});\n\t\treturn unsub;\n\t});\n\n\tconst pathname = $derived(page.url.pathname);\n\n\t// RAT-328: single-letter module match. `startsWith('/a')` would also catch\n\t// `/about`, `/admin`, `/api`. The seg() helper requires the next character\n\t// to be a path terminator so `/a` matches /a + /a/foo + /a?x=1 but never\n\t// /about. Reused throughout this file + BottomNav.\n\tfunction seg(path: string, prefix: string): boolean {\n\t\tif (path === prefix) return true;\n\t\tconst next = path.charAt(prefix.length);\n\t\treturn path.startsWith(prefix) && (next === '/' || next === '?' || next === '#');\n\t}\n\tconst isArena = $derived(seg(pathname, '/a'));\n\t// RAT-420: the swipe deck + likes grid own their top chrome (ArenaTopBar), so\n\t// drop the wordmark there on mobile to free the row for the segmented control.\n\tconst isArenaDeck = $derived(pathname === '/a' || pathname === '/a/likes');\n\n\t// Hide on focus-mode routes that own their own top chrome:\n\t//   • /auth/* uses a centered viewport; the navbar there is noise.\n\t//   • /rate/session/<id> is in-session rating focus mode.\n\t// BottomNav already hides on the same set (kept in sync).\n\tconst navHidden = $derived(\n\t\tpathname.startsWith('/rate/session/') ||\n\t\t\tpathname.startsWith('/auth/') ||\n\t\t\t// The studio is a full-viewport surface with its own chrome: its rail\n\t\t\t// owns the left edge, its composer owns the bottom, and the clip fills\n\t\t\t// what is left. A second nav above it takes a band off the picture and\n\t\t\t// offers the way out twice, since the rail already carries it. On its own\n\t\t\t// domain the studio is also `/`, so this goes by the route too.\n\t\t\tpathname.startsWith('/studio') ||\n\t\t\tpage.route.id === '/studio' ||\n\t\t\t// RAT-632: PornPilot's own pages (/prompts) draw the studio's header,\n\t\t\t// and ratemyd's links would only leave the domain from there.\n\t\t\tisStudioPage(page.route.id)\n\t\t// all livecam pages (verify / greenroom / room) keep the desktop navbar and\n\t\t// hide it only on mobile — see navHiddenMobileOnly below.\n\t);\n\n\t// Mobile-only hide for in-thread chat surfaces — DM threads\n\t// (/c/[nickname]) AND group rooms (/g/[code]). On mobile each thread's\n\t// own `.head` bar owns the top chrome and the global Navbar would be\n\t// wasted space (RAT-294 + RAT-322). On desktop the shared (chat)\n\t// layout renders a rounded master/detail container, so the global\n\t// Navbar above it is the only way back out of the chat module — keep\n\t// it visible (RAT-333 + RAT-337).\n\tconst navHiddenMobileOnly = $derived(\n\t\t(pathname.startsWith('/c/') && pathname !== '/c') ||\n\t\t\tpathname.startsWith('/g/') ||\n\t\t\t// RAT-463: a character chat is a thread like any other — its own `.bar`\n\t\t\t// owns the top on mobile, and the global navbar sat straight on top of it.\n\t\t\tpathname.startsWith('/roleplay/c/') ||\n\t\t\t// A character group room is the same contract: own header, own composer.\n\t\t\tpathname.startsWith('/roleplay/g/') ||\n\t\t\t// all livecam pages: full-screen / focus on mobile (own chrome), desktop navbar kept.\n\t\t\tpathname.startsWith('/livecam/') ||\n\t\t\t// RAT-441: the floor follows the same contract — its room renders its own\n\t\t\t// `.bar` on mobile and offsets under the navbar on desktop.\n\t\t\tpathname.startsWith('/floor/') ||\n\t\t\t// Arena feed for signed-out viewers: the page shows its own sign-in gate,\n\t\t\t// so the logo bar + chrome are noise until they're in. Desktop keeps nav.\n\t\t\t(pathname === '/a' && !isAuthed)\n\t);\n\n\t// RAT-282: on mobile the /f For You / Explore Niches tabs are lifted up\n\t// into this navbar (right of the logo). Mirror the feed page's ?tab logic\n\t// so the active pill matches what's rendered below.\n\tconst isFeed = $derived(seg(pathname, '/f'));\n\t// RAT-337: mobile-only \"+ group chat\" pill on /c inbox. The page\n\t// header that used to host it is gone on mobile; the pill lifts\n\t// the affordance to the navbar's right slot.\n\tconst isChatInbox = $derived(pathname === '/c');\n\t// RAT-386: beat me | bet tabs lift into the navbar (right of the logo) across\n\t// the beat lobby + bet sub-routes — same pattern as the /f feed tabs below.\n\tconst isBetSection = $derived(pathname === '/b' || pathname.startsWith('/b/bet'));\n\tconst feedTab = $derived(page.url.searchParams.get('tab') === 'niches' ? 'niches' : 'foryou');\n\n\t// RAT-327: per-module chrome accent. Store-set modes win (set explicitly\n\t// by each module's +page.svelte). Pathname fallback covers route-only\n\t// modes (arena / duel) that don't bother claiming a mode on mount + the\n\t// legacy rater ecosystem split (r-profiles + onboard + rate-session stay\n\t// 'rater'). RAT-328: single-letter prefixes use seg() to avoid swallowing\n\t// /about, /admin, /api, /pricing, /post, etc.\n\tfunction pickMode(path: string, store: string | null): ActiveMode {\n\t\tif (store) return store as ActiveMode;\n\t\tif (seg(path, '/a')) return 'arena';\n\t\tif (seg(path, '/d')) return 'duel';\n\t\tif (seg(path, '/b')) return 'beat';\n\t\tif (seg(path, '/f') || path.startsWith('/post/')) return 'feed';\n\t\tif (seg(path, '/n')) return 'niches';\n\t\t// /r is the raters directory index; /r/[nickname] (rater profiles +\n\t\t// /r/[nickname]/request) and /r/sign-in fall into the rater ecosystem.\n\t\tif (path === '/r') return 'raters';\n\t\tif (\n\t\t\tpath.startsWith('/r/') ||\n\t\t\tpath.startsWith('/rater/') ||\n\t\t\tpath.startsWith('/onboard/rater') ||\n\t\t\tpath.startsWith('/rate/session/')\n\t\t) {\n\t\t\treturn 'rater';\n\t\t}\n\t\tif (seg(path, '/l')) return 'leaderboard';\n\t\tif (seg(path, '/c')) return 'chat';\n\t\tif (seg(path, '/p')) return 'profile';\n\t\tif (path.startsWith('/u/')) return 'public-profile';\n\t\treturn null;\n\t}\n\tconst pageMode = $derived(pickMode(pathname, storeMode));\n\tconst accent = $derived($accentOverride ?? getModeAccent(pageMode));\n\n\t// Tailwind v4 needs to see literal class strings to compile them — keep\n\t// this table verbatim and never interpolate `bg-${name}`. Indexed by the\n\t// accent.tailwind value emitted by getModeAccent (one row per accent).\n\tconst ACCENT_CLASSES: Record<string, { dot: string; bg: string; hover: string; text: string }> = {\n\t\tcoral: { dot: 'text-coral', bg: 'bg-coral', hover: 'hover:bg-coral-dark', text: 'text-white' },\n\t\tyellow: {\n\t\t\tdot: 'text-yellow',\n\t\t\tbg: 'bg-yellow',\n\t\t\thover: 'hover:bg-yellow/80',\n\t\t\ttext: 'text-bg'\n\t\t},\n\t\tgreen: { dot: 'text-green', bg: 'bg-green', hover: 'hover:bg-green/80', text: 'text-bg' },\n\t\tblue: { dot: 'text-blue', bg: 'bg-blue', hover: 'hover:bg-blue/80', text: 'text-white' },\n\t\torange: {\n\t\t\tdot: 'text-orange',\n\t\t\tbg: 'bg-orange',\n\t\t\thover: 'hover:bg-orange-dark',\n\t\t\ttext: 'text-white'\n\t\t},\n\t\tpurple: {\n\t\t\tdot: 'text-purple',\n\t\t\tbg: 'bg-purple',\n\t\t\thover: 'hover:bg-purple/80',\n\t\t\ttext: 'text-white'\n\t\t},\n\t\tindigo: {\n\t\t\tdot: 'text-indigo',\n\t\t\tbg: 'bg-indigo',\n\t\t\thover: 'hover:bg-indigo-dark',\n\t\t\ttext: 'text-white'\n\t\t},\n\t\tcyan: {\n\t\t\tdot: 'text-cyan',\n\t\t\tbg: 'bg-cyan',\n\t\t\thover: 'hover:bg-cyan-dark',\n\t\t\ttext: 'text-white'\n\t\t},\n\t\tlime: { dot: 'text-lime', bg: 'bg-lime', hover: 'hover:bg-lime/80', text: 'text-bg' },\n\t\tpink: {\n\t\t\tdot: 'text-pink',\n\t\t\tbg: 'bg-pink',\n\t\t\thover: 'hover:bg-pink-dark',\n\t\t\ttext: 'text-white'\n\t\t},\n\t\tfuchsia: {\n\t\t\tdot: 'text-fuchsia',\n\t\t\tbg: 'bg-fuchsia',\n\t\t\thover: 'hover:bg-fuchsia-dark',\n\t\t\ttext: 'text-white'\n\t\t},\n\t\tviolet: {\n\t\t\tdot: 'text-violet',\n\t\t\tbg: 'bg-violet',\n\t\t\thover: 'hover:bg-violet-dark',\n\t\t\ttext: 'text-white'\n\t\t}\n\t};\n\tconst cls = $derived(ACCENT_CLASSES[accent.tailwind] ?? ACCENT_CLASSES.coral);\n\tconst dotColor = $derived(cls.dot);\n\tconst btnBg = $derived(cls.bg);\n\tconst btnHover = $derived(cls.hover);\n\tconst btnText = $derived(cls.text);\n</script>\n\n{#snippet hamburger()}\n\t<button\n\t\ttype=\"button\"\n\t\tclass=\"pointer-events-auto flex h-8 w-8 items-center justify-center rounded-lg text-muted transition-colors hover:text-text\"\n\t\taria-label={t('menu')}\n\t\taria-expanded={$navMenuOpen}\n\t\tonclick={() => navMenuOpen.set(true)}\n\t>\n\t\t<svg\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstroke-width=\"2\"\n\t\t\tstroke-linecap=\"round\"\n\t\t\tclass=\"h-5 w-5\"\n\t\t\taria-hidden=\"true\"\n\t\t>\n\t\t\t<line x1=\"3.5\" y1=\"7\" x2=\"20.5\" y2=\"7\" />\n\t\t\t<line x1=\"3.5\" y1=\"12\" x2=\"20.5\" y2=\"12\" />\n\t\t\t<line x1=\"3.5\" y1=\"17\" x2=\"20.5\" y2=\"17\" />\n\t\t</svg>\n\t</button>\n{/snippet}\n\n{#if !navHidden}\n\t<nav\n\t\tclass=\"fixed top-[var(--ann-h,0px)] z-50 w-full transition-[border-color] duration-200 md:pointer-events-auto md:bg-bg/70 md:backdrop-blur-2xl md:backdrop-saturate-150 {navHiddenMobileOnly\n\t\t\t? 'max-md:hidden'\n\t\t\t: ''} {isArena\n\t\t\t? 'pointer-events-none border-b border-transparent md:border-border md:bg-bg/70'\n\t\t\t: scrolled\n\t\t\t\t? 'border-b border-border bg-bg/95'\n\t\t\t\t: 'border-b border-border bg-bg/95'}\"\n\t>\n\t\t<div class=\"relative mx-auto flex h-16 max-w-5xl items-center justify-between px-5 md:px-0\">\n\t\t\t<a\n\t\t\t\thref=\"/\"\n\t\t\t\ttranslate=\"no\"\n\t\t\t\tclass=\"pointer-events-auto font-display text-xl font-bold tracking-tight text-text no-underline {isArenaDeck\n\t\t\t\t\t? 'max-md:hidden'\n\t\t\t\t\t: ''}\"\n\t\t\t>\n\t\t\t\tratemyd<span class={dotColor}>.</span>\n\t\t\t</a>\n\n\t\t\t<!-- RAT-446: the primary row is now data-picked. leaderboard (lowest\n\t\t\t\t reach, most desktop-leaning surface) and roleplay moved into the\n\t\t\t\t NavMenu; messages (/c — the biggest surface on the site by an\n\t\t\t\t order of magnitude) took the slot it never had. -->\n\t\t\t<div class=\"pointer-events-auto hidden items-center gap-6 md:flex\">\n\t\t\t\t<!-- First in the row, and dressed like the rest of it.\n\t\t\t\t\t It launched in full white to stand out, and standing white next to a\n\t\t\t\t\t white logo it read as part of the logo instead — \"ratemyd. make a\n\t\t\t\t\t film\", a tagline nobody wrote. The row's own order is the emphasis;\n\t\t\t\t\t the landing's first row does the selling. (animations gave up its\n\t\t\t\t\t slot so the row has room; the link is still in the NavMenu.) -->\n\t\t\t\t<!-- data-sveltekit-reload: /studio only redirects to the studio's\n\t\t\t\t\t domain, so a native load follows the 301 straight there instead of\n\t\t\t\t\t the router preloading data for a page it then leaves (RAT-561). -->\n\t\t\t\t<a\n\t\t\t\t\thref=\"/studio\"\n\t\t\t\t\tdata-sveltekit-reload\n\t\t\t\t\tclass=\"text-sm font-medium text-muted no-underline transition-colors hover:text-text\"\n\t\t\t\t\t>{t('make a film')}</a\n\t\t\t\t>\n\t\t\t\t<!-- RAT-569: the FREE badge went with the free-duel promo — duels are\n\t\t\t\t\t now 10 free, then Pro, so a blanket FREE would be untrue for anyone\n\t\t\t\t\t past their tenth. -->\n\t\t\t\t<a\n\t\t\t\t\thref=\"/d\"\n\t\t\t\t\tclass=\"text-sm font-medium text-muted no-underline transition-colors hover:text-text\"\n\t\t\t\t\t>{t('duel')}</a\n\t\t\t\t>\n\t\t\t\t<a\n\t\t\t\t\thref=\"/a\"\n\t\t\t\t\tclass=\"flex items-center gap-1.5 text-sm font-medium text-muted no-underline transition-colors hover:text-text\"\n\t\t\t\t\t>{t('arena')} <span\n\t\t\t\t\t\tclass=\"rounded bg-orange/15 px-1 py-0.5 text-[9px] leading-none font-bold text-orange\"\n\t\t\t\t\t\t>{t('NEW')}</span\n\t\t\t\t\t></a\n\t\t\t\t>\n\t\t\t\t<a\n\t\t\t\t\thref=\"/f\"\n\t\t\t\t\tclass=\"flex items-center gap-1.5 text-sm font-medium text-muted no-underline transition-colors hover:text-text\"\n\t\t\t\t\t>{t('feed')} <span\n\t\t\t\t\t\tclass=\"rounded bg-green/15 px-1 py-0.5 text-[9px] leading-none font-bold text-green\"\n\t\t\t\t\t\t>{t('NEW')}</span\n\t\t\t\t\t></a\n\t\t\t\t>\n\t\t\t\t<a\n\t\t\t\t\thref=\"/b\"\n\t\t\t\t\tclass=\"flex items-center gap-1.5 text-sm font-medium text-muted no-underline transition-colors hover:text-text\"\n\t\t\t\t\t>{t('beat me')} <span\n\t\t\t\t\t\tclass=\"rounded bg-yellow/15 px-1 py-0.5 text-[9px] leading-none font-bold text-yellow\"\n\t\t\t\t\t\t>{t('NEW')}</span\n\t\t\t\t\t></a\n\t\t\t\t>\n\t\t\t\t<a\n\t\t\t\t\thref=\"/c\"\n\t\t\t\t\tclass=\"text-sm font-medium text-muted no-underline transition-colors hover:text-text\"\n\t\t\t\t\t>{t('messages')}</a\n\t\t\t\t>\n\t\t\t\t<a\n\t\t\t\t\thref=\"/search\"\n\t\t\t\t\taria-label={t('search people and clans')}\n\t\t\t\t\tclass=\"flex h-8 w-8 items-center justify-center rounded-full text-muted no-underline transition-colors hover:text-text\"\n\t\t\t\t>\n\t\t\t\t\t<svg\n\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\tstroke-width=\"2\"\n\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\tclass=\"h-[18px] w-[18px]\"\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<circle cx=\"11\" cy=\"11\" r=\"7\" />\n\t\t\t\t\t\t<path d=\"M20 20l-3.5-3.5\" />\n\t\t\t\t\t</svg>\n\t\t\t\t</a>\n\t\t\t\t{#if isAuthed}\n\t\t\t\t\t<a\n\t\t\t\t\t\thref=\"/rate\"\n\t\t\t\t\t\tclass=\"text-sm font-medium text-muted no-underline transition-colors hover:text-text\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{t('get rated')}\n\t\t\t\t\t</a>\n\t\t\t\t\t<NotificationBell userId={page.data.user?.id ?? null} />\n\t\t\t\t\t<a\n\t\t\t\t\t\thref=\"/p\"\n\t\t\t\t\t\tclass=\"flex h-8 w-8 items-center justify-center overflow-hidden rounded-full {avatarUrl\n\t\t\t\t\t\t\t? 'bg-surface'\n\t\t\t\t\t\t\t: btnBg} font-display text-xs font-bold {btnText} no-underline transition-all hover:ring-2 hover:ring-white/20\"\n\t\t\t\t\t\ttitle={userEmail}\n\t\t\t\t\t>\n\t\t\t\t\t\t{#if avatarUrl}\n\t\t\t\t\t\t\t<img src={avatarUrl} alt=\"\" class=\"h-full w-full object-cover\" />\n\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t{initial}\n\t\t\t\t\t\t{/if}\n\t\t\t\t\t</a>\n\t\t\t\t\t{@render hamburger()}\n\t\t\t\t{:else}\n\t\t\t\t\t<a\n\t\t\t\t\t\thref=\"/rate\"\n\t\t\t\t\t\tclass=\"text-sm font-medium text-muted no-underline transition-colors hover:text-text\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{t('get rated')}\n\t\t\t\t\t</a>\n\t\t\t\t\t<!-- RAT: desktop sign-in was only reachable via the mobile bottom\n\t\t\t\t\t\t nav; surface it here so signed-out desktop viewers can reach it.\n\t\t\t\t\t\t Uses the shared AuthSheet controller (same as BottomNav). -->\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tonclick={() => openAuthSheet()}\n\t\t\t\t\t\tclass=\"rounded-lg {btnBg} px-5 py-2 text-sm font-semibold {btnText} transition-all {btnHover}\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{t('sign in')}\n\t\t\t\t\t</button>\n\t\t\t\t\t<!-- RAT-587: signed out too — the drawer's guest view carries the\n\t\t\t\t\t\t sign-in, the games and the language. -->\n\t\t\t\t\t{@render hamburger()}\n\t\t\t\t{/if}\n\t\t\t</div>\n\n\t\t\t<!-- RAT-272: slimmed-down mobile top right. Everything that used to\n\t\t\t live in the hamburger menu (links, get-rated CTA, rater settings,\n\t\t\t arena +submit) moved into BottomNav (Feed/Duel/+/Beat/Profile),\n\t\t\t the + picker (create flows), AuthSheet (sign-in), and the /profile\n\t\t\t page top (rater settings). All that remains is the bell when\n\t\t\t authed, and a sign-in button when not. -->\n\t\t\t<div class=\"pointer-events-auto flex items-center gap-1.5 md:hidden\">\n\t\t\t\t<!-- RAT-282: /f top-level tabs live here on mobile (right of the\n\t\t\t\t logo), replacing the bell on this route to keep the row from\n\t\t\t\t crowding. Active pill mirrors the feed page's ?tab state. -->\n\t\t\t\t{#if isBetSection}\n\t\t\t\t\t<a\n\t\t\t\t\t\thref=\"/b\"\n\t\t\t\t\t\tdata-sveltekit-noscroll\n\t\t\t\t\t\tclass=\"rounded-lg px-2.5 py-1.5 font-display text-[13px] font-semibold whitespace-nowrap no-underline transition-colors {pathname ===\n\t\t\t\t\t\t'/b'\n\t\t\t\t\t\t\t? 'bg-surface text-text'\n\t\t\t\t\t\t\t: 'text-muted hover:text-text'}\">{t('beat me')}</a\n\t\t\t\t\t>\n\t\t\t\t\t<a\n\t\t\t\t\t\thref=\"/b/bet\"\n\t\t\t\t\t\tdata-sveltekit-noscroll\n\t\t\t\t\t\tclass=\"inline-flex items-center gap-1.5 rounded-lg px-2.5 py-1.5 font-display text-[13px] font-semibold whitespace-nowrap no-underline transition-colors {pathname.startsWith(\n\t\t\t\t\t\t\t'/b/bet'\n\t\t\t\t\t\t)\n\t\t\t\t\t\t\t? 'bg-surface text-text'\n\t\t\t\t\t\t\t: 'text-muted hover:text-text'}\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\tclass=\"inline-block h-3 w-3 rounded-full\"\n\t\t\t\t\t\t\tstyle=\"background:radial-gradient(circle at 34% 30%,#ffe27a,#f4b71f 70%);box-shadow:inset 0 0 0 2px rgba(0,0,0,0.28),inset 0 0 0 2.5px rgba(255,255,255,0.16)\"\n\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t></span>\n\t\t\t\t\t\t{t('bet')}\n\t\t\t\t\t</a>\n\t\t\t\t\t{#if isAuthed}\n\t\t\t\t\t\t<NotificationBell userId={page.data.user?.id ?? null} />\n\t\t\t\t\t{/if}\n\t\t\t\t{:else if isFeed}\n\t\t\t\t\t<a\n\t\t\t\t\t\thref=\"/f\"\n\t\t\t\t\t\tdata-sveltekit-noscroll\n\t\t\t\t\t\tclass=\"rounded-lg px-2.5 py-1.5 font-display text-[13px] font-semibold whitespace-nowrap no-underline transition-colors {feedTab ===\n\t\t\t\t\t\t'foryou'\n\t\t\t\t\t\t\t? 'bg-surface text-text'\n\t\t\t\t\t\t\t: 'text-muted hover:text-text'}\">{t('For You')}</a\n\t\t\t\t\t>\n\t\t\t\t\t<a\n\t\t\t\t\t\thref=\"/f?tab=niches\"\n\t\t\t\t\t\tdata-sveltekit-noscroll\n\t\t\t\t\t\tclass=\"rounded-lg px-2.5 py-1.5 font-display text-[13px] font-semibold whitespace-nowrap no-underline transition-colors {feedTab ===\n\t\t\t\t\t\t'niches'\n\t\t\t\t\t\t\t? 'bg-surface text-text'\n\t\t\t\t\t\t\t: 'text-muted hover:text-text'}\">{t('Explore Niches')}</a\n\t\t\t\t\t>\n\t\t\t\t\t{#if isAuthed}\n\t\t\t\t\t\t<NotificationBell userId={page.data.user?.id ?? null} />\n\t\t\t\t\t{/if}\n\t\t\t\t\t<!-- RAT-272 follow-up: hide the bell on /a — that page hosts\n\t\t\t\t\t its own floating chrome (filter / + submit / menu dropdown)\n\t\t\t\t\t in the same top-right slot. Bell duplicated the visual\n\t\t\t\t\t hierarchy + competed for tap targets. -->\n\t\t\t\t{:else if isChatInbox && isAuthed}\n\t\t\t\t\t<!-- RAT-337: + group chat pill (mobile-only on /c). Sets a\n\t\t\t\t\t shared store flag that the (chat) layout watches and\n\t\t\t\t\t opens its create-room modal in response. -->\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tonclick={() => groupCreateOpen.set(true)}\n\t\t\t\t\t\tclass=\"inline-flex items-center gap-1 rounded-full bg-accent px-3 py-1.5 font-display text-[12px] leading-none font-bold text-white\"\n\t\t\t\t\t\taria-label={t('new group chat')}\n\t\t\t\t\t>\n\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\tstroke-width=\"2.6\"\n\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\t\tclass=\"h-3 w-3\"\n\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\" />\n\t\t\t\t\t\t\t<line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\" />\n\t\t\t\t\t\t</svg>\n\t\t\t\t\t\tgroup\n\t\t\t\t\t</button>\n\t\t\t\t\t<NotificationBell userId={page.data.user?.id ?? null} />\n\t\t\t\t{:else if isAuthed && !isArena}\n\t\t\t\t\t<NotificationBell userId={page.data.user?.id ?? null} />\n\t\t\t\t{:else if !isAuthed && !isArena}\n\t\t\t\t\t<!-- Signed out, the bottom nav's last slot was the only way in, and\n\t\t\t\t\t\t a phone visitor reads the top bar first. Same AuthSheet as the\n\t\t\t\t\t\t desktop button and that slot. Not on /a (its own chrome lives\n\t\t\t\t\t\t here), /f or /b (their tabs do). -->\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tonclick={() => openAuthSheet()}\n\t\t\t\t\t\tclass=\"inline-flex h-9 items-center rounded-[10px] border border-border bg-surface px-4 font-display text-[13.5px] font-bold tracking-tight text-text\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{t('sign in')}\n\t\t\t\t\t</button>\n\t\t\t\t{/if}\n\t\t\t\t<!-- The mobile hamburger moved into the BottomNav's rightmost \"menu\"\n\t\t\t\t\t slot — it's a thumb-reachable surface on a 90%-mobile site, and it\n\t\t\t\t\t also stopped this row from fighting /a's own floating chrome.\n\t\t\t\t\t Signed out, that slot is the sign-in, so the menu (and with it the\n\t\t\t\t\t language, RAT-587) opens from here; /a keeps its own chrome. -->\n\t\t\t\t{#if !isAuthed && !isArena}{@render hamburger()}{/if}\n\t\t\t</div>\n\t\t</div>\n\n\t\t<!-- RAT-272: mobile slim drawer + click-outside overlay + arena\n\t\t fullscreen menu DELETED. The bottom nav handles mobile primary\n\t\t navigation; its \"menu\" slot opens the NavMenu drawer (mounted in the\n\t\t root layout) for everything else; AuthSheet handles the sign-in CTA\n\t\t path; /profile owns rater settings. Desktop nav above (md:flex) is\n\t\t the only nav surface left in this file. -->\n\t</nav>\n{/if}\n\n<style>\n\t/* shimmering \"new\" badge — filled cyan chip with a lighter band sweeping\n\t   across it. solid fill, not a glow → within the no-neon rule. */\n\t.new-badge {\n\t\tfont-family: var(--font-display);\n\t\tfont-size: 9px;\n\t\tfont-weight: 800;\n\t\tletter-spacing: 0.08em;\n\t\ttext-transform: uppercase;\n\t\tpadding: 2px 6px;\n\t\tborder-radius: 5px;\n\t\tcolor: #00141a;\n\t\tbackground: linear-gradient(\n\t\t\t100deg,\n\t\t\tvar(--color-cyan) 0%,\n\t\t\tvar(--color-cyan) 40%,\n\t\t\t#a5f3fc 50%,\n\t\t\tvar(--color-cyan) 60%,\n\t\t\tvar(--color-cyan) 100%\n\t\t);\n\t\tbackground-size: 220% 100%;\n\t\tbackground-position: 120% 0;\n\t\tanimation: badge-shimmer 2.8s ease-in-out infinite;\n\t}\n\t@keyframes badge-shimmer {\n\t\t0% {\n\t\t\tbackground-position: 120% 0;\n\t\t}\n\t\t45%,\n\t\t100% {\n\t\t\tbackground-position: -20% 0;\n\t\t}\n\t}\n\t@media (prefers-reduced-motion: reduce) {\n\t\t.new-badge {\n\t\t\tanimation: none;\n\t\t\tbackground: var(--color-cyan);\n\t\t}\n\t}\n</style>\n","<script lang=\"ts\">\n\timport { activeMode, getModeAccent, accentOverride } from '$lib/stores/activeMode';\n\n\tinterface Props {\n\t\tannouncement: {\n\t\t\tmessage: string;\n\t\t\thref: string | null;\n\t\t\tctaLabel: string | null;\n\t\t\taccent: string;\n\t\t\tversion: string;\n\t\t};\n\t\tondismiss: () => void;\n\t}\n\n\tlet { announcement, ondismiss }: Props = $props();\n\n\t// The bar always wears the *current module's* accent — the same source the\n\t// navbar/footer read — so it stays in lockstep with the chrome. The admin\n\t// only owns the copy/link; the color is intentionally NOT theirs to set\n\t// (announcement.accent is ignored). Literal class strings keyed by\n\t// accent.tailwind so Tailwind v4 compiles each fill — never interpolate\n\t// `bg-${name}`. Mirrors Navbar's ACCENT_CLASSES table.\n\tconst BG: Record<string, string> = {\n\t\tcoral: 'bg-coral',\n\t\tyellow: 'bg-yellow',\n\t\tgreen: 'bg-green',\n\t\tblue: 'bg-blue',\n\t\torange: 'bg-orange',\n\t\tpurple: 'bg-purple',\n\t\tindigo: 'bg-indigo',\n\t\tcyan: 'bg-cyan',\n\t\tlime: 'bg-lime',\n\t\tpink: 'bg-pink',\n\t\tfuchsia: 'bg-fuchsia',\n\t\tviolet: 'bg-violet'\n\t};\n\tconst accent = $derived($accentOverride ?? getModeAccent($activeMode));\n\tconst cls = $derived(\n\t\t`${BG[accent.tailwind] ?? 'bg-coral'} ${accent.onLight ? 'text-bg' : 'text-white'}`\n\t);\n\n\t// The bar is a fixed overlay; publish its measured height as --ann-h so the\n\t// navbar drops below it and <main> pads down (no fighting the fixed navbar).\n\tlet barH = $state(0);\n\t$effect(() => {\n\t\t// $effect (and its cleanup) only run client-side — safe to touch document.\n\t\tconst root = document.documentElement.style;\n\t\tif (barH > 0) root.setProperty('--ann-h', `${barH}px`);\n\t\treturn () => root.setProperty('--ann-h', '0px');\n\t});\n\n\tfunction dismiss() {\n\t\tdocument.cookie = `ann_dismissed=${announcement.version}; path=/; max-age=${60 * 60 * 24 * 365}; samesite=lax`;\n\t\tdocument.documentElement.style.setProperty('--ann-h', '0px');\n\t\tondismiss();\n\t}\n</script>\n\n<div\n\tbind:clientHeight={barH}\n\tclass=\"fixed inset-x-0 top-0 z-[60] flex items-center justify-center gap-x-3 px-10 py-2 text-center text-[13px] leading-snug font-semibold {cls}\"\n>\n\t<span>{announcement.message}</span>\n\t{#if announcement.href}\n\t\t<a href={announcement.href} class=\"shrink-0 underline underline-offset-2 hover:no-underline\"\n\t\t\t>{announcement.ctaLabel ?? 'learn more'} →</a\n\t\t>\n\t{/if}\n\t<button\n\t\ttype=\"button\"\n\t\tonclick={dismiss}\n\t\tclass=\"absolute top-1/2 right-2.5 -translate-y-1/2 rounded p-1 opacity-80 transition-opacity hover:opacity-100\"\n\t\taria-label=\"dismiss\"\n\t>\n\t\t<svg\n\t\t\tclass=\"h-3.5 w-3.5\"\n\t\t\tfill=\"none\"\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstroke-width=\"2.5\"\n\t\t\t><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" /></svg\n\t\t>\n\t</button>\n</div>\n","<script lang=\"ts\">\n\timport { t } from '$lib/i18n';\n\t// \"Under review\" bar. Mirrors AnnouncementBar's fixed-overlay + --ann-h\n\t// contract so the navbar drops below it and <main> pads down — it sits in\n\t// the announcement row (and takes its place while a review is open).\n\tlet barH = $state(0);\n\t$effect(() => {\n\t\tconst root = document.documentElement.style;\n\t\tif (barH > 0) root.setProperty('--ann-h', `${barH}px`);\n\t\treturn () => root.setProperty('--ann-h', '0px');\n\t});\n</script>\n\n<div\n\tbind:clientHeight={barH}\n\tclass=\"fixed inset-x-0 top-0 z-[60] flex items-center justify-center gap-x-2 bg-amber-500 px-10 py-2 text-center text-[13px] leading-snug font-semibold text-black\"\n>\n\t<span>{t('your account is under review — actions are paused.')}</span>\n\t<a href=\"/c\" class=\"shrink-0 underline underline-offset-2 hover:no-underline\"\n\t\t>{t('message us →')}</a\n\t>\n</div>\n","<script lang=\"ts\">\n\timport { t } from '$lib/i18n';\n\t/**\n\t * RAT-272: mobile-only bottom navigation.\n\t *\n\t * Six slots: Feed · Duel · Arena · Beat · Messages · Menu (accent dot —\n\t * green if rater queue live, coral if unread notifications).\n\t *\n\t * Profile moved into the drawer: its header is the link to /p, so a\n\t * dedicated slot down here was a second door to the same room.\n\t *\n\t * The center \"+\" create button and its action picker are gone: the picker\n\t * listed contest / rate / raters / roleplay / albums / clans, all of which\n\t * the NavMenu drawer now carries, so the two surfaces were duplicating each\n\t * other. The rightmost \"menu\" slot opens that drawer instead.\n\t *\n\t * Accent logic mirrors Navbar.svelte:84-153 so the two nav surfaces\n\t * agree on what counts as \"in the Feed ecosystem\" vs \"in Duel\" etc.\n\t *\n\t * Hidden on /rate/session/[id] — that route is focus-mode and the nav\n\t * would crowd the score + chat UI.\n\t *\n\t * Reference design: scripts/preview-bottom-nav.html.\n\t */\n\n\timport { page, navigating } from '$app/state';\n\timport { isStudioPage } from '$lib/studio-host';\n\timport { unreadNotifications, unreadChat } from '$lib/stores/unreadNotifications';\n\timport { navMenuOpen } from '$lib/stores/navMenu';\n\timport { openAuthSheet } from '$lib/stores/authSheet';\n\n\tconst pathname = $derived(page.url.pathname);\n\tconst isAuthed = $derived(!!page.data.user);\n\tconst isRater = $derived(!!page.data.isRater);\n\tconst queueOpen = $derived(!!page.data.queueOpen);\n\n\t// Count of the viewer's own open duels still featured on the public board\n\t// (paid placements). Drives a small count badge on the Duel slot so a\n\t// creator can see at a glance how many of their paid duels are live.\n\tconst myFeaturedDuels = $derived((page.data.myFeaturedDuels as number | undefined) ?? 0);\n\n\t// RAT-328: single-letter module match. `startsWith('/a')` would also catch\n\t// `/about`, `/admin`, `/api`. seg() requires a path terminator after the\n\t// prefix so `/a` matches /a + /a/foo + /a?x=1 but never /about.\n\tfunction seg(path: string, prefix: string): boolean {\n\t\tif (path === prefix) return true;\n\t\tconst next = path.charAt(prefix.length);\n\t\treturn path.startsWith(prefix) && (next === '/' || next === '?' || next === '#');\n\t}\n\n\t// Active state per slot. Feed slot owns the whole creator-post ecosystem\n\t// (the niche pages, /post deeplinks, and /n discovery all light up\n\t// the Feed slot since the user thinks of those as \"feed adjacent\"). Same\n\t// philosophy as Navbar.svelte's `isRater` derivation, narrowed to the\n\t// routes the bottom nav can route to.\n\tconst feedActive = $derived(\n\t\tseg(pathname, '/f') || pathname.startsWith('/post/') || seg(pathname, '/n')\n\t);\n\tconst duelActive = $derived(seg(pathname, '/d'));\n\tconst arenaActive = $derived(seg(pathname, '/a'));\n\tconst beatActive = $derived(seg(pathname, '/b'));\n\tconst leaderboardActive = $derived(seg(pathname, '/l'));\n\tconst messagesActive = $derived(seg(pathname, '/c'));\n\n\t// RAT: in-flight feedback. The destination routes (/a, /d especially) have\n\t// heavy server loads that SvelteKit awaits before swapping the page, so a\n\t// tap used to leave the current page frozen with zero response — the exact\n\t// double-tap trap CLAUDE.md warns about. `navigating.to` flips truthy the\n\t// instant a client nav starts (synchronously, in the same tick as the tap),\n\t// so we can light up the tapped slot immediately and hold it for the whole\n\t// await window. Match the target path against each slot with the same seg()\n\t// logic the active state uses; the drawer button owns no route so it's out.\n\tconst navPath = $derived(navigating.to?.url.pathname ?? null);\n\tconst feedPending = $derived(\n\t\tnavPath !== null && (seg(navPath, '/f') || navPath.startsWith('/post/') || seg(navPath, '/n'))\n\t);\n\tconst duelPending = $derived(navPath !== null && seg(navPath, '/d'));\n\tconst arenaPending = $derived(navPath !== null && seg(navPath, '/a'));\n\tconst beatPending = $derived(navPath !== null && seg(navPath, '/b'));\n\tconst messagesPending = $derived(navPath !== null && seg(navPath, '/c'));\n\n\t// Hide on focus-mode routes: in-session rating, active chat threads,\n\t// + the entire auth flow. /auth pages are intentionally distraction-\n\t// free — the bottom nav offering \"go elsewhere\" links during sign-in\n\t// adds friction and (combined with main's 68px clearance) pushed the\n\t// auth sections past the viewport bottom, making them needlessly\n\t// scrollable. The /c inbox keeps the nav (no composer there —\n\t// full-screen would strand the user); /c/<nickname> threads hide\n\t// it so the composer owns the bottom edge.\n\tconst hidden = $derived(\n\t\tpathname.startsWith('/rate/session/') ||\n\t\t\tpathname.startsWith('/auth/') ||\n\t\t\t(pathname.startsWith('/c/') && pathname !== '/c') ||\n\t\t\t// RAT-322: group rooms are focus-mode (own header + composer).\n\t\t\tpathname.startsWith('/g/') ||\n\t\t\t// RAT-463: character chat owns both edges — its composer is the bottom.\n\t\t\tpathname.startsWith('/roleplay/c/') ||\n\t\t\t// A character group room owns both edges too.\n\t\t\tpathname.startsWith('/roleplay/g/') ||\n\t\t\t// Casting a group room: its picks dock owns the bottom edge.\n\t\t\tpathname === '/roleplay/group/new' ||\n\t\t\t// livecam greenroom + public room are focus-mode (own header).\n\t\t\tpathname.startsWith('/livecam') ||\n\t\t\t// RAT-441: same for the floor — the room owns its bottom edge (tip menu\n\t\t\t// + composer), and the z-50 nav would sit on top of both.\n\t\t\tpathname.startsWith('/floor') ||\n\t\t\t// The studio owns both edges the same way: its own rail on the left, its\n\t\t\t// own composer at the bottom. On its own domain it is also `/`.\n\t\t\tpathname.startsWith('/studio') ||\n\t\t\tpage.route.id === '/studio' ||\n\t\t\t// RAT-632: PornPilot's own pages (/prompts), same as the studio.\n\t\t\tisStudioPage(page.route.id) ||\n\t\t\t// The album editor is a focused task: its add / publish bar owns the bottom.\n\t\t\tpage.route.id === '/album/[id]/edit'\n\t);\n\n\t// Profile dot priority: queue-live (green) wins over unread-notif (coral)\n\t// because \"I am working right now\" is the bigger commitment signal than\n\t// \"I have unread mail\" — and seeing both feels noisy. Drop to coral only\n\t// when queue is paused or the user isn't a rater.\n\tconst showQueueDot = $derived(isAuthed && isRater && queueOpen);\n\tconst showNotifDot = $derived(isAuthed && !showQueueDot && $unreadNotifications > 0);\n\t// RAT-300: messages slot gets its own coral dot when there's chat\n\t// unread. Independent of the profile slot — the user might have both\n\t// at the same time (chat from one person, follow notif from another).\n\t// Hide the dot when the user is already inside /c/<nick> since that\n\t// thread's mount triggers markThreadRead and any other thread's unread\n\t// is what the inbox lists anyway; on /c (inbox) we keep showing the\n\t// dot until the count actually drops to 0 via the next poll.\n\tconst showChatDot = $derived(isAuthed && $unreadChat > 0);\n\n\t// The per-mode accent (RAT-327) only ever tinted the \"+\" create button, so\n\t// it went with it — every remaining slot carries its own fixed module color.\n\n\t// Logged-out taps that need an account raise the app-wide sheet (mounted\n\t// in the root layout) in place rather than bouncing to a login page —\n\t// messages, and the rightmost slot, which becomes the sign-in button\n\t// itself (there's nothing in the drawer for a signed-out visitor, so it\n\t// would be a detour). The sheet closes itself via the layout's\n\t// afterNavigate; the drawer owns its own Escape handling.\n\tfunction onMessagesClick(e: MouseEvent) {\n\t\tif (isAuthed) return; // <a href=\"/c\"> handles navigation\n\t\te.preventDefault();\n\t\topenAuthSheet('/c');\n\t}\n</script>\n\n{#if !hidden}\n\t<!-- md:hidden — desktop keeps the existing top navbar untouched per\n\t\t RAT-272 scope. fixed bottom-0 + safe-area-inset-bottom for iOS home\n\t\t indicator. z-50 sits in the same fixed-chrome layer as the top\n\t\t navbar (they don't overlap). The NavMenu drawer renders at higher z\n\t\t so it covers the nav. -->\n\t<!-- bg extension: nav body bg + a ::after pseudo (see styles) that drapes\n\t\t ~120px below to close any iOS Safari chrome-hide visual-vs-layout\n\t\t viewport gap. padding-bottom is uncapped (was min(env, 14px)) — the\n\t\t cap left a band of page bg visible below the nav when chrome\n\t\t retracted on scroll. -->\n\t<nav\n\t\taria-label={t('Primary')}\n\t\tclass=\"bottom-nav fixed inset-x-0 bottom-0 z-50 grid grid-cols-6 gap-0.5 border-t border-border bg-bg/95 px-1.5 backdrop-blur-md md:hidden\"\n\t\tstyle=\"padding-top: 6px; padding-bottom: calc(6px + env(safe-area-inset-bottom, 0px));\"\n\t>\n\t\t<!-- Feed -->\n\t\t<a\n\t\t\thref=\"/f\"\n\t\t\tclass=\"slot {feedActive ? 'slot-active slot-accent-pink' : ''}\"\n\t\t\tclass:slot-pending={feedPending}\n\t\t\taria-current={feedActive ? 'page' : undefined}\n\t\t>\n\t\t\t<span class=\"icon\" aria-hidden=\"true\">\n\t\t\t\t<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\">\n\t\t\t\t\t<path\n\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\td=\"M3.75 7.5l3 3M3.75 7.5l3-3m-3 3h12.75a4.5 4.5 0 010 9H12m-8.25.75l3 3m-3-3l3-3\"\n\t\t\t\t\t/>\n\t\t\t\t</svg>\n\t\t\t</span>\n\t\t\t<span class=\"label\">{t('feed')}</span>\n\t\t</a>\n\n\t\t<!-- Duel -->\n\t\t<a\n\t\t\thref=\"/d\"\n\t\t\tclass=\"slot {duelActive ? 'slot-active slot-accent-blue' : ''}\"\n\t\t\tclass:slot-pending={duelPending}\n\t\t\taria-current={duelActive ? 'page' : undefined}\n\t\t>\n\t\t\t<span class=\"icon relative\" aria-hidden=\"true\">\n\t\t\t\t<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\">\n\t\t\t\t\t<path\n\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\td=\"m3.75 13.5 10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75Z\"\n\t\t\t\t\t/>\n\t\t\t\t</svg>\n\t\t\t\t{#if myFeaturedDuels > 0}\n\t\t\t\t\t<span\n\t\t\t\t\t\tclass=\"slot-count\"\n\t\t\t\t\t\ttitle=\"{myFeaturedDuels} featured duel{myFeaturedDuels > 1 ? 's' : ''} live\"\n\t\t\t\t\t\t>{myFeaturedDuels > 9 ? '9+' : myFeaturedDuels}</span\n\t\t\t\t\t>\n\t\t\t\t{/if}\n\t\t\t</span>\n\t\t\t<span class=\"label\">{t('duel')}</span>\n\t\t</a>\n\n\t\t<!-- Arena -->\n\t\t<a\n\t\t\thref=\"/a\"\n\t\t\tclass=\"slot {arenaActive ? 'slot-active slot-accent-orange' : ''}\"\n\t\t\tclass:slot-pending={arenaPending}\n\t\t\taria-current={arenaActive ? 'page' : undefined}\n\t\t>\n\t\t\t<span class=\"icon\" aria-hidden=\"true\">\n\t\t\t\t<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\">\n\t\t\t\t\t<path\n\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\td=\"M15.362 5.214A8.252 8.252 0 0112 21 8.25 8.25 0 016.038 7.047 8.287 8.287 0 009 9.601a8.983 8.983 0 013.361-6.867 8.21 8.21 0 003 2.48z\"\n\t\t\t\t\t/>\n\t\t\t\t\t<path\n\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\td=\"M12 18a3.75 3.75 0 00.495-7.468 5.99 5.99 0 00-1.925 3.547 5.975 5.975 0 01-2.133-1.001A3.75 3.75 0 0012 18z\"\n\t\t\t\t\t/>\n\t\t\t\t</svg>\n\t\t\t</span>\n\t\t\t<span class=\"label\">{t('arena')}</span>\n\t\t</a>\n\n\t\t<!-- Beat -->\n\t\t<a\n\t\t\thref=\"/b\"\n\t\t\tclass=\"slot {beatActive ? 'slot-active slot-accent-yellow' : ''}\"\n\t\t\tclass:slot-pending={beatPending}\n\t\t\taria-current={beatActive ? 'page' : undefined}\n\t\t>\n\t\t\t<span class=\"icon\" aria-hidden=\"true\">\n\t\t\t\t<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\">\n\t\t\t\t\t<path\n\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\td=\"M15.59 14.37a6 6 0 01-5.84 7.38v-4.8m5.84-2.58a14.98 14.98 0 006.16-12.12A14.98 14.98 0 009.631 8.41m5.96 5.96a14.926 14.926 0 01-5.841 2.58m-.119-8.54a6 6 0 00-7.381 5.84h4.8m2.581-5.84a14.927 14.927 0 00-2.58 5.84m2.699 2.7c-.103.021-.207.041-.311.06a15.09 15.09 0 01-2.448-2.448 14.9 14.9 0 01.06-.312m-2.24 2.39a4.493 4.493 0 00-1.757 4.306 4.493 4.493 0 004.306-1.758M16.5 9a1.5 1.5 0 11-3 0 1.5 1.5 0 013 0z\"\n\t\t\t\t\t/>\n\t\t\t\t</svg>\n\t\t\t</span>\n\t\t\t<span class=\"label\">{t('beat')}</span>\n\t\t</a>\n\n\t\t<!-- Messages — RAT-294. Replaces the leaderboard slot (which moved\n\t\t\t into the NavMenu drawer). Tap → /c inbox. RAT-300: coral dot when\n\t\t\t the viewer has any unread chat_message — same pattern as the\n\t\t\t profile slot's notif dot, driven by the shared $unreadChat\n\t\t\t store (NotificationBell polls the unified endpoint every 5s\n\t\t\t and fans out to both stores). -->\n\t\t<a\n\t\t\thref=\"/c\"\n\t\t\tonclick={onMessagesClick}\n\t\t\tclass=\"slot {messagesActive ? 'slot-active slot-accent-cyan' : ''}\"\n\t\t\tclass:slot-pending={messagesPending}\n\t\t\taria-current={messagesActive ? 'page' : undefined}\n\t\t>\n\t\t\t<span class=\"icon relative\" aria-hidden=\"true\">\n\t\t\t\t<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\">\n\t\t\t\t\t<path\n\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\td=\"M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z\"\n\t\t\t\t\t/>\n\t\t\t\t</svg>\n\t\t\t\t{#if showChatDot}\n\t\t\t\t\t<span class=\"slot-dot slot-dot-coral\" title={t('unread messages')}></span>\n\t\t\t\t{/if}\n\t\t\t</span>\n\t\t\t<span class=\"label\">{t('messages')}</span>\n\t\t</a>\n\n\t\t<!-- Menu — the drawer's mobile entry point, replacing the \"+\" picker.\n\t\t\t Never \"active\": it opens an overlay, it doesn't own a route. -->\n\t\t<!-- Menu — opens the drawer, which is also where the profile lives now\n\t\t\t (its header is the link to /p). The queue/notification dots moved\n\t\t\t here with it: they have to stay visible without opening anything.\n\t\t\t Signed out there's no drawer worth showing, so the slot becomes the\n\t\t\t sign-in button. -->\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\tonclick={() => (isAuthed ? navMenuOpen.set(true) : openAuthSheet())}\n\t\t\taria-label={isAuthed ? t('menu') : t('sign in')}\n\t\t\tclass=\"slot\"\n\t\t>\n\t\t\t<span class=\"icon relative\" aria-hidden=\"true\">\n\t\t\t\t{#if isAuthed}\n\t\t\t\t\t<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\">\n\t\t\t\t\t\t<path\n\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\t\td=\"M3.75 6.75h16.5M3.75 12h16.5M3.75 17.25h16.5\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</svg>\n\t\t\t\t{:else}\n\t\t\t\t\t<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\">\n\t\t\t\t\t\t<path\n\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\t\td=\"M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4m-5-4 5-5-5-5m5 5H3\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</svg>\n\t\t\t\t{/if}\n\t\t\t\t{#if showQueueDot}\n\t\t\t\t\t<span class=\"slot-dot slot-dot-green\" title={t('queue live')}></span>\n\t\t\t\t{:else if showNotifDot}\n\t\t\t\t\t<span class=\"slot-dot slot-dot-coral\" title={t('unread notifications')}></span>\n\t\t\t\t{/if}\n\t\t\t</span>\n\t\t\t<span class=\"label\">{isAuthed ? t('menu') : t('sign in')}</span>\n\t\t</button>\n\t</nav>\n{/if}\n\n<!-- RAT-446: the + picker and the local auth sheet are gone — the rightmost\n\t slot opens the nav drawer (signed in) or raises the app-wide AuthSheet\n\t mounted in the root layout (signed out). -->\n\n<style>\n\t/* Batch 23: focus-mode hide. /d/[code] sets body.duel-focus-mode on\n\t   the lobby / fomo views so the chat dock can be the only bottom\n\t   chrome. Result view (revealed) clears the class and the nav returns\n\t   on its own. The whole nav + its bg tail go away together. */\n\t:global(body.duel-focus-mode) .bottom-nav {\n\t\tdisplay: none;\n\t}\n\n\t/* bg-extension tail: a wide opaque strip glued to the nav's bottom edge\n\t   that extends below the nav. On iOS Safari when the bottom URL bar\n\t   auto-hides while scrolling, fixed elements anchored to `bottom: 0`\n\t   sometimes lag the visual-viewport delta by a few px — the tail covers\n\t   that gap with the same nav bg so there's no visible page strip under\n\t   the nav. Solid (not transparent) so the seam disappears against the\n\t   blurred nav above. */\n\t.bottom-nav::after {\n\t\tcontent: '';\n\t\tposition: absolute;\n\t\ttop: 100%;\n\t\tleft: 0;\n\t\tright: 0;\n\t\theight: 120px;\n\t\tbackground: var(--color-bg);\n\t\tpointer-events: none;\n\t}\n\n\t/* Slot base — flex column, muted by default, accent on active. The\n\t   accent isn't a neon glow per CLAUDE.md: just currentColor + a tiny 2px\n\t   underline indicator. */\n\t.slot {\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\tgap: 4px;\n\t\tmin-height: 56px;\n\t\tpadding: 6px 1px;\n\t\tborder-radius: 12px;\n\t\tcolor: var(--color-muted);\n\t\ttext-decoration: none;\n\t\ttransition: color 180ms ease-out;\n\t\tposition: relative;\n\t\tbackground: transparent;\n\t\tborder: 0;\n\t\tcursor: pointer;\n\t\tfont: inherit;\n\t}\n\t.slot:active {\n\t\ttransform: scale(0.96);\n\t}\n\t.slot .icon {\n\t\twidth: 22px;\n\t\theight: 22px;\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\tposition: relative;\n\t}\n\t.slot .icon :global(svg) {\n\t\twidth: 100%;\n\t\theight: 100%;\n\t}\n\t.slot .label {\n\t\tfont-family: var(--font-display);\n\t\tfont-size: 9.5px;\n\t\tfont-weight: 700;\n\t\tletter-spacing: 0.02em;\n\t\ttext-transform: lowercase;\n\t\tmax-width: 100%;\n\t\toverflow: hidden;\n\t\ttext-overflow: ellipsis;\n\t\twhite-space: nowrap;\n\t}\n\t/* In-flight slot: swap the icon for a spinner the instant the nav starts, so\n\t   a tap on a slow route (heavy /a, /d server load) visibly registers instead\n\t   of leaving the page frozen. Neutral spinner (border ring + currentColor\n\t   head), no colored glow — per CLAUDE.md this is a clean loading affordance,\n\t   not neon. The label + any count/dot badge stay put; only the glyph flips. */\n\t.slot-pending {\n\t\tcolor: var(--color-text);\n\t}\n\t.slot-pending .icon :global(svg) {\n\t\topacity: 0;\n\t}\n\t.slot-pending .icon::after {\n\t\tcontent: '';\n\t\tposition: absolute;\n\t\tinset: 2px;\n\t\tborder-radius: 50%;\n\t\tborder: 2px solid var(--color-border);\n\t\tborder-top-color: currentColor;\n\t\tanimation: slot-spin 0.6s linear infinite;\n\t}\n\t@keyframes slot-spin {\n\t\tto {\n\t\t\ttransform: rotate(360deg);\n\t\t}\n\t}\n\t@keyframes slot-pending-pulse {\n\t\t0%,\n\t\t100% {\n\t\t\topacity: 0.35;\n\t\t}\n\t\t50% {\n\t\t\topacity: 1;\n\t\t}\n\t}\n\t/* Reduced-motion: don't spin — pulse the full ring so the tap still reads as\n\t   \"working\" without a rotating element. */\n\t@media (prefers-reduced-motion: reduce) {\n\t\t.slot-pending .icon::after {\n\t\t\tborder-top-color: var(--color-border);\n\t\t\tanimation: slot-pending-pulse 1s ease-in-out infinite;\n\t\t}\n\t}\n\n\t.slot-active {\n\t\tcolor: var(--color-text);\n\t}\n\t.slot-active::before {\n\t\tcontent: '';\n\t\tposition: absolute;\n\t\ttop: -8px;\n\t\tleft: 50%;\n\t\ttransform: translateX(-50%);\n\t\twidth: 24px;\n\t\theight: 2px;\n\t\tborder-radius: 0 0 2px 2px;\n\t\tbackground: currentColor;\n\t}\n\t.slot-accent-green {\n\t\tcolor: var(--color-green);\n\t}\n\t.slot-accent-blue {\n\t\tcolor: var(--color-blue);\n\t}\n\t.slot-accent-yellow {\n\t\tcolor: var(--color-yellow);\n\t}\n\t.slot-accent-orange {\n\t\tcolor: var(--color-orange);\n\t}\n\t.slot-accent-purple {\n\t\tcolor: var(--color-purple);\n\t}\n\t/* RAT-327: per-module accents added with the rest of the chrome wiring. */\n\t.slot-accent-indigo {\n\t\tcolor: var(--color-indigo);\n\t}\n\t.slot-accent-cyan {\n\t\tcolor: var(--color-cyan);\n\t}\n\t.slot-accent-lime {\n\t\tcolor: var(--color-lime);\n\t}\n\t.slot-accent-pink {\n\t\tcolor: var(--color-pink);\n\t}\n\n\t/* Profile-slot status dots. Positioned right of the icon, offset above\n\t   so they don't crash into the label. 2px border in bg color creates\n\t   the donut-cutout that reads correctly on dark surfaces. */\n\t.slot-dot {\n\t\tposition: absolute;\n\t\ttop: -2px;\n\t\tright: -6px;\n\t\twidth: 8px;\n\t\theight: 8px;\n\t\tborder-radius: 50%;\n\t\tborder: 2px solid var(--color-bg);\n\t}\n\t.slot-dot-green {\n\t\tbackground: var(--color-green);\n\t}\n\t.slot-dot-coral {\n\t\tbackground: var(--color-coral);\n\t}\n\n\t/* Numeric count badge — Duel slot's \"you have N paid duels live on the\n\t   board\" pill. Filled blue (the duel accent), donut-cut against bg. */\n\t.slot-count {\n\t\tposition: absolute;\n\t\ttop: -5px;\n\t\tright: -9px;\n\t\tmin-width: 15px;\n\t\theight: 15px;\n\t\tpadding: 0 3px;\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\tborder-radius: 999px;\n\t\tbackground: var(--color-blue);\n\t\tcolor: #fff;\n\t\tfont-family: var(--font-display);\n\t\tfont-size: 9px;\n\t\tfont-weight: 800;\n\t\tline-height: 1;\n\t\tborder: 2px solid var(--color-bg);\n\t}\n</style>\n","<script lang=\"ts\">\n\timport { t } from '$lib/i18n';\n\timport LanguagePicker from '$lib/components/i18n/LanguagePicker.svelte';\n\t/**\n\t * RAT-446: the \"everything else\" nav surface.\n\t *\n\t * PostHog (identified users, 30d) showed the top navbar was carrying the\n\t * wrong six: leaderboard sat first with the lowest reach (1.5k users, and\n\t * the only desktop-leaning surface at 44%), while /c chat — the biggest\n\t * surface on the site by 10x (3.4M views) — wasn't in the navbar at all.\n\t * The primary row now carries what people actually use; everything else\n\t * lives here, one tap away, instead of being invisible on a 90%-mobile site.\n\t *\n\t * Ported 1:1 from the approved drawer mock. One accent drives the whole\n\t * panel and it comes from `activeMode`, so the menu wears whatever module\n\t * the viewer is currently in (duel = blue, arena = orange, …) — matching\n\t * the navbar/footer chrome instead of introducing a second palette.\n\t *\n\t * Live counters come from /api/nav-stats, fetched lazily on each open —\n\t * putting them in +layout.server.ts would tax every page nav for a panel\n\t * most loads never open.\n\t */\n\n\timport { onMount } from 'svelte';\n\timport { fade, fly } from 'svelte/transition';\n\timport { cubicOut } from 'svelte/easing';\n\timport { page } from '$app/state';\n\timport { devPreviewTier } from '$lib/dev-preview';\n\timport { goto, invalidateAll } from '$app/navigation';\n\timport { deserialize } from '$app/forms';\n\timport { getAvatarUrl } from '$lib/avatar';\n\timport { resetTracking } from '$lib/analytics';\n\timport { createSupabaseBrowserClient } from '$lib/supabase';\n\timport { activeMode, getModeAccent, type ActiveMode } from '$lib/stores/activeMode';\n\timport { navMenuOpen } from '$lib/stores/navMenu';\n\timport { installed } from '$lib/stores/pwa';\n\timport { PLUS_FROM_PER_MONTH, PRO_FROM_PER_MONTH } from '$lib/pricing';\n\timport { PLUS_MONTHLY_TOKENS, PRO_MONTHLY_TOKENS } from '$lib/pro-plan';\n\timport { xpForLevel } from '$lib/xp';\n\timport { payoutUsd } from '$lib/payout-rate';\n\timport AuthSheet from '$lib/components/AuthSheet.svelte';\n\timport UserBadge from '$lib/components/UserBadge.svelte';\n\timport TokenCoin from '$lib/components/TokenCoin.svelte';\n\timport ClanEmblem from '$lib/components/clan/ClanEmblem.svelte';\n\timport DuelAvatar from '$lib/components/duel/DuelAvatar.svelte';\n\n\t// The panel wears the current module's accent — same source the navbar and\n\t// footer read, so opening the menu never changes the page's color identity.\n\tconst accent = $derived(getModeAccent($activeMode).hex);\n\t// Light fills (yellow/green/lime) need dark text on them; everything else\n\t// takes white. Same flag the navbar CTA and BottomNav read.\n\tconst accentInk = $derived(getModeAccent($activeMode).onLight ? '#0b0b0b' : '#fff');\n\n\tconst isAuthed = $derived(!!page.data.user);\n\tconst nickname = $derived((page.data.nickname as string | null) ?? null);\n\tconst tokens = $derived((page.data.tokens as number) ?? 0);\n\tconst level = $derived((page.data.level as number) ?? 1);\n\tconst xp = $derived((page.data.xp as number) ?? 0);\n\tconst clanName = $derived((page.data.clanName as string | null) ?? null);\n\tconst clanTag = $derived((page.data.clanTag as string | null) ?? null);\n\tconst clanAvatarPath = $derived((page.data.clanAvatarPath as string | null) ?? null);\n\t// Same four flags UserBadge takes everywhere else on the site.\n\tconst isSubscribed = $derived(!!page.data.isSubscribed);\n\t// 'pro' | 'plus' | null. Only a pro holder is shown the Plus upsell — someone\n\t// already on plus has nothing to buy here.\n\tconst previewTier = $derived(devPreviewTier(page.url));\n\tconst subscriptionTier = $derived(\n\t\tpreviewTier ?? (page.data.subscriptionTier as string | null) ?? null\n\t);\n\tconst isRater = $derived(!!page.data.isRater);\n\tconst isCreator = $derived(!!page.data.isCreator);\n\tconst isVerified = $derived(!!page.data.isVerified);\n\tconst isAmbassador = $derived(!!page.data.isAmbassador);\n\tconst isAffiliate = $derived(!!page.data.isAffiliate);\n\tconst isAdmin = $derived(!!page.data.isAdmin);\n\tconst isModerator = $derived(!!page.data.isModerator);\n\tconst avatarUrl = $derived(getAvatarUrl(page.data.avatarPath as string | null));\n\n\t// Circumference of the r=26.5 progress ring (2πr) — the dasharray budget.\n\tconst RING_C = 2 * Math.PI * 26.5;\n\n\t// How far into the current level the viewer is — drives the ring around the\n\t// avatar. Guard the span: consecutive thresholds are never equal in practice,\n\t// but a bad/stale level must not divide by zero.\n\tconst xpPct = $derived.by(() => {\n\t\tconst floor = xpForLevel(level);\n\t\tconst span = xpForLevel(level + 1) - floor;\n\t\tif (span <= 0) return 0;\n\t\treturn Math.min(100, Math.max(0, ((xp - floor) / span) * 100));\n\t});\n\n\tlet showAuth = $state(false);\n\n\t// Rater console. Gated on raterStatus='active' for the same reason the navbar\n\t// pill is: queueOpen is a stale wizard default until the rater is publicly\n\t// visible, so showing \"live\" before that would advertise a queue nobody can\n\t// reach.\n\tconst raterStatus = $derived((page.data.raterStatus as string) ?? 'inactive');\n\tconst showRaterConsole = $derived(isRater && raterStatus === 'active');\n\n\t// Seeded through an $effect, not a one-shot initializer: invalidateAll()\n\t// below refreshes page.data, and a plain `$state(page.data.x)` would stay\n\t// frozen at its first value (RAT-117).\n\tlet queueLive = $state(false);\n\t$effect(() => {\n\t\tqueueLive = !!page.data.queueOpen;\n\t});\n\tlet queueBusy = $state(false);\n\n\t/** Posts to /p's existing `setQueueOpen` action rather than adding a second\n\t *  endpoint — that action also promotes a stale rater_status and notifies\n\t *  followers on a real paused→live edge, which a fresh write would skip. */\n\tasync function toggleQueue() {\n\t\tif (queueBusy) return;\n\t\tqueueBusy = true;\n\t\tconst next = !queueLive;\n\t\tqueueLive = next; // optimistic\n\t\ttry {\n\t\t\tconst res = await fetch('/p?/setQueueOpen', {\n\t\t\t\tmethod: 'POST',\n\t\t\t\tbody: new URLSearchParams({ open: next ? '1' : '0' }),\n\t\t\t\theaders: { accept: 'application/json', 'x-sveltekit-action': 'true' }\n\t\t\t});\n\t\t\tlet ok = res.ok;\n\t\t\ttry {\n\t\t\t\tok = deserialize(await res.text()).type === 'success';\n\t\t\t} catch {\n\t\t\t\t/* non-JSON body — fall back to the HTTP status */\n\t\t\t}\n\t\t\tif (ok) await invalidateAll();\n\t\t\telse queueLive = !next;\n\t\t} catch {\n\t\t\tqueueLive = !next; // roll back — the queue is still whatever it was\n\t\t}\n\t\tqueueBusy = false;\n\t}\n\n\t// ── live counters ────────────────────────────────────────────────────────\n\tinterface NavStats {\n\t\tratersOnline: number;\n\t\t/** null when the viewer isn't ranked (opted out, banned, or 0 XP). */\n\t\tmyRank: number | null;\n\t\t/** seated in a live game right now, all games together */\n\t\tgamesLive?: number;\n\t\t/** albums that went live in the last 7 days */\n\t\talbumsNew?: number;\n\t\t/** the viewer's own albums: live count and what unlocks earned (tokens) */\n\t\tmyAlbums?: { live: number; earnedTokens: number } | null;\n\t}\n\tlet stats = $state<NavStats | null>(null);\n\t// The earning card's album row: \"your albums\" for anyone who has started\n\t// one, \"sell your albums\" for everyone else.\n\tconst myAlbums = $derived(stats?.myAlbums ?? null);\n\tconst sellsAlbums = $derived(isCreator || (myAlbums?.live ?? 0) > 0);\n\tconst liveContest = $derived(\n\t\tpage.data.liveContest as { kind: string; endsAt: string | null } | null | undefined\n\t);\n\tconst contestEnds = $derived.by(() => {\n\t\tif (!liveContest?.endsAt) return null;\n\t\tconst ms = new Date(liveContest.endsAt).getTime() - Date.now();\n\t\tif (!Number.isFinite(ms) || ms <= 0) return null;\n\t\tconst days = Math.ceil(ms / 86_400_000);\n\t\treturn days > 1 ? `${days}d` : `${Math.max(1, Math.ceil(ms / 3_600_000))}h`;\n\t});\n\t// RAT-468: refetch on EVERY open, not once per client session. This used to\n\t// be guarded by a `statsRequested` flag, and the menu lives in the layout —\n\t// so a value fetched on the first open never changed again for the life of\n\t// the tab. The counts it carries are volatile (the sweeper cron pauses any\n\t// rater whose heartbeat is >5 min old, so `ratersOnline` steps down every\n\t// 5 minutes), and the chip drifted to half of what the /f + /c live rail\n\t// shows. The endpoint's own `private, max-age=15` absorbs open/close\n\t// drumming, and the drawer is opened rarely enough that this is no\n\t// meaningful extra load.\n\t$effect(() => {\n\t\tif (!$navMenuOpen) return;\n\t\t// A close — or a re-open — supersedes a response still in flight, so a\n\t\t// slow one can't land after a newer one and re-stale the chip. The\n\t\t// previous value stays on screen meanwhile: no flicker back to empty.\n\t\tlet superseded = false;\n\t\tfetch('/api/nav-stats')\n\t\t\t.then((r) => (r.ok ? r.json() : null))\n\t\t\t.then((d) => {\n\t\t\t\tif (d && !superseded) stats = d as NavStats;\n\t\t\t})\n\t\t\t.catch(() => {\n\t\t\t\t/* the menu is still perfectly usable without counters */\n\t\t\t});\n\t\treturn () => {\n\t\t\tsuperseded = true;\n\t\t};\n\t});\n\n\t// Drawer on phones, anchored dropdown on desktop — picks the fly direction.\n\tlet isMobile = $state(true);\n\tonMount(() => {\n\t\tconst mq = window.matchMedia('(max-width: 767px)');\n\t\tisMobile = mq.matches;\n\t\tconst onChange = (e: MediaQueryListEvent) => (isMobile = e.matches);\n\t\tmq.addEventListener('change', onChange);\n\t\treturn () => mq.removeEventListener('change', onChange);\n\t});\n\n\ttype Row = {\n\t\thref: string;\n\t\tlabel: string;\n\t\tmatch: string;\n\t\ticon: string[];\n\t\t/** Which module's accent this row's \"something is happening\" chip wears. */\n\t\tmode: ActiveMode;\n\t\tchip: string | null;\n\t};\n\n\t// Icons are lucide throughout so the column reads as one set — hand-rolled\n\t// flame/mask paths were the odd ones out.\n\t//\n\t// Order = what brings people back or makes them pay (feature audit, 30 days\n\t// to 2026-10-07, RAT-558/559). Roleplay and real raters first: the two best\n\t// menu-to-use rates (80% and 65% used it the same or the next day),\n\t// roleplay is the one feature that retains new users in both cohorts, and\n\t// real raters start 13% of return visits. Albums lead the list (2026-10):\n\t// a quarter of all token spend, the most of any one thing a viewer buys,\n\t// and until then six rows down in \"discover\". The chip says why to look\n\t// now: what went live this week.\n\tconst PLAY = $derived<Row[]>([\n\t\t{\n\t\t\thref: '/albums',\n\t\t\tlabel: t('albums'),\n\t\t\tmatch: '/albums',\n\t\t\tmode: 'albums',\n\t\t\ticon: ['M3 3h18v18H3z', 'M8.5 10a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z', 'm21 15-5-5L5 21'],\n\t\t\tchip: stats?.albumsNew ? t('{n} new', { n: stats.albumsNew }) : null\n\t\t},\n\t\t{\n\t\t\thref: '/roleplay',\n\t\t\tlabel: t('roleplay'),\n\t\t\tmatch: '/roleplay',\n\t\t\tmode: 'roleplay',\n\t\t\ticon: [\n\t\t\t\t'M2 12a5 5 0 0 0 5 5 8 8 0 0 1 5 2 8 8 0 0 1 5-2 5 5 0 0 0 5-5V7a3 3 0 0 0-3-3c-1.6 0-2.5.5-3.5 1.5S13.5 8 12 8s-2.5-.5-3.5-1.5S6.6 4 5 4a3 3 0 0 0-3 3z',\n\t\t\t\t'M6 11c1.5 0 3 .5 3 2-1.5 0-3 0-3-2z',\n\t\t\t\t'M18 11c-1.5 0-3 .5-3 2 1.5 0 3 0 3-2z'\n\t\t\t],\n\t\t\t// Public lobbies/active games you could still walk into right now.\n\t\t\t// RAT-463: no chip. It counted public multiplayer lobbies with a free\n\t\t\t// seat, and there is no lobby to join any more — a character chat is\n\t\t\t// private and asynchronous, so nothing about it is \"live\".\n\t\t\tchip: null\n\t\t},\n\t\t{\n\t\t\thref: '/r',\n\t\t\tlabel: t('real raters'),\n\t\t\tmatch: '/r',\n\t\t\tmode: 'raters',\n\t\t\ticon: ['M12 2l2.6 6.3L21 9l-5 4.4L17.5 21 12 17.3 6.5 21 8 13.4 3 9l6.4-.7z'],\n\t\t\tchip: stats && stats.ratersOnline > 0 ? t('{n} online', { n: stats.ratersOnline }) : null\n\t\t},\n\t\t{\n\t\t\t// Money, not return: the #2 token earner, but people rarely come back\n\t\t\t// for a second film (5% the next week), so it sits under the two that\n\t\t\t// do bring them back.\n\t\t\thref: '/studio',\n\t\t\tlabel: t('make a film'),\n\t\t\tmatch: '/studio',\n\t\t\t// Inert: `mode` only tints the chip, and this row has none. It takes an\n\t\t\t// existing value rather than growing ActiveMode for a colour nothing\n\t\t\t// reads.\n\t\t\tmode: 'roast',\n\t\t\t// lucide clapperboard, so the column stays one set.\n\t\t\ticon: [\n\t\t\t\t'M20.2 6 3 11l-.9-2.4c-.3-1.1.3-2.2 1.3-2.5l13.5-4c1.1-.3 2.2.3 2.5 1.3Z',\n\t\t\t\t'M6.2 5.3l3.1 3.9',\n\t\t\t\t'M12.4 3.4l3.1 4',\n\t\t\t\t'M3 11h18v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2Z'\n\t\t\t],\n\t\t\tchip: null\n\t\t},\n\t\t{\n\t\t\t// Inherited from the + picker that this drawer replaced. /rate is the\n\t\t\t// single biggest funnel on the site — it must keep a one-tap entry.\n\t\t\thref: '/rate',\n\t\t\tlabel: t('get rated'),\n\t\t\tmatch: '/rate',\n\t\t\tmode: 'roast',\n\t\t\ticon: [\n\t\t\t\t'M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z',\n\t\t\t\t'M12 17a4 4 0 1 0 0-8 4 4 0 0 0 0 8z'\n\t\t\t],\n\t\t\tchip: null\n\t\t},\n\t\t// The weekly contest gets its own row while it runs: it is a once-a-week\n\t\t// event we want voters at, and behind /games it was invisible from here.\n\t\t...(liveContest\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\thref: '/contest',\n\t\t\t\t\t\tlabel: liveContest.kind === 'rater' ? t('rater of the week') : t('most beautiful'),\n\t\t\t\t\t\tmatch: '/contest',\n\t\t\t\t\t\tmode: 'contest' as ActiveMode,\n\t\t\t\t\t\t// lucide \"trophy\"\n\t\t\t\t\t\ticon: [\n\t\t\t\t\t\t\t'M6 9H4.5a2.5 2.5 0 0 1 0-5H6',\n\t\t\t\t\t\t\t'M18 9h1.5a2.5 2.5 0 0 0 0-5H18',\n\t\t\t\t\t\t\t'M4 22h16',\n\t\t\t\t\t\t\t'M10 14.66V17c0 .55-.47.98-.97 1.21C7.85 18.75 7 20.24 7 22',\n\t\t\t\t\t\t\t'M14 14.66V17c0 .55.47.98.97 1.21C16.15 18.75 17 20.24 17 22',\n\t\t\t\t\t\t\t'M18 2H6v7a6 6 0 0 0 12 0V2Z'\n\t\t\t\t\t\t],\n\t\t\t\t\t\tchip: contestEnds ? t('ends in {time}', { time: contestEnds }) : t('live now')\n\t\t\t\t\t}\n\t\t\t\t]\n\t\t\t: []),\n\t\t{\n\t\t\t// RAT-559: one door for every game — strip poker, conquest, truth or\n\t\t\t// dare, the contest while one runs, duels. They had a row each and\n\t\t\t// together reach ~5% of active users with no revenue; their rows were\n\t\t\t// the menu's dead ends. Last in PLAY; the chip is its highlight, and\n\t\t\t// it shows only when someone is playing right now.\n\t\t\thref: '/games',\n\t\t\tlabel: t('games'),\n\t\t\tmatch: '/games',\n\t\t\tmode: 'contest',\n\t\t\t// lucide \"gamepad-2\"\n\t\t\ticon: ['M6 11h4', 'M8 9v4', 'M15 12h.01', 'M18 10h.01', 'M17.32 5H6.68a4 4 0 0 0-3.978 3.59c-.006.052-.01.101-.017.152C2.604 9.416 2 14.456 2 16a3 3 0 0 0 3 3c1 0 1.5-.5 2-1l1.414-1.414A2 2 0 0 1 9.828 16h4.344a2 2 0 0 1 1.414.586L17 18c.5.5 1 1 2 1a3 3 0 0 0 3-3c0-1.545-.604-6.584-.685-7.258-.007-.05-.011-.1-.017-.151A4 4 0 0 0 17.32 5z'],\n\t\t\tchip: stats?.gamesLive ? t('{n} playing', { n: stats.gamesLive }) : null\n\t\t}\n\t]);\n\n\tconst DISCOVER = $derived<Row[]>([\n\t\t{\n\t\t\thref: '/l',\n\t\t\tlabel: t('leaderboard'),\n\t\t\tmatch: '/l',\n\t\t\tmode: 'leaderboard',\n\t\t\ticon: ['M4 20V10', 'M12 20V4', 'M20 20v-6'],\n\t\t\t// Your own standing is the reason to tap through — show it, not a count.\n\t\t\tchip: stats?.myRank != null ? `#${stats.myRank.toLocaleString()}` : null\n\t\t},\n\t\t{\n\t\t\t// Finding a person or clan by name — before this, the only way was to\n\t\t\t// stumble on their profile or get a link over Discord.\n\t\t\thref: '/search',\n\t\t\tlabel: t('search'),\n\t\t\tmatch: '/search',\n\t\t\tmode: 'roast',\n\t\t\t// lucide search\n\t\t\ticon: ['M11 19a8 8 0 1 0 0-16 8 8 0 0 0 0 16z', 'm21 21-4.3-4.3'],\n\t\t\tchip: null\n\t\t},\n\t\t{\n\t\t\thref: '/k',\n\t\t\tlabel: t('clans'),\n\t\t\tmatch: '/k',\n\t\t\tmode: 'clan',\n\t\t\ticon: ['M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z'],\n\t\t\tchip: null\n\t\t}\n\t]);\n\n\t// Exact segment match — /r must not light up on /roleplay or /rate.\n\tfunction isActive(match: string): boolean {\n\t\tconst p = page.url.pathname;\n\t\treturn p === match || p.startsWith(match + '/');\n\t}\n\n\tfunction close() {\n\t\tnavMenuOpen.set(false);\n\t}\n\n\tfunction onKeydown(e: KeyboardEvent) {\n\t\tif (e.key === 'Escape' && $navMenuOpen) close();\n\t}\n\n\t// Reuses /p's sign-out verbatim: browser-client signOut, drop the analytics\n\t// identity, then land on /. The goto also clears the session cookie via the\n\t// server load even if the client call throws.\n\tasync function signOut() {\n\t\tclose();\n\t\ttry {\n\t\t\tconst supabase = createSupabaseBrowserClient();\n\t\t\tawait supabase.auth.signOut();\n\t\t\tresetTracking();\n\t\t} catch {\n\t\t\t// non-fatal — the nav below still drops the session server-side\n\t\t}\n\t\tawait goto('/');\n\t}\n\n\t// The drawer is fixed and full-height; without this the page behind it\n\t// scrolls under it on iOS. Restores whatever overflow was there before.\n\t$effect(() => {\n\t\tif (!$navMenuOpen) return;\n\t\tconst prev = document.body.style.overflow;\n\t\tdocument.body.style.overflow = 'hidden';\n\t\treturn () => {\n\t\t\tdocument.body.style.overflow = prev;\n\t\t};\n\t});\n</script>\n\n<svelte:window onkeydown={onKeydown} />\n\n{#snippet navRow(r: Row)}\n\t{@const on = isActive(r.match)}\n\t<!-- The studio row loads natively: /studio only redirects to the studio's\n\t\t domain (RAT-561). -->\n\t<a\n\t\tclass=\"row\"\n\t\tclass:on\n\t\thref={r.href}\n\t\tdata-sveltekit-reload={r.href === '/studio' ? '' : undefined}\n\t\tonclick={close}\n\t>\n\t\t<span class=\"row-ico\">\n\t\t\t<svg\n\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\tfill=\"none\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstroke-width=\"1.8\"\n\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\taria-hidden=\"true\"\n\t\t\t>\n\t\t\t\t{#each r.icon as d (d)}<path {d} />{/each}\n\t\t\t</svg>\n\t\t</span>\n\t\t<span class=\"row-label\">{r.label}</span>\n\t\t{#if r.chip}\n\t\t\t<span class=\"chip\" style=\"--c: {getModeAccent(r.mode).hex};\">{r.chip}</span>\n\t\t{/if}\n\t\t<svg\n\t\t\tclass=\"row-chev\"\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstroke-width=\"2\"\n\t\t\tstroke-linecap=\"round\"\n\t\t\tstroke-linejoin=\"round\"\n\t\t\taria-hidden=\"true\"\n\t\t>\n\t\t\t<path d=\"m9 18 6-6-6-6\" />\n\t\t</svg>\n\t</a>\n{/snippet}\n\n<!-- The seller's row in the earning card: what an album pays to someone who\n     hasn't made one, and theirs (live count, what it earned) to someone who has.\n     Both open the studio. -->\n{#snippet albumsRow()}\n\t<a href=\"/album\" class=\"becomerater\" onclick={close}>\n\t\t<span class=\"br-ico\">\n\t\t\t<svg\n\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\tfill=\"none\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstroke-width=\"1.8\"\n\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\taria-hidden=\"true\"\n\t\t\t>\n\t\t\t\t<path d=\"M3 3h18v18H3z\" /><path d=\"M8.5 10a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z\" /><path\n\t\t\t\t\td=\"m21 15-5-5L5 21\"\n\t\t\t\t/>\n\t\t\t</svg>\n\t\t</span>\n\t\t<span class=\"br-copy\">\n\t\t\t{#if sellsAlbums}\n\t\t\t\t<b>{t('your albums')}</b>\n\t\t\t\t<small\n\t\t\t\t\t>{myAlbums && myAlbums.live > 0\n\t\t\t\t\t\t? t('{n} live', { n: myAlbums.live })\n\t\t\t\t\t\t: t('finish your first one')}</small\n\t\t\t\t>\n\t\t\t{:else}\n\t\t\t\t<b>{t('sell your albums')}</b>\n\t\t\t\t<small>{t('{amount} per unlock', { amount: payoutUsd(15) })}</small>\n\t\t\t{/if}\n\t\t</span>\n\t\t{#if sellsAlbums && myAlbums && myAlbums.earnedTokens > 0}\n\t\t\t<span class=\"earned\">{payoutUsd(myAlbums.earnedTokens)}</span>\n\t\t{/if}\n\t\t<svg\n\t\t\tclass=\"br-chev\"\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstroke-width=\"2.2\"\n\t\t\tstroke-linecap=\"round\"\n\t\t\tstroke-linejoin=\"round\"\n\t\t\taria-hidden=\"true\"><path d=\"m9 18 6-6-6-6\" /></svg\n\t\t>\n\t</a>\n{/snippet}\n\n{#if $navMenuOpen}\n\t<button\n\t\tclass=\"backdrop\"\n\t\taria-label={t('close menu')}\n\t\tonclick={close}\n\t\ttransition:fade={{ duration: 160 }}\n\t></button>\n\t<div\n\t\tclass=\"panel\"\n\t\trole=\"dialog\"\n\t\taria-modal=\"true\"\n\t\taria-label={t('menu')}\n\t\tstyle=\"--accent: {accent}; --accent-ink: {accentInk};\"\n\t\ttransition:fly={isMobile\n\t\t\t? { x: 360, duration: 300, easing: cubicOut }\n\t\t\t: { y: -10, duration: 180, easing: cubicOut }}\n\t>\n\t\t<div class=\"close-row\">\n\t\t\t<button class=\"close-btn\" aria-label={t('close menu')} onclick={close}>\n\t\t\t\t<svg\n\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\tfill=\"none\"\n\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\tstroke-width=\"2\"\n\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\taria-hidden=\"true\"><path d=\"M18 6 6 18M6 6l12 12\" /></svg\n\t\t\t\t>\n\t\t\t</button>\n\t\t</div>\n\n\t\t{#if isAuthed}\n\t\t\t<div class=\"ident\">\n\t\t\t\t<a\n\t\t\t\t\thref=\"/p\"\n\t\t\t\t\tclass=\"ident-av\"\n\t\t\t\t\tonclick={close}\n\t\t\t\t\taria-label=\"your profile — level {level}, {Math.round(xpPct)}% to level {level + 1}\"\n\t\t\t\t>\n\t\t\t\t\t<svg class=\"av-ring\" viewBox=\"0 0 58 58\" aria-hidden=\"true\">\n\t\t\t\t\t\t<circle class=\"ring-track\" cx=\"29\" cy=\"29\" r=\"26.5\" />\n\t\t\t\t\t\t<circle\n\t\t\t\t\t\t\tclass=\"ring-fill\"\n\t\t\t\t\t\t\tcx=\"29\"\n\t\t\t\t\t\t\tcy=\"29\"\n\t\t\t\t\t\t\tr=\"26.5\"\n\t\t\t\t\t\t\tstroke-dasharray={RING_C}\n\t\t\t\t\t\t\tstroke-dashoffset={RING_C - (RING_C * xpPct) / 100}\n\t\t\t\t\t\t\ttransform=\"rotate(-90 29 29)\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</svg>\n\t\t\t\t\t<span class=\"av-face\">\n\t\t\t\t\t\t<DuelAvatar {nickname} {avatarUrl} size={46} link={false} />\n\t\t\t\t\t</span>\n\t\t\t\t\t<span class=\"ident-lv\">{level}</span>\n\t\t\t\t</a>\n\t\t\t\t<div class=\"ident-body\">\n\t\t\t\t\t<div class=\"ident-top\">\n\t\t\t\t\t\t<!-- The bottom nav no longer has a profile slot, so this header is\n\t\t\t\t\t\t\t the way to /p — the chevron makes that readable as a row. -->\n\t\t\t\t\t\t<a href=\"/p\" class=\"ident-nick\" onclick={close}>\n\t\t\t\t\t\t\t<span class=\"nick-text\">@{nickname ?? 'you'}</span>\n\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\tstroke-width=\"2.4\"\n\t\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\t\t\taria-hidden=\"true\"><path d=\"m9 18 6-6-6-6\" /></svg\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t</a>\n\t\t\t\t\t\t<span class=\"ident-badges\">\n\t\t\t\t\t\t\t<UserBadge\n\t\t\t\t\t\t\t\trater={isRater}\n\t\t\t\t\t\t\t\tcreator={isCreator}\n\t\t\t\t\t\t\t\tsubscriber={isSubscribed}\n\t\t\t\t\t\t\t\tadmin={isAdmin}\n\t\t\t\t\t\t\t\tverified={isVerified}\n\t\t\t\t\t\t\t\tambassador={isAmbassador}\n\t\t\t\t\t\t\t\tsize={14}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t{#if clanName && clanTag}\n\t\t\t\t\t\t<a href=\"/k/{clanTag}\" class=\"clan\" onclick={close}>\n\t\t\t\t\t\t\t<ClanEmblem tag={clanTag} avatarPath={clanAvatarPath} size={16} link={false} />\n\t\t\t\t\t\t\t<span class=\"clan-name\">{clanName}</span>\n\t\t\t\t\t\t\t<span class=\"clan-tag\">[{clanTag}]</span>\n\t\t\t\t\t\t</a>\n\t\t\t\t\t{:else}\n\t\t\t\t\t\t<!-- Same line /u/[nickname] shows, so an empty header still has a\n\t\t\t\t\t\t\t shape instead of collapsing to a lone handle. -->\n\t\t\t\t\t\t<span class=\"clan clan-none\">{t('no clan · lone wolf')}</span>\n\t\t\t\t\t{/if}\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{#if isAmbassador || isAffiliate}\n\t\t\t\t<!-- ambassadors and affiliates: their link, clicks, signups and earnings\n\t\t\t\t\t live on the affiliate dashboard (same login) — this is the only way in\n\t\t\t\t\t from the ui. Affiliates had no way in and asked support for their link. -->\n\t\t\t\t<a href=\"/affiliate/dashboard\" class=\"amb\" onclick={close}>\n\t\t\t\t\t<span class=\"amb-tag\">{isAmbassador ? 'AMBASSADOR' : 'AFFILIATE'}</span>\n\t\t\t\t\t<span class=\"amb-copy\"><b>{t('your link & earnings')}</b><span>{t('clicks, signups, what they spent')}</span></span>\n\t\t\t\t\t<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.4\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"m9 18 6-6-6-6\" /></svg>\n\t\t\t\t</a>\n\t\t\t{/if}\n\n\t\t\t{#if !isSubscribed && !previewTier}\n\t\t\t\t<a href=\"/pricing\" class=\"premium\" onclick={close}>\n\t\t\t\t\t<span class=\"prem-tag\">{t('PRO')}</span>\n\t\t\t\t\t<span class=\"prem-copy\">\n\t\t\t\t\t\t<b>{t('Premium')}</b>\n\t\t\t\t\t\t<small>from ${PRO_FROM_PER_MONTH}/mo · {PRO_MONTHLY_TOKENS} tokens monthly</small>\n\t\t\t\t\t</span>\n\t\t\t\t\t<svg\n\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\tstroke-width=\"2.2\"\n\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\taria-hidden=\"true\"><path d=\"m9 18 6-6-6-6\" /></svg\n\t\t\t\t\t>\n\t\t\t\t</a>\n\t\t\t{:else if subscriptionTier === 'pro'}\n\t\t\t\t<!-- Plus is an upgrade, so it only exists for someone already holding\n\t\t\t\t\t pro — same card, same tap-through as the Premium row above, purple\n\t\t\t\t\t instead of coral so the two are never confused at a glance. -->\n\t\t\t\t<a href=\"/pricing/plus\" class=\"premium plus\" onclick={close}>\n\t\t\t\t\t<span class=\"prem-tag\">PLUS</span>\n\t\t\t\t\t<span class=\"prem-copy\">\n\t\t\t\t\t\t<b>{t('Premium Plus')}</b>\n\t\t\t\t\t\t<small>from ${PLUS_FROM_PER_MONTH}/mo · {PLUS_MONTHLY_TOKENS} tokens monthly</small>\n\t\t\t\t\t</span>\n\t\t\t\t\t<svg\n\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\tstroke-width=\"2.2\"\n\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\taria-hidden=\"true\"><path d=\"m9 18 6-6-6-6\" /></svg\n\t\t\t\t\t>\n\t\t\t\t</a>\n\t\t\t{/if}\n\n\t\t\t<!-- The whole card is the tap target — straight to /pricing's token\n\t\t\t\t packs (anchor-scrolled); \"get more\" is a visual affordance now. -->\n\t\t\t<a href=\"/pricing#tokens\" class=\"tokens\" onclick={close}>\n\t\t\t\t<span class=\"tok-ico\"><TokenCoin size={28} /></span>\n\t\t\t\t<span class=\"tok-body\">\n\t\t\t\t\t<span class=\"tok-n\">{tokens.toLocaleString()}</span>\n\t\t\t\t\t<span class=\"tok-l\">{t('tokens')}</span>\n\t\t\t\t</span>\n\t\t\t\t<span class=\"tok-cta\">{t('get more →')}</span>\n\t\t\t</a>\n\n\t\t\t{#if showRaterConsole}\n\t\t\t\t<!-- RAT-559: says what switching on means — strangers sending dick\n\t\t\t\t\t pics — instead of \"paused\"/\"accepting rates\". The switch is the\n\t\t\t\t\t state, so no dot or state word beside it. Off is not \"nobody can\n\t\t\t\t\t send you a pic\": queue_open only lists you among live raters,\n\t\t\t\t\t people can still send from your profile or a chat. -->\n\t\t\t\t<div class=\"rater\" class:live={queueLive}>\n\t\t\t\t\t<div class=\"rater-head\">\n\t\t\t\t\t\t<span class=\"rater-copy\">\n\t\t\t\t\t\t\t<span class=\"rater-title\">{t('live rater')}</span>\n\t\t\t\t\t\t\t<span class=\"rater-sub\"\n\t\t\t\t\t\t\t\t>{queueLive ? t('strangers can send you dick pics') : t('turn on to get dick pics to rate')}</span\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tclass=\"switch\"\n\t\t\t\t\t\t\tclass:on={queueLive}\n\t\t\t\t\t\t\trole=\"switch\"\n\t\t\t\t\t\t\taria-checked={queueLive}\n\t\t\t\t\t\t\taria-label={t('live rater')}\n\t\t\t\t\t\t\tdisabled={queueBusy}\n\t\t\t\t\t\t\tonclick={toggleQueue}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<span class=\"knob\"></span>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</div>\n\t\t\t\t\t<!-- Rows like the rest of the menu. The floor gets the explanation,\n\t\t\t\t\t\t and it has to say \"no camera\": \"live show\" read as a webcam\n\t\t\t\t\t\t show. The camera row is the reverse — viewers rate you — so it\n\t\t\t\t\t\t says so. 686 raters ran a floor in 30 days against 35 on camera,\n\t\t\t\t\t\t so the camera row stays one step quieter. -->\n\t\t\t\t\t<a href=\"/floor/start\" class=\"ract\" onclick={close}>\n\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\tclass=\"ract-ico\"\n\t\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\tstroke-width=\"1.8\"\n\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t><path\n\t\t\t\t\t\t\t\td=\"M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z\"\n\t\t\t\t\t\t\t/></svg\n\t\t\t\t\t\t>\n\t\t\t\t\t\t<span class=\"ract-copy\">\n\t\t\t\t\t\t\t<span class=\"ract-title\">{t('public stage')}</span>\n\t\t\t\t\t\t\t<span class=\"ract-sub\">{t('no camera · one dick at a time, everyone watches')}</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<svg class=\"ract-chev\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"m9 18 6-6-6-6\" /></svg>\n\t\t\t\t\t</a>\n\t\t\t\t\t<a href=\"/livecam/start\" class=\"ract quiet\" onclick={close}>\n\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\tclass=\"ract-ico\"\n\t\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\tstroke-width=\"1.8\"\n\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t><path d=\"m23 7-7 5 7 5V7z\" /><path\n\t\t\t\t\t\t\t\td=\"M1 7a2 2 0 0 1 2-2h11a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2z\"\n\t\t\t\t\t\t\t/></svg\n\t\t\t\t\t\t>\n\t\t\t\t\t\t<span class=\"ract-copy\"><span class=\"ract-title\">{t('get rated on camera')}</span></span>\n\t\t\t\t\t\t<svg class=\"ract-chev\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"m9 18 6-6-6-6\" /></svg>\n\t\t\t\t\t</a>\n\t\t\t\t</div>\n\t\t\t\t<div class=\"earn\">{@render albumsRow()}</div>\n\t\t\t{:else}\n\t\t\t\t<!-- Non-raters (and half-onboarded ones) get the recruitment entry in\n\t\t\t\t\t the same slot the console occupies for active raters — one tap to\n\t\t\t\t\t the profile's rater tab where \"activate\" is a single click. The\n\t\t\t\t\t album row shares the card: the two ways to earn here, together. -->\n\t\t\t\t<div class=\"earn\">\n\t\t\t\t<a href=\"/p?tab=rater\" class=\"becomerater\" onclick={close}>\n\t\t\t\t\t<span class=\"br-ico\">\n\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\tstroke-width=\"1.8\"\n\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<path d=\"M12 2l2.6 6.3L21 9l-5 4.4L17.5 21 12 17.3 6.5 21 8 13.4 3 9l6.4-.7z\" />\n\t\t\t\t\t\t</svg>\n\t\t\t\t\t</span>\n\t\t\t\t\t<span class=\"br-copy\">\n\t\t\t\t\t\t<b>{t('become a rater')}</b>\n\t\t\t\t\t\t<small>{t('rate pics people send you · free')}</small>\n\t\t\t\t\t</span>\n\t\t\t\t\t<svg\n\t\t\t\t\t\tclass=\"br-chev\"\n\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\tstroke-width=\"2.2\"\n\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\taria-hidden=\"true\"><path d=\"m9 18 6-6-6-6\" /></svg\n\t\t\t\t\t>\n\t\t\t\t</a>\n\t\t\t\t{@render albumsRow()}\n\t\t\t\t</div>\n\t\t\t{/if}\n\t\t{:else}\n\t\t\t<!-- Mirrors the signed-in header (avatar + copy, then a card) so the two\n\t\t\t\t states read as one design instead of a bare slab. -->\n\t\t\t<div class=\"guest\">\n\t\t\t\t<span class=\"guest-av\">\n\t\t\t\t\t<svg\n\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\tstroke-width=\"1.7\"\n\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<path d=\"M18 19a6 6 0 0 0-12 0\" /><circle cx=\"12\" cy=\"9\" r=\"3.5\" />\n\t\t\t\t\t</svg>\n\t\t\t\t</span>\n\t\t\t\t<span class=\"guest-copy\">\n\t\t\t\t\t<span class=\"guest-title\">{t('not signed in')}</span>\n\t\t\t\t\t<span class=\"guest-sub\">{t('rate, duel, get ranked')}</span>\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t\t<button\n\t\t\t\tclass=\"signin\"\n\t\t\t\tonclick={() => {\n\t\t\t\t\tclose();\n\t\t\t\t\tshowAuth = true;\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t{t('sign in')}\n\t\t\t\t<svg\n\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\tfill=\"none\"\n\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\tstroke-width=\"2.4\"\n\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\taria-hidden=\"true\"><path d=\"M5 12h13M13 6l6 6-6 6\" /></svg\n\t\t\t\t>\n\t\t\t</button>\n\t\t{/if}\n\n\t\t<div class=\"grp\"><span>{t('play')}</span><i></i></div>\n\t\t{#each PLAY as item (item.label)}{@render navRow(item)}{/each}\n\n\t\t<div class=\"grp\"><span>{t('discover')}</span><i></i></div>\n\t\t{#each DISCOVER as item (item.label)}{@render navRow(item)}{/each}\n\n\t\t<!-- staff shortcut: mods used to reach /mod only through a pasted link,\n\t\t\t which on a phone opened outside the app and lost the session -->\n\t\t{#if isAdmin || isModerator}\n\t\t\t<a href={isAdmin ? '/admin' : '/mod'} class=\"dealrow\" onclick={close}>\n\t\t\t\t<svg\n\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\tfill=\"none\"\n\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\tstroke-width=\"1.8\"\n\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t>\n\t\t\t\t\t<path d=\"M12 3l7 3v6c0 4.5-3 8-7 9.5C8 20 5 16.5 5 12V6z\" />\n\t\t\t\t</svg>\n\t\t\t\t{isAdmin ? 'admin' : 'mod panel'}\n\t\t\t</a>\n\t\t{/if}\n\n\t\t<!-- RAT-446 follow-up: the install entry lives here now — the old\n\t\t\t InstallNudge popup is gone; the drawer is the non-nagging home for it.\n\t\t\t Hidden once installed/standalone (initPwa sets the store). -->\n\t\t<!-- affiliate program — same row treatment as the deal/install entries.\n\t\t\t (took the animations slot; /animations is still reachable directly) -->\n\t\t<a href=\"/affiliate\" class=\"dealrow\" onclick={close}>\n\t\t\t<svg\n\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\tfill=\"none\"\n\t\t\t\tstroke=\"currentColor\"\n\t\t\t\tstroke-width=\"1.8\"\n\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\taria-hidden=\"true\"\n\t\t\t>\n\t\t\t\t<circle cx=\"12\" cy=\"12\" r=\"9\" />\n\t\t\t\t<path d=\"M14.5 9.5a2.5 2.5 0 0 0-2.5-1.5c-1.5 0-2.5.8-2.5 2s1 1.7 2.5 2 2.5.8 2.5 2-1 2-2.5 2a2.5 2.5 0 0 1-2.5-1.5\" />\n\t\t\t\t<path d=\"M12 6.5v1.5M12 16v1.5\" />\n\t\t\t</svg>\n\t\t\t{t('become an affiliate')}\n\t\t\t<span class=\"deal-badge\">{t('earn')}</span>\n\t\t</a>\n\n\t\t{#if !$installed}\n\t\t\t<a href=\"/install\" class=\"install\" onclick={close}>\n\t\t\t\t<svg\n\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\tfill=\"none\"\n\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\tstroke-width=\"1.8\"\n\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t>\n\t\t\t\t\t<path d=\"M12 3v12\" /><path d=\"m7 10 5 5 5-5\" /><path d=\"M5 21h14\" />\n\t\t\t\t</svg>\n\t\t\t\t{t('install the app')}\n\t\t\t</a>\n\t\t{/if}\n\n\t\t<!-- RAT-587: the interface language behind a globe, in the bottom row —\n\t\t\t one compact control instead of a block of chips -->\n\t\t<div class=\"foot\">\n\t\t\t<LanguagePicker variant=\"globe\" />\n\t\t\t{#if isAuthed}\n\t\t\t\t<a href=\"/p\" class=\"foot-link\" onclick={close}>{t('settings')}</a>\n\t\t\t\t<button class=\"foot-link foot-out\" onclick={signOut}>{t('sign out')}</button>\n\t\t\t{/if}\n\t\t</div>\n\t</div>\n{/if}\n\n<AuthSheet bind:open={showAuth} />\n\n<style>\n\t.backdrop {\n\t\tposition: fixed;\n\t\tinset: 0;\n\t\tz-index: 60;\n\t\tborder: 0;\n\t\tpadding: 0;\n\t\tbackground: rgba(0, 0, 0, 0.62);\n\t\tcursor: default;\n\t}\n\n\t.panel {\n\t\tposition: fixed;\n\t\ttop: 0;\n\t\tbottom: 0;\n\t\tright: 0;\n\t\tz-index: 61;\n\t\twidth: min(86vw, 360px);\n\t\toverflow-y: auto;\n\t\toverscroll-behavior: contain;\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t\tborder-left: 1px solid var(--color-border);\n\t\tbackground: var(--color-bg);\n\t\tpadding: 0.4rem 1.15rem calc(1.4rem + env(safe-area-inset-bottom, 0px));\n\t\t/* desktop scrollbar was browser-default white — jarring on the dark panel */\n\t\tscrollbar-width: thin;\n\t\tscrollbar-color: rgba(255, 255, 255, 0.16) transparent;\n\t}\n\t.panel::-webkit-scrollbar {\n\t\twidth: 8px;\n\t}\n\t.panel::-webkit-scrollbar-track {\n\t\tbackground: transparent;\n\t}\n\t.panel::-webkit-scrollbar-thumb {\n\t\tbackground: rgba(255, 255, 255, 0.16);\n\t\tborder-radius: 4px;\n\t}\n\n\t.close-row {\n\t\tdisplay: flex;\n\t\tjustify-content: flex-end;\n\t}\n\t.close-btn {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\twidth: 38px;\n\t\theight: 38px;\n\t\tmargin-right: -0.35rem;\n\t\tborder: 1px solid var(--color-border);\n\t\tborder-radius: 10px;\n\t\tbackground: none;\n\t\tcolor: var(--color-muted);\n\t\tcursor: pointer;\n\t}\n\t.close-btn svg {\n\t\twidth: 17px;\n\t\theight: 17px;\n\t}\n\n\t/* ── identity ─────────────────────────────────────────────────────────── */\n\t.ident {\n\t\tdisplay: flex;\n\t\talign-items: flex-start;\n\t\tgap: 0.8rem;\n\t\tpadding: 0.1rem 0.1rem 0.9rem;\n\t}\n\t/* The ring is the XP bar for the current level. Conic gradient masked to a\n\t * 3.5px annulus; the face sits at 46px so ~2.5px of bg shows through as a\n\t * gap, keeping the ring visually separate from the photo. */\n\t.ident-av {\n\t\tposition: relative;\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\twidth: 58px;\n\t\theight: 58px;\n\t\tflex-shrink: 0;\n\t\tborder-radius: 50%;\n\t\tcolor: var(--color-text);\n\t\ttext-decoration: none;\n\t}\n\t/* SVG, not a masked conic-gradient: the browser antialiases a stroked arc\n\t * (a masked gradient edge it does not — that's what made it look jagged),\n\t * and stroke-linecap gives the progress its rounded ends. */\n\t.av-ring {\n\t\tposition: absolute;\n\t\tinset: 0;\n\t\twidth: 100%;\n\t\theight: 100%;\n\t}\n\t.av-ring circle {\n\t\tfill: none;\n\t\tstroke-width: 3.5;\n\t}\n\t.ring-track {\n\t\tstroke: rgba(255, 255, 255, 0.1);\n\t}\n\t.ring-fill {\n\t\tstroke: var(--accent);\n\t\tstroke-linecap: round;\n\t\ttransition: stroke-dashoffset 0.4s ease;\n\t}\n\t.av-face {\n\t\tposition: absolute;\n\t\ttop: 50%;\n\t\tleft: 50%;\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\twidth: 46px;\n\t\theight: 46px;\n\t\ttransform: translate(-50%, -50%);\n\t\tborder-radius: 50%;\n\t\toverflow: hidden;\n\t\tbackground: var(--color-surface-2);\n\t}\n\t.ident-lv {\n\t\tposition: absolute;\n\t\tright: -3px;\n\t\tbottom: -3px;\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\tmin-width: 19px;\n\t\theight: 19px;\n\t\tpadding: 0 4px;\n\t\tborder: 2px solid var(--color-bg);\n\t\tborder-radius: 999px;\n\t\tbackground: var(--accent);\n\t\tcolor: var(--accent-ink);\n\t\tfont-family: var(--font-display);\n\t\tfont-size: 0.62rem;\n\t\tfont-weight: 700;\n\t}\n\n\t.ident-body {\n\t\tmin-width: 0;\n\t\tpadding-top: 0.15rem;\n\t}\n\t.ident-top {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tgap: 0.4rem;\n\t}\n\t.ident-nick {\n\t\tdisplay: inline-flex;\n\t\talign-items: center;\n\t\tgap: 0.15rem;\n\t\t/* min-width:0 lets a long handle shrink instead of shoving the badges\n\t\t * out of the panel; the ellipsis lives on the text span because\n\t\t * text-overflow does nothing on a flex container. */\n\t\tmin-width: 0;\n\t\tfont-family: var(--font-display);\n\t\tfont-size: 1.05rem;\n\t\tfont-weight: 700;\n\t\tcolor: var(--color-text);\n\t\ttext-decoration: none;\n\t}\n\t.nick-text {\n\t\toverflow: hidden;\n\t\ttext-overflow: ellipsis;\n\t\twhite-space: nowrap;\n\t}\n\t.ident-badges {\n\t\tdisplay: inline-flex;\n\t\tflex-shrink: 0;\n\t}\n\t.ident-nick svg {\n\t\twidth: 13px;\n\t\theight: 13px;\n\t\tflex-shrink: 0;\n\t\tcolor: color-mix(in srgb, var(--color-muted) 70%, transparent);\n\t}\n\t/* Bare clan line — emblem + name, no chip. Matches how /u/[nickname] shows it. */\n\t.amb {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tgap: 0.6rem;\n\t\tmargin: 0.6rem 0 0.65rem;\n\t\tpadding: 0.6rem 0.75rem;\n\t\tborder-radius: 12px;\n\t\tbackground: var(--color-surface);\n\t\tborder: 1px solid var(--color-border);\n\t\ttext-decoration: none;\n\t\tcolor: var(--color-text);\n\t}\n\t.amb-tag {\n\t\tfont-family: var(--font-display);\n\t\tfont-size: 9px;\n\t\tfont-weight: 800;\n\t\tletter-spacing: 0.1em;\n\t\tpadding: 3px 6px;\n\t\tborder-radius: 5px;\n\t\tbackground: var(--color-violet);\n\t\tcolor: #fff;\n\t\tflex-shrink: 0;\n\t}\n\t.amb-copy {\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t\tmin-width: 0;\n\t\tflex: 1;\n\t\tline-height: 1.2;\n\t}\n\t.amb-copy b {\n\t\tfont-family: var(--font-display);\n\t\tfont-size: 0.8rem;\n\t\tfont-weight: 700;\n\t}\n\t.amb-copy span {\n\t\tfont-size: 0.7rem;\n\t\tcolor: var(--color-muted);\n\t}\n\t.amb svg {\n\t\twidth: 14px;\n\t\theight: 14px;\n\t\tcolor: var(--color-muted);\n\t\tflex-shrink: 0;\n\t}\n\t.clan {\n\t\tdisplay: inline-flex;\n\t\talign-items: center;\n\t\tgap: 0.35rem;\n\t\tmargin-top: 0.35rem;\n\t\tmax-width: 100%;\n\t\ttext-decoration: none;\n\t}\n\t.clan-none {\n\t\tfont-size: 0.72rem;\n\t\tcolor: color-mix(in srgb, var(--color-muted) 80%, transparent);\n\t}\n\t.clan-name {\n\t\tmin-width: 0;\n\t\tfont-size: 0.78rem;\n\t\tcolor: var(--color-text);\n\t\toverflow: hidden;\n\t\ttext-overflow: ellipsis;\n\t\twhite-space: nowrap;\n\t}\n\t.clan-tag {\n\t\tflex-shrink: 0;\n\t\tfont-family: var(--font-mono, ui-monospace, monospace);\n\t\tfont-size: 0.62rem;\n\t\tletter-spacing: 0.04em;\n\t\tcolor: var(--color-muted);\n\t}\n\n\t/* ── premium upsell ──────────────────────────────────────────────────── */\n\t/* Fixed brand coral — deliberately NOT var(--accent): the drawer wears the\n\t   current module's color, but the Premium card is one product everywhere.\n\t   Shimmer = the navbar \"new\" badge's sweeping-band pattern (solid fill,\n\t   no glow → within the no-neon rule). */\n\t.premium {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tgap: 0.7rem;\n\t\tmargin: 0 0 0.6rem;\n\t\tpadding: 0.75rem 0.9rem;\n\t\tborder-radius: 12px;\n\t\tbackground: linear-gradient(\n\t\t\t100deg,\n\t\t\tvar(--color-coral) 0%,\n\t\t\tvar(--color-coral) 40%,\n\t\t\t#ff8ba0 50%,\n\t\t\tvar(--color-coral) 60%,\n\t\t\tvar(--color-coral) 100%\n\t\t);\n\t\tbackground-size: 220% 100%;\n\t\tbackground-position: 120% 0;\n\t\tanimation: prem-shimmer 2.8s ease-in-out infinite;\n\t\tcolor: #fff;\n\t\ttext-decoration: none;\n\t}\n\t@keyframes prem-shimmer {\n\t\t0% {\n\t\t\tbackground-position: 120% 0;\n\t\t}\n\t\t45%,\n\t\t100% {\n\t\t\tbackground-position: -20% 0;\n\t\t}\n\t}\n\t@media (prefers-reduced-motion: reduce) {\n\t\t.premium {\n\t\t\tanimation: none;\n\t\t\tbackground: var(--color-coral);\n\t\t}\n\t}\n\t/* Plus wears the same card in purple — the tier above pro, and the only\n\t   difference a pro holder needs to read at a glance. Solid fill + the same\n\t   shimmer band as above, no glow (no-neon rule). */\n\t.premium.plus {\n\t\tbackground: linear-gradient(\n\t\t\t100deg,\n\t\t\tvar(--color-purple) 0%,\n\t\t\tvar(--color-purple) 40%,\n\t\t\t#a78bfa 50%,\n\t\t\tvar(--color-purple) 60%,\n\t\t\tvar(--color-purple) 100%\n\t\t);\n\t\tbackground-size: 220% 100%;\n\t\tbackground-position: 120% 0;\n\t}\n\t@media (prefers-reduced-motion: reduce) {\n\t\t.premium.plus {\n\t\t\tbackground: var(--color-purple);\n\t\t}\n\t}\n\t.prem-tag {\n\t\tflex-shrink: 0;\n\t\tpadding: 0.15rem 0.4rem;\n\t\tborder: 1.5px solid currentColor;\n\t\tborder-radius: 6px;\n\t\tfont-size: 0.62rem;\n\t\tfont-weight: 800;\n\t\tletter-spacing: 0.08em;\n\t}\n\t.prem-copy {\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t\tline-height: 1.2;\n\t\tmin-width: 0;\n\t}\n\t.prem-copy b {\n\t\tfont-size: 0.9rem;\n\t}\n\t.prem-copy small {\n\t\tfont-size: 0.68rem;\n\t\topacity: 0.85;\n\t}\n\t.premium svg {\n\t\twidth: 15px;\n\t\theight: 15px;\n\t\tmargin-left: auto;\n\t\tflex-shrink: 0;\n\t}\n\n\t/* ── tokens ───────────────────────────────────────────────────────────── */\n\t/* The earning card: becoming a rater and selling albums as rows of one\n\t   card, split by a hairline (a rater's card holds just the album row). */\n\t.earn {\n\t\tborder: 1px solid var(--color-border);\n\t\tborder-radius: 14px;\n\t\tbackground: var(--color-surface);\n\t\tmargin-bottom: 0.5rem;\n\t\t/* the drawer is a flex column: without this the card shrinks to its border */\n\t\tflex-shrink: 0;\n\t}\n\t.becomerater {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tgap: 0.7rem;\n\t\tpadding: 0.7rem 0.85rem;\n\t\tcolor: var(--color-text);\n\t\ttext-decoration: none;\n\t}\n\t.becomerater + .becomerater {\n\t\tborder-top: 1px solid var(--color-border);\n\t}\n\t.rater + .earn {\n\t\tmargin-top: 0.5rem;\n\t}\n\t.earned {\n\t\tmargin-left: auto;\n\t\tflex-shrink: 0;\n\t\tborder-radius: 999px;\n\t\tbackground: color-mix(in srgb, var(--color-green) 16%, transparent);\n\t\tcolor: var(--color-green);\n\t\tfont-size: 0.72rem;\n\t\tfont-weight: 700;\n\t\tpadding: 0.15rem 0.5rem;\n\t\tfont-variant-numeric: tabular-nums;\n\t}\n\t.earned + .br-chev {\n\t\tmargin-left: 0.35rem;\n\t}\n\t.br-ico {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\twidth: 34px;\n\t\theight: 34px;\n\t\tflex-shrink: 0;\n\t\tborder: 1px solid var(--color-border);\n\t\tborder-radius: 10px;\n\t\tcolor: var(--color-muted);\n\t}\n\t.br-ico svg {\n\t\twidth: 17px;\n\t\theight: 17px;\n\t}\n\t.br-copy {\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t\tline-height: 1.25;\n\t\tmin-width: 0;\n\t}\n\t.br-copy b {\n\t\tfont-size: 0.88rem;\n\t\tfont-weight: 750;\n\t}\n\t.br-copy small {\n\t\tfont-size: 0.7rem;\n\t\tcolor: var(--color-muted);\n\t}\n\t.br-chev {\n\t\twidth: 15px;\n\t\theight: 15px;\n\t\tmargin-left: auto;\n\t\tflex-shrink: 0;\n\t\tcolor: var(--color-muted);\n\t}\n\t.becomerater:hover .br-chev {\n\t\tcolor: var(--color-text);\n\t}\n\n\t.tokens {\n\t\tcolor: inherit;\n\t\ttext-decoration: none;\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tgap: 0.7rem;\n\t\tborder: 1px solid var(--color-border);\n\t\tborder-radius: 14px;\n\t\tbackground: var(--color-surface);\n\t\tpadding: 0.7rem 0.85rem;\n\t\tmargin-bottom: 0.5rem;\n\t}\n\t/* Bare coin, no tinted tile. It earns attention with a sweep of light rather\n\t * than a colored aura (house rule: no neon glows). */\n\t.tok-ico {\n\t\tposition: relative;\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\tflex-shrink: 0;\n\t\tborder-radius: 50%;\n\t\toverflow: hidden;\n\t}\n\t.tok-ico::after {\n\t\tcontent: '';\n\t\tposition: absolute;\n\t\tinset: 0;\n\t\tbackground: linear-gradient(\n\t\t\t115deg,\n\t\t\ttransparent 38%,\n\t\t\trgba(255, 255, 255, 0.9) 50%,\n\t\t\ttransparent 62%\n\t\t);\n\t\ttransform: translateX(-160%);\n\t\tanimation: coin-sweep 3.2s ease-in-out infinite;\n\t}\n\t@keyframes coin-sweep {\n\t\t0%,\n\t\t68% {\n\t\t\ttransform: translateX(-160%);\n\t\t}\n\t\t100% {\n\t\t\ttransform: translateX(160%);\n\t\t}\n\t}\n\t@media (prefers-reduced-motion: reduce) {\n\t\t.tok-ico::after {\n\t\t\tanimation: none;\n\t\t\topacity: 0;\n\t\t}\n\t}\n\t.tok-body {\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t\tline-height: 1.15;\n\t}\n\t.tok-n {\n\t\tfont-family: var(--font-display);\n\t\tfont-size: 1.15rem;\n\t\tfont-weight: 700;\n\t\tletter-spacing: 0.01em;\n\t}\n\t.tok-l {\n\t\tfont-size: 0.68rem;\n\t\tcolor: var(--color-muted);\n\t}\n\t.tok-cta {\n\t\tmargin-left: auto;\n\t\tfont-size: 0.75rem;\n\t\tfont-weight: 600;\n\t\tcolor: var(--accent);\n\t\ttext-decoration: none;\n\t\twhite-space: nowrap;\n\t}\n\n\t/* ── rater console ────────────────────────────────────────────────────── */\n\t.rater {\n\t\tborder: 1px solid var(--color-border);\n\t\tborder-radius: 14px;\n\t\tbackground: var(--color-surface);\n\t\tpadding: 0 0.75rem;\n\t\tmargin-top: 0.5rem;\n\t\ttransition:\n\t\t\tborder-color 0.2s,\n\t\t\tbackground-color 0.2s;\n\t}\n\t/* Live is a state you should catch without reading — the card itself shifts,\n\t * a tint and a border, not a glow. */\n\t.rater.live {\n\t\tborder-color: color-mix(in srgb, var(--color-green) 32%, var(--color-border));\n\t\tbackground: color-mix(in srgb, var(--color-green) 6%, var(--color-surface));\n\t}\n\n\t.rater-head {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tgap: 0.75rem;\n\t\tpadding: 0.7rem 0;\n\t\tborder-bottom: 1px solid var(--color-border);\n\t}\n\t.rater-copy {\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t\tgap: 0.12rem;\n\t\tmin-width: 0;\n\t}\n\t.rater-title {\n\t\tfont-family: var(--font-display);\n\t\tfont-size: 0.92rem;\n\t\tfont-weight: 700;\n\t\tcolor: var(--color-text);\n\t}\n\t.rater-sub {\n\t\tfont-size: 0.74rem;\n\t\tcolor: var(--color-muted);\n\t}\n\n\t/* iOS proportions: 51 × 31 with a 27px knob, light knob on both tracks. */\n\t.switch {\n\t\tposition: relative;\n\t\twidth: 51px;\n\t\theight: 31px;\n\t\tmargin-left: auto;\n\t\tflex-shrink: 0;\n\t\tborder: 0;\n\t\tborder-radius: 999px;\n\t\t/* a visible track when off: surface-2 sat on the card's own surface */\n\t\tbackground: color-mix(in srgb, var(--color-text) 18%, transparent);\n\t\tcursor: pointer;\n\t\ttransition: background-color 0.18s;\n\t}\n\t.switch.on {\n\t\tbackground: var(--color-green);\n\t}\n\t.switch:disabled {\n\t\tcursor: default;\n\t\topacity: 0.6;\n\t}\n\t.switch:focus-visible {\n\t\toutline: 2px solid var(--color-text);\n\t\toutline-offset: 2px;\n\t}\n\t.knob {\n\t\tposition: absolute;\n\t\ttop: 2px;\n\t\tleft: 2px;\n\t\twidth: 27px;\n\t\theight: 27px;\n\t\tborder-radius: 50%;\n\t\tbackground: var(--color-text);\n\t\tbox-shadow: 0 2px 4px rgba(0, 0, 0, 0.35);\n\t\ttransition: transform 0.18s ease;\n\t}\n\t.switch.on .knob {\n\t\ttransform: translateX(20px);\n\t}\n\t@media (prefers-reduced-motion: reduce) {\n\t\t.switch,\n\t\t.knob {\n\t\t\ttransition: none;\n\t\t}\n\t}\n\n\t.ract {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tgap: 0.7rem;\n\t\tmin-height: 52px;\n\t\tpadding: 0.55rem 0;\n\t\tborder-bottom: 1px solid var(--color-border);\n\t\tcolor: var(--color-text);\n\t\ttext-decoration: none;\n\t}\n\t.ract:last-child {\n\t\tborder-bottom: 0;\n\t}\n\t.ract-ico {\n\t\twidth: 18px;\n\t\theight: 18px;\n\t\tflex-shrink: 0;\n\t\tcolor: var(--color-muted);\n\t}\n\t.ract-copy {\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t\tgap: 0.1rem;\n\t\tmin-width: 0;\n\t}\n\t.ract-title {\n\t\tfont-size: 0.88rem;\n\t\tfont-weight: 600;\n\t}\n\t.ract-sub {\n\t\tfont-size: 0.74rem;\n\t\tcolor: var(--color-muted);\n\t}\n\t/* 35 hosts on camera against 686 on the floor: same row, one step quieter. */\n\t.ract.quiet {\n\t\tmin-height: 46px;\n\t}\n\t.ract.quiet .ract-title {\n\t\tfont-size: 0.84rem;\n\t\tfont-weight: 500;\n\t\tcolor: var(--color-muted);\n\t}\n\t.ract-chev {\n\t\twidth: 15px;\n\t\theight: 15px;\n\t\tflex-shrink: 0;\n\t\tmargin-left: auto;\n\t\tcolor: color-mix(in srgb, var(--color-muted) 55%, transparent);\n\t}\n\n\t.guest {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tgap: 0.8rem;\n\t\tpadding: 0.1rem 0.1rem 0.9rem;\n\t}\n\t.guest-av {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\twidth: 46px;\n\t\theight: 46px;\n\t\tflex-shrink: 0;\n\t\tborder: 1px dashed color-mix(in srgb, var(--color-muted) 45%, transparent);\n\t\tborder-radius: 50%;\n\t\tcolor: var(--color-muted);\n\t}\n\t.guest-av svg {\n\t\twidth: 21px;\n\t\theight: 21px;\n\t}\n\t.guest-copy {\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t\tgap: 0.12rem;\n\t\tmin-width: 0;\n\t}\n\t.guest-title {\n\t\tfont-family: var(--font-display);\n\t\tfont-size: 1rem;\n\t\tfont-weight: 700;\n\t}\n\t.guest-sub {\n\t\tfont-size: 0.72rem;\n\t\tcolor: var(--color-muted);\n\t}\n\n\t.signin {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\tgap: 0.4rem;\n\t\tborder: 0;\n\t\tborder-radius: 14px;\n\t\tbackground: var(--accent);\n\t\tcolor: var(--accent-ink);\n\t\tpadding: 0.9rem;\n\t\tmargin-bottom: 0.5rem;\n\t\tfont-family: var(--font-display);\n\t\tfont-size: 0.95rem;\n\t\tfont-weight: 700;\n\t\tcursor: pointer;\n\t}\n\t.signin svg {\n\t\twidth: 16px;\n\t\theight: 16px;\n\t}\n\n\t/* ── groups + rows ────────────────────────────────────────────────────── */\n\t.grp {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tgap: 0.7rem;\n\t\tpadding: 1rem 0.1rem 0.5rem;\n\t}\n\t.grp span {\n\t\tfont-family: var(--font-display);\n\t\tfont-size: 0.6rem;\n\t\tfont-weight: 700;\n\t\tletter-spacing: 0.22em;\n\t\ttext-transform: uppercase;\n\t\tcolor: var(--color-muted);\n\t}\n\t.grp i {\n\t\tflex: 1;\n\t\theight: 1px;\n\t\tbackground: var(--color-border);\n\t}\n\t.row {\n\t\tposition: relative;\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tgap: 0.7rem;\n\t\tmin-height: 50px;\n\t\tmargin-bottom: 0.3rem;\n\t\tpadding: 0 0.65rem 0 0.55rem;\n\t\tborder-radius: 12px;\n\t\ttext-decoration: none;\n\t\tcolor: var(--color-text);\n\t\ttransition: background-color 0.15s;\n\t}\n\t.row:hover {\n\t\tbackground: var(--color-surface);\n\t}\n\t.row.on {\n\t\tbackground: color-mix(in srgb, var(--accent) 9%, transparent);\n\t}\n\t/* left accent bar on the row you're currently on */\n\t.row.on::before {\n\t\tcontent: '';\n\t\tposition: absolute;\n\t\tleft: -0.55rem;\n\t\ttop: 50%;\n\t\twidth: 3px;\n\t\theight: 26px;\n\t\ttransform: translateY(-50%);\n\t\tborder-radius: 0 3px 3px 0;\n\t\tbackground: var(--accent);\n\t}\n\n\t.row-ico {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\twidth: 34px;\n\t\theight: 34px;\n\t\tflex-shrink: 0;\n\t\tborder-radius: 10px;\n\t\tbackground: var(--color-surface-2);\n\t\tcolor: var(--color-muted);\n\t}\n\t.row-ico svg {\n\t\twidth: 16px;\n\t\theight: 16px;\n\t}\n\t.row.on .row-ico {\n\t\tbackground: color-mix(in srgb, var(--accent) 18%, transparent);\n\t\tcolor: var(--accent);\n\t}\n\n\t.row-label {\n\t\tfont-size: 0.94rem;\n\t\tfont-weight: 500;\n\t\tcolor: color-mix(in srgb, var(--color-text) 82%, transparent);\n\t}\n\t.row.on .row-label {\n\t\tfont-weight: 600;\n\t\tcolor: var(--color-text);\n\t}\n\n\t/* A chip only exists when that surface has something happening, so it always\n\t * gets the loud treatment: its own module's accent plus a slow sweep of\n\t * light. Tint + text, no colored glow (house rule). */\n\t.chip {\n\t\tposition: relative;\n\t\tmargin-left: auto;\n\t\toverflow: hidden;\n\t\tborder-radius: 6px;\n\t\tbackground: color-mix(in srgb, var(--c) 16%, transparent);\n\t\tpadding: 0.16rem 0.42rem;\n\t\tfont-size: 0.62rem;\n\t\tfont-weight: 700;\n\t\tcolor: var(--c);\n\t\twhite-space: nowrap;\n\t}\n\t.chip::after {\n\t\tcontent: '';\n\t\tposition: absolute;\n\t\tinset: 0;\n\t\tbackground: linear-gradient(\n\t\t\t115deg,\n\t\t\ttransparent 38%,\n\t\t\tcolor-mix(in srgb, var(--c) 55%, transparent) 50%,\n\t\t\ttransparent 62%\n\t\t);\n\t\ttransform: translateX(-160%);\n\t\tanimation: chip-sweep 3.2s ease-in-out infinite;\n\t}\n\t@keyframes chip-sweep {\n\t\t0%,\n\t\t68% {\n\t\t\ttransform: translateX(-160%);\n\t\t}\n\t\t100% {\n\t\t\ttransform: translateX(160%);\n\t\t}\n\t}\n\t@media (prefers-reduced-motion: reduce) {\n\t\t.chip::after {\n\t\t\tanimation: none;\n\t\t\topacity: 0;\n\t\t}\n\t}\n\n\t.row-chev {\n\t\twidth: 15px;\n\t\theight: 15px;\n\t\tflex-shrink: 0;\n\t\tmargin-left: auto;\n\t\tcolor: color-mix(in srgb, var(--color-muted) 55%, transparent);\n\t}\n\t.chip + .row-chev {\n\t\tmargin-left: 0.5rem;\n\t}\n\n\t/* ── footer ───────────────────────────────────────────────────────────── */\n\t.install {\n\t\tmargin-top: auto;\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tgap: 0.65rem;\n\t\tpadding: 0.7rem 0.85rem;\n\t\tborder: 1px solid var(--color-border);\n\t\tborder-radius: 12px;\n\t\tbackground: var(--color-surface-2);\n\t\tcolor: var(--color-text);\n\t\tfont-size: 0.85rem;\n\t\tfont-weight: 600;\n\t\ttext-decoration: none;\n\t}\n\n\t/* /partners bounty row — same chrome as .install, the pill carries the pitch. */\n\t.dealrow {\n\t\tmargin-top: auto;\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tgap: 0.65rem;\n\t\tpadding: 0.7rem 0.85rem;\n\t\tborder: 1px solid var(--color-border);\n\t\tborder-radius: 12px;\n\t\tbackground: var(--color-surface-2);\n\t\tcolor: var(--color-text);\n\t\tfont-size: 0.85rem;\n\t\tfont-weight: 600;\n\t\ttext-decoration: none;\n\t}\n\t.dealrow svg {\n\t\twidth: 16px;\n\t\theight: 16px;\n\t}\n\t.dealrow + .dealrow,\n\t.dealrow + .install {\n\t\tmargin-top: 0.5rem;\n\t}\n\t.deal-badge {\n\t\tmargin-left: auto;\n\t\tpadding: 2px 8px;\n\t\tborder-radius: 999px;\n\t\tbackground: var(--color-coral);\n\t\tcolor: #fff;\n\t\tfont-family: var(--font-display, inherit);\n\t\tfont-size: 11px;\n\t\tfont-weight: 800;\n\t\tletter-spacing: 0.02em;\n\t}\n\t.install svg {\n\t\twidth: 16px;\n\t\theight: 16px;\n\t\tcolor: var(--color-muted);\n\t}\n\t.install:hover svg {\n\t\tcolor: var(--color-text);\n\t}\n\t.foot {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tgap: 1.1rem;\n\t\tpadding: 1.1rem 0.1rem 0;\n\t}\n\t.foot-link {\n\t\tborder: 0;\n\t\tbackground: none;\n\t\tpadding: 0;\n\t\tfont: inherit;\n\t\tfont-size: 0.78rem;\n\t\tcolor: var(--color-muted);\n\t\ttext-decoration: none;\n\t\tcursor: pointer;\n\t}\n\t.foot-link:hover {\n\t\tcolor: var(--color-text);\n\t}\n\t.foot-out {\n\t\tmargin-left: auto;\n\t}\n\n\t/* ── desktop: same panel, anchored under the hamburger ─────────────────── */\n\t@media (min-width: 768px) {\n\t\t.backdrop {\n\t\t\tbackground: transparent;\n\t\t}\n\t\t.panel {\n\t\t\ttop: calc(var(--ann-h, 0px) + 4rem + 0.5rem);\n\t\t\tbottom: auto;\n\t\t\tleft: auto;\n\t\t\tright: max(1rem, calc(50vw - 32rem));\n\t\t\twidth: 340px;\n\t\t\tmax-height: calc(100dvh - var(--ann-h, 0px) - 5.5rem);\n\t\t\tborder: 1px solid var(--color-border);\n\t\t\tborder-radius: 18px;\n\t\t\tbackground: var(--color-surface);\n\t\t\tbox-shadow: 0 24px 60px -18px rgba(0, 0, 0, 0.75);\n\t\t\tpadding: 0.5rem 0.85rem 0.9rem;\n\t\t}\n\t\t.close-row {\n\t\t\tdisplay: none;\n\t\t}\n\t\t.ident {\n\t\t\tpadding-top: 0.9rem;\n\t\t}\n\t\t.tokens {\n\t\t\tbackground: var(--color-bg);\n\t\t}\n\t\t.row-ico {\n\t\t\tbackground: var(--color-bg);\n\t\t}\n\t}\n</style>\n","<script lang=\"ts\">\n\t/**\n\t * RAT-332: desktop-only floating chat FAB.\n\t *\n\t * Real user feedback (@rubber on the feed): \"how do I get to my messages\n\t * except the notification symbol?\" — on desktop the chat inbox (`/c`) had\n\t * no discoverable entry point. NotificationBell is an activity feed, not\n\t * an inbox; typing `/c` in the URL is power-user only.\n\t *\n\t * Persistent fixed-position button anchored bottom-right. Mobile already\n\t * has BottomNav's messages slot, so this is `hidden md:flex`. Auth-gated:\n\t * unauth viewers have nothing to message about, so the FAB is just clutter\n\t * for them — matches NotificationBell's `userId` gate.\n\t *\n\t * Unread badge subscribes to the shared `unreadChat` store\n\t * (`$lib/stores/unreadNotifications`) — same source the bell and BottomNav\n\t * messages dot read. NotificationBell owns the single 5s poll and writes\n\t * the store; we don't open a second polling path.\n\t *\n\t * Visibility mirrors Navbar.svelte's `navHidden` (focus-mode routes own\n\t * their bottom chrome). The duel lobby's `body.duel-focus-mode` class\n\t * also kills the FAB via :global so DuelDock owns the bottom-right area\n\t * without us stacking on top of it.\n\t *\n\t * Per CLAUDE.md design rules: no neon glow, no colored outer shadow ring\n\t * — solid dark surface + neutral drop for depth, cyan accent (RAT-327\n\t * chat module color) for hover.\n\t */\n\n\timport { page } from '$app/state';\n\timport { isStudioPage } from '$lib/studio-host';\n\timport { t } from '$lib/i18n';\n\timport { unreadChat } from '$lib/stores/unreadNotifications';\n\n\tconst pathname = $derived(page.url.pathname);\n\tconst isAuthed = $derived(!!page.data.user);\n\n\t// Hide on focus-mode routes — same set as Navbar.navHidden + /c itself\n\t// (we're already inside the chat module, FAB pointing back at it is\n\t// pointless). /g/ rooms own their own focus chrome. /auth/ and\n\t// /rate/session/ are full-page focus flows. The duel lobby toggles\n\t// body.duel-focus-mode globally; that case is handled in CSS below so\n\t// the FAB also disappears mid-route as duel state flips.\n\tconst hidden = $derived(\n\t\tpathname === '/c' ||\n\t\t\tpathname.startsWith('/c/') ||\n\t\t\tpathname.startsWith('/g/') ||\n\t\t\tpathname.startsWith('/auth/') ||\n\t\t\tpathname.startsWith('/rate/session/') ||\n\t\t\t// livecam room owns its bottom edge (chat composer) — the FAB would\n\t\t\t// overlap the send button.\n\t\t\tpathname.startsWith('/livecam') ||\n\t\t\t// RAT-441: the floor room does too.\n\t\t\tpathname.startsWith('/floor') ||\n\t\t\t// The studio's composer owns its bottom edge, and the FAB sat on the\n\t\t\t// send arrow. On its own domain the studio is also `/`.\n\t\t\tpathname.startsWith('/studio') ||\n\t\t\tpage.route.id === '/studio' ||\n\t\t\t// RAT-632: PornPilot's own pages (/prompts), same as the studio.\n\t\t\tisStudioPage(page.route.id)\n\t);\n\n\t// Cap the badge label at 9+ to keep the FAB compact. Mirrors the bell\n\t// menu's truncation behavior (NotificationBell shows raw counts inside\n\t// its dropdown, but the always-visible affordance stays small).\n\tconst chatCount = $derived($unreadChat);\n\tconst badgeLabel = $derived(chatCount > 9 ? '9+' : String(chatCount));\n</script>\n\n{#if isAuthed && !hidden}\n\t<a\n\t\thref=\"/c\"\n\t\tclass=\"chat-fab fixed right-6 bottom-6 z-40 hidden h-14 w-14 items-center justify-center rounded-full border border-border bg-surface text-text shadow-lg transition-colors md:flex\"\n\t\taria-label={chatCount > 0 ? t('messages ({n} unread)', { n: chatCount }) : t('messages')}\n\t\tdata-sveltekit-preload-data=\"hover\"\n\t>\n\t\t<svg\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstroke-width=\"1.8\"\n\t\t\tclass=\"h-6 w-6\"\n\t\t\taria-hidden=\"true\"\n\t\t>\n\t\t\t<path\n\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\td=\"M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z\"\n\t\t\t/>\n\t\t</svg>\n\t\t{#if chatCount > 0}\n\t\t\t<span class=\"chat-fab-badge\" aria-hidden=\"true\">{badgeLabel}</span>\n\t\t{/if}\n\t</a>\n{/if}\n\n<style>\n\t/* CLAUDE.md design rule: no neon glow, no colored shadow ring. This is a\n\t   neutral dark drop for depth — same shadow language as BottomNav's\n\t   slot-create button. The cyan only appears on hover as a fill, not as\n\t   a glow. */\n\t.chat-fab {\n\t\tbox-shadow:\n\t\t\t0 10px 24px -8px rgba(0, 0, 0, 0.6),\n\t\t\t0 2px 6px rgba(0, 0, 0, 0.4);\n\t}\n\t/* Subtle neutral hover — earlier draft used a cyan fill (chat module\n\t   accent) but it read as a brand flash, not a hover state. Step up one\n\t   surface tier + brighten the border so the affordance is visible\n\t   without screaming. Same surface tokens as the BottomNav + picker. */\n\t.chat-fab:hover {\n\t\tbackground: var(--color-surface-2, #1d1d1d);\n\t\tborder-color: var(--color-muted);\n\t}\n\t.chat-fab:active {\n\t\ttransform: scale(0.96);\n\t}\n\n\t/* Coral pill in the top-right of the FAB. Donut border in bg color so\n\t   it reads cleanly against the dark surface, same trick BottomNav uses\n\t   for the messages-slot dot — kept consistent so the two affordances\n\t   feel like one system. */\n\t.chat-fab-badge {\n\t\tposition: absolute;\n\t\ttop: -4px;\n\t\tright: -4px;\n\t\tmin-width: 20px;\n\t\theight: 20px;\n\t\tpadding: 0 6px;\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\tborder-radius: 9999px;\n\t\tbackground: var(--color-coral);\n\t\tcolor: #fff;\n\t\tborder: 2px solid var(--color-bg);\n\t\tfont-family: var(--font-display);\n\t\tfont-size: 10px;\n\t\tfont-weight: 700;\n\t\tline-height: 1;\n\t\tletter-spacing: 0.02em;\n\t}\n\n\t/* Duel lobby + fomo views set body.duel-focus-mode so DuelDock owns the\n\t   bottom chrome. BottomNav hides via the same hook (see\n\t   BottomNav.svelte:446). Mirror it so the FAB doesn't collide with the\n\t   dock visually. Once the duel is revealed, the class clears and the\n\t   FAB returns on its own. */\n\t:global(body.duel-focus-mode) .chat-fab {\n\t\tdisplay: none;\n\t}\n</style>\n","<script lang=\"ts\">\n\timport { navigating } from '$app/state';\n\n\tlet progress = $state(0);\n\tlet visible = $state(false);\n\tlet timer: ReturnType<typeof setInterval> | null = null;\n\n\t$effect(() => {\n\t\tif (navigating.to) {\n\t\t\t// Start\n\t\t\tvisible = true;\n\t\t\tprogress = 15;\n\t\t\ttimer = setInterval(() => {\n\t\t\t\tprogress = Math.min(progress + (90 - progress) * 0.1, 90);\n\t\t\t}, 100);\n\t\t} else if (visible) {\n\t\t\t// Complete\n\t\t\tif (timer) clearInterval(timer);\n\t\t\ttimer = null;\n\t\t\tprogress = 100;\n\t\t\tsetTimeout(() => {\n\t\t\t\tvisible = false;\n\t\t\t\tprogress = 0;\n\t\t\t}, 300);\n\t\t}\n\n\t\treturn () => {\n\t\t\tif (timer) clearInterval(timer);\n\t\t};\n\t});\n</script>\n\n{#if visible}\n\t<div class=\"fixed inset-x-0 top-0 z-[100] h-[2px]\">\n\t\t<div\n\t\t\tclass=\"h-full bg-coral shadow-[0_0_10px_rgba(255,77,109,0.5)] transition-[width] duration-200 ease-out\"\n\t\t\tstyle=\"width: {progress}%\"\n\t\t></div>\n\t</div>\n{/if}\n","<script lang=\"ts\">\n\timport { onMount } from 'svelte';\n\timport { goto } from '$app/navigation';\n\timport { page } from '$app/state';\n\timport { isStandalone } from '$lib/stores/pwa';\n\n\tlet { loggedIn = false }: { loggedIn?: boolean } = $props();\n\n\t// One-time, load-time check: when the installed PWA opens on its start route\n\t// with a logged-in user who hasn't decided on notifications yet, send them to\n\t// the enable screen. Not reactive on purpose — this is an \"app opened\" moment,\n\t// not a computed value.\n\tonMount(() => {\n\t\tif (!loggedIn) return;\n\t\t// Only from the PWA start route, so we never hijack a deep link.\n\t\tif (page.url.pathname !== '/') return;\n\t\tif (!isStandalone()) return;\n\t\tif (!('Notification' in window) || Notification.permission !== 'default') return;\n\t\tif (localStorage.getItem('push-prompt-seen')) return;\n\t\t// One-time: never auto-redirect again, even if they skip.\n\t\tlocalStorage.setItem('push-prompt-seen', '1');\n\t\tvoid goto('/enable-notifications');\n\t});\n</script>\n","<script lang=\"ts\">\n\t/*\n\t * Defers loading of $lib/analytics (and its transitive posthog-js dep, ~187KB\n\t * minified) until first user interaction OR a 4s fallback timeout. Without\n\t * this, the layout chunk pulls posthog-js into the initial bundle graph and\n\t * adds ~1-2 sec of cold-load latency on mobile 4G.\n\t *\n\t * The component renders nothing visible — the <svelte:head> below is just to\n\t * give Svelte 5 a non-empty markup section so the compiler doesn't choke.\n\t */\n\timport { onMount } from 'svelte';\n\timport { afterNavigate, replaceState } from '$app/navigation';\n\timport { page } from '$app/state';\n\timport { receiveHandoff } from '$lib/pixypilot-handoff';\n\n\tinterface Props {\n\t\tuserId: string | null;\n\t}\n\tlet { userId }: Props = $props();\n\n\ttype AnalyticsModule = typeof import('$lib/analytics');\n\tlet analytics: AnalyticsModule | null = $state(null);\n\n\tonMount(() => {\n\t\t// RAT-550: PostHog ids carried over from pixypilot.app. Read now, not when\n\t\t// the SDK loads: they come out of the address before the visitor can copy\n\t\t// it. initPostHog picks them up from the module.\n\t\tconst clean = receiveHandoff(new URL(location.href));\n\t\tif (clean) {\n\t\t\ttry {\n\t\t\t\t// This page's own address with its fragment emptied: nothing to resolve.\n\t\t\t\t// eslint-disable-next-line svelte/no-navigation-without-resolve\n\t\t\t\treplaceState(clean, page.state);\n\t\t\t} catch {\n\t\t\t\t// router not ready: the ids still have to go\n\t\t\t\thistory.replaceState(history.state, '', clean);\n\t\t\t}\n\t\t}\n\n\t\tlet started = false;\n\n\t\tconst start = async () => {\n\t\t\tif (started) return;\n\t\t\tstarted = true;\n\t\t\tconst a = await import('$lib/analytics');\n\t\t\tanalytics = a;\n\t\t\t// Flags the current page evaluated server-side (the homepage's A/B\n\t\t\t// test) — see initPostHog.\n\t\t\ta.initPostHog(page.data.phFlags ?? {});\n\t\t\ta.trackPageView(page.url.pathname);\n\t\t\tif (userId) {\n\t\t\t\ta.setUserId(userId);\n\t\t\t\ta.setUserProperties({ user_type: 'authenticated', has_session: true });\n\t\t\t}\n\t\t};\n\n\t\tconst events = ['pointerdown', 'keydown', 'scroll', 'touchstart'] as const;\n\t\tfor (const e of events) window.addEventListener(e, start, { once: true, passive: true });\n\t\tconst fallbackTimer = setTimeout(start, 4000);\n\n\t\treturn () => {\n\t\t\tfor (const e of events) window.removeEventListener(e, start);\n\t\t\tclearTimeout(fallbackTimer);\n\t\t};\n\t});\n\n\tafterNavigate(({ to }) => {\n\t\tif (to?.url && analytics) analytics.trackPageView(to.url.pathname);\n\t});\n\n\t$effect(() => {\n\t\tif (userId && analytics) {\n\t\t\tanalytics.setUserId(userId);\n\t\t\tanalytics.setUserProperties({ user_type: 'authenticated', has_session: true });\n\t\t}\n\t});\n</script>\n\n<svelte:head></svelte:head>\n","<script lang=\"ts\">\n\timport { onMount } from 'svelte';\n\n\t/** Headless component. Bumps the rater's last_seen_at every 30s while\n\t *  the tab is visible (any page in the app). On tab close /\n\t *  visibilitychange→hidden, fires a sendBeacon to /api/rater/offline so\n\t *  the sweeper backdates them past the grace window — faster than\n\t *  waiting for the heartbeat to stale out naturally.\n\t *\n\t *  Only mount this for raters. Layout passes data.isRater through. */\n\n\tconst HEARTBEAT_MS = 30_000;\n\tlet mounted = false;\n\n\tonMount(() => {\n\t\tmounted = true;\n\t\tlet timer: ReturnType<typeof setInterval> | null = null;\n\n\t\tconst ping = () => {\n\t\t\tif (typeof document === 'undefined') return;\n\t\t\tif (document.visibilityState !== 'visible') return;\n\t\t\tfetch('/api/rater/heartbeat', { method: 'POST', keepalive: true }).catch(() => {});\n\t\t};\n\n\t\tconst sendOffline = () => {\n\t\t\t// Beacon survives tab close. Cheap fire-and-forget.\n\t\t\tif (typeof navigator !== 'undefined' && typeof navigator.sendBeacon === 'function') {\n\t\t\t\tnavigator.sendBeacon('/api/rater/offline');\n\t\t\t}\n\t\t};\n\n\t\tconst onVisibility = () => {\n\t\t\tif (document.visibilityState === 'visible') {\n\t\t\t\tping();\n\t\t\t} else {\n\t\t\t\tsendOffline();\n\t\t\t}\n\t\t};\n\n\t\t// Initial heartbeat + interval.\n\t\tping();\n\t\ttimer = setInterval(ping, HEARTBEAT_MS);\n\n\t\tdocument.addEventListener('visibilitychange', onVisibility);\n\t\twindow.addEventListener('pagehide', sendOffline);\n\n\t\treturn () => {\n\t\t\tif (timer !== null) clearInterval(timer);\n\t\t\tdocument.removeEventListener('visibilitychange', onVisibility);\n\t\t\twindow.removeEventListener('pagehide', sendOffline);\n\t\t};\n\t});\n</script>\n\n{#if mounted}<!-- presence pinger active -->{/if}\n","<script lang=\"ts\">\n\t/** RAT-360 (F4): minimal toast that pops on the latest realtime event the\n\t *  global subscriber considered toast-worthy (chat / group / notification\n\t *  / rate_card_flip). 5s auto-dismiss, tap-to-open when an href exists.\n\t *\n\t *  Privacy: never renders the message body. Title is \"új üzenet — Jenny\"\n\t *  or \"új követőd: alice\" — enough to know it landed, not enough to leak\n\t *  on a lock screen / screenshare.\n\t *\n\t *  Look: solid dark surface + thin border. No neon glow (CLAUDE.md rule).\n\t *\n\t *  Polish (animations, sound, haptic, queueing multiple) lives in the\n\t *  later polish epic. This is the bare-minimum visible signal. */\n\n\timport { goto } from '$app/navigation';\n\timport { latestToast, applyMuteFromToast, type ToastEvent } from '$lib/stores/global-realtime';\n\timport { getAvatarUrl } from '$lib/avatar';\n\n\tconst VISIBLE_MS = 5_000;\n\n\tlet current = $state<ToastEvent | null>(null);\n\t// Dedup against the last event we *presented*, not against `current` —\n\t// when the timer (or user dismiss) clears `current`, we don't want the\n\t// effect to re-run and re-present the same toast. lastShownId is\n\t// monotonic: it only ever moves forward to a new event's id.\n\tlet lastShownId: string | null = null;\n\tlet dismissTimer: ReturnType<typeof setTimeout> | null = null;\n\n\t$effect(() => {\n\t\tconst next = $latestToast;\n\t\tif (!next || next.id === lastShownId) return;\n\t\tlastShownId = next.id;\n\t\tcurrent = next;\n\t\tif (dismissTimer) clearTimeout(dismissTimer);\n\t\tdismissTimer = setTimeout(() => {\n\t\t\t// Only clear if the event we showed is still the one on screen —\n\t\t\t// a newer event could have replaced it during the timeout window.\n\t\t\tif (current?.id === next.id) current = null;\n\t\t\tdismissTimer = null;\n\t\t}, VISIBLE_MS);\n\t});\n\n\tfunction dismiss() {\n\t\tcurrent = null;\n\t\tif (dismissTimer) clearTimeout(dismissTimer);\n\t\tdismissTimer = null;\n\t}\n\n\tfunction tap() {\n\t\tif (!current?.href) {\n\t\t\tdismiss();\n\t\t\treturn;\n\t\t}\n\t\tconst href = current.href;\n\t\tdismiss();\n\t\tgoto(href);\n\t}\n\n\t/** RAT-369: quick-mute button. Dismiss + apply the mute target.\n\t *  stopPropagation so it doesn't also fire the toast's tap-to-open. */\n\tfunction quickMute(event: MouseEvent) {\n\t\tevent.stopPropagation();\n\t\tconst target = current?.muteTarget;\n\t\tif (!target) {\n\t\t\tdismiss();\n\t\t\treturn;\n\t\t}\n\t\tdismiss();\n\t\tvoid applyMuteFromToast(target);\n\t}\n</script>\n\n{#if current}\n\t{@const avatar = current.avatarPath ? getAvatarUrl(current.avatarPath) : null}\n\t<div class=\"toast\" role=\"alert\">\n\t\t<button class=\"toast-tap\" onclick={tap} type=\"button\" aria-label={current.title}>\n\t\t\t<div class=\"avatar\" aria-hidden=\"true\">\n\t\t\t\t{#if avatar}\n\t\t\t\t\t<img src={avatar} alt=\"\" />\n\t\t\t\t{:else if current.fallbackInitial}\n\t\t\t\t\t<span class=\"avatar-initial\">{current.fallbackInitial}</span>\n\t\t\t\t{:else}\n\t\t\t\t\t<span class=\"avatar-fallback\"></span>\n\t\t\t\t{/if}\n\t\t\t</div>\n\t\t\t<div class=\"body\">\n\t\t\t\t<span class=\"title\">{current.title}</span>\n\t\t\t</div>\n\t\t</button>\n\t\t{#if current.muteTarget}\n\t\t\t<!-- RAT-369: one-tap mute. Same target the chat / group settings\n\t\t\t\t panels write to (mutedThreadIds / mutedRoomIds / category). -->\n\t\t\t<button class=\"mute-btn\" onclick={quickMute} type=\"button\" aria-label=\"mute this\">\n\t\t\t\t<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n\t\t\t\t\t<path d=\"M13.73 21a2 2 0 0 1-3.46 0\" />\n\t\t\t\t\t<path d=\"M18.63 13A17.89 17.89 0 0 1 18 8\" />\n\t\t\t\t\t<path d=\"M6.26 6.26A5.86 5.86 0 0 0 6 8c0 7-3 9-3 9h14\" />\n\t\t\t\t\t<path d=\"M18 8a6 6 0 0 0-9.33-5\" />\n\t\t\t\t\t<line x1=\"1\" y1=\"1\" x2=\"23\" y2=\"23\" />\n\t\t\t\t</svg>\n\t\t\t</button>\n\t\t{/if}\n\t</div>\n{/if}\n\n<style>\n\t.toast {\n\t\tposition: fixed;\n\t\tleft: 50%;\n\t\ttransform: translateX(-50%);\n\t\t/* RAT-369: sit just above the bottom nav with a small gap. The old\n\t\t * +76px floated way above the nav; +56px lands ~8px above it on\n\t\t * iPhones with notched safe-area-inset-bottom, ~12px on older. */\n\t\tbottom: calc(env(safe-area-inset-bottom, 0px) + 56px);\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tmax-width: min(360px, calc(100vw - 32px));\n\t\tpadding: 4px 6px 4px 4px;\n\t\tbackground: var(--color-surface);\n\t\tborder: 1px solid var(--color-border);\n\t\tborder-radius: 14px;\n\t\tbox-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);\n\t\tcolor: inherit;\n\t\tfont: inherit;\n\t\ttext-align: left;\n\t\tz-index: 1000;\n\t\tanimation: toast-in 200ms ease-out;\n\t}\n\t.toast-tap {\n\t\tflex: 1;\n\t\tmin-width: 0;\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tgap: 12px;\n\t\tpadding: 6px 10px 6px 8px;\n\t\tbackground: transparent;\n\t\tborder: 0;\n\t\tcolor: inherit;\n\t\tfont: inherit;\n\t\ttext-align: left;\n\t\tcursor: pointer;\n\t\tborder-radius: 10px;\n\t\ttransition: background 120ms ease;\n\t}\n\t.toast-tap:hover {\n\t\tbackground: var(--color-surface-hover, rgba(255, 255, 255, 0.04));\n\t}\n\t.mute-btn {\n\t\tflex-shrink: 0;\n\t\twidth: 32px;\n\t\theight: 32px;\n\t\tdisplay: inline-flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\tbackground: transparent;\n\t\tborder: 0;\n\t\tcolor: var(--color-muted, #9ca3af);\n\t\tcursor: pointer;\n\t\tborder-radius: 50%;\n\t\ttransition: background 120ms ease, color 120ms ease;\n\t}\n\t.mute-btn:hover {\n\t\tcolor: var(--color-text, currentColor);\n\t\tbackground: var(--color-surface-hover, rgba(255, 255, 255, 0.06));\n\t}\n\t.mute-btn svg { width: 14px; height: 14px; }\n\n\t.avatar {\n\t\tflex: 0 0 32px;\n\t\twidth: 32px;\n\t\theight: 32px;\n\t\tborder-radius: 50%;\n\t\toverflow: hidden;\n\t\tbackground: var(--color-border);\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t}\n\t.avatar img {\n\t\twidth: 100%;\n\t\theight: 100%;\n\t\tobject-fit: cover;\n\t}\n\t.avatar-fallback {\n\t\twidth: 100%;\n\t\theight: 100%;\n\t}\n\t.avatar-initial {\n\t\tfont-family: var(--font-display, inherit);\n\t\tfont-size: 14px;\n\t\tfont-weight: 700;\n\t\tcolor: var(--color-text, currentColor);\n\t\tline-height: 1;\n\t\ttext-transform: uppercase;\n\t}\n\n\t.body {\n\t\tmin-width: 0;\n\t\tflex: 1;\n\t}\n\t.title {\n\t\tdisplay: block;\n\t\tfont-size: 14px;\n\t\tfont-weight: 500;\n\t\toverflow: hidden;\n\t\ttext-overflow: ellipsis;\n\t\twhite-space: nowrap;\n\t}\n\n\t@keyframes toast-in {\n\t\tfrom {\n\t\t\topacity: 0;\n\t\t\ttransform: translate(-50%, 8px);\n\t\t}\n\t\tto {\n\t\t\topacity: 1;\n\t\t\ttransform: translate(-50%, 0);\n\t\t}\n\t}\n\n\t@media (min-width: 768px) {\n\t\t.toast {\n\t\t\tbottom: auto;\n\t\t\t/* Desktop: under the navbar, top-pinned. */\n\t\t\ttop: calc(env(safe-area-inset-top, 0px) + 76px);\n\t\t}\n\t}\n</style>\n","<script lang=\"ts\">\n\t/** RAT-361 (F5): subtle visual feedback on the RealtimeClient connection.\n\t *\n\t *  Three states (driven by `$lib/realtime/client.ts`):\n\t *    - `connected`: nothing shown, the default healthy state\n\t *    - `reconnecting`: dim pill \"reconnecting…\" — shown only after a 1s\n\t *      delay so a clean reconnect (typical: 200-500ms) never paints.\n\t *      Frequent flaps on a flaky network shouldn't strobe the UI.\n\t *    - `offline`: red-ish pill. Debounced like `reconnecting` (1s) so a\n\t *      spurious `offline` event during a mobile network handoff doesn't paint,\n\t *      then sticky until the client reports a channel healthy again.\n\t *\n\t *  Every degraded state is also suppressed for the first few seconds after\n\t *  load, so a cold join's CONNECTING transient doesn't look like an outage.\n\t *\n\t *  Design rule (CLAUDE.md): no neon glow, no saturated colored ring.\n\t *  Solid dark surface, thin neutral border, subtle drop shadow.\n\t *  Position: mobile bottom-center (above the BottomNav + Toast safe area),\n\t *  desktop top-right under the Navbar. */\n\n\timport { connectionState } from '$lib/realtime/client';\n\n\tconst RECONNECTING_DELAY_MS = 1_000;\n\t/** Give the offline pill the same short debounce as the reconnecting one: a\n\t *  spurious `offline` event (mobile Safari fires these during a network handoff,\n\t *  while the socket is still delivering) shouldn't strobe the red pill for a\n\t *  blip the connection rides straight through. */\n\tconst OFFLINE_DELAY_MS = 1_000;\n\t/** Suppress the pill for the first few seconds after load: a cold join cycles\n\t *  through CONNECTING before its first SUBSCRIBED, and that transient shouldn't\n\t *  read as an outage before the user has done anything. */\n\tconst STARTUP_SUPPRESS_MS = 5_000;\n\n\t// Mounts once in the root layout when signed in, so this ≈ app load time.\n\tconst startedAt = Date.now();\n\n\tlet visibleState = $state<'reconnecting' | 'offline' | null>(null);\n\tlet delayTimer: ReturnType<typeof setTimeout> | null = null;\n\n\t$effect(() => {\n\t\tconst next = $connectionState;\n\t\tif (delayTimer) {\n\t\t\tclearTimeout(delayTimer);\n\t\t\tdelayTimer = null;\n\t\t}\n\t\tif (next === 'connected') {\n\t\t\tvisibleState = null;\n\t\t\treturn;\n\t\t}\n\t\t// Debounce every degraded state, and hold it at least until the startup\n\t\t// window has elapsed. If a fast recovery lands inside the delay the effect\n\t\t// re-runs on 'connected' and clears the timer before it ever paints.\n\t\tconst pending = next;\n\t\tconst base = pending === 'offline' ? OFFLINE_DELAY_MS : RECONNECTING_DELAY_MS;\n\t\tconst delay = Math.max(base, STARTUP_SUPPRESS_MS - (Date.now() - startedAt));\n\t\tdelayTimer = setTimeout(() => {\n\t\t\tvisibleState = pending;\n\t\t\tdelayTimer = null;\n\t\t}, delay);\n\t});\n</script>\n\n{#if visibleState === 'reconnecting'}\n\t<div class=\"indicator reconnecting\" role=\"status\" aria-live=\"polite\">\n\t\t<span class=\"dot\" aria-hidden=\"true\"></span>\n\t\t<span class=\"label\">reconnecting…</span>\n\t</div>\n{:else if visibleState === 'offline'}\n\t<div class=\"indicator offline\" role=\"status\" aria-live=\"polite\">\n\t\t<span class=\"dot offline-dot\" aria-hidden=\"true\"></span>\n\t\t<span class=\"label\">you're offline — messages will sync when you're back</span>\n\t</div>\n{/if}\n\n<style>\n\t.indicator {\n\t\tposition: fixed;\n\t\tleft: 50%;\n\t\ttransform: translateX(-50%);\n\t\tbottom: calc(env(safe-area-inset-bottom, 0px) + 132px);\n\t\tdisplay: inline-flex;\n\t\talign-items: center;\n\t\tgap: 8px;\n\t\tpadding: 6px 12px;\n\t\tbackground: var(--color-surface);\n\t\tborder: 1px solid var(--color-border);\n\t\tborder-radius: 999px;\n\t\tbox-shadow: 0 6px 16px rgba(0, 0, 0, 0.35);\n\t\tfont-size: 12px;\n\t\tcolor: var(--color-muted, #9ca3af);\n\t\t/* A status, not a control: taps go through it. At z-999 it sat on top of\n\t\t   the live-now sheet (z-210) and swallowed the \"join\" tap under it. 59\n\t\t   keeps it above the bottom nav (50) and the duel/war docks (55), and under\n\t\t   every menu, sheet and modal (60 and up). */\n\t\tpointer-events: none;\n\t\tz-index: 59;\n\t\tanimation: indicator-in 200ms ease-out;\n\t}\n\n\t@media (min-width: 768px) {\n\t\t.indicator {\n\t\t\tleft: auto;\n\t\t\tright: 16px;\n\t\t\tbottom: auto;\n\t\t\ttop: calc(env(safe-area-inset-top, 0px) + 76px);\n\t\t\ttransform: none;\n\t\t}\n\t}\n\n\t.dot {\n\t\twidth: 6px;\n\t\theight: 6px;\n\t\tborder-radius: 50%;\n\t\tbackground: currentColor;\n\t\topacity: 0.6;\n\t\tanimation: dot-pulse 1.2s ease-in-out infinite;\n\t}\n\n\t.offline {\n\t\tcolor: var(--color-coral, #ff4d6d);\n\t}\n\t.offline-dot {\n\t\topacity: 1;\n\t\tanimation: none;\n\t}\n\n\t.label {\n\t\twhite-space: nowrap;\n\t\toverflow: hidden;\n\t\ttext-overflow: ellipsis;\n\t\tmax-width: min(70vw, 360px);\n\t}\n\n\t@keyframes indicator-in {\n\t\tfrom {\n\t\t\topacity: 0;\n\t\t\ttransform: translate(-50%, 8px);\n\t\t}\n\t\tto {\n\t\t\topacity: 1;\n\t\t\ttransform: translate(-50%, 0);\n\t\t}\n\t}\n\n\t/* Desktop indicator-in: same opacity fade but slide down from the navbar. */\n\t@media (min-width: 768px) {\n\t\t@keyframes indicator-in {\n\t\t\tfrom {\n\t\t\t\topacity: 0;\n\t\t\t\ttransform: translateY(-8px);\n\t\t\t}\n\t\t\tto {\n\t\t\t\topacity: 1;\n\t\t\t\ttransform: translateY(0);\n\t\t\t}\n\t\t}\n\t}\n\n\t@keyframes dot-pulse {\n\t\t0%,\n\t\t100% {\n\t\t\topacity: 0.3;\n\t\t}\n\t\t50% {\n\t\t\topacity: 0.9;\n\t\t}\n\t}\n</style>\n","<script lang=\"ts\">\n\timport { t } from '$lib/i18n';\n\timport { goto } from '$app/navigation';\n\timport { onMount } from 'svelte';\n\timport DuelAvatar from '$lib/components/duel/DuelAvatar.svelte';\n\timport { getAvatarUrl } from '$lib/avatar';\n\n\tinterface Viewer {\n\t\tnickname: string | null;\n\t\tavatarPath: string | null;\n\t\tviewedAt: string;\n\t}\n\tinterface Recap {\n\t\tprofileViews: number;\n\t\t/** Pro only — the server sends an empty list to everyone else. */\n\t\tprofileViewers: Viewer[];\n\t\t/** Withheld from this user — drives the upgrade CTA. 0 on Pro. */\n\t\tlockedViewers: number;\n\t\tnewMatches: number;\n\t\tunreadMessages: number;\n\t\tnewFollowers: number;\n\t}\n\n\tlet { loggedIn = false }: { loggedIn?: boolean } = $props();\n\n\t// Every row here is a hand-off: it closes the popup and lands the user on the\n\t// surface that owns that thing. No flow starts (or ends) inside this modal —\n\t// it's a recap, not a destination. The get-verified nudge lived here and was\n\t// the one exception; it's gone.\n\tconst GAP_MS = 6 * 60 * 60 * 1000; // show at most once per 6h\n\tconst LS_KEY = 'wb_last'; // last-shown timestamp, per browser\n\n\tlet open = $state(false);\n\tlet closing = $state(false);\n\tlet recap = $state<Recap | null>(null);\n\n\t// The CLIENT owns the \"when\": if it's been >6h since we last showed this\n\t// (localStorage), fetch the counts and show only if there's something. We\n\t// stamp the timestamp ONLY after a successful response — a failed fetch\n\t// (offline, cold route) must not consume the gate, or the popup would never\n\t// recover. First visit: no stamp → last=0 → proceeds.\n\tonMount(() => {\n\t\tif (!loggedIn || typeof localStorage === 'undefined') return;\n\t\tconst last = Number(localStorage.getItem(LS_KEY) || 0);\n\t\tconst now = Date.now();\n\t\tif (last && now - last < GAP_MS) return;\n\t\tconst sinceIso = last ? new Date(last).toISOString() : '';\n\t\tvoid (async () => {\n\t\t\ttry {\n\t\t\t\tconst qs = sinceIso ? `?since=${encodeURIComponent(sinceIso)}` : '';\n\t\t\t\tconst res = await fetch(`/api/welcome-back${qs}`);\n\t\t\t\tif (!res.ok) return; // don't stamp — retry on the next load\n\t\t\t\tconst r = (await res.json()) as Recap;\n\t\t\t\tlocalStorage.setItem(LS_KEY, String(now)); // definitive answer → stamp\n\t\t\t\tif (r.profileViews + r.newMatches + r.unreadMessages + r.newFollowers <= 0) return;\n\t\t\t\trecap = r;\n\t\t\t\tsetTimeout(() => (open = true), 400);\n\t\t\t} catch {\n\t\t\t\t/* silent — a welcome-back popup is never worth surfacing an error */\n\t\t\t}\n\t\t})();\n\t});\n\n\tfunction dismiss() {\n\t\tclosing = true;\n\t\tsetTimeout(() => {\n\t\t\topen = false;\n\t\t\tclosing = false;\n\t\t}, 200);\n\t}\n\n\tfunction go(href: string) {\n\t\tdismiss();\n\t\tgoto(href);\n\t}\n\n\t// RAT-461: the lookers' names are a Pro right, so there's nothing to unlock\n\t// here — the recap carries them for subscribers and an empty list otherwise.\n\t// The CTA hands off to /p's activity tab, which owns the paywall; keeping the\n\t// upgrade flow in one place means one thing to get right (and to measure).\n\tconst revealed = $derived(recap?.profileViewers ?? []);\n\tconst locked = $derived(recap?.lockedViewers ?? 0);\n\n\tconst plural = (n: number, s: string, p: string) => (n === 1 ? s : p);\n\n\t// Decorative blurred \"viewer\" avatars for the locked tease — NOT real viewers\n\t// (real avatar URLs would be un-blurrable client-side and leak who viewed for\n\t// free). Just varied on-brand gradient discs behind a blur.\n\tconst FAKE_AVATARS = [\n\t\t'linear-gradient(135deg,#ff6b81,#c9384e)',\n\t\t'linear-gradient(135deg,#7c5cff,#4a2fb0)',\n\t\t'linear-gradient(135deg,#f5b50a,#b9800a)',\n\t\t'linear-gradient(135deg,#4aa3ff,#2358b0)',\n\t\t'linear-gradient(135deg,#ff8a5c,#c94e2f)'\n\t];\n</script>\n\n{#if open && recap}\n\t<div\n\t\tclass=\"fixed inset-0 z-[120] flex items-end justify-center sm:items-center {closing\n\t\t\t? 'opacity-0'\n\t\t\t: 'opacity-100'} transition-opacity duration-200\"\n\t>\n\t\t<button class=\"absolute inset-0 bg-black/70 backdrop-blur-sm\" onclick={dismiss} aria-label={t('Close')}\n\t\t></button>\n\n\t\t<div\n\t\t\tclass=\"relative flex max-h-[90dvh] w-full max-w-[420px] flex-col overflow-hidden rounded-t-3xl border border-border bg-bg sm:mx-4 sm:rounded-3xl {closing\n\t\t\t\t? 'translate-y-2'\n\t\t\t\t: 'translate-y-0'} transition-transform duration-200\"\n\t\t>\n\t\t\t<div class=\"flex justify-center pt-3 sm:hidden\">\n\t\t\t\t<div class=\"h-1 w-10 rounded-full bg-muted/20\"></div>\n\t\t\t</div>\n\t\t\t<button\n\t\t\t\tclass=\"absolute top-4 right-4 z-10 flex h-8 w-8 items-center justify-center rounded-full bg-surface text-muted/40 transition-colors hover:text-muted\"\n\t\t\t\tonclick={dismiss}\n\t\t\t\taria-label={t('Close')}\n\t\t\t>\n\t\t\t\t<svg\n\t\t\t\t\tclass=\"h-4 w-4\"\n\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\tfill=\"none\"\n\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\tstroke-width=\"2.5\"\n\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t>\n\t\t\t\t\t<line x1=\"18\" y1=\"6\" x2=\"6\" y2=\"18\" /><line x1=\"6\" y1=\"6\" x2=\"18\" y2=\"18\" />\n\t\t\t\t</svg>\n\t\t\t</button>\n\n\t\t\t<div class=\"shrink-0 px-6 pt-7 pb-1\">\n\t\t\t\t<h2 class=\"font-display text-2xl font-bold tracking-tight text-text\">{t('welcome back')}</h2>\n\t\t\t\t<p class=\"mt-1 text-[13px] text-muted\">{t(\"here's what you missed while you were gone.\")}</p>\n\t\t\t</div>\n\n\t\t\t<div class=\"flex flex-col gap-2.5 overflow-y-auto px-6 pt-4 pb-6\">\n\t\t\t\t{#if recap.profileViews > 0}\n\t\t\t\t\t<div class=\"rounded-2xl border border-border bg-surface p-3.5\">\n\t\t\t\t\t\t<div class=\"flex items-center gap-3\">\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tclass=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-coral/10 text-coral\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\t\tclass=\"h-[18px] w-[18px]\"\n\t\t\t\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\tstroke-width=\"2\"\n\t\t\t\t\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<path d=\"M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7Z\" /><circle\n\t\t\t\t\t\t\t\t\t\tcx=\"12\"\n\t\t\t\t\t\t\t\t\t\tcy=\"12\"\n\t\t\t\t\t\t\t\t\t\tr=\"3\"\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<p class=\"min-w-0 flex-1 font-display text-[14px] font-semibold text-text\">\n\t\t\t\t\t\t\t\t{recap.profileViews === 1\n\t\t\t\t\t\t\t\t\t? t('1 person viewed your profile')\n\t\t\t\t\t\t\t\t\t: t('{n} people viewed your profile', { n: recap.profileViews })}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t{#if revealed.length > 0}\n\t\t\t\t\t\t\t<div class=\"mt-3 flex flex-wrap gap-1.5\">\n\t\t\t\t\t\t\t\t{#each revealed as v (v.nickname)}\n\t\t\t\t\t\t\t\t\t{#if v.nickname}\n\t\t\t\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t\t\t\thref=\"/u/{encodeURIComponent(v.nickname)}\"\n\t\t\t\t\t\t\t\t\t\t\tonclick={dismiss}\n\t\t\t\t\t\t\t\t\t\t\tclass=\"flex items-center gap-1.5 rounded-full border border-border bg-bg py-1 pr-2.5 pl-1 no-underline transition-colors hover:border-coral/40\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<DuelAvatar\n\t\t\t\t\t\t\t\t\t\t\t\tnickname={v.nickname}\n\t\t\t\t\t\t\t\t\t\t\t\tavatarUrl={getAvatarUrl(v.avatarPath)}\n\t\t\t\t\t\t\t\t\t\t\t\tsize={22}\n\t\t\t\t\t\t\t\t\t\t\t\tlink={false}\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t<span class=\"max-w-[7rem] truncate text-[12px] font-semibold text-text\"\n\t\t\t\t\t\t\t\t\t\t\t\t>{v.nickname}</span\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{/if}\n\n\t\t\t\t\t\t{#if locked > 0}\n\t\t\t\t\t\t\t{@const shown = Math.min(locked, FAKE_AVATARS.length)}\n\t\t\t\t\t\t\t<div class=\"mt-3 flex items-center\">\n\t\t\t\t\t\t\t\t<div class=\"flex -space-x-3 select-none blur-[3px]\" aria-hidden=\"true\">\n\t\t\t\t\t\t\t\t\t{#each FAKE_AVATARS.slice(0, shown) as g, i (i)}\n\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\tclass=\"flex h-9 w-9 items-center justify-center rounded-full ring-2 ring-bg\"\n\t\t\t\t\t\t\t\t\t\t\tstyle=\"background:{g}\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<svg class=\"h-5 w-5\" viewBox=\"0 0 24 24\" fill=\"rgba(255,255,255,.5)\">\n\t\t\t\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\t\t\t\td=\"M12 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8Zm0 2c-4.4 0-8 2.7-8 6v1h16v-1c0-3.3-3.6-6-8-6Z\"\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t{#if locked > shown}\n\t\t\t\t\t\t\t\t\t<span class=\"ml-4 text-[12px] font-semibold text-muted\"\n\t\t\t\t\t\t\t\t\t\t>{t('+{n} more', { n: locked - shown })}</span\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tonclick={() => go('/p?tab=activity')}\n\t\t\t\t\t\t\t\tclass=\"mt-3 flex w-full items-center justify-center gap-2 rounded-xl bg-coral px-4 py-2.5 font-display text-[13px] font-bold text-white transition-opacity hover:opacity-90\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{locked === 1\n\t\t\t\t\t\t\t\t\t? t('see who viewed you — 1 name')\n\t\t\t\t\t\t\t\t\t: t('see who viewed you — {n} names', { n: locked })}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t<p class=\"mt-1.5 text-center text-[11px] text-muted/60\">\n\t\t\t\t\t\t\t\t{t('on Premium — every looker, always')}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t{/if}\n\t\t\t\t\t</div>\n\t\t\t\t{/if}\n\n\t\t\t\t{#if recap.newMatches > 0}\n\t\t\t\t\t{@render tapRow('/c', recap.newMatches, plural(recap.newMatches, t('new match'), t('new matches')), heart)}\n\t\t\t\t{/if}\n\t\t\t\t{#if recap.unreadMessages > 0}\n\t\t\t\t\t{@render tapRow('/c', recap.unreadMessages, plural(recap.unreadMessages, t('unread message'), t('unread messages')), bubble)}\n\t\t\t\t{/if}\n\t\t\t\t{#if recap.newFollowers > 0}\n\t\t\t\t\t{@render tapRow('/p?tab=activity', recap.newFollowers, plural(recap.newFollowers, t('new follower'), t('new followers')), userPlus)}\n\t\t\t\t{/if}\n\t\t\t</div>\n\n\t\t\t<div class=\"shrink-0 border-t border-border/60 px-6 py-4\">\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tonclick={dismiss}\n\t\t\t\t\tclass=\"w-full rounded-xl border border-border bg-surface px-4 py-2.5 font-display text-[13px] font-bold text-text transition-colors hover:bg-white/[0.02]\"\n\t\t\t\t\t>{t(\"let's go\")}</button\n\t\t\t\t>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n{/if}\n\n{#snippet tapRow(href: string, count: number, label: string, icon: import('svelte').Snippet)}\n\t<button\n\t\ttype=\"button\"\n\t\tonclick={() => go(href)}\n\t\tclass=\"flex items-center gap-3 rounded-2xl border border-border bg-surface p-3.5 text-left transition-colors hover:bg-white/[0.02]\"\n\t>\n\t\t<span class=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-white/5 text-muted\">\n\t\t\t{@render icon()}\n\t\t</span>\n\t\t<p class=\"min-w-0 flex-1 font-display text-[14px] font-semibold text-text\">\n\t\t\t<span class=\"font-bold\">{count}</span>\n\t\t\t{label}\n\t\t</p>\n\t\t<svg\n\t\t\tclass=\"h-4 w-4 shrink-0 text-muted/40\"\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstroke-width=\"2.2\"\n\t\t\tstroke-linecap=\"round\"\n\t\t\tstroke-linejoin=\"round\"\n\t\t>\n\t\t\t<path d=\"m9 18 6-6-6-6\" />\n\t\t</svg>\n\t</button>\n{/snippet}\n\n{#snippet heart()}\n\t<svg\n\t\tclass=\"h-[18px] w-[18px]\"\n\t\tviewBox=\"0 0 24 24\"\n\t\tfill=\"none\"\n\t\tstroke=\"currentColor\"\n\t\tstroke-width=\"2\"\n\t\tstroke-linecap=\"round\"\n\t\tstroke-linejoin=\"round\"\n\t>\n\t\t<path\n\t\t\td=\"M20.8 4.6a5.5 5.5 0 0 0-7.8 0L12 5.6l-1-1a5.5 5.5 0 0 0-7.8 7.8l1 1L12 21l7.8-7.6 1-1a5.5 5.5 0 0 0 0-7.8Z\"\n\t\t/>\n\t</svg>\n{/snippet}\n\n{#snippet bubble()}\n\t<svg\n\t\tclass=\"h-[18px] w-[18px]\"\n\t\tviewBox=\"0 0 24 24\"\n\t\tfill=\"none\"\n\t\tstroke=\"currentColor\"\n\t\tstroke-width=\"2\"\n\t\tstroke-linecap=\"round\"\n\t\tstroke-linejoin=\"round\"\n\t>\n\t\t<path\n\t\t\td=\"M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5Z\"\n\t\t/>\n\t</svg>\n{/snippet}\n\n{#snippet userPlus()}\n\t<svg\n\t\tclass=\"h-[18px] w-[18px]\"\n\t\tviewBox=\"0 0 24 24\"\n\t\tfill=\"none\"\n\t\tstroke=\"currentColor\"\n\t\tstroke-width=\"2\"\n\t\tstroke-linecap=\"round\"\n\t\tstroke-linejoin=\"round\"\n\t>\n\t\t<path d=\"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2\" /><circle cx=\"9\" cy=\"7\" r=\"4\" /><path\n\t\t\td=\"M19 8v6M22 11h-6\"\n\t\t/>\n\t</svg>\n{/snippet}\n","<script lang=\"ts\">\n\timport './layout.css';\n\timport { invalidateAll, afterNavigate } from '$app/navigation';\n\timport { browser } from '$app/environment';\n\timport { page } from '$app/state';\n\timport { onMount } from 'svelte';\n\timport { routeStudioLinks } from '$lib/studio-link';\n\timport { activeMode, getModeAccent, accentOverride } from '$lib/stores/activeMode';\n\timport {\n\t\tgetSeo,\n\t\tcanonical,\n\t\tSITE_URL,\n\t\tSITE_NAME,\n\t\tDEFAULT_OG_IMAGE,\n\t\tORGANIZATION_SCHEMA,\n\t\tWEBSITE_SCHEMA,\n\t\tEXPLICIT_UGC_PATH,\n\t\trobotsNoindex\n\t} from '$lib/seo';\n\timport { markHydrated } from '$lib/hydration-guard';\n\timport { LOCALES, LOCALE_INFO, isLocalizedPath, localizePath, splitLocalePath } from '$lib/i18n';\n\timport {\n\t\tisStudioHost,\n\t\tisStudioPage,\n\t\tstudioCanonicalPath,\n\t\tstudioOrigin,\n\t\tSTUDIO_BRAND,\n\t\tSTUDIO_ROUTE\n\t} from '$lib/studio-host';\n\timport {\n\t\tgetStudioSeo,\n\t\tstudioAppSchema,\n\t\tstudioOrganizationSchema,\n\t\tstudioWebsiteSchema\n\t} from '$lib/studio-seo';\n\timport Navbar from '$lib/components/Navbar.svelte';\n\timport AnnouncementBar from '$lib/components/AnnouncementBar.svelte';\n\timport SuspendedBanner from '$lib/components/SuspendedBanner.svelte';\n\timport BottomNav from '$lib/components/BottomNav.svelte';\n\timport NavMenu from '$lib/components/NavMenu.svelte';\n\timport AuthSheet from '$lib/components/AuthSheet.svelte';\n\timport { authSheet, closeAuthSheet } from '$lib/stores/authSheet';\n\timport ChatFab from '$lib/components/ChatFab.svelte';\n\timport Footer from '$lib/components/Footer.svelte';\n\timport NavigationProgress from '$lib/components/NavigationProgress.svelte';\n\timport PushPromptRedirect from '$lib/components/PushPromptRedirect.svelte';\n\timport LazyAnalytics from '$lib/components/LazyAnalytics.svelte';\n\timport RaterPresenceHeartbeat from '$lib/components/rater/RaterPresenceHeartbeat.svelte';\n\timport Toast from '$lib/components/realtime/Toast.svelte';\n\timport ConnectionIndicator from '$lib/components/realtime/ConnectionIndicator.svelte';\n\timport WelcomeBackModal from '$lib/components/WelcomeBackModal.svelte';\n\timport {\n\t\tinitGlobalRealtime,\n\t\tdisposeGlobalRealtime,\n\t\tnotificationPrefs\n\t} from '$lib/stores/global-realtime';\n\timport { realtime } from '$lib/realtime/client';\n\timport { initPwa } from '$lib/stores/pwa';\n\n\t// CookieConsent is loaded client-side only — it has a localStorage-derived\n\t// initial state that would create a hydration mismatch on SSR.\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\tlet CookieConsent = $state<any>(null);\n\n\tlet { children, data } = $props();\n\n\t// The studio, whether at /studio or as the front page of its own domain (the\n\t// `reroute` hook serves `/` there from the studio route).\n\tconst studioRoute = $derived(page.route.id === STUDIO_ROUTE);\n\t// RAT-632: PornPilot's own pages beside it (/prompts), PornPilot's wherever\n\t// they are served, the way the studio is.\n\tconst studioPage = $derived(isStudioPage(page.route.id));\n\n\t// RAT-573: the address without its language prefix (/de/games/tod →\n\t// /games/tod), which is what every path rule below is written against.\n\tconst basePath = $derived(splitLocalePath(page.url.pathname).path);\n\tconst pageLocale = $derived(data.locale);\n\t// the server writes <html lang>; a client-side navigation keeps it in step\n\t$effect(() => {\n\t\tdocument.documentElement.lang = LOCALE_INFO[pageLocale].hreflang;\n\t});\n\n\t// RAT-385: announcement bar. `announcement` is null when none active or the\n\t// viewer already dismissed this version (server-side cookie check). Dismiss\n\t// is also tracked locally so the bar disappears without a reload.\n\tconst announcement = $derived(data.announcement);\n\tlet announcementDismissed = $state(false);\n\n\t// Hide the bar on viewport-fit / focus surfaces (chat, auth, rate session).\n\t// They own their height (100svh master/detail), so the --ann-h padding the\n\t// bar adds to <main> would push them past the viewport / overlap their chrome.\n\tconst barHidden = $derived.by(() => {\n\t\tconst p = basePath;\n\t\treturn (\n\t\t\tp === '/a' ||\n\t\t\tp.startsWith('/a/') ||\n\t\t\tp.startsWith('/auth/') ||\n\t\t\tp.startsWith('/rate/session/') ||\n\t\t\tp === '/c' ||\n\t\t\tp.startsWith('/c/') ||\n\t\t\tp.startsWith('/g/') ||\n\t\t\tp.startsWith('/livecam') ||\n\t\t\t// RAT-441: the floor is the same focus-mode contract as livecam — the\n\t\t\t// room owns the whole viewport and its own header, so a z-60 strip over\n\t\t\t// it would bury the host's end button.\n\t\t\tp.startsWith('/floor') ||\n\t\t\t// RAT-632: PornPilot's own pages carry their own header, and ratemyd's\n\t\t\t// announcements are not theirs to show.\n\t\t\tstudioPage\n\t\t);\n\t});\n\tconst showBar = $derived(!!announcement && !announcementDismissed && !barHidden);\n\n\t// RAT-327: per-module accent runtime. The active module's color is written\n\t// into three CSS custom properties on the document root so every component\n\t// that uses `var(--color-accent)` (instead of a hardcoded mode color) flips\n\t// in step with the route. The layout.css @theme block declares\n\t// `--color-accent: var(--accent-color, var(--color-coral))`, so SSR + the\n\t// first-paint window before this effect fires still resolve cleanly.\n\tconst accent = $derived($accentOverride ?? getModeAccent($activeMode));\n\n\t$effect(() => {\n\t\tconst root = document.documentElement.style;\n\t\troot.setProperty('--accent-color', accent.hex);\n\t\troot.setProperty('--accent-color-dark', accent.hexDark);\n\t\troot.setProperty('--accent-color-light', accent.hexLight);\n\t\t// ::selection highlight follows the same color.\n\t\troot.setProperty('--selection-color', accent.hex);\n\t});\n\n\t// RAT-360 (F4): single layout-level subscription to the logged-in user's\n\t// realtime channel. Drives the unread counters + the toast. Components\n\t// that need finer-grained events (NotificationBell items list, sidebar\n\t// patches, open thread/room) subscribe to the primitive directly — the\n\t// RealtimeClient refcounts SDK channels so adding more handlers on the\n\t// same channel reuses the one socket subscription.\n\t// Keyed on the id, not on `data`: the layout load reads the url, so `data`\n\t// is a new object on every navigation, and reading it here re-ran the\n\t// effect each time — the cleanup unsubscribed, the rerun subscribed again,\n\t// and once the channel had seen an event, each resubscribe fetched a replay.\n\tconst realtimeUserId = $derived(data.session?.user?.id);\n\t$effect(() => {\n\t\tconst userId = realtimeUserId;\n\t\tif (!userId) {\n\t\t\tdisposeGlobalRealtime();\n\t\t\treturn;\n\t\t}\n\t\tinitGlobalRealtime(userId);\n\t\treturn () => disposeGlobalRealtime();\n\t});\n\n\t// RAT-365: sync the mute-toggle store from layout data. invalidateAll\n\t// after a /api/profile/notification-prefs POST also flows through here,\n\t// so the optimistic write in the bell settings reconciles against the\n\t// authoritative server row.\n\t$effect(() => {\n\t\tnotificationPrefs.set(data.notificationPrefs ?? {});\n\t});\n\n\t// Pages served on the studio's own domain belong to it, not to ratemyd: their\n\t// canonical points there and they don't claim to be the RateMyD organisation.\n\t// The studio belongs to it wherever it is served, so a preview deployment or\n\t// localhost shows it the same way.\n\tconst onStudioHost = $derived(isStudioHost(page.url.hostname));\n\tconst pornpilot = $derived(onStudioHost || studioRoute || studioPage);\n\tconst siteOrigin = $derived(pornpilot ? (studioOrigin() ?? SITE_URL) : SITE_URL);\n\t// The studio is PornPilot's front page, so /studio is canonical at `/`.\n\t// A translated page is canonical at its own language's address and lists\n\t// the others as alternates; every other page has one English URL.\n\tconst translated = $derived(!pornpilot && isLocalizedPath(basePath));\n\tconst canonicalPath = $derived(\n\t\tpornpilot\n\t\t\t? studioCanonicalPath(page.url.pathname)\n\t\t\t: translated\n\t\t\t\t? localizePath(basePath, pageLocale)\n\t\t\t\t: basePath\n\t);\n\tconst seo = $derived(\n\t\tpornpilot\n\t\t\t? getStudioSeo(canonicalPath, page.data?.seo ?? null)\n\t\t\t: getSeo(basePath, page.data?.seo ?? null, translated ? pageLocale : 'en')\n\t);\n\tconst alternates = $derived(\n\t\ttranslated\n\t\t\t? LOCALES.map((l) => ({\n\t\t\t\t\threflang: LOCALE_INFO[l].hreflang,\n\t\t\t\t\thref: canonical(localizePath(basePath, l), siteOrigin)\n\t\t\t\t}))\n\t\t\t: []\n\t);\n\tconst canonicalUrl = $derived(canonical(canonicalPath, siteOrigin));\n\tconst noindex = $derived(\n\t\trobotsNoindex(seo, { browser, error: !!page.error, status: page.status })\n\t);\n\t// seo.ogImage may be a site-relative path (static OG assets) OR an absolute\n\t// URL (post media / cross-content share cards). Only prefix the relative\n\t// ones — prefixing an absolute URL produced a broken `https://…apphttps://…`.\n\tconst ogImage = $derived.by(() => {\n\t\tconst img = seo.ogImage ?? DEFAULT_OG_IMAGE;\n\t\treturn /^https?:\\/\\//.test(img) ? img : `${siteOrigin}${img}`;\n\t});\n\n\t// Footer renders on the homepage + the SFW SEO content pages (stats/tools)\n\t// so the stats/calculator cluster is internally linked from a crawlable nav\n\t// (RAT-166 de-orphan: these pages had ZERO inbound internal links → not\n\t// indexed). Kept OFF task-focused app routes (chat, arena, rate session)\n\t// where the marketing footer's columns push the real content offscreen\n\t// (RAT-113).\n\tconst showFooter = $derived.by(() => {\n\t\tconst p = basePath;\n\t\treturn (\n\t\t\tp === '/' ||\n\t\t\tp === '/about' ||\n\t\t\tp === '/stats' ||\n\t\t\tp.startsWith('/stats/') ||\n\t\t\tp.startsWith('/tools/') ||\n\t\t\t// RAT-573: a translated page carries the language picker, which lives\n\t\t\t// in the footer — on a phone, signed out, it is the only one in reach.\n\t\t\ttranslated\n\t\t);\n\t});\n\n\tconst jsonLdTag = (data: unknown) =>\n\t\t`<sc${''}ript type=\"application/ld+json\">${JSON.stringify(data)}</sc${''}ript>`;\n\tconst orgJsonLd = jsonLdTag(ORGANIZATION_SCHEMA);\n\tconst siteJsonLd = jsonLdTag(WEBSITE_SCHEMA);\n\t// PornPilot's front page is the studio, and declares it as an application.\n\tconst studioJsonLd = $derived(\n\t\tjsonLdTag(studioOrganizationSchema(siteOrigin)) +\n\t\t\tjsonLdTag(studioWebsiteSchema(siteOrigin)) +\n\t\t\t(studioRoute ? jsonLdTag(studioAppSchema(siteOrigin)) : '')\n\t);\n\n\t// RAT-526: a signed-in click on a studio link goes by /go/studio, which hands\n\t// the sign-in to the studio's domain; the links themselves stay /studio.\n\tonMount(() => routeStudioLinks(() => !!page.data.user));\n\t// The first page is interactive: from here on a failed chunk is a failed\n\t// navigation, which SvelteKit handles itself (see $lib/hydration-guard).\n\tonMount(markHydrated);\n\n\tlet unsubscribe: (() => void) | null = null;\n\tonMount(() => {\n\t\t// PWA: capture the install prompt globally (it fires once at load) so the\n\t\t// /install page + nudge can offer a native install button on Chromium.\n\t\tinitPwa();\n\n\t\t// RAT-91: Supabase punts auth errors back to the SITE_URL with the error\n\t\t// params attached. The implicit / magic-link flow returns them in the URL\n\t\t// *hash* (`#error=access_denied&error_code=…`), but the PKCE flow used by\n\t\t// Google OAuth (createBrowserClient) fails *before* /auth/callback when\n\t\t// GoTrue can't match the flow_state (expired / already-consumed — common\n\t\t// on iOS Safari cross-site cookie handling) and comes back as a *query\n\t\t// string* instead: `?error=invalid_request&error_code=bad_oauth_state&…`.\n\t\t// Check both on every mount and redirect to /auth/error so users see a\n\t\t// friendly explanation + retry instead of the marketing homepage with\n\t\t// junk in the URL bar.\n\t\tif (typeof window !== 'undefined') {\n\t\t\tconst hashParams = new URLSearchParams(window.location.hash.replace(/^#/, ''));\n\t\t\t// Only treat the query string as an auth bounce when it carries\n\t\t\t// GoTrue's error_code / error_description signature — a bare `?error=`\n\t\t\t// (e.g. our own /rate?error=auth_failed) must not be hijacked here.\n\t\t\tconst searchParams = new URLSearchParams(window.location.search);\n\t\t\tconst isQueryAuthError =\n\t\t\t\tsearchParams.has('error_code') || searchParams.has('error_description');\n\t\t\tconst params = window.location.hash.includes('error')\n\t\t\t\t? hashParams\n\t\t\t\t: isQueryAuthError\n\t\t\t\t\t? searchParams\n\t\t\t\t\t: null;\n\t\t\tif (params) {\n\t\t\t\tconst errorCode = params.get('error_code');\n\t\t\t\tconst error = params.get('error');\n\t\t\t\tif (errorCode || error) {\n\t\t\t\t\t// RAT-491: an already-authenticated user can trip this bounce by\n\t\t\t\t\t// re-tapping \"continue with google\" after a successful return —\n\t\t\t\t\t// the second handshake burns the already-consumed PKCE flow_state\n\t\t\t\t\t// and GoTrue punts back with bad_oauth_state. Their session is\n\t\t\t\t\t// intact, so routing them to /auth/error (\"sign-in didn't finish\")\n\t\t\t\t\t// is a lie that abandons a working session — the exact spurious\n\t\t\t\t\t// error the signals flagged. Swallow the bounce: strip the junk\n\t\t\t\t\t// params off the URL (query + hash) and stay on the page.\n\t\t\t\t\t//\n\t\t\t\t\t// Only for the handshake codes, though. An authed user clicking a\n\t\t\t\t\t// stale magic link arrives with otp_expired / invite_expired, and\n\t\t\t\t\t// swallowing that shows them nothing at all — they tapped a link and\n\t\t\t\t\t// the app appears to ignore it. Those still get the explainer page.\n\t\t\t\t\tconst isSpentHandshake =\n\t\t\t\t\t\terrorCode === 'bad_oauth_state' || errorCode === 'flow_state_already_used';\n\t\t\t\t\tif (data.user && isSpentHandshake) {\n\t\t\t\t\t\t// Clean the junk params and fall through to the normal layout\n\t\t\t\t\t\t// init below (auth-state subscription, realtime socket) — we're\n\t\t\t\t\t\t// staying on the page, NOT navigating away, so we must not skip it.\n\t\t\t\t\t\tconst clean = new URL(window.location.href);\n\t\t\t\t\t\tclean.searchParams.delete('error');\n\t\t\t\t\t\tclean.searchParams.delete('error_code');\n\t\t\t\t\t\tclean.searchParams.delete('error_description');\n\t\t\t\t\t\tconst cleanHash = window.location.hash.includes('error') ? '' : window.location.hash;\n\t\t\t\t\t\thistory.replaceState(history.state, '', clean.pathname + clean.search + cleanHash);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconst reason = errorCode || error || 'default';\n\t\t\t\t\t\tconst description = params.get('error_description');\n\t\t\t\t\t\tconst url = new URL('/auth/error', window.location.origin);\n\t\t\t\t\t\turl.searchParams.set('reason', reason);\n\t\t\t\t\t\tif (description) url.searchParams.set('description', description);\n\t\t\t\t\t\twindow.location.replace(url.toString());\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\timport('$lib/supabase').then(({ createSupabaseBrowserClient }) => {\n\t\t\tconst supabase = createSupabaseBrowserClient();\n\t\t\t// RAT-135: only invalidate on real user-facing transitions. The\n\t\t\t// previous unconditional invalidateAll fired on TOKEN_REFRESHED\n\t\t\t// (every ~1h, plus any time safeGetSession refreshed a near-expiry\n\t\t\t// JWT) — which re-ran every server load on the page, which re-ran\n\t\t\t// safeGetSession, which could refresh the token again. that cascade\n\t\t\t// stalled subsequent navigations after a few back/forward hops.\n\t\t\tconst {\n\t\t\t\tdata: { subscription }\n\t\t\t} = supabase.auth.onAuthStateChange(\n\t\t\t\t(event: string, session: { access_token?: string } | null) => {\n\t\t\t\t\t// Re-auth the realtime socket with the fresh JWT. Without this the\n\t\t\t\t\t// websocket keeps using the token it opened with; when that token is\n\t\t\t\t\t// refreshed (roughly hourly, but in practice whenever a navigation's\n\t\t\t\t\t// safeGetSession refreshes a near-expiry JWT — hence the \"random\"\n\t\t\t\t\t// timing) the channels start erroring on the now-stale token and the\n\t\t\t\t\t// SDK's reconnect retries with the same stale token, so the indicator\n\t\t\t\t\t// sticks on \"reconnecting…\" until a full page reload mints a new\n\t\t\t\t\t// client. RAT-458: raw setAuth only pushes the token to channels\n\t\t\t\t\t// that are still *joined* — a channel that already erroed out on\n\t\t\t\t\t// the stale token never picks it up, which is the gap behind the\n\t\t\t\t\t// multi-minute stalls. realtime.reauthenticate() pushes the fresh\n\t\t\t\t\t// token AND force-cycles any errored channels so they rejoin clean.\n\t\t\t\t\t// Same singleton Supabase client under the hood.\n\t\t\t\t\tif (event === 'TOKEN_REFRESHED' || event === 'SIGNED_IN') {\n\t\t\t\t\t\trealtime.reauthenticate(session?.access_token);\n\t\t\t\t\t}\n\t\t\t\t\tif (event === 'SIGNED_IN' || event === 'SIGNED_OUT' || event === 'USER_UPDATED') {\n\t\t\t\t\t\tinvalidateAll();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t);\n\t\t\tunsubscribe = () => subscription.unsubscribe();\n\t\t});\n\n\t\t// Defer cookie consent until after first paint. Renders a fixed-position\n\t\t// overlay so showing it late doesn't shift any content.\n\t\timport('$lib/components/CookieConsent.svelte').then((c) => {\n\t\t\tCookieConsent = c.default;\n\t\t});\n\n\t\t// RAT-193 / RAT-194: Safari + Chrome sometimes refuse autoplay even\n\t\t// with muted+playsinline, especially after navigation or bfcache.\n\t\t// Strategy: keep retrying .play() on every paused video[autoplay] at\n\t\t// every plausible moment — pageshow, visibility, gesture, and as\n\t\t// individual videos hit canplay/loadedmetadata. Cheap, idempotent.\n\t\tconst replayAutoplayVideos = () => {\n\t\t\tdocument.querySelectorAll<HTMLVideoElement>('video[autoplay]').forEach((v) => {\n\t\t\t\tif (v.paused) void v.play().catch(() => {});\n\t\t\t});\n\t\t};\n\t\tconst onPageShow = () => replayAutoplayVideos();\n\t\tconst onVisibility = () => {\n\t\t\tif (!document.hidden) replayAutoplayVideos();\n\t\t};\n\t\t// Catch newly-mounted videos (route changes, hydration) and retry play\n\t\t// once metadata/canplay fires — that's the moment Safari is most\n\t\t// willing. Capture phase so it sees the event regardless of bubble\n\t\t// stops.\n\t\tconst mediaEvents = ['loadedmetadata', 'canplay', 'canplaythrough'] as const;\n\t\tconst onMediaReady = (e: Event) => {\n\t\t\tconst t = e.target as HTMLVideoElement | null;\n\t\t\tif (t && t.tagName === 'VIDEO' && t.autoplay && t.paused) {\n\t\t\t\tvoid t.play().catch(() => {});\n\t\t\t}\n\t\t};\n\t\t// First-gesture safety net for Chrome: a single user gesture\n\t\t// (touchstart, click) is enough to re-enable autoplay on stuck\n\t\t// videos. Passive + non-once so every gesture nudges paused videos.\n\t\tconst onUserGesture = () => replayAutoplayVideos();\n\n\t\twindow.addEventListener('pageshow', onPageShow);\n\t\tdocument.addEventListener('visibilitychange', onVisibility);\n\t\tmediaEvents.forEach((ev) => document.addEventListener(ev, onMediaReady, true));\n\t\twindow.addEventListener('touchstart', onUserGesture, { passive: true });\n\t\twindow.addEventListener('click', onUserGesture, { passive: true });\n\t\twindow.addEventListener('pointerdown', onUserGesture, { passive: true });\n\n\t\t// RAT-195: After OAuth-redirect destination loads, the video element's\n\t\t// `loadedmetadata` may have already fired before our listener attached.\n\t\t// Catch the case with an immediate sweep and a few staggered retries.\n\t\treplayAutoplayVideos();\n\t\tsetTimeout(replayAutoplayVideos, 100);\n\t\tsetTimeout(replayAutoplayVideos, 500);\n\t\tsetTimeout(replayAutoplayVideos, 1500);\n\n\t\treturn () => {\n\t\t\tif (unsubscribe) unsubscribe();\n\t\t\twindow.removeEventListener('pageshow', onPageShow);\n\t\t\tdocument.removeEventListener('visibilitychange', onVisibility);\n\t\t\tmediaEvents.forEach((ev) => document.removeEventListener(ev, onMediaReady, true));\n\t\t\twindow.removeEventListener('touchstart', onUserGesture);\n\t\t\twindow.removeEventListener('click', onUserGesture);\n\t\t\twindow.removeEventListener('pointerdown', onUserGesture);\n\t\t};\n\t});\n\n\t// SvelteKit client-side navigation: stagger retries because the\n\t// destination's <video> element + its metadata may not be ready at\n\t// the rAF tick. 0ms / 200ms / 500ms covers fast and slow hydrations.\n\tafterNavigate(() => {\n\t\t// Dismiss the global sign-in sheet once a navigation lands (e.g. the\n\t\t// post-OTP goto to `next`), so it never overlays the destination.\n\t\tcloseAuthSheet();\n\t\tconst replay = () => {\n\t\t\tdocument.querySelectorAll<HTMLVideoElement>('video[autoplay]').forEach((v) => {\n\t\t\t\tif (v.paused) void v.play().catch(() => {});\n\t\t\t});\n\t\t};\n\t\trequestAnimationFrame(replay);\n\t\tsetTimeout(replay, 200);\n\t\tsetTimeout(replay, 500);\n\t});\n</script>\n\n<svelte:head>\n\t<title>{seo.title}</title>\n\t{#if seo.description}\n\t\t<meta name=\"description\" content={seo.description} />\n\t{/if}\n\t<link rel=\"canonical\" href={canonicalUrl} />\n\t{#each alternates as alt (alt.hreflang)}\n\t\t<link rel=\"alternate\" hreflang={alt.hreflang} href={alt.href} />\n\t{/each}\n\t{#if alternates.length}\n\t\t<link rel=\"alternate\" hreflang=\"x-default\" href={alternates[0].href} />\n\t{/if}\n\t{#if noindex}\n\t\t<meta name=\"robots\" content=\"noindex\" />\n\t{/if}\n\t{#if pornpilot || EXPLICIT_UGC_PATH.test(basePath)}\n\t\t<!-- Google's SafeSearch label for adult content: PornPilot, and ratemyd's\n\t\t     pages of members' explicit photos (EXPLICIT_UGC_PATH). The product\n\t\t     pages and the SFW stats and tools cluster rank without it. -->\n\t\t<meta name=\"rating\" content=\"adult\" />\n\t{/if}\n\n\t<!-- open graph -->\n\t<meta property=\"og:type\" content={seo.ogType ?? 'website'} />\n\t<meta property=\"og:site_name\" content={pornpilot ? STUDIO_BRAND : SITE_NAME} />\n\t<meta property=\"og:title\" content={seo.ogTitle ?? seo.title} />\n\t{#if seo.ogDescription ?? seo.description}\n\t\t<meta property=\"og:description\" content={seo.ogDescription ?? seo.description} />\n\t{/if}\n\t<meta property=\"og:url\" content={canonicalUrl} />\n\t<meta property=\"og:locale\" content={LOCALE_INFO[pageLocale].ogLocale} />\n\t<meta property=\"og:image\" content={ogImage} />\n\t<meta property=\"og:image:width\" content={String(seo.ogImageWidth ?? 1200)} />\n\t<meta property=\"og:image:height\" content={String(seo.ogImageHeight ?? 630)} />\n\n\t<!-- twitter -->\n\t<meta name=\"twitter:card\" content=\"summary_large_image\" />\n\t<meta name=\"twitter:title\" content={seo.ogTitle ?? seo.title} />\n\t{#if seo.ogDescription ?? seo.description}\n\t\t<meta name=\"twitter:description\" content={seo.ogDescription ?? seo.description} />\n\t{/if}\n\t<meta name=\"twitter:image\" content={ogImage} />\n\n\t<!-- structured data -->\n\t{#if pornpilot}\n\t\t{@html studioJsonLd}\n\t{:else}\n\t\t{@html orgJsonLd}\n\t\t{@html siteJsonLd}\n\t{/if}\n</svelte:head>\n\n<NavigationProgress />\n{#if data.isSuspended}\n\t<SuspendedBanner />\n{:else if showBar && announcement}\n\t<AnnouncementBar {announcement} ondismiss={() => (announcementDismissed = true)} />\n{/if}\n<Navbar />\n<main style=\"padding-top: var(--ann-h, 0px)\">\n\t{@render children()}\n</main>\n\n<!-- RAT-272: mobile-only bottom nav. The component itself is md:hidden so\n\t it only paints on small viewports — but mounting unconditionally means\n\t the picker/auth modals it owns are always reachable from a tap on the\n\t bottom of the screen on phones. Hidden on /rate/session/[id] internally. -->\n<BottomNav />\n\n<!-- The nav drawer, mounted once for the whole app: the Navbar hamburger\n\t (desktop, and on mobile for visitors who aren't signed in) and the mobile\n\t BottomNav \"menu\" slot all open it through the shared navMenuOpen store, so\n\t it can't live inside any one surface. -->\n<NavMenu />\n\n<!-- RAT-272 follow-up: single app-wide sign-in sheet. Mounted here (not per\n\t surface) so gated entry points that don't own the sheet — the feed's live\n\t rater rail, a floor room's back-to-chat arrow, the desktop nav — can raise\n\t it in place via `openAuthSheet(next)` instead of a full-page 303 to\n\t /auth/sign-in. `next` sends the user to where they were headed once authed. -->\n<AuthSheet\n\topen={$authSheet.open}\n\tnext={$authSheet.next}\n\tflow={$authSheet.flow}\n\tcontext={$authSheet.context}\n\tonclose={closeAuthSheet}\n/>\n\n<!-- RAT-332: desktop-only floating chat FAB. Internally `hidden md:flex`,\n\t auth-gated, and respects the same focus-mode hide rules as Navbar +\n\t BottomNav. Mobile already has chat in BottomNav so it's a no-op there. -->\n<ChatFab />\n\n{#if showFooter && !studioRoute}\n\t<Footer />\n{/if}\n{#if CookieConsent}<CookieConsent />{/if}\n<PushPromptRedirect loggedIn={!!data.session?.user} />\n{#if data?.isRater}<RaterPresenceHeartbeat />{/if}\n{#if data.session?.user}<Toast />{/if}\n{#if data.session?.user}<ConnectionIndicator />{/if}\n{#if data.session?.user}<WelcomeBackModal loggedIn={true} />{/if}\n<LazyAnalytics userId={data.session?.user?.id ?? null} />\n"],"version":3}