{"release_id":"01a1998d-9802-0000-b0f7-33a23307f4ed","chunk_id":"01a1998d-98ff-7fb2-b5f7-10d764a9151e","file":"BuXxt_wZ.mjs","mappings":"qTAwCO,MAAMA,EAAqD,CACjE,IAAK,CACJ,QAAS,CACR,KAAM,MACN,QAAS,UACT,OAAQ,EACR,MAAO,MACP,SAAU,MACV,OAAQ,0BAAA,EAET,UAAW,CACV,KAAM,MACN,QAAS,YACT,OAAQ,EACR,MAAO,MACP,SAAU,KACV,OAAQ,4BAAA,EAET,OAAQ,CACP,KAAM,MACN,QAAS,SACT,OAAQ,GACR,MAAO,MACP,SAAU,KACV,OAAQ,yBAAA,CACT,EAED,KAAM,CACL,QAAS,CACR,KAAM,OACN,QAAS,UACT,OAAQ,EACR,MAAO,MACP,SAAU,MACV,OAAQ,2BAAA,EAET,UAAW,CACV,KAAM,OACN,QAAS,YACT,OAAQ,EACR,MAAO,MACP,SAAU,MACV,OAAQ,6BAAA,EAET,OAAQ,CACP,KAAM,OACN,QAAS,SACT,OAAQ,GACR,MAAO,MACP,SAAU,KACV,OAAQ,0BAAA,CACT,CAEF,EAGaC,EAA4B,CAAC,SAAU,YAAa,SAAS,EAOnE,SAASC,EAAUC,EAAwB,CACjD,MAAO,IAAIA,EAAO,QAAQ,CAAC,CAAC,EAC7B,CAKO,SAASC,EAAYC,EAAoB,CAC/C,MAAMC,EAASN,EAAMK,EAAK,IAAI,EAAE,QAAQ,SACxC,OAAIA,EAAK,UAAYC,EAAe,EAC7B,KAAK,OAAO,EAAID,EAAK,SAAWC,GAAU,GAAG,CACrD,CAkBO,MAAMC,EAAoBP,EAAM,IAAI,QAAQ,MAGtCQ,EAAmBR,EAAM,IAAI,OAAO,MAGbA,EAAM,IAAI,OAAO,SAG9C,MAAMS,EAAsB,KAMtBC,EAAoBR,EAAUK,CAAiB,EAG5BL,EAAUM,CAAgB,EAGnD,MAAMG,EAAqBX,EAAM,IAAI,OAAO,SAGtCY,EAAsBZ,EAAM,KAAK,OAAO,SAMxCa,EAAyB,KAAK,KAAKN,EAAoBE,CAAmB","names":["PLANS","PLAN_ORDER","formatUsd","amount","discountPct","plan","anchor","PRO_MONTHLY_PRICE","PRO_ANNUAL_PRICE","SINGLE_UNLOCK_PRICE","PRO_MONTHLY_LABEL","PRO_FROM_PER_MONTH","PLUS_FROM_PER_MONTH","PRO_BREAK_EVEN_UNLOCKS"],"sources":["../../../../../../src/lib/pricing.ts"],"sourcesContent":["/**\n * Canonical subscription / unlock prices for everything the UI displays.\n *\n * SINGLE SOURCE OF TRUTH for displayed prices. The real charge always comes from\n * the live Stripe price objects referenced by the `envKey` on each plan below\n * (resolved in src/routes/api/checkout/subscription/+server.ts). These constants\n * must mirror those Stripe prices — when a Stripe price changes, update it here\n * and nowhere else. Never hardcode a price literal in a component or in copy;\n * import from here.\n *\n * Two tiers, three cadences each:\n *   pro  — the everyday plan, replaces the old single-tier Pro\n *   plus — sold only to users who already hold an active pro sub (the drawer\n *          upsell); adds a bigger token stipend and a purchase bonus\n *\n * History: monthly was $20, then $9.99, cut to $4.99 (RAT-199), back to $9.99,\n * now $13.99 with the 3-tier ladder. Stripe price objects are immutable, so each\n * change means a NEW price object on the same product with the env var\n * re-pointed at it — which is also what grandfathers existing subscribers onto\n * the amount they signed up at.\n */\n\nexport type PlanTier = 'pro' | 'plus';\nexport type PlanCadence = 'monthly' | 'quarterly' | 'annual';\n\nexport interface Plan {\n\ttier: PlanTier;\n\tcadence: PlanCadence;\n\t/** Billing period length in months — also the stipend multiplier. */\n\tmonths: number;\n\t/** What Stripe actually charges, per billing period, USD. */\n\ttotal: number;\n\t/** The headline number on the card: total / months, USD. */\n\tperMonth: number;\n\t/** Name of the private env var holding the Stripe price id. */\n\tenvKey: string;\n}\n\n/** The monthly cadence is the strike-through anchor every discount is measured\n *  against, so it must stay first in each tier's list. */\nexport const PLANS: Record<PlanTier, Record<PlanCadence, Plan>> = {\n\tpro: {\n\t\tmonthly: {\n\t\t\ttier: 'pro',\n\t\t\tcadence: 'monthly',\n\t\t\tmonths: 1,\n\t\t\ttotal: 13.99,\n\t\t\tperMonth: 13.99,\n\t\t\tenvKey: 'STRIPE_PRICE_PRO_MONTHLY'\n\t\t},\n\t\tquarterly: {\n\t\t\ttier: 'pro',\n\t\t\tcadence: 'quarterly',\n\t\t\tmonths: 3,\n\t\t\ttotal: 26.97,\n\t\t\tperMonth: 8.99,\n\t\t\tenvKey: 'STRIPE_PRICE_PRO_QUARTERLY'\n\t\t},\n\t\tannual: {\n\t\t\ttier: 'pro',\n\t\t\tcadence: 'annual',\n\t\t\tmonths: 12,\n\t\t\ttotal: 47.88,\n\t\t\tperMonth: 3.99,\n\t\t\tenvKey: 'STRIPE_PRICE_PRO_ANNUAL'\n\t\t}\n\t},\n\tplus: {\n\t\tmonthly: {\n\t\t\ttier: 'plus',\n\t\t\tcadence: 'monthly',\n\t\t\tmonths: 1,\n\t\t\ttotal: 22.38,\n\t\t\tperMonth: 22.38,\n\t\t\tenvKey: 'STRIPE_PRICE_PLUS_MONTHLY'\n\t\t},\n\t\tquarterly: {\n\t\t\ttier: 'plus',\n\t\t\tcadence: 'quarterly',\n\t\t\tmonths: 3,\n\t\t\ttotal: 43.14,\n\t\t\tperMonth: 14.38,\n\t\t\tenvKey: 'STRIPE_PRICE_PLUS_QUARTERLY'\n\t\t},\n\t\tannual: {\n\t\t\ttier: 'plus',\n\t\t\tcadence: 'annual',\n\t\t\tmonths: 12,\n\t\t\ttotal: 76.56,\n\t\t\tperMonth: 6.38,\n\t\t\tenvKey: 'STRIPE_PRICE_PLUS_ANNUAL'\n\t\t}\n\t}\n};\n\n/** Card order: best value first, so the carousel opens on the anchor we want. */\nexport const PLAN_ORDER: PlanCadence[] = ['annual', 'quarterly', 'monthly'];\n\nexport function getPlan(tier: PlanTier, cadence: PlanCadence): Plan {\n\treturn PLANS[tier][cadence];\n}\n\n/** \"$13.99\" */\nexport function formatUsd(amount: number): string {\n\treturn `$${amount.toFixed(2)}`;\n}\n\n/** Discount vs. the same tier's monthly cadence, as a whole percent.\n *  ALWAYS floored — a displayed discount that overstates the real saving is a\n *  consumer-protection problem, an understated one is merely conservative. */\nexport function discountPct(plan: Plan): number {\n\tconst anchor = PLANS[plan.tier].monthly.perMonth;\n\tif (plan.perMonth >= anchor) return 0;\n\treturn Math.floor((1 - plan.perMonth / anchor) * 100);\n}\n\n/** The billing line under the price (\"billed $47.88 / year\"). */\nexport function billingLine(plan: Plan): string {\n\tif (plan.cadence === 'monthly') return 'billed monthly · cancel anytime';\n\tconst per = plan.cadence === 'annual' ? 'year' : `${plan.months} months`;\n\treturn `billed ${formatUsd(plan.total)} / ${per}`;\n}\n\n/** Card heading (\"12 months\"). */\nexport function planTitle(plan: Plan): string {\n\treturn plan.months === 1 ? '1 month' : `${plan.months} months`;\n}\n\n// ── legacy flat exports ──────────────────────────────────────────────────────\n// Kept so existing importers keep working; all derive from PLANS above.\n\n/** Pro monthly subscription, USD. */\nexport const PRO_MONTHLY_PRICE = PLANS.pro.monthly.total;\n\n/** Pro annual subscription total, USD/yr. */\nexport const PRO_ANNUAL_PRICE = PLANS.pro.annual.total;\n\n/** Annual plan expressed per month, USD ($47.88 / 12 = $3.99). */\nexport const PRO_ANNUAL_PER_MONTH = PLANS.pro.annual.perMonth;\n\n/** One breakdown unlock, USD. Mirrors STRIPE_PRICE_SINGLE_UNLOCK. */\nexport const SINGLE_UNLOCK_PRICE = 1.99;\n\n/** Pro monthly in integer cents — last-resort fallback for server revenue records. */\nexport const PRO_MONTHLY_PRICE_CENTS = Math.round(PRO_MONTHLY_PRICE * 100);\n\n/** \"$13.99\" — formatted monthly price for copy. */\nexport const PRO_MONTHLY_LABEL = formatUsd(PRO_MONTHLY_PRICE);\n\n/** \"$47.88\" — formatted annual total for copy. */\nexport const PRO_ANNUAL_LABEL = formatUsd(PRO_ANNUAL_PRICE);\n\n/** The cheapest per-month entry across all pro cadences — the \"from $X/mo\" hook. */\nexport const PRO_FROM_PER_MONTH = PLANS.pro.annual.perMonth;\n\n/** Same, for the Plus tier. */\nexport const PLUS_FROM_PER_MONTH = PLANS.plus.annual.perMonth;\n\n/**\n * How many single unlocks cost as much as one month of Pro — the break-even the\n * upsell copy quotes (\"break-even at N unlocks\"). At $13.99 / $1.99 → 8.\n */\nexport const PRO_BREAK_EVEN_UNLOCKS = Math.ceil(PRO_MONTHLY_PRICE / SINGLE_UNLOCK_PRICE);\n"],"version":3}