Files

150 lines
18 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BEACON — Command Red Palette</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&family=Inter:wght@400;500;600;700&display=swap');
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--bg-base: #0C0A10;
--bg-surface: #14111A;
--bg-raised: #1E1A26;
--border: #2A2434;
--border-subtle: #1E1828;
--primary: #E85D75;
--primary-dim: #8B2E42;
--primary-bg: rgba(232, 93, 117, 0.06);
--secondary: #8B9FE8;
--secondary-dim: #4A5A9E;
--secondary-bg: rgba(139, 159, 232, 0.06);
--green: #5AD8A6;
--green-bg: rgba(90, 216, 166, 0.08);
--danger: #FF6B6B;
--danger-bg: rgba(255, 107, 107, 0.08);
--warn: #F0C674;
--warn-bg: rgba(240, 198, 116, 0.07);
--text-bright: #F0ECF4;
--text-normal: #B4AACC;
--text-muted: #6E6488;
--text-dim: #443C58;
--font-mono: 'JetBrains Mono', monospace;
--font-sans: 'Inter', system-ui, sans-serif;
}
body { background: var(--bg-base); color: var(--text-normal); font-family: var(--font-sans); font-size: 13px; line-height: 1.5; min-height: 100vh; }
.app-header { display: flex; align-items: center; justify-content: space-between; padding: 0 16px; height: 42px; background: var(--bg-surface); border-bottom: 1px solid var(--border); }
.app-title { font-family: var(--font-mono); font-weight: 700; font-size: 14px; color: var(--primary); letter-spacing: 1px; text-transform: uppercase; }
.app-title span { color: var(--text-dim); font-weight: 400; letter-spacing: 0; text-transform: none; }
.header-right { display: flex; align-items: center; gap: 12px; }
.ws-badge { display: flex; align-items: center; gap: 5px; font-family: var(--font-mono); font-size: 10px; color: var(--green); background: var(--green-bg); border: 1px solid rgba(90, 216, 166, 0.15); padding: 2px 8px; border-radius: 3px; }
.ws-badge .dot { width: 5px; height: 5px; border-radius: 50%; background: var(--green); animation: pulse 2s infinite; }
.scope-selector { display: flex; align-items: center; gap: 6px; background: var(--bg-raised); border: 1px solid var(--border); border-radius: 4px; padding: 4px 12px; color: var(--text-bright); font-family: var(--font-mono); font-size: 12px; font-weight: 600; }
.scope-selector .label { color: var(--text-muted); font-weight: 400; font-size: 10px; margin-right: 2px; }
.scope-selector .arrow { color: var(--text-dim); font-size: 10px; }
.tab-bar { display: flex; background: var(--bg-surface); border-bottom: 1px solid var(--border); padding: 0 16px; }
.tab { padding: 9px 18px; font-size: 12px; font-weight: 500; color: var(--text-muted); border-bottom: 2px solid transparent; cursor: pointer; letter-spacing: 0.3px; transition: color 0.15s; }
.tab.active { color: var(--primary); border-bottom-color: var(--primary); }
.tab:hover:not(.active) { color: var(--text-normal); }
.filter-bar { display: flex; align-items: center; gap: 6px; padding: 7px 16px; border-bottom: 1px solid var(--border-subtle); background: var(--bg-base); }
.filter-pill { font-size: 11px; padding: 3px 10px; border-radius: 3px; border: 1px solid var(--border); color: var(--text-muted); background: var(--bg-surface); font-family: var(--font-mono); cursor: pointer; transition: all 0.15s; }
.filter-pill:hover { border-color: var(--text-dim); color: var(--text-normal); }
.filter-pill.active { border-color: var(--primary-dim); background: var(--primary-bg); color: var(--primary); }
.filter-sep { color: var(--border); font-size: 14px; margin: 0 2px; }
.catchup-banner { margin: 0 16px; padding: 6px 12px; background: var(--primary-bg); border: 1px solid rgba(232, 93, 117, 0.12); border-top: none; color: var(--primary); font-size: 12px; font-weight: 500; text-align: center; cursor: pointer; font-family: var(--font-mono); border-radius: 0 0 4px 4px; }
.packet-list { padding: 4px 16px 40px; }
.packet-row { background: var(--bg-surface); border: 1px solid var(--border); border-radius: 4px; margin-top: 2px; padding: 8px 12px; cursor: pointer; transition: all 0.15s; }
.packet-row:hover { border-color: #382E44; background: #18141E; }
.packet-row.expanded { border-color: var(--primary); background: #16121C; }
.packet-row.new-pulse { border-color: var(--primary); box-shadow: 0 0 12px rgba(232, 93, 117, 0.08); }
.row-main { display: flex; align-items: center; gap: 10px; }
.row-expand { color: var(--text-dim); font-size: 11px; width: 14px; font-family: var(--font-mono); }
.row-hash { font-family: var(--font-mono); font-size: 12px; font-weight: 600; color: var(--primary); letter-spacing: 0.5px; }
.row-type { font-family: var(--font-mono); font-size: 10px; font-weight: 600; padding: 2px 7px; border-radius: 2px; letter-spacing: 0.5px; text-transform: uppercase; }
.type-advert { background: var(--green-bg); color: var(--green); border: 1px solid rgba(90, 216, 166, 0.12); }
.type-text { background: var(--secondary-bg); color: var(--secondary); border: 1px solid rgba(139, 159, 232, 0.12); }
.type-trace { background: var(--warn-bg); color: var(--warn); border: 1px solid rgba(240, 198, 116, 0.12); }
.type-ack { background: rgba(110, 100, 136, 0.1); color: var(--text-muted); border: 1px solid rgba(110, 100, 136, 0.12); }
.type-group { background: rgba(200, 140, 255, 0.06); color: #C88CFF; border: 1px solid rgba(200, 140, 255, 0.12); }
.type-req { background: var(--primary-bg); color: var(--primary); border: 1px solid rgba(232, 93, 117, 0.12); }
.row-summary { flex: 1; color: var(--text-bright); font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.row-summary .quoted { color: var(--text-muted); font-style: italic; }
.row-count { font-family: var(--font-mono); font-size: 11px; color: var(--green); font-weight: 600; background: var(--green-bg); padding: 1px 6px; border-radius: 2px; }
.row-meta { display: flex; align-items: center; gap: 8px; margin-top: 4px; padding-left: 24px; font-size: 11px; color: var(--text-dim); }
.row-meta .dot { font-size: 4px; color: var(--border); }
.row-meta .route { font-family: var(--font-mono); font-size: 10px; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.5px; background: rgba(110, 100, 136, 0.08); padding: 1px 5px; border-radius: 2px; }
.row-meta .observer { color: var(--text-normal); }
.row-meta .iata { font-family: var(--font-mono); font-weight: 700; color: var(--primary); font-size: 10px; }
.observations { margin-top: 8px; padding-left: 24px; display: flex; flex-direction: column; gap: 3px; }
.obs-card { background: var(--bg-base); border: 1px solid var(--border); border-radius: 4px; padding: 9px 12px; border-left: 2px solid var(--primary-dim); }
.obs-header { display: flex; align-items: center; gap: 8px; font-size: 11px; margin-bottom: 6px; }
.obs-header .obs-name { color: var(--text-bright); font-weight: 600; }
.obs-header .obs-iata { font-family: var(--font-mono); color: var(--primary); font-weight: 600; font-size: 10px; background: var(--primary-bg); padding: 1px 5px; border-radius: 2px; }
.obs-header .obs-time { color: var(--text-dim); margin-left: auto; font-family: var(--font-mono); font-size: 10px; }
.obs-stats { display: flex; gap: 18px; font-family: var(--font-mono); font-size: 11px; }
.obs-stats .stat { display: flex; flex-direction: column; }
.obs-stats .label { color: var(--text-dim); font-size: 9px; text-transform: uppercase; letter-spacing: 0.5px; }
.obs-stats .value { color: var(--text-normal); font-weight: 500; }
.obs-stats .value.good { color: var(--green); }
.obs-stats .value.mid { color: var(--warn); }
.obs-stats .value.bad { color: var(--danger); }
.obs-path { display: flex; align-items: center; gap: 4px; margin-top: 7px; font-family: var(--font-mono); font-size: 10px; padding-top: 6px; border-top: 1px solid var(--border-subtle); }
.obs-path .path-label { color: var(--text-dim); text-transform: uppercase; font-size: 9px; letter-spacing: 0.5px; margin-right: 4px; }
.hop { padding: 2px 6px; border-radius: 2px; font-weight: 500; }
.hop.resolved { background: var(--green-bg); color: var(--green); border: 1px solid rgba(90, 216, 166, 0.12); }
.hop.ambiguous { background: var(--warn-bg); color: var(--warn); border: 1px solid rgba(240, 198, 116, 0.12); }
.obs-path .arrow { color: var(--text-dim); font-size: 12px; }
.obs-actions { display: flex; gap: 14px; margin-top: 7px; font-size: 10px; }
.obs-actions a { color: var(--secondary); text-decoration: none; font-weight: 500; }
.status-bar { position: fixed; bottom: 0; left: 0; right: 0; display: flex; align-items: center; justify-content: space-between; padding: 5px 16px; background: var(--bg-surface); border-top: 1px solid var(--border); font-family: var(--font-mono); font-size: 10px; color: var(--text-dim); }
.status-left { display: flex; align-items: center; gap: 16px; }
.status-item .val { color: var(--text-muted); }
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }
.palette-label { text-align: center; padding: 24px 16px 8px; font-size: 18px; font-weight: 700; color: var(--primary); font-family: var(--font-mono); letter-spacing: 1px; }
.palette-label span { display: block; font-size: 12px; color: var(--text-muted); font-weight: 400; margin-top: 4px; letter-spacing: 0; }
.palette-label .sub { font-size: 11px; color: var(--text-dim); margin-top: 8px; max-width: 600px; margin-left: auto; margin-right: auto; line-height: 1.6; }
.swatches { display: flex; gap: 8px; padding: 12px 16px 16px; flex-wrap: wrap; justify-content: center; }
.swatch { display: flex; align-items: center; gap: 6px; font-family: var(--font-mono); font-size: 10px; color: var(--text-dim); }
.swatch .dot { width: 12px; height: 12px; border-radius: 2px; border: 1px solid rgba(255,255,255,0.1); }
</style>
</head>
<body>
<div class="palette-label">
OPTION J — COMMAND RED
<span>Military command center aesthetic · muted crimson on deep purple-black · slate-blue secondary</span>
<div class="sub">Red reads as "monitoring active systems" without being alarming at this saturation. The purple-black base and lavender text give it a distinct look that no other mesh tool has. Blue secondary balances the warmth.</div>
</div>
<div class="swatches">
<div class="swatch"><div class="dot" style="background:#E85D75"></div>Primary</div>
<div class="swatch"><div class="dot" style="background:#8B9FE8"></div>Secondary</div>
<div class="swatch"><div class="dot" style="background:#5AD8A6"></div>Good</div>
<div class="swatch"><div class="dot" style="background:#FF6B6B"></div>Danger</div>
<div class="swatch"><div class="dot" style="background:#0C0A10"></div>Base</div>
<div class="swatch"><div class="dot" style="background:#14111A"></div>Surface</div>
<div class="swatch"><div class="dot" style="background:#F0ECF4"></div>Bright</div>
<div class="swatch"><div class="dot" style="background:#6E6488"></div>Muted</div>
</div>
<div class="app-header"><div class="app-title">BEACON <span>v0.1</span></div><div class="header-right"><div class="ws-badge"><span class="dot"></span> LIVE</div><div class="scope-selector"><span class="label">SCOPE</span> YOW <span class="arrow"></span></div></div></div>
<div class="tab-bar"><div class="tab active">Packets</div><div class="tab">Nodes</div><div class="tab">Observers</div><div class="tab">Channels</div><div class="tab">Stats</div><div class="tab">Map</div></div>
<div class="filter-bar"><div class="filter-pill active">All types</div><div class="filter-pill">Advert</div><div class="filter-pill">Text</div><div class="filter-pill">Group</div><div class="filter-pill">Trace</div><div class="filter-pill">Ack</div><div class="filter-sep"></div><div class="filter-pill active">All routes</div><div class="filter-pill">Flood</div><div class="filter-pill">Direct</div></div>
<div class="catchup-banner">▲ 47 new packets · click to scroll to top</div>
<div class="packet-list">
<div class="packet-row expanded">
<div class="row-main"><span class="row-expand"></span><span class="row-hash">9E9B7D6A</span><span class="row-type type-advert">Advert</span><span class="row-summary">Advert <span class="quoted">"WW7STR/PugetMesh Cougar"</span></span><span class="row-count">×15</span></div>
<div class="row-meta"><span class="route">Flood</span><span class="dot">·</span><span>02:37 PM</span><span class="dot">·</span><span class="observer">FlightlessDt</span><span class="dot">·</span><span class="iata">KEH</span></div>
<div class="observations">
<div class="obs-card"><div class="obs-header"><span class="obs-name">FlightlessDt</span><span class="obs-iata">KEH</span><span class="obs-time">02:37:36</span></div><div class="obs-stats"><div class="stat"><span class="label">SNR</span><span class="value good">10.75</span></div><div class="stat"><span class="label">RSSI</span><span class="value mid">-98</span></div><div class="stat"><span class="label">Prop</span><span class="value">1.936s</span></div><div class="stat"><span class="label">Hops</span><span class="value">1</span></div></div><div class="obs-path"><span class="path-label">Path</span><span class="hop resolved">YOW_Kanata</span><span class="arrow"></span><span class="hop resolved">FlightlessDt</span></div><div class="obs-actions"><a href="#">Open analyzer</a><a href="#">Show on map</a></div></div>
<div class="obs-card"><div class="obs-header"><span class="obs-name">NodeRunner</span><span class="obs-iata">SEA</span><span class="obs-time">02:37:38</span></div><div class="obs-stats"><div class="stat"><span class="label">SNR</span><span class="value mid">7.20</span></div><div class="stat"><span class="label">RSSI</span><span class="value bad">-105</span></div><div class="stat"><span class="label">Prop</span><span class="value">4.122s</span></div><div class="stat"><span class="label">Hops</span><span class="value">2</span></div></div><div class="obs-path"><span class="path-label">Path</span><span class="hop resolved">YOW_Kanata</span><span class="arrow"></span><span class="hop ambiguous">AE9B (2 candidates)</span><span class="arrow"></span><span class="hop resolved">NodeRunner</span></div><div class="obs-actions"><a href="#">Open analyzer</a><a href="#">Show on map</a></div></div>
</div>
</div>
<div class="packet-row"><div class="row-main"><span class="row-expand"></span><span class="row-hash">3FA1C20E</span><span class="row-type type-group">Group</span><span class="row-summary">Group text on <span class="quoted">#ottawa</span></span><span class="row-count">×8</span></div><div class="row-meta"><span class="route">Flood</span><span class="dot">·</span><span>02:36 PM</span><span class="dot">·</span><span class="observer">Hull_Hospital</span><span class="dot">·</span><span class="iata">YOW</span></div></div>
<div class="packet-row new-pulse"><div class="row-main"><span class="row-expand"></span><span class="row-hash">B7D44F19</span><span class="row-type type-trace">Trace</span><span class="row-summary">Trace 4-byte path, 6 hops</span><span class="row-count">×3</span></div><div class="row-meta"><span class="route">Flood</span><span class="dot">·</span><span>02:36 PM</span><span class="dot">·</span><span class="observer">GatineauPeak</span><span class="dot">·</span><span class="iata">YOW</span></div></div>
<div class="packet-row"><div class="row-main"><span class="row-expand"></span><span class="row-hash">1CE88A03</span><span class="row-type type-ack">Ack</span><span class="row-summary">Acknowledgment</span><span class="row-count">×2</span></div><div class="row-meta"><span class="route">Direct</span><span class="dot">·</span><span>02:35 PM</span><span class="dot">·</span><span class="observer">RobaStick_YOW</span><span class="dot">·</span><span class="iata">YOW</span></div></div>
<div class="packet-row"><div class="row-main"><span class="row-expand"></span><span class="row-hash">D9F21B7C</span><span class="row-type type-text">Text</span><span class="row-summary">Plain text message</span><span class="row-count">×12</span></div><div class="row-meta"><span class="route">Flood</span><span class="dot">·</span><span>02:34 PM</span><span class="dot">·</span><span class="observer">Bells_Corners</span><span class="dot">·</span><span class="iata">YOW</span></div></div>
<div class="packet-row"><div class="row-main"><span class="row-expand"></span><span class="row-hash">44AE0FD2</span><span class="row-type type-req">Request</span><span class="row-summary">Request dest=A3F1 src=9E2B</span><span class="row-count">×5</span></div><div class="row-meta"><span class="route">Direct</span><span class="dot">·</span><span>02:33 PM</span><span class="dot">·</span><span class="observer">Hull_Hospital</span><span class="dot">·</span><span class="iata">YOW</span></div></div>
<div class="packet-row"><div class="row-main"><span class="row-expand"></span><span class="row-hash">E501CAFE</span><span class="row-type type-advert">Advert</span><span class="row-summary">Advert <span class="quoted">"VE3XYZ/Ottawa_South"</span></span><span class="row-count">×22</span></div><div class="row-meta"><span class="route">Flood</span><span class="dot">·</span><span>02:32 PM</span><span class="dot">·</span><span class="observer">GatineauPeak</span><span class="dot">·</span><span class="iata">YOW</span></div></div>
</div>
<div class="status-bar"><div class="status-left"><div class="status-item"><span class="val">1,247</span> packets</div><div class="status-item"><span class="val">34</span> observers</div><div class="status-item"><span class="val">189</span> nodes</div></div><div>mqtt1 + mqtt2</div></div>
</body>
</html>