// signin/SignIn.jsx // Sign-in flow for the Ordentus iOS app. The server is always ordent.us, so // there is no server-URL step — the user signs straight in. Brand voice: // quiet, plain, declarative — sentence case, no marketing exuberance, // em-dashes for pivots. // // Screens: // Welcome — splash (bedtime gradient), brand mark, CTA // Credentials — username + password (keyboard up) // SigningIn — credentials, primary CTA in busy state // CredentialsError — wrong password // Tour1, Tour2, Tour3 — paginated first-run tour, 3 panels // // Each accepts dark:boolean. Renders inside an IOSDevice frame. // ──────────────────────────────────────────────────────────── // 1. WELCOME // ──────────────────────────────────────────────────────────── function Welcome({ dark = false }) { return (
{/* content */}
Ordentus
One page for your life.
Your calendars and your body, on the same page, every morning.
{/* simple value props — three plain rows */}
{[ { ic: 'calendar', t: 'Every feed, in one grid.' }, { ic: 'heart', t: 'Tonight\u2019s bedtime, worked back from tomorrow.' }, { ic: 'lock', t: 'Private by default \u2014 only you can see it.' }, ].map((r, i) => (
{r.t}
))}
); } window.Welcome = Welcome; // ──────────────────────────────────────────────────────────── // 2. CREDENTIALS — username + password (server is always ordent.us) // ──────────────────────────────────────────────────────────── function Credentials({ dark = false, state = 'idle' /* idle|busy|error */ }) { const isError = state === 'error'; const busy = state === 'busy'; return (
Back
Welcome back
Sign in.
ordent.us
Username
lukas {!isError && }
Password
•••••••• {isError && }
{isError ? (
That password isn't right. Try again — three left before lockout.
) : (
Forgot password? Session lasts 30 days
)}
); } window.Credentials = Credentials; // ──────────────────────────────────────────────────────────── // 3. FIRST-RUN TOUR — three paginated panels // ──────────────────────────────────────────────────────────── function TourPanel({ dark = false, panel = 1 }) { const panels = [ { key: 'cal', dot: 0, eyebrow: 'Calendar', title: 'Every feed, in one grid.', body: 'Subscribed iCal, manual events, your university timetable — Ordentus merges them, deduplicates, and color-codes by source.', art: , }, { key: 'health', dot: 1, eyebrow: 'Health', title: 'Tonight\u2019s bedtime — backed out from tomorrow.', body: 'Connect Apple Health and Ordentus reads sleep, heart rate and activity in the background. The bedtime number lives at the top of the page every morning.', art: , }, { key: 'shared', dot: 2, eyebrow: 'Share', title: 'Share a slice, without the noise.', body: 'Generate a link that exposes one feed or one date range. Revoke it any time. Detail-level visibility is up to you.', art: , }, ]; const p = panels[panel - 1]; return (
{/* Skip */}
Skip
{/* Art */}
{p.art}
{/* Copy */}
{p.eyebrow}
{p.title}
{p.body}
{/* Dots + CTAs */}
{[0,1,2].map(i => )}
); } window.TourPanel = TourPanel; // ── Preview illustrations (data-only, no SVG handiwork) ──────────── function PreviewCalendar({ dark }) { // A miniature day-view artboard built from real chips. return (
Today
Sunday 16
{[ { feed: 'work', t: 'Morning shift', time: '7:15 AM' }, { feed: 'work', t: 'Standup', time: '10:00 AM' }, { feed: 'uni', t: 'Algorithms', time: '2:30 PM' }, { feed: 'gym', t: 'Climbing', time: '5:00 PM' }, { feed: 'bedtime', t: '🌙 Wind down', time: '10:00 PM' }, ].map((e, i) => (
{e.t}
{e.time}
))}
); } function PreviewHealth({ dark }) { return (
Tonight's bedtime
10:30 PM
Wake 6:15 AM · 8h goal
Last night
6.2h
Debt 14d
4.5h
); } function PreviewShare({ dark }) { return (
Work · this week
Active
portal.lukas.dev/s/aB7r2…
Expires Mar 23 · 7 days Revoke
New share link
); }