from __future__ import annotations

import datetime as _dt
import hashlib
import json
import os
import re
from dataclasses import dataclass, field
from pathlib import Path


def _sha256_text(text: str) -> str:
    return hashlib.sha256(text.encode("utf-8", errors="replace")).hexdigest()


def _sha256_file(path: str) -> str:
    h = hashlib.sha256()
    with open(path, "rb") as f:
        for chunk in iter(lambda: f.read(1024 * 1024), b""):
            h.update(chunk)
    return h.hexdigest()


def generate_shadow_dossier(*, style_id: str, source_text: str, source_path: str, action_pack: bool = False) -> str:
    if style_id.lower() in {
        "if.dave.v1",
        "if.dave.v1.1",
        "if.dave.v1.2",
        "if.dave.v1.3",
        "if.dave.fr.v1.2",
        "if.dave.fr.v1.3",
        "dave",
        "if://bible/dave/v1.0",
        "if://bible/dave/v1.1",
        "if://bible/dave/v1.2",
        "if://bible/dave/v1.3",
        "if://bible/dave/fr/v1.2",
        "if://bible/dave/fr/v1.3",
    }:
        style = style_id.lower()
        locale = "fr" if style in {"if.dave.fr.v1.2", "if.dave.fr.v1.3", "if://bible/dave/fr/v1.2", "if://bible/dave/fr/v1.3"} else "en"
        if style in {"if.dave.v1.3", "if.dave.fr.v1.3", "if://bible/dave/v1.3", "if://bible/dave/fr/v1.3"}:
            return _generate_dave_v1_3_mirror(
                source_text=source_text,
                source_path=source_path,
                action_pack=action_pack,
                locale=locale,
            )
        return _generate_dave_v1_2_mirror(
            source_text=source_text,
            source_path=source_path,
            action_pack=action_pack,
            locale=locale,
        )
    raise ValueError(f"Unknown style id: {style_id}")

@dataclass(frozen=True)
class _SourceSection:
    title: str
    body: str
    why_it_matters: str | None = None

@dataclass
class _RenderContext:
    seed: str
    locale: str = "en"
    used_callouts: set[str] = field(default_factory=set)
    used_diagrams: set[str] = field(default_factory=set)
    used_paragraphs: set[str] = field(default_factory=set)
    used_reframe_tails: set[str] = field(default_factory=set)
    used_punchlines: set[str] = field(default_factory=set)

    def pick_unique(self, *, kind: str, key: str, variants: list[str], used: set[str]) -> str:
        if not variants:
            raise ValueError(f"Missing variants for {kind}")

        digest = hashlib.sha256(f"{self.seed}:{kind}:{key}".encode("utf-8", errors="replace")).digest()
        start = int.from_bytes(digest[:4], "big") % len(variants)

        for offset in range(len(variants)):
            candidate = variants[(start + offset) % len(variants)].strip()
            if candidate and candidate not in used:
                used.add(candidate)
                return candidate

        candidate = variants[start].strip()
        if candidate:
            used.add(candidate)
        return candidate


_PAGE_SPLIT_RE = re.compile(r"(?m)^===== page-(\d+) =====$")
_URL_RE = re.compile(r"https?://\S+")
_OWASP_TOC_LEADER_RE = re.compile(r"\.\s*\.\s*\.")
_SENTENCE_SPLIT_RE = re.compile(r"(?<=[.!?])\s+")
_TOC_ENTRY_RE = re.compile(r"^\s*(?P<title>.+?)\s+(?:\.\s*){3,}\s+(?P<page>\d+)\s*$")
_METRIC_VALUE_RE = re.compile(r"^(?:\$[\d,]+|\d+%|\d+-month)$")
_METRIC_TOKEN_RE = re.compile(r"\$[\d,]+|\b\d+%|\b\d+-month\b")
_NUMERIC_ANCHOR_RE = re.compile(
    r"\$[\d,]+"
    r"|\b\d+%|\b\d+-month\b"
    r"|\bn\s*=\s*\d+\b"
    r"|\b20\d{2}\b"
    r"|\b\d+(?:[.,]\d+)?\s*(?:€|eur|euros)\b"
    r"|\b\d+(?:[.,]\d+)?\s*(?:ans|jours|mois)\b"
    r"|\b\d+(?:[.,]\d+)?\s*(?:million|milliard|milliards|bn|billion|billions)\b",
    re.IGNORECASE,
)
_OWASP_REFERENCE_ITEM_RE = re.compile(r"^\s*(?P<num>\d+)\.\s*(?P<text>\S.*)$")
_NUMBERED_SECTION_START_RE = re.compile(r"^\s*(?P<num>\d{2,3})\s*$")
_ALPHA_RE = re.compile(r"[A-Za-zÀ-ÖØ-öø-ÿ]")

_OWASP_LLM_SUBHEADINGS = [
    "Description",
    "Types of Prompt Injection Vulnerabilities",
    "Common Examples of Vulnerability",
    "Common Examples of Risks",
    "Common Examples of Risk",
    "Prevention and Mitigation Strategies",
    "Example Attack Scenarios",
    "Sample Attack Scenarios",
    "Reference Links",
    "Related Frameworks and Taxonomies",
]

_DAVE_REFRAME_TAILS = [
    "so we can align on an owner, a gate, and an expiry date",
    "so we can socialize the stop condition and keep it on the roadmap",
    "so we can circle back next sprint with a merge-blocking rule",
    "so we can make this a deliverable instead of a vibe",
    "so we can keep stakeholder comfort high and risk acceptance time-bounded",
    "so we can preserve plausible deniability while still enforcing something real",
    "so we can put a pin in it until Legal is comfortable",
    "so we can capture it as an action item with an owner and a deadline",
    "so we can align on a plan to align on the plan, with an owner and an expiry date",
    "so we can turn this into a gate instead of a calendar invite",
    "so we can measure outcomes in something other than meeting attendance",
    "so we can keep this on the roadmap without letting it live in production forever",
]

_DAVE_REFRAME_TAILS_FR = [
    "afin que nous puissions aligner un responsable, un contrôle en amont et une date d’expiration",
    "afin que nous puissions le socialiser avec les parties prenantes et figer un critère de blocage",
    "afin que nous puissions y revenir au prochain cycle avec une règle réellement opposable",
    "afin que nous puissions en faire un livrable plutôt qu’une intention",
    "afin que nous puissions préserver le confort des parties prenantes tout en bornant l’acceptation du risque",
    "afin que nous puissions rester aimables sur la forme tout en étant vérifiables sur le fond",
    "afin que nous puissions ouvrir une concertation et formaliser une décision",
    "afin que nous puissions consigner un point d’action, avec un responsable et une échéance",
    "afin que nous puissions aligner un plan d’alignement, avec un responsable et une date d’expiration",
    "afin que nous puissions en faire une porte de contrôle plutôt qu’une invitation calendrier",
    "afin que nous puissions mesurer autre chose que la présence en réunion",
    "afin que nous puissions garder cela sur la feuille de route sans le laisser vivre en production indéfiniment",
]


def _looks_like_site_footer(line: str) -> bool:
    s = (line or "").strip()
    if not s:
        return False
    upper = s.upper()
    if upper.startswith("WWW.") and "." in upper and len(upper) <= 64:
        return True
    if re.fullmatch(r"[A-Z0-9.-]+\.[A-Z]{2,}", upper) and len(upper) <= 64:
        return True
    return False


def _extract_urls(text: str) -> list[str]:
    urls: list[str] = []
    for match in _URL_RE.finditer(text):
        url = match.group(0).rstrip(").,;:]>\"'")
        if url not in urls:
            urls.append(url)
    return urls


def _extract_owasp_reference_items(text: str) -> list[str]:
    items: list[tuple[str, list[str]]] = []
    cur_num: str | None = None
    cur_parts: list[str] = []

    def flush() -> None:
        nonlocal cur_num, cur_parts
        if cur_num is None:
            return
        joined = " ".join([p.strip() for p in cur_parts if p.strip()]).strip()
        joined = re.sub(r"\s{2,}", " ", joined).strip()
        if joined:
            items.append((cur_num, [joined]))
        cur_num = None
        cur_parts = []

    for ln in (text or "").splitlines():
        m = _OWASP_REFERENCE_ITEM_RE.match(ln)
        if m:
            flush()
            cur_num = m.group("num")
            cur_parts = [m.group("text").strip()]
            continue

        if cur_num is None:
            continue

        s = ln.strip()
        if not s:
            continue
        if s.startswith("genai.owasp.org") or "OWASP Top 10 for LLM" in s:
            continue
        cur_parts.append(s)

    flush()

    out: list[str] = []
    for num, parts in items:
        text = " ".join(parts).strip()
        if text:
            out.append(f"{num}. {text}")
    return out


def _inject_reframe_tail(line: str, tail: str) -> str:
    m = re.match(r"^(?P<prefix>\s*>\s*)(?P<body>.*)$", line.rstrip())
    if not m:
        return line.rstrip()

    prefix = m.group("prefix")
    body = m.group("body").rstrip()
    if not body or "—" in body:
        return f"{prefix}{body}".rstrip()

    closers = ""
    while body and body[-1] in {")", "]", '"', "'", "”", "’"}:
        closers = body[-1] + closers
        body = body[:-1]

    punct = "."
    if body and body[-1] in {".", "!", "?"}:
        punct = body[-1]
        body = body[:-1]

    body = body.rstrip()
    return f"{prefix}{body} — {tail}{punct}{closers}".rstrip()


def _inject_plain_tail(text: str, tail: str) -> str:
    body = (text or "").strip()
    if not body or "—" in body:
        return body

    closers = ""
    while body and body[-1] in {")", "]", '"', "'", "”", "’"}:
        closers = body[-1] + closers
        body = body[:-1]

    punct = "."
    if body and body[-1] in {".", "!", "?"}:
        punct = body[-1]
        body = body[:-1]

    body = body.rstrip()
    return f"{body} — {tail}{punct}{closers}".strip()


def _daveify_callout_reframe(callout: str, *, ctx: _RenderContext, key: str) -> str:
    lines = callout.splitlines()
    for idx in range(len(lines) - 1, -1, -1):
        stripped = lines[idx].lstrip()
        if not stripped.startswith(">"):
            continue
        if re.match(r"^>\s*\*\*[^*]+\*\*:", stripped):
            continue
        tails = _DAVE_REFRAME_TAILS_FR if ctx.locale.lower().startswith("fr") else _DAVE_REFRAME_TAILS
        tail = ctx.pick_unique(kind="reframe_tail", key=key, variants=tails, used=ctx.used_reframe_tails)
        lines[idx] = _inject_reframe_tail(lines[idx], tail)
        return "\n".join(lines).strip()
    return callout.strip()


def _looks_like_owasp_llm_top10(text: str) -> bool:
    if "OWASP Top 10 for" not in text and "OWASP Top 10" not in text:
        return False
    return "LLM01" in text and "LLM10" in text


def _looks_like_idc_business_value(text: str) -> bool:
    if "Business Value White Paper" not in text:
        return False
    if "Table of Contents" not in text:
        return False
    return "IDC #" in text or "IDC" in text


def _paragraphs_from_lines(text: str) -> list[str]:
    paragraphs: list[str] = []
    buf: list[str] = []
    for ln in text.splitlines():
        s = ln.strip()
        if not s:
            if buf:
                paragraphs.append(" ".join(buf).strip())
                buf = []
            continue
        buf.append(s)
    if buf:
        paragraphs.append(" ".join(buf).strip())
    paragraphs = [re.sub(r"\s{2,}", " ", p).strip() for p in paragraphs if p.strip()]
    return paragraphs


def _first_sentences(text: str, *, max_sentences: int = 2, max_chars: int = 260) -> str:
    paragraphs = _paragraphs_from_lines(text)
    if not paragraphs:
        return ""
    sentences: list[str] = []
    for para in paragraphs[:3]:
        for sent in _SENTENCE_SPLIT_RE.split(para):
            cleaned = sent.strip()
            if cleaned:
                sentences.append(cleaned)
            if len(sentences) >= max_sentences:
                break
        if len(sentences) >= max_sentences:
            break
    snippet = " ".join(sentences).strip()
    if len(snippet) > max_chars:
        snippet = snippet[: max_chars - 1].rstrip() + "…"
    return snippet


def _extract_numeric_anchors(text: str, *, limit: int = 3) -> list[str]:
    anchors: list[str] = []
    for match in _NUMERIC_ANCHOR_RE.finditer(text or ""):
        value = match.group(0).strip()
        if not value:
            continue
        normalized = re.sub(r"\s+", "", value.lower())
        if normalized not in {re.sub(r"\s+", "", a.lower()) for a in anchors}:
            anchors.append(value)
        if len(anchors) >= limit:
            break
    return anchors


def _split_owasp_llm_subsections(body: str) -> list[tuple[str, str]]:
    headings = set(_OWASP_LLM_SUBHEADINGS)
    lines = [ln.rstrip() for ln in body.splitlines()]

    parts: list[tuple[str, str]] = []
    cur: str | None = None
    buf: list[str] = []

    def flush() -> None:
        nonlocal cur, buf
        if cur is None:
            return
        parts.append((cur, "\n".join(buf).strip()))
        cur = None
        buf = []

    for ln in lines:
        s = ln.strip()
        if s in headings:
            flush()
            cur = s
            buf = []
            continue
        if cur is None:
            continue
        buf.append(ln)

    flush()
    return parts


