← back to Dw Photo Capture

public/pick.html

301 lines

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
<meta name="theme-color" content="#0f0e0c">
<link rel="manifest" href="/manifest.webmanifest">
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
<title>DW Sample Station</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
/* TK-12348 — front page, rebuilt to the Figma spec (10 working themes only: 5 Figma + 5 Photoshop). */
:root{
  --bg:#0F0E0C; --card:#1A1815; --card-selected:#221F1A; --btn-disabled:#24211D;
  --border:#2E2A25; --text:#F5F1EA; --muted:#A89F92; --accent:#C9A96E; --on-accent:#0F0E0C;
  --disabled-label:#7A7268;
}
*{box-sizing:border-box}
html{background:var(--bg);overflow-x:hidden}
html,body{margin:0;padding:0;height:100%}
body{
  background:var(--bg); color:var(--text);
  font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
  -webkit-font-smoothing:antialiased; display:flex; flex-direction:column;
  min-height:100dvh; overflow-x:hidden;
}
@media (prefers-reduced-motion:reduce){ *{scroll-behavior:auto !important; transition:none !important; animation:none !important} }

button{font:inherit;color:inherit;background:none;border:0;padding:0;margin:0}
a{color:inherit}

/* ---- header ---- */
header{display:flex;flex-direction:column;gap:6px;padding:52px 24px 20px}
.eyebrow{font-size:11px;font-weight:600;letter-spacing:1.5px;color:var(--accent);text-transform:uppercase}
h1{margin:0;font-size:28px;font-weight:700;line-height:34px;letter-spacing:-0.4px}
.subtitle{margin:0;font-size:16px;font-weight:400;line-height:22px;color:var(--muted)}

/* ---- theme list ---- */
.list{flex:1;min-height:0}
.group{margin-top:16px}
.group:first-child{margin-top:0}
.group-head{display:flex;align-items:baseline;gap:8px;padding:0 24px}
.group-label{margin:0;font-size:12px;font-weight:600;letter-spacing:1.2px;color:var(--accent);text-transform:uppercase}
.group-count{font-size:12px;font-weight:400;color:var(--muted)}
.row{
  display:flex; gap:12px; padding:10px 24px 4px; margin:0;
  overflow-x:auto; overflow-y:hidden; scroll-snap-type:x proximity; -webkit-overflow-scrolling:touch;
  scrollbar-width:none;
}
.row::-webkit-scrollbar{display:none}

