untitled

Pasted Oct 6, 2026, 6:34 PM1 file475 lines

Scan to open on your phone

Point a camera at the QR code - it opens this exact paste, no app needed.

https://codepastes.com/dabestsiteever
untitled
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Horizon — Focused Personal Dashboard</title>
<meta name="description" content="Horizon is a focused personal dashboard with apps, games, an embedded browser, and an assistant." />
<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=Rubik:wght@400;500&family=Space+Mono:wght@400;700&display=swap" rel="stylesheet" />
<style>
  :root {
    --background: oklch(0.189 0.006 56);
    --foreground: oklch(0.946 0.012 70);
    --surface: oklch(0.245 0.008 49);
    --sidebar: oklch(0.225 0.009 48);
    --primary: oklch(0.704 0.174 49);
    --primary-foreground: oklch(0.19 0.008 49);
    --primary-soft: oklch(0.704 0.174 49 / 0.13);
    --secondary: oklch(0.3 0.008 50);
    --muted-foreground: oklch(0.7 0.021 69);
    --subtle: oklch(0.57 0.015 65);
    --border: oklch(0.94 0.01 70 / 0.09);
  }
  * { box-sizing: border-box; border-color: var(--border); }
  html, body { margin: 0; height: 100%; }
  body {
    background: var(--background); color: var(--foreground);
    font-family: "Rubik", sans-serif; min-width: 320px; overflow: hidden;
  }
  .mono { font-family: "Space Mono", monospace; }
  button { font: inherit; cursor: pointer; background: none; border: none; color: inherit; }
  input, select { font: inherit; }
  ::selection { background: var(--primary-soft); }

  .app { display: flex; height: 100vh; }

  /* Sidebar */
  .sidebar {
    width: 14rem; flex-shrink: 0; background: var(--sidebar);
    border-right: 1px solid var(--border); display: flex; flex-direction: column; padding: 1.25rem 0.75rem;
  }
  .brand { display: flex; align-items: center; gap: 0.6rem; padding: 0 0.75rem 1.25rem; }
  .brand-dot { width: 0.65rem; height: 0.65rem; border-radius: 9999px; background: var(--primary); box-shadow: 0 0 10px var(--primary); }
  .brand-name { font-family: "Space Mono", monospace; font-size: 1.05rem; letter-spacing: 0.02em; }
  .nav { display: flex; flex-direction: column; gap: 2px; }
  .nav button {
    display: flex; align-items: center; gap: 0.65rem; padding: 0.55rem 0.75rem;
    border-radius: 0.4rem; color: var(--muted-foreground); font-size: 0.85rem; text-align: left;
    transition: background 0.15s, color 0.15s;
  }
  .nav button:hover { background: var(--surface); color: var(--foreground); }
  .nav button.active { background: var(--primary-soft); color: var(--primary); }
  .nav svg { width: 1rem; height: 1rem; flex-shrink: 0; }
  .sidebar-foot { margin-top: auto; padding: 0.75rem; font-family: "Space Mono", monospace; font-size: 0.6rem; text-transform: uppercase; color: var(--subtle); }

  /* Main column */
  .main { flex: 1; display: flex; flex-direction: column; min-width: 0; }
  .topbar {
    display: flex; align-items: center; gap: 1rem; padding: 0.65rem 1.25rem;
    border-bottom: 1px solid var(--border); background: var(--surface);
  }
  .topbar .spacer { flex: 1; }
  .chip {
    display: flex; align-items: center; gap: 0.4rem; font-family: "Space Mono", monospace;
    font-size: 0.68rem; color: var(--muted-foreground); text-transform: uppercase;
  }
  .chip svg { width: 0.95rem; height: 0.95rem; }
  .status-dot { width: 0.5rem; height: 0.5rem; border-radius: 9999px; background: var(--primary); box-shadow: 0 0 8px var(--primary); }

  .view { flex: 1; overflow-y: auto; padding: 1.5rem 2rem; }
  .wrap { margin: 0 auto; max-width: 64rem; }
  .wrap.wide { max-width: 80rem; }
  .eyebrow { font-family: "Space Mono", monospace; font-size: 0.62rem; text-transform: uppercase; color: var(--primary); margin: 0; }
  h1 { font-family: "Space Mono", monospace; font-size: 1.9rem; margin: 0.5rem 0 0; font-weight: 700; }
  .sub { margin: 0.5rem 0 0; font-size: 0.88rem; color: var(--muted-foreground); }

  .grid { margin-top: 2rem; display: grid; gap: 1px; background: var(--border); border: 1px solid var(--border); border-radius: 0.5rem; overflow: hidden; grid-template-columns: 1fr; }
  @media (min-width: 640px) { .grid { grid-template-columns: repeat(2, 1fr); } }
  @media (min-width: 1024px) { .grid { grid-template-columns: repeat(3, 1fr); } }
  .tile {
    display: flex; flex-direction: column; align-items: flex-start; justify-content: space-between;
    min-height: 9rem; background: var(--background); padding: 1.25rem; text-align: left;
    transition: background 0.15s;
  }
  .tile:hover { background: var(--surface); }
  .tile svg { width: 1.25rem; height: 1.25rem; color: var(--muted-foreground); }
  .tile:first-child svg { color: var(--primary); }
  .tile h2 { font-size: 0.88rem; font-weight: 500; margin: 0; }
  .tile p { margin: 0.25rem 0 0; font-family: "Space Mono", monospace; font-size: 0.58rem; text-transform: uppercase; color: var(--subtle); }

  .panel { margin-top: 2rem; border: 1px solid var(--border); border-radius: 0.5rem; overflow: hidden; background: color-mix(in oklab, var(--surface) 50%, transparent); }
  .panel-head {
    display: flex; align-items: center; justify-content: space-between; gap: 1rem;
    border-bottom: 1px solid var(--border); padding: 0.5rem 1rem;
    font-family: "Space Mono", monospace; font-size: 0.58rem; text-transform: uppercase; color: var(--muted-foreground);
  }
  .panel-head button { display: flex; align-items: center; gap: 0.35rem; color: var(--muted-foreground); font-family: "Space Mono", monospace; font-size: 0.58rem; text-transform: uppercase; transition: color 0.15s; }
  .panel-head button:hover { color: var(--foreground); }
  .panel-head svg { width: 0.75rem; height: 0.75rem; }
  iframe.preview { width: 100%; height: 30rem; border: 0; background: var(--foreground); display: block; }

  /* Browser */
  .browser-bar { display: flex; gap: 0.5rem; padding: 0.6rem; border-bottom: 1px solid var(--border); background: var(--surface); }
  .browser-bar input {
    flex: 1; background: var(--background); border: 1px solid var(--border); border-radius: 0.4rem;
    color: var(--foreground); padding: 0.45rem 0.75rem; font-size: 0.82rem; outline: none;
  }
  .browser-bar input:focus { border-color: var(--primary); }
  .btn {
    background: var(--primary); color: var(--primary-foreground); border-radius: 0.4rem;
    padding: 0.45rem 0.9rem; font-size: 0.8rem; font-weight: 500; transition: opacity 0.15s;
  }
  .btn:hover { opacity: 0.88; }
  .btn.ghost { background: var(--secondary); color: var(--foreground); }
  iframe.browser { width: 100%; height: calc(100vh - 14.5rem); min-height: 24rem; border: 0; background: #fff; display: block; }

  /* Settings */
  .setting { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 1rem 1.25rem; border-bottom: 1px solid var(--border); background: var(--background); }
  .setting:last-child { border-bottom: 0; }
  .setting h3 { margin: 0; font-size: 0.85rem; font-weight: 500; }
  .setting p { margin: 0.2rem 0 0; font-size: 0.75rem; color: var(--muted-foreground); }
  .toggle { width: 2.5rem; height: 1.35rem; border-radius: 9999px; background: var(--secondary); position: relative; flex-shrink: 0; transition: background 0.15s; }
  .toggle::after { content: ""; position: absolute; top: 0.175rem; left: 0.175rem; width: 1rem; height: 1rem; border-radius: 9999px; background: var(--foreground); transition: transform 0.15s; }
  .toggle.on { background: var(--primary); }
  .toggle.on::after { transform: translateX(1.15rem); background: var(--primary-foreground); }

  /* Assistant */
  .chat { display: flex; flex-direction: column; height: 24rem; }
  .chat-log { flex: 1; overflow-y: auto; padding: 1rem; display: flex; flex-direction: column; gap: 0.6rem; }
  .msg { max-width: 80%; padding: 0.55rem 0.8rem; border-radius: 0.6rem; font-size: 0.83rem; line-height: 1.45; }
  .msg.user { align-self: flex-end; background: var(--primary); color: var(--primary-foreground); }
  .msg.bot { align-self: flex-start; background: var(--secondary); }
  .chat-form { display: flex; gap: 0.5rem; padding: 0.6rem; border-top: 1px solid var(--border); }
  .chat-form input { flex: 1; background: var(--background); border: 1px solid var(--border); border-radius: 0.4rem; color: var(--foreground); padding: 0.45rem 0.75rem; font-size: 0.82rem; outline: none; }
  .chat-form input:focus { border-color: var(--primary); }

  .home-grid { margin-top: 2rem; display: grid; gap: 1rem; grid-template-columns: 1fr; }
  @media (min-width: 900px) { .home-grid { grid-template-columns: 1.2fr 1fr; } }
  .card { border: 1px solid var(--border); border-radius: 0.5rem; background: var(--surface); padding: 1.25rem; }
  .card h2 { margin: 0 0 0.75rem; font-family: "Space Mono", monospace; font-size: 0.7rem; text-transform: uppercase; color: var(--muted-foreground); font-weight: 400; }
  .clock { font-family: "Space Mono", monospace; font-size: 2.4rem; }
  .date-line { color: var(--muted-foreground); font-size: 0.85rem; margin-top: 0.25rem; }
  .search-row { display: flex; gap: 0.5rem; margin-top: 1.5rem; }
  .search-row input { flex: 1; background: var(--background); border: 1px solid var(--border); border-radius: 0.4rem; color: var(--foreground); padding: 0.55rem 0.8rem; font-size: 0.85rem; outline: none; }
  .search-row input:focus { border-color: var(--primary); }
  .stat-row { display: flex; justify-content: space-between; font-size: 0.8rem; padding: 0.4rem 0; border-bottom: 1px solid var(--border); color: var(--muted-foreground); }
  .stat-row:last-child { border-bottom: 0; }
  .stat-row b { color: var(--foreground); font-weight: 500; }

  /* Mobile nav */
  .mobile-nav { display: none; }
  @media (max-width: 760px) {
    .sidebar { display: none; }
    .app { flex-direction: column; }
    .main { height: 100%; }
    .mobile-nav {
      display: flex; order: 2; border-top: 1px solid var(--border); background: var(--sidebar);
    }
    .mobile-nav button { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 0.2rem; padding: 0.6rem 0.25rem; font-size: 0.58rem; color: var(--muted-foreground); font-family: "Space Mono", monospace; text-transform: uppercase; }
    .mobile-nav button.active { color: var(--primary); }
    .mobile-nav svg { width: 1.05rem; height: 1.05rem; }
    .view { padding: 1.25rem; }
  }
  @media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { transition-duration: 0.01ms !important; }
  }
