from pathlib import Path
import math
import openpyxl
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.enum.text import PP_ALIGN, MSO_AUTO_SIZE, MSO_ANCHOR
from pptx.enum.shapes import MSO_SHAPE
from pptx.enum.dml import MSO_THEME_COLOR
from pptx.dml.color import RGBColor
from pptx.oxml import parse_xml
from pptx.oxml.ns import nsdecls
from pptx.enum.dml import MSO_LINE_DASH_STYLE

PROJECT = Path('/Users/bot1/Volumes/root_for_ai/AI工作区/良渚文化_接店满一个月汇报_20260608')
EXCEL = PROJECT / 'deliverables/良渚文化_接店满一个月_流量交易推广综合分析_含推广结论.xlsx'
OUT = PROJECT / 'deliverables/良渚文化_接店满一个月_客户汇报PPT_简约良渚色系.pptx'

# Palette: Liangzhu-inspired jade / earth / warm sand
COLORS = {
    'deep_jade': '244D45',
    'jade': '5D8B7A',
    'light_jade': 'DCE9E1',
    'sand': 'F4EBDD',
    'clay': 'B8875F',
    'dark': '1F2A25',
    'muted': '6C756F',
    'cream': 'FFFDF8',
    'white': 'FFFFFF',
    'warning': 'C06C4F',
    'gold': 'C8A46A',
    'line': 'D7CDBE',
}
FONT = 'PingFang SC'
TITLE_FONT = 'PingFang SC'

wb = openpyxl.load_workbook(EXCEL, data_only=True)

def fmt_money(v):
    return f"¥{v/10000:.2f}万" if abs(v) >= 10000 else f"¥{v:,.0f}"

def fmt_int(v):
    return f"{v/10000:.1f}万" if abs(v) >= 10000 else f"{v:,.0f}"

def fmt_pct(v):
    if v is None:
        return '—'
    return f"{v*100:+.1f}%"

def rgb(hexstr):
    hexstr = hexstr.replace('#','')
    return RGBColor(int(hexstr[0:2],16), int(hexstr[2:4],16), int(hexstr[4:6],16))

def get_metric(sheet, store, metric):
    ws = wb[sheet]
    for row in ws.iter_rows(min_row=2, values_only=True):
        if row[0] == store and row[1] == metric:
            return {
                'pre': row[2], 'post': row[3], 'delta': row[4], 'rate': row[5], 'judge': row[6], 'note': row[7]
            }
    raise KeyError((sheet, store, metric))

uv = get_metric('流量交易_前后30天', '三店合计', '流量/访客数（UV）')
pv = get_metric('流量交易_前后30天', '三店合计', '浏览量（PV）')
payment = get_metric('流量交易_前后30天', '三店合计', '支付/成交金额')
net = get_metric('流量交易_前后30天', '三店合计', '实际成交金额')
buyers = get_metric('流量交易_前后30天', '三店合计', '支付买家数/成交客户数')
unit = get_metric('流量交易_前后30天', '三店合计', '客单价')
conv = get_metric('流量交易_前后30天', '三店合计', '支付转化率')
uv_value = get_metric('流量交易_前后30天', '三店合计', 'UV价值/访客价值')

stores = ['天猫店','淘宝店','京东店']
store_rows = {}
for s in stores:
    store_rows[s] = {
        'uv': get_metric('流量交易_前后30天', s, '流量/访客数（UV）'),
        'pay': get_metric('流量交易_前后30天', s, '支付/成交金额'),
        'net': get_metric('流量交易_前后30天', s, '实际成交金额'),
        'buyer': get_metric('流量交易_前后30天', s, '支付买家数/成交客户数'),
        'unit': get_metric('流量交易_前后30天', s, '客单价'),
    }

promo_summary = []
ws = wb['推广分析结论']
for row in ws.iter_rows(min_row=2, values_only=True):
    promo_summary.append({
        'level': row[0], 'object': row[1], 'pre': row[2], 'post': row[3],
        'change': row[4], 'conclusion': row[5], 'advice': row[6]
    })

promo_metrics = {}
ws = wb['推广效果_前后变化']
for row in ws.iter_rows(min_row=2, values_only=True):
    obj, metric = row[0], row[1]
    promo_metrics[(obj, metric)] = {
        'meaning': row[2], 'pre': row[3], 'post': row[4], 'delta': row[5], 'rate': row[6], 'judge': row[7], 'conclusion': row[8]
    }

