///|
/// Offline review document: no scripts, fonts, images or network dependencies.
/// All input-derived text is escaped; navigation IDs are generated by position.
pub fn render_audit_html(trajectory : @evidence.Trajectory) -> String {
  render_audit_html_in(trajectory, English)
}

///|
/// Localized presentation; input-derived text and machine codes are preserved.
pub fn render_audit_html_in(
  trajectory : @evidence.Trajectory,
  language : ReportLanguage,
) -> String {
  let out = StringBuilder()
  let style =
    #|:root{color-scheme:light;--ink:#263348;--muted:#6e7984;--paper:#f6f3ed;--line:#e2e4df}*{box-sizing:border-box}body{margin:0;background:var(--paper);color:var(--ink);font-family:system-ui,-apple-system,"Segoe UI",sans-serif;font-size:14px;line-height:1.6}.wrap{max-width:1200px;margin:auto;padding:40px 36px 70px}.brand{font-size:11px;letter-spacing:2px;font-weight:750;color:#247b68}.mast{display:flex;gap:30px;justify-content:space-between;align-items:start;padding:20px 0 26px;border-bottom:1px solid var(--line)}h1{font-size:34px;letter-spacing:-1.2px;line-height:1.18;margin:0 0 12px}h2{font-size:19px;margin:0 0 6px}h3{font-size:15px;margin:0}p{margin:6px 0}.muted{color:var(--muted)}.tag,.badge{display:inline-block;border:1px solid var(--line);border-radius:5px;padding:4px 10px;font-size:11px;white-space:nowrap}.badge{font-weight:750;border:0;color:white;font-size:12px}.cards{display:grid;grid-template-columns:repeat(5,1fr);gap:12px;margin:24px 0}.card{display:block;background:#fffdf9;border:1px solid var(--line);border-radius:8px;padding:14px 18px;color:inherit;text-decoration:none}.card strong{display:block;font-size:29px;line-height:1.3}.card small{font-size:11px;letter-spacing:1px}.panel{background:#fffdf9;border:1px solid var(--line);border-radius:9px;padding:23px;margin-top:20px}.panel-head{display:flex;justify-content:space-between;gap:12px;align-items:center}.chart svg{width:100%;height:auto;margin-top:12px}.table-scroll{overflow:auto}table{border-collapse:collapse;width:100%;font-size:12px;margin-top:14px}th{text-align:left;color:var(--muted);font-size:10px;letter-spacing:.7px;text-transform:uppercase;font-weight:650}td,th{padding:11px 12px;border-bottom:1px solid var(--line);vertical-align:top}td:first-child,th:first-child{padding-left:0}td.num,th.num{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap}a{color:#246e70}details{border-top:1px solid var(--line);padding:17px 0}details:first-of-type{border-top:0}summary{cursor:pointer;display:flex;align-items:center;gap:13px}summary .grow{flex:1}.details-inner{margin:15px 0 0 47px}.finding{border-left:3px solid #d8dfdb;padding:5px 12px;margin:9px 0}.finding code{font-size:11px;color:#247b68}.notes{font-size:12px;color:var(--muted);border-top:1px solid var(--line);padding-top:22px;margin-top:28px}.meta{font-size:12px;text-align:right;max-width:290px;overflow-wrap:anywhere}.legend{display:flex;flex-wrap:wrap;gap:16px;font-size:11px;color:var(--muted)}.legend span:before{content:"";display:inline-block;width:7px;height:7px;border-radius:50%;background:var(--c);margin-right:6px}.study-id{overflow-wrap:anywhere}.notice{font-size:12px;background:#edf3f0;border-radius:6px;padding:11px 14px;margin-top:17px}tr:target,details:target{background:#edf3f0}code{overflow-wrap:anywhere}@media(max-width:700px){.wrap{padding:24px 16px}.mast{display:block}h1{font-size:27px}.meta{text-align:left;margin-top:16px}.cards{gap:6px}.card{padding:10px}.card strong{font-size:23px}.panel{padding:15px}.details-inner{margin-left:0}.panel-head{display:block}}@media print{body{background:white}.wrap{padding:0;max-width:none}.panel{break-inside:avoid}.cards{margin:15px 0}details{break-inside:avoid}details::details-content{content-visibility:visible}summary{list-style:none}a{color:inherit;text-decoration:none}}
  out.write_string(
    tr(
      language, "Target-lesion review · ",
      "<!doctype html><html lang=\"zh-CN\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'none'; style-src 'unsafe-inline'; img-src data:; base-uri 'none'; form-action 'none'\"><title>靶病灶复核 · ",
    ) +
    html_escape(trajectory.study.id) +
    "
LESION FOLLOWUP / OFFLINE REVIEW

Every change.
Its reference. Its evidence.

An ordered review of manually measured CT target lesions.

RECIST 1.1 · TARGET SUBSET

", "

LESION FOLLOWUP / 离线复核

靶病灶随访复核
基线、最低值与测量依据

按时间顺序复核人工测量的 CT 靶病灶。

RECIST 1.1 · 靶病灶子集

", ) + html_escape(trajectory.study.id) + tr(language, "

Baseline ", "

基线 ") + trajectory.study.baseline_date + "
" + html_escape(trajectory.study.technique_id) + " · " + format_diameter_mm(trajectory.study.slice_thickness_tenths_mm) + tr( language, " mm slices

Target categories are one component of response review. Non-target disease, new lesions, confirmation and overall response are outside this report.
本报告仅提供靶病灶分类;非靶病灶、新病灶、确认评价和整体疗效需要额外评价。

Target burden over time

Complete comparable visits define the trend; NE visits leave a gap.

DIAMETER SUM / mm
", "

靶病灶总和趋势

完整且可比的随访进入总和趋势;NE 随访断线。

直径总和 / mm
", ) + render_trajectory_svg_in(trajectory, language) + tr( language, "
CRPRSDPDNE / line break

Visit ledger

Baseline is the PR reference. The prior minimum is the PD reference.

", "
CRPRSDPDNE / 断线

随访台账

PR 以基线为参照;PD 以此前可用最低值为参照。

VisitTarget categorySum / mmvs baselinePrior nadir / mmNadir visitvs nadir
", ), ) for i, a in trajectory.assessments { let color = response_color(a.response) out.write_string( "", ) } out.write_string( tr( language, "
随访靶病灶分类总和 / mm较基线此前最低值 / mm最低值来源随访较最低值
" + html_escape(a.visit_id) + "
" + a.date + "
" + a.response.code() + "" + mm_or_missing(a.sum_tenths_mm) + "" + percent(a.baseline_change_basis_points) + "" + format_diameter_mm(a.nadir_sum_tenths_mm) + "" + html_escape(a.nadir_visit_id) + "" + percent(a.nadir_change_basis_points) + "

Measurement evidence

Open a visit to review target observations and the exact rule path.

", "

测量依据

展开随访查看各靶病灶观察与规则分支。

", ), ) for i, a in trajectory.assessments { let color = response_color(a.response) out.write_string( "
" + a.response.code() + "" + html_escape(a.visit_id) + "" + a.date + tr( language, "
", "
TargetAxisObservationBaseline / mmEffective / mm
", ), ) for m in a.measurements { let axis = axis_label(m.kind, language) out.write_string( "", ) } out.write_string("
病灶测量轴观察状态基线 / mm采用直径 / mm
" + html_escape(m.lesion_id) + "\{axis}" + observation_label(m.observation, language) + "" + format_diameter_mm(m.baseline_tenths_mm) + "" + mm_or_missing(m.effective_tenths_mm) + "
" + trace_html(a, language)) for f in a.findings { let target = match f.lesion_id { Some(id) => " · " + html_escape(id) None => "" } out.write_string( "
" + html_escape(f.code) + target + "

" + html_escape(finding_label(f, language)) + "

", ) } out.write_string("
") } out.write_string( "
" + render_lesion_section_html(trajectory, language), ) out.write_string( tr( language, "

Fixed baseline target set

", "

固定基线靶病灶集合

TargetOrgan groupKindBaseline / mm
", ), ) for target in trajectory.study.targets { out.write_string( "", ) } out.write_string( "
病灶器官组测量轴基线 / mm
" + html_escape(target.id) + "" + html_escape(target.organ) + "" + axis_label(target.kind, language) + "" + format_diameter_mm(target.baseline_tenths_mm) + "

" + tr(language, "Terms", "术语说明") + "

" + glossary(language) + "

", ) out.write_string( tr( language, "

Review boundary. This is a software review aid for manually supplied measurements, not an overall oncology response assessment or a treatment recommendation. The caller confirms target selection, measurement axes, organ grouping and comparable acquisition.

Missing observations stay visible. Explicit too-small observations use a documented 5.0 mm default. NE visits do not update the nadir. Percentages are truncated display values; exact integer comparisons drive categories.

Rules: RECIST Working Group · lesion-followup 0.2.0 · Deterministic offline report

\n", "\n", ), ) out.to_string() } ///| pub fn render_audit_manifest(trajectory : @evidence.Trajectory) -> String { let document : Json = { "schema_version": 1, "ruleset": "recist-1.1-target-subset-1", "scope": "target_lesions_only", "artifacts": ["report.html", "audit.json", "audit.csv", "chart.svg"], "summary": summary_json(trajectory), } document.stringify(indent=2) + "\n" }