Flujo completo: entrada de tickers → modelos → decisión crediticia → reporte
GET /. El usuario ingresa los tickers (máx. 20), lanza el análisis y hace polling del estado.analyze y download. Sirve ui/index.html y el directorio outputs/ como estático.TickerRow con todos los campos de resultado que consume el frontend._run_pipeline_task() como BackgroundTask. Serializa summary_df → TickerRow[]. Lee ar.risk_zone directamente del ModelResult para zscore_zone.queued → running → done | error. Crea outputs/jobs/{uuid}/plots/ y reports/ por job. Almacena el result dict completo al terminar.job.result["pdf_path"] y job.result["report_path"].yfinance: balance sheet (hasta 10 años), income statement, cash flow, market data y tasa libre de riesgo (^TNX). Cache en CSV por ticker.SectorClassifier.summary_df con todas las columnas para reporte y API.outputs/jobs/{id}/plots/.credit_report.md con secciones: portada, metodología, resultados por empresa, tabla comparativa (6 cols, markdown="1"), visualizaciones, conclusiones y bibliografía.md_in_html) → imágenes embebidas en base64 → CSS profesional → WeasyPrint → credit_report.pdf. Bibliografía con sangría francesa.