import * as m from '$lib/paraglide/messages'; /** * Supporter configuration. * * The frontend stays fully static and open source; the only server piece is the * tiny `drizzli-paywall` verify API (a separate, self-hosted repo). Point the * build at your deployment with the `VITE_SUPPORTER_*` env vars (e.g. in a * `.env` file), otherwise the sensible drizz.li defaults are used. The * pre-rename `VITE_PAYWALL_*` names still work. */ const env = import.meta.env as Record; const stripTrailingSlash = (url: string): string => url.replace(/\/+$/, ''); /** * Master switch for the supporter features. While this is `false` everything * supporter-related is parked: the nav entry points are hidden, no key is * verified, and every gated page renders as if the visitor were a supporter. * The logic stays in place — set `VITE_SUPPORTER_ENABLED=true` (or default this * to `true`) to bring it back. */ export const SUPPORTER_ENABLED = env.VITE_SUPPORTER_ENABLED === 'true'; /** Base URL of the self-hosted verify API (drizzli-paywall). */ export const SUPPORTER_API_BASE = stripTrailingSlash( env.VITE_SUPPORTER_API_BASE ?? env.VITE_PAYWALL_API_BASE ?? 'https://support.drizz.li' ); /** Where prospective supporters go to sign up (the verify API's signup form). */ export const SIGNUP_URL = env.VITE_SUPPORTER_SIGNUP_URL ?? env.VITE_PAYWALL_SIGNUP_URL ?? `${SUPPORTER_API_BASE}/`; // ─── Location-based pricing ────────────────────────────────────────────────── // The signup form charges 3 in the visitor's currency (EUR / USD / CHF), picked // from their location. Mirror that here so the supporter copy matches. Detection // is timezone/locale based (no network geo lookup) and guarded for SSR. const CURRENCY_SYMBOL: Record = { EUR: '€', USD: '$', CHF: 'CHF' }; const US_ZONES = /^America\/(New_York|Detroit|Chicago|Denver|Boise|Phoenix|Los_Angeles|Anchorage|Adak|Juneau|Sitka|Nome|Yakutat|Menominee|Indiana|Kentucky|North_Dakota)/; export type SupporterCurrency = 'EUR' | 'USD' | 'CHF'; export function detectCurrency(): SupporterCurrency { if (typeof Intl === 'undefined') return 'EUR'; let tz = ''; try { tz = Intl.DateTimeFormat().resolvedOptions().timeZone || ''; } catch { /* ignore */ } if (/Zurich|Vaduz/.test(tz)) return 'CHF'; let region = ''; try { if (typeof navigator !== 'undefined') { region = new Intl.Locale(navigator.language).maximize().region || ''; } } catch { /* ignore */ } if (region === 'CH') return 'CHF'; if (region === 'US' || US_ZONES.test(tz)) return 'USD'; return 'EUR'; } /** Display price for the locked panel, e.g. "€3 / month" or "CHF 3 / Monat". */ export function getSupporterPrice(): string { // VITE_PREMIUM_PRICE is the pre-rename name, still honoured so an existing // deployment's .env keeps working. const configured = env.VITE_SUPPORTER_PRICE ?? env.VITE_PREMIUM_PRICE; if (configured) return configured; const currency = detectCurrency(); const symbol = CURRENCY_SYMBOL[currency]; const amount = currency === 'CHF' ? `${symbol} 3` : `${symbol}3`; return m.supporter_price_per_month({ amount }); } /** Short, human list of what supporting unlocks (shown on the locked panel). * A function, not a constant: the copy has to follow the active locale. */ export const SUPPORTER_PERKS = (): string[] => [ m.supporter_perk_historical(), m.supporter_perk_seasonal(), m.supporter_perk_future() ];