// VOC 报告 HTML 渲染器 · v2 const { esc, fmtNum, fmtMoney, cleanText } = require('./voc-analyze'); const STYLES = require('./voc-styles'); const SCRIPTS = require('./voc-scripts'); function renderNav(product, hasDouyin, hasTikTok) { const sections = [ { id: 's0', tip: '封面' }, { id: 's1', tip: '执行摘要' }, { id: 's2', tip: '核心洞察' }, { id: 's3', tip: '爆款笔记' }, { id: 's4', tip: '用户原声' }, { id: 's5', tip: 'KOL 分析' }, { id: 's6', tip: 'Amazon 对标' }, ...(hasTikTok ? [{ id: 'sttk', tip: 'TikTok 海外' }] : []), ...(hasDouyin ? [{ id: 'sdy', tip: '抖音阵地' }] : []), { id: 's7', tip: '痛点亮点' }, { id: 's8', tip: '机会矩阵' }, { id: 's9', tip: '行动建议' }, ]; return `
${esc(product)} — 洞察报告 v2.0
`; } function renderS0Cover({ product, category, collectedAt, A, slideNum, slideTotal }) { return `
VOC / 2026 / 中国市场 · Amazon 对标

${esc(category)}

${esc(product)}

用户洞察 · VOC 报告

基于小红书 ${A.metrics.notes} 篇相关笔记 · ${A.metrics.sampleComments} 条真实评论 · ${A.kols.length} 位作者画像 · ${A.amazonProducts.length > 0 ? `叠加 Amazon ${A.amazonProducts.length} 款国际对标产品` : '国际品类对标中'}, 解构用户真实需求、场景与未被满足的机会。

${fmtNum(A.metrics.totalLikes)}
累计点赞
${fmtNum(A.metrics.totalComments)}
累计评论
${fmtNum(A.metrics.totalCollected)}
累计收藏
${fmtNum(A.metrics.totalShared)}
累计分享

SCROLL DOWN · 采集时间 ${collectedAt}

`; } function renderS1Summary({ A, slideNum, slideTotal }) { const engRate = A.metrics.totalLikes > 0 ? ((A.metrics.totalComments / A.metrics.totalLikes) * 100).toFixed(1) : '—'; return `
01 · Executive Summary

执行摘要

核心数据一览,洞察始于规模。

笔记样本
${A.metrics.notes}
关键词覆盖 ${Object.keys(A.meta.xiaohongshu?.by_keyword || {}).length} 个维度
评论原声
${A.metrics.sampleComments}
来自 Top 笔记深度抓取
作者画像
${A.kols.length}
头部 ${A.kolTier.head.length} · 腰部 ${A.kolTier.mid.length} · KOC ${A.kolTier.tail.length}
评论/点赞比
${engRate} %
用户参与深度指标
SAMPLE BREAKDOWN

样本规模分布

GEO DISTRIBUTION

用户地域 TOP 10

`; } function renderS2Insights({ A, themes, slideNum, slideTotal }) { const topPain = themes.pain[0] || { tag: '—', count: 0 }; const topBright = themes.bright[0] || { tag: '—', count: 0 }; const topScenario = themes.scenarios[0] || { tag: '—', count: 0 }; const hotNote = A.topNotes[0] || {}; const topComment = A.topComments[0] || {}; return `
02 · Core Insights

三条核心洞察

从数据里提炼出的不言而喻的真相。

用户对此品类的第一反应是 ${esc(topPain.tag)}
而非功效或品牌。

在 ${A.metrics.sampleComments} 条评论中,"${esc(topPain.tag)}"被反复提及 ${topPain.count} 次,超过任何品牌词。这是品类级的心智障碍,比产品本身更值得投资解决。

${topPain.count}提及次数
${topBright.count}正向对照
${((topPain.count / Math.max(1, A.metrics.sampleComments)) * 100).toFixed(1)}%覆盖率

最高赞笔记 ${fmtNum(hotNote.liked_count || 0)} 赞,
主题围绕 ${esc(topScenario.tag)}

"${esc(cleanText(hotNote.title || '').slice(0, 40))}" — 证明 ${esc(topScenario.tag)} 是此品类最强的种草切入点。内容方向 / 产品卖点 / 投流素材都应围绕这一场景聚焦。

${fmtNum(hotNote.liked_count || 0)}最高赞
${fmtNum(hotNote.comments_count || 0)}评论
${fmtNum(hotNote.collected_count || 0)}收藏

一条 ${fmtNum(topComment.like_count || 0)} 赞的评论,
说明 ${esc(topBright.tag)} 是消费者自发认同的卖点。

"${esc(cleanText(topComment.content || '').slice(0, 70))}" — 这条评论的点赞数体现消费者的共识形成。这个表达本身就是可直接挪用的最佳营销文案。

${fmtNum(topComment.like_count || 0)}评论获赞
${topBright.count}亮点提及
${topScenario.count}场景覆盖
`; } function renderS3Notes({ A, slideNum, slideTotal }) { const top12 = A.topNotes.slice(0, 12); return `
03 · Top Notes

爆款笔记 · 种草全景

按点赞数排序的 Top 12 笔记 · 标题 / 互动结构 / 发布人画像

${top12.map((n, i) => `
#${String(i + 1).padStart(2, '0')}
${esc(cleanText(n.title || ''))}
${n.type === 'video' ? '视频' : '图文'} ${n.user?.red_official_verified ? '官方认证' : ''}
${fmtNum(n.liked_count)}LIKES
${fmtNum(n.comments_count)}COMMENTS
${fmtNum(n.collected_count)}SAVES
@${esc(n.user?.nickname || '未知作者')}
`).join('\n ')}
NOTE HEATMAP

笔记互动热力图

X 轴=点赞 · Y 轴=评论 · 气泡大小=收藏 · 找出低评论高收藏的"干货潜力"笔记

`; } function renderS4Voices({ A, slideNum, slideTotal }) { const voices = A.topComments.slice(0, 15); return `
04 · Voice Wall

用户真实原声

高赞评论 Top 15 · 未经任何编辑的 VOC 原始语料

${voices.map((c, i) => `

${esc(cleanText(c.content))}

${esc((c.user_info?.nickname || '匿名').slice(0, 14))}${c.ip_location ? ' · ' + esc(c.ip_location) : ''} ♥ ${fmtNum(c.like_count || 0)}
`).join('\n ')}
`; } function renderS5KOL({ A, slideNum, slideTotal }) { const T = A.kolTier; const totalQualified = A.kols.length; const filtered = A.metrics.kolsFiltered || 0; const hasAnyKol = totalQualified > 0; const hasTopTier = T.head.length + T.mid.length > 0; return `
05 · KOL Landscape