scene_rows = []
ws = wb['推广场景_前后变化']
for row in ws.iter_rows(min_row=2, values_only=True):
    scene_rows.append({
        'store': row[0], 'scene': row[1], 'pre_spend': row[2], 'post_spend': row[3], 'spend_rate': row[4],
        'pre_gmv': row[5], 'post_gmv': row[6], 'gmv_rate': row[7], 'pre_roi': row[8], 'post_roi': row[9], 'roi_rate': row[10],
        'pre_click': row[11], 'post_click': row[12], 'click_rate': row[13], 'pre_cpc': row[14], 'post_cpc': row[15], 'cpc_rate': row[16], 'conclusion': row[17]
    })

prs = Presentation()
prs.slide_width = Inches(13.333)
prs.slide_height = Inches(7.5)
blank = prs.slide_layouts[6]

# ---------- helpers ----------
def add_bg(slide, color='cream'):
    slide.background.fill.solid()
    slide.background.fill.fore_color.rgb = rgb(COLORS[color])

def add_rect(slide, x, y, w, h, fill, line=None, radius=True, transparency=0):
    shape_type = MSO_SHAPE.ROUNDED_RECTANGLE if radius else MSO_SHAPE.RECTANGLE
    shp = slide.shapes.add_shape(shape_type, Inches(x), Inches(y), Inches(w), Inches(h))
    shp.fill.solid(); shp.fill.fore_color.rgb = rgb(COLORS.get(fill, fill))
    if transparency:
        shp.fill.transparency = transparency
    if line:
        shp.line.color.rgb = rgb(COLORS.get(line, line)); shp.line.width = Pt(1)
    else:
        shp.line.color.rgb = rgb(COLORS.get(fill, fill)); shp.line.transparency = 100
    return shp

def add_text(slide, text, x, y, w, h, size=18, color='dark', bold=False, align='left', valign='top', font=FONT, line_spacing=None):
    tx = slide.shapes.add_textbox(Inches(x), Inches(y), Inches(w), Inches(h))
    tf = tx.text_frame
    tf.clear()
    tf.word_wrap = True
    tf.auto_size = MSO_AUTO_SIZE.TEXT_TO_FIT_SHAPE
    tf.vertical_anchor = {'top':MSO_ANCHOR.TOP,'middle':MSO_ANCHOR.MIDDLE,'bottom':MSO_ANCHOR.BOTTOM}.get(valign, MSO_ANCHOR.TOP)
    p = tf.paragraphs[0]
    p.text = text
    p.alignment = {'left':PP_ALIGN.LEFT,'center':PP_ALIGN.CENTER,'right':PP_ALIGN.RIGHT}.get(align, PP_ALIGN.LEFT)
    if line_spacing:
        p.line_spacing = line_spacing
    for run in p.runs:
        run.font.name = font
        run.font.size = Pt(size)
        run.font.bold = bold
        run.font.color.rgb = rgb(COLORS.get(color, color))
    return tx

def add_title(slide, title, subtitle=None, dark=False):
    color = 'white' if dark else 'dark'
    add_text(slide, title, 0.72, 0.50, 8.8, 0.62, size=26, bold=True, color=color, font=TITLE_FONT)
    if subtitle:
        add_text(slide, subtitle, 0.75, 1.08, 10.8, 0.35, size=10.5, color='light_jade' if dark else 'muted')
    add_text(slide, 'LIANGZHU · 运营复盘', 10.85, 0.56, 1.75, 0.25, size=8.5, color='light_jade' if dark else 'jade', bold=True, align='right')

def add_footer(slide, dark=False, text='数据周期：接手前30天 2026.04.09–05.08 / 接手后30天 2026.05.09–06.07'):
    add_text(slide, text, 0.72, 7.05, 9.5, 0.23, size=7.5, color='light_jade' if dark else 'muted')
    add_text(slide, '备注：推广数据覆盖天猫/淘宝；京东纳入店铺整体流量交易分析。', 9.25, 7.05, 3.35, 0.23, size=7.5, color='light_jade' if dark else 'muted', align='right')