</style>
</head>
<body>
<div class="app">
  <aside class="sidebar">
    <div class="brand"><span class="brand-dot"></span><span class="brand-name">Horizon</span></div>
    <nav class="nav" id="nav"></nav>
    <div class="sidebar-foot">v2 · personal build</div>
  </aside>

  <div class="main">
    <header class="topbar">
      <span class="chip"><span class="status-dot"></span> Online</span>
      <span class="spacer"></span>
      <span class="chip" id="batteryChip" title="Battery">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="7" width="16" height="10" rx="2"/><path d="M22 11v2"/></svg>
        <span id="batteryText">--%</span>
      </span>
      <span class="chip" id="clockChip">--:--</span>
    </header>

    <div class="view" id="view"></div>

    <nav class="mobile-nav" id="mobileNav"></nav>
  </div>
</div>

<script>
const PROXY = "https://stillicefr.lovable.app/api/public/proxy?url=";
const proxied = (u) => PROXY + encodeURIComponent(u);
const gnMathUrl = "https://s3.amazonaws.com/prageru-server/mathematics.html";
const homeUrl = "https://duckduckgo.com/";

const icons = {
  home: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 10.5 12 3l9 7.5"/><path d="M5 9.5V21h14V9.5"/></svg>',
  games: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="7" width="20" height="11" rx="5"/><path d="M7 11v4M5 13h4M16 11h.01M18 14h.01"/></svg>',
  browser: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3c2.5 2.5 4 5.7 4 9s-1.5 6.5-4 9c-2.5-2.5-4-5.7-4-9s1.5-6.5 4-9z"/></svg>',
  apps: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="3" width="7" height="7" rx="1.5"/><rect x="3" y="14" width="7" height="7" rx="1.5"/><rect x="14" y="14" width="7" height="7" rx="1.5"/></svg>',
  ai: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 3l1.8 4.6L18 9l-4.2 1.4L12 15l-1.8-4.6L6 9l4.2-1.4z"/><path d="M18 15l.9 2.1L21 18l-2.1.9L18 21l-.9-2.1L15 18l2.1-.9z"/></svg>',
  settings: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19 12a7 7 0 0 0-.1-1.2l2-1.6-2-3.4-2.4 1a7 7 0 0 0-2-1.2L14 3h-4l-.5 2.6a7 7 0 0 0-2 1.2l-2.4-1-2 3.4 2 1.6A7 7 0 0 0 5 12c0 .4 0 .8.1 1.2l-2 1.6 2 3.4 2.4-1a7 7 0 0 0 2 1.2L10 21h4l.5-2.6a7 7 0 0 0 2-1.2l2.4 1 2-3.4-2-1.6c.1-.4.1-.8.1-1.2z"/></svg>',
  ext: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 4h6v6M20 4 10 14M9 5H5a1 1 0 0 0-1 1v13a1 1 0 0 0 1 1h13a1 1 0 0 0 1-1v-4"/></svg>',
  zap: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M13 2 4 14h6l-1 8 9-12h-6z"/></svg>',
};

