← back to Rentv Website

public/markets.html

107 lines

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>Rates &amp; Markets — RENTV.com</title>
<meta name="description" content="Live Treasury yield curve, REIT indices and Western-U.S. CRE fundamentals — RENTV.com Rates &amp; Markets.">
<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,400;0,700;1,400&family=Inter:wght@300;400;600;800&display=swap" rel="stylesheet">
<style>
  :root{--red:#c8102e;--ink:#14171a;--sub:#5b636b;--line:#e4e7ea;--bg:#fff;--wash:#f5f6f8;--up:#0f8a4d;--dn:#c8102e;--sans:'Inter',system-ui,sans-serif;--serif:'Playfair Display',Georgia,serif}
  *{box-sizing:border-box;margin:0;padding:0}
  body{font-family:var(--sans);color:var(--ink);background:var(--wash);line-height:1.45}
  a{color:inherit;text-decoration:none}
  .wrap{max-width:1200px;margin:0 auto;padding:0 20px}
  .util{background:var(--ink);color:#cfd4d9;font-size:12px}
  .util .wrap{display:flex;justify-content:space-between;align-items:center;height:34px}
  .util .live{display:inline-flex;align-items:center;gap:7px;letter-spacing:.05em;text-transform:uppercase;font-size:11px}
  .util .dot{width:7px;height:7px;border-radius:50%;background:#22c55e;animation:pulse 2s infinite}
  @keyframes pulse{0%{box-shadow:0 0 0 0 rgba(34,197,94,.6)}70%{box-shadow:0 0 0 7px rgba(34,197,94,0)}100%{box-shadow:0 0 0 0 rgba(34,197,94,0)}}
  .util .acts a{margin-left:14px;color:#cfd4d9}.util .acts a.sub{color:#fff;background:var(--red);padding:4px 12px;border-radius:3px;font-weight:700}
  .mast{background:#fff;border-bottom:2px solid var(--ink);padding:16px 0 12px}
  .mast .wrap{display:flex;align-items:center;justify-content:space-between}
  .logo{font-family:var(--serif);font-weight:700;font-size:32px;letter-spacing:-1px}.logo span{color:var(--red)}
  .logo small{display:block;font-size:11px;font-weight:600;letter-spacing:3px;color:var(--sub);text-transform:uppercase;margin-top:2px;font-family:var(--sans)}
  nav{background:#fff;border-bottom:1px solid var(--line)}
  nav .wrap{display:flex;gap:2px;align-items:center;height:46px;overflow-x:auto}
  nav a{font-size:13px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;padding:14px 13px;border-bottom:3px solid transparent;white-space:nowrap}
  nav a:hover,nav a.on{color:var(--red);border-color:var(--red)}
  .head{padding:30px 0 6px}
  .head h1{font-family:var(--serif);font-size:34px;letter-spacing:-.5px}
  .head p{color:var(--sub);font-size:14px;margin-top:4px}
  .head .fresh{font-size:12px;color:var(--sub);margin-top:8px}
  section{margin:26px 0}
  h2.sec{font-size:14px;text-transform:uppercase;letter-spacing:.08em;border-top:3px solid var(--ink);padding-top:10px;margin-bottom:14px}
  .cards{display:grid;grid-template-columns:repeat(4,1fr);gap:14px}
  .cards.sp{grid-template-columns:repeat(2,1fr)}
  .cards.re{grid-template-columns:repeat(3,1fr)}
  .card{background:#fff;border:1px solid var(--line);border-radius:8px;padding:16px 18px}
  .card .l{font-size:12px;color:var(--sub);text-transform:uppercase;letter-spacing:.05em}
  .card .v{font-family:var(--serif);font-size:30px;font-weight:700;margin-top:4px;line-height:1}
  .card .c{font-size:13px;font-weight:700;margin-top:6px}
  .up{color:var(--up)}.dn{color:var(--dn)}.flat{color:var(--sub)}
  .note{font-size:12px;color:var(--sub);margin-top:8px}
  footer{background:var(--ink);color:#8b939b;margin-top:40px;padding:26px 0;font-size:12px}
  footer .wrap{display:flex;justify-content:space-between;flex-wrap:wrap;gap:10px}
  @media(max-width:860px){.cards{grid-template-columns:1fr 1fr}.cards.re{grid-template-columns:1fr}}
</style>
</head>
<body>
  <div class="util"><div class="wrap">
    <span class="live"><span class="dot"></span> Live market data</span>
    <div class="acts"><a href="/">Home</a><a href="/review">The REview</a><a href="/la-commercial">LA Commercial</a><a class="sub" href="/#news">Subscribe</a></div>
  </div></div>
  <header class="mast"><div class="wrap">
    <a class="logo" href="/">REN<span>TV</span>.com<small>Commercial Real Estate News</small></a>
    <div style="font-size:12px;color:var(--sub);text-align:right">Western U.S. Edition<br><span id="today"></span></div>
  </div></header>
  <nav><div class="wrap">
    <a href="/">Home</a><a href="/sales">Sales</a><a href="/leases">Leases</a><a href="/financing">Financing</a>
    <a href="/development">Development</a><a href="/multifamily">Multifamily</a><a href="/industrial">Industrial</a>
    <a href="/retail">Retail</a><a class="on" href="/markets.html">Rates &amp; Markets</a>
  </div></nav>

  <main class="wrap">
    <div class="head">
      <h1>Rates &amp; Markets</h1>
      <p>The rate backdrop that moves commercial real estate — Treasury curve, REIT benchmarks and Western-U.S. fundamentals.</p>
      <div class="fresh" id="fresh">Loading live market data…</div>
    </div>

    <section><h2 class="sec">U.S. Treasury Yield Curve</h2><div class="cards" id="curve"></div>
      <div class="note">Higher long rates raise borrowing costs and pressure cap rates. Daily change shown in basis points.</div></section>
    <section><h2 class="sec">Curve Spreads</h2><div class="cards sp" id="spreads"></div>
      <div class="note">A steeper curve (positive spread) generally signals easier financing ahead; inversion has historically preceded slowdowns.</div></section>
    <section><h2 class="sec">REIT Benchmarks</h2><div class="cards re" id="reits"></div>
      <div class="note">Public real-estate equities lead private CRE pricing. Daily change shown as a percentage.</div></section>
    <section><h2 class="sec">Western-U.S. Fundamentals</h2><div class="cards sp" id="fund"></div>
      <div class="note">Market-report figures, updated quarterly (CBRE / JLL / Colliers).</div></section>
  </main>

  <footer><div class="wrap"><span>© RENTV.com — Commercial Real Estate News, Western U.S.</span><span>Market data via Yahoo Finance · delayed · informational only</span></div></footer>

<script>
document.getElementById('today').textContent=new Date().toLocaleDateString(undefined,{weekday:'long',year:'numeric',month:'long',day:'numeric'});
const esc=s=>(s||'').replace(/[&<>"]/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c]));
function chg(m){
  if(m.change_bps!=null){const a=Math.abs(m.change_bps);return m.dir==='up'?'<span class="up">▲ '+a+' bps</span>':m.dir==='down'?'<span class="dn">▼ '+a+' bps</span>':'<span class="flat">— unch</span>';}
  if(m.change_pct!=null){const a=Math.abs(m.change_pct);return m.dir==='up'?'<span class="up">▲ '+a+'%</span>':m.dir==='down'?'<span class="dn">▼ '+a+'%</span>':'<span class="flat">— unch</span>';}
  return '';
}
const card=m=>'<div class="card"><div class="l">'+esc(m.label)+'</div><div class="v">'+esc(m.value)+'</div><div class="c">'+chg(m)+(m.as_of?'<span class="flat" style="font-weight:400"> · '+esc(m.as_of)+'</span>':'')+'</div></div>';
async function load(){
  try{
    const d=await (await fetch('/api/markets')).json();
    document.getElementById('curve').innerHTML=(d.curve||[]).map(card).join('');
    document.getElementById('spreads').innerHTML=(d.spreads||[]).map(card).join('');
    document.getElementById('reits').innerHTML=(d.reits||[]).map(card).join('');
    document.getElementById('fund').innerHTML=(d.fundamentals||[]).map(card).join('');
    const mins=Math.max(0,Math.round((Date.now()-new Date(d.fetched_at))/60000));
    document.getElementById('fresh').textContent=(d.source||'Live market data')+' · '+(mins<2?'updated just now':'updated '+mins+' min ago');
  }catch(e){document.getElementById('fresh').textContent='Market data temporarily unavailable — retrying…';}
}
load(); setInterval(load,120000);
</script>
</body>
</html>