Bizleen
Live · bizleen.comMulti-tier digital business card SaaS (Free / Pro €9/yr / Business €19/yr) on Cloudflare edge. Personal URL /@you, scannable QR code, vCard 4.0, 3D flip + parallax + holo design, Business tier with mini-site (photos, menus, hours, reservation, analytics). Custom Lucia-style auth, D1 + R2 + Workers Standard, 26+ SSR routes, 9 migrations, Stripe billing. France GDPR-compliant.
Why I built it
I saw Azzapp and thought: the "your business card" feature is great, but the app is heavy (multi-features CRM, scanner, teams) and the rendering lacks soul. I just wanted a premium card that looks like me, a /@dan URL, a QR code, a vCard, that's it. The V1 MVP was a static personal site with my card hardcoded, under the name MindVision Card. Once in hand, I pivoted to a real multi-user SaaS in Phase 2-4 — custom auth, D1, R2, Resend — so anyone can create theirs in 3 minutes. Then Phase 5: rebrand to <strong>Bizleen</strong> + add a Business tier with mini-site (photos, menus, hours, reservation, analytics) for freelancers, neighborhood shops, and restaurant owners. Stripe billing 3 tiers. All on Cloudflare edge, $5/mo Workers Standard is enough. No tracking, no ads, France GDPR-complete by design.
Structural technical decisions
A single Astro component for 3 visual contexts (gallery thumbnail 240px, hero phones 240px, /@handle fullscreen 440px). The key: sizes in cqi (container query units) not vw, so the card scales relative to its own container, not the viewport. Result: pixel-perfect identical at any scale, without duplicating markup or maintaining 3 CSS variants.
Lucia v3 has been unmaintained since March 2025 — the author recommends copying ~80 LOC into your repo. Done. Sessions = SHA-256(raw_token) in hex DB-side, raw token client-side HttpOnly/Secure/SameSite=Lax, sliding 30d. Password hash argon2id via @noble/hashes + server-side AUTH_PEPPER (defense in depth). Workers Standard ($5/mo) required for argon2id CPU >10ms — Workers Free caps at 10ms, unusable for serious crypto.
Post-V1 SaaS pivot: 3 tiers (Free / Pro €9/yr / Business €19/yr) with 4 Stripe price IDs including 2 founder-pricing for early adopters. /api/subscribe + signed webhook + customer portal routes. The Business tier unlocks the mini-site (photos, menus, opening hours, reservation link, user-side analytics page) — natural extension of the card into a real tool for proximity pros (restaurants, beauty, retail). Not a bloated CRM: just what a freelancer/shop wants to share via QR on their storefront.
The landing displays 6 hero phones + 8 gallery cards = 14 simultaneous cards with backdrop-filter blur(18px) + bg video on each. Saturated GPU compositor → dramatic flicker on resize, particularly on components placed above the video. Non-obvious diagnosis: it's the backdrop-filter, not the video decoding, that saturates the GPU. Fix: replace with semi-opaque background (rgba 65%) without blur. Associated video scheduler: NEVER evict a playing video to make room — only natural scroll-out pauses. Cap at 6 concurrent decodings.
LCEN cookie banner (single strictly necessary cookie: session HttpOnly). Pages /privacy + /terms + /mentions-legales (SIRET 83515065700012, CNIL, French law). Required consent checkbox on signup with target=_blank links to Terms + Privacy. /api/export-data API (right to portability, JSON dump) + /api/delete-account (right to be forgotten, DB + R2 cascade). Anti-enumeration on /app/forgot (always the same message). Rate-limits login 5/15min, signup 3/h/IP. No tracker, no third-party analytics.
The non-trivial challenge
The card must have the SAME visual rendering at three scales: (1) landing hero in a 240px PhoneMockup, (2) gallery in a 240px PhoneMockup with QR scan on back, (3) /@handle in 440px max-width fullscreen. First iteration with vw font-size gave oversized cards in thumbnail (clamp hitting max) and correct cards in fullscreen. Switching to cqi (container query units, supported since 2023 on Chrome 105 / Safari 16) solves it: font-size: clamp(14px, 7cqi, 22px) scales proportionally to the .cp width itself, not the viewport. At 240px → 17px, at 440px → 31px. Perfect consistency, zero markup fork. More tricky: the back face of a 3D flip must keep rotateY(180deg) — any attempt at layer promotion with transform: translateZ(0) on .cp-face overrides this rotation and renders both faces coplanar (back visible through front). Found after 2h of debugging. The fix is to use will-change alone, or promote a parent.
Lesson learned
UI components that must live at multiple scales should be designed with container queries from commit zero. The vw reflex is intuitive but breaks consistency as soon as context changes. And for GPU flicker on N>=4 surfaces with backdrop-filter + media behind: drop the blur, keep the opacity. An expensive GPU effect × N elements = compositor budget blown. Count before you blur. Product-side: a well-designed personal MVP (canonical component, clean D1 schema, hardened auth) pivots into a multi-tier SaaS in a few sprints without rewriting the base — the MindVision Card → Bizleen rebrand + Business mini-site addition touched neither the auth layer nor the schema nor the card component. The rule: don't specialize until you've seen usage.
Features
Single component, 4 modes (bare/appMode/signupMode/bind), identical render at thumbnail 240px and fullscreen 440px via container queries (cqi)
Front with photo + contacts + CTAs, back with QR + handle. Pointer-tracking tilt, holo sheen, cubic-bezier 0.85s transitions
Photos, menus, opening hours, reservation link, embedded analytics. For freelancers, neighborhood shops, restaurant owners who want link-in-bio + vCard + mini-storefront without installing an app
Free / Pro €9/yr / Business €19/yr. 4 Stripe price IDs (incl. founder pricing for early adopters). Subscribe + signed webhook + change-plan + cancel via Stripe portal
argon2id + salt + server pepper, sliding 30d session, login/signup rate-limits, email verification + password reset via Resend
ruby/gold/emerald/ocean/amber/noir/beige/glass + 12 Vecteezy Pro boomerang-encoded backgrounds, live preview picker in the editor
Per-user dynamic manifest, SW network-first on HTML cache-first on assets. vCard with PHOTO;VALUE=uri fallback for iOS Contacts (which can't parse WebP base64)
Privacy + Terms + Legal notice (SIRET, CNIL, French law), LCEN cookie banner, signup consent checkbox, GDPR-compliant JSON export + account deletion