def add_card(slide, x, y, w, h, title, value, note='', accent='jade', fill='white', value_size=24):
    add_rect(slide, x, y, w, h, fill, line='line', radius=True)
    add_rect(slide, x, y, 0.08, h, accent, radius=False)
    add_text(slide, title, x+0.22, y+0.18, w-0.35, 0.25, size=10.5, color='muted', bold=True)
    add_text(slide, value, x+0.22, y+0.47, w-0.35, 0.45, size=value_size, color=accent, bold=True)
    if note:
        add_text(slide, note, x+0.22, y+0.98, w-0.35, h-1.04, size=9.2, color='dark')

def add_pill(slide, text, x, y, w, h=0.32, fill='light_jade', color='deep_jade'):
    add_rect(slide, x, y, w, h, fill, radius=True)
    add_text(slide, text, x+0.08, y+0.065, w-0.16, h-0.08, size=9, color=color, bold=True, align='center')

def add_bullet(slide, text, x, y, w, h, color='dark', size=12.5):
    add_text(slide, '·', x, y, 0.18, h, size=size+2, color='jade', bold=True)
    return add_text(slide, text, x+0.22, y+0.01, w-0.22, h, size=size, color=color)

def add_bar(slide, x, y, w, h, label, pre, post, fmt='int', color='jade', maxv=None):
    if maxv is None:
        maxv = max(pre, post) * 1.1 if max(pre, post) else 1
    add_text(slide, label, x, y-0.02, 1.8, 0.25, size=9.3, color='dark', bold=True)
    # pre / post bars
    bw_pre = w * (pre / maxv)
    bw_post = w * (post / maxv)
    add_rect(slide, x+1.85, y, bw_pre, h, 'line', radius=True)
    add_rect(slide, x+1.85, y+h+0.09, bw_post, h, color, radius=True)
    vpre = fmt_money(pre) if fmt == 'money' else fmt_int(pre)
    vpost = fmt_money(post) if fmt == 'money' else fmt_int(post)
    add_text(slide, vpre, x+1.85+bw_pre+0.08, y-0.02, 1.3, 0.22, size=8.2, color='muted')
    add_text(slide, vpost, x+1.85+bw_post+0.08, y+h+0.07, 1.3, 0.22, size=8.2, color='dark', bold=True)
    add_text(slide, '前', x+1.58, y-0.02, 0.2, 0.2, size=7.5, color='muted')
    add_text(slide, '后', x+1.58, y+h+0.07, 0.2, 0.2, size=7.5, color='jade', bold=True)

# ---------- slides ----------
# 1 Cover
slide = prs.slides.add_slide(blank)
add_bg(slide, 'deep_jade')
add_rect(slide, 0, 0, 13.333, 7.5, 'deep_jade', radius=False)
# subtle motif blocks
add_rect(slide, 8.75, 0.0, 4.6, 7.5, 'jade', radius=False, transparency=25)
for i, (x,y,w,h,c,t) in enumerate([(8.95,0.5,1.6,1.0,'sand',20),(10.75,1.3,1.9,1.25,'light_jade',18),(9.55,3.05,2.5,1.55,'clay',10),(11.55,4.85,1.0,1.3,'sand',25)]):
    add_rect(slide, x,y,w,h,c,radius=True,transparency=t)
add_text(slide, '良渚文化三店', 0.80, 1.28, 5.4, 0.42, size=17, color='light_jade', bold=True)
add_text(slide, '接手满一个月\n运营变化复盘', 0.76, 1.78, 6.4, 1.55, size=42, color='white', bold=True, font=TITLE_FONT, line_spacing=0.92)
add_text(slide, '天猫店 / 淘宝店 / 京东店 · 2026.05.09—2026.06.07', 0.82, 3.62, 6.1, 0.35, size=13, color='sand')
add_text(slide, '重点看三件事：经营盘是否拉大、推广效率是否提升、后续产品是否有承接储备。', 0.84, 4.30, 6.5, 0.52, size=15, color='white')
add_footer(slide, dark=True, text='资料来源：店铺整体流量交易数据、5月月报基础数据、阿里妈妈/万相台推广报表')

