"""§3 场景 B · 机制兜底 · slide 43-54 (12 内容页) · v0.2 真实内容版

嵌入 4 张图（B6/A4/A13/C4）+ 8 张自画版面（4 hook 样例 / skill 列表 / multi-llm-sdk / progress hook / 互动 / Bypass）
"""

from pathlib import Path
from pptx.util import Inches
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR

from build_main import (
    _fill_ph, _add_textbox, _add_card,
    NAVY, CORAL, CORAL_LIGHT, BLUE_ACCENT, GREEN, GOLD,
    GRAY_300, GRAY_500, GRAY_700, WHITE,
)
from build_sec_M1 import _add_centered_image, _title


def build_slide_43_newbie(prs):
    """slide 43: AI 同事 = 新员工 · 嵌入 B6"""
    s = prs.slides.add_slide(prs.slide_layouts[3])
    _title(s, "§4 · 类比：AI 同事 = 新员工 · 三道防线")
    _add_centered_image(s, "B6-ai-newbie-defense.png")
    return s


def build_slide_44_4layer_hooks(prs):
    """slide 44: 4 层 hook 全景 · v1.3 改 PPT 自画 4 层金字塔（借 template t-09）
    顶 (Layer 1) → 底 (Layer 4) · 按触发时序由先到后
    """
    from pptx.enum.shapes import MSO_SHAPE as _MS
    from pptx.util import Pt as _Pt
    s = prs.slides.add_slide(prs.slide_layouts[3])
    _title(s, "§4 · 4 层 Hook 防御 · 触发时序金字塔（从早到晚）")

    # 左半 (0.55 ~ 6.8 in)：4 层金字塔（中心线 x=3.6）
    cx = 3.6
    layers = [
        # (width, height, fill, layer_label)
        (1.6, 0.9, CORAL,       "Layer 1"),  # 顶
        (2.6, 0.9, GOLD,        "Layer 2"),
        (3.6, 0.9, NAVY,        "Layer 3"),
        (4.6, 0.9, GREEN,       "Layer 4"),  # 底
    ]
    y_top = 1.6  # 顶层 y 起点
    for i, (w, h, fc, lbl) in enumerate(layers):
        y = y_top + i * h
        x = cx - w / 2
        rect = s.shapes.add_shape(_MS.RECTANGLE,
                                   Inches(x), Inches(y),
                                   Inches(w), Inches(h))
        rect.fill.solid(); rect.fill.fore_color.rgb = fc
        rect.line.color.rgb = NAVY; rect.line.width = _Pt(1.5)
        _add_textbox(s, Inches(x), Inches(y), Inches(w), Inches(h),
                     lbl, size_pt=15, bold=True, color=WHITE,
                     align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE)
    # 金字塔下方"触发时序"标注
    _add_textbox(s, Inches(0.55), Inches(5.45), Inches(6.4), Inches(0.4),
                 "↑ 早 (用户提 prompt 时) · 晚 (git commit 时) ↓",
                 size_pt=11, color=GRAY_500, align=PP_ALIGN.CENTER)

    # 右半 (7.0 ~ 13.0 in)：4 行说明卡（顺序与金字塔顶→底对应）
    items = [
        ("Layer 1 · UserPromptSubmit", "scan-secrets.sh · 硬阻断密钥泄漏（8 类 key 模式）", CORAL),
        ("Layer 2 · PreToolUse (Bash)", "block-dangerous-bash.sh · 硬阻断危险命令（rm -rf / DROP TABLE / push --force）", GOLD),
        ("Layer 3 · PostToolUse",       "sanitize-tool-output.sh · 软告警 prompt-injection（'ignore previous' 类）", NAVY),
        ("Layer 4 · git pre-commit",    "scan-commit.sh · commit 前再扫 staged diff（防 Edit/Write 绕过 prompt scan）", GREEN),
    ]
    for i, (head, body, c) in enumerate(items):
        y = Inches(1.4 + i * 1.0)
        _add_card(s, Inches(7.0), y, Inches(6.0), Inches(0.85),
                  fill=WHITE, border=c, accent=c)
        _add_textbox(s, Inches(7.25), y + Inches(0.08), Inches(5.6), Inches(0.4),
                     head, size_pt=14, bold=True, color=c)
        _add_textbox(s, Inches(7.25), y + Inches(0.45), Inches(5.6), Inches(0.4),
                     body, size_pt=10, color=GRAY_700)

    # 底部一行注解
    _add_textbox(s, Inches(0.55), Inches(6.5), Inches(12.2), Inches(0.4),
                 "设计理念：任一层失效不影响其他层 · 撞硬阻断 → 找 PL 授权 · No Equivalent Bypass",
                 size_pt=13, color=CORAL, align=PP_ALIGN.CENTER, bold=True)
    _add_textbox(s, Inches(0.55), Inches(6.95), Inches(12.2), Inches(0.4),
                 "配置位置：.claude/settings.json + .git/hooks/pre-commit · 详见 .claude/standards/security.md",
                 size_pt=11, color=GRAY_500, align=PP_ALIGN.CENTER)
    return s