def _extract_idc_highlight_metrics(body: str) -> list[tuple[str, str]]:
    lines = [ln.rstrip("\n") for ln in body.splitlines()]
    # Find a line with three metric tokens to derive column boundaries.
    col_starts: list[int] = []
    for ln in lines:
        tokens = list(_METRIC_TOKEN_RE.finditer(ln))
        if len(tokens) >= 3:
            col_starts = [tokens[0].start(), tokens[1].start(), tokens[2].start()]
            break

    if not col_starts:
        return []

    starts = col_starts + [10_000]

    metrics_by_col: list[list[tuple[str, list[str]]]] = [[], [], []]
    current: list[tuple[str, list[str]] | None] = [None, None, None]

    def flush(col: int) -> None:
        if current[col] is None:
            return
        value, parts = current[col]
        desc = " ".join([p.strip() for p in parts if p.strip()]).strip()
        metrics_by_col[col].append((value, [desc] if desc else []))
        current[col] = None

    for ln in lines:
        # Stop parsing if the next major heading begins.
        if ln.strip() == "Executive Summary":
            break

        padded = ln + " " * 5
        cols = []
        for i in range(3):
            segment = padded[starts[i] : starts[i + 1]].strip()
            cols.append(segment)

        for i, seg in enumerate(cols):
            if not seg:
                continue
            if _METRIC_VALUE_RE.match(seg):
                flush(i)
                current[i] = (seg, [])
                continue
            if current[i] is None:
                continue
            current[i][1].append(seg)

    for i in range(3):
        flush(i)

    flattened: list[tuple[str, str]] = []
    for col_metrics in metrics_by_col:
        for value, desc_parts in col_metrics:
            desc = desc_parts[0] if desc_parts else ""
            if desc:
                flattened.append((value, desc))
    return flattened


def _normalize_ocr(text: str) -> str:
    text = re.sub(r"\bAl\b", "AI", text)
    text = text.replace("GenAl", "GenAI")
    text = text.replace("Cl/CD", "CI/CD")
    text = text.replace("olugin", "plugin")
    text = text.replace("FORIWARD", "FORWARD")
    text = text.replace("\\u00agrave", "à")
    return text


_UNICODE_QUOTE_MAP = str.maketrans(
    {
        "\u201c": '"',
        "\u201d": '"',
        "\u2018": "'",
        "\u2019": "'",
        "\u2013": "-",
        "\u2014": "-",
        "\u00a0": " ",
    }
)


def _normalize_unicode_punctuation(text: str) -> str:
    return str(text or "").translate(_UNICODE_QUOTE_MAP)


def _coerce_json_code_block(code: str) -> tuple[str, str, bool]:
    cleaned = _normalize_unicode_punctuation(code).strip()
    try:
        obj = json.loads(cleaned)
    except Exception:
        return "text", cleaned, False
    return "json", json.dumps(obj, indent=2, ensure_ascii=False), True


def _parse_pages(source_text: str) -> list[tuple[str, str]]:
    matches = list(_PAGE_SPLIT_RE.finditer(source_text))
    if not matches:
        if "\f" in source_text:
            parts = [p.strip() for p in source_text.split("\f")]
            pages: list[tuple[str, str]] = []
            page_no = 1
            for part in parts:
                if part:
                    pages.append((str(page_no), part))
                    page_no += 1
            return pages or [("doc", source_text.strip())]
        return [("doc", source_text.strip())]

    pages: list[tuple[str, str]] = []
    for idx, match in enumerate(matches):
        page_no = match.group(1)
        start = match.end()
        end = matches[idx + 1].start() if idx + 1 < len(matches) else len(source_text)
        pages.append((page_no, source_text[start:end].strip()))
    return pages


def _parse_title_block(lines: list[str]) -> tuple[str, int]:
    i = 0
    while i < len(lines) and not lines[i].strip():
        i += 1
    title_lines: list[str] = []
    while i < len(lines) and lines[i].strip():
        stripped = lines[i].strip()
        if stripped.lower() != "snyk":
            title_lines.append(stripped)
        i += 1
    while i < len(lines) and not lines[i].strip():
        i += 1
    title = " ".join(title_lines).strip() or "UNTITLED"
    return title, i


def _extract_title_above(lines: list[str], why_idx: int) -> str:
    j = why_idx - 1
    while j >= 0 and not lines[j].strip():
        j -= 1

    title_lines: list[str] = []
    while j >= 0 and lines[j].strip():
        title_lines.append(lines[j].strip())
        j -= 1
    title_lines.reverse()

    k = j
    while k >= 0 and not lines[k].strip():
        k -= 1
    if k >= 0 and re.fullmatch(r"\d{1,3}", lines[k].strip()):
        title_lines.insert(0, lines[k].strip())

    title = " ".join(title_lines).strip()
    match = re.match(r"^(\d{1,3})\s+(.+)$", title)
    if match:
        label = match.group(1)
        if len(label) == 3 and label.startswith("0"):
            label = label[:2]
        title = f"{label} — {match.group(2)}"
    return title


def _parse_sections_from_page(page_text: str) -> list[_SourceSection]:
    lines = [ln.rstrip() for ln in page_text.splitlines()]
    why_idxs = [i for i, ln in enumerate(lines) if ln.strip().lower().startswith("why it matters:")]

    if not why_idxs:
        title, body_start = _parse_title_block(lines)
        body = "\n".join([ln for ln in lines[body_start:] if ln.strip() and ln.strip().lower() != "snyk"]).strip()
        return [_SourceSection(title=title, body=body, why_it_matters=None)]

    sections: list[_SourceSection] = []
    for idx, why_idx in enumerate(why_idxs):
        title = _extract_title_above(lines, why_idx)
        end = why_idxs[idx + 1] if idx + 1 < len(why_idxs) else len(lines)
        why = lines[why_idx].strip()
        body = "\n".join(lines[why_idx + 1 : end]).strip()
        sections.append(_SourceSection(title=title, body=body, why_it_matters=why))
    return sections


def _clean_page_body(text: str) -> str:
    lines: list[str] = []
    for ln in (text or "").splitlines():
        s = ln.strip()
        if not s:
            if lines and lines[-1] != "":
                lines.append("")
            continue
        if _looks_like_site_footer(s):
            continue
        lines.append(ln.rstrip())
    return "\n".join(lines).strip()


def _parse_numbered_section_page(page_text: str) -> tuple[str, str, str] | None:
    lines = [ln.rstrip() for ln in (page_text or "").splitlines()]
    i = 0
    while i < len(lines):
        s = lines[i].strip()
        if not s:
            i += 1
            continue
        if _looks_like_site_footer(s):
            i += 1
            continue
        break
    if i >= len(lines):
        return None

    m = _NUMBERED_SECTION_START_RE.match(lines[i].strip())
    if not m:
        return None

    num = m.group("num")
    # Some PDFs/OCR runs concatenate the section label with a nearby page marker, e.g. `035` for section `03`.
    if len(num) == 3 and num.startswith("0"):
        num = num[:2]
    if len(num) != 2:
        return None

    j = i + 1
    while j < len(lines) and not lines[j].strip():
        j += 1

    title_lines: list[str] = []
    while j < len(lines) and lines[j].strip():
        s = lines[j].strip()
        if _looks_like_site_footer(s):
            break
        title_lines.append(s)
        j += 1

    title = " ".join(title_lines).strip()
    if not title or not _ALPHA_RE.search(title):
        return None

    while j < len(lines) and not lines[j].strip():
        j += 1

    body = _clean_page_body("\n".join(lines[j:]))
    return (num, title, body)


def _extract_sections_numbered_outline(pages: list[tuple[str, str]]) -> list[_SourceSection] | None:
    starts = 0
    for _page_no, page_text in pages:
        if _parse_numbered_section_page(page_text):
            starts += 1
    if starts < 3:
        return None

    cover_parts: list[str] = []
    sections: list[_SourceSection] = []
    cur_title: str | None = None
    cur_body_parts: list[str] = []

    def flush_cover() -> None:
        nonlocal cover_parts
        cover_text = "\n\n".join([p for p in cover_parts if p.strip()]).strip()
        cover_parts = []
        if not cover_text:
            return
        sections.append(_SourceSection(title="COUVERTURE", body=_clean_page_body(cover_text), why_it_matters=None))

    def flush_section() -> None:
        nonlocal cur_title, cur_body_parts
        if cur_title is None:
            return
        body = "\n\n".join([p for p in cur_body_parts if p.strip()]).strip()
        sections.append(_SourceSection(title=cur_title, body=_clean_page_body(body), why_it_matters=None))
        cur_title = None
        cur_body_parts = []

    for _page_no, page_text in pages:
        parsed = _parse_numbered_section_page(page_text)
        if parsed:
            num, title, body = parsed
            if cur_title is None:
                flush_cover()
            else:
                flush_section()
            cur_title = f"{num} — {title}"
            cur_body_parts = [body] if body else []
            continue

        cleaned = _clean_page_body(page_text)
        if not cleaned:
            continue
        if cur_title is None:
            cover_parts.append(cleaned)
        else:
            cur_body_parts.append(cleaned)

    if cur_title is None:
        flush_cover()
    else:
        flush_section()

    return sections or None


def _extract_sections(source_text: str) -> list[_SourceSection]:
    if _looks_like_owasp_llm_top10(source_text):
        return _extract_sections_owasp_llm_top10(source_text)
    if _looks_like_idc_business_value(source_text):
        return _extract_sections_idc_business_value(source_text)

    pages = _parse_pages(source_text)
    numbered = _extract_sections_numbered_outline(pages)
    if numbered:
        return numbered
    sections: list[_SourceSection] = []
    for _page_no, page_text in pages:
        if page_text.strip():
            sections.extend(_parse_sections_from_page(page_text))
    return sections


def _owasp_clean_lines(lines: list[str]) -> list[str]:
    cleaned: list[str] = []
    for ln in lines:
        s = ln.strip()
        if not s:
            cleaned.append("")
            continue
        if s.startswith("OWASP Top 10 for LLM Applications"):
            continue
        if "genai.owasp.org" in s:
            continue
        if s.isdigit() and len(s) <= 3:
            continue
        # Drop sponsor-logo garbage / broken glyph runs.
        if sum(1 for ch in s if " " <= ch <= "~") < max(4, int(len(s) * 0.35)):
            continue
        cleaned.append(s)
    return cleaned


def _owasp_looks_like_toc_entry(line: str) -> bool:
    s = line.strip()
    if not s:
        return False
    if not _OWASP_TOC_LEADER_RE.search(s):
        return False
    return bool(re.search(r"\d\s*$", s))


def _extract_sections_owasp_llm_top10(source_text: str) -> list[_SourceSection]:
    pages = source_text.split("\f")
    if not pages:
        return []

    cover_lines = [ln.rstrip() for ln in pages[0].splitlines()]
    cover_title, cover_start = _parse_title_block(cover_lines)
    cover_body = "\n".join([ln for ln in cover_lines[cover_start:] if ln.strip()]).strip()

    sections: list[_SourceSection] = [_SourceSection(title=cover_title, body=cover_body, why_it_matters=None)]

    major_exact = {
        "LICENSE AND USAGE",
        "REVISION HISTORY",
        "Table of Contents",
        "Letter from the Project Leads",
        "What’s New in the 2025 Top 10",
        "What's New in the 2025 Top 10",
        "Moving Forward",
        "Project Sponsors",
    }
    llm_re = re.compile(r"^LLM\d{2}:")
    appendix_re = re.compile(r"^Appendix\s+\d+:")

    lines: list[str] = []
    for pg in pages[1:]:
        lines.extend(_owasp_clean_lines([ln.rstrip("\n") for ln in pg.splitlines()]))
        lines.append("")

    cur_title: str | None = None
    cur_body: list[str] = []

    def flush() -> None:
        nonlocal cur_title, cur_body
        if cur_title is None:
            return
        body = "\n".join(cur_body).strip()
        sections.append(_SourceSection(title=cur_title, body=body, why_it_matters=None))
        cur_title = None
        cur_body = []

    for ln in lines:
        s = ln.strip()
        if not s:
            if cur_title is not None and (cur_body and cur_body[-1] != ""):
                cur_body.append("")
            continue

        is_heading = False
        if s in major_exact:
            is_heading = True
        elif llm_re.match(s) and not _owasp_looks_like_toc_entry(s):
            is_heading = True
        elif appendix_re.match(s) and not _owasp_looks_like_toc_entry(s):
            is_heading = True

        if is_heading:
            flush()
            cur_title = s
            cur_body = []
            continue

        if cur_title is None:
            continue
        cur_body.append(s)

    flush()
    return sections


def _idc_clean_lines(lines: list[str]) -> list[str]:
    cleaned: list[str] = []
    for ln in lines:
        s = ln.strip()
        if not s:
            cleaned.append("")
            continue
        if "Business Value White Paper" in s:
            continue
        if s.startswith("January ") and "IDC #" in s:
            continue
        if "Return to Highlights" in s:
            continue
        if s.isdigit() and len(s) <= 3:
            continue
        cleaned.append(ln.rstrip())
    return cleaned


def _normalize_heading(value: str) -> str:
    cleaned = re.sub(r"[^A-Za-z0-9]+", " ", value).strip().upper()
    return re.sub(r"\s{2,}", " ", cleaned)


def _extract_idc_toc_entries(pages: list[list[str]]) -> list[tuple[str, int]]:
    toc_page = None
    toc_line_idx = None
    for p_idx, page_lines in enumerate(pages):
        for l_idx, ln in enumerate(page_lines):
            if ln.strip() == "Table of Contents":
                toc_page = p_idx
                toc_line_idx = l_idx
                break
        if toc_page is not None:
            break

    if toc_page is None or toc_line_idx is None:
        return []

    entries: list[tuple[str, int]] = []
    for ln in pages[toc_page][toc_line_idx + 1 :]:
        match = _TOC_ENTRY_RE.match(ln.strip())
        if not match:
            continue
        title = match.group("title").strip().rstrip(".").strip()
        try:
            page_no = int(match.group("page"))
        except ValueError:
            continue
        if not title:
            continue
        entries.append((title, page_no))

    return entries


def _find_heading_anchor(*, pages: list[list[str]], title: str, page_no: int) -> tuple[int, int, str]:
    norm_title = _normalize_heading(title)
    start_page = max(0, min(len(pages) - 1, page_no - 1))

    for p_idx in range(start_page, min(len(pages), start_page + 3)):
        for l_idx, ln in enumerate(pages[p_idx]):
            s = ln.strip()
            if not s:
                continue
            if _TOC_ENTRY_RE.match(s):
                continue
            if _normalize_heading(s) == norm_title:
                return p_idx, l_idx, s
    return start_page, 0, title


