Workshop brochure · six-face map — Map six labeled panel faces to an outside/inside spread and validate the physical reading order before placing final copy in the chosen…
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>Workshop brochure · six-face map</title><style>*{box-sizing:border-box}body{margin:0;font:16px/1.5 Arial,sans-serif;color:var(--ink);background:var(--bg)}main{max-width:1180px;margin:auto;padding:26px 28px 50px}h1,h2,h3,p{margin:0}h1{font-size:34px;line-height:1.15;letter-spacing:-.025em}h2{font-size:23px;margin-bottom:12px}h3{font-size:17px}header{margin-bottom:22px}header p{margin-top:8px}button,input,select,textarea{font:inherit}button{cursor:pointer;min-height:44px;padding:9px 15px;border:1px solid var(--line);border-radius:7px;background:white;color:var(--ink)}button:hover{filter:brightness(.94)}button.active,.primary{background:var(--accent);color:white;border-color:var(--accent)}button:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible{outline:3px solid #117ec1;outline-offset:3px}input,select,textarea{max-width:100%;padding:9px;border:1px solid #8c9caa;border-radius:5px;background:white;color:var(--ink)}input{width:100%;min-width:0}textarea{width:100%;resize:vertical}label{display:block;font-size:14px;font-weight:bold;margin:12px 0 5px}.small,small{font-size:13px;color:var(--muted)}.eyebrow{font-size:12px;letter-spacing:.13em;text-transform:uppercase;margin-bottom:8px;font-weight:bold}.toolbar{display:flex;gap:8px;flex-wrap:wrap;margin:16px 0}.split{display:grid;grid-template-columns:1.5fr 1fr;gap:24px;align-items:start}.paper{background:white;border:1px solid var(--line);padding:25px;border-radius:10px}.fields{display:grid;grid-template-columns:1fr 1fr;gap:0 14px}.message{padding:12px 14px;border-left:4px solid var(--accent);background:var(--tint);margin:15px 0;overflow-wrap:anywhere}.big{font-size:40px;font-weight:700;line-height:1.2}table{border-collapse:collapse;width:100%;font-size:14px}th,td{text-align:left;padding:11px 8px;border-bottom:1px solid var(--line);vertical-align:top}th{font-size:12px;letter-spacing:.04em;text-transform:uppercase;color:var(--muted)}footer{margin-top:28px;padding-top:18px;border-top:1px solid var(--line);font-size:13px;color:var(--muted)}footer p+p{margin-top:8px}[hidden]{display:none!important}.stack{display:grid;gap:12px}.tag{font-size:12px;padding:4px 8px;border-radius:20px;background:var(--tint);display:inline-block}.section{margin-top:26px}.error{color:#a32727}svg{max-width:100%;height:auto;display:block}button[disabled]{opacity:.5;cursor:default}@media(max-width:800px){main{padding:22px 18px}.split{grid-template-columns:1fr}h1{font-size:30px}.paper{padding:20px}}@media(max-width:450px){main{padding:18px 12px}h1{font-size:27px}.fields{gap:0 10px}.paper{padding:16px}.big{font-size:34px}th,td{padding:9px 5px}.toolbar button{flex:1;padding:8px}}
:root{--bg:#f2f2f6;--ink:#24243d;--muted:#67677d;--accent:#5950a1;--line:#d8d5e7;--tint:#eeebfa}.spread{display:grid;grid-template-columns:repeat(3,1fr);margin-top:14px;border:1px solid #c6c1db;box-shadow:0 10px 30px #24243d10}.panel{min-height:265px;border:0;border-radius:0;border-right:1px dashed #b5afd0;padding:22px 16px;text-align:left;display:flex;flex-direction:column;gap:16px;justify-content:flex-start;background:white}.panel:last-child{border:0}.panel .face-id{font:12px Arial;letter-spacing:.08em;text-transform:uppercase;color:#5950a1}.panel .title{font:26px/1.1 Georgia,serif}.panel .copy{font:14px/1.6 Arial}.panel.selected{background:#eeebfa;box-shadow:inset 0 -5px #5950a1}.panel.cover{background:#343161;color:white}.panel.cover .face-id{color:#d8d5f4}.spread.sequence{grid-template-columns:repeat(3,1fr)}.map-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:8px}.proof-grid{display:grid;grid-template-columns:1fr 1fr;gap:0 16px}.position{font-size:12px;color:#74708d}.editor{border-top:6px solid #5950a1}#copy-body{min-height:145px}#spread-label{margin-bottom:4px}@media(max-width:600px){.spread,.spread.sequence{grid-template-columns:1fr}.panel{min-height:180px;border-right:0;border-bottom:1px dashed #b5afd0}.proof-grid{grid-template-columns:1fr}.map-grid{grid-template-columns:1fr}.panel .title{font-size:25px}}</style></head><body><main><header><div class="eyebrow">Workshop handout · six stable face IDs</div><h1>Copy first. Fold proof next.</h1><p>Review the provisional face map without mistaking a screen spread for a paper proof.</p></header><div class="toolbar" aria-label="Review view"><button type="button" id="view-outside">Outside spread</button><button type="button" id="view-inside">Inside spread</button><button type="button" id="view-sequence">Intended reading sequence</button></div><div class="split"><section><h2 id="spread-label"></h2><p class="small">Provisional right-opening convention · equal screen columns are not physical panel widths.</p><div id="spread" class="spread"><button type="button" class="panel" id="face-Cover"><span id="position-Cover" class="position"></span><span class="face-id">Cover</span><span id="title-Cover" class="title"></span><span id="body-Cover" class="copy"></span></button><button type="button" class="panel" id="face-Fold-in"><span id="position-Fold-in" class="position"></span><span class="face-id">Fold-in</span><span id="title-Fold-in" class="title"></span><span id="body-Fold-in" class="copy"></span></button><button type="button" class="panel" id="face-Inside1"><span id="position-Inside1" class="position"></span><span class="face-id">Inside1</span><span id="title-Inside1" class="title"></span><span id="body-Inside1" class="copy"></span></button><button type="button" class="panel" id="face-Inside2"><span id="position-Inside2" class="position"></span><span class="face-id">Inside2</span><span id="title-Inside2" class="title"></span><span id="body-Inside2" class="copy"></span></button><button type="button" class="panel" id="face-Inside3"><span id="position-Inside3" class="position"></span><span class="face-id">Inside3</span><span id="title-Inside3" class="title"></span><span id="body-Inside3" class="copy"></span></button><button type="button" class="panel" id="face-Back"><span id="position-Back" class="position"></span><span class="face-id">Back</span><span id="title-Back" class="title"></span><span id="body-Back" class="copy"></span></button></div><p id="proof-status" class="message" role="status"></p><p id="release-status" class="small"></p></section><aside class="paper editor"><h2 id="edit-label">Edit Cover copy</h2><label for="copy-title">Face heading</label><input id="copy-title"><label for="copy-body">Face copy</label><textarea id="copy-body"></textarea><p id="copy-status" class="small"></p><button type="button" id="reset">Reset provisional draft</button></aside></div><section class="paper section"><h2>Remap positions, preserve identities</h2><p class="small">All six IDs must occur exactly once. Mapping changes retire the earlier proof revision.</p><div class="map-grid"><div><label for="map-0">Outside left</label><input id="map-0" value="Fold-in"></div><div><label for="map-1">Outside center</label><input id="map-1" value="Back"></div><div><label for="map-2">Outside right</label><input id="map-2" value="Cover"></div><div><label for="map-3">Inside left</label><input id="map-3" value="Inside1"></div><div><label for="map-4">Inside center</label><input id="map-4" value="Inside2"></div><div><label for="map-5">Inside right</label><input id="map-5" value="Inside3"></div></div><div class="toolbar"><button type="button" id="apply-map" class="primary">Validate & apply map</button></div><p id="map-status" role="status"></p></section><section class="paper section"><h2>Paper dummy observations</h2><p class="small">Enter actual observations only after making a marked paper dummy. Recording these fields does not certify a print file.</p><div class="proof-grid"><div><label for="proof-0">Sheet / template revision and top edge</label><input id="proof-0" placeholder="Not observed"></div><div><label for="proof-1">First fold and closure direction</label><input id="proof-1" placeholder="Not observed"></div><div><label for="proof-2">Observed cover (face ID)</label><input id="proof-2" placeholder="Not observed"></div><div><label for="proof-3">Observed back (face ID)</label><input id="proof-3" placeholder="Not observed"></div><div><label for="proof-4">First revealed face</label><input id="proof-4" placeholder="Not observed"></div><div><label for="proof-5">Full inside reading order</label><input id="proof-5" placeholder="Not observed"></div><div><label for="proof-6">Upright orientation on both sides</label><input id="proof-6" placeholder="Not observed"></div><div><label for="proof-7">Required remapping or reversal</label><input id="proof-7" placeholder="Not observed"></div></div><div class="toolbar"><button type="button" id="record-proof">Record supplied observations</button></div><p id="proof-error" role="status"></p></section><footer><p>Fictional workshop. Date, location, contact and approved rules: TO CONFIRM. Provider/template, flat/finished dimensions, panel widths/tuck adjustment, fold direction, trim/bleed/safe area, stock/grain/scoring, duplex orientation and native output approval: TO CONFIRM.</p><p>This HTML copy map supports reading and editing, not physical folding simulation, PDF generation or print certification. Map/copy edits require a new fit and sequence check. Unsupplied contact/specifications block release; observations remain local and are not saved.</p></footer></main><script>'use strict';const $=id=>document.getElementById(id);const esc=s=>String(s).replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));const faceIDs=["Cover","Fold-in","Inside1","Inside2","Inside3","Back"],original=[["Workshop Station Guide","Prepare, follow the station instructions, and ask the facilitator when a step is unclear."],["Before you begin","Read the station card. Check which materials the facilitator has approved. Ask before moving equipment."],["1. Choose a station","Read its task and identify the materials it calls for before starting."],["2. Follow the task","Work through the station card. Pause and ask the facilitator if a required material or instruction is missing."],["3. Finish and return","Follow the station’s return instructions and tell the facilitator about anything that needs attention."],["Questions and next step","Contact: [TO CONFIRM]. Ask the facilitator which station or follow-up is appropriate next."]];let copies={},map=[],view='outside',selected='Cover',copyRev=1,mapRev=1,proof=null;
function validateMap(values){return values.length===6&&new Set(values).size===6&&faceIDs.every(id=>values.includes(id));}
function render(){const shown=view==='outside'?map.slice(0,3):view==='inside'?map.slice(3):faceIDs;$('spread').className='spread '+(view==='sequence'?'sequence':'');$('spread-label').textContent=view==='sequence'?'Intended editorial sequence · not observed paper order':view==='outside'?'Outside · '+map.slice(0,3).join(' / ')+' (provisional)':'Inside · '+map.slice(3).join(' / ');faceIDs.forEach(id=>{const el=$('face-'+id);el.hidden=!shown.includes(id);el.style.order=String(shown.indexOf(id));el.className='panel '+(id===selected?'selected ':'')+(id==='Cover'?'cover':'');el.setAttribute('aria-pressed',String(id===selected));$('position-'+id).textContent=shown.includes(id)?(view==='sequence'?'Reading step ':'Position ')+(shown.indexOf(id)+1):'';$('title-'+id).textContent=copies[id].title;$('body-'+id).textContent=copies[id].body;});['outside','inside','sequence'].forEach(v=>{$('view-'+v).className=v===view?'active':'';$('view-'+v).setAttribute('aria-pressed',String(v===view));});$('edit-label').textContent='Edit '+selected+' copy';$('copy-title').value=copies[selected].title;$('copy-body').value=copies[selected].body;$('copy-status').textContent='Copy revision '+copyRev+' · map revision '+mapRev;$('proof-status').textContent=!proof?'Physical proof: pending · no observations recorded.':proof.stale||proof.copyRev!==copyRev||proof.mapRev!==mapRev?'Earlier observations retained for copy '+proof.copyRev+'/map '+proof.mapRev+'; current proof stale.':proof.mismatch?'Observed cover/back mismatch: '+proof.mismatch+'. Remap positions and repeat the paper proof.':'Supplied observations recorded for current revisions; native print approval remains pending.';const empty=faceIDs.filter(id=>!copies[id].title.trim()||!copies[id].body.trim());$('release-status').textContent=empty.length?'Release blocked: empty copy on '+empty.join(', '):'Release blocked: printer specifications/native approval unconfirmed'+(copies.Back.body.includes('[TO CONFIRM]')?' and actual contact missing.':'. Contact still requires organizer review.');}
function choose(id){selected=id;render();}function edit(){copies[selected]={title:$('copy-title').value,body:$('copy-body').value};copyRev++;render();}function reset(){copies=Object.fromEntries(faceIDs.map((id,i)=>[id,{title:original[i][0],body:original[i][1]}]));map=['Fold-in','Back','Cover','Inside1','Inside2','Inside3'];view='outside';selected='Cover';copyRev=mapRev=1;proof=null;map.forEach((id,i)=>$('map-'+i).value=id);for(let i=0;i<8;i++)$('proof-'+i).value='';$('map-status').textContent='';$('proof-error').textContent='';render();}faceIDs.forEach(id=>$('face-'+id).addEventListener('click',()=>choose(id)));['outside','inside','sequence'].forEach(v=>$('view-'+v).addEventListener('click',()=>{view=v;render();}));$('copy-title').addEventListener('input',edit);$('copy-body').addEventListener('input',edit);$('apply-map').addEventListener('click',()=>{const candidate=Array.from({length:6},(_,i)=>$('map-'+i).value.trim());if(!validateMap(candidate)){$('map-status').textContent='Map rejected: use each of the six stable face IDs exactly once. Current map unchanged.';return;}map=candidate;mapRev++;$('map-status').textContent='Map updated. Repeat the paper proof.';render();});$('record-proof').addEventListener('click',()=>{const observations=Array.from({length:8},(_,i)=>$('proof-'+i).value.trim());if(observations.some(v=>!v)){$('proof-error').textContent='Record all eight actual observations; blank fields are not a passed fold check.';return;}proof={copyRev,mapRev,observations,mismatch:[observations[2].toLowerCase()!=='cover'?'cover is '+observations[2]:'',observations[3].toLowerCase()!=='back'?'back is '+observations[3]:''].filter(Boolean).join('; ')};$('proof-error').textContent='Supplied observations retained locally; no printer certification.';render();});for(let i=0;i<8;i++)$('proof-'+i).addEventListener('input',()=>{if(proof)proof.stale=true;$('proof-error').textContent='Observations edited; record the supplied observations again.';render();});for(let i=0;i<6;i++)$('map-'+i).addEventListener('input',()=>{$('map-status').textContent='Unapplied map proposal. The preview still shows the last committed map.';});$('reset').addEventListener('click',reset);reset();globalThis.App={validateMap,reset,render,choose,getState:()=>({map:[...map],copies:JSON.parse(JSON.stringify(copies)),copyRev,mapRev,proof,view})};</script></body></html>