const tabs = [
  { id: "home", label: "Home", icon: icons.home },
  { id: "games", label: "Games", icon: icons.games },
  { id: "browser", label: "Browser", icon: icons.browser },
  { id: "apps", label: "Apps", icon: icons.apps },
  { id: "ai", label: "AI", icon: icons.ai },
  { id: "settings", label: "Settings", icon: icons.settings },
];

const gameLinks = [
  { name: "GN Math", url: gnMathUrl },
  { name: "Quarry", url: gnMathUrl },
  { name: "Solace", url: gnMathUrl },
  { name: "Emberfall", url: gnMathUrl },
  { name: "Night Circuit", url: gnMathUrl },
  { name: "Low Orbit", url: gnMathUrl },
];

const appLinks = [
  { name: "Mail", url: "https://duckduckgo.com/?q=mail" },
  { name: "Calendar", url: "https://duckduckgo.com/?q=calendar" },
  { name: "Notes", url: "https://duckduckgo.com/?q=notes+app" },
  { name: "Files", url: "https://duckduckgo.com/?q=file+manager" },
  { name: "Music", url: "https://duckduckgo.com/?q=music+player" },
  { name: "Photos", url: "https://duckduckgo.com/?q=photos" },
];

const shortcuts = [
  { name: "DuckDuckGo search", url: homeUrl },
  { name: "GN Math practice", url: gnMathUrl },
  { name: "Wikipedia", url: "https://www.wikipedia.org/" },
  { name: "Weather lookup", url: "https://duckduckgo.com/?q=weather" },
];