def build_slide_45_hook1_dangerous(prs):
    """slide 45: Hook 1 危险命令拦截 · 自画"""
    s = prs.slides.add_slide(prs.slide_layouts[3])
    _title(s, "§4 · Hook 1 · 危险命令硬阻断（block-dangerous-bash.sh）")
    cases = [
        ("rm -rf /",            "硬删整盘",          CORAL),
        ("rm -rf <path>",       "递归强删（再 trusted 也拦）", CORAL),
        ("DROP TABLE users",    "SQL 删表",          CORAL),
        ("git push --force",    "强推覆盖历史",      CORAL),
        ("git reset --hard",    "硬重置丢未提交改动", CORAL),
        ("curl ... | sh",       "远程脚本执行链路",  CORAL),
    ]
    for i, (cmd, desc, color) in enumerate(cases):
        y = Inches(1.7 + i * 0.85)
        _add_card(s, Inches(0.55), y, Inches(12.2), Inches(0.75),
                  fill=WHITE, border=color, accent=color)
        _add_textbox(s, Inches(0.85), y + Inches(0.15), Inches(5.5), Inches(0.5),
                     cmd, size_pt=16, bold=True, color=NAVY)
        _add_textbox(s, Inches(6.5), y + Inches(0.22), Inches(6.0), Inches(0.4),
                     desc, size_pt=13, color=GRAY_700)
    _add_textbox(s, Inches(0.55), Inches(6.9), Inches(12.2), Inches(0.4),
                 "撞硬阻断 → 找 PL 授权或换方案 · No Equivalent Bypass",
                 size_pt=14, color=CORAL, bold=True, align=PP_ALIGN.CENTER)
    return s


def build_slide_46_hook2_secrets(prs):
    """slide 46: Hook 2 密钥泄漏拦截 · 自画"""
    s = prs.slides.add_slide(prs.slide_layouts[3])
    _title(s, "§4 · Hook 2 · 密钥泄漏拦截（scan-secrets.sh）")
    _add_textbox(s, Inches(0.55), Inches(1.5), Inches(12.2), Inches(0.5),
                 "8 类密钥模式 · UserPromptSubmit + git pre-commit 双扫",
                 size_pt=16, bold=True, color=NAVY)
    items = [
        ("国际 LLM",  "sk-ant-... (Anthropic) · sk-... (OpenAI) · ya29... (Google)"),
        ("国产 LLM",  "DeepSeek · Doubao · Qwen · MiniMax key"),
        ("云厂商",     "AWS Access Key (AKIA...) · Azure · GCP service account"),
        ("仓库",       "GitHub PAT (ghp_...) · GitLab token · Slack bot"),
        ("PEM",        "-----BEGIN RSA PRIVATE KEY-----（任意 PEM）"),
        ("SSH",        "id_rsa / id_ed25519 私钥头部"),
        ("PuTTY",      "PPK 私钥 (BEGIN OPENSSH PRIVATE KEY)"),
        ("BIP39",      "12/24 助记词（钱包恢复语）"),
    ]
    # 每行加 chip 容器（左侧 4px 章节竖线 + 白底卡）
    from build_main import rotate4
    for i, (cat, samples) in enumerate(items):
        y = Inches(2.2 + i * 0.55)
        c = rotate4(i)
        _add_card(s, Inches(0.55), y, Inches(12.2), Inches(0.48),
                  fill=WHITE, border=c, accent=c)
        _add_textbox(s, Inches(0.85), y + Inches(0.04), Inches(2.2), Inches(0.4),
                     cat, size_pt=14, bold=True, color=c)
        _add_textbox(s, Inches(3.2), y + Inches(0.04), Inches(9.4), Inches(0.4),
                     samples, size_pt=13, color=GRAY_700)
    _add_textbox(s, Inches(0.55), Inches(6.85), Inches(12.2), Inches(0.4),
                 "安装 git pre-commit：ln -sf ../../.claude/hooks/scan-commit.sh .git/hooks/pre-commit",
                 size_pt=12, color=GRAY_500, align=PP_ALIGN.CENTER)
    return s


