/* ============================================================ PRESETS — cover style definitions Each preset defines: bg, layout (position/align), per-field typography defaults, and a decoration recipe. ============================================================ */ const FONTS = { archivo: '"Archivo Black", sans-serif', bricolage: '"Bricolage Grotesque", sans-serif', manrope: '"Manrope", sans-serif', space: '"Space Grotesk", sans-serif', instrument: '"Instrument Serif", serif', playfair: '"Playfair Display", serif', garamond: '"EB Garamond", serif', mono: '"JetBrains Mono", ui-monospace, monospace', }; const FONT_LABEL = { archivo: 'Archivo Black', bricolage: 'Bricolage Grotesque', manrope: 'Manrope', space: 'Space Grotesk', instrument: 'Instrument Serif', playfair: 'Playfair Display', garamond: 'EB Garamond', mono: 'JetBrains Mono', }; const PRESETS = { magazine: { name: 'Журнал', desc: 'Display, плашка', bg: { mode: 'solid', color: '#F2EBDC' }, layout: { position: 'bottom', align: 'left' }, kicker: { font: 'archivo', size: 3.4, weight: 900, color: '#161616', case: 'upper', tracking: 0.18, lh: 1, plate: '#F2C84B', padded: true }, title: { font: 'bricolage', size: 15, weight: 800, color: '#161616', case: 'normal', tracking: -0.025, lh: 0.92 }, subtitle:{ font: 'manrope', size: 3.4, weight: 500, color: '#3C3A33', case: 'normal', tracking: 0, lh: 1.3 }, deco: 'magazine', }, book: { name: 'Книга', desc: 'Классика, серифы', bg: { mode: 'solid', color: '#1F3D2E' }, layout: { position: 'center', align: 'center' }, kicker: { font: 'garamond', size: 2.6, weight: 500, color: '#C9B66E', case: 'upper', tracking: 0.4, lh: 1, italic: true }, title: { font: 'instrument', size: 16, weight: 400, color: '#F4ECDD', case: 'normal', tracking: 0, lh: 1.0 }, subtitle:{ font: 'garamond', size: 3, weight: 400, color: '#C9B66E', case: 'normal', tracking: 0.02, lh: 1.4, italic: true }, deco: 'book', }, modern: { name: 'Модерн', desc: 'Минимал, воздух', bg: { mode: 'solid', color: '#F8F7F3' }, layout: { position: 'center', align: 'left' }, kicker: { font: 'manrope', size: 2.4, weight: 600, color: '#888680', case: 'upper', tracking: 0.32, lh: 1 }, title: { font: 'manrope', size: 10, weight: 200, color: '#1A1A1A', case: 'normal', tracking: -0.04, lh: 1.0 }, subtitle:{ font: 'manrope', size: 3, weight: 400, color: '#6E6C66', case: 'normal', tracking: 0, lh: 1.4 }, deco: 'modern', }, brutalist: { name: 'Brutalist', desc: 'Контраст, гротеск', bg: { mode: 'solid', color: '#F2DC1B' }, layout: { position: 'bottom', align: 'left' }, kicker: { font: 'mono', size: 2.6, weight: 500, color: '#0A0A0A', case: 'upper', tracking: 0.04, lh: 1 }, title: { font: 'archivo', size: 17, weight: 900, color: '#0A0A0A', case: 'upper', tracking: -0.03, lh: 0.86 }, subtitle:{ font: 'mono', size: 2.6, weight: 500, color: '#0A0A0A', case: 'normal', tracking: 0, lh: 1.4 }, deco: 'brutalist', }, editorial: { name: 'Editorial', desc: 'Серифы, контраст', bg: { mode: 'solid', color: '#F6F2EA' }, layout: { position: 'center', align: 'center' }, kicker: { font: 'manrope', size: 2.2, weight: 700, color: '#9C2B2B', case: 'upper', tracking: 0.35, lh: 1 }, title: { font: 'playfair', size: 13, weight: 700, color: '#1A1A1A', case: 'normal', tracking: -0.015, lh: 0.98, italic: true }, subtitle:{ font: 'garamond', size: 3.2, weight: 400, color: '#4A4742', case: 'normal', tracking: 0.01, lh: 1.4, italic: true }, deco: 'editorial', }, tech: { name: 'Tech', desc: 'Линейный, сетка', bg: { mode: 'solid', color: '#0B0B0E' }, layout: { position: 'bottom', align: 'left' }, kicker: { font: 'mono', size: 2.4, weight: 500, color: '#8B89F7', case: 'normal', tracking: 0.03, lh: 1 }, title: { font: 'space', size: 12, weight: 600, color: '#F4F4F6', case: 'normal', tracking: -0.025, lh: 0.98 }, subtitle:{ font: 'manrope', size: 2.8, weight: 400, color: '#85858E', case: 'normal', tracking: 0, lh: 1.45 }, deco: 'tech', }, photo: { name: 'Photo', desc: 'Фото на весь фон', bg: { mode: 'photo', seed: 'beauticard-1' }, layout: { position: 'bottom', align: 'left' }, kicker: { font: 'manrope', size: 2.4, weight: 600, color: '#FFFFFF', case: 'upper', tracking: 0.3, lh: 1, opacity: 0.85 }, title: { font: 'space', size: 11, weight: 700, color: '#FFFFFF', case: 'normal', tracking: -0.02, lh: 1.0 }, subtitle:{ font: 'manrope', size: 2.9, weight: 500, color: '#FFFFFF', case: 'normal', tracking: 0, lh: 1.4, opacity: 0.85 }, deco: 'photo', }, gradient: { name: 'Gradient', desc: 'Мягкий градиент', bg: { mode: 'mesh', a: '#FFB1A5', b: '#9D9AFF', c: '#FFE7A6' }, layout: { position: 'center', align: 'center' }, kicker: { font: 'manrope', size: 2.4, weight: 600, color: '#FFFFFF', case: 'upper', tracking: 0.32, lh: 1, opacity: 0.9 }, title: { font: 'space', size: 13, weight: 700, color: '#FFFFFF', case: 'normal', tracking: -0.025, lh: 1.0 }, subtitle:{ font: 'manrope', size: 3, weight: 500, color: '#FFFFFF', case: 'normal', tracking: 0, lh: 1.4, opacity: 0.85 }, deco: 'gradient', }, }; const PRESET_ORDER = ['magazine','book','modern','brutalist','editorial','tech','photo','gradient']; const ASPECTS = { '3:4': { w: 3, h: 4, label: '3:4', hint: 'Журнал' }, '1:1': { w: 1, h: 1, label: '1:1', hint: 'Соцсети' }, '16:9': { w: 16, h: 9, label: '16:9', hint: 'Блог' }, '9:16': { w: 9, h: 16, label: '9:16', hint: 'Stories' }, }; const POSITIONS = ['top','center','bottom']; const ALIGNS = ['left','center','right']; /* ============================================================ Backgrounds ============================================================ */ function BackgroundLayer({ bg, presetDeco }) { if (!bg) return null; if (bg.mode === 'solid') { return
; } if (bg.mode === 'gradient') { return
; } if (bg.mode === 'mesh') { return
; } if (bg.mode === 'photo') { const url = bg.url || `https://picsum.photos/seed/${encodeURIComponent(bg.seed || 'cover')}/900/1200`; return ( <>
{presetDeco !== false && (
)} ); } if (bg.mode === 'pattern') { const patterns = { dots: `radial-gradient(circle at 1px 1px, ${bg.fg} 1px, transparent 0) 0 0/${bg.scale||16}px ${bg.scale||16}px`, grid: `linear-gradient(${bg.fg} 1px, transparent 1px) 0 0/${bg.scale||24}px ${bg.scale||24}px, linear-gradient(90deg, ${bg.fg} 1px, transparent 1px) 0 0/${bg.scale||24}px ${bg.scale||24}px`, lines: `repeating-linear-gradient(45deg, ${bg.fg} 0 1px, transparent 1px ${bg.scale||10}px)`, }; return (
); } if (bg.mode === 'blocks') { return (
{(bg.shapes||[]).map((s, i) => (
))}
); } return null; } /* ============================================================ Decorations per preset ============================================================ */ function Decoration({ presetId, deco = {}, mute }) { if (mute) return null; const d = { brand: deco.brand ?? 'BEAUTICARD', issue: deco.issue ?? '№ 24', date: deco.date ?? 'МАЙ · 2026', badge: deco.badge ?? 'НОВОЕ', tagline: deco.tagline ?? 'ДИЗАЙН · КУЛЬТУРА · ТЕХНО', readTime: deco.readTime ?? 'ЧТЕНИЕ · 8 МИН', }; if (presetId === 'magazine') { return ( <>
{d.issue && {d.issue}} {d.date && {d.date}}
{d.tagline &&
{d.tagline}
}
); } if (presetId === 'book') { return ( <>
✦ ✦ ✦
{d.date &&
{d.date}
} ); } if (presetId === 'modern') { return ( <>
{d.brand}{d.issue}
{d.tagline}{d.date}
); } if (presetId === 'brutalist') { return ( <>
★ {d.issue} ★{d.date}
{d.badge && <>
{d.badge}
} ); } if (presetId === 'editorial') { return ( <> {d.brand &&
{d.brand}
}
{d.tagline &&
{d.tagline}
} ); } if (presetId === 'tech') { return ( <>
● {d.brand} {d.date}
{d.issue} →{d.readTime}
); } if (presetId === 'photo') { return (
{d.brand}{d.issue}
); } if (presetId === 'gradient') { return (
◇ {d.brand} ◇
); } return null; } /* ============================================================ CoverPreview — renders the actual cover ============================================================ */ function fieldStyle(field) { if (!field) return {}; return { fontFamily: FONTS[field.font] || FONTS.manrope, fontSize: `${field.size}cqi`, fontWeight: field.weight, color: field.color, textTransform: field.case === 'upper' ? 'uppercase' : field.case === 'lower' ? 'lowercase' : 'none', letterSpacing: `${field.tracking||0}em`, lineHeight: field.lh, fontStyle: field.italic ? 'italic' : 'normal', opacity: field.opacity != null ? field.opacity : 1, textAlign: field.align, margin: 0, }; } function CoverPreview({ presetId, content, deco, overrides, bgOverride, layoutOverride, hideDeco, aspect }) { const preset = PRESETS[presetId]; const bg = bgOverride || preset.bg; const layout = { ...preset.layout, ...(layoutOverride||{}) }; // Merge each field's preset defaults with overrides + global align const mk = (key) => ({ ...preset[key], align: layout.align, ...(overrides?.[key]||{}) }); const k = mk('kicker'), t = mk('title'), s = mk('subtitle'); const justify = layout.position === 'top' ? 'flex-start' : layout.position === 'center' ? 'center' : 'flex-end'; const items = layout.align === 'left' ? 'flex-start' : layout.align === 'center' ? 'center' : 'flex-end'; const pad = '7cqi'; const kickerEl = content.kicker && ( k.plate ? ( {content.kicker} ) : (
{content.kicker}
) ); return (
{/* spacer for top deco when content is at top */} {layout.position === 'top' &&
}
{kickerEl} {content.title &&

{content.title}

} {content.subtitle &&
{content.subtitle}
}
); } Object.assign(window, { PRESETS, PRESET_ORDER, ASPECTS, POSITIONS, ALIGNS, FONTS, FONT_LABEL, CoverPreview, });