| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>HireScope AI — Intelligent Resume Screening</title> |
| <meta name="description" content="AI-powered resume screening system with semantic matching, skill extraction, and audio transcription."> |
|
|
| |
| <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=Inter:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet"> |
|
|
| |
| <script src="https://cdn.tailwindcss.com"></script> |
| <script> |
| tailwind.config = { |
| theme: { |
| extend: { |
| fontFamily: { |
| sans: ['Inter', 'system-ui', 'sans-serif'], |
| }, |
| colors: { |
| brand: { |
| 50: '#eef2ff', |
| 100: '#e0e7ff', |
| 200: '#c7d2fe', |
| 300: '#a5b4fc', |
| 400: '#818cf8', |
| 500: '#6366f1', |
| 600: '#4f46e5', |
| 700: '#4338ca', |
| 800: '#3730a3', |
| 900: '#312e81', |
| }, |
| sky: { |
| 50: '#f0f9ff', |
| 400: '#38bdf8', |
| 500: '#0ea5e9', |
| 600: '#0284c7', |
| } |
| } |
| } |
| } |
| } |
| </script> |
| <style> |
| * { font-family: 'Inter', system-ui, sans-serif; } |
| |
| body { |
| background: linear-gradient(135deg, #f0f4f8 0%, #e8edf5 50%, #f0f0ff 100%); |
| min-height: 100vh; |
| } |
| |
| .glass-card { |
| background: rgba(255, 255, 255, 0.85); |
| backdrop-filter: blur(20px); |
| -webkit-backdrop-filter: blur(20px); |
| border: 1px solid rgba(255, 255, 255, 0.9); |
| box-shadow: 0 4px 24px rgba(0, 0, 0, 0.06), 0 1px 2px rgba(0, 0, 0, 0.04); |
| } |
| |
| .glass-card:hover { |
| box-shadow: 0 8px 40px rgba(99, 102, 241, 0.08), 0 2px 8px rgba(0, 0, 0, 0.06); |
| } |
| |
| .gradient-text { |
| background: linear-gradient(135deg, #6366f1, #0ea5e9); |
| -webkit-background-clip: text; |
| -webkit-text-fill-color: transparent; |
| background-clip: text; |
| } |
| |
| .nav-glass { |
| background: rgba(255, 255, 255, 0.92); |
| backdrop-filter: blur(24px); |
| -webkit-backdrop-filter: blur(24px); |
| border-bottom: 1px solid rgba(226, 232, 240, 0.8); |
| } |
| |
| @keyframes fadeInUp { |
| from { opacity: 0; transform: translateY(20px); } |
| to { opacity: 1; transform: translateY(0); } |
| } |
| |
| @keyframes slideInRight { |
| from { opacity: 0; transform: translateX(20px); } |
| to { opacity: 1; transform: translateX(0); } |
| } |
| |
| @keyframes pulse-soft { |
| 0%, 100% { opacity: 1; } |
| 50% { opacity: 0.6; } |
| } |
| |
| .animate-fade-in-up { animation: fadeInUp 0.5s ease-out forwards; } |
| .animate-fade-in-up-delay { animation: fadeInUp 0.5s ease-out 0.1s forwards; opacity: 0; } |
| .animate-fade-in-up-delay-2 { animation: fadeInUp 0.5s ease-out 0.2s forwards; opacity: 0; } |
| .animate-slide-in { animation: slideInRight 0.4s ease-out forwards; } |
| .animate-pulse-soft { animation: pulse-soft 2s ease-in-out infinite; } |
| |
| |
| ::-webkit-scrollbar { width: 6px; } |
| ::-webkit-scrollbar-track { background: transparent; } |
| ::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 3px; } |
| ::-webkit-scrollbar-thumb:hover { background: #94a3b8; } |
| |
| |
| .btn-primary { |
| background: linear-gradient(135deg, #6366f1, #4f46e5); |
| transition: all 0.3s ease; |
| } |
| .btn-primary:hover { |
| background: linear-gradient(135deg, #818cf8, #6366f1); |
| box-shadow: 0 8px 25px rgba(99, 102, 241, 0.3); |
| transform: translateY(-1px); |
| } |
| |
| .btn-secondary { |
| background: linear-gradient(135deg, #0ea5e9, #0284c7); |
| transition: all 0.3s ease; |
| } |
| .btn-secondary:hover { |
| background: linear-gradient(135deg, #38bdf8, #0ea5e9); |
| box-shadow: 0 8px 25px rgba(14, 165, 233, 0.3); |
| transform: translateY(-1px); |
| } |
| |
| |
| .flash-success { background: linear-gradient(135deg, #ecfdf5, #d1fae5); border-left: 4px solid #10b981; } |
| .flash-error { background: linear-gradient(135deg, #fef2f2, #fecaca); border-left: 4px solid #ef4444; } |
| .flash-info { background: linear-gradient(135deg, #eff6ff, #dbeafe); border-left: 4px solid #3b82f6; } |
| .flash-warning { background: linear-gradient(135deg, #fffbeb, #fef3c7); border-left: 4px solid #f59e0b; } |
| |
| |
| .mobile-menu { display: none; } |
| .mobile-menu.open { display: flex; } |
| |
| |
| .modal-backdrop { |
| background: rgba(15, 23, 42, 0.5); |
| backdrop-filter: blur(4px); |
| } |
| |
| |
| .skill-tag { |
| transition: all 0.2s ease; |
| } |
| .skill-tag:hover { |
| transform: translateY(-1px); |
| box-shadow: 0 2px 8px rgba(99, 102, 241, 0.2); |
| } |
| </style> |
| </head> |
| <body class="min-h-screen flex flex-col font-sans antialiased text-slate-800"> |
|
|
| |
| <nav class="nav-glass sticky top-0 z-50"> |
| <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> |
| <div class="flex items-center justify-between h-16"> |
| |
| <div class="flex items-center gap-3"> |
| <div class="bg-gradient-to-br from-brand-500 to-sky-500 p-2 rounded-xl shadow-lg shadow-brand-200"> |
| <svg class="w-6 h-6 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path> |
| </svg> |
| </div> |
| <a href="/" class="text-xl font-extrabold gradient-text tracking-tight">HireScope AI</a> |
| </div> |
| |
| |
| <div class="hidden md:flex items-center gap-2"> |
| {% if session.get('user_id') %} |
| <a href="/" class="px-4 py-2 text-slate-600 hover:text-brand-600 hover:bg-brand-50 rounded-lg transition font-medium text-sm"> |
| <span class="flex items-center gap-2"> |
| <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"></path></svg> |
| Upload |
| </span> |
| </a> |
| <a href="/results" class="px-4 py-2 text-slate-600 hover:text-brand-600 hover:bg-brand-50 rounded-lg transition font-medium text-sm"> |
| <span class="flex items-center gap-2"> |
| <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"></path></svg> |
| Results |
| </span> |
| </a> |
| <a href="/ranking" class="px-4 py-2 text-slate-600 hover:text-brand-600 hover:bg-brand-50 rounded-lg transition font-medium text-sm flex items-center gap-2"> |
| <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6"></path></svg> |
| Leaderboard |
| {% if candidate_count is defined and candidate_count > 0 %} |
| <span class="bg-brand-500 text-white text-xs px-2 py-0.5 rounded-full font-semibold">{{ candidate_count }}</span> |
| {% endif %} |
| </a> |
|
|
| <div class="pl-3 ml-2 border-l border-slate-200 flex items-center gap-3"> |
| <div class="flex items-center gap-2 bg-slate-100 px-3 py-1.5 rounded-lg"> |
| <div class="w-7 h-7 bg-gradient-to-br from-brand-500 to-sky-500 rounded-full flex items-center justify-center text-white text-xs font-bold"> |
| {{ session.username[0]|upper }} |
| </div> |
| <span class="text-sm font-medium text-slate-700">{{ session.username }}</span> |
| </div> |
| <a href="/logout" class="text-sm text-red-500 hover:text-red-600 hover:bg-red-50 px-3 py-1.5 rounded-lg transition font-medium">Log out</a> |
| </div> |
| {% else %} |
| <a href="/login" class="px-4 py-2 text-slate-600 hover:text-brand-600 font-medium text-sm rounded-lg hover:bg-brand-50 transition">Sign In</a> |
| <a href="/register" class="btn-primary text-white px-5 py-2 rounded-lg text-sm font-semibold shadow-md">Sign Up Free</a> |
| {% endif %} |
| </div> |
|
|
| |
| <button onclick="document.getElementById('mobile-nav').classList.toggle('open')" class="md:hidden p-2 rounded-lg hover:bg-slate-100 transition"> |
| <svg class="w-6 h-6 text-slate-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path></svg> |
| </button> |
| </div> |
|
|
| |
| <div id="mobile-nav" class="mobile-menu flex-col gap-2 pb-4 md:hidden"> |
| {% if session.get('user_id') %} |
| <a href="/" class="px-4 py-2 text-slate-600 hover:bg-brand-50 rounded-lg font-medium text-sm">Upload</a> |
| <a href="/results" class="px-4 py-2 text-slate-600 hover:bg-brand-50 rounded-lg font-medium text-sm">Results</a> |
| <a href="/ranking" class="px-4 py-2 text-slate-600 hover:bg-brand-50 rounded-lg font-medium text-sm">Leaderboard</a> |
| <a href="/logout" class="px-4 py-2 text-red-500 hover:bg-red-50 rounded-lg font-medium text-sm">Log out</a> |
| {% else %} |
| <a href="/login" class="px-4 py-2 text-slate-600 hover:bg-brand-50 rounded-lg font-medium text-sm">Sign In</a> |
| <a href="/register" class="px-4 py-2 text-brand-600 hover:bg-brand-50 rounded-lg font-medium text-sm">Sign Up</a> |
| {% endif %} |
| </div> |
| </div> |
| </nav> |
|
|
| |
| {% with messages = get_flashed_messages(with_categories=true) %} |
| {% if messages %} |
| <div class="max-w-5xl mx-auto mt-6 px-4 w-full space-y-3"> |
| {% for category, message in messages %} |
| <div class="flash-{{ category }} px-5 py-4 rounded-xl flex items-center gap-3 animate-slide-in shadow-sm"> |
| {% if category == 'success' %} |
| <svg class="w-5 h-5 text-emerald-600 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg> |
| <span class="text-emerald-800 font-medium text-sm">{{ message }}</span> |
| {% elif category == 'error' %} |
| <svg class="w-5 h-5 text-red-600 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg> |
| <span class="text-red-800 font-medium text-sm">{{ message }}</span> |
| {% elif category == 'warning' %} |
| <svg class="w-5 h-5 text-amber-600 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 16.5c-.77.833.192 2.5 1.732 2.5z"></path></svg> |
| <span class="text-amber-800 font-medium text-sm">{{ message }}</span> |
| {% else %} |
| <svg class="w-5 h-5 text-blue-600 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg> |
| <span class="text-blue-800 font-medium text-sm">{{ message }}</span> |
| {% endif %} |
| </div> |
| {% endfor %} |
| </div> |
| {% endif %} |
| {% endwith %} |
|
|
| |
| <main class="flex-grow flex flex-col pt-6 pb-16"> |
| {% block content %}{% endblock %} |
| </main> |
|
|
| |
| <footer class="border-t border-slate-200 bg-white/60 backdrop-blur py-6"> |
| <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> |
| <div class="flex flex-col md:flex-row items-center justify-between gap-4"> |
| <div class="flex items-center gap-2"> |
| <div class="bg-gradient-to-br from-brand-500 to-sky-500 p-1.5 rounded-lg"> |
| <svg class="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg> |
| </div> |
| <span class="text-sm font-semibold gradient-text">HireScope AI</span> |
| </div> |
| <p class="text-xs text-slate-400">Built with Semantic AI, Whisper & Sentence-Transformers</p> |
| </div> |
| </div> |
| </footer> |
|
|
| </body> |
| </html> |
|
|