Files
2026-05-02 20:14:15 -05:00

1290 lines
69 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover" />
<meta name="theme-color" content="#2b5916" />
<title>Storybid — Storybook Farm Charity Auction Platform</title>
<style>
/* ─── Brand tokens (mirrored from packages/client/tailwind.config.ts) ─── */
:root {
--green-50:#f1f8ec; --green-100:#daefd0; --green-200:#b5dfa1;
--green-500:#4a9528; --green-700:#2b5916; --green-800:#1e3f10; --green-900:#12270a;
--gold-50:#fdf8ed; --gold-100:#faeed0; --gold-300:#ecc45e; --gold-500:#c4952a; --gold-600:#a37820;
--ink:#12270a; --slate:#4b5563; --mute:#6b7280; --line:#e5e7eb; --bg:#f7f7f4; --white:#ffffff;
--shadow-card: 0 1px 3px rgba(0,0,0,.08), 0 1px 2px rgba(0,0,0,.06);
--shadow-lg: 0 12px 36px rgba(20,30,12,.16), 0 4px 12px rgba(20,30,12,.08);
--bid-shadow: 0 6px 20px rgba(43,89,22,.35);
/* Fluid type — scales smoothly between 360px and 1280px viewports */
--fs-h1: clamp(32px, 6vw, 56px);
--fs-h2: clamp(26px, 4vw, 38px);
--fs-h3: clamp(20px, 2.6vw, 26px);
--fs-lead: clamp(15px, 1.4vw, 18px);
--fs-body: clamp(14px, 1.05vw, 15px);
--gutter: clamp(16px, 4vw, 32px);
}
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%; scroll-behavior:smooth}
body{
margin:0; background:var(--bg); color:var(--ink);
font-family:Inter,ui-sans-serif,system-ui,-apple-system,"Segoe UI",sans-serif;
-webkit-font-smoothing:antialiased; line-height:1.55;
overflow-x:hidden;
}
h1,h2,h3,h4{margin:0; font-weight:800; letter-spacing:-.01em; line-height:1.2}
p{margin:0}
a{color:inherit}
img{max-width:100%; display:block}
/* ─── Layout ─── */
.wrap{max-width:1180px; margin:0 auto; padding-left:var(--gutter); padding-right:var(--gutter)}
section{padding:clamp(48px, 7vw, 88px) 0}
section + section{border-top:1px solid var(--line)}
/* ─── Header / Hero ─── */
.hero{
background:
radial-gradient(1200px 500px at 80% -10%, rgba(196,149,42,.18), transparent 60%),
radial-gradient(900px 500px at 0% 100%, rgba(43,89,22,.65), transparent 60%),
linear-gradient(160deg, #1e3f10 0%, #2b5916 60%, #3a771f 100%);
color:#fff; position:relative; overflow:hidden;
}
.hero::after{
content:""; position:absolute; inset:0;
background-image:radial-gradient(rgba(255,255,255,.06) 1px, transparent 1px);
background-size:24px 24px; pointer-events:none;
}
.nav{
display:flex; align-items:center; justify-content:space-between;
padding:18px 0; position:relative; z-index:2;
}
.brand{display:flex; align-items:center; gap:12px; font-weight:900; letter-spacing:.02em; font-size:15px}
.brand-mark{
width:36px; height:36px; border-radius:11px;
background:linear-gradient(140deg,var(--gold-500),#e4ae32);
display:grid; place-items:center; color:#2b5916; font-weight:900; font-size:14px;
box-shadow:0 6px 16px rgba(0,0,0,.25);
flex:0 0 auto;
}
.nav-links{display:flex; gap:22px; font-size:14px; color:rgba(255,255,255,.85); flex-wrap:wrap}
.nav-links a{text-decoration:none}
.nav-links a:hover{color:#fff}
.hero-grid{
position:relative; z-index:2;
display:grid; grid-template-columns:1.05fr .95fr; gap:clamp(28px, 5vw, 56px);
padding:clamp(36px, 5vw, 64px) 0 clamp(48px, 7vw, 96px); align-items:center;
}
.pill{
display:inline-flex; align-items:center; gap:8px;
background:rgba(196,149,42,.18); border:1px solid rgba(196,149,42,.45);
color:var(--gold-300); padding:6px 12px; border-radius:999px;
font-size:12px; font-weight:700; letter-spacing:.08em; text-transform:uppercase;
}
.pill .dot{width:8px; height:8px; border-radius:50%; background:var(--gold-300); box-shadow:0 0 0 4px rgba(236,196,94,.18)}
h1.hero-title{font-size:var(--fs-h1); line-height:1.05; margin:18px 0 14px; font-weight:900}
h1.hero-title .accent{color:var(--gold-300)}
.hero-sub{color:rgba(255,255,255,.85); font-size:var(--fs-lead); max-width:560px}
.cta-row{display:flex; gap:10px; margin-top:24px; flex-wrap:wrap}
.btn{
display:inline-flex; align-items:center; justify-content:center; gap:8px;
padding:13px 20px; border-radius:14px; font-weight:700; font-size:14px;
text-decoration:none; border:1px solid transparent; cursor:pointer;
transition:transform .12s ease;
}
.btn:active{transform:scale(.97)}
.btn-gold{background:var(--gold-500); color:#1e3f10}
.btn-gold:hover{background:var(--gold-600); color:#fff}
.btn-ghost{background:rgba(255,255,255,.08); color:#fff; border-color:rgba(255,255,255,.25)}
.btn-ghost:hover{background:rgba(255,255,255,.16)}
/* hero stats */
.hero-stats{display:flex; gap:clamp(16px,3vw,32px); margin-top:32px; flex-wrap:wrap}
.hero-stat{min-width:120px}
.hero-stat .v{font-size:22px; font-weight:900; color:#fff; line-height:1.1}
.hero-stat .l{font-size:11px; color:rgba(255,255,255,.7); text-transform:uppercase; letter-spacing:.1em; margin-top:4px}
/* ─── Phone mock ─── */
.phone-wrap{display:flex; justify-content:flex-end}
.phone{
width:min(300px, 80vw); aspect-ratio: 300 / 608;
border-radius:42px; background:#0a1308;
border:1px solid rgba(255,255,255,.18);
box-shadow:var(--shadow-lg), inset 0 0 0 6px #060c05;
position:relative; padding:14px;
}
.phone::before{
content:""; position:absolute; top:14px; left:50%; transform:translateX(-50%);
width:35%; height:24px; background:#000; border-radius:14px;
}
.phone-screen{
width:100%; height:100%; border-radius:30px; background:var(--bg);
overflow:hidden; display:flex; flex-direction:column;
}
.status-bar{
display:flex; justify-content:space-between; align-items:center;
font-size:11px; padding:8px 14px 0; color:#1e3f10; font-weight:700;
}
/* ─── Sections shared ─── */
.eyebrow{
display:inline-block; font-size:12px; font-weight:700; letter-spacing:.14em;
text-transform:uppercase; color:var(--green-700);
background:var(--green-50); padding:6px 12px; border-radius:999px;
}
h2.section-title{font-size:var(--fs-h2); margin:14px 0 10px; max-width:760px}
.section-lead{color:var(--slate); font-size:var(--fs-lead); max-width:720px}
/* ─── Feature cards ─── */
.features{
display:grid; gap:16px; margin-top:32px;
grid-template-columns:repeat(auto-fit, minmax(260px, 1fr));
}
.feature{
background:#fff; border:1px solid var(--line); border-radius:18px; padding:22px;
box-shadow:var(--shadow-card); display:flex; flex-direction:column; gap:10px;
}
.feature-icon{
width:42px; height:42px; border-radius:12px;
display:grid; place-items:center; font-size:20px;
background:var(--green-50); color:var(--green-700);
}
.feature.gold .feature-icon{background:var(--gold-100); color:var(--gold-600)}
.feature.future .feature-icon{background:#fef3e7; color:#b45309}
.feature h3{font-size:16px}
.feature p{color:var(--slate); font-size:14px}
.feature-tag{
display:inline-flex; gap:5px; font-size:10px; font-weight:800;
letter-spacing:.1em; text-transform:uppercase;
padding:3px 8px; border-radius:999px; align-self:flex-start;
}
.tag-shipped{background:#d1fadf; color:#107a3a}
.tag-progress{background:#faeed0; color:#a37820}
.tag-planned{background:#fef3e7; color:#b45309}
.tag-future{background:#e0e7ff; color:#4338ca}
/* ─── Resilience callout ─── */
.callout{
background:linear-gradient(135deg, var(--green-700), var(--green-800));
color:#fff; border-radius:24px; padding:clamp(24px, 4vw, 44px);
display:grid; grid-template-columns:1.2fr 1fr; gap:clamp(20px, 4vw, 36px); align-items:center;
}
.callout h3{font-size:var(--fs-h3); margin-bottom:10px}
.callout p{color:rgba(255,255,255,.85); font-size:var(--fs-body)}
.failover{
display:grid; grid-template-columns:repeat(3,1fr); gap:10px;
}
.fo-step{
background:rgba(255,255,255,.06); border:1px solid rgba(255,255,255,.14);
border-radius:14px; padding:14px; text-align:center;
}
.fo-step .n{
width:26px; height:26px; border-radius:50%;
background:var(--gold-500); color:var(--green-900); font-weight:900;
display:grid; place-items:center; margin:0 auto 8px; font-size:13px;
}
.fo-step .t{font-size:13px; font-weight:700}
.fo-step .d{font-size:11px; color:rgba(255,255,255,.7); margin-top:4px}
/* ─── Mock-up gallery ─── */
.mocks{
display:grid; gap:20px; margin-top:32px;
grid-template-columns:repeat(auto-fit, minmax(300px, 1fr));
}
.mock{
background:#fff; border:1px solid var(--line); border-radius:20px; overflow:hidden;
box-shadow:var(--shadow-card); display:flex; flex-direction:column;
}
.mock-head{padding:14px 18px; border-bottom:1px solid var(--line); display:flex; align-items:center; gap:10px}
.mock-dots{display:flex; gap:5px}
.mock-dots span{width:9px; height:9px; border-radius:50%; background:#e5e1d6}
.mock-dots span:nth-child(1){background:#f4b6a4}
.mock-dots span:nth-child(2){background:#f4d893}
.mock-dots span:nth-child(3){background:#a8d49c}
.mock-title{font-size:13px; color:var(--slate); font-weight:600}
.mock-tag{margin-left:auto; font-size:10px; font-weight:800; letter-spacing:.1em; text-transform:uppercase; padding:3px 8px; border-radius:999px}
.mock-body{padding:22px; min-height:260px; background:linear-gradient(180deg,#fdfcf7,#fff)}
/* Live auction mock */
.live-mock{text-align:center}
.live-mock .lot{font-size:11px; letter-spacing:.16em; text-transform:uppercase; color:#9ca3af; margin-bottom:6px}
.live-mock .item{font-weight:800; font-size:18px; margin-bottom:4px}
.live-mock .donor{font-size:13px; color:#9ca3af}
.badge-live{
display:inline-block; background:#d1fadf; color:#107a3a;
font-size:12px; font-weight:700; padding:5px 12px; border-radius:999px; margin:14px 0 10px;
}
.live-mock .label{font-size:11px; letter-spacing:.16em; text-transform:uppercase; color:#9ca3af; margin-top:14px}
.live-mock .bid{font-size:clamp(36px, 7vw, 46px); font-weight:900; color:var(--green-700); line-height:1; margin:6px 0 14px; font-variant-numeric:tabular-nums}
.bid-btn{
display:block; width:100%; padding:18px; border-radius:16px;
background:var(--green-700); color:#fff; font-size:22px; font-weight:900;
border:none; box-shadow:var(--bid-shadow); cursor:pointer;
}
/* Silent auction mock */
.silent-grid{display:grid; grid-template-columns:1fr 1fr; gap:12px}
.silent-card{
background:#fff; border:1px solid var(--line); border-radius:14px; overflow:hidden;
}
.silent-img{
height:80px;
background:linear-gradient(135deg,#daefd0,#b5dfa1);
display:grid; place-items:center; font-size:26px;
}
.silent-card.alt .silent-img{background:linear-gradient(135deg,#faeed0,#ecc45e)}
.silent-card.warn .silent-img{background:linear-gradient(135deg,#fde6d2,#f4b87b)}
.silent-card.cool .silent-img{background:linear-gradient(135deg,#e0f0e8,#9bcfb6)}
.silent-meta{padding:10px 12px}
.silent-meta .t{font-size:12px; font-weight:700; line-height:1.3}
.silent-meta .b{font-size:13px; font-weight:800; color:var(--green-700); margin-top:4px}
.silent-meta .end{font-size:10px; color:#9ca3af; margin-top:2px}
/* Auctioneer console */
.console{background:#0a1308; color:#fff; border-radius:14px; padding:18px}
.console .row{display:flex; justify-content:space-between; align-items:center; margin-bottom:8px; flex-wrap:wrap; gap:6px}
.console .lot-tag{color:var(--gold-300); font-size:11px; letter-spacing:.16em; text-transform:uppercase; font-weight:700}
.console .timer{font-size:12px; color:rgba(255,255,255,.7)}
.console .item-name{font-size:18px; font-weight:800; margin-bottom:14px}
.console .grid{display:grid; grid-template-columns:1fr 1fr; gap:10px}
.console .stat{background:rgba(255,255,255,.06); border-radius:10px; padding:10px 12px}
.console .stat .l{font-size:10px; letter-spacing:.14em; color:rgba(255,255,255,.6); text-transform:uppercase; font-weight:700}
.console .stat .v{font-size:20px; font-weight:900; color:var(--gold-300); font-variant-numeric:tabular-nums}
.console .controls{display:flex; gap:6px; margin-top:14px; flex-wrap:wrap}
.console .ctrl{
flex:1 1 80px; padding:10px; background:var(--green-700); color:#fff;
border-radius:10px; font-size:12px; font-weight:700; text-align:center;
border:1px solid rgba(255,255,255,.1);
}
.console .ctrl.alt{background:rgba(196,149,42,.18); color:var(--gold-300)}
.console .ctrl.sold{background:#7a1f1f}
/* Display board */
.board{
background:linear-gradient(135deg,#1e3f10,#2b5916); color:#fff;
border-radius:14px; padding:20px; text-align:center;
}
.board .org{font-size:11px; color:var(--gold-300); letter-spacing:.18em; text-transform:uppercase; font-weight:700}
.board .item{font-size:18px; font-weight:800; margin:6px 0 10px}
.board .big{font-size:clamp(36px, 7vw, 46px); font-weight:900; color:var(--gold-300); font-variant-numeric:tabular-nums}
.board .paddle{margin-top:8px; font-size:13px; color:rgba(255,255,255,.8)}
.board .thermo{
margin-top:14px; height:10px; background:rgba(255,255,255,.12);
border-radius:999px; overflow:hidden; position:relative;
}
.board .thermo .fill{
width:68%; height:100%; background:linear-gradient(90deg,var(--gold-500),var(--gold-300));
}
.board .thermo-label{font-size:11px; color:rgba(255,255,255,.7); margin-top:8px}
/* Check-in mock */
.checkin{background:#fff; border-radius:14px; border:1px solid var(--line); padding:16px}
.checkin .step{display:flex; align-items:center; gap:12px; padding:12px; border-bottom:1px solid var(--line)}
.checkin .step:last-child{border-bottom:none}
.checkin .num{
width:30px; height:30px; border-radius:50%;
background:var(--green-50); color:var(--green-700); font-weight:900;
display:grid; place-items:center; font-size:13px; flex:0 0 auto;
}
.checkin .step.active .num{background:var(--green-700); color:#fff}
.checkin .step.done .num{background:#d1fadf; color:#107a3a}
.checkin .meta .t{font-weight:700; font-size:13px}
.checkin .meta .d{font-size:11px; color:var(--mute); margin-top:2px}
.checkin .badge-status{
margin-left:auto; font-size:10px; font-weight:800; letter-spacing:.1em; text-transform:uppercase;
padding:3px 8px; border-radius:999px;
}
/* Reporting mock */
.report{background:#fff; border-radius:14px; border:1px solid var(--line); padding:16px}
.report .kpis{display:grid; grid-template-columns:repeat(2, 1fr); gap:8px; margin-bottom:14px}
.report .kpi{background:var(--green-50); border-radius:10px; padding:10px 12px}
.report .kpi .l{font-size:10px; letter-spacing:.14em; text-transform:uppercase; color:var(--green-800); font-weight:700}
.report .kpi .v{font-size:18px; font-weight:900; color:var(--green-800); font-variant-numeric:tabular-nums; line-height:1.1; margin-top:2px}
.report .kpi .delta{font-size:10px; color:#107a3a; font-weight:700; margin-top:2px}
.chart{display:flex; align-items:flex-end; gap:6px; height:80px; padding:10px 4px 0; border-top:1px solid var(--line)}
.chart .bar{flex:1; background:linear-gradient(180deg, var(--gold-300), var(--gold-500)); border-radius:4px 4px 0 0; min-height:8px}
/* Notification stream mock */
.notif{background:#fff; border-radius:14px; border:1px solid var(--line); padding:14px; display:flex; flex-direction:column; gap:8px}
.notif-item{display:flex; gap:10px; align-items:flex-start; padding:10px; border-radius:10px; background:var(--bg)}
.notif-item.warn{background:#fef3e7}
.notif-item.success{background:#ecfdf3}
.notif-icon{width:30px; height:30px; border-radius:8px; display:grid; place-items:center; flex:0 0 auto; font-size:14px; background:#fff; border:1px solid var(--line)}
.notif-meta .t{font-size:13px; font-weight:700}
.notif-meta .d{font-size:11px; color:var(--mute)}
.notif-meta .time{font-size:10px; color:#9ca3af; margin-top:3px}
/* Roles grid */
.roles{display:grid; gap:14px; margin-top:32px; grid-template-columns:repeat(auto-fit, minmax(200px,1fr))}
.role{
background:#fff; border:1px solid var(--line); border-radius:14px; padding:18px;
display:flex; flex-direction:column; gap:8px;
}
.role-icon{
width:38px; height:38px; border-radius:10px; display:grid; place-items:center;
background:var(--green-700); color:#fff; font-size:18px;
}
.role h4{font-size:14px}
.role p{font-size:12.5px; color:var(--slate)}
.role.alt .role-icon{background:var(--gold-500)}
/* ─── Roadmap ─── */
.roadmap{display:grid; gap:14px; margin-top:24px; grid-template-columns:repeat(auto-fit, minmax(240px,1fr))}
.phase{
background:#fff; border:1px solid var(--line); border-radius:16px; padding:20px;
position:relative;
}
.phase-head{display:flex; justify-content:space-between; align-items:center; gap:8px; margin-bottom:8px; flex-wrap:wrap}
.phase-num{
font-size:11px; font-weight:800; color:var(--green-700);
background:var(--green-50); padding:4px 10px; border-radius:999px;
}
.status{font-size:11px; font-weight:700; padding:4px 10px; border-radius:999px}
.status.done{background:#d1fadf; color:#107a3a}
.status.todo{background:#f3f4f6; color:#6b7280}
.status.now{background:#faeed0; color:#a37820}
.phase h4{font-size:16px; margin-bottom:6px}
.phase p{font-size:13px; color:var(--slate)}
.phase ul{margin:8px 0 0; padding:0; list-style:none}
.phase ul li{font-size:12px; color:var(--slate); padding:3px 0; padding-left:14px; position:relative}
.phase ul li::before{content:""; position:absolute; left:0; color:var(--green-500); font-weight:900}
/* ─── Stack table ─── */
.stack{
margin-top:24px; background:#fff; border:1px solid var(--line);
border-radius:18px; overflow:hidden;
}
.stack-row{
display:grid; grid-template-columns:200px 1fr;
padding:14px 22px; border-top:1px solid var(--line);
gap:16px;
}
.stack-row:first-child{border-top:none; background:var(--green-50); font-weight:700; color:var(--green-800)}
.stack-row .l{font-weight:700; color:var(--green-800)}
.stack-row .v{color:var(--slate); font-size:14px}
/* ─── Future features grid ─── */
.future-grid{
display:grid; gap:14px; margin-top:24px;
grid-template-columns:repeat(auto-fit, minmax(260px, 1fr));
}
.future-card{
border:1px dashed var(--gold-500); border-radius:16px; padding:18px;
background:linear-gradient(180deg,#fdf8ed 0%, #fff 100%);
}
.future-card .head{display:flex; align-items:center; gap:10px; margin-bottom:8px}
.future-card h4{font-size:15px; color:var(--green-800)}
.future-card p{font-size:13px; color:var(--slate)}
.future-card .icon{
width:34px; height:34px; border-radius:9px; background:var(--gold-100);
color:var(--gold-600); display:grid; place-items:center; font-size:16px; flex:0 0 auto;
}
.future-card .when{
display:inline-block; font-size:10px; font-weight:800;
letter-spacing:.1em; text-transform:uppercase; margin-top:8px;
padding:3px 8px; border-radius:999px; background:#fef3e7; color:#b45309;
}
/* ─── Approval section ─── */
.approval{
background:linear-gradient(135deg,#fdf8ed,#faeed0);
border:1px solid var(--gold-300); border-radius:24px;
padding:clamp(24px, 4vw, 44px);
display:grid; grid-template-columns:1.4fr 1fr; gap:clamp(20px, 4vw, 32px); align-items:center;
}
.approval h3{font-size:var(--fs-h3); color:var(--green-800); margin-bottom:8px}
.approval p{color:var(--green-900); font-size:var(--fs-body)}
.checklist{list-style:none; padding:0; margin:0}
.checklist li{
display:flex; gap:10px; align-items:flex-start; margin-bottom:10px;
background:rgba(255,255,255,.5); padding:10px 14px; border-radius:12px;
}
.checklist .check{
flex:0 0 22px; height:22px; border-radius:50%; background:var(--green-700);
color:#fff; display:grid; place-items:center; font-size:13px; font-weight:900; margin-top:1px;
}
.checklist .t{font-weight:700; font-size:14px; color:var(--green-900)}
.checklist .d{font-size:13px; color:var(--slate)}
/* ─── FAQ ─── */
.faq{margin-top:32px; display:grid; gap:12px}
details.faq-item{
background:#fff; border:1px solid var(--line); border-radius:14px;
padding:16px 18px; box-shadow:var(--shadow-card);
}
details.faq-item[open]{border-color:var(--green-200)}
details.faq-item summary{
cursor:pointer; font-weight:700; font-size:15px; color:var(--green-800);
list-style:none; display:flex; justify-content:space-between; align-items:center; gap:12px;
}
details.faq-item summary::-webkit-details-marker{display:none}
details.faq-item summary::after{
content:"+"; font-size:22px; color:var(--green-500); font-weight:400;
transition:transform .2s ease;
}
details.faq-item[open] summary::after{content:""}
details.faq-item p{margin-top:10px; color:var(--slate); font-size:14px}
/* ─── Footer ─── */
footer{
background:var(--green-900); color:rgba(255,255,255,.7);
padding:36px 0; text-align:center; font-size:13px;
}
footer .brand{justify-content:center; color:#fff; margin-bottom:8px}
/* ─── Mobile breakpoints ─── */
@media (max-width: 880px){
.hero-grid{grid-template-columns:1fr; text-align:left}
.phone-wrap{justify-content:center; margin-top:8px}
.callout, .approval{grid-template-columns:1fr}
.stack-row{grid-template-columns:140px 1fr; padding:12px 16px}
.nav-links{gap:14px}
}
@media (max-width: 640px){
.nav-links{display:none}
.nav{padding:14px 0}
.stack-row{grid-template-columns:1fr; gap:4px; padding:14px 16px}
.stack-row:first-child{display:none}
.stack-row .l{font-size:11px; text-transform:uppercase; letter-spacing:.12em; color:var(--green-700)}
.silent-grid{grid-template-columns:1fr 1fr}
.console .grid{grid-template-columns:1fr 1fr}
h2.section-title{margin-top:10px}
.checklist li{padding:10px 12px}
.feature{padding:18px}
.mock-body{padding:16px; min-height:0}
}
@media (max-width: 380px){
.silent-grid{grid-template-columns:1fr}
}
/* ─── Print ─── */
@media print{
.hero{background:var(--green-700) !important; -webkit-print-color-adjust:exact; print-color-adjust:exact}
.nav-links, .cta-row{display:none}
section{padding:24px 0; page-break-inside:avoid}
}
</style>
</head>
<body>
<!-- ═══════════════════════ HERO ═══════════════════════ -->
<header class="hero">
<div class="wrap">
<nav class="nav">
<div class="brand">
<div class="brand-mark">SB</div>
<div>STORYBID</div>
</div>
<div class="nav-links">
<a href="#what">Features</a>
<a href="#resilience">Resilience</a>
<a href="#roles">Roles</a>
<a href="#screens">Screens</a>
<a href="#planned">Planned</a>
<a href="#roadmap">Roadmap</a>
<a href="#approval">Approval</a>
</div>
</nav>
<div class="hero-grid">
<div>
<span class="pill"><span class="dot"></span> Built for Storybook Farm</span>
<h1 class="hero-title">A charity auction night that <span class="accent">never goes dark</span>.</h1>
<p class="hero-sub">
Storybid is a self-hosted live + silent auction platform purpose-built for
Storybook Farm. Bidders bid from their phones, the auctioneer drives the
room, and the whole event keeps running on local Wi-Fi if the internet drops.
</p>
<div class="cta-row">
<a class="btn btn-gold" href="#approval">Approve for build →</a>
<a class="btn btn-ghost" href="#screens">See the screens</a>
<a class="btn btn-ghost" href="#planned">What's coming</a>
</div>
<div class="hero-stats">
<div class="hero-stat"><div class="v">1 system</div><div class="l">Live + silent + fund-a-need</div></div>
<div class="hero-stat"><div class="v">0 app store</div><div class="l">Mobile web, install as PWA</div></div>
<div class="hero-stat"><div class="v">100%</div><div class="l">Self-hosted &amp; offline-ready</div></div>
<div class="hero-stat"><div class="v">2 of 6</div><div class="l">Build phases shipped</div></div>
</div>
</div>
<!-- Phone preview: bidder home -->
<div class="phone-wrap">
<div class="phone">
<div class="phone-screen">
<div class="status-bar"><span>9:41</span><span>● ● ● ●</span></div>
<div style="padding:18px 18px 14px; background:linear-gradient(160deg,#2b5916,#1e3f10); color:#fff; margin-top:6px">
<p style="font-size:10px; letter-spacing:.16em; color:#daefd0; text-transform:uppercase; font-weight:700">Welcome back</p>
<h2 style="font-size:22px; font-weight:900; margin-top:4px">Sarah Whitman</h2>
<div style="display:inline-flex; gap:8px; align-items:center; margin-top:10px; background:rgba(0,0,0,.25); padding:5px 10px; border-radius:10px">
<span style="color:#ecc45e; font-weight:900; font-size:16px">#142</span>
<span style="font-size:11px; color:#daefd0">Your paddle</span>
</div>
</div>
<div style="padding:14px; display:flex; flex-direction:column; gap:10px; flex:1">
<div style="background:#fff; border:1px solid #e5e7eb; border-radius:14px; padding:12px; display:flex; align-items:center; gap:10px">
<span style="font-size:22px">🌿</span>
<div>
<div style="font-size:13px; font-weight:700">Storybook Farm Gala</div>
<div style="font-size:11px; color:#9ca3af">Auction is live</div>
</div>
<span style="margin-left:auto; width:8px; height:8px; border-radius:50%; background:#34d399"></span>
</div>
<div style="display:grid; grid-template-columns:1fr 1fr; gap:10px">
<div style="background:#2b5916; color:#fff; border-radius:14px; padding:14px"><div style="font-size:20px">🎙</div><div style="font-weight:800; font-size:13px; margin-top:6px">Live</div><div style="font-size:11px; color:#daefd0">Bid in real time</div></div>
<div style="background:#c4952a; color:#fff; border-radius:14px; padding:14px"><div style="font-size:20px">🏷️</div><div style="font-weight:800; font-size:13px; margin-top:6px">Silent</div><div style="font-size:11px; color:#faeed0">Browse &amp; bid</div></div>
<div style="background:#fff; border:1px solid #e5e7eb; border-radius:14px; padding:14px"><div style="font-size:20px">📋</div><div style="font-weight:800; font-size:13px; margin-top:6px">My Bids</div><div style="font-size:11px; color:#9ca3af">3 active</div></div>
<div style="background:#fff; border:1px solid #e5e7eb; border-radius:14px; padding:14px"><div style="font-size:20px">💳</div><div style="font-weight:800; font-size:13px; margin-top:6px">Checkout</div><div style="font-size:11px; color:#9ca3af">Pay &amp; collect</div></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</header>
<!-- ═══════════════════════ THE PROBLEM ═══════════════════════ -->
<section>
<div class="wrap">
<span class="eyebrow">The problem</span>
<h2 class="section-title">Off-the-shelf auction tools weren't built for Storybook Farm.</h2>
<p class="section-lead">
Existing platforms charge per-event SaaS fees, hold donor data on someone else's
cloud, and quietly assume the venue has reliable Wi-Fi. None of those are safe
bets for a rural fundraiser where a flaky uplink could end the night early —
and where each lost bid is real money that didn't reach the kids.
</p>
<div class="features" style="margin-top:28px">
<div class="feature">
<div class="feature-icon" style="background:#fde6d2; color:#b45309">⚠️</div>
<h3>Bid loss when Wi-Fi blinks</h3>
<p>SaaS auction tools hard-fail when their server is unreachable. A 60-second outage during a $5,000 lot is a $5,000 problem.</p>
</div>
<div class="feature">
<div class="feature-icon" style="background:#fde6d2; color:#b45309">💸</div>
<h3>Per-event fees add up</h3>
<p>Most platforms take 13% of every transaction plus monthly minimums. Over a few galas, that pays for an entire system you'd own.</p>
</div>
<div class="feature">
<div class="feature-icon" style="background:#fde6d2; color:#b45309">🔒</div>
<h3>Donor data lives elsewhere</h3>
<p>Bidder lists, contact info, and giving history sit in a vendor's database. Storybid keeps every record on a server you control.</p>
</div>
</div>
</div>
</section>
<!-- ═══════════════════════ WHAT IT DOES ═══════════════════════ -->
<section id="what">
<div class="wrap">
<span class="eyebrow">What it does today</span>
<h2 class="section-title">Everything you need to run gala night, in one place.</h2>
<p class="section-lead">
One platform covers the live auctioneer call, the silent auction catalog,
paddle-raise / fund-a-need, check-in, payment, and end-of-night reporting.
Guests use their phones — no app store, no downloads.
</p>
<div class="features">
<div class="feature">
<span class="feature-tag tag-shipped">Shipped</span>
<div class="feature-icon">🎙</div>
<h3>Live auction console</h3>
<p>Auctioneer-controlled increments, single-tap bid button on every phone, going-once / going-twice / sold workflow with winner confirmation.</p>
</div>
<div class="feature gold">
<span class="feature-tag tag-progress">In progress</span>
<div class="feature-icon">🏷️</div>
<h3>Silent auction catalog</h3>
<p>Real-time current high bid, countdown timers, configurable section closing, soft-close to stop sniping, and instant outbid alerts.</p>
</div>
<div class="feature">
<span class="feature-tag tag-shipped">Shipped</span>
<div class="feature-icon">📺</div>
<h3>Display board</h3>
<p>Big-screen view for the projector or TV with current lot, current bid, paddle number, branding, and an optional fundraising thermometer.</p>
</div>
<div class="feature gold">
<span class="feature-tag tag-planned">Planned</span>
<div class="feature-icon">💖</div>
<h3>Fund-a-need / paddle raise</h3>
<p>Donation tiers with live totals on screen, suggested amounts, and one-tap commitment from any guest's phone.</p>
</div>
<div class="feature">
<span class="feature-tag tag-planned">Planned</span>
<div class="feature-icon">🪪</div>
<h3>QR check-in &amp; digital paddles</h3>
<p>Guests scan in at the door, get a digital paddle on their phone, and connect their card for fast checkout when the night ends.</p>
</div>
<div class="feature gold">
<span class="feature-tag tag-planned">Planned</span>
<div class="feature-icon">💳</div>
<h3>Stripe checkout &amp; receipts</h3>
<p>End-of-night payment, saved cards, donor receipts, and a clean audit trail for accounting and donor acknowledgment.</p>
</div>
<div class="feature">
<span class="feature-tag tag-shipped">Shipped</span>
<div class="feature-icon">🛠️</div>
<h3>Spotter mode</h3>
<p>Volunteers can enter floor bids by paddle number from their own phone — table captains and roving spotters stay synced with the auctioneer.</p>
</div>
<div class="feature gold">
<span class="feature-tag tag-shipped">Shipped</span>
<div class="feature-icon">🔐</div>
<h3>Email + SMS sign-in</h3>
<p>Magic-link email login for staff and donors who prefer it; Twilio Verify SMS OTP for guests who'd rather use their phone number.</p>
</div>
<div class="feature">
<span class="feature-tag tag-planned">Planned</span>
<div class="feature-icon">📊</div>
<h3>Reporting &amp; donor history</h3>
<p>Revenue, sell-through, bidder activity, donor giving history, and one-click exports for the accountant after the event.</p>
</div>
</div>
</div>
</section>
<!-- ═══════════════════════ RESILIENCE ═══════════════════════ -->
<section id="resilience">
<div class="wrap">
<div class="callout">
<div>
<span class="pill" style="background:rgba(236,196,94,.16); border-color:rgba(236,196,94,.4); color:var(--gold-300)"><span class="dot"></span> The differentiator</span>
<h3 style="margin-top:14px">If the internet drops mid-auction, the auction doesn't drop.</h3>
<p>
Storybid runs on a small server on-site. Phones automatically fall back from the
public address to a local hostname on the venue Wi-Fi, and any bid that can't reach
the server right away is queued on the device and synced the moment connectivity
returns — with an audit-trail tag so the bid history is never in doubt.
</p>
</div>
<div class="failover">
<div class="fo-step"><div class="n">1</div><div class="t">Public URL</div><div class="d">storybid.storybookfarm.org</div></div>
<div class="fo-step"><div class="n">2</div><div class="t">Local DNS</div><div class="d">bid.gala.lan over Wi-Fi</div></div>
<div class="fo-step"><div class="n">3</div><div class="t">Offline queue</div><div class="d">Phone holds &amp; syncs later</div></div>
</div>
</div>
</div>
</section>
<!-- ═══════════════════════ ROLES ═══════════════════════ -->
<section id="roles">
<div class="wrap">
<span class="eyebrow">Who uses it</span>
<h2 class="section-title">A focused screen for every person in the room.</h2>
<p class="section-lead">
Storybid ships separate, optimized views for each role on event night, so the
auctioneer, spotters, check-in volunteers, and the bidders all see only what
they need — no admin clutter, no accidental clicks during the call.
</p>
<div class="roles">
<div class="role"><div class="role-icon">👤</div><h4>Bidder</h4><p>Mobile-first home, live screen, silent catalog, my bids, watchlist, and checkout — all in one PWA.</p></div>
<div class="role alt"><div class="role-icon">🎙</div><h4>Auctioneer</h4><p>Lock-screen console with current lot, current bid, called amount, sold workflow, and bidder paddle.</p></div>
<div class="role"><div class="role-icon">👋</div><h4>Spotter</h4><p>Tap-paddle interface for floor volunteers to log bids by paddle number on behalf of in-room guests.</p></div>
<div class="role alt"><div class="role-icon">🪪</div><h4>Check-in staff</h4><p>QR scan, search, paddle assignment, payment-on-file confirmation — keeps the door line moving.</p></div>
<div class="role"><div class="role-icon">📺</div><h4>Display board</h4><p>Read-only big-screen view for the projector or TV with branding, current lot, and fund-a-need totals.</p></div>
<div class="role alt"><div class="role-icon">💼</div><h4>Event manager</h4><p>Admin console for events, items, bidders, donors, sponsors, increment rules, and reporting exports.</p></div>
<div class="role"><div class="role-icon">🛡️</div><h4>Org admin</h4><p>Branding, Stripe keys, SMS provider config, DNS / failover settings, role assignments, and audit log.</p></div>
<div class="role alt"><div class="role-icon">💳</div><h4>Cashier</h4><p>Staff-assisted checkout, partial settlements, receipt printing, and end-of-night reconciliation.</p></div>
</div>
</div>
</section>
<!-- ═══════════════════════ SCREENS ═══════════════════════ -->
<section id="screens">
<div class="wrap">
<span class="eyebrow">Screen previews</span>
<h2 class="section-title">A peek at the surfaces that matter most.</h2>
<p class="section-lead">
These are the views guests, auctioneers, and the room will actually see.
Final visuals will use Storybook Farm branding, photography, and tone of voice.
</p>
<div class="mocks">
<!-- Bidder live -->
<div class="mock">
<div class="mock-head">
<div class="mock-dots"><span></span><span></span><span></span></div>
<div class="mock-title">Bidder · Live auction</div>
<span class="mock-tag tag-shipped">Shipped</span>
</div>
<div class="mock-body live-mock">
<div class="lot">Lot 14</div>
<div class="item">Weekend at the Lake House</div>
<div class="donor">Donated by The Henderson Family</div>
<div class="badge-live">● Bidding open</div>
<div class="label">Current bid</div>
<div class="bid">$3,200</div>
<button class="bid-btn">Bid $3,400</button>
</div>
</div>
<!-- Silent auction -->
<div class="mock">
<div class="mock-head">
<div class="mock-dots"><span></span><span></span><span></span></div>
<div class="mock-title">Bidder · Silent auction</div>
<span class="mock-tag tag-progress">In progress</span>
</div>
<div class="mock-body">
<div class="silent-grid">
<div class="silent-card"><div class="silent-img">🎨</div><div class="silent-meta"><div class="t">Custom Family Portrait</div><div class="b">$425 · top bid</div><div class="end">Closes in 22:14</div></div></div>
<div class="silent-card alt"><div class="silent-img">🍷</div><div class="silent-meta"><div class="t">Sonoma Wine Trio</div><div class="b">$280 · top bid</div><div class="end">Closes in 31:08</div></div></div>
<div class="silent-card warn"><div class="silent-img">🛶</div><div class="silent-meta"><div class="t">Guided Kayak Day</div><div class="b">$190 · you're top</div><div class="end">Closes in 12:45</div></div></div>
<div class="silent-card cool"><div class="silent-img">📚</div><div class="silent-meta"><div class="t">Storytime Library Set</div><div class="b">$140 · top bid</div><div class="end">Closes in 41:33</div></div></div>
</div>
</div>
</div>
<!-- Auctioneer console -->
<div class="mock">
<div class="mock-head">
<div class="mock-dots"><span></span><span></span><span></span></div>
<div class="mock-title">Staff · Auctioneer console</div>
<span class="mock-tag tag-shipped">Shipped</span>
</div>
<div class="mock-body">
<div class="console">
<div class="row">
<div class="lot-tag">Lot 14 · Active</div>
<div class="timer">⏱ 02:18 on lot</div>
</div>
<div class="item-name">Weekend at the Lake House</div>
<div class="grid">
<div class="stat"><div class="l">Current bid</div><div class="v">$3,200</div></div>
<div class="stat"><div class="l">Next call</div><div class="v">$3,400</div></div>
<div class="stat"><div class="l">High bidder</div><div class="v" style="font-size:18px">Paddle #142</div></div>
<div class="stat"><div class="l">Bidders in</div><div class="v" style="font-size:18px">7</div></div>
</div>
<div class="controls">
<div class="ctrl">+ Increment</div>
<div class="ctrl alt">Going once</div>
<div class="ctrl alt">Going twice</div>
<div class="ctrl sold">SOLD</div>
</div>
</div>
</div>
</div>
<!-- Display board -->
<div class="mock">
<div class="mock-head">
<div class="mock-dots"><span></span><span></span><span></span></div>
<div class="mock-title">Room · Display board</div>
<span class="mock-tag tag-shipped">Shipped</span>
</div>
<div class="mock-body">
<div class="board">
<div class="org">Storybook Farm Charity Gala</div>
<div class="item">Weekend at the Lake House</div>
<div style="font-size:11px; letter-spacing:.16em; text-transform:uppercase; color:rgba(255,255,255,.6); font-weight:700; margin-top:14px">Current bid</div>
<div class="big">$3,200</div>
<div class="paddle">High bidder · Paddle #142</div>
<div class="thermo"><div class="fill"></div></div>
<div class="thermo-label">Fund-a-Need · $34,200 of $50,000</div>
</div>
</div>
</div>
<!-- Check-in flow -->
<div class="mock">
<div class="mock-head">
<div class="mock-dots"><span></span><span></span><span></span></div>
<div class="mock-title">Door · Check-in flow</div>
<span class="mock-tag tag-planned">Planned</span>
</div>
<div class="mock-body">
<div class="checkin">
<div class="step done">
<div class="num"></div>
<div class="meta"><div class="t">Scan QR or look up by name</div><div class="d">Sarah Whitman · Table 7</div></div>
<span class="badge-status" style="background:#d1fadf; color:#107a3a">Done</span>
</div>
<div class="step done">
<div class="num"></div>
<div class="meta"><div class="t">Assign paddle</div><div class="d">Paddle #142 issued</div></div>
<span class="badge-status" style="background:#d1fadf; color:#107a3a">Done</span>
</div>
<div class="step active">
<div class="num">3</div>
<div class="meta"><div class="t">Card on file (Stripe)</div><div class="d">Tap to capture for fast checkout</div></div>
<span class="badge-status" style="background:#faeed0; color:#a37820">Now</span>
</div>
<div class="step">
<div class="num">4</div>
<div class="meta"><div class="t">Send digital paddle to phone</div><div class="d">SMS link, magic email, or QR</div></div>
<span class="badge-status" style="background:#f3f4f6; color:#6b7280">Next</span>
</div>
</div>
</div>
</div>
<!-- Reporting -->
<div class="mock">
<div class="mock-head">
<div class="mock-dots"><span></span><span></span><span></span></div>
<div class="mock-title">Admin · Live reporting</div>
<span class="mock-tag tag-planned">Planned</span>
</div>
<div class="mock-body">
<div class="report">
<div class="kpis">
<div class="kpi"><div class="l">Raised tonight</div><div class="v">$84,250</div><div class="delta">▲ on goal</div></div>
<div class="kpi"><div class="l">Sell-through</div><div class="v">87%</div><div class="delta">42 of 48 lots</div></div>
<div class="kpi"><div class="l">Active bidders</div><div class="v">128</div><div class="delta">+9 since 7pm</div></div>
<div class="kpi"><div class="l">Avg bid</div><div class="v">$310</div><div class="delta">▲ vs last yr</div></div>
</div>
<div class="chart">
<div class="bar" style="height:24%"></div>
<div class="bar" style="height:38%"></div>
<div class="bar" style="height:55%"></div>
<div class="bar" style="height:62%"></div>
<div class="bar" style="height:74%"></div>
<div class="bar" style="height:88%"></div>
<div class="bar" style="height:96%"></div>
<div class="bar" style="height:80%"></div>
<div class="bar" style="height:65%"></div>
</div>
<div style="font-size:10px; color:var(--mute); margin-top:6px; text-align:center">Hourly bid volume · 6pm10pm</div>
</div>
</div>
</div>
<!-- Notifications -->
<div class="mock">
<div class="mock-head">
<div class="mock-dots"><span></span><span></span><span></span></div>
<div class="mock-title">Bidder · Outbid &amp; alerts</div>
<span class="mock-tag tag-planned">Planned</span>
</div>
<div class="mock-body">
<div class="notif">
<div class="notif-item warn">
<div class="notif-icon">⚠️</div>
<div class="notif-meta">
<div class="t">You've been outbid on Sonoma Wine Trio</div>
<div class="d">New top bid is $300. Tap to bid $320.</div>
<div class="time">Just now · push</div>
</div>
</div>
<div class="notif-item success">
<div class="notif-icon">🏆</div>
<div class="notif-meta">
<div class="t">You won Storytime Library Set</div>
<div class="d">Final bid $140. We'll charge your card on file.</div>
<div class="time">2 min ago · email + SMS</div>
</div>
</div>
<div class="notif-item">
<div class="notif-icon"></div>
<div class="notif-meta">
<div class="t">Silent auction closes in 5 min</div>
<div class="d">3 lots in your watchlist still open.</div>
<div class="time">8 min ago · in-app</div>
</div>
</div>
</div>
</div>
</div>
<!-- Fund-a-Need -->
<div class="mock">
<div class="mock-head">
<div class="mock-dots"><span></span><span></span><span></span></div>
<div class="mock-title">Bidder · Fund-a-Need</div>
<span class="mock-tag tag-planned">Planned</span>
</div>
<div class="mock-body" style="text-align:center">
<div style="font-size:11px; letter-spacing:.16em; text-transform:uppercase; color:#9ca3af; font-weight:700">Paddle Raise</div>
<div style="font-size:20px; font-weight:900; color:var(--green-800); margin-top:4px">Send a child to summer camp</div>
<div style="display:grid; grid-template-columns:1fr 1fr 1fr; gap:8px; margin:18px 0">
<button style="border:1px solid var(--green-200); background:#fff; padding:14px 8px; border-radius:12px; font-weight:800; color:var(--green-700)">$50</button>
<button style="border:1px solid var(--green-200); background:#fff; padding:14px 8px; border-radius:12px; font-weight:800; color:var(--green-700)">$100</button>
<button style="border:1px solid var(--green-200); background:#fff; padding:14px 8px; border-radius:12px; font-weight:800; color:var(--green-700)">$250</button>
<button style="border:none; background:var(--green-700); color:#fff; padding:14px 8px; border-radius:12px; font-weight:800; box-shadow:var(--bid-shadow)">$500</button>
<button style="border:1px solid var(--green-200); background:#fff; padding:14px 8px; border-radius:12px; font-weight:800; color:var(--green-700)">$1,000</button>
<button style="border:1px solid var(--green-200); background:#fff; padding:14px 8px; border-radius:12px; font-weight:800; color:var(--green-700)">Other</button>
</div>
<div style="background:var(--green-50); border-radius:12px; padding:12px; font-size:13px; color:var(--green-800); font-weight:700">
68% of $50,000 goal · 142 donors so far
</div>
</div>
</div>
</div>
</div>
</section>
<!-- ═══════════════════════ PLANNED FEATURES ═══════════════════════ -->
<section id="planned">
<div class="wrap">
<span class="eyebrow">What's coming</span>
<h2 class="section-title">Planned features that aren't shipped yet.</h2>
<p class="section-lead">
The four remaining build phases turn what's already running into a complete
event-night system. Here's the work the organization is being asked to greenlight.
</p>
<div class="future-grid">
<div class="future-card">
<div class="head"><div class="icon">🏷️</div><h4>Silent auction engine</h4></div>
<p>Catalog with item pages, current high bid, minimum next bid, configurable closing windows by section/table/category, and bidder watchlists.</p>
<span class="when">Phase 3 · in flight</span>
</div>
<div class="future-card">
<div class="head"><div class="icon"></div><h4>Soft-close / auto-extend</h4></div>
<p>If a valid bid lands in the last 60 seconds, the lot extends automatically — preventing last-second sniping and matching common silent-auction practice.</p>
<span class="when">Phase 3</span>
</div>
<div class="future-card">
<div class="head"><div class="icon">🔔</div><h4>Outbid alerts</h4></div>
<p>Channel-aware notifications: in-app banner, web push where supported, email confirmations, and SMS for time-sensitive outbid and checkout-ready alerts.</p>
<span class="when">Phase 3</span>
</div>
<div class="future-card">
<div class="head"><div class="icon">📲</div><h4>PWA install &amp; offline shell</h4></div>
<p>Service worker caches the app shell so guests can keep browsing even with no signal; a Dexie/IndexedDB outbox queues bids and syncs them on reconnect.</p>
<span class="when">Phase 4</span>
</div>
<div class="future-card">
<div class="head"><div class="icon">📡</div><h4>FQDN → LAN failover</h4></div>
<p>Connection manager attempts the public URL first, then falls back to the UniFi local DNS hostname on the venue Wi-Fi. Origin of every bid is tagged for the audit trail.</p>
<span class="when">Phase 4</span>
</div>
<div class="future-card">
<div class="head"><div class="icon">📜</div><h4>Audit log with origin tagging</h4></div>
<p>Every bid records device id, client timestamp, server-received timestamp, sequence number, and origin (public, local DNS, local IP, offline queue) for full traceability.</p>
<span class="when">Phase 4</span>
</div>
<div class="future-card">
<div class="head"><div class="icon">📥</div><h4>CSV bidder import</h4></div>
<p>Bulk-load donor lists from previous events; assign paddle numbers, table seating, and preferred contact channels in one upload.</p>
<span class="when">Phase 5</span>
</div>
<div class="future-card">
<div class="head"><div class="icon">🪪</div><h4>QR check-in &amp; digital paddles</h4></div>
<p>Guests pre-register, get a QR code by email, and scan in at the door. Digital paddle appears on their phone — no laminated cards to print or lose.</p>
<span class="when">Phase 5</span>
</div>
<div class="future-card">
<div class="head"><div class="icon">💖</div><h4>Fund-a-Need / paddle raise</h4></div>
<p>Donation tier setup, live total updates on the display board, and one-tap commitment from any guest's phone. Optional matching-gift tracking.</p>
<span class="when">Phase 5</span>
</div>
<div class="future-card">
<div class="head"><div class="icon">💳</div><h4>Stripe checkout</h4></div>
<p>Payment Element + saved cards, end-of-night batch charge, staff-assisted cashier station, immediate pay-now for buy-it-now items, and partial settlement.</p>
<span class="when">Phase 5</span>
</div>
<div class="future-card">
<div class="head"><div class="icon">🧾</div><h4>Donor receipts &amp; acknowledgments</h4></div>
<p>Auto-generated tax-deductible receipts with the fair-market-value split, plus a personalized donor thank-you email after the event.</p>
<span class="when">Phase 5</span>
</div>
<div class="future-card">
<div class="head"><div class="icon">📊</div><h4>Reporting &amp; exports</h4></div>
<p>Revenue, sell-through, top donors, fund-a-need totals, and bidder activity. CSV exports for accounting and the donor-relationship system.</p>
<span class="when">Phase 5</span>
</div>
<div class="future-card">
<div class="head"><div class="icon">🎁</div><h4>Buy-it-now &amp; sponsorships</h4></div>
<p>Fixed-price offers (raffle tickets, dinner add-ons, sponsorship packages) sold straight from the catalog without going through the auction flow.</p>
<span class="when">Phase 5</span>
</div>
<div class="future-card">
<div class="head"><div class="icon">🖼️</div><h4>Rich item media</h4></div>
<p>Multiple images, video walkthroughs, donor-provided documents, and external embeds for unique lots like vacation packages or experience auctions.</p>
<span class="when">Phase 5</span>
</div>
<div class="future-card">
<div class="head"><div class="icon"></div><h4>Accessibility hardening</h4></div>
<p>High-contrast mode for low-light ballrooms, larger touch targets, screen-reader audit, and noise-aware notifications for hearing-impaired guests.</p>
<span class="when">Phase 6</span>
</div>
<div class="future-card">
<div class="head"><div class="icon">🏋️</div><h4>Load testing &amp; backups</h4></div>
<p>Realistic bidder-count load tests, automated nightly backups, point-in-time restore, and a tested disaster-recovery procedure for event night.</p>
<span class="when">Phase 6</span>
</div>
<div class="future-card">
<div class="head"><div class="icon">📘</div><h4>Operator manual</h4></div>
<p>Printable preflight checklist, per-role staff runbooks, and a one-page "what to do if X happens" sheet for event-night volunteers.</p>
<span class="when">Phase 6</span>
</div>
<div class="future-card">
<div class="head"><div class="icon">🌐</div><h4>UniFi event-network guide</h4></div>
<p>Documented SSID setup, local DNS records, device prioritization, and on-site UPS recommendations so the venue Wi-Fi is bulletproof.</p>
<span class="when">Phase 6</span>
</div>
</div>
</div>
</section>
<!-- ═══════════════════════ TECH / ROADMAP ═══════════════════════ -->
<section id="roadmap">
<div class="wrap">
<span class="eyebrow">Build plan</span>
<h2 class="section-title">Where the build stands today.</h2>
<p class="section-lead">
Storybid is built in six clear phases. The foundation and live auction core
are already complete. The remaining four phases finish silent auction,
offline resilience, event-night ops, and hardening.
</p>
<div class="roadmap">
<div class="phase">
<div class="phase-head"><span class="phase-num">Phase 1</span><span class="status done">Done</span></div>
<h4>Foundation</h4>
<p>Monorepo, Docker deploy, organization &amp; event models, admin shell, authentication.</p>
<ul>
<li>React + Vite client scaffolded</li>
<li>Express + Prisma server</li>
<li>Magic-link &amp; SMS OTP login</li>
<li>Admin shell &amp; routing</li>
</ul>
</div>
<div class="phase">
<div class="phase-head"><span class="phase-num">Phase 2</span><span class="status done">Done</span></div>
<h4>Live auction core</h4>
<p>Auctioneer console, bidder live screen, increment engine, spotter mode, display board.</p>
<ul>
<li>Socket.io live bid stream</li>
<li>Going-once/twice/sold workflow</li>
<li>Spotter paddle-entry view</li>
<li>Big-screen display board</li>
</ul>
</div>
<div class="phase">
<div class="phase-head"><span class="phase-num">Phase 3</span><span class="status now">In flight</span></div>
<h4>Silent auction</h4>
<p>Catalog, countdowns, soft-close, outbid notifications, watchlists.</p>
<ul>
<li>Item pages with current high bid</li>
<li>Per-section closing windows</li>
<li>Soft-close auto-extend</li>
<li>Push / email / SMS outbid alerts</li>
</ul>
</div>
<div class="phase">
<div class="phase-head"><span class="phase-num">Phase 4</span><span class="status todo">Next</span></div>
<h4>Offline resilience</h4>
<p>PWA shell, IndexedDB outbox, FQDN-to-LAN failover, sync engine, audit tagging.</p>
<ul>
<li>Workbox service worker</li>
<li>Dexie outbox queue</li>
<li>Local DNS failover</li>
<li>Origin-tagged audit log</li>
</ul>
</div>
<div class="phase">
<div class="phase-head"><span class="phase-num">Phase 5</span><span class="status todo">Next</span></div>
<h4>Event ops &amp; checkout</h4>
<p>QR check-in, digital paddles, fund-a-need module, Stripe checkout, receipts.</p>
<ul>
<li>QR registration + door scan</li>
<li>Fund-a-Need / paddle raise</li>
<li>Stripe Payment Element</li>
<li>Tax-receipt email automation</li>
</ul>
</div>
<div class="phase">
<div class="phase-head"><span class="phase-num">Phase 6</span><span class="status todo">Next</span></div>
<h4>Hardening &amp; polish</h4>
<p>Load test, accessibility, backups, runbooks, operator manual, event-day checklist.</p>
<ul>
<li>Realistic load testing</li>
<li>Backups + restore drill</li>
<li>Accessibility audit</li>
<li>Printed staff runbook</li>
</ul>
</div>
</div>
<h3 style="font-size:var(--fs-h3); margin-top:48px; color:var(--green-800)">Technical stack</h3>
<div class="stack">
<div class="stack-row"><div class="l">Layer</div><div class="v">Choice</div></div>
<div class="stack-row"><div class="l">Client</div><div class="v">React 18 + TypeScript + Vite + Tailwind, installable as a PWA</div></div>
<div class="stack-row"><div class="l">Real-time</div><div class="v">Socket.io for live bid streams and silent auction outbid alerts</div></div>
<div class="stack-row"><div class="l">Server</div><div class="v">Node.js + Express + TypeScript, Prisma ORM, PostgreSQL</div></div>
<div class="stack-row"><div class="l">Offline</div><div class="v">Workbox service worker + Dexie/IndexedDB outbox, LAN failover</div></div>
<div class="stack-row"><div class="l">Auth</div><div class="v">Email magic links + Twilio Verify SMS OTP for guest bidders</div></div>
<div class="stack-row"><div class="l">Payments</div><div class="v">Stripe Payment Element — no card data ever touches our server</div></div>
<div class="stack-row"><div class="l">Hosting</div><div class="v">Docker Compose on a self-hosted server with on-site failover</div></div>
<div class="stack-row"><div class="l">Network</div><div class="v">UniFi local DNS records + dedicated event SSID with battery backup</div></div>
</div>
</div>
</section>
<!-- ═══════════════════════ DATA / SECURITY ═══════════════════════ -->
<section>
<div class="wrap">
<span class="eyebrow">Data &amp; security</span>
<h2 class="section-title">Donor data stays with Storybook Farm.</h2>
<p class="section-lead">
Because Storybid is self-hosted, every bidder record, donation, and audit-log
entry lives on a server the organization controls. The only third parties in
the loop are Stripe (payments) and the SMS provider for OTP — both narrowly scoped.
</p>
<div class="features" style="margin-top:28px">
<div class="feature">
<div class="feature-icon">🔐</div>
<h3>PCI-light by design</h3>
<p>Stripe Payment Element handles card entry in an iframe Stripe owns. Card numbers never reach our database, our server, or our logs.</p>
</div>
<div class="feature gold">
<div class="feature-icon">📜</div>
<h3>Append-only audit log</h3>
<p>Every bid, status change, and admin action is logged with timestamp, actor, device id, and origin tag. Disputes get a clean, defensible record.</p>
</div>
<div class="feature">
<div class="feature-icon">👥</div>
<h3>Role-based access</h3>
<p>Admin, event manager, auctioneer, spotter, check-in, cashier, and bidder each see only their own surface — accidental misclicks during the call are nearly impossible.</p>
</div>
<div class="feature gold">
<div class="feature-icon">🛡️</div>
<h3>Server-side bid validation</h3>
<p>The server is the source of truth for accepted bids. Rate-limiting, increment validation, and signed tokens protect against spoofed clients and double-submits.</p>
</div>
<div class="feature">
<div class="feature-icon">💾</div>
<h3>Backups you actually own</h3>
<p>Nightly database snapshots to a location of the organization's choosing, plus a tested restore procedure documented in the operator manual.</p>
</div>
<div class="feature gold">
<div class="feature-icon">🏠</div>
<h3>Self-hosted on Docker</h3>
<p>One <code>docker compose up</code> on Unraid or a small Linux VM. No vendor lock-in, no monthly minimums, no surprise renewals.</p>
</div>
</div>
</div>
</section>
<!-- ═══════════════════════ TIMELINE ═══════════════════════ -->
<section>
<div class="wrap">
<span class="eyebrow">Indicative timeline</span>
<h2 class="section-title">From approval to gala-ready in four working blocks.</h2>
<p class="section-lead">
Below is a rough cadence assuming approval is granted now. Exact dates depend
on event date and Storybook Farm's preferred review checkpoints; this is a
planning frame, not a contract.
</p>
<div class="roadmap" style="margin-top:24px">
<div class="phase">
<div class="phase-head"><span class="phase-num">Block A</span><span class="status now">Weeks 13</span></div>
<h4>Finish silent auction</h4>
<p>Phase 3 wraps: catalog, soft-close, outbid alerts, watchlists. First end-to-end staff demo at week 3.</p>
</div>
<div class="phase">
<div class="phase-head"><span class="phase-num">Block B</span><span class="status todo">Weeks 46</span></div>
<h4>Offline &amp; LAN failover</h4>
<p>Phase 4: PWA shell, outbox queue, FQDN→LAN failover, simulated internet-drop drill recorded for review.</p>
</div>
<div class="phase">
<div class="phase-head"><span class="phase-num">Block C</span><span class="status todo">Weeks 710</span></div>
<h4>Check-in &amp; checkout</h4>
<p>Phase 5: QR check-in, fund-a-need, Stripe end-to-end, donor receipts, reporting exports.</p>
</div>
<div class="phase">
<div class="phase-head"><span class="phase-num">Block D</span><span class="status todo">Weeks 1112</span></div>
<h4>Dress rehearsal &amp; hardening</h4>
<p>Phase 6: load test, accessibility pass, backup/restore drill, printed runbook, full mock-event night.</p>
</div>
</div>
</div>
</section>
<!-- ═══════════════════════ FAQ ═══════════════════════ -->
<section>
<div class="wrap">
<span class="eyebrow">Questions the board usually asks</span>
<h2 class="section-title">FAQ</h2>
<div class="faq">
<details class="faq-item">
<summary>What does it cost the organization to run?</summary>
<p>The software itself has no per-event fee. Running costs are the small server (a Linux VM or Unraid host the org already owns), Stripe's standard 2.9% + 30¢ on each charge, and a few dollars per event in Twilio SMS for guest sign-in. There are no SaaS subscriptions, seat licenses, or transaction surcharges from us.</p>
</details>
<details class="faq-item">
<summary>What happens if the venue Wi-Fi has no internet at all?</summary>
<p>The on-site server keeps running, the local Wi-Fi keeps working, and phones automatically fall back from the public URL to the local DNS hostname. The auction continues normally — guests can still bid, the auctioneer can still call, and the display board still updates. When the WAN comes back, the server pushes the night's data to its public state and any device-side queued bids sync up.</p>
</details>
<details class="faq-item">
<summary>Do guests need to download an app?</summary>
<p>No. Storybid is a PWA — a website the phone treats like an app. Guests scan a QR code or follow an SMS link, the browser opens, and that's it. They can optionally tap "Add to Home Screen" to get an icon, but it's not required.</p>
</details>
<details class="faq-item">
<summary>How do guests log in?</summary>
<p>Two options: an email magic link (we send a one-tap login link to the email on file), or an SMS one-time code via Twilio Verify. Donors and staff who use Storybid often will tend toward email; walk-up guests usually prefer SMS. No passwords to manage either way.</p>
</details>
<details class="faq-item">
<summary>Can we run more than one event a year?</summary>
<p>Yes. The platform is built around one organization with many events over time — gala night, summer auction, online-only catalogs, sponsorship campaigns. Donor and item history follow the organization, so year-over-year reporting is built in.</p>
</details>
<details class="faq-item">
<summary>What about disputes — "I bid first, the system didn't take it"?</summary>
<p>Every bid attempt is logged with the device id, the client timestamp, the server-received timestamp, a client sequence number, and an origin tag (public URL, local DNS, local IP, or offline queue). If a guest disputes a result, the audit log shows exactly when their device sent the bid, when the server saw it, and how it was processed.</p>
</details>
<details class="faq-item">
<summary>Where does donor data live, and who can see it?</summary>
<p>Everything lives on Storybook Farm's own server. Stripe sees only the data needed to charge a card (name, email, amount). Twilio sees only the phone number for OTP. Storybid itself isn't a service — there's no central vendor with a copy of your donor list.</p>
</details>
<details class="faq-item">
<summary>Is this ready for the next gala?</summary>
<p>Phases 1 and 2 are live and demo-able now. The remaining four phases (silent auction, offline resilience, check-in &amp; checkout, hardening) are scoped and sequenced. Approval today puts the system on track for the next major gala with a full dress rehearsal beforehand.</p>
</details>
</div>
</div>
</section>
<!-- ═══════════════════════ APPROVAL ═══════════════════════ -->
<section id="approval">
<div class="wrap">
<div class="approval">
<div>
<span class="pill" style="background:rgba(43,89,22,.12); border-color:rgba(43,89,22,.3); color:var(--green-700)">For board &amp; staff review</span>
<h3 style="margin-top:14px">Ready to greenlight Storybid?</h3>
<p>
We're asking the organization to approve the direction shown here so the
remaining phases — silent auction, offline resilience, check-in &amp; checkout,
and hardening — can be scheduled and finished in time for gala night.
</p>
<div class="cta-row" style="margin-top:22px">
<a class="btn btn-gold" href="mailto:?subject=Storybid%20approval">Approve &amp; proceed</a>
<a class="btn" style="background:#fff; color:var(--green-700); border:1px solid var(--green-200)" href="#screens">Review screens again</a>
<a class="btn" style="background:#fff; color:var(--green-700); border:1px solid var(--green-200)" href="#planned">See planned features</a>
</div>
</div>
<ul class="checklist">
<li><div class="check"></div><div><div class="t">Brand fit</div><div class="d">Storybook Farm green &amp; gold, clean and donor-appropriate.</div></div></li>
<li><div class="check"></div><div><div class="t">Both auction modes</div><div class="d">Live + silent + fund-a-need in one event, one app.</div></div></li>
<li><div class="check"></div><div><div class="t">Resilient to bad Wi-Fi</div><div class="d">Local server, LAN failover, offline bid queue with audit trail.</div></div></li>
<li><div class="check"></div><div><div class="t">Self-hosted &amp; owned</div><div class="d">No per-event SaaS fees; data stays with the organization.</div></div></li>
<li><div class="check"></div><div><div class="t">PCI-light payments</div><div class="d">Stripe handles cards; we never store card data.</div></div></li>
<li><div class="check"></div><div><div class="t">Year-over-year history</div><div class="d">Donor records and item history follow the organization across events.</div></div></li>
</ul>
</div>
</div>
</section>
<footer>
<div class="wrap">
<div class="brand"><div class="brand-mark">SB</div><div>STORYBID</div></div>
<div>Storybid · A self-hosted charity auction platform built for Storybook Farm.</div>
<div style="margin-top:6px; opacity:.6">This is a preview document for organizational approval — not a live application.</div>
</div>
</footer>
</body>
</html>