# 2 Logic
slide = prs.slides.add_slide(blank)
add_bg(slide)
add_title(slide, '这份汇报的主线', '不把数据拆散看，而是看“接手后有没有形成可持续运营节奏”。')
steps = [
    ('01', '经营盘变化', '访客、成交、买家规模是否拉起来'),
    ('02', '成交质量变化', '实际成交、客单价与退款风险一起看'),
    ('03', '推广效率变化', '重点判断是不是靠加预算换增长'),
    ('04', '动作与储备', '把增长原因连接到运营动作和新品规划'),
]
for i, (num, title, desc) in enumerate(steps):
    x = 0.86 + i*3.05
    add_rect(slide, x, 1.75, 2.52, 3.1, 'white', line='line', radius=True)
    add_text(slide, num, x+0.22, 2.02, 0.7, 0.45, size=22, color='clay', bold=True)
    add_text(slide, title, x+0.22, 2.65, 2.08, 0.35, size=17, color='deep_jade', bold=True)
    add_text(slide, desc, x+0.22, 3.25, 2.08, 0.70, size=11.5, color='dark')
    if i < 3:
        add_text(slide, '→', x+2.62, 2.95, 0.35, 0.35, size=18, color='clay', bold=True, align='center')
add_rect(slide, 0.85, 5.35, 11.65, 0.72, 'light_jade', radius=True)
add_text(slide, '核心判断：接手后的变化不是单点波动，而是“流量放大 + 成交提升 + 推广效率改善 + 产品节奏补位”的组合效果。', 1.12, 5.56, 11.1, 0.28, size=14.5, color='deep_jade', bold=True, align='center')
add_footer(slide)

# 3 Overall before-after
slide = prs.slides.add_slide(blank)
add_bg(slide)
add_title(slide, '接手前后30天：整体经营盘明显放大', '三店合计看，接手后访客、成交、买家数和客单价同步改善。')
add_card(slide, 0.78, 1.48, 2.9, 1.38, '访客数 UV', f"{fmt_int(uv['pre'])} → {fmt_int(uv['post'])}", f"变化 {fmt_pct(uv['rate'])}，流量盘被拉大", 'jade')
add_card(slide, 3.95, 1.48, 2.9, 1.38, '支付/成交金额', f"{fmt_money(payment['pre'])} → {fmt_money(payment['post'])}", f"变化 {fmt_pct(payment['rate'])}，成交规模同步提升", 'deep_jade')
add_card(slide, 7.12, 1.48, 2.9, 1.38, '实际成交金额', f"{fmt_money(net['pre'])} → {fmt_money(net['post'])}", f"变化 {fmt_pct(net['rate'])}，扣除退款后仍增长", 'clay')
add_card(slide, 10.29, 1.48, 2.25, 1.38, '客单价', f"¥{unit['pre']:.0f} → ¥{unit['post']:.0f}", f"变化 {fmt_pct(unit['rate'])}", 'gold')
# bars
add_rect(slide, 0.78, 3.28, 6.0, 2.85, 'white', line='line', radius=True)
add_text(slide, '前后对比走势', 1.05, 3.52, 2.0, 0.30, size=14, color='deep_jade', bold=True)
maxv = max(uv['post'], payment['post'], net['post']) * 1.08
add_bar(slide, 1.05, 4.02, 3.65, 0.16, 'UV', uv['pre'], uv['post'], fmt='int', color='jade', maxv=maxv)
add_bar(slide, 1.05, 4.82, 3.65, 0.16, '支付成交', payment['pre'], payment['post'], fmt='money', color='deep_jade', maxv=maxv)
add_bar(slide, 1.05, 5.62, 3.65, 0.16, '实际成交', net['pre'], net['post'], fmt='money', color='clay', maxv=maxv)
# conclusion
add_rect(slide, 7.12, 3.28, 5.42, 2.85, 'sand', line='line', radius=True)
add_text(slide, '阶段判断', 7.42, 3.56, 3.5, 0.30, size=14, color='deep_jade', bold=True)
add_bullet(slide, '接手后不是只有流量上升，支付成交和实际成交也同步提升。', 7.45, 4.04, 4.65, 0.40, size=12)
add_bullet(slide, '客单价提升，说明成交质量不是单纯靠低价促销拉动。', 7.45, 4.68, 4.65, 0.40, size=12)
add_bullet(slide, '退款金额上升需要继续复盘，后续不能只看支付金额，要看净成交。', 7.45, 5.32, 4.75, 0.46, size=12)
add_footer(slide)

