H1 · Weekly writing review

HTML for this example

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

<!doctype html><html lang="en-US"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>H1 · Weekly writing review</title><style>*{box-sizing:border-box}body{margin:0;background:#f1f3f5;color:#223040;font:16px/1.5 system-ui,sans-serif}main{max-width:1120px;margin:auto;padding:32px 28px 64px}h1{font-size:clamp(28px,4vw,40px);line-height:1.15;letter-spacing:-.035em;margin:6px 0 12px}h2{font-size:21px;line-height:1.3;margin:0 0 16px}h3{font-size:16px;margin:0 0 8px}p{margin:8px 0 16px}.eyebrow{text-transform:uppercase;letter-spacing:.14em;font-size:12px;font-weight:750;color:#526b83}.lede,.muted{color:#5a6a79}.small,small{font-size:13px;color:#5a6a79}.panel{border:1px solid #d6dfe5;border-radius:14px;padding:24px;background:white;margin:20px 0}.split{display:grid;grid-template-columns:1fr 1fr;gap:24px}.row{display:flex;flex-wrap:wrap;align-items:center;gap:12px}.fields{display:grid;grid-template-columns:1fr 1fr;gap:14px}.big{font-size:clamp(30px,4.8vw,54px);font-weight:750;letter-spacing:-.045em;line-height:1.2}.badge{display:inline-block;border-radius:20px;padding:4px 10px;font-size:12px;background:#e7edf4;color:#36546e;font-weight:700}.line{border-top:1px solid #dce3e8;margin-top:16px;padding-top:16px}label{display:block;font-size:14px;font-weight:650;margin:12px 0 6px}input,textarea,select,button{font:inherit}input,textarea,select{width:100%;min-height:44px;border:1px solid #a9b9c7;border-radius:7px;padding:10px;background:white;color:#223040}textarea{min-height:86px;resize:vertical}button{min-height:44px;padding:9px 14px;border:1px solid #879aac;border-radius:7px;background:#fff;color:#244966;cursor:pointer}button.primary{background:#244e72;border-color:#244e72;color:white}button:disabled{opacity:.48;cursor:default}:focus-visible{outline:3px solid #d47728;outline-offset:3px}input[type=checkbox]{width:20px;min-height:20px;height:20px;accent-color:#244e72}.check{display:flex;gap:12px;align-items:flex-start;font-weight:500}.check input{flex-shrink:0;margin-top:2px}.error{min-height:24px;color:#a33d22}.record,pre{font:13px/1.65 ui-monospace,monospace;background:#f0f3f6;border-radius:8px;padding:16px;white-space:pre-wrap;overflow-wrap:anywhere}details{margin:20px 0}summary{font-weight:650;cursor:pointer;min-height:44px}svg{display:block;width:100%;height:auto}[hidden]{display:none!important}@media(max-width:720px){main{padding:22px 16px 48px}.split{grid-template-columns:1fr;gap:0}.panel{padding:18px;margin:16px 0}.big{font-size:38px}}@media(max-width:400px){main{padding:18px 14px}.panel{padding:16px}h1{font-size:29px}.row>button{flex:1}.fields{gap:10px}}body{background:#f4f2f6;color:#322e40}.eyebrow{color:#706080}.review{border:1px solid #d8cfe2;border-radius:15px;overflow:hidden;background:white;margin-top:24px}.totals{display:grid;grid-template-columns:1fr 1fr 1.25fr;gap:24px;padding:26px;background:#f0ebf5}.totals p{margin:4px 0}.totals .big{color:#5f407d;font-size:58px}.breakdown{border-left:1px solid #cfc1da;padding-left:22px}.breakdown strong{display:inline-block;min-width:24px}.runs strong{color:#60457b;font-size:25px;display:inline-block;min-width:25px}.runs small{font-size:11px}.calendar{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:0;padding:20px 14px 10px}.day{border:0;border-right:1px solid #e5e0eb;border-radius:0;display:flex;flex-direction:column;align-items:center;gap:4px;padding:10px 4px;min-width:0;color:#433b51}.day:last-child{border-right:0}.dayName{font-size:12px;text-transform:uppercase;letter-spacing:.05em}.dayDate{font-weight:700}.daySymbol{font-size:27px;line-height:1.3;margin:8px 0}.dayStatus{font-size:12px}.day small{font-size:10px}.day.done .daySymbol{color:#3d775e}.day.notDone .daySymbol{color:#9e4a3b}.day.unrecorded{background:#fff7e8}.day.unrecorded .daySymbol{color:#97691a}.day.notScheduled{color:#7b7582;background:#f6f5f8}.legend{display:flex;flex-wrap:wrap;gap:16px;padding:12px 22px 20px;font-size:12px;color:#665d70}.feedback{background:#f2edf8;border-left:3px solid #8b6ca7;padding:12px;font-size:14px;margin-top:18px}.versions{display:grid;grid-template-columns:1fr 1fr;gap:24px;margin:12px 0}.versions h3{margin-top:10px}.future{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:8px}.future div{border-top:2px solid #c6b6d7;padding-top:10px}.future strong,.future span,.future small{display:block;font-size:11px}.confirmation{padding:16px;background:#fff4dd;border:1px solid #ddc48c;border-radius:8px}.hold{padding:12px 0;border-bottom:1px solid #ded5e7}.hold h3{font-size:14px;overflow-wrap:anywhere}.hold p{font-size:13px}button.primary{background:#654b7d;border-color:#654b7d}@media(max-width:720px){.totals{grid-template-columns:1fr 1fr;gap:16px;padding:20px}.totals .runs{grid-column:1/-1;display:grid;grid-template-columns:1fr 1fr}.runs small{grid-row:2}.calendar{grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;padding:12px}.day{border:1px solid #e5e0eb;border-radius:7px;min-height:150px}.day:last-child{border:1px solid #e5e0eb}.future{grid-template-columns:repeat(4,1fr)}.versions{gap:16px}}@media(max-width:400px){.totals{padding:18px;gap:12px}.totals .big{font-size:47px}.breakdown{padding-left:12px;font-size:12px}.calendar{grid-template-columns:repeat(3,minmax(0,1fr))}.totals .runs{display:block}.runs small{display:block}.runs p{margin-top:12px}.legend{gap:10px;padding:12px}.versions{grid-template-columns:1fr}.future{grid-template-columns:repeat(3,1fr)}}</style></head><body><main><div class="eyebrow">Fictional writing ledger · H1 · As of October 11, 2026</div><h1>Keep the unknown day visible</h1><p class="lede">Write an outline for at least 10 minutes on scheduled weekdays.</p><section class="review"><div class="totals"><div><span class="small">October 5–11 · plan v1</span><div class="big" id="doneCount">3 / 5</div><p>confirmed Done / scheduled</p></div><div class="breakdown"><p><strong id="missCount">1</strong> explicitly Not Done</p><p><strong id="unknownCount">1</strong> Unrecorded</p><p><strong id="notScheduledCount">2</strong> Not Scheduled</p></div><div class="runs"><p><strong id="latest">1</strong> latest confirmed run</p><small>Ending Friday, October 9</small><p><strong id="longest">2</strong> longest confirmed run</p><small>Scheduled opportunities, not calendar days</small></div></div><div id="calendar" class="calendar" aria-label="Weekly dated records"></div><div class="legend"><span>● Done</span><span>× Not Done</span><span>? Unrecorded</span><span>— Not Scheduled</span></div></section><section class="split"><div class="panel"><h2>Edit one dated demo record</h2><form id="entryForm"><label for="date">Local calendar date</label><input type="date" id="date" value="2026-10-08"><label for="status">User-confirmed status</label><select id="status"><option value="unrecorded">Unrecorded</option><option value="done">Done</option><option value="notDone">Not Done</option><option value="notScheduled">Not Scheduled</option></select><label for="minutes">Minutes note · optional</label><input id="minutes" inputmode="decimal" placeholder="No minutes recorded"><p class="small">0–1,440 minutes, up to two decimal places. Time never automatically marks a day Done.</p><label for="note">Record note · optional</label><textarea id="note"></textarea><div class="row"><button class="primary" type="submit">Apply to demo</button><button type="button" id="reset">Reset Demo…</button></div></form><p id="feedback" role="status" class="feedback">Thursday is unrecorded. No completion or failure has been confirmed.</p><div id="resetQuestion" hidden class="confirmation"><h3>Restore only the fictional H1 fixture?</h3><p>This discards edits in this temporary page. No personal logs or saved record are involved.</p><div class="row"><button id="confirmReset">Restore demo fixture</button><button id="cancelReset">Cancel</button></div></div></div><div class="panel"><h2>Review holds</h2><p class="small">Future or unscheduled outcomes and criterion conflicts stay outside confirmed counts until resolved.</p><div id="holds"></div><h3 class="line">Extra activity · Saturday, October 10</h3><label for="extra">Optional writing note outside plan v1</label><textarea id="extra" placeholder="Add an extra activity note"></textarea><p class="small">An extra note adds neither an opportunity nor a scheduled completion.</p><label for="reason">Personal reason for this routine</label><textarea id="reason"></textarea><label for="reflection">Weekly reflection</label><textarea id="reflection"></textarea></div></section><details open class="panel"><summary>Plan history · next week stays prospective</summary><div class="versions"><article><span class="badge">v1 · October 5–11</span><h3>Monday through Friday</h3><p>Five planned opportunities in the reviewed week. Wednesday remains an explicit Not Done.</p></article><article><span class="badge">v2 · effective October 12</span><h3>Monday · Wednesday · Friday</h3><p>Applies from October 12 onward. It does not rewrite the earlier five-day schedule.</p></article></div><div id="future" class="future"></div></details><details><summary>Definition of the counts and confirmed runs</summary><p>Done means the user confirms the outline criterion was performed; minutes support that report without measuring quality. An explicit Not Done ends a run. Unrecorded prevents an assertion of confirmed continuity without becoming a failed day. Not Scheduled days are skipped.</p><p>The latest run ends at the last scheduled opportunity, Friday, October 9. Weekends do not extend it. Every edit recomputes from stable H1/local-date keys. Repeated selections cannot add a second completion for a date.</p><p>This interactive page is a temporary reading and editing copy of a fictional fixture. Keep the definition, plan versions and dated records in the maintained spreadsheet or calendar. No reminders, personal record storage, automatic tracking, export or mastery claims are provided. Reload loses changes.</p></details></main><script>const $=id=>document.getElementById(id),put=(id,v)=>$(id).textContent=String(v),esc=s=>String(s).replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
const ASOF='2026-10-11',start='2026-10-05',end='2026-10-11',names=['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'],statusNames={done:'Done',notDone:'Not Done',unrecorded:'Unrecorded',notScheduled:'Not Scheduled'},symbols={done:'●',notDone:'×',unrecorded:'?',notScheduled:'—'},versions=[{id:'v1',effective:'2026-10-05',through:'2026-10-11',days:[1,2,3,4,5]},{id:'v2',effective:'2026-10-12',through:null,days:[1,3,5]}];
function dateParts(raw){if(!/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/.test(String(raw)))throw Error('Use a complete local calendar date.');const[y,m,d]=raw.split('-').map(Number),leap=y%4===0&&(y%100!==0||y%400===0),days=[31,leap?29:28,31,30,31,30,31,31,30,31,30,31];if(y<1||m<1||m>12||d<1||d>days[m-1])throw Error('Invalid local calendar date.');return{y,m,d}}
function weekday(raw){let{y,m,d}=dateParts(raw);if(m<3)y--;return(y+Math.floor(y/4)-Math.floor(y/100)+Math.floor(y/400)+[0,3,2,5,0,3,5,1,4,6,2,4][m-1]+d)%7}
function plan(date){dateParts(date);return versions.find(v=>date>=v.effective&&(!v.through||date<=v.through))||null}function scheduled(date){const p=plan(date);return!!p&&p.days.includes(weekday(date))}const key=date=>{dateParts(date);return'H1/'+date};
function fixture(){return{records:Object.fromEntries([['2026-10-05','done','10'],['2026-10-06','done','12'],['2026-10-07','notDone','0'],['2026-10-08','unrecorded',''],['2026-10-09','done','15'],['2026-10-10','notScheduled',''],['2026-10-11','notScheduled','']].map(([date,status,minutes])=>[key(date),{date,status,minutes,note:''}])),holds:{},extra:'',reason:'',reflection:''}}
let state=fixture();function days(first,last){const a=[];let{y,m,d}=dateParts(first);dateParts(last);while(true){const s=String(y).padStart(4,'0')+'-'+String(m).padStart(2,'0')+'-'+String(d).padStart(2,'0');if(s>last)break;a.push(s);if(a.length>366)throw Error('Review window too long.');d++;try{dateParts(String(y).padStart(4,'0')+'-'+String(m).padStart(2,'0')+'-'+String(d).padStart(2,'0'))}catch{d=1;m++;if(m>12){m=1;y++}}}return a}
function summary(s){const dates=days(start,end).filter(d=>scheduled(d)&&d<=ASOF);let done=0,notDone=0,unrecorded=0,run=0,longest=0;for(const d of dates){const record=s.records[key(d)],status=record?.status||'unrecorded';if(status==='done'){done++;run++;longest=Math.max(longest,run)}else{run=0;if(status==='notDone')notDone++;else unrecorded++}}return{opportunities:dates.length,done,notDone,unrecorded,notScheduled:days(start,end).length-dates.length,latest:run,longest,endpoint:dates.at(-1)}}
function minuteValue(raw){if(raw==='')return null;if(!/^[0-9]+(?:[.][0-9]{1,2})?$/.test(raw)||!Number.isFinite(Number(raw))||Number(raw)>1440)throw Error('Minutes must be blank or a dot decimal from 0 to 1,440, with up to two decimal places.');return Number(raw)}
function apply(s,date,status,minutes,note){dateParts(date);if(!Object.hasOwn(statusNames,status))throw Error('Choose one of the four record states.');const p=plan(date);if(!p)throw Error('No plan is defined for this date.');const min=minuteValue(minutes),record={date,status,minutes,note:String(note)},k=key(date);let hold='';if(date>ASOF)hold='Beyond the fixed October 11 as-of date; future logs remain blank.';else if(!scheduled(date)&&status!=='notScheduled')hold='Unscheduled under the effective plan. Treat as an extra activity or schedule issue.';else if(scheduled(date)&&status==='notScheduled')hold='Scheduled opportunity cannot be removed by a status edit.';else if(status==='done'&&min!==null&&min<10)hold='Done conflicts with the ten-minute outline criterion. Confirm or correct this entry.';if(hold){s.holds[k]={...record,reason:hold};return{held:true,message:'Held for review. '+hold+' The counted record is unchanged.'}}s.records[k]=record;delete s.holds[k];return{held:false,message:'Updated '+k+'. Counts and confirmed runs recomputed; nothing saved outside this page.'}}
function render(message){const r=summary(state);put('doneCount',r.done+' / '+r.opportunities);put('missCount',r.notDone);put('unknownCount',r.unrecorded);put('notScheduledCount',r.notScheduled);put('latest',r.latest);put('longest',r.longest);$('calendar').innerHTML=days(start,end).map(date=>{const a=state.records[key(date)],status=a?.status||'unrecorded',day=weekday(date);return'<button class="day '+status+'" data-date="'+date+'" aria-label="Edit '+names[day]+', October '+Number(date.slice(-2))+': '+statusNames[status]+'"><span class="dayName">'+names[day].slice(0,3)+'</span><span class="dayDate">Oct '+Number(date.slice(-2))+'</span><strong class="daySymbol" aria-hidden="true">'+symbols[status]+'</strong><span class="dayStatus">'+statusNames[status]+'</span><small>'+((a?.minutes??'')!==''?esc(a.minutes)+' min note':'No minutes note')+'</small></button>'}).join('');$('holds').innerHTML=Object.keys(state.holds).length?Object.entries(state.holds).map(([k,a])=>'<article class="hold"><h3>'+esc(k)+' · '+statusNames[a.status]+'</h3><p>'+esc(a.reason)+'</p><p class="small">Minutes: '+esc(a.minutes||'not supplied')+' · Note: '+esc(a.note||'none')+'</p><button data-load="'+a.date+'">Review this proposal</button></article>').join(''):'<p class="muted">No proposals held for review.</p>';$('future').innerHTML=days('2026-10-12','2026-10-18').map(d=>'<div><strong>'+names[weekday(d)].slice(0,3)+' '+Number(d.slice(-2))+'</strong><span>'+ (scheduled(d)?'v2 opportunity':'Not Scheduled')+'</span><small>Actual log: blank</small></div>').join('');if(message!==undefined)put('feedback',message)}
function loadDate(date,proposal=false){const a=(proposal?state.holds[key(date)]:state.records[key(date)])||{date,status:scheduled(date)?'unrecorded':'notScheduled',minutes:'',note:''};$('date').value=date;$('status').value=a.status;$('minutes').value=a.minutes;$('note').value=a.note;put('feedback',proposal?'Review proposal; the counted record is unchanged.':'Editing '+key(date)+' under '+(plan(date)?.id||'no defined plan')+'. Apply an explicit status when ready.')}
$('calendar').addEventListener('click',e=>{const b=e.target.closest?e.target.closest('[data-date]'):e.target;if(!b?.dataset.date)return;loadDate(b.dataset.date);$('status').focus()});$('holds').addEventListener('click',e=>{if(!e.target.dataset.load)return;loadDate(e.target.dataset.load,true);$('status').focus()});$('date').addEventListener('change',()=>{try{dateParts($('date').value);loadDate($('date').value)}catch(e){put('feedback',e.message)}});for(const id of ['status','minutes','note'])$(id).addEventListener(id==='status'?'change':'input',()=>put('feedback','Draft changed. Apply to update the dated demo record.'));$('entryForm').addEventListener('submit',e=>{e.preventDefault();try{const r=apply(state,$('date').value,$('status').value,$('minutes').value,$('note').value);render(r.message)}catch(err){put('feedback',err.message)}});for(const id of ['extra','reason','reflection'])$(id).addEventListener('input',()=>{state[id]=$(id).value;render(id==='extra'?'Extra note updated outside scheduled counts and runs.':'Temporary '+id+' note updated. Counts still derive from dated records.')});$('reset').addEventListener('click',()=>{$('resetQuestion').hidden=false;put('feedback','Reset is awaiting your explicit confirmation.');$('cancelReset').focus()});$('cancelReset').addEventListener('click',()=>{$('resetQuestion').hidden=true;put('feedback','Reset canceled; demo edits kept.');$('reset').focus()});$('confirmReset').addEventListener('click',()=>{state=fixture();for(const id of ['extra','reason','reflection'])$(id).value='';$('resetQuestion').hidden=true;loadDate('2026-10-08');render('Fictional fixture restored. Thursday is unrecorded; 3 of 5 confirmed.');$('reset').focus()});globalThis.Tool={dateParts,weekday,plan,scheduled,key,days,fixture,summary,apply,getState:()=>state};render();</script>
<script>
// Local calculation controls: no native form submission or sandbox permissions required.
(()=>{const form=document.getElementById('entryForm');if(!form)throw Error('Calculation form missing');const dispatch=()=>form.dispatchEvent(new Event('submit',{bubbles:true,cancelable:true}));const buttons=[...form.querySelectorAll('button')].filter(button=>button.type==='submit');if(buttons.length!==1)throw Error('Expected one calculation control');for(const button of buttons){button.type='button';button.addEventListener('click',dispatch);}form.addEventListener('keydown',event=>{if(event.key==='Enter'&&!event.isComposing&&['INPUT','SELECT'].includes(event.target.tagName)){event.preventDefault();dispatch();}});})();
</script>
</body></html>