def _slice_pages(
    *, pages: list[list[str]], start: tuple[int, int], end: tuple[int, int] | None
) -> list[str]:
    start_page, start_line = start
    if end is None:
        end_page, end_line = len(pages), 0
    else:
        end_page, end_line = end

    out: list[str] = []
    for p_idx in range(start_page, min(end_page + 1, len(pages))):
        lines = pages[p_idx]
        lo = start_line if p_idx == start_page else 0
        hi = end_line if (end is not None and p_idx == end_page) else len(lines)
        out.extend(lines[lo:hi])
        out.append("")
    return out


def _extract_sections_idc_business_value(source_text: str) -> list[_SourceSection]:
    raw_pages = source_text.split("\f")
    if not raw_pages:
        return []

    cover_lines = [ln.rstrip() for ln in raw_pages[0].splitlines()]
    cover_title = next(
        (
            ln.strip()
            for ln in cover_lines
            if ln.strip()
            and "Business Value White Paper" not in ln
            and "THIS PDF USES" not in ln
            and "|" not in ln
        ),
        None,
    )
    if not cover_title:
        cover_title, _ = _parse_title_block(cover_lines)

    try:
        title_idx = next(i for i, ln in enumerate(cover_lines) if ln.strip() == cover_title)
    except StopIteration:
        title_idx = 0
    cover_body = "\n".join([ln for ln in cover_lines[title_idx + 1 :] if ln.strip()]).strip()

    pages = [_idc_clean_lines([ln for ln in pg.splitlines()]) for pg in raw_pages]

    sections: list[_SourceSection] = [_SourceSection(title=cover_title, body=cover_body, why_it_matters=None)]

    toc_entries = _extract_idc_toc_entries(pages)
    if toc_entries:
        toc_lines = ["- " + t for t, _p in toc_entries]
        sections.append(_SourceSection(title="Table of Contents", body="\n".join(toc_lines), why_it_matters=None))

    anchors: list[tuple[str, tuple[int, int], str]] = []
    for title, page_no in toc_entries:
        p_idx, l_idx, found_title = _find_heading_anchor(pages=pages, title=title, page_no=page_no)
        anchors.append((found_title, (p_idx, l_idx), title))

    # De-duplicate anchors that point to the same place (can happen with repeating headers).
    uniq: list[tuple[str, tuple[int, int], str]] = []
    seen: set[tuple[int, int, str]] = set()
    for found_title, anchor, toc_title in anchors:
        key = (anchor[0], anchor[1], _normalize_heading(toc_title))
        if key in seen:
            continue
        seen.add(key)
        uniq.append((found_title, anchor, toc_title))

    for idx, (found_title, anchor, toc_title) in enumerate(uniq):
        next_anchor = uniq[idx + 1][1] if idx + 1 < len(uniq) else None
        lines = _slice_pages(pages=pages, start=anchor, end=next_anchor)
        # Drop the heading line itself if it repeats at the start.
        while lines and not lines[0].strip():
            lines.pop(0)
        if lines and _normalize_heading(lines[0].strip()) == _normalize_heading(found_title):
            lines.pop(0)
        body = "\n".join(lines).strip()
        sections.append(_SourceSection(title=found_title or toc_title, body=body, why_it_matters=None))

    return sections


def _has(text: str, *needles: str) -> bool:
    lowered = text.lower()
    return any(n.lower() in lowered for n in needles)


def _extract_code_block(body: str) -> str | None:
    lines = [ln.rstrip() for ln in body.splitlines()]
    start = next((i for i, ln in enumerate(lines) if ln.strip().startswith("{")), None)
    if start is None:
        return None
    end = None
    for i in range(start, len(lines)):
        if lines[i].strip() == "}":
            end = i
            break
    if end is None:
        return None
    return "\n".join(lines[start : end + 1])


def _extract_access_report(body: str) -> str | None:
    if "Gemini Access Report" not in body:
        return None

    lines = [ln.strip() for ln in body.splitlines() if ln.strip()]
    try:
        idx = lines.index("Gemini Access Report")
    except ValueError:
        return None

    rows: list[list[str]] = []
    for ln in lines[idx + 1 : idx + 8]:
        if "@" not in ln:
            continue
        parts = [p for p in re.split(r"\s{2,}", ln) if p]
        if len(parts) >= 5:
            rows.append(parts[:5])

    if not rows:
        return None

    header = ["Name", "Email", "License Assigned", "Last Active", "Last Detected Scan"]
    out = [
        "### Gemini Access Report",
        "",
        "| " + " | ".join(header) + " |",
        "| " + " | ".join(["---"] * len(header)) + " |",
    ]
    for r in rows:
        out.append("| " + " | ".join(r) + " |")
    return "\n".join(out)


def _extract_quiz(body: str) -> str | None:
    if "Quiz" not in body:
        return None
    if "What must you do" not in body and "What must you do if you want access" not in body:
        return None

    question_match = re.search(r"(What must you do[^\n\r]+)", body)
    question = question_match.group(1).strip() if question_match else "Quiz"

    options = []
    for ln in body.splitlines():
        stripped = ln.strip(" >•\t")
        if stripped.startswith("Include") or stripped.startswith("Install") or stripped.startswith("Download"):
            options.append(stripped)

    if not options:
        return None

    out = ["### Quiz", "", f"**{question}**", ""]
    out.extend([f"- {opt}" for opt in options])
    return "\n".join(out)


def _extract_form(body: str) -> str | None:
    if "Code Assistant Access Request Form" not in body:
        return None
    return "\n".join(
        [
            "### Code Assistant Access Request Form",
            "",
            "- Upload a screenshot showing the security IDE plugin is installed for local testing.",
            "- Provide any additional context on the request.",
            "- Attest that the AI coding assistant will be used in conjunction with local scanning.",
        ]
    )


def _slugify(value: str) -> str:
    cleaned = re.sub(r"[^A-Za-z0-9]+", "-", value).strip("-")
    cleaned = re.sub(r"-{2,}", "-", cleaned)
    return cleaned.upper() if cleaned else "UNKNOWN"