/* ---- card ---- */
.card{
  position:relative; flex:0 0 150px; width:150px; height:234px;
  border-radius:16px; overflow:hidden; border:1px solid var(--border);
  background:var(--card); text-align:left; cursor:pointer; scroll-snap-align:start;
  display:flex; flex-direction:column;
}
.card:focus-visible{outline:3px solid var(--accent); outline-offset:2px}
.card[aria-checked="true"]{border:2px solid var(--accent); background:var(--card-selected)}
.thumb-wrap{position:relative; width:100%; height:150px; flex:0 0 150px; background:#000}
.thumb-wrap img{width:100%;height:100%;object-fit:cover;object-position:top;display:block}
.tag{
  position:absolute; top:8px; left:8px; padding:4px 8px; border-radius:999px;
  background:rgba(15,14,12,.78); color:var(--text); font-size:11px; font-weight:600; letter-spacing:.3px;
}
.check{
  position:absolute; top:8px; right:8px; width:28px; height:28px; border-radius:50%;
  background:var(--accent); display:none; align-items:center; justify-content:center;
}
.card[aria-checked="true"] .check{display:flex}
.check svg path{stroke:var(--on-accent)}
.last-used{
  position:absolute; bottom:8px; left:8px; padding:4px 8px; border-radius:999px;
  background:var(--accent); color:var(--on-accent); font-size:11px; font-weight:600;
}
.body{padding:10px 12px 12px; display:flex; flex-direction:column; gap:4px; min-height:0}
.name{font-size:15px; font-weight:600; line-height:20px}
.desc{
  font-size:13px; font-weight:400; line-height:18px; color:var(--muted);
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden;
}

/* ---- footer ---- */
footer{
  flex:0 0 auto; background:var(--bg); border-top:1px solid var(--border);
  padding:12px 24px calc(20px + env(safe-area-inset-bottom));
  display:flex; flex-direction:column; align-items:center; gap:0;
}
.continue{
  width:100%; max-width:342px; height:56px; border-radius:14px; font-size:17px; font-weight:600;
  display:flex; align-items:center; justify-content:center;
  background:var(--btn-disabled); border:1px solid var(--border); color:var(--disabled-label);
}
.continue:not(:disabled){background:var(--accent); border-color:var(--accent); color:var(--on-accent); cursor:pointer}
.continue:focus-visible{outline:3px solid var(--accent); outline-offset:2px}
.classic-link{
  margin-top:8px; min-height:44px; padding:0 12px; display:flex; align-items:center;
  font-size:15px; font-weight:500; color:var(--accent); text-decoration:underline;
}
.classic-link:focus-visible{outline:3px solid var(--accent); outline-offset:2px; border-radius:6px}
.helper{display:none; margin:4px 0 0; font-size:13px; color:var(--muted); text-align:center}

/* ---- mobile: fixed viewport, no page scroll (rows scroll sideways by design) ---- */
@media (max-width:699.98px){
  html,body{height:100dvh; overflow:hidden}
  .list{overflow-y:auto; overflow-x:hidden}
}

/* ---- tablet / landscape iPad: 700px+ ---- */
@media (min-width:700px){
  html,body{height:auto; overflow-x:hidden}
  body{min-height:100dvh}
  .page{max-width:820px; margin:0 auto; width:100%; display:flex; flex-direction:column; flex:1}
  header{padding:64px 32px 20px}
  h1{font-size:36px; line-height:43px}
  .subtitle{font-size:18px; line-height:25px}
  .group{margin-top:32px}
  .group-head{padding:0 32px}
  .row{
    padding:10px 32px 4px; display:grid; grid-template-columns:repeat(5,1fr); gap:16px;
    overflow:visible; scroll-snap-type:none;
  }
  .card{width:100%; height:332px; flex:auto}
  .thumb-wrap{height:248px; flex-basis:248px}
  footer{padding:12px 32px calc(32px + env(safe-area-inset-bottom)); gap:4px}
  .continue{max-width:420px; margin:0 auto}
  .classic-link{margin-top:4px}
  .helper{display:block}
}
</style>
</head>
<body>
<div class="page">
  <header>
    <div class="eyebrow" id="eyebrow">DESIGNER WALLCOVERINGS</div>
    <h1>DW Sample Station</h1>
    <p class="subtitle">Pick how you want the app to look</p>
  </header>

  <main class="list" id="list">
    <section class="group" data-group="figma">
      <div class="group-head">
        <h2 class="group-label" id="label-figma">FIGMA</h2>
        <span class="group-count">· 5 themes</span>
      </div>
      <div class="row" id="row-figma" role="radiogroup" aria-labelledby="label-figma"></div>
    </section>
    <section class="group" data-group="photoshop">
      <div class="group-head">
        <h2 class="group-label" id="label-photoshop">PHOTOSHOP</h2>
        <span class="group-count">· 5 themes</span>
      </div>
      <div class="row" id="row-photoshop" role="radiogroup" aria-labelledby="label-photoshop"></div>
    </section>
  </main>

  <footer>
    <button type="button" class="continue" id="continueBtn" disabled aria-disabled="true">Continue</button>
    <a class="classic-link" href="/app">Use the classic app</a>
    <p class="helper">You can switch themes anytime in Settings</p>
  </footer>
</div>

<script>
(function(){
  // The 10 working themes shown on the front page (5 Figma mockups + 5 Photoshop
  // "kid-simple" mockups). Everything else under /themes/ and /kid-simple/ is still
  // reachable directly by URL, just not listed here.
  var THEMES = [
    { group:'figma', key:'fg-v05', ver:'v05', slug:'f-swipe-deck',   name:'Swipe Deck',      desc:'Swipe cards to confirm' },
    { group:'figma', key:'fg-v10', ver:'v10', slug:'f-scan-first',   name:'Scan First',      desc:'Camera opens first' },
    { group:'figma', key:'fg-v12', ver:'v12', slug:'f-bottom-tabs',  name:'Bottom Tabs',     desc:'Classic tab bar' },
    { group:'figma', key:'fg-v20', ver:'v20', slug:'f-luxe-gold',    name:'Luxe Gold',       desc:'Premium showroom look' },
    { group:'figma', key:'fg-v24', ver:'v24', slug:'f-split-screen', name:'Split Screen',    desc:'Photo + details side by side' },
    { group:'photoshop', key:'ps-v07', ver:'v07', slug:'p-scan-and-go',   name:'Scan & Go',      desc:'Scan, then one big action' },
    { group:'photoshop', key:'ps-v08', ver:'v08', slug:'p-mode-dial',    name:'Mode Dial',      desc:'Turn a dial to pick the job' },
    { group:'photoshop', key:'ps-v10', ver:'v10', slug:'p-todays-inbox', name:'Today’s Inbox', desc:'Today’s tasks first' },
    { group:'photoshop', key:'ps-v12', ver:'v12', slug:'p-hold-to-talk', name:'Hold to Talk',   desc:'Voice-first' },
    { group:'photoshop', key:'ps-v13', ver:'v13', slug:'p-sticker-book', name:'Sticker Book',   desc:'Playful sticker cards' }
  ];
  var STORE_KEY = 'dwTheme';
  var $ = function(id){ return document.getElementById(id); };
  var reduceMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;

  function getStored(){ try { return localStorage.getItem(STORE_KEY); } catch(e){ return null; } }
  function setStored(v){ try { localStorage.setItem(STORE_KEY, v); } catch(e){} }

  var initialSlug = getStored();
  var isReturning = !!(initialSlug && THEMES.some(function(t){ return t.slug === initialSlug; }));
  var selectedSlug = isReturning ? initialSlug : null;

  if (isReturning) $('eyebrow').textContent = 'WELCOME BACK';

  var rows = { figma: $('row-figma'), photoshop: $('row-photoshop') };
  var cardEls = {}; // slug -> button element

  function checkSvg(){
    return '<svg width="14" height="11" viewBox="0 0 14 11" fill="none" aria-hidden="true">' +
      '<path d="M1.5 5.5L5 9L12.5 1.5" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/></svg>';
  }

  THEMES.forEach(function(t, i){
    var btn = document.createElement('button');
    btn.type = 'button';
    btn.className = 'card';
    btn.setAttribute('role', 'radio');
    btn.setAttribute('aria-checked', String(t.slug === selectedSlug));
    btn.setAttribute('data-slug', t.slug);
    btn.tabIndex = -1; // roving tabindex set below
    btn.innerHTML =
      '<span class="thumb-wrap">' +
        '<img src="/pick-thumbs/' + t.key + '.jpg" alt="" width="150" height="150" loading="' + (i < 3 ? 'eager' : 'lazy') + '">' +
        '<span class="tag">' + t.ver + '</span>' +
        '<span class="check">' + checkSvg() + '</span>' +
        '<span class="last-used" hidden>Last used</span>' +
      '</span>' +
      '<span class="body">' +
        '<span class="name">' + t.name + '</span>' +
        '<span class="desc">' + t.desc + '</span>' +
      '</span>';
    btn.setAttribute('aria-label', t.name + ' — ' + t.desc);
    btn.addEventListener('click', function(){ select(t.slug); });
    rows[t.group].appendChild(btn);
    cardEls[t.slug] = btn;
  });

  // "Last used" pill sticks to whichever card was selected at page-load time on a
  // returning visit, independent of anything the user re-picks during this session.
  if (isReturning && cardEls[initialSlug]) {
    cardEls[initialSlug].querySelector('.last-used').hidden = false;
  }

  function updateRovingTabindex(){
    Object.keys(rows).forEach(function(g){
      var els = Array.prototype.slice.call(rows[g].children);
      var activeSlug = selectedSlug && THEMES.some(function(t){ return t.group===g && t.slug===selectedSlug; }) ? selectedSlug : (els[0] && els[0].getAttribute('data-slug'));
      els.forEach(function(el){ el.tabIndex = (el.getAttribute('data-slug') === activeSlug) ? 0 : -1; });
    });
  }

  function updateContinue(){
    var btn = $('continueBtn');
    var t = THEMES.filter(function(x){ return x.slug === selectedSlug; })[0];
    if (t) {
      btn.disabled = false;
      btn.removeAttribute('aria-disabled');
      btn.textContent = 'Continue with ' + t.name;
      btn.dataset.href = '/t/' + t.slug;
    } else {
      btn.disabled = true;
      btn.setAttribute('aria-disabled', 'true');
      btn.textContent = 'Continue';
      delete btn.dataset.href;
    }
  }

  function select(slug){
    selectedSlug = slug;
    THEMES.forEach(function(t){ cardEls[t.slug].setAttribute('aria-checked', String(t.slug === slug)); });
    updateRovingTabindex();
    updateContinue();
  }

  // Arrow-key roving focus within each row's radiogroup (Enter/Space select natively,
  // since every card is a real <button>).
  Object.keys(rows).forEach(function(g){
    rows[g].addEventListener('keydown', function(e){
      if (e.key !== 'ArrowRight' && e.key !== 'ArrowLeft') return;
      var els = Array.prototype.slice.call(rows[g].children);
      var idx = els.indexOf(document.activeElement);
      if (idx === -1) return;
      e.preventDefault();
      var next = e.key === 'ArrowRight' ? Math.min(idx + 1, els.length - 1) : Math.max(idx - 1, 0);
      els.forEach(function(el, i){ el.tabIndex = (i === next) ? 0 : -1; });
      els[next].focus();
    });
  });

  $('continueBtn').addEventListener('click', function(){
    var href = this.dataset.href;
    if (!href || this.disabled) return;
    location.href = href;
  });

  updateRovingTabindex();
  updateContinue();

  if (isReturning && cardEls[initialSlug]) {
    // Scroll the returning user's card into view without moving the page itself.
    requestAnimationFrame(function(){
      cardEls[initialSlug].scrollIntoView({ block: 'nearest', inline: 'center', behavior: reduceMotion ? 'auto' : 'smooth' });
    });
  }
})();
</script>
</body>
</html>