← back to Animals

public/dogpark/index.html

168 lines

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Dog Park · AnimalsDirectory</title>
<style>
  *{box-sizing:border-box;margin:0;padding:0}
  html,body{width:100%;height:100%;overflow:hidden;background:#1a2a1f;font-family:-apple-system,BlinkMacSystemFont,sans-serif;color:#fff}
  canvas{display:block}
  #ui{position:fixed;top:0;left:0;right:0;padding:10px 14px;background:linear-gradient(180deg,rgba(0,0,0,.55),transparent);z-index:10;display:flex;align-items:center;font-size:13px;pointer-events:none}
  #ui > *{pointer-events:auto}
  #ui a{color:#fff;text-decoration:none;opacity:.9}
  #ui a:hover{opacity:1}
  #brand{display:flex;align-items:center;gap:.5em;font-weight:700;letter-spacing:.02em}
  #brand .mark{font-size:18px;line-height:1}
  #brand .name{font-size:14px}
  #nav-toggle{margin-left:auto;width:38px;height:34px;display:flex;flex-direction:column;justify-content:center;align-items:center;gap:5px;background:rgba(0,0,0,.45);border:1px solid rgba(255,255,255,.18);border-radius:8px;cursor:pointer;padding:0}
  #nav-toggle span{display:block;width:18px;height:2px;background:#fff;border-radius:2px;transition:transform 180ms,opacity 180ms}
  #nav-toggle[aria-expanded="true"] span:nth-child(1){transform:translateY(7px) rotate(45deg)}
  #nav-toggle[aria-expanded="true"] span:nth-child(2){opacity:0}
  #nav-toggle[aria-expanded="true"] span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}
  #nav-menu{position:fixed;top:54px;right:14px;min-width:200px;background:rgba(14,14,16,.96);border:1px solid rgba(255,255,255,.12);border-radius:10px;padding:6px;display:none;flex-direction:column;z-index:11;backdrop-filter:blur(10px);box-shadow:0 16px 48px rgba(0,0,0,.55)}
  #nav-menu.show{display:flex}
  #nav-menu a{padding:.55em .8em;border-radius:6px;font-size:13px;color:#f4f1ea;opacity:.85}
  #nav-menu a:hover{background:rgba(212,160,74,.18);opacity:1;color:#f4f1ea}
  #nav-menu a.active{background:rgba(212,160,74,.28);color:#f4f1ea;opacity:1;font-weight:600}
  #join{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.7);z-index:100}
  #join .panel{background:#fff;color:#1a1a1a;padding:2em 2.5em;border-radius:14px;width:360px;max-width:90vw}
  #join h1{margin-bottom:.5em;font-size:1.4em;color:#0f4d3a}
  #join p{font-size:.9em;color:#666;margin-bottom:1em}
  #join label{display:block;margin:.6em 0;font-weight:600;font-size:.92em}
  #join input,#join select{width:100%;padding:.6em .8em;border:1px solid #ddd;border-radius:6px;font:inherit;margin-top:.2em}
  #join button{margin-top:1em;width:100%;background:#0f4d3a;color:#fff;border:0;padding:.85em;font-weight:700;font-size:1em;border-radius:8px;cursor:pointer}
  #avatar-banner{display:none;align-items:center;gap:12px;padding:10px;margin-bottom:14px;background:#fdf5e6;border:1px solid #d4a04a;border-radius:8px;font-size:.85em;color:#5a4020}
  #avatar-banner.show{display:flex}
  #avatar-banner img{width:48px;height:48px;border-radius:6px;object-fit:cover;flex-shrink:0}
  #avatar-banner b{color:#0e0e10}
  #avatar-banner a{color:#a07028;text-decoration:underline;font-size:.78em;display:block;margin-top:2px}
  #avatar-link{display:block;margin-top:.7em;text-align:center;font-size:.85em;letter-spacing:.06em;color:#0f4d3a;text-decoration:none;border:1px solid #d4d8d2;padding:.7em;border-radius:8px;font-weight:500;transition:background 160ms,border-color 160ms}
  #avatar-link:hover{background:#fdf5e6;border-color:#d4a04a;color:#5a4020}
  #hud{position:fixed;bottom:14px;left:14px;background:rgba(0,0,0,.55);padding:10px 14px;border-radius:10px;font-size:12px;line-height:1.6}
  #hud kbd{background:#fff;color:#000;padding:1px 6px;border-radius:4px;font-family:ui-monospace,Menlo,monospace;font-size:11px;margin:0 2px}
  #autoBtn{background:rgba(212,160,74,.18);color:#f4f1ea;border:1px solid #d4a04a;padding:.4em .9em;border-radius:6px;font:inherit;font-size:12px;font-weight:600;cursor:pointer;letter-spacing:.04em}
  #autoBtn:hover{background:rgba(212,160,74,.32)}
  #autoBtn.on{background:#d4a04a;color:#0e0e10}
  #counter{position:fixed;top:50px;left:14px;background:rgba(0,0,0,.55);padding:8px 12px;border-radius:8px;font-size:12px}
  #log{position:fixed;bottom:14px;right:14px;width:240px;max-height:140px;overflow:auto;background:rgba(0,0,0,.55);padding:8px 12px;border-radius:8px;font-size:11px;line-height:1.5}
  #chatbar{position:fixed;left:50%;bottom:24px;transform:translateX(-50%) translateY(120%);background:rgba(14,14,16,.92);border:1px solid #d4a04a;border-radius:14px;padding:10px 14px;display:flex;align-items:center;gap:12px;z-index:60;backdrop-filter:blur(10px);box-shadow:0 12px 40px rgba(0,0,0,.5);transition:transform 220ms cubic-bezier(.2,.7,.2,1);min-width:380px}
  #chatbar.show{transform:translateX(-50%) translateY(0)}
  #chatbar .who{font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:#d4a04a;font-weight:500;flex-shrink:0}
  #chatbar input{flex:1;background:transparent;border:0;outline:0;color:#f4f1ea;font:inherit;font-size:14px;padding:6px 0;min-width:200px}
  #chatbar input::placeholder{color:#7a7873}
  #chatbar .send{background:#d4a04a;color:#0e0e10;border:0;padding:6px 12px;border-radius:8px;font-weight:600;font-size:11px;letter-spacing:.14em;text-transform:uppercase;cursor:pointer}
  #chatbar .send:hover{background:#e6b86a}
</style>
</head>
<body>
<div id="ui">
  <a id="brand" href="/" aria-label="AnimalsDirectory home">
    <span class="mark">🐾</span><span class="name">AnimalsDirectory</span>
  </a>
  <button id="nav-toggle" aria-label="Open menu" aria-controls="nav-menu" aria-expanded="false">
    <span></span><span></span><span></span>
  </button>
</div>
<nav id="nav-menu" aria-label="Site sections">
  <a href="/">Home</a>
  <a href="/breeds">Breeds</a>
  <a href="/dog-park" class="active">Dog Park</a>
  <a href="/shows">Shows</a>
  <a href="/photographers">Photographers</a>
  <a href="/vets">Vets</a>
  <a href="/marketplace">Marketplace</a>
  <a href="/circles">Circles</a>
  <a href="/my-pets">My Pets</a>
  <a href="/register-dog">Register Dog</a>
  <a href="/about">About</a>
</nav>

<div id="join">
  <div class="panel">
    <h1>🐕 Welcome to the dog park</h1>
    <p>Pick a name + dog color, then walk around with WASD. Click to throw a ball, Shift+click for a frisbee. Press C to call your dog.</p>
    <div id="avatar-banner">
      <img id="avatar-thumb" alt="">
      <div>Using your <b id="avatar-tag">custom avatar</b>. <a href="/static/dogpark/avatars-gallery.html">change</a></div>
    </div>
    <label>Your name <input id="ownerName" maxlength="20" placeholder="Your first name"></label>
    <label>Dog's name <input id="dogName" maxlength="20" placeholder="What's your dog called?"></label>
    <label>Dog color <select id="dogColor">
      <option value="#c97a3e">Golden</option>
      <option value="#3a2818">Black Lab</option>
      <option value="#8b6f47">Brown</option>
      <option value="#e8d4a8">Cream</option>
      <option value="#d4a574">Yellow</option>
      <option value="#5a3a28">Chocolate</option>
      <option value="#fff">White</option>
      <option value="#888">Grey</option>
    </select></label>
    <button id="joinBtn">Enter park →</button>
    <a href="/static/dogpark/avatars-gallery.html" id="avatar-link">Browse the avatar gallery →</a>
  </div>
</div>

<script>
  // Hamburger nav: toggle on click, close on outside-click / Escape. Sits over
  // the Three.js canvas so the menu must not steal pointer events when closed
  // (handled in CSS with pointer-events:none on #ui + auto on its children).
  (() => {
    const btn = document.getElementById('nav-toggle');
    const menu = document.getElementById('nav-menu');
    if (!btn || !menu) return;
    const set = (open) => {
      menu.classList.toggle('show', open);
      btn.setAttribute('aria-expanded', open ? 'true' : 'false');
      btn.setAttribute('aria-label', open ? 'Close menu' : 'Open menu');
    };
    btn.addEventListener('click', (e) => { e.stopPropagation(); set(!menu.classList.contains('show')); });
    document.addEventListener('click', (e) => {
      if (!menu.classList.contains('show')) return;
      if (menu.contains(e.target) || btn.contains(e.target)) return;
      set(false);
    });
    document.addEventListener('keydown', (e) => { if (e.key === 'Escape') set(false); });
  })();
</script>

<script>
  // Surface custom avatar if present
  (() => {
    const url = localStorage.getItem('dogpark_avatar_url');
    const dogName = localStorage.getItem('dogpark_dog_name');
    if (url) {
      const banner = document.getElementById('avatar-banner');
      document.getElementById('avatar-thumb').src = url;
      if (dogName) document.getElementById('avatar-tag').textContent = dogName + ' avatar';
      banner.classList.add('show');
      // Pre-fill dog name from chosen avatar
      if (dogName) document.getElementById('dogName').value = dogName;
    }
  })();
</script>

<div id="counter" style="display:none">👤 1 owner · 🐕 1 dog · 🎾 0 toys</div>
<div id="chatbar">
  <span class="who" id="chatWho">—</span>
  <input id="chatInput" maxlength="140" placeholder="Say something…" autocomplete="off">
  <button class="send" id="chatSend">Send</button>
</div>
<div id="log" style="display:none"></div>
<div id="hud" style="display:none">
  <div><kbd>W</kbd><kbd>A</kbd><kbd>S</kbd><kbd>D</kbd> or <kbd>↑</kbd><kbd>↓</kbd><kbd>←</kbd><kbd>→</kbd> walk · <kbd>Shift</kbd> run · <kbd>C</kbd> call dog</div>
  <div><kbd>Click</kbd> throw ball · <kbd>Shift+Click</kbd> throw frisbee</div>
  <div><kbd>P</kbd> first/third person · <kbd>T</kbd> auto walk</div>
  <div style="margin-top:6px"><button id="autoBtn" type="button">▶ Auto walk</button></div>
</div>

<script type="importmap">
{ "imports": {
  "three": "https://unpkg.com/three@0.160.0/build/three.module.js",
  "three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/"
} }
</script>
<script type="module" src="/static/dogpark/dogpark.js"></script>
</body>
</html>