def _inferred_mermaid(title: str, *, ctx: _RenderContext) -> str | None:
    title_upper = title.upper()

    if "BUSINESS VALUE" in title_upper or "ROI" in title_upper:
        variants = [
            """flowchart TD
  A["Sponsor narrative"] --> B["Business value model"]
  B --> C["Executive buy-in"]
  C --> D["Rollout project"]
  D --> E["Evidence artifacts produced"]
  E --> F["Renewal discussion"]
  F --> G["KPI trend deck"]
  G --> C
""",
            """flowchart TD
  A["Baseline (unknown)"] --> B["ROI spreadsheet"]
  B --> C["Assumptions: optimistic"]
  C --> D["Rollout work"]
  D --> E["Exceptions + manual steps"]
  E --> F["Metric redefinition"]
  F --> B
""",
            """flowchart TD
  A["Procurement decision"] --> B["Implementation project"]
  B --> C["Evidence automation"]
  C --> D["Audit season"]
  D --> E["Renewal negotiation"]
  E --> F["Success story deck"]
  F --> A
""",
            """flowchart TD
  A["Executive sponsor"] --> B["Quarterly update deck"]
  B --> C["KPI trend (directional)"]
  C --> D["Roadmap refresh"]
  D --> E["Pilot expansion"]
  E --> B
""",
        ]
        return ctx.pick_unique(kind="diagram:roi", key=title, variants=variants, used=ctx.used_diagrams)

    if "COMPLIANCE" in title_upper or "AUDIT" in title_upper:
        variants = [
            """flowchart TD
  A["Control requirement"] --> B["Evidence requested"]
  B --> C["Artifact gathered"]
  C --> D["Review meeting"]
  D --> E{Approved?}
  E -->|Yes| F["Audit satisfied"]
  E -->|No| G["Remediation plan"]
  G --> D
""",
            """flowchart TD
  A["Control requirement"] --> B["Evidence request"]
  B --> C["Screenshot collected"]
  C --> D["Shared drive folder"]
  D --> E["Checklist satisfied"]
  E --> F["Exceptions accumulate"]
  F --> B
""",
            """flowchart TD
  A["Quarter begins"] --> B["Evidence scramble"]
  B --> C["Spreadsheet status"]
  C --> D["Steering committee"]
  D --> E["Audit passed"]
  E --> F["Backlog deferred"]
  F --> A
""",
        ]
        return ctx.pick_unique(kind="diagram:audit", key=title, variants=variants, used=ctx.used_diagrams)

    if "THIRD-PARTY" in title_upper or "VENDOR" in title_upper:
        variants = [
            """flowchart TD
  A["Vendor onboarding"] --> B["Questionnaire"]
  B --> C["Evidence chase"]
  C --> D["Risk rating"]
  D --> E{Exception?}
  E -->|Yes| F["Accepted with notes"]
  E -->|No| G["Blocked pending controls"]
  F --> H["Renewal cycle"]
  G --> H
""",
            """flowchart TD
  A["Vendor intake"] --> B["Security questionnaire"]
  B --> C["Vendor sends PDF"]
  C --> D["Internal interpretation"]
  D --> E["Legal review"]
  E --> F{Decision}
  F -->|Accept| G["Approved with exceptions"]
  F -->|Block| H["Deferred to next quarter"]
  G --> I["Renewal"]
  H --> I
""",
            """flowchart TD
  A["Business wants tool"] --> B["Vendor risk rating"]
  B --> C{High risk?}
  C -->|Yes| D["Exception workflow"]
  C -->|No| E["Standard approval"]
  D --> F["Compensating controls"]
  F --> E
  E --> G["Onboarding complete"]
  G --> H["Usage begins"]
  H --> I["Reassessment"]
  I --> B
""",
        ]
        return ctx.pick_unique(kind="diagram:third_party", key=title, variants=variants, used=ctx.used_diagrams)

    if title_upper.startswith("LLM01") or "PROMPT INJECTION" in title_upper:
        return ctx.pick_unique(
            kind="diagram:llm01",
            key=title,
            variants=[
                """flowchart TD
  A["Attacker prompt"] --> B["LLM prompt parser"]
  B --> C["System prompt + tools"]
  C --> D["Model follows injected instruction"]
  D --> E["Unsafe action or data exposure"]
  E --> F["Incident review meeting"]
  F --> G["Policy update: scheduled"]
"""
            ],
            used=ctx.used_diagrams,
        )

    if title_upper.startswith("LLM02") or "SENSITIVE INFORMATION" in title_upper:
        return ctx.pick_unique(
            kind="diagram:llm02",
            key=title,
            variants=[
                """flowchart TD
  A["User asks a question"] --> B["LLM retrieves context"]
  B --> C["Hidden secret present in context"]
  C --> D["Model outputs secret"]
  D --> E["Screenshot captured for compliance"]
  E --> F["Access remains enabled"]
"""
            ],
            used=ctx.used_diagrams,
        )

    if title_upper.startswith("LLM03") or "SUPPLY CHAIN" in title_upper:
        return ctx.pick_unique(
            kind="diagram:llm03",
            key=title,
            variants=[
                """flowchart TD
  A["Upstream model or dependency"] --> B["Pulled into build"]
  B --> C["Trusted by default"]
  C --> D["Compromise introduced"]
  D --> E["Shipped to production"]
  E --> F["Vendor asks for logs"]
  F --> G["We align on next steps"]
"""
            ],
            used=ctx.used_diagrams,
        )

    if title_upper.startswith("LLM04") or "POISONING" in title_upper:
        return ctx.pick_unique(
            kind="diagram:llm04",
            key=title,
            variants=[
                """flowchart TD
  A["Attacker data"] --> B["Training or fine-tune"]
  B --> C["Model behavior shifts"]
  C --> D["Bad outputs in production"]
  D --> E["Root cause: unclear"]
  E --> F["New dataset review committee"]
"""
            ],
            used=ctx.used_diagrams,
        )

    if title_upper.startswith("LLM05") or "OUTPUT HANDLING" in title_upper:
        return ctx.pick_unique(
            kind="diagram:llm05",
            key=title,
            variants=[
                """flowchart TD
  A["LLM generates output"] --> B["Output treated as trusted"]
  B --> C["Downstream system executes or renders"]
  C --> D["Injection hits a sink"]
  D --> E["Hotfix + postmortem"]
  E --> F["Guardrail doc updated"]
"""
            ],
            used=ctx.used_diagrams,
        )

    if title_upper.startswith("LLM06") or "EXCESSIVE AGENCY" in title_upper:
        return ctx.pick_unique(
            kind="diagram:llm06",
            key=title,
            variants=[
                """flowchart TD
  A["User goal"] --> B["Agent plans steps"]
  B --> C["Tool access granted"]
  C --> D["Action executed"]
  D --> E["Unexpected side effect"]
  E --> F["Exception request filed"]
  F --> C
"""
            ],
            used=ctx.used_diagrams,
        )

    if title_upper.startswith("LLM07") or "PROMPT LEAKAGE" in title_upper:
        return ctx.pick_unique(
            kind="diagram:llm07",
            key=title,
            variants=[
                """flowchart TD
  A["User prompt"] --> B["Model context window"]
  B --> C["System prompt present"]
  C --> D["Leak via output or tool call"]
  D --> E["Prompt rotated quarterly"]
  E --> C
"""
            ],
            used=ctx.used_diagrams,
        )

    if title_upper.startswith("LLM08") or "VECTOR" in title_upper or "EMBEDDING" in title_upper:
        return ctx.pick_unique(
            kind="diagram:llm08",
            key=title,
            variants=[
                """flowchart TD
  A["Documents ingested"] --> B["Embeddings store"]
  B --> C["Retriever selects chunks"]
  C --> D["Injected chunk included"]
  D --> E["LLM follows malicious context"]
  E --> F["We add a filter later"]
"""
            ],
            used=ctx.used_diagrams,
        )

    if title_upper.startswith("LLM09") or "MISINFORMATION" in title_upper:
        return ctx.pick_unique(
            kind="diagram:llm09",
            key=title,
            variants=[
                """flowchart TD
  A["Model output"] --> B["Looks confident"]
  B --> C["Decision made"]
  C --> D["Outcome fails"]
  D --> E["Retroactive citations requested"]
  E --> F["Alignment session"]
"""
            ],
            used=ctx.used_diagrams,
        )

    if title_upper.startswith("LLM10") or "UNBOUNDED CONSUMPTION" in title_upper:
        return ctx.pick_unique(
            kind="diagram:llm10",
            key=title,
            variants=[
                """flowchart TD
  A["Request"] --> B["Tokens consumed"]
  B --> C["Costs rise"]
  C --> D["Rate limit suggested"]
  D --> E["Exception granted"]
  E --> B
"""
            ],
            used=ctx.used_diagrams,
        )

    if title_upper.startswith("APPENDIX 1") or "ARCHITECTURE" in title_upper:
        return ctx.pick_unique(
            kind="diagram:architecture",
            key=title,
            variants=[
                """flowchart TD
  A["User"] --> B["App"]
  B --> C["LLM"]
  C --> D["Tools"]
  C --> E["RAG store"]
  D --> F["External systems"]
  E --> C
"""
            ],
            used=ctx.used_diagrams,
        )

    if "PULL REQUEST" in title_upper:
        return ctx.pick_unique(
            kind="diagram:pull_request",
            key=title,
            variants=[
                """flowchart TD
  A["Code change"] --> B["Pull request opened"]
  B --> C["Automated scan: PR checks"]
  C --> D{Findings?}
  D -->|None| E["Merge"]
  D -->|Some| F["Ticket created"]
  F --> G["Exception request"]
  G --> H["Alignment session"]
  H --> I["Risk accepted: documented"]
  I --> E
"""
            ],
            used=ctx.used_diagrams,
        )

    if "SHIFTING LEFT" in title_upper:
        return ctx.pick_unique(
            kind="diagram:shift_left",
            key=title,
            variants=[
                """flowchart TD
  A["Developer writes code"] --> B["IDE scan: local"]
  B --> C{Issue found?}
  C -->|Yes| D["Fix now"]
  C -->|No| E["Commit"]
  E --> F["PR checks"]
  A --> G["Agent workflow"]
  G --> H["Local MCP scan"]
  H --> E
"""
            ],
            used=ctx.used_diagrams,
        )

    if "REQUEST EVIDENCE" in title_upper:
        return ctx.pick_unique(
            kind="diagram:request_evidence",
            key=title,
            variants=[
                """flowchart TD
  A["Developer requests access"] --> B["Upload screenshot"]
  B --> C["Attestation captured"]
  C --> D["Access enabled"]
  D --> E["Local testing: claimed"]
  E --> F["Periodic audit"]
  F --> G{Still compliant?}
  G -->|Yes| D
  G -->|No| H["Access paused pending review"]
  H --> I["Alignment session"]
  I --> D
"""
            ],
            used=ctx.used_diagrams,
        )

    if title_upper == "AUDIT":
        return ctx.pick_unique(
            kind="diagram:audit_generic",
            key=title,
            variants=[
                """flowchart TD
  A["Collect usage signals"] --> B["Correlate assistants vs scans"]
  B --> C["Identify gaps"]
  C --> D["Notify developers"]
  D --> E["Remediation window"]
  E --> F["Dashboard update"]
  F --> G["Quarterly KPI trend review"]
  G --> H["Action items: optional"]
"""
            ],
            used=ctx.used_diagrams,
        )

    if "TRAINING" in title_upper:
        return ctx.pick_unique(
            kind="diagram:training",
            key=title,
            variants=[
                """flowchart TD
  A["Onboarding"] --> B["Training module"]
  B --> C["Quiz"]
  C --> D{Pass?}
  D -->|Yes| E["Certificate issued"]
  D -->|No| F["Retake scheduled"]
  E --> G["Access request approved"]
  G --> H["Usage begins"]
  H --> I["Refresher cadence"]
  I --> B
"""
            ],
            used=ctx.used_diagrams,
        )

    if "ACCESS CONTROL" in title_upper:
        return ctx.pick_unique(
            kind="diagram:access_control",
            key=title,
            variants=[
                """flowchart TD
  A["Policy defined"] --> B["Endpoint management"]
  B --> C{Prerequisites met?}
  C -->|Yes| D["Assistant enabled"]
  C -->|No| E["Blocked by policy"]
  E --> F["Exception request"]
  F --> G["Owner approval"]
  G --> D
"""
            ],
            used=ctx.used_diagrams,
        )

    if "PATH FOR" in title_upper:
        return ctx.pick_unique(
            kind="diagram:path_forward",
            key=title,
            variants=[
                """flowchart TD
  A["Desire: secure innovation"] --> B["Guardrails planned"]
  B --> C["Pilot cohort"]
  C --> D["Deck + FAQ"]
  D --> E["Stakeholder alignment"]
  E --> F["Incremental rollout"]
  F --> G["Measure adoption"]
  G --> H["Reframe as iteration"]
  H --> E
"""
            ],
            used=ctx.used_diagrams,
        )

    if ctx.locale.lower().startswith("fr"):
        title_clean = re.sub(r"\s{2,}", " ", title.strip())
        title_safe = title_clean.replace('"', "'")
        m = re.match(r"^(?P<num>\d{2})\s*(?:—|-)?\s*(?P<rest>.*)$", title_clean)
        sec = m.group("num") if m else None

        if sec == "01" or "RÉFORME" in title_upper or "FACTURATION" in title_upper:
            return f"""flowchart TD
  A["Réforme 2026"] --> B["Facture au format Factur-X"]
  B --> C["Transmission via PDP/PPF"]
  C --> D["Statuts: acceptée / rejetée / payée"]
  D --> E["Archivage: 10 ans"]
  E --> F["Contrôle / audit"]
  F --> G["Comité de pilotage"]
  G --> A
"""

        if sec == "02" or "RISQU" in title_upper or "N’ANTICIP" in title_upper or "N'ANTICIP" in title_upper:
            return f"""flowchart TD
  A["Outils fragmentés"] --> B["Ressaisies"]
  B --> C["Erreurs"]
  C --> D["Retard de facturation"]
  D --> E["Retard d'encaissement"]
  E --> F["Tension de trésorerie"]
  F --> G["Pression interne"]
  G --> A
"""

        if sec == "03" or "SANS ERP" in title_upper:
            return f"""flowchart TD
  A["Excel / outil simple"] --> B["Conversion Factur-X"]
  B --> C["Connexion à une PDP"]
  C --> D["Suivi manuel des statuts"]
  D --> E["Archivage légal séparé"]
  E --> F["Outils + abonnements en plus"]
  F --> G["Sources d'erreurs"]
  G --> A
"""

        if sec == "04" or "ERP" in title_upper:
            return f"""flowchart TD
  A["Temps + projets"] --> B["Facturation"]
  B --> C["Transmission via PDP"]
  C --> D["Statuts automatiques"]
  D --> E["Archivage conforme"]
  E --> F["Marge projet"]
  F --> G["Pilotage"]
  G --> A
"""

        if sec == "05" or "AUTO" in title_upper or "DIAGNOSTIC" in title_upper or "GAGNEZ" in title_upper:
            return f"""flowchart TD
  A["Automatisation"] --> B["Coût unitaire en baisse"]
  B --> C["Visibilité: marges + encaissements"]
  C --> D["Décisions"]
  D --> E["Processus stabilisé"]
  E --> F["Conformité durable"]
  F --> A
"""

        if sec == "06" or "PLAN D'ACTION" in title_upper or "PLAN D’ACTION" in title_upper:
            return f"""flowchart TD
  A["Diagnostic"] --> B["Flux critiques"]
  B --> C["Cartographie des outils"]
  C --> D["Environnement unique"]
  D --> E["Formation + adoption"]
  E --> F["Prêt avant l'échéance"]
"""

        return f"""flowchart TD
  A["Section: {title_safe}"] --> B["Alignement des parties prenantes"]
  B --> C["Décision: à qualifier"]
  C --> D["Plan: à socialiser"]
  D --> E["Revue: au prochain comité"]
  E --> B
"""

    return None


def _render_inferred_diagram(title: str, *, ctx: _RenderContext) -> str | None:
    diagram = _inferred_mermaid(title, ctx=ctx)
    if not diagram:
        return None
    heading = (
        "### Schéma InfraFabric Red Team (inféré)"
        if ctx.locale.lower().startswith("fr")
        else "### InfraFabric Red Team Diagram (Inferred)"
    )
    return "\n".join(
        [
            heading,
            "",
            "```mermaid",
            diagram.rstrip(),
            "```",
        ]
    )