def build_slide_47_hook3_injection(prs):
    """slide 47: Hook 3 prompt-injection 软告警 · 嵌入 A13"""
    s = prs.slides.add_slide(prs.slide_layouts[3])
    _title(s, "§4 · Hook 3 · Prompt Injection 软告警（sanitize-tool-output.sh）")
    _add_centered_image(s, "A13-prompt-injection.png")
    return s


def build_slide_48_hook4_pre_commit(prs):
    """slide 48: Hook 4 commit 前再扫 · 自画"""
    s = prs.slides.add_slide(prs.slide_layouts[3])
    _title(s, "§4 · Hook 4 · pre-commit 兜底 · 防 Edit/Write 绕过", size_pt=24)
    _add_textbox(s, Inches(0.55), Inches(1.6), Inches(12.2), Inches(0.6),
                 "设计意图：Edit/Write 工具不走 UserPromptSubmit hook，单靠 Layer 1 会漏",
                 size_pt=16, color=CORAL, align=PP_ALIGN.CENTER, bold=True)
    # 流程
    items = [
        ("①  git commit",                          "用户在终端执行 commit"),
        ("②  .git/hooks/pre-commit 触发",         "调 .claude/hooks/scan-commit.sh"),
        ("③  对 staged diff 跑同套 secret 正则", "把要 commit 的内容再扫一遍"),
        ("④a 发现密钥 → 阻断",                    "exit 1 + 报告所在文件 + 行号"),
        ("④b 通过 → commit 成功",                 "exit 0"),
    ]
    for i, (head, body) in enumerate(items):
        y = Inches(2.6 + i * 0.85)
        color = CORAL if "阻断" in head else NAVY if "③" in head or "①" in head else GREEN if "成功" in head else GOLD
        _add_card(s, Inches(0.55), y, Inches(12.2), Inches(0.75),
                  fill=WHITE, border=color, accent=color)
        _add_textbox(s, Inches(0.85), y + Inches(0.15), Inches(5.0), Inches(0.5),
                     head, size_pt=16, bold=True, color=color)
        _add_textbox(s, Inches(6.0), y + Inches(0.2), Inches(6.5), Inches(0.4),
                     body, size_pt=13, color=GRAY_700)
    return s


