Initial decks site

This commit is contained in:
Codex 2026-07-07 08:34:01 +05:30
commit 666218035c
25 changed files with 3111 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View file

@ -0,0 +1,462 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Redesign the Course - A Playable Teaching Essay</title>
<meta
name="description"
content="A playable long-form learning experience about moving a course from topic coverage to aligned learning."
>
<link rel="stylesheet" href="styles.css?v=2">
<script defer src="script.js?v=2"></script>
</head>
<body>
<a class="skip-link" href="#main">Skip to the experience</a>
<div class="progress-line" aria-hidden="true"><span></span></div>
<header class="topbar">
<a href="#top" class="brand" aria-label="Redesign the Course home">
<span class="mark">RC</span>
<span>Redesign the Course</span>
</a>
<nav aria-label="Experience sections">
<a href="#diagnose">Diagnose</a>
<a href="#outcomes">Outcomes</a>
<a href="#alignment">Alignment</a>
<a href="#class-session">Class</a>
<a href="#feedback">Feedback</a>
<a href="#launch">Launch</a>
</nav>
</header>
<main id="main">
<section class="hero scene" id="top">
<div class="hero-copy">
<p class="eyebrow">A course-design simulation in essay form</p>
<h1>The Course That Covers Everything</h1>
<p class="lede">
You inherit a course that looks complete. Students still cannot use it. Your job is to
redesign one course file, and every choice changes the same first class and the same learner.
</p>
<div class="hero-actions">
<a class="primary-link" href="#diagnose">Open the course file</a>
<button type="button" class="ghost-link" data-action="reset">Reset design</button>
</div>
</div>
<div class="hero-art" aria-label="A course file being redesigned from topics to learning" role="img">
<div class="folder">
<div class="folder-tab">Data Structures I</div>
<div class="folder-page page-a">
<strong>Old syllabus</strong>
<span>arrays</span>
<span>lists</span>
<span>trees</span>
<span>graphs</span>
<span>hashing</span>
</div>
<div class="folder-page page-b">
<strong>What students must do</strong>
<span>choose a structure</span>
<span>analyze a tradeoff</span>
<span>debug a traversal</span>
</div>
<div class="learner-note">
<b>Maya, week 7</b>
<span>I know the names. I do not know when to use them.</span>
</div>
</div>
</div>
</section>
<div class="experience-shell">
<aside class="course-file" aria-label="Living course file">
<div class="file-head">
<p class="eyebrow">Living course file</p>
<h2>Data Structures I</h2>
<p id="fileStatus">Topic coverage is still driving the course.</p>
</div>
<div class="file-section">
<h3>Course spine</h3>
<dl class="spine-list">
<div>
<dt>Outcome</dt>
<dd id="spineOutcome">not yet written</dd>
</div>
<div>
<dt>Practice</dt>
<dd id="spinePractice">mostly listening</dd>
</div>
<div>
<dt>Assessment</dt>
<dd id="spineAssessment">midterm plus final</dd>
</div>
<div>
<dt>Feedback</dt>
<dd id="spineFeedback">late and scarce</dd>
</div>
<div>
<dt>Motivation</dt>
<dd id="spineMotivation">grades carry the message</dd>
</div>
</dl>
</div>
<div class="file-section">
<h3>Maya's current experience</h3>
<div class="learner-state">
<div class="state-meter" aria-hidden="true">
<span id="meterUse"></span>
<span id="meterConfidence"></span>
<span id="meterTransfer"></span>
</div>
<ul id="learnerNotes">
<li>She can repeat definitions after lecture.</li>
<li>She waits until the exam to find out what counts.</li>
<li>She cannot yet transfer the ideas to a new problem.</li>
</ul>
</div>
</div>
<div class="file-section memory-file">
<h3>Memory trace</h3>
<div class="memory-row" id="memoryTrace">
<span>0 checks settled</span>
</div>
</div>
</aside>
<article class="story">
<section class="chapter" id="diagnose">
<p class="section-number">1 / Diagnose</p>
<h2>The first problem is not that the course is empty. It is that the course is full.</h2>
<p>
The inherited course is familiar: a list of important topics, a lecture for each topic,
and two exams. It looks responsible because nothing obvious is missing. It fails quietly
because the design never says what students should be able to do with the knowledge.
</p>
<div class="old-course-lab">
<div class="topic-board" aria-label="Old topic list">
<h3>Old plan</h3>
<button type="button" class="topic-pill">arrays</button>
<button type="button" class="topic-pill">linked lists</button>
<button type="button" class="topic-pill">stacks</button>
<button type="button" class="topic-pill">queues</button>
<button type="button" class="topic-pill">trees</button>
<button type="button" class="topic-pill">graphs</button>
<button type="button" class="topic-pill">hashing</button>
<button type="button" class="topic-pill">complexity</button>
</div>
<div class="semester-sim">
<div class="sim-head">
<h3>Run the semester</h3>
<button type="button" id="runOldCourse">Run old course</button>
</div>
<div class="week-track" id="weekTrack" aria-label="Semester timeline"></div>
<div class="student-log" id="studentLog" aria-live="polite">
<p>Before the run, the course looks complete on paper.</p>
</div>
</div>
</div>
<div class="instructor-note">
<div>
<p class="eyebrow">Course voice</p>
<h3>The course's central turn</h3>
<p>
Teaching becomes design when the object of attention moves from what the teacher
covers to what students learn, practice, and revise.
</p>
</div>
<video controls preload="metadata" poster="assets/posters/teaching.jpg">
<source src="assets/clips/teaching-is-for-learning.mp4" type="video/mp4">
</video>
</div>
</section>
<section class="chapter" id="outcomes">
<p class="section-number">2 / Outcomes</p>
<h2>Now write the sentence the course has been avoiding.</h2>
<p>
A learning outcome is not a topic. It is a claim about the student's end state:
what the student understands or can do after the course. The sentence has to be
visible enough that activities and assessment can answer to it.
</p>
<div class="decision-lab">
<div class="prompt-card">
<p class="eyebrow">Decision</p>
<h3>Which outcome can actually design the course?</h3>
<p>Pick one. Watch what it does to the living course file.</p>
</div>
<div class="choice-grid" id="outcomeChoices">
<button type="button" class="choice-card" data-outcome="vague">
<span>Students will understand trees, graphs, and hashing.</span>
<small>comfortable, but hard to observe</small>
</button>
<button type="button" class="choice-card" data-outcome="usable">
<span>Students will choose and justify a data structure for a stated constraint.</span>
<small>observable and course-level</small>
</button>
<button type="button" class="choice-card" data-outcome="tooBig">
<span>Students will design industrial-scale storage systems from scratch.</span>
<small>ambitious beyond the course</small>
</button>
</div>
<div class="decision-feedback" id="outcomeFeedback" aria-live="polite">
<strong>No outcome chosen yet.</strong>
<span>The course still has topics, not a destination.</span>
</div>
</div>
<div class="memory-check" data-check="outcome">
<div>
<p class="eyebrow">Retrieval check</p>
<h3>What does a learning outcome name?</h3>
</div>
<div class="check-options">
<button type="button" data-answer="wrong">The order of lecture topics</button>
<button type="button" data-answer="right">The student's end-state knowledge or skill</button>
<button type="button" data-answer="wrong">The instructor's teaching style</button>
</div>
<p class="check-feedback" aria-live="polite">Choose before moving on.</p>
</div>
</section>
<section class="chapter" id="alignment">
<p class="section-number">3 / Alignment</p>
<h2>Assessment is the hidden course. Students study what the course rewards.</h2>
<p>
If the outcome asks students to justify a choice, but the exam rewards definitions,
the actual course is definitions. Alignment is not administrative neatness. It is how
the course tells students where effort belongs.
</p>
<div class="alignment-studio">
<div class="wire-map" aria-label="Alignment map">
<svg id="wireSvg" viewBox="0 0 760 420" role="img" aria-label="Outcome, practice, and assessment alignment">
<line class="wire wire-a" x1="170" y1="120" x2="380" y2="210"></line>
<line class="wire wire-b" x1="590" y1="120" x2="380" y2="210"></line>
<line class="wire wire-c" x1="170" y1="310" x2="380" y2="210"></line>
<line class="wire wire-d" x1="590" y1="310" x2="380" y2="210"></line>
<g class="node node-outcome">
<rect x="62" y="62" width="216" height="116" rx="10"></rect>
<text x="170" y="111" text-anchor="middle">outcome</text>
<text id="mapOutcome" x="170" y="141" text-anchor="middle">not written</text>
</g>
<g class="node node-practice">
<rect x="482" y="62" width="216" height="116" rx="10"></rect>
<text x="590" y="111" text-anchor="middle">practice</text>
<text id="mapPractice" x="590" y="141" text-anchor="middle">listening</text>
</g>
<g class="node node-assess">
<rect x="62" y="252" width="216" height="116" rx="10"></rect>
<text x="170" y="301" text-anchor="middle">assessment</text>
<text id="mapAssessment" x="170" y="331" text-anchor="middle">recall exam</text>
</g>
<g class="node node-learner">
<circle cx="380" cy="210" r="70"></circle>
<text x="380" y="202" text-anchor="middle">student</text>
<text x="380" y="232" text-anchor="middle">effort</text>
</g>
<g class="node node-feedback">
<rect x="482" y="252" width="216" height="116" rx="10"></rect>
<text x="590" y="301" text-anchor="middle">feedback</text>
<text id="mapFeedback" x="590" y="331" text-anchor="middle">after exam</text>
</g>
</svg>
</div>
<div class="alignment-controls">
<div>
<h3>Choose a practice structure</h3>
<div class="select-list" id="practiceChoices">
<button type="button" data-practice="lecture">Lecture examples only</button>
<button type="button" data-practice="compare">Compare two structures for one constraint</button>
<button type="button" data-practice="debug">Debug a bad structure choice in pairs</button>
</div>
</div>
<div>
<h3>Choose an assessment signal</h3>
<div class="select-list" id="assessmentChoices">
<button type="button" data-assessment="definitions">Define tree, graph, hash table</button>
<button type="button" data-assessment="justify">Given a scenario, justify a structure</button>
<button type="button" data-assessment="project">Mini-project with design memo</button>
</div>
</div>
</div>
</div>
<div class="hidden-course" id="hiddenCourse" aria-live="polite">
<strong>The hidden course is still recall.</strong>
<span>Students will protect marks by memorizing definitions.</span>
</div>
</section>
<section class="chapter" id="class-session">
<p class="section-number">4 / Class session</p>
<h2>A lecture can stay a lecture. It cannot stay a monologue.</h2>
<p>
The course's active-learning advice is modest and powerful: every 10 to 15 minutes,
ask students to do something with the idea. Predict, compare, explain, answer, reflect.
The activity is short because it is not replacing the lecture. It is making the lecture learnable.
</p>
<div class="class-builder">
<div class="move-palette">
<h3>Choose a move, then place it in the hour</h3>
<div class="move-list" id="movePalette">
<button type="button" data-move="hook">Opening problem</button>
<button type="button" data-move="predict">Prediction</button>
<button type="button" data-move="pair">Think-pair-share</button>
<button type="button" data-move="quiz">One-minute quiz</button>
<button type="button" data-move="paper">Minute paper</button>
</div>
</div>
<div class="hour-stage">
<div class="attention-plot">
<svg id="attentionSvg" viewBox="0 0 780 280" role="img" aria-label="Attention and practice during a class hour">
<line x1="42" y1="232" x2="742" y2="232"></line>
<line x1="42" y1="34" x2="42" y2="232"></line>
<path id="attentionCurve"></path>
<g id="attentionMarkers"></g>
</svg>
</div>
<div class="slot-row" id="slotRow" aria-label="Class hour slots"></div>
<div class="class-feedback" id="classFeedback" aria-live="polite">
Place an opening problem and two short thinking moves.
</div>
</div>
</div>
<div class="instructor-note compact-note">
<div>
<p class="eyebrow">Watch when useful</p>
<h3>The 15-minute reset</h3>
<p>
This clip explains why active lectures use short cycles of attention, activity,
and feedback rather than one long stream of explanation.
</p>
</div>
<video controls preload="metadata" poster="assets/posters/attention.jpg">
<source src="assets/clips/attention-reset.mp4" type="video/mp4">
</video>
</div>
<div class="memory-check" data-check="alignment">
<div>
<p class="eyebrow">Retrieval check</p>
<h3>If a final exam rewards recall, what will students optimize for?</h3>
</div>
<div class="check-options">
<button type="button" data-answer="right">Recall, even if the stated outcome is higher</button>
<button type="button" data-answer="wrong">The instructor's private intention</button>
<button type="button" data-answer="wrong">Only the in-class activities</button>
</div>
<p class="check-feedback" aria-live="polite">Choose the signal students can see.</p>
</div>
</section>
<section class="chapter" id="feedback">
<p class="section-number">5 / Feedback</p>
<h2>Practice without feedback lets mistakes become architecture.</h2>
<p>
Formative assessment is valuable because it is frequent, low stakes, and fast enough
to change what students do next. Summative assessment still matters, but it cannot be
the first moment students learn whether their thinking works.
</p>
<div class="feedback-lab">
<div class="misconception-board">
<h3>The misconception</h3>
<p class="bug-line">"Hash tables are always faster than trees."</p>
<div class="persistence-bars" id="persistenceBars" aria-label="Misconception persistence over time"></div>
</div>
<div class="feedback-choices">
<h3>When does feedback arrive?</h3>
<button type="button" data-feedback="late">After the midterm</button>
<button type="button" data-feedback="quick">Same class, after a comparison quiz</button>
<button type="button" data-feedback="revision">Next day, with a revision attempt</button>
</div>
</div>
</section>
<section class="chapter" id="motivation">
<p class="section-number">6 / Motivation</p>
<h2>Motivation is designed through value, expectancy, and support.</h2>
<p>
The student has many goals. Your course competes with all of them. Motivation grows
when the outcome has value, the student believes effort can work, and the environment
makes support visible.
</p>
<div class="opening-lab">
<div class="opening-scene">
<h3>Pick the first five minutes of class</h3>
<div class="select-list" id="openingChoices">
<button type="button" data-opening="rules">Read the grading rules first</button>
<button type="button" data-opening="mystery">Show a real design failure and ask what went wrong</button>
<button type="button" data-opening="support">Map the outcome, practice route, and help route</button>
</div>
</div>
<div class="motivation-response" id="motivationResponse" aria-live="polite">
<strong>Maya is waiting to see what this course values.</strong>
<span>The opening has not yet told her why effort will be worth it.</span>
</div>
</div>
<div class="memory-check" data-check="motivation">
<div>
<p class="eyebrow">Retrieval check</p>
<h3>What three factors shape motivation in this course?</h3>
</div>
<div class="check-options">
<button type="button" data-answer="wrong">Coverage, speed, difficulty</button>
<button type="button" data-answer="right">Value, expectancy, support</button>
<button type="button" data-answer="wrong">Attendance, marks, discipline</button>
</div>
<p class="check-feedback" aria-live="polite">Choose the model that helps a teacher act.</p>
</div>
</section>
<section class="chapter final-chapter" id="launch">
<p class="section-number">7 / Launch</p>
<h2>The redesigned course is not prettier. It is more legible to the learner.</h2>
<p>
You now have the pieces of a first class that tells students what matters, gives them
something to do, and shows how feedback will help them improve. The file below is built
from your choices.
</p>
<div class="launch-board">
<div class="plan-panel">
<div class="panel-head">
<h3>Your first-class plan</h3>
<button type="button" id="copyPlan">Copy plan</button>
</div>
<pre id="finalPlan" tabindex="0"></pre>
<p id="copyStatus" class="copy-status" aria-live="polite">The plan updates as the course file changes.</p>
</div>
<div class="closing-claim">
<p class="eyebrow">The durable idea</p>
<h3>Teach by designing what students do, think, practice, and revise.</h3>
<p>
Outcomes name the destination. Activities create practice. Assessment sends the
signal. Feedback helps students steer. Motivation keeps effort alive long enough
for knowledge to change.
</p>
</div>
</div>
</section>
</article>
</div>
</main>
</body>
</html>

