Scan to open on your phone
Point a camera at the QR code - it opens this exact paste, no app needed.
https://codepastes.com/dabestsiteeveruntitledPlain Text
<!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, """)}" 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