// 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 (
      <PhotoPlaceholder tone={fallbackTone} label={label} style={style}>
        {children}
      </PhotoPlaceholder>
    );
  }
  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 (
    <div style={{ position: 'absolute', inset: 0, overflow: 'hidden', ...style }}>
      {/* effect underlay — sticks out as a thin frame around the photo */}
      <PhotoPlaceholder tone={fallbackTone}/>
      <div style={{ position: 'absolute', inset: peek, overflow: 'hidden' }}>
        {isVideo ? (
          <video src={media.url} autoPlay muted loop playsInline style={mediaStyle}/>
        ) : (
          <img src={media.url} crossOrigin="anonymous" style={mediaStyle}/>
        )}
      </div>
      {children}
    </div>
  );
}
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 `<key>Sz` (slider) + `<key>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) => (
    <div style={{ position: 'absolute', inset: 0, background: d.bgColor, color: d.textColor, overflow: 'hidden', fontFamily: 'Poppins' }}>
      <GrainOverlay opacity={0.08} blend={d.bgColor === UARINI.black ? 'screen' : 'multiply'}/>
      <div style={{ position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
        <LogoLockup height={520 * (d.logoSz || 1)} color={d.textColor}/>
      </div>
      <div style={{ position: 'absolute', left: 60, top: 60, ...txt(d, 'eyebrow', 26, d.accentColor, { letterSpacing: '0.32em', fontWeight: 600 }) }}>{d.eyebrow}</div>
      <div style={{ position: 'absolute', right: 60, top: 60, opacity: 0.6, ...txt(d, 'eyebrowR', 26, d.textColor, { letterSpacing: '0.22em' }) }}>{d.eyebrowR}</div>
      <div style={{ position: 'absolute', bottom: 60, left: 0, right: 0, textAlign: 'center', fontWeight: 500, opacity: 0.7, ...txt(d, 'footer', 28, d.textColor, { letterSpacing: '0.22em' }) }}>{d.footer}</div>
    </div>
  ),
  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) => (
    <div style={{ position: 'absolute', inset: 0, background: d.frameColor || d.bgColor, padding: 60, fontFamily: 'Poppins', boxSizing: 'border-box' }}>
      <div style={{ width: '100%', height: '100%', position: 'relative', overflow: 'hidden' }}>
        <MediaSlot media={d.media} fallbackTone={d.photo} label="RETRATO 01">
          <div style={{ position: 'absolute', left: 30, top: 30 }}>
            <SpiralPatch size={130 * (d.spiralSz || 1)} tone="cream-on-dark" rotation={-12}/>
          </div>
        </MediaSlot>
        <div style={{ position: 'absolute', left: 0, right: 0, bottom: 0, padding: '40px 36px', background: 'linear-gradient(180deg, rgba(0,0,0,0) 0%, rgba(14,13,13,0.85) 100%)', color: d.textColor }}>
          <div style={{ opacity: 0.7, marginBottom: 8, ...txt(d, 'badge', 22, d.textColor, { letterSpacing: '0.22em' }) }}>{d.badge}</div>
          <div style={{ ...txt(d, 'title', 56, d.textColor, { fontWeight: 700, letterSpacing: '-0.02em', lineHeight: 1.0, whiteSpace: 'pre-line' }) }}>{d.title}</div>
        </div>
      </div>
    </div>
  ),
  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) => (
    <div style={{ position: 'absolute', inset: 0, background: d.bgColor, color: d.textColor, padding: 80, overflow: 'hidden', fontFamily: 'Poppins', boxSizing: 'border-box' }}>
      <GrainOverlay opacity={0.18} blend="multiply"/>
      <div style={{ position: 'absolute', right: -60, top: -40, color: d.spiralColor }}>
        <SpiralPatch size={420 * (d.spiralSz || 1)} tone={spiralToneFor(d.bgColor, d.spiralColor)} rotation={-15}/>
      </div>
      <div style={{ position: 'relative', zIndex: 2, height: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'space-between' }}>
        <div style={{ fontWeight: 600, ...txt(d, 'eyebrow', 26, d.textColor, { letterSpacing: '0.32em' }) }}>{d.eyebrow}</div>
        <div style={{ maxWidth: '78%', whiteSpace: 'pre-line', ...txt(d, 'headline', 76, d.textColor, { fontWeight: 700, lineHeight: 1.0, letterSpacing: '-0.03em' }) }}>{d.headline}</div>
        <div style={{ maxWidth: '70%', opacity: 0.85, ...txt(d, 'sub', 22, d.textColor, { letterSpacing: '0.05em', lineHeight: 1.4 }) }}>{d.sub}</div>
      </div>
    </div>
  ),
  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) => (
    <div style={{ position: 'absolute', inset: 0, background: d.bgColor, color: d.textColor, padding: 70, overflow: 'hidden', fontFamily: 'Poppins', boxSizing: 'border-box' }}>
      <GrainOverlay opacity={0.06} blend="screen"/>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
        <div style={{ fontWeight: 600, ...txt(d, 'eyebrow', 22, d.accentColor, { letterSpacing: '0.32em' }) }}>{d.eyebrow}</div>
        <SpiralPatch size={80 * (d.spiralSz || 1)} tone={spiralToneFor(d.bgColor, d.accentColor)} rotation={6}/>
      </div>
      <div style={{ marginTop: 56 }}>
        {d.services.map(([n, t, p], i) => (
          <div key={i} style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', padding: '24px 0', borderBottom: `1px solid ${d.textColor}22`, gap: 20 }}>
            <div style={{ fontSize: 36, fontWeight: 700, letterSpacing: '-0.01em' }}>{n}</div>
            <div style={{ flex: 1, borderBottom: `1px dashed ${d.textColor}33`, height: 1, alignSelf: 'flex-end', marginBottom: 12, margin: '0 18px' }}/>
            <div style={{ fontSize: 18, opacity: 0.55, letterSpacing: '0.18em' }}>{t}</div>
            <div style={{ fontSize: 36, fontWeight: 700, color: d.accentColor, marginLeft: 24, minWidth: 140, textAlign: 'right' }}>{p}</div>
          </div>
        ))}
      </div>
      <div style={{ position: 'absolute', bottom: 50, left: 70, right: 70, opacity: 0.5, textAlign: 'center', ...txt(d, 'footer', 16, d.textColor, { letterSpacing: '0.22em' }) }}>{d.footer}</div>
    </div>
  ),
  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) => (
    <div style={{ position: 'absolute', inset: 0, background: d.bgColor, display: 'flex', flexDirection: 'column', overflow: 'hidden', fontFamily: 'Poppins', boxSizing: 'border-box' }}>
      <div style={{ flex: 1, display: 'flex', position: 'relative', padding: '40px 40px 0 40px', gap: 12 }}>
        <div style={{ flex: 1, position: 'relative', overflow: 'hidden' }}>
          <MediaSlot media={d.beforeMedia} fallbackTone={d.beforeTone} label="ANTES"/>
          <div style={{ position: 'absolute', left: 32, top: 32, fontWeight: 600, fontSize: 16 * (d.lblSz || 1), letterSpacing: '0.32em', color: d.lblCol || d.textColor }}>ANTES</div>
        </div>
        <div style={{ width: 4, background: d.accentColor, alignSelf: 'stretch' }}/>
        <div style={{ flex: 1, position: 'relative', overflow: 'hidden' }}>
          <MediaSlot media={d.afterMedia} fallbackTone={d.afterTone} label="DEPOIS"/>
          <div style={{ position: 'absolute', right: 32, top: 32, fontWeight: 600, fontSize: 16 * (d.lblSz || 1), letterSpacing: '0.32em', color: d.lblCol || d.textColor }}>DEPOIS</div>
        </div>
      </div>
      <div style={{ color: d.textColor, padding: '32px 60px', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
        <div>
          <div style={{ fontWeight: 600, ...txt(d, 'rid', 14, d.accentColor, { letterSpacing: '0.22em' }) }}>{d.rid}</div>
          <div style={{ marginTop: 4, ...txt(d, 'name', 28, d.textColor, { fontWeight: 700 }) }}>{d.name}</div>
        </div>
        <Wordmark height={42} color={d.textColor} withStudio={false}/>
      </div>
    </div>
  ),
  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) => (
    <div style={{ position: 'absolute', inset: 0, background: d.bgColor, overflow: 'hidden', fontFamily: 'Poppins' }}>
      <MediaSlot media={d.media} fallbackTone={d.photo}/>
      <div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(180deg, rgba(14,13,13,0.0) 35%, rgba(14,13,13,0.95) 100%)' }}/>
      <div style={{ position: 'absolute', left: 60, top: 60, mixBlendMode: 'screen', opacity: 0.85 }}>
        <SpiralPatch size={150 * (d.spiralSz || 1)} tone="green-on-dark" rotation={-8}/>
      </div>
      <div style={{ position: 'absolute', left: 0, right: 0, bottom: 60, textAlign: 'center', color: d.textColor }}>
        <div style={{ opacity: 0.7, fontWeight: 500, marginBottom: 18, ...txt(d, 'credit', 16, d.textColor, { letterSpacing: '0.32em' }) }}>{d.credit}</div>
        <Wordmark height={56} color={d.textColor}/>
      </div>
    </div>
  ),
  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) => (
    <div style={{ position: 'absolute', inset: 0, background: d.bgColor, color: d.textColor, padding: 70, overflow: 'hidden', fontFamily: 'Poppins', boxSizing: 'border-box' }}>
      <div style={{ position: 'absolute', right: -100, bottom: -100 }}>
        <SpiralPatch size={520 * (d.spiralSz || 1)} tone={spiralToneFor(d.bgColor, d.accentColor)} rotation={20}/>
      </div>
      <div style={{ position: 'relative', zIndex: 2, height: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'space-between' }}>
        <div style={{ fontWeight: 600, ...txt(d, 'eyebrow', 22, d.accentColor, { letterSpacing: '0.32em' }) }}>{d.eyebrow}</div>
        <div>
          <div style={{ whiteSpace: 'pre-line', ...txt(d, 'title', 100, d.textColor, { fontWeight: 700, lineHeight: 0.92, letterSpacing: '-0.04em' }) }}>{d.title}</div>
          <div style={{ opacity: 0.6, marginTop: 24, maxWidth: '64%', ...txt(d, 'sub', 22, d.textColor, { letterSpacing: '0.04em', lineHeight: 1.4 }) }}>{d.sub}</div>
        </div>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
          <div style={{ fontWeight: 600, ...txt(d, 'handle', 16, d.textColor, { letterSpacing: '0.32em' }) }}>{d.handle}</div>
          {d.showButton === false
            ? <div style={{ fontSize: 20, fontWeight: 700, letterSpacing: '0.16em', color: d.btnBg }}>{d.cta}</div>
            : <div style={{ padding: '14px 28px', background: d.btnBg, color: d.btnText, fontSize: 18, fontWeight: 600, letterSpacing: '0.22em', borderRadius: 200 }}>{d.cta}</div>}
        </div>
      </div>
    </div>
  ),
  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) => (
    <div style={{ position: 'absolute', inset: 0, background: d.bgColor, color: d.textColor, overflow: 'hidden', fontFamily: 'Poppins' }}>
      <div style={{ position: 'absolute', inset: 0, top: '40%' }}>
        <MediaSlot media={d.media} fallbackTone={d.photo}/>
      </div>
      <div style={{ position: 'absolute', inset: 0, padding: 70 }}>
        <div style={{ fontWeight: 600, ...txt(d, 'eyebrow', 22, d.accentColor, { letterSpacing: '0.32em' }) }}>{d.eyebrow}</div>
        <div style={{ marginTop: 16, whiteSpace: 'pre-line', ...txt(d, 'title', 84, d.textColor, { fontWeight: 700, lineHeight: 0.95, letterSpacing: '-0.03em' }) }}>{d.title}</div>
      </div>
      <div style={{ position: 'absolute', right: 60, top: '46%', mixBlendMode: 'screen' }}>
        <StripesMark width={220 * (d.spiralSz || 1)} color={d.textColor}/>
      </div>
      <div style={{ position: 'absolute', left: 70, right: 70, bottom: 60, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
        <div style={{ opacity: 0.85, maxWidth: '70%', whiteSpace: 'pre-line', ...txt(d, 'sub', 18, d.textColor, { lineHeight: 1.4 }) }}>{d.sub}</div>
        <SpiralPatch size={90 * (d.spiralSz || 1)} tone={spiralToneFor(d.bgColor, d.textColor)} rotation={-6}/>
      </div>
    </div>
  ),
  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) => (
    <div style={{ position: 'absolute', inset: 0, background: d.bgColor, color: d.textColor, padding: 80, overflow: 'hidden', fontFamily: 'Poppins', boxSizing: 'border-box' }}>
      <GrainOverlay opacity={0.10} blend="multiply"/>
      <div style={{ position: 'absolute', left: 80, top: 80 }}>
        <SpiralPatch size={120 * (d.spiralSz || 1)} tone={spiralToneFor(d.bgColor, d.accentColor)} rotation={-10}/>
      </div>
      <div style={{ position: 'absolute', left: 80, right: 80, top: '35%', transform: 'translateY(-30%)' }}>
        <div style={{ fontWeight: 600, marginBottom: 18, ...txt(d, 'eyebrow', 14, d.accentColor, { letterSpacing: '0.32em' }) }}>{d.eyebrow}</div>
        <div style={{ whiteSpace: 'pre-line', ...txt(d, 'quote', 60, d.textColor, { fontWeight: 700, lineHeight: 1.05, letterSpacing: '-0.025em' }) }}>{d.quote}</div>
      </div>
      <div style={{ position: 'absolute', left: 80, right: 80, bottom: 80, display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end' }}>
        <div>
          <div style={{ ...txt(d, 'author', 16, d.textColor, { fontWeight: 700, letterSpacing: '0.22em' }) }}>{d.author}</div>
          <div style={{ opacity: 0.55, marginTop: 4, ...txt(d, 'role', 12, d.textColor, { letterSpacing: '0.22em' }) }}>{d.role}</div>
        </div>
        <div style={{ fontSize: 14, opacity: 0.55, letterSpacing: '0.22em' }}>STUDIO UARINÍ</div>
      </div>
    </div>
  ),
  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) => (
    <div style={{ position: 'absolute', inset: 0, background: d.bgColor, color: d.textColor, overflow: 'hidden', fontFamily: 'Poppins' }}>
      <GrainOverlay opacity={0.10} blend="screen"/>
      <div style={{ position: 'absolute', left: 80, top: 220, fontSize: 36, letterSpacing: '0.32em', color: d.accentColor, fontWeight: 600 }}>{d.eyebrow}</div>
      <div style={{ position: 'absolute', left: 80, right: 80, top: 360, fontSize: 160, fontWeight: 700, lineHeight: 0.92, letterSpacing: '-0.04em', whiteSpace: 'pre-line' }}>{d.title}</div>
      <div style={{ position: 'absolute', right: 60, top: 920 }}>
        <SpiralPatch size={420} tone={spiralToneFor(d.bgColor, d.accentColor)} rotation={-12}/>
      </div>
      <div style={{ position: 'absolute', left: 80, right: 80, bottom: 320, fontSize: 42, letterSpacing: '0.04em', lineHeight: 1.35, opacity: 0.85, whiteSpace: 'pre-line' }}>{d.sub}</div>
      <div style={{ position: 'absolute', left: 0, right: 0, bottom: 140, textAlign: 'center', fontSize: 32, letterSpacing: '0.42em', color: d.accentColor, fontWeight: 600 }}>{d.cta}</div>
    </div>
  ),
  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) => (
    <div style={{ position: 'absolute', inset: 0, background: d.bgColor, color: d.textColor, overflow: 'hidden', fontFamily: 'Poppins' }}>
      <GrainOverlay opacity={0.14} blend="multiply"/>
      <div style={{ position: 'absolute', left: '50%', top: 200, transform: 'translateX(-50%)', fontSize: 32, letterSpacing: '0.32em', fontWeight: 600 }}>{d.badge}</div>
      <div style={{ position: 'absolute', left: 80, right: 80, top: 420, fontSize: 180, fontWeight: 700, lineHeight: 0.92, letterSpacing: '-0.04em', whiteSpace: 'pre-line', textAlign: 'center' }}>{d.title}</div>
      <div style={{ position: 'absolute', left: '50%', top: 1100, transform: 'translateX(-50%)' }}>
        <SpiralPatch size={300} tone={spiralToneFor(d.bgColor, d.textColor)} rotation={-8}/>
      </div>
      <div style={{ position: 'absolute', left: 0, right: 0, bottom: 320, textAlign: 'center', fontSize: 44, fontWeight: 700, letterSpacing: '0.06em' }}>{d.price}</div>
      <div style={{ position: 'absolute', left: 0, right: 0, bottom: 220, textAlign: 'center', fontSize: 26, letterSpacing: '0.32em', opacity: 0.7, fontWeight: 500 }}>{d.duration}</div>
    </div>
  ),
  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) => (
    <div style={{ position: 'absolute', inset: 0, background: d.bgColor, overflow: 'hidden', fontFamily: 'Poppins' }}>
      <MediaSlot media={d.media} fallbackTone={d.photo}/>
      <div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(180deg, rgba(14,13,13,0.55) 0%, rgba(14,13,13,0.1) 35%, rgba(14,13,13,0.85) 100%)' }}/>
      <div style={{ position: 'absolute', left: 80, top: 240, fontSize: 32, letterSpacing: '0.32em', color: d.accentColor, fontWeight: 600 }}>{d.eyebrow}</div>
      <div style={{ position: 'absolute', left: 80, right: 80, top: 340, fontSize: 130, fontWeight: 700, lineHeight: 0.95, letterSpacing: '-0.03em', color: d.textColor, whiteSpace: 'pre-line' }}>{d.question}</div>
      <div style={{ position: 'absolute', left: 80, right: 80, bottom: 280, display: 'flex', flexDirection: 'column', gap: 26 }}>
        {[d.optA, d.optB].filter((o) => o && o.trim()).map((opt, i, arr) => (
          <div key={i} style={{
            padding: '36px 42px', background: i === 0 && arr.length > 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}</div>
        ))}
      </div>
    </div>
  ),
  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 <div ref={ref} style={{ width: size, height: size, display: 'inline-block' }}/>;
}
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) => (
    <div style={{ position: 'absolute', inset: 0, background: d.bgColor, color: d.textColor, padding: 60, fontFamily: 'Poppins', overflow: 'hidden', boxSizing: 'border-box' }}>
      <GrainOverlay opacity={0.08} blend="screen"/>
      <div style={{ position: 'absolute', right: -80, bottom: -80 }}>
        <SpiralPatch size={360} tone={spiralToneFor(d.bgColor, d.accentColor)} rotation={-12}/>
      </div>
      <div style={{ position: 'relative', zIndex: 2, height: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'space-between' }}>
        <div style={{ fontSize: 14, letterSpacing: '0.32em', color: d.accentColor, fontWeight: 600 }}>{d.tag}</div>
        <div>
          <div style={{ fontSize: 56, fontWeight: 700, letterSpacing: '-0.025em', lineHeight: 1.0 }}>{d.name}</div>
          <div style={{ fontSize: 18, opacity: 0.65, marginTop: 8, letterSpacing: '0.18em', textTransform: 'uppercase' }}>{d.role}</div>
        </div>
      </div>
    </div>
  ),
  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) => (
    <div style={{ position: 'absolute', inset: 0, background: d.bgColor, color: d.textColor, padding: 60, fontFamily: 'Poppins', display: 'flex', alignItems: 'center', gap: 50, boxSizing: 'border-box' }}>
      <div style={{ background: '#fff', padding: 14, borderRadius: 12, border: `1px solid ${d.textColor}22` }}>
        <QRBlock url={d.url} size={300} color={d.textColor} bg="#fff"/>
      </div>
      <div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 18 }}>
        <div style={{ fontSize: 12, letterSpacing: '0.32em', color: d.accentColor, fontWeight: 600 }}>{d.cta}</div>
        <div style={{ fontSize: 32, fontWeight: 700, letterSpacing: '-0.02em' }}>{d.handle}</div>
        <div style={{ fontSize: 16, opacity: 0.7, letterSpacing: '0.04em', lineHeight: 1.6 }}>
          {d.phone}<br/>{d.addr}
        </div>
      </div>
    </div>
  ),
  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) => (
    <div style={{ position: 'absolute', inset: 0, background: d.bgColor, color: d.textColor, padding: 90, fontFamily: 'Poppins', overflow: 'hidden', boxSizing: 'border-box', display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
      <GrainOverlay opacity={0.08} blend="multiply"/>
      <div style={{ fontSize: 22, letterSpacing: '0.42em', color: d.accentColor, fontWeight: 600, alignSelf: 'flex-start' }}>{d.eyebrow}</div>
      <div style={{ fontSize: 130, fontWeight: 700, lineHeight: 0.9, letterSpacing: '-0.04em', marginTop: 40, alignSelf: 'flex-start', whiteSpace: 'pre-line' }}>{d.title}</div>
      <div style={{ fontSize: 26, opacity: 0.65, marginTop: 28, lineHeight: 1.4, alignSelf: 'flex-start', whiteSpace: 'pre-line', maxWidth: '70%' }}>{d.sub}</div>
      <div style={{ marginTop: 'auto', padding: 36, background: '#fff', borderRadius: 24, boxShadow: `0 12px 40px ${d.textColor}1a` }}>
        <QRBlock url={d.url} size={620} color={d.textColor} bg="#fff"/>
      </div>
      <div style={{ marginTop: 36, fontSize: 22, letterSpacing: '0.32em', fontWeight: 600 }}>{d.cta}</div>
    </div>
  ),
  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) => (
    <div style={{ position: 'absolute', inset: 0, background: d.bgColor, color: d.textColor, padding: 70, fontFamily: 'Poppins', overflow: 'hidden', boxSizing: 'border-box', display: 'grid', gridTemplateColumns: '1fr auto', gap: 50, alignItems: 'center' }}>
      <GrainOverlay opacity={0.10} blend="multiply"/>
      <div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'space-between', height: '100%' }}>
        <div style={{ fontSize: 18, letterSpacing: '0.42em', color: d.accentColor, fontWeight: 600 }}>{d.eyebrow}</div>
        <div style={{ fontSize: 92, fontWeight: 700, lineHeight: 0.92, letterSpacing: '-0.04em', whiteSpace: 'pre-line' }}>{d.title}</div>
        <div style={{ fontSize: 18, letterSpacing: '0.32em', fontWeight: 600 }}>{d.cta}</div>
      </div>
      <div style={{ background: '#fff', padding: 24, borderRadius: 20 }}>
        <QRBlock url={d.url} size={500} color={d.textColor} bg="#fff"/>
      </div>
    </div>
  ),
  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) => (
    <div style={{ position: 'absolute', inset: 0, overflow: 'hidden', fontFamily: 'Poppins' }}>
      <div style={{ position: 'absolute', inset: 30, background: d.bgColor, color: d.textColor, borderRadius: '50%', overflow: 'hidden' }}>
        <GrainOverlay opacity={0.10} blend="multiply"/>
        <svg viewBox="0 0 1020 1020" style={{ position: 'absolute', inset: 0, width: '100%', height: '100%' }}>
          <defs>
            <path id="curveTop" d="M 510,510 m -380,0 a 380,380 0 1,1 760,0 a 380,380 0 1,1 -760,0"/>
          </defs>
          <text fontSize="48" fontWeight="600" letterSpacing="14" fill={d.textColor} fontFamily="Poppins">
            <textPath href="#curveTop" startOffset="0">{d.top}{d.top}</textPath>
          </text>
        </svg>
        <div style={{ position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 20 }}>
          <SpiralPatch size={220} tone={spiralToneFor(d.bgColor, d.accentColor)} rotation={0}/>
          <div style={{ fontSize: 56, fontWeight: 700, letterSpacing: '0.04em', color: d.accentColor }}>{d.center}</div>
          <div style={{ fontSize: 18, letterSpacing: '0.32em', opacity: 0.7, fontWeight: 600 }}>{d.bottom}</div>
        </div>
      </div>
    </div>
  ),
  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) => (
    <div style={{ position: 'absolute', inset: 0, background: d.bgColor, color: d.textColor, padding: 50, borderRadius: 30, overflow: 'hidden', fontFamily: 'Poppins', display: 'flex', alignItems: 'center', gap: 40, boxSizing: 'border-box' }}>
      <SpiralPatch size={420} tone={spiralToneFor(d.bgColor, d.accentColor)} rotation={-10}/>
      <div style={{ flex: 1 }}>
        <div style={{ fontSize: 110, fontWeight: 700, lineHeight: 0.92, letterSpacing: '-0.04em', whiteSpace: 'pre-line' }}>{d.text}</div>
        <div style={{ fontSize: 22, letterSpacing: '0.32em', color: d.accentColor, fontWeight: 600, marginTop: 18 }}>{d.handle}</div>
      </div>
    </div>
  ),
  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) => (
    <div style={{ position: 'absolute', inset: 0, background: d.bgColor, color: d.textColor, padding: 100, fontFamily: 'Poppins', overflow: 'hidden', boxSizing: 'border-box', display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>
      <GrainOverlay opacity={0.10} blend="multiply"/>
      <div style={{ fontSize: 36, letterSpacing: '0.32em', color: d.accentColor, fontWeight: 600, marginBottom: 40 }}>{d.eyebrow}</div>
      <div style={{ fontSize: 86, fontWeight: 700, lineHeight: 1.1, letterSpacing: '-0.025em' }}>{d.quote}</div>
      <div style={{ fontSize: 32, letterSpacing: '0.22em', fontWeight: 600, marginTop: 60 }}>{d.author}</div>
      <div style={{ position: 'absolute', right: -80, bottom: -80 }}>
        <SpiralPatch size={420} tone={spiralToneFor(d.bgColor, d.accentColor)} rotation={20}/>
      </div>
    </div>
  ),
  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) => (
    <div style={{ position: 'absolute', inset: 0, background: d.bgColor, color: d.textColor, padding: 80, fontFamily: 'Poppins', overflow: 'hidden', boxSizing: 'border-box' }}>
      <GrainOverlay opacity={0.10} blend="screen"/>
      <div style={{ fontSize: 32, letterSpacing: '0.32em', color: d.accentColor, fontWeight: 600 }}>{d.eyebrow}</div>
      <div style={{ position: 'absolute', left: '50%', top: '38%', transform: 'translate(-50%, -50%)', fontSize: 600, fontWeight: 700, lineHeight: 0.85, letterSpacing: '-0.06em', color: d.accentColor }}>{d.number}</div>
      <div style={{ position: 'absolute', left: 80, right: 80, bottom: 280, fontSize: 64, fontWeight: 700, lineHeight: 1.0, letterSpacing: '-0.03em', whiteSpace: 'pre-line' }}>{d.label}</div>
      <div style={{ position: 'absolute', left: 0, right: 0, bottom: 140, textAlign: 'center', fontSize: 22, letterSpacing: '0.42em', opacity: 0.7, fontWeight: 600 }}>{d.cta}</div>
    </div>
  ),
  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) => (
    <div style={{ position: 'absolute', inset: 0, overflow: 'hidden', fontFamily: 'Poppins' }}>
      <MediaSlot media={d.media} fallbackTone={d.photo}/>
      <div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(180deg, rgba(14,13,13,0.6) 0%, rgba(14,13,13,0.2) 50%, rgba(14,13,13,0.95) 100%)' }}/>
      <div style={{ position: 'absolute', left: 80, top: 220, fontSize: 32, letterSpacing: '0.32em', color: d.accentColor, fontWeight: 600 }}>{d.eyebrow}</div>
      <div style={{ position: 'absolute', left: 80, right: 80, top: 320, fontSize: 150, fontWeight: 700, lineHeight: 0.9, letterSpacing: '-0.04em', color: d.textColor, whiteSpace: 'pre-line' }}>{d.title}</div>
      <div style={{ position: 'absolute', left: 80, bottom: 380, color: d.textColor }}>
        <div style={{ fontSize: 26, letterSpacing: '0.18em', opacity: 0.55, textDecoration: 'line-through' }}>{d.priceFrom}</div>
        <div style={{ fontSize: 130, fontWeight: 700, letterSpacing: '-0.03em', color: d.accentColor, lineHeight: 1.0, marginTop: 8 }}>{d.priceTo}</div>
      </div>
      <div style={{ position: 'absolute', left: 0, right: 0, bottom: 140, textAlign: 'center', fontSize: 24, letterSpacing: '0.42em', color: d.textColor, opacity: 0.7, fontWeight: 600 }}>{d.cta}</div>
    </div>
  ),
  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) => (
    <div style={{ position: 'absolute', inset: 0, background: d.bgColor, color: d.textColor, padding: 80, fontFamily: 'Poppins', overflow: 'hidden', boxSizing: 'border-box' }}>
      <GrainOverlay opacity={0.12} blend="multiply"/>
      <div style={{ fontSize: 38, letterSpacing: '0.32em', fontWeight: 600, marginTop: 100 }}>{d.eyebrow}</div>
      <div style={{ fontSize: 120, fontWeight: 700, lineHeight: 0.95, letterSpacing: '-0.04em', marginTop: 40, whiteSpace: 'pre-line' }}>{d.question}</div>
      <div style={{ position: 'absolute', left: 80, right: 80, bottom: 360, padding: '38px 40px', background: d.boxColor, color: d.textColor, borderRadius: 28, fontSize: 38, opacity: 0.85 }}>{d.placeholder}</div>
    </div>
  ),
  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) => (
    <div style={{ position: 'absolute', inset: 0, background: d.bgColor, color: d.textColor, padding: 80, fontFamily: 'Poppins', overflow: 'hidden', boxSizing: 'border-box' }}>
      <GrainOverlay opacity={0.10} blend="screen"/>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
        <div style={{ fontSize: 22, letterSpacing: '0.32em', color: d.accentColor, fontWeight: 600 }}>{d.eyebrow}</div>
        <div style={{ fontSize: 22, letterSpacing: '0.22em', opacity: 0.5, fontFamily: 'JetBrains Mono, monospace' }}>1 / {d.count}</div>
      </div>
      <div style={{ position: 'absolute', left: 80, right: 80, top: '40%', transform: 'translateY(-40%)', fontSize: 110, fontWeight: 700, lineHeight: 0.95, letterSpacing: '-0.04em', whiteSpace: 'pre-line' }}>{d.title}</div>
      <div style={{ position: 'absolute', right: 60, bottom: 200 }}>
        <SpiralPatch size={300} tone={spiralToneFor(d.bgColor, d.accentColor)} rotation={-15}/>
      </div>
      <div style={{ position: 'absolute', left: 0, right: 0, bottom: 80, textAlign: 'center', fontSize: 22, letterSpacing: '0.42em', color: d.accentColor, fontWeight: 600 }}>{d.cta}</div>
    </div>
  ),
  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) => (
    <div style={{ position: 'absolute', inset: 0, background: d.bgColor, color: d.textColor, padding: 80, fontFamily: 'Poppins', overflow: 'hidden', boxSizing: 'border-box' }}>
      <GrainOverlay opacity={0.08} blend="multiply"/>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
        <div style={{ fontSize: 130, fontWeight: 700, color: d.accentColor, lineHeight: 0.85, letterSpacing: '-0.04em' }}>{d.badge}</div>
        <div style={{ fontSize: 22, letterSpacing: '0.22em', opacity: 0.5, fontFamily: 'JetBrains Mono, monospace' }}>{d.index} / {d.count}</div>
      </div>
      <div style={{ position: 'absolute', left: 80, right: 80, top: '38%', transform: 'translateY(-50%)', fontSize: 96, fontWeight: 700, lineHeight: 0.92, letterSpacing: '-0.035em', whiteSpace: 'pre-line' }}>{d.title}</div>
      <div style={{ position: 'absolute', left: 80, right: 80, bottom: 100, fontSize: 26, lineHeight: 1.5, letterSpacing: '0.01em', opacity: 0.75, maxWidth: '85%' }}>{d.body}</div>
    </div>
  ),
  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) => (
    <div style={{ position: 'absolute', inset: 0, background: d.bgColor, color: d.textColor, padding: 80, fontFamily: 'Poppins', overflow: 'hidden', boxSizing: 'border-box' }}>
      <GrainOverlay opacity={0.10} blend="multiply"/>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
        <div style={{ fontSize: 22, letterSpacing: '0.32em', fontWeight: 600 }}>{d.eyebrow}</div>
        <div style={{ fontSize: 22, letterSpacing: '0.22em', opacity: 0.5, fontFamily: 'JetBrains Mono, monospace' }}>{d.count} / {d.count}</div>
      </div>
      <div style={{ position: 'absolute', left: 80, right: 80, top: '38%', transform: 'translateY(-50%)', fontSize: 108, fontWeight: 700, lineHeight: 0.92, letterSpacing: '-0.04em', whiteSpace: 'pre-line' }}>{d.title}</div>
      <div style={{ position: 'absolute', left: 80, right: 80, top: '64%', fontSize: 24, opacity: 0.75, lineHeight: 1.45, whiteSpace: 'pre-line', maxWidth: '80%' }}>{d.sub}</div>
      <div style={{ position: 'absolute', left: 80, right: 80, bottom: 80, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
        <div style={{ fontSize: 16, letterSpacing: '0.32em', fontWeight: 600 }}>{d.handle}</div>
        {d.showButton === false
          ? <div style={{ fontSize: 22, fontWeight: 700, letterSpacing: '0.16em', color: d.btnBg }}>{d.cta}</div>
          : <div style={{ padding: '20px 32px', background: d.btnBg, color: d.btnText, fontSize: 18, fontWeight: 600, letterSpacing: '0.32em', borderRadius: 200 }}>{d.cta}</div>}
      </div>
      <div style={{ position: 'absolute', right: -100, top: -100 }}>
        <SpiralPatch size={420} tone={spiralToneFor(d.bgColor, d.accentColor)} rotation={20}/>
      </div>
    </div>
  ),
  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) => (
    <div style={{ position: 'absolute', inset: 0, background: d.bgColor, color: d.textColor, overflow: 'hidden', fontFamily: 'Poppins' }}>
      <GrainOverlay opacity={0.06} blend="screen"/>
      <div style={{ position: 'absolute', left: '50%', top: '50%', transform: 'translate(-50%, -50%)' }}>
        <SpiralPatch size={260} tone={spiralToneFor(d.bgColor, d.accentColor)} rotation={-6}/>
      </div>
      <div style={{ position: 'absolute', left: 40, top: 40, fontSize: 18, fontWeight: 500, letterSpacing: '0.32em', opacity: 0.5 }}>{d.top}</div>
      <div style={{ position: 'absolute', right: 40, top: 40, fontSize: 18, fontWeight: 500, letterSpacing: '0.22em', opacity: 0.5 }}>{d.num}</div>
    </div>
  ),
  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 (
      <div style={{ position: 'absolute', inset: 0, background: d.bgColor, color: d.textColor, overflow: 'hidden', fontFamily: 'Poppins' }}>
        <GrainOverlay opacity={0.06} blend="screen"/>
        <div style={{ position: 'absolute', left: 40, top: 50 }}>
          <SpiralPatch size={110} tone={spiralToneFor(d.bgColor, d.accentColor)} rotation={-6}/>
        </div>
        <div style={{ position: 'absolute', right: 40, top: 62, fontSize: 18, fontWeight: 500, letterSpacing: '0.22em', opacity: 0.5 }}>{d.top}</div>

        <div style={{ position: 'absolute', left: 40, right: 40, top: 260 }}>
          <div style={{ fontSize: 18, letterSpacing: '0.32em', color: d.accentColor, fontWeight: 600, marginBottom: 24 }}>{d.label}</div>
          <div style={{ display: 'grid', gridTemplateColumns: `repeat(${cols}, 1fr)`, gap: 14 }}>
            {Array.from({ length: total }).map((_, i) => (
              <div key={i} style={{
                aspectRatio: '1', borderRadius: '50%',
                background: i < stamps ? d.accentColor : 'transparent',
                border: `2px solid ${i < stamps ? d.accentColor : d.textColor + '50'}`,
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                fontSize: 22, fontWeight: 700, color: d.bgColor,
              }}>
                {i < stamps ? '✓' : ''}
              </div>
            ))}
          </div>
        </div>

        <div style={{ position: 'absolute', left: 40, right: 40, bottom: 100 }}>
          <div style={{ fontSize: 14, letterSpacing: '0.32em', opacity: 0.55, marginBottom: 8 }}>{d.clientLabel}</div>
          <div style={{ fontSize: 28, fontWeight: 700, borderBottom: `1px solid ${d.textColor}30`, paddingBottom: 14, minHeight: 50 }}>{d.clientName || '_________________________'}</div>
        </div>

        <div style={{ position: 'absolute', left: 40, bottom: 40, fontSize: 13, opacity: 0.5, letterSpacing: '0.22em' }}>{d.footer}</div>
      </div>
    );
  },
  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 (
      <div style={{ position: 'absolute', inset: 0, overflow: 'hidden' }}>
        <div style={{ position: 'absolute', inset: 30, background: d.bgColor, borderRadius: '50%', overflow: 'hidden', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
          <GrainOverlay opacity={0.10} blend="multiply"/>
          <SpiralPatch size={sz} tone={spiralToneFor(d.bgColor, d.textColor)} rotation={rot}/>
        </div>
      </div>
    );
  },
  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 (
      <div style={{ position: 'absolute', inset: 0, overflow: 'hidden' }}>
        <div style={{ position: 'absolute', inset: 30, background: d.bgColor, borderRadius: radius, overflow: 'hidden', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
          <GrainOverlay opacity={0.10} blend="multiply"/>
          <SpiralPatch size={sz} tone={spiralToneFor(d.bgColor, d.textColor)} rotation={rot}/>
        </div>
      </div>
    );
  },
  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 (
      <div style={{ position: 'absolute', inset: 0, overflow: 'hidden', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
        {/* outline backdrop offset */}
        <div style={{ position: 'absolute', inset: 60, borderRadius: '50%', background: d.outlineColor, transform: 'translate(20px, 24px)' }}/>
        <div style={{ position: 'absolute', inset: 60, borderRadius: '50%', background: d.bgColor, overflow: 'hidden', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
          <GrainOverlay opacity={0.10} blend="screen"/>
          <SpiralPatch size={760} tone={spiralToneFor(d.bgColor, d.textColor)} rotation={rot}/>
        </div>
      </div>
    );
  },
  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 (
    <div style={{ position: 'absolute', inset: 0, overflow: 'hidden', display: 'flex', alignItems: 'center', justifyContent: 'center', fontFamily: 'Poppins' }}>
      <div style={{ position: 'absolute', inset: 30, background: bg, borderRadius: '50%', overflow: 'hidden', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', color: fg }}>
        <GrainOverlay opacity={0.10} blend="multiply"/>
        {glyph}
      </div>
    </div>
  );
}

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) => (
    <HighlightCover bg={d.bgColor} fg={d.textColor}
      glyph={<SpiralPatch size={520} tone={spiralToneFor(d.bgColor, d.textColor)} rotation={parseFloat(d.rotation)||0}/>}
    />
  ),
  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) => (
    <HighlightCover bg={d.bgColor} fg={d.textColor}
      glyph={<div style={{ fontSize: 620, fontWeight: 700, letterSpacing: '-0.05em', lineHeight: 1, color: d.textColor }}>{d.letter}</div>}
    />
  ),
  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) => (
    <HighlightCover bg={d.bgColor} fg={d.textColor}
      glyph={<div style={{ fontSize: (d.word||'').length > 6 ? 140 : 180, fontWeight: 700, letterSpacing: '0.04em', color: d.textColor, textAlign: 'center' }}>{d.word}</div>}
    />
  ),
  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) => (
    <HighlightCover bg={d.bgColor} fg={d.textColor}
      glyph={<>
        <div style={{ fontSize: 460, fontWeight: 700, letterSpacing: '-0.05em', lineHeight: 0.9, color: d.textColor }}>{d.number}</div>
        <div style={{ fontSize: 56, letterSpacing: '0.32em', marginTop: 20, fontWeight: 600, color: d.textColor }}>{d.label}</div>
      </>}
    />
  ),
  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) => (
    <HighlightCover bg={d.bgColor} fg={d.textColor}
      glyph={<>
        <StripesMark width={760} color={d.textColor} lines={9} ratio={0.6}/>
        {d.label && <div style={{ position: 'absolute', fontSize: 100, fontWeight: 700, letterSpacing: '0.32em', color: d.textColor, mixBlendMode: 'difference' }}>{d.label}</div>}
      </>}
    />
  ),
  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) => (
    <HighlightCover bg={d.bgColor} fg={d.textColor}
      glyph={<>
        <div style={{ fontSize: 460, lineHeight: 0.8, color: d.textColor }}>{d.glyph}</div>
        <div style={{ fontSize: 64, letterSpacing: '0.32em', fontWeight: 600, marginTop: 30, color: d.textColor }}>{d.label}</div>
      </>}
    />
  ),
  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) => (
    <div style={{ position: 'absolute', inset: 0, overflow: 'hidden', fontFamily: 'Poppins' }}>
      <MediaSlot media={d.media} fallbackTone={d.photo}/>
      <div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(180deg, rgba(14,13,13,0.7) 0%, rgba(14,13,13,0.1) 35%, rgba(14,13,13,0.95) 100%)' }}/>
      <div style={{ position: 'absolute', left: 80, right: 80, top: 80, display: 'flex', justifyContent: 'space-between', color: d.textColor }}>
        <div style={{ fontSize: 22, letterSpacing: '0.32em', color: d.accentColor, fontWeight: 600 }}>{d.eyebrow}</div>
        <div style={{ fontSize: 22, letterSpacing: '0.22em', opacity: 0.7, fontFamily: 'JetBrains Mono, monospace' }}>{d.index} / {d.count}</div>
      </div>
      <div style={{ position: 'absolute', left: 80, right: 80, bottom: 120, fontSize: 92, fontWeight: 700, lineHeight: 0.95, letterSpacing: '-0.04em', color: d.textColor, whiteSpace: 'pre-line' }}>{d.title}</div>
    </div>
  ),
  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) => (
    <div style={{ position: 'absolute', inset: 0, background: d.bgColor, color: d.textColor, padding: 80, fontFamily: 'Poppins', overflow: 'hidden', boxSizing: 'border-box' }}>
      <GrainOverlay opacity={0.08} blend="multiply"/>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
        <div style={{ fontSize: 20, letterSpacing: '0.32em', color: d.accentColor, fontWeight: 600 }}>{d.eyebrow}</div>
        <div style={{ fontSize: 20, letterSpacing: '0.22em', opacity: 0.5, fontFamily: 'JetBrains Mono, monospace' }}>{d.index} / {d.count}</div>
      </div>
      <div style={{ fontSize: 72, fontWeight: 700, lineHeight: 0.95, letterSpacing: '-0.035em', marginTop: 40, whiteSpace: 'pre-line' }}>{d.title}</div>
      <div style={{ marginTop: 60, display: 'flex', flexDirection: 'column', gap: 24 }}>
        {(d.items || []).map(([n, t], i) => (
          <div key={i} style={{ display: 'flex', alignItems: 'baseline', gap: 24, paddingBottom: 20, borderBottom: `1px solid ${d.textColor}1a` }}>
            <div style={{ fontSize: 56, fontWeight: 700, color: d.accentColor, lineHeight: 1, minWidth: 80 }}>{n}</div>
            <div style={{ fontSize: 32, fontWeight: 500, letterSpacing: '-0.01em' }}>{t}</div>
          </div>
        ))}
      </div>
    </div>
  ),
  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) => (
    <div style={{ position: 'absolute', inset: 0, background: d.bgColor, color: d.textColor, padding: 100, fontFamily: 'Poppins', overflow: 'hidden', boxSizing: 'border-box', display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>
      <GrainOverlay opacity={0.10} blend="multiply"/>
      <div style={{ position: 'absolute', top: 40, right: 80, fontSize: 18, letterSpacing: '0.22em', opacity: 0.5, fontFamily: 'JetBrains Mono, monospace' }}>{d.index} / {d.count}</div>
      <div style={{ fontSize: 84, fontWeight: 700, lineHeight: 1.0, letterSpacing: '-0.03em', whiteSpace: 'pre-line' }}>{d.quote}</div>
      <div style={{ fontSize: 24, letterSpacing: '0.32em', fontWeight: 600, marginTop: 40, color: d.accentColor }}>{d.attrib}</div>
      <div style={{ position: 'absolute', left: -60, bottom: -60 }}>
        <SpiralPatch size={360} tone={spiralToneFor(d.bgColor, d.accentColor)} rotation={20}/>
      </div>
    </div>
  ),
  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;