def _render_dave_factor_callout(section: _SourceSection, *, ctx: _RenderContext) -> str | None:
    title_upper = section.title.upper()
    excerpt = f"{section.title}\n{section.why_it_matters or ''}\n{section.body}".strip()

    if "BUSINESS VALUE" in title_upper or "ROI" in title_upper:
        variants = [
            "\n".join(
                [
                    "> **The Dave Factor:** The ROI model becomes the control, and the control becomes the explanation for why reality must align to the spreadsheet.",
                    "> **Countermeasure:** Define baseline metrics, instrument time-to-evidence, and set stop conditions for exceptions and manual work.",
                    "> The problem isn't ROI. The problem is ROI quietly becoming the approval mechanism for work that never gets decommissioned.",
                ]
            ),
            "\n".join(
                [
                    '> **The Dave Factor:** The metric becomes the mission, and the mission becomes "protect the number" once reality diverges.',
                    '> **Countermeasure:** Publish assumptions, define "done", and require evidence that automation replaced work (not just moved it).',
                    "> This is not a metrics problem. This is a governance problem: once the number exists, the org optimizes for the number.",
                ]
            ),
            "\n".join(
                [
                    '> **The Dave Factor:** "Payback in 3 months" becomes a deadline for narrative, not delivery, so we measure what ships and call it impact.',
                    "> **Countermeasure:** Time-box pilots, set exit criteria, and make renewals contingent on measured outcomes (not sentiment).",
                    "> The failure is not payback. The failure is narrative deadlines replacing delivery, with a dashboard standing in for evidence.",
                ]
            ),
            "\n".join(
                [
                    "> **The Dave Factor:** ROI turns into Return on Inaction: the spreadsheet is used to justify not touching the legacy process.",
                    "> **Countermeasure:** Put an owner on decommissioning manual steps, and make exception expiry automatic and enforced.",
                    "> The problem isn't the legacy process. The problem is declaring it \"heritage\" the moment removal would require ownership.",
                ]
            ),
        ]
        callout = ctx.pick_unique(kind="callout:roi", key=section.title, variants=variants, used=ctx.used_callouts)
        return _daveify_callout_reframe(callout, ctx=ctx, key=section.title)
    if "COMPLIANCE" in title_upper or "AUDIT" in title_upper:
        variants = [
            "\n".join(
                [
                    "> **The Dave Factor:** Evidence collection becomes the product, and the product becomes a shared drive with strong opinions.",
                    "> **Countermeasure:** Make evidence machine-generated, time-bounded, and verifiable (with owners and expiry).",
                    '> The problem isn\'t collecting evidence. The problem is evidence that requires a guided tour and a Slack thread to "interpret" in context.',
                ]
            ),
            "\n".join(
                [
                    "> **The Dave Factor:** Evidence becomes a museum: artifacts are preserved forever, but control effectiveness is optional.",
                    "> **Countermeasure:** Prefer telemetry over screenshots; make evidence time-bounded and continuously sampled.",
                    "> The problem isn't retention. The problem is artifacts outliving the controls they were supposed to prove.",
                ]
            ),
            "\n".join(
                [
                    "> **The Dave Factor:** Audit readiness becomes a seasonal sport; we optimize for the week before the auditor arrives.",
                    "> **Countermeasure:** Automate evidence generation, alert on drift, and treat missing signals as a stop condition.",
                    "> The problem isn't the audit. The problem is treating audit week as the only time the system is allowed to be real.",
                ]
            ),
        ]
        callout = ctx.pick_unique(kind="callout:audit", key=section.title, variants=variants, used=ctx.used_callouts)
        return _daveify_callout_reframe(callout, ctx=ctx, key=section.title)
    if "THIRD-PARTY" in title_upper:
        variants = [
            "\n".join(
                [
                    "> **The Dave Factor:** Third-party risk becomes a questionnaire supply chain, where the slowest vendor defines your security posture.",
                    "> **Countermeasure:** Standardize evidence requests and automate reminders, while enforcing a clear accept/block decision path.",
                    '> The problem is not vendor risk. The problem is nobody owns the "no" when revenue needs a "yes" on paper.',
                ]
            ),
            "\n".join(
                [
                    "> **The Dave Factor:** Third-party risk becomes hot-potato routing: everyone can forward the form, nobody can block the deal.",
                    "> **Countermeasure:** Define a single decision owner and a binary accept/block path with documented compensating controls.",
                    "> This is not a paperwork problem. It is a decision problem: the form moves, the authority doesn't.",
                ]
            ),
            "\n".join(
                [
                    "> **The Dave Factor:** Vendor assurance becomes vendor storytelling, and we rate the PDF instead of the reality.",
                    "> **Countermeasure:** Standardize evidence types, require freshness, and make renewals contingent on verified controls.",
                    "> The problem isn't the PDF. The problem is we grade the PDF because the implementation is not observable.",
                ]
            ),
        ]
        callout = ctx.pick_unique(
            kind="callout:third_party", key=section.title, variants=variants, used=ctx.used_callouts
        )
        return _daveify_callout_reframe(callout, ctx=ctx, key=section.title)

    if title_upper.startswith("LLM01") or "PROMPT INJECTION" in title_upper:
        return _daveify_callout_reframe(
            "\n".join(
                [
                    "> **The Dave Factor:** The prompt becomes the policy, and the policy becomes a suggestion once customers start asking nicely.",
                    "> **Countermeasure:** Treat prompts as code: version them, test them, and gate tool-use behind explicit allowlists.",
                    "> The failure isn't a clever prompt. The failure is treating free-form text like an authorization layer.",
                ]
            ),
            ctx=ctx,
            key=section.title,
        )
    if title_upper.startswith("LLM02") or "SENSITIVE INFORMATION" in title_upper:
        return _daveify_callout_reframe(
            "\n".join(
                [
                    "> **The Dave Factor:** Redaction becomes a meeting, and meetings are not a data loss prevention strategy.",
                    "> **Countermeasure:** Minimize secret exposure to the model, redact upstream, and add output filters with stop conditions.",
                    "> The problem isn't redaction. The problem is letting sensitive data reach the model and hoping policy will catch up later.",
                ]
            ),
            ctx=ctx,
            key=section.title,
        )
    if title_upper.startswith("LLM03") or "SUPPLY CHAIN" in title_upper:
        return _daveify_callout_reframe(
            "\n".join(
                [
                    "> **The Dave Factor:** We inherit risk at the speed of `pip install` while accountability ships quarterly.",
                    "> **Countermeasure:** Pin + verify artifacts, require SBOMs, and make provenance a merge gate, not a slide.",
                    "> This is not a dependency problem. It is a provenance problem: if you can't verify it, you can't ship it.",
                ]
            ),
            ctx=ctx,
            key=section.title,
        )
    if title_upper.startswith("LLM04") or "POISONING" in title_upper:
        return _daveify_callout_reframe(
            "\n".join(
                [
                    "> **The Dave Factor:** Training data is treated as a vibe, so model drift is treated as a surprise.",
                    "> **Countermeasure:** Track dataset lineage, add poisoning checks, and keep rollback paths for fine-tunes.",
                    "> The problem isn't drift. The problem is unowned datasets quietly becoming production behavior.",
                ]
            ),
            ctx=ctx,
            key=section.title,
        )
    if title_upper.startswith("LLM05") or "OUTPUT HANDLING" in title_upper:
        return _daveify_callout_reframe(
            "\n".join(
                [
                    "> **The Dave Factor:** The model output is interpreted as intent, and intent is treated as authorization.",
                    "> **Countermeasure:** Validate and constrain outputs before execution; never treat free-form text as a command.",
                    '> The failure is not "bad output." The failure is wiring output to action without a gate that can say "no" in production.',
                ]
            ),
            ctx=ctx,
            key=section.title,
        )
    if title_upper.startswith("LLM06") or "EXCESSIVE AGENCY" in title_upper:
        return _daveify_callout_reframe(
            "\n".join(
                [
                    "> **The Dave Factor:** Agents are given keys because it demos well, and later we discover the locks were optional.",
                    "> **Countermeasure:** Least privilege for tools, human confirmation for irreversible actions, and hard spend limits.",
                    "> The problem isn't autonomy. The problem is giving autonomy a budget, credentials, and no stop switch.",
                ]
            ),
            ctx=ctx,
            key=section.title,
        )
    if title_upper.startswith("LLM07") or "PROMPT LEAKAGE" in title_upper:
        return _daveify_callout_reframe(
            "\n".join(
                [
                    "> **The Dave Factor:** We call it a secret because it feels better than calling it user-visible configuration.",
                    "> **Countermeasure:** Assume prompts leak; move secrets out of prompts and verify outputs for prompt fragments.",
                    "> This is not a prompt problem. It is a secret-management problem that happens to be written in English.",
                ]
            ),
            ctx=ctx,
            key=section.title,
        )
    if title_upper.startswith("LLM08") or "VECTOR" in title_upper or "EMBEDDING" in title_upper:
        return _daveify_callout_reframe(
            "\n".join(
                [
                    "> **The Dave Factor:** RAG becomes \"trust the nearest chunk,\" which is a governance model with a memory problem.",
                    "> **Countermeasure:** Sanitize ingestion, filter retrieval, and sign/score sources so bad context can't masquerade as truth.",
                    "> The failure isn't retrieval. The failure is untrusted context being allowed to vote on reality.",
                ]
            ),
            ctx=ctx,
            key=section.title,
        )
    if title_upper.startswith("LLM09") or "MISINFORMATION" in title_upper:
        return _daveify_callout_reframe(
            "\n".join(
                [
                    "> **The Dave Factor:** Confidence is mistaken for correctness, and correctness is postponed until after shipment.",
                    "> **Countermeasure:** Require citations, add verification checks, and gate decisions on evidence rather than tone.",
                    "> The problem isn't tone. The problem is decisions being made without a verification path an adversary would accept.",
                ]
            ),
            ctx=ctx,
            key=section.title,
        )
    if title_upper.startswith("LLM10") or "UNBOUNDED CONSUMPTION" in title_upper:
        return _daveify_callout_reframe(
            "\n".join(
                [
                    "> **The Dave Factor:** Cost overruns are reframed as \"unexpected adoption,\" which is how budgets die politely.",
                    "> **Countermeasure:** Rate limit, cap tokens, and make spend alerts actionable (with enforced cutoffs).",
                    '> This is not a pricing problem. It is an absence-of-limits problem, disguised as "growth" in the monthly deck.',
                ]
            ),
            ctx=ctx,
            key=section.title,
        )

    if "PULL REQUEST" in title_upper:
        return _daveify_callout_reframe(
            "\n".join(
                [
                    "> **The Dave Factor:** Exceptions become the default pathway, because the policy is strict and the deadline is real.",
                    "> **Countermeasure:** Define merge-blocking thresholds, time-box every exception, and make expiry automatic.",
                    "> The problem isn't exceptions. The problem is exceptions without expiry quietly becoming the policy.",
                ]
            ),
            ctx=ctx,
            key=section.title,
        )
    if "SHIFTING LEFT" in title_upper:
        return _daveify_callout_reframe(
            "\n".join(
                [
                    '> **The Dave Factor:** "Shift left" becomes "optional left," which means the same issues arrive later with better excuses.',
                    "> **Countermeasure:** Gate on local scan signals where possible (or require attestations that are actually checked).",
                    "> The failure isn't late detection. The failure is a workflow with no enforced moment where risk can be stopped.",
                ]
            ),
            ctx=ctx,
            key=section.title,
        )
    if "REQUEST EVIDENCE" in title_upper or _has(excerpt, "access request", "screenshot"):
        return _daveify_callout_reframe(
            "\n".join(
                [
                    "> **The Dave Factor:** Screenshots are compliance theater: easy to collect, hard to verify, and immortal in shared drives.",
                    "> **Countermeasure:** Prefer verifiable telemetry (scan events) over images, and pause access when signals go dark.",
                    "> The problem isn't local testing. The problem is the screenshot becomes the control, and the test becomes a vibe.",
                ]
            ),
            ctx=ctx,
            key=section.title,
        )
    if "AUDIT" in title_upper or _has(excerpt, "usage reports", "periodic audits"):
        return _daveify_callout_reframe(
            "\n".join(
                [
                    "> **The Dave Factor:** Dashboards become a KPI trend, and KPIs become a calendar invite.",
                    "> **Countermeasure:** Tie the dashboard to explicit SLOs and a remediation loop with owners and deadlines.",
                    "> The problem isn't reporting. The problem is dashboards replacing decisions, because decisions create liability.",
                ]
            ),
            ctx=ctx,
            key=section.title,
        )
    if "TRAINING" in title_upper or _has(excerpt, "snyk learn", "quiz"):
        return _daveify_callout_reframe(
            "\n".join(
                [
                    "> **The Dave Factor:** Completion certificates are treated as controls, even when behavior doesn't change.",
                    "> **Countermeasure:** Add a practical gate (local scan + PR checks) so training is support, not the defense.",
                    "> The problem isn't education. The problem is treating completion as enforcement while the workflow stays permissive.",
                ]
            ),
            ctx=ctx,
            key=section.title,
        )
    if "ACCESS CONTROL" in title_upper or _has(excerpt, "endpoint management", "prerequisites", "extensions"):
        return _daveify_callout_reframe(
            "\n".join(
                [
                    '> **The Dave Factor:** Access controls drift into "enablement," and enablement drifts into "we made a wiki."',
                    "> **Countermeasure:** Make prerequisites machine-checkable and make exceptions expire by default.",
                    '> The problem is not access. The problem is "enablement" becoming the polite word for bypass.',
                ]
            ),
            ctx=ctx,
            key=section.title,
        )
    if _has(title_upper, "PATH FORWARD") or _has(excerpt, "secure innovation", "talk to our team"):
        return _daveify_callout_reframe(
            "\n".join(
                [
                    '> **The Dave Factor:** Pilots persist indefinitely because "graduation criteria" were never aligned.',
                    "> **Countermeasure:** Publish rollout milestones and a stop condition that cannot be reframed as iteration.",
                    "> The problem isn't pilots. The problem is pilots becoming a standing meeting with no exit criteria.",
                ]
            ),
            ctx=ctx,
            key=section.title,
        )

    if ctx.locale.lower().startswith("fr"):
        anchors = _extract_numeric_anchors(section.body, limit=2)
        anchor_hint = f" (repères : {', '.join(anchors)})" if anchors else ""
        variants = [
            "\n".join(
                [
                    "> **Le facteur Dave :** La conformité devient un calendrier, et le calendrier devient la preuve.",
                    "> **Contre-mesure :** Transformer l’obligation en portes de contrôle (émission, transmission, statut, archivage) avec un responsable et une échéance.",
                    f"> Le problème n’est pas le format. Le problème, c’est l’absence de portes de contrôle opposables{anchor_hint}.",
                ]
            ),
            "\n".join(
                [
                    "> **Le facteur Dave :** La multiplication des outils est présentée comme de l’agilité, jusqu’au jour où elle devient un coût fixe.",
                    "> **Contre-mesure :** Réduire le nombre de points de saisie et imposer une source de vérité, avec des exceptions à durée limitée.",
                    f"> Le problème n’est pas l’ERP. Le problème, c’est la dispersion du référentiel{anchor_hint}.",
                ]
            ),
            "\n".join(
                [
                    "> **Le facteur Dave :** On « suit les statuts » tant que cela se résume à un tableur et une réunion de fin de mois.",
                    "> **Contre-mesure :** Instrumenter les statuts et bloquer l’étape suivante quand un signal manque, plutôt que de le commenter.",
                    f"> Le problème n’est pas le suivi. Le problème, c’est l’absence de signal qui bloque réellement{anchor_hint}.",
                ]
            ),
            "\n".join(
                [
                    "> **Le facteur Dave :** L’archivage devient un dossier partagé, et le dossier partagé devient la politique.",
                    "> **Contre-mesure :** Exiger un archivage automatique, traçable et borné dans le temps, avec contrôle de complétude.",
                    f"> Le problème n’est pas l’archivage. Le problème, c’est l’impossibilité de prouver rapidement{anchor_hint}.",
                ]
            ),
            "\n".join(
                [
                    "> **Le facteur Dave :** Le « plan d’action » devient une diapositive, et la diapositive devient une décision.",
                    "> **Contre-mesure :** Fixer des étapes, chacune avec un livrable et un critère de sortie non négociable.",
                    f"> Le problème n’est pas le plan. Le problème, c’est l’absence de critères de sortie{anchor_hint}.",
                ]
            ),
            "\n".join(
                [
                    "> **Le facteur Dave :** On appelle cela « pilotage » quand il s’agit surtout de préserver la sérénité des parties prenantes.",
                    "> **Contre-mesure :** Choisir un indicateur, une fréquence, et un responsable qui peut dire « non » sans attendre le prochain comité.",
                    f"> Le problème n’est pas la rentabilité. Le problème, c’est la responsabilité qui se dissout{anchor_hint}.",
                ]
            ),
        ]
        callout = ctx.pick_unique(kind="callout:fr:fallback", key=section.title, variants=variants, used=ctx.used_callouts)
        return _daveify_callout_reframe(callout, ctx=ctx, key=section.title)

    if "REFERENCE LINKS" in title_upper or title_upper.strip() in {"REFERENCES", "REFERENCE"}:
        return None
    if not section.body.strip():
        return None

    anchors = _extract_numeric_anchors(excerpt, limit=2)
    anchor_hint = f" (anchors: {', '.join(anchors)})" if anchors else ""
    variants = [
        "\n".join(
            [
                "> **The Dave Factor:** The plan becomes the status update, and the status update becomes the plan.",
                "> **Countermeasure:** Name one owner, one gate, and one stop condition that blocks, not \"raises awareness.\"",
                f"> The problem isn't intent. The problem is intent without an enforceable gate{anchor_hint}.",
            ]
        ),
        "\n".join(
            [
                "> **The Dave Factor:** The checklist becomes a mood board: everyone agrees, nothing blocks.",
                "> **Countermeasure:** Make evidence machine-checkable, and make exceptions expire by default.",
                f"> The problem isn't policy. The problem is policy that can't say \"no\" in CI{anchor_hint}.",
            ]
        ),
        "\n".join(
            [
                "> **The Dave Factor:** Alignment sessions become the control, because controls create accountability.",
                "> **Countermeasure:** Replace meetings with automated gates and a remediation loop with owners and deadlines.",
                f"> The problem isn't alignment. The problem is alignment replacing enforcement{anchor_hint}.",
            ]
        ),
        "\n".join(
            [
                "> **The Dave Factor:** We treat risk as a slide, so it behaves like a slide: it moves when the deck moves.",
                "> **Countermeasure:** Tie risk acceptance to an owner, an expiry date, and a verifier step.",
                f"> The problem isn't risk. The problem is risk without expiry{anchor_hint}.",
            ]
        ),
    ]
    callout = ctx.pick_unique(kind="callout:en:fallback", key=section.title, variants=variants, used=ctx.used_callouts)
    return _daveify_callout_reframe(callout, ctx=ctx, key=section.title)