def build_slide_49_skill_enforce(prs):
    """slide 49: Skill 强制 vs 推荐 · 自画双列"""
    s = prs.slides.add_slide(prs.slide_layouts[3])
    _title(s, "§4 · Skill 强制 vs 推荐")
    # 左列强制
    _add_textbox(s, Inches(0.55), Inches(1.5), Inches(6.0), Inches(0.5),
                 "强制（必走）", size_pt=22, bold=True, color=CORAL)
    forced = [
        ("agf-running-sit-tests",            "dev 自跑 SIT + 证据落 progress"),
        ("superpowers:test-driven-development", "TDD red-green-refactor"),
        ("agf-wiring-multi-llm-sdk",         "多 LLM SDK 接入"),
        ("agf-writing-qa-report",            "qa 写 E2E / UAT 报告"),
        ("agf-writing-prd",                  "PL 写 PRD"),
        ("agf-writing-adr",                  "TL 写 ADR"),
    ]
    # 左列 6 行 chip（CORAL 左竖线）
    for i, (name, desc) in enumerate(forced):
        y = Inches(2.2 + i * 0.7)
        _add_card(s, Inches(0.55), y, Inches(6.0), Inches(0.6),
                  fill=WHITE, border=CORAL, accent=CORAL)
        _add_textbox(s, Inches(0.85), y + Inches(0.05), Inches(5.7), Inches(0.3),
                     name, size_pt=13, bold=True, color=NAVY)
        _add_textbox(s, Inches(0.85), y + Inches(0.32), Inches(5.7), Inches(0.3),
                     desc, size_pt=11, color=GRAY_700)
    # 右列推荐
    _add_textbox(s, Inches(7.0), Inches(1.5), Inches(6.0), Inches(0.5),
                 "推荐（视情况）", size_pt=22, bold=True, color=GOLD)
    recommended = [
        ("superpowers:systematic-debugging", "bug 出现时用"),
        ("superpowers:verification-before-completion", "claim done 前用"),
        ("superpowers:brainstorming",        "创意工作前用"),
        ("superpowers:writing-plans",        "多步任务前用"),
        ("superpowers:receiving-code-review", "收 review 时用"),
        ("agf-running-release-retro",        "MAJOR/MINOR release 后用"),
    ]
    # 右列 6 行 chip（GOLD 左竖线）
    for i, (name, desc) in enumerate(recommended):
        y = Inches(2.2 + i * 0.7)
        _add_card(s, Inches(7.0), y, Inches(6.0), Inches(0.6),
                  fill=WHITE, border=GOLD, accent=GOLD)
        _add_textbox(s, Inches(7.3), y + Inches(0.05), Inches(5.7), Inches(0.3),
                     name, size_pt=13, bold=True, color=NAVY)
        _add_textbox(s, Inches(7.3), y + Inches(0.32), Inches(5.7), Inches(0.3),
                     desc, size_pt=11, color=GRAY_700)
    return s


def build_slide_50_multi_llm(prs):
    """slide 50: agf-wiring-multi-llm-sdk 强制点 · 自画"""
    s = prs.slides.add_slide(prs.slide_layouts[3])
    _title(s, "§4 · agf-wiring-multi-llm-sdk · 4 个强制点")
    items = [
        ("①  OpenAI 兼容 adapter",  "所有 LLM 调用走统一接口（不裸调每家 SDK）· 切厂商只改 1 处"),
        ("②  env-var 契约",          "DEEPSEEK_API_KEY / DOUBAO_API_KEY / ... 命名固定，不混 hardcoded"),
        ("③  fallback 链",            "Pro → Lite → Mini 同厂商 · 跨厂商演练单独写故障注入"),
        ("④  cost guardrail",         "每次调用前估算 cost · 超 AC 预算自动 fail + 通知 PL"),
    ]
    for i, (head, body) in enumerate(items):
        y = Inches(1.7 + i * 1.15)
        _add_card(s, Inches(0.55), y, Inches(12.2), Inches(1.0),
                  fill=WHITE, border=BLUE_ACCENT, accent=BLUE_ACCENT)
        _add_textbox(s, Inches(0.85), y + Inches(0.15), Inches(5.0), Inches(0.55),
                     head, size_pt=20, bold=True, color=NAVY)
        _add_textbox(s, Inches(0.85), y + Inches(0.6), Inches(11.5), Inches(0.4),
                     body, size_pt=14, color=GRAY_700)
    _add_textbox(s, Inches(0.55), Inches(6.6), Inches(12.2), Inches(0.4),
                 "实现：backend/app/config/llm_routing.py · 见 02-dispatch.md ⑥ Skills 必走",
                 size_pt=12, color=GRAY_500, align=PP_ALIGN.CENTER)
    return s