# 4 Store split
slide = prs.slides.add_slide(blank)
add_bg(slide)
add_title(slide, '三店角色：天猫做主盘，淘宝看效率，京东继续做承接优化', '每个渠道的任务不同，后续运营动作也要分层。')
store_notes = {
    '天猫店': ('主力成交盘', '成交规模最大，推广优化效果最明显；继续承担主力活动承接。', 'deep_jade'),
    '淘宝店': ('高效率小盘', '转化与ROI表现较好，适合围绕高效商品继续做预算和内容放大。', 'jade'),
    '京东店': ('待提升承接盘', '访客基础有拉动，后续重点是商品承接、页面转化和退款原因复盘。', 'clay'),
}
for i, s in enumerate(stores):
    x = 0.75 + i*4.12
    label, note, accent = store_notes[s]
    add_rect(slide, x, 1.55, 3.65, 4.72, 'white', line='line', radius=True)
    add_text(slide, s, x+0.28, 1.83, 1.45, 0.35, size=17, color=accent, bold=True)
    add_pill(slide, label, x+2.05, 1.82, 1.45, 0.30, fill='sand', color=accent)
    r = store_rows[s]
    add_text(slide, 'UV变化', x+0.32, 2.55, 0.9, 0.23, size=9.5, color='muted', bold=True)
    add_text(slide, f"{fmt_int(r['uv']['pre'])} → {fmt_int(r['uv']['post'])}", x+1.25, 2.49, 2.0, 0.28, size=13, color='dark', bold=True)
    add_text(slide, f"{fmt_pct(r['uv']['rate'])}", x+2.92, 2.49, 0.55, 0.28, size=10.5, color=accent, bold=True, align='right')
    add_text(slide, '支付成交', x+0.32, 3.10, 0.9, 0.23, size=9.5, color='muted', bold=True)
    add_text(slide, f"{fmt_money(r['pay']['pre'])} → {fmt_money(r['pay']['post'])}", x+1.25, 3.04, 2.0, 0.28, size=13, color='dark', bold=True)
    add_text(slide, f"{fmt_pct(r['pay']['rate'])}", x+2.92, 3.04, 0.55, 0.28, size=10.5, color=accent, bold=True, align='right')
    add_text(slide, '实际成交', x+0.32, 3.65, 0.9, 0.23, size=9.5, color='muted', bold=True)
    add_text(slide, f"{fmt_money(r['net']['pre'])} → {fmt_money(r['net']['post'])}", x+1.25, 3.59, 2.0, 0.28, size=13, color='dark', bold=True)
    add_text(slide, f"{fmt_pct(r['net']['rate'])}", x+2.92, 3.59, 0.55, 0.28, size=10.5, color=accent, bold=True, align='right')
    add_rect(slide, x+0.28, 4.36, 3.08, 1.22, 'sand', line='sand', radius=True)
    add_text(slide, note, x+0.48, 4.62, 2.68, 0.65, size=11.2, color='dark')
add_footer(slide)

# 5 Promotion efficiency
slide = prs.slides.add_slide(blank)
add_bg(slide)
add_title(slide, '推广效率：最有利的结论是“不是靠加预算换增长”', '天猫/淘宝推广接手后30天：花费基本持平略降，成交、ROI、点击质量同时改善。')
metrics = [
    ('推广花费', '花费', 'money', 'deep_jade'),
    ('推广成交金额', '总成交金额', 'money', 'jade'),
    ('ROI', 'ROI', 'ratio', 'clay'),
    ('点击量', '点击量', 'int', 'gold'),
    ('CPC', 'CPC', 'money', 'warning'),
]
for i, (label, key, kind, color) in enumerate(metrics):
    m = promo_metrics[('天猫+淘宝合计', key)]
    x = 0.78 + i*2.52
    if kind == 'money':
        v = f"{fmt_money(m['pre'])}\n→ {fmt_money(m['post'])}"
    elif kind == 'ratio':
        v = f"{m['pre']:.2f}\n→ {m['post']:.2f}"
    else:
        v = f"{fmt_int(m['pre'])}\n→ {fmt_int(m['post'])}"
    add_card(slide, x, 1.55, 2.22, 1.78, label, v, f"变化 {fmt_pct(m['rate'])}", color, value_size=20)
