Source date lab • library lesson — Teach learners to trace a current practical claim to its original dated source, recognize an old secondary quotation and explain why…

HTML for this example

Select the code and use your copy shortcut. Changes in this preview are temporary.

<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Source date lab • library lesson</title><style>*{box-sizing:border-box}body{margin:0;background:var(--bg,#f3f7fa);color:var(--ink,#203342);font:16px/1.55 system-ui,Segoe UI,sans-serif}main{max-width:1100px;margin:auto;padding:28px 30px 50px}h1{font-size:clamp(27px,3.5vw,40px);line-height:1.18;letter-spacing:-.035em;margin:8px 0 12px}h2{font-size:21px;line-height:1.35;margin:0 0 16px}h3{font-size:18px}.eyebrow{color:var(--accent,#176783);font-size:12px;letter-spacing:.12em;font-weight:800;text-transform:uppercase}.intro,.muted,small{color:#516272}.intro{margin:0 0 24px}.panel{padding:24px;border:1px solid var(--line,#d6e0e7);border-radius:12px;background:white;margin:18px 0}.grid{display:grid;grid-template-columns:1fr 1fr;gap:22px}.grid>*{min-width:0}.controls{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin:16px 0}button,input,textarea,select{font:inherit}button{border:1px solid var(--accent,#176783);background:var(--accent,#176783);color:white;padding:10px 16px;min-height:44px;border-radius:7px;font-weight:700;cursor:pointer}button.secondary{background:white;color:var(--accent,#176783)}button[aria-pressed=true]{background:var(--tint,#e3f2f8);color:var(--ink,#203342);box-shadow:inset 0 0 0 2px var(--accent,#176783)}button:hover{filter:brightness(.96)}:focus-visible{outline:3px solid #d56b00;outline-offset:3px}label{display:block;margin:12px 0 5px;font-size:14px;font-weight:650}input:not([type=checkbox]),select,textarea{width:100%;padding:10px 12px;min-height:44px;border:1px solid #94a5b2;background:white;color:inherit;border-radius:7px}textarea{resize:vertical;min-height:120px}input[type=checkbox]{width:20px;height:20px;margin-right:9px}label.inline{display:flex;align-items:center;font-weight:500;gap:7px}.result{padding:24px;background:var(--tint,#e3f2f8);border:1px solid var(--line,#c8dce7);border-radius:12px}.big{font-size:clamp(30px,5vw,48px);font-weight:780;line-height:1.25;letter-spacing:-.035em;margin:10px 0}.trace{font:15px/1.7 ui-monospace,Consolas,monospace;white-space:pre-wrap;overflow-wrap:anywhere}.error{color:#a12820;font-weight:650;min-height:24px}.table-wrap{overflow-x:auto}table{border-collapse:collapse;width:100%;font-size:15px}th,td{text-align:left;border-bottom:1px solid var(--line,#d6e0e7);padding:12px 9px;vertical-align:top}th{font-size:12px;text-transform:uppercase;letter-spacing:.03em;color:#516272}code,pre,.code{font:14px/1.65 ui-monospace,Consolas,monospace}pre{white-space:pre-wrap;overflow-wrap:anywhere;background:var(--tint,#eef3f7);padding:16px;border-radius:6px}details{border-top:1px solid var(--line,#d6e0e7);margin:24px 0;padding-top:16px}summary{cursor:pointer;font-weight:700;min-height:44px}footer{border-top:1px solid var(--line,#d6e0e7);margin-top:26px;padding-top:16px;font-size:13px;color:#516272}.bar{height:16px;background:#dce4ea;border-radius:20px;overflow:hidden;margin:12px 0}.bar span{display:block;height:100%;background:var(--accent,#176783)}[hidden]{display:none!important}@media(max-width:720px){main{padding:20px}.grid{grid-template-columns:1fr;gap:12px}.panel,.result{padding:20px}}@media(max-width:400px){main{padding:16px}.panel,.result{padding:16px}button{padding:10px 13px}} .sheet{background:white;border:1px solid var(--line,#d6e0e7);padding:26px}.tiles{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}.tiles>article{padding:20px;background:white;border:1px solid var(--line,#d6e0e7)}.badge{display:inline-block;border-radius:30px;padding:4px 11px;background:var(--tint,#e3f3fa);font-size:13px;font-weight:750}.preview{white-space:pre-wrap;overflow-wrap:anywhere}.schedule-row{display:grid;grid-template-columns:1fr 100px 100px;gap:15px;align-items:center;border-bottom:1px solid var(--line,#d6e0e7);padding:12px 0}.schedule-row label{margin:0}.schedule-row small{display:block}.schedule-row input{margin-top:6px}.export textarea{min-height:180px}.note{border-left:4px solid var(--accent,#176783);background:var(--tint,#e3f3fa);padding:16px}svg{max-width:100%;height:auto}button{overflow-wrap:anywhere}h2,h3,p{overflow-wrap:anywhere}@media(max-width:720px){.tiles{grid-template-columns:1fr}.schedule-row{grid-template-columns:1fr 80px}.schedule-row .range{grid-column:1/3}.sheet{padding:20px}}@media(max-width:400px){.sheet{padding:16px}}
body{--accent:#2b6865;--tint:#eaf4f2;--bg:#edf3f1;--ink:#243e3e;--line:#c8d9d3}h1{font-family:Georgia,serif}.source-desk{display:grid;grid-template-columns:210px 1fr;gap:20px}.source-tabs{display:flex;flex-direction:column;gap:12px}.source-tabs button{text-align:left}.source-paper{background:white;padding:30px;border:1px solid #bdd0c8;box-shadow:4px 5px 0 #d9e5df}.source-paper blockquote{font:29px/1.35 Georgia,serif;margin:10px 0 24px}.date-chain{display:flex;gap:18px;align-items:center}.date-chain>div{flex:1;border-top:3px solid #739b8e;padding-top:12px}.date-chain small,.date-chain strong{display:block}.date-chain strong{font-size:18px}@media(max-width:720px){.source-desk{grid-template-columns:1fr}.source-tabs{flex-direction:row;flex-wrap:wrap}.source-tabs button{flex:1}.source-paper{padding:21px}.date-chain{gap:10px}.date-chain strong{font-size:16px}}</style></head><body><main>
<header><span class="eyebrow">Library source lab / fictional L01</span><h1>A new post. An older source.</h1><p class="intro">What time does the library close on Tuesdays this term?</p></header>
<section class="source-desk"><nav class="source-tabs" aria-label="Supplied source cards"><button type="button" id="source-1" class="secondary">S01 · library notice</button><button type="button" id="source-2" class="secondary">S02 · community blog</button><button type="button" id="source-3" class="secondary">S03 · promotional flyer</button></nav><article class="source-paper"><span id="source-owner" class="eyebrow"></span><blockquote id="claim"></blockquote><div class="date-chain"><div><small>Publication date</small><strong id="published"></strong></div><span aria-hidden="true">→</span><div><small>Underlying evidence</small><strong id="underlying"></strong></div></div><p id="source-limit" class="note"></p></article></section>
<div class="controls"><button type="button" id="learner-tab" class="secondary">Learner comparison</button><button type="button" id="teacher-tab" class="secondary">Teacher plan & reference</button></div>
<section id="learner-view" class="sheet"><h2>Make a claim-to-source record</h2><p>Compare all three supplied cards. No real hours or current confirmation are provided.</p><div class="grid"><div><label for="best-source">Which supplied card directly supports an hour?</label><select id="best-source"><option value="">Choose a card</option><option>S01</option><option>S02</option><option>S03</option></select><label for="answer">Your bounded answer (name source, hour, limitation)</label><textarea id="answer"></textarea></div><div><label for="next-check">What current check would you make before a visit?</label><textarea id="next-check"></textarea><label for="confirmation">Actual current confirmation (leave blank until obtained)</label><textarea id="confirmation"></textarea></div></div><button type="button" id="build-answer">Prepare learner record</button><p id="learner-error" class="error" role="alert"></p><label for="learner-record">Learner record for teacher review</label><textarea id="learner-record" readonly></textarea><button type="button" id="select-record" class="secondary">Select learner record</button><p>No automatic reasoning grade. Copy manually into the approved workflow.</p></section>
<section id="teacher-view" hidden><article class="sheet"><h2>Bounded teacher reference</h2><p>According to the supplied September 1, 2026 L01 notice, Tuesday closing is <b>4 p.m.</b> Confirm the current official schedule and any closure or later update before an actual visit.</p><p>S01 is the most direct recent supplied statement. S02’s October 2026 publication does not refresh its April 2024 evidence. S03 gives no closing hour. An official label alone is not universal proof.</p><p>Accept other accurate phrasings naming the supplied source, hour, currentness limit and next check. Actual learner reasoning starts blank.</p></article><section class="panel"><h2>Editable teaching sequence</h2><b id="total" aria-live="polite"></b><label for="budget">Available planned minutes</label><input id="budget" type="number" value="30"><div class="schedule-row"><div><b>Question</b><small>Distinguish Tuesday closing time from a general invitation.</small></div><div><label for="minutes-0">Minutes</label><input id="minutes-0" type="number" value="5"></div><b class="range" id="range-0"></b></div><div class="schedule-row"><div><b>Model the two-date trace</b><small>Follow S02 publication back to its quoted notice.</small></div><div><label for="minutes-1">Minutes</label><input id="minutes-1" type="number" value="7"></div><b class="range" id="range-1"></b></div><div class="schedule-row"><div><b>Pair comparison</b><small>Compare owner, date, exact support and missing evidence.</small></div><div><label for="minutes-2">Minutes</label><input id="minutes-2" type="number" value="10"></div><b class="range" id="range-2"></b></div><div class="schedule-row"><div><b>Bounded answer</b><small>Name the source, hour and remaining currentness check.</small></div><div><label for="minutes-3">Minutes</label><input id="minutes-3" type="number" value="5"></div><b class="range" id="range-3"></b></div><div class="schedule-row"><div><b>Exit verification</b><small>Name the current official check and evidence to retain.</small></div><div><label for="minutes-4">Minutes</label><input id="minutes-4" type="number" value="3"></div><b class="range" id="range-4"></b></div><p id="schedule-error" class="error" role="alert"></p></section><section class="panel export"><h2>Manual-copy planning text</h2><button type="button" id="prepare">Prepare text</button><button type="button" id="select-text" class="secondary">Select text</button><p id="export-error" class="error" role="alert"></p><label for="export-text">Generated plain text</label><textarea id="export-text" readonly></textarea><small>Select the text, then copy manually. Nothing is saved or sent.</small></section></section>
<details><summary>All three original source cards / clean comparison facts</summary><p><b>S01:</b> Official L01 library notice, issued September 1, 2026. “Tuesday closes at 4 p.m.” Underlying evidence is that notice.</p><p><b>S02:</b> Community blog published October 1, 2026. “Tuesday closes at 5 p.m.” Explicitly quotes an April 1, 2024 library notice.</p><p><b>S03:</b> Undated promotional flyer. “Visit after school.” No closing time or dated underlying evidence.</p><p>Log: claim; source owner; publication date; underlying quoted date; exact supporting text; limitation; next check; actual confirmation and where/when obtained.</p></details>
<footer>Printed fictional source exercise; no real library URLs, current schedule, browsing or contact is supplied by the activity. Learner text is assembled for human review, not graded. No records persist. Maintained school materials and actual verification remain authoritative.</footer>
</main><script>const $=id=>document.getElementById(id);const on=(id,e,fn)=>$(id).addEventListener(e,fn);const txt=(id,v)=>$(id).textContent=v;const esc=s=>String(s).replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));const positive=x=>String(x).trim()!==''&&Number.isFinite(Number(x))&&Number(x)>0;const num=x=>Number(x.toPrecision(10)).toString();function enter(id,fn){on(id,'keydown',e=>{if(e.key==='Enter'&&!e.isComposing&&e.keyCode!==229){e.preventDefault();fn()}})}function selectText(id){$(id).focus();$(id).select()}
function isoDay(s){if(!/^\d{4}-\d{2}-\d{2}$/.test(s))throw Error('Use a real calendar date as YYYY-MM-DD.');const d=new Date(s+'T00:00:00Z');if(!Number.isFinite(d.getTime())||d.toISOString().slice(0,10)!==s)throw Error('Use a real calendar date as YYYY-MM-DD.');return d.getTime()/86400000}


const phases=['Question','Model the two-date trace','Pair comparison','Bounded answer','Exit verification'];
const sources={S01:{owner:'Official L01 library notice',claim:'“Tuesday closes at 4 p.m.”',published:'September 1, 2026',underlying:'September 1, 2026 notice',limit:'A dated hour is supplied. Current official schedule, closure notices and later updates remain unchecked.'},S02:{owner:'Community blog',claim:'“Tuesday closes at 5 p.m.”',published:'October 1, 2026',underlying:'April 1, 2024 library notice',limit:'The newer blog publication does not update the older quoted schedule. No new library confirmation is supplied.'},S03:{owner:'Undated promotional flyer',claim:'“Visit after school”',published:'Unknown / undated',underlying:'Unknown; no closing time',limit:'An invitation to visit does not supply a closing hour. Do not invent one.'}};
function source(id){const s=sources[id];txt('source-owner',id+' / '+s.owner);txt('claim',s.claim);txt('published',s.published);txt('underlying',s.underlying);txt('source-limit',s.limit);['S01','S02','S03'].forEach((x,i)=>$('source-'+(i+1)).setAttribute('aria-pressed',String(x===id)))}
['S01','S02','S03'].forEach((x,i)=>on('source-'+(i+1),'click',()=>source(x)));
function learnerView(v){$('learner-view').hidden=!v;$('teacher-view').hidden=v;$('learner-tab').setAttribute('aria-pressed',String(v));$('teacher-tab').setAttribute('aria-pressed',String(!v))}
on('learner-tab','click',()=>learnerView(true));on('teacher-tab','click',()=>learnerView(false));
function clearLearner(){$('learner-record').value='';txt('learner-error','Inputs changed; prepare the current record for teacher review.')}
for(const n of ['best-source','answer','next-check','confirmation']){on(n,'input',clearLearner);on(n,'change',clearLearner)}
on('build-answer','click',()=>{$('learner-record').value='';const id=$('best-source').value;if(!sources[id]||!$('answer').value.trim()||!$('next-check').value.trim()){txt('learner-error','Choose a source and supply your answer and next check.');return}const s=sources[id];$('learner-record').value='FICTIONAL L01 / learner-supplied reasoning for teacher review\nChosen card: '+id+'\nOwner: '+s.owner+'\nPublication: '+s.published+'\nUnderlying evidence: '+s.underlying+'\nExact supplied text: '+s.claim+'\nLearner answer: '+$('answer').value+'\nNext check proposed by learner: '+$('next-check').value+'\nActual current confirmation (user-supplied, not independently verified): '+($('confirmation').value||'NOT OBTAINED / NOT RECORDED');txt('learner-error','Record prepared for teacher review; no correctness or current-hours verification is claimed.')});
on('select-record','click',()=>selectText('learner-record'));
on('prepare','click',()=>{const values=timing();if(!scheduleValid){txt('export-error','Resolve the timetable before preparing the plan.');return}$('export-text').value='PROPOSED LIBRARY SOURCE-DATE LESSON\n'+phases.map((p,i)=>p+': '+values[i]+' min').join('\n')+'\nQuestion: What time does L01 close on Tuesdays this term?\nS01: Official notice, September 1, 2026, Tuesday closes at 4 p.m.\nS02: Community blog, October 1, 2026, quotes April 1, 2024 notice, Tuesday closes at 5 p.m.\nS03: Undated flyer, Visit after school, no closing hour.\nTeacher reference: according to supplied S01, 4 p.m.; confirm current official schedule, closures and later updates before an actual visit.\nBlank learner log: claim / source owner / publication date / quoted date / support / limitation / next check.\nActual current confirmation: ______\nActual learner reasoning: ______\nClass/access/material confirmation: TO CONFIRM.';txt('export-error','Teacher plan prepared; actual evidence remains blank.')});
source('S02');learnerView(true);

let scheduleValid=false;
function timing(){scheduleValid=false;$('export-text').value='';txt('export-error','');try{const values=phases.map((p,i)=>{const s=$('minutes-'+i).value;if(!/^\d+$/.test(s)||!Number.isSafeInteger(Number(s))||Number(s)<1||Number(s)>240)throw Error('Enter whole minutes from 1 to 240 for every phase.');return Number(s)});const b=$('budget').value;if(!/^\d+$/.test(b)||Number(b)<1||Number(b)>1200)throw Error('Enter an available budget from 1 to 1200 whole minutes.');let at=0;values.forEach((v,i)=>{txt('range-'+i,at+'–'+(at+v)+' min');at+=v});scheduleValid=at===Number(b);txt('total',scheduleValid?at+'-minute plan ready':at+' scheduled / '+b+' available');txt('schedule-error',scheduleValid?'':'Adjust the allocations or available budget before preparing a plan.');return values}catch(e){for(let i=0;i<phases.length;i++)txt('range-'+i,'Time pending');txt('total','Timing needs input');txt('schedule-error',e.message);return null}}
for(let i=0;i<phases.length;i++)on('minutes-'+i,'input',timing);on('budget','input',timing);
on('select-text','click',()=>selectText('export-text'));
timing();</script></body></html>