← back to Wine Finder
public/verify.html
2119 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-capable" content="yes">
<title>Wine Verification Platform - Red Thunder</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap">
<style>
/* ========================================
PROFESSIONAL DESIGN SYSTEM
======================================== */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
/* Professional Color Palette - Deep Navy & Gold */
--navy-950: #0a1628;
--navy-900: #0f2744;
--navy-800: #1a3a5f;
--navy-700: #244d7a;
--navy-600: #2e6095;
--gold-500: #d4af37;
--gold-400: #e6c45a;
--gold-300: #f0d97d;
--white: #ffffff;
--gray-50: #f9fafb;
--gray-100: #f3f4f6;
--gray-200: #e5e7eb;
--gray-300: #d1d5db;
--gray-400: #9ca3af;
--gray-500: #6b7280;
--gray-600: #4b5563;
--gray-700: #374151;
--gray-800: #1f2937;
--gray-900: #111827;
--blue-500: #3b82f6;
--blue-600: #2563eb;
--green-500: #10b981;
--red-500: #ef4444;
--amber-500: #f59e0b;
/* Shadows */
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
--shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
--shadow-2xl: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
/* Gold Glow */
--glow-gold: 0 0 20px rgba(212, 175, 55, 0.3);
--glow-gold-lg: 0 0 40px rgba(212, 175, 55, 0.4);
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
background: linear-gradient(135deg, var(--navy-950) 0%, var(--navy-900) 50%, var(--navy-800) 100%);
color: var(--gray-800);
line-height: 1.6;
min-height: 100vh;
overflow-x: hidden;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* Subtle animated background gradient */
body::before {
content: '';
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background:
radial-gradient(circle at 20% 30%, rgba(212, 175, 55, 0.08) 0%, transparent 50%),
radial-gradient(circle at 80% 70%, rgba(59, 130, 246, 0.06) 0%, transparent 50%);
animation: subtleFloat 15s ease-in-out infinite;
pointer-events: none;
z-index: 0;
}
@keyframes subtleFloat {
0%, 100% { opacity: 0.4; transform: translate(0, 0); }
50% { opacity: 0.6; transform: translate(20px, -20px); }
}
.page-container {
position: relative;
z-index: 1;
min-height: 100vh;
}
/* ========================================
PROFESSIONAL HEADER
======================================== */
.header {
background: rgba(10, 22, 40, 0.8);
backdrop-filter: blur(20px) saturate(180%);
border-bottom: 1px solid rgba(212, 175, 55, 0.1);
position: sticky;
top: 0;
z-index: 100;
}
.header-content {
max-width: 1400px;
margin: 0 auto;
padding: 24px 48px;
display: flex;
justify-content: space-between;
align-items: center;
}
.brand {
display: flex;
align-items: center;
gap: 16px;
}
.brand-icon {
width: 48px;
height: 48px;
background: linear-gradient(135deg, var(--gold-500), var(--gold-300));
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
box-shadow: var(--glow-gold);
}
.brand-text h1 {
font-size: 24px;
font-weight: 700;
color: var(--white);
letter-spacing: -0.5px;
margin-bottom: 2px;
}
.brand-text p {
font-size: 11px;
font-weight: 600;
color: var(--gold-400);
text-transform: uppercase;
letter-spacing: 1.5px;
}
.header-actions {
display: flex;
align-items: center;
gap: 16px;
}
.nav-link {
color: var(--gray-300);
text-decoration: none;
padding: 10px 20px;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
font-weight: 500;
font-size: 14px;
transition: all 0.2s ease;
}
.nav-link:hover {
background: rgba(255, 255, 255, 0.1);
color: var(--white);
transform: translateY(-1px);
}
.logout-btn {
padding: 10px 24px;
font-size: 14px;
font-weight: 600;
color: var(--white);
background: rgba(239, 68, 68, 0.15);
border: 1px solid rgba(239, 68, 68, 0.3);
border-radius: 8px;
cursor: pointer;
transition: all 0.2s ease;
}
.logout-btn:hover {
background: rgba(239, 68, 68, 0.25);
border-color: rgba(239, 68, 68, 0.5);
transform: translateY(-1px);
}
/* ========================================
HERO SECTION - GIANT SEARCH
======================================== */
.hero-section {
max-width: 1400px;
margin: 0 auto;
padding: 80px 48px 60px;
text-align: center;
}
.hero-title {
font-size: 56px;
font-weight: 800;
color: var(--white);
letter-spacing: -2px;
margin-bottom: 20px;
line-height: 1.1;
}
.hero-title .highlight {
background: linear-gradient(135deg, var(--gold-500), var(--gold-300));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.hero-subtitle {
font-size: 20px;
color: var(--gray-300);
max-width: 600px;
margin: 0 auto 48px;
line-height: 1.6;
font-weight: 400;
}
/* Giant Search Container */
.giant-search-container {
max-width: 900px;
margin: 0 auto 32px;
position: relative;
}
.search-box {
position: relative;
background: var(--white);
border-radius: 16px;
box-shadow: var(--shadow-2xl);
overflow: hidden;
transition: all 0.3s ease;
}
.search-box:focus-within {
box-shadow: var(--shadow-2xl), var(--glow-gold-lg);
transform: translateY(-2px);
}
.search-input-wrapper {
display: flex;
align-items: center;
gap: 16px;
padding: 12px 12px 12px 32px;
}
.search-icon-left {
font-size: 28px;
color: var(--gray-400);
flex-shrink: 0;
}
.search-input-main {
flex: 1;
border: none;
outline: none;
font-size: 20px;
font-weight: 500;
color: var(--gray-900);
padding: 12px 0;
background: transparent;
font-family: 'Inter', sans-serif;
}
.search-input-main::placeholder {
color: var(--gray-400);
}
.search-button-giant {
padding: 20px 48px;
font-size: 20px;
font-weight: 700;
color: var(--white);
background: linear-gradient(135deg, var(--gold-500), var(--gold-400));
border: none;
border-radius: 12px;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 8px 24px rgba(212, 175, 55, 0.3);
display: flex;
align-items: center;
gap: 12px;
white-space: nowrap;
flex-shrink: 0;
}
.search-button-giant:hover {
background: linear-gradient(135deg, var(--gold-400), var(--gold-300));
box-shadow: 0 12px 32px rgba(212, 175, 55, 0.5);
transform: translateY(-2px);
}
.search-button-giant:active {
transform: translateY(0);
}
/* Quick Filter Pills */
.quick-filters-hero {
display: flex;
justify-content: center;
gap: 12px;
flex-wrap: wrap;
margin-top: 24px;
}
.filter-pill {
padding: 12px 24px;
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 24px;
color: var(--gray-300);
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
display: flex;
align-items: center;
gap: 8px;
}
.filter-pill:hover {
background: rgba(255, 255, 255, 0.15);
border-color: var(--gold-400);
color: var(--white);
transform: translateY(-2px);
}
.filter-pill.active {
background: linear-gradient(135deg, rgba(212, 175, 55, 0.2), rgba(212, 175, 55, 0.15));
border-color: var(--gold-400);
color: var(--gold-400);
}
/* ========================================
MAIN CONTENT LAYOUT
======================================== */
.content-wrapper {
max-width: 1400px;
margin: 0 auto;
padding: 0 48px 80px;
}
.content-grid {
display: grid;
grid-template-columns: 1fr 400px;
gap: 32px;
}
/* ========================================
PROFESSIONAL CARDS
======================================== */
.card {
background: var(--white);
border-radius: 16px;
box-shadow: var(--shadow-lg);
overflow: hidden;
}
.card-header {
padding: 32px 32px 24px;
border-bottom: 1px solid var(--gray-200);
}
.card-title {
font-size: 24px;
font-weight: 700;
color: var(--gray-900);
margin-bottom: 8px;
display: flex;
align-items: center;
gap: 12px;
}
.card-subtitle {
font-size: 14px;
color: var(--gray-500);
line-height: 1.6;
}
.card-body {
padding: 32px;
}
/* ========================================
WINE VERIFICATION UPLOAD
======================================== */
.upload-area {
border: 2px dashed var(--gray-300);
border-radius: 12px;
padding: 60px 32px;
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
background: var(--gray-50);
position: relative;
overflow: hidden;
}
.upload-area:hover {
border-color: var(--gold-500);
background: var(--white);
box-shadow: var(--shadow-md);
}
.upload-area.dragover {
border-color: var(--gold-500);
background: rgba(212, 175, 55, 0.05);
transform: scale(1.02);
}
.upload-icon-large {
font-size: 64px;
color: var(--gray-400);
margin-bottom: 20px;
display: inline-block;
}
.upload-title {
font-size: 20px;
font-weight: 700;
color: var(--gray-900);
margin-bottom: 8px;
}
.upload-subtitle {
font-size: 14px;
color: var(--gray-500);
margin-bottom: 24px;
}
.upload-buttons {
display: flex;
gap: 12px;
justify-content: center;
flex-wrap: wrap;
}
.upload-btn {
padding: 14px 28px;
font-size: 15px;
font-weight: 600;
border-radius: 10px;
border: none;
cursor: pointer;
transition: all 0.2s ease;
display: flex;
align-items: center;
gap: 8px;
}
.upload-btn-primary {
background: linear-gradient(135deg, var(--blue-600), var(--blue-500));
color: var(--white);
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
}
.upload-btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(59, 130, 246, 0.4);
}
.upload-btn-secondary {
background: var(--gray-100);
color: var(--gray-700);
border: 1px solid var(--gray-300);
}
.upload-btn-secondary:hover {
background: var(--gray-200);
transform: translateY(-1px);
}
/* Preview Image */
.preview-container {
display: none;
margin-bottom: 24px;
animation: fadeIn 0.3s ease;
}
.preview-container.show {
display: block;
}
.preview-wrapper {
position: relative;
border-radius: 12px;
overflow: hidden;
background: var(--gray-100);
box-shadow: var(--shadow-lg);
}
.preview-image {
width: 100%;
height: auto;
display: block;
}
.remove-preview-btn {
position: absolute;
top: 16px;
right: 16px;
width: 40px;
height: 40px;
background: rgba(239, 68, 68, 0.95);
color: var(--white);
border: none;
border-radius: 50%;
font-size: 20px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
box-shadow: var(--shadow-md);
}
.remove-preview-btn:hover {
background: var(--red-500);
transform: scale(1.1);
}
/* Verify Button */
.verify-button-large {
width: 100%;
padding: 20px;
font-size: 18px;
font-weight: 700;
color: var(--white);
background: linear-gradient(135deg, var(--gold-500), var(--gold-400));
border: none;
border-radius: 12px;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 8px 24px rgba(212, 175, 55, 0.3);
margin-top: 24px;
}
.verify-button-large:hover:not(:disabled) {
background: linear-gradient(135deg, var(--gold-400), var(--gold-300));
box-shadow: 0 12px 32px rgba(212, 175, 55, 0.5);
transform: translateY(-2px);
}
.verify-button-large:disabled {
background: var(--gray-300);
cursor: not-allowed;
box-shadow: none;
opacity: 0.6;
}
/* ========================================
SEARCH RESULTS
======================================== */
.search-results {
display: none;
margin-top: 24px;
}
.search-results.show {
display: block;
}
.search-result-item {
background: var(--gray-50);
border: 1px solid var(--gray-200);
border-radius: 12px;
padding: 20px;
margin-bottom: 12px;
cursor: pointer;
transition: all 0.2s ease;
}
.search-result-item:hover {
background: var(--white);
border-color: var(--gold-400);
box-shadow: var(--shadow-md);
transform: translateX(4px);
}
.result-name {
font-size: 16px;
font-weight: 600;
color: var(--gray-900);
margin-bottom: 8px;
}
.result-meta {
display: flex;
gap: 16px;
font-size: 13px;
color: var(--gray-500);
flex-wrap: wrap;
}
.result-price {
color: var(--gold-600);
font-weight: 700;
}
.search-loading {
text-align: center;
padding: 40px;
display: none;
}
.search-loading.show {
display: block;
}
/* ========================================
SIDEBAR - DASHBOARD STYLE
======================================== */
.sidebar {
display: flex;
flex-direction: column;
gap: 24px;
}
/* Statistics Dashboard */
.stats-dashboard {
background: linear-gradient(135deg, var(--navy-700), var(--navy-600));
border-radius: 16px;
padding: 32px;
box-shadow: var(--shadow-xl);
}
.stats-title {
font-size: 18px;
font-weight: 700;
color: var(--white);
margin-bottom: 24px;
display: flex;
align-items: center;
gap: 10px;
}
.stats-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
margin-bottom: 24px;
}
.stat-card {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 12px;
padding: 20px;
text-align: center;
transition: all 0.2s ease;
}
.stat-card:hover {
background: rgba(255, 255, 255, 0.15);
transform: translateY(-2px);
}
.stat-number {
font-size: 32px;
font-weight: 800;
color: var(--white);
display: block;
margin-bottom: 6px;
background: linear-gradient(135deg, var(--gold-400), var(--white));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.stat-label {
font-size: 11px;
color: var(--gray-300);
text-transform: uppercase;
letter-spacing: 0.5px;
font-weight: 600;
}
.stat-divider {
height: 1px;
background: rgba(255, 255, 255, 0.2);
margin: 24px 0;
}
.history-items {
display: flex;
flex-direction: column;
gap: 12px;
}
.history-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px;
background: rgba(255, 255, 255, 0.08);
border-radius: 8px;
transition: all 0.2s ease;
}
.history-item:hover {
background: rgba(255, 255, 255, 0.12);
}
.history-date {
font-size: 13px;
color: var(--gray-300);
font-weight: 500;
}
.history-count {
font-size: 14px;
font-weight: 700;
color: var(--gold-400);
}
/* Recent Verifications */
.verification-list {
display: flex;
flex-direction: column;
gap: 12px;
}
.verification-item {
background: var(--gray-50);
border: 1px solid var(--gray-200);
border-radius: 12px;
padding: 16px;
transition: all 0.2s ease;
cursor: pointer;
}
.verification-item:hover {
background: var(--white);
border-color: var(--gold-400);
box-shadow: var(--shadow-md);
transform: translateX(4px);
}
.verification-status {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 12px;
border-radius: 16px;
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 8px;
}
.verification-status.authentic {
background: rgba(16, 185, 129, 0.15);
color: var(--green-500);
}
.verification-status.pending {
background: rgba(245, 158, 11, 0.15);
color: var(--amber-500);
}
.verification-status.suspicious {
background: rgba(239, 68, 68, 0.15);
color: var(--red-500);
}
.verification-wine {
font-size: 14px;
font-weight: 600;
color: var(--gray-900);
margin-bottom: 4px;
}
.verification-time {
font-size: 12px;
color: var(--gray-500);
}
/* ========================================
RESULTS DISPLAY
======================================== */
.results-display {
display: none;
margin-top: 32px;
animation: fadeIn 0.5s ease;
}
.results-display.show {
display: block;
}
.result-card-large {
background: var(--gray-50);
border: 2px solid var(--gray-200);
border-radius: 16px;
padding: 32px;
margin-bottom: 20px;
}
.result-status {
display: flex;
align-items: center;
gap: 16px;
padding: 24px;
border-radius: 12px;
margin-bottom: 32px;
font-size: 20px;
font-weight: 700;
}
.result-status.authentic {
background: rgba(16, 185, 129, 0.1);
border: 2px solid var(--green-500);
color: var(--green-500);
}
.result-status.likely_authentic {
background: rgba(245, 158, 11, 0.1);
border: 2px solid var(--amber-500);
color: var(--amber-500);
}
.result-status.suspicious {
background: rgba(239, 68, 68, 0.1);
border: 2px solid var(--red-500);
color: var(--red-500);
}
.status-icon {
font-size: 36px;
}
/* Confidence Bar */
.confidence-section {
margin: 24px 0;
}
.confidence-label {
display: flex;
justify-content: space-between;
margin-bottom: 12px;
font-size: 14px;
color: var(--gray-600);
font-weight: 600;
}
.confidence-bar {
height: 12px;
background: var(--gray-200);
border-radius: 6px;
overflow: hidden;
}
.confidence-fill {
height: 100%;
background: linear-gradient(90deg, var(--green-500), var(--gold-500));
border-radius: 6px;
transition: width 1s ease;
width: 0%;
}
/* Wine Info Grid */
.wine-info-grid {
display: grid;
gap: 16px;
}
.info-row {
display: flex;
justify-content: space-between;
padding: 16px 0;
border-bottom: 1px solid var(--gray-200);
}
.info-label {
font-size: 14px;
color: var(--gray-600);
font-weight: 500;
}
.info-value {
font-size: 15px;
color: var(--gray-900);
font-weight: 600;
text-align: right;
}
/* Details Section */
.details-section {
margin-top: 32px;
padding-top: 32px;
border-top: 2px solid var(--gray-200);
}
.details-title {
font-size: 18px;
font-weight: 700;
color: var(--gray-900);
margin-bottom: 16px;
}
.details-list {
list-style: none;
padding: 0;
}
.details-list li {
padding: 12px 0;
color: var(--gray-700);
font-size: 14px;
display: flex;
align-items: center;
gap: 12px;
}
.details-list li::before {
content: '✓';
color: var(--green-500);
font-weight: bold;
font-size: 16px;
}
/* ========================================
LOADING OVERLAY
======================================== */
.loading-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(10, 22, 40, 0.95);
backdrop-filter: blur(10px);
z-index: 1000;
align-items: center;
justify-content: center;
flex-direction: column;
}
.loading-overlay.show {
display: flex;
}
.loading-spinner {
width: 80px;
height: 80px;
border: 6px solid rgba(255, 255, 255, 0.1);
border-top-color: var(--gold-500);
border-radius: 50%;
animation: spin 1s linear infinite;
}
.loading-text {
margin-top: 24px;
font-size: 18px;
color: var(--white);
font-weight: 600;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
/* ========================================
EMPTY STATES
======================================== */
.empty-state {
text-align: center;
padding: 60px 20px;
color: var(--gray-500);
}
.empty-icon {
font-size: 64px;
margin-bottom: 16px;
opacity: 0.5;
}
.empty-text {
font-size: 16px;
color: var(--gray-600);
}
/* ========================================
RESPONSIVE DESIGN
======================================== */
@media (max-width: 1024px) {
.content-grid {
grid-template-columns: 1fr;
}
.sidebar {
order: 2;
}
}
@media (max-width: 768px) {
.hero-title {
font-size: 40px;
}
.hero-subtitle {
font-size: 18px;
}
.header-content,
.hero-section,
.content-wrapper {
padding-left: 24px;
padding-right: 24px;
}
.search-input-wrapper {
flex-direction: column;
align-items: stretch;
}
.search-button-giant {
width: 100%;
justify-content: center;
}
.quick-filters-hero {
gap: 8px;
}
.filter-pill {
padding: 10px 16px;
font-size: 13px;
}
.stats-grid {
grid-template-columns: 1fr;
}
.card-header,
.card-body {
padding: 24px;
}
}
@media (max-width: 480px) {
.hero-title {
font-size: 32px;
}
.hero-section {
padding-top: 48px;
}
.brand-icon {
width: 40px;
height: 40px;
font-size: 20px;
}
.brand-text h1 {
font-size: 20px;
}
.search-input-main {
font-size: 16px;
}
.search-button-giant {
font-size: 18px;
padding: 18px 32px;
}
}
/* Custom Scrollbar */
::-webkit-scrollbar {
width: 10px;
}
::-webkit-scrollbar-track {
background: var(--gray-200);
}
::-webkit-scrollbar-thumb {
background: var(--gray-400);
border-radius: 5px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--gold-500);
}
/* ========================================
DATABASE SEARCH RESULTS STYLES
======================================== */
#databaseSearchResults {
background: white;
border-radius: 16px;
padding: 32px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
}
.search-results-header h3 {
font-size: 24px;
font-weight: 700;
color: var(--navy-900);
margin-bottom: 8px;
}
.search-results-header p {
color: var(--gray-600);
font-size: 16px;
margin-bottom: 24px;
}
.search-results-list {
display: grid;
gap: 16px;
}
.wine-search-result {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px;
background: var(--gray-50);
border: 2px solid var(--gray-200);
border-radius: 12px;
transition: all 0.3s ease;
}
.wine-search-result:hover {
border-color: var(--gold-500);
background: white;
box-shadow: 0 4px 12px rgba(212, 175, 55, 0.15);
transform: translateY(-2px);
}
.wine-result-info h4 {
font-size: 18px;
font-weight: 600;
color: var(--navy-900);
margin-bottom: 8px;
}
.wine-meta {
font-size: 14px;
color: var(--gray-600);
margin-bottom: 4px;
}
.wine-source {
font-size: 12px;
color: var(--gray-500);
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.wine-result-price {
text-align: right;
}
.wine-result-price .price {
display: block;
font-size: 24px;
font-weight: 700;
color: var(--gold-500);
margin-bottom: 4px;
}
.wine-result-price .price-na {
display: block;
font-size: 16px;
color: var(--gray-400);
}
.wine-result-price .rating {
display: block;
font-size: 14px;
color: var(--gray-600);
}
.search-empty {
text-align: center;
padding: 48px 24px;
color: var(--gray-500);
}
@keyframes pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.7;
}
}
</style>
</head>
<body>
<div class="page-container">
<!-- Professional Header -->
<header class="header">
<div class="header-content">
<div class="brand">
<div class="brand-icon">🍷</div>
<div class="brand-text">
<h1>Red Thunder</h1>
<p>Wine Verification</p>
</div>
</div>
<div class="header-actions">
<a href="/" class="nav-link">Wine Tracker</a>
<button id="logoutBtn" class="logout-btn">Logout</button>
</div>
</div>
</header>
<!-- Tab Navigation -->
<nav class="tab-navigation" style="background: linear-gradient(135deg, #1a3a5f, #244d7a); padding: 0; box-shadow: 0 4px 12px rgba(0,0,0,0.15);">
<div style="max-width: 1400px; margin: 0 auto; display: flex; gap: 0;">
<button id="tabSearch" class="tab-btn tab-active" style="flex: 1; padding: 20px 32px; background: linear-gradient(135deg, #d4af37, #f0d97d); color: #0a1628; border: none; font-size: 16px; font-weight: 700; cursor: pointer; transition: all 0.3s ease; border-bottom: 4px solid #d4af37; display: flex; align-items: center; justify-content: center; gap: 12px;">
<span style="font-size: 24px;">🔍</span>
<span>Search & Verify</span>
</button>
<button id="tabHistory" class="tab-btn" style="flex: 1; padding: 20px 32px; background: rgba(255,255,255,0.1); color: white; border: none; font-size: 16px; font-weight: 600; cursor: pointer; transition: all 0.3s ease; border-bottom: 4px solid transparent; display: flex; align-items: center; justify-content: center; gap: 12px;">
<span style="font-size: 24px;">📊</span>
<span>Historical Data</span>
</button>
</div>
</nav>
<!-- Hero Section with Giant Search -->
<section id="searchTab" class="hero-section tab-content">
<h1 class="hero-title">
Verify Your <span class="highlight">Premium Wine</span>
</h1>
<p class="hero-subtitle">
Search our extensive database or upload a label photo for instant authenticity verification powered by AI
</p>
<!-- Giant Search Box -->
<div class="giant-search-container">
<div class="search-box">
<div class="search-input-wrapper">
<span class="search-icon-left">🔍</span>
<input
type="text"
id="mainSearchInput"
class="search-input-main"
placeholder="Search by wine name, winery, region, or vintage..."
autocomplete="off"
/>
<button id="searchButtonGiant" class="search-button-giant">
<span>Search</span>
<span>→</span>
</button>
</div>
</div>
<!-- Search Results Dropdown -->
<div id="searchResults" class="search-results">
<!-- Results populated by JS -->
</div>
<!-- Search Loading -->
<div id="searchLoading" class="search-loading">
<div class="loading-spinner"></div>
<p style="margin-top: 16px; color: var(--gray-600); font-weight: 600;">Searching database...</p>
</div>
</div>
<!-- Quick Filters -->
<div class="quick-filters-hero">
<button class="filter-pill" data-filter="type" data-value="red">
<span>🍷</span>
<span>Red Wines</span>
</button>
<button class="filter-pill" data-filter="type" data-value="white">
<span>🥂</span>
<span>White Wines</span>
</button>
<button class="filter-pill" data-filter="region" data-value="napa">
<span>🌄</span>
<span>Napa Valley</span>
</button>
<button class="filter-pill" data-filter="region" data-value="bordeaux">
<span>🇫🇷</span>
<span>Bordeaux</span>
</button>
<button class="filter-pill" data-filter="price" data-value="premium">
<span>💎</span>
<span>Premium</span>
</button>
</div>
</section>
<!-- Main Content -->
<div class="content-wrapper">
<div class="content-grid">
<!-- Left Column - Upload & Verification -->
<div>
<!-- Wine Verification Card -->
<div class="card">
<div class="card-header">
<h2 class="card-title">
<span>📸</span>
<span>Wine Label Verification</span>
</h2>
<p class="card-subtitle">
Upload a clear photo of your wine label for AI-powered authenticity verification and detailed analysis
</p>
</div>
<div class="card-body">
<!-- Preview Container -->
<div id="previewContainer" class="preview-container">
<div class="preview-wrapper">
<img id="previewImage" class="preview-image" alt="Wine label preview">
<button id="removePreviewBtn" class="remove-preview-btn">✕</button>
</div>
</div>
<!-- Upload Area -->
<div id="uploadArea" class="upload-area">
<span class="upload-icon-large">📸</span>
<h3 class="upload-title">Upload Wine Label</h3>
<p class="upload-subtitle">Drag and drop or click to select an image</p>
<div class="upload-buttons">
<button id="cameraBtn" class="upload-btn upload-btn-primary">
<span>📷</span>
<span>Take Photo</span>
</button>
<button id="galleryBtn" class="upload-btn upload-btn-secondary">
<span>🖼️</span>
<span>Choose from Gallery</span>
</button>
</div>
<input type="file" id="fileInput" accept="image/*" style="display: none;">
<input type="file" id="cameraInput" accept="image/*" capture="environment" style="display: none;">
</div>
<!-- Verify Button -->
<button id="verifyButton" class="verify-button-large" disabled>
🔍 Verify Authenticity
</button>
<!-- Search Status (Camera Auto-Search) -->
<div id="searchStatus" style="display: none; margin: 24px 0; padding: 16px; background: linear-gradient(135deg, #1e3a8a, #3b82f6); border-radius: 12px; color: white; text-align: center; font-weight: 600; animation: pulse 2s infinite;">
🔍 Searching all databases...
</div>
<!-- Database Search Results -->
<div id="databaseSearchResults" style="display: none; margin-top: 32px;">
<!-- Populated by JS after camera capture -->
</div>
<!-- Results Display -->
<div id="resultsContainer" class="results-display">
<div class="result-card-large">
<div id="resultStatus" class="result-status">
<span class="status-icon">⏳</span>
<span>Analyzing wine label...</span>
</div>
<div class="confidence-section">
<div class="confidence-label">
<span>Confidence Score</span>
<span id="confidenceValue">0%</span>
</div>
<div class="confidence-bar">
<div id="confidenceFill" class="confidence-fill"></div>
</div>
</div>
<div id="wineInfo" class="wine-info-grid">
<!-- Wine info populated by JS -->
</div>
<div id="detailSection" class="details-section" style="display: none;">
<h3 class="details-title">Verification Details</h3>
<ul id="detailList" class="details-list">
<!-- Details populated by JS -->
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Right Column - Sidebar -->
<aside class="sidebar">
<!-- Statistics Dashboard -->
<div class="stats-dashboard">
<h3 class="stats-title">
<span>📊</span>
<span>Analytics Dashboard</span>
</h3>
<div class="stats-grid">
<div class="stat-card">
<span class="stat-number" id="totalUploads">47</span>
<span class="stat-label">Total Scans</span>
</div>
<div class="stat-card">
<span class="stat-number" id="authenticCount">42</span>
<span class="stat-label">Authentic</span>
</div>
<div class="stat-card">
<span class="stat-number" id="suspiciousCount">3</span>
<span class="stat-label">Flagged</span>
</div>
<div class="stat-card">
<span class="stat-number" id="pendingCount">2</span>
<span class="stat-label">Pending</span>
</div>
</div>
<div class="stat-divider"></div>
<div style="font-size: 14px; font-weight: 600; color: var(--gray-200); margin-bottom: 16px;">
Recent Activity
</div>
<div class="history-items">
<div class="history-item">
<span class="history-date">Today</span>
<span class="history-count">3 scans</span>
</div>
<div class="history-item">
<span class="history-date">Yesterday</span>
<span class="history-count">5 scans</span>
</div>
<div class="history-item">
<span class="history-date">This Week</span>
<span class="history-count">12 scans</span>
</div>
<div class="history-item">
<span class="history-date">This Month</span>
<span class="history-count">47 scans</span>
</div>
</div>
</div>
<!-- Recent Verifications -->
<div class="card">
<div class="card-header">
<h3 class="card-title">
<span>📋</span>
<span>Recent Verifications</span>
</h3>
</div>
<div class="card-body">
<div id="recentVerifications" class="verification-list">
<div class="verification-item">
<span class="verification-status authentic">✓ Authentic</span>
<div class="verification-wine">Château Margaux 2015</div>
<div class="verification-time">2 hours ago</div>
</div>
<div class="verification-item">
<span class="verification-status pending">⏳ Pending</span>
<div class="verification-wine">Opus One 2018</div>
<div class="verification-time">5 hours ago</div>
</div>
<div class="verification-item">
<span class="verification-status authentic">✓ Authentic</span>
<div class="verification-wine">Screaming Eagle 2019</div>
<div class="verification-time">1 day ago</div>
</div>
<div class="verification-item">
<span class="verification-status suspicious">⚠ Flagged</span>
<div class="verification-wine">Penfolds Grange 2014</div>
<div class="verification-time">2 days ago</div>
</div>
</div>
</div>
</div>
</aside>
</div>
</div>
<!-- Loading Overlay -->
<div id="loadingOverlay" class="loading-overlay">
<div class="loading-spinner"></div>
<p class="loading-text">Analyzing wine label...</p>
</div>
</div>
<script>
// ========================================
// AUTHENTICATION
// ========================================
document.getElementById('logoutBtn').addEventListener('click', async () => {
try {
await fetch('/auth/logout', { method: 'POST' });
window.location.href = '/login.html';
} catch (error) {
console.error('Logout error:', error);
window.location.href = '/login.html';
}
});
// ========================================
// GIANT SEARCH FUNCTIONALITY
// ========================================
let searchTimeout;
const mainSearchInput = document.getElementById('mainSearchInput');
const searchButtonGiant = document.getElementById('searchButtonGiant');
const searchResults = document.getElementById('searchResults');
const searchLoading = document.getElementById('searchLoading');
// Search on input
mainSearchInput.addEventListener('input', (e) => {
const query = e.target.value.trim();
if (query.length === 0) {
searchResults.classList.remove('show');
searchResults.innerHTML = '';
return;
}
clearTimeout(searchTimeout);
searchTimeout = setTimeout(() => {
if (query.length >= 2) {
performSearch(query);
}
}, 500);
});
// Search on button click
searchButtonGiant.addEventListener('click', () => {
const query = mainSearchInput.value.trim();
if (query.length >= 2) {
performSearch(query);
}
});
// Search on Enter key
mainSearchInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
const query = mainSearchInput.value.trim();
if (query.length >= 2) {
performSearch(query);
}
}
});
async function performSearch(query) {
searchLoading.classList.add('show');
searchResults.classList.remove('show');
searchResults.innerHTML = '';
try {
// Include ALL sources: Wine Enthusiast 130k, UC Davis 824k, Vivino, K&L, Total Wine
const response = await fetch(`/api/scraper/search?query=${encodeURIComponent(query)}&sources=wineenthusiast,winesensed,vivino,kl,totalwine`);
const data = await response.json();
searchLoading.classList.remove('show');
if (data.wines && data.wines.length > 0) {
displaySearchResults(data.wines);
} else {
searchResults.innerHTML = `
<div class="empty-state">
<div class="empty-icon">🔍</div>
<div class="empty-text">No wines found matching "${query}"</div>
</div>
`;
searchResults.classList.add('show');
}
} catch (error) {
console.error('Search error:', error);
searchLoading.classList.remove('show');
searchResults.innerHTML = `
<div class="empty-state">
<div class="empty-icon">⚠️</div>
<div class="empty-text">Error searching wines. Please try again.</div>
</div>
`;
searchResults.classList.add('show');
}
}
function displaySearchResults(wines) {
searchResults.innerHTML = `
<div style="margin-bottom: 20px; padding: 16px; background: linear-gradient(135deg, #1e3a8a, #3b82f6); border-radius: 12px; color: white;">
<h3 style="margin: 0 0 8px 0; font-size: 20px; font-weight: 700;">Search Results</h3>
<p style="margin: 0; opacity: 0.9;">Found ${wines.length} wine${wines.length !== 1 ? 's' : ''}</p>
</div>
${wines.map((wine, index) => `
<div class="dynamic-wine-card" style="background: white; border-radius: 16px; padding: 24px; margin-bottom: 16px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); border: 2px solid #e5e7eb; transition: all 0.3s ease; cursor: pointer;"
onmouseover="this.style.borderColor='#d4af37'; this.style.boxShadow='0 4px 16px rgba(212,175,55,0.2)'; this.style.transform='translateY(-2px)';"
onmouseout="this.style.borderColor='#e5e7eb'; this.style.boxShadow='0 2px 8px rgba(0,0,0,0.1)'; this.style.transform='translateY(0)';"
onclick='selectWine(${JSON.stringify(wine).replace(/'/g, "'")})'>
<!-- Header with Wine Number -->
<div style="display: flex; justify-content: space-between; align-items: start; margin-bottom: 16px;">
<div style="flex: 1;">
<div style="display: inline-block; background: linear-gradient(135deg, #d4af37, #f0d97d); color: #0a1628; padding: 4px 12px; border-radius: 6px; font-size: 12px; font-weight: 700; margin-bottom: 8px;">
#${index + 1}
</div>
<h4 style="margin: 0; font-size: 18px; font-weight: 700; color: #1a3a5f; line-height: 1.4;">
${wine.name || 'Unknown Wine'}
</h4>
</div>
${wine.price ? `
<div style="text-align: right;">
<div style="font-size: 24px; font-weight: 800; color: #d4af37;">$${wine.price}</div>
<div style="font-size: 11px; color: #6b7280; text-transform: uppercase;">Price</div>
</div>
` : ''}
</div>
<!-- Wine Details Grid -->
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin-bottom: 16px;">
${wine.winery ? `
<div style="background: #f9fafb; padding: 12px; border-radius: 8px; border-left: 3px solid #d4af37;">
<div style="font-size: 11px; color: #6b7280; text-transform: uppercase; margin-bottom: 4px; font-weight: 600;">🏭 Winery</div>
<div style="font-size: 14px; color: #1a3a5f; font-weight: 600;">${wine.winery}</div>
</div>
` : ''}
${wine.vintage ? `
<div style="background: #f9fafb; padding: 12px; border-radius: 8px; border-left: 3px solid #3b82f6;">
<div style="font-size: 11px; color: #6b7280; text-transform: uppercase; margin-bottom: 4px; font-weight: 600;">📅 Vintage</div>
<div style="font-size: 14px; color: #1a3a5f; font-weight: 600;">${wine.vintage}</div>
</div>
` : ''}
${wine.region ? `
<div style="background: #f9fafb; padding: 12px; border-radius: 8px; border-left: 3px solid #10b981;">
<div style="font-size: 11px; color: #6b7280; text-transform: uppercase; margin-bottom: 4px; font-weight: 600;">📍 Region</div>
<div style="font-size: 14px; color: #1a3a5f; font-weight: 600;">${wine.region}</div>
</div>
` : ''}
${wine.type ? `
<div style="background: #f9fafb; padding: 12px; border-radius: 8px; border-left: 3px solid #8b5cf6;">
<div style="font-size: 11px; color: #6b7280; text-transform: uppercase; margin-bottom: 4px; font-weight: 600;">🍷 Type</div>
<div style="font-size: 14px; color: #1a3a5f; font-weight: 600;">${wine.type}</div>
</div>
` : ''}
${wine.rating ? `
<div style="background: #f9fafb; padding: 12px; border-radius: 8px; border-left: 3px solid #f59e0b;">
<div style="font-size: 11px; color: #6b7280; text-transform: uppercase; margin-bottom: 4px; font-weight: 600;">⭐ Rating</div>
<div style="font-size: 14px; color: #1a3a5f; font-weight: 600;">${wine.rating}/100</div>
</div>
` : ''}
${wine.ratingsCount ? `
<div style="background: #f9fafb; padding: 12px; border-radius: 8px; border-left: 3px solid #06b6d4;">
<div style="font-size: 11px; color: #6b7280; text-transform: uppercase; margin-bottom: 4px; font-weight: 600;">👥 Reviews</div>
<div style="font-size: 14px; color: #1a3a5f; font-weight: 600;">${wine.ratingsCount.toLocaleString()}</div>
</div>
` : ''}
</div>
<!-- Availability & Source -->
<div style="display: flex; justify-content: space-between; align-items: center; padding-top: 12px; border-top: 1px solid #e5e7eb;">
<div style="display: flex; gap: 8px; align-items: center;">
${wine.availability ? `
<span style="display: inline-block; padding: 4px 10px; background: ${wine.availability.includes('Available') ? '#d1fae5' : '#fef3c7'}; color: ${wine.availability.includes('Available') ? '#065f46' : '#92400e'}; border-radius: 6px; font-size: 12px; font-weight: 600;">
${wine.availability}
</span>
` : ''}
${wine.source ? `
<span style="display: inline-block; padding: 4px 10px; background: #dbeafe; color: #1e40af; border-radius: 6px; font-size: 12px; font-weight: 600;">
📦 ${wine.source}
</span>
` : ''}
</div>
${wine.url ? `
<a href="${wine.url}" target="_blank" rel="noopener noreferrer" style="color: #d4af37; text-decoration: none; font-size: 14px; font-weight: 600; display: flex; align-items: center; gap: 4px;"
onclick="event.stopPropagation();">
View Details →
</a>
` : ''}
</div>
${wine.image ? `
<div style="margin-top: 16px; text-align: center;">
<img src="${wine.image}" alt="${wine.name}" style="max-width: 150px; max-height: 200px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);" onerror="this.style.display='none';">
</div>
` : ''}
</div>
`).join('')}
`;
searchResults.classList.add('show');
}
window.selectWine = function(wine) {
console.log('Selected wine:', wine);
alert(`Selected: ${wine.name}\nPrice: $${wine.price}\n\nThis would compare against your uploaded label.`);
};
// ========================================
// QUICK FILTERS
// ========================================
document.querySelectorAll('.filter-pill').forEach(pill => {
pill.addEventListener('click', () => {
pill.classList.toggle('active');
const activeFilters = Array.from(document.querySelectorAll('.filter-pill.active'));
if (activeFilters.length > 0) {
const filterText = activeFilters.map(f => f.dataset.value).join(' ');
mainSearchInput.value = filterText;
performSearch(filterText);
}
});
});
// ========================================
// IMAGE UPLOAD FUNCTIONALITY
// ========================================
const uploadArea = document.getElementById('uploadArea');
const fileInput = document.getElementById('fileInput');
const cameraInput = document.getElementById('cameraInput');
const cameraBtn = document.getElementById('cameraBtn');
const galleryBtn = document.getElementById('galleryBtn');
const verifyButton = document.getElementById('verifyButton');
const previewContainer = document.getElementById('previewContainer');
const previewImage = document.getElementById('previewImage');
const removePreviewBtn = document.getElementById('removePreviewBtn');
const resultsDisplay = document.getElementById('resultsDisplay');
const loadingOverlay = document.getElementById('loadingOverlay');
let selectedFile = null;
// Upload area click
uploadArea.addEventListener('click', () => {
fileInput.click();
});
// Drag and drop
uploadArea.addEventListener('dragover', (e) => {
e.preventDefault();
uploadArea.classList.add('dragover');
});
uploadArea.addEventListener('dragleave', () => {
uploadArea.classList.remove('dragover');
});
uploadArea.addEventListener('drop', (e) => {
e.preventDefault();
uploadArea.classList.remove('dragover');
const files = e.dataTransfer.files;
if (files.length > 0) {
handleFileSelect(files[0]);
}
});
// Camera button
cameraBtn.addEventListener('click', (e) => {
e.stopPropagation();
cameraInput.click();
});
// Gallery button
galleryBtn.addEventListener('click', (e) => {
e.stopPropagation();
fileInput.click();
});
// File input changes
fileInput.addEventListener('change', (e) => {
if (e.target.files.length > 0) {
handleFileSelect(e.target.files[0]);
}
});
cameraInput.addEventListener('change', (e) => {
if (e.target.files.length > 0) {
handleFileSelect(e.target.files[0]);
}
});
// Handle file selection
function handleFileSelect(file) {
if (!file.type.startsWith('image/')) {
alert('Please select an image file');
return;
}
selectedFile = file;
const reader = new FileReader();
reader.onload = (e) => {
previewImage.src = e.target.result;
previewContainer.classList.add('show');
verifyButton.disabled = false;
resultsDisplay.classList.remove('show');
};
reader.readAsDataURL(file);
}
// Remove image
removePreviewBtn.addEventListener('click', (e) => {
e.stopPropagation();
selectedFile = null;
previewContainer.classList.remove('show');
verifyButton.disabled = true;
resultsDisplay.classList.remove('show');
fileInput.value = '';
cameraInput.value = '';
});
// Verify button - REAL AI VERIFICATION
verifyButton.addEventListener('click', async () => {
if (!selectedFile) return;
loadingOverlay.classList.add('show');
try {
// Convert file to base64
const reader = new FileReader();
reader.onload = async (e) => {
try {
const imageData = e.target.result;
// Call real Roboflow AI verification API
const response = await fetch('/api/verification/verify-label-base64', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
image: imageData,
options: {
useCache: true,
confidenceThreshold: 0.40,
crossCheckDatabase: true
}
})
});
const result = await response.json();
loadingOverlay.classList.remove('show');
if (result.success) {
showResults(result);
} else {
alert('Verification failed: ' + (result.error || 'Unknown error'));
loadingOverlay.classList.remove('show');
}
} catch (error) {
console.error('Verification error:', error);
alert('Failed to verify wine label. Please try again.');
loadingOverlay.classList.remove('show');
}
};
reader.readAsDataURL(selectedFile);
} catch (error) {
console.error('File reading error:', error);
alert('Failed to read image file.');
loadingOverlay.classList.remove('show');
}
});
// Show results - REAL AI DATA
function showResults(result) {
const resultStatus = document.getElementById('resultStatus');
const confidenceValue = document.getElementById('confidenceValue');
const confidenceFill = document.getElementById('confidenceFill');
const wineInfoGrid = document.getElementById('wineInfoGrid');
const detailsSection = document.getElementById('detailsSection');
const detailsList = document.getElementById('detailsList');
// Extract real AI data
const confidence = Math.round((result.confidence || 0) * 100);
const detected = result.detected || false;
const wineName = result.wineInfo?.name || result.detectedText || 'Wine Label Detected';
const status = confidence > 80 ? 'authentic' : confidence > 60 ? 'likely_authentic' : 'suspicious';
const statusText = confidence > 80 ? 'Verified Authentic' : confidence > 60 ? 'Likely Authentic' : 'Requires Review';
const statusIcon = confidence > 80 ? '✓' : confidence > 60 ? '⚠️' : '❌';
resultStatus.className = `result-status ${status}`;
resultStatus.innerHTML = `
<span class="status-icon">${statusIcon}</span>
<span>${statusText}</span>
`;
confidenceValue.textContent = `${confidence}%`;
confidenceFill.style.width = `${confidence}%`;
// Wine info from real AI detection
wineInfoGrid.innerHTML = `
<div class="info-row">
<span class="info-label">Wine Name</span>
<span class="info-value">${wineName}</span>
</div>
<div class="info-row">
<span class="info-label">Detection Status</span>
<span class="info-value">${detected ? 'Label Detected' : 'No Label Found'}</span>
</div>
<div class="info-row">
<span class="info-label">AI Confidence</span>
<span class="info-value">${confidence}%</span>
</div>
<div class="info-row">
<span class="info-label">Model</span>
<span class="info-value">Roboflow Wine Detection</span>
</div>
`;
// Build details list from AI response
const details = [];
if (detected) {
details.push(`Wine label detected with ${confidence}% confidence`);
}
if (result.boundingBox) {
details.push('Bounding box coordinates validated');
}
if (result.predictions && result.predictions.length > 0) {
details.push(`${result.predictions.length} detection(s) found in image`);
}
if (result.processingTime) {
details.push(`Analyzed in ${result.processingTime}ms`);
}
if (confidence > 80) {
details.push('High confidence - label appears authentic');
} else if (confidence > 60) {
details.push('Moderate confidence - manual review recommended');
} else {
details.push('Low confidence - suspicious label detected');
}
detailsList.innerHTML = details.map(d => `<li>${d}</li>`).join('');
detailsSection.style.display = 'block';
resultsDisplay.classList.add('show');
// Update recent verifications with real wine name
addRecentVerification(wineName, status, 'Just now');
}
// Add to recent verifications
function addRecentVerification(wine, status, time) {
const recentDiv = document.getElementById('recentVerifications');
const statusClass = status === 'authentic' ? 'authentic' : status === 'likely_authentic' ? 'pending' : 'suspicious';
const statusIcon = status === 'authentic' ? '✓' : status === 'likely_authentic' ? '⏳' : '⚠';
const statusText = status === 'authentic' ? 'Authentic' : status === 'likely_authentic' ? 'Pending' : 'Flagged';
const newItem = document.createElement('div');
newItem.className = 'verification-item';
newItem.innerHTML = `
<span class="verification-status ${statusClass}">${statusIcon} ${statusText}</span>
<div class="verification-wine">${wine}</div>
<div class="verification-time">${time}</div>
`;
recentDiv.insertBefore(newItem, recentDiv.firstChild);
// Update stats
const totalUploads = document.getElementById('totalUploads');
totalUploads.textContent = parseInt(totalUploads.textContent) + 1;
if (status === 'authentic') {
const authenticCount = document.getElementById('authenticCount');
authenticCount.textContent = parseInt(authenticCount.textContent) + 1;
} else if (status === 'suspicious') {
const suspiciousCount = document.getElementById('suspiciousCount');
suspiciousCount.textContent = parseInt(suspiciousCount.textContent) + 1;
}
}
// ===== TAB SWITCHING =====
const tabSearch = document.getElementById('tabSearch');
const tabHistory = document.getElementById('tabHistory');
const searchTabContent = document.getElementById('searchTab');
tabSearch.addEventListener('click', () => {
// Activate search tab
tabSearch.style.background = 'linear-gradient(135deg, #d4af37, #f0d97d)';
tabSearch.style.color = '#0a1628';
tabSearch.style.borderBottom = '4px solid #d4af37';
tabSearch.style.fontWeight = '700';
// Deactivate history tab
tabHistory.style.background = 'rgba(255,255,255,0.1)';
tabHistory.style.color = 'white';
tabHistory.style.borderBottom = '4px solid transparent';
tabHistory.style.fontWeight = '600';
// Show search content
searchTabContent.style.display = 'block';
document.querySelector('.content-wrapper').style.display = 'block';
// Hide historical data
const historyContent = document.getElementById('historicalDataContent');
if (historyContent) historyContent.style.display = 'none';
});
tabHistory.addEventListener('click', () => {
// Activate history tab
tabHistory.style.background = 'linear-gradient(135deg, #d4af37, #f0d97d)';
tabHistory.style.color = '#0a1628';
tabHistory.style.borderBottom = '4px solid #d4af37';
tabHistory.style.fontWeight = '700';
// Deactivate search tab
tabSearch.style.background = 'rgba(255,255,255,0.1)';
tabSearch.style.color = 'white';
tabSearch.style.borderBottom = '4px solid transparent';
tabSearch.style.fontWeight = '600';
// Hide search content
searchTabContent.style.display = 'none';
document.querySelector('.content-wrapper').style.display = 'none';
// Show historical data
let historyContent = document.getElementById('historicalDataContent');
if (!historyContent) {
// Create historical data section if it doesn't exist
historyContent = document.createElement('div');
historyContent.id = 'historicalDataContent';
historyContent.style.cssText = 'max-width: 1400px; margin: 40px auto; padding: 0 24px;';
historyContent.innerHTML = `
<div style="background: white; border-radius: 16px; padding: 32px; box-shadow: 0 4px 16px rgba(0,0,0,0.1);">
<h2 style="font-size: 28px; font-weight: 800; color: #1a3a5f; margin-bottom: 16px; display: flex; align-items: center; gap: 12px;">
<span>📊</span>
<span>Historical Wine Data</span>
</h2>
<p style="font-size: 16px; color: #6b7280; margin-bottom: 32px;">
Track wine prices and ratings over time from our database of 131,000+ wines
</p>
<!-- Dataset Stats -->
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin-bottom: 32px;">
<div style="background: linear-gradient(135deg, #e6c45a, #f0d97d); padding: 24px; border-radius: 12px; text-align: center;">
<div style="font-size: 36px; font-weight: 800; color: #0a1628;">131K+</div>
<div style="font-size: 14px; color: #1a3a5f; font-weight: 600; margin-top: 8px;">Total Wines</div>
</div>
<div style="background: linear-gradient(135deg, #3b82f6, #60a5fa); padding: 24px; border-radius: 12px; text-align: center; color: white;">
<div style="font-size: 36px; font-weight: 800;">824K</div>
<div style="font-size: 14px; font-weight: 600; margin-top: 8px; opacity: 0.9;">UC Davis Reviews</div>
</div>
<div style="background: linear-gradient(135deg, #10b981, #34d399); padding: 24px; border-radius: 12px; text-align: center; color: white;">
<div style="font-size: 36px; font-weight: 800;">130K</div>
<div style="font-size: 14px; font-weight: 600; margin-top: 8px; opacity: 0.9;">Wine Enthusiast</div>
</div>
<div style="background: linear-gradient(135deg, #f59e0b, #fbbf24); padding: 24px; border-radius: 12px; text-align: center; color: white;">
<div style="font-size: 36px; font-weight: 800;">350K+</div>
<div style="font-size: 14px; font-weight: 600; margin-top: 8px; opacity: 0.9;">Unique Vintages</div>
</div>
</div>
<!-- Data Sources -->
<h3 style="font-size: 20px; font-weight: 700; color: #1a3a5f; margin-bottom: 16px;">Data Sources</h3>
<div style="display: grid; gap: 16px; margin-bottom: 32px;">
<div style="border: 2px solid #e5e7eb; border-radius: 12px; padding: 20px;">
<h4 style="font-size: 16px; font-weight: 700; color: #1a3a5f; margin-bottom: 8px;">Wine Enthusiast Magazine (130,000 wines)</h4>
<p style="font-size: 14px; color: #6b7280; line-height: 1.6;">Professional wine reviews and ratings from Wine Enthusiast Magazine. Includes detailed tasting notes, professional scores, and pricing data collected in 2017.</p>
</div>
<div style="border: 2px solid #e5e7eb; border-radius: 12px; padding: 20px;">
<h4 style="font-size: 16px; font-weight: 700; color: #1a3a5f; margin-bottom: 8px;">UC Davis Wine Database (824,000 reviews)</h4>
<p style="font-size: 14px; color: #6b7280; line-height: 1.6;">Academic research dataset from UC Davis and NeurIPS 2023 "Learning to Taste" study. Covers vintages from 1950-2021 with 350,000+ unique wines, grape composition, and historical pricing.</p>
</div>
<div style="border: 2px solid #e5e7eb; border-radius: 12px; padding: 20px;">
<h4 style="font-size: 16px; font-weight: 700; color: #1a3a5f; margin-bottom: 8px;">Live Pricing Data (Vivino, K&L, Total Wine)</h4>
<p style="font-size: 14px; color: #6b7280; line-height: 1.6;">Real-time price data scraped from major wine retailers. Updated daily to track current market prices and availability.</p>
</div>
</div>
<!-- Coming Soon -->
<div style="background: linear-gradient(135deg, #1a3a5f, #244d7a); color: white; padding: 32px; border-radius: 12px; text-align: center;">
<div style="font-size: 48px; margin-bottom: 16px;">📈</div>
<h3 style="font-size: 24px; font-weight: 700; margin-bottom: 12px;">Price Trends Coming Soon</h3>
<p style="font-size: 16px; opacity: 0.9; margin-bottom: 20px;">We're building interactive charts to track wine price changes over time, compare vintages, and identify market trends.</p>
<p style="font-size: 14px; opacity: 0.7;">Search any wine above to see current pricing across all sources</p>
</div>
</div>
`;
document.body.insertBefore(historyContent, document.querySelector('script'));
}
historyContent.style.display = 'block';
});
</script>
</body>
</html>