add_rect(slide, 0.78, 3.78, 5.95, 2.25, 'deep_jade', radius=True)
add_text(slide, '推广端阶段判断', 1.10, 4.12, 4.9, 0.32, size=15, color='sand', bold=True)
add_text(slide, '接手后推广端不是单纯增加预算，而是在整体花费基本持平略降的情况下，推广成交金额提升26.2%，ROI从1.93提升到2.48，点击量提升且CPC下降。', 1.10, 4.70, 5.25, 0.98, size=16, color='white')
add_rect(slide, 7.10, 3.78, 5.42, 2.25, 'sand', line='line', radius=True)
add_text(slide, '这说明什么', 7.42, 4.12, 2.2, 0.32, size=15, color='deep_jade', bold=True)
add_bullet(slide, '推广结构、关键词、人群/素材匹配在改善。', 7.45, 4.62, 4.4, 0.32, size=12)
add_bullet(slide, '货品承接效率提升，投入没有明显放大但产出更好。', 7.45, 5.10, 4.55, 0.36, size=12)
add_footer(slide)

# 6 Promotion scene
slide = prs.slides.add_slide(blank)
add_bg(slide)
add_title(slide, '推广场景：货品全站是基础盘，关键词继续做精细化', '从场景拆分看，能放大的方向和需要控制的点已经比较清楚。')
for i, r in enumerate(scene_rows):
    x = 0.78 + i*4.12
    accent = 'deep_jade' if i == 0 else ('jade' if i == 1 else 'clay')
    add_rect(slide, x, 1.48, 3.65, 4.78, 'white', line='line', radius=True)
    add_text(slide, r['store'], x+0.28, 1.76, 0.9, 0.28, size=11, color='muted', bold=True)
    add_text(slide, r['scene'], x+0.28, 2.10, 2.4, 0.35, size=16, color=accent, bold=True)
    add_text(slide, '成交变化', x+0.30, 2.86, 0.85, 0.24, size=9.5, color='muted', bold=True)
    add_text(slide, f"{fmt_money(r['pre_gmv'])} → {fmt_money(r['post_gmv'])}", x+1.20, 2.80, 1.7, 0.30, size=12.5, color='dark', bold=True)
    add_text(slide, fmt_pct(r['gmv_rate']), x+2.88, 2.80, 0.55, 0.30, size=10.5, color=accent, bold=True, align='right')
    add_text(slide, 'ROI变化', x+0.30, 3.39, 0.85, 0.24, size=9.5, color='muted', bold=True)
    add_text(slide, f"{r['pre_roi']:.2f} → {r['post_roi']:.2f}", x+1.20, 3.33, 1.7, 0.30, size=12.5, color='dark', bold=True)
    add_text(slide, fmt_pct(r['roi_rate']), x+2.88, 3.33, 0.55, 0.30, size=10.5, color=accent, bold=True, align='right')
    add_text(slide, 'CPC变化', x+0.30, 3.92, 0.85, 0.24, size=9.5, color='muted', bold=True)
    add_text(slide, f"¥{r['pre_cpc']:.2f} → ¥{r['post_cpc']:.2f}", x+1.20, 3.86, 1.7, 0.30, size=12.5, color='dark', bold=True)
    add_text(slide, fmt_pct(r['cpc_rate']), x+2.88, 3.86, 0.55, 0.30, size=10.5, color=accent, bold=True, align='right')
    add_rect(slide, x+0.28, 4.55, 3.08, 1.22, 'sand', line='sand', radius=True)
    add_text(slide, r['conclusion'], x+0.48, 4.80, 2.70, 0.62, size=10.7, color='dark')
add_footer(slide)

