Self-contained landing page with parallax hero, mock app preview, workload heatmap demo, benefit cards, focus view mockup, how-it-works steps, and Docker quick-start block. Matches FabDash dark/gold visual theme. File lives in frontend/public/ so Vite copies it to dist/ and Flask serves it at /landing.html without touching app routing. https://claude.ai/code/session_0112Vq5nD3hGgvrTJPnDvU5B
1418 lines
46 KiB
HTML
1418 lines
46 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>FabDash — Fabrication Project Dashboard</title>
|
|
<link rel="icon" href="/logo.png" />
|
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;900&display=swap" rel="stylesheet" />
|
|
<style>
|
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
|
|
:root {
|
|
--gold: #C9A84C;
|
|
--gold-light: #E8C96A;
|
|
--gold-muted: #8A6E2F;
|
|
--bg: #111111;
|
|
--raised: #1A1A1A;
|
|
--elevated: #242424;
|
|
--border: #2E2E2E;
|
|
--text: #F5F5F5;
|
|
--muted: #888888;
|
|
--blue: #0EA5E9;
|
|
--amber: #F59E0B;
|
|
--green: #22C55E;
|
|
--red: #EF4444;
|
|
}
|
|
|
|
html { scroll-behavior: smooth; }
|
|
|
|
body {
|
|
font-family: 'Inter', system-ui, sans-serif;
|
|
background: var(--bg);
|
|
color: var(--text);
|
|
overflow-x: hidden;
|
|
}
|
|
|
|
/* ── Scrollbar ── */
|
|
::-webkit-scrollbar { width: 6px; }
|
|
::-webkit-scrollbar-track { background: var(--raised); }
|
|
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
|
|
::-webkit-scrollbar-thumb:hover { background: var(--gold-muted); }
|
|
|
|
/* ── Nav ── */
|
|
nav {
|
|
position: fixed;
|
|
top: 0; left: 0; right: 0;
|
|
z-index: 100;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 0 2.5rem;
|
|
height: 64px;
|
|
background: rgba(17,17,17,0.88);
|
|
backdrop-filter: blur(12px);
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
.nav-brand {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
text-decoration: none;
|
|
}
|
|
.nav-brand img {
|
|
width: 32px; height: 32px;
|
|
object-fit: contain;
|
|
}
|
|
.nav-brand span {
|
|
font-size: 1.1rem;
|
|
font-weight: 900;
|
|
letter-spacing: 0.12em;
|
|
color: var(--gold);
|
|
}
|
|
.nav-cta {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
background: var(--gold);
|
|
color: #000;
|
|
font-weight: 700;
|
|
font-size: 0.82rem;
|
|
letter-spacing: 0.05em;
|
|
padding: 8px 20px;
|
|
border-radius: 4px;
|
|
text-decoration: none;
|
|
transition: background 0.2s;
|
|
}
|
|
.nav-cta:hover { background: var(--gold-light); }
|
|
|
|
/* ── Hero ── */
|
|
.hero {
|
|
min-height: 100vh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
text-align: center;
|
|
padding: 100px 2rem 4rem;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
.hero-bg {
|
|
position: absolute;
|
|
inset: 0;
|
|
background:
|
|
radial-gradient(ellipse 80% 60% at 50% 0%, rgba(201,168,76,0.10) 0%, transparent 70%),
|
|
radial-gradient(ellipse 50% 40% at 80% 80%, rgba(201,168,76,0.06) 0%, transparent 60%);
|
|
pointer-events: none;
|
|
}
|
|
/* parallax grid */
|
|
.hero-grid {
|
|
position: absolute;
|
|
inset: 0;
|
|
background-image:
|
|
linear-gradient(rgba(201,168,76,0.04) 1px, transparent 1px),
|
|
linear-gradient(90deg, rgba(201,168,76,0.04) 1px, transparent 1px);
|
|
background-size: 60px 60px;
|
|
pointer-events: none;
|
|
transform: translateZ(0);
|
|
}
|
|
.hero-logo {
|
|
width: 100px; height: 100px;
|
|
object-fit: contain;
|
|
margin-bottom: 1.5rem;
|
|
filter: drop-shadow(0 0 24px rgba(201,168,76,0.5));
|
|
animation: float 4s ease-in-out infinite;
|
|
}
|
|
@keyframes float {
|
|
0%, 100% { transform: translateY(0); }
|
|
50% { transform: translateY(-10px); }
|
|
}
|
|
.hero-eyebrow {
|
|
font-size: 0.72rem;
|
|
font-weight: 700;
|
|
letter-spacing: 0.2em;
|
|
color: var(--gold);
|
|
text-transform: uppercase;
|
|
margin-bottom: 1rem;
|
|
}
|
|
.hero-title {
|
|
font-size: clamp(2.6rem, 6vw, 4.8rem);
|
|
font-weight: 900;
|
|
line-height: 1.05;
|
|
letter-spacing: -0.02em;
|
|
max-width: 820px;
|
|
margin: 0 auto 1.25rem;
|
|
}
|
|
.hero-title em {
|
|
font-style: normal;
|
|
color: var(--gold);
|
|
}
|
|
.hero-sub {
|
|
font-size: clamp(1rem, 2vw, 1.2rem);
|
|
color: var(--muted);
|
|
max-width: 560px;
|
|
margin: 0 auto 2.5rem;
|
|
line-height: 1.65;
|
|
}
|
|
.hero-actions {
|
|
display: flex;
|
|
gap: 1rem;
|
|
align-items: center;
|
|
justify-content: center;
|
|
flex-wrap: wrap;
|
|
}
|
|
.btn-primary {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
background: var(--gold);
|
|
color: #000;
|
|
font-weight: 700;
|
|
font-size: 0.9rem;
|
|
letter-spacing: 0.04em;
|
|
padding: 12px 28px;
|
|
border-radius: 4px;
|
|
text-decoration: none;
|
|
transition: background 0.2s, box-shadow 0.2s;
|
|
}
|
|
.btn-primary:hover {
|
|
background: var(--gold-light);
|
|
box-shadow: 0 0 20px rgba(201,168,76,0.4);
|
|
}
|
|
.btn-ghost {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
background: transparent;
|
|
color: var(--text);
|
|
font-weight: 600;
|
|
font-size: 0.9rem;
|
|
padding: 12px 24px;
|
|
border: 1px solid var(--border);
|
|
border-radius: 4px;
|
|
text-decoration: none;
|
|
transition: border-color 0.2s, color 0.2s;
|
|
}
|
|
.btn-ghost:hover { border-color: var(--gold); color: var(--gold); }
|
|
|
|
/* ── Section base ── */
|
|
section {
|
|
padding: 6rem 2rem;
|
|
}
|
|
.container {
|
|
max-width: 1120px;
|
|
margin: 0 auto;
|
|
}
|
|
.section-label {
|
|
font-size: 0.7rem;
|
|
font-weight: 700;
|
|
letter-spacing: 0.22em;
|
|
text-transform: uppercase;
|
|
color: var(--gold);
|
|
margin-bottom: 0.75rem;
|
|
}
|
|
.section-title {
|
|
font-size: clamp(1.8rem, 4vw, 2.8rem);
|
|
font-weight: 800;
|
|
letter-spacing: -0.02em;
|
|
line-height: 1.1;
|
|
margin-bottom: 1rem;
|
|
}
|
|
.section-sub {
|
|
color: var(--muted);
|
|
font-size: 1.05rem;
|
|
line-height: 1.65;
|
|
max-width: 560px;
|
|
}
|
|
|
|
/* ── Demo Preview ── */
|
|
.demo-section {
|
|
background: linear-gradient(180deg, var(--bg) 0%, #0d0d0d 100%);
|
|
}
|
|
.demo-shell {
|
|
background: var(--raised);
|
|
border: 1px solid var(--border);
|
|
border-radius: 8px;
|
|
overflow: hidden;
|
|
box-shadow: 0 24px 80px rgba(0,0,0,0.6), 0 0 0 1px var(--border);
|
|
margin-top: 3rem;
|
|
}
|
|
/* Window chrome */
|
|
.demo-topbar {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 12px 16px;
|
|
background: #0f0f0f;
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
.dot { width: 10px; height: 10px; border-radius: 50%; }
|
|
.dot-r { background: #FF5F57; }
|
|
.dot-y { background: #FEBC2E; }
|
|
.dot-g { background: #28C840; }
|
|
.demo-url {
|
|
flex: 1;
|
|
text-align: center;
|
|
font-size: 0.72rem;
|
|
color: var(--muted);
|
|
}
|
|
|
|
/* App layout mock */
|
|
.app-mock {
|
|
display: flex;
|
|
height: 520px;
|
|
}
|
|
|
|
/* Sidebar */
|
|
.mock-sidebar {
|
|
width: 200px;
|
|
flex-shrink: 0;
|
|
background: #0f0f0f;
|
|
border-right: 1px solid var(--border);
|
|
display: flex;
|
|
flex-direction: column;
|
|
padding: 14px 0;
|
|
}
|
|
.sidebar-logo-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 0 14px 14px;
|
|
border-bottom: 1px solid var(--border);
|
|
margin-bottom: 14px;
|
|
}
|
|
.sidebar-logo-row img {
|
|
width: 24px; height: 24px; object-fit: contain;
|
|
}
|
|
.sidebar-wordmark {
|
|
font-size: 0.7rem;
|
|
font-weight: 900;
|
|
letter-spacing: 0.14em;
|
|
color: var(--gold);
|
|
}
|
|
.sidebar-project {
|
|
padding: 0 10px;
|
|
margin-bottom: 4px;
|
|
}
|
|
.project-bar {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 7px 8px;
|
|
border-radius: 4px;
|
|
background: var(--elevated);
|
|
cursor: pointer;
|
|
}
|
|
.project-dot {
|
|
width: 8px; height: 8px;
|
|
border-radius: 50%;
|
|
flex-shrink: 0;
|
|
}
|
|
.project-name {
|
|
font-size: 0.72rem;
|
|
font-weight: 600;
|
|
color: var(--text);
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
.project-count {
|
|
margin-left: auto;
|
|
font-size: 0.62rem;
|
|
color: var(--muted);
|
|
background: var(--raised);
|
|
border-radius: 10px;
|
|
padding: 1px 6px;
|
|
}
|
|
.deliverable-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 7px;
|
|
padding: 5px 8px 5px 24px;
|
|
font-size: 0.67rem;
|
|
color: var(--muted);
|
|
}
|
|
.badge {
|
|
font-size: 0.58rem;
|
|
font-weight: 600;
|
|
padding: 1px 5px;
|
|
border-radius: 3px;
|
|
white-space: nowrap;
|
|
}
|
|
.badge-upcoming { background: rgba(14,165,233,0.18); color: #38BDF8; }
|
|
.badge-progress { background: rgba(245,158,11,0.18); color: #FCD34D; }
|
|
.badge-complete { background: rgba(34,197,94,0.18); color: #4ADE80; }
|
|
.badge-overdue { background: rgba(239,68,68,0.18); color: #F87171; }
|
|
|
|
/* Main calendar mock */
|
|
.mock-main {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow: hidden;
|
|
}
|
|
.cal-header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 12px 18px;
|
|
border-bottom: 1px solid var(--border);
|
|
background: var(--raised);
|
|
}
|
|
.cal-title {
|
|
font-size: 0.85rem;
|
|
font-weight: 700;
|
|
letter-spacing: 0.02em;
|
|
}
|
|
.cal-nav {
|
|
display: flex;
|
|
gap: 6px;
|
|
}
|
|
.cal-btn {
|
|
background: var(--elevated);
|
|
border: 1px solid var(--border);
|
|
border-radius: 4px;
|
|
color: var(--text);
|
|
font-size: 0.68rem;
|
|
font-weight: 600;
|
|
padding: 4px 10px;
|
|
cursor: pointer;
|
|
}
|
|
.cal-btn.active {
|
|
background: var(--gold);
|
|
color: #000;
|
|
border-color: var(--gold);
|
|
}
|
|
/* Calendar grid */
|
|
.cal-grid {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow: hidden;
|
|
}
|
|
.cal-dow {
|
|
display: grid;
|
|
grid-template-columns: repeat(7, 1fr);
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
.cal-dow-cell {
|
|
padding: 6px;
|
|
text-align: center;
|
|
font-size: 0.6rem;
|
|
font-weight: 700;
|
|
letter-spacing: 0.08em;
|
|
color: var(--muted);
|
|
text-transform: uppercase;
|
|
}
|
|
.cal-weeks {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
.cal-week {
|
|
flex: 1;
|
|
display: grid;
|
|
grid-template-columns: repeat(7, 1fr);
|
|
border-bottom: 1px solid var(--border);
|
|
}
|
|
.cal-week:last-child { border-bottom: none; }
|
|
.cal-day {
|
|
border-right: 1px solid var(--border);
|
|
padding: 4px 5px;
|
|
min-height: 0;
|
|
overflow: hidden;
|
|
}
|
|
.cal-day:last-child { border-right: none; }
|
|
.cal-day.other-month .day-num { color: #444; }
|
|
.cal-day.today .day-num {
|
|
background: var(--gold);
|
|
color: #000;
|
|
border-radius: 50%;
|
|
width: 20px; height: 20px;
|
|
display: flex; align-items: center; justify-content: center;
|
|
}
|
|
.day-num {
|
|
font-size: 0.6rem;
|
|
font-weight: 600;
|
|
color: var(--muted);
|
|
margin-bottom: 3px;
|
|
width: 20px; height: 20px;
|
|
display: flex; align-items: center; justify-content: center;
|
|
}
|
|
.cal-event {
|
|
font-size: 0.55rem;
|
|
font-weight: 600;
|
|
padding: 2px 5px;
|
|
border-radius: 2px;
|
|
margin-bottom: 2px;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
color: #000;
|
|
}
|
|
|
|
/* ── Benefits ── */
|
|
.benefits-section {
|
|
background: linear-gradient(180deg, #0d0d0d 0%, var(--bg) 100%);
|
|
}
|
|
.benefits-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
gap: 1.5rem;
|
|
margin-top: 3rem;
|
|
}
|
|
.benefit-card {
|
|
background: var(--raised);
|
|
border: 1px solid var(--border);
|
|
border-radius: 6px;
|
|
padding: 1.75rem;
|
|
position: relative;
|
|
overflow: hidden;
|
|
transition: border-color 0.25s, transform 0.25s;
|
|
}
|
|
.benefit-card::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0; left: 0; right: 0;
|
|
height: 2px;
|
|
background: linear-gradient(90deg, var(--gold), var(--gold-muted));
|
|
opacity: 0;
|
|
transition: opacity 0.25s;
|
|
}
|
|
.benefit-card:hover {
|
|
border-color: var(--gold-muted);
|
|
transform: translateY(-3px);
|
|
}
|
|
.benefit-card:hover::before { opacity: 1; }
|
|
.benefit-icon {
|
|
font-size: 1.8rem;
|
|
margin-bottom: 1rem;
|
|
line-height: 1;
|
|
}
|
|
.benefit-title {
|
|
font-size: 1rem;
|
|
font-weight: 700;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
.benefit-desc {
|
|
font-size: 0.875rem;
|
|
color: var(--muted);
|
|
line-height: 1.65;
|
|
}
|
|
|
|
/* ── Heatmap Demo ── */
|
|
.heatmap-section {
|
|
background: var(--bg);
|
|
}
|
|
.heatmap-wrap {
|
|
margin-top: 3rem;
|
|
background: var(--raised);
|
|
border: 1px solid var(--border);
|
|
border-radius: 6px;
|
|
padding: 1.5rem;
|
|
}
|
|
.heatmap-header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-bottom: 1rem;
|
|
}
|
|
.heatmap-title {
|
|
font-size: 0.8rem;
|
|
font-weight: 700;
|
|
letter-spacing: 0.06em;
|
|
text-transform: uppercase;
|
|
color: var(--gold);
|
|
}
|
|
.heatmap-stats {
|
|
display: flex;
|
|
gap: 1.5rem;
|
|
}
|
|
.heatmap-stat { text-align: right; }
|
|
.heatmap-stat-val {
|
|
font-size: 1.1rem;
|
|
font-weight: 800;
|
|
color: var(--gold);
|
|
}
|
|
.heatmap-stat-lbl {
|
|
font-size: 0.62rem;
|
|
color: var(--muted);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.1em;
|
|
}
|
|
.heatmap-weeks {
|
|
display: flex;
|
|
gap: 4px;
|
|
overflow-x: auto;
|
|
padding-bottom: 4px;
|
|
}
|
|
.heatmap-week {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 3px;
|
|
flex-shrink: 0;
|
|
}
|
|
.heatmap-week-label {
|
|
font-size: 0.55rem;
|
|
color: var(--muted);
|
|
text-align: center;
|
|
height: 14px;
|
|
line-height: 14px;
|
|
}
|
|
.heatmap-cell {
|
|
width: 28px; height: 28px;
|
|
border-radius: 3px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 0.58rem;
|
|
font-weight: 700;
|
|
color: #000;
|
|
}
|
|
.heatmap-cell.d0 { background: var(--elevated); color: var(--muted); }
|
|
.heatmap-cell.d1 { background: rgba(201,168,76,0.20); color: var(--gold); }
|
|
.heatmap-cell.d2 { background: rgba(201,168,76,0.40); color: var(--gold-light); }
|
|
.heatmap-cell.d3 { background: rgba(201,168,76,0.65); color: #1a1200; }
|
|
.heatmap-cell.d4 { background: var(--gold); }
|
|
.heatmap-cell.d5 { background: var(--gold-light); }
|
|
|
|
/* ── Features ── */
|
|
.features-section {
|
|
background: linear-gradient(180deg, var(--bg) 0%, #0d0d0d 100%);
|
|
}
|
|
.features-grid {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 4rem;
|
|
align-items: center;
|
|
margin-top: 3rem;
|
|
}
|
|
@media (max-width: 768px) {
|
|
.features-grid { grid-template-columns: 1fr; gap: 2rem; }
|
|
.app-mock { height: 420px; }
|
|
.mock-sidebar { width: 140px; }
|
|
nav { padding: 0 1rem; }
|
|
}
|
|
.feature-list {
|
|
list-style: none;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1.2rem;
|
|
}
|
|
.feature-item {
|
|
display: flex;
|
|
gap: 1rem;
|
|
align-items: flex-start;
|
|
}
|
|
.feature-icon-wrap {
|
|
width: 36px; height: 36px;
|
|
border-radius: 6px;
|
|
background: rgba(201,168,76,0.1);
|
|
border: 1px solid rgba(201,168,76,0.2);
|
|
display: flex; align-items: center; justify-content: center;
|
|
flex-shrink: 0;
|
|
font-size: 1rem;
|
|
}
|
|
.feature-text h4 {
|
|
font-size: 0.9rem;
|
|
font-weight: 700;
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
.feature-text p {
|
|
font-size: 0.82rem;
|
|
color: var(--muted);
|
|
line-height: 1.6;
|
|
}
|
|
|
|
/* Focus drawer mock */
|
|
.focus-mock {
|
|
background: var(--raised);
|
|
border: 1px solid var(--border);
|
|
border-radius: 6px;
|
|
overflow: hidden;
|
|
}
|
|
.focus-header {
|
|
padding: 12px 16px;
|
|
border-bottom: 1px solid var(--border);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
}
|
|
.focus-header-title {
|
|
font-size: 0.8rem;
|
|
font-weight: 700;
|
|
}
|
|
.focus-project-dot {
|
|
width: 8px; height: 8px;
|
|
border-radius: 50%;
|
|
display: inline-block;
|
|
margin-right: 6px;
|
|
}
|
|
.focus-timeline {
|
|
display: flex;
|
|
gap: 0;
|
|
padding: 1.5rem 1rem;
|
|
overflow-x: auto;
|
|
}
|
|
.timeline-item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 8px;
|
|
min-width: 110px;
|
|
}
|
|
.timeline-card {
|
|
background: var(--elevated);
|
|
border: 1px solid var(--border);
|
|
border-radius: 4px;
|
|
padding: 8px 10px;
|
|
width: 100%;
|
|
text-align: center;
|
|
}
|
|
.timeline-card.active {
|
|
border-color: var(--gold);
|
|
box-shadow: 0 0 10px rgba(201,168,76,0.25);
|
|
}
|
|
.timeline-card-title {
|
|
font-size: 0.67rem;
|
|
font-weight: 600;
|
|
color: var(--text);
|
|
margin-bottom: 4px;
|
|
}
|
|
.timeline-card-date {
|
|
font-size: 0.58rem;
|
|
color: var(--muted);
|
|
}
|
|
.timeline-line {
|
|
display: flex;
|
|
align-items: center;
|
|
width: 100%;
|
|
position: relative;
|
|
}
|
|
.timeline-dot {
|
|
width: 8px; height: 8px;
|
|
border-radius: 50%;
|
|
background: var(--gold);
|
|
flex-shrink: 0;
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
.timeline-connector {
|
|
flex: 1;
|
|
height: 1px;
|
|
background: var(--border);
|
|
}
|
|
.timeline-item:last-child .timeline-connector { display: none; }
|
|
|
|
/* ── How It Works ── */
|
|
.how-section {
|
|
background: #0d0d0d;
|
|
}
|
|
.steps-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
|
gap: 2rem;
|
|
margin-top: 3rem;
|
|
counter-reset: steps;
|
|
}
|
|
.step-card {
|
|
position: relative;
|
|
padding-left: 0;
|
|
}
|
|
.step-num {
|
|
font-size: 3rem;
|
|
font-weight: 900;
|
|
color: rgba(201,168,76,0.12);
|
|
line-height: 1;
|
|
margin-bottom: 0.75rem;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
.step-title {
|
|
font-size: 1rem;
|
|
font-weight: 700;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
.step-desc {
|
|
font-size: 0.875rem;
|
|
color: var(--muted);
|
|
line-height: 1.65;
|
|
}
|
|
|
|
/* ── Docker CTA ── */
|
|
.docker-section {
|
|
background: var(--bg);
|
|
}
|
|
.docker-box {
|
|
background: var(--raised);
|
|
border: 1px solid var(--border);
|
|
border-radius: 8px;
|
|
padding: 2rem 2rem 1.5rem;
|
|
margin-top: 2rem;
|
|
}
|
|
.docker-box-label {
|
|
font-size: 0.65rem;
|
|
font-weight: 700;
|
|
letter-spacing: 0.16em;
|
|
text-transform: uppercase;
|
|
color: var(--muted);
|
|
margin-bottom: 0.75rem;
|
|
}
|
|
.code-block {
|
|
background: #0a0a0a;
|
|
border: 1px solid var(--border);
|
|
border-radius: 4px;
|
|
padding: 1rem 1.25rem;
|
|
font-family: 'JetBrains Mono', 'Fira Code', 'Courier New', monospace;
|
|
font-size: 0.8rem;
|
|
color: var(--text);
|
|
overflow-x: auto;
|
|
margin-bottom: 1rem;
|
|
white-space: pre;
|
|
line-height: 1.7;
|
|
}
|
|
.code-block .c-comment { color: var(--muted); }
|
|
.code-block .c-gold { color: var(--gold); }
|
|
.code-block .c-green { color: #4ADE80; }
|
|
.code-block .c-blue { color: #60A5FA; }
|
|
|
|
/* ── Footer ── */
|
|
footer {
|
|
background: #0a0a0a;
|
|
border-top: 1px solid var(--border);
|
|
padding: 2.5rem 2rem;
|
|
text-align: center;
|
|
}
|
|
.footer-brand {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 8px;
|
|
margin-bottom: 0.75rem;
|
|
}
|
|
.footer-brand img {
|
|
width: 24px; height: 24px; object-fit: contain;
|
|
}
|
|
.footer-brand span {
|
|
font-size: 0.85rem;
|
|
font-weight: 900;
|
|
letter-spacing: 0.14em;
|
|
color: var(--gold);
|
|
}
|
|
.footer-note {
|
|
font-size: 0.78rem;
|
|
color: var(--muted);
|
|
}
|
|
.gold-divider {
|
|
width: 60px;
|
|
height: 2px;
|
|
background: linear-gradient(90deg, transparent, var(--gold), transparent);
|
|
margin: 2rem auto;
|
|
}
|
|
|
|
/* ── Parallax scroll effect ── */
|
|
.parallax-layer {
|
|
will-change: transform;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<!-- Navigation -->
|
|
<nav>
|
|
<a class="nav-brand" href="#">
|
|
<img src="/logo.png" alt="FabDash logo" onerror="this.style.display='none'" />
|
|
<span>FABDASH</span>
|
|
</a>
|
|
<a class="nav-cta" href="/">Open App →</a>
|
|
</nav>
|
|
|
|
<!-- ══════════════════════════════════════════
|
|
HERO
|
|
══════════════════════════════════════════ -->
|
|
<section class="hero">
|
|
<div class="hero-bg"></div>
|
|
<div class="hero-grid parallax-layer" data-speed="0.3"></div>
|
|
|
|
<img src="/logo.png" class="hero-logo" alt="FabDash logo"
|
|
onerror="this.style.display='none'" />
|
|
|
|
<p class="hero-eyebrow">Fabrication Project Dashboard</p>
|
|
<h1 class="hero-title">
|
|
Track every cut, weld,<br/>and deadline — <em>visually</em>
|
|
</h1>
|
|
<p class="hero-sub">
|
|
FabDash is a self-hosted calendar dashboard built for fabrication shops.
|
|
Drag, drop, and deliver on time — no spreadsheet gymnastics required.
|
|
</p>
|
|
<div class="hero-actions">
|
|
<a href="/" class="btn-primary">Open Dashboard →</a>
|
|
<a href="#demo" class="btn-ghost">See Demo ↓</a>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ══════════════════════════════════════════
|
|
DEMO PREVIEW
|
|
══════════════════════════════════════════ -->
|
|
<section class="demo-section" id="demo">
|
|
<div class="container">
|
|
<p class="section-label">Live Demo</p>
|
|
<h2 class="section-title">Your shop, at a glance</h2>
|
|
<p class="section-sub">
|
|
Every active project, every deliverable, color-coded and laid out on an
|
|
interactive calendar — exactly as it looks inside FabDash.
|
|
</p>
|
|
|
|
<div class="demo-shell">
|
|
<!-- Window chrome -->
|
|
<div class="demo-topbar">
|
|
<span class="dot dot-r"></span>
|
|
<span class="dot dot-y"></span>
|
|
<span class="dot dot-g"></span>
|
|
<span class="demo-url">fabdash.local:8080</span>
|
|
</div>
|
|
|
|
<!-- App layout -->
|
|
<div class="app-mock">
|
|
<!-- Sidebar -->
|
|
<div class="mock-sidebar">
|
|
<div class="sidebar-logo-row">
|
|
<img src="/logo.png" alt="" onerror="this.style.display='none'" />
|
|
<span class="sidebar-wordmark">FABDASH</span>
|
|
</div>
|
|
|
|
<!-- Project: Tucson -->
|
|
<div class="sidebar-project">
|
|
<div class="project-bar">
|
|
<span class="project-dot" style="background:#E67E22"></span>
|
|
<span class="project-name">Tucson</span>
|
|
<span class="project-count">4</span>
|
|
</div>
|
|
<div class="deliverable-row">
|
|
<span style="flex:1;font-size:0.64rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">Cut Extrusion</span>
|
|
<span class="badge badge-progress">Active</span>
|
|
</div>
|
|
<div class="deliverable-row">
|
|
<span style="flex:1;font-size:0.64rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">Fabricate Assembly</span>
|
|
<span class="badge badge-upcoming">Soon</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Project: Mesa -->
|
|
<div class="sidebar-project">
|
|
<div class="project-bar">
|
|
<span class="project-dot" style="background:#3B82F6"></span>
|
|
<span class="project-name">Mesa</span>
|
|
<span class="project-count">3</span>
|
|
</div>
|
|
<div class="deliverable-row">
|
|
<span style="flex:1;font-size:0.64rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">Weld Subframe</span>
|
|
<span class="badge badge-complete">Done</span>
|
|
</div>
|
|
<div class="deliverable-row">
|
|
<span style="flex:1;font-size:0.64rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">Paint & Finish</span>
|
|
<span class="badge badge-progress">Active</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Project: Phoenix Shelter -->
|
|
<div class="sidebar-project">
|
|
<div class="project-bar">
|
|
<span class="project-dot" style="background:#8B5CF6"></span>
|
|
<span class="project-name">Phoenix Shelter</span>
|
|
<span class="project-count">5</span>
|
|
</div>
|
|
<div class="deliverable-row">
|
|
<span style="flex:1;font-size:0.64rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">Roof Panel Cuts</span>
|
|
<span class="badge badge-overdue">Late</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Project: Scottsdale -->
|
|
<div class="sidebar-project">
|
|
<div class="project-bar">
|
|
<span class="project-dot" style="background:#10B981"></span>
|
|
<span class="project-name">Scottsdale</span>
|
|
<span class="project-count">2</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Calendar -->
|
|
<div class="mock-main">
|
|
<div class="cal-header">
|
|
<span class="cal-title">March 2026</span>
|
|
<div class="cal-nav">
|
|
<button class="cal-btn">←</button>
|
|
<button class="cal-btn active">Month</button>
|
|
<button class="cal-btn">Week</button>
|
|
<button class="cal-btn">Today</button>
|
|
<button class="cal-btn">→</button>
|
|
</div>
|
|
</div>
|
|
<div class="cal-grid">
|
|
<div class="cal-dow">
|
|
<div class="cal-dow-cell">Sun</div>
|
|
<div class="cal-dow-cell">Mon</div>
|
|
<div class="cal-dow-cell">Tue</div>
|
|
<div class="cal-dow-cell">Wed</div>
|
|
<div class="cal-dow-cell">Thu</div>
|
|
<div class="cal-dow-cell">Fri</div>
|
|
<div class="cal-dow-cell">Sat</div>
|
|
</div>
|
|
<div class="cal-weeks">
|
|
<!-- Week 1 (Mar 1 → Mar 7) — note: Mar 1 is Sunday 2026 -->
|
|
<div class="cal-week">
|
|
<div class="cal-day"><div class="day-num">1</div></div>
|
|
<div class="cal-day"><div class="day-num">2</div>
|
|
<div class="cal-event" style="background:#E67E22">Cut Extrusion</div>
|
|
</div>
|
|
<div class="cal-day"><div class="day-num">3</div></div>
|
|
<div class="cal-day"><div class="day-num">4</div>
|
|
<div class="cal-event" style="background:#3B82F6">Weld Subframe</div>
|
|
</div>
|
|
<div class="cal-day"><div class="day-num">5</div>
|
|
<div class="cal-event" style="background:#8B5CF6">Roof Cuts</div>
|
|
</div>
|
|
<div class="cal-day"><div class="day-num">6</div>
|
|
<div class="cal-event" style="background:#10B981">Scottsdale</div>
|
|
</div>
|
|
<div class="cal-day"><div class="day-num">7</div></div>
|
|
</div>
|
|
<!-- Week 2 -->
|
|
<div class="cal-week">
|
|
<div class="cal-day"><div class="day-num">8</div></div>
|
|
<div class="cal-day"><div class="day-num">9</div>
|
|
<div class="cal-event" style="background:#E67E22">Fab Assembly</div>
|
|
</div>
|
|
<div class="cal-day today"><div class="day-num">10</div>
|
|
<div class="cal-event" style="background:#3B82F6">Paint & Finish</div>
|
|
</div>
|
|
<div class="cal-day"><div class="day-num">11</div></div>
|
|
<div class="cal-day"><div class="day-num">12</div>
|
|
<div class="cal-event" style="background:#8B5CF6">Panel Weld</div>
|
|
</div>
|
|
<div class="cal-day"><div class="day-num">13</div>
|
|
<div class="cal-event" style="background:#E67E22">QC Check</div>
|
|
</div>
|
|
<div class="cal-day"><div class="day-num">14</div></div>
|
|
</div>
|
|
<!-- Week 3 -->
|
|
<div class="cal-week">
|
|
<div class="cal-day"><div class="day-num">15</div></div>
|
|
<div class="cal-day"><div class="day-num">16</div>
|
|
<div class="cal-event" style="background:#10B981">Install Prep</div>
|
|
</div>
|
|
<div class="cal-day"><div class="day-num">17</div></div>
|
|
<div class="cal-day"><div class="day-num">18</div>
|
|
<div class="cal-event" style="background:#E67E22">Powder Coat</div>
|
|
</div>
|
|
<div class="cal-day"><div class="day-num">19</div>
|
|
<div class="cal-event" style="background:#3B82F6">Final QC</div>
|
|
</div>
|
|
<div class="cal-day"><div class="day-num">20</div></div>
|
|
<div class="cal-day"><div class="day-num">21</div></div>
|
|
</div>
|
|
<!-- Week 4 -->
|
|
<div class="cal-week">
|
|
<div class="cal-day"><div class="day-num">22</div>
|
|
<div class="cal-event" style="background:#8B5CF6">Glass Mount</div>
|
|
</div>
|
|
<div class="cal-day"><div class="day-num">23</div></div>
|
|
<div class="cal-day"><div class="day-num">24</div>
|
|
<div class="cal-event" style="background:#E67E22">Ship TUC-01</div>
|
|
</div>
|
|
<div class="cal-day"><div class="day-num">25</div></div>
|
|
<div class="cal-day"><div class="day-num">26</div>
|
|
<div class="cal-event" style="background:#10B981">Delivery</div>
|
|
</div>
|
|
<div class="cal-day"><div class="day-num">27</div></div>
|
|
<div class="cal-day"><div class="day-num">28</div></div>
|
|
</div>
|
|
<!-- Week 5 -->
|
|
<div class="cal-week">
|
|
<div class="cal-day"><div class="day-num">29</div></div>
|
|
<div class="cal-day"><div class="day-num">30</div>
|
|
<div class="cal-event" style="background:#3B82F6">MSA Closeout</div>
|
|
</div>
|
|
<div class="cal-day"><div class="day-num">31</div></div>
|
|
<div class="cal-day other-month"><div class="day-num">1</div></div>
|
|
<div class="cal-day other-month"><div class="day-num">2</div></div>
|
|
<div class="cal-day other-month"><div class="day-num">3</div></div>
|
|
<div class="cal-day other-month"><div class="day-num">4</div></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ══════════════════════════════════════════
|
|
BENEFITS
|
|
══════════════════════════════════════════ -->
|
|
<section class="benefits-section" id="benefits">
|
|
<div class="container">
|
|
<p class="section-label">Why FabDash</p>
|
|
<h2 class="section-title">Built for the shop floor,<br/>not the boardroom</h2>
|
|
<p class="section-sub">
|
|
Spreadsheets break. Sticky notes fall. FabDash keeps every project visible
|
|
and every deadline accountable — in your browser, on your server.
|
|
</p>
|
|
|
|
<div class="benefits-grid">
|
|
<div class="benefit-card">
|
|
<div class="benefit-icon">🗓️</div>
|
|
<h3 class="benefit-title">Drag-and-drop calendar</h3>
|
|
<p class="benefit-desc">
|
|
Reschedule deliverables by dragging events between days. Month, week,
|
|
and day views let you zoom in when deadlines stack up.
|
|
</p>
|
|
</div>
|
|
<div class="benefit-card">
|
|
<div class="benefit-icon">🎨</div>
|
|
<h3 class="benefit-title">Color-coded projects</h3>
|
|
<p class="benefit-desc">
|
|
Assign a distinct color to each job. At a glance you know which events
|
|
belong to which project — no key required.
|
|
</p>
|
|
</div>
|
|
<div class="benefit-card">
|
|
<div class="benefit-icon">🌡️</div>
|
|
<h3 class="benefit-title">Workload heatmap</h3>
|
|
<p class="benefit-desc">
|
|
A 20-week density grid reveals crunch periods before they sneak up on
|
|
your crew. Spot hot weeks and redistribute the load.
|
|
</p>
|
|
</div>
|
|
<div class="benefit-card">
|
|
<div class="benefit-icon">⚡</div>
|
|
<h3 class="benefit-title">Instant status tracking</h3>
|
|
<p class="benefit-desc">
|
|
Upcoming, In Progress, Completed, and Overdue statuses are
|
|
auto-computed and color-coded so nothing slips through the cracks.
|
|
</p>
|
|
</div>
|
|
<div class="benefit-card">
|
|
<div class="benefit-icon">🔄</div>
|
|
<h3 class="benefit-title">Undo any action</h3>
|
|
<p class="benefit-desc">
|
|
Deleted a deliverable by accident? A 30-second countdown toast lets you
|
|
reverse any change before it's permanent.
|
|
</p>
|
|
</div>
|
|
<div class="benefit-card">
|
|
<div class="benefit-icon">🔒</div>
|
|
<h3 class="benefit-title">100% self-hosted</h3>
|
|
<p class="benefit-desc">
|
|
Your project data never leaves your network. Deploy with a single Docker
|
|
command and own everything — database and all.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ══════════════════════════════════════════
|
|
HEATMAP DEMO
|
|
══════════════════════════════════════════ -->
|
|
<section class="heatmap-section">
|
|
<div class="container">
|
|
<p class="section-label">Workload Heatmap</p>
|
|
<h2 class="section-title">See crunch before it hits</h2>
|
|
<p class="section-sub">
|
|
The 20-week heatmap shows deliverable density across your entire pipeline.
|
|
Gold-hot cells flag overloaded weeks so you can rebalance while there's
|
|
still time.
|
|
</p>
|
|
|
|
<div class="heatmap-wrap">
|
|
<div class="heatmap-header">
|
|
<span class="heatmap-title">20-Week Workload — March → July 2026</span>
|
|
<div class="heatmap-stats">
|
|
<div class="heatmap-stat">
|
|
<div class="heatmap-stat-val">18</div>
|
|
<div class="heatmap-stat-lbl">Active</div>
|
|
</div>
|
|
<div class="heatmap-stat">
|
|
<div class="heatmap-stat-val">4</div>
|
|
<div class="heatmap-stat-lbl">Projects</div>
|
|
</div>
|
|
<div class="heatmap-stat">
|
|
<div class="heatmap-stat-val">3</div>
|
|
<div class="heatmap-stat-lbl">Overdue</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="heatmap-weeks" id="heatmap"></div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ══════════════════════════════════════════
|
|
FEATURE DETAILS (Focus View)
|
|
══════════════════════════════════════════ -->
|
|
<section class="features-section" id="features">
|
|
<div class="container">
|
|
<div class="features-grid">
|
|
<!-- Focus timeline mock -->
|
|
<div class="focus-mock">
|
|
<div class="focus-header">
|
|
<span class="focus-header-title">
|
|
<span class="focus-project-dot" style="background:#E67E22"></span>
|
|
Tucson — Focus View
|
|
</span>
|
|
<span class="badge badge-progress">4 deliverables</span>
|
|
</div>
|
|
<div class="focus-timeline">
|
|
<div class="timeline-item">
|
|
<div class="timeline-card">
|
|
<div class="timeline-card-title">Cut Extrusion</div>
|
|
<div class="timeline-card-date">Mar 2</div>
|
|
<div style="margin-top:4px"><span class="badge badge-complete">Done</span></div>
|
|
</div>
|
|
<div class="timeline-line">
|
|
<div class="timeline-dot" style="background:#22C55E"></div>
|
|
<div class="timeline-connector"></div>
|
|
</div>
|
|
</div>
|
|
<div class="timeline-item">
|
|
<div class="timeline-card active">
|
|
<div class="timeline-card-title">Fab Assembly</div>
|
|
<div class="timeline-card-date">Mar 9</div>
|
|
<div style="margin-top:4px"><span class="badge badge-progress">Active</span></div>
|
|
</div>
|
|
<div class="timeline-line">
|
|
<div class="timeline-dot" style="background:#F59E0B"></div>
|
|
<div class="timeline-connector"></div>
|
|
</div>
|
|
</div>
|
|
<div class="timeline-item">
|
|
<div class="timeline-card">
|
|
<div class="timeline-card-title">QC Check</div>
|
|
<div class="timeline-card-date">Mar 13</div>
|
|
<div style="margin-top:4px"><span class="badge badge-upcoming">Soon</span></div>
|
|
</div>
|
|
<div class="timeline-line">
|
|
<div class="timeline-dot" style="background:#0EA5E9"></div>
|
|
<div class="timeline-connector"></div>
|
|
</div>
|
|
</div>
|
|
<div class="timeline-item">
|
|
<div class="timeline-card">
|
|
<div class="timeline-card-title">Ship TUC-01</div>
|
|
<div class="timeline-card-date">Mar 24</div>
|
|
<div style="margin-top:4px"><span class="badge badge-upcoming">Soon</span></div>
|
|
</div>
|
|
<div class="timeline-line">
|
|
<div class="timeline-dot"></div>
|
|
<div class="timeline-connector"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Feature list -->
|
|
<div>
|
|
<p class="section-label">Focus View</p>
|
|
<h2 class="section-title">Zoom in on one project</h2>
|
|
<p class="section-sub" style="margin-bottom:1.75rem">
|
|
Click any project to slide up a horizontal timeline showing every
|
|
deliverable in order. Edit, drag, and update status without leaving
|
|
the view.
|
|
</p>
|
|
<ul class="feature-list">
|
|
<li class="feature-item">
|
|
<div class="feature-icon-wrap">🖱️</div>
|
|
<div class="feature-text">
|
|
<h4>Double-click to edit</h4>
|
|
<p>Double-click any deliverable card to open an inline editor — change the title, date, or status in one step.</p>
|
|
</div>
|
|
</li>
|
|
<li class="feature-item">
|
|
<div class="feature-icon-wrap">⌨️</div>
|
|
<div class="feature-text">
|
|
<h4>Keyboard shortcuts</h4>
|
|
<p>N to create, B to toggle the sidebar, ← → to navigate months, T to jump to today, Esc to close modals.</p>
|
|
</div>
|
|
</li>
|
|
<li class="feature-item">
|
|
<div class="feature-icon-wrap">📁</div>
|
|
<div class="feature-text">
|
|
<h4>Google Drive link</h4>
|
|
<p>Attach a Drive folder to each project so every drawing, spec sheet, and photo lives one click away.</p>
|
|
</div>
|
|
</li>
|
|
<li class="feature-item">
|
|
<div class="feature-icon-wrap">🖱️</div>
|
|
<div class="feature-text">
|
|
<h4>Right-click context menus</h4>
|
|
<p>Right-click any event or deliverable to edit, duplicate, change status, or delete — no menu diving.</p>
|
|
</div>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ══════════════════════════════════════════
|
|
HOW IT WORKS
|
|
══════════════════════════════════════════ -->
|
|
<section class="how-section" id="how">
|
|
<div class="container">
|
|
<p class="section-label">Getting Started</p>
|
|
<h2 class="section-title">Up and running in minutes</h2>
|
|
<p class="section-sub">
|
|
One Docker command. No accounts, no SaaS subscriptions, no per-seat pricing.
|
|
Just your data on your hardware.
|
|
</p>
|
|
|
|
<div class="steps-grid">
|
|
<div class="step-card">
|
|
<div class="step-num">01</div>
|
|
<h3 class="step-title">Pull the image</h3>
|
|
<p class="step-desc">Run <code style="color:var(--gold)">docker run</code> with a volume mount for persistent storage. That's the entire installation.</p>
|
|
</div>
|
|
<div class="step-card">
|
|
<div class="step-num">02</div>
|
|
<h3 class="step-title">Create a project</h3>
|
|
<p class="step-desc">Hit <kbd style="background:var(--elevated);border:1px solid var(--border);border-radius:3px;padding:1px 5px;font-size:0.8em">N</kbd> or click the + button. Name it, pick a color, add an optional Drive link.</p>
|
|
</div>
|
|
<div class="step-card">
|
|
<div class="step-num">03</div>
|
|
<h3 class="step-title">Add deliverables</h3>
|
|
<p class="step-desc">Each deliverable gets a title, a due date, and a status. They appear instantly on the calendar as color-coded events.</p>
|
|
</div>
|
|
<div class="step-card">
|
|
<div class="step-num">04</div>
|
|
<h3 class="step-title">Drag and deliver</h3>
|
|
<p class="step-desc">Reschedule by dragging events. Mark complete with a right-click. Watch the heatmap cool down as work ships.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ══════════════════════════════════════════
|
|
DOCKER CTA
|
|
══════════════════════════════════════════ -->
|
|
<section class="docker-section">
|
|
<div class="container" style="text-align:center">
|
|
<p class="section-label">Self-Hosted</p>
|
|
<h2 class="section-title">Deploy on your own hardware</h2>
|
|
<p class="section-sub" style="margin:1rem auto 0">
|
|
FabDash ships as a single Docker container with an embedded SQLite database.
|
|
Works perfectly on Unraid, TrueNAS, a Raspberry Pi, or any Linux server.
|
|
</p>
|
|
|
|
<div class="docker-box" style="max-width:680px;margin:2rem auto 0;text-align:left">
|
|
<div class="docker-box-label">Quick Start</div>
|
|
<div class="code-block"><span class="c-comment"># Pull and run FabDash</span>
|
|
docker run -d \
|
|
--name fabdash \
|
|
-p <span class="c-blue">8080</span>:<span class="c-blue">8080</span> \
|
|
-v <span class="c-green">./data</span>:/app/data \
|
|
--restart unless-stopped \
|
|
fabdash:latest</div>
|
|
<div class="docker-box-label" style="margin-top:1rem">Then open</div>
|
|
<div class="code-block"><span class="c-gold">http://your-server-ip:<span class="c-blue">8080</span></span></div>
|
|
</div>
|
|
|
|
<div style="margin-top:2.5rem">
|
|
<a href="/" class="btn-primary" style="font-size:1rem;padding:14px 36px">
|
|
Open FabDash →
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<div class="gold-divider"></div>
|
|
|
|
<!-- ══════════════════════════════════════════
|
|
FOOTER
|
|
══════════════════════════════════════════ -->
|
|
<footer>
|
|
<div class="footer-brand">
|
|
<img src="/logo.png" alt="" onerror="this.style.display='none'" />
|
|
<span>FABDASH</span>
|
|
</div>
|
|
<p class="footer-note">Self-hosted fabrication dashboard — your shop, your data.</p>
|
|
</footer>
|
|
|
|
<!-- ══════════════════════════════════════════
|
|
SCRIPTS
|
|
══════════════════════════════════════════ -->
|
|
<script>
|
|
/* ── Heatmap generator ── */
|
|
(function () {
|
|
const density = [0,1,0,2,3,1,0,2,4,5,3,2,1,0,2,3,4,2,1,0];
|
|
const labels = [
|
|
'Mar 1','Mar 8','Mar 15','Mar 22','Mar 29',
|
|
'Apr 5','Apr 12','Apr 19','Apr 26',
|
|
'May 3','May 10','May 17','May 24','May 31',
|
|
'Jun 7','Jun 14','Jun 21','Jun 28',
|
|
'Jul 5','Jul 12'
|
|
];
|
|
const wrap = document.getElementById('heatmap');
|
|
density.forEach((d, i) => {
|
|
const col = document.createElement('div');
|
|
col.className = 'heatmap-week';
|
|
const lbl = document.createElement('div');
|
|
lbl.className = 'heatmap-week-label';
|
|
lbl.textContent = labels[i];
|
|
col.appendChild(lbl);
|
|
for (let row = 0; row < 5; row++) {
|
|
const cell = document.createElement('div');
|
|
const v = Math.max(0, Math.min(5, d + Math.round((Math.random() - 0.4) * 1.2)));
|
|
cell.className = `heatmap-cell d${v}`;
|
|
cell.textContent = v || '';
|
|
col.appendChild(cell);
|
|
}
|
|
wrap.appendChild(col);
|
|
});
|
|
})();
|
|
|
|
/* ── Parallax on scroll ── */
|
|
(function () {
|
|
const layers = document.querySelectorAll('.parallax-layer');
|
|
function onScroll () {
|
|
const y = window.scrollY;
|
|
layers.forEach(el => {
|
|
const speed = parseFloat(el.dataset.speed || 0.2);
|
|
el.style.transform = `translateY(${y * speed}px)`;
|
|
});
|
|
}
|
|
window.addEventListener('scroll', onScroll, { passive: true });
|
|
})();
|
|
|
|
/* ── Scroll-reveal ── */
|
|
(function () {
|
|
const style = document.createElement('style');
|
|
style.textContent = `
|
|
.reveal { opacity: 0; transform: translateY(28px); transition: opacity 0.55s ease, transform 0.55s ease; }
|
|
.reveal.visible { opacity: 1; transform: none; }
|
|
`;
|
|
document.head.appendChild(style);
|
|
|
|
const targets = document.querySelectorAll(
|
|
'.benefit-card, .step-card, .feature-item, .focus-mock, .heatmap-wrap, .demo-shell, .docker-box'
|
|
);
|
|
targets.forEach((el, i) => {
|
|
el.classList.add('reveal');
|
|
el.style.transitionDelay = `${(i % 4) * 80}ms`;
|
|
});
|
|
|
|
const io = new IntersectionObserver(entries => {
|
|
entries.forEach(e => {
|
|
if (e.isIntersecting) { e.target.classList.add('visible'); io.unobserve(e.target); }
|
|
});
|
|
}, { threshold: 0.12 });
|
|
|
|
targets.forEach(el => io.observe(el));
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|