from pathlib import Path
html = r'''<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Invite Studio — Birthday Invitation Business</title>
<style>
:root{--ink:#28243a;--muted:#77738a;--purple:#7657d6;--pink:#f5d9e8;--cream:#fffaf6;--line:#eee8f1;--green:#287b61;--shadow:0 12px 35px rgba(54,38,75,.08)}
*{box-sizing:border-box}body{margin:0;background:#f8f6fb;color:var(--ink);font:15px/1.5 Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif}
header{background:#fff;border-bottom:1px solid var(--line);padding:18px 5vw;display:flex;align-items:center;justify-content:space-between;gap:12px;position:sticky;top:0;z-index:5}
.brand{display:flex;align-items:center;gap:10px;font-weight:850;font-size:19px}.logo{width:38px;height:38px;border-radius:13px;background:linear-gradient(135deg,#8669e8,#e99bc4);display:grid;place-items:center;color:white;font-size:20px}
header small{color:var(--muted)}.wrap{max-width:1150px;margin:28px auto;padding:0 20px}.hero{background:linear-gradient(115deg,#f0eaff,#fff0f5);border:1px solid #eadff6;border-radius:24px;padding:28px 30px;margin-bottom:22px}.hero h1{margin:0 0 6px;font-size:clamp(25px,4vw,34px);letter-spacing:-1px}.hero p{margin:0;color:#69627d}
nav{display:flex;gap:8px;flex-wrap:wrap;margin:18px 0 22px}.tab{border:0;border-radius:12px;padding:10px 16px;background:#fff;color:var(--muted);font-weight:700;cursor:pointer}.tab.active{background:var(--ink);color:white}
.panel{display:none}.panel.active{display:block}.grid{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}.stat,.card{background:#fff;border:1px solid var(--line);border-radius:18px;padding:19px;box-shadow:var(--shadow)}.stat span{color:var(--muted);font-size:13px}.stat strong{display:block;font-size:27px;margin-top:7px}.sectionhead{display:flex;justify-content:space-between;align-items:center;gap:12px;margin:24px 0 12px}.sectionhead h2{font-size:19px;margin:0}.muted{color:var(--muted)}button,.btn{cursor:pointer;border:0;border-radius:11px;padding:10px 14px;background:var(--purple);color:white;font-weight:750}.btn.secondary{background:#f0ebfa;color:#5943a6}.btn.danger{background:#fff0f0;color:#b34343}.formgrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:13px}.field{display:flex;flex-direction:column;gap:6px}.field label{font-size:13px;color:#69647a;font-weight:700}.field input,.field select,.field textarea{width:100%;padding:11px 12px;border:1px solid #e4deeb;border-radius:10px;background:#fff;font:inherit;color:var(--ink)}.field textarea{min-height:75px;resize:vertical}.full{grid-column:1/-1}.actions{display:flex;gap:9px;flex-wrap:wrap;margin-top:15px}
table{width:100%;border-collapse:collapse;font-size:14px}th,td{text-align:left;padding:12px 9px;border-bottom:1px solid var(--line)}th{color:var(--muted);font-size:12px;text-transform:uppercase;letter-spacing:.04em}.tablewrap{overflow:auto}.pill{display:inline-block;border-radius:20px;padding:4px 9px;background:#f0ebfa;color:#654eb0;font-size:12px;font-weight:750}.empty{padding:25px;text-align:center;color:var(--muted)}
.preview-wrap{display:grid;grid-template-columns:minmax(260px,1fr) minmax(260px,1fr);gap:18px;align-items:start}.invite{min-height:440px;border-radius:22px;padding:30px 24px;text-align:center;background:radial-gradient(circle at top left,#fff 0,#fff3f8 48%,#eee7ff 100%);border:1px solid #eadcf1;display:flex;flex-direction:column;align-items:center;justify-content:center;box-shadow:var(--shadow);position:relative;overflow:hidden}.invite:before,.invite:after{content:"✦ ✧ ✦";position:absolute;color:#c6a8dd;font-size:22px;letter-spacing:14px}.invite:before{top:22px}.invite:after{bottom:22px}.invite .eyebrow{letter-spacing:3px;text-transform:uppercase;font-size:11px;color:#8b6ca8;font-weight:800}.invite h2{font-family:Georgia,serif;font-size:38px;line-height:1.08;margin:16px 0;color:#624b85}.invite .bigage{font-family:Georgia,serif;font-size:66px;color:#d47ba9;line-height:1}.invite p{margin:7px 0;color:#62566e}.invite .name{font-family:Georgia,serif;font-size:29px;color:#624b85;font-weight:700}.invite .details{margin-top:18px;font-size:14px;white-space:pre-line}.note{font-size:12px;color:var(--muted);margin-top:12px}.price-row{display:grid;grid-template-columns:1fr 140px;gap:12px;align-items:center;padding:12px 0;border-bottom:1px solid var(--line)}.price-row input{max-width:140px;padding:9px;border:1px solid #e4deeb;border-radius:9px}
footer{color:var(--muted);font-size:12px;text-align:center;padding:30px 12px}
@media(max-width:760px){.grid{grid-template-columns:1fr}.preview-wrap{grid-template-columns:1fr}.formgrid{grid-template-columns:1fr}.full{grid-column:auto}.hero{padding:22px}header{padding:14px 18px}.wrap{margin:18px auto;padding:0 14px}}
</style>
</head>
<body>
<header><div class="brand"><div class="logo">✉</div> Invite Studio</div><small>Birthday invitation business organiser</small></header>
<main class="wrap">
<section class="hero"><h1>Your invitation business, organised.</h1><p>Keep track of orders, create a simple invitation preview, and check your sales.</p></section>
<nav>
<button class="tab active" data-tab="dashboard">Overview</button>
<button class="tab" data-tab="orders">Orders</button>
<button class="tab" data-tab="creator">Invitation maker</button>
<button class="tab" data-tab="prices">Pricing</button>
</nav>
<section class="panel active" id="dashboard">
<div class="grid">
<div class="stat"><span>Total orders</span><strong id="statOrders">0</strong></div>
<div class="stat"><span>Completed orders</span><strong id="statDone">0</strong></div>
<div class="stat"><span>Recorded sales</span><strong id="statSales">Rp0</strong></div>
</div>
<div class="sectionhead"><h2>Recent orders</h2><button onclick="showTab('orders')">View orders</button></div>
<div class="card tablewrap" id="recent"></div>
<div class="card" style="margin-top:14px"><strong>Quick tip</strong><p class="muted" style="margin-bottom:0">Add an order when someone requests an invitation. Update its status as you work. Ask a parent or guardian to supervise payments and customer communication.</p></div>
</section>
<section class="panel" id="orders">
<div class="sectionhead"><h2>Order tracker</h2><button onclick="document.getElementById('orderForm').scrollIntoView({behavior:'smooth'})">+ Add order</button></div>
<div class="card tablewrap" id="ordersTable"></div>
<div class="card" style="margin-top:15px" id="orderForm">
<h3 style="margin-top:0">Add a birthday invitation order</h3>
<div class="formgrid">
<div class="field"><label>Customer first name only</label><input id="customer" placeholder="e.g. Aisha" maxlength="30"></div>
<div class="field"><label>Birthday person's name</label><input id="birthdayName" placeholder="Name on invitation" maxlength="40"></div>
<div class="field"><label>Invitation theme</label><input id="theme" placeholder="e.g. pastel garden" maxlength="50"></div>
<div class="field"><label>Package</label><select id="package"><option>Classic</option><option>Custom theme</option><option>Premium</option></select></div>
<div class="field"><label>Price (Rp)</label><input id="price" type="number" min="0" placeholder="Enter agreed price"></div>
<div class="field"><label>Due date</label><input id="due" type="date"></div>
<div class="field full"><label>Order status</label><select id="status"><option>New</option><option>In progress</option><option>Waiting for approval</option><option>Completed</option></select></div>
</div>
<div class="actions"><button onclick="addOrder()">Save order</button><button class="btn secondary" onclick="clearOrderForm()">Clear</button></div>
<p class="note">Only save the minimum details needed. Don't enter phone numbers, addresses, or private information here.</p>
</div>
</section>
<section class="panel" id="creator">
<div class="sectionhead"><h2>Invitation preview maker</h2><span class="pill">Digital invitation only</span></div>
<div class="preview-wrap">
<div class="card">
<div class="formgrid">
<div class="field full"><label>Birthday person's name</label><input id="invName" value="Birthday Star" oninput="updateInvite()"></div>
<div class="field"><label>Age</label><input id="invAge" value="8" oninput="updateInvite()"></div>
<div class="field"><label>Theme colour</label><select id="invColor" onchange="updateInvite()"><option value="pink">Pink & lavender</option><option value="blue">Sky blue</option><option value="gold">Golden party</option><option value="green">Garden green</option></select></div>
<div class="field full"><label>Date and time</label><input id="invDate" placeholder="Saturday, 3 PM" oninput="updateInvite()"></div>
<div class="field full"><label>Venue</label><input id="invVenue" placeholder="Party venue" oninput="updateInvite()"></div>
<div class="field full"><label>Extra message</label><textarea id="invMessage" placeholder="We hope you can join us!" oninput="updateInvite()"></textarea></div>
</div>
<div class="actions"><button onclick="downloadInvite()">Download preview</button></div>
<p class="note">Check every detail with the customer before sending the final invitation.</p>
</div>
<div class="invite" id="invitePreview"><div class="eyebrow">You're invited to a</div><h2>Birthday<br>Celebration</h2><div class="name" id="pName">Birthday Star</div><div class="bigage" id="pAge">8</div><p>Let's celebrate together!</p><div class="details" id="pDetails">Date and time<br>Party venue</div><p id="pMessage">We hope you can join us!</p></div>
</div>
</section>
<section class="panel" id="prices">
<div class="sectionhead"><h2>Your package prices</h2><span class="pill">Editable</span></div>
<div class="card">
<p class="muted" style="margin-top:0">Set your own prices with a parent or guardian. These are blank starter prices, not fixed recommendations.</p>
<div class="price-row"><strong>Classic invitation</strong><input id="priceClassic" type="number" min="0" placeholder="Rp price"></div>
<div class="price-row"><strong>Custom theme invitation</strong><input id="priceCustom" type="number" min="0" placeholder="Rp price"></div>
<div class="price-row"><strong>Premium invitation</strong><input id="pricePremium" type="number" min="0" placeholder="Rp price"></div>
<div class="actions"><button onclick="savePrices()">Save prices</button></div>
<p class="note" id="priceNote">Prices are saved in this browser on this device.</p>
</div>
</section>
<footer>Invite Studio • Your information stays in this browser on this device. This is a personal organiser, not a public website.</footer>
</main>
<script>
const $=id=>document.getElementById(id);
let orders=JSON.parse(localStorage.getItem('inviteStudioOrders')||'[]');
function money(n){return 'Rp'+Number(n||0).toLocaleString('id-ID')}
function save(){localStorage.setItem('inviteStudioOrders',JSON.stringify(orders));render()}
function showTab(id){document.querySelectorAll('.tab').forEach(b=>b.classList.toggle('active',b.dataset.tab===id));document.querySelectorAll('.panel').forEach(p=>p.classList.toggle('active',p.id===id))}
document.querySelectorAll('.tab').forEach(b=>b.addEventListener('click',()=>showTab(b.dataset.tab)));
function table(items,withActions=false){if(!items.length)return '<div class="empty">No orders yet. Add your first invitation order!</div>';return '<table><thead><tr><th>Customer</th><th>Invitation</th><th>Price</th><th>Status</th>'+(withActions?'<th>Action</th>':'')+'</tr></thead><tbody>'+items.map(o=>'<tr><td>'+esc(o.customer)+'</td><td>'+esc(o.birthdayName)+'<br><span class="muted">'+esc(o.theme)+'</span></td><td>'+money(o.price)+'</td><td><span class="pill">'+esc(o.status)+'</span></td>'+(withActions?'<td><select onchange="changeStatus('+o.id+',this.value)">'+['New','In progress','Waiting for approval','Completed'].map(s=>'<option '+(s===o.status?'selected':'')+'>'+s+'</option>').join('')+'</select> <button class="btn danger" onclick="removeOrder('+o.id+')">Delete</button></td>':'')+'</tr>').join('')+'</tbody></table>'}
function esc(s){return String(s||'').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]))}
function render(){$('statOrders').textContent=orders.length;$('statDone').textContent=orders.filter(o=>o.status==='Completed').length;$('statSales').textContent=money(orders.filter(o=>o.status==='Completed').reduce((a,o)=>a+Number(o.price||0),0));$('recent').innerHTML=table(orders.slice(-5).reverse());$('ordersTable').innerHTML=table([...orders].reverse(),true)}
function addOrder(){let customer=$('customer').value.trim(),birthdayName=$('birthdayName').value.trim();if(!customer||!birthdayName){alert('Please enter the customer first name and birthday name.');return}orders.push({id:Date.now(),customer,birthdayName,theme:$('theme').value,package:$('package').value,price:Number($('price').value||0),due:$('due').value,status:$('status').value});save();clearOrderForm();alert('Order saved on this device.')}
function clearOrderForm(){['customer','birthdayName','theme','price','due'].forEach(id=>$(id).value='');$('status').value='New';$('package').value='Classic'}
function changeStatus(id,status){let o=orders.find(x=>x.id===id);if(o){o.status=status;save()}}
function removeOrder(id){if(confirm('Delete this order?')){orders=orders.filter(x=>x.id!==id);save()}}
function updateInvite(){let name=$('invName').value||'Birthday Star';$('pName').textContent=name;$('pAge').textContent=$('invAge').value||'';$('pDetails').textContent=[$('invDate').value,$('invVenue').value].filter(Boolean).join('\n')||'Date and time\nParty venue';$('pMessage').textContent=$('invMessage').value||'We hope you can join us!';let colors={pink:['#fff3f8','#eee7ff','#624b85','#d47ba9'],blue:['#eaf7ff','#e6edff','#365b87','#66a9d8'],gold:['#fff7df','#f8e9c9','#80602d','#d3a64e'],green:['#eff9ed','#e2f0dc','#426b4a','#7ca77d']}[ $('invColor').value];let p=$('invitePreview');p.style.background='radial-gradient(circle at top left,'+colors[0]+' 0,'+colors[0]+' 48%,'+colors[1]+' 100%)';p.querySelector('h2').style.color=colors[2];$('pName').style.color=colors[2];$('pAge').style.color=colors[3]}
function downloadInvite(){let node=$('invitePreview');let svg='<svg xmlns="http://www.w3.org/2000/svg" width="900" height="1200"><foreignObject width="100%" height="100%"><div xmlns="http://www.w3.org/1999/xhtml" style="width:900px;height:1200px">'+node.outerHTML+'</div></foreignObject></svg>';let blob=new Blob([svg],{type:'image/svg+xml;charset=utf-8'});let a=document.createElement('a');a.href=URL.createObjectURL(blob);a.download='birthday-invitation-preview.svg';a.click();URL.revokeObjectURL(a.href)}
function savePrices(){['priceClassic','priceCustom','pricePremium'].forEach(id=>localStorage.setItem(id,$(id).value));$('priceNote').textContent='Prices saved on this device.'}
['priceClassic','priceCustom','pricePremium'].forEach(id=>$(id).value=localStorage.getItem(id)||'');
render();updateInvite();
</script>
</body></html>'''
path = Path("/mnt/data/invite_studio.html")
path.write_text(html, encoding="utf-8")
print(f"Created {path} ({path.stat().st_size:,} bytes)")