// Shared product tile — real photo if available, else monochrome gradient with initials
function ProductTile({ tono = 'beige', catalogo, imagen, size = 56, radius = 8 }) {
const T = window.T;
const bg = window.TONE_GRADIENT[tono] || window.TONE_GRADIENT.beige;
const initials = (catalogo || '').replace(/[^A-Za-z]/g, '').slice(0, 2).toUpperCase();
const [imgFailed, setImgFailed] = React.useState(false);
const useImage = imagen && !imgFailed;
return (
{useImage ? (

setImgFailed(true)}
style={{
width: '100%', height: '100%', objectFit: 'contain',
padding: Math.max(2, size * 0.06),
}}
/>
) : (
{initials}
)}
);
}
function StatusPill({ estado, enviado, size = 'md' }) {
const T = window.T;
const map = {
pendiente: { label: enviado ? 'En curso' : 'Borrador', bg: enviado ? T.warnSoft : T.paperDeep, fg: enviado ? 'oklch(0.45 0.12 70)' : T.inkSoft, dot: enviado ? T.warn : T.inkMute },
completado: { label: 'Completado', bg: T.okSoft, fg: 'oklch(0.38 0.1 155)', dot: T.ok },
cancelado: { label: 'Cancelado', bg: T.dangerSoft, fg: T.accentInk, dot: T.danger },
};
const s = map[estado] || map.pendiente;
const px = size === 'sm' ? '3px 8px' : '5px 10px';
const fs = size === 'sm' ? 10 : 11;
return (
{s.label}
);
}
// Section header for inside screens (small serif eyebrow + title)
function SectionHead({ eyebrow, title, action }) {
const T = window.T;
return (
{eyebrow && (
{eyebrow}
)}
{title}
{action}
);
}
// Primary button
function PrimaryBtn({ children, onClick, disabled, icon, variant = 'accent', style = {} }) {
const T = window.T;
const bg = variant === 'accent' ? T.ink : T.accent;
return (
);
}
function GhostBtn({ children, onClick, icon, style = {} }) {
const T = window.T;
return (
);
}
Object.assign(window, { ProductTile, StatusPill, SectionHead, PrimaryBtn, GhostBtn });