const state = {
  tab: "home",
  browserUrl: homeUrl,
  settings: { reduceMotion: false, showClock: true, showBattery: true, compactTiles: false },
  messages: [{ from: "bot", text: "Hi — I'm the Horizon assistant. This standalone file runs in demo mode, so replies are placeholder text." }],
};

const view = document.getElementById("view");

function setTab(id) { state.tab = id; render(); }

function openInBrowser(url) {
  state.browserUrl = url;
  state.tab = "browser";
  render();
}

function navigateBrowser(input) {
  let url = input.trim();
  if (!url) return;
  if (!/^https?:\/\//i.test(url)) {
    url = url.includes(".") && !url.includes(" ") ? "https://" + url
      : "https://duckduckgo.com/?q=" + encodeURIComponent(url);
  }
  state.browserUrl = url;
  render();
}

function tileGrid(items, icon) {
  return '<div class="grid">' + items.map((it, i) =>
    `<button class="tile" onclick="openInBrowser('${it.url.replace(/'/g, "\\'")}')">${icon}<div><h2>${it.name}</h2><p>Open in browser</p></div></button>`
  ).join("") + "</div>";
}

function renderHome() {
  const s = state.settings;
  return `<div class="wrap">
    <p class="eyebrow">Dashboard</p>
    <h1 class="mono">Horizon Dashboard</h1>
    <p class="sub">Everything you need, nothing you don't.</p>
    <div class="home-grid">
      <div class="card">
        <h2>Time</h2>
        <div class="clock mono" id="bigClock">--:--</div>
        <div class="date-line" id="bigDate"></div>
        <div class="search-row">
          <input id="homeSearch" placeholder="Search the web…" onkeydown="if(event.key==='Enter')navigateBrowser(this.value)" />
          <button class="btn" onclick="navigateBrowser(document.getElementById('homeSearch').value)">Search</button>
        </div>
      </div>
      <div class="card">
        <h2>System status</h2>
        <div class="stat-row"><span>Connection</span><b>Online</b></div>
        <div class="stat-row"><span>Battery</span><b id="statBattery">--%</b></div>
        <div class="stat-row"><span>Clock</span><b>${s.showClock ? "Visible" : "Hidden"}</b></div>
        <div class="stat-row"><span>Mode</span><b>${s.compactTiles ? "Compact" : "Comfortable"}</b></div>
      </div>
    </div>
    <h2 class="mono" style="margin-top:2rem;font-size:0.7rem;text-transform:uppercase;color:var(--muted-foreground);font-weight:400">Shortcuts</h2>
    ${tileGrid(shortcuts, icons.zap)}
  </div>`;
}