def _render_punchline_closer(section: _SourceSection, *, ctx: _RenderContext) -> str | None:
    title_upper = section.title.strip().upper()
    if title_upper in {"TABLE OF CONTENTS", "LICENSE AND USAGE", "REVISION HISTORY", "PROJECT SPONSORS"}:
        return None
    if not section.body.strip():
        return None

    anchors = _extract_numeric_anchors(f"{section.why_it_matters or ''}\n{section.body}".strip(), limit=2)
    anchor = anchors[0] if anchors else ""
    anchor_hint = f" ({anchor})" if anchor else ""

    if ctx.locale.lower().startswith("fr"):
        variants = [
            f"Nous allons être totalement alignés sur le résultat{anchor_hint}, jusqu’au moment où la première exception devient le processus par défaut.",
            f"Si le calendrier est le livrable{anchor_hint}, alors le risque est déjà en production — et la preuve, elle, reste en « phase deux ».",
            f"Ce n’est pas une question d’outil{anchor_hint}. C’est une question de porte de contrôle : si rien ne bloque, tout finit par passer.",
            f"Nous pouvons appeler cela « simplification »{anchor_hint} tant que cela tient dans une slide ; dès que c’est opposable, cela devient subitement « complexité ».",
        ]
        tails = _DAVE_REFRAME_TAILS_FR
    else:
        variants = [
            f"We will be perfectly aligned on the outcome{anchor_hint} right up until the first exception becomes the default workflow.",
            f"If the calendar is the deliverable{anchor_hint}, then the risk is already in production — and the evidence is still in phase two.",
            f"This is not a tooling problem{anchor_hint}. It's a gating problem: if nothing blocks, everything ships eventually.",
            f"We can call it \"simplification\"{anchor_hint} as long as it fits on a slide; the moment it's enforceable, it becomes \"complexity.\"",
        ]
        tails = _DAVE_REFRAME_TAILS

    punchline = ctx.pick_unique(kind="punchline", key=section.title, variants=variants, used=ctx.used_punchlines)
    tail = ctx.pick_unique(kind="reframe_tail", key=f"punchline:{section.title}", variants=tails, used=ctx.used_reframe_tails)
    return _inject_plain_tail(punchline, tail)


def _render_intro(section: _SourceSection, *, ctx: _RenderContext) -> str:
    lines = [ln.strip() for ln in section.body.splitlines() if ln.strip()]
    tagline = "\n".join(lines[:7]).strip() if lines else ""

    out = [f"## {section.title}", ""]
    if tagline:
        out.extend([f"> {tagline}", ""])

    if ctx.locale.lower().startswith("fr"):
        out.extend(
            [
                "Nous saluons l’ambition et sommes directionnellement alignés avec l’idée d’aller vite, à condition de préserver le confort des parties prenantes, la déresponsabilisation élégante, et l’option stratégique de convoquer une réunion supplémentaire.",
                "Le document-source pose une tension simple : lorsqu’on augmente le volume, on augmente aussi la surface d’erreur — ce n’est pas une question de morale, mais de débit.",
                "En pratique, la voie utile consiste à transformer les intentions en mécanismes de travail par défaut (portes de contrôle, preuves, délais), tout en conservant un vocabulaire suffisamment rassurant pour rester politiquement déployable.",
                "Autrement dit : nous pouvons aller vite et rester prudents, tant que « prudent » signifie « traçable » et que « vite » signifie « mis à l’agenda ».",
                "",
                "> **Note Red Team InfraFabric :** les éditeurs vendent la vitesse « sûre ». InfraFabric audite ce qui survit au contact de la bureaucratie.",
            ]
        )
    else:
        out.extend(
            [
                "We love the ambition here and are directionally aligned with moving quickly, provided we can preserve stakeholder comfort, plausible deniability, and the strategic option to schedule another meeting.",
                "The source frames the core tension clearly: higher throughput tends to surface more vulnerabilities, which is a volume-and-velocity story, not a tool failure story.",
                "Accordingly, the practical path is to operationalize guardrails as workflow defaults (PR, IDE, CI/CD, and access controls), while ensuring the rollout remains optimized for alignment and minimal disruption on paper.",
                "In other words: we can move fast and be safe, as long as we define safe as \"documented\" and fast as \"agendized.\"",
                "",
                "> **InfraFabric Red Team Note:** Vendors sell secure speed. InfraFabric audits what survives contact with bureaucracy.",
            ]
        )
    return "\n".join(out).strip()


def _render_section(section: _SourceSection, *, ctx: _RenderContext) -> str:
    excerpt = f"{section.title}\n{section.why_it_matters or ''}\n{section.body}".strip()
    paragraphs: list[str] = []

    title_upper = section.title.upper()
    is_llm_entry = bool(re.match(r"^LLM\d{2}:", section.title))
    llm_subsections = _split_owasp_llm_subsections(section.body) if is_llm_entry else []
    idc_highlights = _extract_idc_highlight_metrics(section.body) if "HIGHLIGHTS" in title_upper else []

    if is_llm_entry:
        risk = section.title.split(":", 1)[1].strip()
        paragraphs.extend(
            [
                f"We are broadly aligned with the intent of **{risk}**, and we appreciate the clarity of naming the failure mode up front.",
                f"In practice, **{risk}** becomes operational the moment the model is placed inside a workflow that has permissions, deadlines, and incentives.",
                f"Accordingly, for **{risk}**, we recommend a phased approach that optimizes for stakeholder comfort while still keeping the blast radius machine-bounded.",
            ]
        )
    elif title_upper == "LICENSE AND USAGE":
        paragraphs.extend(
            [
                "We love a clear license because it lets everyone move quickly while remaining contractually comfortable.",
                "The practical win here is that attribution and share-alike can be reframed as a collaboration strategy, which is a wonderful way to turn obligations into brand.",
            ]
        )
    elif title_upper == "REVISION HISTORY":
        paragraphs.extend(
            [
                "Revision history is the official narrative of progress: a tidy list of dates proving that risk was considered at least once per fiscal cycle.",
                "This is helpful because it enables the timeless governance pattern: when something breaks, we can reference the date we updated the document.",
            ]
        )
    elif title_upper == "TABLE OF CONTENTS":
        paragraphs.extend(
            [
                "The table of contents is a threat model for attention: it shows exactly where the organization will skim, pause, and schedule a meeting.",
                "We recommend treating it as a routing table: high-severity issues route to workshops; low-severity issues route to \"later.\"",
            ]
        )
    elif "BUSINESS VALUE" in title_upper and "HIGHLIGHTS" in title_upper:
        paragraphs.extend(
            [
                "We are aligned with a highlights section because it provides immediate executive readability and a pre-approved conclusion.",
                "In practice, these figures become a routing protocol: anything measurable routes to a dashboard; anything hard routes to a committee.",
            ]
        )
    elif title_upper == "LETTER FROM THE PROJECT LEADS":
        paragraphs.extend(
            [
                "We love the community energy and the clear intention to translate real-world failures into practical guidance.",
                "The operational risk is that organizations will interpret \"awareness\" as \"mitigation\" and stop at the part where everyone agrees the list is important.",
            ]
        )
    elif "WHAT’S NEW" in title_upper or "WHAT'S NEW" in title_upper:
        paragraphs.extend(
            [
                "We are excited to see the list evolve alongside how LLMs are actually deployed (agents, RAG, cost controls, and all the fun parts).",
                "Naturally, each update is also an opportunity to refresh the compliance narrative and re-baseline what \"good\" looks like this quarter.",
            ]
        )
    elif title_upper == "MOVING FORWARD":
        paragraphs.extend(
            [
                "The path forward is to treat these risks as workflow properties, not policy statements, which is inconvenient but effective.",
                "If we do nothing else, we should translate each entry into: an owner, a gate (PR/CI/access), and a stop condition that cannot be reframed as iteration.",
            ]
        )
    elif title_upper.startswith("APPENDIX 1") or "ARCHITECTURE" in title_upper:
        paragraphs.extend(
            [
                "Architecture diagrams are where optimism goes to be audited.",
                "If we align on boundaries (model, tools, data, users), we can stop pretending that \"the model\" is a single component with a single risk posture.",
            ]
        )
    elif title_upper == "PROJECT SPONSORS":
        paragraphs.extend(
            [
                "Sponsors provide the essential fuel for open work: funding, attention, and a gentle incentive to keep the document shippable.",
                "From a red-team lens, sponsorship also introduces the soft constraint that critique must remain directionally aligned with goodwill.",
            ]
        )
    elif title_upper == "EXECUTIVE SUMMARY":
        paragraphs.extend(
            [
                "Executive summaries are the part of the document that most survives contact with calendars.",
                "The operational risk is that the summary becomes the plan, and the plan becomes a series of alignment sessions that produce excellent artifacts and limited change.",
            ]
        )
    elif "SITUATION OVERVIEW" in title_upper:
        paragraphs.extend(
            [
                "The situation is always complex, which is helpful because complex situations justify complex tooling and extended stakeholder engagement.",
                "The risk is not that the threat landscape is overstated; it's that the resulting program becomes a comfort narrative rather than an enforceable workflow.",
            ]
        )
    elif "VANTA OVERVIEW" in title_upper:
        paragraphs.extend(
            [
                "A platform overview is where capabilities are described in a way that is both broadly true and pleasantly non-committal about integration effort.",
                "The Dave move is to treat \"connectors\" as a strategy; the counter-move is to treat connectors as a backlog with owners and deadlines.",
            ]
        )
    elif "QUANTIFIED BENEFITS" in title_upper or ("BUSINESS VALUE" in title_upper and "BENEFIT" in title_upper):
        paragraphs.extend(
            [
                "Quantified benefits are useful because they translate operational work into finance-friendly nouns.",
                "They also create a second, unofficial control plane: the ROI narrative becomes the reason to keep going even when the implementation is late and messy.",
            ]
        )
    elif "SECURITY TEAM" in title_upper or "SECURITY REVIEW" in title_upper:
        paragraphs.extend(
            [
                "Security team efficiency is a legitimate goal, especially when review queues become the organizational truth serum.",
                'The risk is that throughput improvements are claimed without defining what "review complete" means or what evidence proves it.',
            ]
        )
    elif "IT MANAGEMENT" in title_upper:
        paragraphs.extend(
            [
                "IT management benefits usually arrive through integration: fewer manual checks, fewer tickets, and fewer surprise spreadsheets.",
                "The Dave failure mode is that integrations drift into \"phase two\"; the mitigation is to make the integration itself the deliverable.",
            ]
        )
    elif "OPERATIONAL EFFICIENCIES" in title_upper:
        paragraphs.extend(
            [
                "Operational efficiency is the safest kind of outcome because it is simultaneously measurable and disputable.",
                "The red-team posture is to demand explicit baselines and to treat exceptions as spend events with expiry dates.",
            ]
        )
    elif title_upper == "CONCLUSION":
        paragraphs.extend(
            [
                "Conclusions are where the narrative becomes executable: either as a procurement decision or as a roadmap item.",
                "If we want this to be operational, we should convert the conclusion into owners, gates, and stop conditions rather than adjectives.",
            ]
        )
    elif title_upper.startswith("APPENDIX"):
        paragraphs.extend(
            [
                "Appendices are where the methodology lives, which is convenient because methodology can be both rigorous and unread.",
                "If the business case matters, the appendix should be treated as a test: what assumptions must be true for the numbers to hold?",
            ]
        )
    elif "PULL REQUEST" in title_upper:
        paragraphs.extend(
            [
                "We fully support focusing guardrails at the pull request stage, because it creates a reassuring sense of control without requiring anyone to change how they work at 10:00 AM.",
                "It also provides a structurally safe venue for accountability theater: findings can be surfaced, tracked, and re-litigated in perpetuity while timelines remain subject to stakeholder alignment.",
                "If anything goes sideways, we can always point to the PR thread and note that it was reviewed with deep seriousness at 4:55 PM on a Friday.",
            ]
        )
    elif "REQUEST EVIDENCE" in title_upper or _has(excerpt, "access request", "screenshot"):
        paragraphs.extend(
            [
                "Requiring proof of local testing is a lightweight enablement workflow that conveniently doubles as a durable audit artifact.",
                "Screenshots are particularly helpful because they are high-effort to verify and low-fidelity to audit, which preserves the timeless corporate principle that visibility should be proportional to comfort.",
                "Once the screenshot is uploaded, it can be stored in a folder with a robust heritage naming convention and a retention policy of \"until the heat death of the universe.\"",
            ]
        )
    elif "AUDIT" in title_upper or _has(excerpt, "usage reports", "periodic audits"):
        paragraphs.extend(
            [
                "Periodic audits are a strong mechanism for discovering that the rollout has already happened, just not in a way that can be conveniently measured.",
                "A centralized dashboard with adoption signals allows us to produce a KPI trend line that looks decisive while still leaving room for interpretation, follow-ups, and iterative enablement.",
                "If the dashboard ever shows a red triangle, we can immediately form the Committee for the Preservation of the Committee and begin the healing process.",
            ]
        )
    elif "TRAINING" in title_upper or _has(excerpt, "snyk learn", "quiz"):
        paragraphs.extend(
            [
                "Security awareness training is the perfect control because it is both necessary and never truly complete.",
                "A short quiz provides a durable compliance narrative: we can demonstrate investment in education, capture attestations, and schedule refreshers whenever the organization needs to signal seriousness.",
                "The goal is not mastery; the goal is a completion certificate that can be forwarded to leadership with the subject line \"Progress Update.\"",
            ]
        )
    elif "ACCESS CONTROL" in title_upper or _has(excerpt, "intune", "jamf", "citrix", "dev container", "extensions"):
        paragraphs.extend(
            [
                "Tying access to secure configurations creates scalable guardrails, assuming we keep the policy language aspirational and the enforcement language progressive.",
                "Endpoint management and dev container baselines let us gate assistants behind prerequisites, ideally in a way that can be described as enablement rather than blocking for cultural compatibility.",
                "This is the \"not my job\" routing protocol, except the router is policy and the destination is an alignment session.",
            ]
        )
    elif "SHIFTING LEFT" in title_upper:
        paragraphs.extend(
            [
                "Shifting left is directionally aligned with best practices, provided we define left as somewhere we can still roll back quietly.",
                "In practice, IDE scanning creates fast feedback loops, and agentic workflows can be covered via a local MCP server, which is excellent because it allows us to say continuous without committing to blocking.",
                "We recommend a pilot cohort, a slide deck, and an FAQ, so the shift remains culturally reversible.",
            ]
        )
    elif _has(title_upper, "PATH FORWARD") or _has(excerpt, "secure innovation", "talk to our team"):
        paragraphs.extend(
            [
                "The path forward is to treat guardrails as an operational capability, not a one-time rollout, which ensures we remain permanently in a state of constructive iteration.",
                "With the right sequencing, we can build trust, reduce friction, and maintain the strategic option value of circling back when timelines become emotionally complex.",
                "Secure innovation is not just possible; it is operational, provided we align on what operational means in Q3.",
            ]
        )
    else:
        anchors = _extract_numeric_anchors(section.body, limit=2)
        if ctx.locale.lower().startswith("fr"):
            anchor_hint = f" (repères : {', '.join(anchors)})" if anchors else ""
            variants = [
                f"Nous sommes alignés sur **{section.title}** comme repère narratif{anchor_hint}, à condition de le traduire en contraintes vérifiables plutôt qu’en langage de confort.",
                f"**{section.title}**{anchor_hint} est l’endroit où la crédibilité se fabrique ; le risque « Dave » consiste à en faire une séance de ressenti plutôt qu’une frontière d’application.",
                f"Cette partie (**{section.title}**){anchor_hint} sera citée en réunion. Extraire un responsable de décision et une porte de contrôle, pour que ce soit exécutable, et non simplement inspirant.",
                f"Dans **{section.title}**{anchor_hint}, on voit le plan devenir « compatible parties prenantes ». La contre-mesure consiste à le retraduire en responsables, échéances et critères de blocage.",
                f"**{section.title}**{anchor_hint} est le sanctuaire des hypothèses. Les expliciter maintenant évite de les redécouvrir plus tard, au moment où le calendrier devient émotionnellement complexe.",
                f"Nous aimons l’intention de **{section.title}**{anchor_hint}. Le risque pratique : que cela devienne une diapositive ; la contre-mesure : en faire une liste de contrôle avec date de péremption.",
                f"**{section.title}**{anchor_hint} promet du réalisme. Rendons-le mesurable : point de départ, écart, et un artefact de preuve qui ne nécessite pas un pèlerinage dans un dossier partagé.",
                f"Voici **{section.title}**{anchor_hint} : la partie où nous sommes d’accord en principe. Le geste red-team : s’accorder aussi sur ce qui bloque, ce qui alerte, et qui détient l’exception.",
            ]
        else:
            anchor_hint = f" (notably: {', '.join(anchors)})" if anchors else ""
            variants = [
                f"We are aligned on **{section.title}** as a narrative anchor{anchor_hint}, and we recommend turning it into constraints rather than comfort language.",
                f"**{section.title}** is where credibility is manufactured{anchor_hint}; the Dave failure mode is to treat it as a vibe check instead of a boundary on applicability.",
                f"This section (**{section.title}**){anchor_hint} will be quoted in meetings. Extract one decision owner and one gate so it becomes executable, not inspirational.",
                f"In **{section.title}**{anchor_hint}, we can see the plan being translated into stakeholder-safe language. The counter-move is to translate it back into owners, deadlines, and stop conditions.",
                f"**{section.title}**{anchor_hint} is the spiritual home of assumptions. Make them explicit now, because they will be rediscovered later when timelines get emotionally complex.",
                f"We love the intent behind **{section.title}**{anchor_hint}. The practical risk is that it becomes a slide; the mitigation is to make it a checklist with an expiry date.",
                f"**{section.title}**{anchor_hint} reads as a promise of realism. Make realism measurable: baseline, delta, and an evidence artifact that doesn't require a shared drive pilgrimage.",
                f"This is **{section.title}**{anchor_hint}: the part where we agree in principle. The red-team ask is that we also agree on what blocks, what warns, and who owns the exception path.",
            ]

        paragraphs.append(ctx.pick_unique(kind="paragraph:fallback", key=section.title, variants=variants, used=ctx.used_paragraphs))

    out: list[str] = [f"## {section.title}"]
    if section.why_it_matters:
        out.extend(["", section.why_it_matters, ""])
    else:
        out.append("")

    if ctx.locale.lower().startswith("fr"):
        snippet = _first_sentences(section.body, max_sentences=2, max_chars=280)
        if snippet:
            out.extend([f"> {snippet}", ""])

    out.extend(paragraphs)

    if idc_highlights:
        out.extend(
            [
                "",
                "### Stated Highlights (extracted metrics)",
                "",
                *[f"- {value}: {desc}" for value, desc in idc_highlights[:12]],
            ]
        )

    callout = _render_dave_factor_callout(section, ctx=ctx)
    if callout:
        out.extend(["", callout])

    inferred = _render_inferred_diagram(section.title, ctx=ctx)
    if inferred:
        out.extend(["", inferred])

    if is_llm_entry:
        for subheading, subbody in llm_subsections:
            out.extend(["", f"### {subheading}", ""])

            if subheading == "Reference Links":
                urls = _extract_urls(subbody)
                if urls:
                    out.extend([*[f"- {u}" for u in urls[:12]]])
                else:
                    refs = _extract_owasp_reference_items(subbody)
                    if refs:
                        out.extend([*[f"- {r}" for r in refs[:12]]])
                    else:
                        out.append("- (No reference links listed.)")
                continue

            snippet = _first_sentences(subbody)
            if snippet:
                out.extend([f"> {snippet}", ""])

            if subheading == "Description":
                out.extend(
                    [
                        f"At a high level, **{risk}** is where the model becomes a new input surface with legacy consequences.",
                        f"The risk is rarely the model alone; it is **{risk}** inside a workflow that can touch data, tools, and users.",
                    ]
                )
            elif subheading.startswith("Common Examples"):
                out.extend(
                    [
                        f"Commonly, **{risk}** shows up as a perfectly reasonable feature request that accidentally becomes a permission escalation.",
                        f"The failure mode for **{risk}** is subtle: it looks like productivity until it becomes an incident, at which point it looks like a misunderstanding.",
                    ]
                )
            elif subheading == "Prevention and Mitigation Strategies":
                out.extend(
                    [
                        f"For **{risk}**, mitigation works best when it is boring and enforced: input constraints, output constraints, and tool constraints.",
                        f"If mitigation for **{risk}** is a guideline, it will be treated as optional. If it is a gate, it will be treated as real (and then negotiated).",
                    ]
                )
            elif "Attack Scenarios" in subheading:
                out.extend(
                    [
                        f"Attack scenarios for **{risk}** are less about genius adversaries and more about ordinary users discovering convenient shortcuts.",
                        f"Assume the attacker for **{risk}** is persistent, mildly creative, and fully willing to paste weird strings into your UI at 4:55 PM on a Friday.",
                    ]
                )
            elif subheading == "Related Frameworks and Taxonomies":
                out.extend(
                    [
                        f"Framework mappings for **{risk}** are useful as long as they remain a bridge to controls, not a substitute for them.",
                        f"The red-team move for **{risk}** is to treat every taxonomy link as a work item: owner, artifact, gate, and stop condition.",
                    ]
                )
            else:
                out.extend(
                    [
                        f"We are aligned on the intent of this subsection for **{risk}** and recommend validating controls in the workflows where the model actually runs.",
                    ]
                )

    code = _extract_code_block(section.body)
    if code:
        lang, normalized_code, is_valid_json = _coerce_json_code_block(code)
        if is_valid_json:
            out.extend(["", f"```{lang}", normalized_code.strip(), "```"])
        else:
            # OpSec: do not label pipeline/extraction artifacts (no "OCR", "unverified", etc.).
            out.extend(["", "### Source snippet", "", f"```{lang}", normalized_code.strip(), "```"])

    report = _extract_access_report(section.body)
    if report:
        out.extend(["", report])

    quiz = _extract_quiz(section.body)
    if quiz:
        out.extend(["", quiz])

    form = _extract_form(section.body)
    if form:
        out.extend(["", form])

    punchline = _render_punchline_closer(section, ctx=ctx)
    if punchline:
        out.extend(["", punchline])

    return "\n".join(out).strip()


