diff --git a/src/prototypes/onboarding-mentor/card-action-restyled/ClassPageWithBanner.tsx b/src/prototypes/onboarding-mentor/card-action-restyled/ClassPageWithBanner.tsx
new file mode 100644
index 0000000..634c44a
--- /dev/null
+++ b/src/prototypes/onboarding-mentor/card-action-restyled/ClassPageWithBanner.tsx
@@ -0,0 +1,153 @@
+import type { ReactNode } from 'react'
+import { Button, Card } from '../../../kit'
+import type { ClassGroup, Project } from '../../../fixtures'
+
+// A COPY of screens/classroom/EducatorClassPage, changed here rather than
+// there.
+//
+// The shared screen renders its two bands — the white title block and the
+// off-white content below it — as a fragment, with nothing between them and no
+// prop to put anything there. Putting the "no members yet" alert where it
+// belongs means opening that seam, and opening it in the shared screen would
+// change the page for every other prototype using it. So it is copied in, per
+// the repo rule.
+//
+// What is different from the shared screen:
+//
+// - A `banner` slot between the two bands.
+//
+// That is the whole change. Everything else is the shared screen as it stands,
+// including the real page's furniture: "Class members" is the primary action,
+// projects sit in a bordered list of pale rows, and a project name is bold body
+// text rather than a link. Keep it that way — if the shared screen gains
+// something, this copy should gain it too.
+
+interface Props {
+ classGroup: ClassGroup
+ projects: Project[]
+ memberCount: number
+ /** The blurb under the class title. */
+ description?: string
+ /**
+ * Rendered between the title band and the content, which is where a page-
+ * level message belongs — after a mentor has read what page they are on, and
+ * before the thing they would otherwise start doing.
+ */
+ banner?: ReactNode
+ onAddProject: () => void
+ onOpenProject: (projectId: string) => void
+ onCopyLink: () => void
+ onClassMembers: () => void
+ /** Projects students cannot see yet. */
+ hiddenProjectIds?: string[]
+ /** The per-row overflow menu. Renders whether or not you wire it. */
+ onProjectMenu?: (projectId: string) => void
+ /** The gear beside "Copy link". Renders whether or not you wire it. */
+ onSettings?: () => void
+}
+
+export function ClassPageWithBanner({
+ classGroup,
+ projects,
+ memberCount,
+ description,
+ banner,
+ onAddProject,
+ onOpenProject,
+ onCopyLink,
+ onClassMembers,
+ hiddenProjectIds = [],
+ onProjectMenu,
+ onSettings,
+}: Props) {
+ return (
+ <>
+
+
+
{classGroup.name}
+ {description &&
{description}
}
+
+
+
+ {/* Primary on the live page. Getting young people into the class is
+ the job a mentor comes here to do. */}
+
+
+
+
+
+ {/* Padded to the same gutter as both bands, so the alert lines up with
+ the title above it and the Projects card below it. */}
+ {banner &&
{banner}
}
+
+
+
+
+
+
Projects
+
+ Projects are shared with students and contain starter code created by a teacher.
+
+
+ {hidden && (
+ // Labelled rather than aria-hidden: the icon is the only
+ // thing on the row carrying this, so it has to be read.
+
+ visibility_off
+
+ )}
+
+
+
+ )
+ })}
+
+ )}
+
+
+ >
+ )
+}
diff --git a/src/prototypes/onboarding-mentor/card-action-restyled/DashboardWithClassroom.tsx b/src/prototypes/onboarding-mentor/card-action-restyled/DashboardWithClassroom.tsx
new file mode 100644
index 0000000..e141dfd
--- /dev/null
+++ b/src/prototypes/onboarding-mentor/card-action-restyled/DashboardWithClassroom.tsx
@@ -0,0 +1,154 @@
+import { Alert, Button } from '../../../kit'
+import type { School } from '../../../fixtures'
+
+// A COPY of screens/code-club/MentorDashboard, changed here rather than there.
+//
+// The shared screen has no prop for another club action, and the change this
+// prototype is making is not a prop — it is a reordering of the card's
+// actions, which is the whole hypothesis. So it is copied in, per the repo
+// rule: never edit a shared screen, copy it and change your copy.
+//
+// What is different from the live dashboard:
+//
+// - "Code Classroom" is the primary button on each club card, first in the row.
+// - "Create an event" is gone from the card entirely. That is a real cost, not
+// a detail — events are how a club fills its sessions and how Code Club
+// counts activity — and it is the trade this variant is making on purpose.
+// - Nothing on the card is labelled as a proposal. The page reads as if it
+// shipped, so a mentor in a session reacts to the product rather than to a
+// prototype. What is and is not real is in notes.md.
+//
+// Everything else is left exactly as the shared screen has it.
+
+interface Props {
+ clubsManaged: School[]
+ pendingRequests: string[]
+ onDismissRequest: (request: string) => void
+ onManageClub: (schoolId: string) => void
+ onViewPublicProfile: (schoolId: string) => void
+ onStartAClub: () => void
+ onFindAClub: () => void
+ clubsVolunteeredAt?: School[]
+ /** Which of these clubs already has a Code Classroom behind it. */
+ isSetUp: (schoolId: string) => boolean
+ onCodeClassroom: (schoolId: string) => void
+}
+
+export function DashboardWithClassroom({
+ clubsManaged,
+ pendingRequests,
+ onDismissRequest,
+ onManageClub,
+ onViewPublicProfile,
+ onStartAClub,
+ onFindAClub,
+ clubsVolunteeredAt = [],
+ isSetUp,
+ onCodeClassroom,
+}: Props) {
+ return (
+
+ {/* The proposal. Primary, and first — a mentor arriving with
+ "what are we doing on Thursday?" should not have to know
+ the words "Manage club". "Create an event" is not here at
+ all: this variant gives the card's top slot to Code
+ Classroom rather than sharing it. */}
+
+ )
+}
diff --git a/src/prototypes/onboarding-mentor/card-action-restyled/FeatureBox.tsx b/src/prototypes/onboarding-mentor/card-action-restyled/FeatureBox.tsx
new file mode 100644
index 0000000..4371bba
--- /dev/null
+++ b/src/prototypes/onboarding-mentor/card-action-restyled/FeatureBox.tsx
@@ -0,0 +1,61 @@
+import type { ReactNode } from 'react'
+
+interface Props {
+ /**
+ * A Material Symbols ligature name, e.g. "checklist". The font renders the
+ * text content as the glyph, so this IS the icon.
+ */
+ icon: string
+ title: string
+ children: ReactNode
+}
+
+/**
+ * A pale green panel with an icon, a bold title and a line underneath — what
+ * the three "what Code Classroom is" points are shown as, instead of bullets.
+ *
+ * Local to this prototype rather than added to kit/: the kit has no equivalent,
+ * and the repo rule is that a prototype adds to its own folder. If a second
+ * prototype wants one of these, copy it.
+ *
+ * Material Symbols Outlined is already loaded by the design system's
+ * stylesheet — the same family Surface uses for its breadcrumb chevron and
+ * safeguarding flag — so there is no font to add and no dependency to install.
+ *
+ * The icon is decorative and hidden from screen readers: the title next to it
+ * says the same thing, and a reader announcing the ligature name would just
+ * read "checklist" twice.
+ */
+export function FeatureBox({ icon, title, children }: Props) {
+ return (
+
+
+ {icon}
+
+ {/* Body size, bold — still an h3, so the heading structure survives
+ it reading at the same scale as the line underneath. */}
+
+ {title}
+
+
+ {children}
+
+
+ )
+}
diff --git a/src/prototypes/onboarding-mentor/card-action-restyled/meta.ts b/src/prototypes/onboarding-mentor/card-action-restyled/meta.ts
new file mode 100644
index 0000000..44398a2
--- /dev/null
+++ b/src/prototypes/onboarding-mentor/card-action-restyled/meta.ts
@@ -0,0 +1,20 @@
+import type { PrototypeMeta } from '../../types'
+
+export const meta: PrototypeMeta = {
+ title: 'Club card action, restyled',
+
+ owner: 'Sarah Tucker',
+
+ // DRAFT — Sarah to correct. The flow is a straight copy of
+ // "Code Classroom as the club card action", so the route is not what is
+ // being tested here; only how the confirm screen is presented.
+ hypothesis:
+ 'The confirm screen is where a mentor decides whether Code Classroom is for them, and two dense cards of prose get skimmed. Presented as one centred explainer — a short claim per panel, each with an icon — a mentor will be able to say in their own words what Code Classroom is before they press yes, rather than agreeing and finding out afterwards.',
+
+ status: 'sketch',
+
+ // The same mentor as the prototype this copies, on purpose: two visual
+ // treatments of one route only compare if the club, the code and the class
+ // name are identical.
+ cast: { piAccount: 'pi-mentor-jo' },
+}
diff --git a/src/prototypes/onboarding-mentor/card-action-restyled/notes.md b/src/prototypes/onboarding-mentor/card-action-restyled/notes.md
new file mode 100644
index 0000000..f953df4
--- /dev/null
+++ b/src/prototypes/onboarding-mentor/card-action-restyled/notes.md
@@ -0,0 +1,104 @@
+A copy of **Code Classroom as the club card action** (Divya Mahadevan) with one
+screen restyled. The route, the fixtures, the fake latency and every other step
+are unchanged, on purpose — if more than the presentation varied, a session
+could not tell which change caused the reaction.
+
+## What question is this answering?
+
+The `onboarding-mentor` lane, but not the part about where the entry point
+lives. The prototype this copies already bets on that: Code Classroom as the
+primary button on the club card. This one takes that bet as settled and asks
+the next question down.
+
+**Does a mentor know what they have agreed to?**
+
+The confirm screen is the only place a mentor is told what Code Classroom is.
+They arrive from a button on a dashboard, having possibly never heard the
+product name, and one press later they have a school, a club code and a class.
+If that screen gets skimmed, the route succeeds on the measure it set itself —
+one click to a working Code Classroom — while leaving a mentor who cannot say
+what they now own.
+
+## Why this approach
+
+Two dense cards of prose, stacked full width, is a layout people scroll past.
+So the confirm screen is rebuilt as a single centred explainer, modelled on the
+Experience CS → Code Classroom interstitial that already exists in the real
+products:
+
+- **One card, not two.** Title inside it, narrow measure, floating on the page
+ green. It reads as a thing to be read rather than a form to get past.
+- **Three green panels instead of bullets.** One claim each, an icon, a short
+ line. A bullet list invites skimming to the end; separated panels are harder
+ to skip without noticing you skipped them.
+- **Primary action last, bottom right.** Matching the reference. It also puts
+ the button after the explanation rather than beside it.
+
+**Rejected:** a "Learn more" link out to a marketing page, as the reference has.
+It solves the comprehension problem by moving it somewhere a mentor in a club
+session will not go. If the explanation cannot fit on the screen that asks for
+consent, the ask is in the wrong place.
+
+**Rejected:** a second confirm step. The one-press claim is the whole point of
+the route being copied; adding a step would test a different thing.
+
+## What is not real
+
+- **The confirm screen does not exist in either product.** The entire thing is
+ a proposal. Nothing in Code Club today offers to create a Code Classroom
+ school for you.
+- **There is no illustration.** The reference card carries a diagram of the two
+ products handing off, between the list and the buttons. Left out for now —
+ what it should show has not been designed, and an invented one would get
+ reacted to in a session as though it had been. Worth deciding before this
+ goes in front of anyone: the card currently explains Code Classroom in words
+ only.
+- Automatic setup infers **one** class from the club's session time. Westlands
+ really runs a Scratch group and a Python group — the same honest limit as the
+ prototype this copies.
+- **The "no members yet" alert is not in the product.** The class page itself
+ is the real, verified screen; the alert sitting between its two bands is this
+ prototype's addition, and so is the "Adding members" panel behind it. The
+ class page is copied into this folder as `ClassPageWithBanner` purely to open
+ a seam between the title band and the content — one added prop, nothing else
+ changed.
+- **The steps in that panel are not a screen either.** Step 2, creating the
+ accounts, has no screen anywhere in this repo. The panel says so rather than
+ implying a flow exists behind it.
+
+## What I'd want to watch in testing
+
+- **At the moment they press yes:** ask them to say what Code Classroom is
+ without looking. That is the whole hypothesis, and it is the only question
+ here that the original prototype does not already answer.
+- **Whether the green panels get read or counted.** Three panels may just be a
+ bulleted list with more furniture. If people's eyes go straight to the
+ button, the restyle has not earned its keep and the finding is worth as much
+ as a positive one.
+- **"Creators".** The confirm card now says creators throughout — it is the
+ word Code Club uses. Two places still do not: the created screen says "an
+ account for each young person at your club", and Code Classroom itself will
+ say students the moment a mentor arrives in it. So the handover is where the
+ word changes, which is exactly where a mentor is least able to absorb it.
+ Worth watching whether anyone notices they have become a teacher with
+ students.
+- **The word "school"**, still. A volunteer at a library is told they are
+ getting a school. The card no longer glosses it — the earlier draft said
+ "Code Classroom's word, not ours", which was the prototype apologising for
+ the product. Without the aside the screen is a fairer test of whether the
+ word actually lands, but it also means nobody is warned. This is the sharpest
+ edge on the screen, and the school page at the end says it again, in the
+ title, at full size.
+- **Whether anyone opens "How do I add members?"** The alert names the blocker
+ on arrival; the action behind it is the test of whether naming it is enough.
+ If people read the alert and still press "Add project" first, the alert is
+ decoration.
+- **Whether the alert reads as help or as failure.** "Your class has no members
+ yet" arrives seconds after a screen that said setup was done. It is either
+ the obvious next step or evidence that the one-press promise was oversold —
+ and which of those it is, is the most useful thing a session could tell us
+ about this whole route.
+
+## What we learned
+
+Not tested yet.
diff --git a/src/prototypes/onboarding-mentor/card-action-restyled/prototype.tsx b/src/prototypes/onboarding-mentor/card-action-restyled/prototype.tsx
new file mode 100644
index 0000000..7cae99a
--- /dev/null
+++ b/src/prototypes/onboarding-mentor/card-action-restyled/prototype.tsx
@@ -0,0 +1,370 @@
+import { useEffect, useState, type CSSProperties } from 'react'
+import { Alert, Button, Card, ProgressBar } from '../../../kit'
+import { schoolsForMentor, type ClassGroup, type School } from '../../../fixtures'
+import { ClassPageWithBanner } from './ClassPageWithBanner'
+import { Surface } from '../../../surfaces'
+import { meta } from './meta'
+import { DashboardWithClassroom } from './DashboardWithClassroom'
+import { FeatureBox } from './FeatureBox'
+
+// A mentor, signed in on codeclub.org. Everything below comes from the cast in
+// meta.ts — Jo manages two clubs, so the dashboard has two cards and "which
+// club am I setting up?" is answered by which card you press.
+const CLUBS: School[] = schoolsForMentor(meta.cast?.piAccount ?? '')
+
+const DAYS = [
+ 'Monday',
+ 'Tuesday',
+ 'Wednesday',
+ 'Thursday',
+ 'Friday',
+ 'Saturday',
+ 'Sunday',
+] as const
+
+/**
+ * The class automatic setup would create, named from what codeclub.org already
+ * knows about the club's sessions. Derived, not hardcoded: change the cast and
+ * the club, the code and the class name all follow.
+ *
+ * Only one class, because that is all an automatic setup can honestly infer.
+ * Westlands really runs a Scratch group and a Python group — see notes.
+ */
+function autoClass(club: School): ClassGroup {
+ const day = DAYS.find((d) => club.schedule?.includes(d))
+ return {
+ id: `auto-${club.id}`,
+ schoolId: club.id,
+ name: day ? `${day} club` : 'Club session',
+ kind: 'code-club',
+ studentIds: [],
+ }
+}
+
+type Step = 'dashboard' | 'confirm' | 'creating' | 'created' | 'classroom'
+
+/**
+ * A section heading and the copy it introduces, held tight to each other.
+ *
+ * The card's own column gap is 16px, which is right between blocks but too
+ * loose between a heading and the sentence it belongs to. So a section is its
+ * own little column at 4px, and the 16px top margin — on top of the card's
+ * 16px — is what gives every section the same 32px above it.
+ */
+const SECTION: CSSProperties = {
+ display: 'flex',
+ flexDirection: 'column',
+ gap: 'var(--space-05)',
+ marginTop: 'var(--space-2)',
+}
+
+/**
+ * The card layout both of this flow's own screens use: one narrow card
+ * centred on the page colour, everything inside it, actions bottom right.
+ *
+ * Shared between the confirm screen and the created screen so the two look
+ * like one route rather than two prototypes. Not in kit/ — a prototype adds to
+ * its own folder.
+ */
+const PAGE: CSSProperties = {
+ display: 'flex',
+ justifyContent: 'center',
+ padding: 'var(--space-4) var(--space-2)',
+}
+const COLUMN: CSSProperties = { width: '100%', maxWidth: 760 }
+const CARD_STACK: CSSProperties = {
+ display: 'flex',
+ flexDirection: 'column',
+ gap: 'var(--space-2)',
+}
+const ACTIONS: CSSProperties = { justifyContent: 'flex-end', marginTop: 'var(--space-2)' }
+
+export default function DashboardPrimaryAction() {
+ const [step, setStep] = useState('dashboard')
+ /** Clubs that have been through setup, so the button changes on return. */
+ const [setUpClubIds, setSetUpClubIds] = useState([])
+ const [activeClubId, setActiveClubId] = useState(CLUBS[0].id)
+ /** What a route we have not built would have done. */
+ const [notice, setNotice] = useState()
+ /** Whether the mentor has asked how members get added. */
+ const [showHowTo, setShowHowTo] = useState(false)
+
+ const club = CLUBS.find((c) => c.id === activeClubId) ?? CLUBS[0]
+ const classes = [autoClass(club)]
+
+ // Fake latency, because "it set itself up" is a claim about how long a
+ // mentor waits, and a setup that returns instantly tests a different thing.
+ useEffect(() => {
+ if (step !== 'creating') return
+ const timer = setTimeout(() => {
+ setSetUpClubIds((ids) => (ids.includes(club.id) ? ids : [...ids, club.id]))
+ setStep('created')
+ }, 1800)
+ return () => clearTimeout(timer)
+ }, [step, club.id])
+
+ const noticeAlert = notice ? (
+ setNotice(undefined)} />
+ ) : null
+
+ /**
+ * What setup makes, derived from the club. Shown before and after, so it
+ * stays tenseless — the confirm screen supplies "we'll create:" above it and
+ * the created screen supplies "What was set up".
+ *
+ * The values stay interpolated. Changing the cast has to move the club name,
+ * the code and the class name together, or the flow offers one club's code
+ * and then names a different club's session.
+ */
+ const whatYouGet = (
+
+
+ A school called {club.name}
+
+
+ A club code {club.schoolCode} for creators to sign in
+
+
+ A class called {classes[0].name}, based on your session
+
+
+ )
+
+ // The mentor is asked once, and asked nothing else. No form, no naming, no
+ // choosing whether the club is a school or a class — all of that is
+ // answerable from what Code Club already knows.
+ //
+ // THIS IS THE ONLY SCREEN THAT DIFFERS from the prototype this copies. Laid
+ // out like the Experience CS → Code Classroom interstitial: one narrow card
+ // centred on the page colour, everything inside it, and the two actions
+ // bottom right with the primary last. The three points about what Code
+ // Classroom is are green panels with an icon rather than bullets, because
+ // the bet is that the explanation is what gets skimmed.
+ //
+ // Inline styles rather than new classes: global.css is shared, and the repo
+ // rule is that a prototype changes nothing outside its own folder.
+ if (step === 'confirm') {
+ return (
+
+
+
+
+
+
Set up Code Classroom for {club.name}?
+
+
+
What is Code Classroom?
+
+ Code Classroom is a free, safe environment for teaching and learning
+ computing. You can choose projects for your creators, create their accounts,
+ and see the work they save.
+
+
+
+
+ Share projects with your club so creators know what to work on
+
+
+ Set up usernames and passwords without email addresses or parent sign-up
+
+
+ Creators can sign in again and continue where they left off
+
+
+
+
What we'll set up
+
If you continue, we'll create:
+ {whatYouGet}
+
+
+
+ Nothing will be shared with your club or made public. You can change these
+ names later.
+
+
+ {/* The reference card carries a diagram of the two products
+ between the list and the buttons. Left out for now — what it
+ should show has not been designed. */}
+
+
This will take a few seconds. You do not need to do anything.
+
+
+
+ )
+ }
+
+ // Same card as the confirm screen, so arriving here reads as the other side
+ // of the question rather than a different product. The title moves inside
+ // the card, the list keeps the tight heading spacing, and the one action
+ // sits bottom right where "Set up Code Classroom" was a moment ago.
+ if (step === 'created') {
+ return (
+
+
+
+
+
+
Your Code Classroom account has been created
+
+
+
What was set up
+ {whatYouGet}
+
+
+
+ You sign in with the same Raspberry Pi account you use for Code Club — there is
+ no new password to remember.
+
+
+ Not set up: an account for each young person at your club. Nothing on
+ codeclub.org knows who they are, so that part cannot be done for you.
+
+
+
+ setStep('classroom')}
+ />
+
+
+
+
+
+
+ )
+ }
+
+ // Where a mentor lands: the real Code Classroom class page, in the empty
+ // state a mentor who has just been set up would actually find.
+ //
+ // The alert above it is THIS PROTOTYPE'S ADDITION and is not in the product.
+ // Setup makes a school, a code and a class and then stops — the creators are
+ // the one thing it cannot do, because nothing on codeclub.org knows who they
+ // are. A mentor who does not realise that leaves with a class nobody can get
+ // into, and finds out in front of a room of young people. So the blocker is
+ // stated on arrival, and the alert offers the next step rather than just
+ // naming the problem.
+ if (step === 'classroom') {
+ return (
+
+
+ {noticeAlert}
+ setStep('dashboard')}
+ />
+
+ setShowHowTo(true) },
+ ]}
+ >
+ Setting up made the class, but not the creators in it. Until you create their
+ accounts nobody can sign in — the club code on its own is not enough.
+
+
+ {showHowTo && (
+
+
Adding members
+
+
+ Open Class members at the top of this page.
+
+
+ Create a username and a password for each creator. No email addresses,
+ and nothing for a parent to sign up to.
+
+
+ Give them the club code {club.schoolCode} with the
+ username and password you made for them, and they can sign in.
+
+
+
+ Step 2 is not built in this prototype. What it actually takes to create a
+ set of accounts — one at a time, or in a batch, and who writes the
+ passwords down — is its own question, and this flow stops at the point
+ where it becomes one.
+
+
+ )}
+
+ }
+ onAddProject={() =>
+ setNotice(
+ 'Not built here — getting a Code Club project into Code Classroom is the importing lane’s question.',
+ )
+ }
+ onOpenProject={() => {}}
+ onCopyLink={() =>
+ setNotice(
+ 'Copy link shares the class. It skips the club code screen, but a creator still needs an account you made for them.',
+ )
+ }
+ onClassMembers={() =>
+ setNotice(
+ 'Not built here — creating the accounts is the step this flow stops at. See "How do I add members?" above.',
+ )
+ }
+ />
+
+
+ )
+ }
+
+ return (
+
+ {}}
+ onManageClub={() => {}}
+ onViewPublicProfile={() => {}}
+ onStartAClub={() => {}}
+ onFindAClub={() => {}}
+ isSetUp={(schoolId) => setUpClubIds.includes(schoolId)}
+ onCodeClassroom={(schoolId) => {
+ setActiveClubId(schoolId)
+ // Already set up? Straight in. Otherwise the mentor is asked first.
+ setStep(setUpClubIds.includes(schoolId) ? 'classroom' : 'confirm')
+ }}
+ />
+
+ )
+}