* 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]>
325 lines
20 KiB
HTML
325 lines
20 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<title>Lesson player — 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 · Player</span>
|
|
<h1>Lesson player</h1>
|
|
<p>
|
|
HLS video with captions, speed, PiP, background audio, and lock-screen controls, authorized by the new
|
|
<code>GET /hls/:assetId/playback-token</code> because a native player can't carry the web's per-asset cookie.
|
|
Transcript and the AI Lesson Tutor are bottom sheets over the same screen; mark-complete queues locally when offline.
|
|
</p>
|
|
<div class="proto-links">
|
|
<a href="course.html">← Course outline</a>
|
|
<a href="exercise.html">Next: knowledge check →</a>
|
|
<a href="downloads.html">Sync queue →</a>
|
|
</div>
|
|
</header>
|
|
|
|
<div class="stage">
|
|
<!-- 1. ONLINE PLAYER -->
|
|
<figure class="device">
|
|
<div class="phone">
|
|
<div class="phone-inner">
|
|
<div class="notch"></div>
|
|
<div class="statusbar" data-status="9:46"></div>
|
|
|
|
<div class="navbar">
|
|
<a class="icon-btn plain" href="course.html" aria-label="Back"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m15 18-6-6 6-6"/></svg></a>
|
|
<div class="grow">
|
|
<div class="sub">Section 2 · Lesson 4 of 12</div>
|
|
<div class="title-sm">Pre-operation inspection</div>
|
|
</div>
|
|
<button class="icon-btn" aria-label="Lesson language"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="10"/><path d="M2 12h20M12 2a15 15 0 0 1 0 20 15 15 0 0 1 0-20"/></svg></button>
|
|
</div>
|
|
|
|
<div class="screen">
|
|
<div class="player">
|
|
<span class="pill"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg> Downloaded</span>
|
|
<div class="play"><svg viewBox="0 0 24 24" fill="currentColor" stroke="none"><path d="M8 5v14l11-7z"/></svg></div>
|
|
<div class="ctrl">
|
|
<div class="bar"><span style="width: 34%"></span></div>
|
|
<div class="meta">
|
|
<span>3:04</span>
|
|
<span style="opacity: 0.7">9:12</span>
|
|
<span class="right">
|
|
<span style="font-weight: 600">1.25x</span>
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><rect x="2" y="5" width="20" height="14" rx="2"/><path d="M7 15h4M14 15h3"/></svg>
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M5 17H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v3"/><rect x="12" y="13" width="10" height="7" rx="2"/></svg>
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M8 3H5a2 2 0 0 0-2 2v3M16 3h3a2 2 0 0 1 2 2v3M8 21H5a2 2 0 0 1-2-2v-3M16 21h3a2 2 0 0 0 2-2v-3"/></svg>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row" style="margin: 12px 0 6px">
|
|
<div class="segs">
|
|
<span class="seg done"></span><span class="seg done"></span><span class="seg done"></span><span class="seg now"></span>
|
|
<span class="seg"></span><span class="seg"></span><span class="seg"></span><span class="seg"></span>
|
|
<span class="seg"></span><span class="seg"></span><span class="seg"></span><span class="seg"></span>
|
|
</div>
|
|
<span class="text-muted tnum" style="font-size: 11.5px">4/12</span>
|
|
</div>
|
|
|
|
<h2 style="font-size: 18px; font-weight: 600; letter-spacing: -0.02em; margin: 10px 0 8px">
|
|
Pre-operation inspection checklist
|
|
</h2>
|
|
<div class="row" style="margin-bottom: 12px; gap: 6px; flex-wrap: wrap">
|
|
<span class="badge badge-outline">Video · 9 min</span>
|
|
<span class="badge badge-outline">English</span>
|
|
<span class="badge badge-success-soft">Available offline</span>
|
|
</div>
|
|
|
|
<div class="prose">
|
|
<p>
|
|
A pre-operation inspection takes about four minutes and catches most of the faults that cause workplace
|
|
incidents. Work through it in the same order every time so nothing gets skipped when you're in a hurry.
|
|
</p>
|
|
<h3>The seven checks</h3>
|
|
<ul>
|
|
<li><strong>Tyres and wheels</strong> — cuts, embedded debris, correct pressure.</li>
|
|
<li><strong>Forks</strong> — cracks at the heel, bent tips, locking pins seated.</li>
|
|
<li><strong>Hydraulic hoses</strong> — chafing, weeping seals, pooled fluid underneath.</li>
|
|
<li><strong>Mast and chains</strong> — even tension, no kinked links.</li>
|
|
<li><strong>Horn, lights, and alarm</strong> — audible and visible from 10 metres.</li>
|
|
<li><strong>Data plate</strong> — legible, and the rated capacity matches today's load.</li>
|
|
<li><strong>Seatbelt</strong> — retracts cleanly and latches.</li>
|
|
</ul>
|
|
<p>Log the result before you move the truck. An unlogged inspection counts as no inspection.</p>
|
|
</div>
|
|
|
|
<div class="stack" style="margin: 16px 0 12px">
|
|
<button class="lrow" data-sheet-open="transcript" style="width: 100%; text-align: left; cursor: pointer">
|
|
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6M8 13h8M8 17h5"/></svg></div>
|
|
<div class="grow"><div class="ltitle">Transcript</div><div class="lsub">Tap a line to jump to that moment</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>
|
|
</button>
|
|
<button class="lrow" data-sheet-open="tutor" style="width: 100%; text-align: left; cursor: pointer">
|
|
<div class="media is-current"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m12 3 1.9 5.1L19 10l-5.1 1.9L12 17l-1.9-5.1L5 10l5.1-1.9z"/></svg></div>
|
|
<div class="grow"><div class="ltitle">Ask the AI tutor</div><div class="lsub">Answers from this lesson only · 8 of 20 questions left today</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>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="divider"></div>
|
|
|
|
<a class="btn btn-primary btn-lg btn-block" href="lesson.html">
|
|
Mark complete & continue
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg>
|
|
</a>
|
|
<div class="hint" style="text-align: center">Next: Load stability and centre of gravity · 7 min</div>
|
|
</div>
|
|
|
|
<div class="scrim" data-scrim-for="transcript" style="display: none"></div>
|
|
<div class="sheet" id="transcript" style="display: none">
|
|
<div class="grabber"></div>
|
|
<div class="sheet-head">
|
|
<div class="st">Transcript</div>
|
|
<button class="icon-btn plain" data-sheet-close="transcript" style="margin-left: auto" aria-label="Close"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M18 6 6 18M6 6l12 12"/></svg></button>
|
|
</div>
|
|
<div class="sheet-body">
|
|
<div class="lrow flat"><span class="text-muted tnum" style="font-size: 11.5px; width: 34px">0:12</span><div class="grow"><div style="font-size: 13.5px">Before you touch the controls, walk the truck. Start at the front-left tyre.</div></div></div>
|
|
<div class="lrow flat" style="background: color-mix(in oklab, var(--primary) 8%, transparent); border-radius: var(--radius-md); padding: 11px 8px">
|
|
<span class="text-primary tnum" style="font-size: 11.5px; width: 34px; font-weight: 600">3:04</span>
|
|
<div class="grow"><div style="font-size: 13.5px; font-weight: 500">Look for weeping seals — a wet film on the ram is a fault, not a maybe.</div></div>
|
|
</div>
|
|
<div class="lrow flat"><span class="text-muted tnum" style="font-size: 11.5px; width: 34px">3:41</span><div class="grow"><div style="font-size: 13.5px">Chains should have even tension. If one side sags, tag the truck out.</div></div></div>
|
|
<div class="lrow flat"><span class="text-muted tnum" style="font-size: 11.5px; width: 34px">4:18</span><div class="grow"><div style="font-size: 13.5px">The data plate tells you the rated capacity at a given load centre.</div></div></div>
|
|
<div class="lrow flat"><span class="text-muted tnum" style="font-size: 11.5px; width: 34px">5:02</span><div class="grow"><div style="font-size: 13.5px">Finally, the seatbelt. It has to retract cleanly and latch first time.</div></div></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="scrim" data-scrim-for="tutor" style="display: none"></div>
|
|
<div class="sheet" id="tutor" style="display: none">
|
|
<div class="grabber"></div>
|
|
<div class="sheet-head">
|
|
<div class="media is-current" style="width: 30px; height: 30px"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m12 3 1.9 5.1L19 10l-5.1 1.9L12 17l-1.9-5.1L5 10l5.1-1.9z"/></svg></div>
|
|
<div><div class="st">AI lesson tutor</div><div class="lsub" style="margin: 0">Scoped to this lesson</div></div>
|
|
<button class="icon-btn plain" data-sheet-close="tutor" style="margin-left: auto" aria-label="Close"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M18 6 6 18M6 6l12 12"/></svg></button>
|
|
</div>
|
|
<div class="sheet-body">
|
|
<div style="display: flex; justify-content: flex-end; margin-bottom: 10px">
|
|
<div style="max-width: 78%; background: var(--primary); color: var(--primary-foreground); padding: 9px 12px; border-radius: 16px 16px 4px 16px; font-size: 13.5px">
|
|
What counts as a "weeping" seal versus normal residue?
|
|
</div>
|
|
</div>
|
|
<div style="display: flex; gap: 8px; margin-bottom: 12px">
|
|
<div class="media is-current" style="width: 26px; height: 26px"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m12 3 1.9 5.1L19 10l-5.1 1.9L12 17l-1.9-5.1L5 10l5.1-1.9z"/></svg></div>
|
|
<div style="max-width: 82%; background: var(--muted); padding: 9px 12px; border-radius: 16px 16px 16px 4px; font-size: 13.5px; line-height: 1.5">
|
|
Residue is dry and dull — old dust bonded to grease. Weeping is a wet, shiny film that reappears after you
|
|
wipe it. This lesson's rule at 3:04: if it comes back, tag the truck out.
|
|
<div class="lmeta" style="margin-top: 7px"><span class="badge badge-outline">From 3:04</span></div>
|
|
</div>
|
|
</div>
|
|
<div class="row" style="gap: 8px">
|
|
<input class="input" placeholder="Ask about this lesson…" />
|
|
<button class="icon-btn" style="width: 40px; height: 40px" aria-label="Send"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m22 2-7 20-4-9-9-4z"/></svg></button>
|
|
</div>
|
|
<div class="hint">8 of 20 questions left today · resets at midnight</div>
|
|
</div>
|
|
</div>
|
|
<div class="home-bar"></div>
|
|
</div>
|
|
</div>
|
|
<figcaption><b>Online player</b>Video, native prose, and two sheets (transcript, AI tutor) — tap either row in the live prototype to open it.</figcaption>
|
|
</figure>
|
|
|
|
<!-- 2. AI TUTOR CAP -->
|
|
<figure class="device">
|
|
<div class="phone">
|
|
<div class="phone-inner">
|
|
<div class="notch"></div>
|
|
<div class="statusbar" data-status="21:18"></div>
|
|
|
|
<div class="navbar">
|
|
<a class="icon-btn plain" href="course.html" aria-label="Back"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m15 18-6-6 6-6"/></svg></a>
|
|
<div class="grow">
|
|
<div class="sub">Section 2 · Lesson 5 of 12</div>
|
|
<div class="title-sm">Load stability</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="screen">
|
|
<div class="player">
|
|
<div class="play"><svg viewBox="0 0 24 24" fill="currentColor" stroke="none"><path d="M8 5v14l11-7z"/></svg></div>
|
|
<div class="ctrl">
|
|
<div class="bar"><span style="width: 88%"></span></div>
|
|
<div class="meta"><span>6:11</span><span style="opacity: 0.7">7:02</span></div>
|
|
</div>
|
|
</div>
|
|
<div class="prose" style="margin-top: 14px">
|
|
<p>
|
|
The load centre is the horizontal distance from the fork face to the load's centre of gravity. Every truck's
|
|
data plate is rated at a specific load centre — usually 500 mm.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="scrim"></div>
|
|
<div class="sheet" style="display: flex">
|
|
<div class="grabber"></div>
|
|
<div class="sheet-head">
|
|
<div class="media is-current" style="width: 30px; height: 30px"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m12 3 1.9 5.1L19 10l-5.1 1.9L12 17l-1.9-5.1L5 10l5.1-1.9z"/></svg></div>
|
|
<div><div class="st">AI lesson tutor</div><div class="lsub" style="margin: 0">Scoped to this lesson</div></div>
|
|
<button class="icon-btn plain" style="margin-left: auto" aria-label="Close"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M18 6 6 18M6 6l12 12"/></svg></button>
|
|
</div>
|
|
<div class="sheet-body">
|
|
<div class="empty" style="padding: 22px 12px 16px">
|
|
<div class="ei">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg>
|
|
</div>
|
|
<div class="et">You've used today's 20 questions</div>
|
|
<div class="ed">Your allowance resets at midnight. The transcript and lesson notes are still available.</div>
|
|
</div>
|
|
<button class="btn btn-outline btn-block">Open transcript instead</button>
|
|
<button class="btn btn-ghost btn-block" style="margin-top: 6px">Ask in Community</button>
|
|
<div class="hint" style="text-align: center; margin-top: 10px">
|
|
Fair-use caps are set by your organization.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="home-bar"></div>
|
|
</div>
|
|
</div>
|
|
<figcaption><b>AI tutor, cap reached</b>The fair-use limit is stated plainly with two real alternatives, not a paywall — the app never sells token packs.</figcaption>
|
|
</figure>
|
|
|
|
<!-- 3. OFFLINE / QUEUED -->
|
|
<figure class="device">
|
|
<div class="phone">
|
|
<div class="phone-inner">
|
|
<div class="notch"></div>
|
|
<div class="statusbar" data-status="18:40" 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>
|
|
Offline — progress will sync automatically
|
|
</div>
|
|
|
|
<div class="navbar">
|
|
<a class="icon-btn plain" href="course.html" aria-label="Back"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m15 18-6-6 6-6"/></svg></a>
|
|
<div class="grow">
|
|
<div class="sub">Section 2 · Lesson 5 of 12</div>
|
|
<div class="title-sm">Load stability</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="screen">
|
|
<div class="player">
|
|
<span class="pill"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg> Playing from device</span>
|
|
<div class="play"><svg viewBox="0 0 24 24" fill="currentColor" stroke="none"><path d="M6 4h4v16H6zM14 4h4v16h-4z"/></svg></div>
|
|
<div class="ctrl">
|
|
<div class="bar"><span style="width: 100%"></span></div>
|
|
<div class="meta"><span>7:02</span><span style="opacity: 0.7">7:02</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="banner banner-muted" style="margin: 14px 0 12px">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg>
|
|
<div class="grow">
|
|
<div class="bt">Marked complete</div>
|
|
<div class="bd">Saved on this device · will sync when you're back online</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="sec-head" style="margin-top: 6px"><h2>Waiting to sync</h2><span class="count">2</span></div>
|
|
<div class="stack">
|
|
<div class="lrow">
|
|
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M21 12a9 9 0 1 1-6.2-8.6"/><path d="M21 3v6h-6"/></svg></div>
|
|
<div class="grow">
|
|
<div class="ltitle">Lesson 5 completed</div>
|
|
<div class="lmeta"><span>Queued 18:40</span></div>
|
|
</div>
|
|
<span class="badge badge-muted">Queued</span>
|
|
</div>
|
|
<div class="lrow">
|
|
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M21 12a9 9 0 1 1-6.2-8.6"/><path d="M21 3v6h-6"/></svg></div>
|
|
<div class="grow">
|
|
<div class="ltitle">Lesson 4 position 9:12</div>
|
|
<div class="lmeta"><span>Queued 18:31</span></div>
|
|
</div>
|
|
<span class="badge badge-muted">Queued</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="divider"></div>
|
|
|
|
<a class="btn btn-primary btn-lg btn-block" href="lesson.html">
|
|
Next lesson
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg>
|
|
</a>
|
|
|
|
<div class="lrow locked" style="margin-top: 10px">
|
|
<div class="media"><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></div>
|
|
<div class="grow">
|
|
<div class="ltitle">Section 2 check</div>
|
|
<div class="lsub">Exercises need a connection — we'll nudge you when you're back online</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="home-bar"></div>
|
|
</div>
|
|
</div>
|
|
<figcaption><b>Offline & queued</b>Completion is a monotonic, idempotent intent held on-device — the reason v1 offline covers content but not assessments.</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>
|