作者生态分层

仅展示 ≥ 1000 粉且真实活跃的作者 · 已过滤 ${filtered} 位低粉低互动账号

头部 KOL (50w+)
${T.head.length}
品牌背书 · 单篇合作单价高
腰部达人 (5w-50w)
${T.mid.length}
性价比最高 · 批量合作
尾部 (5k-5w)
${T.tail.length}
垂类精准 · 素材铺量
KOC (1k-5k)
${T.koc.length}
真实口碑 · 低成本试水
${!hasAnyKol ? `

本次采样未发现 ≥1000 粉合格作者
说明此品类在小红书尚未形成规模化 KOL 生态。

有效样本仅 ${filtered} 个微型账号(粉丝多在百位数),无稳定流量池。这是品类空白信号
· 策略上 — 用 品牌自营矩阵 抢占心智(而非依赖达人合作)
· 执行上 — 做 大号跨品类合作(健康/养生/母婴领域头部)切入垂类
· 测试上 — 站外抖音 + 微信私域 成本可能更低效果更高

` : `

合格作者 Top ${Math.min(totalQualified, 9)} · 按粉丝排序

${A.kols.slice(0, 9).map((k) => `
${k.images ? `` : '
'}
${esc(k.nickname || '')}${k.red_official_verified ? ' 官方' : ''}
${esc(cleanText(k.desc || '暂无简介').slice(0, 70))}
${fmtNum(k.fans)} 粉丝 ${fmtNum(k.interaction || k.likes)} 赞藏 ${k.ip_location ? `${esc(k.ip_location)}` : ''}
`).join('\n ')}
${!hasTopTier ? `
STRATEGIC NOTE

头部/腰部缺失的含义

目前合格样本全部集中在「尾部 + KOC」区间,这说明:
1. 此品类属于 真实口碑驱动型,靠数十位 KOC 铺量比签一位头部更有效;
2. 建议 优先批量投放 ${T.tail.length + T.koc.length} 位尾部/KOC 作者,以真实体验 × 素材密度驱动种草;
3. 未来如需引爆,再考虑跨品类头部合作(如 母婴 / 健康科普 / 生活方式 赛道的 5w+ 达人)。

` : ''} `}
`; } function renderS6Amazon({ A, slideNum, slideTotal }) { const amz = A.amazon; if (!amz || amz.empty) { return `
06 · Amazon Benchmark

Amazon 国际对标

此品类在 Sorftime 未返回产品,需补充英文关键词或确认 domain 映射。

数据未采集或关键词无匹配产品。建议补充英文关键词后重跑采集脚本。

`; } const hasReviews = Object.keys(amz.reviewsSummary || {}).length > 0; const hasDetails = (amz.detailProducts || []).length > 0; // ═════════════════════════════════════════════ // 6.0 · 核心发现 & 机会评估(分析层 · hero) // ═════════════════════════════════════════════ const op = amz.opportunity; const findings = amz.keyFindings || []; const toneColor = (t) => ({ green: 'var(--green)', amber: 'var(--amber)', rose: 'var(--rose)', blue: 'var(--blue)', purple: 'var(--purple)', }[t] || 'var(--amber)'); const executiveLayer = op ? `

6.0 · 核心发现 & 机会评估

基于 ${amz.total} 款产品 + ${op.dimensions.length} 维度诊断的判断层 · 每个结论都是可以直接拍板的行动锚点

CATEGORY OPPORTUNITY
${esc(op.grade.split(' · ')[0])}
${esc(op.grade.split(' · ')[1] || '')}
${op.totalScore} / ${op.maxScore}
甜点价位:${esc(op.sweetSpot)}
${op.dimensions.map((d) => `
${d.icon}
${esc(d.name)} ${esc(d.level)} ${[1,2,3,4,5].map((i) => ``).join('')}
${d.note.replace(/\*\*(.+?)\*\*/g, '$1')}
`).join('')}
${findings.map((f, i) => `
FINDING ${String(i + 1).padStart(2, '0')}
${f.icon}

${esc(f.title)}

${f.body}

`).join('')}
` : ''; // 6.1 品类全景 const overview = `

6.1 · 品类全景

产品样本
${amz.total}
${amz.keywords.length} 组关键词 × 3 页
均价 / 中位
$${amz.priceStats.mean.toFixed(1)}
中位 $${amz.priceStats.median.toFixed(2)} · $${amz.priceStats.min.toFixed(1)}–$${amz.priceStats.max.toFixed(0)}
平均评分
${amz.avgRating.toFixed(2)}
用户质量期望 · 中位 ${amz.medianRating.toFixed(1)}
月销总额
${fmtMoney(amz.totalMonthlyRevenue)}
CR5 ${amz.top5Share.toFixed(1)}% · HHI ${Math.round(amz.hhi)}
PRICE BAND

价格带分布

RATING DISTRIBUTION

评分分布

`; // 6.2 品牌格局 const top10Brands = amz.topBrands.slice(0, 10); const brandStructure = `

6.2 · 品牌格局

BRAND SHARE

品牌月销份额

TOP 10 BRANDS

Top 10 品牌表

${top10Brands.map((b, i) => ``).join('')}
#品牌SKU份额月销额
${String(i + 1).padStart(2, '0')} ${esc(b.brand)} ${b.count} ${b.share.toFixed(1)}% ${fmtMoney(b.sales)}
⚠ 市场集中度:CR5 = ${amz.top5Share.toFixed(1)}%, HHI = ${Math.round(amz.hhi)} ${amz.top5Share >= 60 ? '(高度集中,头部垄断)' : amz.top5Share >= 40 ? '(中度集中,需差异化切入)' : '(竞争充分,进入门槛低)'}
`; // ═════════════════════════════════════ // 6.2+ · Top 5 竞品深度画像(分析层 · 紧跟 6.2 品牌格局) // ═════════════════════════════════════ const dossiers = amz.competitorDossiers || []; const dossiersSection = dossiers.length ? `

6.2+ · Top ${dossiers.length} 竞品深度画像

不是看品牌数据,而是看它在市场中的位置它外部的优势它给新玩家留下的缝

${dossiers.map((d, i) => `
#${String(i + 1).padStart(2, '0')}

${esc(d.brand)}

${esc(d.positioning)}
份额
${d.share.toFixed(1)}%
SKU
${d.skuCount}
均价
$${d.avgPrice.toFixed(1)}
评分
★ ${d.avgRating.toFixed(2)}
${d.topProduct ? `
TOP PRODUCT · ${esc(d.topProduct.asin)}
${esc((d.topProduct.title || '').slice(0, 70))}
$${d.topProduct.price.toFixed(2)} ★ ${d.topProduct.rating.toFixed(1)} ${fmtNum(d.topProduct.reviewCount)} 评 ${fmtNum(d.topProduct.monthlySales)}/月
` : ''}
✅ STRENGTHS
${d.strengths.length ? d.strengths.map((s) => `
${esc(s.label)}×${s.n}
`).join('') : '
评论样本少,未提炼出明显优势
'}
⚠ WEAKNESSES
${d.weaknesses.length ? d.weaknesses.map((w) => `
${esc(w.label)}×${w.n}
`).join('') : '
差评样本少,未捉到明显缝
'}
价格差 ${d.priceVsCategory > 0 ? '+' : ''}$${d.priceVsCategory} · 评分差 ${d.ratingVsCategory > 0 ? '+' : ''}${d.ratingVsCategory} ${d.totalReviews ? fmtNum(d.totalReviews) + ' 评论' : ''}
`).join('')}
` : ''; // 6.3 Top 产品矩阵 const topBySalesRows = amz.topBySales.slice(0, 8).map((p, i) => ` ${String(i + 1).padStart(2, '0')}
${esc(p.Brand || 'N/A')}
${esc((p.Title || '').slice(0, 80))}
$${((p.SalesPrice || p.Price || 0) / 100).toFixed(2)} ${fmtNum(p.ListingSalesVolumeOfMonth || 0)} ${p.Ratings ? `★ ${p.Ratings}` : '—'}
${fmtNum(p.RatingsCount || 0)} 评 ${esc(p.Asin || '—')} `).join(''); const topByReviewsRows = amz.topByReviews.slice(0, 8).map((p, i) => ` ${String(i + 1).padStart(2, '0')}
${esc(p.Brand || 'N/A')}
${esc((p.Title || '').slice(0, 80))}
★ ${p.Ratings || '—'} ${fmtNum(p.RatingsCount || 0)} $${((p.SalesPrice || p.Price || 0) / 100).toFixed(2)} `).join(''); const topMatrix = `

6.3 · Top 产品矩阵

MONTHLY SALES LEADERS

月销量 Top 8

${topBySalesRows}
#品牌 / 产品售价月销评分ASIN
REVIEW VOLUME LEADERS

评论数 Top 8 · 用户口碑沉淀

${topByReviewsRows}
#品牌 / 产品评分评论数售价
REVIEW COUNT DISTRIBUTION

评论数分布 · 口碑沉淀结构

`; // 6.4 深度产品画像 const detailSection = hasDetails ? `

6.4 · 深度产品画像

对 Top ${amz.detailProducts.length} 款拉取完整 ProductRequest · 含 BSR / 上线日 / Buybox 卖家 / 变体数

${amz.detailProducts.slice(0, 6).map((p) => { const photo = (typeof p.photo === 'string' ? p.photo : p.photo?.Url) || ''; return `
${photo ? `` : '
'}
${esc(p.brand || 'N/A')} ${p.hasVideo ? '有视频' : ''} ${p.aplus ? 'A+' : ''} ${p.brandStore ? '品牌店' : ''} ${p.variantCount > 1 ? `${p.variantCount} 变体` : ''}
${esc((p.title || '').slice(0, 78))}
ASIN · ${esc(p.asin)}
售价
${fmtMoney(p.price)}
评分
★ ${p.rating || '—'} · ${fmtNum(p.ratingsCount || 0)}
月销
${fmtNum(p.monthlySales || 0)}
BSR
${p.bsrRank ? '#' + fmtNum(p.bsrRank) : '—'}
上线天数
${p.onlineDays ? p.onlineDays + 'd' : '—'}
Buybox 卖家
${esc((p.buyboxSeller || '—').slice(0, 16))}
`; }).join('')}
${amz.avgOnlineDays > 0 ? `
MARKET MATURITY

品类成熟度:平均上线 ${amz.avgOnlineDays} 天 (${(amz.avgOnlineDays / 365).toFixed(1)} 年)

Top ${amz.detailProducts.length} 深度样本中 · ${amz.newProducts} 款新品(<180d) · ${amz.matureProducts} 款成熟品(≥1年) ${amz.matureProducts > amz.newProducts ? '· 市场结构稳固,新玩家需强差异化' : '· 新品频出,市场动态,有迭代窗口'}

` : ''}` : ''; // 6.5 评论深度 const reviewSamples = hasReviews ? Object.values(amz.reviewsSummary) : []; const reviewSection = hasReviews ? `

6.5 · 真实评论抽样

对 Top ${reviewSamples.length} 款抓取真实评论 · 按星级分布 + 正负面抽样

${reviewSamples.map((r) => { const posSample = r.positiveReviews[0]; const negSample = r.negativeReviews[0]; const starStr = [5, 4, 3, 2, 1].map((s) => `${'★'.repeat(s)} ${r.starDist[s] || 0}`).join(' '); return `
${esc(r.asin)}

${esc(r.brand || 'N/A')} · ${esc((r.title || '').slice(0, 50))}

★ ${r.averageRating.toFixed(2)}
${r.total} 条抽样
${starStr}
${posSample ? `
★ ${posSample.Star ?? posSample.Rating ?? posSample.rating} · ${esc(String(posSample.Title || posSample.title || '好评').slice(0, 40))}${posSample.ConsumerName ? ' · ' + esc(String(posSample.ConsumerName).slice(0, 16)) : ''}${posSample.IsVP ? ' ✓VP' : ''}
${esc(cleanText(posSample.Content || posSample.content || '').slice(0, 240))}
` : '
'} ${negSample ? `
★ ${negSample.Star ?? negSample.Rating ?? negSample.rating} · ${esc(String(negSample.Title || negSample.title || '差评').slice(0, 40))}${negSample.ConsumerName ? ' · ' + esc(String(negSample.ConsumerName).slice(0, 16)) : ''}${negSample.IsVP ? ' ✓VP' : ''}
${esc(cleanText(negSample.Content || negSample.content || '').slice(0, 240))}
` : '
'}
`; }).join('')}
STAR DISTRIBUTION

各产品 5 星分布对比

` : ''; // ═════════════════════════════════════ // 6.5+ · VOC 用户原声(分析层 · 紧跟 6.5 评论抽样) // ═════════════════════════════════════ const vt = amz.amazonThemes; const renderBarRow = (items, color, totalRef) => items.map((t) => { const pct = totalRef ? Math.min(100, (t.count / totalRef * 100)) : 0; return `
${t.emoji || t.icon || '•'} ${esc(t.tag.split(' · ')[1] || t.tag)}
${t.count} · ${t.pct.toFixed(1)}%
${t.examples?.[0] ? `
"${esc(t.examples[0].text.slice(0, 90))}${t.examples[0].text.length > 90 ? '…' : ''}"${t.examples[0].brand ? ` · ${esc(t.examples[0].brand)}` : ''}
` : ''}
`; }).join(''); const vocThemesSection = vt && (vt.pain.length || vt.bright.length || vt.scenarios.length) ? `

6.5+ · VOC 用户原声主题

${vt.totalReviews} 条真实评论中聚类 · ${vt.negReviews} 条差评 × ${vt.posReviews} 条好评 · 高频词聚类后得出的结构性主题

PAIN POINTS · 痛点

🔴 用户核心痛点 Top ${vt.pain.length}

${vt.pain.length ? renderBarRow(vt.pain, 'var(--rose)', vt.negReviews || 1) : '
差评样本不足以提炼共同痛点
'}
BRIGHT SPOTS · 亮点

🟢 用户最爱亮点 Top ${vt.bright.length}

${vt.bright.length ? renderBarRow(vt.bright, 'var(--green)', vt.posReviews || 1) : '
好评样本不足
'}
USAGE SCENARIOS · 场景

📍 使用场景分布

${vt.scenarios.length ? renderBarRow(vt.scenarios, 'var(--blue)', vt.totalReviews || 1) : '
评论中未明显捉到场景信号
'}
` : ''; // ═════════════════════════════════════ // 6.6 · 战略建议(分析层 · 收尾) // ═════════════════════════════════════ const st = amz.strategy; const renderTrack = (track, accent, eyebrow) => `
${eyebrow}
${track.badge}

${esc(track.title)}

`; const strategySection = st ? `

6.6 · 从数据到行动 · 三马车战略建议

基于以上所有分析维度(机会等级 ${esc(op?.grade || '—')})导出的分阶段可执行动作

${renderTrack(st.short, 'var(--amber)', 'TRACK 1 · 0-30 DAYS')} ${renderTrack(st.mid, 'var(--green)', 'TRACK 2 · 30-90 DAYS')} ${renderTrack(st.long, 'var(--purple)', 'TRACK 3 · 90+ DAYS')}
` : ''; return `
06 · Amazon Benchmark

Amazon 国际对标

${amz.keywords.length} 组英文关键词 × 3 页 · ${amz.total} 款产品 · Top ${amz.detailProducts.length} 深度详情 · Top ${Object.keys(amz.reviewsSummary).length} 真实评论

${executiveLayer} ${overview} ${brandStructure} ${dossiersSection} ${topMatrix} ${detailSection} ${reviewSection} ${vocThemesSection} ${strategySection}
`; } function renderSDouyin({ A, slideNum, slideTotal }) { const dy = A.douyin; if (!dy || dy.empty) return ''; const v = dy.video; const u = dy.userProfile; const topCmts = dy.topComments || []; const topPosts = dy.topPosts || []; const userPosts = dy.userPosts || []; const searches = dy.searches || {}; const shareType = dy.shareType || 'video'; const accountCards = dy.accountCards || []; const agg = dy.aggMetrics; // ---------- 账号矩阵(主号 + 竞品号 + 垂类号,多卡一排)---------- // 只在有 2+ 账号时才展示矩阵,单账号走 heroUser 旧样式 const roleToneMap = { '官方号': { bg: 'rgba(255,0,80,.14)', fg: '#FF3E75', border: 'rgba(255,0,80,.3)' }, '品牌竞品': { bg: 'rgba(245,166,35,.14)', fg: '#F5A623', border: 'rgba(245,166,35,.3)' }, '品牌竞品号': { bg: 'rgba(245,166,35,.14)', fg: '#F5A623', border: 'rgba(245,166,35,.3)' }, '垂类博主': { bg: 'rgba(139,92,246,.14)', fg: '#A78BFA', border: 'rgba(139,92,246,.3)' }, '垂类博主号': { bg: 'rgba(139,92,246,.14)', fg: '#A78BFA', border: 'rgba(139,92,246,.3)' }, '补充号': { bg: 'rgba(0,220,130,.14)', fg: '#22C581', border: 'rgba(0,220,130,.3)' }, }; const accountMatrix = accountCards.length >= 2 ? `
抖音账号矩阵 · ${accountCards.length} 个账号 ${agg ? `· 累计粉丝 ${fmtNum(agg.total_followers)} · 采样作品 ${agg.total_posts_sampled} · 认证账号 ${agg.verified_count}/${agg.total_accounts}` : ''}
${accountCards.map((c) => { const tone = roleToneMap[c.role] || roleToneMap['官方号']; const hasData = !c.error && c.nickname !== '—'; if (!hasData) { return `
${esc(c.role)}

${esc(c.label || c.role)}

⚠ ${esc((c.error || '数据不可用').slice(0, 80))}
${esc(c.share_url || '')}
`; } return `
${esc(c.role)} ${c.custom_verify ? `✓ ${esc(c.custom_verify.slice(0, 14))}` : ''}
${c.avatar ? `` : `
`}
${esc((c.nickname || '—').slice(0, 20))}
${esc((c.signature || '').slice(0, 40))}
${fmtNum(c.follower_count)}
粉丝
${fmtNum(c.aweme_count)}
作品
${fmtNum(c.total_favorited)}
获赞
${c.top_post ? `
TOP POST
${esc(cleanText(c.top_post.desc || '').slice(0, 90))}
♥ ${fmtNum(c.top_post.digg)} 💬 ${fmtNum(c.top_post.comment)} ${c.top_post.play > 0 ? `▶ ${fmtNum(c.top_post.play)}` : ''}
` : `
暂无作品数据
`}
`; }).join('')}
` : ''; // ---------- Hero:作者画像卡 + 作品节奏 ---------- const heroUser = u ? `
${u.avatar ? `` : '
'}
抖音 · ${shareType === 'user' ? '品牌旗舰店' : '视频'} ${u.custom_verify ? `${esc(u.custom_verify.slice(0, 20))}` : ''} ${u.ip_location ? `${esc(u.ip_location)}` : ''}

${esc(u.nickname || '—')}

${esc((u.signature || '暂无简介').slice(0, 120))}
粉丝
${fmtNum(u.follower_count || 0)}
作品
${fmtNum(u.aweme_count || 0)}
获赞
${fmtNum(u.total_favorited || 0)}
关注
${fmtNum(u.following_count || 0)}
PRODUCTION RHYTHM

作品节奏均值

${dy.authorStats ? `
${fmtNum(Math.round(dy.authorStats.avgDigg))}
AVG ♥ LIKES
${fmtNum(Math.round(dy.authorStats.avgComment))}
AVG 💬 COMMENT
${fmtNum(Math.round(dy.authorStats.avgPlay))}
AVG ▶ PLAY
${(u.follower_count || 0) > 500000 ? `头部品牌号:${fmtNum(u.follower_count)} 粉丝 + 高频发布 (${u.aweme_count} 作品) 已建立稳定内容资产,适合做 KOL 型种草。` : (u.follower_count || 0) > 10000 ? `成长期品牌号:粉丝 ${fmtNum(u.follower_count)},内容储备 ${u.aweme_count} 支,应加大高转化爆款内容产出。` : `起步品牌号:粉丝仅 ${fmtNum(u.follower_count)},首要任务是建立内容基线和选定垂类话题。`}
` : '

暂无作品均值数据。

'}
` : ''; // ---------- Hot video 卡片(仅 video-type 分享链)---------- const videoCard = v ? `
${v.cover ? `` : ''}
品牌视频 ${(v.text_extra || []).slice(0, 3).map((t) => `#${esc(t)}`).join('')}
${esc(cleanText(v.desc || '').slice(0, 160))}
点赞
${fmtNum(v.statistics?.digg_count || 0)}
评论
${fmtNum(v.statistics?.comment_count || 0)}
分享
${fmtNum(v.statistics?.share_count || 0)}
播放
${fmtNum(v.statistics?.play_count || 0)}
` : ''; // ---------- Top Posts 散点 + 卡片网格 ---------- const topPostsSection = topPosts.length ? `

品牌号作品热度图 · Top ${topPosts.length}

POST SCATTER

X=点赞 · Y=评论 · 气泡=转发

TOP ${Math.min(topPosts.length, 5)} POSTS

爆款作品清单

${topPosts.slice(0, 5).map((p, i) => `
${p.cover ? `` : '
'}
#${String(i + 1).padStart(2, '0')}
${esc(cleanText(p.desc || '').slice(0, 80))}
♥ ${fmtNum(p.digg)} 💬 ${fmtNum(p.comment)} ▶ ${fmtNum(p.play)}
`).join('')}
` : ''; // ---------- 评论墙 ---------- const commentWall = topCmts.length ? `

抖音评论墙 · Top ${Math.min(topCmts.length, 12)}

${topCmts.slice(0, 12).map((c, i) => `

${esc(cleanText(c.text || ''))}

${esc((c.user?.nickname || '匿名').slice(0, 14))}${c.ip_label ? ' · ' + esc(c.ip_label) : ''} ♥ ${fmtNum(c.digg_count || 0)}
`).join('')}
` : ''; // ---------- 完整作品表 ---------- const userPostsTable = userPosts.length ? `

品牌号完整作品流 · ${userPosts.length} 条

${userPosts.slice().sort((a, b) => (b.statistics?.digg_count || 0) - (a.statistics?.digg_count || 0)).slice(0, 12).map((p, i) => ``).join('')}
#内容点赞评论播放分享收藏
${String(i + 1).padStart(2, '0')} ${esc(cleanText(p.desc || '').slice(0, 72))} ${fmtNum(p.statistics?.digg_count || 0)} ${fmtNum(p.statistics?.comment_count || 0)} ${fmtNum(p.statistics?.play_count || 0)} ${fmtNum(p.statistics?.share_count || 0)} ${fmtNum(p.statistics?.collect_count || 0)}
` : ''; // ---------- 关键词搜索(若有 cookie)---------- const searchSection = Object.keys(searches).length ? `

品类关键词热度

${Object.entries(searches).map(([kw, info]) => { if (!info || info._error) return `
"${esc(kw)}"
⚠ ${esc(String(info?._error || '无数据').slice(0, 60))}
`; if (!info.count) return `
KEYWORD "${esc(kw)}"
当前品类曝光低,搜索结果为 0 条 — 说明此品类在抖音尚未形成内容沉淀,是蓝海机会
`; return `
KEYWORD "${esc(kw)}"
视频数
${info.count}
总点赞
${fmtNum(info.totalDigg)}
${(info.top || []).slice(0, 3).map((t) => `
${fmtNum(t.digg)}❤${esc((t.desc || '').slice(0, 50))}
`).join('')}
`; }).join('')}
` : ''; return `
DY · Douyin Presence

抖音阵地深度

${accountCards.length >= 2 ? `多账号矩阵采样:官方号 + 品牌竞品 + 垂类博主 · 主号深度画像如下` : (shareType === 'user' ? '通过品牌旗舰店分享链直抓 · 账号画像 / 作品流 / 评论 / 品类搜索' : '通过品牌视频分享链直抓 · 视频/评论/作者主页/品类搜索')}

${renderDouyinTrendsBlock({ A })} ${accountMatrix} ${heroUser} ${videoCard} ${topPostsSection} ${commentWall} ${userPostsTable} ${searchSection}
`; } function renderS7Themes({ themes, slideNum, slideTotal }) { const renderGroup = (list, cls, title, eyebrow, color) => `
${eyebrow}

${title}

${list.slice(0, 6).map((t) => `

${esc(t.tag)}

${t.count}次提及
    ${(t.examples || []).slice(0, 2).map((e) => `
  • "${esc(cleanText(e).slice(0, 80))}"
  • `).join('')}
`).join('\n ')}
`; return `
07 · Themes Cluster

痛点 · 亮点 · 场景

从真实语料中提炼出的 VOC 主题 · 标注频次与典型原声

${renderGroup(themes.pain, 'pain', '痛点主题', '07.1 · PAIN POINTS', 'var(--rose)')} ${renderGroup(themes.bright, 'bright', '亮点主题', '07.2 · BRIGHT SPOTS', 'var(--green)')} ${renderGroup(themes.scenarios, 'scene', '场景主题', '07.3 · USAGE SCENARIOS', 'var(--blue)')}
`; } function renderS8Matrix({ themes, slideNum, slideTotal }) { const q1 = themes.pain.filter((t) => ['口感差', '效果不显'].includes(t.tag)).slice(0, 2); const q2 = themes.bright.slice(0, 3); const q3 = themes.pain.filter((t) => ['信任危机', '价格敏感', '副作用担忧'].includes(t.tag)).slice(0, 2); const q4 = themes.scenarios.slice(0, 2); return `
08 · Opportunity Matrix

品类机会矩阵

四象限 · 产品可解决性 × 用户痛点强度

痛点强度 LOW → HIGH
QUADRANT I · 产品机会
高痛点 · 产品可优化解决

${q1.map((t) => `${esc(t.tag)}(${t.count})`).join(' / ') || '尚无强信号'}。这些痛点可通过产品配方、剂型、包装直接优化 — 直接迭代 ROI 最高。

QUADRANT II · 强化既有
低痛点 · 产品可强化放大

${q2.map((t) => `${esc(t.tag)}(${t.count})`).join(' / ') || '尚无强信号'}。这些是消费者已认同的优势,内容营销直接放大。

QUADRANT III · 教育必投
高痛点 · 非产品端

${q3.map((t) => `${esc(t.tag)}(${t.count})`).join(' / ') || '尚无强信号'}。这些是品类心智问题,需要 PR / 教育内容 / 品牌信任建设长期解决。

QUADRANT IV · 场景机会
潜在使用场景 · 可扩展

${q4.map((t) => `${esc(t.tag)}(${t.count})`).join(' / ') || '尚无强信号'}。这些场景是新增长入口,可通过 campaign / 联名 / KOC 铺量打开。

产品可解决性 LOW → HIGH
`; } function renderS9Action({ A, themes, slideNum, slideTotal }) { const topPain = themes.pain[0]?.tag || '核心痛点'; const topScene = themes.scenarios[0]?.tag || '场景'; const topKol = A.kols[0]; return `
09 · Action Plan

30 天行动建议

产品 / 内容 / 渠道 三路并进,从 VOC 到落地。

TRACK A · PRODUCT

产品端

  • ✓ 针对「${esc(topPain)}」推出改良版 SKU / 新剂型
  • ✓ 包装文案直接借用高赞评论原文
  • ✓ 上市前用同样 VOC 链路做 A/B 预测试
TRACK B · CONTENT

内容端

  • ✓ 切入「${esc(topScene)}」场景做系列种草
  • ✓ 复用 Top 爆款笔记标题公式批量产出
  • ✓ 用高赞评论作为视频字幕 / 金句 BGM
TRACK C · CHANNEL

渠道端

  • ✓ 头部 ${A.kolTier.head.length} 位优先合作${topKol ? `(如 ${esc(topKol.nickname)})` : ''}
  • ✓ 腰部 ${A.kolTier.mid.length} 位批量铺垫品类心智
  • ✓ KOC ${A.kolTier.tail.length} 位素材铺量支撑投流

30 天时间线

DAY 01 — 07 · 对齐
对齐核心 VOC 发现 + 快速小样测试

把本报告的 Top 3 洞察 / Top 痛点 / Top 爆款笔记,在内部做 1 次品牌 / 产品 / 运营 / 投放四方对齐会。选 1-2 个高赞评论金句,做小样社交投流测试。

DAY 08 — 14 · 内容
复用爆款结构,启动 KOC 铺量

以「${esc(topScene)}」为切入点,量产 20-30 条 KOC 笔记;每条笔记用不同的高赞评论做卡点。配合 ${A.kolTier.mid.length} 位腰部达人同步发种草。

DAY 15 — 21 · 放大
基于数据优选,头部合作上场

根据 Day 08-14 的测试数据,锁定 2-3 个最强素材方向,启动头部 KOL 合作。同步通过 Amazon 国际对标数据测试是否跨境放量。

DAY 22 — 30 · 复盘
收集新一轮 VOC,迭代下次投放

重新跑 VOC 采集脚本,对比 30 天前后的痛点词频变化。把"新涌现的痛点"作为下一迭代的产品优化 input,形成闭环。

`; } function renderFooter() { return ``; } // ============================================================ // TikTok · Amazon 的内容端对标(独立 slide) // ============================================================ function renderSTikTok({ A, slideNum, slideTotal }) { const tt = A.tiktok; if (!tt || tt.empty) return ''; const m = tt.metrics || {}; const topVids = tt.topVideos || []; const topAuthors = tt.topAuthors || []; const topCmts = tt.topComments || []; const topTags = tt.topHashtags || []; const topRegions = tt.topRegions || []; const byKw = tt.byKeywordStats || {}; const keywords = tt.keywords_used || Object.keys(byKw); const topCatUsers = tt.topCategoryUsers || []; const TT_CYAN = '#25F4EE'; const TT_PINK = '#FE2C55'; // --- Hero metrics 四大数字 + 认证率 + 触达粉丝 --- const heroMetrics = `
VIDEOS
${fmtNum(m.totalVideos)}
跨 ${keywords.length} 个英文关键词
TOTAL PLAYS
${fmtNum(m.totalPlay)}
平均 ER ${(m.avgEngagementRate * 100).toFixed(1)}%
TOTAL LIKES
${fmtNum(m.totalDigg)}
评论 ${fmtNum(m.totalComment)} · 分享 ${fmtNum(m.totalShare)}
CREATORS
${m.totalAuthors}
认证 ${m.verifiedAuthors} · 触达粉丝 ${fmtNum(m.totalFollowersReached)}
`; // --- 关键词覆盖 bar row --- const maxKwDigg = Math.max(1, ...Object.values(byKw).map((v) => v.totalDigg || 0)); const keywordCoverage = Object.keys(byKw).length ? `
KEYWORD COVERAGE

英文关键词覆盖深度 · TikTok 搜索反馈

${Object.entries(byKw).map(([kw, s]) => { const pct = Math.round((s.totalDigg / maxKwDigg) * 100); return `
${esc(kw)}
${s.videos} 视频 · ${fmtNum(s.totalDigg)} 赞 · ${fmtNum(s.totalPlay)}
`; }).join('')}
` : ''; // --- Top 作者卡 --- const authorsBlock = topAuthors.length ? `

Top ${topAuthors.length} 海外创作者矩阵 按粉丝排序

${topAuthors.map((a) => `
${a.avatar ? `` : `
`}
${esc((a.nickname || '—').slice(0, 20))}
@${esc(a.unique_id || '')}${a.region ? ' · ' + esc(a.region) : ''}
${a.custom_verify ? `✓ ${esc(String(a.custom_verify).slice(0, 8))}` : ''}
${esc(a.signature || '(no bio)')}
${fmtNum(a.follower_count)}
FOLLOWERS
${fmtNum(a.aweme_count)}
VIDEOS
${fmtNum(a.total_favorited)}
LIKES
${a.best_post ? `
BEST POST
${esc(cleanText(a.best_post.desc || '').slice(0, 90))}
♥ ${fmtNum(a.best_post.digg)} ▶ ${fmtNum(a.best_post.play)}
` : `
暂无 Best Post 数据
`}
`).join('')}
` : ''; // --- Top Videos 网格 --- const topVideosBlock = topVids.length ? `

爆款视频 Top ${Math.min(topVids.length, 9)} 按点赞排序 · 品类流量锚点

${topVids.slice(0, 9).map((v, i) => `
${v.cover ? `` : `
`}
#${String(i + 1).padStart(2, '0')} ${v._from_keyword ? `· ${esc(v._from_keyword)}` : ''}
${esc(cleanText(v.desc || '').slice(0, 140))}
♥ ${fmtNum(v.digg)} 💬 ${fmtNum(v.comment)} ▶ ${fmtNum(v.play)} ↗ ${fmtNum(v.share)}
${v.author ? `
@${esc(v.author.unique_id || v.author.nickname || '')} · ${fmtNum(v.author.follower_count || 0)}粉 ${v.author.custom_verify ? `` : ''}
` : ''} ${(v.hashtags || []).length ? `
${(v.hashtags || []).slice(0, 4).map((h) => `#${esc(h)}`).join('')}
` : ''}
`).join('')}
` : ''; // --- 评论墙 --- const commentBlock = topCmts.length ? `

📣 海外用户原声 Top ${Math.min(topCmts.length, 12)}

${topCmts.slice(0, 12).map((c, i) => `

${esc(cleanText(c.text || ''))}

${esc((c.user?.nickname || c.user?.unique_id || 'user').slice(0, 20))}${c.user?.region ? ' · ' + esc(c.user.region) : ''} ♥ ${fmtNum(c.digg_count || 0)}
`).join('')}
` : ''; // --- Hashtag 词云(Top List) --- const hashtagBlock = topTags.length ? `
HASHTAG FREQUENCY

Top ${Math.min(topTags.length, 16)} 热门标签

${topTags.slice(0, 16).map((t, i) => { const size = i < 3 ? '1.1rem' : i < 8 ? '.95rem' : '.85rem'; const opacity = i < 3 ? 1 : 0.85; const color = i < 3 ? TT_PINK : TT_CYAN; return ` #${esc(t.tag)} ${t.count} `; }).join('')}
REGION MIX

创作者地区分布

${topRegions.length ? `
${topRegions.map(([r, c]) => { const pct = Math.round((c / (m.totalVideos || 1)) * 100); return `
${esc(r)}
${c} · ${pct}%
`; }).join('')}
` : `

无地区数据

`}
` : ''; // --- 品类头部账号(从 user search 获取) --- const catUserBlock = topCatUsers.length ? `

🏷 品类头部账号 来自 TikTok 用户搜索 · 可作为 B 侧合作 / 学习对象

${topCatUsers.map((u) => `
${u.avatar ? `` : `
`}
${esc((u.nickname || '').slice(0, 18))}${u.custom_verify ? ' ' : ''}
@${esc(u.unique_id || '')}
${fmtNum(u.follower_count || 0)}
粉丝
`).join('')}
` : ''; return `
TT · TikTok Presence

TikTok 海外阵地

Amazon 的内容端对标 · ${keywords.map((k) => esc(k)).join(' / ')} · 不需 cookie 直通海外版

${heroMetrics} ${keywordCoverage} ${authorsBlock} ${topVideosBlock} ${commentBlock} ${hashtagBlock} ${catUserBlock} ${renderTTAdsBlock({ A })}
`; } // ============================================================ // Market Intel · TikTok Ads 官方情报 block(注入 TikTok slide 尾部) // ============================================================ function renderTTAdsBlock({ A }) { const mi = A.marketIntel; if (!mi || mi.empty || !mi.ttAds?.hasData) return ''; const { insights, relatedKeywords, relatedFromKeyword } = mi.ttAds; const TT_CYAN = '#25F4EE'; const TT_PINK = '#FE2C55'; // Insight 卡:每个 keyword 1 张 const insightsCards = insights.length ? `
${insights.map((ins, i) => { const postChange = ins.post_change || 0; const trendColor = postChange > 50 ? '#22c55e' : (postChange > 0 ? TT_CYAN : '#f97316'); const trendArrow = postChange > 0 ? '▲' : (postChange < 0 ? '▼' : '●'); return `
AD KEYWORD #${i + 1}
TikTok Creative Center

"${esc(ins.keyword)}"

IMPRESSIONS
${fmtNum(ins.impression)}
POST CHANGE
${trendArrow} ${(postChange || 0).toFixed(1)}%
CTR
${(ins.ctr || 0).toFixed(2)}%
CVR
${(ins.cvr || 0).toFixed(2)}%
📝 ${fmtNum(ins.post)} posts ♥ ${fmtNum(ins.like)} 💬 ${fmtNum(ins.comment)} ↗ ${fmtNum(ins.share)} 💰 ${fmtNum(ins.cost)}
`; }).join('')}
` : ''; // Related keywords 词云:按 score 渐变字号 const relatedBlock = relatedKeywords.length ? `
RELATED KEYWORD NETWORK
Top ${relatedKeywords.length} · 来源: "${esc(relatedFromKeyword)}"

TikTok Ads 官方相关词图谱

${relatedKeywords.map((r, i) => { const pct = (r.score || 0) / 100; const fontSize = (0.8 + pct * 0.6).toFixed(2); // 0.8~1.4rem const color = i < 3 ? TT_PINK : (i < 10 ? TT_CYAN : 'var(--text-2)'); const opacity = (0.6 + pct * 0.4).toFixed(2); return ` ${esc(r.name)} ${r.score} `; }).join('')}
` : ''; return `
TT · OFFICIAL MARKET INTEL

TikTok Creative Center 官方情报 第一方数据 · 广告主视角的市场真相

${insightsCards} ${relatedBlock}
`; } // ============================================================ // Market Intel · Douyin 全站热榜基线 block(注入 Douyin slide 顶部) // ============================================================ function renderDouyinTrendsBlock({ A }) { const mi = A.marketIntel; if (!mi || mi.empty || !mi.douyinGlobal?.hasGlobal) return ''; const { hotWords, webHotSearch, hotTopics, categoryHitTotal, filterKeywords } = mi.douyinGlobal; const DY_PINK = '#FF3E75'; const hotWordsBlock = hotWords.length ? `
HOT WORD BILLBOARD
抖音星图 · 近 24h 热词 Top 6
${hotWords.map((w, i) => `
#${String(i + 1).padStart(2, '0')} ${esc(w.title)} ${fmtNum(w.score)}
`).join('')}
` : ''; const webHotBlock = webHotSearch.length ? `
WEB HOT SEARCH
抖音 Web 首页热搜 Top 6
${webHotSearch.map((w, i) => `
#${String(i + 1).padStart(2, '0')} ${esc(w.word)} ${fmtNum(w.view_count)}
`).join('')}
` : ''; const hotTopicsBlock = hotTopics.length ? `
HOT TOPIC CHALLENGE
抖音热挑战 Top 5
${hotTopics.map((t, i) => `
#${esc(t.challenge_name)} ▶ ${fmtNum(t.play_cnt)}
📝 ${fmtNum(t.publish_cnt)} 篇参与
`).join('')}
` : ''; const categoryNote = filterKeywords.length ? `
品类过滤命中: 用关键词 [${filterKeywords.slice(0, 6).map((k) => esc(k)).join(' / ')}] 过滤以上 5 份站级热榜,共命中 ${categoryHitTotal} 条相关内容 — ${categoryHitTotal === 0 ? '说明本品类在抖音全站 Top 榜未形成内容沉淀,是品牌蓝海:现有头部创作者尚未进入健康/滋补赛道。' : '说明本品类已有热点渗透,可顺势挂载热话题做内容承接。'}
` : ''; return `
DOUYIN · CONTENT ECOSYSTEM BASELINE

抖音站级热榜信号 中国内容生态基线 · 决定品牌内容投放语境

Douyin Billboard API
${hotWordsBlock} ${webHotBlock} ${hotTopicsBlock}
${categoryNote}
`; } function buildHTML({ product, category, collectedAt, A, themes }) { const amz = A.amazon || {}; const hasDouyin = A.douyin && !A.douyin.empty; const hasTikTok = A.tiktok && !A.tiktok.empty; // 鲜活的星级分布:把每款产品的 5 星分布转成 ECharts 友好结构 const starDistProducts = Object.values(amz.reviewsSummary || {}).map((r) => ({ asin: r.asin, brand: r.brand || 'N/A', title: (r.title || '').slice(0, 28), stars: [5, 4, 3, 2, 1].map((s) => r.starDist[s] || 0), })); // 抖音作品散点 const douyinPosts = hasDouyin ? (A.douyin.topPosts || []).concat( (A.douyin.userPosts || []).slice(0, 20).map((v) => ({ aweme_id: v.aweme_id, desc: v.desc, digg: v.statistics?.digg_count || 0, comment: v.statistics?.comment_count || 0, share: v.statistics?.share_count || 0, play: v.statistics?.play_count || 0, })) ).filter((v, i, arr) => arr.findIndex((x) => x.aweme_id === v.aweme_id) === i).slice(0, 30) : []; const chartData = { priceBuckets: amz.priceBuckets || A.priceBuckets || {}, topBrands: (A.topBrands || []).slice(0, 8), topBrandShares: (amz.topBrands || []).slice(0, 8).map((b) => ({ name: b.brand, value: +b.share.toFixed(1), sales: b.sales, count: b.count, })), ratingBuckets: amz.ratingBuckets || {}, reviewCountBuckets: amz.reviewCountBuckets || {}, starDistProducts, topIPs: A.topIPs || [], noteHeatmap: (A.topNotes || []).slice(0, 20).map((n) => ({ title: cleanText(n.title || '').slice(0, 22), liked: n.liked_count || 0, comments: n.comments_count || 0, collected: n.collected_count || 0, })), douyinPosts, }; // Slide 编号(基础 10 张 + TikTok/抖音 各占一张) const slideTotal = 10 + (hasTikTok ? 1 : 0) + (hasDouyin ? 1 : 0); const sn = (i) => String(i).padStart(2, '0'); let n = 1; const s0 = sn(n++), s1 = sn(n++), s2 = sn(n++), s3 = sn(n++), s4 = sn(n++); const s5 = sn(n++), s6 = sn(n++); const sttk = hasTikTok ? sn(n++) : ''; const sdy = hasDouyin ? sn(n++) : ''; const s7 = sn(n++), s8 = sn(n++), s9 = sn(n++); const ST = String(slideTotal).padStart(2, '0'); return ` ${esc(product)} · VOC 洞察报告 ${STYLES} ${renderNav(product, hasDouyin, hasTikTok)}
${s0} / ${ST}
/ Space | 滚动或按键切换
${renderS0Cover({ product, category, collectedAt, A, slideNum: s0, slideTotal: ST })} ${renderS1Summary({ A, slideNum: s1, slideTotal: ST })} ${renderS2Insights({ A, themes, slideNum: s2, slideTotal: ST })} ${renderS3Notes({ A, slideNum: s3, slideTotal: ST })} ${renderS4Voices({ A, slideNum: s4, slideTotal: ST })} ${renderS5KOL({ A, slideNum: s5, slideTotal: ST })} ${renderS6Amazon({ A, slideNum: s6, slideTotal: ST })} ${hasTikTok ? renderSTikTok({ A, slideNum: sttk, slideTotal: ST }) : ''} ${hasDouyin ? renderSDouyin({ A, slideNum: sdy, slideTotal: ST }) : ''} ${renderS7Themes({ themes, slideNum: s7, slideTotal: ST })} ${renderS8Matrix({ themes, slideNum: s8, slideTotal: ST })} ${renderS9Action({ A, themes, slideNum: s9, slideTotal: ST })}
${renderFooter()} `; } module.exports = { buildHTML };