#!/usr/bin/env python3
"""Render one self-contained CARBON HTML page for every user-facing command."""

from __future__ import annotations

import argparse
import base64
import datetime as dt
import hashlib
import html
import json
import os
import re
import secrets
import tempfile
from pathlib import Path
from typing import Any
from urllib.parse import urlparse

from carbon_run import REPORT_FAVICON, REPORT_THEME_CSS, carbon_lockup, report_heading, report_icon
from carbon_brand import schema_provenance, schema_provenance_html
from carbon_promotions import select_promotion
from carbon_report_visuals import REPORT_VISUAL_CSS, render_decision_hero, render_evidence_spine, render_stats_visuals


RUNTIME_ROOT = Path(__file__).resolve().parents[1]
PLUGIN_ROOT = Path(__file__).resolve().parents[3]
SPECIALIST_REGISTRY = json.loads((RUNTIME_ROOT / "references" / "specialists.json").read_text(encoding="utf-8"))
TESTING_DOMAIN_REGISTRY = json.loads((RUNTIME_ROOT / "references" / "testing-domains.json").read_text(encoding="utf-8"))
WCAG22_REGISTRY = json.loads((RUNTIME_ROOT / "references" / "wcag22-criteria.json").read_text(encoding="utf-8"))
ACR_REGISTRY = json.loads((RUNTIME_ROOT / "references" / "acr-reporting.json").read_text(encoding="utf-8"))
SPECIALISTS = {row["id"]: row for row in SPECIALIST_REGISTRY.get("specialists", [])}
TESTING_DOMAINS = {row["id"]: row for row in TESTING_DOMAIN_REGISTRY.get("domains", [])}


STATUSES = {"completed", "passed", "planned", "running", "blocked", "failed", "partial", "unknown"}
TONES = {"good", "risk", "warning", "bad", "neutral"}
ICONS = {
    "artifact", "budget", "change", "confidence", "context", "coverage", "cycle",
    "evidence", "execution", "findings", "hypothesis", "learn", "observation",
    "position", "product", "risk", "safety", "tests", "timeline", "unknown",
}
DOMAIN_COMMANDS = {row["command"] for row in TESTING_DOMAINS.values()}
CARBON_COMMANDS = {
    "ai-upgrade", "carbon", "carbon-more", "carbon-auto", "carbon-browser", "carbon-confidence",
    "carbon-confidence-init", "carbon-confidence-plan", "carbon-eval",
    "carbon-eval-design-review", "carbon-generate", "carbon-help", "carbon-incident",
    "carbon-release", "carbon-release-review", "carbon-review", "carbon-security-review",
    "carbon-integrations", "carbon-settings", "carbon-human", "carbon-skeptical-review", "carbon-statistical-review", "carbon-stats",
    "carbon-tests", "chatbot", "failure", "testers-import", "testers-plan", "testers-results",
    "testers-run", "testers-schedule",
} | DOMAIN_COMMANDS | {"carbon-test"}
SECRET_PATTERNS = [
    re.compile(r"\bsk-[A-Za-z0-9_-]{16,}\b"),
    re.compile(r"\bghp_[A-Za-z0-9]{20,}\b"),
    re.compile(r"\bAKIA[0-9A-Z]{16}\b"),
    re.compile(r"\bBearer\s+[A-Za-z0-9._~+/-]{16,}\b", re.IGNORECASE),
    re.compile(r"-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----"),
]
CHAT_IMAGE_SUFFIXES = {".png", ".jpg", ".jpeg", ".webp"}

SPECIALIST_IDENTITY_CSS = """
.specialist-avatar{position:relative;width:max-content;align-self:start}
.specialist-avatar img{display:block;filter:grayscale(1)!important}
.specialist-ai-badge{position:absolute;right:6px;bottom:6px;display:inline-grid;min-width:28px;height:22px;place-items:center;padding:0 6px;border:1px solid rgba(255,255,255,.72);border-radius:999px;background:#111;color:#fff!important;font:850 10px/1 ui-monospace,SFMono-Regular,Menlo,monospace!important;letter-spacing:.08em!important;text-transform:uppercase;box-shadow:0 2px 10px rgba(0,0,0,.48)}
"""

REPORT_ACTION_SCRIPT = r"""
<script data-carbon-report-actions>
(() => {
  const buttons = [...document.querySelectorAll("[data-carbon-command-action]")];
  if (!buttons.length) return;

  const pending = new Map();
  let nextRequestId = 1;
  let standardMessageAvailable = false;
  const openaiBridge = typeof window !== "undefined" ? window.openai : undefined;

  function setMode(canSend) {
    buttons.forEach((button) => {
      button.dataset.mode = canSend ? "send" : "copy";
      const label = button.querySelector("[data-carbon-action-label]");
      if (label) label.textContent = canSend ? "Run in agent" : "Copy command";
    });
  }

  function setStatus(button, message, tone = "neutral") {
    const status = button.closest(".command-card")?.querySelector("[data-carbon-command-status]");
    if (!status) return;
    status.textContent = message;
    status.dataset.tone = tone;
  }

  function request(method, params, timeoutMs = 5000) {
    const id = nextRequestId++;
    window.parent.postMessage({ jsonrpc: "2.0", id, method, params }, "*");
    return new Promise((resolve, reject) => {
      const timeout = window.setTimeout(() => {
        pending.delete(id);
        reject(new Error(`${method} was not acknowledged by this host.`));
      }, timeoutMs);
      pending.set(id, { resolve, reject, timeout });
    });
  }

  window.addEventListener("message", (event) => {
    if (event.source !== window.parent) return;
    const message = event.data;
    if (!message || message.jsonrpc !== "2.0" || message.id === undefined || !pending.has(message.id)) return;
    const waiter = pending.get(message.id);
    pending.delete(message.id);
    window.clearTimeout(waiter.timeout);
    if (message.error) waiter.reject(new Error(message.error.message || "The host rejected the request."));
    else waiter.resolve(message.result || {});
  }, { passive: true });

  async function connectStandardBridge() {
    if (window.parent === window) return;
    try {
      const initialized = await request("ui/initialize", {
        protocolVersion: "2026-01-26",
        appInfo: { name: "CARBON Report", version: "1.0.0" },
        appCapabilities: { availableDisplayModes: ["inline", "fullscreen"] },
      }, 1500);
      window.parent.postMessage({
        jsonrpc: "2.0",
        method: "ui/notifications/initialized",
        params: {},
      }, "*");
      standardMessageAvailable = Boolean(initialized?.hostCapabilities?.message?.text);
      setMode(standardMessageAvailable || typeof openaiBridge?.sendFollowUpMessage === "function");
    } catch {
      setMode(typeof openaiBridge?.sendFollowUpMessage === "function");
    }
  }

  async function copyInvocation(invocation) {
    if (navigator.clipboard?.writeText) {
      await navigator.clipboard.writeText(invocation);
      return;
    }
    const fallback = document.createElement("textarea");
    fallback.value = invocation;
    fallback.setAttribute("readonly", "");
    fallback.style.position = "fixed";
    fallback.style.opacity = "0";
    document.body.appendChild(fallback);
    fallback.select();
    const copied = document.execCommand("copy");
    fallback.remove();
    if (!copied) throw new Error("Clipboard access is unavailable.");
  }

  async function sendInvocation(invocation) {
    if (standardMessageAvailable) {
      const result = await request("ui/message", {
        role: "user",
        content: [{ type: "text", text: invocation }],
      });
      if (result?.isError) throw new Error("The host declined the follow-up message.");
      return;
    }
    if (typeof openaiBridge?.sendFollowUpMessage === "function") {
      await openaiBridge.sendFollowUpMessage({ prompt: invocation, scrollToBottom: true });
      return;
    }
    throw new Error("This host does not expose follow-up messaging.");
  }

  buttons.forEach((button) => {
    button.addEventListener("click", async () => {
      const invocation = String(button.dataset.invocation || "").trim();
      if (!invocation.startsWith("/")) return;
      button.disabled = true;
      setStatus(button, button.dataset.mode === "send" ? "Sending to the agent…" : "Copying…");
      try {
        if (button.dataset.mode === "send") {
          await sendInvocation(invocation);
          setStatus(button, "Sent to the agent. Normal CARBON approval gates still apply.", "good");
        } else {
          await copyInvocation(invocation);
          setStatus(button, "Copied. Paste it into your coding agent.", "good");
        }
      } catch (error) {
        try {
          await copyInvocation(invocation);
          setStatus(button, "The host could not run it, so CARBON copied the command instead.", "risk");
          setMode(false);
        } catch {
          setStatus(button, error?.message || "The command could not be sent or copied.", "bad");
        }
      } finally {
        button.disabled = false;
      }
    });
  });

  setMode(typeof openaiBridge?.sendFollowUpMessage === "function");
  void connectStandardBridge();
})();
</script>
"""


