// 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) => (
),
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.services.map(([n, t, p], i) => (
))}
{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) => (
),
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) => (
),
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.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) => (
),
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) => (
),
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) => (
),
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) => (
),
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) => (
),
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) => (
),
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 (
);
}
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) => (
))}
),
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;