# 7 Actions behind changes
slide = prs.slides.add_slide(blank)
add_bg(slide)
add_title(slide, '这些变化背后的运营动作', '本月不是单点动作，而是围绕“商品—活动—页面—推广”连续推进。')
actions = [
    ('商品上新', '完成三款产品上新，补充活动和推广商品池。'),
    ('618节奏', '完成活动上线跟进，并按两波节奏做活动优化。'),
    ('店铺视觉', '推进店铺视觉优化，提升进入店铺后的品牌化感受。'),
    ('分类页优化', '优化分类页结构，帮助用户更快找到合适商品。'),
    ('推广方案', '调整推广计划与预算结构，强化重点商品承接。'),
    ('关键词优化', '梳理关键词方向，减少低效消耗，提高精准流量。'),
]
for i, (title, desc) in enumerate(actions):
    row, col = divmod(i, 3)
    x = 0.82 + col*4.08
    y = 1.65 + row*2.08
    add_rect(slide, x, y, 3.55, 1.52, 'white', line='line', radius=True)
    add_rect(slide, x+0.22, y+0.26, 0.55, 0.55, 'light_jade', radius=True)
    add_text(slide, f"{i+1}", x+0.34, y+0.37, 0.30, 0.18, size=12, color='deep_jade', bold=True, align='center')
    add_text(slide, title, x+0.95, y+0.25, 1.85, 0.28, size=14.5, color='deep_jade', bold=True)
    add_text(slide, desc, x+0.95, y+0.72, 2.25, 0.48, size=10.8, color='dark')
add_rect(slide, 0.82, 6.10, 11.8, 0.52, 'light_jade', radius=True)
add_text(slide, '阶段判断：数据上的改善，能对应到已经推进的运营动作；后续要把这些动作变成固定节奏，而不是一次性调整。', 1.08, 6.25, 11.25, 0.20, size=12.5, color='deep_jade', bold=True, align='center')
add_footer(slide)

# 8 Next steps + product planning placeholders
slide = prs.slides.add_slide(blank)
add_bg(slide)
add_title(slide, '下一步：把已经出现的变化，沉淀成稳定增长节奏', '保留高效场景、继续优化承接，同时把新品节奏提前接入运营规划。')
nexts = [
    ('保留高效推广场景', '货品全站推广继续作为基础盘，并围绕高ROI商品做预算倾斜。'),
    ('控制关键词投放效率', '天猫关键词推广继续做词包分层、低效词清理和承接商品优化。'),
    ('强化高效商品承接', '茶具、晴雨伞、冰箱贴、玉鸟咕咕毛绒挂件等商品继续结合活动与内容放大。'),
    ('复盘退款与售后预期', '退款影响已经显现，需要持续看商品、页面说明和售后原因。'),
]
for i, (title, desc) in enumerate(nexts):
    x = 0.78 + (i%2)*6.08
    y = 1.52 + (i//2)*1.03
    add_rect(slide, x, y, 5.58, 0.78, 'white', line='line', radius=True)
    add_text(slide, title, x+0.25, y+0.17, 1.9, 0.22, size=12.5, color='deep_jade', bold=True)
    add_text(slide, desc, x+2.25, y+0.14, 2.95, 0.36, size=10.0, color='dark')
# Product planning zone bottom
add_rect(slide, 0.78, 3.85, 11.84, 2.65, 'sand', line='line', radius=True)
add_text(slide, '近期规划产品（图片预留区）', 1.05, 4.12, 2.6, 0.30, size=15, color='deep_jade', bold=True)
add_text(slide, '可把设计图/效果图直接填入下方框内，用于展示后续产品储备。', 3.55, 4.17, 4.5, 0.22, size=9.5, color='muted')
placeholders = [('毛绒产品 01','3款毛绒'),('毛绒产品 02','3款毛绒'),('毛绒产品 03','3款毛绒'),('冰箱贴 01','2款冰箱贴'),('冰箱贴 02','2款冰箱贴'),('夜灯产品','1款夜灯')]
for i, (name, tag) in enumerate(placeholders):
    x = 1.06 + i*1.88
    y = 4.62
    ph = add_rect(slide, x, y, 1.55, 1.18, 'cream', line='line', radius=True)
    ph.line.dash_style = MSO_LINE_DASH_STYLE.DASH
    add_text(slide, '图片', x+0.48, y+0.38, 0.58, 0.22, size=12, color='line', bold=True, align='center')
    add_text(slide, name, x+0.08, y+1.28, 1.39, 0.22, size=8.8, color='deep_jade', bold=True, align='center')
    add_text(slide, tag, x+0.08, y+1.52, 1.39, 0.20, size=7.5, color='muted', align='center')
add_footer(slide)

# Final shape/text hygiene
for slide in prs.slides:
    for shape in slide.shapes:
        if hasattr(shape, 'text_frame'):
            for p in shape.text_frame.paragraphs:
                for run in p.runs:
                    run.font.name = FONT

prs.save(OUT)
print(OUT)
print(OUT.stat().st_size)
