| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>MATCH.AI — AI Resume Screener Studio (Executive 3D Edition)</title> |
| |
| <link rel="preconnect" href="https://fonts.googleapis.com"> |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> |
| <link href="https://fonts.googleapis.com/css2?family=Geist:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet"> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"> |
| <style> |
| |
| :root { |
| --bg-page: #080f1e; |
| --surface-card: rgba(18, 26, 44, 0.82); |
| --surface-hover: rgba(26, 36, 60, 0.92); |
| --border-card: rgba(76, 215, 246, 0.28); |
| --border-hover: rgba(76, 215, 246, 0.65); |
| --primary-accent: #4cd7f6; |
| --secondary-accent: #d0bcff; |
| --success-color: #10b981; |
| --warning-color: #f59e0b; |
| --danger-color: #ef4444; |
| --text-main: #f1f5f9; |
| --text-muted: #94a3b8; |
| --header-bg: rgba(8, 15, 30, 0.88); |
| --input-bg: rgba(11, 19, 38, 0.75); |
| --card-shadow: 0 25px 60px rgba(0, 0, 0, 0.45), 0 0 20px rgba(76, 215, 246, 0.12); |
| --dropzone-border: rgba(76, 215, 246, 0.35); |
| } |
| |
| |
| [data-theme="light"] { |
| --bg-page: #eaf0f8; |
| --surface-card: rgba(255, 255, 255, 0.96); |
| --surface-hover: #ffffff; |
| --border-card: rgba(2, 132, 199, 0.32); |
| --border-hover: #0284c7; |
| --primary-accent: #0284c7; |
| --secondary-accent: #6366f1; |
| --success-color: #059669; |
| --warning-color: #d97706; |
| --danger-color: #dc2626; |
| --text-main: #0f172a; |
| --text-muted: #475569; |
| --header-bg: rgba(234, 240, 248, 0.92); |
| --input-bg: #f8fafc; |
| --card-shadow: 0 20px 50px rgba(15, 23, 42, 0.11), 0 4px 15px rgba(2, 132, 199, 0.08); |
| --dropzone-border: rgba(2, 132, 199, 0.38); |
| } |
| |
| * { |
| box-sizing: border-box; |
| margin: 0; |
| padding: 0; |
| transition: background-color 0.35s ease, color 0.35s ease, border-color 0.35s ease, box-shadow 0.35s ease; |
| } |
| |
| body { |
| font-family: 'Geist', -apple-system, sans-serif; |
| background-color: var(--bg-page); |
| color: var(--text-main); |
| min-height: 100vh; |
| overflow-x: hidden; |
| position: relative; |
| } |
| |
| |
| #bg-canvas { |
| position: fixed; |
| top: 0; |
| left: 0; |
| width: 100vw; |
| height: 100vh; |
| z-index: 0; |
| pointer-events: none; |
| } |
| |
| |
| header { |
| position: sticky; |
| top: 0; |
| z-index: 50; |
| background: var(--header-bg); |
| backdrop-filter: blur(28px); |
| border-bottom: 1.5px solid var(--border-card); |
| padding: 1rem 2.75rem; |
| box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05); |
| } |
| |
| .header-inner { |
| max-width: 1380px; |
| margin: 0 auto; |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| } |
| |
| .brand { |
| display: flex; |
| align-items: center; |
| gap: 0.9rem; |
| font-weight: 800; |
| font-size: 1.5rem; |
| letter-spacing: -0.025em; |
| color: var(--text-main); |
| text-decoration: none; |
| } |
| |
| .brand-logo { |
| width: 44px; |
| height: 44px; |
| border-radius: 13px; |
| background: linear-gradient(135deg, var(--primary-accent), var(--secondary-accent)); |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| color: #ffffff; |
| font-size: 1.4rem; |
| box-shadow: 0 8px 24px rgba(2, 132, 199, 0.35); |
| transform: rotate(-5deg); |
| transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); |
| } |
| |
| .brand:hover .brand-logo { |
| transform: rotate(10deg) scale(1.1); |
| } |
| |
| .header-controls { |
| display: flex; |
| align-items: center; |
| gap: 1.1rem; |
| } |
| |
| |
| .theme-switch-btn { |
| display: inline-flex; |
| align-items: center; |
| gap: 0.65rem; |
| padding: 0.55rem 1.25rem; |
| background: var(--surface-card); |
| border: 1.5px solid var(--border-card); |
| border-radius: 999px; |
| color: var(--text-main); |
| font-family: 'JetBrains Mono', monospace; |
| font-size: 0.84rem; |
| font-weight: 700; |
| cursor: pointer; |
| box-shadow: 0 4px 14px rgba(0,0,0,0.06); |
| transition: all 0.25s cubic-bezier(0.34, 1.56, 0.64, 1); |
| } |
| |
| .theme-switch-btn:hover { |
| border-color: var(--border-hover); |
| transform: translateY(-2px); |
| box-shadow: 0 8px 20px rgba(2, 132, 199, 0.18); |
| } |
| |
| .theme-switch-btn i { |
| font-size: 1rem; |
| color: var(--primary-accent); |
| } |
| |
| .stitch-badge { |
| display: inline-flex; |
| align-items: center; |
| gap: 0.55rem; |
| padding: 0.5rem 1.15rem; |
| background: rgba(2, 132, 199, 0.1); |
| border: 1.5px solid var(--border-card); |
| border-radius: 999px; |
| font-family: 'JetBrains Mono', monospace; |
| font-size: 0.78rem; |
| font-weight: 700; |
| color: var(--primary-accent); |
| letter-spacing: 0.04em; |
| } |
| |
| .stitch-dot { |
| width: 8px; |
| height: 8px; |
| border-radius: 50%; |
| background: var(--primary-accent); |
| box-shadow: 0 0 10px var(--primary-accent); |
| animation: pulse-ring 2s infinite; |
| } |
| |
| @keyframes pulse-ring { |
| 0%, 100% { transform: scale(1); opacity: 1; } |
| 50% { transform: scale(1.4); opacity: 0.45; } |
| } |
| |
| |
| .main-container { |
| position: relative; |
| z-index: 10; |
| max-width: 1380px; |
| margin: 2.75rem auto; |
| padding: 0 1.5rem; |
| display: grid; |
| grid-template-columns: 485px 1fr; |
| gap: 2.4rem; |
| align-items: start; |
| } |
| |
| @media (max-width: 1100px) { |
| .main-container { |
| grid-template-columns: 1fr; |
| } |
| } |
| |
| |
| .glass-card { |
| position: relative; |
| background: var(--surface-card); |
| border: 1.5px solid var(--border-card); |
| border-radius: 24px; |
| padding: 2.3rem; |
| backdrop-filter: blur(32px); |
| box-shadow: var(--card-shadow); |
| transform-style: preserve-3d; |
| transform: perspective(1200px); |
| transition: transform 0.12s ease-out, border-color 0.3s ease, box-shadow 0.3s ease; |
| overflow: hidden; |
| } |
| |
| .glass-card::before { |
| content: ''; |
| position: absolute; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 4px; |
| background: linear-gradient(90deg, var(--primary-accent), var(--secondary-accent)); |
| opacity: 0.9; |
| } |
| |
| .glass-card::after { |
| content: ''; |
| position: absolute; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| background: radial-gradient( |
| 650px circle at var(--mouse-x, 50%) var(--mouse-y, 50%), |
| rgba(2, 132, 199, 0.08), |
| transparent 45% |
| ); |
| pointer-events: none; |
| z-index: 1; |
| } |
| |
| .glass-card:hover { |
| border-color: var(--border-hover); |
| } |
| |
| .card-header { |
| display: flex; |
| align-items: center; |
| gap: 0.75rem; |
| font-size: 1.4rem; |
| font-weight: 800; |
| margin-bottom: 0.35rem; |
| letter-spacing: -0.015em; |
| } |
| |
| .card-sub { |
| font-size: 0.92rem; |
| color: var(--text-muted); |
| margin-bottom: 1.85rem; |
| line-height: 1.5; |
| } |
| |
| |
| .field-label { |
| display: flex; |
| justify-content: space-between; |
| font-family: 'JetBrains Mono', monospace; |
| font-size: 0.8rem; |
| font-weight: 700; |
| text-transform: uppercase; |
| letter-spacing: 0.06em; |
| color: var(--text-muted); |
| margin-bottom: 0.65rem; |
| } |
| |
| .dropzone { |
| border: 2px dashed var(--dropzone-border); |
| border-radius: 18px; |
| padding: 1.9rem 1.35rem; |
| text-align: center; |
| cursor: pointer; |
| transition: all 0.25s cubic-bezier(0.16, 1, 0.3, 1); |
| background: var(--input-bg); |
| margin-bottom: 1.75rem; |
| } |
| |
| .dropzone:hover { |
| border-color: var(--primary-accent); |
| background: rgba(2, 132, 199, 0.06); |
| transform: translateY(-3px); |
| box-shadow: 0 10px 25px rgba(0, 0, 0, 0.06); |
| } |
| |
| .dropzone-icon { |
| font-size: 2.4rem; |
| background: linear-gradient(135deg, var(--primary-accent), var(--secondary-accent)); |
| -webkit-background-clip: text; |
| -webkit-text-fill-color: transparent; |
| margin-bottom: 0.7rem; |
| } |
| |
| .dropzone h4 { |
| font-size: 1rem; |
| font-weight: 700; |
| margin-bottom: 0.25rem; |
| color: var(--text-main); |
| } |
| |
| .dropzone p { |
| font-size: 0.83rem; |
| color: var(--text-muted); |
| } |
| |
| .badge-selected { |
| margin-top: 0.85rem; |
| padding: 0.55rem 1.05rem; |
| background: rgba(16, 185, 129, 0.15); |
| border: 1.5px solid var(--success-color); |
| border-radius: 12px; |
| font-size: 0.85rem; |
| font-weight: 700; |
| color: var(--success-color); |
| display: none; |
| align-items: center; |
| justify-content: center; |
| gap: 0.55rem; |
| } |
| |
| |
| .tab-box { |
| display: flex; |
| gap: 0.5rem; |
| background: var(--input-bg); |
| padding: 0.4rem; |
| border-radius: 14px; |
| border: 1.5px solid var(--border-card); |
| margin-bottom: 1.1rem; |
| } |
| |
| .tab-btn { |
| flex: 1; |
| padding: 0.65rem; |
| font-family: 'JetBrains Mono', monospace; |
| font-size: 0.83rem; |
| font-weight: 700; |
| border: none; |
| background: transparent; |
| color: var(--text-muted); |
| border-radius: 10px; |
| cursor: pointer; |
| transition: all 0.2s ease; |
| } |
| |
| .tab-btn.active { |
| background: rgba(2, 132, 199, 0.15); |
| color: var(--primary-accent); |
| border: 1px solid var(--primary-accent); |
| box-shadow: 0 2px 8px rgba(0,0,0,0.05); |
| } |
| |
| textarea { |
| width: 100%; |
| height: 140px; |
| background: var(--input-bg); |
| border: 1.5px solid var(--border-card); |
| border-radius: 14px; |
| padding: 1.1rem; |
| color: var(--text-main); |
| font-family: inherit; |
| font-size: 0.92rem; |
| resize: vertical; |
| margin-bottom: 1.75rem; |
| } |
| |
| textarea:focus { |
| outline: none; |
| border-color: var(--primary-accent); |
| box-shadow: 0 0 0 3px rgba(2, 132, 199, 0.15); |
| } |
| |
| |
| .btn-row { |
| display: flex; |
| gap: 1.1rem; |
| } |
| |
| .btn { |
| flex: 1; |
| padding: 1rem 1.5rem; |
| border-radius: 14px; |
| font-weight: 800; |
| font-size: 0.96rem; |
| display: inline-flex; |
| align-items: center; |
| justify-content: center; |
| gap: 0.65rem; |
| cursor: pointer; |
| transition: all 0.25s cubic-bezier(0.34, 1.56, 0.64, 1); |
| border: none; |
| } |
| |
| .btn-primary { |
| background: linear-gradient(135deg, var(--primary-accent), var(--secondary-accent)); |
| color: #ffffff; |
| box-shadow: 0 8px 25px rgba(2, 132, 199, 0.35); |
| } |
| |
| [data-theme="dark"] .btn-primary { |
| color: #0b1326; |
| } |
| |
| .btn-primary:hover { |
| transform: translateY(-3px); |
| box-shadow: 0 14px 32px rgba(2, 132, 199, 0.5); |
| } |
| |
| .btn-secondary { |
| background: var(--surface-card); |
| color: var(--text-main); |
| border: 1.5px solid var(--border-card); |
| } |
| |
| .btn-secondary:hover { |
| border-color: var(--primary-accent); |
| transform: translateY(-2px); |
| background: var(--surface-hover); |
| } |
| |
| |
| .empty-state { |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| justify-content: center; |
| min-height: 540px; |
| text-align: center; |
| padding: 3.5rem; |
| background: var(--surface-card); |
| border: 2px dashed var(--border-card); |
| border-radius: 24px; |
| box-shadow: var(--card-shadow); |
| } |
| |
| .empty-icon { |
| width: 90px; |
| height: 90px; |
| border-radius: 50%; |
| background: rgba(2, 132, 199, 0.1); |
| border: 1.5px solid var(--border-card); |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| font-size: 2.5rem; |
| color: var(--primary-accent); |
| margin-bottom: 1.6rem; |
| animation: float 4s ease-in-out infinite; |
| } |
| |
| @keyframes float { |
| 0%, 100% { transform: translateY(0); } |
| 50% { transform: translateY(-12px); } |
| } |
| |
| |
| .loader-box { |
| display: none; |
| flex-direction: column; |
| align-items: center; |
| justify-content: center; |
| min-height: 540px; |
| gap: 1.85rem; |
| background: var(--surface-card); |
| border: 1.5px solid var(--border-card); |
| border-radius: 24px; |
| box-shadow: var(--card-shadow); |
| } |
| |
| .cube { |
| width: 68px; |
| height: 68px; |
| position: relative; |
| transform-style: preserve-3d; |
| animation: spinCube 3s infinite linear; |
| } |
| |
| .cube-face { |
| position: absolute; |
| width: 68px; |
| height: 68px; |
| background: rgba(2, 132, 199, 0.18); |
| border: 2px solid var(--primary-accent); |
| box-shadow: 0 0 18px rgba(2, 132, 199, 0.35); |
| } |
| |
| .front { transform: rotateY(0deg) translateZ(34px); } |
| .back { transform: rotateY(180deg) translateZ(34px); } |
| .right { transform: rotateY(90deg) translateZ(34px); } |
| .left { transform: rotateY(-90deg) translateZ(34px); } |
| .top { transform: rotateX(90deg) translateZ(34px); } |
| .bottom { transform: rotateX(-90deg) translateZ(34px); } |
| |
| @keyframes spinCube { |
| to { transform: rotateX(360deg) rotateY(360deg); } |
| } |
| |
| |
| #results-dashboard { |
| display: none; |
| } |
| |
| .score-banner { |
| display: flex; |
| align-items: center; |
| gap: 2.4rem; |
| background: linear-gradient(135deg, rgba(16, 185, 129, 0.12), rgba(2, 132, 199, 0.08)); |
| border: 1.5px solid rgba(16, 185, 129, 0.35); |
| border-radius: 20px; |
| padding: 1.85rem; |
| margin-bottom: 2rem; |
| box-shadow: 0 8px 24px rgba(0, 0, 0, 0.05); |
| } |
| |
| .gauge-wrap { |
| position: relative; |
| width: 120px; |
| height: 120px; |
| flex-shrink: 0; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| } |
| |
| .score-center { |
| position: absolute; |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| } |
| |
| .score-val { |
| font-size: 2.25rem; |
| font-weight: 800; |
| line-height: 1; |
| } |
| |
| .score-lbl { |
| font-family: 'JetBrains Mono', monospace; |
| font-size: 0.68rem; |
| font-weight: 700; |
| letter-spacing: 0.08em; |
| color: var(--text-muted); |
| margin-top: 3px; |
| } |
| |
| .rationale-header { |
| font-size: 1.2rem; |
| font-weight: 800; |
| color: var(--text-main); |
| margin-bottom: 0.45rem; |
| } |
| |
| .rationale-body { |
| font-size: 0.95rem; |
| line-height: 1.62; |
| color: var(--text-muted); |
| } |
| |
| .grid-2 { |
| display: grid; |
| grid-template-columns: 1fr 1fr; |
| gap: 1.5rem; |
| margin-bottom: 1.5rem; |
| } |
| |
| @media (max-width: 850px) { |
| .grid-2 { |
| grid-template-columns: 1fr; |
| } |
| } |
| |
| .section-box { |
| background: var(--input-bg); |
| border: 1.5px solid var(--border-card); |
| border-radius: 18px; |
| padding: 1.5rem; |
| } |
| |
| .section-title { |
| display: flex; |
| align-items: center; |
| gap: 0.65rem; |
| font-size: 1.03rem; |
| font-weight: 800; |
| margin-bottom: 1.15rem; |
| } |
| |
| .list-row { |
| display: flex; |
| align-items: flex-start; |
| gap: 0.7rem; |
| font-size: 0.9rem; |
| line-height: 1.58; |
| color: var(--text-main); |
| margin-bottom: 0.9rem; |
| padding-bottom: 0.9rem; |
| border-bottom: 1px solid var(--border-card); |
| } |
| |
| .list-row:last-child { |
| margin-bottom: 0; |
| padding-bottom: 0; |
| border-bottom: none; |
| } |
| |
| .footer-disclaimer { |
| font-size: 0.8rem; |
| color: var(--text-muted); |
| background: var(--input-bg); |
| border: 1.5px solid var(--border-card); |
| border-radius: 14px; |
| padding: 1.1rem 1.35rem; |
| line-height: 1.58; |
| margin-top: 1.5rem; |
| } |
| </style> |
| </head> |
| <body> |
|
|
| |
| <canvas id="bg-canvas"></canvas> |
|
|
| |
| <header> |
| <div class="header-inner"> |
| <a href="#" class="brand"> |
| <div class="brand-logo"> |
| <i class="fa-solid fa-cube"></i> |
| </div> |
| <span>MATCH.AI <small style="font-weight: 500; color: var(--primary-accent); font-size: 0.88rem;">Executive Studio</small></span> |
| </a> |
|
|
| <div class="header-controls"> |
| |
| <button class="theme-switch-btn" id="theme-toggle" onclick="toggleTheme()" title="Switch Light / Dark Theme"> |
| <i class="fa-solid fa-moon" id="theme-icon"></i> |
| <span id="theme-label">Dark Theme</span> |
| </button> |
|
|
| <div class="stitch-badge"> |
| <span class="stitch-dot"></span> |
| Google Stitch • 3D Edition |
| </div> |
| </div> |
| </div> |
| </header> |
|
|
| |
| <main class="main-container"> |
| |
| <div class="glass-card"> |
| <div class="card-header"> |
| <i class="fa-solid fa-wand-magic-sparkles" style="color: var(--primary-accent);"></i> |
| Document Screening Deck |
| </div> |
| <div class="card-sub">Upload candidate resume & job description</div> |
|
|
| <div class="field-label"> |
| <span>Candidate Resume (.pdf / .docx / .txt)</span> |
| <span>Max 10MB</span> |
| </div> |
| <div class="dropzone" id="resume-dropzone" onclick="document.getElementById('resume-file').click()"> |
| <i class="fa-solid fa-file-pdf dropzone-icon"></i> |
| <h4>Drag & Drop candidate resume</h4> |
| <p>Supports .pdf, Word (.docx), or .txt</p> |
| <input type="file" id="resume-file" accept=".pdf,.txt,.docx,.doc" style="display: none;"> |
| <div class="badge-selected" id="resume-badge"> |
| <i class="fa-solid fa-check"></i> |
| <span id="resume-name"></span> |
| </div> |
| </div> |
|
|
| <div class="field-label"> |
| <span>Job Description</span> |
| </div> |
| <div class="tab-box"> |
| <button class="tab-btn active" id="btn-file" onclick="setTab('file')">Upload File</button> |
| <button class="tab-btn" id="btn-text" onclick="setTab('text')">Paste Text</button> |
| </div> |
|
|
| <div id="view-jd-file"> |
| <div class="dropzone" id="jd-dropzone" onclick="document.getElementById('jd-file').click()"> |
| <i class="fa-solid fa-briefcase dropzone-icon"></i> |
| <h4>Drag & Drop Job Spec file</h4> |
| <p>Supports .pdf, Word (.docx), or .txt</p> |
| <input type="file" id="jd-file" accept=".pdf,.txt,.docx,.doc" style="display: none;"> |
| <div class="badge-selected" id="jd-badge"> |
| <i class="fa-solid fa-check"></i> |
| <span id="jd-name"></span> |
| </div> |
| </div> |
| </div> |
|
|
| <div id="view-jd-text" style="display: none;"> |
| <textarea id="jd-textarea" placeholder="Paste the complete job description or requirements here..."></textarea> |
| </div> |
|
|
| <div class="btn-row"> |
| <button class="btn btn-secondary" onclick="triggerDemo()"> |
| <i class="fa-solid fa-bolt" style="color: var(--warning-color);"></i> 1-Click Demo |
| </button> |
| <button class="btn btn-primary" onclick="runAssessment()"> |
| <i class="fa-solid fa-radar"></i> Run 3D Scan |
| </button> |
| </div> |
| </div> |
|
|
| |
| <div> |
| |
| <div class="empty-state" id="panel-empty"> |
| <div class="empty-icon"> |
| <i class="fa-solid fa-layer-group"></i> |
| </div> |
| <h3 style="font-size: 1.4rem; font-weight: 800; margin-bottom: 0.5rem; color: var(--text-main);">Awaiting Document Scan</h3> |
| <p style="color: var(--text-muted); max-width: 390px; line-height: 1.62; font-size: 0.94rem;"> |
| Upload documents on the left or click <strong>1-Click Demo</strong> to experience the structured Pydantic AI assessment. |
| </p> |
| </div> |
|
|
| |
| <div class="loader-box" id="panel-loader"> |
| <div class="cube"> |
| <div class="cube-face front"></div> |
| <div class="cube-face back"></div> |
| <div class="cube-face right"></div> |
| <div class="cube-face left"></div> |
| <div class="cube-face top"></div> |
| <div class="cube-face bottom"></div> |
| </div> |
| <div style="text-align: center;"> |
| <h3 style="font-size: 1.3rem; font-weight: 800; margin-bottom: 0.35rem; color: var(--text-main);">AI Assessment in Progress...</h3> |
| <p style="color: var(--text-muted); font-size: 0.9rem;">Validating against Pydantic schema & extracting evidence</p> |
| </div> |
| </div> |
|
|
| |
| <div class="glass-card" id="results-dashboard"> |
| <div class="score-banner"> |
| <div class="gauge-wrap"> |
| <svg width="120" height="120" viewBox="0 0 120 120" style="transform: rotate(-90deg);"> |
| <circle cx="60" cy="60" r="48" stroke="rgba(128,128,128,0.18)" stroke-width="10" fill="none" /> |
| <circle id="gauge-ring" cx="60" cy="60" r="48" stroke="#10b981" stroke-width="10" fill="none" |
| stroke-dasharray="301" stroke-dashoffset="301" stroke-linecap="round" |
| style="transition: stroke-dashoffset 1.5s cubic-bezier(0.34, 1.56, 0.64, 1), stroke 0.5s ease;" /> |
| </svg> |
| <div class="score-center"> |
| <span class="score-val" id="res-score">0</span> |
| <span class="score-lbl">MATCH</span> |
| </div> |
| </div> |
|
|
| <div> |
| <div class="rationale-header">Executive Evaluation Rationale</div> |
| <div class="rationale-body" id="res-rationale"></div> |
| </div> |
| </div> |
|
|
| <div class="grid-2"> |
| <div class="section-box"> |
| <div class="section-title" style="color: var(--success-color);"> |
| <i class="fa-solid fa-circle-check"></i> Matched Qualifications |
| </div> |
| <div id="list-matched"></div> |
| </div> |
|
|
| <div class="section-box"> |
| <div class="section-title" style="color: var(--warning-color);"> |
| <i class="fa-solid fa-triangle-exclamation"></i> Identified Gaps |
| </div> |
| <div id="list-missing"></div> |
| </div> |
| </div> |
|
|
| <div class="section-box"> |
| <div class="section-title" style="color: var(--primary-accent);"> |
| <i class="fa-solid fa-lightbulb"></i> Actionable Presentation Suggestions |
| </div> |
| <div id="list-suggestions"></div> |
| </div> |
|
|
| <div class="footer-disclaimer"> |
| <strong style="color: var(--text-main);">Technical Transparency Note:</strong> <span id="res-limitations"></span> |
| </div> |
| </div> |
| </div> |
| </main> |
|
|
| <script> |
| |
| |
| |
| function initTheme() { |
| const saved = localStorage.getItem('match_ai_theme') || 'light'; |
| applyTheme(saved); |
| } |
| |
| function toggleTheme() { |
| const current = document.documentElement.getAttribute('data-theme') === 'light' ? 'light' : 'dark'; |
| const next = current === 'dark' ? 'light' : 'dark'; |
| applyTheme(next); |
| localStorage.setItem('match_ai_theme', next); |
| } |
| |
| function applyTheme(theme) { |
| document.documentElement.setAttribute('data-theme', theme); |
| const icon = document.getElementById('theme-icon'); |
| const label = document.getElementById('theme-label'); |
| if (theme === 'light') { |
| icon.className = 'fa-solid fa-sun'; |
| label.textContent = 'Light Theme'; |
| } else { |
| icon.className = 'fa-solid fa-moon'; |
| label.textContent = 'Dark Theme'; |
| } |
| } |
| initTheme(); |
| |
| |
| |
| |
| const canvas = document.getElementById('bg-canvas'); |
| const ctx = canvas.getContext('2d'); |
| let width, height; |
| let particles = []; |
| |
| function resizeCanvas() { |
| width = canvas.width = window.innerWidth; |
| height = canvas.height = window.innerHeight; |
| } |
| window.addEventListener('resize', resizeCanvas); |
| resizeCanvas(); |
| |
| class Particle { |
| constructor() { |
| this.x = Math.random() * width; |
| this.y = Math.random() * height; |
| this.z = Math.random() * 0.8 + 0.4; |
| this.vx = (Math.random() - 0.5) * 0.6 * this.z; |
| this.vy = (Math.random() - 0.5) * 0.6 * this.z; |
| this.radius = (Math.random() * 1.8 + 1.2) * this.z; |
| } |
| update() { |
| this.x += this.vx; |
| this.y += this.vy; |
| if (this.x < 0 || this.x > width) this.vx = -this.vx; |
| if (this.y < 0 || this.y > height) this.vy = -this.vy; |
| } |
| draw() { |
| ctx.beginPath(); |
| ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2); |
| const isLight = document.documentElement.getAttribute('data-theme') === 'light'; |
| ctx.fillStyle = isLight |
| ? `rgba(2, 132, 199, ${0.55 * this.z})` |
| : `rgba(76, 215, 246, ${0.6 * this.z})`; |
| ctx.fill(); |
| } |
| } |
| |
| function initParticles() { |
| particles = []; |
| const count = Math.floor((width * height) / 13000); |
| for (let i = 0; i < count; i++) { |
| particles.push(new Particle()); |
| } |
| } |
| initParticles(); |
| |
| function animateCanvas() { |
| ctx.clearRect(0, 0, width, height); |
| const isLight = document.documentElement.getAttribute('data-theme') === 'light'; |
| for (let i = 0; i < particles.length; i++) { |
| particles[i].update(); |
| particles[i].draw(); |
| |
| for (let j = i + 1; j < particles.length; j++) { |
| const dx = particles[i].x - particles[j].x; |
| const dy = particles[i].y - particles[j].y; |
| const dist = Math.sqrt(dx * dx + dy * dy); |
| if (dist < 120) { |
| ctx.beginPath(); |
| ctx.moveTo(particles[i].x, particles[i].y); |
| ctx.lineTo(particles[j].x, particles[j].y); |
| ctx.strokeStyle = isLight |
| ? `rgba(2, 132, 199, ${0.22 * (1 - dist / 120)})` |
| : `rgba(76, 215, 246, ${0.2 * (1 - dist / 120)})`; |
| ctx.lineWidth = 1; |
| ctx.stroke(); |
| } |
| } |
| } |
| requestAnimationFrame(animateCanvas); |
| } |
| animateCanvas(); |
| |
| |
| |
| |
| document.querySelectorAll('.glass-card').forEach(card => { |
| card.addEventListener('mousemove', (e) => { |
| const rect = card.getBoundingClientRect(); |
| const x = e.clientX - rect.left; |
| const y = e.clientY - rect.top; |
| const rotateX = (((y - rect.height / 2) / (rect.height / 2)) * -5.5).toFixed(2); |
| const rotateY = (((x - rect.width / 2) / (rect.width / 2)) * 5.5).toFixed(2); |
| |
| card.style.transform = `perspective(1200px) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`; |
| card.style.setProperty('--mouse-x', `${x}px`); |
| card.style.setProperty('--mouse-y', `${y}px`); |
| }); |
| |
| card.addEventListener('mouseleave', () => { |
| card.style.transform = `perspective(1200px) rotateX(0deg) rotateY(0deg)`; |
| }); |
| }); |
| |
| |
| |
| |
| let activeTab = 'file'; |
| |
| function setTab(tab) { |
| activeTab = tab; |
| document.getElementById('btn-file').classList.toggle('active', tab === 'file'); |
| document.getElementById('btn-text').classList.toggle('active', tab === 'text'); |
| document.getElementById('view-jd-file').style.display = tab === 'file' ? 'block' : 'none'; |
| document.getElementById('view-jd-text').style.display = tab === 'text' ? 'block' : 'none'; |
| } |
| |
| document.getElementById('resume-file').addEventListener('change', (e) => { |
| if (e.target.files[0]) { |
| document.getElementById('resume-name').textContent = e.target.files[0].name; |
| document.getElementById('resume-badge').style.display = 'inline-flex'; |
| } |
| }); |
| |
| document.getElementById('jd-file').addEventListener('change', (e) => { |
| if (e.target.files[0]) { |
| document.getElementById('jd-name').textContent = e.target.files[0].name; |
| document.getElementById('jd-badge').style.display = 'inline-flex'; |
| } |
| }); |
| |
| function switchPanel(panel) { |
| document.getElementById('panel-empty').style.display = panel === 'empty' ? 'flex' : 'none'; |
| document.getElementById('panel-loader').style.display = panel === 'loader' ? 'flex' : 'none'; |
| document.getElementById('results-dashboard').style.display = panel === 'results' ? 'block' : 'none'; |
| } |
| |
| |
| |
| |
| function renderAssessment(data) { |
| switchPanel('results'); |
| |
| const score = data.match_score; |
| document.getElementById('res-score').textContent = score; |
| document.getElementById('res-rationale').textContent = data.score_rationale; |
| |
| const ring = document.getElementById('gauge-ring'); |
| const circumference = 301; |
| const offset = circumference - (score / 100) * circumference; |
| |
| let color = '#10b981'; |
| if (score < 75) color = '#f59e0b'; |
| if (score < 55) color = '#ef4444'; |
| ring.style.stroke = color; |
| |
| setTimeout(() => { |
| ring.style.strokeDashoffset = offset; |
| }, 100); |
| |
| populateList('list-matched', data.matched_requirements, 'fa-check', '#10b981'); |
| populateList('list-missing', data.missing_requirements, 'fa-exclamation-circle', '#f59e0b'); |
| populateList('list-suggestions', data.suggestions, 'fa-arrow-right', 'var(--primary-accent)'); |
| |
| const lims = data.limitations && data.limitations.length > 0 |
| ? data.limitations.join(' • ') |
| : 'No document limitations noted.'; |
| document.getElementById('res-limitations').textContent = lims; |
| } |
| |
| function populateList(elementId, items, icon, color) { |
| const container = document.getElementById(elementId); |
| container.innerHTML = ''; |
| if (!items || items.length === 0) { |
| container.innerHTML = `<div class="list-row" style="color: var(--text-muted);">None identified.</div>`; |
| return; |
| } |
| items.forEach(text => { |
| const row = document.createElement('div'); |
| row.className = 'list-row'; |
| row.innerHTML = `<i class="fa-solid ${icon}" style="color: ${color}; margin-top: 3px;"></i> <span>${text}</span>`; |
| container.appendChild(row); |
| }); |
| } |
| |
| async function triggerDemo() { |
| switchPanel('loader'); |
| try { |
| const res = await fetch('/api/sample'); |
| const data = await res.json(); |
| renderAssessment(data); |
| } catch (err) { |
| alert('Demo execution error: ' + err.message); |
| switchPanel('empty'); |
| } |
| } |
| |
| async function runAssessment() { |
| const resumeFile = document.getElementById('resume-file').files[0]; |
| if (!resumeFile) { |
| alert('Please select a candidate resume file (.pdf, .docx, or .txt)'); |
| return; |
| } |
| |
| const formData = new FormData(); |
| formData.append('resume_file', resumeFile); |
| |
| if (activeTab === 'file') { |
| const jdFile = document.getElementById('jd-file').files[0]; |
| if (!jdFile) { |
| alert('Please select a job description file (.pdf, .docx, or .txt)'); |
| return; |
| } |
| formData.append('jd_file', jdFile); |
| } else { |
| const jdText = document.getElementById('jd-textarea').value.trim(); |
| if (!jdText) { |
| alert('Please paste the job description text.'); |
| return; |
| } |
| formData.append('jd_text', jdText); |
| } |
| |
| switchPanel('loader'); |
| try { |
| const res = await fetch('/api/scan', { |
| method: 'POST', |
| body: formData |
| }); |
| if (!res.ok) { |
| const err = await res.json(); |
| throw new Error(err.detail || 'Server error occurred'); |
| } |
| const data = await res.json(); |
| renderAssessment(data); |
| } catch (err) { |
| alert('Scan error: ' + err.message); |
| switchPanel('empty'); |
| } |
| } |
| </script> |
| </body> |
| </html> |
|
|