← back to Dw Photo Capture

public/kid-simple/v4-level-up.html

445 lines

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>v4 — Level Up</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,500;9..144,700&family=Nunito:wght@500;700;800;900&display=swap" rel="stylesheet">
<style>
  :root{
    --plum:#4A2E4E; --gold:#E8B84B; --cream:#F5EFE6; --green:#59B37A;
    --ink:#2B2B2B;
  }
  *{box-sizing:border-box;}
  html,body{margin:0;padding:0;background:#ddd;font-family:'Nunito',sans-serif;color:var(--ink);}
  body{display:flex;align-items:flex-start;justify-content:center;min-height:100vh;padding:20px 0;}
  .phone{
    width:390px; min-height:780px; background:var(--cream);
    border-radius:36px; overflow:hidden; box-shadow:0 24px 60px rgba(0,0,0,.28);
    display:flex; flex-direction:column; position:relative;
  }
  header{
    background:var(--plum); color:#fff; padding:12px 14px; gap:8px;
    display:flex; align-items:center; justify-content:space-between; min-height:56px;
  }
  header .mark{font-family:'Fraunces',serif; font-weight:700; font-size:18px; letter-spacing:.5px;}
  .navbtn{
    background:rgba(255,255,255,.14);border:none;color:#fff;font-size:16px;font-weight:800;cursor:pointer;
    min-width:56px;min-height:56px;line-height:1;border-radius:14px; padding:0 14px;
    font-family:'Nunito',sans-serif; display:inline-flex; align-items:center; justify-content:center; gap:4px;
  }
  .navbtn:hover{background:rgba(255,255,255,.24);}
  .navbtn:active{transform:scale(.97);}
  .screen{flex:1; display:flex; flex-direction:column; gap:16px; padding:22px 20px 28px; overflow-y:auto;}
  h1.title{font-family:'Fraunces',serif; font-weight:700; font-size:26px; margin:0 0 2px; color:var(--plum);}
  p.sub{margin:0; font-size:17px; color:#4a4a4a;}
  .dots{display:flex; gap:8px; margin:4px 0 6px;}
  .dots span{width:16px;height:16px;border-radius:50%;background:#e2d9c9;border:2px solid #d8cdb8;}
  .dots span.filled{background:var(--gold); border-color:var(--gold);}
  button{font-family:'Nunito',sans-serif; cursor:pointer;}
  .btn{
    min-height:56px; width:100%; border-radius:18px; font-size:18px; font-weight:800;
    border:none; display:flex; align-items:center; justify-content:center; gap:8px; padding:0 16px;
  }
  .btn:active{transform:scale(.97);}
  .btn-primary{background:var(--gold); color:#3a2a06;}
  .btn-secondary{background:#fff; color:var(--plum); border:2px solid var(--plum);}
  .btn-ghost{background:transparent; color:var(--plum); text-decoration:underline; min-height:44px;}
  .btn:focus-visible, a:focus-visible, .brandcard:focus-visible, .resultcard:focus-visible,
  .navbtn:focus-visible, .demotoggle:focus-visible, .codeinput:focus-visible, .searchbox:focus-visible,
  .toastundo:focus-visible{
    outline:3px solid #2B6CB0; outline-offset:2px;
  }
  .card{background:#fff; border-radius:20px; padding:18px; box-shadow:0 6px 18px rgba(0,0,0,.08);}
  .levelcard{background:#fff; border-radius:20px; padding:18px; display:flex; flex-direction:column; gap:10px; box-shadow:0 6px 18px rgba(0,0,0,.08);}
  .levelcard h2{margin:0; font-family:'Fraunces',serif; font-size:20px; color:var(--plum);}
  .levelcard .startrow{display:flex; align-items:center; justify-content:space-between; gap:12px; margin-top:8px;}
  .camview{
    flex:1; min-height:260px; background:#1c1c1e; border-radius:20px; position:relative;
    display:flex; align-items:center; justify-content:center; color:#eee; overflow:hidden;
  }
  .camview .hint{position:absolute; top:14px; left:0; right:0; text-align:center; font-size:15px; color:#f0e8d8;}
  .camview .status{font-size:20px; font-weight:700;}
  .shutter{
    position:absolute; bottom:20px; left:50%; transform:translateX(-50%);
    width:76px; height:76px; border-radius:50%; background:var(--gold); border:5px solid #fff;
    font-size:30px; display:flex; align-items:center; justify-content:center;
  }
  .shutter:active{transform:translateX(-50%) scale(.97);}
  .swatch{width:100%; height:180px; border-radius:16px;}
  .sw1{background:repeating-linear-gradient(45deg,#8FAE8B 0 18px,#7c9c78 18px 36px), radial-gradient(circle at 30% 30%, #a9c7a4, transparent 60%);}
  .sw2{background:repeating-linear-gradient(-45deg,#7C93B0 0 16px,#617a99 16px 32px);}
  .sw3{background:repeating-linear-gradient(90deg,#D9A441 0 14px,#c98f2c 14px 28px), radial-gradient(circle at 70% 40%, #e9c06b, transparent 55%);}
  .brandgrid{display:grid; grid-template-columns:1fr 1fr; gap:12px;}
  .brandcard{
    min-height:64px; border-radius:16px; background:#fff; border:2px solid #e3d9c6;
    font-size:17px; font-weight:700; color:var(--plum); display:flex; align-items:center; justify-content:center; text-align:center; padding:8px;
  }
  .brandcard:active{transform:scale(.97);}
  .brandcard.selected{border-color:var(--gold); background:#fff7e6;}
  .sectionlabel{margin:0; font-size:14px; font-weight:800; letter-spacing:.04em; text-transform:uppercase; color:#8a7a5a;}
  .searchbox{
    width:100%; min-height:56px; border-radius:16px; border:2px solid #e3d9c6; padding:0 16px;
    font-size:17px; font-family:'Nunito',sans-serif; background:#fff; color:var(--ink);
  }
  .codeinput{
    width:100%; min-height:56px; border-radius:16px; border:2px solid #e3d9c6; padding:0 16px;
    font-size:17px; font-family:'Nunito',sans-serif; background:#fff; color:var(--ink);
  }
  .demotoggle{
    background:none; border:2px dashed #cbbfa0; color:#4a4a4a; font-size:17px; font-weight:700;
    border-radius:12px; padding:8px 12px; min-height:56px; align-self:flex-start;
  }
  .demotoggle:active{transform:scale(.97);}
  .itemname{font-size:19px; font-weight:800; margin:0 0 2px;}
  .itemmeta{font-size:17px; color:#5a5a5a; margin:0;}
  .resultlist{display:flex; flex-direction:column; gap:10px;}
  .resultcard{
    display:flex; gap:12px; align-items:center; background:#fff; border-radius:16px; padding:10px;
    border:2px solid #eee; min-height:72px; text-align:left; width:100%;
  }
  .resultcard:active{transform:scale(.97);}
  .resultcard .thumb{width:52px; height:52px; border-radius:10px; flex:none;}
  .confetti-banner{
    background:var(--green); color:#fff; border-radius:18px; padding:18px; text-align:center;
    font-family:'Fraunces',serif; font-size:22px; font-weight:700;
  }
  .row{display:flex; gap:12px;}
  .row .btn{flex:1;}
  .homegrid{display:flex; flex-direction:column; gap:16px;}
  .sr-only{position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0;}
  .toast{
    position:absolute; left:16px; right:16px; bottom:16px; background:#232323; color:#fff;
    border-radius:14px; padding:14px 18px; display:flex; align-items:center; justify-content:space-between;
    gap:12px; font-size:16px; font-weight:700; opacity:0; transform:translateY(12px); pointer-events:none;
    transition:opacity .25s ease, transform .25s ease; z-index:30;
  }
  .toast.show{opacity:1; transform:translateY(0); pointer-events:auto;}
  .toastundo{background:none; border:none; color:var(--gold); font-weight:800; font-size:16px; min-height:56px; padding:0 6px;}
  @media (prefers-reduced-motion: reduce){
    *{transition:none !important; animation:none !important;}
  }
</style>
</head>
<body>
<div class="phone">
  <header>
    <button class="navbtn" aria-label="Back" onclick="goBack()" id="backBtn">‹ Back</button>
    <span class="mark">DW · Photo</span>
    <button class="navbtn" aria-label="Home" onclick="go('home')" id="homeBtnTop">⌂ Home</button>
  </header>
  <div class="screen" id="screen" role="main"></div>
  <div class="toast" id="toast" role="status"></div>
  <div class="sr-only" id="live" aria-live="polite"></div>
</div>

<script>
const JOBS = [
  {id:'new', title:'Add New Item', emoji:'🆕', levels:3},
  {id:'update', title:'Update an Item', emoji:'🔄', levels:3},
  {id:'find', title:'Find an Item', emoji:'🔍', levels:2},
  {id:'sample', title:'A Sample Came In', emoji:'📦', levels:2},
];
const BRANDS = ['York Wallcoverings','Brewster Home','Kravet','Cole & Son','Thibaut','Ronald Redding'];
const RECENT_BRANDS = BRANDS.slice(0,3);
const MOCK_ITEMS = [
  {name:'Palm Damask', color:'Sage', code:'DW-10423', sw:'sw1', brand:'York Wallcoverings'},
  {name:'Trellis Weave', color:'Indigo', code:'DW-88214', sw:'sw2', brand:'Cole & Son'},
  {name:'Linen Bloom', color:'Blush', code:'DW-55071', sw:'sw3', brand:'Thibaut'},
];
const DONE_TOAST = {'new-done':'Saved','update-done':'Updated','sample-done':'Marked Received'};

const state = {screen:'home', job:null, brand:null, item:null, demoNoMatch:false, checkNext:null, checkRetake:null, confirmScreen:null};
let historyStack = [];
let toastTimer = null;

function announce(text){ const el=document.getElementById('live'); if(el) el.textContent = text; }

function go(screen){
  if(screen==='home'){ historyStack = []; }
  else if(state.screen && state.screen!==screen){
    if(String(screen).endsWith('-done')) state.confirmScreen = state.screen;
    historyStack.push(state.screen);
  }
  state.screen = screen;
  render();
}
function goReplace(screen){ state.screen = screen; render(); }
function goBack(){
  if(historyStack.length){ state.screen = historyStack.pop(); render(); }
  else go('home');
}
function startJob(id){ state.job = id; state.brand=null; state.item=null; go(id+'-1'); }
function jobTitle(){ return JOBS.find(j=>j.id===state.job).title; }
function jobLevels(){ return JOBS.find(j=>j.id===state.job).levels; }

function dots(filled){
  const total = jobLevels();
  let out = '<div class="dots">';
  for(let i=1;i<=total;i++) out += `<span class="${i<=filled?'filled':''}"></span>`;
  out += '</div>';
  return out;
}
function levelFromScreen(scr){ const m = /-(\d+)$/.exec(scr||''); return m ? parseInt(m[1],10) : null; }

function levelHeader(n, name){
  return `<h1 class="title">${jobTitle()}</h1>${dots(n)}<p class="sub"><strong>Level ${n} of ${jobLevels()}</strong> — ${name}</p>`;
}

function cameraScreen(hint, onShoot){
  return `
    <div class="camview">
      <div class="hint">${hint}</div>
      <div class="status" id="camStatus">Tap the shutter</div>
      <button class="shutter" aria-label="Snap It" onclick="${onShoot}">📸</button>
    </div>`;
}
function shoot(cb, loadingLabel){
  const fromScreen = state.screen;
  const st = document.getElementById('camStatus');
  const btn = document.querySelector('.shutter');
  if(btn) btn.disabled = true;
  if(st) st.textContent = 'Snapping…';
  announce('Snapping photo…');
  setTimeout(()=>{
    if(state.screen!==fromScreen) return; // user navigated away mid-shot
    const item = MOCK_ITEMS[Math.floor(Math.random()*MOCK_ITEMS.length)];
    state.item = item;
    if(loadingLabel){
      if(st) st.textContent = loadingLabel;
      announce(loadingLabel);
      setTimeout(()=>{ if(state.screen===fromScreen) cb(); }, 500);
    } else cb();
  }, 700);
}
function shootThen(nextScreen, retakeScreen){
  shoot(()=>{ state.checkNext=nextScreen; state.checkRetake=retakeScreen; go('photocheck'); });
}
function shootOrMiss(nextScreen, retakeScreen, missScreen, loadingLabel){
  shoot(()=>{
    if(state.demoNoMatch){ go(missScreen); return; }
    state.checkNext=nextScreen; state.checkRetake=retakeScreen; go('photocheck');
  }, loadingLabel);
}
function finishCheck(){
  const next = state.checkNext;
  go(next);
  if(next==='update-done') state.confirmScreen = 'update-3';
  if(DONE_TOAST[next]) showToast(DONE_TOAST[next]);
}
function toggleDemo(){ state.demoNoMatch = !state.demoNoMatch; render(); }
function demoToggleHtml(){
  return `<button class="demotoggle" aria-pressed="${state.demoNoMatch}" onclick="toggleDemo()">Demo: show "no match" ${state.demoNoMatch?'✅ On':'⬜ Off'}</button>`;
}
function tryFind(){
  go('find-loading');
  announce('Looking for matches…');
  setTimeout(()=>{ if(state.screen==='find-loading') goReplace(state.demoNoMatch ? 'find-nomatch' : 'find-2'); }, 600);
}
function filterBrands(q){
  q = q.trim().toLowerCase();
  const recentWrap = document.getElementById('brandRecent');
  if(recentWrap) recentWrap.style.display = q ? 'none' : '';
  const full = document.getElementById('brandFullGrid');
  if(!full) return;
  full.querySelectorAll('.brandcard').forEach(btn=>{
    btn.style.display = btn.textContent.toLowerCase().includes(q) ? '' : 'none';
  });
}
function showToast(msg){
  const t = document.getElementById('toast');
  t.innerHTML = `${msg} <button class="toastundo" onclick="undoLast()">Undo</button>`;
  t.classList.add('show');
  announce(msg + '. Undo available for 5 seconds.');
  clearTimeout(toastTimer);
  toastTimer = setTimeout(()=>{ t.classList.remove('show'); }, 5000);
}
function undoLast(){
  const t = document.getElementById('toast');
  t.classList.remove('show');
  clearTimeout(toastTimer);
  if(state.confirmScreen) go(state.confirmScreen);
}
function saveDraft(){ go('new-done'); showToast('Saved'); }
function markReceived(){
  const fromScreen = state.screen;
  const btn = document.getElementById('markReceivedBtn');
  if(btn){ btn.disabled = true; btn.textContent = 'Printing sticker…'; }
  announce('Printing sticker…');
  setTimeout(()=>{
    if(state.screen!==fromScreen) return; // user navigated away mid-print
    go('sample-done'); showToast('Marked Received');
  }, 700);
}

function brandGridHtml(nextScreen){
  let html = `<input type="text" class="searchbox" id="brandSearch" placeholder="Search brands…" aria-label="Search brands" oninput="filterBrands(this.value)">`;
  html += `<div id="brandRecent"><p class="sectionlabel">Recent</p><div class="brandgrid">`;
  RECENT_BRANDS.forEach(b=>{ html += `<button class="brandcard" onclick="state.brand='${b}'; go('${nextScreen}')">${b}</button>`; });
  html += `</div></div>`;
  html += `<p class="sectionlabel">All Brands</p><div class="brandgrid" id="brandFullGrid">`;
  BRANDS.forEach(b=>{ html += `<button class="brandcard" onclick="state.brand='${b}'; go('${nextScreen}')">${b}</button>`; });
  html += `</div>`;
  return html;
}

function render(){
  const s = document.getElementById('screen');
  let html = '';
  const onHome = state.screen==='home';
  document.getElementById('homeBtnTop').style.display = onHome ? 'none' : 'inline-flex';
  document.getElementById('backBtn').style.display = onHome ? 'none' : 'inline-flex';

  if(state.screen==='home'){
    html += `<h1 class="title">Pick a Job</h1><p class="sub">Every job is a quick level path.</p>
    <div class="homegrid">`;
    JOBS.forEach(j=>{
      html += `<div class="levelcard">
        <h2>${j.emoji} ${j.title}</h2>
        <p class="sub">Level 1 of ${j.levels}</p>
        <div class="startrow">
          ${dots(0)}
          <button class="btn btn-primary" style="width:auto;min-width:110px;" onclick="startJob('${j.id}')">Start</button>
        </div>
      </div>`;
    });
    html += `</div>`;
  }

  else if(state.screen==='photocheck'){
    const lvl = levelFromScreen(state.checkRetake);
    html += lvl ? levelHeader(lvl, 'Photo Check') : `<h1 class="title">Photo Check</h1>`;
    html += `<div class="card"><div class="swatch ${state.item.sw}"></div></div>`;
    html += `<p class="sub">Does this look right?</p>`;
    html += `<div class="row"><button class="btn btn-secondary" onclick="go(state.checkRetake)">Retake</button><button class="btn btn-primary" onclick="finishCheck()">Looks good</button></div>`;
  }

  else if(state.screen==='new-1'){
    html += levelHeader(1,'Snap It');
    html += cameraScreen('Take a photo of the sample', "shootThen('new-2','new-1')");
  }
  else if(state.screen==='new-2'){
    html += levelHeader(2,'Pick the Brand');
    html += brandGridHtml('new-3');
  }
  else if(state.screen==='new-3'){
    html += levelHeader(3,'Save It');
    html += `<div class="card">
      <div class="swatch ${state.item.sw}"></div>
      <p class="itemname">${state.item.name} — ${state.item.color}</p>
      <p class="itemmeta">Brand: ${state.brand} · code ${state.item.code}</p>
    </div>`;
    html += `<button class="btn btn-primary" onclick="saveDraft()">Save as Draft ⭐</button>`;
  }
  else if(state.screen==='new-done'){
    html += `<div class="confetti-banner">🎉 +10 points! Nice work</div>`;
    html += `<div class="card"><div class="swatch ${state.item.sw}"></div><p class="itemname">${state.item.name} — ${state.item.color}</p><p class="itemmeta">Brand: ${state.brand} · Code: ${state.item.code}</p><p class="itemmeta">Saved as a new item draft.</p></div>`;
    html += `<div class="row"><button class="btn btn-primary" onclick="startJob('new')">Do Another</button><button class="btn btn-secondary" onclick="go('home')">Home</button></div>`;
  }

  else if(state.screen==='update-1'){
    html += levelHeader(1,'Find It');
    html += `<p class="sub">Scan the label, or search by typing.</p>`;
    html += cameraScreen('Show me the label or pattern', "shootOrMiss('update-2','update-1','update-nomatch','Looking for matches…')");
    html += demoToggleHtml();
  }
  else if(state.screen==='update-nomatch'){
    html += `<h1 class="title">No Match Found</h1><p class="sub">We couldn't find a matching item.</p>`;
    html += `<div class="row"><button class="btn btn-secondary" onclick="go('update-1')">Try again</button><button class="btn btn-primary" onclick="go('update-typecode')">Type the code instead</button></div>`;
  }
  else if(state.screen==='update-typecode'){
    html += `<h1 class="title">Type the Code</h1>`;
    html += `<input type="text" class="codeinput" placeholder="e.g. DW-10423" aria-label="Item code">`;
    html += `<button class="btn btn-primary" onclick="state.item=MOCK_ITEMS[0]; go('update-2')">Find</button>`;
  }
  else if(state.screen==='update-2'){
    html += levelHeader(2,'Confirm It');
    html += `<div class="card">
      <div class="swatch ${state.item.sw}"></div>
      <p class="itemname">${state.item.name} — ${state.item.color}</p>
      <p class="itemmeta">code ${state.item.code}</p>
    </div>`;
    html += `<div class="row"><button class="btn btn-secondary" onclick="go('update-1')">No, search again</button><button class="btn btn-primary" onclick="go('update-3')">Yes, that's it</button></div>`;
  }
  else if(state.screen==='update-3'){
    html += levelHeader(3,'Snap the New Photo');
    html += cameraScreen('Take the new photo', "shootThen('update-done','update-3')");
  }
  else if(state.screen==='update-done'){
    html += `<div class="confetti-banner">✅ Updated!</div>`;
    html += `<div class="card"><div class="swatch ${state.item.sw}"></div><p class="itemname">${state.item.name} — ${state.item.color}</p><p class="itemmeta">Brand: ${state.item.brand} · Code: ${state.item.code}</p><p class="itemmeta">Photo updated.</p></div>`;
    html += `<div class="row"><button class="btn btn-primary" onclick="startJob('update')">Do Another</button><button class="btn btn-secondary" onclick="go('home')">Home</button></div>`;
  }

  else if(state.screen==='find-1'){
    html += levelHeader(1,'Search');
    html += `<div class="row" style="flex-direction:column;">
      <button class="btn btn-secondary" onclick="tryFind()">📷 Scan</button>
      <button class="btn btn-secondary" onclick="tryFind()">🎤 Voice</button>
      <button class="btn btn-secondary" onclick="tryFind()">⌨️ Type</button>
    </div>`;
    html += demoToggleHtml();
  }
  else if(state.screen==='find-loading'){
    html += `<div class="card" style="text-align:center;"><p class="sub">Looking for matches…</p></div>`;
  }
  else if(state.screen==='find-nomatch'){
    html += `<h1 class="title">No Match Found</h1><p class="sub">We couldn't find a matching item.</p>`;
    html += `<div class="row"><button class="btn btn-secondary" onclick="go('find-1')">Try again</button><button class="btn btn-primary" onclick="go('find-typecode')">Type the code instead</button></div>`;
  }
  else if(state.screen==='find-typecode'){
    html += `<h1 class="title">Type the Code</h1>`;
    html += `<input type="text" class="codeinput" placeholder="e.g. DW-10423" aria-label="Item code">`;
    html += `<button class="btn btn-primary" onclick="state.item=MOCK_ITEMS[0]; go('find-done')">Find</button>`;
  }
  else if(state.screen==='find-2'){
    html += levelHeader(2,'Pick It');
    html += `<div class="resultlist">`;
    MOCK_ITEMS.forEach((it,i)=>{
      html += `<button class="resultcard" onclick="state.item=MOCK_ITEMS[${i}]; go('find-done')">
        <div class="thumb ${it.sw}"></div>
        <div><p class="itemname" style="font-size:17px;">${it.name} — ${it.color}</p><p class="itemmeta">code ${it.code}</p></div>
      </button>`;
    });
    html += `</div>`;
  }
  else if(state.screen==='find-done'){
    html += `<h1 class="title">Found It</h1>`;
    html += `<div class="card"><div class="swatch ${state.item.sw}"></div><p class="itemname">${state.item.name} — ${state.item.color}</p><p class="itemmeta">Brand: ${state.item.brand} · Code: ${state.item.code}</p></div>`;
    html += `<div class="row"><button class="btn btn-primary" onclick="startJob('find')">Do Another</button><button class="btn btn-secondary" onclick="go('home')">Home</button></div>`;
  }

  else if(state.screen==='sample-1'){
    html += levelHeader(1,'Scan the Box');
    html += cameraScreen('Scan the label on the box', "shootOrMiss('sample-2','sample-1','sample-nomatch','Looking for matches…')");
    html += demoToggleHtml();
  }
  else if(state.screen==='sample-nomatch'){
    html += `<h1 class="title">No Match Found</h1><p class="sub">We couldn't find a matching item.</p>`;
    html += `<div class="row"><button class="btn btn-secondary" onclick="go('sample-1')">Try again</button><button class="btn btn-primary" onclick="go('sample-typecode')">Type the code instead</button></div>`;
  }
  else if(state.screen==='sample-typecode'){
    html += `<h1 class="title">Type the Code</h1>`;
    html += `<input type="text" class="codeinput" placeholder="e.g. DW-10423" aria-label="Item code">`;
    html += `<button class="btn btn-primary" onclick="state.item=MOCK_ITEMS[0]; go('sample-2')">Find</button>`;
  }
  else if(state.screen==='sample-2'){
    html += levelHeader(2,'Confirm & Print');
    html += `<div class="card"><div class="swatch ${state.item.sw}"></div><p class="itemname">${state.item.name} — ${state.item.color}</p><p class="itemmeta">code ${state.item.code}</p></div>`;
    html += `<button class="btn btn-primary" id="markReceivedBtn" onclick="markReceived()">Mark It Received</button>`;
  }
  else if(state.screen==='sample-done'){
    html += `<div class="confetti-banner">🖨️ Sticker printed! +10 points</div>`;
    html += `<div class="card"><div class="swatch ${state.item.sw}"></div><p class="itemname">${state.item.name} — ${state.item.color}</p><p class="itemmeta">Brand: ${state.item.brand} · Code: ${state.item.code}</p><p class="itemmeta">Marked received.</p></div>`;
    html += `<div class="row"><button class="btn btn-primary" onclick="startJob('sample')">Do Another</button><button class="btn btn-secondary" onclick="go('home')">Home</button></div>`;
  }

  s.innerHTML = html;
}

render();
</script>
</body>
</html>