- Support this open-source weather project and unlock the extras from {PREMIUM_PRICE}.
+ Support this open-source weather project and unlock the extras from {price}.
diff --git a/src/lib/paywall/UnlockDialog.svelte b/src/lib/paywall/UnlockDialog.svelte
index 6f71c93..8e8e7ba 100644
--- a/src/lib/paywall/UnlockDialog.svelte
+++ b/src/lib/paywall/UnlockDialog.svelte
@@ -6,7 +6,7 @@
import { Input } from '$lib/components/ui/input';
import { Label } from '$lib/components/ui/label';
- import { PREMIUM_PRICE, SIGNUP_URL } from './config';
+ import { SIGNUP_URL, getPremiumPrice } from './config';
import { clearLicense, isPremium, premiumState, storedLicenseKey, verifyKey } from './premium';
interface Props {
@@ -15,6 +15,8 @@
let { open = $bindable(false) }: Props = $props();
+ const price = getPremiumPrice();
+
// Prefill with the stored key so an existing subscriber sees their key.
let keyInput = $state('');
let submitting = $state(false);
@@ -129,7 +131,7 @@
rel="noopener"
class="font-semibold text-primary underline-offset-2 hover:underline"
>
- Subscribe from {PREMIUM_PRICE}
+ Subscribe from {price}
{/if}
diff --git a/src/lib/paywall/config.ts b/src/lib/paywall/config.ts
index e2842d5..6c20004 100644
--- a/src/lib/paywall/config.ts
+++ b/src/lib/paywall/config.ts
@@ -19,8 +19,46 @@ export const PAYWALL_API_BASE = stripTrailingSlash(
/** Where prospective subscribers go to sign up (the paywall repo's signup form). */
export const SIGNUP_URL = env.VITE_PAYWALL_SIGNUP_URL ?? `${PAYWALL_API_BASE}/`;
-/** Display price, shown on the paywall panel. */
-export const PREMIUM_PRICE = env.VITE_PREMIUM_PRICE ?? '€3 / month';
+// ─── 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 paywall 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 PremiumCurrency = 'EUR' | 'USD' | 'CHF';
+
+export function detectCurrency(): PremiumCurrency {
+ 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 paywall panel, e.g. "€3 / month" or "CHF 3 / month". */
+export function getPremiumPrice(): string {
+ if (env.VITE_PREMIUM_PRICE) return env.VITE_PREMIUM_PRICE;
+ const currency = detectCurrency();
+ const symbol = CURRENCY_SYMBOL[currency];
+ return currency === 'CHF' ? `${symbol} 3 / month` : `${symbol}3 / month`;
+}
/** Short, human list of what premium unlocks (shown on the locked panel). */
export const PREMIUM_PERKS = [
diff --git a/src/routes/weather/week/[location]/+page.svelte b/src/routes/weather/week/[location]/+page.svelte
index 62fb67a..3ec0d4a 100644
--- a/src/routes/weather/week/[location]/+page.svelte
+++ b/src/routes/weather/week/[location]/+page.svelte
@@ -18,6 +18,7 @@
import { defaultParameters } from '../../options';
import DailyCards from './DailyCards.svelte';
+ import DailyStripSticky from './DailyStripSticky.svelte';
import HourlyTable from './HourlyTable.svelte';
import MeteogramCharts from './MeteogramCharts.svelte';
import ModelSelector from './ModelSelector.svelte';
@@ -199,33 +200,55 @@
{/if}
- (forecastDays = 15)}
- canExtendPast={pastDays < 3}
- onExtendPast={() => (pastDays = 3)}
- />
-
- {#if fetchedHourly && fetchedDaily}
-
+