{"release_id":"01a1998d-9802-0000-b0f7-33a23307f4ed","chunk_id":"01a1998d-9902-7e12-a136-6aca4383da53","file":"C1f-42bR.mjs","mappings":"wVA0CO,MAAMA,EAAiE,CAC7E,IAAK,CAAE,QAAS,GAAI,UAAW,GAAI,OAAQ,EAAA,EAC3C,KAAM,CAAE,QAAS,IAAK,UAAW,IAAK,OAAQ,GAAA,CAC/C,EAMO,SAASC,EAAgBC,EAAgBC,EAA8B,CAC7E,OAAOH,EAAgBE,CAAI,EAAEC,CAAO,CACrC,CAKO,SAASC,EAAiBF,EAAgBC,EAAsBE,EAAwB,CAC9F,OAAOJ,EAAgBC,EAAMC,CAAO,EAAIE,CACzC,CAOO,MAAMC,EAAwB,IAGxBC,EAAyB,IAAI,KAAK,MAAMD,EAAwB,GAAG,CAAC,IAI1E,SAASE,EAAcC,EAAgBC,EAAyB,CACtE,OAAOA,EAAS,KAAK,MAAMD,GAAU,EAAIH,EAAsB,EAAIG,CACpE,CAIO,MAAME,EAAqBX,EAAgB,IAAI,QAGzCY,EAAsBZ,EAAgB,KAAK,QAc3Ca,EAA0C,CACtD,CACC,IAAK,WACL,MAAOC,EAAI,aAAa,EACxB,MAAO,UACP,MAAO,CACNA,EAAI,yCAAyC,EAC7CA,EAAI,+CAA+C,EACnDA,EAAI,wBAAwB,EAC5BA,EAAI,kCAAkC,CAAA,CACvC,EAED,CACC,IAAK,QACL,MAAOA,EAAI,OAAO,EAClB,MAAO,UACP,MAAO,CACNA,EAAI,kBAAkB,EACtBA,EAAI,4BAA4B,EAChCA,EAAI,uBAAuB,EAC3BA,EAAI,sBAAsB,CAAA,CAC3B,EAED,CAIC,IAAK,UACL,MAAOA,EAAI,SAAS,EACpB,MAAO,UACP,MAAO,CAACA,EAAI,6BAA6B,CAAC,CAAA,EAE3C,CACC,IAAK,OACL,MAAOA,EAAI,WAAW,EACtB,MAAO,UACP,MAAO,CAACA,EAAI,0BAA0B,CAAC,CAAA,EAExC,CACC,IAAK,OACL,MAAOA,EAAI,MAAM,EACjB,MAAO,UAKP,MAAO,CAACA,EAAI,iBAAiB,EAAGA,EAAI,iBAAiB,EAAGA,EAAI,+BAA+B,CAAC,CAAA,EAE7F,CACC,IAAK,SACL,MAAOA,EAAI,QAAQ,EACnB,MAAO,UACP,MAAO,CAACA,EAAI,wBAAwB,CAAC,CAAA,CAEvC,EAQaC,EAAuB,CACnCD,EAAI,qCAAqC,EACzCA,EAAI,4CAA4C,EAChDA,EAAI,uBAAuB,CAC5B,EAIaE,EAA+D,CAC3E,yBAA0B,CAAE,EAAGL,CAAA,EAC/B,sCAAuC,CAAE,EAAGC,CAAA,EAC5C,6CAA8C,CAAE,MAAOL,CAAA,CACxD","names":["MONTHLY_STIPEND","stipendPerMonth","tier","cadence","stipendPerPeriod","months","PLUS_TOKEN_BONUS_RATE","PLUS_TOKEN_BONUS_LABEL","withPlusBonus","tokens","isPlus","PRO_MONTHLY_TOKENS","PLUS_MONTHLY_TOKENS","PREMIUM_PERK_GROUPS","msg","PLUS_PERKS","PERK_PARAMS"],"sources":["../../../../../../src/lib/pro-plan.ts"],"sourcesContent":["/** Subscription extras that aren't prices: the monthly token stipend, the Plus\n *  purchase bonus and the perk bullets — shared by the upgrade modal, the\n *  homepage plan cards, the drawer and the subscription webhook so they never\n *  drift. Prices live in $lib/pricing (the single source of truth that mirrors\n *  Stripe) — never duplicate a price here. Client-safe (constants only). */\n\nimport type { PlanCadence, PlanTier } from '$lib/pricing';\nimport { msg } from '$lib/i18n/mark';\n\n/**\n * Tokens granted per month of subscription, by tier and cadence.\n *\n * Flat per tier: the allowance is the same 50 (Plus: 150) on every cadence, so\n * \"50 tokens every month\" holds no matter which card was bought and Plus stays\n * exactly 3x Premium everywhere. Committing to a longer term buys a lower\n * price, never a smaller allowance.\n *\n * The consequence is deliberate and worth stating plainly, because it inverts\n * the usual rule that the stipend must price WORSE than the packs:\n *\n *   cheapest token otherwise buyable: starter 10.0¢, whale pack 12.5¢.\n *\n *              per month      implied ¢/token\n *   pro  1mo   $13.99 /  50       28.0¢   above every pack\n *        3mo    $8.99 /  50       18.0¢   above the whale, under the dipper\n *        12mo   $3.99 /  50        8.0¢   under the cheapest token sold\n *   plus 1mo   $22.38 / 150       14.9¢   above the whale\n *        3mo   $14.38 / 150        9.6¢   under the cheapest token sold\n *        12mo   $6.38 / 150        4.3¢   under by 2.3x\n *\n * So on the longer cadences the subscription IS the cheapest token source in\n * the system, and the packs are dominated for anyone willing to prepay. That\n * is a priced-in trade, not an oversight: the annual seat is sold on committed\n * cash up front, and the whale rail is expected to move to Plus (which still\n * carries the pack bonus on top). Revisit if token-pack revenue drops after\n * launch — the lever is the annual PRICE, not the allowance.\n *\n * Still enforced: the monthly allowance must stay below GIFT_SUB_COST\n * (server-side, 110), or a subscription funds more gifted months than it costs.\n * At 50 a Premium seat yields 0.45 gifted months per month; at 150 a Plus seat\n * yields 1.36 — which is why gifting is capped there rather than here.\n */\nexport const MONTHLY_STIPEND: Record<PlanTier, Record<PlanCadence, number>> = {\n\tpro: { monthly: 50, quarterly: 50, annual: 50 },\n\tplus: { monthly: 150, quarterly: 150, annual: 150 }\n};\n\n/** Tokens dripped on each grant for a given plan. The allowance is monthly on\n *  every cadence — an annual sub gets 12 monthly drips, not one lump sum. A\n *  lump sum would hand an annual subscriber a year of tokens at a price that\n *  undercuts every pack, and removes the only reason to come back mid-term. */\nexport function stipendPerMonth(tier: PlanTier, cadence: PlanCadence): number {\n\treturn MONTHLY_STIPEND[tier][cadence];\n}\n\n/** Total tokens included across the whole billing period — the number the card\n *  advertises (\"180 tokens included\"), because the absolute figure is what the\n *  eye compares between cadences. */\nexport function stipendPerPeriod(tier: PlanTier, cadence: PlanCadence, months: number): number {\n\treturn stipendPerMonth(tier, cadence) * months;\n}\n\n/** Extra tokens Plus members get on every pack purchase, as a fraction.\n *  Framed to users as a bonus (\"+25% tokens\"), never as a discount: the price\n *  stays put, the token count goes up. Economically the same as 20% off, but it\n *  never teaches anyone that tokens are supposed to be cheaper, and it needs no\n *  second set of Stripe price objects. */\nexport const PLUS_TOKEN_BONUS_RATE = 0.25;\n\n/** \"+25%\" */\nexport const PLUS_TOKEN_BONUS_LABEL = `+${Math.round(PLUS_TOKEN_BONUS_RATE * 100)}%`;\n\n/** Applies the Plus bonus to a pack's token count (rounded down — never hand\n *  out a fractional token). */\nexport function withPlusBonus(tokens: number, isPlus: boolean): number {\n\treturn isPlus ? Math.floor(tokens * (1 + PLUS_TOKEN_BONUS_RATE)) : tokens;\n}\n\n/** Tokens granted to a Pro subscriber on subscribe and on every monthly drip.\n *  Kept for the copy that quotes a single figure on the monthly plan. */\nexport const PRO_MONTHLY_TOKENS = MONTHLY_STIPEND.pro.monthly;\n\n/** Same for Plus — the headline on the drawer upsell. */\nexport const PLUS_MONTHLY_TOKENS = MONTHLY_STIPEND.plus.monthly;\n\n/** Premium perks grouped by module — the pricing page, the homepage pricing\n *  section (both via PremiumPlans.svelte) and the upgrade modal all render from\n *  this one array so the value story never drifts. Colors follow the site's\n *  module accent language (activeMode.ts). Values audited from the actual\n *  gates — don't add a perk here without a real gate behind it. */\nexport interface PremiumPerkGroup {\n\tkey: 'arena' | 'profile' | 'rate' | 'duel' | 'tokens' | 'roleplay';\n\tlabel: string;\n\tcolor: string;\n\tperks: string[];\n}\n\nexport const PREMIUM_PERK_GROUPS: PremiumPerkGroup[] = [\n\t{\n\t\tkey: 'roleplay',\n\t\tlabel: msg('ai roleplay'),\n\t\tcolor: '#8b5cf6',\n\t\tperks: [\n\t\t\tmsg('unlimited messages with every character'),\n\t\t\tmsg('send them photos, they react to what they see'),\n\t\t\tmsg('unlimited group scenes'),\n\t\t\tmsg('hear them — voice on every reply')\n\t\t]\n\t},\n\t{\n\t\tkey: 'arena',\n\t\tlabel: msg('arena'),\n\t\tcolor: '#f97316',\n\t\tperks: [\n\t\t\tmsg('unlimited swipes'),\n\t\t\tmsg('see everyone who liked you'),\n\t\t\tmsg('3 super emotes / week'),\n\t\t\tmsg('unlimited match chat')\n\t\t]\n\t},\n\t{\n\t\t// RAT-461: was a 5-token unlock that charged subscribers too. Now a Pro\n\t\t// right, gated server-side in getViewerStats() — a free profile owner\n\t\t// sees the count and no names.\n\t\tkey: 'profile',\n\t\tlabel: msg('profile'),\n\t\tcolor: '#ff4d6d',\n\t\tperks: [msg('see who viewed your profile')]\n\t},\n\t{\n\t\tkey: 'rate',\n\t\tlabel: msg('get rated'),\n\t\tcolor: '#ff4d6d',\n\t\tperks: [msg('every breakdown, no blur')]\n\t},\n\t{\n\t\tkey: 'duel',\n\t\tlabel: msg('duel'),\n\t\tcolor: '#3b82f6',\n\t\t// \"unlimited duels\" is gated in getDuelCreateQuota() — free accounts\n\t\t// create DUEL_FREE_CREATES, lifetime (joining is always free).\n\t\t// \"unlimited targeted challenges\" is gated in getTargetedDuelQuota() —\n\t\t// free accounts get TARGETED_DUEL_FREE_DAILY per rolling 24h.\n\t\tperks: [msg('unlimited duels'), msg('pick your judge'), msg('unlimited targeted challenges')]\n\t},\n\t{\n\t\tkey: 'tokens',\n\t\tlabel: msg('tokens'),\n\t\tcolor: '#f5b50a',\n\t\tperks: [msg('{n} tokens every month')]\n\t}\n];\n\n/** What Plus adds ON TOP of everything in PREMIUM_PERK_GROUPS. Same audit rule:\n *  only gates that actually exist. The status/visibility perks this tier is\n *  eventually meant to carry (badge, boost multiplier, queue priority, bigger\n *  tip presets) are deliberately absent until each has a real gate behind it —\n *  they are the margin-safe way to grow this tier later, since they cost no\n *  token margin at all. */\nexport const PLUS_PERKS: string[] = [\n\tmsg('{n} tokens every month — 3× premium'),\n\tmsg('{bonus} bonus tokens on every pack you buy'),\n\tmsg('everything in premium')\n];\n\n/** The numbers in the perks above. A perk is the t() source it is shown\n *  through, so its numbers travel beside it: `t(perk, PERK_PARAMS[perk])`. */\nexport const PERK_PARAMS: Record<string, Record<string, string | number>> = {\n\t'{n} tokens every month': { n: PRO_MONTHLY_TOKENS },\n\t'{n} tokens every month — 3× premium': { n: PLUS_MONTHLY_TOKENS },\n\t'{bonus} bonus tokens on every pack you buy': { bonus: PLUS_TOKEN_BONUS_LABEL }\n};\n"],"version":3}