tiny Sign-in chip only, no extra branches.
Anonymous sync keeps working; this just lets users optionally sign in with Google or Magic Link (email). When they sign in, we merge local favorites into their cloud doc automatically.

0) Add two env vars

In .env (or .env.local):

VITE_APP_URL=http://localhost:5173
VITE_FIREBASE_ENABLE_EMAIL_LINK=true


VITE_APP_URL must match your site origin for magic links.

1) Update src/lib/firebase.ts

Adds Google/Magic-link helpers and completes email links on load.

import { initializeApp, getApps } from "firebase/app";
import {
  getAuth,
  signInAnonymously,
  onAuthStateChanged,
  User,
  GoogleAuthProvider,
  signInWithPopup,
  signOut,
  isSignInWithEmailLink,
  sendSignInLinkToEmail,
  signInWithEmailLink,
} from "firebase/auth";
import {
  getFirestore,
  doc,
  onSnapshot,
  setDoc,
  updateDoc,
  arrayUnion,
  arrayRemove,
} from "firebase/firestore";

const cfg = {
  apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
  authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,
  projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
  appId: import.meta.env.VITE_FIREBASE_APP_ID,
  storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID,
};

export function isFirebaseConfigured() {
  return !!(cfg.apiKey && cfg.authDomain && cfg.projectId && cfg.appId);
}

let _authReady: Promise<User | null> | null = null;

export function ensureFirebase() {
  if (!isFirebaseConfigured()) return null;
  const app = getApps().length ? getApps()[0] : initializeApp(cfg);
  const auth = getAuth(app);
  const db = getFirestore(app);

  // Complete pending magic link sign-in if URL contains it
  if (typeof window !== "undefined" && isSignInWithEmailLink(auth, window.location.href)) {
    const key = "ibrandbiz_emailForSignIn";
    let email = window.localStorage.getItem(key) || "";
    if (!email) {
      // As a last resort, prompt (tiny UX); you can replace with a modal if you prefer
      email = window.prompt("Confirm your email to complete sign in") || "";
    }
    if (email) {
      signInWithEmailLink(auth, email, window.location.href)
        .then(() => window.localStorage.removeItem(key))
        .catch(() => {/* ignore */});
    }
  }

  if (!_authReady) {
    _authReady = new Promise<User | null>((resolve) => {
      onAuthStateChanged(auth, async (u) => {
        if (u) return resolve(u);
        try {
          const cred = await signInAnonymously(auth);
          resolve(cred.user);
        } catch {
          resolve(null);
        }
      });
    });
  }

  return { app, auth, db, authReady: _authReady };
}

// ---- Sign-in helpers ----
export async function signInWithGoogle() {
  const fb = ensureFirebase();
  if (!fb) return null;
  const provider = new GoogleAuthProvider();
  const { auth } = fb;
  const res = await signInWithPopup(auth, provider);
  return res.user;
}

export async function startMagicLink(email: string) {
  const fb = ensureFirebase();
  if (!fb) return null;
  const { auth } = fb;
  const actionCodeSettings = {
    url: import.meta.env.VITE_APP_URL || window.location.origin,
    handleCodeInApp: true,
  };
  await sendSignInLinkToEmail(auth, email, actionCodeSettings);
  window.localStorage.setItem("ibrandbiz_emailForSignIn", email);
  return true;
}

export async function doSignOut() {
  const fb = ensureFirebase();
  if (!fb) return;
  await signOut(fb.auth);
}

// re-exports used by favorites hook
export { getAuth, getFirestore, doc, onSnapshot, setDoc, updateDoc, arrayUnion, arrayRemove };

2) Keep the favorites hook as-is (it already merges)

No change needed to src/store/favorites.ts from the Cloud Favorites step — it calls setDoc(..., { ids: lsLoad() }, { merge: true }) when a user session appears, so local → cloud favorites migrate automatically.

3) Add the tiny chip component

Create src/components/SignInChip.tsx

import React, { useEffect, useState } from "react";
import { ensureFirebase, isFirebaseConfigured, signInWithGoogle, startMagicLink, doSignOut } from "../lib/firebase";
import { LogIn, LogOut, Mail, User } from "lucide-react";

