/** * How many try-ons this browser has already spent on the demo. * * Kept separate from the gallery on purpose: the gallery is the shopper's to * delete, and deleting a picture must not hand out another try-on. * * The authority is still the server. The brain caps the demo per IP and answers * 429 when that cap is hit, which the widget already turns into its own screen. * This counter exists so the number is on screen BEFORE the visitor starts, * instead of only when they walk into the wall. */ const COUNT_KEY = 'aisthetix-demo-tryons'; export function countDemoTryOns(): number { try { const raw = Number( localStorage.getItem( COUNT_KEY ) ); if ( ! Number.isFinite( raw ) || raw < 0 ) { return 0; } return Math.floor( raw ); } catch { return 0; } } /** Records one spent try-on and returns the new total. */ export function recordDemoTryOn(): number { const next = countDemoTryOns() + 1; try { localStorage.setItem( COUNT_KEY, String( next ) ); } catch { // A browser that refuses storage loses the counter, not the try-on. } return next; } /** * Try-ons left, never below zero. A limit of 0 means the demo did not * advertise one, and callers should show nothing rather than "0 left". */ export function remainingDemoTryOns( limit: number ): number { return Math.max( 0, limit - countDemoTryOns() ); }