Files
967e81d33f docs(prd): learner-first mobile app PRD and interactive prototypes (#916)
* docs(prd): add learner-first mobile app PRD and interactive prototypes

Draft the ClassroomIO iOS/Android companion as a learner-first product
(attention home, compliance wallet, offline content, push on top of
prd/notification-system) with clickable HTML prototypes as the UX source
of truth.

Co-authored-by: Rotimi Best <[email protected]>

* style(prd): quiet mobile app prototype colours to match @cio/ui

Replace filled red/orange/green surfaces with the dashboard Badge and Alert recipes: 10% tint chips, card-background alerts, and default rows. Keep destructive fills only for actual destroy actions.

Co-authored-by: Rotimi Best <[email protected]>

---------

Co-authored-by: Cursor Agent <[email protected]>
Co-authored-by: Rotimi Best <[email protected]>
2026-08-18 12:02:35 +01:00

204 lines
11 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Sign in — ClassroomIO Mobile</title>
<link href="https://fonts.googleapis.com/css2?family=Geist:[email protected]&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="app-theme.css" />
<script>
if (localStorage.cioTheme === 'dark' || (!localStorage.cioTheme && matchMedia('(prefers-color-scheme: dark)').matches))
document.documentElement.classList.add('dark');
</script>
</head>
<body>
<header class="proto-head">
<a class="back" href="index.html"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" width="14" height="14"><path d="m15 18-6-6 6-6"/></svg> All screens</a>
<span class="eyebrow">Learner · Entry</span>
<h1>Sign in &amp; organization</h1>
<p>
Email/password, Google, and enterprise SSO all run through Better Auth's Expo client — the session cookie lives in
<code>expo-secure-store</code> and OAuth returns through the <code>classroomio://</code> deep link. Multi-org accounts
pick an organization, which sets the <code>cio-org-id</code> header for every later request and re-themes the app.
Self-hosted installs point the same binary at their own API and skip the picker.
</p>
<div class="proto-links">
<a href="home.html">Next: Home →</a>
<a href="profile.html">Org switcher (Profile)</a>
<a href="downloads.html">Offline entry</a>
</div>
</header>
<div class="stage">
<!-- 1. SIGN IN -->
<figure class="device">
<div class="phone">
<div class="phone-inner">
<div class="notch"></div>
<div class="statusbar" data-status="9:41"></div>
<div class="screen centered">
<div style="text-align: center; margin-bottom: 26px">
<div class="avatar brand lg" style="margin: 0 auto 14px; width: 60px; height: 60px; border-radius: 16px; font-size: 24px">C</div>
<div style="font-size: 21px; font-weight: 600; letter-spacing: -0.02em">Welcome back</div>
<div class="text-muted" style="font-size: 13.5px; margin-top: 4px">Sign in to continue your training</div>
</div>
<div class="field">
<label class="label">Work email</label>
<input class="input" type="email" value="[email protected]" readonly />
</div>
<div class="field">
<label class="label">Password</label>
<input class="input" type="password" value="············" readonly />
</div>
<button class="btn btn-primary btn-lg btn-block" style="margin-top: 4px">Sign in</button>
<div class="row" style="margin: 18px 0 16px">
<div class="divider" style="flex: 1; margin: 0"></div>
<span class="text-muted" style="font-size: 11.5px">OR</span>
<div class="divider" style="flex: 1; margin: 0"></div>
</div>
<div class="stack">
<button class="btn btn-outline btn-lg btn-block">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="10"/><path d="M12 8v8M8 12h8"/></svg>
Continue with Google
</button>
<button class="btn btn-outline btn-lg btn-block">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
Sign in with SSO
</button>
</div>
<div style="text-align: center; margin-top: 22px">
<a class="text-primary" style="font-size: 13px; font-weight: 500">Forgot password?</a>
</div>
<div style="text-align: center; margin-top: 14px">
<a class="text-muted" style="font-size: 12px">Use a custom server</a>
<div class="hint" style="margin-top: 3px">Self-hosted ClassroomIO</div>
</div>
</div>
<div class="home-bar"></div>
</div>
</div>
<figcaption><b>Sign in</b>Three auth paths in one screen. "Use a custom server" is how a self-hosted deployment points the same app at its own API.</figcaption>
</figure>
<!-- 2. ORG PICKER -->
<figure class="device">
<div class="phone">
<div class="phone-inner">
<div class="notch"></div>
<div class="statusbar" data-status="9:42"></div>
<div class="navbar">
<div>
<div class="title">Choose organization</div>
<div class="sub">You're a member of 3 academies</div>
</div>
</div>
<div class="screen">
<div class="stack">
<a class="lrow current" href="home.html">
<div class="avatar brand" style="background: oklch(0.488 0.243 264.376)">NL</div>
<div class="grow">
<div class="ltitle">Northwind Logistics</div>
<div class="lmeta"><span class="badge badge-primary-soft">Learner</span><span>4 courses in progress</span></div>
</div>
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
</a>
<a class="lrow" href="tutor-grading.html">
<div class="avatar brand" style="background: oklch(0.424 0.199 265.638)">AH</div>
<div class="grow">
<div class="ltitle">Acme Health Academy</div>
<div class="lmeta"><span class="badge badge-success-soft">Tutor</span><span>12 submissions to grade</span></div>
</div>
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
</a>
<a class="lrow" href="home.html">
<div class="avatar brand" style="background: oklch(0.546 0.245 262.881)">MP</div>
<div class="grow">
<div class="ltitle">Meridian Partner Network</div>
<div class="lmeta"><span class="badge badge-primary-soft">Learner</span><span>Certification expires in 21 days</span></div>
</div>
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
</a>
</div>
<div class="banner banner-muted" style="margin-top: 18px">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4M12 8h.01"/></svg>
<div>
<div class="bd" style="color: var(--muted-foreground)">
Each organization keeps its own courses, branding, and records. You can switch any time from your profile.
</div>
</div>
</div>
</div>
<div class="home-bar"></div>
</div>
</div>
<figcaption><b>Organization picker</b>Only for multi-org accounts. The choice sets <code>cio-org-id</code> and re-themes the app; single-org and self-hosted users never see it.</figcaption>
</figure>
<!-- 3. BIOMETRIC + OFFLINE LAUNCH -->
<figure class="device">
<div class="phone">
<div class="phone-inner">
<div class="notch"></div>
<div class="statusbar" data-status="7:02" data-offline></div>
<div class="offline-strip">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M2 2 22 22M8.5 16.5a5 5 0 0 1 7 0M5 13a10 10 0 0 1 4-2.4M12 20h.01"/></svg>
You're offline — showing downloaded content
</div>
<div class="screen centered">
<div style="text-align: center">
<div
style="width: 88px; height: 88px; border-radius: 999px; margin: 0 auto 20px; display: grid; place-items: center; background: color-mix(in oklab, var(--primary) 12%, transparent); color: var(--primary)"
>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" width="40" height="40" stroke-width="1.6">
<path d="M12 11c0 3-1 5.5-2 7"/><path d="M5 11a7 7 0 0 1 12-5"/><path d="M5 15v-4"/>
<path d="M8.5 20a12 12 0 0 0 1.5-6 2 2 0 1 1 4 0 12 12 0 0 0 .5 3.5"/><path d="M19 11a7 7 0 0 0-.5-2.6"/>
<path d="M17.5 19a20 20 0 0 0 .5-5"/>
</svg>
</div>
<div style="font-size: 20px; font-weight: 600; letter-spacing: -0.02em">Unlock ClassroomIO</div>
<div class="text-muted" style="font-size: 13.5px; margin-top: 6px; line-height: 1.5; padding: 0 12px">
Use Face ID to open your saved session. Your training records stay on this device until you reconnect.
</div>
<button class="btn btn-primary btn-lg btn-block" style="margin-top: 26px">Unlock with Face ID</button>
<button class="btn btn-ghost btn-block" style="margin-top: 8px">Use password instead</button>
</div>
<a class="card card-pad row" href="downloads.html" style="margin-top: 30px; text-align: left">
<div class="media is-current">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M12 15V3M7 10l5 5 5-5"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/></svg>
</div>
<div class="grow">
<div class="ltitle">3 courses available offline</div>
<div class="lsub">Forklift Safety, Fire Safety, GDPR Essentials</div>
</div>
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
</a>
</div>
<div class="home-bar"></div>
</div>
</div>
<figcaption><b>Biometric re-entry, offline launch</b>Face ID unlocks an existing stored session — it never replaces sign-in. Launching with no network goes straight to downloaded content instead of an error.</figcaption>
</figure>
</div>
<button class="theme-toggle" title="Toggle theme" aria-label="Toggle theme">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>
</button>
<script src="proto.js"></script>
</body>
</html>