| <!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> |
| |
| :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 { |
| 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; |
| } |
| |
| |
| .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 { |
| 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-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; |
| } |
| |
| |
| .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 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> |
|
|
| |
| <div id="landingScreen"> |
| |
| <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> |
|
|
| |
| <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> |
|
|
| |
| <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 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> |
|
|
| |
| <div id="analyzerScreen" class="analyzer-screen"> |
| <div class="container"> |
| <button class="back-to-landing" id="backToLandingBtn"> |
| ← Back to Overview |
| </button> |
|
|
| |
| <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> |
|
|
| |
| <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> |
|
|
| |
| <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> |
| |
| </div> |
| </div> |
| </div> |
|
|
| |
| <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"> |
| |
| </div> |
| <div id="riskTab" class="results-tab-pane"> |
| |
| </div> |
| <div id="termsTab" class="results-tab-pane"> |
| |
| </div> |
| <div id="protectionsTab" class="results-tab-pane"> |
| |
| </div> |
| <div id="negotiationTab" class="results-tab-pane"> |
| |
| </div> |
| <div id="clausesTab" class="results-tab-pane"> |
| |
| </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> |