mirror of
https://github.com/ipnet-mesh/meshcore-hub.git
synced 2026-08-07 01:13:11 +02:00
Stash
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Field Station — Advertisements</title>
|
||||
<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=Chakra+Petch:wght@500;600;700&family=IBM+Plex+Sans:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="field-station.css">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="nav">
|
||||
<a href="field-station.html" class="brand"><svg class="logo" viewBox="0 0 115 100" aria-hidden="true"><rect x="0" y="0" width="25" height="100" rx="2" fill="currentColor" opacity=".5"/><rect x="35" y="0" width="25" height="100" rx="2" fill="currentColor"/><g class="arcs" fill="none" stroke="currentColor" stroke-width="10" stroke-linecap="round" transform="translate(-30,-10)"><path d="M 110,65 A 20,20 0 0 0 90,45"/><path d="M 125,65 A 35,35 0 0 0 90,30"/><path d="M 140,65 A 50,50 0 0 0 90,15"/></g></svg>IPNET</a>
|
||||
<div class="navlinks">
|
||||
<a href="field-station.html"><span class="i" style="background:#7AA7FF"></span>Dashboard</a>
|
||||
<a href="field-station-list.html"><span class="i" style="background:#9B7BFF"></span>Nodes</a>
|
||||
<a class="active"><span class="i" style="background:#E07BC0"></span>Adverts</a>
|
||||
<a><span class="i" style="background:var(--rx)"></span>Messages</a>
|
||||
<a><span class="i" style="background:#7FD98A"></span>Packets</a>
|
||||
<a href="field-station-map.html"><span class="i" style="background:var(--tx)"></span>Map</a>
|
||||
<a><span class="i" style="background:#E0A24A"></span>Members</a>
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
<button class="iconbtn" onclick="toggleTheme()" title="Toggle light/dark">◐</button>
|
||||
<button class="iconbtn burger">≡</button>
|
||||
</nav>
|
||||
|
||||
<div class="wrap">
|
||||
<div class="page-head">
|
||||
<h1 class="page-title">Advertisements</h1>
|
||||
<span class="chip">UTC+1</span>
|
||||
<span class="chip"><b>1,284</b> total</span>
|
||||
<div class="spacer"></div>
|
||||
<button class="iconbtn" title="Auto-refresh: on">⏸</button>
|
||||
</div>
|
||||
|
||||
<form class="filters" onsubmit="return false">
|
||||
<div class="field"><label>Search</label><input placeholder="Node name or public key…"></div>
|
||||
<div class="field"><label>Route type</label><select><option>All routes</option><option>Flood</option><option>Direct relay</option></select></div>
|
||||
<div class="field"><label>Operator</label><select><option>Any operator</option><option>kestrel</option><option>g7xyz</option></select></div>
|
||||
<button class="btn">Apply</button>
|
||||
<button class="btn btn-ghost">Clear</button>
|
||||
</form>
|
||||
|
||||
<!-- mobile sort -->
|
||||
<div class="tbl-mobile-sort field"><label>Sort by</label><select><option>Newest first</option><option>Oldest first</option><option>Node name</option></select></div>
|
||||
|
||||
<!-- desktop table -->
|
||||
<div class="panel desktop-only">
|
||||
<div class="table-scroll">
|
||||
<table>
|
||||
<thead><tr>
|
||||
<th class="sort">Node <span class="caret"></span></th>
|
||||
<th>Public key</th>
|
||||
<th class="sort">Route <span class="caret"></span></th>
|
||||
<th class="sort desc">Time <span class="caret"></span></th>
|
||||
<th>Observers</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
<tr><td><div class="node"><span class="em">📡</span><span class="nm">Holbeck-Relay</span></div></td><td class="key">a1f3…9c2e</td><td><span class="badge b-rx">DIRECT</span></td><td class="time">2m ago</td><td><span class="observers">📡 4</span></td></tr>
|
||||
<tr><td><div class="node"><span class="em">💬</span><span class="nm">kestrel</span></div></td><td class="key">7b02…4d1a</td><td><span class="badge b-tx">FLOOD</span></td><td class="time">6m ago</td><td><span class="observers">📡 7</span></td></tr>
|
||||
<tr><td><div class="node"><span class="em">🪧</span><span class="nm">Briggate-Room</span></div></td><td class="key">e5c9…aa70</td><td><span class="badge b-tx">FLOOD</span></td><td class="time">11m ago</td><td><span class="observers">📡 3</span></td></tr>
|
||||
<tr><td><div class="node"><span class="em">📡</span><span class="nm">Armley-Hill</span></div></td><td class="key">3d8e…1f55</td><td><span class="badge b-rx">DIRECT</span></td><td class="time">18m ago</td><td><span class="observers">📡 5</span></td></tr>
|
||||
<tr><td><div class="node"><span class="em">📱</span><span class="nm">g7xyz-companion</span></div></td><td class="key">b240…77c1</td><td><span class="badge b-tx">FLOOD</span></td><td class="time">24m ago</td><td><span class="observers">📡 2</span></td></tr>
|
||||
<tr><td><div class="node"><span class="em">📡</span><span class="nm">Hyde-Park-Hub</span></div></td><td class="key">9f10…3b8d</td><td><span class="badge b-rx">DIRECT</span></td><td class="time">31m ago</td><td><span class="observers">📡 6</span></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="pager"><a>‹</a><a class="cur">1</a><a>2</a><a>3</a><a>…</a><a>9</a><a>›</a></div>
|
||||
</div>
|
||||
|
||||
<!-- mobile cards -->
|
||||
<div class="cards mobile-only">
|
||||
<div class="ncard"><span class="em">📡</span><div class="meta"><div class="nm">Holbeck-Relay</div><div class="sub">a1f3…9c2e · 📡 4</div></div><span class="badge b-rx">DIRECT</span></div>
|
||||
<div class="ncard"><span class="em">💬</span><div class="meta"><div class="nm">kestrel</div><div class="sub">7b02…4d1a · 📡 7</div></div><span class="badge b-tx">FLOOD</span></div>
|
||||
<div class="ncard"><span class="em">🪧</span><div class="meta"><div class="nm">Briggate-Room</div><div class="sub">e5c9…aa70 · 📡 3</div></div><span class="badge b-tx">FLOOD</span></div>
|
||||
<div class="ncard"><span class="em">📡</span><div class="meta"><div class="nm">Armley-Hill</div><div class="sub">3d8e…1f55 · 📡 5</div></div><span class="badge b-rx">DIRECT</span></div>
|
||||
<div class="pager"><a>‹</a><a class="cur">1</a><a>2</a><a>3</a><a>›</a></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="foot">IPNET · 53.80°N 1.55°W — powered by MeshCore Hub</footer>
|
||||
<div class="note">Field Station · List view · ◐ toggle theme · resize narrow for mobile cards</div>
|
||||
<script>function toggleTheme(){const h=document.documentElement;h.dataset.theme=h.dataset.theme==='dark'?'light':'dark'}</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,78 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Field Station — Map</title>
|
||||
<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=Chakra+Petch:wght@500;600;700&family=IBM+Plex+Sans:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="field-station.css">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="nav">
|
||||
<a href="field-station.html" class="brand"><svg class="logo" viewBox="0 0 115 100" aria-hidden="true"><rect x="0" y="0" width="25" height="100" rx="2" fill="currentColor" opacity=".5"/><rect x="35" y="0" width="25" height="100" rx="2" fill="currentColor"/><g class="arcs" fill="none" stroke="currentColor" stroke-width="10" stroke-linecap="round" transform="translate(-30,-10)"><path d="M 110,65 A 20,20 0 0 0 90,45"/><path d="M 125,65 A 35,35 0 0 0 90,30"/><path d="M 140,65 A 50,50 0 0 0 90,15"/></g></svg>IPNET</a>
|
||||
<div class="navlinks">
|
||||
<a href="field-station.html"><span class="i" style="background:#7AA7FF"></span>Dashboard</a>
|
||||
<a href="field-station-list.html"><span class="i" style="background:#9B7BFF"></span>Nodes</a>
|
||||
<a href="field-station-list.html"><span class="i" style="background:#E07BC0"></span>Adverts</a>
|
||||
<a><span class="i" style="background:var(--rx)"></span>Messages</a>
|
||||
<a><span class="i" style="background:#7FD98A"></span>Packets</a>
|
||||
<a class="active"><span class="i" style="background:var(--tx)"></span>Map</a>
|
||||
<a><span class="i" style="background:#E0A24A"></span>Members</a>
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
<button class="iconbtn" onclick="toggleTheme()" title="Toggle light/dark">◐</button>
|
||||
<button class="iconbtn burger">≡</button>
|
||||
</nav>
|
||||
|
||||
<div class="wrap">
|
||||
<div class="page-head">
|
||||
<h1 class="page-title">Map</h1>
|
||||
<span class="chip"><b>42</b> nodes</span>
|
||||
<span class="chip"><b>18</b> shown</span>
|
||||
<span class="chip">UTC+1</span>
|
||||
</div>
|
||||
|
||||
<div class="map-layout">
|
||||
<!-- filter sidebar -->
|
||||
<form class="filters" style="grid-template-columns:1fr;align-content:start" onsubmit="return false">
|
||||
<div class="field"><label>Show</label><select><option>All nodes</option><option>Infrastructure only</option></select></div>
|
||||
<div class="field"><label>Node type</label><select><option>All types</option><option>Chat</option><option>Repeater</option><option>Room</option></select></div>
|
||||
<div class="field"><label>Operator</label><select><option>Any operator</option><option>kestrel</option></select></div>
|
||||
<label style="display:flex;gap:.5rem;align-items:center;font-size:.82rem"><input type="checkbox" checked style="width:auto"> Show labels</label>
|
||||
<button class="btn btn-ghost">Clear filters</button>
|
||||
</form>
|
||||
|
||||
<!-- fake map -->
|
||||
<div class="mapwrap">
|
||||
<div class="ring" style="width:160px;height:160px;left:30%;top:42%;transform:translate(-50%,-50%)"></div>
|
||||
<div class="ring" style="width:280px;height:280px;left:30%;top:42%;transform:translate(-50%,-50%)"></div>
|
||||
<div class="pin infra" style="left:30%;top:42%"><span class="plabel">Holbeck-Relay · 2m</span></div>
|
||||
<div class="pin public" style="left:52%;top:30%"><span class="plabel">kestrel · 6m</span></div>
|
||||
<div class="pin public" style="left:66%;top:55%"><span class="plabel">Briggate-Room · 11m</span></div>
|
||||
<div class="pin infra" style="left:46%;top:64%"><span class="plabel">Armley-Hill · 18m</span></div>
|
||||
<div class="pin public" style="left:74%;top:38%"><span class="plabel">g7xyz · 24m</span></div>
|
||||
|
||||
<div class="popup" style="left:32%;top:46%">
|
||||
<h3>📡 Holbeck-Relay</h3>
|
||||
<div class="row"><span>Type</span><span>Repeater</span></div>
|
||||
<div class="row"><span>Owner</span><span>kestrel</span></div>
|
||||
<div class="row"><span>Key</span><span>a1f3…9c2e</span></div>
|
||||
<div class="row"><span>Last seen</span><span>2m ago</span></div>
|
||||
<a href="#">View details →</a>
|
||||
</div>
|
||||
|
||||
<div class="legend">
|
||||
<div class="li"><span class="sw" style="background:var(--infra)"></span> Infrastructure</div>
|
||||
<div class="li"><span class="sw" style="background:var(--public)"></span> Public node</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="foot">IPNET · 53.80°N 1.55°W — powered by MeshCore Hub</footer>
|
||||
<div class="note">Field Station · Map view · ◐ toggle theme · resize narrow to stack filters</div>
|
||||
<script>function toggleTheme(){const h=document.documentElement;h.dataset.theme=h.dataset.theme==='dark'?'light':'dark'}</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,145 @@
|
||||
/* ===== Field Station — shared theme stylesheet (mockup) ===== */
|
||||
:root{
|
||||
--display:"Chakra Petch",system-ui,sans-serif;
|
||||
--body:"IBM Plex Sans",system-ui,sans-serif;
|
||||
--mono:"IBM Plex Mono",ui-monospace,monospace;
|
||||
--radius:10px;
|
||||
}
|
||||
[data-theme="dark"]{
|
||||
--bg:#0B0F14; --panel:#11171F; --panel-2:#161E29; --line:#1B2430;
|
||||
--text:#E6EDF3; --muted:#8B9AAB;
|
||||
--tx:#E8932A; --tx-soft:#E8932A26;
|
||||
--rx:#2BB8AD; --rx-soft:#2BB8AD22;
|
||||
--good:#5BD17F; --warn:#E8C44A;
|
||||
--infra:#7AA7FF; --public:#5BD17F;
|
||||
--map-bg:#0C1219;
|
||||
}
|
||||
[data-theme="light"]{
|
||||
--bg:#F3F5F7; --panel:#FFFFFF; --panel-2:#F7F9FB; --line:#DCE2E8;
|
||||
--text:#10171F; --muted:#5A6B7B;
|
||||
--tx:#B86A12; --tx-soft:#B86A1218;
|
||||
--rx:#0E857C; --rx-soft:#0E857C14;
|
||||
--good:#1E9E4A; --warn:#9A7710;
|
||||
--infra:#2563EB; --public:#1E9E4A;
|
||||
--map-bg:#E9EEF2;
|
||||
}
|
||||
*{box-sizing:border-box}
|
||||
body{margin:0;font-family:var(--body);background:var(--bg);color:var(--text);
|
||||
-webkit-font-smoothing:antialiased;line-height:1.5}
|
||||
a{color:inherit;text-decoration:none}
|
||||
.mono{font-family:var(--mono)}
|
||||
.wrap{max-width:1080px;margin:0 auto;padding:1.25rem 1rem 3rem}
|
||||
|
||||
/* NAV */
|
||||
.nav{position:sticky;top:0;z-index:20;display:flex;align-items:center;gap:.5rem;
|
||||
padding:.6rem 1rem;background:color-mix(in oklab,var(--panel) 88%,transparent);
|
||||
backdrop-filter:blur(10px);border-bottom:1px solid var(--line)}
|
||||
.brand{display:flex;align-items:center;gap:.6rem;font-family:var(--display);
|
||||
font-weight:700;letter-spacing:.04em;font-size:1.05rem}
|
||||
.brand .logo{height:22px;width:auto;color:var(--text)}
|
||||
.brand .logo .arcs{stroke:var(--tx)}
|
||||
.navlinks{display:none;margin-left:1rem;gap:.15rem}
|
||||
.navlinks a{font-size:.85rem;padding:.35rem .6rem;border-radius:7px;color:var(--muted);
|
||||
display:flex;align-items:center;gap:.35rem}
|
||||
.navlinks a:hover{background:var(--tx-soft);color:var(--text)}
|
||||
.navlinks a.active{color:var(--text);background:var(--tx-soft)}
|
||||
.navlinks a .i{width:6px;height:6px;border-radius:50%;background:currentColor;opacity:.7}
|
||||
.spacer{flex:1}
|
||||
.iconbtn{width:34px;height:34px;border-radius:8px;border:1px solid var(--line);
|
||||
background:var(--panel-2);color:var(--text);display:grid;place-items:center;cursor:pointer}
|
||||
@media(min-width:1000px){.navlinks{display:flex}.burger{display:none}}
|
||||
|
||||
/* PANEL + buttons + badges */
|
||||
.panel{border:1px solid var(--line);border-radius:var(--radius);background:var(--panel);overflow:hidden}
|
||||
.panel-h{display:flex;align-items:center;justify-content:space-between;padding:.85rem 1rem;border-bottom:1px solid var(--line)}
|
||||
.panel-h h2{font-family:var(--display);font-weight:600;font-size:1rem;margin:0;letter-spacing:.02em}
|
||||
.btn{font-family:var(--body);font-weight:600;font-size:.82rem;padding:.45rem .8rem;border-radius:8px;
|
||||
background:var(--tx);color:#0B0F14;border:none;cursor:pointer}
|
||||
[data-theme="light"] .btn{color:#fff}
|
||||
.btn-ghost{background:transparent;color:var(--muted);border:1px solid var(--line)}
|
||||
.badge{font-family:var(--mono);font-size:.66rem;letter-spacing:.04em;padding:.12rem .5rem;border-radius:99px;border:1px solid var(--line)}
|
||||
.b-tx{color:var(--tx);background:var(--tx-soft);border-color:transparent}
|
||||
.b-rx{color:var(--rx);background:var(--rx-soft);border-color:transparent}
|
||||
.chip{display:inline-flex;align-items:center;gap:.35rem;font-family:var(--mono);font-size:.68rem;
|
||||
color:var(--muted);border:1px solid var(--line);border-radius:99px;padding:.18rem .55rem;background:var(--panel-2)}
|
||||
.chip b{color:var(--text);font-weight:600}
|
||||
|
||||
/* PAGE HEAD */
|
||||
.page-head{display:flex;align-items:center;flex-wrap:wrap;gap:.6rem;margin-bottom:1rem}
|
||||
.page-title{font-family:var(--display);font-weight:700;font-size:1.5rem;letter-spacing:.02em;margin:0}
|
||||
.page-head .spacer{flex:1}
|
||||
|
||||
/* FORMS / FILTERS */
|
||||
.filters{border:1px solid var(--line);border-radius:var(--radius);background:var(--panel);
|
||||
padding:.9rem 1rem;margin-bottom:1rem;display:grid;gap:.7rem;
|
||||
grid-template-columns:1fr;align-items:end}
|
||||
@media(min-width:760px){.filters{grid-template-columns:2fr 1fr 1fr auto auto}}
|
||||
.field{display:flex;flex-direction:column;gap:.25rem}
|
||||
.field label{font-family:var(--mono);font-size:.62rem;letter-spacing:.1em;text-transform:uppercase;color:var(--muted)}
|
||||
input,select{font-family:var(--body);font-size:.85rem;background:var(--panel-2);color:var(--text);
|
||||
border:1px solid var(--line);border-radius:8px;padding:.5rem .6rem;width:100%}
|
||||
input:focus,select:focus{outline:2px solid var(--tx);outline-offset:1px}
|
||||
|
||||
/* TABLE */
|
||||
.tbl-mobile-sort{display:block;margin-bottom:.75rem}
|
||||
@media(min-width:760px){.tbl-mobile-sort{display:none}}
|
||||
table{width:100%;border-collapse:collapse;font-size:.85rem}
|
||||
th{text-align:left;font-family:var(--mono);font-size:.66rem;letter-spacing:.1em;text-transform:uppercase;
|
||||
color:var(--muted);font-weight:500;padding:.55rem 1rem;white-space:nowrap}
|
||||
th.sort{cursor:pointer}
|
||||
th.sort .caret{opacity:.55;margin-left:.25rem}
|
||||
th.sort.asc .caret::after{content:"▲"} th.sort.desc .caret::after{content:"▼"}
|
||||
td{padding:.6rem 1rem;border-top:1px solid var(--line)}
|
||||
tbody tr:hover td{background:var(--panel-2)}
|
||||
.key{font-family:var(--mono);font-size:.78rem;color:var(--muted)}
|
||||
.node{display:flex;align-items:center;gap:.6rem}.node .em{font-size:1.1rem}.node .nm{font-weight:600}
|
||||
.time{font-family:var(--mono);color:var(--muted);font-size:.8rem}
|
||||
.observers{display:inline-flex;align-items:center;gap:.3rem;font-family:var(--mono);font-size:.72rem;color:var(--muted)}
|
||||
.table-scroll{overflow-x:auto}
|
||||
.desktop-only{display:none}
|
||||
@media(min-width:760px){.desktop-only{display:block}.mobile-only{display:none}}
|
||||
|
||||
/* MOBILE CARDS */
|
||||
.cards{display:flex;flex-direction:column;gap:.55rem}
|
||||
.ncard{border:1px solid var(--line);border-radius:var(--radius);background:var(--panel);padding:.75rem .85rem;
|
||||
display:flex;align-items:center;gap:.7rem}
|
||||
.ncard .meta{flex:1;min-width:0}
|
||||
.ncard .nm{font-weight:600}
|
||||
.ncard .sub{font-family:var(--mono);font-size:.72rem;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
||||
|
||||
/* PAGINATION */
|
||||
.pager{display:flex;justify-content:center;gap:.25rem;padding:1rem;flex-wrap:wrap}
|
||||
.pager a{min-width:34px;text-align:center;padding:.35rem .6rem;border:1px solid var(--line);border-radius:8px;
|
||||
font-family:var(--mono);font-size:.8rem;color:var(--muted)}
|
||||
.pager a.cur{background:var(--tx-soft);color:var(--text);border-color:transparent}
|
||||
|
||||
/* MAP */
|
||||
.map-layout{display:grid;gap:1rem;grid-template-columns:1fr}
|
||||
@media(min-width:900px){.map-layout{grid-template-columns:260px 1fr}}
|
||||
.mapwrap{position:relative;border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;
|
||||
min-height:460px;background:var(--map-bg);
|
||||
background-image:
|
||||
linear-gradient(color-mix(in oklab,var(--line) 60%,transparent) 1px,transparent 1px),
|
||||
linear-gradient(90deg,color-mix(in oklab,var(--line) 60%,transparent) 1px,transparent 1px);
|
||||
background-size:42px 42px}
|
||||
.mapwrap .ring{position:absolute;border:1px solid var(--tx);border-radius:50%;opacity:.18;pointer-events:none}
|
||||
.pin{position:absolute;width:16px;height:16px;border-radius:50%;transform:translate(-50%,-50%);
|
||||
border:2px solid var(--bg);box-shadow:0 0 0 2px currentColor}
|
||||
.pin.infra{background:var(--infra);color:var(--infra)}
|
||||
.pin.public{background:var(--public);color:var(--public)}
|
||||
.pin .plabel{position:absolute;left:14px;top:-2px;white-space:nowrap;font-family:var(--mono);font-size:.66rem;
|
||||
color:var(--text);background:color-mix(in oklab,var(--panel) 85%,transparent);padding:.05rem .35rem;border-radius:4px;border:1px solid var(--line)}
|
||||
.popup{position:absolute;width:210px;border:1px solid var(--line);border-radius:var(--radius);background:var(--panel);
|
||||
box-shadow:0 8px 24px #0006;padding:.7rem .8rem;font-size:.8rem}
|
||||
.popup h3{font-family:var(--display);font-size:.95rem;margin:0 0 .3rem;display:flex;align-items:center;gap:.4rem}
|
||||
.popup .row{display:flex;justify-content:space-between;color:var(--muted);font-family:var(--mono);font-size:.72rem;margin:.12rem 0}
|
||||
.popup a{color:var(--tx);font-weight:600;font-size:.78rem}
|
||||
.legend{position:absolute;left:12px;bottom:12px;background:color-mix(in oklab,var(--panel) 90%,transparent);
|
||||
border:1px solid var(--line);border-radius:8px;padding:.5rem .7rem;font-size:.74rem}
|
||||
.legend .li{display:flex;align-items:center;gap:.45rem;margin:.15rem 0;color:var(--muted)}
|
||||
.legend .sw{width:11px;height:11px;border-radius:50%}
|
||||
|
||||
.foot{max-width:1080px;margin:2rem auto 0;padding:1.5rem 1rem;color:var(--muted);font-size:.78rem;
|
||||
border-top:1px solid var(--line);text-align:center}
|
||||
.note{position:fixed;left:50%;transform:translateX(-50%);bottom:14px;font-family:var(--mono);font-size:.68rem;
|
||||
color:var(--muted);background:var(--panel);border:1px solid var(--line);padding:.3rem .7rem;border-radius:99px;z-index:30}
|
||||
@@ -0,0 +1,175 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Field Station — MeshCore Hub mockup</title>
|
||||
<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=Chakra+Petch:wght@500;600;700&family=IBM+Plex+Sans:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root{
|
||||
--display:"Chakra Petch",system-ui,sans-serif;
|
||||
--body:"IBM Plex Sans",system-ui,sans-serif;
|
||||
--mono:"IBM Plex Mono",ui-monospace,monospace;
|
||||
--radius:10px;
|
||||
}
|
||||
/* DARK — field at night */
|
||||
[data-theme="dark"]{
|
||||
--bg:#0B0F14; --panel:#11171F; --panel-2:#161E29; --line:#1B2430;
|
||||
--text:#E6EDF3; --muted:#8B9AAB;
|
||||
--tx:#E8932A; /* transmit / amber primary */
|
||||
--tx-soft:#E8932A26;
|
||||
--rx:#2BB8AD; /* receive / cyan secondary */
|
||||
--rx-soft:#2BB8AD22;
|
||||
--good:#5BD17F; --warn:#E8C44A;
|
||||
}
|
||||
/* LIGHT — daytime bench */
|
||||
[data-theme="light"]{
|
||||
--bg:#F3F5F7; --panel:#FFFFFF; --panel-2:#F7F9FB; --line:#DCE2E8;
|
||||
--text:#10171F; --muted:#5A6B7B;
|
||||
--tx:#B86A12; --tx-soft:#B86A1218;
|
||||
--rx:#0E857C; --rx-soft:#0E857C14;
|
||||
--good:#1E9E4A; --warn:#9A7710;
|
||||
}
|
||||
*{box-sizing:border-box}
|
||||
body{margin:0;font-family:var(--body);background:var(--bg);color:var(--text);
|
||||
-webkit-font-smoothing:antialiased;line-height:1.5}
|
||||
a{color:inherit;text-decoration:none}
|
||||
.mono{font-family:var(--mono)}
|
||||
.eyebrow{font-family:var(--mono);font-size:.7rem;letter-spacing:.18em;
|
||||
text-transform:uppercase;color:var(--muted)}
|
||||
|
||||
/* NAV */
|
||||
.nav{position:sticky;top:0;z-index:20;display:flex;align-items:center;gap:.5rem;
|
||||
padding:.6rem 1rem;background:color-mix(in oklab,var(--panel) 88%,transparent);
|
||||
backdrop-filter:blur(10px);border-bottom:1px solid var(--line)}
|
||||
.brand{display:flex;align-items:center;gap:.6rem;font-family:var(--display);
|
||||
font-weight:700;letter-spacing:.04em;font-size:1.05rem}
|
||||
.brand .logo{height:22px;width:auto;color:var(--text)}
|
||||
.brand .logo .arcs{stroke:var(--tx)}
|
||||
.hero .logo-lg{height:52px;width:auto;color:var(--text);display:block;margin-bottom:.7rem;position:relative}
|
||||
.hero .logo-lg .arcs{stroke:var(--tx)}
|
||||
.navlinks{display:none;margin-left:1rem;gap:.15rem}
|
||||
.navlinks a{font-size:.85rem;padding:.35rem .6rem;border-radius:7px;color:var(--muted);
|
||||
display:flex;align-items:center;gap:.35rem}
|
||||
.navlinks a:hover{background:var(--tx-soft);color:var(--text)}
|
||||
.navlinks a.active{color:var(--text);background:var(--tx-soft)}
|
||||
.navlinks a .i{width:6px;height:6px;border-radius:50%;background:currentColor;opacity:.7}
|
||||
.spacer{flex:1}
|
||||
.iconbtn{width:34px;height:34px;border-radius:8px;border:1px solid var(--line);
|
||||
background:var(--panel-2);color:var(--text);display:grid;place-items:center;cursor:pointer}
|
||||
@media(min-width:1000px){.navlinks{display:flex}.burger{display:none}}
|
||||
|
||||
/* HERO with RF range-rings signature */
|
||||
.wrap{max-width:1080px;margin:0 auto;padding:1.25rem 1rem 3rem}
|
||||
.hero{position:relative;overflow:hidden;border:1px solid var(--line);border-radius:var(--radius);
|
||||
background:var(--panel);padding:1.6rem 1.4rem;margin-bottom:1.25rem}
|
||||
.hero .rings{position:absolute;right:-60px;top:50%;transform:translateY(-50%);
|
||||
width:340px;height:340px;pointer-events:none;opacity:.5}
|
||||
.hero .rings span{position:absolute;inset:0;margin:auto;border-radius:50%;
|
||||
border:1px solid var(--tx);opacity:.35}
|
||||
.r1{width:90px;height:90px} .r2{width:170px;height:170px;opacity:.22!important}
|
||||
.r3{width:260px;height:260px;opacity:.12!important} .r4{width:340px;height:340px;opacity:.06!important}
|
||||
.hero .ping{position:absolute;right:calc(-60px + 170px - 5px);top:calc(50% - 5px);
|
||||
width:10px;height:10px;border-radius:50%;background:var(--tx);box-shadow:0 0 10px var(--tx)}
|
||||
.hero h1{font-family:var(--display);font-weight:700;font-size:clamp(1.6rem,5vw,2.4rem);
|
||||
margin:.4rem 0 .3rem;letter-spacing:.02em}
|
||||
.hero p{color:var(--muted);max-width:46ch;margin:0}
|
||||
.status{display:inline-flex;align-items:center;gap:.45rem;font-family:var(--mono);
|
||||
font-size:.72rem;color:var(--good);border:1px solid var(--line);border-radius:99px;
|
||||
padding:.2rem .6rem;background:var(--panel-2)}
|
||||
.status .live{width:7px;height:7px;border-radius:50%;background:var(--good)}
|
||||
|
||||
/* STATS */
|
||||
.grid{display:grid;gap:.75rem;grid-template-columns:repeat(2,1fr);margin-bottom:1.25rem}
|
||||
@media(min-width:760px){.grid{grid-template-columns:repeat(4,1fr)}}
|
||||
.stat{border:1px solid var(--line);border-radius:var(--radius);background:var(--panel);
|
||||
padding:.9rem 1rem;position:relative;overflow:hidden}
|
||||
.stat::after{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--accent,var(--tx))}
|
||||
.stat .label{font-family:var(--mono);font-size:.66rem;letter-spacing:.12em;
|
||||
text-transform:uppercase;color:var(--muted)}
|
||||
.stat .val{font-family:var(--display);font-weight:700;font-size:1.9rem;line-height:1.1;margin-top:.2rem}
|
||||
.stat .sub{font-size:.72rem;color:var(--muted)}
|
||||
|
||||
/* PANEL + TABLE */
|
||||
.panel{border:1px solid var(--line);border-radius:var(--radius);background:var(--panel);overflow:hidden}
|
||||
.panel-h{display:flex;align-items:center;justify-content:space-between;padding:.85rem 1rem;
|
||||
border-bottom:1px solid var(--line)}
|
||||
.panel-h h2{font-family:var(--display);font-weight:600;font-size:1rem;margin:0;letter-spacing:.02em}
|
||||
table{width:100%;border-collapse:collapse;font-size:.85rem}
|
||||
th{text-align:left;font-family:var(--mono);font-size:.66rem;letter-spacing:.1em;
|
||||
text-transform:uppercase;color:var(--muted);font-weight:500;padding:.55rem 1rem}
|
||||
td{padding:.6rem 1rem;border-top:1px solid var(--line)}
|
||||
tr:hover td{background:var(--panel-2)}
|
||||
.key{font-family:var(--mono);font-size:.78rem;color:var(--muted)}
|
||||
.badge{font-family:var(--mono);font-size:.66rem;letter-spacing:.04em;padding:.12rem .5rem;
|
||||
border-radius:99px;border:1px solid var(--line)}
|
||||
.b-tx{color:var(--tx);background:var(--tx-soft);border-color:transparent}
|
||||
.b-rx{color:var(--rx);background:var(--rx-soft);border-color:transparent}
|
||||
.btn{font-family:var(--body);font-weight:600;font-size:.82rem;padding:.45rem .8rem;border-radius:8px;
|
||||
background:var(--tx);color:#0B0F14;border:none;cursor:pointer}
|
||||
[data-theme="light"] .btn{color:#fff}
|
||||
.node{display:flex;align-items:center;gap:.6rem}
|
||||
.node .em{font-size:1.1rem}
|
||||
.node .nm{font-weight:600}
|
||||
.foot{max-width:1080px;margin:0 auto;padding:1.5rem 1rem;color:var(--muted);
|
||||
font-size:.78rem;border-top:1px solid var(--line);margin-top:2rem;text-align:center}
|
||||
.note{position:fixed;left:50%;transform:translateX(-50%);bottom:14px;font-family:var(--mono);
|
||||
font-size:.68rem;color:var(--muted);background:var(--panel);border:1px solid var(--line);
|
||||
padding:.3rem .7rem;border-radius:99px}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="nav">
|
||||
<div class="brand"><svg class="logo" viewBox="0 0 115 100" aria-hidden="true"><rect x="0" y="0" width="25" height="100" rx="2" fill="currentColor" opacity=".5"/><rect x="35" y="0" width="25" height="100" rx="2" fill="currentColor"/><g class="arcs" fill="none" stroke="currentColor" stroke-width="10" stroke-linecap="round" transform="translate(-30,-10)"><path d="M 110,65 A 20,20 0 0 0 90,45"/><path d="M 125,65 A 35,35 0 0 0 90,30"/><path d="M 140,65 A 50,50 0 0 0 90,15"/></g></svg>IPNET</div>
|
||||
<div class="navlinks">
|
||||
<a class="active"><span class="i" style="background:#7AA7FF"></span>Dashboard</a>
|
||||
<a href="field-station-list.html"><span class="i" style="background:#9B7BFF"></span>Nodes</a>
|
||||
<a href="field-station-list.html"><span class="i" style="background:#E07BC0"></span>Adverts</a>
|
||||
<a><span class="i" style="background:var(--rx)"></span>Messages</a>
|
||||
<a><span class="i" style="background:#7FD98A"></span>Packets</a>
|
||||
<a href="field-station-map.html"><span class="i" style="background:var(--tx)"></span>Map</a>
|
||||
<a><span class="i" style="background:#E0A24A"></span>Members</a>
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
<button class="iconbtn" onclick="toggleTheme()" title="Toggle light/dark">◐</button>
|
||||
<button class="iconbtn burger">≡</button>
|
||||
</nav>
|
||||
|
||||
<div class="wrap">
|
||||
<section class="hero">
|
||||
<div class="rings"><span class="r1"></span><span class="r2"></span><span class="r3"></span><span class="r4"></span></div>
|
||||
<span class="ping"></span>
|
||||
<svg class="logo-lg" viewBox="0 0 115 100" aria-hidden="true"><rect x="0" y="0" width="25" height="100" rx="2" fill="currentColor" opacity=".5"/><rect x="35" y="0" width="25" height="100" rx="2" fill="currentColor"/><g class="arcs" fill="none" stroke="currentColor" stroke-width="10" stroke-linecap="round" transform="translate(-30,-10)"><path d="M 110,65 A 20,20 0 0 0 90,45"/><path d="M 125,65 A 35,35 0 0 0 90,30"/><path d="M 140,65 A 50,50 0 0 0 90,15"/></g></svg>
|
||||
<span class="status"><span class="live"></span>ON AIR · 42 NODES</span>
|
||||
<h1>IPNET</h1>
|
||||
<p>Off-grid encrypted LoRa mesh. Tracking who's transmitting, how packets propagate, and where the network reaches.</p>
|
||||
</section>
|
||||
|
||||
<div class="grid">
|
||||
<div class="stat" style="--accent:#9B7BFF"><div class="label">Nodes</div><div class="val">42</div><div class="sub">+3 this week</div></div>
|
||||
<div class="stat" style="--accent:#E07BC0"><div class="label">Adverts · 7d</div><div class="val">1,284</div><div class="sub">flood & direct</div></div>
|
||||
<div class="stat" style="--accent:var(--rx)"><div class="label">Messages · 7d</div><div class="val">318</div><div class="sub">6 channels</div></div>
|
||||
<div class="stat" style="--accent:var(--tx)"><div class="label">Reach</div><div class="val">31<span style="font-size:1rem">km</span></div><div class="sub">max link</div></div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-h"><h2>Recent nodes</h2><button class="btn">View all</button></div>
|
||||
<table>
|
||||
<thead><tr><th>Node</th><th>Public key</th><th>Route</th><th>Last heard</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td><div class="node"><span class="em">📡</span><span class="nm">Holbeck-Relay</span></div></td><td class="key">a1f3…9c2e</td><td><span class="badge b-rx">DIRECT</span></td><td class="mono" style="color:var(--muted)">2m ago</td></tr>
|
||||
<tr><td><div class="node"><span class="em">💬</span><span class="nm">kestrel</span></div></td><td class="key">7b02…4d1a</td><td><span class="badge b-tx">FLOOD</span></td><td class="mono" style="color:var(--muted)">11m ago</td></tr>
|
||||
<tr><td><div class="node"><span class="em">🪧</span><span class="nm">Briggate-Room</span></div></td><td class="key">e5c9…aa70</td><td><span class="badge b-tx">FLOOD</span></td><td class="mono" style="color:var(--muted)">38m ago</td></tr>
|
||||
<tr><td><div class="node"><span class="em">📡</span><span class="nm">Armley-Hill</span></div></td><td class="key">3d8e…1f55</td><td><span class="badge b-rx">DIRECT</span></td><td class="mono" style="color:var(--muted)">1h ago</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="foot">IPNET · 53.80°N 1.55°W — powered by MeshCore Hub</footer>
|
||||
<div class="note">Field Station · click ◐ to toggle light/dark · resize to portrait to test mobile</div>
|
||||
<script>function toggleTheme(){const h=document.documentElement;h.dataset.theme=h.dataset.theme==='dark'?'light':'dark'}</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,49 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>IPNet — design mockups</title>
|
||||
<style>
|
||||
body{margin:0;font-family:system-ui,sans-serif;background:#0d1117;color:#e6edf3;
|
||||
display:grid;place-items:center;min-height:100vh;padding:2rem}
|
||||
.card{max-width:640px;width:100%}
|
||||
h1{font-weight:700;letter-spacing:.02em;margin-bottom:.25rem}
|
||||
.lead{color:#8b949e;line-height:1.6;margin-top:0}
|
||||
.theme{border:1px solid #30363d;border-radius:12px;padding:1rem 1.2rem;margin:.8rem 0}
|
||||
.theme h2{margin:.1rem 0 .15rem;font-size:1.1rem}
|
||||
.theme p{color:#8b949e;font-size:.86rem;margin:0 0 .7rem;line-height:1.5}
|
||||
.row{display:flex;gap:.5rem;flex-wrap:wrap}
|
||||
.row a{flex:1;min-width:120px;text-align:center;padding:.55rem .8rem;border:1px solid #30363d;border-radius:8px;
|
||||
text-decoration:none;color:inherit;font-size:.9rem;transition:.15s}
|
||||
.row a:hover{border-color:#58a6ff;background:#161b22}
|
||||
.tip{color:#6e7681;font-size:.8rem;margin-top:1rem}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card">
|
||||
<h1>IPNet — theme mockups</h1>
|
||||
<p class="lead">Each theme has three pages. Click ◐ (top-right of any page) to toggle light/dark. Resize the window narrow to test mobile portrait. Nav links move between that theme's pages. All keep the existing navigation and functionality — only palette, type, and a signature element change.</p>
|
||||
|
||||
<div class="theme">
|
||||
<h2>1 · Field Station</h2>
|
||||
<p>Radio instrument console. Amber transmit + cyan receive. Chakra Petch + IBM Plex. RF range-rings signature.</p>
|
||||
<div class="row"><a href="field-station.html">Dashboard</a><a href="field-station-list.html">List</a><a href="field-station-map.html">Map</a></div>
|
||||
</div>
|
||||
|
||||
<div class="theme">
|
||||
<h2>2 · Topographic</h2>
|
||||
<p>Cartographic survey. Rust + topo green. Archivo Narrow + Inter. Faint contour-line signature.</p>
|
||||
<div class="row"><a href="topographic.html">Dashboard</a><a href="topographic-list.html">List</a><a href="topographic-map.html">Map</a></div>
|
||||
</div>
|
||||
|
||||
<div class="theme">
|
||||
<h2>3 · Packet Monitor</h2>
|
||||
<p>Live ops console. Monospace-forward, dense, square corners. JetBrains Mono + IBM Plex Sans.</p>
|
||||
<div class="row"><a href="packet-monitor.html">Dashboard</a><a href="packet-monitor-list.html">List</a><a href="packet-monitor-map.html">Map</a></div>
|
||||
</div>
|
||||
|
||||
<p class="tip">Maps are stylised placeholders (CSS-drawn) to show the themed chrome — filters, markers, popups, legend — not real Leaflet tiles. The production build keeps the real Leaflet map and just restyles its container, popups, and markers.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,78 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Packet Monitor — Advertisements</title>
|
||||
<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=JetBrains+Mono:wght@400;500;700;800&family=IBM+Plex+Sans:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="packet-monitor.css">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="nav">
|
||||
<a href="packet-monitor.html" class="brand"><svg class="logo" viewBox="0 0 115 100" aria-hidden="true"><rect x="0" y="0" width="25" height="100" rx="2" fill="currentColor" opacity=".5"/><rect x="35" y="0" width="25" height="100" rx="2" fill="currentColor"/><g class="arcs" fill="none" stroke="currentColor" stroke-width="10" stroke-linecap="round" transform="translate(-30,-10)"><path d="M 110,65 A 20,20 0 0 0 90,45"/><path d="M 125,65 A 35,35 0 0 0 90,30"/><path d="M 140,65 A 50,50 0 0 0 90,15"/></g></svg>IPNET</a>
|
||||
<div class="navlinks">
|
||||
<a href="packet-monitor.html">dashboard</a><a href="packet-monitor-list.html">nodes</a><a class="active">adverts</a><a>messages</a><a>packets</a><a href="packet-monitor-map.html">map</a><a>members</a>
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
<button class="iconbtn" onclick="toggleTheme()" title="Toggle light/dark">◐</button>
|
||||
<button class="iconbtn burger">≡</button>
|
||||
</nav>
|
||||
|
||||
<div class="wrap">
|
||||
<div class="page-head">
|
||||
<h1 class="page-title">advertisements</h1>
|
||||
<span class="chip">UTC+1</span>
|
||||
<span class="chip"><b>1284</b> total</span>
|
||||
<div class="spacer"></div>
|
||||
<button class="iconbtn" title="Auto-refresh: on">⏸</button>
|
||||
</div>
|
||||
|
||||
<form class="filters" onsubmit="return false">
|
||||
<div class="field"><label>search</label><input placeholder="node name or pubkey…"></div>
|
||||
<div class="field"><label>route_type</label><select><option>all routes</option><option>flood</option><option>direct relay</option></select></div>
|
||||
<div class="field"><label>operator</label><select><option>any operator</option><option>kestrel</option><option>g7xyz</option></select></div>
|
||||
<button class="btn">apply</button>
|
||||
<button class="btn btn-ghost">clear</button>
|
||||
</form>
|
||||
|
||||
<div class="tbl-mobile-sort field"><label>sort_by</label><select><option>newest first</option><option>oldest first</option><option>node name</option></select></div>
|
||||
|
||||
<div class="panel desktop-only">
|
||||
<div class="table-scroll">
|
||||
<table>
|
||||
<thead><tr>
|
||||
<th class="sort">node <span class="caret"></span></th>
|
||||
<th>pubkey</th>
|
||||
<th class="sort">route <span class="caret"></span></th>
|
||||
<th class="sort desc">time <span class="caret"></span></th>
|
||||
<th>obs</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
<tr><td><div class="node"><span class="em">📡</span><span class="nm">Holbeck-Relay</span></div></td><td class="key">a1f3…9c2e</td><td><span class="badge b-acc">DIRECT</span></td><td class="time">2m</td><td><span class="observers">📡 4</span></td></tr>
|
||||
<tr><td><div class="node"><span class="em">💬</span><span class="nm">kestrel</span></div></td><td class="key">7b02…4d1a</td><td><span class="badge b-amb">FLOOD</span></td><td class="time">6m</td><td><span class="observers">📡 7</span></td></tr>
|
||||
<tr><td><div class="node"><span class="em">🪧</span><span class="nm">Briggate-Room</span></div></td><td class="key">e5c9…aa70</td><td><span class="badge b-amb">FLOOD</span></td><td class="time">11m</td><td><span class="observers">📡 3</span></td></tr>
|
||||
<tr><td><div class="node"><span class="em">📡</span><span class="nm">Armley-Hill</span></div></td><td class="key">3d8e…1f55</td><td><span class="badge b-acc">DIRECT</span></td><td class="time">18m</td><td><span class="observers">📡 5</span></td></tr>
|
||||
<tr><td><div class="node"><span class="em">📱</span><span class="nm">g7xyz-companion</span></div></td><td class="key">b240…77c1</td><td><span class="badge b-amb">FLOOD</span></td><td class="time">24m</td><td><span class="observers">📡 2</span></td></tr>
|
||||
<tr><td><div class="node"><span class="em">📡</span><span class="nm">Hyde-Park-Hub</span></div></td><td class="key">9f10…3b8d</td><td><span class="badge b-acc">DIRECT</span></td><td class="time">31m</td><td><span class="observers">📡 6</span></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="pager"><a>‹</a><a class="cur">1</a><a>2</a><a>3</a><a>…</a><a>9</a><a>›</a></div>
|
||||
</div>
|
||||
|
||||
<div class="cards mobile-only">
|
||||
<div class="ncard"><span class="em">📡</span><div class="meta"><div class="nm">Holbeck-Relay</div><div class="sub">a1f3…9c2e · 📡 4</div></div><span class="badge b-acc">DIRECT</span></div>
|
||||
<div class="ncard"><span class="em">💬</span><div class="meta"><div class="nm">kestrel</div><div class="sub">7b02…4d1a · 📡 7</div></div><span class="badge b-amb">FLOOD</span></div>
|
||||
<div class="ncard"><span class="em">🪧</span><div class="meta"><div class="nm">Briggate-Room</div><div class="sub">e5c9…aa70 · 📡 3</div></div><span class="badge b-amb">FLOOD</span></div>
|
||||
<div class="ncard"><span class="em">📡</span><div class="meta"><div class="nm">Armley-Hill</div><div class="sub">3d8e…1f55 · 📡 5</div></div><span class="badge b-acc">DIRECT</span></div>
|
||||
<div class="pager"><a>‹</a><a class="cur">1</a><a>2</a><a>3</a><a>›</a></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="foot">IPNET · 53.80N 1.55W — powered by MeshCore Hub</footer>
|
||||
<div class="note">Packet Monitor · List view · ◐ toggle theme · resize narrow for mobile cards</div>
|
||||
<script>function toggleTheme(){const h=document.documentElement;h.dataset.theme=h.dataset.theme==='dark'?'light':'dark'}</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,68 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Packet Monitor — Map</title>
|
||||
<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=JetBrains+Mono:wght@400;500;700;800&family=IBM+Plex+Sans:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="packet-monitor.css">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="nav">
|
||||
<a href="packet-monitor.html" class="brand"><svg class="logo" viewBox="0 0 115 100" aria-hidden="true"><rect x="0" y="0" width="25" height="100" rx="2" fill="currentColor" opacity=".5"/><rect x="35" y="0" width="25" height="100" rx="2" fill="currentColor"/><g class="arcs" fill="none" stroke="currentColor" stroke-width="10" stroke-linecap="round" transform="translate(-30,-10)"><path d="M 110,65 A 20,20 0 0 0 90,45"/><path d="M 125,65 A 35,35 0 0 0 90,30"/><path d="M 140,65 A 50,50 0 0 0 90,15"/></g></svg>IPNET</a>
|
||||
<div class="navlinks">
|
||||
<a href="packet-monitor.html">dashboard</a><a href="packet-monitor-list.html">nodes</a><a href="packet-monitor-list.html">adverts</a><a>messages</a><a>packets</a><a class="active">map</a><a>members</a>
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
<button class="iconbtn" onclick="toggleTheme()" title="Toggle light/dark">◐</button>
|
||||
<button class="iconbtn burger">≡</button>
|
||||
</nav>
|
||||
|
||||
<div class="wrap">
|
||||
<div class="page-head">
|
||||
<h1 class="page-title">map</h1>
|
||||
<span class="chip"><b>42</b> nodes</span>
|
||||
<span class="chip"><b>18</b> shown</span>
|
||||
<span class="chip">UTC+1</span>
|
||||
</div>
|
||||
|
||||
<div class="map-layout">
|
||||
<form class="filters" style="grid-template-columns:1fr;align-content:start" onsubmit="return false">
|
||||
<div class="field"><label>show</label><select><option>all nodes</option><option>infrastructure only</option></select></div>
|
||||
<div class="field"><label>node_type</label><select><option>all types</option><option>chat</option><option>repeater</option><option>room</option></select></div>
|
||||
<div class="field"><label>operator</label><select><option>any operator</option><option>kestrel</option></select></div>
|
||||
<label style="display:flex;gap:.5rem;align-items:center;font-size:.8rem;font-family:var(--mono)"><input type="checkbox" checked style="width:auto"> show labels</label>
|
||||
<button class="btn btn-ghost">clear filters</button>
|
||||
</form>
|
||||
|
||||
<div class="mapwrap">
|
||||
<div class="pin infra" style="left:32%;top:44%"><span class="plabel">Holbeck-Relay · 2m</span></div>
|
||||
<div class="pin public" style="left:54%;top:30%"><span class="plabel">kestrel · 6m</span></div>
|
||||
<div class="pin public" style="left:68%;top:56%"><span class="plabel">Briggate-Room · 11m</span></div>
|
||||
<div class="pin infra" style="left:46%;top:66%"><span class="plabel">Armley-Hill · 18m</span></div>
|
||||
<div class="pin public" style="left:76%;top:40%"><span class="plabel">g7xyz · 24m</span></div>
|
||||
|
||||
<div class="popup" style="left:34%;top:48%">
|
||||
<h3>📡 Holbeck-Relay</h3>
|
||||
<div class="row"><span>type</span><span>repeater</span></div>
|
||||
<div class="row"><span>owner</span><span>kestrel</span></div>
|
||||
<div class="row"><span>key</span><span>a1f3…9c2e</span></div>
|
||||
<div class="row"><span>last_seen</span><span>2m</span></div>
|
||||
<a href="#">view_details →</a>
|
||||
</div>
|
||||
|
||||
<div class="legend">
|
||||
<div class="li"><span class="sw" style="background:var(--infra)"></span> infrastructure</div>
|
||||
<div class="li"><span class="sw" style="background:var(--public)"></span> public node</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="foot">IPNET · 53.80N 1.55W — powered by MeshCore Hub</footer>
|
||||
<div class="note">Packet Monitor · Map view · ◐ toggle theme · resize narrow to stack filters</div>
|
||||
<script>function toggleTheme(){const h=document.documentElement;h.dataset.theme=h.dataset.theme==='dark'?'light':'dark'}</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,121 @@
|
||||
/* ===== Packet Monitor — shared theme stylesheet (mockup) ===== */
|
||||
:root{
|
||||
--display:"JetBrains Mono",ui-monospace,monospace;
|
||||
--body:"IBM Plex Sans",system-ui,sans-serif;
|
||||
--mono:"JetBrains Mono",ui-monospace,monospace;
|
||||
--radius:3px;
|
||||
}
|
||||
[data-theme="dark"]{
|
||||
--bg:#07090B; --panel:#0E1316; --panel-2:#141B1F; --line:#1A2129;
|
||||
--text:#D7E0E2; --muted:#6F8088;
|
||||
--accent:#5FB874; --accent-soft:#5FB87422;
|
||||
--amber:#D39E2A; --amber-soft:#D39E2A1c;
|
||||
--infra:#5FB874; --public:#D39E2A;
|
||||
--map-bg:#080B0D;
|
||||
}
|
||||
[data-theme="light"]{
|
||||
--bg:#F6F8F8; --panel:#FFFFFF; --panel-2:#F0F3F3; --line:#D7DEDE;
|
||||
--text:#0C1416; --muted:#5A696D;
|
||||
--accent:#2F8F4E; --accent-soft:#2F8F4E14;
|
||||
--amber:#9A7415; --amber-soft:#9A741514;
|
||||
--infra:#2F8F4E; --public:#9A7415;
|
||||
--map-bg:#EAEFEF;
|
||||
}
|
||||
*{box-sizing:border-box}
|
||||
body{margin:0;font-family:var(--body);background:var(--bg);color:var(--text);-webkit-font-smoothing:antialiased;line-height:1.5}
|
||||
a{color:inherit;text-decoration:none}
|
||||
.mono{font-family:var(--mono)}
|
||||
.wrap{max-width:1100px;margin:0 auto;padding:1.1rem 1rem 3rem}
|
||||
|
||||
/* NAV */
|
||||
.nav{position:sticky;top:0;z-index:20;display:flex;align-items:center;gap:.5rem;padding:.55rem 1rem;background:var(--panel);border-bottom:1px solid var(--line)}
|
||||
.brand{display:flex;align-items:center;gap:.5rem;font-family:var(--display);font-weight:800;font-size:.95rem;letter-spacing:.02em}
|
||||
.brand .logo{height:20px;width:auto;color:var(--text)}
|
||||
.brand .logo .arcs{stroke:var(--accent)}
|
||||
.navlinks{display:none;margin-left:1rem;gap:0}
|
||||
.navlinks a{font-family:var(--mono);font-size:.8rem;padding:.3rem .55rem;color:var(--muted)}
|
||||
.navlinks a::before{content:"/";opacity:.4;margin-right:1px}
|
||||
.navlinks a:hover{color:var(--text);background:var(--accent-soft)}
|
||||
.navlinks a.active{color:var(--accent)}
|
||||
.spacer{flex:1}
|
||||
.iconbtn{width:32px;height:32px;border-radius:3px;border:1px solid var(--line);background:var(--panel-2);color:var(--text);display:grid;place-items:center;cursor:pointer}
|
||||
@media(min-width:1000px){.navlinks{display:flex}.burger{display:none}}
|
||||
|
||||
/* PANEL + buttons + badges */
|
||||
.panel{border:1px solid var(--line);border-radius:var(--radius);background:var(--panel);overflow:hidden}
|
||||
.panel-h{display:flex;align-items:center;justify-content:space-between;padding:.65rem 1rem;border-bottom:1px solid var(--line)}
|
||||
.panel-h h2{font-family:var(--display);font-weight:700;font-size:.85rem;margin:0;letter-spacing:.02em;text-transform:uppercase}
|
||||
.btn{font-family:var(--mono);font-weight:700;font-size:.78rem;padding:.4rem .8rem;border-radius:3px;background:transparent;color:var(--accent);border:1px solid var(--accent);cursor:pointer}
|
||||
.btn-ghost{color:var(--muted);border-color:var(--line)}
|
||||
.badge{font-family:var(--mono);font-size:.64rem;padding:.08rem .4rem;border-radius:2px}
|
||||
.b-tx,.b-amb{color:var(--amber);background:var(--amber-soft)}
|
||||
.b-rx,.b-acc{color:var(--accent);background:var(--accent-soft)}
|
||||
.chip{display:inline-flex;align-items:center;gap:.35rem;font-family:var(--mono);font-size:.68rem;color:var(--muted);border:1px solid var(--line);border-radius:3px;padding:.16rem .5rem;background:var(--panel-2)}
|
||||
.chip b{color:var(--accent);font-weight:700}
|
||||
|
||||
/* PAGE HEAD */
|
||||
.page-head{display:flex;align-items:center;flex-wrap:wrap;gap:.6rem;margin-bottom:.9rem}
|
||||
.page-title{font-family:var(--display);font-weight:800;font-size:1.3rem;letter-spacing:.02em;margin:0;text-transform:lowercase}
|
||||
.page-title::before{content:"/";color:var(--muted);opacity:.5}
|
||||
.page-head .spacer{flex:1}
|
||||
|
||||
/* FORMS / FILTERS */
|
||||
.filters{border:1px solid var(--line);border-radius:var(--radius);background:var(--panel);padding:.8rem 1rem;margin-bottom:.9rem;display:grid;gap:.6rem;grid-template-columns:1fr;align-items:end}
|
||||
@media(min-width:760px){.filters{grid-template-columns:2fr 1fr 1fr auto auto}}
|
||||
.field{display:flex;flex-direction:column;gap:.25rem}
|
||||
.field label{font-family:var(--mono);font-size:.62rem;letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}
|
||||
input,select{font-family:var(--mono);font-size:.82rem;background:var(--panel-2);color:var(--text);border:1px solid var(--line);border-radius:3px;padding:.45rem .6rem;width:100%}
|
||||
input:focus,select:focus{outline:2px solid var(--accent);outline-offset:1px}
|
||||
|
||||
/* TABLE (log style) */
|
||||
.tbl-mobile-sort{display:block;margin-bottom:.7rem}
|
||||
@media(min-width:760px){.tbl-mobile-sort{display:none}}
|
||||
table{width:100%;border-collapse:collapse;font-size:.82rem;font-family:var(--mono)}
|
||||
th{text-align:left;font-size:.62rem;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);font-weight:500;padding:.45rem 1rem;white-space:nowrap}
|
||||
th.sort{cursor:pointer}
|
||||
th.sort .caret{opacity:.55;margin-left:.25rem}
|
||||
th.sort.asc .caret::after{content:"▲"} th.sort.desc .caret::after{content:"▼"}
|
||||
td{padding:.42rem 1rem;border-top:1px solid var(--line)}
|
||||
tbody tr:hover td{background:var(--panel-2)}
|
||||
.key{color:var(--muted)}
|
||||
.node{display:flex;align-items:center;gap:.5rem}.node .em{font-size:1rem}.node .nm{font-family:var(--body);font-weight:600}
|
||||
.time{color:var(--muted);font-size:.8rem}
|
||||
.observers{display:inline-flex;align-items:center;gap:.3rem;font-size:.72rem;color:var(--muted)}
|
||||
.table-scroll{overflow-x:auto}
|
||||
.desktop-only{display:none}
|
||||
@media(min-width:760px){.desktop-only{display:block}.mobile-only{display:none}}
|
||||
|
||||
/* MOBILE CARDS */
|
||||
.cards{display:flex;flex-direction:column;gap:.45rem}
|
||||
.ncard{border:1px solid var(--line);border-radius:var(--radius);background:var(--panel);padding:.65rem .8rem;display:flex;align-items:center;gap:.6rem}
|
||||
.ncard .meta{flex:1;min-width:0}.ncard .nm{font-weight:600}
|
||||
.ncard .sub{font-family:var(--mono);font-size:.72rem;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
||||
|
||||
/* PAGINATION */
|
||||
.pager{display:flex;justify-content:center;gap:.25rem;padding:.9rem;flex-wrap:wrap}
|
||||
.pager a{min-width:32px;text-align:center;padding:.3rem .55rem;border:1px solid var(--line);border-radius:3px;font-family:var(--mono);font-size:.8rem;color:var(--muted)}
|
||||
.pager a.cur{background:var(--accent-soft);color:var(--accent);border-color:transparent}
|
||||
|
||||
/* MAP — fine grid */
|
||||
.map-layout{display:grid;gap:.9rem;grid-template-columns:1fr}
|
||||
@media(min-width:900px){.map-layout{grid-template-columns:250px 1fr}}
|
||||
.mapwrap{position:relative;border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;min-height:460px;background:var(--map-bg);
|
||||
background-image:
|
||||
linear-gradient(color-mix(in oklab,var(--line) 55%,transparent) 1px,transparent 1px),
|
||||
linear-gradient(90deg,color-mix(in oklab,var(--line) 55%,transparent) 1px,transparent 1px);
|
||||
background-size:28px 28px}
|
||||
.pin{position:absolute;width:14px;height:14px;border-radius:2px;transform:translate(-50%,-50%);border:2px solid var(--bg);box-shadow:0 0 0 2px currentColor,0 0 8px currentColor}
|
||||
.pin.infra{background:var(--infra);color:var(--infra)}
|
||||
.pin.public{background:var(--public);color:var(--public)}
|
||||
.pin .plabel{position:absolute;left:12px;top:-3px;white-space:nowrap;font-family:var(--mono);font-size:.66rem;
|
||||
color:var(--text);background:color-mix(in oklab,var(--panel) 88%,transparent);padding:.05rem .35rem;border-radius:2px;border:1px solid var(--line)}
|
||||
.popup{position:absolute;width:210px;border:1px solid var(--line);border-radius:var(--radius);background:var(--panel);box-shadow:0 8px 24px #000a;padding:.6rem .7rem;font-size:.78rem;font-family:var(--mono)}
|
||||
.popup h3{font-family:var(--display);font-size:.85rem;margin:0 0 .3rem;display:flex;align-items:center;gap:.4rem;text-transform:uppercase}
|
||||
.popup .row{display:flex;justify-content:space-between;color:var(--muted);font-size:.72rem;margin:.12rem 0}
|
||||
.popup a{color:var(--accent);font-weight:700;font-size:.76rem}
|
||||
.legend{position:absolute;left:12px;bottom:12px;background:color-mix(in oklab,var(--panel) 92%,transparent);border:1px solid var(--line);border-radius:3px;padding:.5rem .7rem;font-size:.72rem;font-family:var(--mono)}
|
||||
.legend .li{display:flex;align-items:center;gap:.45rem;margin:.15rem 0;color:var(--muted)}
|
||||
.legend .sw{width:10px;height:10px;border-radius:2px}
|
||||
|
||||
.foot{max-width:1100px;margin:1.8rem auto 0;padding:1.4rem 1rem;color:var(--muted);font-family:var(--mono);font-size:.74rem;border-top:1px solid var(--line);text-align:center}
|
||||
.note{position:fixed;left:50%;transform:translateX(-50%);bottom:14px;font-family:var(--mono);font-size:.68rem;color:var(--muted);background:var(--panel);border:1px solid var(--line);padding:.3rem .7rem;border-radius:99px;z-index:30}
|
||||
@@ -0,0 +1,140 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Packet Monitor — MeshCore Hub mockup</title>
|
||||
<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=JetBrains+Mono:wght@400;500;700;800&family=IBM+Plex+Sans:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root{
|
||||
--display:"JetBrains Mono",ui-monospace,monospace;
|
||||
--body:"IBM Plex Sans",system-ui,sans-serif;
|
||||
--mono:"JetBrains Mono",ui-monospace,monospace;
|
||||
--radius:3px;
|
||||
}
|
||||
/* DARK — near-black console */
|
||||
[data-theme="dark"]{
|
||||
--bg:#07090B; --panel:#0E1316; --panel-2:#141B1F; --line:#1A2129;
|
||||
--text:#D7E0E2; --muted:#6F8088;
|
||||
--accent:#5FB874; --accent-soft:#5FB87422;
|
||||
--amber:#D39E2A; --amber-soft:#D39E2A1c;
|
||||
}
|
||||
/* LIGHT — clean light terminal */
|
||||
[data-theme="light"]{
|
||||
--bg:#F6F8F8; --panel:#FFFFFF; --panel-2:#F0F3F3; --line:#D7DEDE;
|
||||
--text:#0C1416; --muted:#5A696D;
|
||||
--accent:#2F8F4E; --accent-soft:#2F8F4E14;
|
||||
--amber:#9A7415; --amber-soft:#9A741514;
|
||||
}
|
||||
*{box-sizing:border-box}
|
||||
body{margin:0;font-family:var(--body);background:var(--bg);color:var(--text);
|
||||
-webkit-font-smoothing:antialiased;line-height:1.5}
|
||||
a{color:inherit;text-decoration:none}.mono{font-family:var(--mono)}
|
||||
|
||||
/* NAV */
|
||||
.nav{position:sticky;top:0;z-index:20;display:flex;align-items:center;gap:.5rem;
|
||||
padding:.55rem 1rem;background:var(--panel);border-bottom:1px solid var(--line)}
|
||||
.brand{display:flex;align-items:center;gap:.5rem;font-family:var(--display);font-weight:800;
|
||||
font-size:.95rem;letter-spacing:.02em}
|
||||
.brand .logo{height:20px;width:auto;color:var(--text)}
|
||||
.brand .logo .arcs{stroke:var(--accent)}
|
||||
.hero .logo-lg{height:40px;width:auto;color:var(--text);display:block;margin-bottom:.5rem}
|
||||
.hero .logo-lg .arcs{stroke:var(--accent)}
|
||||
.navlinks{display:none;margin-left:1rem;gap:0}
|
||||
.navlinks a{font-family:var(--mono);font-size:.8rem;padding:.3rem .55rem;color:var(--muted)}
|
||||
.navlinks a::before{content:"/";opacity:.4;margin-right:1px}
|
||||
.navlinks a:hover{color:var(--text);background:var(--accent-soft)}
|
||||
.navlinks a.active{color:var(--accent)}
|
||||
.spacer{flex:1}
|
||||
.iconbtn{width:32px;height:32px;border-radius:3px;border:1px solid var(--line);
|
||||
background:var(--panel-2);color:var(--text);display:grid;place-items:center;cursor:pointer}
|
||||
@media(min-width:1000px){.navlinks{display:flex}.burger{display:none}}
|
||||
|
||||
.wrap{max-width:1100px;margin:0 auto;padding:1.1rem 1rem 3rem}
|
||||
|
||||
/* HERO — status header line */
|
||||
.hero{border:1px solid var(--line);border-radius:var(--radius);background:var(--panel);
|
||||
padding:1.1rem 1.2rem;margin-bottom:1rem}
|
||||
.hero .l{font-family:var(--mono);font-size:.72rem;color:var(--muted);letter-spacing:.04em}
|
||||
.hero h1{font-family:var(--display);font-weight:800;font-size:clamp(1.4rem,5vw,2rem);
|
||||
margin:.25rem 0;letter-spacing:.01em}
|
||||
.hero .st{color:var(--accent)}
|
||||
.bar{display:flex;flex-wrap:wrap;gap:1.2rem;margin-top:.6rem;font-family:var(--mono);font-size:.8rem}
|
||||
.bar b{color:var(--accent);font-weight:700}.bar span{color:var(--muted)}
|
||||
|
||||
/* STATS */
|
||||
.grid{display:grid;gap:.5rem;grid-template-columns:repeat(2,1fr);margin-bottom:1rem}
|
||||
@media(min-width:760px){.grid{grid-template-columns:repeat(4,1fr)}}
|
||||
.stat{border:1px solid var(--line);border-radius:var(--radius);background:var(--panel);padding:.7rem .85rem}
|
||||
.stat .label{font-family:var(--mono);font-size:.64rem;letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}
|
||||
.stat .val{font-family:var(--display);font-weight:800;font-size:1.7rem;line-height:1.1;margin-top:.15rem}
|
||||
.stat .val .u{font-size:.8rem;color:var(--muted)}
|
||||
|
||||
/* PANEL + TABLE (log style) */
|
||||
.panel{border:1px solid var(--line);border-radius:var(--radius);background:var(--panel);overflow:hidden}
|
||||
.panel-h{display:flex;align-items:center;justify-content:space-between;padding:.65rem 1rem;border-bottom:1px solid var(--line)}
|
||||
.panel-h h2{font-family:var(--display);font-weight:700;font-size:.85rem;margin:0;letter-spacing:.02em;text-transform:uppercase}
|
||||
table{width:100%;border-collapse:collapse;font-size:.82rem;font-family:var(--mono)}
|
||||
th{text-align:left;font-size:.62rem;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);font-weight:500;padding:.45rem 1rem}
|
||||
td{padding:.42rem 1rem;border-top:1px solid var(--line)}
|
||||
tr:hover td{background:var(--panel-2)}
|
||||
.key{color:var(--muted)}
|
||||
.badge{font-family:var(--mono);font-size:.64rem;padding:.08rem .4rem;border-radius:2px}
|
||||
.b-acc{color:var(--accent);background:var(--accent-soft)}
|
||||
.b-amb{color:var(--amber);background:var(--amber-soft)}
|
||||
.btn{font-family:var(--mono);font-weight:700;font-size:.78rem;padding:.4rem .8rem;border-radius:3px;
|
||||
background:transparent;color:var(--accent);border:1px solid var(--accent);cursor:pointer}
|
||||
.node{display:flex;align-items:center;gap:.5rem}.node .em{font-size:1rem}
|
||||
.node .nm{font-family:var(--body);font-weight:600}
|
||||
.foot{max-width:1100px;margin:1.8rem auto 0;padding:1.4rem 1rem;color:var(--muted);
|
||||
font-family:var(--mono);font-size:.74rem;border-top:1px solid var(--line);text-align:center}
|
||||
.note{position:fixed;left:50%;transform:translateX(-50%);bottom:14px;font-family:var(--mono);font-size:.68rem;color:var(--muted);background:var(--panel);border:1px solid var(--line);padding:.3rem .7rem;border-radius:99px}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="nav">
|
||||
<div class="brand"><svg class="logo" viewBox="0 0 115 100" aria-hidden="true"><rect x="0" y="0" width="25" height="100" rx="2" fill="currentColor" opacity=".5"/><rect x="35" y="0" width="25" height="100" rx="2" fill="currentColor"/><g class="arcs" fill="none" stroke="currentColor" stroke-width="10" stroke-linecap="round" transform="translate(-30,-10)"><path d="M 110,65 A 20,20 0 0 0 90,45"/><path d="M 125,65 A 35,35 0 0 0 90,30"/><path d="M 140,65 A 50,50 0 0 0 90,15"/></g></svg>IPNET</div>
|
||||
<div class="navlinks">
|
||||
<a class="active">dashboard</a><a href="packet-monitor-list.html">nodes</a><a href="packet-monitor-list.html">adverts</a><a>messages</a><a>packets</a><a href="packet-monitor-map.html">map</a><a>members</a>
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
<button class="iconbtn" onclick="toggleTheme()" title="Toggle light/dark">◐</button>
|
||||
<button class="iconbtn burger">≡</button>
|
||||
</nav>
|
||||
|
||||
<div class="wrap">
|
||||
<section class="hero">
|
||||
<svg class="logo-lg" viewBox="0 0 115 100" aria-hidden="true"><rect x="0" y="0" width="25" height="100" rx="2" fill="currentColor" opacity=".5"/><rect x="35" y="0" width="25" height="100" rx="2" fill="currentColor"/><g class="arcs" fill="none" stroke="currentColor" stroke-width="10" stroke-linecap="round" transform="translate(-30,-10)"><path d="M 110,65 A 20,20 0 0 0 90,45"/><path d="M 125,65 A 35,35 0 0 0 90,30"/><path d="M 140,65 A 50,50 0 0 0 90,15"/></g></svg>
|
||||
<div class="l">// network</div>
|
||||
<h1>IPNET <span class="st">● ONLINE</span></h1>
|
||||
<div class="bar"><span>nodes</span> <b>42</b> <span>adv/7d</span> <b>1284</b> <span>msg/7d</span> <b>318</b> <span>channels</span> <b>6</b> <span>reach</span> <b>31km</b></div>
|
||||
</section>
|
||||
|
||||
<div class="grid">
|
||||
<div class="stat"><div class="label">nodes</div><div class="val">42</div></div>
|
||||
<div class="stat"><div class="label">adverts · 7d</div><div class="val">1284</div></div>
|
||||
<div class="stat"><div class="label">messages · 7d</div><div class="val">318</div></div>
|
||||
<div class="stat"><div class="label">max reach</div><div class="val">31<span class="u">km</span></div></div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-h"><h2>recent_nodes</h2><button class="btn">view_all</button></div>
|
||||
<table>
|
||||
<thead><tr><th>node</th><th>pubkey</th><th>route</th><th>last_heard</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td><div class="node"><span class="em">📡</span><span class="nm">Holbeck-Relay</span></div></td><td class="key">a1f3…9c2e</td><td><span class="badge b-acc">DIRECT</span></td><td class="key">2m</td></tr>
|
||||
<tr><td><div class="node"><span class="em">💬</span><span class="nm">kestrel</span></div></td><td class="key">7b02…4d1a</td><td><span class="badge b-amb">FLOOD</span></td><td class="key">11m</td></tr>
|
||||
<tr><td><div class="node"><span class="em">🪧</span><span class="nm">Briggate-Room</span></div></td><td class="key">e5c9…aa70</td><td><span class="badge b-amb">FLOOD</span></td><td class="key">38m</td></tr>
|
||||
<tr><td><div class="node"><span class="em">📡</span><span class="nm">Armley-Hill</span></div></td><td class="key">3d8e…1f55</td><td><span class="badge b-acc">DIRECT</span></td><td class="key">1h</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="foot">IPNET · 53.80N 1.55W — powered by MeshCore Hub</footer>
|
||||
<div class="note">Packet Monitor · click ◐ to toggle light/dark · resize to portrait to test mobile</div>
|
||||
<script>function toggleTheme(){const h=document.documentElement;h.dataset.theme=h.dataset.theme==='dark'?'light':'dark'}</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,78 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Topographic — Advertisements</title>
|
||||
<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=Archivo:wght@600;700;800&family=Archivo+Narrow:wght@500;600;700&family=Inter:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="topographic.css">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="nav">
|
||||
<a href="topographic.html" class="brand"><svg class="logo" viewBox="0 0 115 100" aria-hidden="true"><rect x="0" y="0" width="25" height="100" rx="2" fill="currentColor" opacity=".5"/><rect x="35" y="0" width="25" height="100" rx="2" fill="currentColor"/><g class="arcs" fill="none" stroke="currentColor" stroke-width="10" stroke-linecap="round" transform="translate(-30,-10)"><path d="M 110,65 A 20,20 0 0 0 90,45"/><path d="M 125,65 A 35,35 0 0 0 90,30"/><path d="M 140,65 A 50,50 0 0 0 90,15"/></g></svg>IPNet</a>
|
||||
<div class="navlinks">
|
||||
<a href="topographic.html">Dashboard</a><a href="topographic-list.html">Nodes</a><a class="active">Adverts</a><a>Messages</a><a>Packets</a><a href="topographic-map.html">Map</a><a>Members</a>
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
<button class="iconbtn" onclick="toggleTheme()" title="Toggle light/dark">◐</button>
|
||||
<button class="iconbtn burger">≡</button>
|
||||
</nav>
|
||||
|
||||
<div class="wrap">
|
||||
<div class="page-head">
|
||||
<h1 class="page-title">Advertisements</h1>
|
||||
<span class="chip">UTC+1</span>
|
||||
<span class="chip"><b>1,284</b> total</span>
|
||||
<div class="spacer"></div>
|
||||
<button class="iconbtn" title="Auto-refresh: on">⏸</button>
|
||||
</div>
|
||||
|
||||
<form class="filters" onsubmit="return false">
|
||||
<div class="field"><label>Search</label><input placeholder="Node name or public key…"></div>
|
||||
<div class="field"><label>Route type</label><select><option>All routes</option><option>Flood</option><option>Direct relay</option></select></div>
|
||||
<div class="field"><label>Operator</label><select><option>Any operator</option><option>kestrel</option><option>g7xyz</option></select></div>
|
||||
<button class="btn">Apply</button>
|
||||
<button class="btn btn-ghost">Clear</button>
|
||||
</form>
|
||||
|
||||
<div class="tbl-mobile-sort field"><label>Sort by</label><select><option>Newest first</option><option>Oldest first</option><option>Node name</option></select></div>
|
||||
|
||||
<div class="panel desktop-only">
|
||||
<div class="table-scroll">
|
||||
<table>
|
||||
<thead><tr>
|
||||
<th class="sort">Node <span class="caret"></span></th>
|
||||
<th>Public key</th>
|
||||
<th class="sort">Route <span class="caret"></span></th>
|
||||
<th class="sort desc">Time <span class="caret"></span></th>
|
||||
<th>Observers</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
<tr><td><div class="node"><span class="em">📡</span><span class="nm">Holbeck-Relay</span></div></td><td class="key">a1f3…9c2e</td><td><span class="badge b-grn">DIRECT</span></td><td class="time">2m ago</td><td><span class="observers">📡 4</span></td></tr>
|
||||
<tr><td><div class="node"><span class="em">💬</span><span class="nm">kestrel</span></div></td><td class="key">7b02…4d1a</td><td><span class="badge b-acc">FLOOD</span></td><td class="time">6m ago</td><td><span class="observers">📡 7</span></td></tr>
|
||||
<tr><td><div class="node"><span class="em">🪧</span><span class="nm">Briggate-Room</span></div></td><td class="key">e5c9…aa70</td><td><span class="badge b-acc">FLOOD</span></td><td class="time">11m ago</td><td><span class="observers">📡 3</span></td></tr>
|
||||
<tr><td><div class="node"><span class="em">📡</span><span class="nm">Armley-Hill</span></div></td><td class="key">3d8e…1f55</td><td><span class="badge b-grn">DIRECT</span></td><td class="time">18m ago</td><td><span class="observers">📡 5</span></td></tr>
|
||||
<tr><td><div class="node"><span class="em">📱</span><span class="nm">g7xyz-companion</span></div></td><td class="key">b240…77c1</td><td><span class="badge b-acc">FLOOD</span></td><td class="time">24m ago</td><td><span class="observers">📡 2</span></td></tr>
|
||||
<tr><td><div class="node"><span class="em">📡</span><span class="nm">Hyde-Park-Hub</span></div></td><td class="key">9f10…3b8d</td><td><span class="badge b-grn">DIRECT</span></td><td class="time">31m ago</td><td><span class="observers">📡 6</span></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="pager"><a>‹</a><a class="cur">1</a><a>2</a><a>3</a><a>…</a><a>9</a><a>›</a></div>
|
||||
</div>
|
||||
|
||||
<div class="cards mobile-only">
|
||||
<div class="ncard"><span class="em">📡</span><div class="meta"><div class="nm">Holbeck-Relay</div><div class="sub">a1f3…9c2e · 📡 4</div></div><span class="badge b-grn">DIRECT</span></div>
|
||||
<div class="ncard"><span class="em">💬</span><div class="meta"><div class="nm">kestrel</div><div class="sub">7b02…4d1a · 📡 7</div></div><span class="badge b-acc">FLOOD</span></div>
|
||||
<div class="ncard"><span class="em">🪧</span><div class="meta"><div class="nm">Briggate-Room</div><div class="sub">e5c9…aa70 · 📡 3</div></div><span class="badge b-acc">FLOOD</span></div>
|
||||
<div class="ncard"><span class="em">📡</span><div class="meta"><div class="nm">Armley-Hill</div><div class="sub">3d8e…1f55 · 📡 5</div></div><span class="badge b-grn">DIRECT</span></div>
|
||||
<div class="pager"><a>‹</a><a class="cur">1</a><a>2</a><a>3</a><a>›</a></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="foot">IPNET · 53.80°N 1.55°W — powered by MeshCore Hub</footer>
|
||||
<div class="note">Topographic · List view · ◐ toggle theme · resize narrow for mobile cards</div>
|
||||
<script>function toggleTheme(){const h=document.documentElement;h.dataset.theme=h.dataset.theme==='dark'?'light':'dark'}</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,68 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Topographic — Map</title>
|
||||
<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=Archivo:wght@600;700;800&family=Archivo+Narrow:wght@500;600;700&family=Inter:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="topographic.css">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="nav">
|
||||
<a href="topographic.html" class="brand"><svg class="logo" viewBox="0 0 115 100" aria-hidden="true"><rect x="0" y="0" width="25" height="100" rx="2" fill="currentColor" opacity=".5"/><rect x="35" y="0" width="25" height="100" rx="2" fill="currentColor"/><g class="arcs" fill="none" stroke="currentColor" stroke-width="10" stroke-linecap="round" transform="translate(-30,-10)"><path d="M 110,65 A 20,20 0 0 0 90,45"/><path d="M 125,65 A 35,35 0 0 0 90,30"/><path d="M 140,65 A 50,50 0 0 0 90,15"/></g></svg>IPNet</a>
|
||||
<div class="navlinks">
|
||||
<a href="topographic.html">Dashboard</a><a href="topographic-list.html">Nodes</a><a href="topographic-list.html">Adverts</a><a>Messages</a><a>Packets</a><a class="active">Map</a><a>Members</a>
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
<button class="iconbtn" onclick="toggleTheme()" title="Toggle light/dark">◐</button>
|
||||
<button class="iconbtn burger">≡</button>
|
||||
</nav>
|
||||
|
||||
<div class="wrap">
|
||||
<div class="page-head">
|
||||
<h1 class="page-title">Map</h1>
|
||||
<span class="chip"><b>42</b> nodes</span>
|
||||
<span class="chip"><b>18</b> shown</span>
|
||||
<span class="chip">UTC+1</span>
|
||||
</div>
|
||||
|
||||
<div class="map-layout">
|
||||
<form class="filters" style="grid-template-columns:1fr;align-content:start" onsubmit="return false">
|
||||
<div class="field"><label>Show</label><select><option>All nodes</option><option>Infrastructure only</option></select></div>
|
||||
<div class="field"><label>Node type</label><select><option>All types</option><option>Chat</option><option>Repeater</option><option>Room</option></select></div>
|
||||
<div class="field"><label>Operator</label><select><option>Any operator</option><option>kestrel</option></select></div>
|
||||
<label style="display:flex;gap:.5rem;align-items:center;font-size:.82rem"><input type="checkbox" checked style="width:auto"> Show labels</label>
|
||||
<button class="btn btn-ghost">Clear filters</button>
|
||||
</form>
|
||||
|
||||
<div class="mapwrap">
|
||||
<div class="pin infra" style="left:32%;top:44%"><span class="plabel">Holbeck-Relay · 2m</span></div>
|
||||
<div class="pin public" style="left:54%;top:30%"><span class="plabel">kestrel · 6m</span></div>
|
||||
<div class="pin public" style="left:68%;top:56%"><span class="plabel">Briggate-Room · 11m</span></div>
|
||||
<div class="pin infra" style="left:46%;top:66%"><span class="plabel">Armley-Hill · 18m</span></div>
|
||||
<div class="pin public" style="left:76%;top:40%"><span class="plabel">g7xyz · 24m</span></div>
|
||||
|
||||
<div class="popup" style="left:34%;top:48%">
|
||||
<h3>📡 Holbeck-Relay</h3>
|
||||
<div class="row"><span>Type</span><span>Repeater</span></div>
|
||||
<div class="row"><span>Owner</span><span>kestrel</span></div>
|
||||
<div class="row"><span>Key</span><span>a1f3…9c2e</span></div>
|
||||
<div class="row"><span>Last seen</span><span>2m ago</span></div>
|
||||
<a href="#">View details →</a>
|
||||
</div>
|
||||
|
||||
<div class="legend">
|
||||
<div class="li"><span class="sw" style="background:var(--infra)"></span> Infrastructure</div>
|
||||
<div class="li"><span class="sw" style="background:var(--public)"></span> Public node</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="foot">IPNET · 53.80°N 1.55°W — powered by MeshCore Hub</footer>
|
||||
<div class="note">Topographic · Map view · ◐ toggle theme · resize narrow to stack filters</div>
|
||||
<script>function toggleTheme(){const h=document.documentElement;h.dataset.theme=h.dataset.theme==='dark'?'light':'dark'}</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,117 @@
|
||||
/* ===== Topographic — shared theme stylesheet (mockup) ===== */
|
||||
:root{
|
||||
--display:"Archivo Narrow","Archivo",system-ui,sans-serif;
|
||||
--body:"Inter",system-ui,sans-serif;
|
||||
--mono:"IBM Plex Mono",ui-monospace,monospace;
|
||||
--radius:8px;
|
||||
}
|
||||
[data-theme="dark"]{
|
||||
--bg:#14180F; --panel:#1C2116; --panel-2:#232A1C; --line:#2E3722;
|
||||
--text:#ECE8DA; --muted:#9AA284;
|
||||
--accent:#D8642A; --accent-soft:#D8642A24;
|
||||
--green:#92AE5E; --green-soft:#92AE5E22;
|
||||
--infra:#D8642A; --public:#92AE5E;
|
||||
--map-bg:#171C11;
|
||||
}
|
||||
[data-theme="light"]{
|
||||
--bg:#F5F1E6; --panel:#FFFDF7; --panel-2:#F0EAD8; --line:#DED7C3;
|
||||
--text:#23271A; --muted:#6E7257;
|
||||
--accent:#B14A1B; --accent-soft:#B14A1B16;
|
||||
--green:#5F7741; --green-soft:#5F774114;
|
||||
--infra:#B14A1B; --public:#5F7741;
|
||||
--map-bg:#EFE9D8;
|
||||
}
|
||||
*{box-sizing:border-box}
|
||||
body{margin:0;font-family:var(--body);background:var(--bg);color:var(--text);-webkit-font-smoothing:antialiased;line-height:1.5}
|
||||
a{color:inherit;text-decoration:none}
|
||||
.mono{font-family:var(--mono)}
|
||||
.wrap{max-width:1080px;margin:0 auto;padding:1.25rem 1rem 3rem}
|
||||
|
||||
/* NAV */
|
||||
.nav{position:sticky;top:0;z-index:20;display:flex;align-items:center;gap:.5rem;padding:.6rem 1rem;
|
||||
background:color-mix(in oklab,var(--panel) 90%,transparent);backdrop-filter:blur(8px);border-bottom:1px solid var(--line)}
|
||||
.brand{display:flex;align-items:center;gap:.55rem;font-family:var(--display);font-weight:700;letter-spacing:.02em;font-size:1.15rem;text-transform:uppercase}
|
||||
.brand .logo{height:22px;width:auto;color:var(--text)}
|
||||
.brand .logo .arcs{stroke:var(--accent)}
|
||||
.navlinks{display:none;margin-left:1rem;gap:.1rem}
|
||||
.navlinks a{font-family:var(--display);font-size:.95rem;padding:.3rem .55rem;border-radius:6px;color:var(--muted);text-transform:uppercase;letter-spacing:.02em}
|
||||
.navlinks a:hover{background:var(--accent-soft);color:var(--text)}
|
||||
.navlinks a.active{color:var(--text);background:var(--accent-soft)}
|
||||
.spacer{flex:1}
|
||||
.iconbtn{width:34px;height:34px;border-radius:7px;border:1px solid var(--line);background:var(--panel-2);color:var(--text);display:grid;place-items:center;cursor:pointer}
|
||||
@media(min-width:1000px){.navlinks{display:flex}.burger{display:none}}
|
||||
|
||||
/* PANEL + buttons + badges */
|
||||
.panel{border:1px solid var(--line);border-radius:var(--radius);background:var(--panel);overflow:hidden}
|
||||
.panel-h{display:flex;align-items:center;justify-content:space-between;padding:.8rem 1rem;border-bottom:1px solid var(--line)}
|
||||
.panel-h h2{font-family:var(--display);font-weight:700;text-transform:uppercase;font-size:1.05rem;margin:0;letter-spacing:.02em}
|
||||
.btn{font-family:var(--display);text-transform:uppercase;font-weight:700;letter-spacing:.03em;font-size:.82rem;padding:.45rem .9rem;border-radius:6px;background:var(--accent);color:#fff;border:none;cursor:pointer}
|
||||
.btn-ghost{background:transparent;color:var(--muted);border:1px solid var(--line)}
|
||||
.badge{font-family:var(--mono);font-size:.64rem;letter-spacing:.04em;padding:.12rem .5rem;border-radius:4px}
|
||||
.b-tx,.b-acc{color:var(--accent);background:var(--accent-soft)}
|
||||
.b-rx,.b-grn{color:var(--green);background:var(--green-soft)}
|
||||
.chip{display:inline-flex;align-items:center;gap:.35rem;font-family:var(--mono);font-size:.68rem;color:var(--muted);border:1px solid var(--line);border-radius:99px;padding:.18rem .55rem;background:var(--panel-2)}
|
||||
.chip b{color:var(--text);font-weight:600}
|
||||
|
||||
/* PAGE HEAD */
|
||||
.page-head{display:flex;align-items:center;flex-wrap:wrap;gap:.6rem;margin-bottom:1rem}
|
||||
.page-title{font-family:var(--display);font-weight:700;text-transform:uppercase;font-size:1.7rem;letter-spacing:.02em;margin:0}
|
||||
.page-head .spacer{flex:1}
|
||||
|
||||
/* FORMS / FILTERS */
|
||||
.filters{border:1px solid var(--line);border-radius:var(--radius);background:var(--panel);padding:.9rem 1rem;margin-bottom:1rem;display:grid;gap:.7rem;grid-template-columns:1fr;align-items:end}
|
||||
@media(min-width:760px){.filters{grid-template-columns:2fr 1fr 1fr auto auto}}
|
||||
.field{display:flex;flex-direction:column;gap:.25rem}
|
||||
.field label{font-family:var(--mono);font-size:.62rem;letter-spacing:.1em;text-transform:uppercase;color:var(--muted)}
|
||||
input,select{font-family:var(--body);font-size:.85rem;background:var(--panel-2);color:var(--text);border:1px solid var(--line);border-radius:6px;padding:.5rem .6rem;width:100%}
|
||||
input:focus,select:focus{outline:2px solid var(--accent);outline-offset:1px}
|
||||
|
||||
/* TABLE */
|
||||
.tbl-mobile-sort{display:block;margin-bottom:.75rem}
|
||||
@media(min-width:760px){.tbl-mobile-sort{display:none}}
|
||||
table{width:100%;border-collapse:collapse;font-size:.85rem}
|
||||
th{text-align:left;font-family:var(--mono);font-size:.64rem;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);font-weight:500;padding:.55rem 1rem;white-space:nowrap}
|
||||
th.sort{cursor:pointer}
|
||||
th.sort .caret{opacity:.55;margin-left:.25rem}
|
||||
th.sort.asc .caret::after{content:"▲"} th.sort.desc .caret::after{content:"▼"}
|
||||
td{padding:.6rem 1rem;border-top:1px solid var(--line)}
|
||||
tbody tr:hover td{background:var(--panel-2)}
|
||||
.key{font-family:var(--mono);font-size:.78rem;color:var(--muted)}
|
||||
.node{display:flex;align-items:center;gap:.6rem}.node .em{font-size:1.1rem}.node .nm{font-weight:600}
|
||||
.time{font-family:var(--mono);color:var(--muted);font-size:.8rem}
|
||||
.observers{display:inline-flex;align-items:center;gap:.3rem;font-family:var(--mono);font-size:.72rem;color:var(--muted)}
|
||||
.table-scroll{overflow-x:auto}
|
||||
.desktop-only{display:none}
|
||||
@media(min-width:760px){.desktop-only{display:block}.mobile-only{display:none}}
|
||||
|
||||
/* MOBILE CARDS */
|
||||
.cards{display:flex;flex-direction:column;gap:.55rem}
|
||||
.ncard{border:1px solid var(--line);border-radius:var(--radius);background:var(--panel);padding:.75rem .85rem;display:flex;align-items:center;gap:.7rem}
|
||||
.ncard .meta{flex:1;min-width:0}.ncard .nm{font-weight:600}
|
||||
.ncard .sub{font-family:var(--mono);font-size:.72rem;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
||||
|
||||
/* PAGINATION */
|
||||
.pager{display:flex;justify-content:center;gap:.25rem;padding:1rem;flex-wrap:wrap}
|
||||
.pager a{min-width:34px;text-align:center;padding:.35rem .6rem;border:1px solid var(--line);border-radius:6px;font-family:var(--mono);font-size:.8rem;color:var(--muted)}
|
||||
.pager a.cur{background:var(--accent-soft);color:var(--text);border-color:transparent}
|
||||
|
||||
/* MAP — concentric contour background */
|
||||
.map-layout{display:grid;gap:1rem;grid-template-columns:1fr}
|
||||
@media(min-width:900px){.map-layout{grid-template-columns:260px 1fr}}
|
||||
.mapwrap{position:relative;border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;min-height:460px;background:var(--map-bg);
|
||||
background-image:repeating-radial-gradient(circle at 32% 44%,transparent 0 32px,color-mix(in oklab,var(--line) 70%,transparent) 32px 33px);}
|
||||
.pin{position:absolute;width:14px;height:14px;border-radius:50% 50% 50% 0;transform:translate(-50%,-100%) rotate(-45deg);border:2px solid var(--bg);box-shadow:0 0 0 2px currentColor}
|
||||
.pin.infra{background:var(--infra);color:var(--infra)}
|
||||
.pin.public{background:var(--public);color:var(--public)}
|
||||
.pin .plabel{position:absolute;left:10px;top:-8px;white-space:nowrap;font-family:var(--mono);font-size:.66rem;transform:rotate(45deg);transform-origin:left;
|
||||
color:var(--text);background:color-mix(in oklab,var(--panel) 88%,transparent);padding:.05rem .35rem;border-radius:4px;border:1px solid var(--line)}
|
||||
.popup{position:absolute;width:210px;border:1px solid var(--line);border-radius:var(--radius);background:var(--panel);box-shadow:0 8px 24px #0006;padding:.7rem .8rem;font-size:.8rem}
|
||||
.popup h3{font-family:var(--display);text-transform:uppercase;font-size:1rem;margin:0 0 .3rem;display:flex;align-items:center;gap:.4rem}
|
||||
.popup .row{display:flex;justify-content:space-between;color:var(--muted);font-family:var(--mono);font-size:.72rem;margin:.12rem 0}
|
||||
.popup a{color:var(--accent);font-weight:600;font-size:.78rem}
|
||||
.legend{position:absolute;left:12px;bottom:12px;background:color-mix(in oklab,var(--panel) 90%,transparent);border:1px solid var(--line);border-radius:8px;padding:.5rem .7rem;font-size:.74rem}
|
||||
.legend .li{display:flex;align-items:center;gap:.45rem;margin:.15rem 0;color:var(--muted)}
|
||||
.legend .sw{width:11px;height:11px;border-radius:50%}
|
||||
|
||||
.foot{max-width:1080px;margin:2rem auto 0;padding:1.5rem 1rem;color:var(--muted);font-size:.78rem;border-top:1px solid var(--line);text-align:center}
|
||||
.note{position:fixed;left:50%;transform:translateX(-50%);bottom:14px;font-family:var(--mono);font-size:.68rem;color:var(--muted);background:var(--panel);border:1px solid var(--line);padding:.3rem .7rem;border-radius:99px;z-index:30}
|
||||
@@ -0,0 +1,156 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Topographic — MeshCore Hub mockup</title>
|
||||
<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=Archivo:wght@600;700;800&family=Archivo+Narrow:wght@500;600;700&family=Inter:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root{
|
||||
--display:"Archivo Narrow","Archivo",system-ui,sans-serif;
|
||||
--body:"Inter",system-ui,sans-serif;
|
||||
--mono:"IBM Plex Mono",ui-monospace,monospace;
|
||||
--radius:8px;
|
||||
}
|
||||
/* DARK — field at night */
|
||||
[data-theme="dark"]{
|
||||
--bg:#14180F; --panel:#1C2116; --panel-2:#232A1C; --line:#2E3722;
|
||||
--text:#ECE8DA; --muted:#9AA284;
|
||||
--accent:#D8642A; /* rust survey marker */
|
||||
--accent-soft:#D8642A24;
|
||||
--green:#92AE5E; /* topo green */
|
||||
--green-soft:#92AE5E22;
|
||||
--contour:#3A4528;
|
||||
}
|
||||
/* LIGHT — parchment survey map */
|
||||
[data-theme="light"]{
|
||||
--bg:#F5F1E6; --panel:#FFFDF7; --panel-2:#F0EAD8; --line:#DED7C3;
|
||||
--text:#23271A; --muted:#6E7257;
|
||||
--accent:#B14A1B; --accent-soft:#B14A1B16;
|
||||
--green:#5F7741; --green-soft:#5F774114;
|
||||
--contour:#E2D9C1;
|
||||
}
|
||||
*{box-sizing:border-box}
|
||||
body{margin:0;font-family:var(--body);background:var(--bg);color:var(--text);
|
||||
-webkit-font-smoothing:antialiased;line-height:1.5}
|
||||
a{color:inherit;text-decoration:none}
|
||||
.mono{font-family:var(--mono)}
|
||||
|
||||
/* NAV */
|
||||
.nav{position:sticky;top:0;z-index:20;display:flex;align-items:center;gap:.5rem;
|
||||
padding:.6rem 1rem;background:color-mix(in oklab,var(--panel) 90%,transparent);
|
||||
backdrop-filter:blur(8px);border-bottom:1px solid var(--line)}
|
||||
.brand{display:flex;align-items:center;gap:.55rem;font-family:var(--display);
|
||||
font-weight:700;letter-spacing:.02em;font-size:1.15rem;text-transform:uppercase}
|
||||
.brand .logo{height:22px;width:auto;color:var(--text)}
|
||||
.brand .logo .arcs{stroke:var(--accent)}
|
||||
.hero .logo-lg{height:56px;width:auto;color:var(--text);display:block;margin-bottom:.6rem;position:relative}
|
||||
.hero .logo-lg .arcs{stroke:var(--accent)}
|
||||
.navlinks{display:none;margin-left:1rem;gap:.1rem}
|
||||
.navlinks a{font-family:var(--display);font-size:.95rem;padding:.3rem .55rem;border-radius:6px;
|
||||
color:var(--muted);text-transform:uppercase;letter-spacing:.02em}
|
||||
.navlinks a:hover{background:var(--accent-soft);color:var(--text)}
|
||||
.navlinks a.active{color:var(--text);background:var(--accent-soft)}
|
||||
.spacer{flex:1}
|
||||
.iconbtn{width:34px;height:34px;border-radius:7px;border:1px solid var(--line);
|
||||
background:var(--panel-2);color:var(--text);display:grid;place-items:center;cursor:pointer}
|
||||
@media(min-width:1000px){.navlinks{display:flex}.burger{display:none}}
|
||||
|
||||
.wrap{max-width:1080px;margin:0 auto;padding:1.25rem 1rem 3rem}
|
||||
|
||||
/* HERO with contour-line signature */
|
||||
.hero{position:relative;overflow:hidden;border:1px solid var(--line);border-radius:var(--radius);
|
||||
background:var(--panel);padding:1.7rem 1.4rem;margin-bottom:1.25rem}
|
||||
.hero .topo{position:absolute;inset:0;opacity:.6;pointer-events:none}
|
||||
.hero .coord{font-family:var(--mono);font-size:.72rem;color:var(--accent);letter-spacing:.05em}
|
||||
.hero h1{font-family:var(--display);font-weight:700;text-transform:uppercase;
|
||||
font-size:clamp(1.9rem,6vw,3rem);margin:.3rem 0 .35rem;letter-spacing:.01em;line-height:1}
|
||||
.hero p{color:var(--muted);max-width:48ch;margin:0;position:relative}
|
||||
.tag{display:inline-flex;gap:.4rem;align-items:center;font-family:var(--mono);font-size:.7rem;
|
||||
color:var(--green);border:1px solid var(--green);border-radius:4px;padding:.15rem .5rem}
|
||||
|
||||
/* STATS — survey datum blocks */
|
||||
.grid{display:grid;gap:.7rem;grid-template-columns:repeat(2,1fr);margin-bottom:1.25rem}
|
||||
@media(min-width:760px){.grid{grid-template-columns:repeat(4,1fr)}}
|
||||
.stat{border:1px solid var(--line);border-left:3px solid var(--c,var(--accent));
|
||||
border-radius:var(--radius);background:var(--panel);padding:.85rem 1rem}
|
||||
.stat .label{font-family:var(--mono);font-size:.64rem;letter-spacing:.1em;text-transform:uppercase;color:var(--muted)}
|
||||
.stat .val{font-family:var(--display);font-weight:700;font-size:2.1rem;line-height:1;margin-top:.25rem}
|
||||
.stat .sub{font-size:.72rem;color:var(--muted);margin-top:.15rem}
|
||||
|
||||
/* PANEL + TABLE */
|
||||
.panel{border:1px solid var(--line);border-radius:var(--radius);background:var(--panel);overflow:hidden}
|
||||
.panel-h{display:flex;align-items:center;justify-content:space-between;padding:.8rem 1rem;border-bottom:1px solid var(--line)}
|
||||
.panel-h h2{font-family:var(--display);font-weight:700;text-transform:uppercase;font-size:1.05rem;margin:0;letter-spacing:.02em}
|
||||
table{width:100%;border-collapse:collapse;font-size:.85rem}
|
||||
th{text-align:left;font-family:var(--mono);font-size:.64rem;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);font-weight:500;padding:.55rem 1rem}
|
||||
td{padding:.6rem 1rem;border-top:1px solid var(--line)}
|
||||
tr:hover td{background:var(--panel-2)}
|
||||
.key{font-family:var(--mono);font-size:.78rem;color:var(--muted)}
|
||||
.badge{font-family:var(--mono);font-size:.64rem;letter-spacing:.04em;padding:.12rem .5rem;border-radius:4px}
|
||||
.b-acc{color:var(--accent);background:var(--accent-soft)}
|
||||
.b-grn{color:var(--green);background:var(--green-soft)}
|
||||
.btn{font-family:var(--display);text-transform:uppercase;font-weight:700;letter-spacing:.03em;
|
||||
font-size:.82rem;padding:.45rem .9rem;border-radius:6px;background:var(--accent);color:#fff;border:none;cursor:pointer}
|
||||
.node{display:flex;align-items:center;gap:.6rem}.node .em{font-size:1.1rem}.node .nm{font-weight:600}
|
||||
.foot{max-width:1080px;margin:2rem auto 0;padding:1.5rem 1rem;color:var(--muted);font-size:.78rem;border-top:1px solid var(--line);text-align:center}
|
||||
.note{position:fixed;left:50%;transform:translateX(-50%);bottom:14px;font-family:var(--mono);font-size:.68rem;color:var(--muted);background:var(--panel);border:1px solid var(--line);padding:.3rem .7rem;border-radius:99px}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="nav">
|
||||
<div class="brand"><svg class="logo" viewBox="0 0 115 100" aria-hidden="true"><rect x="0" y="0" width="25" height="100" rx="2" fill="currentColor" opacity=".5"/><rect x="35" y="0" width="25" height="100" rx="2" fill="currentColor"/><g class="arcs" fill="none" stroke="currentColor" stroke-width="10" stroke-linecap="round" transform="translate(-30,-10)"><path d="M 110,65 A 20,20 0 0 0 90,45"/><path d="M 125,65 A 35,35 0 0 0 90,30"/><path d="M 140,65 A 50,50 0 0 0 90,15"/></g></svg>IPNet</div>
|
||||
<div class="navlinks">
|
||||
<a class="active">Dashboard</a><a href="topographic-list.html">Nodes</a><a href="topographic-list.html">Adverts</a><a>Messages</a><a>Packets</a><a href="topographic-map.html">Map</a><a>Members</a>
|
||||
</div>
|
||||
<div class="spacer"></div>
|
||||
<button class="iconbtn" onclick="toggleTheme()" title="Toggle light/dark">◐</button>
|
||||
<button class="iconbtn burger">≡</button>
|
||||
</nav>
|
||||
|
||||
<div class="wrap">
|
||||
<section class="hero">
|
||||
<svg class="topo" preserveAspectRatio="none" viewBox="0 0 600 240">
|
||||
<g fill="none" stroke="var(--contour)" stroke-width="1.2">
|
||||
<path d="M-20,200 C120,150 200,250 340,180 C460,120 540,200 640,150"/>
|
||||
<path d="M-20,160 C120,110 220,210 360,140 C470,90 560,160 640,110"/>
|
||||
<path d="M-20,120 C140,80 240,170 380,100 C480,60 560,120 640,80"/>
|
||||
<path d="M-20,80 C150,50 260,130 400,70 C500,40 570,90 640,55"/>
|
||||
<path d="M-20,40 C160,20 280,90 420,40 C520,15 580,55 640,30"/>
|
||||
</g>
|
||||
</svg>
|
||||
<svg class="logo-lg" viewBox="0 0 115 100" aria-hidden="true"><rect x="0" y="0" width="25" height="100" rx="2" fill="currentColor" opacity=".5"/><rect x="35" y="0" width="25" height="100" rx="2" fill="currentColor"/><g class="arcs" fill="none" stroke="currentColor" stroke-width="10" stroke-linecap="round" transform="translate(-30,-10)"><path d="M 110,65 A 20,20 0 0 0 90,45"/><path d="M 125,65 A 35,35 0 0 0 90,30"/><path d="M 140,65 A 50,50 0 0 0 90,15"/></g></svg>
|
||||
<span class="coord">53.80°N · 1.55°W</span>
|
||||
<h1>IPNet</h1>
|
||||
<p>Off-grid encrypted LoRa mesh — a survey of the territory: who's on air, how packets cross the hops, and where the network reaches.</p>
|
||||
<p style="margin-top:.8rem"><span class="tag">● 42 NODES ON AIR</span></p>
|
||||
</section>
|
||||
|
||||
<div class="grid">
|
||||
<div class="stat" style="--c:#7E78D8"><div class="label">Nodes</div><div class="val">42</div><div class="sub">+3 this week</div></div>
|
||||
<div class="stat" style="--c:var(--accent)"><div class="label">Adverts · 7d</div><div class="val">1,284</div><div class="sub">flood & direct</div></div>
|
||||
<div class="stat" style="--c:var(--green)"><div class="label">Messages · 7d</div><div class="val">318</div><div class="sub">6 channels</div></div>
|
||||
<div class="stat" style="--c:#C99A3A"><div class="label">Reach</div><div class="val">31<span style="font-size:1rem">km</span></div><div class="sub">max link</div></div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-h"><h2>Recent nodes</h2><button class="btn">View all</button></div>
|
||||
<table>
|
||||
<thead><tr><th>Node</th><th>Public key</th><th>Route</th><th>Last heard</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td><div class="node"><span class="em">📡</span><span class="nm">Holbeck-Relay</span></div></td><td class="key">a1f3…9c2e</td><td><span class="badge b-grn">DIRECT</span></td><td class="mono" style="color:var(--muted)">2m ago</td></tr>
|
||||
<tr><td><div class="node"><span class="em">💬</span><span class="nm">kestrel</span></div></td><td class="key">7b02…4d1a</td><td><span class="badge b-acc">FLOOD</span></td><td class="mono" style="color:var(--muted)">11m ago</td></tr>
|
||||
<tr><td><div class="node"><span class="em">🪧</span><span class="nm">Briggate-Room</span></div></td><td class="key">e5c9…aa70</td><td><span class="badge b-acc">FLOOD</span></td><td class="mono" style="color:var(--muted)">38m ago</td></tr>
|
||||
<tr><td><div class="node"><span class="em">📡</span><span class="nm">Armley-Hill</span></div></td><td class="key">3d8e…1f55</td><td><span class="badge b-grn">DIRECT</span></td><td class="mono" style="color:var(--muted)">1h ago</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="foot">IPNET · 53.80°N 1.55°W — powered by MeshCore Hub</footer>
|
||||
<div class="note">Topographic · click ◐ to toggle light/dark · resize to portrait to test mobile</div>
|
||||
<script>function toggleTheme(){const h=document.documentElement;h.dataset.theme=h.dataset.theme==='dark'?'light':'dark'}</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user