View file

@ -0,0 +1,616 @@
/*
* ANIMATION STORYBOARD
*
* 0ms old course run starts, all weeks reset
* 260ms next week activates, student log receives one consequence
* 1200ms active week reaches exam signal, learner state updates
* 2400ms run ends with diagnosis visible in course file
*/
const TIMING = {
oldCourseStep: 260,
feedbackDelay: 900
};
const $ = (selector, root = document) => root.querySelector(selector);
const $$ = (selector, root = document) => Array.from(root.querySelectorAll(selector));
const course = {
outcome: null,
practice: null,
assessment: null,
feedback: null,
opening: null,
classMoves: {},
memory: new Set()
};
const labels = {
outcome: {
vague: "understand trees, graphs, and hashing",
usable: "choose and justify a data structure for a stated constraint",
tooBig: "design industrial-scale storage systems from scratch"
},
practice: {
lecture: "lecture examples only",
compare: "compare structures for a constraint",
debug: "debug a bad choice in pairs"
},
assessment: {
definitions: "definition-heavy exam",
justify: "scenario justification task",
project: "mini-project with design memo"
},
feedback: {
late: "after the midterm",
quick: "same-class comparison feedback",
revision: "revision attempt after feedback"
},
opening: {
rules: "grading rules first",
mystery: "real design failure first",
support: "outcome, practice, and help route first"
},
move: {
hook: "opening problem",
predict: "prediction",
pair: "pair talk",
quiz: "quick quiz",
paper: "minute paper"
}
};
function clamp(value, min, max) {
return Math.max(min, Math.min(max, value));
}
function setText(selector, text) {
const node = typeof selector === "string" ? $(selector) : selector;
if (node) node.textContent = text;
}
function initProgress() {
const bar = $(".progress-line span");
const update = () => {
const max = document.documentElement.scrollHeight - window.innerHeight;
const percent = max <= 0 ? 0 : (window.scrollY / max) * 100;
bar.style.width = `${clamp(percent, 0, 100)}%`;
};
update();
window.addEventListener("scroll", update, { passive: true });
}
function initNav() {
const links = $$(".topbar nav a");
const byId = new Map(links.map((link) => [link.getAttribute("href").slice(1), link]));
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (!entry.isIntersecting) return;
links.forEach((link) => link.classList.remove("active"));
const link = byId.get(entry.target.id);
if (link) link.classList.add("active");
});
},
{ rootMargin: "-36% 0px -54% 0px", threshold: 0.01 }
);
links.forEach((link) => {
const section = $(link.getAttribute("href"));
if (section) observer.observe(section);
});
}
function initWeeks() {
const track = $("#weekTrack");
if (!track) return;
track.replaceChildren();
for (let i = 1; i <= 12; i += 1) {
const week = document.createElement("div");
week.className = "week";
week.textContent = `W${i}`;
track.append(week);
}
}
function runOldCourse() {
const weeks = $$(".week");
const log = $("#studentLog");
const entries = [
"Week 2: Maya copies the list implementation. It works, but she cannot explain the design choice.",
"Week 5: Trees arrive. The course has moved on before her earlier misconception is visible.",
"Week 8: The midterm asks definitions. Her effort shifts toward remembering names.",
"Week 12: She passes many recall items. Transfer is still fragile."
];
weeks.forEach((week) => week.classList.remove("active"));
log.replaceChildren();
setText(log.appendChild(document.createElement("p")), "The run starts from the topic list.");
let i = 0;
const timer = window.setInterval(() => {
if (i > 0) weeks[i - 1]?.classList.remove("active");
if (i >= weeks.length) {
window.clearInterval(timer);
updateLearnerNotes("diagnosed");
updateCourseFile();
return;
}
weeks[i].classList.add("active");
if ([1, 4, 7, 11].includes(i)) {
const p = document.createElement("p");
p.textContent = entries.shift();
log.append(p);
}
i += 1;
}, TIMING.oldCourseStep);
}
function outcomeFeedback(kind) {
const node = $("#outcomeFeedback");
const messages = {
vague: [
"This is a topic in disguise.",
"It does not tell you what students must produce, so assessment will drift."
],
usable: [
"This can design the course.",
"It names a student action, a judgment, and a realistic course-level capability."
],
tooBig: [
"This overpromises.",
"Ambition helps only when students can practice the component skills inside the course."
]
};
const [title, detail] = messages[kind];
node.innerHTML = `<strong>${title}</strong><span>${detail}</span>`;
}
function initOutcomes() {
$$("#outcomeChoices .choice-card").forEach((button) => {
button.addEventListener("click", () => {
$$("#outcomeChoices .choice-card").forEach((item) => item.classList.toggle("selected", item === button));
course.outcome = button.dataset.outcome;
outcomeFeedback(course.outcome);
updateLearnerNotes("outcome");
updateCourseFile();
updateAlignmentMap();
updatePlan();
});
});
}
function initAlignmentControls() {
$$("#practiceChoices button").forEach((button) => {
button.addEventListener("click", () => {
$$("#practiceChoices button").forEach((item) => item.classList.toggle("selected", item === button));
course.practice = button.dataset.practice;
updateAlignmentMap();
updateLearnerNotes("alignment");
updateCourseFile();
updatePlan();
});
});
$$("#assessmentChoices button").forEach((button) => {
button.addEventListener("click", () => {
$$("#assessmentChoices button").forEach((item) => item.classList.toggle("selected", item === button));
course.assessment = button.dataset.assessment;
updateAlignmentMap();
updateLearnerNotes("alignment");
updateCourseFile();
updatePlan();
});
});
}
function updateAlignmentMap() {
const outcome = course.outcome ? labels.outcome[course.outcome] : "not written";
const practice = course.practice ? labels.practice[course.practice] : "listening";
const assessment = course.assessment ? labels.assessment[course.assessment] : "recall exam";
const feedback = course.feedback ? labels.feedback[course.feedback] : "after exam";
setText("#mapOutcome", shorten(outcome, 24));
setText("#mapPractice", shorten(practice, 24));
setText("#mapAssessment", shorten(assessment, 24));
setText("#mapFeedback", shorten(feedback, 24));
const goodOutcome = course.outcome === "usable";
const goodPractice = course.practice === "compare" || course.practice === "debug";
const goodAssessment = course.assessment === "justify" || course.assessment === "project";
const goodFeedback = course.feedback === "quick" || course.feedback === "revision";
$(".wire-a")?.classList.toggle("strong", goodOutcome);
$(".wire-b")?.classList.toggle("strong", goodPractice);
$(".wire-c")?.classList.toggle("strong", goodAssessment);
$(".wire-d")?.classList.toggle("strong", goodFeedback);
$(".wire-c")?.classList.toggle("warn", course.assessment === "definitions");
const hidden = $("#hiddenCourse");
if (!course.assessment || course.assessment === "definitions") {
hidden.classList.remove("good");
hidden.innerHTML = "<strong>The hidden course is still recall.</strong><span>Students will protect marks by memorizing definitions.</span>";
} else if (goodOutcome && goodPractice && goodAssessment) {
hidden.classList.add("good");
hidden.innerHTML = "<strong>The hidden course now matches the stated course.</strong><span>Students can see that practice and marks both point toward justified choice.</span>";
} else {
hidden.classList.remove("good");
hidden.innerHTML = "<strong>The signals are mixed.</strong><span>One corner has improved, but students still receive competing messages about effort.</span>";
}
}
function shorten(text, limit) {
return text.length > limit ? `${text.slice(0, limit - 3)}...` : text;
}
function initClassBuilder() {
const palette = $$("#movePalette button");
const slotRow = $("#slotRow");
let selectedMove = null;
palette.forEach((button) => {
button.addEventListener("click", () => {
selectedMove = button.dataset.move;
palette.forEach((item) => item.classList.toggle("selected", item === button));
});
});
slotRow.replaceChildren();
[0, 10, 20, 30, 40, 50].forEach((minute) => {
const slot = document.createElement("button");
slot.type = "button";
slot.className = "slot";
slot.dataset.minute = String(minute);
slot.textContent = `${minute} min`;
slot.addEventListener("click", () => {
if (!selectedMove) {
setText("#classFeedback", "Choose a move first, then place it in the hour.");
return;
}
course.classMoves[minute] = selectedMove;
slot.classList.add("filled");
slot.textContent = `${minute}: ${labels.move[selectedMove]}`;
updateAttention();
updateLearnerNotes("class");
updateCourseFile();
updatePlan();
});
slotRow.append(slot);
});
updateAttention();
}
function attentionAt(minute) {
const placed = Object.entries(course.classMoves)
.map(([key, value]) => ({ minute: Number(key), move: value }))
.sort((a, b) => a.minute - b.minute);
const previous = placed.filter((item) => item.minute <= minute).pop();
const since = previous ? minute - previous.minute : minute;
const reset = previous && since < 5 ? 11 : 0;
const endLift = minute > 54 ? (minute - 54) * 2 : 0;
return clamp(86 - since * 2.15 + reset + endLift, 26, 94);
}
function updateAttention() {
const path = $("#attentionCurve");
const markers = $("#attentionMarkers");
if (!path || !markers) return;
const points = [];
for (let minute = 0; minute <= 60; minute += 2) {
const x = 42 + (minute / 60) * 700;
const y = 232 - (attentionAt(minute) / 100) * 190;
points.push(`${x},${y}`);
}
path.setAttribute("d", `M ${points.join(" L ")}`);
markers.replaceChildren();
Object.entries(course.classMoves).forEach(([minute, move]) => {
const x = 42 + (Number(minute) / 60) * 700;
const y = 232 - (attentionAt(Number(minute)) / 100) * 190;
const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
circle.setAttribute("cx", x);
circle.setAttribute("cy", y);
circle.setAttribute("r", 8);
const text = document.createElementNS("http://www.w3.org/2000/svg", "text");
text.setAttribute("x", x);
text.setAttribute("y", y - 15);
text.setAttribute("text-anchor", "middle");
text.textContent = labels.move[move];
markers.append(circle, text);
});
const moves = Object.values(course.classMoves);
const hasHook = moves.includes("hook");
const thinkingMoves = moves.filter((move) => ["predict", "pair", "quiz", "paper"].includes(move)).length;
if (hasHook && thinkingMoves >= 2) {
setText("#classFeedback", "This hour now asks students to predict, explain, or retrieve before the idea goes cold.");
} else if (thinkingMoves >= 1) {
setText("#classFeedback", "Good start. Add one more thinking move so the middle of the hour does not sag.");
} else {
setText("#classFeedback", "Place an opening problem and two short thinking moves.");
}
}
function initFeedbackLab() {
const bars = $("#persistenceBars");
bars.replaceChildren();
["same day", "one week", "midterm"].forEach((label) => {
const row = document.createElement("div");
row.className = "persistence-row";
row.innerHTML = `<span>${label}</span><div class="bar-shell"><span></span></div>`;
bars.append(row);
});
updatePersistence("late");
$$(".feedback-choices button").forEach((button) => {
button.addEventListener("click", () => {
$$(".feedback-choices button").forEach((item) => item.classList.toggle("selected", item === button));
course.feedback = button.dataset.feedback;
updatePersistence(course.feedback);
updateAlignmentMap();
updateLearnerNotes("feedback");
updateCourseFile();
updatePlan();
});
});
}
function updatePersistence(kind) {
const levels = {
late: [80, 72, 58],
quick: [58, 30, 18],
revision: [62, 24, 10]
};
$$(".bar-shell span").forEach((bar, index) => {
bar.style.setProperty("--amount", `${levels[kind][index]}%`);
bar.style.background = kind === "late" ? "var(--coral)" : "var(--teal)";
});
}
function initOpening() {
$$("#openingChoices button").forEach((button) => {
button.addEventListener("click", () => {
$$("#openingChoices button").forEach((item) => item.classList.toggle("selected", item === button));
course.opening = button.dataset.opening;
const response = $("#motivationResponse");
if (course.opening === "rules") {
response.classList.remove("good");
response.innerHTML = "<strong>Maya hears that marks matter.</strong><span>Value, expectancy, and support are still mostly implicit.</span>";
} else if (course.opening === "mystery") {
response.classList.add("good");
response.innerHTML = "<strong>Maya sees value first.</strong><span>The opening gives the outcome a reason to exist before naming the machinery.</span>";
} else {
response.classList.add("good");
response.innerHTML = "<strong>Maya sees the route.</strong><span>The opening joins value with expectancy: here is the goal, the practice path, and where help lives.</span>";
}
updateLearnerNotes("motivation");
updateCourseFile();
updatePlan();
});
});
}
function initMemoryChecks() {
$$(".memory-check").forEach((check) => {
$$("button", check).forEach((button) => {
button.addEventListener("click", () => {
$$("button", check).forEach((item) => item.classList.remove("correct", "incorrect"));
const isRight = button.dataset.answer === "right";
button.classList.add(isRight ? "correct" : "incorrect");
const feedback = $(".check-feedback", check);
if (isRight) {
course.memory.add(check.dataset.check);
feedback.textContent = "Settled. This will show up in the course file.";
} else {
feedback.textContent = "Not yet. Try the option that changes what the teacher designs next.";
}
updateCourseFile();
});
});
});
}
function updateLearnerNotes(stage) {
const notes = {
diagnosed: [
"She can repeat definitions after lecture.",
"She waits until the exam to find out what counts.",
"She cannot yet transfer the ideas to a new problem."
],
outcome: [
course.outcome === "usable" ? "She can see the course is about justified choices, not names alone." : "The destination is still hard to recognize from a student's seat.",
"She needs practice that matches the promise.",
"The exam will decide what she actually studies."
],
alignment: [
course.practice === "compare" || course.practice === "debug" ? "She has a place to practice choosing, not just hearing." : "She is still mostly watching someone else reason.",
course.assessment === "justify" || course.assessment === "project" ? "Marks now reward the capability the outcome names." : "The visible reward still points toward recall.",
"Alignment is becoming a message she can read."
],
class: [
Object.values(course.classMoves).length >= 3 ? "She has to predict, explain, and retrieve during class." : "The class has started to ask for thinking, but the rhythm is still thin.",
"Short tasks make confusion visible before the exam.",
"Attention is being used as a design material."
],
feedback: [
course.feedback === "late" ? "Her misconception survives until the midterm." : "Her misconception gets interrupted while it is still fixable.",
"Feedback now changes the next attempt, not just the grade record.",
"Formative work has become part of teaching."
],
motivation: [
course.opening === "rules" ? "She hears the grading system first." : "She sees why the outcome matters before the syllabus details.",
course.opening === "support" ? "She also sees how effort can succeed." : "Support still needs to be named clearly.",
"Motivation is being designed, not wished for."
]
};
const list = $("#learnerNotes");
list.replaceChildren();
notes[stage].forEach((note) => {
const item = document.createElement("li");
item.textContent = note;
list.append(item);
});
}
function designLevels() {
let use = 22;
let confidence = 18;
let transfer = 14;
if (course.outcome === "usable") {
use += 18;
transfer += 16;
} else if (course.outcome) {
use += 6;
}
if (course.practice === "compare" || course.practice === "debug") {
use += 16;
transfer += 14;
}
if (course.assessment === "justify" || course.assessment === "project") {
use += 16;
confidence += 14;
transfer += 12;
}
if (Object.values(course.classMoves).length >= 3) {
confidence += 14;
use += 10;
}
if (course.feedback === "quick" || course.feedback === "revision") {
confidence += 18;
transfer += 10;
}
if (course.opening === "mystery" || course.opening === "support") {
confidence += 10;
use += 8;
}
return {
use: clamp(use, 8, 96),
confidence: clamp(confidence, 8, 96),
transfer: clamp(transfer, 8, 96)
};
}
function updateCourseFile() {
setText("#spineOutcome", course.outcome ? labels.outcome[course.outcome] : "not yet written");
setText("#spinePractice", course.practice ? labels.practice[course.practice] : "mostly listening");
setText("#spineAssessment", course.assessment ? labels.assessment[course.assessment] : "midterm plus final");
setText("#spineFeedback", course.feedback ? labels.feedback[course.feedback] : "late and scarce");
setText("#spineMotivation", course.opening ? labels.opening[course.opening] : "grades carry the message");
const levels = designLevels();
$("#meterUse").style.setProperty("--fill", `${levels.use}%`);
$("#meterConfidence").style.setProperty("--fill", `${levels.confidence}%`);
$("#meterTransfer").style.setProperty("--fill", `${levels.transfer}%`);
const memory = $("#memoryTrace");
memory.replaceChildren();
if (course.memory.size === 0) {
const span = document.createElement("span");
span.textContent = "0 checks settled";
memory.append(span);
} else {
course.memory.forEach((check) => {
const span = document.createElement("span");
span.className = "settled";
span.textContent = check;
memory.append(span);
});
}
const aligned =
course.outcome === "usable" &&
(course.practice === "compare" || course.practice === "debug") &&
(course.assessment === "justify" || course.assessment === "project") &&
(course.feedback === "quick" || course.feedback === "revision");
if (aligned) {
setText("#fileStatus", "The course now gives students one coherent message.");
} else if (course.outcome || course.practice || course.assessment || course.feedback || course.opening) {
setText("#fileStatus", "The course is improving, but at least one signal still needs work.");
} else {
setText("#fileStatus", "Topic coverage is still driving the course.");
}
}
function updatePlan() {
const moves = Object.entries(course.classMoves)
.sort((a, b) => Number(a[0]) - Number(b[0]))
.map(([minute, move]) => `${minute} min: ${labels.move[move]}`);
const plan = `Data Structures I - first class redesign
Learning outcome
${course.outcome ? labels.outcome[course.outcome] : "Write one observable course outcome before class."}
Opening
${course.opening ? labels.opening[course.opening] : "Open by showing why the outcome matters to a real design problem."}
In-class practice
${moves.length ? moves.join("\n") : "0 min: opening problem\n15 min: prediction\n30 min: think-pair-share\n45 min: one-minute quiz"}
Assessment signal
${course.assessment ? labels.assessment[course.assessment] : "Use a scenario task that asks students to justify a structure."}
Feedback route
${course.feedback ? labels.feedback[course.feedback] : "Give quick formative feedback before the first high-stakes exam."}
Teacher check
Students should leave knowing what they are trying to learn, how they will practice it, how it will be assessed, and where to get help.`;
setText("#finalPlan", plan);
}
function initCopy() {
$("#copyPlan")?.addEventListener("click", async () => {
try {
await navigator.clipboard.writeText($("#finalPlan").textContent);
setText("#copyStatus", "Copied.");
} catch {
const range = document.createRange();
range.selectNodeContents($("#finalPlan"));
const selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
setText("#copyStatus", "Selected the plan text.");
}
});
}
function initReset() {
$$("[data-action='reset']").forEach((button) => {
button.addEventListener("click", () => {
course.outcome = null;
course.practice = null;
course.assessment = null;
course.feedback = null;
course.opening = null;
course.classMoves = {};
course.memory.clear();
$$(".selected, .correct, .incorrect, .filled").forEach((node) => node.classList.remove("selected", "correct", "incorrect", "filled"));
$$(".slot").forEach((slot) => {
slot.textContent = `${slot.dataset.minute} min`;
});
updateLearnerNotes("diagnosed");
updateAlignmentMap();
updateAttention();
updatePersistence("late");
updateCourseFile();
updatePlan();
setText("#classFeedback", "Place an opening problem and two short thinking moves.");
$("#outcomeFeedback").innerHTML = "<strong>No outcome chosen yet.</strong><span>The course still has topics, not a destination.</span>";
$("#motivationResponse").innerHTML = "<strong>Maya is waiting to see what this course values.</strong><span>The opening has not yet told her why effort will be worth it.</span>";
$("#hiddenCourse").innerHTML = "<strong>The hidden course is still recall.</strong><span>Students will protect marks by memorizing definitions.</span>";
});
});
}
document.addEventListener("DOMContentLoaded", () => {
initProgress();
initNav();
initWeeks();
initOutcomes();
initAlignmentControls();
initClassBuilder();
initFeedbackLab();
initOpening();
initMemoryChecks();
initCopy();
initReset();
$("#runOldCourse")?.addEventListener("click", runOldCourse);
updateLearnerNotes("diagnosed");
updateAlignmentMap();
updateCourseFile();
updatePlan();
});

File diff suppressed because it is too large Load diff