ContractIntel_AI / static /index.html
satyaki-mitra's picture
llama-cpp support and hf space integration
9ce162e
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ContractIntel AI - Contract Intelligence AI Platform</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
<style>
/* ========== BASE STYLES & COLOR SCHEME ========= */
:root {
--primary: #0A2463;
--secondary: #00B4D8;
--accent: #FFD700;
--background: #F5F7FA;
--text: #333333;
--light: #FFFFFF;
--danger: #dc2626;
--warning: #f97316;
--info: #ca8a04;
--success: #16a34a;
--border: #e5e5e5;
--shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background-color: var(--background);
color: var(--text);
line-height: 1.7;
}
/* ========== HEADER ========= */
.header {
background: var(--light);
border-bottom: 1px solid var(--border);
padding: 0.8rem 2rem;
display: flex;
justify-content: space-between;
align-items: center;
position: fixed;
width: 100%;
top: 0;
z-index: 1000;
box-shadow: var(--shadow);
}
.logo {
display: flex;
align-items: center;
gap: 0.8rem;
font-size: 1.4rem;
font-weight: 700;
color: var(--primary);
}
.logo-icon {
width: 40px;
height: 40px;
background: var(--primary);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
color: var(--light);
}
.subtitle {
color: var(--text);
font-size: 0.9rem;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 1.5rem;
}
/* ========== LANDING PAGE ========= */
.hero-section {
background: linear-gradient(135deg, var(--primary) 0%, #1a3a8e 100%);
color: var(--light);
padding: 5rem 0 3rem;
text-align: center;
}
.hero-title {
font-size: 3.5rem;
font-weight: 1000;
margin-bottom: 1.5rem;
}
.hero-subtitle {
font-size: 1.8rem;
margin-bottom: 3rem;
max-width: 1200px;
margin-left: auto;
margin-right: auto;
}
.cta-button-container {
display: flex;
justify-content: center;
width: 100%;
margin-top: 2rem;
}
.cta-button {
background: var(--light);
color: var(--primary);
border: none;
padding: 1.2rem 3.5rem;
border-radius: 50px;
font-size: 1.2rem;
font-weight: 700;
cursor: pointer;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
display: inline-flex;
align-items: center;
gap: 0.8rem;
transition: all 0.3s ease;
}
.cta-button:hover {
transform: translateY(-2px);
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.3);
}
.section {
padding: 1rem 0;
text-align: center;
}
.section-title {
font-size: 2.8rem;
font-weight: 700;
margin-bottom: 1rem;
color: var(--primary);
}
.section-subtitle {
font-size: 1.4rem;
color: var(--text);
margin-bottom: 2rem;
max-width: 900px;
margin-left: auto;
margin-right: auto;
}
.features-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 2rem;
margin-bottom: 1rem;
}
.feature-card {
background: var(--light);
border-radius: 12px;
padding: 1.0rem;
box-shadow: var(--shadow);
text-align: left;
transition: transform 0.3s ease;
}
.feature-card:hover {
transform: translateY(-5px);
}
.feature-icon {
font-size: 3.5rem;
margin-bottom: 1.0rem;
color: var(--secondary);
}
.feature-title {
font-size: 1.8rem;
font-weight: 700;
margin-bottom: 0.8rem;
color: var(--primary);
}
.steps-section {
background: var(--light);
padding: 1rem 0;
}
.workflow-steps {
display: flex;
justify-content: space-between;
margin: 1rem auto;
max-width: 1000px;
}
.workflow-step {
text-align: center;
flex: 1;
padding: 0 1rem;
}
.step-icon {
width: 80px;
height: 80px;
background: var(--primary);
color: var(--light);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 2rem;
margin: 0 auto 1.5rem;
}
.step-title {
font-size: 1.5rem;
font-weight: 700;
margin-bottom: 1rem;
color: var(--primary);
}
.footer {
text-align: center;
padding: 1.0rem 1.0rem;
color: var(--text);
border-top: 1px solid var(--border);
background: var(--light);
}
.footer-links {
display: flex;
justify-content: center;
gap: 2rem;
margin: 1.0rem 0;
}
.footer-link {
color: var(--primary);
text-decoration: none;
font-weight: 600;
}
/* ========== ANALYZER SCREEN ========= */
.analyzer-screen {
display: none;
padding-top: 80px;
background: var(--background);
min-height: 100vh;
}
.upload-card {
background: white;
border-radius: 16px;
padding: 2.5rem;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
max-width: 1000px;
margin: 0 auto;
}
.back-to-landing {
background: none;
border: none;
color: #4169e1;
cursor: pointer;
font-size: 1.3rem;
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 2rem;
padding: 1.5rem 1.5rem;
border-radius: 10px;
transition: background 0.2s;
}
.back-to-landing:hover {
background: #f8f9ff;
}
.tabs {
display: flex;
gap: 1rem;
border-bottom: 2px solid #e5e5e5;
margin-bottom: 2rem;
}
.tab {
padding: 0.75rem 1.5rem;
background: none;
border: none;
font-size: 1.3rem;
color: #666;
cursor: pointer;
border-bottom: 3px solid transparent;
transition: all 0.2s;
}
.tab.active {
color: #4169e1;
border-bottom-color: #4169e1;
font-weight: 500;
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
.textarea {
width: 100%;
min-height: 250px;
padding: 1rem;
border: 2px solid #e5e5e5;
border-radius: 8px;
font-family: inherit;
resize: vertical;
font-size: 0.95rem;
transition: border-color 0.2s;
}
.textarea:focus {
outline: none;
border-color: #4169e1;
}
.file-upload-area {
border: 2px dashed #d0d0d0;
border-radius: 8px;
padding: 3rem 2rem;
text-align: center;
cursor: pointer;
transition: all 0.2s;
}
.file-upload-area:hover {
border-color: #4169e1;
background: #f8f9ff;
}
.file-upload-area.dragover {
border-color: #4169e1;
background: #f0f4ff;
}
.file-input {
display: none;
}
.selected-file {
display: flex;
align-items: center;
gap: 1rem;
padding: 1rem;
background: #f8f9ff;
border-radius: 8px;
margin-top: 1rem;
}
.file-icon {
font-size: 2rem;
}
.file-info {
flex: 1;
}
.file-name {
font-weight: 500;
margin-bottom: 0.25rem;
}
.file-size {
font-size: 0.875rem;
color: #666;
}
.remove-file {
background: none;
border: none;
color: #999;
cursor: pointer;
font-size: 1.5rem;
padding: 0.25rem;
}
.analyze-btn-container {
display: flex;
justify-content: center;
margin-top: 2rem;
}
.analyze-btn {
background: #4169e1;
color: white;
border: none;
padding: 1rem 3rem;
border-radius: 8px;
font-size: 1.1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
}
.analyze-btn:hover {
background: #3154c5;
transform: translateY(-2px);
}
.loading-screen {
display: none;
text-align: center;
padding: 4rem 2rem;
}
.spinner {
width: 80px;
height: 80px;
border: 6px solid #e5e5e5;
border-top-color: #4169e1;
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 0 auto 2rem;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.loading-title {
font-size: 1.5rem;
font-weight: 600;
margin-bottom: 0.5rem;
}
/* ========== RESULTS SCREEN STYLES ========= */
.results-content {
display: none;
}
.results-content.active {
display: block;
}
.results-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 2rem;
gap: 2rem;
}
.results-title {
font-size: 2rem;
font-weight: 700;
flex: 1;
color: #1a1a1a;
}
.results-actions {
display: flex;
gap: 1rem;
align-items: center;
justify-content: flex-end;
}
.btn {
padding: 0.75rem 1.5rem;
border-radius: 8px;
font-size: 0.95rem;
font-weight: 500;
cursor: pointer;
border: none;
transition: all 0.2s;
display: flex;
align-items: center;
gap: 0.5rem;
}
.btn-primary {
background: #4169e1;
color: white;
}
.btn-primary:hover {
background: #3154c5;
}
.btn-secondary {
background: white;
color: #4169e1;
border: 2px solid #4169e1;
}
.btn-secondary:hover {
background: #f8f9ff;
}
.results-grid {
display: grid;
grid-template-columns: 1fr 2fr;
gap: 1.5rem;
margin-bottom: 2rem;
}
.risk-score-card {
background: white;
border-radius: 12px;
padding: 2rem;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
}
.risk-score-title {
font-size: 1.25rem;
font-weight: 600;
margin-bottom: 1rem;
}
.risk-circle {
width: 200px;
height: 200px;
margin: 0 auto 1rem;
position: relative;
}
.risk-circle svg {
transform: rotate(-90deg);
}
.risk-score-value {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 3rem;
font-weight: 700;
}
.risk-level {
display: inline-block;
padding: 0.5rem 1rem;
border-radius: 6px;
font-weight: 600;
font-size: 0.9rem;
margin-top: 1rem;
}
.risk-critical {
background: #fee;
color: #dc2626;
}
.risk-high {
background: #fff4e6;
color: #f97316;
}
.risk-medium {
background: #fef9c3;
color: #ca8a04;
}
.risk-low {
background: #dcfce7;
color: #16a34a;
}
.executive-summary-card {
background: white;
border-radius: 12px;
padding: 2rem;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
min-height: 150px;
}
.executive-summary-title {
font-size: 1.25rem;
font-weight: 600;
margin-bottom: 1rem;
}
.executive-summary {
font-size: 1.0rem;
line-height: 1.8;
color: #444;
}
.three-column-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.5rem;
margin-bottom: 2rem;
}
.analysis-card {
background: white;
border-radius: 12px;
padding: 1.5rem;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
overflow: hidden;
}
.analysis-card-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 1rem;
padding-bottom: 0.5rem;
border-bottom: 1px solid #e5e5e5;
}
.analysis-card-icon {
font-size: 1.25rem;
}
.analysis-card-title {
font-size: 1.1rem;
font-weight: 600;
color: #333;
}
.analysis-card-body {
max-height: 300px;
overflow-y: auto;
padding-right: 0.5rem;
}
.item-list {
list-style: none;
margin: 0;
padding: 0;
}
.item-list li {
padding: 0.75rem 0;
border-bottom: 1px solid #f0f0f0;
display: flex;
align-items: flex-start;
gap: 0.5rem;
}
.item-list li:last-child {
border-bottom: none;
}
.item-icon {
color: #4169e1;
margin-top: 0.25rem;
}
.item-text {
flex: 1;
font-size: 0.95rem;
}
.item-text strong {
font-weight: 600;
}
.progress-bar {
height: 8px;
background: #f0f0f0;
border-radius: 4px;
overflow: hidden;
margin-bottom: 0.5rem;
}
.progress-fill {
height: 100%;
transition: width 0.5s ease;
}
.progress-critical { background: #dc2626; }
.progress-high { background: #f97316; }
.progress-medium { background: #ca8a04; }
.progress-low { background: #16a34a; }
.error-display {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: none;
align-items: center;
justify-content: center;
z-index: 2000;
}
.error-card {
background: white;
border-radius: 12px;
padding: 2.5rem;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
max-width: 500px;
width: 90%;
text-align: center;
}
.error-icon {
font-size: 3rem;
margin-bottom: 1rem;
}
.error-title {
font-size: 1.5rem;
font-weight: 600;
color: #dc2626;
margin-bottom: 1rem;
}
.error-message {
color: #666;
line-height: 1.6;
margin-bottom: 2rem;
}
.error-ok-btn {
background: #4169e1;
color: white;
border: none;
padding: 0.75rem 2rem;
border-radius: 8px;
font-size: 1rem;
font-weight: 500;
cursor: pointer;
}
/* ========== NEW RESULTS TAB STYLES ========== */
.results-tabs {
margin-bottom: 2rem;
}
.results-tabs-nav {
display: flex;
border-bottom: 2px solid #e5e5e5;
margin-bottom: 1.5rem;
overflow-x: auto;
}
.results-tab {
padding: 0.75rem 1.5rem;
background: none;
border: none;
font-size: 1rem;
color: #666;
cursor: pointer;
border-bottom: 3px solid transparent;
transition: all 0.2s;
white-space: nowrap;
}
.results-tab.active {
color: #4169e1;
border-bottom-color: #4169e1;
font-weight: 500;
}
.results-tab-pane {
display: none;
}
.results-tab-pane.active {
display: block;
}
.risk-category-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 1rem;
margin-bottom: 2rem;
}
.risk-category-card {
background: white;
border-radius: 8px;
padding: 1.25rem;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
border-left: 4px solid #dc2626;
}
.risk-category-card.high {
border-left-color: #f97316;
}
.risk-category-card.medium {
border-left-color: #ca8a04;
}
.risk-category-card.low {
border-left-color: #16a34a;
}
.risk-category-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.75rem;
}
.risk-category-name {
font-weight: 600;
font-size: 1.1rem;
}
.risk-category-score {
font-weight: 700;
font-size: 1.2rem;
}
.score-critical { color: #dc2626; }
.score-high { color: #f97316; }
.score-medium { color: #ca8a04; }
.score-low { color: #16a34a; }
.risk-category-summary {
font-size: 0.9rem;
color: #666;
margin-bottom: 0.5rem;
}
.risk-findings {
font-size: 0.9rem;
color: #666;
}
.risk-findings ul {
margin: 0.5rem 0;
padding-left: 1.25rem;
}
.risk-findings li {
margin-bottom: 0.25rem;
}
.terms-table, .protections-table, .negotiation-table {
width: 100%;
border-collapse: collapse;
margin-bottom: 1.5rem;
background: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
}
.terms-table th, .protections-table th, .negotiation-table th {
background: #f8f9fa;
padding: 0.75rem;
text-align: left;
font-weight: 600;
border-bottom: 2px solid #e5e5e5;
position: sticky;
top: 0;
}
.terms-table td, .protections-table td, .negotiation-table td {
padding: 0.75rem;
border-bottom: 1px solid #e5e5e5;
vertical-align: top;
}
.terms-table tr:hover, .protections-table tr:hover, .negotiation-table tr:hover {
background: #f8f9ff;
}
.severity-badge {
display: inline-block;
padding: 0.25rem 0.5rem;
border-radius: 4px;
font-size: 0.75rem;
font-weight: 600;
}
.badge-critical {
background: #fee;
color: #dc2626;
}
.badge-high {
background: #fff4e6;
color: #f97316;
}
.badge-medium {
background: #fef9c3;
color: #ca8a04;
}
.badge-low {
background: #dcfce7;
color: #16a34a;
}
.clause-details {
background: #f8f9fa;
border-radius: 6px;
padding: 1rem;
margin-top: 0.5rem;
}
.clause-section {
margin-bottom: 1rem;
}
.clause-section:last-child {
margin-bottom: 0;
}
.clause-section-title {
font-weight: 600;
font-size: 0.9rem;
margin-bottom: 0.5rem;
color: #333;
}
.clause-section-text {
font-size: 0.9rem;
line-height: 1.5;
}
.clause-section-text ul {
margin: 0.5rem 0;
padding-left: 1.25rem;
}
.clause-section-text li {
margin-bottom: 0.25rem;
}
.stat-value {
font-size: 2rem;
font-weight: 700;
color: #4169e1;
margin-bottom: 0.25rem;
}
.stat-label {
font-size: 0.9rem;
color: #666;
margin-bottom: 1rem;
}
.scrollable-table-container {
max-height: 60vh;
overflow-y: auto;
border: 1px solid #e5e5e5;
border-radius: 8px;
margin-bottom: 1.5rem;
}
.clauses-container {
max-height: 70vh;
overflow-y: auto;
padding-right: 0.5rem;
}
.clause-item {
border: 1px solid #e5e5e5;
border-left: 4px solid #dc2626;
border-radius: 8px;
padding: 1.5rem;
margin-bottom: 1rem;
background: white;
transition: background 0.2s;
}
.clause-item:hover {
background: #f8f9ff;
}
.clause-item.high {
border-left-color: #f97316;
}
.clause-item.medium {
border-left-color: #ca8a04;
}
.clause-item.low {
border-left-color: #16a34a;
}
.clause-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 1rem;
padding-bottom: 0.5rem;
border-bottom: 1px solid #e5e5e5;
}
.clause-reference {
font-size: 0.75rem;
text-transform: uppercase;
font-weight: 600;
color: #999;
margin-bottom: 0.5rem;
}
.clause-text {
font-size: 0.95rem;
font-weight: 500;
color: #333;
line-height: 1.6;
background: #f8f9fa;
padding: 0.75rem;
border-radius: 4px;
border-left: 3px solid #4169e1;
}
@media (max-width: 768px) {
.features-grid {
grid-template-columns: 1fr;
}
.workflow-steps {
flex-direction: column;
gap: 2rem;
}
.results-grid {
grid-template-columns: 1fr;
}
.three-column-grid {
grid-template-columns: 1fr;
}
.results-header {
flex-direction: column;
gap: 1rem;
}
.results-actions {
width: 100%;
flex-direction: column;
}
.btn {
width: 100%;
}
.risk-category-grid {
grid-template-columns: 1fr;
}
.terms-table, .protections-table, .negotiation-table {
display: block;
overflow-x: auto;
}
}
</style>
</head>
<body>
<!-- Header -->
<header class="header">
<div class="logo">
<div class="logo-icon">
<i class="fas fa-shield-alt"></i>
</div>
<span>ContractIntel AI</span>
</div>
<div class="subtitle">Legal Intelligence Platform</div>
</header>
<!-- Landing Screen -->
<div id="landingScreen">
<!-- Hero Section -->
<section class="hero-section">
<div class="container">
<h1 class="hero-title">Transform Contract Review Leveraging AI</h1>
<p class="hero-subtitle">Stop signing blind. Our AI-powered Contract Reviewer analyzes your contracts in minutes, identifying hidden risks, unfair terms, clause-by-clause analysis, missing protections, and potential negotiation points; so you can negotiate with confidence.</p>
<div class="cta-button-container">
<button class="cta-button" id="getStartedBtn">
<i class="fas fa-bolt"></i> Try Now for Free
</button>
</div>
</div>
</section>
<!-- Features -->
<section class="section">
<div class="container">
<h2 class="section-title">A Smarter Way to Review Legal Documents</h2>
<p class="section-subtitle">Our platform goes beyond simple keyword searches to provide a deep, contextual understanding of your contracts leveraging Transformers and Artificial Intelligence.</p>
<div class="features-grid">
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-search"></i>
</div>
<h3 class="feature-title">Clause-by-Clause Scrutiny</h3>
<p>Get a granular breakdown of every clause, explained in plain English, so you understand exactly what you're agreeing to.</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-bullhorn"></i>
</div>
<h3 class="feature-title">Negotiation Power</h3>
<p>Receive prioritized talking points and suggested language to strengthen your position before you sign.</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-shield-alt"></i>
</div>
<h3 class="feature-title">Your Data, Your Sovereignty</h3>
<p>We process your documents securely and delete them immediately after analysis. No data retention, ever.</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-chart-line"></i>
</div>
<h3 class="feature-title">Market Context</h3>
<p>Compare your terms against industry benchmarks to ensure you're getting a fair deal.</p>
</div>
</div>
</div>
</section>
<!-- How It Works -->
<section class="steps-section">
<div class="container">
<h2 class="section-title">How It Works</h2>
<p class="section-subtitle">A seamless journey from document to decisive action.</p>
<div class="workflow-steps">
<div class="workflow-step">
<div class="step-icon">
<i class="fas fa-file-upload"></i>
</div>
<h3 class="step-title">Upload or Paste</h3>
<p>Securely provide your contract by pasting text or uploading a PDF/DOCX file.</p>
</div>
<div class="workflow-step">
<div class="step-icon">
<i class="fas fa-robot"></i>
</div>
<h3 class="step-title">AI Analyzes</h3>
<p>Our intelligent engine scrutinizes every detail of your document in seconds.</p>
</div>
<div class="workflow-step">
<div class="step-icon">
<span style="font-size: 2rem;">📄</span>
</div>
<h3 class="step-title">Get Your Report</h3>
<p>Receive a comprehensive report with your risk score and actionable insights.</p>
</div>
</div>
</div>
</section>
<!-- Footer -->
<footer class="footer">
<div class="container">
<div class="footer-links">
<a href="#" class="footer-link">API Documentation</a>
<a href="#" class="footer-link">Blog</a>
<a href="#" class="footer-link">GitHub</a>
</div>
<p>© 2025 ContractIntel AI. For informational purposes only. Not legal advice.</p>
</div>
</footer>
</div>
<!-- Analyzer Screen -->
<div id="analyzerScreen" class="analyzer-screen">
<div class="container">
<button class="back-to-landing" id="backToLandingBtn">
← Back to Overview
</button>
<!-- Upload Section -->
<div id="uploadSection">
<div style="text-align: center; margin-bottom: 3rem;">
<h1 style="font-size: 2.5rem; margin-bottom: 1rem;">Analyze Your Contract</h1>
<p style="font-size: 1.1rem; color: #666;">Paste your contract or upload a file to get an instant risk assessment.</p>
</div>
<div class="upload-card">
<div class="tabs">
<button class="tab active" data-tab="paste">Paste Text</button>
<button class="tab" data-tab="upload">Upload File</button>
</div>
<div id="pasteTab" class="tab-content active">
<textarea class="textarea" id="contractText" placeholder="Paste your full contract text here..."></textarea>
</div>
<div id="uploadTab" class="tab-content">
<div class="file-upload-area" id="fileUploadArea">
<input type="file" id="fileInput" class="file-input" accept=".pdf,.docx,.txt">
<div style="font-size: 3rem; margin-bottom: 1rem;">📄</div>
<div>Click to upload or drag and drop</div>
<div style="font-size: 0.875rem; color: #999; margin-top: 0.5rem;">PDF, DOCX, or TXT files (Max 10MB)</div>
</div>
<div id="selectedFile" class="selected-file" style="display: none;">
<div class="file-icon">📄</div>
<div class="file-info">
<div class="file-name" id="fileName"></div>
<div class="file-size" id="fileSize"></div>
</div>
<button class="remove-file" id="removeFile">×</button>
</div>
</div>
<div class="analyze-btn-container">
<button class="analyze-btn" id="analyzeBtn">
Analyze Contract
</button>
</div>
</div>
</div>
<!-- Loading Screen -->
<div id="loadingScreen" class="loading-screen">
<div class="spinner"></div>
<h2 class="loading-title">Performing in-depth analysis...</h2>
<p>This may take a moment for large documents.</p>
</div>
<!-- Results Screen -->
<div id="resultsContent" class="results-content">
<div class="results-header">
<h1 class="results-title">Analysis Report</h1>
<div class="results-actions">
<button class="btn btn-primary" id="downloadBtn">
<i class="fas fa-download"></i> Download PDF Report
</button>
<button class="btn btn-secondary" id="analyzeAnotherBtn">
Analyze Another Contract
</button>
</div>
</div>
<!-- Tabs will be inserted here by JavaScript -->
</div>
</div>
</div>
<!-- Error Display -->
<div id="errorDisplay" class="error-display">
<div class="error-card">
<div class="error-icon">⚠️</div>
<h3 class="error-title">An Error Occurred</h3>
<p class="error-message" id="errorMessageText"></p>
<button class="error-ok-btn" id="errorOkBtn">OK</button>
</div>
</div>
<script>
// ========== WORKING JAVASCRIPT WITH REAL API INTEGRATION ==========
// Global variables
const API_BASE_URL = `${window.location.protocol}//${window.location.host}/api/v1`;
let selectedFile = null;
let currentAnalysisResult = null;
// Get elements
const landingScreen = document.getElementById('landingScreen');
const analyzerScreen = document.getElementById('analyzerScreen');
const getStartedBtn = document.getElementById('getStartedBtn');
const backToLandingBtn = document.getElementById('backToLandingBtn');
const analyzeAnotherBtn = document.getElementById('analyzeAnotherBtn');
const analyzeBtn = document.getElementById('analyzeBtn');
const downloadBtn = document.getElementById('downloadBtn');
const uploadSection = document.getElementById('uploadSection');
const loadingScreen = document.getElementById('loadingScreen');
const resultsContent = document.getElementById('resultsContent');
const contractText = document.getElementById('contractText');
const fileInput = document.getElementById('fileInput');
const fileUploadArea = document.getElementById('fileUploadArea');
const selectedFileDiv = document.getElementById('selectedFile');
const fileNameSpan = document.getElementById('fileName');
const fileSizeSpan = document.getElementById('fileSize');
const removeFileBtn = document.getElementById('removeFile');
const errorDisplay = document.getElementById('errorDisplay');
const errorMessageText = document.getElementById('errorMessageText');
const errorOkBtn = document.getElementById('errorOkBtn');
// Screen management
function showLanding() {
landingScreen.style.display = 'block';
analyzerScreen.style.display = 'none';
}
function showAnalyzer() {
landingScreen.style.display = 'none';
analyzerScreen.style.display = 'block';
showUploadSection();
}
function showUploadSection() {
uploadSection.style.display = 'block';
loadingScreen.style.display = 'none';
resultsContent.style.display = 'none';
// Reset form
contractText.value = '';
selectedFile = null;
fileInput.value = '';
selectedFileDiv.style.display = 'none';
fileUploadArea.style.display = 'block';
}
function showLoading() {
uploadSection.style.display = 'none';
loadingScreen.style.display = 'block';
resultsContent.style.display = 'none';
}
function showResults() {
uploadSection.style.display = 'none';
loadingScreen.style.display = 'none';
resultsContent.style.display = 'block';
}
function showError(message) {
errorMessageText.textContent = message;
errorDisplay.style.display = 'flex';
}
// Tab switching
function setupTabs() {
const tabs = document.querySelectorAll('.tab');
tabs.forEach(tab => {
tab.addEventListener('click', function() {
const tabName = this.getAttribute('data-tab');
// Update tabs
tabs.forEach(t => t.classList.remove('active'));
this.classList.add('active');
// Update content
document.querySelectorAll('.tab-content').forEach(content => {
content.classList.remove('active');
});
document.getElementById(tabName + 'Tab').classList.add('active');
});
});
}
// File upload handling
function setupFileUpload() {
// Click to upload
fileUploadArea.addEventListener('click', () => fileInput.click());
// File input change
fileInput.addEventListener('change', function(e) {
const file = e.target.files[0];
handleFileSelect(file);
});
// Drag and drop
fileUploadArea.addEventListener('dragover', (e) => {
e.preventDefault();
fileUploadArea.classList.add('dragover');
});
fileUploadArea.addEventListener('dragleave', () => {
fileUploadArea.classList.remove('dragover');
});
fileUploadArea.addEventListener('drop', (e) => {
e.preventDefault();
fileUploadArea.classList.remove('dragover');
const file = e.dataTransfer.files[0];
handleFileSelect(file);
});
// Remove file
removeFileBtn.addEventListener('click', function(e) {
e.stopPropagation();
selectedFile = null;
fileInput.value = '';
selectedFileDiv.style.display = 'none';
fileUploadArea.style.display = 'block';
});
function handleFileSelect(file) {
if (!file) return;
// Validate file type
const validTypes = [
'application/pdf',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'text/plain'
];
const isValidType = validTypes.includes(file.type) || file.name.match(/\.(pdf|docx|txt)$/i);
if (!isValidType) {
showError('Please upload a PDF, DOCX, or TXT file');
return;
}
// Validate file size (10MB)
if (file.size > 10 * 1024 * 1024) {
showError('File size must be less than 10MB');
return;
}
selectedFile = file;
fileNameSpan.textContent = file.name;
fileSizeSpan.textContent = formatFileSize(file.size);
fileUploadArea.style.display = 'none';
selectedFileDiv.style.display = 'flex';
}
function formatFileSize(bytes) {
if (bytes < 1024) return bytes + ' B';
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(2) + ' KB';
return (bytes / (1024 * 1024)).toFixed(2) + ' MB';
}
}
// Analyze button - REAL API CALLS
function setupAnalyzeButton() {
analyzeBtn.addEventListener('click', async function() {
const activeTab = document.querySelector('.tab.active').getAttribute('data-tab');
try {
showLoading();
if (activeTab === 'paste') {
const text = contractText.value.trim();
if (!text) {
showError('Please paste contract text');
showUploadSection();
return;
}
await analyzeContractText(text);
} else {
if (!selectedFile) {
showError('Please select a file');
showUploadSection();
return;
}
await analyzeContractFile(selectedFile);
}
} catch (error) {
console.error('Analysis error:', error);
showError('Error starting analysis: ' + error.message);
showUploadSection();
}
});
}
// Real API call for text analysis
async function analyzeContractText(text) {
try {
const formData = new FormData();
formData.append('contract_text', text);
formData.append('max_clauses', '15');
formData.append('interpret_clauses', 'true');
formData.append('generate_negotiation_points', 'true');
formData.append('compare_to_market', 'false');
const response = await fetch(`${API_BASE_URL}/analyze/text`, {
method: 'POST',
body: formData
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.detail || 'Analysis failed');
}
const result = await response.json();
currentAnalysisResult = result;
displayResults(result);
showResults();
} catch (error) {
throw error;
}
}
// Real API call for file analysis
async function analyzeContractFile(file) {
try {
const formData = new FormData();
formData.append('file', file);
formData.append('max_clauses', '15');
formData.append('interpret_clauses', 'true');
formData.append('generate_negotiation_points', 'true');
formData.append('compare_to_market', 'false');
const response = await fetch(`${API_BASE_URL}/analyze/file`, {
method: 'POST',
body: formData
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.detail || 'Analysis failed');
}
const result = await response.json();
currentAnalysisResult = result;
displayResults(result);
showResults();
} catch (error) {
throw error;
}
}
// Download PDF - REAL API CALL
function setupDownloadButton() {
downloadBtn.addEventListener('click', async function() {
if (!currentAnalysisResult) {
showError('No analysis results available to download');
return;
}
try {
const response = await fetch(`${API_BASE_URL}/generate-pdf`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(currentAnalysisResult)
});
if (!response.ok) {
throw new Error('Failed to generate PDF');
}
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = `contract_analysis_${currentAnalysisResult.analysis_id}.pdf`;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
} catch (error) {
console.error('PDF download error:', error);
showError('Error downloading PDF: ' + error.message);
}
});
}
// ========== RESULTS DISPLAY FUNCTIONS ==========
function displayResults(result) {
// Store the result globally for PDF download
currentAnalysisResult = result;
// Create tabbed interface for results
createResultsTabs();
// Populate all sections
populateOverview(result);
populateUnfavorableTerms(result);
populateMissingProtections(result);
populateNegotiationPoints(result);
populateRiskBreakdown(result);
populateClauseAnalysis(result);
showResults();
}
// Update the createResultsTabs function to remove the old risk circle update
function createResultsTabs() {
const resultsContent = document.getElementById('resultsContent');
// Clear any existing tabs
const existingTabs = resultsContent.querySelector('.results-tabs');
if (existingTabs) {
existingTabs.remove();
}
// Create tabs container
const tabsContainer = document.createElement('div');
tabsContainer.className = 'results-tabs';
tabsContainer.innerHTML = `
<div class="results-tabs-nav">
<button class="results-tab active" data-tab="overview">Overview</button>
<button class="results-tab" data-tab="risk">Risk Analysis</button>
<button class="results-tab" data-tab="terms">Unfavorable Terms</button>
<button class="results-tab" data-tab="protections">Missing Protections</button>
<button class="results-tab" data-tab="negotiation">Negotiation Points</button>
<button class="results-tab" data-tab="clauses">Clause Analysis</button>
</div>
<div class="results-tabs-content">
<div id="overviewTab" class="results-tab-pane active">
<!-- Overview content will be populated here -->
</div>
<div id="riskTab" class="results-tab-pane">
<!-- Risk analysis content will be populated here -->
</div>
<div id="termsTab" class="results-tab-pane">
<!-- Unfavorable terms content will be populated here -->
</div>
<div id="protectionsTab" class="results-tab-pane">
<!-- Missing protections content will be populated here -->
</div>
<div id="negotiationTab" class="results-tab-pane">
<!-- Negotiation points content will be populated here -->
</div>
<div id="clausesTab" class="results-tab-pane">
<!-- Clause analysis content will be populated here -->
</div>
</div>
`;
// Insert tabs after the results header
const resultsHeader = resultsContent.querySelector('.results-header');
resultsHeader.parentNode.insertBefore(tabsContainer, resultsHeader.nextSibling);
// Add tab switching functionality
const tabs = tabsContainer.querySelectorAll('.results-tab');
tabs.forEach(tab => {
tab.addEventListener('click', function() {
const tabName = this.getAttribute('data-tab');
// Update tabs
tabs.forEach(t => t.classList.remove('active'));
this.classList.add('active');
// Update content
document.querySelectorAll('.results-tab-pane').forEach(pane => {
pane.classList.remove('active');
});
document.getElementById(tabName + 'Tab').classList.add('active');
});
});
}
// Update the populateOverview function to handle the risk circle properly
function populateOverview(result) {
const overviewTab = document.getElementById('overviewTab');
const score = result.risk_analysis.overall_score;
const riskLevel = result.risk_analysis.risk_level;
overviewTab.innerHTML = `
<div class="results-grid">
<div class="risk-score-card">
<h2 class="risk-score-title">Overall Risk Score</h2>
<div class="risk-circle">
<svg width="200" height="200">
<circle cx="100" cy="100" r="85" fill="none" stroke="#f0f0f0" stroke-width="20"/>
<circle id="riskCircleOverview" cx="100" cy="100" r="85" fill="none" stroke="#dc2626" stroke-width="20" stroke-dasharray="534" stroke-dashoffset="534" stroke-linecap="round"/>
</svg>
<div class="risk-score-value">${score}</div>
</div>
<div class="risk-level risk-${getRiskClass(score)}">${riskLevel.toUpperCase()}</div>
</div>
<div class="executive-summary-card">
<h2 class="executive-summary-title">Executive Summary</h2>
<p class="executive-summary">${result.executive_summary}</p>
</div>
</div>
<div class="three-column-grid">
<div class="analysis-card">
<div class="analysis-card-header">
<div class="analysis-card-icon">⚠️</div>
<h3 class="analysis-card-title">Unfavorable Terms</h3>
</div>
<div class="analysis-card-body">
<div class="stat-value">${result.unfavorable_terms ? result.unfavorable_terms.length : 0}</div>
<div class="stat-label">Terms identified</div>
<ul class="item-list">
${result.unfavorable_terms && result.unfavorable_terms.length > 0 ?
result.unfavorable_terms.slice(0, 3).map(term =>
`<li><span class="item-icon"></span><span class="item-text">${formatText(term.term)}</span></li>`
).join('') :
'<li>No unfavorable terms detected</li>'
}
</ul>
</div>
</div>
<div class="analysis-card">
<div class="analysis-card-header">
<div class="analysis-card-icon">🛡️</div>
<h3 class="analysis-card-title">Missing Protections</h3>
</div>
<div class="analysis-card-body">
<div class="stat-value">${result.missing_protections ? result.missing_protections.length : 0}</div>
<div class="stat-label">Protections missing</div>
<ul class="item-list">
${result.missing_protections && result.missing_protections.length > 0 ?
result.missing_protections.slice(0, 3).map(protection =>
`<li><span class="item-icon"></span><span class="item-text">${formatText(protection.protection)}</span></li>`
).join('') :
'<li>No missing protections detected</li>'
}
</ul>
</div>
</div>
<div class="analysis-card">
<div class="analysis-card-header">
<div class="analysis-card-icon">📖</div>
<h3 class="analysis-card-title">Negotiation Points</h3>
</div>
<div class="analysis-card-body">
<div class="stat-value">${result.negotiation_points ? result.negotiation_points.length : 0}</div>
<div class="stat-label">Points to negotiate</div>
<ul class="item-list">
${result.negotiation_points && result.negotiation_points.length > 0 ?
result.negotiation_points.slice(0, 3).map(point =>
`<li><span class="item-icon"></span><span class="item-text">${formatText(point.issue)}</span></li>`
).join('') :
'<li>No negotiation points generated</li>'
}
</ul>
</div>
</div>
</div>
<div class="analysis-card">
<div class="analysis-card-header">
<div class="analysis-card-icon">📊</div>
<h3 class="analysis-card-title">Contract Classification</h3>
</div>
<div class="analysis-card-body">
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem;">
<div>
<div class="stat-label">Primary Category</div>
<div class="stat-value">${capitalizeFirst(result.classification.category)}</div>
</div>
<div>
<div class="stat-label">Subcategory</div>
<div class="stat-value">${capitalizeFirst(result.classification.subcategory) || 'N/A'}</div>
</div>
<div>
<div class="stat-label">Confidence</div>
<div class="stat-value">${(result.classification.confidence * 100).toFixed(1)}%</div>
</div>
<div>
<div class="stat-label">Contract Keywords Identified</div>
<div class="stat-value">${result.classification.detected_keywords.length}</div>
</div>
</div>
</div>
</div>
`;
// Update risk circle in overview after the HTML is rendered
setTimeout(() => {
const circle = document.getElementById('riskCircleOverview');
if (circle) {
const circumference = 534;
const offset = circumference - (score / 100) * circumference;
circle.style.strokeDashoffset = offset;
circle.style.stroke = getRiskColor(score);
}
}, 100);
}
// Populate unfavorable terms in a table
function populateUnfavorableTerms(result) {
const termsTab = document.getElementById('termsTab');
if (!result.unfavorable_terms || result.unfavorable_terms.length === 0) {
termsTab.innerHTML = '<p>No unfavorable terms detected in this contract.</p>';
return;
}
let tableHTML = `
<h3>Unfavorable Terms (${result.unfavorable_terms.length} found)</h3>
<div class="scrollable-table-container">
<table class="terms-table">
<thead>
<tr>
<th>Term</th>
<th>Category</th>
<th>Severity</th>
<th>Clause Reference</th>
<th>Explanation</th>
</tr>
</thead>
<tbody>
`;
result.unfavorable_terms.forEach(term => {
tableHTML += `
<tr>
<td><strong>${formatText(term.term)}</strong></td>
<td>${formatCategoryName(term.category)}</td>
<td><span class="severity-badge badge-${term.severity}">${term.severity.toUpperCase()}</span></td>
<td>${term.clause_reference || 'N/A'}</td>
<td>${formatText(term.explanation)}</td>
</tr>
`;
});
tableHTML += `
</tbody>
</table>
</div>
`;
termsTab.innerHTML = tableHTML;
}
// Populate missing protections in a table
function populateMissingProtections(result) {
const protectionsTab = document.getElementById('protectionsTab');
if (!result.missing_protections || result.missing_protections.length === 0) {
protectionsTab.innerHTML = '<p>No missing protections detected in this contract.</p>';
return;
}
let tableHTML = `
<h3>Missing Protections (${result.missing_protections.length} found)</h3>
<div class="scrollable-table-container">
<table class="protections-table">
<thead>
<tr>
<th>Protection</th>
<th>Importance</th>
<th>Risk Score</th>
<th>Explanation</th>
<th>Suggested Language</th>
</tr>
</thead>
<tbody>
`;
result.missing_protections.forEach(protection => {
tableHTML += `
<tr>
<td><strong>${formatText(protection.protection)}</strong></td>
<td><span class="severity-badge badge-${protection.importance}">${protection.importance.toUpperCase()}</span></td>
<td>${protection.risk_score}/100</td>
<td>${formatText(protection.explanation)}</td>
<td>${formatText(protection.suggested_language || protection.recommendation)}</td>
</tr>
`;
});
tableHTML += `
</tbody>
</table>
</div>
`;
protectionsTab.innerHTML = tableHTML;
}
// Populate negotiation points in a table
function populateNegotiationPoints(result) {
const negotiationTab = document.getElementById('negotiationTab');
if (!result.negotiation_points || result.negotiation_points.length === 0) {
negotiationTab.innerHTML = '<p>No negotiation points generated for this contract.</p>';
return;
}
let tableHTML = `
<h3>Negotiation Points (${result.negotiation_points.length} identified)</h3>
<div class="scrollable-table-container">
<table class="negotiation-table">
<thead>
<tr>
<th>Priority</th>
<th>Issue</th>
<th>Category</th>
<th>Current Language</th>
<th>Proposed Language</th>
<th>Rationale</th>
</tr>
</thead>
<tbody>
`;
// Sort by priority
const sortedPoints = [...result.negotiation_points].sort((a, b) => a.priority - b.priority);
sortedPoints.forEach(point => {
tableHTML += `
<tr>
<td>${point.priority}</td>
<td><strong>${formatText(point.issue)}</strong></td>
<td>${formatCategoryName(point.category)}</td>
<td>${formatText(point.current_language || 'Not specified')}</td>
<td>${formatText(point.proposed_language || 'Request balanced language')}</td>
<td>${formatText(point.rationale)}</td>
</tr>
`;
});
tableHTML += `
</tbody>
</table>
</div>
`;
negotiationTab.innerHTML = tableHTML;
}
// Populate risk breakdown with cards
function populateRiskBreakdown(result) {
const riskTab = document.getElementById('riskTab');
if (!result.risk_analysis.category_scores) {
riskTab.innerHTML = '<p>No risk breakdown available for this contract.</p>';
return;
}
// Filter out categories with 0 risk and sort by risk score (descending)
const filteredCategories = Object.entries(result.risk_analysis.category_scores)
.filter(([category, score]) => score > 0)
.sort(([, scoreA], [, scoreB]) => scoreB - scoreA);
if (filteredCategories.length === 0) {
riskTab.innerHTML = '<p>No significant risk categories identified in this contract.</p>';
return;
}
let riskHTML = `
<h3>Risk Category Breakdown</h3>
<div class="risk-category-grid">
`;
filteredCategories.forEach(([category, score]) => {
const riskClass = getRiskClass(score);
const categoryDetails = result.risk_analysis.risk_breakdown?.find(
item => item.category.toLowerCase().replace(/\s+/g, '_') === category
) || { summary: 'Review recommended based on risk score', findings: [] };
riskHTML += `
<div class="risk-category-card ${riskClass}">
<div class="risk-category-header">
<span class="risk-category-name">${formatCategoryName(category)}</span>
<span class="risk-category-score score-${riskClass}">${score}/100</span>
</div>
<div class="progress-bar">
<div class="progress-fill progress-${riskClass}" style="width: ${score}%"></div>
</div>
<div class="risk-category-summary">${categoryDetails.summary}</div>
${categoryDetails.findings && categoryDetails.findings.length > 0 ? `
<div class="risk-findings">
<strong>Key Findings:</strong>
<ul>
${categoryDetails.findings.slice(0, 3).map(finding => `<li>${formatText(finding)}</li>`).join('')}
</ul>
</div>
` : ''}
</div>
`;
});
riskHTML += `</div>`;
riskTab.innerHTML = riskHTML;
}
// Populate clause analysis with detailed view
function populateClauseAnalysis(result) {
const clausesTab = document.getElementById('clausesTab');
if (!result.clauses || result.clauses.length === 0) {
clausesTab.innerHTML = '<p>No clauses extracted from this contract.</p>';
return;
}
let clausesHTML = `
<h3>Clause-by-Clause Analysis (${result.clauses.length} clauses)</h3>
<div class="clauses-container">
`;
result.clauses.forEach(clause => {
const riskClass = getRiskClass(clause.risk_score || 0);
const interpretation = result.clause_interpretations?.find(
interp => interp.clause_reference === clause.reference
);
clausesHTML += `
<div class="clause-item ${riskClass}">
<div class="clause-header">
<div>
<div class="clause-reference">${clause.reference || 'CLAUSE'}</div>
<div class="clause-text">${clause.text}</div>
</div>
<div class="severity-badge badge-${riskClass}">
${Math.round(clause.risk_score || 0)}% risk
</div>
</div>
<div class="clause-details">
<div class="clause-section">
<div class="clause-section-title">Category</div>
<div class="clause-section-text">${formatCategoryName(clause.category)}</div>
</div>
${interpretation ? `
<div class="clause-section">
<div class="clause-section-title">Plain English Summary</div>
<div class="clause-section-text">${interpretation.plain_english_summary}</div>
</div>
<div class="clause-section">
<div class="clause-section-title">Key Points</div>
<div class="clause-section-text">
<ul>
${interpretation.key_points.map(point => `<li>${formatText(point)}</li>`).join('')}
</ul>
</div>
</div>
<div class="clause-section">
<div class="clause-section-title">Potential Risks</div>
<div class="clause-section-text">
<ul>
${interpretation.potential_risks.map(risk => `<li>${formatText(risk)}</li>`).join('')}
</ul>
</div>
</div>
<div class="clause-section">
<div class="clause-section-title">Suggested Improvements</div>
<div class="clause-section-text">
<ul>
${interpretation.suggested_improvements.map(improvement => `<li>${formatText(improvement)}</li>`).join('')}
</ul>
</div>
</div>
` : `
<div class="clause-section">
<div class="clause-section-title">Analysis</div>
<div class="clause-section-text">${clause.explanation || 'Analysis not available.'}</div>
</div>
<div class="clause-section">
<div class="clause-section-title">Recommendation</div>
<div class="clause-section-text">${clause.suggested_fix || 'Review with legal counsel.'}</div>
</div>
`}
</div>
</div>
`;
});
clausesHTML += `</div>`;
clausesTab.innerHTML = clausesHTML;
}
// Helper functions
function getRiskClass(score) {
if (score >= 80) return 'critical';
if (score >= 60) return 'high';
if (score >= 40) return 'medium';
return 'low';
}
function getRiskColor(score) {
if (score >= 80) return '#dc2626';
if (score >= 60) return '#f97316';
if (score >= 40) return '#ca8a04';
return '#16a34a';
}
function formatCategoryName(category) {
return category.split('_').map(word =>
word.charAt(0).toUpperCase() + word.slice(1)
).join(' ');
}
function formatText(text) {
if (!text) return '';
return text.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
}
function capitalizeFirst(text) {
if (!text) return '';
return text.charAt(0).toUpperCase() + text.slice(1);
}
// Initialize everything
function init() {
// Navigation
getStartedBtn.addEventListener('click', showAnalyzer);
backToLandingBtn.addEventListener('click', showLanding);
analyzeAnotherBtn.addEventListener('click', showUploadSection);
errorOkBtn.addEventListener('click', () => errorDisplay.style.display = 'none');
// Setup features
setupTabs();
setupFileUpload();
setupAnalyzeButton();
setupDownloadButton();
// Start with landing screen
showLanding();
console.log('ContractIntel AI initialized successfully');
}
// Start when page loads
window.addEventListener('load', init);
</script>
</body>
</html>