function renderGames() {
  return `<div class="wrap wide">
    <p class="eyebrow">Library</p>
    <h1>Games</h1>
    <p class="sub">Pick up where you left off.</p>
    <section class="panel">
      <div class="panel-head">
        <span>GN Math preview</span>
        <button onclick="openInBrowser(gnMathUrl)">${icons.ext} Open in browser</button>
      </div>
      <iframe class="preview" title="GN Math preview" src="${proxied(gnMathUrl)}"
        sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts"
        referrerpolicy="strict-origin-when-cross-origin"></iframe>
    </section>
    ${tileGrid(gameLinks, icons.games)}
  </div>`;
}

function renderBrowser() {
  return `<div class="wrap wide">
    <p class="eyebrow">Proxy browser</p>
    <h1>Browser</h1>
    <p class="sub">Pages load through the Horizon proxy.</p>
    <section class="panel">
      <div class="browser-bar">
        <input id="urlInput" value="${state.browserUrl.replace(/"/g, "&quot;")}" placeholder="Enter a URL or search…"
          onkeydown="if(event.key==='Enter')navigateBrowser(this.value)" />
        <button class="btn" onclick="navigateBrowser(document.getElementById('urlInput').value)">Go</button>
        <button class="btn ghost" onclick="window.open(state.browserUrl,'_blank')">${icons.ext}</button>
      </div>
      <iframe class="browser" title="Embedded browser" src="${proxied(state.browserUrl)}"
        sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts"
        referrerpolicy="strict-origin-when-cross-origin"></iframe>
    </section>
  </div>`;
}

function renderApps() {
  return `<div class="wrap">
    <p class="eyebrow">Library</p>
    <h1>Apps</h1>
    <p class="sub">Your everyday tools.</p>
    ${tileGrid(appLinks, icons.apps)}
  </div>`;
}

function renderAI() {
  return `<div class="wrap">
    <p class="eyebrow">Assistant</p>
    <h1>AI</h1>
    <p class="sub">Ask anything.</p>
    <section class="panel">
      <div class="chat">
        <div class="chat-log" id="chatLog">
          ${state.messages.map(m => `<div class="msg ${m.from}">${m.text}</div>`).join("")}
        </div>
        <form class="chat-form" onsubmit="sendMessage(event)">
          <input id="chatInput" placeholder="Message the assistant…" autocomplete="off" />
          <button class="btn" type="submit">Send</button>
        </form>
      </div>
    </section>
  </div>`;
}

