/* Brand Lens — shared UI primitives (exported to window) */ const { useState, useEffect, useRef, useCallback, useMemo } = React; /* ---- inline icon set ---- */ const ICON_PATHS = { check: 'M20 6 9 17l-5-5', x: 'M18 6 6 18M6 6l12 12', warn: 'M12 9v4M12 17h.01M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z', upload: 'M4 16v2a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2M8 8l4-4 4 4M12 4v12', shuffle: 'M16 3h5v5M4 20 21 3M21 16v5h-5M15 15l6 6M4 4l5 5', trash: 'M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6', arrow: 'M5 12h14M13 6l6 6-6 6', eye: 'M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7Z', eyeOff: 'M9.9 4.2A9.1 9.1 0 0 1 12 4c6.5 0 10 7 10 7a13.2 13.2 0 0 1-2.2 3M6.6 6.6A13.3 13.3 0 0 0 2 11s3.5 7 10 7a9 9 0 0 0 3.5-.7M2 2l20 20M9.9 9.9a3 3 0 0 0 4.2 4.2', sparkle: 'M12 3v4M12 17v4M3 12h4M17 12h4M6.3 6.3l2.8 2.8M14.9 14.9l2.8 2.8M17.7 6.3l-2.8 2.8M9.1 14.9l-2.8 2.8', refresh: 'M21 12a9 9 0 1 1-3-6.7L21 8M21 3v5h-5', heart: 'M19 14c1.5-1.5 3-3.3 3-5.5A4.5 4.5 0 0 0 12 5 4.5 4.5 0 0 0 2 8.5C2 12 5.5 15 12 21c2.6-2.4 4.8-4.4 7-7Z', filter: 'M22 3H2l8 9.5V19l4 2v-8.5L22 3Z', scale: 'M16 6h6M22 6l-3-3M22 6l-3 3M3 6h7M3 12h18M3 18h11', cards: 'M3 5h18v6H3zM3 15h11v4H3zM18 15h3v4h-3z', panel: 'M3 4h18v16H3zM10 4v16', image: 'M3 5h18v14H3zM3 15l5-5 4 4 3-3 6 6M9 9a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0Z', share: 'M18 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6ZM6 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6ZM18 22a3 3 0 1 0 0-6 3 3 0 0 0 0 6ZM8.6 13.5l6.8 4M15.4 6.5l-6.8 4', shapes: 'M12 2l4 7H8l4-7ZM17.5 22a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7ZM4 14h7v7H4z', download: 'M12 3v12M7 11l5 5 5-5M5 21h14', save: 'M4 4h12l4 4v12H4zM8 4v6h8M8 20v-6h8v6', }; function Icon({ name, size = 18, stroke = 2, style, className }) { const d = ICON_PATHS[name] || ''; return ( ); } /* ---- circular brand-fit score ---- */ function ScoreRing({ score, size = 96, label = 'fit' }) { const r = (size - 12) / 2; const c = 2 * Math.PI * r; const pct = Math.max(0, Math.min(100, score)) / 100; const col = score >= 70 ? 'var(--st-success)' : score >= 45 ? 'var(--st-warning)' : 'var(--ps-flag)'; return (
{Math.round(score)} {label}
); } /* ---- verdict pill ---- */ function VerdictPill({ verdict, big }) { const keep = verdict === 'keep'; return ( {keep ? 'Brand fit' : 'Off brand'} ); } /* ---- status chip for library cards ---- */ function StatusChip({ rec }) { let txt, bg, fg, dot; if (!rec.user) { txt = 'Not reviewed'; bg = 'var(--ps-cloud-gray)'; fg = 'var(--fg-3)'; dot = 'var(--ps-line-strong)'; } else { const agree = (rec.ai && rec.ai.verdict === 'keep') === !!rec.user.like; if (rec.ai && agree) { txt = 'Aligned'; bg = '#EAF6F0'; fg = '#1F7A52'; dot = 'var(--st-success)'; } else { txt = 'Diverged'; bg = 'var(--ps-flag-soft)'; fg = 'var(--ps-flag-ink)'; dot = 'var(--ps-flag)'; } } return ( {txt} ); } /* ---- pass/warn/fail reason row ---- */ function ReasonRow({ r }) { const map = { pass: { c: 'var(--st-success)', i: 'check' }, warn: { c: 'var(--st-warning)', i: 'warn' }, fail: { c: 'var(--ps-flag)', i: 'x' }, }; const m = map[r.status] || map.warn; return (
{r.dimension}
{r.note}
); } /* ---- use-channel chips (renders any short label) ---- */ function UseChips({ uses }) { if (!uses || !uses.length) return null; const cap = (s) => s.charAt(0).toUpperCase() + s.slice(1); return (
{uses.map((u, i) => ( {cap(u)} ))}
); } /* ---- category control (auto / photo / social / clip-art) ---- */ const CAT_META = { auto: { label: 'Auto', icon: 'sparkle' }, photography: { label: 'Photo', icon: 'image' }, social: { label: 'Social', icon: 'share' }, illustration: { label: 'Clip-art', icon: 'shapes' }, }; function CategoryControl({ value, onChange }) { return (
{['auto', 'photography', 'social', 'illustration'].map((k) => { const m = CAT_META[k]; const on = value === k; return ( ); })}
); } function CategoryBadge({ category, small }) { const m = CAT_META[category] || CAT_META.photography; return ( {m.label} ); } /* ---- buttons ---- */ function Btn({ kind = 'ghost', icon, children, style, ...p }) { const base = { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 8, fontFamily: 'var(--font-sans)', fontSize: 14, fontWeight: 600, cursor: 'pointer', borderRadius: 10, padding: '10px 16px', border: '1px solid transparent', transition: 'all .15s var(--ease-standard)', lineHeight: 1, whiteSpace: 'nowrap', }; const kinds = { primary: { background: 'var(--ps-primary-blue)', color: '#fff' }, dark: { background: 'var(--ps-navy)', color: '#fff' }, ghost: { background: 'var(--ps-cloud-gray)', color: 'var(--ps-primary-blue)', borderColor: 'var(--ps-line)' }, danger: { background: 'var(--ps-flag-soft)', color: 'var(--ps-flag-ink)', borderColor: '#F0D2CD' }, teal: { background: 'var(--ps-secondary-teal)', color: 'var(--ps-navy)' }, }; return ( ); } /* ---- spinner ---- */ function Spinner({ size = 18, color = 'currentColor' }) { return ( ); } Object.assign(window, { Icon, ScoreRing, VerdictPill, StatusChip, ReasonRow, UseChips, CategoryControl, CategoryBadge, CAT_META, Btn, Spinner, useState, useEffect, useRef, useCallback, useMemo });