Files
Inlander-Restaurant-Week-Pi…/restaurant-picker.html
2026-02-24 22:25:14 -08:00

405 lines
21 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>IRW Restaurant Picker</title>
<style>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:'Segoe UI',sans-serif;background:#1a0a00;color:#f0e6d3;min-height:100vh}
header{background:#8b1a1a;padding:20px 30px;display:flex;align-items:center;gap:16px}
header h1{font-size:1.5rem;font-weight:700;color:#f5c842}
header p{font-size:0.85rem;color:#f0cca0;margin-top:3px}
.toolbar{background:#2a1200;padding:12px 24px;display:flex;flex-wrap:wrap;gap:10px;align-items:center;border-bottom:1px solid #5a2a00}
.toolbar input,.toolbar select{background:#3a1a00;border:1px solid #7a4a00;color:#f0e6d3;padding:7px 10px;border-radius:6px;font-size:0.85rem}
.toolbar input{width:200px}
.toolbar select{cursor:pointer}
.btn{padding:7px 16px;border:none;border-radius:6px;cursor:pointer;font-size:0.85rem;font-weight:600;transition:all 0.2s}
.btn-primary{background:#c0392b;color:white}
.btn-primary:hover{background:#e74c3c}
.btn-gold{background:#f5c842;color:#1a0a00}
.btn-gold:hover{background:#f7d76c}
.btn-outline{background:transparent;border:1px solid #7a4a00;color:#f0e6d3;text-decoration:none;display:inline-block}
.btn-outline:hover{background:#3a1a00}
.spacer{flex:1}
.tag-counts{font-size:0.82rem;color:#c0906a}
.main{display:flex;height:calc(100vh - 118px)}
.list-panel{width:360px;min-width:280px;overflow-y:auto;border-right:1px solid #5a2a00;background:#1f0c00}
.restaurant-card{padding:12px 14px;border-bottom:1px solid #3a1a00;display:flex;align-items:flex-start;gap:10px;cursor:pointer;transition:background 0.15s}
.restaurant-card:hover{background:#2e1500}
.restaurant-card.active{background:#4a2000;border-left:3px solid #f5c842;padding-left:11px}
.card-checks{display:flex;flex-direction:column;gap:5px;padding-top:2px}
.check-label{display:flex;align-items:center;gap:5px;cursor:pointer;font-size:0.72rem;white-space:nowrap}
.check-label input{cursor:pointer;accent-color:#f5c842;width:13px;height:13px}
.check-you{color:#7ec8e3}
.check-wife{color:#f9a8d4}
.card-info{flex:1;min-width:0}
.card-name{font-weight:600;font-size:0.9rem;color:#f0e6d3;line-height:1.3}
.card-meta{font-size:0.75rem;color:#a0785a;margin-top:2px}
.price-badge{display:inline-block;padding:1px 6px;border-radius:10px;font-size:0.7rem;font-weight:700;margin-right:5px}
.price-25{background:#1e5c2a;color:#7edd9a}
.price-35{background:#5c4a0a;color:#f5c842}
.price-45{background:#6b0f0f;color:#f9a8a8}
.interest-dots{margin-top:4px;display:flex;gap:4px}
.dot{width:7px;height:7px;border-radius:50%}
.dot-you{background:#7ec8e3}
.dot-wife{background:#f9a8d4}
.detail-panel{flex:1;overflow-y:auto;padding:28px}
.detail-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:#5a3a20;text-align:center;gap:12px}
.detail-empty .icon{font-size:3.5rem}
.detail-header h2{font-size:1.7rem;color:#f5c842}
.detail-header .meta{color:#a0785a;margin-top:6px;font-size:0.9rem}
.detail-actions{display:flex;gap:10px;margin-top:14px;flex-wrap:wrap}
.detail-actions .check-label{font-size:0.88rem;background:#2a1200;padding:7px 12px;border-radius:8px;border:1px solid #5a2a00}
.detail-actions .check-label input{width:15px;height:15px}
.hours{background:#2a1200;padding:9px 14px;border-radius:8px;margin-bottom:18px;font-size:0.83rem;color:#c0906a}
.course-section{margin-bottom:22px}
.course-title{font-size:0.9rem;font-weight:700;color:#f5c842;text-transform:uppercase;letter-spacing:1px;border-bottom:1px solid #5a2a00;padding-bottom:7px;margin-bottom:12px}
.menu-item{margin-bottom:12px}
.menu-item-name{font-weight:600;color:#f0e6d3;font-size:0.92rem}
.menu-item-desc{color:#a0785a;font-size:0.82rem;margin-top:3px;line-height:1.5}
.modal-overlay{position:fixed;inset:0;background:rgba(0,0,0,0.75);z-index:100;display:flex;align-items:center;justify-content:center}
.modal{background:#1f0c00;border:1px solid #7a4a00;border-radius:12px;padding:28px;max-width:460px;width:90%;text-align:center}
.modal h2{color:#f5c842;font-size:1.3rem;margin-bottom:6px}
.modal p{color:#a0785a;font-size:0.88rem;margin-bottom:20px}
.winner-card{background:#2e1500;border:2px solid #f5c842;border-radius:10px;padding:18px;margin:14px 0}
.winner-name{font-size:1.4rem;font-weight:700;color:#f5c842}
.winner-meta{color:#c0906a;margin-top:5px}
.winner-interest{margin-top:9px;display:flex;gap:8px;justify-content:center}
.interest-pill{padding:3px 11px;border-radius:12px;font-size:0.78rem;font-weight:600}
.pill-you{background:#1a3f50;color:#7ec8e3}
.pill-wife{background:#4a1a2e;color:#f9a8d4}
.modal-btns{display:flex;gap:10px;justify-content:center;margin-top:18px}
.spin-anim{animation:pop 0.25s ease-out}
@keyframes pop{from{transform:scale(0.85);opacity:0}to{transform:scale(1);opacity:1}}
.no-results{padding:30px;text-align:center;color:#5a3a20}
::-webkit-scrollbar{width:5px}::-webkit-scrollbar-track{background:#1a0a00}::-webkit-scrollbar-thumb{background:#5a2a00;border-radius:3px}
.course-browse-bar{background:#241000;padding:8px 24px;display:flex;gap:10px;align-items:center;border-bottom:1px solid #5a2a00}
.course-browse-bar select{background:#3a1a00;border:1px solid #7a4a00;color:#f0e6d3;padding:7px 10px;border-radius:6px;font-size:0.85rem;cursor:pointer;margin-left:auto;flex-shrink:0}
.btn-course{background:#3a1a00;border:1px solid #7a4a00;color:#f5c842;padding:7px 20px;border-radius:20px;cursor:pointer;font-size:0.85rem;font-weight:600;transition:all 0.2s}
.btn-course:hover{background:#5a2a00;border-color:#f5c842}
.course-modal-overlay{position:fixed;inset:0;background:rgba(0,0,0,0.78);z-index:100;display:flex;align-items:center;justify-content:center}
.course-modal{background:#1f0c00;border:1px solid #7a4a00;border-radius:12px;padding:24px 24px 18px;max-width:480px;width:90%;max-height:80vh;display:flex;flex-direction:column}
.course-modal h2{color:#f5c842;font-size:1.2rem;margin-bottom:14px;flex-shrink:0}
.course-modal-list{overflow-y:auto;flex:1;border:1px solid #3a1a00;border-radius:8px}
.course-dish-item{padding:10px 14px;border-bottom:1px solid #2a1200;cursor:pointer;transition:background 0.15s}
.course-dish-item:last-child{border-bottom:none}
.course-dish-item:hover{background:#2e1500}
.course-dish-name{font-weight:600;color:#f0e6d3;font-size:0.9rem}
.course-dish-restaurant{font-size:0.78rem;color:#a0785a;margin-top:2px}
.course-modal-close{margin-top:14px;flex-shrink:0;text-align:right}
.back-btn{display:none;align-items:center;gap:6px;background:#3a1a00;border:1px solid #7a4a00;color:#f5c842;padding:9px 16px;border-radius:6px;cursor:pointer;font-size:0.88rem;font-weight:600;margin-bottom:16px;width:100%}
@media(max-width:640px){
header{padding:12px 16px;gap:10px}
header h1{font-size:1.15rem}
header p{font-size:0.78rem}
.toolbar{padding:10px 12px;gap:8px}
.toolbar input{width:100%}
.toolbar select{flex:1;min-width:0}
.spacer{display:none}
.tag-counts{width:100%;order:10;text-align:center}
.course-browse-bar{padding:8px 12px;flex-wrap:wrap}
.btn-course{flex:1;padding:7px 6px;text-align:center}
.course-browse-bar select{margin-left:0;width:100%}
.main{height:auto;min-height:calc(100svh - 98px);flex-direction:column}
.list-panel{width:100%;min-width:0;border-right:none;min-height:calc(100svh - 98px)}
.detail-panel{display:none;width:100%;padding:16px 14px}
.main.show-detail .list-panel{display:none}
.main.show-detail .detail-panel{display:block}
.back-btn{display:flex}
.detail-header h2{font-size:1.3rem}
.detail-header .meta{font-size:0.82rem}
.detail-actions{gap:8px}
.detail-actions .check-label{font-size:0.82rem;padding:6px 10px}
.menu-item-name{font-size:0.88rem}
.menu-item-desc{font-size:0.8rem}
}
</style>
</head>
<body>
<header>
<div>
<h1>🍽️ Inlander Restaurant Week Picker</h1>
<p>Browse menus · Mark favorites · Let fate decide</p>
</div>
<span id="eventDates" style="margin-left:auto;font-size:1.5rem;font-weight:700;color:#f5c842;white-space:nowrap">FEBRUARY 26 MARCH 7, 2026</span>
</header>
<div class="toolbar">
<input type="text" id="search" placeholder="Search restaurants..." oninput="applyFilters()"/>
<select id="filterPrice" onchange="applyFilters()">
<option value="">All Prices</option>
<option value="25">$25</option><option value="35">$35</option><option value="45">$45</option>
</select>
<select id="filterArea" onchange="applyFilters()"><option value="">All Areas</option></select>
<select id="filterCuisine" onchange="applyFilters()"><option value="">All Cuisines</option></select>
<select id="filterInterest" onchange="applyFilters()">
<option value="">All Restaurants</option>
<option value="you">His Picks</option>
<option value="wife">Her Picks</option>
<option value="both">Both Picked</option>
<option value="either">Either Picked</option>
</select>
<button class="btn btn-outline" onclick="resetFilters()">Clear Filters</button>
<div class="spacer"></div>
<span class="tag-counts" id="tagCounts"></span>
<button class="btn btn-outline" onclick="clearAll()">Reset Selected</button>
<button class="btn btn-gold" onclick="openPicker()">🎲 Pick for Us!</button>
</div>
<div class="course-browse-bar">
<button class="btn btn-course" onclick="openCourseBrowse('First Course')">🥗 First Course</button>
<button class="btn btn-course" onclick="openCourseBrowse('Second Course')">🍖 Second Course</button>
<button class="btn btn-course" onclick="openCourseBrowse('Third Course')">🍮 Third Course</button>
<select id="yearSelect" onchange="loadYear(this.value)">
<option value="2026">2026</option>
<option value="2025">2025</option>
</select>
</div>
<div class="main">
<div class="list-panel" id="listPanel"></div>
<div class="detail-panel" id="detailPanel">
<div class="detail-empty">
<div class="icon">🍴</div>
<h3 style="color:#8b4a20">Select a restaurant to see its menu</h3>
<p>Check boxes to mark ones you're interested in, then hit "Pick for Us!"</p>
</div>
</div>
</div>
<div class="modal-overlay" id="pickerModal" style="display:none">
<div class="modal">
<h2>🎲 Tonight's Pick</h2>
<p id="pickerSubtitle"></p>
<div id="winnerDisplay"></div>
<div class="modal-btns">
<button class="btn btn-outline" onclick="closePicker()">Close</button>
<button class="btn btn-primary" onclick="pickAgain()">Pick Again!</button>
<button class="btn btn-gold" onclick="viewWinner()">View Menu</button>
</div>
</div>
</div>
<script>
let RESTAURANTS = [];
let currentYear = '2026';
let interests = JSON.parse(localStorage.getItem('irw_interests_2026')||'{}');
let filteredList = [];
let activeSlug = null;
let currentWinner = null;
// Build filter dropdowns
function initDropdowns(){
const areas = new Set(), cuisines = new Set();
RESTAURANTS.forEach(r => { r.areas.forEach(a=>areas.add(a)); cuisines.add(r.cuisine); });
const asel = document.getElementById('filterArea');
[...areas].sort().forEach(a => { const o=document.createElement('option'); o.value=o.textContent=a; asel.appendChild(o); });
const csel = document.getElementById('filterCuisine');
[...cuisines].sort().forEach(c => { const o=document.createElement('option'); o.value=o.textContent=c; csel.appendChild(o); });
}
function applyFilters(){
const search = document.getElementById('search').value.toLowerCase();
const price = document.getElementById('filterPrice').value;
const area = document.getElementById('filterArea').value;
const cuisine = document.getElementById('filterCuisine').value;
const interest = document.getElementById('filterInterest').value;
filteredList = RESTAURANTS.filter(r => {
if(search && !r.name.toLowerCase().includes(search) && !r.cuisine.toLowerCase().includes(search)) return false;
if(price && r.price !== parseInt(price)) return false;
if(area && !r.areas.includes(area)) return false;
if(cuisine && r.cuisine !== cuisine) return false;
const ri = interests[r.slug]||{};
if(interest==='you' && !ri.you) return false;
if(interest==='wife' && !ri.wife) return false;
if(interest==='both' && !(ri.you && ri.wife)) return false;
if(interest==='either' && !(ri.you||ri.wife)) return false;
return true;
});
renderList();
updateCounts();
}
function renderList(){
const panel = document.getElementById('listPanel');
if(!filteredList.length){ panel.innerHTML='<div class="no-results">No restaurants match your filters.</div>'; return; }
panel.innerHTML = filteredList.map(r => {
const ri = interests[r.slug]||{};
const dots = [ri.you?'<span class="dot dot-you"></span>':'', ri.wife?'<span class="dot dot-wife"></span>':''].join('');
return `<div class="restaurant-card${r.slug===activeSlug?' active':''}" data-slug="${r.slug}">
<div class="card-checks">
<label class="check-label check-you" onclick="event.stopPropagation()"><input type="checkbox" ${ri.you?'checked':''} onchange="toggleInterest('${r.slug}','you',this.checked)"/>His</label>
<label class="check-label check-wife" onclick="event.stopPropagation()"><input type="checkbox" ${ri.wife?'checked':''} onchange="toggleInterest('${r.slug}','wife',this.checked)"/>Her</label>
</div>
<div class="card-info" onclick="showDetail('${r.slug}')">
<div class="card-name">${r.name}</div>
<div class="card-meta"><span class="price-badge price-${r.price}">$${r.price}</span>${r.cuisine} · ${r.areas.join(', ')}</div>
${dots ? `<div class="interest-dots">${dots}</div>` : ''}
</div>
</div>`;
}).join('');
}
function updateCounts(){
let you=0,wife=0,both=0;
Object.values(interests).forEach(v=>{ if(v.you)you++; if(v.wife)wife++; if(v.you&&v.wife)both++; });
document.getElementById('tagCounts').textContent = `His: ${you} · Her: ${wife} · Both: ${both}`;
}
function toggleInterest(slug,who,checked){
if(!interests[slug]) interests[slug]={};
interests[slug][who]=checked;
if(!interests[slug].you && !interests[slug].wife) delete interests[slug];
localStorage.setItem('irw_interests_'+currentYear,JSON.stringify(interests));
renderList();
updateCounts();
if(activeSlug===slug) showDetail(slug);
}
function clearAll(){
if(!confirm('Clear all your selections?')) return;
interests={};
localStorage.setItem('irw_interests_'+currentYear,JSON.stringify(interests));
renderList(); updateCounts();
}
function resetFilters(){
document.getElementById('search').value='';
document.getElementById('filterPrice').value='';
document.getElementById('filterArea').value='';
document.getElementById('filterCuisine').value='';
document.getElementById('filterInterest').value='';
applyFilters();
}
function showDetail(slug){
activeSlug=slug;
renderList();
const r = RESTAURANTS.find(r=>r.slug===slug);
const ri = interests[slug]||{};
if(!r) return;
const m = r.menu||{};
let coursesHTML = '';
if(m.courses){
['First Course','Second Course','Third Course'].forEach(course => {
const items = m.courses[course];
if(!items||!items.length) return;
coursesHTML += `<div class="course-section"><div class="course-title">${course}</div>${
items.map(item => `<div class="menu-item">
<div class="menu-item-name">${item.name}</div>
${item.desc?`<div class="menu-item-desc">${item.desc}</div>`:''}
</div>`).join('')
}</div>`;
});
}
document.getElementById('detailPanel').innerHTML = `
<button class="back-btn" onclick="backToList()">&#8592; Back to List</button>
<div class="detail-header">
<h2>${r.name}</h2>
<div class="meta"><span class="price-badge price-${r.price}">$${r.price} per person</span>${r.cuisine} · ${r.areas.join(', ')}${m.phone?' · '+m.phone:''}</div>
<div class="detail-actions">
<label class="check-label check-you"><input type="checkbox" ${ri.you?'checked':''} onchange="toggleInterest('${slug}','you',this.checked)"/>He's interested</label>
<label class="check-label check-wife"><input type="checkbox" ${ri.wife?'checked':''} onchange="toggleInterest('${slug}','wife',this.checked)"/>She's interested</label>
<a href="${r.url}" target="_blank" class="btn btn-outline" style="font-size:0.82rem">View on Site ↗</a>
</div>
</div>
${m.hours?`<div class="hours">🕐 ${m.hours}</div>`:''}
${coursesHTML||'<div style="color:#5a3a20;padding:20px 0">Menu details not available for this restaurant.</div>'}
<div style="margin-top:28px;padding-top:18px;border-top:1px solid #3a1a00">
<a href="${r.url}" target="_blank" style="color:#f5c842;font-size:0.88rem">View full menu on Inlander Restaurant Week ↗</a>
</div>`;
document.getElementById('detailPanel').scrollTop=0;
document.querySelector('.main').classList.add('show-detail');
}
function backToList(){ activeSlug=null; document.querySelector(".main").classList.remove("show-detail"); renderList();}
function getPool(){
const both = RESTAURANTS.filter(r=>{ const ri=interests[r.slug]||{}; return ri.you&&ri.wife; });
if(both.length) return both;
return RESTAURANTS.filter(r=>{ const ri=interests[r.slug]||{}; return ri.you||ri.wife; });
}
function openPicker(){
const pool = getPool();
if(!pool.length){ alert('No restaurants selected!\n\nCheck some boxes first, then try again.'); return; }
document.getElementById('pickerModal').style.display='flex';
pickAgain();
}
function pickAgain(){
const pool = getPool();
const winner = pool[Math.floor(Math.random()*pool.length)];
currentWinner = winner;
const ri = interests[winner.slug]||{};
const pills = [ri.you?'<span class="interest-pill pill-you">✓ His pick</span>':'', ri.wife?'<span class="interest-pill pill-wife">✓ Her pick</span>':''].filter(Boolean).join('');
const bothCount = RESTAURANTS.filter(r=>{ const ri=interests[r.slug]||{}; return ri.you&&ri.wife; }).length;
document.getElementById('pickerSubtitle').textContent = bothCount>0 ? `Picking from ${bothCount} restaurants you both liked!` : `Picking from ${pool.length} restaurants either of you liked`;
document.getElementById('winnerDisplay').innerHTML = `<div class="winner-card spin-anim">
<div class="winner-name">${winner.name}</div>
<div class="winner-meta"><span class="price-badge price-${winner.price}">$${winner.price}</span>${winner.cuisine} · ${winner.areas.join(', ')}</div>
<div class="winner-interest">${pills||'<span class="interest-pill" style="background:#3a2a00;color:#c0906a">Random selection</span>'}</div>
</div>`;
}
function closePicker(){ document.getElementById('pickerModal').style.display='none'; }
function viewWinner(){ closePicker(); if(currentWinner) showDetail(currentWinner.slug); }
function openCourseBrowse(course){
const dishes = [];
RESTAURANTS.forEach(r => {
const items = r.menu && r.menu.courses && r.menu.courses[course];
if(items && items.length){
items.forEach(item => dishes.push({name: item.name, restaurant: r.name, slug: r.slug}));
}
});
dishes.sort((a,b) => a.name.localeCompare(b.name));
document.getElementById('courseBrowseTitle').textContent = course + ' Dishes';
document.getElementById('courseBrowseList').innerHTML = dishes.length
? dishes.map(d => `<div class="course-dish-item" onclick="selectCourseDish('${d.slug}')">
<div class="course-dish-name">${d.name}</div>
<div class="course-dish-restaurant">${d.restaurant}</div>
</div>`).join('')
: '<div style="color:#5a3a20;padding:20px;text-align:center">No dishes available</div>';
document.getElementById('courseBrowseOverlay').style.display = 'flex';
}
function closeCourseBrowse(){ document.getElementById('courseBrowseOverlay').style.display = 'none'; }
function selectCourseDish(slug){ closeCourseBrowse(); showDetail(slug); }
function loadYear(year){
currentYear = year;
interests = JSON.parse(localStorage.getItem('irw_interests_'+year)||'{}');
activeSlug = null;
currentWinner = null;
document.querySelector('.main').classList.remove('show-detail');
document.getElementById('detailPanel').innerHTML = `
<div class="detail-empty">
<div class="icon">🍴</div>
<h3 style="color:#8b4a20">Select a restaurant to see its menu</h3>
<p>Check boxes to mark ones you're interested in, then hit "Pick for Us!"</p>
</div>`;
document.getElementById('filterArea').innerHTML = '<option value="">All Areas</option>';
document.getElementById('filterCuisine').innerHTML = '<option value="">All Cuisines</option>';
resetFilters();
fetch(year+'-restaurants.json', {cache:'no-store'})
.then(function(r){ return r.json(); })
.then(function(data){
RESTAURANTS = Array.isArray(data) ? data : data.restaurants;
if(data.eventDates) document.getElementById('eventDates').textContent = data.eventDates;
initDropdowns();
applyFilters();
updateCounts();
})
.catch(function(err){
document.getElementById('listPanel').innerHTML = '<div class="no-results">⚠️ Failed to load restaurant data.<br><small>'+err+'</small><br><small>Try serving this file from a local web server instead of opening it directly.</small></div>';
});
}
// Init
loadYear('2026');
</script>
<div class="course-modal-overlay" id="courseBrowseOverlay" style="display:none" onclick="if(event.target===this)closeCourseBrowse()">
<div class="course-modal">
<h2 id="courseBrowseTitle"></h2>
<div class="course-modal-list" id="courseBrowseList"></div>
<div class="course-modal-close">
<button class="btn btn-outline" onclick="closeCourseBrowse()">Close</button>
</div>
</div>
</div>
</body>
</html>