export default function SignInChip() {
  const [uid, setUid] = useState<string | null>(null);
  const [email, setEmail] = useState("");
  const [sending, setSending] = useState(false);
  const [open, setOpen] = useState(false);
  const enabled = isFirebaseConfigured();

  useEffect(() => {
    const fb = ensureFirebase();
    if (!fb) return;
    fb.authReady?.then((user) => setUid(user?.uid ?? null));
    // reflect future auth changes
    if (fb) {
      fb.auth.onAuthStateChanged((u) => setUid(u?.uid ?? null));
    }
  }, []);

  if (!enabled) return null; // no chip if firebase not configured

  return (
    <div className="relative">
      <button
        onClick={() => setOpen((s) => !s)}
        className="inline-flex items-center gap-2 px-3 py-1.5 rounded-full border border-white/10 bg-slate-900/60 text-sm hover:bg-white/5"
        title={uid ? `Signed in (${uid.slice(0,6)}…)` : "Sign in"}
      >
        <User className="w-4 h-4" />
        {uid ? "Account" : "Sign in"}
      </button>

      {open && (
        <div className="absolute right-0 mt-2 w-64 rounded-xl border border-white/10 bg-slate-900/95 p-3 z-50">
          {!uid ? (
            <div className="space-y-3">
              <button
                onClick={async () => { await signInWithGoogle(); setOpen(false); }}
                className="w-full inline-flex items-center justify-center gap-2 px-3 py-2 rounded-lg bg-white text-slate-900 hover:bg-slate-200"
              >
                <LogIn className="w-4 h-4" /> Continue with Google
              </button>

              {import.meta.env.VITE_FIREBASE_ENABLE_EMAIL_LINK && (
                <div className="space-y-2">
                  <div className="text-xs text-slate-400">or use Magic Link</div>
                  <div className="flex gap-2">
                    <input
                      type="email"
                      value={email}
                      onChange={(e) => setEmail(e.target.value)}
                      placeholder="you@example.com"
                      className="flex-1 rounded-lg bg-slate-950/60 border border-white/10 p-2 text-slate-100 placeholder:text-slate-500"
                    />
                    <button
                      onClick={async () => { if (!email) return; setSending(true); await startMagicLink(email).catch(()=>{}); setSending(false); setOpen(false); alert("Magic link sent! Check your email."); }}
                      disabled={sending || !email}
                      className="inline-flex items-center gap-2 px-3 py-2 rounded-lg border border-white/10 hover:bg-white/5 text-white"
                    >
                      <Mail className="w-4 h-4" /> {sending ? "Sending…" : "Send"}
                    </button>
                  </div>
                </div>
              )}
            </div>
          ) : (
            <div className="space-y-2">
              <div className="text-xs text-slate-400">UID</div>
              <div className="text-sm font-mono break-all">{uid}</div>
              <button
                onClick={async () => { await doSignOut(); setOpen(false); }}
                className="w-full inline-flex items-center justify-center gap-2 px-3 py-2 rounded-lg border border-white/10 hover:bg-white/5 text-white mt-2"
              >
                <LogOut className="w-4 h-4" /> Sign out
              </button>
            </div>
          )}
        </div>
      )}
    </div>
  );
}

4) Show the chip on the Icons page header (only there)

Open src/pages/business-assets/stock/Icons.tsx and import + place the chip in the title row:

// add at top
import SignInChip from "../../../components/SignInChip";

// ...

<div className="flex flex-wrap items-center justify-between gap-3">
  <h1 className="text-2xl font-bold">Icons</h1>

  <div className="flex items-center gap-3">
    <SignInChip />
    <div className="inline-flex rounded-xl overflow-hidden border border-white/10">
      {/* existing Browse/Generate toggle buttons */}
    </div>
  </div>
</div>


Nothing else changes — favorites keep syncing anonymously; if a user signs in, their favorites are stored under their UID and local favorites get merged into the cloud doc.

Done

Tiny Sign-in chip with Google and optional Magic Link.

Anonymous remains default, with auto-merge on sign-in.

No other branches touched.