A key that knows when to stop — Build and peer-test a written dichotomous key for a bounded set of original specimen drawings, record each route and stop when a trait is…

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>A key that knows when to stop</title><style>*{box-sizing:border-box}body{margin:0;font:16px/1.55 system-ui,sans-serif;color:var(--ink);background:var(--bg)}main{max-width:1120px;margin:auto;padding:30px}h1{font-size:clamp(30px,4vw,44px);line-height:1.12;letter-spacing:-.035em;margin:5px 0 12px}h2{font-size:23px;line-height:1.3;margin:0 0 15px}h3{font-size:17px;margin:0 0 8px}p{margin:0 0 12px}.eyebrow{font-size:11px;text-transform:uppercase;letter-spacing:.13em;font-weight:750;color:var(--accent)}.small,footer{font-size:13px;color:var(--muted)}footer{margin:27px 0}.panel{background:white;border:1px solid var(--line);padding:23px;border-radius:9px;margin-top:22px}.two{display:grid;grid-template-columns:1.35fr 1fr;gap:23px}.row{display:flex;gap:9px;flex-wrap:wrap;align-items:center}button{font:700 14px system-ui;min-height:44px;padding:10px 16px;border:1px solid var(--accent);background:var(--accent);color:white;border-radius:5px;cursor:pointer}button.secondary{color:var(--accent);background:white}button[aria-pressed=true]{box-shadow:inset 0 0 0 2px var(--ink)}button:disabled{opacity:.45;cursor:default}:focus-visible{outline:3px solid #d28b25;outline-offset:3px}label{display:block;font-size:13px;font-weight:650}input,textarea,select{display:block;width:100%;padding:11px;font:16px/1.5 system-ui;color:var(--ink);background:white;border:1px solid #9baab6;border-radius:4px;margin:6px 0 15px}textarea{min-height:100px;resize:vertical}.error{color:#a02b20;font-size:14px;min-height:23px}.result{font-size:40px;line-height:1.25;letter-spacing:-.04em;font-weight:750}.notice{padding:15px;background:#fff3db;border-left:4px solid #cf9d3e;font-size:14px;margin:16px 0}details{margin-top:23px;border-top:1px solid var(--line);padding-top:15px}summary{cursor:pointer;min-height:44px;font-weight:700}.time-row{display:grid;grid-template-columns:75px 1fr 70px;gap:14px;padding:15px 0;border-top:1px solid var(--line)}.time-row p{font-size:14px;margin:5px 0}.time-row input{margin:0;text-align:center}.range{font-size:13px;font-weight:700;color:var(--accent)}[hidden]{display:none!important}pre{white-space:pre-wrap;overflow-wrap:anywhere;font:13px/1.6 ui-monospace,Consolas,monospace} @media(max-width:740px){main{padding:23px 16px}.two{grid-template-columns:1fr;gap:0}.panel{padding:20px}}@media(max-width:380px){main{padding:20px 12px}.panel{padding:17px}.time-row{grid-template-columns:1fr 65px}.range{grid-column:1/-1}.row button{flex:1}}:root{--ink:#304b38;--bg:#f5f7ef;--accent:#587b48;--muted:#718169;--line:#d1dcc9}h1{font-family:Georgia,serif;font-weight:500}.drawings{display:grid;grid-template-columns:repeat(3,1fr);gap:13px;margin:24px 0}.drawing{padding:14px;background:white;border:1px solid var(--line);border-radius:8px}.drawing strong{display:block}.drawing svg{display:block;width:100%;height:155px}.route{padding:22px;background:#e5eedb;border-radius:9px;margin-top:18px}.route .result{font-size:29px}.route p{font-size:14px}.record{min-height:115px}@media(max-width:600px){.drawings{grid-template-columns:repeat(2,1fr)}.drawing{padding:10px}.drawing svg{height:145px}}</style></head><body><main><p class="eyebrow">Observable-trait key · fictional drawings, not species</p><h1>A key that knows when to stop.</h1><p>Build paired choices for A–D; keep “outside this key” and “insufficient observation” as valid stopping outcomes.</p><section class="drawings"><article class="drawing" id="drawing-A"><strong>Drawing A</strong><svg viewBox="0 0 260 230" role="img" aria-label="Drawing A: broad flat smooth blades; paired attachments at same heights"><path d="M130 210V25" stroke="#537451" stroke-width="5"/><path d="M0 0 C28 -32 62 -35 82 0 C56 30 27 25 0 0Z" transform="translate(130 75) scale(-1 .72)" fill="#a9c994" stroke="#476b47" stroke-width="2"/><path d="M0 0 C28 -32 62 -35 82 0 C56 30 27 25 0 0Z" transform="translate(130 75) scale(1 .72)" fill="#a9c994" stroke="#476b47" stroke-width="2"/><path d="M0 0 C28 -32 62 -35 82 0 C56 30 27 25 0 0Z" transform="translate(130 150) scale(-1 .72)" fill="#a9c994" stroke="#476b47" stroke-width="2"/><path d="M0 0 C28 -32 62 -35 82 0 C56 30 27 25 0 0Z" transform="translate(130 150) scale(1 .72)" fill="#a9c994" stroke="#476b47" stroke-width="2"/></svg></article><article class="drawing" id="drawing-B"><strong>Drawing B</strong><svg viewBox="0 0 260 230" role="img" aria-label="Drawing B: broad flat smooth blades; alternate attachments at different heights"><path d="M130 210V25" stroke="#537451" stroke-width="5"/><path d="M0 0 C28 -32 62 -35 82 0 C56 30 27 25 0 0Z" transform="translate(130 65) scale(-1 .72)" fill="#a9c994" stroke="#476b47" stroke-width="2"/><path d="M0 0 C28 -32 62 -35 82 0 C56 30 27 25 0 0Z" transform="translate(130 100) scale(1 .72)" fill="#a9c994" stroke="#476b47" stroke-width="2"/><path d="M0 0 C28 -32 62 -35 82 0 C56 30 27 25 0 0Z" transform="translate(130 140) scale(-1 .72)" fill="#a9c994" stroke="#476b47" stroke-width="2"/><path d="M0 0 C28 -32 62 -35 82 0 C56 30 27 25 0 0Z" transform="translate(130 175) scale(1 .72)" fill="#a9c994" stroke="#476b47" stroke-width="2"/></svg></article><article class="drawing" id="drawing-C"><strong>Drawing C</strong><svg viewBox="0 0 260 230" role="img" aria-label="Drawing C: broad flat lobed blades; paired attachments at same heights"><path d="M130 210V25" stroke="#537451" stroke-width="5"/><path d="M0 0 L18 -10 L13 -24 L35 -18 L42 -34 L54 -20 L70 -24 L65 -9 L84 0 L65 11 L69 27 L49 20 L40 34 L30 20 L13 25 L17 10Z" transform="translate(130 75) scale(-1 .72)" fill="#8db487" stroke="#476b47" stroke-width="2"/><path d="M0 0 L18 -10 L13 -24 L35 -18 L42 -34 L54 -20 L70 -24 L65 -9 L84 0 L65 11 L69 27 L49 20 L40 34 L30 20 L13 25 L17 10Z" transform="translate(130 75) scale(1 .72)" fill="#8db487" stroke="#476b47" stroke-width="2"/><path d="M0 0 L18 -10 L13 -24 L35 -18 L42 -34 L54 -20 L70 -24 L65 -9 L84 0 L65 11 L69 27 L49 20 L40 34 L30 20 L13 25 L17 10Z" transform="translate(130 150) scale(-1 .72)" fill="#8db487" stroke="#476b47" stroke-width="2"/><path d="M0 0 L18 -10 L13 -24 L35 -18 L42 -34 L54 -20 L70 -24 L65 -9 L84 0 L65 11 L69 27 L49 20 L40 34 L30 20 L13 25 L17 10Z" transform="translate(130 150) scale(1 .72)" fill="#8db487" stroke="#476b47" stroke-width="2"/></svg></article><article class="drawing" id="drawing-D"><strong>Drawing D</strong><svg viewBox="0 0 260 230" role="img" aria-label="Drawing D: broad flat lobed blades; alternate attachments at different heights"><path d="M130 210V25" stroke="#537451" stroke-width="5"/><path d="M0 0 L18 -10 L13 -24 L35 -18 L42 -34 L54 -20 L70 -24 L65 -9 L84 0 L65 11 L69 27 L49 20 L40 34 L30 20 L13 25 L17 10Z" transform="translate(130 65) scale(-1 .72)" fill="#8db487" stroke="#476b47" stroke-width="2"/><path d="M0 0 L18 -10 L13 -24 L35 -18 L42 -34 L54 -20 L70 -24 L65 -9 L84 0 L65 11 L69 27 L49 20 L40 34 L30 20 L13 25 L17 10Z" transform="translate(130 100) scale(1 .72)" fill="#8db487" stroke="#476b47" stroke-width="2"/><path d="M0 0 L18 -10 L13 -24 L35 -18 L42 -34 L54 -20 L70 -24 L65 -9 L84 0 L65 11 L69 27 L49 20 L40 34 L30 20 L13 25 L17 10Z" transform="translate(130 140) scale(-1 .72)" fill="#8db487" stroke="#476b47" stroke-width="2"/><path d="M0 0 L18 -10 L13 -24 L35 -18 L42 -34 L54 -20 L70 -24 L65 -9 L84 0 L65 11 L69 27 L49 20 L40 34 L30 20 L13 25 L17 10Z" transform="translate(130 175) scale(1 .72)" fill="#8db487" stroke="#476b47" stroke-width="2"/></svg></article><article class="drawing" id="drawing-E"><strong>Drawing E</strong><svg viewBox="0 0 260 230" role="img" aria-label="Drawing E: needle-only sprig, no broad flat blades"><path d="M130 205V25" stroke="#537451" stroke-width="5"/><path d="M130 55 l-35 -20 M130 55 l35 -20" stroke="#477457" stroke-width="4"/><path d="M130 85 l-35 -20 M130 85 l35 -20" stroke="#477457" stroke-width="4"/><path d="M130 115 l-35 -20 M130 115 l35 -20" stroke="#477457" stroke-width="4"/><path d="M130 145 l-35 -20 M130 145 l35 -20" stroke="#477457" stroke-width="4"/><path d="M130 175 l-35 -20 M130 175 l35 -20" stroke="#477457" stroke-width="4"/></svg></article><article class="drawing" id="drawing-F"><strong>Drawing F</strong><svg viewBox="0 0 260 230" role="img" aria-label="Drawing F: broad flat lobed blades; attachment points hidden"><path d="M130 210V25" stroke="#537451" stroke-width="5"/><path d="M0 0 L18 -10 L13 -24 L35 -18 L42 -34 L54 -20 L70 -24 L65 -9 L84 0 L65 11 L69 27 L49 20 L40 34 L30 20 L13 25 L17 10Z" transform="translate(130 65) scale(-1 .72)" fill="#8db487" stroke="#476b47" stroke-width="2"/><path d="M0 0 L18 -10 L13 -24 L35 -18 L42 -34 L54 -20 L70 -24 L65 -9 L84 0 L65 11 L69 27 L49 20 L40 34 L30 20 L13 25 L17 10Z" transform="translate(130 100) scale(1 .72)" fill="#8db487" stroke="#476b47" stroke-width="2"/><path d="M0 0 L18 -10 L13 -24 L35 -18 L42 -34 L54 -20 L70 -24 L65 -9 L84 0 L65 11 L69 27 L49 20 L40 34 L30 20 L13 25 L17 10Z" transform="translate(130 140) scale(-1 .72)" fill="#8db487" stroke="#476b47" stroke-width="2"/><path d="M0 0 L18 -10 L13 -24 L35 -18 L42 -34 L54 -20 L70 -24 L65 -9 L84 0 L65 11 L69 27 L49 20 L40 34 L30 20 L13 25 L17 10Z" transform="translate(130 175) scale(1 .72)" fill="#8db487" stroke="#476b47" stroke-width="2"/><rect x="102" y="25" width="56" height="190" rx="5" fill="#c4c7c2"/><path d="M110 35l40 40m-40 5l40 40m-40 5l40 40m-40 5l30 30" stroke="#a5aaa3" stroke-width="3"/><text x="130" y="225" text-anchor="middle" font-size="12">Attachments concealed</text></svg></article></section><section class="panel"><h2>Teacher route review · separate from student construction</h2><div id="card-buttons" class="row"></div><div class="route"><p id="route-path">F · 1 → 2 → 3 → stop</p><div id="route-result" class="result">Insufficient observation</div><p id="route-evidence">F has lobed blades, but its attachment points are concealed.</p></div><p class="small">Paired = opposite attachments at the same stem height. Alternate = successive attachments at different heights. Smooth edges are uninterrupted; lobed edges have inward notches. Required hidden, unclear or unmatched traits always stop the route.</p></section><div class="two"><section class="panel"><h2>Construct and peer-test your written key</h2><p>Start with A–D. Draft paired choices, numbered destinations and terminal drawing IDs. Exchange keys and test all six cards; another valid branch order is possible.</p><label for="student-key">Student’s own branching / written-key draft<textarea id="student-key" class="record" placeholder="Blank before construction"></textarea></label><label for="route-log">Actual route log · card, steps, visible evidence, outcome, unclear choice, repair<textarea id="route-log" class="record"></textarea></label></section><section class="panel"><h2>Repair one unsupported branch</h2><label for="revision">Problem → evidence reference → change → retested card → actual retest route<textarea id="revision" class="record"></textarea></label><label for="exit-response">Exit: trace C with evidence; explain why F must stop<textarea id="exit-response" class="record"></textarea></label><p class="small">Actual student observations, routes and explanations stay blank until used. A teacher key reveal does not supply student evidence.</p></section></div><section class="panel"><div class="row" style="justify-content:space-between"><h2>Observe → construct → peer test → revise</h2><strong id="total" class="result">45 minutes</strong></div><div id="time-rows"></div><p id="time-error" class="error" role="alert"></p><button id="restore-times" type="button" class="secondary">Restore 45-minute sequence</button></section><details><summary>Teacher written key and six-card audit</summary><p>1: broad flat blades → 2; needle-only/no broad flat blades → outside this key.<br>2: lobed → 3; smooth → 4.<br>3: paired → C; alternate → D.<br>4: paired → A; alternate → B.<br>At any step, a hidden/unclear/unmatched required trait means stop.</p><p>A 1–2–4–A; B 1–2–4–B; C 1–2–3–C; D 1–2–3–D. E outside at1. F 1–2–3–stop: arrangement hidden. Different student branch orders need route testing, not exact-string grading.</p></details><section class="panel"><h2>Answer-free student text</h2><label for="student-copy">Manual-copy observation / route sheet<textarea id="student-copy" readonly></textarea></label><button id="select-copy" type="button">Select student text</button><p id="select-status" class="small" role="status"></p></section><footer>Original SVG drawing cards and HTML teacher-review companion to the paper lesson. No actual species identification, collection/lab work, automatic grading, saved record or measured learning claim. Confirm age, standards, materials and accommodations; verify traits and biological accuracy if replacing drawings with real images. Screen and drawing clarity review remain required before class.</footer></main><script>
'use strict';const $=id=>document.getElementById(id);

const matrix={A:{blade:'broad',margin:'smooth',arrangement:'paired'},B:{blade:'broad',margin:'smooth',arrangement:'alternate'},C:{blade:'broad',margin:'lobed',arrangement:'paired'},D:{blade:'broad',margin:'lobed',arrangement:'alternate'},E:{blade:'needle',margin:null,arrangement:null},F:{blade:'broad',margin:'lobed',arrangement:null}};let card='F';function trace(t){const path=[1];if(t.blade==='needle')return{path:[1,'outside this key'],outcome:'Outside this key',reason:'No broad flat blades; do not force an A–D identification.'};if(t.blade!=='broad')return{path:[1,'stop'],outcome:'Insufficient observation',reason:'Blade type is hidden, unclear or unmatched.'};path.push(2);if(!['smooth','lobed'].includes(t.margin))return{path:[...path,'stop'],outcome:'Insufficient observation',reason:'Margin is hidden, unclear or unmatched.'};path.push(t.margin==='lobed'?3:4);if(!['paired','alternate'].includes(t.arrangement))return{path:[...path,'stop'],outcome:'Insufficient observation',reason:'Attachment points are concealed or unclear. Do not choose paired or alternate by default.'};const id=t.margin==='lobed'?(t.arrangement==='paired'?'C':'D'):(t.arrangement==='paired'?'A':'B');return{path:[...path,id],outcome:'Drawing '+id,reason:'Supported by visible broad flat blades, '+t.margin+' margin and '+t.arrangement+' attachment arrangement.'};}function render(){const r=trace(matrix[card]);$('route-path').textContent=card+' · '+r.path.join(' → ');$('route-result').textContent=r.outcome;$('route-evidence').textContent=r.reason;for(const id of Object.keys(matrix))$('card-'+id).setAttribute('aria-pressed',String(id===card));}for(const id of Object.keys(matrix)){const b=document.createElement('button');b.id='card-'+id;b.type='button';b.className='secondary';b.textContent=id;b.setAttribute('aria-label','Review drawing '+id);b.addEventListener('click',()=>{card=id;render();});$('card-buttons').appendChild(b);}$('student-copy').value=['OBSERVABLE-TRAIT KEY · ORIGINAL DRAWINGS','Observe A–D and record visible blade, margin and attachment traits.','Build paired choices with numbered destinations and drawing-ID terminals.','Peer-test every card including challenge cards E and F. Never guess a hidden trait.','Observation: card __; visible blade __; margin __; attachment evidence __.','Route: card __; steps visited __; evidence per choice __; outcome __.','Unclear choice __; proposed repair __.','Revision: problem __; evidence reference __; change __; retested card __; actual route __.','Exit: trace C using visible evidence. Explain why F must stop.','Teacher confirms required materials, age suitability and access supports.'].join('\n');$('select-copy').addEventListener('click',()=>{$('student-copy').focus();$('student-copy').select();$('select-status').textContent='Answer-free student text selected. Copy manually; drawing cards must accompany the paper task.';});const target=45,blocks=[['Observe','Introduce A–D and visible traits; students record only what is observable.',5],['Model','Demonstrate one paired decision and numbered destination; ask for evidence.',8],['Construct','Pairs draft branches, then translate into written numbered choices.',12],['Peer routes','Exchange keys; include E/F; log routes and unsupported choices.',10],['Revise','Repair the specific branch or destination; retain evidence and retest route.',6],['Exit','Trace C with evidence and explain a stopping case without forced identification.',4]];render();function schedule(values){if(values.some(v=>!/^\d+$/.test(v.trim())||+v<1||+v>180))return{error:'Enter whole positive minutes from 1 to 180 for every phase.'};let n=0;return{total:values.reduce((a,b)=>a+(+b),0),ranges:values.map(v=>{const s=n;n+=+v;return s+'–'+n+' min';})};}function timing(){const r=schedule(blocks.map((_,i)=>$('minutes-'+i).value));$('total').textContent=r.error?'Pending':r.total+' minutes';$('time-error').textContent=r.error||(r.total===target?'':'Review the edited length: original '+target+' minutes.');blocks.forEach((_,i)=>$('range-'+i).textContent=r.error?'Not calculated':r.ranges[i]);}blocks.forEach((b,i)=>{const row=document.createElement('div');row.className='time-row';const range=document.createElement('span');range.id='range-'+i;range.className='range';row.appendChild(range);const content=document.createElement('div'),h=document.createElement('strong'),p=document.createElement('p');h.textContent=b[0];p.textContent=b[1];content.appendChild(h);content.appendChild(p);row.appendChild(content);const label=document.createElement('label');label.textContent='Minutes';label.setAttribute('for','minutes-'+i);const input=document.createElement('input');input.id='minutes-'+i;input.value=String(b[2]);input.setAttribute('aria-label',b[0]+' minutes');input.setAttribute('inputmode','numeric');input.addEventListener('input',timing);label.appendChild(input);row.appendChild(label);$('time-rows').appendChild(row);});$('restore-times').addEventListener('click',()=>{blocks.forEach((b,i)=>$('minutes-'+i).value=String(b[2]));timing();});timing();
</script></body></html>