diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 00000000..fa771c2b --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,38 @@ +name: Deploy GitHub Pages + +# Publishes the static marketing site in site/ to GitHub Pages. +# Custom domain: set it under Settings → Pages → Custom domain (this +# creates the CNAME for you). No CNAME file is committed yet. + +on: + push: + branches: [main] + paths: + - "site/**" + - ".github/workflows/pages.yml" + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +# Allow one concurrent deployment; don't cancel an in-progress publish. +concurrency: + group: pages + cancel-in-progress: false + +jobs: + deploy: + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - uses: actions/checkout@v4 + - uses: actions/configure-pages@v5 + - uses: actions/upload-pages-artifact@v3 + with: + path: site + - id: deployment + uses: actions/deploy-pages@v4 diff --git a/site/.nojekyll b/site/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/site/index.html b/site/index.html new file mode 100644 index 00000000..db3ca8a2 --- /dev/null +++ b/site/index.html @@ -0,0 +1,184 @@ + + + + + + SignUpFlow — volunteer scheduling that schedules itself + + + + + + + + +
+
+ + +
+
+ +
+
+
+ Open-source · API-first +

Volunteer scheduling that schedules itself.

+

+ SignUpFlow auto-generates fair rosters for churches, sports leagues, + and non-profits — a constraint-based solver does the hard part while + volunteers self-serve open shifts and cover each other's swaps. +

+ +
+
+ +
+
+
+

Everything a roster needs, end to end

+

From the first invite to a published, calendar-synced schedule — + with the scheduling itself automated.

+
+
+
+
⚙︎
+

Auto-scheduling solver

+

A greedy heuristic solver with constraint-based optimization + builds fair schedules across roles and people automatically.

+
+
+
◷
+

Availability & time-off

+

Volunteers set time-off and recurring (RRULE) unavailability; + the solver respects every block.

+
+
+
↻
+

Self-serve & swaps

+

Volunteers claim open shifts and cover each other's swap + requests without an admin in the loop.

+
+
+
⛁
+

Teams, roles & tenancy

+

Role-based access (admin / volunteer) and strict per-org + isolation so every organization stays separate.

+
+
+
∑
+

Constraints DSL

+

Express scheduling rules — spacing, eligibility, caps — in a + compact constraint language the solver evaluates.

+
+
+
⧉
+

Calendar sync & analytics

+

Each volunteer gets an ICS subscription of published shifts, + plus volunteer and event analytics.

+
+
+
{ }
+

API-first & CLI

+

Headless by design: a full REST API plus a YAML-in / JSON-out + CLI for scripted, reproducible scheduling.

+
+
+
✉
+

Invitations & inbox

+

Token-based invites onboard people in one tap; an in-app inbox + keeps assignees notified.

+
+
+
+
+ +
+
+
+

Plans that scale with your program

+

Limits mirror the platform's enforced quotas. Pick a tier by the + size of your volunteer base.

+
+
+
+
Free
+
Get started — small teams & trials.
+
    +
  • Volunteers10
  • +
  • Events / month50
  • +
  • Storage100 MB
  • +
  • API calls / day1,000
  • +
+ Start free +
+
+
Starter
+
Growing volunteer programs.
+
    +
  • Volunteers50
  • +
  • Events / month200
  • +
  • Storage1,000 MB
  • +
  • API calls / day10,000
  • +
+ Choose Starter +
+ +
+
Enterprise
+
Large / multi-ministry. Custom terms.
+
    +
  • VolunteersUnlimited
  • +
  • Events / monthUnlimited
  • +
  • StorageUnlimited
  • +
  • API calls / dayUnlimited
  • +
+ Contact us +
+
+
+
+ +
+
+
+

Run it yourself

+

SignUpFlow is open source. Clone the repo, run the API and CLI, + and own your scheduling stack end to end.

