← back to Dw Photo Capture

public/kid-simple/v8-mode-dial.html

355 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>v8 — Mode Dial</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;9..144,800&family=Manrope:wght@400;600;700;800&display=swap" rel="stylesheet">
<style>
  :root{
    --navy:#141B2E; --navy2:#1E2740; --cream:#F6EFE4; --ink:#241C14; --muted:#8B8172;
    --copper:#C97B3F; --copper-ink:#3A200C;
    --teal:#4F8A8B; --teal-ink:#0D2626;
    --plum:#8B5A8F; --plum-ink:#2B0F2C;
    --mustard:#D4A24C; --mustard-ink:#3A2707;
    --green:#4C9A6A;
  }
  *{box-sizing:border-box;}
  html,body{margin:0;padding:0;background:#ddd;font-family:'Manrope',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;
  }
  button{ font-family:'Manrope',sans-serif; cursor:pointer; }
  .screen{ flex:1; display:flex; flex-direction:column; position:relative; }

  /* ---------- HOME: camera + mode dial ---------- */
  .cam{
    flex:1; background:
      radial-gradient(120px 120px at 78% 22%, rgba(201,123,63,.28), transparent 60%),
      radial-gradient(160px 160px at 15% 68%, rgba(79,138,139,.22), transparent 60%),
      radial-gradient(220px 220px at 60% 85%, rgba(139,90,143,.20), transparent 60%),
      linear-gradient(180deg, var(--navy) 0%, var(--navy2) 100%);
    position:relative; display:flex; flex-direction:column; color:var(--cream); min-height:780px;
  }
  .dwmark{ position:absolute; top:18px; left:20px; font-family:'Fraunces',serif; font-weight:700; font-size:14px;
    letter-spacing:.14em; color:rgba(246,239,228,.72); }
  .reticle{
    position:absolute; top:50%; left:50%; transform:translate(-50%,-58%);
    width:200px; height:200px; border:2px solid rgba(246,239,228,.28); border-radius:28px;
  }
  .reticle::before,.reticle::after{ content:''; position:absolute; width:22px; height:22px; border-color:rgba(246,239,228,.65); }
  .reticle::before{ top:-2px; left:-2px; border-top:3px solid; border-left:3px solid; border-radius:6px 0 0 0; }
  .reticle::after{ bottom:-2px; right:-2px; border-bottom:3px solid; border-right:3px solid; border-radius:0 0 6px 0; }

  .hint{
    position:absolute; top:52px; left:20px; right:20px; text-align:center;
    font-family:'Fraunces',serif; font-weight:600; font-size:21px; color:var(--cream);
  }
  .hint .sub{ display:block; margin-top:4px; font-family:'Manrope',sans-serif; font-weight:600; font-size:14px; color:rgba(246,239,228,.68); }

  .dialdock{ position:absolute; left:0; right:0; bottom:0; padding:0 0 30px; display:flex; flex-direction:column; align-items:center; gap:16px; }
  .modestrip{
    display:flex; gap:10px; overflow-x:auto; scroll-snap-type:x proximity; padding:4px 20px;
    max-width:390px; -webkit-overflow-scrolling:touch;
  }
  .modestrip::-webkit-scrollbar{ display:none; }
  .modepill{
    scroll-snap-align:center; flex:0 0 auto; min-height:56px; padding:0 18px; border-radius:18px;
    background:rgba(255,255,255,.10); border:2px solid rgba(255,255,255,.16); color:rgba(246,239,228,.62);
    font-weight:800; font-size:13px; letter-spacing:.10em; display:flex; align-items:center; gap:8px;
  }
  .modepill .ic{ font-size:18px; }
  .modepill.active{ background:rgba(255,255,255,.16); color:var(--cream); }
  .modepill.active[data-m="new"]{ border-color:var(--copper); box-shadow:0 0 0 1px var(--copper) inset; }
  .modepill.active[data-m="update"]{ border-color:var(--teal); box-shadow:0 0 0 1px var(--teal) inset; }
  .modepill.active[data-m="find"]{ border-color:var(--plum); box-shadow:0 0 0 1px var(--plum) inset; }
  .modepill.active[data-m="sample"]{ border-color:var(--mustard); box-shadow:0 0 0 1px var(--mustard) inset; }
  .modepill:focus-visible{ outline:3px solid #fff; outline-offset:2px; }

  .shutterwrap{ display:flex; flex-direction:column; align-items:center; gap:10px; }
  .shutterlabel{ font-weight:800; font-size:15px; color:var(--cream); }
  .shutter{
    width:96px; height:96px; border-radius:50%; border:6px solid rgba(255,255,255,.9);
    display:flex; align-items:center; justify-content:center; font-size:36px; color:#fff;
    transition:transform .08s ease;
  }
  .shutter:active{ transform:scale(.94); }
  .shutter:focus-visible{ outline:3px solid #fff; outline-offset:4px; }
  .flash{ position:absolute; inset:0; background:#fff; opacity:0; pointer-events:none; }
  .flash.go{ animation:flashfx .5s ease; }
  @keyframes flashfx{ 0%{opacity:0;} 12%{opacity:.9;} 100%{opacity:0;} }

  /* ---------- generic light screens ---------- */
  .pad{ padding:60px 26px 30px; flex:1; display:flex; flex-direction:column; }
  .backbar{
    position:absolute; top:14px; left:14px; z-index:5; min-width:56px; min-height:56px;
    border-radius:14px; background:#fff; border:2px solid #EBE3D6; color:var(--ink);
    font-size:14px; font-weight:800; display:flex; align-items:center; justify-content:center; gap:4px;
    padding:0 14px;
  }
  .backbar:focus-visible{ outline:3px solid #2B6CB0; outline-offset:2px; }
  .question{ font-family:'Fraunces',serif; font-weight:700; font-size:28px; line-height:1.2; margin:6px 0 22px; }
  .answers{ display:flex; flex-direction:column; gap:14px; margin-top:auto; }
  .bigbtn{
    width:100%; min-height:60px; border-radius:18px; font-size:18px; font-weight:800;
    background:#fff; border:2px solid #EBE3D6; color:var(--ink); padding:12px 18px;
  }
  .bigbtn-secondary{ background:#fff; color:var(--muted); border:2px solid #E3DACB; }
  .bigbtn:focus-visible{ outline:3px solid #2B6CB0; outline-offset:2px; }
  .linktext{ background:none; border:none; color:var(--muted); font-size:16px; text-decoration:underline; min-height:56px; }

  .brandlist{ display:flex; flex-direction:column; gap:12px; overflow-y:auto; }

  .card{ display:flex; flex-direction:column; align-items:center; text-align:center; gap:6px; margin:8px 0 24px; }
  .swatch{ width:100%; max-width:260px; height:170px; border-radius:18px; }
  .sw1{background:repeating-linear-gradient(45deg,#C9A24B 0 16px,#b98a2e 16px 32px);}
  .sw2{background:repeating-linear-gradient(-45deg,#7C93B0 0 16px,#5f7a99 16px 32px);}
  .sw3{background:repeating-linear-gradient(90deg,#8FAE8B 0 14px,#729070 14px 28px);}
  .sw4{background:repeating-linear-gradient(135deg,#C0785F 0 15px,#a85f47 15px 30px);}
  .itemname{ font-size:19px; font-weight:800; margin:2px 0 0; }
  .itemmeta{ font-size:15px; color:var(--muted); margin:2px 0 0; }

  .center{ display:flex; flex-direction:column; align-items:center; justify-content:center; flex:1; gap:8px; text-align:center; }
</style>
</head>
<body>
<div class="phone">
  <div class="screen" id="screen" role="main"></div>
</div>

<script>
const MODES = {
  new:    { label:'NEW',    icon:'🆕', color:'var(--copper)',  ink:'var(--copper-ink)',  shutterIcon:'📸', hint:'Point at anything new.', sub:'Tap to add a new item' },
  update: { label:'UPDATE', icon:'🔄', color:'var(--teal)',    ink:'var(--teal-ink)',    shutterIcon:'📷', hint:'Scan the label or pattern.', sub:'Tap to update an item' },
  find:   { label:'FIND',   icon:'🔍', color:'var(--plum)',    ink:'var(--plum-ink)',    shutterIcon:'🔎', hint:'Scan to find a match.', sub:'Tap to find an item' },
  sample: { label:'SAMPLE', icon:'📦', color:'var(--mustard)', ink:'var(--mustard-ink)', shutterIcon:'📦', hint:'Scan the box label.', sub:'Tap to log a sample' },
};
const MODE_ORDER = ['new','update','find','sample'];

const MOCK_ITEMS = [
  { name:'Harbor Stripe', color:'Indigo', code:'item 4471', sw:'sw2' },
  { name:'Fern & Fable',  color:'Moss',   code:'item 2093', sw:'sw3' },
  { name:'Ember Weave',   color:'Rust',   code:'item 6650', sw:'sw4' },
];
const BRANDS = ['York Wallcoverings','Brewster Home','Kravet','Cole & Son','Thibaut','Ronald Redding'];

const state = { screen:'home', mode:'new', job:null, item:null, brand:null, resultIndex:0, shooting:false };

function go(screen){ state.screen = screen; render(); }
function setMode(m){ state.mode = m; render(); }

function accent(job){
  return (MODES[job] || MODES.new).color;
}
function accentInk(job){
  return (MODES[job] || MODES.new).ink;
}

function shootFromHome(){
  if(state.shooting) return;
  state.shooting = true;
  state.job = state.mode;
  render();
  setTimeout(()=>{
    state.shooting = false;
    state.item = randomItem();
    if(state.mode==='new') go('new-brand');
    else if(state.mode==='update') go('update-match');
    else if(state.mode==='find'){ state.resultIndex = 0; go('find-result'); }
    else if(state.mode==='sample') go('sample-match');
  }, 650);
}
function shootPlain(cb){
  const st = document.getElementById('camStatus2');
  if(st) st.textContent = 'Snapping…';
  setTimeout(cb, 650);
}
function randomItem(){ return MOCK_ITEMS[Math.floor(Math.random()*MOCK_ITEMS.length)]; }

function backBtn(target){
  return `<button class="backbar" onclick="go('${target}')">‹ Camera</button>`;
}

function plainCamera(hint, onShoot, backTarget){
  return `<div class="cam" style="min-height:auto; flex:1;">
      ${backBtn(backTarget)}
      <div class="hint" style="top:70px;">${hint}</div>
      <div class="dialdock" style="position:static; margin-top:auto; padding-bottom:40px;">
        <div class="shutterwrap">
          <span class="shutterlabel" id="camStatus2">Tap to shoot</span>
          <button class="shutter" style="background:${accent(state.job)};" aria-label="Take photo" onclick="${onShoot}">📸</button>
        </div>
      </div>
    </div>`;
}

function render(){
  const s = document.getElementById('screen');
  let html = '';

  if(state.screen==='home'){
    const m = MODES[state.mode];
    let pills = '';
    MODE_ORDER.forEach(key=>{
      const mm = MODES[key];
      pills += `<button class="modepill ${key===state.mode?'active':''}" data-m="${key}" onclick="setMode('${key}')" aria-pressed="${key===state.mode}">
        <span class="ic">${mm.icon}</span>${mm.label}</button>`;
    });
    html = `<div class="cam">
      <div class="flash" id="flash"></div>
      <div class="dwmark">DW · PHOTO</div>
      <div class="reticle" aria-hidden="true"></div>
      <div class="hint">${m.hint}<span class="sub">${m.sub}</span></div>
      <div class="dialdock">
        <div class="modestrip" role="tablist" aria-label="Choose what you're doing">${pills}</div>
        <div class="shutterwrap">
          <span class="shutterlabel">${state.shooting ? 'Snapping…' : m.label + ' MODE'}</span>
          <button class="shutter" style="background:${m.color};" aria-label="Shoot for ${m.label}" onclick="shootFromHome()" ${state.shooting?'disabled':''}>${m.shutterIcon}</button>
        </div>
      </div>
    </div>`;
    s.innerHTML = html;
    if(state.shooting){
      const f = document.getElementById('flash');
      if(f){ f.classList.remove('go'); void f.offsetWidth; f.classList.add('go'); }
    }
    return;
  }

  // ---- NEW ----
  if(state.screen==='new-brand'){
    html = `<div class="pad">${backBtn('home')}
      <h1 class="question">Which brand is it?</h1>
      <div class="brandlist">`;
    BRANDS.forEach(b=>{ html += `<button class="bigbtn" onclick="state.brand='${b}'; go('new-save')">${b}</button>`; });
    html += `</div></div>`;
  }
  else if(state.screen==='new-save'){
    html = `<div class="pad">${backBtn('new-brand')}
      <h1 class="question">Save it?</h1>
      <div class="card">
        <div class="swatch ${state.item.sw}"></div>
        <p class="itemmeta">${state.brand} · ${state.item.code}</p>
      </div>
      <div class="answers">
        <button class="bigbtn" style="background:${accent('new')}; border-color:${accent('new')}; color:#fff;" onclick="go('new-done')">Save as Draft</button>
        <button class="linktext" onclick="go('home')">Retake Photo</button>
      </div></div>`;
  }
  else if(state.screen==='new-done'){
    html = `<div class="pad"><div class="center">
        <h1 class="question">Saved! 🎉</h1>
      </div>
      <div class="answers">
        <button class="bigbtn" style="background:${accent('new')}; border-color:${accent('new')}; color:#fff;" onclick="setMode('new'); go('home')">Add Another</button>
        <button class="linktext" onclick="go('home')">Home</button>
      </div></div>`;
  }

  // ---- UPDATE ----
  else if(state.screen==='update-match'){
    html = `<div class="pad">${backBtn('home')}
      <h1 class="question">Is this it?</h1>
      <div class="card">
        <div class="swatch ${state.item.sw}"></div>
        <p class="itemname">${state.item.name} — ${state.item.color}</p>
        <p class="itemmeta">${state.item.code}</p>
      </div>
      <div class="answers">
        <button class="bigbtn" style="background:${accent('update')}; border-color:${accent('update')}; color:#fff;" onclick="go('update-cam2')">Yes, That's It</button>
        <button class="bigbtn bigbtn-secondary" onclick="go('home')">No, Let Me Search</button>
      </div></div>`;
  }
  else if(state.screen==='update-cam2'){
    html = plainCamera('Take the new photo.', "shootPlain(()=>go('update-done'))", 'update-match');
  }
  else if(state.screen==='update-done'){
    html = `<div class="pad"><div class="center">
        <h1 class="question">Updated! ✅</h1>
      </div>
      <div class="answers">
        <button class="bigbtn" style="background:${accent('update')}; border-color:${accent('update')}; color:#fff;" onclick="go('home')">Home</button>
      </div></div>`;
  }

  // ---- FIND ----
  else if(state.screen==='find-result'){
    const it = MOCK_ITEMS[state.resultIndex];
    html = `<div class="pad">${backBtn('home')}
      <h1 class="question">Is this the one?</h1>
      <div class="card">
        <div class="swatch ${it.sw}"></div>
        <p class="itemname">${it.name} — ${it.color}</p>
        <p class="itemmeta">${it.code}</p>
      </div>
      <div class="answers">
        <button class="bigbtn" style="background:${accent('find')}; border-color:${accent('find')}; color:#fff;" onclick="pickResult()">Yes, This One</button>
        <button class="bigbtn bigbtn-secondary" onclick="nextResult()">Next Result</button>
      </div></div>`;
  }
  else if(state.screen==='find-exhausted'){
    html = `<div class="pad">${backBtn('home')}
      <h1 class="question">That's all we found.</h1>
      <div class="answers">
        <button class="bigbtn" style="background:${accent('find')}; border-color:${accent('find')}; color:#fff;" onclick="state.resultIndex=0; go('find-result')">Try Again</button>
        <button class="linktext" onclick="go('home')">Home</button>
      </div></div>`;
  }
  else if(state.screen==='find-detail'){
    html = `<div class="pad"><div class="center">
        <h1 class="question" style="font-size:24px;">${state.item.name}</h1>
        <div class="swatch ${state.item.sw}" style="max-width:220px;"></div>
        <p class="itemmeta">${state.item.color} · ${state.item.code}</p>
      </div>
      <div class="answers">
        <button class="bigbtn" style="background:${accent('update')}; border-color:${accent('update')}; color:#fff;" onclick="setMode('update'); go('update-cam2')">Update This</button>
        <button class="bigbtn bigbtn-secondary" onclick="setMode('find'); go('home')">Search Again</button>
      </div></div>`;
  }

  // ---- SAMPLE ----
  else if(state.screen==='sample-match'){
    html = `<div class="pad">${backBtn('home')}
      <h1 class="question">Mark it received?</h1>
      <div class="card">
        <div class="swatch ${state.item.sw}"></div>
        <p class="itemname">${state.item.name} — ${state.item.color}</p>
        <p class="itemmeta">${state.item.code}</p>
      </div>
      <div class="answers">
        <button class="bigbtn" style="background:${accent('sample')}; border-color:${accent('sample')}; color:#fff;" onclick="go('sample-printing')">Yes, Mark Received</button>
      </div></div>`;
  }
  else if(state.screen==='sample-printing'){
    html = `<div class="pad"><div class="center">
        <h1 class="question">Printing your sticker…</h1>
        <p class="itemmeta">🖨️ One sec…</p>
      </div></div>`;
    setTimeout(()=>{ if(state.screen==='sample-printing') go('sample-done'); }, 1000);
  }
  else if(state.screen==='sample-done'){
    html = `<div class="pad"><div class="center">
        <h1 class="question">Done! ✅</h1>
      </div>
      <div class="answers">
        <button class="bigbtn" style="background:${accent('sample')}; border-color:${accent('sample')}; color:#fff;" onclick="go('home')">Home</button>
      </div></div>`;
  }

  s.innerHTML = html;
}

function pickResult(){ state.item = MOCK_ITEMS[state.resultIndex]; go('find-detail'); }
function nextResult(){
  if(state.resultIndex < MOCK_ITEMS.length-1){ state.resultIndex++; go('find-result'); }
  else { go('find-exhausted'); }
}

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