function sendMessage(e) {
  e.preventDefault();
  const input = document.getElementById("chatInput");
  const text = input.value.trim();
  if (!text) return;
  state.messages.push({ from: "user", text });
  state.messages.push({ from: "bot", text: "Demo mode in this standalone file — connect the full app for real answers." });
  render();
  const log = document.getElementById("chatLog");
  if (log) log.scrollTop = log.scrollHeight;
}

function toggleSetting(key) {
  state.settings[key] = !state.settings[key];
  applySettings();
  render();
}

function renderSettings() {
  const s = state.settings;
  const row = (key, title, desc) => `<div class="setting">
      <div><h3>${title}</h3><p>${desc}</p></div>
      <button class="toggle ${s[key] ? "on" : ""}" onclick="toggleSetting('${key}')" aria-label="${title}"></button>
    </div>`;
  return `<div class="wrap">
    <p class="eyebrow">Preferences</p>
    <h1>Settings</h1>
    <p class="sub">Tune Horizon to your liking.</p>
    <section class="panel">
      ${row("showClock", "Show clock", "Display the time in the top bar.")}
      ${row("showBattery", "Show battery", "Display battery percentage in the top bar.")}
      ${row("compactTiles", "Compact tiles", "Denser grid layout for libraries.")}
      ${row("reduceMotion", "Reduce motion", "Minimize animations and transitions.")}
    </section>
    <section class="panel">
      <div class="setting">
        <div><h3>Browser home page</h3><p>Where the embedded browser starts.</p></div>
        <button class="btn ghost" onclick="openInBrowser(homeUrl)">DuckDuckGo</button>
      </div>
      <div class="setting">
        <div><h3>Reset layout</h3><p>Return all settings to defaults.</p></div>
        <button class="btn ghost" onclick="resetSettings()">Reset</button>
      </div>
    </section>
  </div>`;
}

function resetSettings() {
  state.settings = { reduceMotion: false, showClock: true, showBattery: true, compactTiles: false };
  applySettings();
  render();
}

function applySettings() {
  document.getElementById("clockChip").style.display = state.settings.showClock ? "" : "none";
  document.getElementById("batteryChip").style.display = state.settings.showBattery ? "" : "none";
}

function render() {
  const renders = { home: renderHome, games: renderGames, browser: renderBrowser, apps: renderApps, ai: renderAI, settings: renderSettings };
  view.innerHTML = renders[state.tab]();
  document.getElementById("nav").innerHTML = tabs.map(t =>
    `<button class="${state.tab === t.id ? "active" : ""}" onclick="setTab('${t.id}')">${t.icon} ${t.label}</button>`).join("");
  document.getElementById("mobileNav").innerHTML = tabs.map(t =>
    `<button class="${state.tab === t.id ? "active" : ""}" onclick="setTab('${t.id}')">${t.icon}<span>${t.label}</span></button>`).join("");
  tickClock();
}

function tickClock() {
  const now = new Date();
  const time = now.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
  const chip = document.getElementById("clockChip");
  if (chip) chip.textContent = time;
  const big = document.getElementById("bigClock");
  if (big) big.textContent = time;
  const bd = document.getElementById("bigDate");
  if (bd) bd.textContent = now.toLocaleDateString([], { weekday: "long", month: "long", day: "numeric" });
}
setInterval(tickClock, 1000);

if (navigator.getBattery) {
  navigator.getBattery().then((b) => {
    const update = () => {
      const pct = Math.round(b.level * 100) + "%" + (b.charging ? " ⚡" : "");
      const el = document.getElementById("batteryText");
      if (el) el.textContent = pct;
      const stat = document.getElementById("statBattery");
      if (stat) stat.textContent = pct;
    };
    update();
    b.addEventListener("levelchange", update);
    b.addEventListener("chargingchange", update);
  });
} else {
  document.getElementById("batteryChip").style.display = "none";
}

render();
</script>
</body>
</html>

23,482 chars · codepastes.com