← back to Commercialrealestate

public/login.html

82 lines

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex,nofollow">
<title>Sign in · CRCP</title>
<style>
  :root{ --bg:#0b0e13; --panel:#131822; --line:#232c3b; --ink:#eef2f8; --mut:#8b97a8;
         --gold:#d9b25f; --blue:#3b7dff; --err:#ff6b6b; }
  *{ box-sizing:border-box; }
  html,body{ height:100%; }
  body{ margin:0; background:radial-gradient(1200px 800px at 70% -10%, #1a2333 0%, var(--bg) 55%);
        color:var(--ink); font:15px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
        display:grid; place-items:center; }
  .card{ width:min(92vw,380px); background:var(--panel); border:1px solid var(--line);
         border-radius:16px; padding:30px 28px 26px; box-shadow:0 24px 60px rgba(0,0,0,.5); }
  .brand{ display:flex; align-items:center; gap:10px; margin-bottom:4px; }
  .brand .dot{ width:26px; height:26px; border-radius:7px; background:linear-gradient(135deg,var(--gold),#a67c2e);
               display:grid; place-items:center; font-weight:800; color:#1a130a; }
  .brand b{ font-size:17px; letter-spacing:.3px; }
  h1{ font-size:20px; margin:16px 0 2px; }
  p.sub{ margin:0 0 20px; color:var(--mut); font-size:13px; }
  label{ display:block; font-size:12px; text-transform:uppercase; letter-spacing:.5px; color:var(--mut); margin:14px 0 6px; }
  input{ width:100%; padding:11px 12px; border-radius:10px; border:1px solid var(--line);
         background:#0e131c; color:var(--ink); font-size:15px; outline:none; }
  input:focus{ border-color:var(--blue); box-shadow:0 0 0 3px rgba(59,125,255,.18); }
  button{ width:100%; margin-top:22px; padding:12px; border:0; border-radius:999px; cursor:pointer;
          background:var(--blue); color:#fff; font-size:15px; font-weight:600; }
  button:disabled{ opacity:.6; cursor:default; }
  .err{ display:none; margin-top:14px; color:var(--err); font-size:13px; }
  .foot{ margin-top:18px; color:var(--mut); font-size:12px; text-align:center; }
</style>
</head>
<body>
  <form class="card" id="f" autocomplete="on">
    <div class="brand"><span class="dot">C</span><b>CRCP</b></div>
    <h1>Sign in</h1>
    <p class="sub">crcp.agentabrams.com</p>
    <label for="u">Username</label>
    <input id="u" name="username" autocapitalize="none" autocorrect="off" spellcheck="false">
    <label for="p">Password</label>
    <input id="p" name="password" type="password">
    <button id="b" type="submit">Sign In</button>
    <div class="err" id="e"></div>
    <div class="foot">Authorized users only.</div>
  </form>
<script>
  // Open-redirect guard: only honor a same-origin, single-slash path; otherwise land on the root.
  function safeNext(){
    try{
      var n = new URLSearchParams(location.search).get('next') || '/';
      if(!n.startsWith('/') || n.startsWith('//')) return '/';
      return n;
    }catch(_){ return '/'; }
  }
  var f=document.getElementById('f'), b=document.getElementById('b'), e=document.getElementById('e');
  document.getElementById('u').focus();
  f.addEventListener('submit', async function(ev){
    ev.preventDefault();
    e.style.display='none';
    b.disabled=true; b.textContent='Signing in…';
    try{
      var r = await fetch('/auth/login', {
        method:'POST',
        headers:{ 'Content-Type':'application/json' },
        body: JSON.stringify({ username: document.getElementById('u').value, password: document.getElementById('p').value })
      });
      var j = await r.json().catch(function(){ return {}; });
      if(r.ok && j.ok){ location.href = safeNext(); return; }
      e.textContent = j.error || 'Sign-in failed.';
      e.style.display='block';
    }catch(_){
      e.textContent = 'Network error — try again.';
      e.style.display='block';
    }
    b.disabled=false; b.textContent='Sign In';
  });
</script>
</body>
</html>