Club roster • linking be — Build present-simple linking-be descriptions of supplied identity/location facts and maintain subject agreement and true negative facts.

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>Club roster • linking be</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}} .paper{background:white;padding:26px;border:1px solid var(--line,#d6e0e7)}.badge{display:inline-block;padding:4px 10px;border-radius:20px;background:var(--tint,#e3f2f8);font-size:13px;font-weight:700}.feedback{padding:18px;background:var(--tint,#e3f2f8);border-left:4px solid var(--accent,#176783);line-height:1.7}.schedule-row{display:grid;grid-template-columns:70px 1fr 90px;gap:14px;padding:16px 0;border-bottom:1px solid var(--line,#d6e0e7);align-items:start}.schedule-row label{margin:0}.schedule-row small{display:block;margin-top:6px}.schedule-row b{font-size:17px}.schedule-time{font-weight:700;font-variant-numeric:tabular-nums}.export{margin:24px 0}.export textarea{min-height:180px}.choice-list{display:flex;gap:10px;flex-wrap:wrap;margin:18px 0}.choice-list button{background:white;color:var(--ink,#203342);border-color:var(--line,#94a5b2)}@media(max-width:400px){.schedule-row{grid-template-columns:48px 1fr 65px;gap:9px}.schedule-row input{padding:8px}}
body{--accent:#5d4794;--bg:#f7f5fc;--tint:#ece6f7;--ink:#33284d;--line:#dcd2eb}.roster{display:grid;grid-template-columns:repeat(4,1fr);gap:12px}.roster article{background:white;border-radius:12px;padding:18px;border:1px solid #dcd2eb;border-top:5px solid #9b83c4}.roster span{font-size:13px;color:#645079;font-weight:700}.roster h2{font:23px/1.3 Georgia,serif;margin:10px 0}.roster p{font-size:14px;margin:0}.sentence{font:clamp(25px,3.5vw,36px)/1.4 Georgia,serif}.exercise{padding:28px}.hint{font-size:14px;color:#5c4676}@media(max-width:720px){.roster{grid-template-columns:1fr 1fr}.roster article{padding:15px}.roster h2{font-size:21px}}</style></head><body><main>
<header><span class="eyebrow">Club language lab / linking be</span><h1>A role. A place. The right form.</h1><p class="intro">Use the fictional roster to complete statements and questions without changing its facts.</p></header>
<section class="roster" aria-label="Fixed fictional club facts"><article><span>B1 · Narrator</span><h2>Club helper</h2><p>The speaker uses “I.”</p></article><article><span>B2 · Rin</span><h2>Editor</h2><p>One named subject.</p></article><article><span>B3 · Maya and Jo</span><h2>At the library</h2><p>Two named people.</p></article><article><span>B4 · Rin</span><h2>Not at the gym</h2><p>A negative location fact.</p></article></section>
<section class="panel exercise"><div class="controls"><button type="button" id="b1" class="secondary">B1</button><button type="button" id="b2" class="secondary">B2</button><button type="button" id="b3" class="secondary">B3</button><button type="button" id="b4" class="secondary">B4</button><button type="button" id="q2" class="secondary">Rin question</button><button type="button" id="q3" class="secondary">Library question</button></div><span class="eyebrow" id="item-label"></span><h2 id="prompt" class="sentence"></h2><div class="choice-list"><button type="button" id="answer-0"></button><button type="button" id="answer-1"></button><button type="button" id="answer-2"></button></div><p id="attempt" aria-live="polite">Not answered.</p><div class="controls"><button type="button" id="check">Check answer</button><button type="button" id="hint" class="secondary">Context hint</button><button type="button" id="retry" class="secondary">Clear choice</button><button type="button" id="reset" class="secondary">Reset worksheet</button></div><p id="hint-text" class="hint" aria-live="polite"></p><p id="feedback" class="feedback" aria-live="polite">Choose one supplied option.</p></section>
<footer>Original fictional role/location facts. Names do not imply gender or guessed pronouns. These items practice present-simple linking be; no -ing action, do/does auxiliary, open-text grading, or saved learner data is involved.</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()}

const items={b1:{fact:'B1 · narrator role',prompt:'I ___ the club helper.',options:['am','is','are'],key:0,hint:'Look at the subject: the narrator uses I.',reason:'I takes am in this present-simple linking statement.'},b2:{fact:'B2 · Rin’s role',prompt:'Rin ___ the editor.',options:['am','is','are'],key:1,hint:'Rin is one named subject; the complement is a role.',reason:'One named subject takes is here; the complement names Rin’s role.'},b3:{fact:'B3 · supplied location',prompt:'Maya and Jo ___ at the library.',options:['is','are'],key:1,hint:'The subject names two people joined by and.',reason:'The coordinated subject Maya and Jo takes are.'},b4:{fact:'B4 · preserve the negative location',prompt:'Rin ___ at the gym.',options:['is','is not'],key:1,hint:'Check the polarity of B4. A location fact does not change Rin’s editor role.',reason:'B4 says Rin is not at the gym. This does not negate Rin’s editor role.'},q2:{fact:'B2 · supporting question',prompt:'Ask whether Rin holds the supplied role.',options:['Is Rin the editor?','Does Rin the editor?','Are Rin the editor?'],key:0,hint:'Put the linking verb before the one named subject. Do not add does.',reason:'Is comes before Rin: Is Rin the editor? The roster supports Yes, Rin is.'},q3:{fact:'B3 · supporting question',prompt:'Ask about Maya and Jo’s supplied location.',options:['Is Maya and Jo at the library?','Are Maya and Jo at the library?','Do Maya and Jo at the library?'],key:1,hint:'Use the plural linking verb before the two named people.',reason:'Are comes before Maya and Jo. The roster supports Yes, Maya and Jo are.'}};let current='b1',choice=null;
function beFeedback(id,index){const i=items[id];if(index===null)return 'Not answered. Choose an option first.';if(index===i.key)return 'Correct. '+i.reason;if(id==='b4')return 'The verb agrees with Rin, but this answer contradicts the supplied negative location. '+i.reason;return 'Check the agreement or linking-question form. '+i.reason+' This feedback applies to the requested item.'}
function clearChoice(){choice=null;for(let i=0;i<3;i++)$('answer-'+i).setAttribute('aria-pressed','false');txt('attempt','Not answered.');txt('feedback','Choose one supplied option.');txt('hint-text','')}
function view(){const i=items[current];txt('item-label',i.fact);txt('prompt',i.prompt);for(let n=0;n<3;n++){$('answer-'+n).hidden=n>=i.options.length;txt('answer-'+n,i.options[n]||'')}for(const id of Object.keys(items))$(id).setAttribute('aria-pressed',String(id===current));clearChoice()}
for(let n=0;n<3;n++)on('answer-'+n,'click',()=>{if(n>=items[current].options.length)return;choice=n;for(let j=0;j<3;j++)$('answer-'+j).setAttribute('aria-pressed',String(n===j));txt('attempt',items[current].options[n]);txt('feedback','Choice changed. Check against the roster.')});for(const id of Object.keys(items))on(id,'click',()=>{current=id;view()});on('check','click',()=>txt('feedback',beFeedback(current,choice)));on('hint','click',()=>txt('hint-text',items[current].hint));on('retry','click',clearChoice);on('reset','click',()=>{current='b1';view()});view();
</script></body></html>