def utc_now() -> str:
    return dt.datetime.now(dt.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")


def clean_text(value: Any, limit: int = 12_000) -> str:
    text = str(value or "").replace("\x00", "").strip()[:limit]
    for pattern in SECRET_PATTERNS:
        text = pattern.sub("[REDACTED]", text)
    return text


def slug(value: str) -> str:
    normalized = re.sub(r"[^a-z0-9]+", "-", value.lower()).strip("-")
    return normalized[:64] or "carbon"


def normalize_domain(value: Any) -> str:
    return re.sub(r"[_\s]+", "-", clean_text(value, 120).lower().removeprefix("carbon-")).strip("-")


def infer_domain_ids(payload: dict[str, Any], command: str) -> list[str]:
    selected: list[str] = []
    for value in list_of_text(payload.get("testingDomains"), 15):
        token = normalize_domain(value)
        for domain in TESTING_DOMAINS.values():
            searchable = {normalize_domain(domain["id"]), normalize_domain(domain["command"]), normalize_domain(domain["title"]), *(normalize_domain(alias) for alias in domain.get("aliases", []))}
            if token in searchable and domain["id"] not in selected:
                selected.append(domain["id"])
    for domain in TESTING_DOMAINS.values():
        if command == domain["command"] and domain["id"] not in selected:
            selected.append(domain["id"])
    if selected:
        return selected
    corpus = " ".join([
        command,
        clean_text(payload.get("title"), 500),
        clean_text(payload.get("summary"), 2_000),
        clean_text(payload.get("scope"), 1_000),
        " ".join(clean_text(row.get("title"), 300) for row in list_of_objects(payload.get("findings"), 100)),
        " ".join(clean_text(row.get("title"), 300) for row in list_of_objects(payload.get("sections"), 24)),
    ]).lower()
    for domain in TESTING_DOMAINS.values():
        tokens = [domain["id"], domain["title"], *domain.get("aliases", [])]
        if any(re.search(rf"(?<![a-z0-9]){re.escape(token.lower())}(?![a-z0-9])", corpus) for token in tokens):
            selected.append(domain["id"])
    return selected[:6]


def selected_specialists(payload: dict[str, Any], domain_ids: list[str]) -> list[dict[str, Any]]:
    """Return one accountable AI testing perspective for each report.

    Early broad-run payloads could carry the entire roster. That made an
    accessibility companion show unrelated security, privacy, and checkout
    agents, which looks like attribution rather than useful context. A report
    needs a named lead whose perspective matches the scope; the evidence in the
    report still supports (or limits) the result.
    """
    explicit = list_of_text(payload.get("specialistIds"), 21)
    domain_ids = [domain_id for domain_id in domain_ids if domain_id in TESTING_DOMAINS]
    command = clean_text(payload.get("command"), 120).lower()
    title = clean_text(payload.get("title"), 500).lower()
    if command == "carbon-stats":
        return [SPECIALISTS["jason"]]
    if command == "carbon" and any(token in title for token in ("first look", "homepage", "landing")):
        return [SPECIALISTS["yuki"]]

    ids: list[str] = []
    for domain_id in domain_ids:
        ids.extend(TESTING_DOMAINS[domain_id].get("specialistIds", []))
    ids.extend(explicit)

    if not ids:
        # A First Look of a public marketing page benefits from a first-impression
        # specialist. Other broad /carbon and activity reports are led by the
        # AI-code testing perspective, rather than displaying an arbitrary grid.
        ids.append("yuki" if any(token in title for token in ("first look", "homepage", "landing")) else "jason")
    for specialist_id in ids:
        if specialist_id in SPECIALISTS:
            return [SPECIALISTS[specialist_id]]
    return [SPECIALISTS["jason"]]


def should_render_accessibility_companions(payload: dict[str, Any], command: str, domain_ids: list[str]) -> bool:
    """Avoid publishing empty WCAG/ACR scaffolds from an unrelated broad run.

    A focused accessibility command deliberately produces its companion report
    set, even when it is blocked awaiting evidence. Other commands publish
    those heavyweight documents only when they supplied criterion observations.
    """
    if command == "carbon-accessibility":
        return True
    wcag = payload.get("wcagResults")
    return isinstance(wcag, dict) and bool(list_of_objects(wcag.get("criteria"), 100))


def avatar_data_uri(specialist: dict[str, Any]) -> str:
    avatar = (PLUGIN_ROOT / clean_text(specialist.get("avatar"), 500)).resolve()
    try:
        data = base64.b64encode(avatar.read_bytes()).decode("ascii")
    except (OSError, ValueError):
        return ""
    return f"data:image/jpeg;base64,{data}"


def render_specialists(specialists: list[dict[str, Any]], domain_ids: list[str]) -> str:
    if not specialists:
        return ""
    cards = []
    for specialist in specialists:
        image = avatar_data_uri(specialist)
        name = html.escape(clean_text(specialist.get("name"), 80))
        cards.append(
            f'''<article class="specialist-card">
              <div class="specialist-avatar">
                {f'<img src="{image}" alt="Grayscale portrait of {name}, an AI testing agent">' if image else '<div class="specialist-fallback" aria-hidden="true"></div>'}
                <span class="specialist-ai-badge" aria-label="AI testing agent" title="AI testing agent">AI</span>
              </div>
              <div><span>{html.escape(clean_text(specialist.get("title"), 160))}</span><h3>{name}</h3><p>{html.escape(clean_text(specialist.get("bio"), 1_000))}</p></div>
            </article>'''
        )
    labels = [TESTING_DOMAINS[domain_id]["title"] for domain_id in domain_ids if domain_id in TESTING_DOMAINS]
    intro = " · ".join(labels) if labels else "Selected testing perspectives"
    return f'''<section class="specialist-section" aria-label="CARBON testing specialists">
      <div class="specialist-heading"><span>Testing perspectives</span><strong>{html.escape(intro)}</strong></div>
      <div class="specialist-deck">{"".join(cards)}</div>
      <p class="specialist-boundary">Named specialists identify the applied testing perspective. Findings and pass states still require the evidence shown in this report.</p>
    </section>'''


def render_domain_start_state(domain_ids: list[str], has_observed_content: bool) -> str:
    """Make an unscoped focused report useful without masquerading as an assessment.

    A domain command can legitimately be blocked before it has a target or safe
    evidence channel.  In that case, rendering a generic command shell with a
    zeroed confidence panel reads like a broken report.  Show the applied
    domain's investigation boundary and the specific evidence that is still
    needed instead.  This is deliberately absent once the caller supplied
    findings, artifacts, metrics, or evidence-led sections.
    """
    if has_observed_content or not domain_ids:
        return ""
    domains = [TESTING_DOMAINS[domain_id] for domain_id in domain_ids if domain_id in TESTING_DOMAINS]
    if not domains:
        return ""
    cards: list[str] = []
    for domain in domains:
        families = [clean_text(value, 180) for value in domain.get("families", [])[:8]]
        family_items = "".join(f"<li>{html.escape(value)}</li>" for value in families)
        evidence = " · ".join(clean_text(value, 80) for value in domain.get("artifacts", [])[:6])
        cards.append(f'''<article class="domain-start-card">
          <span>Assessment status</span><h3>{html.escape(clean_text(domain.get("title"), 160))} evidence not collected</h3>
          <p>{html.escape(clean_text(domain.get("purpose"), 1_000))}</p>
          <p class="domain-start-evidence"><strong>Needed next:</strong> {html.escape(evidence or "a scoped target and observable evidence")}</p>
          <details><summary>What CARBON will examine once a target is available</summary><ul>{family_items}</ul></details>
        </article>''')
    return f'''<section class="domain-start-section" aria-label="Assessment readiness">
      <div class="domain-start-heading"><span>Assessment readiness</span><strong>Not a completed {html.escape(' · '.join(clean_text(domain.get('title'), 80) for domain in domains))} assessment</strong></div>
      <p>CARBON has the specialist playbook ready, but no inspectable product evidence was supplied. Choose a reachable page, API, feature, source tree, or privacy-safe test environment before treating this as a result.</p>
      <div class="domain-start-deck">{"".join(cards)}</div>
    </section>'''


def list_of_objects(value: Any, limit: int = 100) -> list[dict[str, Any]]:
    if not isinstance(value, list):
        return []
    return [item for item in value[:limit] if isinstance(item, dict)]


def list_of_text(value: Any, limit: int = 100) -> list[str]:
    if not isinstance(value, list):
        return []
    return [clean_text(item, 2_000) for item in value[:limit] if clean_text(item, 2_000)]


def icon_name(value: Any, fallback: str) -> str:
    candidate = clean_text(value, 40)
    return candidate if candidate in ICONS else fallback


def status_name(value: Any) -> str:
    candidate = clean_text(value, 40).lower()
    return candidate if candidate in STATUSES else "completed"


def artifact_href(root: Path, value: Any) -> str:
    raw = clean_text(value, 4_000)
    parsed = urlparse(raw)
    if parsed.scheme in {"http", "https", "file"}:
        return raw
    path = Path(raw).expanduser()
    if not path.is_absolute():
        path = (root / path).resolve()
    return path.as_uri()


def valid_chat_image(path: Path) -> bool:
    try:
        with path.open("rb") as handle:
            header = handle.read(12)
    except OSError:
        return False
    if path.suffix.lower() == ".png":
        return header.startswith(b"\x89PNG\r\n\x1a\n")
    if path.suffix.lower() in {".jpg", ".jpeg"}:
        return header.startswith(b"\xff\xd8\xff")
    return path.suffix.lower() == ".webp" and header.startswith(b"RIFF") and header[8:12] == b"WEBP"


def chat_screenshots(root: Path, payload: dict[str, Any]) -> list[dict[str, Any]]:
    """Return a small, explicitly privacy-reviewed visual story for host chat."""
    carbon_root = (root / ".carbon").resolve()
    selected: list[dict[str, Any]] = []
    seen_paths: set[Path] = set()
    seen_hashes: set[str] = set()
    for row in list_of_objects(payload.get("chatScreenshots"), 8):
        if row.get("safeToShow") is not True or row.get("sensitive") is True or row.get("privacyWithheld") is True:
            continue
        raw = clean_text(row.get("path"), 4_000)
        if not raw or urlparse(raw).scheme:
            continue
        candidate = Path(raw).expanduser()
        if not candidate.is_absolute():
            candidate = root / candidate
        try:
            candidate = candidate.resolve(strict=True)
            relative = candidate.relative_to(carbon_root)
        except (OSError, RuntimeError, ValueError):
            continue
        if not relative.parts or candidate.suffix.lower() not in CHAT_IMAGE_SUFFIXES or not candidate.is_file() or not valid_chat_image(candidate):
            continue
        if candidate in seen_paths or candidate.stat().st_size > 20_000_000:
            continue
        digest = hashlib.sha256(candidate.read_bytes()).hexdigest()
        if digest in seen_hashes:
            continue
        seen_paths.add(candidate)
        seen_hashes.add(digest)
        label = clean_text(row.get("label"), 160) or f"Browser evidence {len(selected) + 1}"
        caption = clean_text(row.get("caption"), 500) or label
        reason = clean_text(row.get("reason"), 500) or "Representative browser evidence from this test result."
        markdown_path = f"<{candidate}>" if any(character.isspace() for character in str(candidate)) else str(candidate)
        selected.append({
            "path": str(candidate),
            "label": label,
            "caption": caption,
            "reason": reason,
            "markdown": f"![{label}]({markdown_path})",
        })
        if len(selected) == 4:
            break
    return selected


def render_text_list(items: list[str], empty: str = "Nothing recorded.") -> str:
    if not items:
        return f'<p class="muted">{html.escape(empty)}</p>'
    return '<ul class="smart-list">' + "".join(
        f'<li><span class="list-marker" aria-hidden="true"></span><span>{html.escape(item)}</span></li>'
        for item in items
    ) + "</ul>"


def render_metrics(metrics: list[dict[str, Any]]) -> str:
    if not metrics:
        return ""
    rows = []
    for index, metric in enumerate(metrics):
        tone = clean_text(metric.get("tone"), 20).lower()
        if tone not in TONES:
            tone = "neutral"
        icon = icon_name(metric.get("icon"), ("confidence", "coverage", "risk", "evidence")[index % 4])
        rows.append(
            f'''<article class="signal-row tone-{tone}">
              <div class="signal-icon">{report_icon(icon)}</div>
              <div class="signal-copy"><span>{html.escape(clean_text(metric.get("label"), 120) or "Signal")}</span>
              <small>{html.escape(clean_text(metric.get("detail"), 1_000))}</small></div>
              <strong>{html.escape(clean_text(metric.get("value"), 120) or "—")}</strong>
            </article>'''
        )
    return '<section class="signal-ledger" aria-label="Command signals">' + "".join(rows) + "</section>"


def render_sections(sections: list[dict[str, Any]]) -> str:
    rendered = []
    fallbacks = ("evidence", "coverage", "risk", "tests", "context", "execution")
    for index, section in enumerate(sections):
        title = clean_text(section.get("title"), 160) or f"Detail {index + 1}"
        intro = clean_text(section.get("intro"), 4_000)
        icon = icon_name(section.get("icon"), fallbacks[index % len(fallbacks)])
        tone = clean_text(section.get("tone"), 20).lower()
        if tone not in TONES:
            tone = "neutral"
        items = list_of_objects(section.get("items"), 80)
        item_html = []
        for item in items:
            item_status = status_name(item.get("status")) if item.get("status") else "unknown"
            item_html.append(
                f'''<article class="evidence-entry">
                  <div class="entry-rail"><span class="entry-dot status-{html.escape(item_status)}"></span></div>
                  <div><div class="entry-title"><strong>{html.escape(clean_text(item.get("title"), 240) or "Result")}</strong>
                  <span class="status-pill status-{html.escape(item_status)}">{html.escape(item_status)}</span></div>
                  <p>{html.escape(clean_text(item.get("detail"), 4_000))}</p>
                  {f'<small>{html.escape(clean_text(item.get("evidence"), 2_000))}</small>' if item.get("evidence") else ''}</div>
                </article>'''
            )
        body = "".join(item_html) or render_text_list(list_of_text(section.get("bullets")), "No additional detail was needed.")
        rendered.append(
            f'''<section class="editorial-section tone-{tone}">
              <div class="section-index">{index + 1:02d}</div>
              <div class="section-body">
                {report_heading(title, icon)}
                {f'<p class="section-intro">{html.escape(intro)}</p>' if intro else ''}
                <div class="evidence-stream">{body}</div>
              </div>
            </section>'''
        )
    return "".join(rendered)


def render_findings(findings: list[dict[str, Any]]) -> str:
    if not findings:
        return ""
    entries = []
    for finding in findings:
        severity = clean_text(finding.get("severity"), 20).lower() or "unknown"
        entries.append(
            f'''<article class="finding-line severity-{html.escape(severity)}">
              <div class="finding-severity">{report_icon("findings")}<span>{html.escape(severity)}</span></div>
              <div><h3>{html.escape(clean_text(finding.get("title") or finding.get("bug_title"), 240) or "Finding")}</h3>
              <p>{html.escape(clean_text(finding.get("detail"), 4_000))}</p>
              {f'<p class="evidence-note"><strong>Evidence</strong> · {html.escape(clean_text(finding.get("evidence"), 2_000))}</p>' if finding.get("evidence") else ''}
              {f'<p class="fix-note"><strong>Next repair</strong> · {html.escape(clean_text(finding.get("fix"), 2_000))}</p>' if finding.get("fix") else ''}</div>
            </article>'''
        )
    return f'''<section class="findings-section">
      <div class="section-index">!</div><div class="section-body">{report_heading("Findings", "findings")}
      <div class="finding-ledger">{"".join(entries)}</div></div>
    </section>'''


def render_artifacts(root: Path, artifacts: list[dict[str, Any]]) -> str:
    if not artifacts:
        return ""
    rows = []
    for artifact in artifacts:
        label = clean_text(artifact.get("label"), 180) or "Artifact"
        path = clean_text(artifact.get("path") or artifact.get("url"), 4_000)
        if not path:
            continue
        rows.append(
            f'''<a class="artifact-row" href="{html.escape(artifact_href(root, path), quote=True)}">
              <span class="artifact-icon">{report_icon(icon_name(artifact.get("icon"), "artifact"))}</span>
              <span><strong>{html.escape(label)}</strong><small>{html.escape(clean_text(artifact.get("detail"), 1_000) or path)}</small></span>
              <span class="artifact-arrow" aria-hidden="true">↗</span>
            </a>'''
        )
    if not rows:
        return ""
    return f'''<section class="artifact-section">
      <div class="section-index">A</div><div class="section-body">{report_heading("Artifacts", "artifact")}
      <div class="artifact-ledger">{"".join(rows)}</div></div>
    </section>'''


def recommendation(command: str, prompt: str, reason: str) -> dict[str, str]:
    prompt = clean_text(prompt, 700)
    invocation = f"/{command}{f' {prompt}' if prompt else ''}"
    return {"command": command, "prompt": prompt, "invocation": invocation, "reason": clean_text(reason, 1_000)}


def fallback_recommendations(command: str, scope: str) -> list[dict[str, str]]:
    target = clean_text(scope, 240) or "the current project"
    plan_commands = {
        "carbon-generate", "carbon-confidence-plan", "testers-plan", "carbon-eval",
        "carbon-eval-design-review", "carbon-statistical-review",
    }
    execution_commands = {"carbon-more", "carbon-auto", "carbon-browser", "testers-run", "testers-results"}
    review_commands = {
        "carbon-review", "carbon-skeptical-review", "carbon-security-review",
        "carbon-release-review", "carbon-confidence",
    }
    if command in DOMAIN_COMMANDS:
        return [
            recommendation("carbon-test", f"retest only {target} where this {command.removeprefix('carbon-')} pass found risk", "Keeps the next run tightly scoped to the affected feature while preserving the current specialist findings as regression context."),
            recommendation("carbon-confidence", f"update shipping confidence for {target} using this specialist report", "Combines the domain-specific evidence with the other quality dimensions and makes missing evidence explicit."),
        ]
    if command == "carbon-test":
        return [
            recommendation("carbon-review", f"challenge the focused evidence and adjacent risks for {target}", "Checks whether the narrow pass missed an affected consumer, state transition, or neighboring failure mode."),
            recommendation("carbon-confidence", f"update shipping confidence for {target}", "Places the focused result in the broader release-risk and evidence context."),
        ]
    if command == "carbon":
        return [
            recommendation("carbon-more", f"run the next deferred coverage for {target}", "Continues the durable candidate pool without repeating the bounded First Look."),
            recommendation("carbon-review", f"challenge failures, evidence, and blind spots from {target}", "Separates confirmed defects from execution trouble and identifies the most consequential remaining unknown."),
        ]
    if command == "carbon-human":
        return [
            recommendation("carbon-confidence", f"re-interpret confidence for {target} using the recorded human context", "Brings the newly explicit business, risk, and accountability assumptions into the evidence-qualified confidence call."),
            recommendation("carbon-test", f"test the highest-risk workflow for {target} using the human decision context", "Converts the most decision-changing answer into an observed, bounded technical check."),
        ]
    if command == "carbon-more":
        return [
            recommendation("carbon-confidence", f"recalculate shipping confidence after the added coverage for {target}", "Synthesizes the expanded evidence, remaining risk, and decision-changing gaps."),
            recommendation("carbon-generate", f"generate new context-specific coverage beyond the saved pool for {target}", "Adds fresh candidates only when the durable pool is exhausted or the product context changed."),
        ]
    if command in {"carbon-help", "carbon-settings", "carbon-integrations", "carbon-confidence-init"}:
        return [
            recommendation("carbon", f"inspect {target} and run the highest-value local checks", "Turns the current setup and project context into an evidence-producing validation pass."),
            recommendation("carbon-generate", f"build a prioritized test plan for {target}", "Creates a reviewable test inventory before execution or mutation."),
        ]
    if command in plan_commands:
        return [
            recommendation("carbon-tests", f"review and refine the planned tests for {target}", "Lets you inspect, edit, deduplicate, and approve the generated test intent."),
            recommendation("carbon-browser", f"execute the highest-risk non-destructive flow for {target}", "Converts the plan into direct runtime evidence against the most important behavior."),
            recommendation("carbon-review", f"challenge the evidence and missing coverage for {target}", "Finds weak assumptions and gaps before the plan is treated as sufficient."),
        ]
    if command in execution_commands:
        return [
            recommendation("carbon-review", f"review failures, evidence, and blind spots from {target}", "Separates confirmed defects from automation trouble and identifies missing coverage."),
            recommendation("carbon-confidence", f"summarize release confidence for {target}", "Turns the observed evidence and remaining risk into a decision-ready confidence brief."),
        ]
    if command in review_commands:
        return [
            recommendation("carbon-browser", f"verify the highest-risk unresolved claim for {target}", "Collects the runtime evidence most likely to change the review conclusion."),
            recommendation("carbon-confidence", f"update confidence after reviewing {target}", "Re-synthesizes evidence, uncertainty, and remaining risk after the independent challenge."),
        ]
    if command == "carbon-incident":
        return [
            recommendation("carbon-security-review", f"inspect containment and trust boundaries for {target}", "Checks whether the incident exposed a broader security or permissioning weakness."),
            recommendation("carbon-eval", f"create a regression evaluation for {target}", "Turns the incident into durable cases that can prevent recurrence."),
        ]
    if command == "carbon-release":
        return [
            recommendation("carbon-release-review", f"independently challenge the release decision for {target}", "Adds an adversarial evidence review before shipping, canarying, or holding."),
            recommendation("carbon-browser", f"run the final critical user flow for {target}", "Provides fresh runtime evidence for the behavior with the greatest release impact."),
        ]
    if command == "ai-upgrade":
        return [
            recommendation("carbon-tests", f"review the upgraded test intent for {target}", "Checks that generated and augmented tests are useful, editable, and not redundant."),
            recommendation("carbon", f"run the upgraded checks against {target}", "Exercises the improved suite locally and captures current evidence."),
        ]
    if command == "chatbot":
        return [
            recommendation("carbon-tests", f"review and extend the chatbot cases for {target}", "Lets you refine conversational inputs, expected behavior, personas, and safety coverage."),
            recommendation("carbon", f"execute the highest-priority chatbot flows for {target}", "Moves the generated suite from planned coverage to observed behavior."),
        ]
    if command == "failure":
        return [
            recommendation("carbon-reliability", f"retest recovery and state integrity for {target}", "Turns the most important injected failures into focused resilience and recovery evidence."),
            recommendation("carbon-review", f"challenge the failure-handling evidence and untested conditions for {target}", "Checks that the copy-only pass did not miss unsafe side effects, weak oracles, or a neighboring failure boundary."),
        ]
    if command in {"testers-import", "testers-schedule"}:
        return [
            recommendation("testers-run", f"run the approved cloud tests for {target}", "Executes the imported or scheduled scope in managed browsers with durable evidence."),
            recommendation("testers-results", f"review the latest results for {target}", "Retrieves failures, evidence, scores, and fix-ready follow-up prompts."),
        ]
    if command == "carbon-tests":
        return [
            recommendation("carbon", f"execute the approved tests for {target}", "Turns the curated AI-intent tests into local runtime evidence."),
            recommendation("carbon-generate", f"find additional high-risk gaps for {target}", "Adds relevant cases only where the current library still lacks coverage."),
        ]
    return [
        recommendation("carbon", f"test {target}", "Runs a prioritized local validation pass using the current project context."),
        recommendation("carbon-help", f"recommend the best next validation for {target}", "Reassesses the project and explains which Carbon workflow will add the most evidence next."),
    ]


def recommended_commands(payload: dict[str, Any], command: str, scope: str) -> list[dict[str, str]]:
    selected: list[dict[str, str]] = []
    seen = {command}
    for item in list_of_objects(payload.get("recommendedCommands"), 6):
        raw_command = clean_text(item.get("command"), 800).lstrip("/$").strip()
        parts = raw_command.split(maxsplit=1)
        candidate = (parts[0] if parts else "").lower()
        inline_prompt = parts[1] if len(parts) > 1 else ""
        prompt = clean_text(item.get("prompt"), 700) or inline_prompt
        reason = clean_text(item.get("reason"), 1_000)
        if candidate not in CARBON_COMMANDS or candidate in seen or not reason:
            continue
        selected.append(recommendation(candidate, prompt, reason))
        seen.add(candidate)
        if len(selected) == 4:
            break
    for item in fallback_recommendations(command, scope):
        if len(selected) >= 2:
            break
        if item["command"] not in seen:
            selected.append(item)
            seen.add(item["command"])
    return selected[:4]


def render_recommended_commands(items: list[dict[str, str]]) -> str:
    cards = []
    for item in items:
        invocation = html.escape(item["invocation"], quote=True)
        cards.append(
            f'''<article class="command-card">
              <div class="command-card-label">Run next</div>
              <code>{invocation}</code>
              <p>{html.escape(item["reason"])}</p>
              <button type="button" class="command-action" data-carbon-command-action data-invocation="{invocation}" data-mode="copy"><span data-carbon-action-label>Copy command</span><span aria-hidden="true">→</span></button>
              <small class="command-action-status" data-carbon-command-status data-tone="neutral" aria-live="polite">Runs only after you click. Agent approval gates still apply.</small>
            </article>'''
        )
    return f'''<section class="recommended-section"><div class="section-index">→</div><div class="section-body">
      {report_heading("Next Carbon commands", "change")}
      <p class="section-intro">Run a focused follow-up inside a compatible coding agent, or copy it when the host does not support interactive report actions.</p>
      <div class="command-deck">{"".join(cards)}</div></div></section>'''


def confidence_score(value: Any) -> int | None:
    if isinstance(value, bool) or value is None:
        return None
    try:
        score = round(float(value))
    except (TypeError, ValueError):
        return None
    return score if 0 <= score <= 100 else None


def confidence_label(score: int | None, supplied: Any) -> str:
    if score is None:
        return "Not assessed"
    custom = clean_text(supplied, 80)
    if custom:
        return custom
    if score < 25:
        return "Low"
    if score < 50:
        return "Guarded"
    if score < 75:
        return "Moderate"
    if score < 90:
        return "Strong"
    return "Very strong"


def normalize_gap_command(value: Any) -> str:
    raw = clean_text(value, 800).lstrip("/$").strip()
    parts = raw.split(maxsplit=1)
    if not parts or parts[0].lower() not in CARBON_COMMANDS:
        return ""
    command = parts[0].lower()
    prompt = clean_text(parts[1], 700) if len(parts) > 1 else ""
    return f"/{command}{f' {prompt}' if prompt else ''}"


def confidence_meter(
    payload: dict[str, Any],
    recommendations: list[dict[str, str]],
    findings: list[dict[str, Any]],
    boundary: str,
) -> dict[str, Any]:
    supplied = payload.get("confidenceMeter")
    source = supplied if isinstance(supplied, dict) else {}
    score = confidence_score(source.get("score"))
    measured = score is not None
    label = confidence_label(score, source.get("label"))
    basis = clean_text(source.get("basis"), 2_000)
    if not basis:
        basis = (
            "This index summarizes the strength and breadth of observed shipping evidence; it is not a probability of success."
            if measured else
            "No evidence-backed confidence index was supplied for this command. The gaps below show what would make shipping confidence assessable."
        )

    gaps: list[dict[str, str]] = []
    for item in list_of_objects(source.get("missingItems"), 6):
        title = clean_text(item.get("item") or item.get("title"), 240)
        why = clean_text(item.get("why") or item.get("detail"), 1_200)
        next_command = normalize_gap_command(item.get("nextCommand"))
        if not title or not why:
            continue
        gaps.append({"item": title, "why": why, "nextCommand": next_command})
        if len(gaps) == 4:
            break

    authored_gaps = bool(gaps)
    if not authored_gaps:
        for finding in findings:
            severity = clean_text(finding.get("severity"), 20).lower()
            title = clean_text(finding.get("title"), 240)
            detail = clean_text(finding.get("detail"), 1_200)
            if severity not in {"critical", "high"} or not title or not detail:
                continue
            next_command = recommendations[0]["invocation"] if recommendations else ""
            gaps.append({"item": f"Resolve {title}", "why": detail, "nextCommand": next_command})
            if len(gaps) == 2:
                break
        if not gaps and boundary:
            gaps.append({
                "item": "Close the largest evidence boundary",
                "why": boundary,
                "nextCommand": recommendations[0]["invocation"] if recommendations else "",
            })
        for item in recommendations:
            if len(gaps) >= 3:
                break
            gaps.append({
                "item": f"Add evidence with {item['invocation']}",
                "why": item["reason"],
                "nextCommand": item["invocation"],
            })

    blocks = 20
    filled = round((score or 0) / 100 * blocks)
    text_bar = f"[{'█' * filled}{'░' * (blocks - filled)}] {score}/100 · higher is better" if measured else f"[{'░' * blocks}] NOT ASSESSED"
    return {
        "measured": measured,
        "score": score,
        "label": label,
        "basis": basis,
        "direction": "higher-better",
        "meaning": "Evidence strength and breadth, not probability of success.",
        "textBar": text_bar,
        "missingItems": gaps[:4],
    }


def render_confidence_meter(meter: dict[str, Any]) -> str:
    measured = bool(meter["measured"])
    score = meter["score"] if measured else 0
    score_text = f"{score}<small>/100</small>" if measured else "NOT<br>ASSESSED"
    gaps = []
    for index, item in enumerate(meter["missingItems"], start=1):
        next_command = item.get("nextCommand") or ""
        gaps.append(
            f'''<article class="confidence-gap">
              <span class="gap-index">{index:02d}</span>
              <div><strong>{html.escape(item["item"])}</strong><p>{html.escape(item["why"])}</p>
              {f'<code>{html.escape(next_command)}</code>' if next_command else ''}</div>
            </article>'''
        )
    return f'''<section class="confidence-panel {'is-unmeasured' if not measured else ''}" aria-label="Shipping confidence">
      <div class="confidence-meter">
        <div class="confidence-heading">{report_icon("confidence")}<span>Shipping confidence</span></div>
        <div class="confidence-number">{score_text}</div>
        <div class="confidence-label">{html.escape(meter["label"])}</div>
        <div class="confidence-track" role="meter" aria-valuemin="0" aria-valuemax="100" {'aria-valuenow=' + chr(34) + str(score) + chr(34) if measured else 'aria-valuetext="Not assessed"'}>
          <span style="width:{score}%"></span>
        </div>
        <div class="confidence-scale"><span>0</span><span>25</span><span>50</span><span>75</span><span>100</span></div>
        <p class="confidence-meaning">Evidence strength and breadth · higher is better · not probability</p>
        <p class="confidence-basis">{html.escape(meter["basis"])}</p>
      </div>
      <div class="confidence-gaps">
        <div class="confidence-heading">{report_icon("risk")}<span>Biggest gaps to stronger confidence</span></div>
        <div class="gap-ledger">{"".join(gaps)}</div>
      </div>
    </section>'''


def normalized_wcag(payload: dict[str, Any]) -> dict[str, Any]:
    supplied = payload.get("wcagResults") if isinstance(payload.get("wcagResults"), dict) else {}
    provided = {
        clean_text(row.get("id"), 20): row
        for row in list_of_objects(supplied.get("criteria"), 100)
        if clean_text(row.get("id"), 20)
    }
    allowed = {"pass", "fail", "not-tested", "not-applicable", "needs-manual-review"}
    rows = []
    for criterion in WCAG22_REGISTRY.get("criteria", []):
        observation = provided.get(criterion["id"], {})
        outcome = clean_text(observation.get("outcome"), 40).lower()
        if outcome not in allowed:
            outcome = "not-tested"
        rows.append({
            **criterion,
            "outcome": outcome,
            "method": clean_text(observation.get("method"), 2_000),
            "evidence": clean_text(observation.get("evidence"), 4_000),
            "impact": clean_text(observation.get("impact"), 2_000),
            "fix": clean_text(observation.get("fix"), 2_000),
        })
    counts = {outcome: sum(row["outcome"] == outcome for row in rows) for outcome in allowed}
    return {
        "targetLevel": clean_text(supplied.get("targetLevel"), 10) or "AA",
        "scope": clean_text(supplied.get("scope"), 4_000) or clean_text(payload.get("scope"), 2_000),
        "pages": list_of_text(supplied.get("pages"), 100),
        "criteria": rows,
        "counts": counts,
    }


def render_wcag_document(payload: dict[str, Any], specialists: list[dict[str, Any]]) -> str:
    wcag = normalized_wcag(payload)
    generated_at = utc_now()
    status = status_name(payload.get("status"))
    scope = wcag["scope"] or "Accessibility scope was not supplied."
    pages = render_text_list(wcag["pages"], "No tested pages or states were named.")
    principle_sections = []
    for principle in ("Perceivable", "Operable", "Understandable", "Robust"):
        rows = []
        for criterion in [row for row in wcag["criteria"] if row["principle"] == principle]:
            detail = "".join([
                f'<p><strong>Method</strong> · {html.escape(criterion["method"])}</p>' if criterion["method"] else "",
                f'<p><strong>Evidence</strong> · {html.escape(criterion["evidence"])}</p>' if criterion["evidence"] else "",
                f'<p><strong>User impact</strong> · {html.escape(criterion["impact"])}</p>' if criterion["impact"] else "",
                f'<p><strong>Recommended fix</strong> · {html.escape(criterion["fix"])}</p>' if criterion["fix"] else "",
            ]) or '<p class="muted">No criterion-specific evidence was recorded.</p>'
            rows.append(
                f'''<article class="criterion outcome-{html.escape(criterion["outcome"])}">
                  <div class="criterion-id"><strong>{html.escape(criterion["id"])}</strong><span>Level {html.escape(criterion["level"])}</span></div>
                  <div class="criterion-body"><div class="criterion-title"><h3>{html.escape(criterion["title"])}</h3><span>{html.escape(criterion["outcome"].replace("-", " "))}</span></div>{detail}</div>
                </article>'''
            )
        principle_sections.append(
            f'''<details class="principle" open><summary>{html.escape(principle)} <span>{len(rows)} criteria</span></summary><div class="criterion-list">{"".join(rows)}</div></details>'''
        )
    counts = wcag["counts"]
    metric_order = (("pass", "Passed"), ("fail", "Failed"), ("needs-manual-review", "Manual review"), ("not-tested", "Not tested"), ("not-applicable", "N/A"))
    metrics = "".join(
        f'<article class="wcag-metric outcome-{outcome}"><strong>{counts[outcome]}</strong><span>{label}</span></article>'
        for outcome, label in metric_order
    )
    wcag_css = """
    .wcag-hero{display:grid;grid-template-columns:minmax(0,1.35fr) minmax(260px,.65fr);border:1px solid var(--line);background:var(--line);gap:1px;box-shadow:var(--shadow)}
    .wcag-lead,.wcag-scope{padding:clamp(28px,5vw,58px);background:var(--panel)}.wcag-lead h1{margin:10px 0 18px;font-size:clamp(42px,7vw,84px);line-height:.92;letter-spacing:-.06em}.wcag-lead p{max-width:760px;color:var(--muted-strong);font-size:18px;line-height:1.55}.wcag-scope small{display:block;color:var(--muted);font-size:10px;font-weight:850;letter-spacing:.12em;text-transform:uppercase}.wcag-scope strong{display:block;margin:10px 0 24px;font-size:30px}.wcag-scope p{color:var(--muted-strong);line-height:1.55}
    .wcag-metrics{display:grid;grid-template-columns:repeat(5,1fr);gap:1px;margin:1px 0 34px;border:1px solid var(--line);background:var(--line)}.wcag-metric{padding:20px;background:var(--panel-soft)}.wcag-metric strong,.wcag-metric span{display:block}.wcag-metric strong{font-size:34px;letter-spacing:-.05em}.wcag-metric span{margin-top:6px;color:var(--muted);font-size:10px;font-weight:800;letter-spacing:.08em;text-transform:uppercase}
    .outcome-fail{--outcome:#ff6b6b}.outcome-pass{--outcome:#c7f9cc}.outcome-needs-manual-review{--outcome:var(--risk)}.outcome-not-tested,.outcome-not-applicable{--outcome:#777}.wcag-metric strong,.criterion-title>span{color:var(--outcome)}
    .wcag-boundary,.wcag-pages{margin:26px 0;padding:22px;border:1px solid var(--line);background:var(--panel-soft)}.wcag-boundary{border-left:3px solid var(--risk)}.wcag-boundary h2,.wcag-pages h2{margin:0 0 10px;font-size:19px}.wcag-boundary p{margin:0;color:var(--muted-strong);line-height:1.6}
    .principle{margin:0 0 14px;border:1px solid var(--line);background:var(--panel)}.principle summary{display:flex;justify-content:space-between;gap:18px;padding:20px 22px;cursor:pointer;font-size:24px;font-weight:800;letter-spacing:-.03em}.principle summary span{color:var(--muted);font-size:11px;letter-spacing:.08em;text-transform:uppercase}.criterion-list{border-top:1px solid var(--line)}.criterion{display:grid;grid-template-columns:120px minmax(0,1fr);gap:24px;padding:22px;border-bottom:1px solid var(--line);border-left:3px solid var(--outcome)}.criterion:last-child{border-bottom:0}.criterion-id strong,.criterion-id span{display:block}.criterion-id strong{font:800 18px/1 ui-monospace,SFMono-Regular,Menlo,monospace}.criterion-id span{margin-top:8px;color:var(--muted);font-size:10px;text-transform:uppercase}.criterion-title{display:flex;align-items:flex-start;justify-content:space-between;gap:18px}.criterion-title h3{margin:0;font-size:18px}.criterion-title>span{white-space:nowrap;font-size:10px;font-weight:850;letter-spacing:.07em;text-transform:uppercase}.criterion-body p{margin:10px 0 0;color:var(--muted-strong);font-size:13px;line-height:1.55}
    .specialist-section{margin:32px 0}.specialist-deck{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:1px;border:1px solid var(--line);background:var(--line)}.specialist-card{display:grid;grid-template-columns:92px 1fr;gap:18px;padding:18px;background:var(--panel)}.specialist-card img{width:92px;height:92px;object-fit:cover;filter:grayscale(1);border:1px solid var(--line-bright)}.specialist-card span{color:var(--muted);font-size:10px;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.specialist-card h3{margin:5px 0 7px;font-size:22px}.specialist-card p{margin:0;color:var(--muted-strong);font-size:12px;line-height:1.5}.specialist-heading,.specialist-boundary{display:none}
    @media(max-width:760px){.wcag-hero{grid-template-columns:1fr}.wcag-metrics{grid-template-columns:repeat(2,1fr)}.criterion{grid-template-columns:1fr}.criterion-title{display:block}.criterion-title>span{display:block;margin-top:8px}.specialist-card{grid-template-columns:72px 1fr}.specialist-card img{width:72px;height:72px}}
    """
    return f'''<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
<meta name="color-scheme" content="dark"><link rel="icon" href="{REPORT_FAVICON}"><title>CARBON · WCAG 2.2 report</title><style>{REPORT_THEME_CSS}{SPECIALIST_IDENTITY_CSS}{wcag_css}</style></head>
<body><main>
  <header class="masthead">{carbon_lockup("WCAG 2.2 report")}<div class="masthead-meta"><span class="status-pill status-{html.escape(status)}">{html.escape(status)}</span><span class="updated">{html.escape(generated_at)}</span></div></header>
  <section class="wcag-hero"><div class="wcag-lead"><p class="eyebrow">WCAG 2.2 · target level {html.escape(wcag["targetLevel"])}</p><h1>Accessibility evidence, criterion by criterion.</h1><p>This report distinguishes observed passes and failures from criteria that were not tested, were not applicable, or still require human judgment.</p></div><aside class="wcag-scope"><small>Evaluation scope</small><strong>{html.escape(clean_text(payload.get("title"), 240) or "Accessibility review")}</strong><p>{html.escape(scope)}</p></aside></section>
  <section class="wcag-metrics" aria-label="WCAG outcomes">{metrics}</section>
  {render_specialists(specialists, ["accessibility"])}
  <section class="wcag-boundary"><h2>Conformance boundary</h2><p>WCAG conformance applies to complete pages and complete processes. CARBON does not claim conformance from an automated scan, a partial page, or missing manual checks. Level AA requires every applicable Level A and AA criterion to be satisfied.</p></section>
  <section class="wcag-pages"><h2>Pages and states evaluated</h2>{pages}</section>
  {"".join(principle_sections)}
  <footer class="footer"><span>CARBON is a <a href="https://testers.ai">testers.ai</a> product.</span><span>Standard: <a href="https://www.w3.org/TR/WCAG22/">W3C WCAG 2.2</a></span></footer>
</main></body></html>'''


def normalized_acr(payload: dict[str, Any]) -> dict[str, Any]:
    wcag = normalized_wcag(payload)
    metadata = payload.get("acrMetadata") if isinstance(payload.get("acrMetadata"), dict) else {}
    supplied = payload.get("acrResults") if isinstance(payload.get("acrResults"), dict) else {}
    overrides = {
        clean_text(row.get("id"), 20): row
        for row in list_of_objects(supplied.get("criteria"), 100)
        if clean_text(row.get("id"), 20)
    }
    official_levels = {value.lower(): value for value in ACR_REGISTRY["conformanceLevels"]}
    working_state = ACR_REGISTRY["workingState"]
    scope_complete = metadata.get("scopeComplete") is True
    rows = []
    for criterion in wcag["criteria"]:
        override = overrides.get(criterion["id"], {})
        raw_conformance = clean_text(override.get("conformance"), 80).lower()
        conformance = official_levels.get(raw_conformance)
        derived = False
        if not conformance:
            derived = True
            if criterion["outcome"] == "pass" and not scope_complete:
                conformance = working_state
            else:
                conformance = ACR_REGISTRY["derivation"].get(criterion["outcome"], working_state)
        remarks = clean_text(override.get("remarks"), 6_000)
        if not remarks:
            parts = []
            if criterion["evidence"]:
                parts.append(criterion["evidence"])
            if criterion["method"]:
                parts.append(f'Method: {criterion["method"]}.')
            if criterion["impact"]:
                parts.append(f'User impact: {criterion["impact"].rstrip(".")}.' )
            if criterion["fix"]:
                parts.append(f'Recommended remediation: {criterion["fix"].rstrip(".")}.' )
            if conformance == working_state:
                parts.append("Conformance not determined; complete the missing evaluation and product-scope review before publication.")
            remarks = " ".join(parts)
        rows.append({**criterion, "conformance": conformance, "remarks": remarks, "derived": derived})

    methods = list_of_text(metadata.get("evaluationMethods"), 40)
    if not methods:
        methods = list(dict.fromkeys(row["method"] for row in wcag["criteria"] if row["method"]))[:40]
    normalized_metadata = {
        "productName": clean_text(metadata.get("productName"), 240),
        "productVersion": clean_text(metadata.get("productVersion"), 120),
        "productDescription": clean_text(metadata.get("productDescription"), 4_000),
        "contact": clean_text(metadata.get("contact"), 500),
        "reportAuthor": clean_text(metadata.get("reportAuthor"), 240),
        "reportDate": clean_text(metadata.get("reportDate"), 40) or dt.date.today().isoformat(),
        "evaluationMethods": methods,
        "assistiveTechnologies": list_of_text(metadata.get("assistiveTechnologies"), 40),
        "notes": clean_text(metadata.get("notes"), 6_000),
        "scopeComplete": scope_complete,
        "productOwnerReviewed": metadata.get("productOwnerReviewed") is True,
    }
    def metadata_present(value: Any) -> bool:
        if isinstance(value, list):
            return bool(value)
        text = clean_text(value, 6_000).strip().lower()
        placeholders = ("not supplied", "not yet supplied", "unknown", "tbd", "to be determined")
        return bool(text) and not any(marker in text for marker in placeholders)

    missing_metadata = [
        field for field in ACR_REGISTRY["requiredMetadata"]
        if not metadata_present(normalized_metadata.get(field))
    ]
    unresolved = [row["id"] for row in rows if row["conformance"] == working_state]
    missing_remarks = [
        row["id"] for row in rows
        if row["conformance"] in ACR_REGISTRY["publicationReadiness"]["remarksRequiredFor"] and not row["remarks"]
    ]
    review_ready = not missing_metadata and not unresolved and not missing_remarks and scope_complete
    publication_ready = review_ready and normalized_metadata["productOwnerReviewed"]
    counts = {level: sum(row["conformance"] == level for row in rows) for level in [*ACR_REGISTRY["conformanceLevels"], working_state]}
    return {
        "schema": "carbon.acr/v1",
        "templateBasis": ACR_REGISTRY["templateBasis"],
        "metadata": normalized_metadata,
        "scope": wcag["scope"],
        "pages": wcag["pages"],
        "criteria": rows,
        "counts": counts,
        "missingMetadata": missing_metadata,
        "unresolvedCriteria": unresolved,
        "missingRemarks": missing_remarks,
        "reviewReady": review_ready,
        "publicationReady": publication_ready,
        "readinessLabel": "Owner-reviewed ACR candidate" if publication_ready else ("Review-ready draft" if review_ready else "Working draft"),
        "boundary": ACR_REGISTRY["boundary"],
    }


def render_acr_document(payload: dict[str, Any], specialists: list[dict[str, Any]]) -> str:
    acr = normalized_acr(payload)
    metadata = acr["metadata"]
    basis = acr["templateBasis"]
    generated_at = utc_now()
    title = metadata["productName"] or clean_text(payload.get("title"), 240) or "Unnamed product"
    version = metadata["productVersion"] or "Not supplied"
    description = metadata["productDescription"] or "Product description not supplied."
    contact = metadata["contact"] or "Not supplied"
    author = metadata["reportAuthor"] or "Not supplied"
    methods = render_text_list(metadata["evaluationMethods"], "Evaluation methods not supplied.")
    assistive = render_text_list(metadata["assistiveTechnologies"], "Assistive technologies were not named.")
    pages = render_text_list(acr["pages"], "No evaluated pages, states, or processes were named.")
    missing = []
    if acr["missingMetadata"]:
        missing.append("Required product metadata: " + ", ".join(acr["missingMetadata"]))
    if not metadata["scopeComplete"]:
        missing.append("Explicit confirmation that the evaluated scope covers the applicable product, complete pages, and complete processes")
    if acr["unresolvedCriteria"]:
        missing.append(f'{len(acr["unresolvedCriteria"])} criteria still marked Not Determined')
    if acr["missingRemarks"]:
        missing.append("Required remarks for: " + ", ".join(acr["missingRemarks"]))
    if not metadata["productOwnerReviewed"]:
        missing.append("Product-owner review and approval")
    readiness = render_text_list(missing, "No structural completion gaps remain; retain evidence and product-owner approval before external use.")

    level_sections = []
    for level in ("A", "AA"):
        rows = []
        for criterion in [row for row in acr["criteria"] if row["level"] == level]:
            conformance_slug = slug(criterion["conformance"])
            remarks = criterion["remarks"] or "No remarks supplied."
            source = "Evidence-derived" if criterion["derived"] else "Explicitly supplied"
            rows.append(
                f'''<tr class="acr-{html.escape(conformance_slug)}">
                  <th scope="row"><strong>{html.escape(criterion["id"])} {html.escape(criterion["title"])}</strong><span>Level {html.escape(level)}</span></th>
                  <td><span class="acr-level acr-{html.escape(conformance_slug)}">{html.escape(criterion["conformance"])}</span><small>{source}</small></td>
                  <td>{html.escape(remarks)}</td>
                </tr>'''
            )
        level_sections.append(
            f'''<section class="acr-table-section"><div class="acr-table-heading"><h2>WCAG 2.2 Level {level}</h2><span>{len(rows)} criteria</span></div>
            <div class="table-scroll"><table><thead><tr><th scope="col">Criteria</th><th scope="col">Conformance level</th><th scope="col">Remarks and explanations</th></tr></thead><tbody>{"".join(rows)}</tbody></table></div></section>'''
        )

    counts = acr["counts"]
    metric_order = ("Supports", "Partially Supports", "Does Not Support", "Not Applicable", "Not Determined")
    metrics = "".join(
        f'<article class="acr-metric acr-{slug(label)}"><strong>{counts[label]}</strong><span>{html.escape(label)}</span></article>'
        for label in metric_order
    )
    metadata_rows = (
        ("Product", title), ("Version", version), ("Report date", metadata["reportDate"]),
        ("Contact", contact), ("Report author", author), ("Template basis", f'{basis["name"]} · {basis["release"]}'),
    )
    metadata_html = "".join(f'<div><dt>{html.escape(label)}</dt><dd>{html.escape(value)}</dd></div>' for label, value in metadata_rows)
    acr_css = """
    .acr-hero{display:grid;grid-template-columns:minmax(0,1.35fr) minmax(280px,.65fr);border:1px solid var(--line);background:var(--line);gap:1px}.acr-lead,.acr-state{padding:clamp(28px,5vw,58px);background:var(--panel)}.acr-lead h1{margin:10px 0 18px;font-size:clamp(42px,7vw,78px);line-height:.94;letter-spacing:-.06em}.acr-lead p{max-width:760px;color:var(--muted-strong);font-size:17px;line-height:1.6}.acr-state small{display:block;color:var(--muted);font-size:10px;font-weight:850;letter-spacing:.12em;text-transform:uppercase}.acr-state strong{display:block;margin:11px 0 18px;font-size:30px}.acr-state p{color:var(--muted-strong);line-height:1.55}
    .acr-metrics{display:grid;grid-template-columns:repeat(5,1fr);gap:1px;margin:1px 0 30px;border:1px solid var(--line);background:var(--line)}.acr-metric{padding:20px;background:var(--panel-soft)}.acr-metric strong,.acr-metric span{display:block}.acr-metric strong{font-size:34px}.acr-metric span{margin-top:6px;color:var(--muted);font-size:10px;font-weight:800;letter-spacing:.07em;text-transform:uppercase}.acr-supports strong{color:var(--good)}.acr-partially-supports strong,.acr-not-determined strong{color:var(--risk)}.acr-does-not-support strong{color:var(--bad)}
    .acr-meta,.acr-context,.acr-readiness,.acr-boundary{margin:26px 0;border:1px solid var(--line);padding:24px;background:var(--panel)}.acr-meta{display:grid;grid-template-columns:repeat(3,1fr);gap:1px;padding:1px;background:var(--line)}.acr-meta>div{padding:18px;background:var(--panel)}.acr-meta dt{color:var(--muted);font-size:9px;font-weight:850;letter-spacing:.1em;text-transform:uppercase}.acr-meta dd{margin:7px 0 0;font-size:15px;overflow-wrap:anywhere}.acr-context{display:grid;grid-template-columns:repeat(2,1fr);gap:30px}.acr-context h2,.acr-readiness h2,.acr-boundary h2{margin:0 0 12px;font-size:20px}.acr-context p,.acr-boundary p{color:var(--muted-strong);line-height:1.6}.acr-readiness{border-left:3px solid var(--risk)}.acr-boundary{border-left:3px solid var(--bad)}
    .acr-table-section{margin:28px 0;border:1px solid var(--line);background:var(--panel)}.acr-table-heading{display:flex;align-items:center;justify-content:space-between;padding:20px 22px}.acr-table-heading h2{margin:0;font-size:26px}.acr-table-heading span{color:var(--muted);font-size:10px;font-weight:800;text-transform:uppercase}.table-scroll{overflow-x:auto}table{width:100%;border-collapse:collapse;min-width:840px}th,td{border-top:1px solid var(--line);border-right:1px solid var(--line);padding:17px;text-align:left;vertical-align:top;font-size:13px;line-height:1.55}th:last-child,td:last-child{border-right:0}thead th{color:var(--muted);font-size:9px;letter-spacing:.1em;text-transform:uppercase}tbody th{width:28%}tbody th strong,tbody th span,.acr-level,td small{display:block}tbody th span,td small{margin-top:7px;color:var(--muted);font-size:9px;text-transform:uppercase}.acr-level{font-weight:850}.acr-level.acr-supports{color:var(--good)}.acr-level.acr-partially-supports,.acr-level.acr-not-determined{color:var(--risk)}.acr-level.acr-does-not-support{color:var(--bad)}
    .specialist-section{margin:30px 0}.specialist-deck{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:1px;border:1px solid var(--line);background:var(--line)}.specialist-card{display:grid;grid-template-columns:92px 1fr;gap:18px;padding:18px;background:var(--panel)}.specialist-card img{width:92px;height:92px;object-fit:cover;filter:grayscale(1);border:1px solid var(--line-bright)}.specialist-card span{color:var(--muted);font-size:10px;font-weight:800;text-transform:uppercase}.specialist-card h3{margin:5px 0 7px;font-size:22px}.specialist-card p{margin:0;color:var(--muted-strong);font-size:12px;line-height:1.5}.specialist-heading,.specialist-boundary{display:none}
    @media(max-width:760px){.acr-hero{grid-template-columns:1fr}.acr-metrics{grid-template-columns:repeat(2,1fr)}.acr-meta,.acr-context{grid-template-columns:1fr}.specialist-card{grid-template-columns:72px 1fr}.specialist-card img{width:72px;height:72px}}
    """
    return f'''<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
<meta name="color-scheme" content="dark"><link rel="icon" href="{REPORT_FAVICON}"><title>CARBON · Accessibility Conformance Report · {html.escape(title)}</title><style>{REPORT_THEME_CSS}{SPECIALIST_IDENTITY_CSS}{acr_css}</style></head>
<body><main>
  <header class="masthead">{carbon_lockup("Accessibility Conformance Report")}<div class="masthead-meta"><span class="status-pill status-{'passed' if acr['publicationReady'] else 'partial'}">{html.escape(acr['readinessLabel'])}</span><span class="updated">{html.escape(generated_at)}</span></div></header>
  <section class="acr-hero"><div class="acr-lead"><p class="eyebrow">ACR · {html.escape(basis['name'])} · {html.escape(basis['standard'])}</p><h1>{html.escape(title)}</h1><p>{html.escape(description)}</p></div><aside class="acr-state"><small>Document state</small><strong>{html.escape(acr['readinessLabel'])}</strong><p>Version {html.escape(version)}. This is an evidence-derived conformance disclosure, not a certification.</p></aside></section>
  <section class="acr-metrics" aria-label="ACR conformance levels">{metrics}</section>
  {render_specialists(specialists, ["accessibility"])}
  <dl class="acr-meta">{metadata_html}</dl>
  <section class="acr-context"><div><h2>Evaluation scope</h2><p>{html.escape(acr['scope'] or 'Accessibility scope was not supplied.')}</p><h3>Pages, states, and processes</h3>{pages}</div><div><h2>Evaluation methods used</h2>{methods}<h3>Assistive technologies</h3>{assistive}</div></section>
  <section class="acr-readiness"><h2>Publication-readiness gaps</h2>{readiness}</section>
  {"".join(level_sections)}
  <section class="acr-boundary"><h2>Evidence and authority boundary</h2><p>{html.escape(acr['boundary'])}</p></section>
  <footer class="footer"><span>CARBON is a <a href="https://testers.ai">testers.ai</a> product.</span><span>Template basis: <a href="{html.escape(basis['url'], quote=True)}">ITI {html.escape(basis['name'])}</a> · Standard: <a href="https://www.w3.org/TR/WCAG22/">WCAG 2.2</a></span></footer>
</main></body></html>'''


def render_document(root: Path, payload: dict[str, Any]) -> str:
    command = clean_text(payload.get("command"), 120) or "carbon"
    title = clean_text(payload.get("title"), 240) or f"{command} result"
    summary = clean_text(payload.get("summary"), 8_000) or "CARBON completed the requested workflow."
    status = status_name(payload.get("status"))
    scope = clean_text(payload.get("scope"), 2_000) or root.name
    boundary = clean_text(payload.get("evidenceBoundary"), 4_000) or "This page reports only the evidence observed during this command. Missing evidence remains explicit."
    metrics = list_of_objects(payload.get("metrics"), 24)
    sections = list_of_objects(payload.get("sections"), 24)
    findings = list_of_objects(payload.get("findings"), 100)
    artifacts = list_of_objects(payload.get("artifacts"), 100)
    next_actions = list_of_text(payload.get("nextActions"), 40)
    recommendations = recommended_commands(payload, command, scope)
    confidence = confidence_meter(payload, recommendations, findings, boundary)
    domain_ids = infer_domain_ids(payload, command)
    specialists = selected_specialists(payload, domain_ids)
    has_observed_content = bool(metrics or sections or findings or artifacts or list_of_objects(payload.get("chatScreenshots"), 8))
    domain_start_state = render_domain_start_state(domain_ids, has_observed_content)
    generated_at = utc_now()
    status_class = "blocked" if status == "partial" else status
    promotion = select_promotion(payload.get("promotionContext"), f"command-report:{command}")
    supplied_decision = payload.get("decision") if isinstance(payload.get("decision"), dict) else {}
    decision_label = clean_text(supplied_decision.get("label"), 80) or ("HOLD" if status in {"failed", "blocked"} else "RESULT")
    decision_headline = clean_text(supplied_decision.get("headline"), 300) or title
    decision_summary = clean_text(supplied_decision.get("summary"), 2_000) or summary
    decision_basis = clean_text(supplied_decision.get("basis"), 2_000) or boundary
    decision_tone = clean_text(supplied_decision.get("tone"), 40) or ("risk" if status in {"failed", "blocked", "partial"} else "neutral")
    narrative = payload.get("narrative") if isinstance(payload.get("narrative"), list) else []
    if not narrative:
        narrative = [
            {"value": scope},
            {"value": f"{len(findings)} finding(s) recorded", "tone": "risk" if findings else "neutral"},
            {"value": f"{len(artifacts)} artifact(s) linked" if artifacts else "Evidence boundary recorded"},
            {"value": decision_label, "tone": "risk" if decision_tone == "risk" else "neutral"},
            {"value": next_actions[0] if next_actions else (recommendations[0]["command"] if recommendations else "No next action supplied")},
        ]
    stats_visuals = render_stats_visuals(payload.get("statsVisuals") if isinstance(payload.get("statsVisuals"), dict) else {})

    command_css = """
    .command-hero{display:grid;grid-template-columns:minmax(0,1.5fr) minmax(240px,.5fr);gap:1px;margin-bottom:1px;border:1px solid var(--line);background:var(--line);box-shadow:var(--shadow)}
    .command-lead,.command-aside{min-width:0;background:var(--panel);padding:clamp(28px,5vw,58px)}
    .command-lead{background:linear-gradient(135deg,rgba(255,255,255,.07),transparent 48%),var(--panel)}
    .command-lead h1{max-width:850px;margin:8px 0 18px;font-size:clamp(38px,7vw,82px);line-height:.94;letter-spacing:-.06em}
    .command-summary{max-width:830px;margin:0;color:var(--muted-strong);font-size:clamp(16px,2.2vw,22px);line-height:1.5}
    .eyebrow{margin:0;color:var(--text);font-size:11px;font-weight:850;letter-spacing:.15em;text-transform:uppercase}
    .command-aside{display:flex;flex-direction:column;justify-content:space-between;gap:30px}
    .command-aside strong{display:block;margin-top:12px;font-size:clamp(30px,4vw,48px);line-height:1;letter-spacing:-.05em;overflow-wrap:anywhere;text-transform:capitalize}
    .aside-fact{min-width:0;border-top:1px solid var(--line);padding-top:16px}.aside-fact:first-child{border-top:0;padding-top:0}.aside-fact small{display:block;color:var(--muted);font-size:10px;font-weight:800;letter-spacing:.1em;text-transform:uppercase}.aside-fact p{margin:7px 0 0;color:var(--muted-strong);overflow-wrap:anywhere}
    .signal-ledger{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));margin:0 0 32px;border:1px solid var(--line);border-top:0;background:var(--line);gap:1px}
    .signal-row{display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:13px;min-width:0;min-height:92px;padding:18px;background:var(--panel-soft)}
    .signal-icon{display:grid;width:38px;height:38px;place-items:center;border:1px solid var(--line-bright);border-radius:50%;color:var(--text)}.signal-icon .section-icon{width:18px;height:18px}
    .signal-copy{min-width:0}.signal-copy span,.signal-copy small{display:block;overflow-wrap:anywhere}.signal-copy span{font-size:11px;font-weight:800;letter-spacing:.07em;text-transform:uppercase}.signal-copy small{margin-top:3px;color:var(--muted);font-size:11px}.signal-row>strong{min-width:0;font-size:22px;letter-spacing:-.04em;overflow-wrap:anywhere}.tone-risk>strong,.tone-risk .signal-icon,.tone-risk>.section-index{color:var(--risk)}
    .specialist-section{margin:0 0 32px;border:1px solid var(--line);background:var(--panel);box-shadow:var(--shadow)}.specialist-heading{display:flex;align-items:center;justify-content:space-between;gap:18px;padding:18px 22px;border-bottom:1px solid var(--line)}.specialist-heading span{color:var(--muted);font-size:10px;font-weight:850;letter-spacing:.11em;text-transform:uppercase}.specialist-heading strong{font-size:13px}.specialist-deck{display:grid;grid-template-columns:repeat(auto-fit,minmax(290px,1fr));gap:1px;background:var(--line)}.specialist-card{display:grid;grid-template-columns:96px 1fr;gap:18px;min-height:154px;padding:20px;background:var(--panel-soft)}.specialist-card img{width:96px;height:112px;object-fit:cover;filter:grayscale(1);border:1px solid var(--line-bright)}.specialist-fallback{width:96px;height:112px;border:1px solid var(--line-bright);background:repeating-linear-gradient(135deg,#111 0,#111 8px,#1a1a1a 8px,#1a1a1a 16px)}.specialist-card span{color:var(--muted);font-size:10px;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.specialist-card h3{margin:5px 0 8px;font-size:24px;letter-spacing:-.04em}.specialist-card p{margin:0;color:var(--muted-strong);font-size:12px;line-height:1.52}.specialist-boundary{margin:0;padding:13px 20px;border-top:1px solid var(--line);color:var(--muted);font-size:11px}
    .domain-start-section{margin:0 0 32px;border:1px solid var(--risk-line);background:var(--risk-soft);box-shadow:var(--shadow)}.domain-start-heading{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:18px 22px;border-bottom:1px solid var(--risk-line)}.domain-start-heading span{color:var(--risk);font-size:10px;font-weight:850;letter-spacing:.11em;text-transform:uppercase}.domain-start-heading strong{font-size:13px}.domain-start-section>p{max-width:900px;margin:0;padding:20px 22px;color:var(--muted-strong);line-height:1.55}.domain-start-deck{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:1px;border-top:1px solid var(--risk-line);background:var(--line)}.domain-start-card{padding:22px;background:var(--panel)}.domain-start-card>span{color:var(--risk);font-size:10px;font-weight:850;letter-spacing:.11em;text-transform:uppercase}.domain-start-card h3{margin:8px 0 10px;font-size:22px;letter-spacing:-.04em}.domain-start-card p{margin:0;color:var(--muted-strong);font-size:13px;line-height:1.55}.domain-start-card .domain-start-evidence{margin-top:14px;padding-top:14px;border-top:1px solid var(--line)}.domain-start-card details{margin-top:16px;border-top:1px solid var(--line);padding-top:14px}.domain-start-card summary{cursor:pointer;font-size:12px;font-weight:800}.domain-start-card ul{display:grid;gap:7px;margin:12px 0 0;padding-left:18px;color:var(--muted);font-size:12px;line-height:1.45}
    .confidence-panel{display:grid;grid-template-columns:minmax(300px,.8fr) minmax(0,1.2fr);gap:1px;margin:0 0 32px;border:1px solid var(--line);background:var(--line);box-shadow:var(--shadow)}.confidence-meter,.confidence-gaps{padding:clamp(24px,4vw,42px);background:var(--panel)}.confidence-heading{display:flex;align-items:center;gap:10px;color:var(--muted-strong);font-size:11px;font-weight:850;letter-spacing:.11em;text-transform:uppercase}.confidence-heading .section-icon{width:19px;height:19px}.confidence-number{margin:30px 0 4px;font-size:clamp(48px,8vw,92px);font-weight:850;line-height:.78;letter-spacing:-.07em}.confidence-number small{font-size:.24em;letter-spacing:-.02em}.is-unmeasured .confidence-number{font-size:clamp(32px,5vw,55px);line-height:.86;color:var(--muted-strong)}.confidence-label{margin:13px 0 24px;font-size:14px;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.confidence-track{height:18px;border:1px solid var(--line-bright);background:repeating-linear-gradient(90deg,transparent 0,transparent calc(5% - 1px),var(--line) calc(5% - 1px),var(--line) 5%);overflow:hidden}.confidence-track span{display:block;height:100%;background:#fff}.is-unmeasured .confidence-track span{width:0!important}.confidence-scale{display:flex;justify-content:space-between;margin-top:7px;color:var(--muted);font:700 9px/1 ui-monospace,SFMono-Regular,Menlo,monospace}.confidence-meaning{margin:22px 0 8px;color:var(--text);font-size:11px;font-weight:800;text-transform:uppercase}.confidence-basis{margin:0;color:var(--muted-strong);font-size:13px;line-height:1.55}.gap-ledger{margin-top:22px;border-top:1px solid var(--line)}.confidence-gap{display:grid;grid-template-columns:40px 1fr;gap:14px;padding:18px 0;border-bottom:1px solid var(--line)}.gap-index{color:var(--risk);font:800 12px/1.4 ui-monospace,SFMono-Regular,Menlo,monospace}.confidence-gap strong{font-size:15px}.confidence-gap p{margin:6px 0 9px;color:var(--muted-strong);font-size:13px;line-height:1.5}.confidence-gap code{color:var(--text);font:700 11px/1.45 ui-monospace,SFMono-Regular,Menlo,monospace;overflow-wrap:anywhere}
    .editorial-section,.findings-section,.artifact-section,.next-section,.recommended-section{display:grid;grid-template-columns:76px minmax(0,1fr);border-top:1px solid var(--line);padding:34px 0}
    .section-index{color:#666;font:700 13px/1 ui-monospace,SFMono-Regular,Menlo,monospace;letter-spacing:.12em}.section-body>.icon-heading{margin-top:0;font-size:clamp(24px,4vw,40px);letter-spacing:-.04em}.section-intro{max-width:850px;color:var(--muted-strong);font-size:17px}
    .evidence-stream{margin-top:20px}.evidence-entry{display:grid;grid-template-columns:28px minmax(0,1fr);gap:14px;padding:0 0 24px}.evidence-entry:last-child{padding-bottom:0}.entry-rail{position:relative}.entry-rail:after{position:absolute;top:18px;bottom:-6px;left:7px;width:1px;background:var(--line);content:""}.evidence-entry:last-child .entry-rail:after{display:none}.entry-dot{position:relative;z-index:1;display:block;width:15px;height:15px;border:3px solid #000;border-radius:50%;background:var(--muted)}.entry-dot.status-completed,.entry-dot.status-passed{background:var(--good)}.entry-dot.status-blocked,.entry-dot.status-partial{background:var(--risk)}.entry-dot.status-failed{background:var(--bad)}
    .entry-title{display:flex;align-items:center;justify-content:space-between;gap:14px}.evidence-entry p{margin:7px 0;color:var(--muted-strong)}.evidence-entry small{color:var(--muted)}
    .smart-list{display:grid;gap:0;margin:0;padding:0;list-style:none}.smart-list li{display:grid;grid-template-columns:28px 1fr;gap:14px;border-top:1px solid var(--line);padding:15px 0}.list-marker{width:8px;height:8px;margin-top:8px;border:1px solid var(--text);border-radius:50%}
    .finding-ledger{display:grid;gap:1px;background:var(--line);border:1px solid var(--line)}.finding-line{display:grid;grid-template-columns:145px 1fr;gap:24px;padding:22px;background:var(--panel)}.finding-severity{display:flex;align-items:center;gap:9px;color:var(--risk);font-size:11px;font-weight:850;letter-spacing:.09em;text-transform:uppercase}.finding-line h3{margin:0;font-size:20px}.finding-line p{color:var(--muted-strong)}.evidence-note,.fix-note{font-size:12px}.severity-critical .finding-severity,.severity-high .finding-severity{color:var(--bad)}
    .artifact-ledger{border:1px solid var(--line)}.artifact-row{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:14px;border-bottom:1px solid var(--line);padding:16px;color:var(--text);text-decoration:none}.artifact-row:last-child{border-bottom:0}.artifact-row:hover{background:var(--accent-soft)}.artifact-icon{display:grid;width:34px;height:34px;place-items:center;border:1px solid var(--line);border-radius:50%}.artifact-icon .section-icon{width:17px}.artifact-row strong,.artifact-row small{display:block}.artifact-row small{margin-top:2px;color:var(--muted);overflow-wrap:anywhere}.artifact-arrow{font-size:22px}
    .boundary{margin:30px 0 0;border-left:3px solid var(--risk);padding:18px 20px;background:var(--risk-soft)}.boundary .icon-heading{margin:0 0 9px;color:var(--risk);font-size:17px}.boundary p{margin:0;color:var(--muted-strong)}
    .recommended-section{margin-top:34px;border-top:1px solid var(--line-bright);border-bottom:1px solid var(--line-bright)}.command-deck{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:1px;margin-top:22px;border:1px solid var(--line);background:var(--line)}.command-card{display:flex;min-height:236px;flex-direction:column;padding:22px;background:var(--panel);transition:background .18s ease}.command-card:hover{background:var(--panel-soft)}.command-card-label{color:var(--risk);font-size:10px;font-weight:850;letter-spacing:.13em;text-transform:uppercase}.command-card code{display:block;margin:15px 0 16px;color:var(--text);font:700 15px/1.45 ui-monospace,SFMono-Regular,Menlo,monospace;overflow-wrap:anywhere}.command-card p{margin:0 0 20px;color:var(--muted-strong);font-size:13px;line-height:1.55}.command-action{display:flex;align-items:center;justify-content:space-between;gap:16px;width:100%;margin-top:auto;border:1px solid var(--line-bright);border-radius:0;padding:12px 14px;background:#fff;color:#000;font:850 11px/1 ui-monospace,SFMono-Regular,Menlo,monospace;letter-spacing:.07em;text-transform:uppercase;cursor:pointer;transition:background .16s ease,color .16s ease,transform .16s ease}.command-action:hover{background:var(--risk);color:#000;transform:translateY(-1px)}.command-action:focus-visible{outline:2px solid var(--risk);outline-offset:3px}.command-action:disabled{cursor:wait;opacity:.62;transform:none}.command-action-status{display:block;min-height:28px;margin-top:10px;color:var(--muted);font-size:10px;line-height:1.4}.command-action-status[data-tone="good"]{color:var(--good)}.command-action-status[data-tone="risk"]{color:var(--risk)}.command-action-status[data-tone="bad"]{color:var(--bad)}
    @media(max-width:760px){.command-hero,.confidence-panel{grid-template-columns:1fr}.command-lead,.command-aside{padding:27px 20px}.editorial-section,.findings-section,.artifact-section,.next-section,.recommended-section{grid-template-columns:1fr;gap:14px}.finding-line{grid-template-columns:1fr}.entry-title{align-items:flex-start;flex-direction:column}.signal-ledger{grid-template-columns:1fr}.masthead{align-items:flex-start}.masthead-meta{align-items:flex-end;flex-direction:column}.specialist-card{grid-template-columns:76px 1fr}.specialist-card img,.specialist-fallback{width:76px;height:88px}.specialist-heading,.domain-start-heading{align-items:flex-start;flex-direction:column}}
    """

    next_section = ""
    if next_actions:
        next_section = f'''<section class="next-section"><div class="section-index">→</div><div class="section-body">
          {report_heading("Next actions", "change")}{render_text_list(next_actions)}</div></section>'''

    footer_offer = "" if not promotion else (
        f'<span class="promotion-offer"><b>{html.escape(promotion["label"])}</b>'
        f'{html.escape(promotion["text"])} <a href="{html.escape(promotion["url"])}">'
        f'{html.escape(promotion["linkLabel"])}</a></span>'
    )
    return f'''<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
<meta name="color-scheme" content="dark"><link rel="icon" href="{REPORT_FAVICON}">
<title>CARBON · {html.escape(command)} · {html.escape(title)}</title><style>{REPORT_THEME_CSS}{REPORT_VISUAL_CSS}{SPECIALIST_IDENTITY_CSS}{command_css}</style></head>
<body><main>
  <header class="masthead">{carbon_lockup("Command report")}<div class="masthead-meta"><span class="status-pill status-{html.escape(status_class)}">{html.escape(status)}</span><span class="updated">{html.escape(generated_at)}</span></div></header>
  {render_decision_hero(decision_label, decision_headline, decision_summary, decision_basis, decision_tone)}
  {render_evidence_spine(narrative)}
  <section class="command-hero forensic-summary">
    <div class="command-lead"><p class="eyebrow">/{html.escape(command)} · command result</p><h1>{html.escape(title)}</h1><p class="command-summary">{html.escape(summary)}</p></div>
    <aside class="command-aside"><div class="aside-fact"><small>Result</small><strong>{html.escape(status)}</strong></div><div class="aside-fact"><small>Scope</small><p>{html.escape(scope)}</p></div><div class="aside-fact"><small>Generated</small><p>{html.escape(generated_at)}</p></div></aside>
  </section>
  {render_specialists(specialists, domain_ids)}
  {domain_start_state}
  {render_confidence_meter(confidence)}
  {stats_visuals}
  {render_metrics(metrics)}
  {render_sections(sections)}
  {render_findings(findings)}
  {render_artifacts(root, artifacts)}
  {next_section}
  <section class="boundary">{report_heading("Evidence boundary", "risk", level=3)}<p>{html.escape(boundary)}</p></section>
  {render_recommended_commands(recommendations)}
  <footer class="footer"><span>CARBON is a <a href="https://testers.ai">testers.ai</a> product.</span>{schema_provenance_html()}{footer_offer}</footer>
</main>{REPORT_ACTION_SCRIPT}</body></html>'''


def render(root: Path, payload: dict[str, Any]) -> dict[str, Any]:
    root = root.expanduser().resolve()
    if not root.is_dir():
        raise ValueError("root must be an existing project directory")
    payload = dict(payload)
    command = clean_text(payload.get("command"), 120) or "carbon"
    stamp = dt.datetime.now(dt.timezone.utc).strftime("%Y%m%dT%H%M%SZ")
    report_dir = root / ".carbon" / "reports" / "commands"
    report_dir.mkdir(parents=True, exist_ok=True)
    output = report_dir / f"{stamp}-{slug(command)}-{secrets.token_hex(2)}.html"
    domain_ids = infer_domain_ids(payload, command)
    specialists = selected_specialists(payload, domain_ids)
    wcag_output: Path | None = None
    acr_output: Path | None = None
    if should_render_accessibility_companions(payload, command, domain_ids):
        wcag_output = report_dir / f"{stamp}-wcag-2-2-{secrets.token_hex(2)}.html"
        wcag_document = render_wcag_document(payload, specialists or [SPECIALISTS["alejandro"], SPECIALISTS["hiroshi"]])
        with tempfile.NamedTemporaryFile("w", encoding="utf-8", dir=report_dir, delete=False) as handle:
            handle.write(wcag_document)
            wcag_temporary = Path(handle.name)
        os.replace(wcag_temporary, wcag_output)
        artifacts = list_of_objects(payload.get("artifacts"), 100)
        artifacts.append({
            "label": "WCAG 2.2 criterion report",
            "path": str(wcag_output),
            "detail": "Level A and AA outcomes with tested, failed, not-tested, not-applicable, and manual-review states.",
            "icon": "coverage",
        })
        payload["artifacts"] = artifacts
        acr_output = report_dir / f"{stamp}-accessibility-conformance-report-{secrets.token_hex(2)}.html"
        acr_document = render_acr_document(payload, specialists or [SPECIALISTS["alejandro"], SPECIALISTS["hiroshi"]])
        with tempfile.NamedTemporaryFile("w", encoding="utf-8", dir=report_dir, delete=False) as handle:
            handle.write(acr_document)
            acr_temporary = Path(handle.name)
        os.replace(acr_temporary, acr_output)
        artifacts = list_of_objects(payload.get("artifacts"), 100)
        artifacts.append({
            "label": "Accessibility Conformance Report",
            "path": str(acr_output),
            "detail": "Evidence-derived ACR working draft structured around the ITI VPAT 2.5Rev WCAG Edition.",
            "icon": "evidence",
        })
        payload["artifacts"] = artifacts
    document = render_document(root, payload)
    recommendations = recommended_commands(payload, command, clean_text(payload.get("scope"), 2_000) or root.name)
    confidence = confidence_meter(
        payload,
        recommendations,
        list_of_objects(payload.get("findings"), 100),
        clean_text(payload.get("evidenceBoundary"), 4_000) or "This command did not supply an explicit evidence boundary.",
    )
    with tempfile.NamedTemporaryFile("w", encoding="utf-8", dir=report_dir, delete=False) as handle:
        handle.write(document)
        temporary = Path(handle.name)
    os.replace(temporary, output)
    relative_output = output.relative_to(root).as_posix()
    relative_chat_link = f"[Open the CARBON report]({relative_output})"
    absolute_chat_link = f"[Open the CARBON report]({output})"
    claude_chat_link = absolute_chat_link
    result = {
        "schema": "carbon.command-report/v1",
        "schemaOrigin": schema_provenance(),
        "command": command,
        "status": status_name(payload.get("status")),
        "reportPath": str(output),
        "reportUrl": output.as_uri(),
        "chatLink": relative_chat_link,
        "claudeChatLink": claude_chat_link,
        "claudeFileLink": claude_chat_link,
        "codexChatLink": absolute_chat_link,
        "codexFileLink": absolute_chat_link,
        "absoluteChatLink": absolute_chat_link,
        "presentation": {
            "schema": "carbon.in-agent-presentation/v1",
            "preferredSurface": "host-html-preview",
            "fallbackSurface": "host-embedded-browser",
            "externalBrowserFallback": "explicit-only",
            "openByDefault": True,
            "url": output.as_uri(),
            "artifactPath": str(output),
            "title": "CARBON Report",
            "chatLink": relative_chat_link,
            "claudeChatLink": claude_chat_link,
            "codexChatLink": absolute_chat_link,
            "instruction": "Open this self-contained HTML beside chat. In Codex, open codexChatLink as a native file preview or navigate the built-in Browser to the protected codexBrowserUrl when returned by MCP. In Claude, attach the HTML artifact and use the absolute in-workspace claudeChatLink so its file viewer does not resolve the path against the wrong root. Never launch the operating-system browser. If no internal surface exists, leave chatLink available as the fallback.",
        },
        "recommendedCommands": recommendations,
        "confidenceMeter": confidence,
        "chatScreenshots": chat_screenshots(root, payload),
        "singleFile": True,
    }
    if wcag_output:
        wcag_relative = wcag_output.relative_to(root).as_posix()
        result.update({
            "wcagReportPath": str(wcag_output),
            "wcagReportUrl": wcag_output.as_uri(),
            "wcagReportLink": f"[Open the WCAG 2.2 report]({wcag_relative})",
            "wcagClaudeFileLink": f"[Open the WCAG 2.2 report]({wcag_output})",
            "wcagCodexFileLink": f"[Open the WCAG 2.2 report]({wcag_output})",
            "wcagAbsoluteReportLink": f"[Open the WCAG 2.2 report]({wcag_output})",
            "wcagStandard": "WCAG 2.2",
            "wcagTargetLevel": normalized_wcag(payload)["targetLevel"],
        })
    if acr_output:
        acr = normalized_acr(payload)
        acr_relative = acr_output.relative_to(root).as_posix()
        result.update({
            "acrReportPath": str(acr_output),
            "acrReportUrl": acr_output.as_uri(),
            "acrReportLink": f"[Open the Accessibility Conformance Report]({acr_relative})",
            "acrClaudeFileLink": f"[Open the Accessibility Conformance Report]({acr_output})",
            "acrCodexFileLink": f"[Open the Accessibility Conformance Report]({acr_output})",
            "acrAbsoluteReportLink": f"[Open the Accessibility Conformance Report]({acr_output})",
            "acrTemplateBasis": acr["templateBasis"]["name"],
            "acrReadiness": acr["readinessLabel"],
            "acrReviewReady": acr["reviewReady"],
            "acrPublicationReady": acr["publicationReady"],
            "acrMissingMetadata": acr["missingMetadata"],
            "acrUnresolvedCriteria": len(acr["unresolvedCriteria"]),
        })
    return result


def parser() -> argparse.ArgumentParser:
    root = argparse.ArgumentParser(description=__doc__)
    sub = root.add_subparsers(dest="action", required=True)
    render_parser = sub.add_parser("render")
    render_parser.add_argument("--root", default=".")
    source = render_parser.add_mutually_exclusive_group(required=True)
    source.add_argument("--payload-json")
    source.add_argument("--input")
    return root


def main() -> int:
    args = parser().parse_args()
    if args.payload_json:
        payload = json.loads(args.payload_json)
    else:
        payload = json.loads(Path(args.input).expanduser().read_text(encoding="utf-8"))
    if not isinstance(payload, dict):
        raise ValueError("command report payload must be a JSON object")
    print(json.dumps(render(Path(args.root), payload), indent=2))
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