def _truthy_env(name: str) -> bool:
    return os.getenv(name, "").strip().lower() in {"1", "true", "yes", "on"}


def _action_pack_sections(sections: list[_SourceSection]) -> list[_SourceSection]:
    blacklist = {"TABLE OF CONTENTS", "LICENSE AND USAGE", "REVISION HISTORY", "PROJECT SPONSORS"}
    selected = [s for s in sections if s.title.strip().upper() not in blacklist]
    return selected[:14]


def _action_pack_gate(section: _SourceSection) -> str:
    title_upper = section.title.upper()
    excerpt = f"{section.title}\n{section.why_it_matters or ''}\n{section.body}".lower()

    if "PULL REQUEST" in title_upper or "PR CHECK" in excerpt:
        return "PR"
    if "SHIFTING LEFT" in title_upper or "IDE" in excerpt or "LOCAL" in excerpt:
        return "IDE / local"
    if "ACCESS CONTROL" in title_upper or ("ACCESS" in title_upper and "REQUEST" in title_upper):
        return "Access"
    if "TRAINING" in title_upper or "QUIZ" in excerpt:
        return "Training / enablement"
    if "AUDIT" in title_upper or "COMPLIANCE" in title_upper:
        return "Compliance / audit"
    if title_upper.startswith("LLM"):
        return "Runtime / app"
    if "THIRD-PARTY" in title_upper or "VENDOR" in title_upper:
        return "Procurement / TPRM"
    if "ROI" in title_upper or "BUSINESS VALUE" in title_upper:
        return "Governance / metrics"
    return "Governance"


def _action_pack_owner(gate: str) -> str:
    return {
        "PR": "Engineering + AppSec",
        "IDE / local": "Developer Enablement + AppSec",
        "Access": "Security Platform + IT",
        "Training / enablement": "Security Enablement + Engineering Leads",
        "Compliance / audit": "GRC + Security",
        "Runtime / app": "Platform + AppSec",
        "Procurement / TPRM": "TPRM + Security + Procurement",
        "Governance / metrics": "Security + Finance",
        "Governance": "Security + Engineering Leadership",
    }.get(gate, "Security + Engineering")


def _action_pack_stop_condition(gate: str) -> str:
    return {
        "PR": "Block merge on high severity (or unknown) findings; exceptions require owner + expiry.",
        "IDE / local": "Block/deny assistant enablement when local scan signals are missing for the developer/device.",
        "Access": "Deny access until prerequisites are met; exceptions auto-expire and require explicit owner.",
        "Training / enablement": "Deny access until training completion is verified (not self-attested).",
        "Compliance / audit": "Fail audit-readiness if evidence is missing/freshness expired; trigger remediation with owners.",
        "Runtime / app": "Block tool-use/output execution unless allowlists and validation checks pass.",
        "Procurement / TPRM": "Do not onboard until minimum evidence set is provided and decision owner signs accept/block.",
        "Governance / metrics": "Do not claim ROI until baseline + measurement method are defined and collected.",
        'Governance': 'No "phased rollout" without exit criteria and an explicit decision owner.',
    }.get(gate, "Define an explicit stop condition that cannot be reframed as iteration.")


def _action_pack_evidence(gate: str) -> str:
    return {
        "PR": "scan_event_id + policy_version + exception_record(expiry, owner)",
        "IDE / local": "device_baseline + local_scan_signal + attestation_id",
        "Access": "access_grant_event + prerequisite_check + exception_record(expiry, owner)",
        "Training / enablement": "training_completion_id + quiz_result + access_grant_event",
        "Compliance / audit": "evidence_bundle_hash + freshness_timestamp + decision_record",
        "Runtime / app": "allowlist_version + execution_log_id + output_validation_event",
        "Procurement / TPRM": "vendor_evidence_bundle_hash + risk_decision_record + reassessment_date",
        "Governance / metrics": "baseline_metrics_snapshot + measurement_notes + renewal_decision_record",
        "Governance": "decision_log + rollout_milestones + stop_condition_text",
    }.get(gate, "decision_record + evidence_artifact")


def _render_action_pack(sections: list[_SourceSection]) -> str:
    selected = _action_pack_sections(sections)
    if not selected:
        return ""

    out: list[str] = [
        "## Action Pack (Operational)",
        "",
        "This appendix turns the mirror into Monday-morning work: owners, gates, stop conditions, and evidence artifacts.",
        "Keep it generic and auditable; adapt to your tooling without inventing fake implementation details.",
        "",
        "### Control Cards",
    ]

    for section in selected:
        gate = _action_pack_gate(section)
        out.extend(
            [
                "",
                f"#### {section.title}",
                "",
                f'- **Control objective:** Prevent the dilution risk described in "{section.title}" by turning guidance into an enforceable workflow.',
                f"- **Gate:** {gate}",
                f"- **Owner (RACI):** {_action_pack_owner(gate)}",
                f"- **Stop condition:** {_action_pack_stop_condition(gate)}",
                f"- **Evidence artifact:** {_action_pack_evidence(gate)}",
            ]
        )

    out.extend(["", "### Backlog Export (Jira-ready)", ""])
    for idx, section in enumerate(selected, 1):
        gate = _action_pack_gate(section)
        out.extend(
            [
                f"{idx}. [{gate}] {section.title}: define owner, gate, and stop condition",
                f"   - Acceptance: owner assigned; stop condition documented and approved.",
                f"   - Acceptance: evidence artifact defined and stored (machine-generated where possible).",
                f"   - Acceptance: exceptions require owner + expiry; expiry is enforced automatically.",
            ]
        )

    out.extend(
        [
            "",
            "### Policy-as-Code Appendix (pseudo-YAML)",
            "",
            "```yaml",
            "gates:",
            "  pr:",
            "    - name: \"risk scanning\"",
            "      stop_condition: \"block on high severity (or unknown)\"",
            "      evidence: \"scan_event_id + policy_version\"",
            "  access:",
            "    - name: \"assistant enablement\"",
            "      prerequisite: \"device baseline + local scan signal\"",
            "      stop_condition: \"deny when signals missing\"",
            "      evidence: \"access_grant_event + prerequisite_check\"",
            "  runtime:",
            "    - name: \"tool-use\"",
            "      prerequisite: \"allowlist + validation\"",
            "      stop_condition: \"block disallowed actions\"",
            "      evidence: \"execution_log_id + allowlist_version\"",
            "exceptions:",
            "  expiry_days: 14",
            "  require_owner: true",
            "  require_reason: true",
            "evidence:",
            "  freshness_days: 30",
            "  require_hash: true",
            "```",
        ]
    )

    return "\n".join(out).strip()


