/* Brand Lens — app shell + navigation. The chrome matches the published Proper Sky Brand Guide pages (Imagery.html et al.): shared sidebar nav, topbar crumb, page hero, underline tab strip. All workflow views (Library / Review / Scoreboard / StyleGuide / Playbook) are untouched — this file is presentation only. */ function App() { const [recs, setRecs] = useState(null); const [tab, setTab] = useState('library'); const [layout, setLayoutState] = useState(() => localStorage.getItem('lens-layout') || 'focus'); const [navOpen, setNavOpen] = useState(false); const reload = useCallback(async () => { const all = await window.LensDB.getAll(); setRecs(all); }, []); useEffect(() => { reload(); }, [reload]); const setLayout = (l) => { setLayoutState(l); localStorage.setItem('lens-layout', l); }; // navigate + refresh from DB so every tab sees the latest verdicts const go = useCallback((t) => { reload(); setTab(t); }, [reload]); // mobile drawer (same behavior as mobile-nav.js on the published pages, // but React-owned so it survives re-renders) useEffect(() => { document.body.classList.toggle('mnav-open', navOpen); return () => document.body.classList.remove('mnav-open'); }, [navOpen]); useEffect(() => { const onKey = (e) => { if (e.key === 'Escape') setNavOpen(false); }; document.addEventListener('keydown', onKey); return () => document.removeEventListener('keydown', onKey); }, []); if (recs === null) { return
In Practice · Imagery & Social
The judge that learns our eye. The AI calls each image first — you agree or disagree and say why. Your comments re-teach it between rounds, until its calls match ours.