def build_slide_51_dor_dod(prs):
    """slide 51: DoR vs DoD · v1.3 改 PPT 自画 3 层金字塔（借 template t-09）
    左半保留 DoR 起跑线 checklist；右半改三层金字塔（PBI / Sprint / Release）
    """
    from pptx.enum.shapes import MSO_SHAPE as _MS
    from pptx.util import Pt as _Pt
    s = prs.slides.add_slide(prs.slide_layouts[3])
    _title(s, "§4 · DoR vs DoD · 起跑线 vs 终点线 · DoD 3 层（Scrum 2025）")

    # === 左半 (0.55 ~ 6.2 in)：DoR 起跑线 ===
    _add_textbox(s, Inches(0.55), Inches(1.45), Inches(5.7), Inches(0.5),
                 "DoR · Definition of Ready · 起跑线", size_pt=18, bold=True, color=NAVY)
    _add_textbox(s, Inches(0.55), Inches(1.9), Inches(5.7), Inches(0.4),
                 "（不达 DoR → 不开 dev）", size_pt=11, color=GRAY_500)
    dor_items = [
        "PRD 含 ≥ 5 条 AC（可量化）",
        "派单消息 6 段全（含 AC + 依赖）",
        "模型选型有 ADR（高风险 TL 介入）",
        "Token / cost 预算上限明确",
        "上下游 worktree 隔离已就位",
    ]
    for i, item in enumerate(dor_items):
        _add_textbox(s, Inches(0.7), Inches(2.4 + i * 0.5), Inches(5.5), Inches(0.42),
                     f"✓ {item}", size_pt=13, color=GRAY_700)

    # 中间竖向分隔线
    sep = s.shapes.add_shape(_MS.RECTANGLE, Inches(6.4), Inches(1.5),
                              Inches(0.02), Inches(4.6))
    sep.fill.solid(); sep.fill.fore_color.rgb = GRAY_300
    sep.line.fill.background()

    # === 右半 (6.7 ~ 13.0 in)：DoD 3 层金字塔 ===
    _add_textbox(s, Inches(6.7), Inches(1.45), Inches(6.3), Inches(0.5),
                 "✓ DoD · Definition of Done · 终点线 · 3 层", size_pt=18, bold=True, color=NAVY)

    # 金字塔 3 层 · 顶 Release / 中 Sprint / 底 PBI（中心线 x=9.85）
    cx = 9.85
    pyramid = [
        # (width_in, height_in, fill, layer, num_str, sub)
        (1.6, 0.85, CORAL, "Release",  "0 项",       "AGF 不做 → 业务方 UAT 签字"),
        (2.6, 0.85, GOLD,  "Sprint",   "4 项",       "每 sprint 通过：integration + regression"),
        (3.6, 0.85, GREEN, "PBI",      "5 项",       "每 task / PBI 通过：unit + SIT + review + AC + progress"),
    ]
    y_top = 2.05
    for i, (w, h, fc, layer, num, sub) in enumerate(pyramid):
        y = y_top + i * h
        x = cx - w / 2
        rect = s.shapes.add_shape(_MS.RECTANGLE,
                                   Inches(x), Inches(y),
                                   Inches(w), Inches(h))
        rect.fill.solid(); rect.fill.fore_color.rgb = fc
        rect.line.color.rgb = NAVY; rect.line.width = _Pt(1.5)
        # 层内字 "Release · 0 项"
        _add_textbox(s, Inches(x), Inches(y), Inches(w), Inches(h),
                     f"{layer}  ·  {num}", size_pt=14, bold=True,
                     color=WHITE if fc != GOLD else NAVY,
                     align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE)

    # 金字塔右侧 3 行说明（顶/中/底对应）
    legend = [
        (2.05, "顶 · Release · AGF 借 UAT", "业务方签字 = release pass"),
        (2.90, "中 · Sprint · 4 项",         "integration + regression + perf + security"),
        (3.75, "底 · PBI · 5 项",            "unit + SIT + review + AC 自验 + progress 自报"),
    ]
    # 由于金字塔右边到 slide 右 (13.0 in) 较窄，legend 写到金字塔底下
    _add_textbox(s, Inches(6.7), Inches(4.9), Inches(6.3), Inches(0.4),
                 "金字塔自底向上：覆盖范围递增（PBI → Sprint → Release）",
                 size_pt=11, color=GRAY_500, align=PP_ALIGN.CENTER)

    # === 底部跨栏：AGF 9 项 DoD checklist 实际算法 + 注解 ===
    _add_textbox(s, Inches(0.55), Inches(6.0), Inches(12.2), Inches(0.4),
                 "AGF 9 项 DoD checklist 实际算法（v3.0）：PBI 层 5 项 + Sprint 层 4 项 + Release 层 0 项",
                 size_pt=14, bold=True, color=CORAL, align=PP_ALIGN.CENTER)
    _add_textbox(s, Inches(0.55), Inches(6.5), Inches(12.2), Inches(0.4),
                 "AGF 取舍：9 = PBI 5 + Sprint 4 + Release 0 · Release 层不做 DoD（业务签字才算 done · 不在团队责任边界）",
                 size_pt=11, color=GRAY_700, align=PP_ALIGN.CENTER)
    _add_textbox(s, Inches(0.55), Inches(6.95), Inches(12.2), Inches(0.4),
                 "Scrum Guide 2020-11 · scrumguides.org · 中文版免费 PDF",
                 size_pt=11, color=NAVY, align=PP_ALIGN.CENTER, bold=True)
    return s


