{"release_id":"01a1c502-4ad1-0000-ec62-adc54d3c69aa","chunk_id":"01a1c502-4c3d-7890-a314-09177de7acc4","file":"DqZnLJij.mjs","mappings":"2iIAAA,UAiDE,IAAAA,mBAAU,IAAI,EACdC,oBAAW,IAAI,EACfC,uBAAc,mBAAmB,EACjCC,0BAAiB,IAAI,EACrBC,uBAAc,IAAI,EAClBC,GAAMC,EAAAC,EAAA,SAAA,EAAG,IAAA,QAGJC,EAAUC,EAAA,IAAYT,GAAO,GAAI,SAAS,EAE1CU,GAAiC,CACtC,QAAS,0CACT,UAAW,0CACX,iBAAkB,uCAClB,MAAO,4CACP,KAAM,0CACN,KAAM,sCAEN,QAAS,0CAGT,SAAU,6CAGLC,GAAmC,CACxC,QAAS,aACT,UAAW,aACX,iBAAkB,cAClB,MAAO,cACP,KAAM,aACN,KAAM,YACN,QAAS,aACT,SAAU,eASLC,GAAgE,CACrE,MAAO,QACP,KAAM,OACN,KAAM,OACN,QAAS,UACT,UAAW,OACX,iBAAkB,OAClB,SAAU,YAGLC,GAAUJ,EAAA,IAAqB,OAC9BK,EAAOF,GAAaG,EAACP,CAAC,CAAA,EACvB,OAAAM,MAEDE,EAAoB,OAAQC,GAAMA,EAAE,MAAQH,CAAI,KAChDE,EAAoB,OAAQC,GAAMA,EAAE,MAAQH,CAAI,GAHlCE,CAKnB,CAAC,MAMGE,EAAUC,GAAoB,SAAS,EACvCC,EAAUD,GAAO,EAAK,EAMpB,MAAAE,GAAuB,CAAI,UAAW,YAAa,QAAQ,EAC3DC,EAAIb,EAAA,IAAYc,GAAM,MAAIL,CAAO,CAAA,CAAA,EAGjCM,EAAa,GAAmBC,GAAYF,GAAM,IAAI,CAAC,CAAA,EAGvDG,GAAWC,GAAaA,EAAE,SAAW,EAAIC,EAAE,SAAS,EAAIA,EAAE,cAAgB,EAAGD,EAAE,MAAM,CAAA,WAClFE,GAAUF,EAAiB,CAC/B,GAAAA,EAAE,UAAY,UAAS,OAASC,EAAE,iCAAiC,EACjE,MAAAE,EAAQC,GAAUJ,EAAE,KAAK,SACxBA,EAAE,UAAY,SAClBC,EAAE,wBAAuB,CAAI,MAAAE,CAAK,CAAA,EAClCF,EAAE,8BAA6B,CAAI,MAAAE,EAAO,EAAGH,EAAE,OAAM,CACzD,CAEe,eAAAK,IAAQ,OAClBZ,CAAO,EACXa,CAAAA,EAAAb,EAAU,EAAI,EACdc,GAA6B,EAAC,IAAAnB,EAAMO,CAAI,EAAC,KAAK,GAAA,gBAAAP,EAAoBG,CAAO,CAAA,IAAAH,EAAIP,CAAC,CAAA,EAAA,EAC1E,GAAA,OACG2B,EAAG,MAAS,MAAM,6BAA4B,CACnD,OAAQ,OACR,QAAO,CAAI,eAAgB,kBAAkB,EAC7C,KAAM,KAAK,UAAS,CACnB,OAAMjB,CAAO,EACb,OAAQb,GAAM,GAAA,iBAAAU,EAAqBP,CAAC,CAAA,GACpC,YAAAN,GAAW,EAGX,WAAU,GAAK,SAAS,QAAQ,qBAG5BkC,EAAM,MAASD,EAAI,KAAI,EACzB,GAAAA,EAAI,IAAMC,EAAO,IAAK,CACzB,OAAO,SAAS,KAAOA,EAAO,UAE/B,CACIA,EAAO,mBAAiB7B,EAAA,QAAA,CAC7B,MAAQ,CAER,QAAC,CACA0B,EAAAb,EAAU,EAAK,CAChB,EACD,kCAIC,IAAAiB,EAAAC,GAAA,EAMCC,EAAAC,EANDH,CAAA,EAaCI,GAAAC,EAPAH,EAAA,CAAA,EAWCI,EAAAH,EAJDC,EAAA,EAwBCG,EAAGF,EApBHC,EAAA,CAAA,EAwBCE,KAJDD,CAAG,EAOFE,IAHAD,GAAC,CAAA,OAGDC,EAAE,EAAA,IAAFA,CAAE,EAGF,IAAAC,IAHAD,EAAE,CAAA,OAGFC,CAAC,EAC6BC,EAAIN,EAAAO,EAAA,OAAJD,EAAI,EAAA,IAAJA,CAAI,EACjC,IAAAE,IAD6BF,EAAI,CAAA,OACjCE,EAAI,EAAA,IAAJA,CAAI,EACJ,IAAAC,IADAD,EAAI,CAAA,OACJC,EAAI,EAAA,IAAJA,CAAI,EACJ,IAAAC,IADAD,EAAI,CAAA,OACJC,EAAI,EAAA,IAAJA,CAAI,WAAJA,CAAI,IAJLL,CAAC,EAQD,IAAAM,EAAAX,EARAK,EAAC,CAAA,EAQDO,EAAAD,EAAA,GAAA,IAKOhC,GAAYkC,GAAGA,KAAHA,IAAC,CAClB,IAAAC,EAAAC,GAAA,EAAAC,EAAAlB,EAAAgB,CAAA,oBAaE,IAAAG,EAAAC,GAAA,EAAAC,EAAArB,EAAAmB,CAAA,EAAAG,EAAAH,CAAA,EAGEI,EAAAC,GAAAC,EAAAJ,EAAA,IAAAG,GAAA,EAAA,GAAA,EAAA,CAAA,IAAAxC,EAAU+B,CAAC,CAAA,CAAA,EAHbW,EAAAC,EAAAR,CAAA,WAJGnC,EAAU+B,CAAC,EAAI,CAAC,uBATrBO,EAAAN,CAAA,UAAAA,EAAA,gBAAAzC,EAGeG,CAAO,IAAKqC,CAAC,IAH5BC;UAMEzC,EAAAG,CAAO,IAAKqC,EAAI,mCAAqC,4BAA4B,sCAElF,CAAA,IAAA7B,GAAQH,GAAM,IAAIgC,CAAC,CAAA,CAAA,aARpBC,EAAA,IAAAvB,EAIgBf,EAAUqC,EAAC,EAAA,CAAA,EAJ3BW,EAAAC,EAAAX,CAAA,IANFM,EAAAT,CAAA,EA8BA,IAAAe,EAAG1B,EA9BHW,EAAA,CAAA,EA+BCgB,KADDD,CAAG,OACFC,EAAI,EACaC,GAAI5B,EAAA6B,EAAA,OAAJD,GAAI,EAAA,IAAJA,EAAI,IADrBD,EAAI,IADLD,CAAG,EAKH,IAAAI,IALAJ,EAAG,CAAA,OAKHI,CAAC,kCAGGC,EAAI/B,EAAAgC,EAAAC,CAAA,CAAA,MAAJF,EAAI,EAAA,IAAJA,CAAI,UAAJA,CAAI,wCAAqB,IAAA7C,EAAE,YAAW,CAAI,EAAGJ,IAAUN,CAAO,CAAA,EAAA,EAAgB,IAAAU,EAAE,gBAAgB,qBAD/FJ,EAAST,EAACG,CAAO,CAAA,EAAI,CAAC,4BAF3BsD,CAAC,EAaD,IAAAI,KAbAJ,EAAC,CAAA,IAaDI,GAAG,GAAA,IAAA7D,EACIF,EAAU,EAAII,GAAGA,EAAE,IAAG,CAAAkD,EAARlD,IAAC,YACpB4D,EAAGH,EAAAI,CAAA,EACFC,IADDF,CAAG,MACFE,CAAI,IADLF,CAAG,EAIH,IAAAG,IAJAH,EAAG,CAAA,IAIHG,EAAE,GAAA,IAAAjE,EACKE,CAAC,EAAC,MAASgE,GAAMA,EAAI,CAAAd,EAAVc,IAAI,KACpBC,EAAEC,GAAA,EACDC,IADDF,CAAE,MACDE,CAAG,IADJF,CAAE,UACDE,EAAG,SAAArE,EAAyCE,CAAC,EAAC,KAAK,oBAGnD,CAAA,IAAAW,EAAEqD,EAAMI,GAAYJ,CAAI,CAAA,CAAA,OAJzBC,CAAE,MAFJF,CAAE,WAHDD,EAAI,cAAAhE,EAA8DE,CAAC,EAAC,OAAK,EAAA,EAAA,yBACzEW,EAACb,EAACE,CAAC,EAAC,KAAK,CAAA,cAJZ2D,EAAG,IAlEJhC,CAAG,EAuFH,IAAA0C,KAvFA1C,EAAG,CAAA,EAwFF2C,EAAA/C,EADD8C,EAAG,EAMDE,GAAIhD,EALL+C,CAAA,OAKCC,EAAI,wBAEFzB,EAAAC,GAAAC,EAAAwB,EAAAzB,CAAA,EAAA,CAAA,IAAApC,EAAE,wBAAwB,CAAA,CAAA,wCAE1B,IAAAA,EAAE,2BAA0B,CAAI,MAAKb,EAAEO,CAAI,EAAC,QAAQ,CAAA,0CAEpD,IAAAM,EAAE,iCAAgC,CAAI,MAAKb,EAAEO,CAAI,EAAC,MAAO,KAAMI,KAAQJ,CAAI,CAAA,CAAA,CAAA,uBALxEF,CAAO,EAAAsE,EAAAC,EAAA,EAAA5E,EAEFG,CAAO,IAAK,UAASwE,EAAAE,GAAA,CAAA,EAAAF,EAAAG,GAAA,EAAA,MAH/BL,EAAI,EALL1B,EAAAyB,CAAA,EAAA,IAAAO,GAAApD,EAAA6C,EAAA,CAAA,cAgBC,IAAAQ,EAAAC,GAAA,EAAAC,EAAAzD,EAAAuD,EAAA,EAAA,EAAAjC,EAAAiC,CAAA,YAKC5F,GAAc,CAAA,CAAA,EALf+F,EAAA,QAAAH,EAAA,YAAAI,EAAA,CAGS/F,MAAW,MAAA,KAAA+F,CAAA,IAHpBjC,EAAAC,EAAA4B,CAAA,YADG5F,GAAc,GAAIC,MAAWsF,EAAAU,EAAA,QASjCC,GAAC3D,EAAAoD,GAAA,CAAA,OAADO,GAAC,EAAA,IAADA,EAAC,IAzBFf,EAAG,EA/GJxB,EAAArB,EAAA,EAbDqB,EAAAzB,CAAA,kCAMCiE,EAAA/D,EAAA,aAAAyB,CAAA,EAWCsC,EAAA3D,EAAA,aAAA4D,CAAA,IAwBC1D,GAAC,EAAA,uDAA6DlC,GAAUI,EAACP,CAAC,CAAA,GAAA,EAAA,aAAA,eAAA,8EAc1E8F,EAAAjD,EAAA,aAAAmD,CAAA,EAgCGvC,EAAAM,GAAA,IAAAxD,EAAAO,CAAI,EAAC,UAAQ,EAAA,EAAA,8BAsChBiE,EAAA,EAAA,8LACmM7E,KAASF,CAAC,CAAA,GAAA,EAAA,GAAA,eAAA,EAD7M+E,EAAA,SAAAxE,EAGUK,CAAO,YAvHP,IAAAQ,EAAE,OAAO,EAWR,IAAAA,EAAE,OAAO,MAyBnB3B,GAAQ,GAAI2B,EAAE,uBAAuB,EAGrC,IAAAA,EAAE,uBAAuB,EAA6C,IAAAA,EAAE,aAAa,EAC5C,IAAAA,EAAE,OAAO,EACT,IAAAA,EAAE,MAAM,EACR,IAAAA,EAAE,WAAW,EAAW,IAAAA,EAAE,mBAAmB,EAO3E,IAAAA,EAAE,gBAAgB,EA6BsC,IAAAA,EAAE,MAAM,EAI3E,IAAAC,KAAUP,CAAI,CAAA,EA0DyC,IAAAM,EAAE,kCAAkC,IA/I9FsE,EAAA,QAAA3D,EAAA,YAAA4D,EAAA,4BAWCD,EAAA,QAAAvD,EAAA,YAAAwD,EAAA,4BA4GCD,EAAA,QAAAX,EAESvD,EAAK,EA/HjBkC,EAAAC,EAAA9B,CAAA,yCAHM","names":["feature","headline","successPath","secondaryLabel","onsecondary","source","$.prop","$$props","f","$.derived","ctaColor","accentText","FEATURE_GROUP","perkGroups","lead","$.get","PREMIUM_PERK_GROUPS","g","billing","$.state","loading","CADENCES","plan","PLANS","savingPct","discountPct","titleOf","p","t","billingOf","price","formatUsd","goPro","$.set","analytics.trackPurchaseClick","res","result","div","root_1","button","$.child","div_1","$.sibling","button_1","div_2","p_1","h3","p_2","span","text_1","span_1","span_2","span_3","div_3","$.each","c","button_2","root_2","text_7","span_4","root_3","text_8","$.reset","$.template_effect","$0","$.set_text","$.append","$$anchor","div_4","span_5","span_6","text_9","p_3","span_7","$.first_child","fragment_1","div_5","div_6","fragment_2","span_8","ul","perk","li","root_6","svg","PERK_PARAMS","div_7","button_3","span_9","text_16","$$render","consequent_2","consequent_3","alternate","node_4","button_4","root_10","text_19","$.delegated","$$args","consequent_4","p_4","$.set_attribute","$1","$9"],"sources":["../../../../../../src/lib/components/UpgradeModal.svelte"],"sourcesContent":["<script lang=\"ts\">\n\timport { t } from '$lib/i18n';\n\timport * as analytics from '$lib/analytics';\n\timport { PERK_PARAMS, PREMIUM_PERK_GROUPS, type PremiumPerkGroup } from '$lib/pro-plan';\n\timport {\n\t\tPLANS,\n\t\tdiscountPct,\n\t\tformatUsd,\n\t\ttype Plan,\n\t\ttype PlanCadence\n\t} from '$lib/pricing';\n\n\ttype Feature =\n\t\t| 'generic'\n\t\t| 'breakdown'\n\t\t| 'breakdown-hype'\n\t\t| 'arena'\n\t\t| 'rate'\n\t\t| 'duel'\n\t\t| 'profile'\n\t\t| 'roleplay';\n\n\tinterface Props {\n\t\topen: boolean;\n\t\tonclose: () => void;\n\t\tfeature?: Feature | null;\n\t\t// RAT-418: optional value-prop headline for a specific gate (e.g. the arena\n\t\t// direct-chat button → \"unlimited direct chat\"). Falls back to the brand line.\n\t\theadline?: string | null;\n\t\t// Where Stripe returns after a successful subscribe. Defaults to the\n\t\t// homepage; callers on a specific surface (e.g. the rate result paywall)\n\t\t// pass their own path so the user lands back where they upgraded.\n\t\tsuccessPath?: string;\n\t\t// Optional free alternative under the CTA. A quota wall converts ~18% at\n\t\t// best, so without an escape hatch the other ~82% leave with nothing — on\n\t\t// the targeted-duel cap that means losing the duel intent entirely, which\n\t\t// is the exact thing the cap exists to preserve. Only pass this when the\n\t\t// alternative is genuinely free.\n\t\tsecondaryLabel?: string | null;\n\t\tonsecondary?: (() => void) | null;\n\t\t// Checkout attribution when one feature opens this modal from more than one\n\t\t// place (the /d create flow and the /d board, RAT-518). Defaults to\n\t\t// `upgrade_modal_<feature>`, what every caller has sent until now.\n\t\tsource?: string | null;\n\t}\n\n\tlet {\n\t\topen,\n\t\tonclose,\n\t\tfeature = null,\n\t\theadline = null,\n\t\tsuccessPath = '/?subscribed=true',\n\t\tsecondaryLabel = null,\n\t\tonsecondary = null,\n\t\tsource = null\n\t}: Props = $props();\n\n\tconst f: Feature = $derived(feature ?? 'generic');\n\n\tconst ctaColor: Record<Feature, string> = {\n\t\tgeneric: 'bg-coral hover:bg-coral-dark text-white',\n\t\tbreakdown: 'bg-coral hover:bg-coral-dark text-white',\n\t\t'breakdown-hype': 'bg-yellow hover:bg-yellow/80 text-bg',\n\t\tarena: 'bg-orange hover:bg-orange-dark text-white',\n\t\trate: 'bg-coral hover:bg-coral-dark text-white',\n\t\tduel: 'bg-blue hover:bg-blue/80 text-white',\n\t\t// RAT-461: /p owns the coral chrome accent, so the profile gate matches it.\n\t\tprofile: 'bg-coral hover:bg-coral-dark text-white',\n\t\t// Roleplay's own surface is purple end to end — the wall should look like\n\t\t// the place it interrupted.\n\t\troleplay: 'bg-purple hover:bg-purple-dark text-white'\n\t};\n\n\tconst accentText: Record<Feature, string> = {\n\t\tgeneric: 'text-coral',\n\t\tbreakdown: 'text-coral',\n\t\t'breakdown-hype': 'text-yellow',\n\t\tarena: 'text-orange',\n\t\trate: 'text-coral',\n\t\tduel: 'text-blue',\n\t\tprofile: 'text-coral',\n\t\troleplay: 'text-purple'\n\t};\n\n\t// Which perk group answers each gate. The module grid overflows the modal's\n\t// scroll area on a 375x812 phone (~63px hidden), so whichever group is last\n\t// is simply not read — and it used to be the one the user hit the wall on:\n\t// the duel gate pushed \"unlimited targeted challenges\" out of sight. Leading\n\t// with the gate's own module keeps the justification above the fold on every\n\t// wall; the rest keep their existing order underneath.\n\tconst FEATURE_GROUP: Partial<Record<Feature, PremiumPerkGroup['key']>> = {\n\t\tarena: 'arena',\n\t\tduel: 'duel',\n\t\trate: 'rate',\n\t\tprofile: 'profile',\n\t\tbreakdown: 'rate',\n\t\t'breakdown-hype': 'rate',\n\t\troleplay: 'roleplay'\n\t};\n\n\tconst perkGroups = $derived.by(() => {\n\t\tconst lead = FEATURE_GROUP[f];\n\t\tif (!lead) return PREMIUM_PERK_GROUPS;\n\t\treturn [\n\t\t\t...PREMIUM_PERK_GROUPS.filter((g) => g.key === lead),\n\t\t\t...PREMIUM_PERK_GROUPS.filter((g) => g.key !== lead)\n\t\t];\n\t});\n\n\t// Opens on monthly, deliberately: a $47.88 headline commitment is heavy for\n\t// an impulse surface (median signup->pay is ~15 minutes). The corner badges\n\t// still advertise the cheaper terms one tap away; /pricing leads with the\n\t// annual card for the considered path.\n\tlet billing = $state<PlanCadence>('monthly');\n\tlet loading = $state(false);\n\n\t// The same three terms /pricing sells, read from the same table — a modal\n\t// that offers a subset teaches a price the plan grid then contradicts.\n\t// Ascending commitment, so the cheapest headline sits where the eye lands\n\t// last and the saving badges read as a reason to move right.\n\tconst CADENCES: PlanCadence[] = ['monthly', 'quarterly', 'annual'];\n\tconst plan = $derived(PLANS.pro[billing]);\n\t// Derived, never hardcoded: the badge drifted from the real saving the last\n\t// two times the annual price moved. Floored so it can never overstate.\n\tconst savingPct = (c: PlanCadence) => discountPct(PLANS.pro[c]);\n\t// planTitle() / billingLine() word for word, in the page's language (RAT-573).\n\t// The English they return is unchanged; the price still comes from formatUsd.\n\tconst titleOf = (p: Plan) => (p.months === 1 ? t('1 month') : t('{n} months', { n: p.months }));\n\tfunction billingOf(p: Plan): string {\n\t\tif (p.cadence === 'monthly') return t('billed monthly · cancel anytime');\n\t\tconst price = formatUsd(p.total);\n\t\treturn p.cadence === 'annual'\n\t\t\t? t('billed {price} / year', { price })\n\t\t\t: t('billed {price} / {n} months', { price, n: p.months });\n\t}\n\n\tasync function goPro() {\n\t\tif (loading) return;\n\t\tloading = true;\n\t\tanalytics.trackPurchaseClick(0, `$${plan.total}`, `subscription_${billing}_${f}`);\n\t\ttry {\n\t\t\tconst res = await fetch('/api/checkout/subscription', {\n\t\t\t\tmethod: 'POST',\n\t\t\t\theaders: { 'Content-Type': 'application/json' },\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tplan: billing,\n\t\t\t\t\tsource: source ?? `upgrade_modal_${f}`,\n\t\t\t\t\tsuccessPath,\n\t\t\t\t\t// Stripe's back button returns to the page the modal was opened on\n\t\t\t\t\t// (the rate paywall, the arena wall, …), not to the homepage.\n\t\t\t\t\tcancelPath: `${location.pathname}?canceled=true`\n\t\t\t\t})\n\t\t\t});\n\t\t\tconst result = await res.json();\n\t\t\tif (res.ok && result.url) {\n\t\t\t\twindow.location.href = result.url;\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (result.alreadySubscribed) onclose();\n\t\t} catch {\n\t\t\t/* fall through */\n\t\t} finally {\n\t\t\tloading = false;\n\t\t}\n\t}\n</script>\n\n{#if open}\n\t<div\n\t\tclass=\"fixed inset-0 z-[100] flex items-stretch justify-center sm:items-center\"\n\t\trole=\"dialog\"\n\t\taria-modal=\"true\"\n\t>\n\t\t<!-- backdrop -->\n\t\t<button\n\t\t\tclass=\"absolute inset-0 bg-black/70 backdrop-blur-sm\"\n\t\t\tonclick={onclose}\n\t\t\taria-label={t('Close')}\n\t\t></button>\n\n\t\t<!-- modal -->\n\t\t<div\n\t\t\tclass=\"upgrade-card relative flex h-full max-h-full w-full flex-col border-0 sm:mx-4 sm:h-auto sm:max-h-[90dvh] sm:max-w-[400px] sm:rounded-3xl sm:border sm:border-border lg:max-w-[520px]\"\n\t\t>\n\t\t\t<!-- close -->\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-2 text-muted/50 transition-colors hover:bg-[#232326] hover:text-text\"\n\t\t\t\tonclick={onclose}\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\" />\n\t\t\t\t\t<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<!-- body -->\n\t\t\t<div class=\"upgrade-scroll min-h-0 flex-1 overflow-y-auto px-6 pt-8 pb-2 sm:px-7\">\n\t\t\t\t<!-- header — eyebrow + hero line. a gate-specific headline (e.g. \"see\n\t\t\t\t\t who liked you\") becomes the hero; otherwise the brand line. accent\n\t\t\t\t\t shifts with feature context. -->\n\t\t\t\t<p class=\"font-display text-[11px] font-bold tracking-[0.2em] {accentText[f]} uppercase\">\n\t\t\t\t\tratemyd premium\n\t\t\t\t</p>\n\t\t\t\t<h3 class=\"mt-2 font-display text-[30px] leading-[1.05] font-bold tracking-tight text-balance\">\n\t\t\t\t\t{headline ?? t('Everything, unlocked.')}\n\t\t\t\t</h3>\n\t\t\t\t<p class=\"mt-2 text-[13.5px] leading-relaxed text-muted\">\n\t\t\t\t\t{t('one membership across')} <span class=\"font-semibold text-text/80\">{t('ai roleplay')}</span>,\n\t\t\t\t\t<span class=\"font-semibold text-text/80\">{t('arena')}</span>,\n\t\t\t\t\t<span class=\"font-semibold text-text/80\">{t('duel')}</span>,\n\t\t\t\t\t<span class=\"font-semibold text-text/80\">{t('get rated')}</span> {t('and your profile.')}\n\t\t\t\t</p>\n\n\t\t\t\t<!-- billing toggle -->\n\t\t\t\t<div\n\t\t\t\t\tclass=\"mt-5 grid grid-cols-3 gap-1 rounded-xl border border-border bg-[#0a0a0b] p-1\"\n\t\t\t\t\trole=\"tablist\"\n\t\t\t\t\taria-label={t('billing period')}\n\t\t\t\t>\n\t\t\t\t\t{#each CADENCES as c (c)}\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\trole=\"tab\"\n\t\t\t\t\t\t\taria-selected={billing === c}\n\t\t\t\t\t\t\tonclick={() => (billing = c)}\n\t\t\t\t\t\t\tclass=\"relative cursor-pointer rounded-lg px-2 py-2 font-display text-[12.5px] font-semibold tracking-tight transition-all\n\t\t\t\t\t\t\t\t{billing === c ? 'bg-surface-2 text-text shadow-sm' : 'text-muted hover:text-text'}\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{titleOf(PLANS.pro[c])}\n\t\t\t\t\t\t\t{#if savingPct(c) > 0}\n\t\t\t\t\t\t\t\t<!-- Corner-pinned rather than stacked under the label: the badge\n\t\t\t\t\t\t\t\t\t used to add its own line to every tab, so the toggle was three\n\t\t\t\t\t\t\t\t\t rows tall for one row of information. -->\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tclass=\"absolute -top-1.5 -right-0.5 rounded-full bg-green px-1 py-px text-[8.5px] leading-tight font-extrabold text-bg\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t−{savingPct(c)}%\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t{/if}\n\t\t\t\t\t\t</button>\n\t\t\t\t\t{/each}\n\t\t\t\t</div>\n\n\t\t\t\t<!-- price -->\n\t\t\t\t<div class=\"mt-5 flex items-baseline gap-2.5\">\n\t\t\t\t\t<span class=\"font-display text-[46px] leading-none font-bold tracking-tight tabular-nums\">\n\t\t\t\t\t\t${plan.perMonth}<span class=\"text-base font-semibold text-muted\"> {t('/ mo')}</span>\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t\t<p class=\"mt-1.5 text-[12px] text-muted/60\">\n\t\t\t\t\t{billingOf(plan)}\n\t\t\t\t\t{#if savingPct(billing) > 0}\n\t\t\t\t\t\t· <span class=\"text-green\">{t('save {n}%', { n: savingPct(billing) })}</span> · {t('cancel anytime')}\n\t\t\t\t\t{/if}\n\t\t\t\t</p>\n\n\t\t\t\t<hr class=\"my-5 border-0 border-t border-border\" />\n\n\t\t\t\t<!-- what premium unlocks — compact two-column module grid: the module\n\t\t\t\t\t label (with its accent dot) sits left, its perks right. single-perk\n\t\t\t\t\t modules cost one row, not two, and every check is unambiguous\n\t\t\t\t\t because its module is named beside it. copy/values from pro-plan.ts. -->\n\t\t\t\t<div class=\"grid grid-cols-[74px_1fr] items-start gap-x-3.5 gap-y-3.5\">\n\t\t\t\t\t{#each perkGroups as g (g.key)}\n\t\t\t\t\t\t<div class=\"flex items-center gap-2 pt-0.5 text-[10px] font-bold tracking-[0.13em] text-muted uppercase\">\n\t\t\t\t\t\t\t<span class=\"h-1.5 w-1.5 shrink-0 rounded-full\" style=\"background:{g.color}\"></span>\n\t\t\t\t\t\t\t{t(g.label)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<ul class=\"flex flex-col gap-2\">\n\t\t\t\t\t\t\t{#each g.perks as perk (perk)}\n\t\t\t\t\t\t\t\t<li class=\"flex items-center gap-2.5 text-[14px] text-text/90\">\n\t\t\t\t\t\t\t\t\t<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke={g.color} stroke-width=\"2.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"h-[15px] w-[15px] shrink-0\" aria-hidden=\"true\">\n\t\t\t\t\t\t\t\t\t\t<path d=\"M20 6 9 17l-5-5\" />\n\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t{t(perk, PERK_PARAMS[perk])}\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t{/each}\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t{/each}\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<!-- footer -->\n\t\t\t<div class=\"shrink-0 px-6 pt-4 pb-7 sm:px-7 sm:pb-8\">\n\t\t\t\t<button\n\t\t\t\t\tclass=\"cta-shine relative block w-full cursor-pointer overflow-hidden rounded-2xl py-4 font-display text-[15.5px] font-bold tracking-tight transition-all active:scale-[0.99] disabled:opacity-50 {ctaColor[f]}\"\n\t\t\t\t\tonclick={goPro}\n\t\t\t\t\tdisabled={loading}\n\t\t\t\t>\n\t\t\t\t\t<span class=\"relative z-10\">\n\t\t\t\t\t\t{#if loading}\n\t\t\t\t\t\t\t{t('redirecting to stripe…')}\n\t\t\t\t\t\t{:else if billing === 'monthly'}\n\t\t\t\t\t\t\t{t('Go Premium — ${price}/mo', { price: plan.perMonth })}\n\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t{t('Go Premium — ${price} / {term}', { price: plan.total, term: titleOf(plan) })}\n\t\t\t\t\t\t{/if}\n\t\t\t\t\t</span>\n\t\t\t\t</button>\n\t\t\t\t{#if secondaryLabel && onsecondary}\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclass=\"mt-2.5 block w-full cursor-pointer rounded-2xl border border-border py-3.5 font-display text-[14px] font-semibold tracking-tight text-text/80 transition-colors hover:bg-surface-2 hover:text-text\"\n\t\t\t\t\t\tonclick={onsecondary}\n\t\t\t\t\t>\n\t\t\t\t\t\t{secondaryLabel}\n\t\t\t\t\t</button>\n\t\t\t\t{/if}\n\t\t\t\t<p class=\"mt-3 text-center text-[11.5px] text-muted/60\">{t('secure checkout · cancel anytime')}</p>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n{/if}\n\n<style>\n\t/* Subtle depth, not glow: a lifted card with a faint coral sheen up top.\n\t   Kept a scoped block for the gradient + the CTA shimmer — everything else\n\t   stays Tailwind. */\n\t.upgrade-card {\n\t\tbackground: linear-gradient(180deg, #141416 0%, #0f0f10 100%);\n\t\tbox-shadow: 0 1px 0 rgba(255, 255, 255, 0.04) inset, 0 40px 80px -20px rgba(0, 0, 0, 0.8);\n\t}\n\t@media (min-width: 640px) {\n\t\t.upgrade-card::before {\n\t\t\tcontent: '';\n\t\t\tposition: absolute;\n\t\t\tinset: 0 0 auto 0;\n\t\t\theight: 130px;\n\t\t\tborder-radius: 24px 24px 0 0;\n\t\t\tbackground: radial-gradient(120% 100% at 50% 0%, rgba(255, 77, 109, 0.08), transparent 70%);\n\t\t\tpointer-events: none;\n\t\t}\n\t}\n\n\t/* CTA shimmer — a light sweep every few seconds. Elegant, not arcade. */\n\t.cta-shine::after {\n\t\tcontent: '';\n\t\tposition: absolute;\n\t\tinset: 0;\n\t\tbackground: linear-gradient(\n\t\t\t105deg,\n\t\t\ttransparent 30%,\n\t\t\trgba(255, 255, 255, 0.38) 48%,\n\t\t\ttransparent 66%\n\t\t);\n\t\ttransform: translateX(-120%);\n\t\tanimation: cta-shimmer 3.6s ease-in-out infinite;\n\t\tpointer-events: none;\n\t}\n\t@keyframes cta-shimmer {\n\t\t0% {\n\t\t\ttransform: translateX(-120%);\n\t\t}\n\t\t55%,\n\t\t100% {\n\t\t\ttransform: translateX(120%);\n\t\t}\n\t}\n\t@media (prefers-reduced-motion: reduce) {\n\t\t.cta-shine::after {\n\t\t\tanimation: none;\n\t\t\tdisplay: none;\n\t\t}\n\t}\n\n\t/* Thin, unobtrusive scrollbar on the plan list — the default desktop bar\n\t   read as a heavy grey slab down the card's edge. */\n\t.upgrade-scroll {\n\t\tscrollbar-width: thin;\n\t\tscrollbar-color: var(--color-border) transparent;\n\t}\n\t.upgrade-scroll::-webkit-scrollbar {\n\t\twidth: 5px;\n\t}\n\t.upgrade-scroll::-webkit-scrollbar-track {\n\t\tbackground: transparent;\n\t}\n\t.upgrade-scroll::-webkit-scrollbar-thumb {\n\t\tbackground: var(--color-border);\n\t\tborder-radius: 3px;\n\t}\n</style>\n"],"version":3}