<!doctype html><html lang="es"><head>  <meta charset="utf-8" />  <meta name="viewport" content="width=device-width, initial-scale=1" />  <title>Picapon — Mundo Gaturro (demo)</title>  <style>    /* Estética noventera/2000s: colores pastel, bordes redondeados y fuente con sombra */    :root{      --bg:#f5f4fb; --card:#ffffff; --accent:#ff7fbf; --muted:#777; --glass: rgba(255,255,255,0.6);    }    *{box-sizing:border-box}    body{font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background:linear-gradient(180deg,#f8f0ff 0%, #f5f8ff 100%); color:#222; margin:0; padding:20px}    .wrap{max-width:1100px;margin:0 auto;display:grid;grid-template-columns:280px 1fr 260px;gap:18px}
    header{grid-column:1/-1; display:flex;align-items:center;justify-content:space-between;padding:12px 18px;border-radius:12px;background:linear-gradient(90deg,#fff,#fff7fb);box-shadow:0 6px 20px rgba(0,0,0,0.06)}    header h1{margin:0;font-size:20px;letter-spacing:1px}    header .brand{display:flex;gap:12px;align-items:center}    .logo{width:44px;height:44px;background:var(--accent);display:flex;align-items:center;justify-content:center;border-radius:10px;color:white;font-weight:700;box-shadow:0 4px 10px rgba(0,0,0,0.08)}
    .card{background:var(--card);padding:12px;border-radius:12px;box-shadow:0 6px 18px rgba(0,0,0,0.04)}    .sidebar .profile{display:flex;gap:12px;align-items:center}    .avatar{width:64px;height:64px;border-radius:12px;background:linear-gradient(180deg,#fff,#eee);display:flex;align-items:center;justify-content:center;font-weight:700;color:#333}    .btn{display:inline-block;padding:8px 10px;border-radius:10px;background:var(--accent);color:white;text-decoration:none;cursor:pointer;border:none}    .muted{color:var(--muted);font-size:13px}
    /* composer */    .composer textarea{width:100%;min-height:80px;border-radius:8px;border:1px solid #eee;padding:10px;font-size:14px;resize:vertical}    .composer .actions{display:flex;justify-content:space-between;align-items:center;margin-top:8px}
    /* feed */    .feed .post{margin-bottom:12px;padding:12px;border-radius:10px}    .post .meta{display:flex;gap:12px;align-items:center}    .post .content{margin-top:8px;white-space:pre-wrap}    .post img.media{max-width:100%;border-radius:8px;margin-top:8px}    .post .post-actions{display:flex;gap:8px;margin-top:10px}
    /* right column */    .search input{width:100%;padding:8px;border-radius:8px;border:1px solid #eee}    .users-list{display:flex;flex-direction:column;gap:8px;margin-top:8px}    .user-item{display:flex;gap:8px;align-items:center}
    footer{grid-column:1/-1;text-align:center;padding:12px;color:var(--muted);font-size:13px}
    /* responsive */    @media (max-width:900px){.wrap{grid-template-columns:1fr;padding:0}.right,.left{order:2}.feed{order:1}}  </style></head><body>  <div class="wrap">    <header class="card">      <div class="brand">        <div class="logo">P</div>        <div>          <h1>Picapon</h1>          <div class="muted">red social demo • estilo Mundo Gaturro</div>        </div>      </div>      <div id="top-controls">        <span id="welcomeText" class="muted">No conectado</span>        <button id="logoutBtn" class="btn" style="display:none">Cerrar sesión</button>      </div>    </header>
    <!-- Left: perfil rápido -->    <aside class="left card sidebar">      <div class="profile">        <div id="sideAvatar" class="avatar">?</div>        <div>          <div id="sideName" style="font-weight:700">Invitado</div>          <div id="sideHandle" class="muted">@visitante</div>        </div>      </div>
      <hr style="margin:12px 0;border:none;border-top:1px solid #f0eef3">
      <div id="authArea">        <div style="margin-bottom:8px"><strong>Iniciar / Registrar</strong></div>        <input id="inputName" placeholder="Nombre" style="width:100%;padding:8px;border-radius:8px;border:1px solid #eee;margin-bottom:6px">        <input id="inputHandle" placeholder="Usuario (sin @)" style="width:100%;padding:8px;border-radius:8px;border:1px solid #eee;margin-bottom:6px">        <label class="muted" style="font-size:12px">Avatar (opcional)</label>        <input id="inputAvatarFile" type="file" accept="image/*" style="width:100%;margin-top:6px">        <div style="display:flex;gap:8px;margin-top:8px">          <button id="loginBtn" class="btn">Entrar / Crear</button>          <button id="demoBtn" class="btn" style="background:#6fc3ff">Demo</button>        </div>      </div>
      <hr style="margin:12px 0;border:none;border-top:1px solid #f0eef3">
      <div>        <strong>Estadísticas</strong>        <div style="margin-top:8px" class="muted">Publicaciones: <span id="statPosts">0</span></div>        <div class="muted">Usuarios: <span id="statUsers">0</span></div>      </div>    </aside>
    <!-- Middle: feed -->    <main class="feed">      <section class="card composer">        <div style="display:flex;gap:12px;align-items:flex-start">          <div id="composeAvatar" class="avatar">?</div>          <div style="flex:1">            <textarea id="postText" placeholder="¿Qué está pasando en Picapon?" maxlength="500"></textarea>            <div class="actions">              <div>                <input id="postImage" type="file" accept="image/*">              </div>              <div>                <button id="postBtn" class="btn">Publicar</button>              </div>            </div>          </div>        </div>      </section>
      <section id="feedList" class="card" style="margin-top:12px">        <h3 style="margin-top:0">Inicio</h3>        <div id="postsContainer"></div>      </section>    </main>
    <!-- Right: buscador / usuarios -->    <aside class="right card">      <div class="search">        <input id="searchInput" placeholder="Buscar usuarios o texto...">      </div>      <div style="margin-top:12px">        <strong>Usuarios</strong>        <div id="usersList" class="users-list"></div>      </div>    </aside>
    <footer class="muted">Picapon — demo local • Los datos se guardan en tu navegador (localStorage)</footer>  </div>
  <script>    // --- Simple data layer usando localStorage ---    const STORE_USERS = 'picapon_users_v1';    const STORE_POSTS = 'picapon_posts_v1';    const STORE_SESSION = 'picapon_session_v1';
    function loadJSON(key, fallback){ try{ return JSON.parse(localStorage.getItem(key))||fallback }catch(e){return fallback} }    function saveJSON(key, value){ localStorage.setItem(key, JSON.stringify(value)); }
    let users = loadJSON(STORE_USERS, {});    let posts = loadJSON(STORE_POSTS, []);    let session = loadJSON(STORE_SESSION, null);
    // --- Demo seed if empty ---    if(Object.keys(users).length===0){      const seed = { 'gatito': {name:'Gatilín', handle:'gatito', avatar:null}, 'nico':{name:'Nico', handle:'nico', avatar:null} };      users = seed; saveJSON(STORE_USERS, users);      posts = [        {id:genId(), author:'gatito', text:'Bienvenido a Picapon — recuerda que es una demo.', ts:Date.now()-3600e3, likes:1, comments:[], media:null},        {id:genId(), author:'nico', text:'¿Quién quiere jugar con Gati? 🐱', ts:Date.now()-1800e3, likes:2, comments:[], media:null}      ];      saveJSON(STORE_POSTS, posts);    }
    // --- Helpers ---    function genId(){ return Math.random().toString(36).slice(2,9); }    function timeAgo(ts){ const s = Math.floor((Date.now()-ts)/1000); if(s<60) return s+'s'; if(s<3600) return Math.floor(s/60)+'m'; if(s<86400) return Math.floor(s/3600)+'h'; return Math.floor(s/86400)+'d'; }
    // --- UI refs ---    const sideAvatar = document.getElementById('sideAvatar');    const sideName = document.getElementById('sideName');    const sideHandle = document.getElementById('sideHandle');    const welcomeText = document.getElementById('welcomeText');    const logoutBtn = document.getElementById('logoutBtn');    const postBtn = document.getElementById('postBtn');    const postsContainer = document.getElementById('postsContainer');    const statPosts = document.getElementById('statPosts');    const statUsers = document.getElementById('statUsers');    const usersList = document.getElementById('usersList');    const postText = document.getElementById('postText');    const postImage = document.getElementById('postImage');    const composeAvatar = document.getElementById('composeAvatar');    const inputName = document.getElementById('inputName');    const inputHandle = document.getElementById('inputHandle');    const inputAvatarFile = document.getElementById('inputAvatarFile');    const loginBtn = document.getElementById('loginBtn');    const demoBtn = document.getElementById('demoBtn');    const searchInput = document.getElementById('searchInput');
    // --- Render functions ---    function renderStats(){ statPosts.innerText = posts.length; statUsers.innerText = Object.keys(users).length; }
    function renderUserList(filter=''){      usersList.innerHTML='';      const entries = Object.values(users).filter(u=> (u.name+u.handle).toLowerCase().includes(filter.toLowerCase()));      entries.sort((a,b)=> a.handle.localeCompare(b.handle));      entries.forEach(u=>{        const el = document.createElement('div'); el.className='user-item';        const av = document.createElement('div'); av.className='avatar'; av.style.width='40px'; av.style.height='40px'; av.innerText = u.avatar? '' : (u.name||u.handle).slice(0,1).toUpperCase();        if(u.avatar){ const i = document.createElement('img'); i.src=u.avatar; i.style.width='100%'; i.style.height='100%'; i.style.objectFit='cover'; i.style.borderRadius='8px'; av.innerHTML=''; av.appendChild(i); }        const info = document.createElement('div'); info.innerHTML = `<div style="font-weight:700">${escapeHtml(u.name||'Sin nombre')}</div><div class="muted">@${escapeHtml(u.handle)}</div>`;        const btn = document.createElement('button'); btn.className='btn'; btn.style.marginLeft='auto'; btn.innerText='Seguir';        btn.onclick = ()=>{ alert('Funcionalidad de seguir es demostrativa.'); }        el.appendChild(av); el.appendChild(info); el.appendChild(btn);        usersList.appendChild(el);      });    }
    function renderHeader(){ if(session){ welcomeText.innerText = `Hola, ${session.name}`; logoutBtn.style.display='inline-block'; } else { welcomeText.innerText='No conectado'; logoutBtn.style.display='none'; } }
    function renderProfileSidebar(){ if(session){ sideName.innerText = session.name; sideHandle.innerText = '@'+session.handle; sideAvatar.innerText=''; composeAvatar.innerText='';        if(session.avatar){ const i=document.createElement('img'); i.src=session.avatar; i.style.width='100%'; i.style.height='100%'; i.style.objectFit='cover'; i.style.borderRadius='8px'; sideAvatar.innerHTML=''; sideAvatar.appendChild(i); const j=i.cloneNode(); j.style.width='64px'; j.style.height='64px'; composeAvatar.innerHTML=''; composeAvatar.appendChild(j); }      } else { sideName.innerText='Invitado'; sideHandle.innerText='@visitante'; sideAvatar.innerText='?'; composeAvatar.innerText='?'; }    }
    function renderPosts(filterText=''){      postsContainer.innerHTML='';      let list = posts.slice().sort((a,b)=> b.ts-a.ts);      if(filterText){ list = list.filter(p => p.text.toLowerCase().includes(filterText.toLowerCase()) || (users[p.author] && (users[p.author].name+users[p.author].handle).toLowerCase().includes(filterText.toLowerCase())) ); }      list.forEach(p=>{        const el = document.createElement('div'); el.className='post'; el.classList.add('card');        const author = users[p.author] || {name:'Desconocido', handle:p.author, avatar:null};        const meta = document.createElement('div'); meta.className='meta';        const av = document.createElement('div'); av.className='avatar'; av.style.width='48px'; av.style.height='48px'; av.innerText = author.avatar? '' : (author.name||author.handle||'?').slice(0,1).toUpperCase();        if(author.avatar){ const i=document.createElement('img'); i.src=author.avatar; i.style.width='100%'; i.style.height='100%'; i.style.objectFit='cover'; i.style.borderRadius='8px'; av.innerHTML=''; av.appendChild(i); }        const m2 = document.createElement('div'); m2.innerHTML=`<div style="font-weight:700">${escapeHtml(author.name||'Sin nombre')} <span class="muted">@${escapeHtml(author.handle)}</span></div><div class="muted">${timeAgo(p.ts)}</div>`;        meta.appendChild(av); meta.appendChild(m2);        el.appendChild(meta);        const content = document.createElement('div'); content.className='content'; content.innerText = p.text || '';        el.appendChild(content);        if(p.media){ const img = document.createElement('img'); img.className='media'; img.src=p.media; el.appendChild(img); }        const actions = document.createElement('div'); actions.className='post-actions';        const likeBtn = document.createElement('button'); likeBtn.className='btn'; likeBtn.style.background='#ffd24d'; likeBtn.innerText = `❤ ${p.likes||0}`;        likeBtn.onclick = ()=>{ p.likes = (p.likes||0)+1; saveJSON(STORE_POSTS, posts); renderPosts(searchInput.value); renderStats(); }        const commentBtn = document.createElement('button'); commentBtn.className='btn'; commentBtn.style.background='#9be7a3'; commentBtn.innerText = `💬 ${p.comments.length}`;        commentBtn.onclick = ()=>{ const txt = prompt('Agregar comentario:'); if(txt){ p.comments.push({id:genId(), author: session? session.handle : 'anon', text:txt, ts:Date.now()}); saveJSON(STORE_POSTS, posts); renderPosts(searchInput.value); } }        actions.appendChild(likeBtn); actions.appendChild(commentBtn);        if(session && session.handle===p.author){ const del = document.createElement('button'); del.className='btn'; del.style.background='#ff8b8b'; del.innerText='Eliminar'; del.onclick = ()=>{ if(confirm('Eliminar publicación?')){ posts = posts.filter(x=> x.id!==p.id); saveJSON(STORE_POSTS, posts); renderPosts(searchInput.value); renderStats(); } } actions.appendChild(del); }        el.appendChild(actions);
        // comments preview        if(p.comments && p.comments.length){          const cprev = document.createElement('div'); cprev.style.marginTop='8px'; cprev.style.paddingTop='8px'; cprev.style.borderTop='1px dashed #eee';          p.comments.slice(-3).forEach(c=>{            const cu = users[c.author] || {name:c.author, handle:c.author};            const line = document.createElement('div'); line.innerHTML = `<strong>${escapeHtml(cu.name||cu.handle)}</strong> <span class="muted">@${escapeHtml(cu.handle||c.author)}</span>: ${escapeHtml(c.text)}`;            cprev.appendChild(line);          });          el.appendChild(cprev);        }
        postsContainer.appendChild(el);      });    }
    // --- Auth ---    function loginFromInputs(){ const name = inputName.value.trim(); const handle = (inputHandle.value.trim()||'').replace(/@/g,''); if(!name||!handle){ alert('Ingresa nombre y usuario.'); return; }      if(users[handle] && users[handle].name && users[handle].name!==name){ if(!confirm('El usuario existe con otro nombre. ¿Usar igual?')) return; }      const user = users[handle] || {name, handle};      user.name = name; user.handle = handle;      // avatar      const file = inputAvatarFile.files[0];      if(file){ const reader = new FileReader(); reader.onload = e=>{ user.avatar = e.target.result; users[handle]=user; saveJSON(STORE_USERS, users); setSession(user); renderAll(); }; reader.readAsDataURL(file); }      else{ users[handle]=user; saveJSON(STORE_USERS, users); setSession(user); renderAll(); }    }
    function setSession(user){ session = user; saveJSON(STORE_SESSION, session); renderHeader(); renderProfileSidebar(); }    function logout(){ session=null; localStorage.removeItem(STORE_SESSION); renderAll(); }
    // --- Posting ---    postBtn.onclick = ()=>{      if(!session){ alert('Debes iniciar sesión para publicar.'); return; }      const text = postText.value.trim();      if(!text && !postImage.files[0]){ alert('Escribe algo o sube una imagen.'); return; }      const newPost = { id:genId(), author:session.handle, text, ts:Date.now(), likes:0, comments:[], media:null };      const file = postImage.files[0];      if(file){ const r=new FileReader(); r.onload = e=>{ newPost.media = e.target.result; posts.push(newPost); saveJSON(STORE_POSTS, posts); postText.value=''; postImage.value=''; renderAll(); }; r.readAsDataURL(file); }      else{ posts.push(newPost); saveJSON(STORE_POSTS, posts); postText.value=''; renderAll(); }    }
    // --- small helpers ---    function escapeHtml(s){ if(!s) return ''; return s.replace(/[&<>"']/g, c=>({ '&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c])); }
    // --- events ---    loginBtn.onclick = loginFromInputs;    demoBtn.onclick = ()=>{ setSession({name:'Demo', handle:'demo', avatar:null}); renderAll(); };    logoutBtn.onclick = ()=>{ logout(); };    searchInput.oninput = ()=>{ renderPosts(searchInput.value); renderUserList(searchInput.value); }
    // quick load current session    if(session){ /* already loaded */ }
    function renderAll(){ renderHeader(); renderProfileSidebar(); renderPosts(); renderUserList(); renderStats(); }
    renderAll();  </script></body></html>