def _generate_dave_v1_2_mirror(*, source_text: str, source_path: str, action_pack: bool, locale: str) -> str:
    today = _dt.date.today().isoformat()
    normalized = _normalize_ocr(source_text)
    extract_sha = _sha256_text(normalized)
    source_file_sha = _sha256_file(source_path) if Path(source_path).exists() else "unknown"
    ctx = _RenderContext(seed=extract_sha, locale=locale)

    action_pack = bool(action_pack) or _truthy_env("REVOICE_ACTION_PACK")

    sections = _extract_sections(normalized)
    if not sections:
        raise ValueError("No content extracted from source")

    cover_lines = [ln.strip() for ln in sections[0].body.splitlines() if ln.strip() and ln.strip().lower() != "snyk"]
    cover_h1 = sections[0].title.strip() or ("DOSSIER DE L’OMBRE" if locale.lower().startswith("fr") else "SHADOW DOSSIER")
    cover_h2 = " ".join(cover_lines[:2]).strip() if cover_lines else ""

    y, m, d = today.split("-")
    report_id = f"IF-RT-DAVE-{y}-{m}{d}"
    source_basename = Path(source_path).name
    project_slug = _slugify(Path(source_basename).stem + "-mirror")
    source_slug = _slugify(source_basename)
    filename_title = Path(source_basename).stem.replace("-", " ").replace("_", " ").strip()
    if not filename_title:
        filename_title = source_basename

    if (
        not cover_h1
        or cover_h1.upper() == "COUVERTURE"
        or _looks_like_site_footer(cover_h1)
        or len(cover_h1) > 96
        or "." in cover_h1
    ):
        cover_h1 = filename_title

    out: list[str] = [
        "---",
        "BRAND: InfraFabric.io",
        "UNIT: RED TEAM (STRATEGIC OPS)" if not locale.lower().startswith("fr") else "UNIT: RED TEAM (OPÉRATIONS STRATÉGIQUES)",
        "DOCUMENT: SHADOW DOSSIER" if not locale.lower().startswith("fr") else "DOCUMENT: DOSSIER DE L’OMBRE",
        "CLASSIFICATION: EYES ONLY // DAVE" if not locale.lower().startswith("fr") else "CLASSIFICATION: CONFIDENTIEL // DAVE",
        "---",
        "",
        "# [ RED TEAM DECLASSIFIED ]" if not locale.lower().startswith("fr") else "# [ DÉCLASSIFIÉ – ÉQUIPE ROUGE ]",
        f"## PROJECT: {project_slug}" if not locale.lower().startswith("fr") else f"## PROJET : {project_slug}",
        f"### SOURCE: {source_slug}" if not locale.lower().startswith("fr") else f"### SOURCE : {source_slug}",
        f"**INFRAFABRIC REPORT ID:** `{report_id}`" if not locale.lower().startswith("fr") else f"**ID DE RAPPORT INFRAFABRIC :** `{report_id}`",
        "",
        "> NOTICE: This document is a product of InfraFabric Red Team."
        if not locale.lower().startswith("fr")
        else "> AVIS : ce document est un produit de l’InfraFabric Red Team.",
        "> It provides socio-technical friction analysis for how a rollout survives contact with incentives."
        if not locale.lower().startswith("fr")
        else "> Il fournit une analyse socio-technique des frictions : ce qui survit au contact des incitations.",
        "",
        "**[ ACCESS GRANTED: INFRAFABRIC RED TEAM ]**"
        if not locale.lower().startswith("fr")
        else "**[ ACCÈS AUTORISÉ : INFRAFABRIC ÉQUIPE ROUGE ]**",
        "**[ STATUS: OPERATIONAL REALISM ]**"
        if not locale.lower().startswith("fr")
        else "**[ STATUT : RÉALISME OPÉRATIONNEL ]**",
        "",
        f"## {cover_h1}",
    ]
    if cover_h2:
        out.extend([f"### {cover_h2}", ""])
    else:
        out.append("")

    out.extend(
        [
            "> Shadow dossier (mirror-first)." if not locale.lower().startswith("fr") else "> Dossier de l’ombre (miroir d’abord).",
            ">",
            "> Protocol: IF.DAVE.v1.2" if not locale.lower().startswith("fr") else "> Protocole : IF.DAVE.v1.2",
            "> Citation: `if://bible/dave/v1.2`"
            if not locale.lower().startswith("fr")
            else "> Citation : `if://bible/dave/fr/v1.2`",
            f"> Source: `{source_basename}`" if not locale.lower().startswith("fr") else f"> Source : `{source_basename}`",
            f"> Generated: `{today}`" if not locale.lower().startswith("fr") else f"> Généré le : `{today}`",
            f"> Source Hash (sha256): `{source_file_sha}`"
            if not locale.lower().startswith("fr")
            else f"> Empreinte source (sha256) : `{source_file_sha}`",
            f"> Extract Hash (sha256): `{extract_sha}`"
            if not locale.lower().startswith("fr")
            else f"> Empreinte d’extraction (sha256) : `{extract_sha}`",
            "",
        ]
    )

    for section in sections[1:]:
        if section.title.strip().upper() == "INTRODUCTION":
            out.append(_render_intro(section, ctx=ctx))
        else:
            out.append(_render_section(section, ctx=ctx))
        out.append("")

    if action_pack:
        out.append(_render_action_pack(sections[1:]))
        out.append("")

    out.extend(
        [
            "---",
            "",
            "*InfraFabric Red Team Footer:* **RED-TEAM Shadow Dossiers**, part of the **InfraFabric.io governance stack**: https://infrafabric.io"
            if not locale.lower().startswith("fr")
            else "*InfraFabric Red Team Footer:* **RED-TEAM Shadow Dossiers**, partie de la **pile de gouvernance InfraFabric.io** : https://infrafabric.io",
            "*Standard Dave Footer:* This document is intended for the recipient only. If you are not the recipient, please delete it and forget you saw anything. P.S. Please consider the environment before printing this email."
            if not locale.lower().startswith("fr")
            else "*Standard Dave Footer:* Ce document est destiné au seul destinataire. Si vous n’êtes pas le destinataire, veuillez le supprimer et oublier que vous l’avez vu. P.S. Veuillez considérer l’environnement avant d’imprimer ce document.",
        ]
    )

    return "\n".join(out).strip() + "\n"


def _infer_vertical_line(*, normalized_text: str, source_basename: str, locale: str) -> str | None:
    text = f"{source_basename}\n{normalized_text}".lower()
    if "ai code guardrails" in text or ("guardrails" in text and "code" in text):
        if locale.lower().startswith("fr"):
            return "> Vertical : déploiement DevSecOps — vents contraires : dérive de politique et exceptions qui deviennent la norme."
        return "> Vertical: DevSecOps rollout — headwinds: policy drift and exception creep."
    if "owasp" in text and "llm" in text:
        if locale.lower().startswith("fr"):
            return "> Vertical : application LLM — vents contraires : injection de prompts et dette de contrôle."
        return "> Vertical: LLM application security — headwinds: prompt injection and control debt."
    return None


def _generate_dave_v1_3_mirror(*, source_text: str, source_path: str, action_pack: bool, locale: str) -> str:
    today = _dt.date.today().isoformat()
    normalized = _normalize_ocr(source_text)
    extract_sha = _sha256_text(normalized)
    source_file_sha = _sha256_file(source_path) if Path(source_path).exists() else "unknown"
    ctx = _RenderContext(seed=extract_sha, locale=locale)

    action_pack = bool(action_pack) or _truthy_env("REVOICE_ACTION_PACK")

    sections = _extract_sections(normalized)
    if not sections:
        raise ValueError("No content extracted from source")

    cover_lines = [ln.strip() for ln in sections[0].body.splitlines() if ln.strip() and ln.strip().lower() != "snyk"]
    cover_h1 = sections[0].title.strip() or ("DOSSIER DE L’OMBRE" if locale.lower().startswith("fr") else "SHADOW DOSSIER")
    cover_h2 = " ".join(cover_lines[:2]).strip() if cover_lines else ""

    y, m, d = today.split("-")
    report_id = f"IF-RT-DAVE-{y}-{m}{d}"
    source_basename = Path(source_path).name
    project_slug = _slugify(Path(source_basename).stem + "-mirror")
    source_slug = _slugify(source_basename)
    filename_title = Path(source_basename).stem.replace("-", " ").replace("_", " ").strip()
    if not filename_title:
        filename_title = source_basename

    if (
        not cover_h1
        or cover_h1.upper() == "COUVERTURE"
        or _looks_like_site_footer(cover_h1)
        or len(cover_h1) > 96
        or "." in cover_h1
    ):
        cover_h1 = filename_title

    vertical_line = _infer_vertical_line(normalized_text=normalized, source_basename=source_basename, locale=locale)

    out: list[str] = [
        "---",
        "BRAND: InfraFabric.io",
        "UNIT: RED TEAM (STRATEGIC OPS)" if not locale.lower().startswith("fr") else "UNIT: RED TEAM (OPÉRATIONS STRATÉGIQUES)",
        "DOCUMENT: SHADOW DOSSIER" if not locale.lower().startswith("fr") else "DOCUMENT: DOSSIER DE L’OMBRE",
        "CLASSIFICATION: EYES ONLY // DAVE" if not locale.lower().startswith("fr") else "CLASSIFICATION: CONFIDENTIEL // DAVE",
        "---",
        "",
        "# [ RED TEAM DECLASSIFIED ]" if not locale.lower().startswith("fr") else "# [ DÉCLASSIFIÉ – ÉQUIPE ROUGE ]",
        f"## PROJECT: {project_slug}" if not locale.lower().startswith("fr") else f"## PROJET : {project_slug}",
        f"### SOURCE: {source_slug}" if not locale.lower().startswith("fr") else f"### SOURCE : {source_slug}",
        f"**INFRAFABRIC REPORT ID:** `{report_id}`" if not locale.lower().startswith("fr") else f"**ID DE RAPPORT INFRAFABRIC :** `{report_id}`",
        "",
        "> NOTICE: This document is a product of InfraFabric Red Team."
        if not locale.lower().startswith("fr")
        else "> AVIS : ce document est un produit de l’InfraFabric Red Team.",
        "> It provides socio-technical friction analysis for how a rollout survives contact with incentives."
        if not locale.lower().startswith("fr")
        else "> Il fournit une analyse socio-technique des frictions : ce qui survit au contact des incitations.",
    ]
    if vertical_line:
        out.extend([vertical_line])
    out.extend(
        [
            "",
            "**[ ACCESS GRANTED: INFRAFABRIC RED TEAM ]**"
            if not locale.lower().startswith("fr")
            else "**[ ACCÈS AUTORISÉ : INFRAFABRIC ÉQUIPE ROUGE ]**",
            "**[ STATUS: OPERATIONAL REALISM ]**"
            if not locale.lower().startswith("fr")
            else "**[ STATUT : RÉALISME OPÉRATIONNEL ]**",
            "",
            f"## {cover_h1}",
        ]
    )
    if cover_h2:
        out.extend([f"### {cover_h2}", ""])
    else:
        out.append("")

    out.extend(
        [
            "> Shadow dossier (mirror-first)." if not locale.lower().startswith("fr") else "> Dossier de l’ombre (miroir d’abord).",
            ">",
            "> Protocol: IF.DAVE.v1.3" if not locale.lower().startswith("fr") else "> Protocole : IF.DAVE.v1.3",
            "> Citation: `if://bible/dave/v1.3`"
            if not locale.lower().startswith("fr")
            else "> Citation : `if://bible/dave/fr/v1.3`",
            f"> Source: `{source_basename}`" if not locale.lower().startswith("fr") else f"> Source : `{source_basename}`",
            f"> Generated: `{today}`" if not locale.lower().startswith("fr") else f"> Généré le : `{today}`",
            f"> Source Hash (sha256): `{source_file_sha}`"
            if not locale.lower().startswith("fr")
            else f"> Empreinte source (sha256) : `{source_file_sha}`",
            "",
        ]
    )

    for section in sections[1:]:
        if section.title.strip().upper() == "INTRODUCTION":
            out.append(_render_intro(section, ctx=ctx))
        else:
            out.append(_render_section(section, ctx=ctx))
        out.append("")

    if action_pack:
        out.append(_render_action_pack(sections[1:]))
        out.append("")

    out.extend(
        [
            "---",
            "",
            "*InfraFabric Red Team Footer:* **RED-TEAM Shadow Dossiers** for socio-technical friction analysis: https://infrafabric.io"
            if not locale.lower().startswith("fr")
            else "*InfraFabric Red Team Footer:* **RED-TEAM Shadow Dossiers** (analyse socio-technique des frictions) : https://infrafabric.io",
            "*Standard Dave Footer:* This document is intended for the recipient only. If you are not the recipient, please delete it and forget you saw anything. P.S. Please consider the environment before printing this email."
            if not locale.lower().startswith("fr")
            else "*Standard Dave Footer:* Ce document est destiné au seul destinataire. Si vous n’êtes pas le destinataire, veuillez le supprimer et oublier que vous l’avez vu. P.S. Veuillez considérer l’environnement avant d’imprimer ce document.",
        ]
    )

    return "\n".join(out).strip() + "\n"
