diff --git a/public/screenshots/archive.webp b/public/screenshots/archive.webp new file mode 100644 index 0000000..61d5c00 Binary files /dev/null and b/public/screenshots/archive.webp differ diff --git a/public/screenshots/describe.webp b/public/screenshots/describe.webp new file mode 100644 index 0000000..5cf9428 Binary files /dev/null and b/public/screenshots/describe.webp differ diff --git a/public/screenshots/review.webp b/public/screenshots/review.webp new file mode 100644 index 0000000..f151b37 Binary files /dev/null and b/public/screenshots/review.webp differ diff --git a/public/screenshots/search.webp b/public/screenshots/search.webp new file mode 100644 index 0000000..081c82e Binary files /dev/null and b/public/screenshots/search.webp differ diff --git a/public/screenshots/upload-ocr.webp b/public/screenshots/upload-ocr.webp new file mode 100644 index 0000000..c6912f3 Binary files /dev/null and b/public/screenshots/upload-ocr.webp differ diff --git a/src/components/ArchivalFlowVisualizer.css b/src/components/ArchivalFlowVisualizer.css index 896d2be..52c7e5d 100644 --- a/src/components/ArchivalFlowVisualizer.css +++ b/src/components/ArchivalFlowVisualizer.css @@ -861,3 +861,16 @@ align-items: flex-start; } } + +/* The showcase card is dark in both themes; keep its headings light when the + global light-theme heading colour would otherwise turn them dark. */ +[data-theme="light"] .flow-showcase-card h3.showcase-title, +[data-theme="light"] .flow-showcase-card .step-footer-info h4.step-heading { + color: #ffffff; +} + +@media (max-width: 640px) { + .flow-path-timeline { + flex-wrap: wrap; + } +} diff --git a/src/components/ArchivalFlowVisualizer.jsx b/src/components/ArchivalFlowVisualizer.jsx index 4deb944..f57fa3c 100644 --- a/src/components/ArchivalFlowVisualizer.jsx +++ b/src/components/ArchivalFlowVisualizer.jsx @@ -98,13 +98,13 @@ const ArchivalFlowVisualizer = () => {
1

Ingest Scans

- Drag & drop PDFs, TIFFs, or batch scans. + Drop in PDFs, TIFFs, JPEGs or whole folders.

{/* ------------------------------------------------------------- - STEP 2: AI CLEAN & DESKEW + STEP 2: AUTO-ROTATE ------------------------------------------------------------- */}
{
2
-

AI Clean & Deskew

+

Auto-Rotate

- Auto rotation, skew correction, noise removal. + Sideways and upside-down pages are turned upright.

@@ -207,9 +207,9 @@ const ArchivalFlowVisualizer = () => {
3
-

Dual-Pass OCR

+

Local OCR

- Multi-language extraction with bounding boxes and confidence. + Text extraction in 10 languages, with word confidence.

@@ -241,15 +241,15 @@ const ArchivalFlowVisualizer = () => {
Title - AI-inferred title + Suggested title
Author - AI-inferred author + Suggested author
Date - 1923 (inferred) + 1923 (from text)
Subject @@ -261,7 +261,7 @@ const ArchivalFlowVisualizer = () => {
Schema - Dublin Core / MARC21 + Dublin Core
@@ -274,9 +274,9 @@ const ArchivalFlowVisualizer = () => {
4
-

Metadata Tagging

+

Describe

- Dublin Core / MARC21 tagging with AI inference. + Dublin Core fields, suggested from the text.

@@ -335,7 +335,7 @@ const ArchivalFlowVisualizer = () => {
5

Index & Export

- Searchable PDF/A, full-text index, and flexible export options. + PDF/A-2b archive, full-text index and Dublin Core export.

@@ -375,14 +375,14 @@ const ArchivalFlowVisualizer = () => { - {/* 100% Private Air-Gapped Ready Badge */} + {/* Offline badge */}
- 100% PRIVATE - AIR-GAPPED READY + WORKS OFFLINE + NOTHING UPLOADED
diff --git a/src/context/Toast.css b/src/context/Toast.css index 5f75ef3..b774d44 100644 --- a/src/context/Toast.css +++ b/src/context/Toast.css @@ -15,7 +15,7 @@ min-width: 300px; max-width: 450px; background: var(--color-bg, rgba(255, 255, 255, 0.9)); - color: var(--color-text, #1a1a1a); + color: #1a1a1a; padding: 1rem; border-radius: 10px; box-shadow: @@ -118,34 +118,32 @@ } } -/* Dark mode */ -@media (prefers-color-scheme: dark) { - .toast { - background: var(--color-bg-secondary, #1f2937); - color: var(--color-text, #f3f4f6); - } +/* Dark theme: follows the app's theme switch, not the OS setting */ +:root[data-theme="dark"] .toast { + background: var(--color-bg-elevated, #2a2e35); + color: var(--color-text-primary, #f3f4f6); +} - .toast-success { - background: rgba(34, 197, 94, 0.15); - } +:root[data-theme="dark"] .toast-success { + background: rgba(34, 197, 94, 0.15); +} - .toast-error { - background: rgba(239, 68, 68, 0.15); - } +:root[data-theme="dark"] .toast-error { + background: rgba(239, 68, 68, 0.15); +} - .toast-warning { - background: rgba(245, 158, 11, 0.15); - } +:root[data-theme="dark"] .toast-warning { + background: rgba(245, 158, 11, 0.15); +} - .toast-info { - background: rgba(59, 130, 246, 0.15); - } +:root[data-theme="dark"] .toast-info { + background: rgba(59, 130, 246, 0.15); +} - .toast-close { - color: #9ca3af; - } +:root[data-theme="dark"] .toast-close { + color: #9ca3af; +} - .toast-close:hover { - color: #e5e7eb; - } -} \ No newline at end of file +:root[data-theme="dark"] .toast-close:hover { + color: #e5e7eb; +} diff --git a/src/pages/LandingPage.css b/src/pages/LandingPage.css index c049eb3..5d2f9ac 100644 --- a/src/pages/LandingPage.css +++ b/src/pages/LandingPage.css @@ -75,20 +75,6 @@ color: var(--color-primary); } -.landing-nav-links .nav-highlight { - color: var(--color-accent); - padding: 4px 10px; - background: rgba(56, 189, 248, 0.1); - border: 1px solid rgba(56, 189, 248, 0.3); - border-radius: var(--radius-sm); - font-weight: var(--font-weight-semibold); -} - -.landing-nav-links .nav-highlight:hover { - background: rgba(56, 189, 248, 0.2); - color: #ffffff; -} - .landing-nav-actions { display: flex; align-items: center; @@ -147,6 +133,10 @@ align-items: center; } +.landing-hero > * { + min-width: 0; +} + .hero-content { display: flex; flex-direction: column; @@ -243,42 +233,6 @@ color: #ffffff; } -.hero-trust-row { - display: flex; - align-items: center; - gap: var(--space-6); - flex-wrap: wrap; - margin-top: var(--space-2); -} - -.trust-item { - display: flex; - align-items: center; - gap: var(--space-3); -} - -.trust-text-stack { - display: flex; - flex-direction: column; -} - -.trust-title { - font-size: 0.78rem; - font-weight: 700; - color: var(--color-text-primary); - line-height: 1.2; -} - -.trust-sub { - font-size: 0.68rem; - color: var(--color-text-tertiary); - line-height: 1.2; -} - -.trust-icon.success { color: #34d399; } -.trust-icon.primary { color: #818cf8; } -.trust-icon.accent { color: #38bdf8; } - /* ---------------- Hero Flow Visualizer ---------------- */ .hero-flow-3d-wrapper { position: relative; @@ -290,26 +244,6 @@ position: relative; } -.preview-glass-card { - background: rgba(25, 27, 31, 0.85); - border: 1px solid rgba(226, 232, 240, 0.15); - border-radius: var(--radius-xl); - padding: var(--space-5); - box-shadow: var(--shadow-xl); - position: relative; - overflow: hidden; -} - -.preview-glass-card::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - height: 2px; - background: var(--gradient-primary); -} - .preview-card-header { display: flex; align-items: center; @@ -319,50 +253,18 @@ border-bottom: 1px solid var(--color-border); } -.card-dots { - display: flex; - gap: 6px; -} - .dot { width: 10px; height: 10px; border-radius: 50%; } -.dot.red { background: #ef4444; } -.dot.yellow { background: #f59e0b; } -.dot.green { background: #10b981; } - .card-filename { font-family: var(--font-mono); font-size: var(--font-size-xs); color: var(--color-text-secondary); } -.card-status-badge { - font-size: 11px; - color: var(--color-success); - background: rgba(16, 185, 129, 0.12); - padding: 2px 8px; - border-radius: var(--radius-sm); - font-weight: var(--font-weight-medium); -} - -.preview-split { - display: grid; - grid-template-columns: 1fr 1fr; - gap: var(--space-4); -} - -.preview-pane-original, -.preview-pane-extracted { - background: var(--color-bg-secondary); - border: 1px solid var(--color-border); - border-radius: var(--radius-md); - padding: var(--space-3); -} - .pane-header { display: flex; align-items: center; @@ -375,126 +277,6 @@ margin-bottom: var(--space-3); } -.manuscript-mock { - height: 140px; - background: #101216; - border: 1px dashed rgba(56, 189, 248, 0.3); - border-radius: var(--radius-sm); - padding: var(--space-3); - display: flex; - flex-direction: column; - gap: 8px; - position: relative; - overflow: hidden; -} - -.mock-seal { - position: absolute; - top: 10px; - right: 10px; - width: 28px; - height: 28px; - border-radius: 50%; - background: rgba(59, 130, 246, 0.25); - border: 1px solid rgba(59, 130, 246, 0.6); -} - -.mock-line { - height: 6px; - background: rgba(255, 255, 255, 0.15); - border-radius: 3px; - width: 90%; -} - -.mock-line.title { - width: 60%; - height: 8px; - background: rgba(56, 189, 248, 0.4); -} - -.mock-line.short { - width: 45%; -} - -.mock-stamp { - position: absolute; - bottom: 8px; - right: 8px; - font-size: 9px; - font-family: var(--font-mono); - color: rgba(56, 189, 248, 0.8); - border: 1px solid rgba(56, 189, 248, 0.4); - padding: 1px 4px; - transform: rotate(-5deg); -} - -.extracted-text-box { - background: #0f1114; - border-radius: var(--radius-sm); - padding: var(--space-2); - font-family: var(--font-mono); - font-size: 11px; - color: var(--color-text-secondary); - line-height: 1.5; - height: 105px; - overflow: hidden; -} - -.token-keyword { - color: var(--color-accent); - font-weight: var(--font-weight-semibold); -} - -.preview-tag-list { - display: flex; - flex-wrap: wrap; - gap: 4px; - margin-top: var(--space-2); -} - -.chip-tag { - font-size: 10px; - padding: 2px 6px; - border-radius: var(--radius-sm); - background: rgba(59, 130, 246, 0.15); - color: var(--color-primary); - border: 1px solid rgba(59, 130, 246, 0.25); -} - -/* ---------------- Metrics Section ---------------- */ -.landing-metrics { - max-width: 1280px; - margin: 0 auto var(--space-16) auto; - padding: 0 var(--space-6); - display: grid; - grid-template-columns: repeat(4, 1fr); - gap: var(--space-5); -} - -.metric-card { - background: var(--color-bg-secondary); - border: 1px solid var(--color-border); - border-radius: var(--radius-lg); - padding: var(--space-5); - transition: transform var(--transition-fast), border-color var(--transition-fast); -} - -.metric-card:hover { - transform: translateY(-2px); - border-color: var(--color-primary); -} - -.metric-number { - font-size: 1.875rem; - font-weight: var(--font-weight-extrabold); - color: var(--color-primary); - background: var(--gradient-primary); - -webkit-background-clip: text; - background-clip: text; - -webkit-text-fill-color: transparent; - margin-bottom: var(--space-1); -} - .metric-label { font-size: var(--font-size-sm); font-weight: var(--font-weight-semibold); @@ -502,12 +284,6 @@ margin-bottom: var(--space-1); } -.metric-desc { - font-size: var(--font-size-xs); - color: var(--color-text-muted); - line-height: 1.45; -} - /* ---------------- Section Header Styles ---------------- */ .landing-section-header { text-align: center; @@ -538,14 +314,6 @@ text-align: center; } -.landing-section-subtitle { - font-size: var(--font-size-sm); - color: var(--color-text-secondary); - line-height: 1.6; - text-align: center; - max-width: 680px; -} - /* ---------------- Features Section ---------------- */ .landing-features { max-width: 1280px; @@ -611,292 +379,91 @@ text-align: left; } -/* ---------------- Pipeline Section ---------------- */ -.landing-pipeline { +.security-icon-box { + width: 48px; + height: 48px; + border-radius: var(--radius-md); + display: flex; + align-items: center; + justify-content: center; + margin-bottom: var(--space-4); +} + +.security-icon-box.success { background: rgba(16, 185, 129, 0.15); color: var(--color-success); } +.security-icon-box.primary { background: rgba(59, 130, 246, 0.15); color: var(--color-primary); } +.security-icon-box.accent { background: rgba(56, 189, 248, 0.15); color: var(--color-accent); } +.security-icon-box.info { background: rgba(6, 182, 212, 0.15); color: var(--color-info); } + +/* ---------------- CTA Banner ---------------- */ +.landing-cta-banner { max-width: 1280px; margin: 0 auto var(--space-16) auto; padding: 0 var(--space-6); - scroll-margin-top: 80px; } -.landing-pipeline-grid { - display: grid; - grid-template-columns: repeat(5, 1fr); - gap: var(--space-4); -} - -.landing-pipeline-card { - background: var(--color-bg-secondary); - border: 1px solid var(--color-border); - border-radius: var(--radius-lg); - padding: var(--space-5) var(--space-4); +.cta-banner-content { + background: linear-gradient(135deg, rgba(59, 130, 246, 0.15) 0%, rgba(56, 189, 248, 0.1) 100%); + border: 1px solid rgba(59, 130, 246, 0.35); + border-radius: var(--radius-2xl); + padding: var(--space-12) var(--space-8); + text-align: center; display: flex; flex-direction: column; - align-items: flex-start; - text-align: left; - position: relative; - transition: all var(--transition-fast); -} - -.landing-pipeline-card:hover { - border-color: var(--color-primary); - transform: translateY(-2px); -} - -.landing-step-badge { - font-family: var(--font-mono); - font-size: var(--font-size-xs); - font-weight: var(--font-weight-bold); - color: var(--color-primary); - margin-bottom: var(--space-2); -} - -.landing-step-icon { - color: var(--color-accent); - margin-bottom: var(--space-3); + align-items: center; } -.landing-pipeline-card h4 { - font-size: var(--font-size-sm); +.cta-banner-content h2 { + font-size: 2rem; font-weight: var(--font-weight-bold); color: var(--color-text-primary); - margin: 0 0 var(--space-2) 0; - text-align: left; + margin-bottom: var(--space-3); } -.landing-pipeline-card p { - font-size: 12px; +.cta-banner-content p { + font-size: var(--font-size-base); color: var(--color-text-secondary); - line-height: 1.5; - margin: 0; - text-align: left; -} - -/* ---------------- Solutions Section ---------------- */ -.landing-solutions { - max-width: 1280px; - margin: 0 auto var(--space-16) auto; - padding: 0 var(--space-6); - scroll-margin-top: 80px; + max-width: 600px; + margin-bottom: var(--space-6); } -.landing-solutions-grid { - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: var(--space-6); +.cta-btn-group { + display: flex; + gap: var(--space-4); + flex-wrap: wrap; + justify-content: center; } -.landing-solution-card { +/* ---------------- Footer ---------------- */ +.landing-footer { background: var(--color-bg-secondary); - border: 1px solid var(--color-border); - border-radius: var(--radius-lg); - padding: var(--space-6); - display: flex; - flex-direction: column; - align-items: flex-start; - text-align: left; - transition: all var(--transition-fast); + border-top: 1px solid var(--color-border); + padding: var(--space-12) var(--space-6) var(--space-6) var(--space-6); } -.landing-solution-card:hover { - border-color: var(--color-border-hover); - box-shadow: var(--shadow-md); +.footer-inner { + max-width: 1280px; + margin: 0 auto var(--space-10) auto; + display: grid; + grid-template-columns: 2fr 1fr 1fr 1.2fr; + gap: var(--space-8); } -.solution-icon-wrap { - width: 44px; - height: 44px; - border-radius: var(--radius-md); - background: rgba(59, 130, 246, 0.12); - color: var(--color-primary); +.footer-brand { display: flex; align-items: center; - justify-content: center; - margin-bottom: var(--space-4); -} - -.landing-solution-card h3 { - font-size: var(--font-size-base); + gap: var(--space-3); + font-size: var(--font-size-lg); font-weight: var(--font-weight-bold); color: var(--color-text-primary); - margin: 0 0 var(--space-2) 0; - text-align: left; + margin-bottom: var(--space-3); } -.landing-solution-card p { - font-size: var(--font-size-sm); - color: var(--color-text-secondary); - margin: 0 0 var(--space-4) 0; +.footer-col.brand p { + font-size: var(--font-size-xs); + color: var(--color-text-tertiary); line-height: 1.6; - text-align: left; -} - -.solution-list { - list-style: none; - padding: 0; - margin: 0; - display: flex; - flex-direction: column; - gap: var(--space-2); -} - -.solution-list li { - display: flex; - align-items: center; - gap: var(--space-2); - font-size: var(--font-size-xs); - color: var(--color-text-tertiary); -} - -.solution-list svg { - color: var(--color-success); -} - -/* ---------------- Security Section ---------------- */ -.landing-security { - max-width: 1280px; - margin: 0 auto var(--space-16) auto; - padding: 0 var(--space-6); - scroll-margin-top: 80px; -} - -.landing-security-grid { - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: var(--space-6); -} - -.landing-security-card { - background: var(--color-bg-secondary); - border: 1px solid var(--color-border); - border-radius: var(--radius-lg); - padding: var(--space-6); - display: flex; - flex-direction: column; - align-items: flex-start; - justify-content: space-between; - text-align: left; - transition: all var(--transition-fast); -} - -.landing-security-card:hover { - border-color: rgba(59, 130, 246, 0.4); - box-shadow: var(--shadow-md); - transform: translateY(-2px); -} - -.security-icon-box { - width: 48px; - height: 48px; - border-radius: var(--radius-md); - display: flex; - align-items: center; - justify-content: center; - margin-bottom: var(--space-4); -} - -.security-icon-box.success { background: rgba(16, 185, 129, 0.15); color: var(--color-success); } -.security-icon-box.primary { background: rgba(59, 130, 246, 0.15); color: var(--color-primary); } -.security-icon-box.accent { background: rgba(56, 189, 248, 0.15); color: var(--color-accent); } -.security-icon-box.info { background: rgba(6, 182, 212, 0.15); color: var(--color-info); } - -.landing-security-card h3 { - font-size: var(--font-size-base); - font-weight: var(--font-weight-bold); - color: var(--color-text-primary); - margin: 0 0 var(--space-2) 0; - text-align: left; -} - -.landing-security-card p { - font-size: var(--font-size-sm); - color: var(--color-text-secondary); - line-height: 1.6; - margin: 0 0 var(--space-4) 0; - text-align: left; -} - -.security-badge-pill { - font-size: 11px; - font-weight: var(--font-weight-semibold); - color: var(--color-success); - background: rgba(16, 185, 129, 0.1); - border: 1px solid rgba(16, 185, 129, 0.25); - padding: 4px 10px; - border-radius: var(--radius-sm); - width: fit-content; -} - -/* ---------------- CTA Banner ---------------- */ -.landing-cta-banner { - max-width: 1280px; - margin: 0 auto var(--space-16) auto; - padding: 0 var(--space-6); -} - -.cta-banner-content { - background: linear-gradient(135deg, rgba(59, 130, 246, 0.15) 0%, rgba(56, 189, 248, 0.1) 100%); - border: 1px solid rgba(59, 130, 246, 0.35); - border-radius: var(--radius-2xl); - padding: var(--space-12) var(--space-8); - text-align: center; - display: flex; - flex-direction: column; - align-items: center; -} - -.cta-banner-content h2 { - font-size: 2rem; - font-weight: var(--font-weight-bold); - color: var(--color-text-primary); - margin-bottom: var(--space-3); -} - -.cta-banner-content p { - font-size: var(--font-size-base); - color: var(--color-text-secondary); - max-width: 600px; - margin-bottom: var(--space-6); -} - -.cta-btn-group { - display: flex; - gap: var(--space-4); - flex-wrap: wrap; - justify-content: center; -} - -/* ---------------- Footer ---------------- */ -.landing-footer { - background: var(--color-bg-secondary); - border-top: 1px solid var(--color-border); - padding: var(--space-12) var(--space-6) var(--space-6) var(--space-6); -} - -.footer-inner { - max-width: 1280px; - margin: 0 auto var(--space-10) auto; - display: grid; - grid-template-columns: 2fr 1fr 1fr 1.2fr; - gap: var(--space-8); -} - -.footer-brand { - display: flex; - align-items: center; - gap: var(--space-3); - font-size: var(--font-size-lg); - font-weight: var(--font-weight-bold); - color: var(--color-text-primary); - margin-bottom: var(--space-3); -} - -.footer-col.brand p { - font-size: var(--font-size-xs); - color: var(--color-text-tertiary); - line-height: 1.6; - margin-bottom: var(--space-4); - max-width: 320px; + margin-bottom: var(--space-4); + max-width: 320px; } .footer-badge { @@ -969,55 +536,6 @@ color: var(--color-text-muted); } -/* ---------------- Login Modal ---------------- */ -.landing-modal-overlay { - position: fixed; - top: 0; - left: 0; - width: 100vw; - height: 100vh; - background: rgba(0, 0, 0, 0.75); - z-index: 1000; - display: flex; - align-items: center; - justify-content: center; - padding: var(--space-4); - animation: fadeIn var(--transition-fast) ease-out; -} - -.landing-modal-card { - background: var(--color-bg-secondary); - border: 1px solid rgba(226, 232, 240, 0.2); - border-radius: var(--radius-xl); - width: 100%; - max-width: 440px; - padding: var(--space-8); - box-shadow: var(--shadow-xl); - position: relative; - animation: slideUp var(--transition-base) cubic-bezier(0.16, 1, 0.3, 1); -} - -.modal-close-btn { - position: absolute; - top: var(--space-4); - right: var(--space-4); - background: transparent; - border: none; - color: var(--color-text-muted); - cursor: pointer; - padding: 6px; - border-radius: var(--radius-sm); - display: flex; - align-items: center; - justify-content: center; - transition: all var(--transition-fast); -} - -.modal-close-btn:hover { - background: var(--color-surface); - color: var(--color-white); -} - .modal-header { text-align: center; margin-bottom: var(--space-6); @@ -1036,111 +554,10 @@ line-height: 1.5; } -.modal-form { - display: flex; - flex-direction: column; - gap: var(--space-4); -} - -.modal-form .input-group { - position: relative; - display: flex; - align-items: center; -} - -.modal-form .input-icon { - position: absolute; - left: 14px; - color: var(--color-text-muted); - pointer-events: none; -} - -.modal-form input { - width: 100%; - padding: 12px 14px 12px 42px; - background: var(--color-bg-primary); - border: 1px solid var(--color-border); - border-radius: var(--radius-md); - color: var(--color-text-primary); - font-size: var(--font-size-sm); - transition: all var(--transition-fast); -} - -.modal-form input:focus { - outline: none; - border-color: var(--color-primary); - box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2); -} - -.modal-form input.error { - border-color: var(--color-error); -} - -.modal-error-box { - background: rgba(239, 68, 68, 0.12); - border: 1px solid rgba(239, 68, 68, 0.35); - color: #fca5a5; - padding: 8px 12px; - border-radius: var(--radius-sm); - font-size: var(--font-size-xs); -} - -.modal-success-box { - background: rgba(16, 185, 129, 0.12); - border: 1px solid rgba(16, 185, 129, 0.35); - color: var(--color-success); - padding: 8px 12px; - border-radius: var(--radius-sm); - font-size: var(--font-size-xs); - display: flex; - align-items: center; - gap: var(--space-2); -} - -.btn-modal-submit { - display: flex; - align-items: center; - justify-content: center; - gap: var(--space-2); - padding: 12px; - background: var(--gradient-primary); - color: #ffffff; - border: none; - border-radius: var(--radius-md); - font-size: var(--font-size-sm); - font-weight: var(--font-weight-semibold); - cursor: pointer; - box-shadow: var(--shadow-md); - transition: all var(--transition-fast); - margin-top: var(--space-2); -} - -.btn-modal-submit:hover:not(:disabled) { - transform: translateY(-1px); - box-shadow: 0 6px 16px rgba(59, 130, 246, 0.45); -} - -.btn-modal-submit:disabled { - opacity: 0.6; - cursor: not-allowed; -} - -.modal-footer-note { - display: flex; - align-items: center; - justify-content: center; - gap: var(--space-2); - margin-top: var(--space-5); - padding-top: var(--space-4); - border-top: 1px solid var(--color-border); - font-size: 11px; - color: var(--color-text-muted); -} - /* ---------------- Responsive Breakpoints ---------------- */ @media (max-width: 1024px) { .landing-hero { - grid-template-columns: 1fr; + grid-template-columns: minmax(0, 1fr); gap: var(--space-8); padding-top: var(--space-8); } @@ -1149,14 +566,6 @@ grid-template-columns: repeat(2, 1fr); } - .landing-pipeline-grid { - grid-template-columns: repeat(3, 1fr); - } - - .landing-metrics { - grid-template-columns: repeat(2, 1fr); - } - .footer-inner { grid-template-columns: 1fr 1fr; } @@ -1188,6 +597,11 @@ gap: var(--space-3); text-align: center; } + + .footer-bottom-links { + flex-wrap: wrap; + justify-content: center; + } } /* ========================================================================== @@ -1223,14 +637,6 @@ color: #42505c; } -[data-theme="light"] .trust-title { - color: #1f2933; -} - -[data-theme="light"] .trust-sub { - color: #64748b; -} - [data-theme="light"] .btn-hero-secondary { background: #ffffff; color: #1f2933; @@ -1243,28 +649,14 @@ border-color: #f08418; } -[data-theme="light"] .metric-card { - background: #ffffff; - border: 1px solid var(--color-border); - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.04); -} - [data-theme="light"] .metric-label { color: #1f2933; } -[data-theme="light"] .metric-desc { - color: #64748b; -} - [data-theme="light"] .landing-section-title { color: #1f2933; } -[data-theme="light"] .landing-section-subtitle { - color: #42505c; -} - [data-theme="light"] .landing-feature-card, [data-theme="light"] .landing-solution-card, [data-theme="light"] .landing-pipeline-card, @@ -1318,3 +710,176 @@ [data-theme="light"] .footer-col a:hover { color: #f08418; } + +/* ========================================================================== + Landing page sections: hero small print, step-by-step tour, privacy + ========================================================================== */ +.hero-smallprint { + font-size: var(--font-size-sm); + color: var(--color-text-tertiary); + margin: 0; +} + +.landing-steps { + max-width: 1180px; + margin: 0 auto var(--space-16) auto; + padding: 0 var(--space-6); + scroll-margin-top: 80px; +} + +.landing-step-list { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + gap: var(--space-12); +} + +.landing-step-row { + display: grid; + grid-template-columns: 0.75fr 1.25fr; + gap: var(--space-8); + align-items: center; +} + +.landing-step-row:nth-child(even) .landing-step-text { + order: 2; +} + +.landing-step-number { + display: inline-flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + border-radius: 50%; + background: rgba(59, 130, 246, 0.15); + color: var(--color-primary); + font-weight: var(--font-weight-bold); + margin-bottom: var(--space-3); +} + +.landing-step-text h3 { + font-size: 1.375rem; + font-weight: var(--font-weight-bold); + color: var(--color-text-primary); + margin: 0 0 var(--space-3) 0; +} + +.landing-step-text p { + font-size: var(--font-size-base); + color: var(--color-text-secondary); + line-height: 1.65; + margin: 0; +} + +.landing-shot { + margin: 0; + border: 1px solid var(--color-border); + border-radius: var(--radius-lg); + overflow: hidden; + box-shadow: var(--shadow-xl); + background: var(--color-bg-secondary); +} + +.landing-shot img { + display: block; + width: 100%; + height: auto; +} + +.landing-shot-wide { + max-width: 980px; + margin: var(--space-10) auto 0 auto; +} + +.landing-features-grid > .landing-feature-card:last-child:nth-child(3n + 2) { + grid-column: span 2; +} + +.landing-privacy { + max-width: 1280px; + margin: 0 auto var(--space-16) auto; + padding: 0 var(--space-6); + scroll-margin-top: 80px; +} + +.landing-privacy-card { + display: flex; + gap: var(--space-6); + align-items: flex-start; + background: var(--color-bg-secondary); + border: 1px solid var(--color-border); + border-radius: var(--radius-2xl); + padding: var(--space-10) var(--space-8); +} + +.landing-privacy-icon { + flex-shrink: 0; + color: var(--color-success); +} + +.landing-privacy-card h2 { + font-size: 1.75rem; + font-weight: var(--font-weight-bold); + color: var(--color-text-primary); + margin: 0 0 var(--space-3) 0; +} + +.landing-privacy-card p { + font-size: var(--font-size-base); + color: var(--color-text-secondary); + line-height: 1.65; + margin: 0 0 var(--space-3) 0; + max-width: 720px; +} + +.landing-privacy-card .landing-privacy-note { + font-size: var(--font-size-sm); + color: var(--color-text-tertiary); + margin: 0; +} + +@media (max-width: 1024px) { + .landing-step-row { + grid-template-columns: 1fr; + gap: var(--space-5); + } + + .landing-step-row:nth-child(even) .landing-step-text { + order: 0; + } + + .landing-features-grid > .landing-feature-card:last-child:nth-child(3n + 2) { + grid-column: auto; + } +} + +@media (max-width: 640px) { + .landing-privacy-card { + flex-direction: column; + padding: var(--space-6); + } +} + +[data-theme="light"] .hero-smallprint, +[data-theme="light"] .landing-privacy-card .landing-privacy-note { + color: #64748b; +} + +[data-theme="light"] .landing-step-text h3, +[data-theme="light"] .landing-privacy-card h2 { + color: #1f2933; +} + +[data-theme="light"] .landing-step-text p, +[data-theme="light"] .landing-privacy-card p { + color: #42505c; +} + +[data-theme="light"] .landing-privacy-card, +[data-theme="light"] .landing-shot { + background: #ffffff; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08); +} diff --git a/src/pages/LandingPage.jsx b/src/pages/LandingPage.jsx index 7a1566b..50ad179 100644 --- a/src/pages/LandingPage.jsx +++ b/src/pages/LandingPage.jsx @@ -2,21 +2,14 @@ import { useState, useEffect } from 'react' import { ShieldCheck, Lock, - Sparkles, Layers, - FileText, Search, - Cpu, ArrowRight, - CheckCircle2, - Database, - Zap, - BookOpen, - FolderCheck, - Building2, - Scale, - GraduationCap, - Landmark, + Archive, + PenLine, + Languages, + Download, + WifiOff, Sun, Moon } from 'lucide-react' @@ -26,10 +19,75 @@ import LogoLoader from '../components/LogoLoader' import LoginScreen from '../components/LoginScreen' import ArchivalFlowVisualizer from '../components/ArchivalFlowVisualizer' +const DOWNLOAD_URL = 'https://github.com/carthworks/LibraDigitAI/releases/latest' +const SCREENSHOT_BASE = `${import.meta.env.BASE_URL}screenshots/` + +const STEPS = [ + { + title: 'Upload and extract the text', + text: 'Drop in scans, photos or PDFs, one at a time or a whole batch. OCR runs on your computer, page by page, with a progress bar. Scanned PDFs are detected automatically and pages are turned upright.', + image: 'upload-ocr.webp', + alt: 'Upload page after OCR has finished on a scanned 1954 library report', + }, + { + title: 'Review', + text: 'Correct recognition errors side by side with the original page. Words the OCR was unsure about can be highlighted.', + image: 'review.webp', + alt: 'Cleanup editor showing the scanned page next to its editable text', + }, + { + title: 'Describe', + text: 'Add title, author, year and subject. LibraDigit suggests them from the text, and you stay in control of what is saved.', + image: 'describe.webp', + alt: 'Metadata form with title, author, year and subject filled in', + }, + { + title: 'Archive', + text: 'Get a PDF/A-2b file with your catalogue details embedded, in a BagIt folder with checksums and a Dublin Core record.', + image: 'archive.webp', + alt: 'Archive result showing a PDF/A-2b file saved to the archive folder', + }, +] + +const FEATURES = [ + { + icon: Search, + tone: 'warning', + title: 'Full-text search', + text: 'Search everything you have archived, with highlighted matches and filters by field and year.', + }, + { + icon: Layers, + tone: 'accent', + title: 'Batch processing', + text: 'Process whole folders of scans with a status for every file.', + }, + { + icon: Archive, + tone: 'success', + title: 'Archival formats', + text: 'PDF/A-2b checked with the veraPDF validator, BagIt packages with SHA-256 checksums, and Dublin Core export as CSV (for Excel, DSpace or Omeka) or oai_dc XML.', + }, + { + icon: PenLine, + tone: 'primary', + title: 'Handwriting mode', + text: 'A dedicated mode for handwritten pages, including a handwriting-to-PDF converter.', + }, + { + icon: Languages, + tone: 'info', + title: '10 OCR languages included', + text: 'English, Spanish, French, German, Italian, Portuguese, Hindi, Chinese (Simplified), Japanese and Russian.', + }, +] + const LandingPage = ({ onLogin, isLoggedIn }) => { const navigate = useNavigate() const [showLoginModal, setShowLoginModal] = useState(false) const [isFirstRun, setIsFirstRun] = useState(false) + // Inside the desktop app the visitor already has LibraDigit, so offer to open it instead of downloading it. + const isDesktop = Boolean(window.electron) useEffect(() => { const storedHash = localStorage.getItem('auth_hash') @@ -46,6 +104,18 @@ const LandingPage = ({ onLogin, isLoggedIn }) => { } } + const primaryAction = isDesktop ? ( + + ) : ( + + + Download for Windows + + ) + return (
{/* Header / Navigation */} @@ -55,16 +125,14 @@ const LandingPage = ({ onLogin, isLoggedIn }) => {
LibraDigit AI - v{__APP_VERSION__} • Local Archival AI + v{__APP_VERSION__} • Free for Windows
@@ -98,309 +166,126 @@ const LandingPage = ({ onLogin, isLoggedIn }) => {
- {/* Hero Section */} + {/* 1. Hero */}
- - Next-Gen Archival Preservation & Multi-Engine OCR + + For libraries, museums, schools, colleges and government offices

- Transform Physical Heritage Into AI-Searchable Digital Archives + Turn scanned documents into a searchable digital archive on your own computer.

- High-accuracy local OCR, automated image restoration, Dublin Core & MARC21 metadata generation, and instant full-text search—engineered for 100% privacy and air-gapped security. + LibraDigit reads your scans and PDFs with OCR, lets you correct the text and add catalogue details, + and saves each document as a preservation-ready PDF/A package. Nothing is uploaded anywhere.

- - - - Explore Marketing Overview - + {primaryAction} + + See how it works +
-
-
- -
- 100% Offline & Private - Your data stays yours -
-
-
- -
- Local AI Engines - No cloud, no tracking -
-
-
- -
- Dublin Core & MARC21 - Library standards ready -
-
-
+

+ Free · Windows 10 and 11 · Works offline · OCR in 10 languages included +

- {/* Hero 3D Purpose Flow Visualization */}
- {/* Metrics Showcase */} -
-
-
99.8%
-
OCR Character Accuracy
-
Dual-pass engine optimized for historical and degraded fonts
-
-
-
0 bytes
-
External Telemetry
-
All processing executes locally in an air-gapped runtime
-
-
-
10x
-
Batch Throughput
-
Multi-threaded concurrent page deskewing and OCR pipeline
-
-
-
100%
-
Standards Compliance
-
Dublin Core, MARC21, PDF/A-1b, and TEI-XML export schemas
+ {/* 2. How it works */} +
+
+ How it works +

From a scanned page to an archived, searchable record

+ +
    + {STEPS.map((step, index) => ( +
  1. +
    + {index + 1} +

    {step.title}

    +

    {step.text}

    +
    +
    + {step.alt} +
    +
  2. + ))} +
- {/* Core Features Grid */} + {/* 3. What you get */}
- Enterprise Capabilities -

Engineered for Curators, Librarians & Archivists

-

- Everything required to ingest, clean, extract, catalog, and preserve complex physical media collections in high fidelity. -

+ What you get +

Everything a small archive needs, in one app

-
-
- -
-

Multi-Engine AI OCR

-

Adaptive binarization, edge smoothing, deskewing, and high-precision text recognition for modern prints and historic manuscripts.

-
- -
-
- -
-

High-Volume Batch Processing

-

Feed entire folders of high-resolution TIFF, JPG, and PDF scans with live status monitoring, automated queuing, and batch export.

-
- -
-
- -
-

Dublin Core & MARC21 Tagging

-

Smart metadata suggestions with structured cataloging fields, automatic date resolution, and standardized vocabulary mapping.

-
- -
-
- -
-

Deep Full-Text Search

-

Instant keyword, wildcard, and fuzzy search across your entire repository with OCR confidence scoring and highlighted snippets.

-
- -
-
- -
-

Document Cleanup & Restoration

-

Interactive contrast stretching, deskewing, background noise erasing, and border cropping before text extraction.

-
- -
-
- + {FEATURES.map(({ icon: Icon, tone, title, text }) => ( +
+
+ +
+

{title}

+

{text}

-

Air-Gapped Privacy & Security

-

Bcrypt password-protected local storage, zero third-party cloud dependence, and complete compliance with institutional data governance.

-
-
-
- - {/* Interactive Pipeline Showcase */} -
-
- End-to-End Workflow -

The Five-Step Archival Digitization Pipeline

-

- From raw scanned imagery to a fully indexed, archival-grade preservation repository in minutes. -

-
- -
-
-
01
-
-

1. Ingest Scans

-

Drag and drop multi-page PDFs, high-res TIFFs, or batch directory scans.

-
- -
-
02
-
-

2. AI Clean & Deskew

-

Automated rotation, skew correction, thresholding, and speckle removal.

-
- -
-
03
-
-

3. Dual-Pass OCR

-

Multi-language text extraction with bounding box coordinates and confidence levels.

-
- -
-
04
-
-

4. Metadata Tagging

-

Dublin Core / MARC21 schema tagging with AI title and author inference.

-
- -
-
05
-
-

5. Index & Export

-

Searchable PDF/A generation, SQLite full-text indexation, and archive package export.

-
-
-
- - {/* Target Solutions */} -
-
- Industry Solutions -

Tailored for Heritage, Legal & Academic Institutions

+ ))}
-
-
-
-

Heritage Libraries & Museums

-

Digitize rare manuscripts, historical newspapers, and fragile ledger books with delicate preservation care and zero risk of data loss.

-
    -
  • Preservation-grade PDF/A-1b
  • -
  • High dynamic range image cleanup
  • -
-
- -
-
-

Universities & Research Centers

-

Transform special collections, theses, journals, and scientific archives into instantly searchable, citation-ready research databases.

-
    -
  • Dublin Core schema validation
  • -
  • Multi-language OCR recognition
  • -
-
- -
-
-

Legal & Government Registries

-

Process sensitive court documents, municipal registers, and confidential contracts under strict air-gapped compliance requirements.

-
    -
  • Zero external cloud transmission
  • -
  • Audit trail & local encryption
  • -
-
- -
-
-

Corporate & Media Archives

-

Centralize decades of legacy documentation, engineering drawings, and press clippings into a scalable digital intelligence hub.

-
    -
  • High-throughput batch ingestion
  • -
  • Sub-second full-text retrieval
  • -
-
-
+
+ Archive search finding the 1954 report by a phrase from its text +
- {/* Security & Air-Gapped Architecture Section */} -
-
- Zero-Trust & Sovereignty -

Institutional Security & Air-Gapped Architecture

-

- Engineered from the ground up for strict confidentiality, complete cryptographic privacy, and compliance with high-security heritage, legal, and government environments. -

-
- -
-
-
- -
-

100% Air-Gapped Processing

-

Every single operation—including dual-pass OCR, image binarization, deskewing, and metadata parsing—runs strictly on local hardware with zero external API calls.

-
Zero Cloud Telemetry
-
- -
-
- -
-

Bcrypt Cryptographic Authentication

-

Local archive access is protected with salted bcrypt key derivation. Your master passphrase never leaves your device memory.

-
Salted Key Derivation
-
- -
-
- -
-

Local Storage & Sovereign Database

-

Documents and full-text inverted indexes are persisted in embedded SQLite databases on your local file system, granting you absolute physical ownership.

-
Embedded SQLite 3 & FTS5
-
- -
-
- -
-

Regulatory & Preservation Compliance

-

Designed for compliance with FIPS 140-2 environments, HIPAA/GDPR data sovereignty mandates, and ISO 19005 (PDF/A) preservation standards.

-
ISO 19005 & Dublin Core
+ {/* 4. Private by design */} +
+
+ +
+

Your documents never leave your computer.

+

+ OCR, search and archiving all run locally. There is no account and no telemetry, + and the app keeps working with no internet connection. +

+

+ The optional Translate button sends the selected text to Google Translate. + Administrators can switch it off. +

- {/* CTA Banner */} + {/* 5. Closing call to action */}
-

Ready to Digitize Your Physical Heritage?

-

Start digitizing and archiving today with full offline security and state-of-the-art AI accuracy.

+

Start digitizing today.

+

{isDesktop ? 'Your archive is ready when you are.' : 'Free. Installs in about two minutes.'}

- - - Read Complete Marketing Deck - + {primaryAction}
@@ -413,20 +298,19 @@ const LandingPage = ({ onLogin, isLoggedIn }) => { LibraDigit AI
-

The sovereign, local-first artificial intelligence platform for physical record digitization and archival preservation.

+

Free desktop software for digitizing and preserving documents, for libraries, museums, schools, colleges and government offices.

- Air-Gapped & WCAG 2.1 AA Compliant + Works offline

Product

+ How it works Features - Workflow - Solutions - Transparent Pricing - Marketing Overview + Privacy + Pricing
diff --git a/src/pages/Metadata.jsx b/src/pages/Metadata.jsx index 1684a78..b4584d9 100644 --- a/src/pages/Metadata.jsx +++ b/src/pages/Metadata.jsx @@ -24,6 +24,17 @@ const SUBJECT_PRESETS = [ 'Rare Library Collections' ] +// Text edited in Cleanup is stored as the editor's HTML; show and copy it as plain text. +const projectPlainText = (project) => { + const text = project?.cleaned_text || project?.ocr_text || '' + if (!/<[a-z][^>]*>/i.test(text)) return text + const doc = new DOMParser().parseFromString(text, 'text/html') + const blocks = doc.body.querySelectorAll('p, li, h1, h2, h3, h4, h5, h6, blockquote, pre') + return blocks.length + ? Array.from(blocks, block => block.textContent).join('\n') + : doc.body.textContent || '' +} + const Metadata = () => { const { projectId } = useParams() const navigate = useNavigate() @@ -234,7 +245,7 @@ const Metadata = () => { } const handleCopyOcrText = () => { - const text = currentProject?.cleaned_text || currentProject?.ocr_text || '' + const text = projectPlainText(currentProject) if (text) { navigator.clipboard.writeText(text) setCopiedText(true) @@ -255,7 +266,7 @@ const Metadata = () => { const filename = currentProject?.filename || 'Document' const isPdf = filename.toLowerCase().endsWith('.pdf') const originalFileUrl = `${API_URL}/projects/${projectId}/file?type=original` - const ocrSnippet = (currentProject?.cleaned_text || currentProject?.ocr_text || '').trim() + const ocrSnippet = projectPlainText(currentProject).trim() // Calculated Archival Paths const subjectFolder = sanitizeFilename(formData.subject) || 'General'