+
+ Get started on GitHub +
+
+
+ + + + + diff --git a/site/styles.css b/site/styles.css new file mode 100644 index 00000000..0959891f --- /dev/null +++ b/site/styles.css @@ -0,0 +1,148 @@ +/* SignUpFlow landing — brand-aligned with web/static/css/styles.css. + No external assets so it works offline on GitHub Pages. */ +:root { + --bg: #F0EFEC; + --bg-soft: #FAFAF9; + --card: #FFFFFF; + --ink: #1C1917; + --ink-soft: #57534E; + --line: #E7E5E4; + --accent: #0F766E; + --accent-soft: #CCFBF1; + --accent-ink: #134E4A; + --radius: 14px; + --max: 1080px; +} +@media (prefers-color-scheme: dark) { + :root { + --bg: #09090B; --bg-soft: #09090B; --card: #18181B; + --ink: #FAFAF9; --ink-soft: #A1A1AA; --line: #27272A; + --accent-soft: #134E4A; --accent-ink: #CCFBF1; + } +} +* { box-sizing: border-box; margin: 0; padding: 0; } +html { scroll-behavior: smooth; } +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, + Helvetica, Arial, sans-serif; + background: var(--bg); color: var(--ink); + line-height: 1.6; -webkit-font-smoothing: antialiased; +} +a { color: var(--accent); text-decoration: none; } +.wrap { max-width: var(--max); margin: 0 auto; padding: 0 24px; } + +/* Header */ +header { + position: sticky; top: 0; z-index: 10; + background: color-mix(in srgb, var(--bg) 88%, transparent); + backdrop-filter: saturate(180%) blur(8px); + border-bottom: 1px solid var(--line); +} +.bar { + display: flex; align-items: center; justify-content: space-between; + height: 64px; +} +.logo { display: flex; align-items: center; gap: 10px; font-weight: 700; + font-size: 18px; color: var(--ink); } +.logo .mark { + width: 28px; height: 28px; border-radius: 8px; + background: var(--accent); color: #fff; display: grid; + place-items: center; font-size: 15px; font-weight: 800; +} +nav.links a { color: var(--ink-soft); font-size: 14px; font-weight: 500; + margin-left: 22px; } +nav.links a:hover { color: var(--accent); } + +/* Buttons */ +.btn { + display: inline-block; font-weight: 600; font-size: 15px; + padding: 12px 22px; border-radius: 999px; border: 1px solid transparent; + transition: transform .06s ease, opacity .15s ease; +} +.btn:active { transform: translateY(1px); } +.btn-primary { background: var(--accent); color: #fff; } +.btn-primary:hover { opacity: .92; } +.btn-ghost { border-color: var(--line); color: var(--ink); } +.btn-ghost:hover { border-color: var(--accent); color: var(--accent); } +.btn-sm { padding: 9px 16px; font-size: 14px; } + +/* Hero */ +.hero { padding: 84px 0 64px; text-align: center; } +.eyebrow { + display: inline-block; font-size: 13px; font-weight: 600; + letter-spacing: .04em; text-transform: uppercase; + color: var(--accent-ink); background: var(--accent-soft); + padding: 6px 14px; border-radius: 999px; margin-bottom: 22px; +} +.hero h1 { + font-size: clamp(34px, 6vw, 56px); line-height: 1.08; + letter-spacing: -.02em; max-width: 18ch; margin: 0 auto 20px; +} +.hero p.lede { + font-size: clamp(16px, 2.4vw, 20px); color: var(--ink-soft); + max-width: 60ch; margin: 0 auto 34px; +} +.cta-row { display: flex; gap: 14px; justify-content: center; + flex-wrap: wrap; } + +/* Sections */ +section { padding: 64px 0; } +.section-head { text-align: center; max-width: 56ch; margin: 0 auto 48px; } +.section-head h2 { + font-size: clamp(26px, 4vw, 38px); letter-spacing: -.02em; + margin-bottom: 12px; +} +.section-head p { color: var(--ink-soft); font-size: 17px; } + +/* Feature grid */ +.grid { + display: grid; gap: 20px; + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); +} +.card { + background: var(--card); border: 1px solid var(--line); + border-radius: var(--radius); padding: 26px; +} +.card .ico { + width: 40px; height: 40px; border-radius: 10px; + background: var(--accent-soft); color: var(--accent-ink); + display: grid; place-items: center; font-size: 19px; + margin-bottom: 16px; +} +.card h3 { font-size: 18px; margin-bottom: 8px; } +.card p { color: var(--ink-soft); font-size: 15px; } + +/* Pricing / limits */ +.tier-grid { + display: grid; gap: 20px; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); +} +.tier { + background: var(--card); border: 1px solid var(--line); + border-radius: var(--radius); padding: 26px; display: flex; + flex-direction: column; +} +.tier.featured { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); } +.tier .name { font-size: 13px; font-weight: 700; text-transform: uppercase; + letter-spacing: .05em; color: var(--accent-ink); } +.tier .blurb { color: var(--ink-soft); font-size: 14px; margin: 6px 0 18px; } +.tier ul { list-style: none; font-size: 14px; flex: 1; } +.tier li { padding: 8px 0; border-top: 1px solid var(--line); + display: flex; justify-content: space-between; gap: 12px; } +.tier li:first-child { border-top: none; } +.tier li span:first-child { color: var(--ink-soft); } +.tier li span:last-child { font-weight: 600; } + +/* Footer */ +footer { border-top: 1px solid var(--line); padding: 40px 0; + color: var(--ink-soft); font-size: 14px; } +.foot { display: flex; justify-content: space-between; flex-wrap: wrap; + gap: 16px; align-items: center; } +footer a { color: var(--ink-soft); } +footer a:hover { color: var(--accent); } + +@media (max-width: 640px) { + nav.links a:not(.btn) { display: none; } + .hero { padding: 56px 0 40px; } + section { padding: 48px 0; } +}