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 ? (
+
+ {isFirstRun && !isLoggedIn ? 'Set up my archive' : 'Open my archive'}
+
+
+ ) : (
+
+
+ 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
+ How it works
Features
- Solutions
- Workflow
- Security
- Marketing Info
+ Privacy
@@ -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.
-
- Access Archive System
-
-
-
-
-
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) => (
+
+
+
{index + 1}
+
{step.title}
+
{step.text}
+
+
+
+
+
+ ))}
+
- {/* 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 }) => (
+
-
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
-
-
-
+
+
+
- {/* 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.'}
-
- {isLoggedIn ? 'Go to Application Workspace' : 'Sign In / Get Started'}
-
-
-
-
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
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'