// Shared primitives for the SannaTrip site
// Designed to work identically at mobile and desktop widths — consumer passes container width via CSS.
// ── Placeholder imagery: striped SVG with a monospace label calling out what goes there ──
function Imagery({ label, aspect = '3/2', tone = 'navy', mono = true, src, alt, objectPosition = 'center', style = {} }) {
const tones = {
navy: { bg: '#E8EDF3', stripe: '#D3DCE7', ink: '#1b3661' },
pink: { bg: '#F3E7EE', stripe: '#EAD7E1', ink: '#7A3A62' },
ivory: { bg: '#F0EADF', stripe: '#E5DCC9', ink: '#6B5A3B' },
moss: { bg: '#E5EAE2', stripe: '#D0D8CA', ink: '#3E5A3E' },
warm: { bg: '#EFE6DC', stripe: '#DFD1BD', ink: '#5C4A35' },
charcoal: { bg: '#2A2F36', stripe: '#353A42', ink: '#E8EAED' },
}[tone] || { bg: '#E8EDF3', stripe: '#D3DCE7', ink: '#1b3661' };
return (
{src && (

)}
{!src && mono && (
{label}
)}
);
}
// ── Eyebrow label (navy, uppercase, tracked) ──
function Eyebrow({ children, color, style = {} }) {
return (
{children}
);
}
// ── Editorial serif display title ──
function Display({ children, size = 'lg', style = {} }) {
const sizes = {
xl: { fs: 'clamp(44px, 7vw, 88px)', lh: 0.95, wt: 340 },
lg: { fs: 'clamp(32px, 5vw, 56px)', lh: 1.02, wt: 360 },
md: { fs: 'clamp(24px, 3.5vw, 38px)', lh: 1.1, wt: 380 },
sm: { fs: 'clamp(20px, 2.4vw, 26px)', lh: 1.15, wt: 420 },
}[size];
return (
{children}
);
}
// ── Button (primary navy, secondary ghost, accent pink) ──
function Btn({ children, variant = 'primary', size = 'md', style = {}, onClick, type }) {
const sizes = {
md: { pad: '14px 24px', fs: 14 },
lg: { pad: '18px 30px', fs: 15 },
sm: { pad: '10px 18px', fs: 13 },
}[size];
const variants = {
primary: {
background: TOKENS.color.navy, color: '#fff',
border: `1px solid ${TOKENS.color.navy}`,
},
ghost: {
background: 'transparent', color: TOKENS.color.navy,
border: `1px solid ${TOKENS.color.hairline}`,
},
accent: {
background: TOKENS.color.pink, color: TOKENS.color.navy,
border: `1px solid ${TOKENS.color.pink}`,
},
ink: {
background: 'transparent', color: TOKENS.color.navy,
border: 'none', padding: 0, textDecoration: 'underline',
textUnderlineOffset: '4px', textDecorationColor: TOKENS.color.pink,
textDecorationThickness: '1px',
},
}[variant];
return (
);
}
// ── Hairline rule ──
function Rule({ color, style = {} }) {
return ;
}
// ── Section container with configurable padding ──
function Section({ children, bg, pad = 'lg', narrow = false, id, style = {} }) {
const pads = {
lg: 'clamp(48px, 9vw, 120px) clamp(20px, 5vw, 80px)',
md: 'clamp(36px, 6vw, 80px) clamp(20px, 5vw, 80px)',
}[pad];
return (
);
}
Object.assign(window, { Imagery, Eyebrow, Display, Btn, Rule, Section });