def build_slide_52_progress_hook(prs):
    """slide 52: progress hook 兜底 · 自画"""
    s = prs.slides.add_slide(prs.slide_layouts[3])
    _title(s, "§4 · progress hook 兜底 · check-progress-file.sh")
    _add_textbox(s, Inches(0.55), Inches(1.6), Inches(12.2), Inches(0.5),
                 "设计意图：dev 跑完不写 progress → 下游 reviewer 无证据可 audit",
                 size_pt=18, bold=True, color=NAVY, align=PP_ALIGN.CENTER)
    # 触发 + 行为
    _add_card(s, Inches(0.55), Inches(2.5), Inches(12.2), Inches(2.0),
              fill=CORAL_LIGHT, border=CORAL)
    _add_textbox(s, Inches(0.85), Inches(2.65), Inches(11.6), Inches(0.4),
                 "🪝 触发时机：SubagentStop / TeammateIdle",
                 size_pt=16, bold=True, color=CORAL)
    _add_textbox(s, Inches(0.85), Inches(3.15), Inches(11.6), Inches(0.4),
                 "🔍 检查：progress/<role>.md 是否存在 + 是否含 **SIT 证据** 段",
                 size_pt=14, color=GRAY_700)
    _add_textbox(s, Inches(0.85), Inches(3.55), Inches(11.6), Inches(0.9),
                 "缺则阻断 dev idle · 提示 dev 补完再退出",
                 size_pt=14, color=GRAY_700)
    # 反例
    _add_textbox(s, Inches(0.55), Inches(4.8), Inches(12.2), Inches(0.5),
                 "真实踩坑案例（hook 拦下的）：",
                 size_pt=16, bold=True, color=NAVY)
    cases = [
        "① dev 跑完直接 'task done' → hook 阻：你的 progress/ai-agent-dev.md 不存在",
        "② dev 写了 progress 但没 SIT 证据段 → hook 阻：缺 **SIT 证据** 子标题",
        "③ dev 写 SIT 但缺真实输出（口述）→ hook 不能查内容真伪，靠 reviewer audit",
    ]
    for i, case in enumerate(cases):
        _add_textbox(s, Inches(0.85), Inches(5.4 + i * 0.5), Inches(12.0), Inches(0.4),
                     case, size_pt=13, color=GRAY_700)
    return s


