Travel quote · TQ01 — Reconcile supplied travelers, rooms and nights with different price bases, then explain why removing one traveler does not automatically…

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>Travel quote · TQ01</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:#edf3f8;--ink:#18354c;--muted:#526e81;--accent:#155b85;--line:#cddce7;--tint:#eaf3f9}.quote{border-top:7px solid #155b85}.quote-top{display:flex;justify-content:space-between;font-size:12px;letter-spacing:.1em;font-weight:bold}.comparison{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}.comparison strong,.comparison small{display:block}.comparison strong{font-size:25px;margin:6px 0}.quote .message{background:#f1f7fb}.room{padding:10px 0;border-bottom:1px solid #dce7ee}@media(max-width:600px){.comparison{grid-template-columns:1fr}.quote table{font-size:13px}.quote th{font-size:10px}}
/* Keep native date fields and table content within each responsive grid track. */
.split>*{min-width:0}.fields{grid-template-columns:repeat(2,minmax(0,1fr))}.quote table{table-layout:fixed;overflow-wrap:anywhere}
</style></head><body><main><header><div class="eyebrow">TQ01 · Fictional planning quote</div><h1>A room is not a traveler.</h1><p>Compare known costs while preserving room-night, traveler and group price bases.</p></header><div class="toolbar" aria-label="Quote scenario"><button type="button" id="scenario-a">A · Four travelers</button><button type="button" id="scenario-b">B · T4 withdraws</button><button type="button" id="scenario-c">C · One extra night</button></div><div class="split"><article class="paper quote"><div class="quote-top"><span>TRAVEL COST REVIEW</span><span id="version">TQ01-A</span></div><div id="quote-result" class="message"></div><table><thead><tr><th>Component / basis</th><th>Quantity</th><th>Known amount</th></tr></thead><tbody id="components"></tbody></table><p class="message" id="allocation"></p><p class="small">Required train travel: TO CONFIRM. A complete trip total is unavailable.</p><section class="section"><h2>Rooms & occupied nights</h2><div id="occupancy"></div><p id="nights" class="small"></p></section></article><aside class="paper"><h2>Scenario assumptions</h2><div class="fields"><div><label for="travelers">Travelers (T1 onward)</label><input id="travelers" type="number" min="1" max="5" value="4"></div><div><label for="room-rate">USD / room-night</label><input id="room-rate" type="number" min="0" value="100"></div><div><label for="arrival">Check-in</label><input id="arrival" type="date" value="2026-10-20"></div><div><label for="departure">Checkout (excluded)</label><input id="departure" type="date" value="2026-10-22"></div><div><label for="activity-rate">Activity / traveler</label><input id="activity-rate" type="number" min="0" value="30"></div><div><label for="activity-currency">Activity currency</label><select id="activity-currency"><option>USD</option><option>EUR</option></select></div></div><p class="small section">Two retained rooms, capacity two each. Group transfer: USD 80 once. Source: fictional TQ01 supplied assumptions, revision 1. All components unconfirmed.</p><p id="quote-error" role="status" class="error"></p></aside></div><section class="paper section"><h2>Independent scenario comparison</h2><div class="comparison"><div>A · Baseline<strong>USD 600</strong><small>4 people · 2 rooms · 2 nights</small></div><div>B · T4 withdraws<strong>USD 570</strong><small>3 people · rooms retained · reduction USD 30</small></div><div>C · Original roster + night<strong>USD 800</strong><small>4 people · 2 rooms · 3 nights</small></div></div></section><footer><p>HTML planning companion, not an Excel/DOCX export, supplier offer, invoice or booking. Prices are fictional. Equal allocation is illustrative, not a supplier rate or amount owed.</p><p>Bedding, accessibility, availability, inclusions, exclusions, taxes/fees, validity, acceptance, payment and refunds: TO CONFIRM. Currency amounts are never combined without an explicit conversion basis. No information is saved.</p></footer></main><script>'use strict';const $=id=>document.getElementById(id);const esc=s=>String(s).replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
function dateValue(s){if(!/^\d{4}-\d{2}-\d{2}$/.test(s))return NaN;const d=new Date(s+'T00:00:00Z');return Number.isFinite(+d)&&d.toISOString().slice(0,10)===s?+d:NaN;}
function calculate(n,a,b,rr,ar,currency){n=Number(n);rr=Number(rr);ar=Number(ar);const start=dateValue(a),end=dateValue(b),days=(end-start)/86400000;if(!Number.isInteger(n)||n<1||n>5)return {error:'Enter a whole traveler count from 1 to 5.'};if(!Number.isFinite(days)||days<1||days>365)return {error:'Use valid dates with checkout 1–365 calendar nights after check-in.'};if(!Number.isFinite(rr)||!Number.isFinite(ar)||rr<0||ar<0||rr>1000000||ar>1000000)return {error:'Rates must be numbers from 0 to 1,000,000.'};if(n>4)return {error:'Allocation unresolved: T5 has no place in the four supplied places. No room is added.'};return {n,days,room:2*days*rr,activity:n*ar,transfer:80,total:2*days*rr+n*ar+80,allocation:(2*days*rr+n*ar+80)/n,currency,start};}
const money=n=>n.toLocaleString('en-US',{minimumFractionDigits:2,maximumFractionDigits:2});
function render(){const ids=['travelers','arrival','departure','room-rate','activity-rate'];let r=calculate(...ids.map(id=>$(id).value),$('activity-currency').value);if(ids.some(id=>!$(id).value.trim()))r={error:'Complete every scenario field; missing values are not zero.'};$('quote-error').textContent=r.error||'';if(r.error){$('quote-result').textContent='Known subtotal unconfirmed';$('allocation').textContent='No current allocation.';$('components').innerHTML='';$('occupancy').textContent=r.error;$('nights').textContent='';return;}const same=r.currency==='USD';$('quote-result').innerHTML='<div class="small">Known-component subtotal · '+(same?'USD':'separate currencies')+'</div><div class="big">'+(same?'USD '+money(r.total):'USD '+money(r.room+80)+' + EUR '+money(r.activity))+'</div>';$('components').innerHTML='<tr><td>Rooms<br><small>USD '+money(Number($('room-rate').value))+' / room-night</small></td><td>2 × '+r.days+'</td><td>USD '+money(r.room)+'</td></tr><tr><td>Activity<br><small>'+r.currency+' '+money(Number($('activity-rate').value))+' / traveler</small></td><td>'+r.n+'</td><td>'+r.currency+' '+money(r.activity)+'</td></tr><tr><td>Group transfer<br><small>USD 80 / group</small></td><td>1</td><td>USD 80.00</td></tr><tr><td>Required train</td><td>Unknown</td><td>TO CONFIRM</td></tr>';$('allocation').textContent=same?'Illustrative equal allocation: USD '+money(r.allocation)+' per traveler. Train excluded.':'Equal allocation withheld: currencies have no supplied conversion basis.';$('occupancy').innerHTML='<div class="room"><b>R01</b> · '+(r.n===1?'T1':'T1, T2')+' · capacity 2</div><div class="room"><b>R02</b> · '+(r.n<3?'No traveler assigned; room retained':r.n===3?'T3':'T3, T4')+' · capacity 2</div>';$('nights').textContent='Occupied night labels: '+Array.from({length:r.days},(_,i)=>new Date(r.start+i*86400000).toISOString().slice(0,10)).join(', ')+'. Checkout excluded.';}
function scenario(code){$('travelers').value=code==='B'?'3':'4';$('arrival').value='2026-10-20';$('departure').value=code==='C'?'2026-10-23':'2026-10-22';$('room-rate').value='100';$('activity-rate').value='30';$('activity-currency').value='USD';$('version').textContent='TQ01-'+code;['a','b','c'].forEach(c=>$('scenario-'+c).className=c===code.toLowerCase()?'active':'');render();}['a','b','c'].forEach(c=>$('scenario-'+c).addEventListener('click',()=>scenario(c.toUpperCase())));['travelers','arrival','departure','room-rate','activity-rate','activity-currency'].forEach(id=>$(id).addEventListener('input',()=>{$('version').textContent='TQ01 · Custom draft';render();}));scenario('A');globalThis.App={calculate,scenario,render};</script></body></html>