// Studio Uariní — Generator templates // Each template is a "renderer" that takes a `data` object (form values) and renders an editable post. // Templates declare their FIELDS upfront so the form panel knows what controls to show. const { UARINI, SpiralPatch, Wordmark, LogoLockup, GrainOverlay, StripesMark, PhotoPlaceholder } = window; // ─────────────────────────── Field type helpers ──────────────────────────── const F = { text: (key, label, def, opts = {}) => ({ kind: 'text', key, label, def, ...opts }), textarea: (key, label, def, opts = {}) => ({ kind: 'textarea', key, label, def, ...opts }), select: (key, label, def, options) => ({ kind: 'select', key, label, def, options }), photoTone: (key, label, def) => ({ kind: 'select', key, label, def, options: [ { value: 'warm', label: 'Quente / sépia' }, { value: 'moss', label: 'Verde musgo' }, { value: 'night', label: 'Noite' }, { value: 'sun', label: 'Pôr-do-sol' }, { value: 'fog', label: 'Névoa' }, { value: 'smoke', label: 'Fumaça' }, ], }), rows: (key, label, def, columns, opts = {}) => ({ kind: 'rows', key, label, def, columns, ...opts }), media: (key, label, opts = {}) => ({ kind: 'media', key, label, def: null, ...opts }), slider: (key, label, def, min, max, step = 1, opts = {}) => ({ kind: 'slider', key, label, def, min, max, step, ...opts }), toggle: (key, label, def, opts = {}) => ({ kind: 'toggle', key, label, def, ...opts }), heading: (label, key) => ({ kind: 'heading', key: key || ('_h_' + label), label }), color: (key, label, def, palette = null) => ({ kind: 'color', key, label, def, options: palette || [ { value: UARINI.cream, name: 'Creme' }, { value: UARINI.creamSoft, name: 'Creme suave' }, { value: UARINI.black, name: 'Preto' }, { value: UARINI.green, name: 'Verde' }, { value: UARINI.greenSoft, name: 'Verde claro' }, { value: UARINI.greenDeep, name: 'Verde profundo' }, { value: '#FFFFFF', name: 'Branco' }, ], }), }; // ─── Standard text + bg color fields, on every template ──────────────────── const COLOR_FIELDS = (defaults) => [ F.color('bgColor', 'Cor de fundo', defaults.bgColor), F.color('textColor', 'Cor do texto', defaults.textColor), ]; // Render a media slot — uploaded image/video, or fall back to PhotoPlaceholder. // When a photo is uploaded, the PhotoPlaceholder (smoke/warm/moss effect) still // peeks through as a thin border behind the image, so the chosen "tom" stays // visible as a frame around the photo. function MediaSlot({ media, fallbackTone = 'warm', label, children, style = {}, peek = 18 }) { if (!media || !media.url) { return ( {children} ); } const isVideo = media.type && media.type.startsWith('video'); // resize / reposition controls (all optional, sensible defaults) const fit = media.fit || 'cover'; const scale = media.scale ?? 1; const posX = media.posX ?? 50; const posY = media.posY ?? 50; const mediaStyle = { width: '100%', height: '100%', display: 'block', objectFit: fit, objectPosition: `${posX}% ${posY}%`, transform: scale !== 1 ? `scale(${scale})` : undefined, transformOrigin: `${posX}% ${posY}%`, }; return (
{/* effect underlay — sticks out as a thin frame around the photo */}
{isVideo ? (
{children}
); } window.MediaSlot = MediaSlot; // ─── Per-text style helpers: read optional size-multiplier + color override ── // txt(d, key, baseSize, baseColor, extra) → a style object. Templates opt-in by // passing a `key`; the form can then expose `Sz` (slider) + `Col` (color). function txt(d, key, baseSize, baseColor, extra = {}) { const mult = d[key + 'Sz']; const col = d[key + 'Col']; return { ...extra, fontSize: baseSize * (mult == null ? 1 : mult), color: col || baseColor, }; } window.txt = txt; // Build a compact "text style" field group (heading + content + size slider + color) // for a text block. `contentField` is the F.text/F.textarea for the block's text. const TXT_GROUP = (key, label, defColor, contentField, opts = {}) => [ F.heading(label, '_h_' + key), contentField, F.slider(key + 'Sz', 'Tamanho', 1, opts.min || 0.5, opts.max || 2, 0.05), F.color(key + 'Col', 'Cor', defColor), ]; window.TXT_GROUP = TXT_GROUP; // Legacy compact group without heading/content (size + color only) const TXT_FIELDS = (key, label, defColor, opts = {}) => [ F.slider(key + 'Sz', label + ' · tamanho', 1, opts.min || 0.6, opts.max || 1.8, 0.05), F.color(key + 'Col', label + ' · cor', defColor), ]; window.TXT_FIELDS = TXT_FIELDS; // Pick a spiral tone from a bg color (closest match) function spiralToneFor(bg, fg) { if (bg === UARINI.green) return fg === UARINI.cream ? 'cream-on-green' : 'dark-on-cream'; if (bg === UARINI.cream || bg === UARINI.creamSoft || bg === '#FFFFFF') { return fg === UARINI.green ? 'green-on-cream' : 'dark-on-cream'; } return fg === UARINI.green ? 'green-on-dark' : 'cream-on-dark'; } // ────────────────────────────── 01 · LOGO HERO ───────────────────────────── const TplLogoHero = { id: 'logo-hero', label: 'Logo de abertura', group: 'POST · 1080×1080', size: { w: 1080, h: 1080 }, fields: [ ...COLOR_FIELDS({ bgColor: UARINI.black, textColor: UARINI.cream }), F.color('accentColor', 'Cor do destaque', UARINI.green), F.heading('Logo'), F.slider('logoSz', 'Tamanho', 1, 0.5, 1.4, 0.05), ...TXT_GROUP('eyebrow', 'Etiqueta esquerda', UARINI.green, F.text('eyebrow', 'Texto', 'EST. 2024')), ...TXT_GROUP('eyebrowR', 'Etiqueta direita', UARINI.cream, F.text('eyebrowR', 'Texto', 'SP · BR')), ...TXT_GROUP('footer', 'Linha inferior', UARINI.cream, F.text('footer', 'Texto', 'BARBEARIA · TERAPIA CAPILAR')), ], defaults: { bgColor: UARINI.black, textColor: UARINI.cream, accentColor: UARINI.green, logoSz: 1, eyebrow: 'EST. 2024', eyebrowCol: UARINI.green, eyebrowR: 'SP · BR', eyebrowRCol: UARINI.cream, footer: 'BARBEARIA · TERAPIA CAPILAR', footerCol: UARINI.cream }, render: (d) => (
{d.eyebrow}
{d.eyebrowR}
{d.footer}
), caption: (d) => `${d.footer.toLowerCase()} ✦ nome da casa: studio uariní — espiral, raiz, ritual. ${d.eyebrow.toLowerCase()} · ${d.eyebrowR.toLowerCase()}`, }; // ─────────────────────────── 02 · RETRATO + MOLDURA ───────────────────────── const TplPortrait = { id: 'portrait-cream', label: 'Retrato + moldura', group: 'POST · 1080×1080', size: { w: 1080, h: 1080 }, fields: [ F.media('media', 'Subir foto ou vídeo'), F.photoTone('photo', 'Tom (se sem foto)', 'warm'), ...COLOR_FIELDS({ bgColor: UARINI.cream, textColor: UARINI.cream }), F.color('frameColor', 'Cor da moldura', UARINI.cream), F.heading('Espiral'), F.slider('spiralSz', 'Tamanho', 1, 0.3, 2, 0.05), ...TXT_GROUP('badge', 'Etiqueta', UARINI.cream, F.text('badge', 'Texto', 'PROCESSO #14')), ...TXT_GROUP('title', 'Título', UARINI.cream, F.textarea('title', 'Texto', 'O cuidado\nantes do corte.')), ], defaults: { media: null, badge: 'PROCESSO #14', badgeCol: UARINI.cream, title: 'O cuidado\nantes do corte.', titleCol: UARINI.cream, photo: 'warm', textColor: UARINI.cream, bgColor: UARINI.cream, frameColor: UARINI.cream, spiralSz: 1 }, render: (d) => (
{d.badge}
{d.title}
), caption: (d) => `${d.title.replace(/\n/g, ' ')} ✦ o ritual começa antes do corte: escuta, raiz, respiração. #${d.badge.replace(/[#·\s]+/g, '').toLowerCase() || 'studiouarini'} #studiouarini`, }; // ─────────────────────────── 03 · QUOTE / ESPIRAL ─────────────────────────── const TplQuote = { id: 'quote-green', label: 'Espiral · simbologia', group: 'POST · 1080×1080', size: { w: 1080, h: 1080 }, fields: [ ...COLOR_FIELDS({ bgColor: UARINI.green, textColor: UARINI.black }), F.heading('Espiral'), F.slider('spiralSz', 'Tamanho', 1, 0.3, 2, 0.05), F.color('spiralColor', 'Cor', UARINI.black), ...TXT_GROUP('eyebrow', 'Etiqueta', UARINI.black, F.text('eyebrow', 'Texto', 'ESPIRAL · 01')), ...TXT_GROUP('headline', 'Manchete', UARINI.black, F.textarea('headline', 'Texto', 'A espiral é\nmovimento\ncontínuo.')), ...TXT_GROUP('sub', 'Subtexto', UARINI.black, F.textarea('sub', 'Texto', 'Símbolo ancestral indígena de transformação e conexão entre realidades.')), ], defaults: { bgColor: UARINI.green, textColor: UARINI.black, spiralColor: UARINI.black, spiralSz: 1, eyebrow: 'ESPIRAL · 01', eyebrowCol: UARINI.black, headline: 'A espiral é\nmovimento\ncontínuo.', headlineCol: UARINI.black, sub: 'Símbolo ancestral indígena de transformação e conexão entre realidades.', subCol: UARINI.black }, render: (d) => (
{d.eyebrow}
{d.headline}
{d.sub}
), caption: (d) => `${d.headline.replace(/\n/g, ' ')} ${d.sub} #studiouarini #espiral #ancestralidade`, }; // ────────────────────────── 04 · SERVIÇOS / TABELA ────────────────────────── const TplServices = { id: 'service-corte', label: 'Tabela de serviços', group: 'POST · 1080×1080', size: { w: 1080, h: 1080 }, fields: [ ...COLOR_FIELDS({ bgColor: UARINI.black, textColor: UARINI.cream }), F.color('accentColor', 'Cor do destaque', UARINI.green), F.heading('Espiral'), F.slider('spiralSz', 'Tamanho', 1, 0.3, 2, 0.05), ...TXT_GROUP('eyebrow', 'Etiqueta', UARINI.green, F.text('eyebrow', 'Texto', 'SERVIÇOS · 03')), F.rows('services', 'Serviços (nome · duração · preço)', [ ['Corte clássico', '60 min', 'R$ 90'], ['Corte + barba', '90 min', 'R$ 140'], ['Terapia capilar', '120 min', 'R$ 220'], ['Ritual completo', '180 min', 'R$ 320'], ], ['Nome', 'Duração', 'Preço'] ), ...TXT_GROUP('footer', 'Rodapé', UARINI.cream, F.text('footer', 'Texto', 'AGENDA ABERTA TODA SEGUNDA · LINK NA BIO')), ], defaults: { bgColor: UARINI.black, textColor: UARINI.cream, accentColor: UARINI.green, spiralSz: 1, eyebrow: 'SERVIÇOS · 03', eyebrowCol: UARINI.green, services: [ ['Corte clássico', '60 min', 'R$ 90'], ['Corte + barba', '90 min', 'R$ 140'], ['Terapia capilar', '120 min', 'R$ 220'], ['Ritual completo', '180 min', 'R$ 320'], ], footer: 'AGENDA ABERTA TODA SEGUNDA · LINK NA BIO', footerCol: UARINI.cream, }, render: (d) => (
{d.eyebrow}
{d.services.map(([n, t, p], i) => (
{n}
{t}
{p}
))}
{d.footer}
), caption: (d) => { const lines = d.services.map(([n, t, p]) => `· ${n} — ${t} — ${p}`).join('\n'); return `tabela atualizada de serviços ✦\n\n${lines}\n\n${d.footer.toLowerCase()}`; }, }; // ─────────────────────────── 05 · ANTES / DEPOIS ──────────────────────────── const TplBeforeAfter = { id: 'before-after', label: 'Antes / depois', group: 'POST · 1080×1080', size: { w: 1080, h: 1080 }, fields: [ F.media('beforeMedia', 'Subir foto/vídeo · ANTES'), F.media('afterMedia', 'Subir foto/vídeo · DEPOIS'), F.photoTone('beforeTone', 'Tom ANTES (se sem foto)', 'smoke'), F.photoTone('afterTone', 'Tom DEPOIS (se sem foto)', 'moss'), ...COLOR_FIELDS({ bgColor: UARINI.black, textColor: UARINI.cream }), F.color('accentColor', 'Cor do destaque', UARINI.green), F.heading('Etiquetas ANTES / DEPOIS'), F.slider('lblSz', 'Tamanho', 1, 0.5, 2, 0.05), F.color('lblCol', 'Cor', UARINI.cream), ...TXT_GROUP('rid', 'Ritual nº', UARINI.green, F.text('rid', 'Texto', 'RITUAL #207')), ...TXT_GROUP('name', 'Serviço', UARINI.cream, F.text('name', 'Texto', 'Hidratação + corte navalhado')), ], defaults: { beforeMedia: null, afterMedia: null, beforeTone: 'smoke', afterTone: 'moss', rid: 'RITUAL #207', ridCol: UARINI.green, name: 'Hidratação + corte navalhado', nameCol: UARINI.cream, bgColor: UARINI.black, textColor: UARINI.cream, accentColor: UARINI.green, lblSz: 1, lblCol: UARINI.cream }, render: (d) => (
ANTES
DEPOIS
{d.rid}
{d.name}
), caption: (d) => `${d.rid.toLowerCase()} · ${d.name.toLowerCase()} ✦ antes e depois — sem filtro, só ritual. agendar pelo link da bio.`, }; // ─────────────────────── 06 · FOTO + ASSINATURA (BLEED) ───────────────────── const TplPhotoWatermark = { id: 'photo-watermark', label: 'Foto + assinatura', group: 'POST · 1080×1080', size: { w: 1080, h: 1080 }, fields: [ F.media('media', 'Subir foto/vídeo'), F.photoTone('photo', 'Tom (se sem foto)', 'sun'), ...COLOR_FIELDS({ bgColor: UARINI.black, textColor: UARINI.cream }), F.heading('Espiral'), F.slider('spiralSz', 'Tamanho', 1, 0.3, 2, 0.05), ...TXT_GROUP('credit', 'Crédito / etiqueta', UARINI.cream, F.text('credit', 'Texto', 'FILME 35MM · TROYAN')), ], defaults: { media: null, photo: 'sun', credit: 'FILME 35MM · TROYAN', creditCol: UARINI.cream, bgColor: UARINI.black, textColor: UARINI.cream, spiralSz: 1 }, render: (d) => (
{d.credit}
), caption: (d) => `${d.credit.toLowerCase()} ✦ clima de casa, registro do ritual. #studiouarini #processo`, }; // ─────────────────────────── 07 · AGENDA / CTA ────────────────────────────── const TplAgenda = { id: 'cta-agenda', label: 'Agenda aberta', group: 'POST · 1080×1080', size: { w: 1080, h: 1080 }, fields: [ ...COLOR_FIELDS({ bgColor: UARINI.cream, textColor: UARINI.black }), F.color('accentColor', 'Cor do destaque', UARINI.green), F.color('btnBg', 'Cor do botão', UARINI.black), F.color('btnText', 'Texto do botão', UARINI.cream), F.toggle('showButton', 'CTA', true, { onLabel: 'Botão', offLabel: 'Só texto' }), F.heading('Espiral'), F.slider('spiralSz', 'Tamanho', 1, 0.3, 1.6, 0.05), ...TXT_GROUP('eyebrow', 'Etiqueta', UARINI.green, F.text('eyebrow', 'Texto', 'AGENDA · MAIO')), ...TXT_GROUP('title', 'Manchete', UARINI.black, F.textarea('title', 'Texto', 'Abriu\nhorário.')), ...TXT_GROUP('sub', 'Subtexto', UARINI.black, F.textarea('sub', 'Texto', 'Quartas e sextas a partir das 14h. Vagas limitadas — link na bio.')), ...TXT_GROUP('handle', '@ no rodapé', UARINI.black, F.text('handle', 'Texto', '@STUDIOUARINI')), F.text('cta', 'CTA do botão', 'RESERVAR →'), ], defaults: { bgColor: UARINI.cream, textColor: UARINI.black, accentColor: UARINI.green, btnBg: UARINI.black, btnText: UARINI.cream, showButton: true, spiralSz: 1, eyebrow: 'AGENDA · MAIO', eyebrowCol: UARINI.green, title: 'Abriu\nhorário.', titleCol: UARINI.black, sub: 'Quartas e sextas a partir das 14h. Vagas limitadas — link na bio.', subCol: UARINI.black, handle: '@STUDIOUARINI', handleCol: UARINI.black, cta: 'RESERVAR →', }, render: (d) => (
{d.eyebrow}
{d.title}
{d.sub}
{d.handle}
{d.showButton === false ?
{d.cta}
:
{d.cta}
}
), caption: (d) => `${d.title.replace(/\n/g, ' ')} ✦ ${d.sub} ${d.handle.toLowerCase()} — ${d.cta.toLowerCase()}`, }; // ─────────────────────────── 08 · TERAPIA CAPILAR ─────────────────────────── const TplTerapia = { id: 'terapia', label: 'Terapia capilar', group: 'POST · 1080×1080', size: { w: 1080, h: 1080 }, fields: [ F.media('media', 'Subir foto/vídeo de fundo'), F.photoTone('photo', 'Tom (se sem foto)', 'moss'), ...COLOR_FIELDS({ bgColor: UARINI.black, textColor: UARINI.cream }), F.color('accentColor', 'Cor do destaque', UARINI.green), F.heading('Espiral / listras'), F.slider('spiralSz', 'Tamanho', 1, 0.3, 1.8, 0.05), ...TXT_GROUP('eyebrow', 'Etiqueta', UARINI.green, F.text('eyebrow', 'Texto', 'NOVO · 04')), ...TXT_GROUP('title', 'Manchete', UARINI.cream, F.textarea('title', 'Texto', 'Terapia\nCapilar\nAncestral')), ...TXT_GROUP('sub', 'Subtexto', UARINI.cream, F.textarea('sub', 'Texto', 'Massagem, óleos vegetais, escalda-pés.\nCuidado que vem antes da estética.')), ], defaults: { media: null, eyebrow: 'NOVO · 04', eyebrowCol: UARINI.green, title: 'Terapia\nCapilar\nAncestral', titleCol: UARINI.cream, sub: 'Massagem, óleos vegetais, escalda-pés.\nCuidado que vem antes da estética.', subCol: UARINI.cream, photo: 'moss', bgColor: UARINI.black, textColor: UARINI.cream, accentColor: UARINI.green, spiralSz: 1, }, render: (d) => (
{d.eyebrow}
{d.title}
{d.sub}
), caption: (d) => `${d.title.replace(/\n/g, ' ').toLowerCase()} ✦ ${d.sub} agende sua sessão pelo link da bio.`, }; // ─────────────────────── 09 · SABEDORIA / CITAÇÃO ─────────────────────────── const TplWisdom = { id: 'wisdom', label: 'Sabedoria · citação', group: 'POST · 1080×1080', size: { w: 1080, h: 1080 }, fields: [ ...COLOR_FIELDS({ bgColor: UARINI.cream, textColor: UARINI.black }), F.color('accentColor', 'Cor do destaque', UARINI.green), F.heading('Espiral'), F.slider('spiralSz', 'Tamanho', 1, 0.3, 2, 0.05), ...TXT_GROUP('eyebrow', 'Etiqueta', UARINI.green, F.text('eyebrow', 'Texto', 'SABEDORIA · 02')), ...TXT_GROUP('quote', 'Citação', UARINI.black, F.textarea('quote', 'Texto', '"Cabelo é raiz.\nCorte é rito.\nAqui a gente cuida\ndo que vem\nde longe."')), ...TXT_GROUP('author', 'Autora', UARINI.black, F.text('author', 'Texto', 'NAIANE TENÓRIO')), ...TXT_GROUP('role', 'Função', UARINI.black, F.text('role', 'Texto', 'BARBEIRA · TERAPEUTA')), ], defaults: { bgColor: UARINI.cream, textColor: UARINI.black, accentColor: UARINI.green, spiralSz: 1, eyebrow: 'SABEDORIA · 02', eyebrowCol: UARINI.green, quote: '"Cabelo é raiz.\nCorte é rito.\nAqui a gente cuida\ndo que vem\nde longe."', quoteCol: UARINI.black, author: 'NAIANE TENÓRIO', authorCol: UARINI.black, role: 'BARBEIRA · TERAPEUTA', roleCol: UARINI.black, }, render: (d) => (
{d.eyebrow}
{d.quote}
{d.author}
{d.role}
STUDIO UARINÍ
), caption: (d) => `${d.quote.replace(/\n/g, ' ')} — ${d.author.toLowerCase()}, ${d.role.toLowerCase()} #studiouarini #ancestralidade #sabedoria`, }; // ─────────────────────────── STORY · BEM-VINDX ────────────────────────────── const TplStoryWelcome = { id: 'story-welcome', label: 'Story · Bem-vindx', group: 'STORY · 1080×1920', size: { w: 1080, h: 1920 }, fields: [ ...COLOR_FIELDS({ bgColor: UARINI.black, textColor: UARINI.cream }), F.color('accentColor', 'Cor do destaque', UARINI.green), F.text('eyebrow', 'Etiqueta superior', 'CASA NOVA · 2024'), F.textarea('title', 'Manchete grande', 'Seja\nbem-vindx.'), F.textarea('sub', 'Subtexto', 'Studio Uariní é\nbarbearia, terapia\ne territorio ancestral.'), F.text('cta', 'CTA inferior', 'ARRASTA →'), ], defaults: { bgColor: UARINI.black, textColor: UARINI.cream, accentColor: UARINI.green, eyebrow: 'CASA NOVA · 2024', title: 'Seja\nbem-vindx.', sub: 'Studio Uariní é\nbarbearia, terapia\ne territorio ancestral.', cta: 'ARRASTA →', }, render: (d) => (
{d.eyebrow}
{d.title}
{d.sub}
{d.cta}
), caption: (d) => `story · ${d.eyebrow.toLowerCase()} texto: "${d.title.replace(/\n/g, ' ')}" sub: ${d.sub.replace(/\n/g, ' ')}`, }; // ─────────────────────────── STORY · NOVO SERVIÇO ─────────────────────────── const TplStoryService = { id: 'story-service', label: 'Story · Novo serviço', group: 'STORY · 1080×1920', size: { w: 1080, h: 1920 }, fields: [ ...COLOR_FIELDS({ bgColor: UARINI.green, textColor: UARINI.black }), F.text('badge', 'Etiqueta', 'NOVO SERVIÇO'), F.textarea('title', 'Nome do serviço', 'Terapia\ncapilar.'), F.text('price', 'Preço', 'A PARTIR DE R$ 220'), F.text('duration', 'Duração', '120 MIN · COM HORA MARCADA'), ], defaults: { bgColor: UARINI.green, textColor: UARINI.black, badge: 'NOVO SERVIÇO', title: 'Terapia\ncapilar.', price: 'A PARTIR DE R$ 220', duration: '120 MIN · COM HORA MARCADA', }, render: (d) => (
{d.badge}
{d.title}
{d.price}
{d.duration}
), caption: (d) => `story · novo serviço ${d.title.replace(/\n/g, ' ')} — ${d.price.toLowerCase()} ${d.duration.toLowerCase()}`, }; // ─────────────────────────── STORY · ENQUETE ──────────────────────────────── const TplStoryPoll = { id: 'story-poll', label: 'Story · Enquete', group: 'STORY · 1080×1920', size: { w: 1080, h: 1920 }, fields: [ F.media('media', 'Subir foto/vídeo de fundo'), F.photoTone('photo', 'Tom (se sem foto)', 'warm'), ...COLOR_FIELDS({ bgColor: UARINI.black, textColor: UARINI.cream }), F.color('accentColor', 'Cor do destaque', UARINI.green), F.text('eyebrow', 'Etiqueta', 'ENQUETE'), F.textarea('question', 'Pergunta', 'Qual é o\nseu ritual?'), F.text('optA', 'Opção A (vazio = oculta)', 'Corte clássico'), F.text('optB', 'Opção B (vazio = oculta)', 'Terapia capilar'), ], defaults: { media: null, eyebrow: 'ENQUETE', question: 'Qual é o\nseu ritual?', optA: 'Corte clássico', optB: 'Terapia capilar', photo: 'warm', bgColor: UARINI.black, textColor: UARINI.cream, accentColor: UARINI.green, }, render: (d) => (
{d.eyebrow}
{d.question}
{[d.optA, d.optB].filter((o) => o && o.trim()).map((opt, i, arr) => (
1 ? d.textColor : 'transparent', color: i === 0 && arr.length > 1 ? UARINI.black : d.textColor, border: `2px solid ${d.textColor}`, borderRadius: 200, fontSize: 44, fontWeight: 600, letterSpacing: '0.04em', textAlign: arr.length === 1 ? 'center' : 'left', }}>{opt}
))}
), caption: (d) => `story · enquete ${d.question.replace(/\n/g, ' ')} A: ${d.optA} · B: ${d.optB}`, }; // ─────────────────────────── QR CODE COMPONENT ───────────────────────────── function QRBlock({ url = 'https://uarinistudio.com', size = 360, color = '#0E0D0D', bg = '#ffffff' }) { const ref = React.useRef(null); React.useEffect(() => { if (!ref.current || !window.QRCode || !url) return; ref.current.innerHTML = ''; try { new window.QRCode(ref.current, { text: url, width: size, height: size, colorDark: color, colorLight: bg, correctLevel: window.QRCode.CorrectLevel.H, }); } catch (e) { /* noop */ } }, [url, size, color, bg]); return
; } window.QRBlock = QRBlock; // ─────────────────────────── 13 · CARTÃO DE VISITA (FRENTE) ───────────────── const TplCardFront = { id: 'card-front', label: 'Cartão · frente', group: 'CARTÃO · 90×50mm', size: { w: 1063, h: 591 }, // 300dpi · 90x50mm fields: [ ...COLOR_FIELDS({ bgColor: UARINI.black, textColor: UARINI.cream }), F.color('accentColor', 'Cor do destaque', UARINI.green), F.text('name', 'Nome', 'Naiane Tenório'), F.text('role', 'Função', 'Barbeira · Terapeuta'), F.text('tag', 'Etiqueta sup.', 'STUDIO UARINÍ'), ], defaults: { bgColor: UARINI.black, textColor: UARINI.cream, accentColor: UARINI.green, name: 'Naiane Tenório', role: 'Barbeira · Terapeuta', tag: 'STUDIO UARINÍ' }, render: (d) => (
{d.tag}
{d.name}
{d.role}
), caption: (d) => `cartão · ${d.name.toLowerCase()} — ${d.role.toLowerCase()}`, }; // ─────────────────────── 14 · CARTÃO DE VISITA (VERSO/QR) ─────────────────── const TplCardBack = { id: 'card-back', label: 'Cartão · verso (QR)', group: 'CARTÃO · 90×50mm', size: { w: 1063, h: 591 }, fields: [ ...COLOR_FIELDS({ bgColor: UARINI.cream, textColor: UARINI.black }), F.color('accentColor', 'Cor do destaque', UARINI.green), F.text('url', 'Link do QR Code', 'https://uarinistudio.com'), F.text('handle', '@', '@STUDIOUARINI'), F.text('phone', 'Telefone', '+55 11 9 0000-0000'), F.text('addr', 'Endereço', 'R. ALAMEDA 123 · SP'), F.text('cta', 'CTA do QR', 'AGENDE PELO LINK'), ], defaults: { bgColor: UARINI.cream, textColor: UARINI.black, accentColor: UARINI.green, url: 'https://uarinistudio.com', handle: '@STUDIOUARINI', phone: '+55 11 9 0000-0000', addr: 'R. ALAMEDA 123 · SP', cta: 'AGENDE PELO LINK' }, render: (d) => (
{d.cta}
{d.handle}
{d.phone}
{d.addr}
), caption: (d) => `cartão · verso · ${d.url}`, }; // ─────────────────────── 15 · PLACA QR (RETRATO A4) ───────────────────────── const TplQRSign = { id: 'qr-sign', label: 'Placa QR · retrato', group: 'PLACA · QR CODE', size: { w: 1240, h: 1754 }, // A4 @ 150dpi fields: [ ...COLOR_FIELDS({ bgColor: UARINI.cream, textColor: UARINI.black }), F.color('accentColor', 'Cor do destaque', UARINI.green), F.text('url', 'Link do QR Code', 'https://uarinistudio.com/agendar'), F.text('eyebrow', 'Etiqueta', 'STUDIO UARINÍ'), F.textarea('title', 'Título', 'Aponte\na câmera.'), F.textarea('sub', 'Subtexto', 'Agende sua sessão direto pelo link.\nAtendimento com hora marcada.'), F.text('cta', 'CTA inferior', 'WWW.UARINISTUDIO.COM'), ], defaults: { bgColor: UARINI.cream, textColor: UARINI.black, accentColor: UARINI.green, url: 'https://uarinistudio.com/agendar', eyebrow: 'STUDIO UARINÍ', title: 'Aponte\na câmera.', sub: 'Agende sua sessão direto pelo link.\nAtendimento com hora marcada.', cta: 'WWW.UARINISTUDIO.COM' }, render: (d) => (
{d.eyebrow}
{d.title}
{d.sub}
{d.cta}
), caption: (d) => `placa qr · ${d.url}`, }; // ─────────────────────── 16 · PLACA QR · QUADRADA ──────────────────────────── const TplQRSquare = { id: 'qr-square', label: 'Placa QR · quadrada', group: 'PLACA · QR CODE', size: { w: 1080, h: 1080 }, fields: [ ...COLOR_FIELDS({ bgColor: UARINI.green, textColor: UARINI.black }), F.color('accentColor', 'Cor do destaque', UARINI.cream), F.text('url', 'Link do QR Code', 'https://uarinistudio.com'), F.text('eyebrow', 'Etiqueta', 'WIFI · MENU · AGENDA'), F.textarea('title', 'Título', 'Escaneie\npara entrar.'), F.text('cta', 'CTA do QR', 'UARINISTUDIO.COM'), ], defaults: { bgColor: UARINI.green, textColor: UARINI.black, accentColor: UARINI.cream, url: 'https://uarinistudio.com', eyebrow: 'WIFI · MENU · AGENDA', title: 'Escaneie\npara entrar.', cta: 'UARINISTUDIO.COM' }, render: (d) => (
{d.eyebrow}
{d.title}
{d.cta}
), caption: (d) => `placa qr quadrada · ${d.url}`, }; // ─────────────────────── 17 · ADESIVO REDONDO ─────────────────────── const TplStickerRound = { id: 'sticker-round', label: 'Adesivo redondo', group: 'ADESIVO · DIE-CUT', size: { w: 1080, h: 1080 }, fields: [ ...COLOR_FIELDS({ bgColor: UARINI.green, textColor: UARINI.cream }), F.color('accentColor', 'Cor do destaque', UARINI.cream), F.text('top', 'Texto curvado superior', '✦ STUDIO UARINÍ ✦ BARBEARIA '), F.text('center', 'Texto central', 'ESPIRAL'), F.text('bottom', 'Linha inferior', 'EST. 2024 · SP'), ], defaults: { bgColor: UARINI.green, textColor: UARINI.cream, accentColor: UARINI.cream, top: '✦ STUDIO UARINÍ ✦ BARBEARIA ', center: 'ESPIRAL', bottom: 'EST. 2024 · SP' }, render: (d) => (
{d.top}{d.top}
{d.center}
{d.bottom}
), caption: () => `adesivo redondo · die-cut`, }; // ─────────────────────── 18 · ADESIVO RETANGULAR ─────────────────────── const TplStickerRect = { id: 'sticker-rect', label: 'Adesivo retangular', group: 'ADESIVO · DIE-CUT', size: { w: 1500, h: 600 }, fields: [ ...COLOR_FIELDS({ bgColor: UARINI.cream, textColor: UARINI.black }), F.color('accentColor', 'Cor do destaque', UARINI.green), F.textarea('text', 'Frase', 'a espiral\né raiz.'), F.text('handle', '@ rodapé', '@STUDIOUARINI'), ], defaults: { bgColor: UARINI.cream, textColor: UARINI.black, accentColor: UARINI.green, text: 'a espiral\né raiz.', handle: '@STUDIOUARINI' }, render: (d) => (
{d.text}
{d.handle}
), caption: () => `adesivo retangular · slogan`, }; // ─────────────────────── 19 · STORY · DEPOIMENTO ─────────────────────── const TplStoryQuote = { id: 'story-quote', label: 'Story · Depoimento', group: 'STORY · 1080×1920', size: { w: 1080, h: 1920 }, fields: [ ...COLOR_FIELDS({ bgColor: UARINI.cream, textColor: UARINI.black }), F.color('accentColor', 'Cor do destaque', UARINI.green), F.text('eyebrow', 'Etiqueta', 'O QUE FALAM ✦'), F.textarea('quote', 'Depoimento', '"Saí leve. Como se tivessem cuidado da raiz, não só do cabelo."'), F.text('author', 'Autor', '— ANA M.'), ], defaults: { bgColor: UARINI.cream, textColor: UARINI.black, accentColor: UARINI.green, eyebrow: 'O QUE FALAM ✦', quote: '"Saí leve. Como se tivessem cuidado da raiz, não só do cabelo."', author: '— ANA M.' }, render: (d) => (
{d.eyebrow}
{d.quote}
{d.author}
), caption: () => `story · depoimento`, }; // ─────────────────────── 20 · STORY · CONTAGEM ─────────────────────── const TplStoryCountdown = { id: 'story-countdown', label: 'Story · Contagem', group: 'STORY · 1080×1920', size: { w: 1080, h: 1920 }, fields: [ ...COLOR_FIELDS({ bgColor: UARINI.black, textColor: UARINI.cream }), F.color('accentColor', 'Cor do destaque', UARINI.green), F.text('eyebrow', 'Etiqueta', 'CONTAGEM ✦'), F.text('number', 'Número grande', '03'), F.textarea('label', 'Legenda', 'dias para\na próxima\nlua nova.'), F.text('cta', 'CTA inferior', 'TOQUE PARA LEMBRAR'), ], defaults: { bgColor: UARINI.black, textColor: UARINI.cream, accentColor: UARINI.green, eyebrow: 'CONTAGEM ✦', number: '03', label: 'dias para\na próxima\nlua nova.', cta: 'TOQUE PARA LEMBRAR' }, render: (d) => (
{d.eyebrow}
{d.number}
{d.label}
{d.cta}
), caption: () => `story · contagem`, }; // ─────────────────────── 21 · STORY · PRODUTO/PREÇO ─────────────────────── const TplStoryPrice = { id: 'story-price', label: 'Story · Preço destaque', group: 'STORY · 1080×1920', size: { w: 1080, h: 1920 }, fields: [ F.media('media', 'Foto/vídeo de fundo'), F.photoTone('photo', 'Tom (se sem foto)', 'sun'), ...COLOR_FIELDS({ bgColor: UARINI.black, textColor: UARINI.cream }), F.color('accentColor', 'Destaque', UARINI.green), F.text('eyebrow', 'Etiqueta', 'OFERTA · MAIO'), F.textarea('title', 'Nome', 'Ritual\ncompleto.'), F.text('priceFrom', 'De', 'DE R$ 380'), F.text('priceTo', 'Por', 'R$ 290'), F.text('cta', 'CTA', 'ARRASTA PARA AGENDAR'), ], defaults: { media: null, photo: 'sun', bgColor: UARINI.black, textColor: UARINI.cream, accentColor: UARINI.green, eyebrow: 'OFERTA · MAIO', title: 'Ritual\ncompleto.', priceFrom: 'DE R$ 380', priceTo: 'R$ 290', cta: 'ARRASTA PARA AGENDAR' }, render: (d) => (
{d.eyebrow}
{d.title}
{d.priceFrom}
{d.priceTo}
{d.cta}
), caption: () => `story · oferta`, }; // ─────────────────────── 22 · STORY · PERGUNTA ─────────────────────── const TplStoryQuestion = { id: 'story-question', label: 'Story · Pergunta', group: 'STORY · 1080×1920', size: { w: 1080, h: 1920 }, fields: [ ...COLOR_FIELDS({ bgColor: UARINI.green, textColor: UARINI.black }), F.color('boxColor', 'Cor da caixa', UARINI.cream), F.text('eyebrow', 'Etiqueta', 'ME CONTA ✦'), F.textarea('question', 'Pergunta', 'O que você\nesperava\ndo seu ritual?'), F.text('placeholder', 'Placeholder caixa', 'escreve aqui...'), ], defaults: { bgColor: UARINI.green, textColor: UARINI.black, boxColor: UARINI.cream, eyebrow: 'ME CONTA ✦', question: 'O que você\nesperava\ndo seu ritual?', placeholder: 'escreve aqui...' }, render: (d) => (
{d.eyebrow}
{d.question}
{d.placeholder}
), caption: () => `story · pergunta`, }; // ─────────────────────── 23 · CARROSSEL · CAPA ─────────────────────── const TplCarouselCover = { id: 'carousel-cover', label: 'Carrossel · capa', group: 'CARROSSEL · 3 SLIDES', size: { w: 1080, h: 1350 }, fields: [ ...COLOR_FIELDS({ bgColor: UARINI.black, textColor: UARINI.cream }), F.color('accentColor', 'Destaque', UARINI.green), F.text('count', 'Total de slides', '3'), F.text('eyebrow', 'Etiqueta', 'GUIA · 01'), F.textarea('title', 'Manchete', '3 sinais\nde que sua\nraiz pede\ncuidado.'), F.text('cta', 'CTA inferior', 'ARRASTA →'), ], defaults: { bgColor: UARINI.black, textColor: UARINI.cream, accentColor: UARINI.green, count: '3', eyebrow: 'GUIA · 01', title: '3 sinais\nde que sua\nraiz pede\ncuidado.', cta: 'ARRASTA →' }, render: (d) => (
{d.eyebrow}
1 / {d.count}
{d.title}
{d.cta}
), caption: () => `carrossel · capa`, }; // ─────────────────────── 24 · CARROSSEL · CONTEÚDO ─────────────────────── const TplCarouselContent = { id: 'carousel-content', label: 'Carrossel · conteúdo', group: 'CARROSSEL · 3 SLIDES', size: { w: 1080, h: 1350 }, fields: [ ...COLOR_FIELDS({ bgColor: UARINI.cream, textColor: UARINI.black }), F.color('accentColor', 'Destaque', UARINI.green), F.text('index', 'Slide n', '2'), F.text('count', 'Total', '3'), F.text('badge', 'Numeração', '01'), F.textarea('title', 'Título', 'Pontas\nressecadas\n+ couro\nirritado.'), F.textarea('body', 'Corpo', 'É sinal de que sua raiz pede pausa, não só corte. Terapia capilar age antes da estética: hidrata, descansa, reorganiza.'), ], defaults: { bgColor: UARINI.cream, textColor: UARINI.black, accentColor: UARINI.green, index: '2', count: '3', badge: '01', title: 'Pontas\nressecadas\n+ couro\nirritado.', body: 'É sinal de que sua raiz pede pausa, não só corte. Terapia capilar age antes da estética: hidrata, descansa, reorganiza.' }, render: (d) => (
{d.badge}
{d.index} / {d.count}
{d.title}
{d.body}
), caption: () => `carrossel · conteúdo`, }; // ─────────────────────── 25 · CARROSSEL · FECHAMENTO ─────────────────────── const TplCarouselClose = { id: 'carousel-close', label: 'Carrossel · final/CTA', group: 'CARROSSEL · 3 SLIDES', size: { w: 1080, h: 1350 }, fields: [ ...COLOR_FIELDS({ bgColor: UARINI.green, textColor: UARINI.black }), F.color('accentColor', 'Destaque', UARINI.cream), F.color('btnBg', 'Cor do botão', UARINI.black), F.color('btnText', 'Texto do botão', UARINI.cream), F.toggle('showButton', 'CTA', true, { onLabel: 'Botão', offLabel: 'Só texto' }), F.text('count', 'Total', '3'), F.text('eyebrow', 'Etiqueta', 'AGENDE'), F.textarea('title', 'Manchete', 'Hora de\ncuidar da\nsua raiz.'), F.textarea('sub', 'Sub', 'Atendimento com hora marcada.\nVagas limitadas em maio.'), F.text('cta', 'Texto do botão', 'AGENDAR →'), F.text('handle', '@ rodapé', '@STUDIOUARINI · LINK NA BIO'), ], defaults: { bgColor: UARINI.green, textColor: UARINI.black, accentColor: UARINI.cream, btnBg: UARINI.black, btnText: UARINI.cream, count: '3', eyebrow: 'AGENDE', title: 'Hora de\ncuidar da\nsua raiz.', sub: 'Atendimento com hora marcada.\nVagas limitadas em maio.', cta: 'AGENDAR →', handle: '@STUDIOUARINI · LINK NA BIO' }, render: (d) => (
{d.eyebrow}
{d.count} / {d.count}
{d.title}
{d.sub}
{d.handle}
{d.showButton === false ?
{d.cta}
:
{d.cta}
}
), caption: () => `carrossel · final/CTA`, }; // ─────────────────────── 26 · CARTÃO VIP · FRENTE ─────────────────────── const TplVIPFront = { id: 'vip-front', label: 'Card VIP · frente', group: 'CARTÃO FIDELIDADE · 50×75mm', size: { w: 591, h: 886 }, fields: [ ...COLOR_FIELDS({ bgColor: UARINI.black, textColor: UARINI.cream }), F.color('accentColor', 'Cor da espiral', UARINI.green), F.text('top', 'Etiqueta superior', 'CARD VIP'), F.text('num', 'Numeração', 'N° 014'), ], defaults: { bgColor: UARINI.black, textColor: UARINI.cream, accentColor: UARINI.green, top: 'CARD VIP', num: 'N° 014' }, render: (d) => (
{d.top}
{d.num}
), caption: () => `card vip · frente`, }; // ─────────────────────── 27 · CARTÃO VIP · VERSO (FIDELIDADE) ─────────────────────── const TplVIPBack = { id: 'vip-back', label: 'Card VIP · verso (fidelidade)', group: 'CARTÃO FIDELIDADE · 50×75mm', size: { w: 591, h: 886 }, fields: [ ...COLOR_FIELDS({ bgColor: UARINI.black, textColor: UARINI.cream }), F.color('accentColor', 'Cor do destaque', UARINI.green), F.text('top', 'Etiqueta superior', 'CARD VIP'), F.text('label', 'Etiqueta da progressão', '10 RITUAIS · 1 PRESENTE'), F.text('stamps', 'Selos preenchidos (0-10)', '4'), F.text('total', 'Total de selos', '10'), F.text('clientLabel', 'Etiqueta cliente', 'CLIENTE'), F.text('clientName', 'Nome do cliente', ''), F.text('footer', 'Rodapé', 'STUDIO UARINÍ · NAIANE TENÓRIO'), ], defaults: { bgColor: UARINI.black, textColor: UARINI.cream, accentColor: UARINI.green, top: 'CARD VIP', label: '10 RITUAIS · 1 PRESENTE', stamps: '4', total: '10', clientLabel: 'CLIENTE', clientName: '', footer: 'STUDIO UARINÍ · NAIANE TENÓRIO' }, render: (d) => { const stamps = Math.max(0, Math.min(parseInt(d.total)||10, parseInt(d.stamps)||0)); const total = Math.max(1, parseInt(d.total)||10); const cols = total > 8 ? 5 : Math.min(total, 4); return (
{d.top}
{d.label}
{Array.from({ length: total }).map((_, i) => (
{i < stamps ? '✓' : ''}
))}
{d.clientLabel}
{d.clientName || '_________________________'}
{d.footer}
); }, caption: (d) => `card vip · ${d.stamps}/${d.total} rituais`, }; // ─────────────────────── 28 · ADESIVO · SÓ ESPIRAL (REDONDO) ───────────────── const TplSpiralRound = { id: 'spiral-round', label: 'Adesivo · só espiral redondo', group: 'ADESIVO · ESPIRAL', size: { w: 1080, h: 1080 }, fields: [ ...COLOR_FIELDS({ bgColor: UARINI.green, textColor: UARINI.cream }), F.text('rotation', 'Rotação (graus)', '0'), F.text('sizePct', 'Tamanho (%)', '70'), ], defaults: { bgColor: UARINI.green, textColor: UARINI.cream, rotation: '0', sizePct: '70' }, render: (d) => { const pct = Math.max(20, Math.min(95, parseFloat(d.sizePct) || 70)); const sz = 1000 * pct / 100; const rot = parseFloat(d.rotation) || 0; return (
); }, caption: () => `adesivo · espiral redondo`, }; // ─────────────────────── 29 · ADESIVO · ESPIRAL QUADRADO ───────────────── const TplSpiralSquare = { id: 'spiral-square', label: 'Adesivo · espiral quadrado', group: 'ADESIVO · ESPIRAL', size: { w: 1080, h: 1080 }, fields: [ ...COLOR_FIELDS({ bgColor: UARINI.cream, textColor: UARINI.green }), F.text('rotation', 'Rotação (graus)', '-8'), F.text('sizePct', 'Tamanho (%)', '75'), F.text('radius', 'Borda arredondada (px)', '60'), ], defaults: { bgColor: UARINI.cream, textColor: UARINI.green, rotation: '-8', sizePct: '75', radius: '60' }, render: (d) => { const pct = Math.max(20, Math.min(95, parseFloat(d.sizePct) || 75)); const sz = 1000 * pct / 100; const rot = parseFloat(d.rotation) || 0; const radius = parseInt(d.radius) || 0; return (
); }, caption: () => `adesivo · espiral quadrado`, }; // ─────────────────────── 30 · ADESIVO · ESPIRAL OFFSET ───────────────── const TplSpiralOffset = { id: 'spiral-offset', label: 'Adesivo · espiral c/ borda', group: 'ADESIVO · ESPIRAL', size: { w: 1080, h: 1080 }, fields: [ ...COLOR_FIELDS({ bgColor: UARINI.black, textColor: UARINI.green }), F.color('outlineColor', 'Cor da borda', UARINI.cream), F.text('rotation', 'Rotação (graus)', '12'), ], defaults: { bgColor: UARINI.black, textColor: UARINI.green, outlineColor: UARINI.cream, rotation: '12' }, render: (d) => { const rot = parseFloat(d.rotation) || 0; return (
{/* outline backdrop offset */}
); }, caption: () => `adesivo · espiral com borda offset`, }; // ─────────────────────── 31 · BOLINHAS DE DESTAQUE (HIGHLIGHTS) ───────────── // Each highlight cover: 1080×1080 circle, brand color + label function HighlightCover({ bg, fg, label, glyph, sizePct, rotation = 0 }) { const sz = 1000 * (sizePct || 0.6); return (
{glyph}
); } const TplHighlightSpiral = { id: 'highlight-spiral', label: 'Destaque · Espiral', group: 'DESTAQUES · BOLINHAS', size: { w: 1080, h: 1080 }, fields: [ ...COLOR_FIELDS({ bgColor: UARINI.green, textColor: UARINI.cream }), F.text('rotation', 'Rotação', '-6'), ], defaults: { bgColor: UARINI.green, textColor: UARINI.cream, rotation: '-6' }, render: (d) => ( } /> ), caption: () => `destaque · espiral`, }; const TplHighlightLetter = { id: 'highlight-letter', label: 'Destaque · letra/inicial', group: 'DESTAQUES · BOLINHAS', size: { w: 1080, h: 1080 }, fields: [ ...COLOR_FIELDS({ bgColor: UARINI.cream, textColor: UARINI.black }), F.text('letter', 'Letra/glifo', 'U'), ], defaults: { bgColor: UARINI.cream, textColor: UARINI.black, letter: 'U' }, render: (d) => ( {d.letter}
} /> ), caption: () => `destaque · letra`, }; const TplHighlightWord = { id: 'highlight-word', label: 'Destaque · palavra curta', group: 'DESTAQUES · BOLINHAS', size: { w: 1080, h: 1080 }, fields: [ ...COLOR_FIELDS({ bgColor: UARINI.black, textColor: UARINI.cream }), F.text('word', 'Palavra (curta)', 'AGENDA'), ], defaults: { bgColor: UARINI.black, textColor: UARINI.cream, word: 'AGENDA' }, render: (d) => ( 6 ? 140 : 180, fontWeight: 700, letterSpacing: '0.04em', color: d.textColor, textAlign: 'center' }}>{d.word}
} /> ), caption: (d) => `destaque · ${d.word.toLowerCase()}`, }; const TplHighlightNumber = { id: 'highlight-number', label: 'Destaque · número', group: 'DESTAQUES · BOLINHAS', size: { w: 1080, h: 1080 }, fields: [ ...COLOR_FIELDS({ bgColor: UARINI.green, textColor: UARINI.black }), F.text('number', 'Número', '01'), F.text('label', 'Legenda (curta)', 'CORTE'), ], defaults: { bgColor: UARINI.green, textColor: UARINI.black, number: '01', label: 'CORTE' }, render: (d) => (
{d.number}
{d.label}
} /> ), caption: (d) => `destaque · ${d.number} · ${d.label.toLowerCase()}`, }; const TplHighlightStripes = { id: 'highlight-stripes', label: 'Destaque · listras', group: 'DESTAQUES · BOLINHAS', size: { w: 1080, h: 1080 }, fields: [ ...COLOR_FIELDS({ bgColor: UARINI.cream, textColor: UARINI.green }), F.text('label', 'Texto sobreposto', 'NOVO'), ], defaults: { bgColor: UARINI.cream, textColor: UARINI.green, label: 'NOVO' }, render: (d) => ( {d.label &&
{d.label}
} } /> ), caption: (d) => `destaque · ${d.label.toLowerCase()}`, }; const TplHighlightStar = { id: 'highlight-star', label: 'Destaque · símbolo ✦', group: 'DESTAQUES · BOLINHAS', size: { w: 1080, h: 1080 }, fields: [ ...COLOR_FIELDS({ bgColor: UARINI.black, textColor: UARINI.green }), F.text('glyph', 'Símbolo', '✦'), F.text('label', 'Legenda', 'BTS'), ], defaults: { bgColor: UARINI.black, textColor: UARINI.green, glyph: '✦', label: 'BTS' }, render: (d) => (
{d.glyph}
{d.label}
} /> ), caption: (d) => `destaque · ${d.label.toLowerCase()}`, }; // ─────────────────────── 32-34 · CARROSSEL FORMATOS EXTRAS ───────────────── // Carrossel com FOTO const TplCarouselPhoto = { id: 'carousel-photo', label: 'Carrossel · foto + texto', group: 'CARROSSEL · 3 SLIDES', size: { w: 1080, h: 1350 }, fields: [ F.media('media', 'Foto/vídeo'), F.photoTone('photo', 'Tom (se sem foto)', 'warm'), ...COLOR_FIELDS({ bgColor: UARINI.black, textColor: UARINI.cream }), F.color('accentColor', 'Destaque', UARINI.green), F.text('index', 'Slide n', '2'), F.text('count', 'Total', '3'), F.text('eyebrow', 'Etiqueta', 'PROCESSO ✦'), F.textarea('title', 'Manchete', 'Cuidar do\ncabelo é\ncuidar da\nraiz.'), ], defaults: { media: null, photo: 'warm', bgColor: UARINI.black, textColor: UARINI.cream, accentColor: UARINI.green, index: '2', count: '3', eyebrow: 'PROCESSO ✦', title: 'Cuidar do\ncabelo é\ncuidar da\nraiz.' }, render: (d) => (
{d.eyebrow}
{d.index} / {d.count}
{d.title}
), caption: () => `carrossel · foto + texto`, }; // Carrossel · 2 col (lista) const TplCarouselList = { id: 'carousel-list', label: 'Carrossel · lista 2 colunas', group: 'CARROSSEL · 3 SLIDES', size: { w: 1080, h: 1350 }, fields: [ ...COLOR_FIELDS({ bgColor: UARINI.cream, textColor: UARINI.black }), F.color('accentColor', 'Destaque', UARINI.green), F.text('index', 'Slide n', '2'), F.text('count', 'Total', '3'), F.text('eyebrow', 'Etiqueta', 'CHECKLIST'), F.textarea('title', 'Título', 'O que vai\nacontecer\nno seu ritual.'), F.rows('items', 'Itens da lista', [['1', 'Acolhida + escuta'], ['2', 'Massagem capilar'], ['3', 'Escalda-pés'], ['4', 'Corte + finalização']], ['#', 'Item']), ], defaults: { bgColor: UARINI.cream, textColor: UARINI.black, accentColor: UARINI.green, index: '2', count: '3', eyebrow: 'CHECKLIST', title: 'O que vai\nacontecer\nno seu ritual.', items: [['1', 'Acolhida + escuta'], ['2', 'Massagem capilar'], ['3', 'Escalda-pés'], ['4', 'Corte + finalização']] }, render: (d) => (
{d.eyebrow}
{d.index} / {d.count}
{d.title}
{(d.items || []).map(([n, t], i) => (
{n}
{t}
))}
), caption: () => `carrossel · lista`, }; // Carrossel · citação grande (slide intermediário) const TplCarouselQuote = { id: 'carousel-quote', label: 'Carrossel · citação', group: 'CARROSSEL · 3 SLIDES', size: { w: 1080, h: 1350 }, fields: [ ...COLOR_FIELDS({ bgColor: UARINI.green, textColor: UARINI.black }), F.color('accentColor', 'Destaque', UARINI.cream), F.text('index', 'Slide n', '2'), F.text('count', 'Total', '3'), F.textarea('quote', 'Citação', '"Antes da estética,\no cuidado.\nAntes do corte,\na escuta."'), F.text('attrib', 'Atribuição', '— SABEDORIA DA CASA'), ], defaults: { bgColor: UARINI.green, textColor: UARINI.black, accentColor: UARINI.cream, index: '2', count: '3', quote: '"Antes da estética,\no cuidado.\nAntes do corte,\na escuta."', attrib: '— SABEDORIA DA CASA' }, render: (d) => (
{d.index} / {d.count}
{d.quote}
{d.attrib}
), caption: () => `carrossel · citação`, }; const TEMPLATES = [ TplLogoHero, TplPortrait, TplQuote, TplServices, TplBeforeAfter, TplPhotoWatermark, TplAgenda, TplTerapia, TplWisdom, TplStoryWelcome, TplStoryService, TplStoryPoll, TplStoryQuote, TplStoryCountdown, TplStoryPrice, TplStoryQuestion, TplCardFront, TplCardBack, TplQRSign, TplQRSquare, TplStickerRound, TplStickerRect, TplCarouselCover, TplCarouselContent, TplCarouselClose, TplCarouselPhoto, TplCarouselList, TplCarouselQuote, TplVIPFront, TplVIPBack, TplSpiralRound, TplSpiralSquare, TplSpiralOffset, TplHighlightSpiral, TplHighlightLetter, TplHighlightWord, TplHighlightNumber, TplHighlightStripes, TplHighlightStar, ]; window.UARINI_TEMPLATES = TEMPLATES;