← back to Rentv 2026
public/advertise.html
315 lines
<!doctype html><html lang="en"><head>
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>Advertise on RENTV — Buy Ad Slots · Sponsor CRE Video</title>
<meta name="description" content="Buy monthly banner ad slots and sponsor commercial-real-estate videos on RENTV — the video-first CRE network. Live rate card, real availability, reserve online.">
<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=Playfair+Display:ital,wght@0,700;0,800;1,600&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<style>
:root{--red:#c8102e;--ink:#14171a;--sub:#5b636b;--line:#e4e7ea;--wash:#f5f6f8;--up:#0f8a4d;--gold:#c6a24a;
--sans:'Inter',system-ui,sans-serif;--serif:'Playfair Display',Georgia,serif;--cols:4}
*{box-sizing:border-box;margin:0}
body{font-family:var(--sans);color:var(--ink);background:#fff;line-height:1.5}
a{color:inherit;text-decoration:none}
.wrap{max-width:1240px;margin:0 auto;padding:0 22px}
.top{position:sticky;top:0;z-index:30;display:flex;align-items:center;justify-content:space-between;height:56px;background:#0b0e12;color:#e7ecf1;padding:0 22px;border-bottom:2px solid var(--red)}
.top .brand{font-family:var(--serif);font-weight:800;font-size:23px}.top .brand span{color:var(--red)}
.top .nav{display:flex;gap:4px;align-items:center;font-size:12.5px;font-weight:700}
.top .nav a{padding:8px 11px;border-radius:7px;color:#c3cbd3}.top .nav a:hover{background:#161d25;color:#fff}
.top .nav a.cta{background:var(--red);color:#fff}
.phero{background:linear-gradient(120deg,#0b0e12 0%,#20121a 55%,#c8102e 140%);color:#fff;padding:56px 0 48px}
.phero .k{font-size:12px;letter-spacing:.22em;text-transform:uppercase;color:#f0b7bf;margin-bottom:12px;font-weight:800}
.phero h1{font-family:var(--serif);font-size:clamp(32px,5vw,54px);line-height:1.06;letter-spacing:-.5px;max-width:20ch}
.phero p{max-width:62ch;margin-top:14px;font-size:17.5px;color:#e7ecf1;opacity:.95}
.phero .stats{display:flex;flex-wrap:wrap;gap:38px;margin-top:26px}
.phero .stats .v{font-family:var(--serif);font-size:34px;line-height:1}.phero .stats .l{font-size:12px;letter-spacing:.05em;text-transform:uppercase;color:#e7b9c1;margin-top:5px}
.phero .jump{margin-top:26px;display:flex;gap:10px;flex-wrap:wrap}
.phero .jump a{background:rgba(255,255,255,.1);border:1px solid rgba(255,255,255,.25);color:#fff;font-weight:800;font-size:13px;padding:11px 18px;border-radius:8px}
.phero .jump a.solid{background:#fff;color:#0b0e12;border-color:#fff}
section.blk{padding:44px 0;border-bottom:1px solid var(--line)}
.eyebrow{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.14em;color:var(--red);margin-bottom:8px}
h2.big{font-family:var(--serif);font-size:clamp(25px,3.2vw,36px);line-height:1.12;letter-spacing:-.4px}
.lead2{max-width:70ch;color:var(--sub);margin-top:10px;font-size:16.5px}
/* rate-card placement grid */
.rate{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;margin-top:26px}
@media(max-width:900px){.rate{grid-template-columns:1fr}}
.slot{border:1px solid var(--line);border-radius:12px;padding:20px 20px 18px;display:flex;flex-direction:column;gap:6px;transition:.2s;background:#fff;position:relative}
.slot:hover{transform:translateY(-3px);box-shadow:0 16px 40px rgba(0,0,0,.08);border-color:#cfd4d9}
.slot .surface{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.08em;color:var(--sub)}
.slot h3{font-family:var(--serif);font-size:21px}
.slot .pos{color:var(--sub);font-size:14px;flex:1}
.slot .fmt{font-size:12px;color:#8a929b}
.slot .row{display:flex;align-items:baseline;justify-content:space-between;margin-top:8px;gap:10px}
.slot .price{font-family:var(--serif);font-size:26px;color:var(--ink)}.slot .price span{font-size:13px;color:var(--sub);font-family:var(--sans);font-weight:600}
.slot .imp{font-size:12px;color:var(--sub)}
.status{font-size:10.5px;font-weight:800;text-transform:uppercase;letter-spacing:.05em;padding:3px 9px;border-radius:20px}
.status.available{background:#eafaf0;color:#0f8a4d}
.status.reserved{background:#fff6e6;color:#a06a00}
.status.sold{background:#fdeaec;color:var(--red)}
.slot .buy{margin-top:12px;background:var(--red);color:#fff;border:0;border-radius:8px;padding:11px 14px;font-weight:800;font-size:13px;cursor:pointer;letter-spacing:.02em}
.slot .buy[disabled]{background:#e7eaee;color:#98a0a8;cursor:not-allowed}
.slot .buy:not([disabled]):hover{filter:brightness(1.07)}
/* video inventory */
.invhead{display:flex;flex-wrap:wrap;align-items:center;gap:14px;margin-top:22px}
.invhead .count{font-size:13px;color:var(--sub);font-weight:600}
.controls{display:flex;align-items:center;gap:18px;flex-wrap:wrap;margin-left:auto}
.controls label{font-size:11px;font-weight:800;letter-spacing:.05em;text-transform:uppercase;color:var(--sub);display:flex;align-items:center;gap:8px}
.controls select{font:inherit;font-size:13px;font-weight:600;border:1px solid var(--line);border-radius:7px;padding:8px 10px;background:#fff;color:var(--ink)}
.controls input[type=range]{accent-color:var(--red)}
.filters{display:flex;flex-wrap:wrap;gap:7px;margin-top:16px}
.chip{cursor:pointer;border:1px solid var(--line);background:#fff;color:var(--ink);font:inherit;font-size:12.5px;font-weight:700;border-radius:20px;padding:6px 13px}
.chip:hover{border-color:var(--red);color:var(--red)}
.chip.on{background:var(--red);color:#fff;border-color:var(--red)}
.invgrid{display:grid;grid-template-columns:repeat(var(--cols),1fr);gap:16px;margin-top:18px}
@media(max-width:900px){.invgrid{grid-template-columns:repeat(2,1fr)!important}}
@media(max-width:560px){.invgrid{grid-template-columns:1fr!important}}
.vslot{border:1px solid var(--line);border-radius:11px;overflow:hidden;background:#fff;display:flex;flex-direction:column;transition:.16s}
.vslot:hover{transform:translateY(-3px);box-shadow:0 14px 34px rgba(0,0,0,.09);border-color:#cfd4d9}
.vthumb{position:relative;aspect-ratio:16/9;background:linear-gradient(135deg,#1f2a37,#c8102e) center/cover;display:block}
.vthumb img{width:100%;height:100%;object-fit:cover;display:block}
.vthumb .src{position:absolute;left:8px;top:8px;background:rgba(11,14,18,.82);color:#fff;font-size:9.5px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;padding:3px 7px;border-radius:4px}
.vthumb .st{position:absolute;right:8px;top:8px}
.vthumb .sold-flag{position:absolute;left:8px;bottom:8px;background:var(--gold);color:#20180a;font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:.04em;padding:3px 8px;border-radius:4px}
.vbody{padding:11px 12px 13px;display:flex;flex-direction:column;gap:6px;flex:1}
.vbody .cat{font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:.05em;color:var(--red)}
.vbody h4{font:600 13.5px/1.3 var(--sans);flex:1}
.vbody .foot{display:flex;align-items:baseline;justify-content:space-between;gap:8px;margin-top:2px}
.vbody .price{font-weight:800;font-size:15px}.vbody .price span{font-size:11px;color:var(--sub);font-weight:600}
.vbody .spbtn{width:100%;margin-top:4px;background:#0b0e12;color:#fff;border:0;border-radius:7px;padding:9px;font-weight:800;font-size:12.5px;cursor:pointer}
.vbody .spbtn[disabled]{background:#e7eaee;color:#98a0a8;cursor:not-allowed}
.vbody .spbtn:not([disabled]):hover{background:var(--red)}
.more{display:block;margin:22px auto 0;background:#fff;border:1px solid var(--line);border-radius:8px;padding:12px 22px;font-weight:800;cursor:pointer;font-size:13px}
.more:hover{border-color:var(--red);color:var(--red)}
footer{background:#0b0e12;color:#8b939b;padding:34px 0 28px;font-size:13px}
footer .cols{display:grid;grid-template-columns:2fr 1fr 1fr 1fr;gap:26px}
@media(max-width:820px){footer .cols{grid-template-columns:1fr 1fr}}
footer .logo{color:#fff;font-family:var(--serif);font-size:24px;margin-bottom:8px}footer .logo span{color:var(--red)}
footer h5{color:#e7ecf1;font-size:12px;text-transform:uppercase;letter-spacing:.06em;margin-bottom:8px}footer a{display:block;padding:3px 0;color:#9aa4ad}
footer .fine{border-top:1px solid #1c232b;margin-top:24px;padding-top:16px;color:#6b747d}
/* reserve modal */
.ovl{position:fixed;inset:0;z-index:100;background:rgba(6,9,12,.6);backdrop-filter:blur(3px);opacity:0;pointer-events:none;transition:.2s;display:flex;align-items:center;justify-content:center;padding:18px}
.ovl.open{opacity:1;pointer-events:auto}
.sheet{background:#fff;border-radius:14px;max-width:520px;width:100%;max-height:92vh;overflow:auto;box-shadow:0 30px 80px rgba(0,0,0,.4)}
.sheet .sh{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;padding:20px 22px 8px}
.sheet .sh h3{font-family:var(--serif);font-size:22px}
.sheet .sh .sub{color:var(--sub);font-size:13px;margin-top:3px}
.sheet .sh .x{border:1px solid var(--line);background:#fff;width:34px;height:34px;border-radius:8px;font-size:15px;cursor:pointer}
.sheet .quote{margin:8px 22px 0;background:var(--wash);border:1px solid var(--line);border-radius:10px;padding:14px 16px;display:flex;justify-content:space-between;align-items:center;gap:12px}
.sheet .quote .q{font-size:13px;color:var(--sub)}.sheet .quote .q b{color:var(--ink)}
.sheet .quote .amt{font-family:var(--serif);font-size:24px}
form.rf{padding:14px 22px 22px;display:grid;gap:12px}
form.rf .two{display:grid;grid-template-columns:1fr 1fr;gap:12px}
@media(max-width:480px){form.rf .two{grid-template-columns:1fr}}
form.rf label{font-size:11px;font-weight:800;text-transform:uppercase;letter-spacing:.05em;color:var(--sub);display:block;margin-bottom:5px}
form.rf input,form.rf select,form.rf textarea{width:100%;padding:11px 13px;border:1px solid var(--line);border-radius:8px;font:inherit;font-size:14.5px}
form.rf textarea{min-height:70px;resize:vertical}
form.rf .submit{background:var(--red);color:#fff;border:0;border-radius:8px;padding:13px;font-weight:800;font-size:14px;cursor:pointer;margin-top:4px}
form.rf .note{font-size:12px;color:var(--sub);text-align:center}
form.rf .ok{display:none;padding:14px 16px;background:#ecfdf3;border:1px solid #a7f3d0;border-radius:9px;color:#067647;font-size:14px;font-weight:600}
</style></head><body>
<header class="top">
<a class="brand" href="/">REN<span>TV</span></a>
<nav class="nav">
<a href="/">Home</a><a href="/review">The REview</a><a href="/markets">Markets</a><a href="#slots">Ad Slots</a><a href="#video">Sponsor Video</a><a href="/2026/">2026 Redesign</a>
</nav>
</header>
<div class="phero"><div class="wrap">
<div class="k">Media Kit · 2026 · Buy online</div>
<h1>Put your firm in front of the CRE market — by the month.</h1>
<p>RENTV is the video-first commercial-real-estate network. Buy a monthly banner slot, or sponsor any one of our <b id="heroCount">150+</b> videos. Live rate card, real availability, reserve in two minutes — no charge until we confirm.</p>
<div class="stats">
<div><div class="v">1998</div><div class="l">Trusted since</div></div>
<div><div class="v">Daily</div><div class="l">Newsletter</div></div>
<div><div class="v" id="statVideos">—</div><div class="l">Sponsorable videos</div></div>
<div><div class="v" id="statSlots">—</div><div class="l">Banner slots</div></div>
</div>
<div class="jump"><a class="solid" href="#slots">See banner slots ↓</a><a href="#video">Sponsor a video ↓</a></div>
</div></div>
<section class="blk" id="slots"><div class="wrap">
<div class="eyebrow">Banner & placement slots · monthly</div>
<h2 class="big">Buy a slot. Own the position for the month.</h2>
<p class="lead2">Fixed monthly placements across the homepage, section pages, The REview and the RENTV Daily newsletter. One advertiser per slot — when it's yours, it's yours for the flight.</p>
<div class="rate" id="rate"></div>
</div></section>
<section class="blk" id="video"><div class="wrap">
<div class="eyebrow">Video sponsorship · every RENTV video</div>
<h2 class="big">Sponsor the content the market actually watches.</h2>
<p class="lead2">The entire RENTV video library is sponsorable — CRE Talk episodes, The REview interviews, JLL TV, the deal wire and RENTV Originals. Put your name on a title, a category or a single video, monthly.</p>
<div class="invhead">
<span class="count" id="invCount">Loading inventory…</span>
<div class="controls">
<label>Sort
<select id="sort">
<option value="featured">Featured first</option>
<option value="views">Most viewed</option>
<option value="price-hi">Price ↓</option>
<option value="price-lo">Price ↑</option>
<option value="title">Title A→Z</option>
</select>
</label>
<label>Density <input type="range" id="density" min="2" max="6" step="1" value="4"></label>
</div>
</div>
<div class="filters" id="filters"></div>
<div class="invgrid" id="invgrid"></div>
<button class="more" id="more" style="display:none">Show more videos</button>
</div></section>
<footer><div class="wrap"><div class="cols">
<div><div class="logo">REN<span>TV</span>.com</div><p style="max-width:36ch;color:#9aa4ad">The video-first commercial real estate network — deals, financing, development and conferences across the Western U.S. since 1998.</p></div>
<div><h5>Advertise</h5><a href="#slots">Banner slots</a><a href="#video">Sponsor video</a><a href="/subscribe">Newsletter</a><a href="/2026/">2026 redesign</a></div>
<div><h5>Watch</h5><a href="/review">The REview</a><a href="/cre-talk">CRE Talk</a><a href="/markets">Markets</a></div>
<div><h5>Company</h5><a href="/about">About</a><a href="/">Home</a></div>
</div><div class="fine">© 2026 RENTV.com · Reserving a slot records an inquiry only — no charge is made until RENTV confirms availability and rate. · (310) 213-6409</div></div></footer>
<!-- reserve / buy modal -->
<div class="ovl" id="ovl" role="dialog" aria-modal="true" aria-label="Reserve ad slot">
<div class="sheet">
<div class="sh"><div><h3 id="rTitle">Reserve slot</h3><div class="sub" id="rSub"></div></div><button class="x" id="rClose" aria-label="Close">✕</button></div>
<div class="quote"><div class="q" id="rQuote"></div><div class="amt" id="rAmt"></div></div>
<form class="rf" id="rForm">
<div class="ok" id="rOk"></div>
<div class="two">
<div><label>Months</label><select id="fMonths"><option>1</option><option>3</option><option selected>6</option><option>12</option></select></div>
<div><label>Start month</label><input id="fStart" type="month"></div>
</div>
<div><label>Advertiser / brand</label><input id="fAdv" required placeholder="Your firm"></div>
<div class="two">
<div><label>Your name</label><input id="fName" required placeholder="Full name"></div>
<div><label>Phone</label><input id="fPhone" placeholder="Optional"></div>
</div>
<div><label>Work email</label><input id="fEmail" type="email" required placeholder="you@company.com"></div>
<div><label>Notes</label><textarea id="fNote" placeholder="Creative, targeting, questions…"></textarea></div>
<button class="submit" type="submit">Reserve — no charge yet</button>
<div class="note">We'll confirm availability & rate within one business day. No payment is taken online.</div>
</form>
</div>
</div>
<script>
const $=s=>document.querySelector(s), esc=s=>(s||'').replace(/[&<>"]/g,c=>({'&':'&','<':'<','>':'>','"':'"'}[c]));
const money=n=>'$'+Number(n||0).toLocaleString('en-US');
async function jget(u){try{const r=await fetch(u,{credentials:'same-origin'});return r.ok?await r.json():null;}catch(e){return null;}}
/* ---------- banner rate card ---------- */
async function loadRate(){
const d=await jget('/api/ad/rate-card'); const el=$('#rate'); const P=(d&&d.placements)||[];
$('#statSlots').textContent=P.length;
el.innerHTML=P.map(p=>{
const st=p.status||'available'; const avail=st==='available';
return `<div class="slot">
<span class="surface">${esc(p.surface)}</span>
<h3>${esc(p.name)}</h3>
<div class="pos">${esc(p.position)}</div>
<div class="fmt">${esc(p.format)} · ${esc(p.impressions_est||'')}</div>
<div class="row"><div class="price">${money(p.price_monthly)}<span>/mo</span></div><span class="status ${st}">${st}${p.advertiser?' · '+esc(p.advertiser):''}</span></div>
<button class="buy" data-kind="placement" data-id="${esc(p.id)}" data-name="${esc(p.name)}" data-price="${p.price_monthly}" ${avail?'':'disabled'}>${avail?'Reserve this slot':'Taken'}</button>
</div>`;
}).join('');
}
/* ---------- video sponsorship inventory (sort + density + filters) ---------- */
let INV=[], curSource='', PAGE=60;
const SORTS={
featured:(a,b)=>rank(a)-rank(b),
views:(a,b)=>(b.plays||0)-(a.plays||0),
'price-hi':(a,b)=>(b.sponsorship.price_monthly||0)-(a.sponsorship.price_monthly||0),
'price-lo':(a,b)=>(a.sponsorship.price_monthly||0)-(b.sponsorship.price_monthly||0),
title:(a,b)=>String(a.title||'').localeCompare(String(b.title||''))
};
const SRCRANK={vimeo:0,youtube:1,original:2,'news-wire':3,social:4};
function rank(x){ // featured: episodes/originals first, then by plays
let r=SRCRANK[x.source]!=null?SRCRANK[x.source]:9;
if(x.kind==='episode')r-=0.5; if(x.pinned)r-=1;
return r - Math.min((x.plays||0)/1000,0.9);
}
function loadPersist(){
try{ const s=localStorage.getItem('rentv_adv_sort'); if(s)$('#sort').value=s;
const d=localStorage.getItem('rentv_adv_density'); if(d){$('#density').value=d;document.documentElement.style.setProperty('--cols',d);} }catch(e){}
}
async function loadInv(){
const d=await jget('/api/ad/inventory'); INV=(d&&d.items)||[];
const sum=(d&&d.summary)||{}; $('#statVideos').textContent=sum.total||INV.length; $('#heroCount').textContent=(sum.total||INV.length)+'';
// source filter chips
const srcs=['',...Object.keys(sum.by_source||{})];
const nice={'':'All','youtube':'The REview','vimeo':'CRE Talk','original':'RENTV Originals','news-wire':'Deal Wire','social':'Social'};
$('#filters').innerHTML=srcs.map(s=>`<button class="chip ${s===curSource?'on':''}" data-src="${s}">${esc(nice[s]||s)}${s?` · ${(sum.by_source||{})[s]||0}`:` · ${sum.total||INV.length}`}</button>`).join('');
render();
}
function view(){
let v=curSource?INV.filter(x=>x.source===curSource):INV.slice();
v.sort(SORTS[$('#sort').value]||SORTS.featured);
return v;
}
function render(){
const v=view(); const slice=v.slice(0,PAGE);
$('#invCount').textContent=v.length+' sponsorable video'+(v.length===1?'':'s')+(curSource?'':' · all sources');
$('#invgrid').innerHTML=slice.map(card).join('');
$('#more').style.display=v.length>PAGE?'block':'none';
}
function card(x){
const sp=x.sponsorship||{}; const sold=sp.status==='sold'; const reserved=sp.status==='reserved';
const thumb=x.thumb?`<img loading="lazy" src="${esc(x.thumb)}" alt="" onerror="this.style.display='none'">`:'';
const stat=sold?`<span class="status sold">Sold</span>`:reserved?`<span class="status reserved">Reserved</span>`:`<span class="status available">Open</span>`;
const soldFlag=sold?`<span class="sold-flag">◆ ${esc(sp.advertiser||'Sponsored')}</span>`:'';
const canBuy=!sold;
return `<div class="vslot">
<a class="vthumb" href="${esc(x.view_url||'#')}" ${x.view_url?'target="_blank" rel="noopener"':''} title="Watch">
${thumb}<span class="src">${esc(x.source==='youtube'?'The REview':x.source==='vimeo'?'CRE Talk':x.source==='news-wire'?'Deal Wire':x.source==='original'?'Original':x.source)}</span>
<span class="st">${stat}</span>${soldFlag}
</a>
<div class="vbody">
<div class="cat">${esc(x.cat||'')}</div>
<h4>${esc(x.title||'Untitled')}</h4>
<div class="foot"><div class="price">${money(sp.price_monthly)}<span>/mo</span></div><div style="font-size:11px;color:var(--sub)">${x.plays?esc(x.plays)+' plays':''}</div></div>
<button class="spbtn" data-kind="video" data-id="${esc(x.uid)}" data-name="${esc(x.title||x.uid)}" data-price="${sp.price_monthly||0}" ${canBuy?'':'disabled'}>${canBuy?'Sponsor this →':'Sponsored'}</button>
</div>
</div>`;
}
$('#sort').addEventListener('change',()=>{try{localStorage.setItem('rentv_adv_sort',$('#sort').value)}catch(e){}; render();});
$('#density').addEventListener('input',()=>{const v=$('#density').value;document.documentElement.style.setProperty('--cols',v);try{localStorage.setItem('rentv_adv_density',v)}catch(e){}});
$('#filters').addEventListener('click',e=>{const b=e.target.closest('[data-src]');if(!b)return;curSource=b.dataset.src;PAGE=60;
document.querySelectorAll('#filters .chip').forEach(c=>c.classList.toggle('on',c===b));render();});
$('#more').addEventListener('click',()=>{PAGE+=60;render();});
/* ---------- reserve modal ---------- */
let RES={};
document.addEventListener('click',e=>{const b=e.target.closest('[data-kind]');if(!b||b.disabled)return;
RES={kind:b.dataset.kind,id:b.dataset.id,name:b.dataset.name,price:Number(b.dataset.price)||0};
$('#rTitle').textContent=(RES.kind==='video'?'Sponsor: ':'Reserve: ')+RES.name;
$('#rSub').textContent=RES.kind==='video'?'Video sponsorship · monthly':'Banner / placement slot · monthly';
updateQuote();
$('#rOk').style.display='none'; $('#rForm').querySelectorAll('input,select,textarea,button').forEach(x=>x.disabled=false);
$('#ovl').classList.add('open');});
function updateQuote(){const m=Number($('#fMonths').value)||1;const tot=RES.price*m;
$('#rQuote').innerHTML=`<b>${money(RES.price)}</b>/mo × ${m} month${m===1?'':'s'}`;
$('#rAmt').textContent=money(tot);}
$('#fMonths').addEventListener('change',updateQuote);
$('#rClose').onclick=()=>$('#ovl').classList.remove('open');
$('#ovl').addEventListener('click',e=>{if(e.target===$('#ovl'))$('#ovl').classList.remove('open');});
document.addEventListener('keydown',e=>{if(e.key==='Escape')$('#ovl').classList.remove('open');});
$('#rForm').addEventListener('submit',async e=>{e.preventDefault();
const email=$('#fEmail').value.trim();
if(!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email)){$('#fEmail').focus();return;}
const body={kind:RES.kind,target_id:RES.id,target_name:RES.name,months:Number($('#fMonths').value)||1,
start:$('#fStart').value||'',advertiser:$('#fAdv').value.trim(),contact_name:$('#fName').value.trim(),
email,phone:$('#fPhone').value.trim(),note:$('#fNote').value.trim(),quoted_monthly:RES.price};
const btn=$('#rForm .submit'); btn.disabled=true; btn.textContent='Reserving…';
const r=await fetch('/api/ad/reserve',{method:'POST',headers:{'Content-Type':'application/json'},credentials:'same-origin',body:JSON.stringify(body)});
const j=await r.json().catch(()=>null);
if(j&&j.ok){ const ok=$('#rOk'); ok.textContent='✓ '+j.message; ok.style.display='block';
$('#rForm').querySelectorAll('input,select,textarea').forEach(x=>x.disabled=true); btn.style.display='none';
loadRate(); loadInv(); // reflect the soft-hold
} else { btn.disabled=false; btn.textContent='Reserve — no charge yet'; alert((j&&j.error)?('Please check the form — '+j.error):'Could not reserve, please try again.'); }
});
/* today default for start month */
(function(){const d=new Date();$('#fStart').value=d.getFullYear()+'-'+String(d.getMonth()+1).padStart(2,'0');})();
loadPersist(); loadRate(); loadInv();
</script>
<script src="/2026/elements.js" defer></script>
<script src="/template-toggle.js" defer></script>
</body></html>