def build_slide_53_interactive(prs):
    """slide 53: 实战互动 · 找出违规命令 · 自画 5 case"""
    s = prs.slides.add_slide(prs.slide_layouts[3])
    _title(s, "§4 · 实战互动 · 找出哪一层 hook 会拦")
    cases = [
        ("rm -rf /tmp/test",                       "Layer 2 · block-dangerous-bash"),
        ("用户 prompt 含 ANTHROPIC_API_KEY=sk-ant-xxx", "Layer 1 · scan-secrets"),
        ("WebFetch 返回内容含 'ignore previous'",   "Layer 3 · sanitize-tool-output"),
        ("Edit 文件含 -----BEGIN RSA-----  + git commit", "Layer 4 · scan-commit"),
        ("dev 退出但 progress 缺 SIT 证据段",        "workflow hook · check-progress-file"),
    ]
    from build_main import rotate5
    for i, (case, answer) in enumerate(cases):
        y = Inches(1.7 + i * 1.0)
        c = rotate5(i)
        _add_card(s, Inches(0.55), y, Inches(12.2), Inches(0.9),
                  fill=WHITE, border=c, accent=c)
        _add_textbox(s, Inches(0.85), y + Inches(0.1), Inches(0.6), Inches(0.6),
                     f"Q{i+1}", size_pt=20, bold=True, color=c)
        _add_textbox(s, Inches(1.6), y + Inches(0.1), Inches(11.0), Inches(0.45),
                     case, size_pt=15, bold=True, color=NAVY)
        _add_textbox(s, Inches(1.6), y + Inches(0.5), Inches(11.0), Inches(0.4),
                     f"→ {answer}", size_pt=13, color=GREEN)
    return s


def build_slide_54_no_bypass(prs):
    """slide 54: No Equivalent Bypass · 自画"""
    s = prs.slides.add_slide(prs.slide_layouts[3])
    _title(s, "§4 · No Equivalent Bypass · 撞硬阻断不绕路")
    _add_textbox(s, Inches(0.55), Inches(1.6), Inches(12.2), Inches(0.7),
                 "🛑 当 hook 硬阻断你时，下面这些都是错的——",
                 size_pt=20, bold=True, color=CORAL, align=PP_ALIGN.CENTER)
    wrong = [
        ("rm -rf 被阻 → 用 find . -delete",   "找等价命令绕路"),
        ("脚本被 hook 拒 → 用 python -c \"...\"", "切换语言绕路"),
        ("commit 含 key 被拦 → 改 .git/hooks/pre-commit", "改 hook 配置本身"),
        ("WebFetch 返回告警 → 把内容存盘后再 Read", "分步绕过扫描"),
    ]
    # 4 反例加 chip 容器（CORAL 左竖线 + 白底）
    for i, (sample, kind) in enumerate(wrong):
        y = Inches(2.5 + i * 0.7)
        _add_card(s, Inches(0.55), y, Inches(12.2), Inches(0.6),
                  fill=WHITE, border=CORAL, accent=CORAL)
        _add_textbox(s, Inches(0.85), y + Inches(0.1), Inches(0.5), Inches(0.4),
                     "✗", size_pt=18, color=CORAL, bold=True)
        _add_textbox(s, Inches(1.5), y + Inches(0.1), Inches(8.0), Inches(0.4),
                     sample, size_pt=14, color=NAVY, bold=True)
        _add_textbox(s, Inches(9.5), y + Inches(0.1), Inches(3.2), Inches(0.4),
                     kind, size_pt=12, color=GRAY_500)
    # 正确做法
    _add_card(s, Inches(0.55), Inches(5.6), Inches(12.2), Inches(1.5),
              fill=CORAL_LIGHT, border=CORAL, accent=CORAL)
    _add_textbox(s, Inches(0.85), Inches(5.75), Inches(11.6), Inches(0.5),
                 "✓ 正确做法：找 PL 授权 / 换方案 / 接受暂时做不到",
                 size_pt=18, bold=True, color=CORAL)
    _add_textbox(s, Inches(0.85), Inches(6.25), Inches(11.6), Inches(0.8),
                 "撞阻断 = 系统设计想保护你 · 绕路 = 把红线踩出洞 · 一次绕路 = 全队的成本",
                 size_pt=14, color=GRAY_700)
    return s


def build(prs):
    """构建 §3 12 张内容 slide (slide 43-54)"""
    build_slide_43_newbie(prs)
    build_slide_44_4layer_hooks(prs)
    build_slide_45_hook1_dangerous(prs)
    build_slide_46_hook2_secrets(prs)
    build_slide_47_hook3_injection(prs)
    build_slide_48_hook4_pre_commit(prs)
    build_slide_49_skill_enforce(prs)
    build_slide_50_multi_llm(prs)
    build_slide_51_dor_dod(prs)
    build_slide_52_progress_hook(prs)
    build_slide_53_interactive(prs)
    build_slide_54_no_bypass(prs)
