1e357244e1
Three full-fidelity dashboard mockups (Bridge/Console, Aurora/Glass, Bento/Modular) plus a chooser index and a tracker-detail page in the chosen Aurora language. Self-contained HTML, no build needed.
1411 lines
52 KiB
HTML
1411 lines
52 KiB
HTML
<!doctype html>
|
||
<html lang="en" data-theme="dark">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
<title>Notify Bridge — Aurora</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=Geist:wght@300..700&family=Geist+Mono:wght@300..600&family=Newsreader:ital,opsz,wght@0,6..72,300..700;1,6..72,300..700&display=swap" rel="stylesheet">
|
||
<style>
|
||
/* ============================================================
|
||
OPTION B — "AURORA GLASS"
|
||
Vivid aurora gradient base, frosted glass surfaces, soft pastel
|
||
accents. Stripe / visionOS / Apple-modern energy. Heavy on
|
||
blur, rounded corners, subtle inner highlights.
|
||
============================================================ */
|
||
:root[data-theme="dark"] {
|
||
--bg: #050613;
|
||
--bg-deep: #02030a;
|
||
--fg: #f3f1ff;
|
||
--fg-dim: #b6b2d4;
|
||
--mute: #6f6c92;
|
||
--rule: rgba(255, 255, 255, 0.08);
|
||
--rule-strong: rgba(255, 255, 255, 0.16);
|
||
--glass: rgba(255, 255, 255, 0.04);
|
||
--glass-strong: rgba(255, 255, 255, 0.07);
|
||
--glass-elev: rgba(255, 255, 255, 0.10);
|
||
--highlight: rgba(255, 255, 255, 0.14);
|
||
|
||
--lavender: #b8a7ff;
|
||
--orchid: #ff9ec4;
|
||
--mint: #7ee8c4;
|
||
--citrus: #f0e16a;
|
||
--coral: #ff8a78;
|
||
--sky: #8ec9ff;
|
||
|
||
--shadow-card: 0 1px 0 rgba(255,255,255,0.07) inset, 0 30px 60px -20px rgba(0,0,0,0.6);
|
||
}
|
||
:root[data-theme="pearl"] {
|
||
--bg: #f5f3ff;
|
||
--bg-deep: #ede9fe;
|
||
--fg: #1a1530;
|
||
--fg-dim: #4a4670;
|
||
--mute: #7c789c;
|
||
--rule: rgba(20, 15, 60, 0.08);
|
||
--rule-strong: rgba(20, 15, 60, 0.16);
|
||
--glass: rgba(255, 255, 255, 0.55);
|
||
--glass-strong: rgba(255, 255, 255, 0.65);
|
||
--glass-elev: rgba(255, 255, 255, 0.80);
|
||
--highlight: rgba(255, 255, 255, 0.9);
|
||
|
||
--lavender: #6d4ce0;
|
||
--orchid: #d63384;
|
||
--mint: #008a64;
|
||
--citrus: #a07a00;
|
||
--coral: #e0512f;
|
||
--sky: #1f6fcc;
|
||
|
||
--shadow-card: 0 1px 0 rgba(255,255,255,0.5) inset, 0 20px 40px -16px rgba(80, 50, 180, 0.18);
|
||
}
|
||
|
||
*, *::before, *::after { box-sizing: border-box; }
|
||
html, body { margin: 0; padding: 0; }
|
||
body {
|
||
background: var(--bg);
|
||
color: var(--fg);
|
||
font-family: 'Geist', ui-sans-serif, system-ui, sans-serif;
|
||
-webkit-font-smoothing: antialiased;
|
||
-moz-osx-font-smoothing: grayscale;
|
||
min-height: 100vh;
|
||
overflow-x: hidden;
|
||
font-size: 14px;
|
||
line-height: 1.5;
|
||
letter-spacing: -0.005em;
|
||
}
|
||
|
||
/* AURORA — vivid blurred blobs as global atmosphere */
|
||
body::before {
|
||
content: '';
|
||
position: fixed; inset: -20vh -10vw;
|
||
background:
|
||
radial-gradient(40vw 40vw at 12% 18%, rgba(184, 167, 255, 0.55), transparent 60%),
|
||
radial-gradient(35vw 35vw at 88% 22%, rgba(255, 158, 196, 0.45), transparent 60%),
|
||
radial-gradient(50vw 35vw at 78% 88%, rgba(126, 232, 196, 0.40), transparent 60%),
|
||
radial-gradient(40vw 30vw at 6% 92%, rgba(142, 201, 255, 0.42), transparent 60%);
|
||
filter: blur(60px) saturate(140%);
|
||
pointer-events: none;
|
||
z-index: 0;
|
||
animation: drift 28s ease-in-out infinite alternate;
|
||
}
|
||
body::after {
|
||
content: '';
|
||
position: fixed; inset: 0;
|
||
background:
|
||
radial-gradient(circle at 50% 50%, transparent 30%, var(--bg-deep) 100%);
|
||
pointer-events: none;
|
||
z-index: 0;
|
||
opacity: 0.7;
|
||
}
|
||
@keyframes drift {
|
||
from { transform: translate(0, 0) scale(1); }
|
||
to { transform: translate(-2%, 1%) scale(1.05); }
|
||
}
|
||
|
||
/* Subtle film grain — keeps the glass from feeling sterile */
|
||
.grain {
|
||
position: fixed; inset: 0;
|
||
z-index: 1; pointer-events: none;
|
||
opacity: 0.05;
|
||
mix-blend-mode: overlay;
|
||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
|
||
}
|
||
|
||
::selection { background: var(--lavender); color: var(--bg-deep); }
|
||
::-webkit-scrollbar { width: 8px; height: 8px; }
|
||
::-webkit-scrollbar-thumb { background: var(--rule-strong); border-radius: 999px; }
|
||
|
||
/* ============================================================
|
||
SHELL
|
||
============================================================ */
|
||
.shell {
|
||
position: relative; z-index: 2;
|
||
display: grid;
|
||
grid-template-columns: 240px 1fr;
|
||
min-height: 100vh;
|
||
padding: 18px;
|
||
gap: 18px;
|
||
}
|
||
|
||
/* GLASS — the foundational surface */
|
||
.glass {
|
||
background: var(--glass);
|
||
backdrop-filter: blur(28px) saturate(160%);
|
||
-webkit-backdrop-filter: blur(28px) saturate(160%);
|
||
border: 1px solid var(--rule);
|
||
border-radius: 22px;
|
||
box-shadow: var(--shadow-card);
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
.glass::after {
|
||
content: '';
|
||
position: absolute;
|
||
inset: 0;
|
||
border-radius: inherit;
|
||
pointer-events: none;
|
||
background: linear-gradient(180deg, var(--highlight), transparent 30%);
|
||
opacity: 0.4;
|
||
}
|
||
|
||
/* ============================================================
|
||
RAIL
|
||
============================================================ */
|
||
.rail {
|
||
composes: glass;
|
||
background: var(--glass);
|
||
backdrop-filter: blur(28px) saturate(160%);
|
||
-webkit-backdrop-filter: blur(28px) saturate(160%);
|
||
border: 1px solid var(--rule);
|
||
border-radius: 22px;
|
||
box-shadow: var(--shadow-card);
|
||
position: sticky;
|
||
top: 18px;
|
||
height: calc(100vh - 36px);
|
||
display: flex; flex-direction: column;
|
||
overflow: hidden;
|
||
}
|
||
.rail::after {
|
||
content: '';
|
||
position: absolute;
|
||
inset: 0;
|
||
border-radius: inherit;
|
||
pointer-events: none;
|
||
background: linear-gradient(180deg, var(--highlight), transparent 30%);
|
||
opacity: 0.4;
|
||
}
|
||
.rail__brand {
|
||
padding: 22px 22px 18px;
|
||
display: flex; align-items: center; gap: 12px;
|
||
position: relative; z-index: 1;
|
||
}
|
||
.rail__logo {
|
||
width: 36px; height: 36px;
|
||
border-radius: 12px;
|
||
background: conic-gradient(from 220deg, var(--lavender), var(--orchid), var(--mint), var(--lavender));
|
||
display: grid; place-items: center;
|
||
box-shadow: 0 4px 14px rgba(184, 167, 255, 0.35);
|
||
position: relative;
|
||
}
|
||
.rail__logo::after {
|
||
content: '';
|
||
position: absolute; inset: 4px;
|
||
border-radius: 8px;
|
||
background: radial-gradient(circle at 30% 25%, rgba(255,255,255,0.6), transparent 50%);
|
||
}
|
||
.rail__brand-text { line-height: 1.1; }
|
||
.rail__brand-text b { font-weight: 600; font-size: 16px; letter-spacing: -0.01em; }
|
||
.rail__brand-text small { color: var(--mute); font-size: 11px; font-family: 'Geist Mono', monospace; }
|
||
|
||
.rail__group {
|
||
padding: 8px 12px;
|
||
position: relative; z-index: 1;
|
||
}
|
||
.rail__label {
|
||
font-size: 10px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.13em;
|
||
color: var(--mute);
|
||
padding: 12px 10px 8px;
|
||
font-weight: 500;
|
||
}
|
||
.rail__nav { display: flex; flex-direction: column; gap: 2px; }
|
||
.rail__link {
|
||
display: flex; align-items: center; gap: 12px;
|
||
padding: 9px 12px;
|
||
color: var(--fg-dim);
|
||
text-decoration: none;
|
||
font-size: 13.5px;
|
||
border-radius: 12px;
|
||
transition: all .2s cubic-bezier(.4,.4,0,1);
|
||
position: relative;
|
||
font-weight: 450;
|
||
}
|
||
.rail__link:hover { color: var(--fg); background: var(--glass-strong); }
|
||
.rail__link.is-active {
|
||
color: var(--fg);
|
||
background: var(--glass-elev);
|
||
box-shadow: inset 0 1px 0 var(--highlight), 0 4px 18px -8px rgba(184,167,255,0.35);
|
||
}
|
||
.rail__link.is-active::before {
|
||
content: '';
|
||
position: absolute; left: 6px; top: 50%; transform: translateY(-50%);
|
||
width: 3px; height: 14px; border-radius: 2px;
|
||
background: linear-gradient(var(--lavender), var(--orchid));
|
||
}
|
||
.rail__link svg { width: 17px; height: 17px; flex-shrink: 0; opacity: 0.85; }
|
||
.rail__link .count {
|
||
margin-left: auto;
|
||
font-family: 'Geist Mono', monospace;
|
||
font-size: 10.5px;
|
||
color: var(--mute);
|
||
background: var(--glass);
|
||
padding: 2px 7px;
|
||
border-radius: 999px;
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
.rail__link.is-active .count { color: var(--fg); background: var(--glass-elev); }
|
||
|
||
.rail__foot {
|
||
margin-top: auto;
|
||
padding: 16px;
|
||
position: relative; z-index: 1;
|
||
}
|
||
.rail__op {
|
||
background: var(--glass-strong);
|
||
border: 1px solid var(--rule);
|
||
border-radius: 14px;
|
||
padding: 10px 12px;
|
||
display: flex; align-items: center; gap: 12px;
|
||
}
|
||
.rail__avatar {
|
||
width: 34px; height: 34px;
|
||
border-radius: 50%;
|
||
background: linear-gradient(135deg, var(--orchid), var(--lavender));
|
||
display: grid; place-items: center;
|
||
color: white; font-weight: 600; font-size: 14px;
|
||
box-shadow: 0 0 0 2px var(--glass) inset;
|
||
}
|
||
.rail__op-name { font-size: 13px; font-weight: 500; }
|
||
.rail__op-role { font-size: 10.5px; color: var(--mute); margin-top: 1px; }
|
||
.rail__chip {
|
||
margin-left: auto;
|
||
width: 8px; height: 8px;
|
||
border-radius: 50%;
|
||
background: var(--mint);
|
||
box-shadow: 0 0 8px var(--mint);
|
||
}
|
||
|
||
/* ============================================================
|
||
MAIN
|
||
============================================================ */
|
||
.main {
|
||
display: flex; flex-direction: column;
|
||
gap: 18px;
|
||
min-width: 0;
|
||
}
|
||
|
||
/* Top bar — search + actions */
|
||
.topbar {
|
||
background: var(--glass);
|
||
backdrop-filter: blur(28px) saturate(160%);
|
||
-webkit-backdrop-filter: blur(28px) saturate(160%);
|
||
border: 1px solid var(--rule);
|
||
border-radius: 22px;
|
||
padding: 12px 18px;
|
||
display: flex; align-items: center; gap: 14px;
|
||
position: relative;
|
||
}
|
||
.topbar::after {
|
||
content: '';
|
||
position: absolute; inset: 0;
|
||
border-radius: inherit;
|
||
pointer-events: none;
|
||
background: linear-gradient(180deg, var(--highlight), transparent 30%);
|
||
opacity: 0.4;
|
||
}
|
||
.search {
|
||
flex: 1;
|
||
display: flex; align-items: center; gap: 10px;
|
||
background: var(--glass-strong);
|
||
border: 1px solid var(--rule);
|
||
border-radius: 14px;
|
||
padding: 9px 14px;
|
||
color: var(--mute);
|
||
font-size: 13px;
|
||
cursor: text;
|
||
transition: all .2s;
|
||
}
|
||
.search:hover { background: var(--glass-elev); border-color: var(--rule-strong); }
|
||
.search svg { width: 16px; height: 16px; }
|
||
.search .kbd {
|
||
margin-left: auto;
|
||
font-family: 'Geist Mono', monospace;
|
||
font-size: 10px;
|
||
padding: 2px 6px;
|
||
border-radius: 6px;
|
||
background: var(--glass);
|
||
border: 1px solid var(--rule);
|
||
color: var(--fg-dim);
|
||
}
|
||
.icon-btn {
|
||
width: 36px; height: 36px;
|
||
border-radius: 12px;
|
||
border: 1px solid var(--rule);
|
||
background: var(--glass-strong);
|
||
color: var(--fg-dim);
|
||
display: grid; place-items: center;
|
||
cursor: pointer;
|
||
transition: all .2s;
|
||
}
|
||
.icon-btn:hover { background: var(--glass-elev); color: var(--fg); }
|
||
.icon-btn svg { width: 16px; height: 16px; }
|
||
.new-btn {
|
||
display: inline-flex; align-items: center; gap: 8px;
|
||
padding: 0 16px; height: 36px;
|
||
border-radius: 12px;
|
||
border: 1px solid transparent;
|
||
background: linear-gradient(135deg, var(--lavender), var(--orchid));
|
||
color: white;
|
||
font-size: 13px; font-weight: 500;
|
||
cursor: pointer;
|
||
box-shadow: 0 6px 20px -8px rgba(184,167,255,0.6), inset 0 1px 0 rgba(255,255,255,0.4);
|
||
transition: transform .15s;
|
||
}
|
||
.new-btn:hover { transform: translateY(-1px); }
|
||
.new-btn svg { width: 14px; height: 14px; }
|
||
|
||
/* ============================================================
|
||
HERO
|
||
============================================================ */
|
||
.hero {
|
||
padding: 36px 38px 32px;
|
||
position: relative;
|
||
}
|
||
.hero__row {
|
||
display: grid;
|
||
grid-template-columns: 1fr auto;
|
||
align-items: end;
|
||
gap: 32px;
|
||
position: relative; z-index: 1;
|
||
}
|
||
.hero__crumb {
|
||
font-size: 11.5px;
|
||
color: var(--mute);
|
||
margin-bottom: 14px;
|
||
display: flex; align-items: center; gap: 8px;
|
||
font-weight: 500;
|
||
}
|
||
.hero__crumb .live {
|
||
display: inline-flex; align-items: center; gap: 6px;
|
||
color: var(--mint);
|
||
}
|
||
.hero__crumb .live::before {
|
||
content: '';
|
||
width: 6px; height: 6px; border-radius: 50%;
|
||
background: var(--mint);
|
||
box-shadow: 0 0 8px var(--mint);
|
||
animation: ping 1.4s ease-in-out infinite;
|
||
}
|
||
@keyframes ping {
|
||
0%, 100% { transform: scale(1); opacity: 1; }
|
||
50% { transform: scale(1.5); opacity: 0.6; }
|
||
}
|
||
.hero__title {
|
||
font-family: 'Newsreader', serif;
|
||
font-weight: 400;
|
||
font-size: 56px;
|
||
line-height: 1.0;
|
||
letter-spacing: -0.025em;
|
||
margin: 0 0 16px;
|
||
color: var(--fg);
|
||
}
|
||
.hero__title em {
|
||
font-style: italic;
|
||
background: linear-gradient(135deg, var(--orchid), var(--lavender) 60%, var(--sky));
|
||
-webkit-background-clip: text;
|
||
background-clip: text;
|
||
color: transparent;
|
||
}
|
||
.hero__sub {
|
||
font-size: 15px;
|
||
color: var(--fg-dim);
|
||
max-width: 560px;
|
||
line-height: 1.6;
|
||
font-weight: 400;
|
||
}
|
||
.hero__sub b { color: var(--fg); font-weight: 500; }
|
||
.hero__meter { text-align: right; }
|
||
.hero__meter-label {
|
||
font-size: 11px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.12em;
|
||
color: var(--mute);
|
||
margin-bottom: 10px;
|
||
font-weight: 500;
|
||
}
|
||
.hero__meter-value {
|
||
font-family: 'Geist', sans-serif;
|
||
font-feature-settings: "ss01", "tnum";
|
||
font-size: 56px;
|
||
color: var(--fg);
|
||
font-weight: 500;
|
||
font-variant-numeric: tabular-nums;
|
||
line-height: 1;
|
||
letter-spacing: -0.03em;
|
||
}
|
||
.hero__meter-value sup {
|
||
font-size: 14px; color: var(--fg-dim); margin-left: 6px; font-weight: 400;
|
||
background: var(--glass-strong); padding: 4px 8px; border-radius: 8px;
|
||
vertical-align: middle;
|
||
}
|
||
.hero__meter-row {
|
||
margin-top: 16px;
|
||
display: flex; gap: 10px;
|
||
justify-content: flex-end;
|
||
}
|
||
.pill {
|
||
display: inline-flex; align-items: center; gap: 6px;
|
||
padding: 5px 11px;
|
||
border-radius: 999px;
|
||
background: var(--glass-strong);
|
||
border: 1px solid var(--rule);
|
||
font-size: 11.5px;
|
||
color: var(--fg-dim);
|
||
}
|
||
.pill b { color: var(--fg); font-weight: 500; font-variant-numeric: tabular-nums; }
|
||
.pill .dot { width: 6px; height: 6px; border-radius: 50%; }
|
||
|
||
/* ============================================================
|
||
STAT GRID — orb cards
|
||
============================================================ */
|
||
.stats {
|
||
display: grid;
|
||
grid-template-columns: repeat(4, 1fr);
|
||
gap: 18px;
|
||
}
|
||
.stat {
|
||
background: var(--glass);
|
||
backdrop-filter: blur(28px) saturate(160%);
|
||
-webkit-backdrop-filter: blur(28px) saturate(160%);
|
||
border: 1px solid var(--rule);
|
||
border-radius: 22px;
|
||
padding: 22px 22px 20px;
|
||
position: relative;
|
||
overflow: hidden;
|
||
cursor: pointer;
|
||
transition: transform .25s cubic-bezier(.4,.4,0,1);
|
||
}
|
||
.stat::after {
|
||
content: '';
|
||
position: absolute; inset: 0;
|
||
border-radius: inherit;
|
||
pointer-events: none;
|
||
background: linear-gradient(180deg, var(--highlight), transparent 30%);
|
||
opacity: 0.4;
|
||
}
|
||
.stat::before {
|
||
content: '';
|
||
position: absolute;
|
||
width: 220px; height: 220px;
|
||
border-radius: 50%;
|
||
right: -100px; top: -90px;
|
||
background: radial-gradient(circle, var(--orb-color, var(--lavender)) 0%, transparent 70%);
|
||
opacity: 0.45;
|
||
pointer-events: none;
|
||
filter: blur(20px);
|
||
transition: transform .4s;
|
||
}
|
||
.stat:hover { transform: translateY(-3px); }
|
||
.stat:hover::before { transform: scale(1.15); }
|
||
.stat__head {
|
||
position: relative; z-index: 1;
|
||
display: flex; align-items: center; justify-content: space-between;
|
||
margin-bottom: 24px;
|
||
}
|
||
.stat__icon {
|
||
width: 36px; height: 36px;
|
||
border-radius: 12px;
|
||
display: grid; place-items: center;
|
||
background: var(--glass-elev);
|
||
border: 1px solid var(--rule);
|
||
color: var(--orb-color, var(--lavender));
|
||
}
|
||
.stat__icon svg { width: 18px; height: 18px; }
|
||
.stat__delta {
|
||
font-family: 'Geist Mono', monospace;
|
||
font-size: 11px;
|
||
padding: 3px 8px;
|
||
border-radius: 999px;
|
||
background: rgba(126, 232, 196, 0.15);
|
||
color: var(--mint);
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
.stat__delta.down { background: rgba(255, 138, 120, 0.15); color: var(--coral); }
|
||
.stat__value {
|
||
position: relative; z-index: 1;
|
||
font-size: 44px;
|
||
font-weight: 500;
|
||
letter-spacing: -0.025em;
|
||
line-height: 1;
|
||
font-variant-numeric: tabular-nums;
|
||
color: var(--fg);
|
||
}
|
||
.stat__value .frac { color: var(--mute); font-size: 24px; font-weight: 400; margin-left: 2px; }
|
||
.stat__caption {
|
||
position: relative; z-index: 1;
|
||
margin-top: 14px;
|
||
font-size: 13px;
|
||
color: var(--fg-dim);
|
||
line-height: 1.45;
|
||
}
|
||
.stat__caption b { color: var(--fg); font-weight: 500; }
|
||
|
||
/* ============================================================
|
||
LAYOUT BLOCKS
|
||
============================================================ */
|
||
.row { display: grid; grid-template-columns: 1.55fr 1fr; gap: 18px; }
|
||
.row-3 { display: grid; grid-template-columns: 1.4fr 1fr; gap: 18px; }
|
||
|
||
.panel {
|
||
background: var(--glass);
|
||
backdrop-filter: blur(28px) saturate(160%);
|
||
-webkit-backdrop-filter: blur(28px) saturate(160%);
|
||
border: 1px solid var(--rule);
|
||
border-radius: 22px;
|
||
box-shadow: var(--shadow-card);
|
||
overflow: hidden;
|
||
position: relative;
|
||
}
|
||
.panel::after {
|
||
content: '';
|
||
position: absolute; inset: 0;
|
||
border-radius: inherit;
|
||
pointer-events: none;
|
||
background: linear-gradient(180deg, var(--highlight), transparent 30%);
|
||
opacity: 0.4;
|
||
}
|
||
.panel__head {
|
||
display: flex; align-items: center; justify-content: space-between;
|
||
padding: 22px 24px 16px;
|
||
position: relative; z-index: 1;
|
||
}
|
||
.panel__title {
|
||
font-family: 'Newsreader', serif;
|
||
font-weight: 400;
|
||
font-size: 24px;
|
||
letter-spacing: -0.02em;
|
||
color: var(--fg);
|
||
margin: 0;
|
||
}
|
||
.panel__title em {
|
||
font-style: italic;
|
||
background: linear-gradient(135deg, var(--orchid), var(--lavender));
|
||
-webkit-background-clip: text; background-clip: text; color: transparent;
|
||
}
|
||
.panel__meta { font-size: 12px; color: var(--mute); display: flex; gap: 14px; align-items: center; }
|
||
.panel__meta b { color: var(--fg); font-weight: 500; font-variant-numeric: tabular-nums; }
|
||
|
||
.seg {
|
||
display: inline-flex; padding: 3px;
|
||
background: var(--glass-strong);
|
||
border: 1px solid var(--rule);
|
||
border-radius: 12px;
|
||
gap: 2px;
|
||
}
|
||
.seg button {
|
||
padding: 5px 12px;
|
||
border: 0; background: transparent;
|
||
color: var(--fg-dim);
|
||
font-size: 12px; font-weight: 500;
|
||
border-radius: 9px;
|
||
cursor: pointer;
|
||
transition: all .15s;
|
||
}
|
||
.seg button.is-active {
|
||
background: var(--glass-elev);
|
||
color: var(--fg);
|
||
box-shadow: inset 0 1px 0 var(--highlight);
|
||
}
|
||
|
||
/* Live signal stream */
|
||
.stream { position: relative; z-index: 1; padding-bottom: 8px; }
|
||
.signal {
|
||
display: grid;
|
||
grid-template-columns: 38px 1fr auto;
|
||
gap: 16px;
|
||
padding: 14px 24px;
|
||
align-items: center;
|
||
transition: background .15s;
|
||
cursor: pointer;
|
||
position: relative;
|
||
}
|
||
.signal + .signal { border-top: 1px solid var(--rule); }
|
||
.signal:hover { background: var(--glass-strong); }
|
||
.signal__avatar {
|
||
width: 38px; height: 38px;
|
||
border-radius: 12px;
|
||
display: grid; place-items: center;
|
||
background: linear-gradient(135deg, var(--avatar-from, var(--lavender)), var(--avatar-to, var(--orchid)));
|
||
color: white;
|
||
box-shadow: 0 4px 14px -4px var(--avatar-from, rgba(184,167,255,0.4)), inset 0 1px 0 rgba(255,255,255,0.3);
|
||
}
|
||
.signal__avatar svg { width: 18px; height: 18px; }
|
||
.signal__head {
|
||
font-size: 14px;
|
||
color: var(--fg);
|
||
line-height: 1.4;
|
||
}
|
||
.signal__head b { font-weight: 600; }
|
||
.signal__head .verb { color: var(--fg-dim); font-weight: 400; }
|
||
.signal__sub {
|
||
margin-top: 4px;
|
||
font-size: 12px;
|
||
color: var(--mute);
|
||
display: flex; align-items: center; gap: 6px;
|
||
}
|
||
.signal__sub .ch {
|
||
color: var(--fg-dim);
|
||
font-family: 'Geist Mono', monospace;
|
||
font-size: 11px;
|
||
background: var(--glass-strong);
|
||
padding: 2px 6px;
|
||
border-radius: 6px;
|
||
}
|
||
.signal__sub .arrow { color: var(--mute); }
|
||
.signal__when {
|
||
text-align: right;
|
||
font-family: 'Geist Mono', monospace;
|
||
font-size: 11px;
|
||
color: var(--mute);
|
||
}
|
||
.signal__when b {
|
||
display: block;
|
||
color: var(--fg-dim);
|
||
font-size: 13px;
|
||
font-weight: 500;
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
|
||
/* Provider deck */
|
||
.providers { padding: 8px 0 16px; position: relative; z-index: 1; }
|
||
.provider {
|
||
display: grid;
|
||
grid-template-columns: 44px 1fr auto;
|
||
gap: 14px;
|
||
padding: 14px 24px;
|
||
align-items: center;
|
||
cursor: pointer;
|
||
transition: background .15s;
|
||
}
|
||
.provider + .provider { border-top: 1px solid var(--rule); }
|
||
.provider:hover { background: var(--glass-strong); }
|
||
.provider__icon {
|
||
width: 44px; height: 44px;
|
||
border-radius: 14px;
|
||
display: grid; place-items: center;
|
||
background: var(--glass-elev);
|
||
border: 1px solid var(--rule);
|
||
color: var(--prov-color, var(--lavender));
|
||
box-shadow: inset 0 1px 0 var(--highlight);
|
||
}
|
||
.provider__icon svg { width: 22px; height: 22px; }
|
||
.provider__name {
|
||
font-size: 14.5px; font-weight: 500; color: var(--fg);
|
||
display: flex; align-items: center; gap: 8px;
|
||
}
|
||
.provider__name .pulse {
|
||
width: 8px; height: 8px;
|
||
border-radius: 50%;
|
||
background: var(--mint);
|
||
box-shadow: 0 0 8px var(--mint);
|
||
animation: ping 1.6s ease-in-out infinite;
|
||
}
|
||
.provider__name .pulse.warn { background: var(--citrus); box-shadow: 0 0 8px var(--citrus); }
|
||
.provider__name .pulse.idle { background: var(--mute); box-shadow: none; opacity: 0.5; animation: none; }
|
||
.provider__sub {
|
||
margin-top: 3px; font-size: 12px; color: var(--mute);
|
||
font-family: 'Geist Mono', monospace;
|
||
}
|
||
.provider__bar {
|
||
width: 110px;
|
||
text-align: right;
|
||
}
|
||
.provider__num {
|
||
font-size: 16.5px; font-weight: 500;
|
||
color: var(--fg);
|
||
font-variant-numeric: tabular-nums;
|
||
letter-spacing: -0.01em;
|
||
}
|
||
.provider__bar-track {
|
||
margin-top: 6px;
|
||
height: 4px;
|
||
border-radius: 2px;
|
||
background: var(--glass-strong);
|
||
overflow: hidden;
|
||
}
|
||
.provider__bar-fill {
|
||
height: 100%;
|
||
background: linear-gradient(90deg, var(--prov-color, var(--lavender)), var(--prov-color-2, var(--orchid)));
|
||
border-radius: 2px;
|
||
box-shadow: 0 0 8px -2px var(--prov-color, var(--lavender));
|
||
}
|
||
|
||
/* Wave chart */
|
||
.wave-wrap { padding: 12px 24px 24px; position: relative; z-index: 1; }
|
||
.wave {
|
||
width: 100%; height: 180px; display: block;
|
||
}
|
||
.wave-legend {
|
||
display: flex; gap: 16px;
|
||
justify-content: center;
|
||
margin-top: 12px;
|
||
font-size: 11px; color: var(--mute);
|
||
}
|
||
.wave-legend span { display: inline-flex; align-items: center; gap: 6px; }
|
||
.wave-legend i {
|
||
display: inline-block; width: 16px; height: 3px; border-radius: 2px;
|
||
}
|
||
|
||
/* Channels — Sankey */
|
||
.channels { padding: 12px 24px 22px; position: relative; z-index: 1; }
|
||
.channel {
|
||
display: grid;
|
||
grid-template-columns: 1fr 70px 1fr;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 12px 0;
|
||
}
|
||
.channel + .channel { border-top: 1px solid var(--rule); }
|
||
.channel__from, .channel__to { display: flex; align-items: center; gap: 10px; font-size: 13px; }
|
||
.channel__to { justify-content: flex-end; text-align: right; }
|
||
.channel__from svg, .channel__to svg { width: 14px; height: 14px; color: var(--fg-dim); }
|
||
.channel__name { color: var(--fg); font-weight: 500; }
|
||
.channel__sub { font-size: 11px; color: var(--mute); margin-top: 2px; font-family: 'Geist Mono', monospace; }
|
||
.channel__wire { position: relative; display: flex; justify-content: center; align-items: center; height: 22px; }
|
||
.channel__wire::before {
|
||
content: '';
|
||
position: absolute; inset: 50% 0 auto 0; height: 2px; border-radius: 2px;
|
||
background: linear-gradient(90deg, var(--lavender), var(--orchid), var(--mint));
|
||
opacity: 0.6;
|
||
}
|
||
.channel__count {
|
||
position: relative; z-index: 1;
|
||
background: var(--glass-elev);
|
||
border: 1px solid var(--rule);
|
||
padding: 2px 9px;
|
||
border-radius: 999px;
|
||
font-family: 'Geist Mono', monospace;
|
||
font-size: 10.5px;
|
||
color: var(--fg);
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
|
||
/* Compose */
|
||
.compose {
|
||
padding: 28px 32px;
|
||
display: grid;
|
||
grid-template-columns: 1fr auto;
|
||
gap: 28px;
|
||
align-items: center;
|
||
position: relative;
|
||
}
|
||
.compose__title {
|
||
font-family: 'Newsreader', serif;
|
||
font-weight: 400;
|
||
font-size: 32px;
|
||
line-height: 1.05;
|
||
letter-spacing: -0.02em;
|
||
margin: 0 0 8px;
|
||
color: var(--fg);
|
||
position: relative; z-index: 1;
|
||
}
|
||
.compose__title em {
|
||
font-style: italic;
|
||
background: linear-gradient(135deg, var(--mint), var(--sky));
|
||
-webkit-background-clip: text; background-clip: text; color: transparent;
|
||
}
|
||
.compose__sub {
|
||
color: var(--fg-dim); font-size: 14px; max-width: 540px;
|
||
line-height: 1.55;
|
||
position: relative; z-index: 1;
|
||
}
|
||
.compose__cta { display: flex; gap: 10px; position: relative; z-index: 1; }
|
||
.ghost-btn {
|
||
display: inline-flex; align-items: center; gap: 8px;
|
||
padding: 0 16px; height: 40px;
|
||
border-radius: 12px;
|
||
border: 1px solid var(--rule-strong);
|
||
background: var(--glass-strong);
|
||
color: var(--fg);
|
||
font-size: 13px; font-weight: 500;
|
||
cursor: pointer;
|
||
transition: all .15s;
|
||
}
|
||
.ghost-btn:hover { background: var(--glass-elev); }
|
||
|
||
/* Footer stamp */
|
||
.stamp {
|
||
text-align: center;
|
||
padding: 24px 0;
|
||
color: var(--mute);
|
||
font-size: 11.5px;
|
||
font-family: 'Geist Mono', monospace;
|
||
}
|
||
|
||
/* Theme toggle */
|
||
.theme-toggle {
|
||
position: fixed; right: 22px; top: 22px; z-index: 50;
|
||
background: var(--glass);
|
||
backdrop-filter: blur(28px) saturate(160%);
|
||
-webkit-backdrop-filter: blur(28px) saturate(160%);
|
||
border: 1px solid var(--rule-strong);
|
||
border-radius: 999px;
|
||
padding: 4px;
|
||
display: inline-flex;
|
||
box-shadow: var(--shadow-card);
|
||
}
|
||
.theme-toggle button {
|
||
padding: 7px 14px;
|
||
border: 0; background: transparent;
|
||
color: var(--fg-dim);
|
||
font-size: 11.5px; font-weight: 500;
|
||
border-radius: 999px;
|
||
cursor: pointer;
|
||
}
|
||
.theme-toggle button.is-active {
|
||
background: var(--fg); color: var(--bg);
|
||
}
|
||
|
||
@keyframes rise {
|
||
from { opacity: 0; transform: translateY(14px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
.stagger > * { animation: rise .6s cubic-bezier(.2,.7,.2,1) both; }
|
||
.stagger > *:nth-child(1){animation-delay:.05s}
|
||
.stagger > *:nth-child(2){animation-delay:.13s}
|
||
.stagger > *:nth-child(3){animation-delay:.21s}
|
||
.stagger > *:nth-child(4){animation-delay:.29s}
|
||
.stagger > *:nth-child(5){animation-delay:.37s}
|
||
.stagger > *:nth-child(6){animation-delay:.45s}
|
||
|
||
@media (max-width: 1100px) {
|
||
.row, .row-3 { grid-template-columns: 1fr; }
|
||
.stats { grid-template-columns: repeat(2, 1fr); }
|
||
}
|
||
@media (max-width: 820px) {
|
||
.shell { grid-template-columns: 1fr; padding: 12px; }
|
||
.rail { position: static; height: auto; }
|
||
.hero__row { grid-template-columns: 1fr; }
|
||
.hero__meter { text-align: left; }
|
||
.hero__meter-row { justify-content: flex-start; }
|
||
.hero__title { font-size: 40px; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="grain"></div>
|
||
|
||
<div class="theme-toggle">
|
||
<button id="t-dark" class="is-active" onclick="setTheme('dark')">Aurora</button>
|
||
<button id="t-pearl" onclick="setTheme('pearl')">Pearl</button>
|
||
</div>
|
||
|
||
<div class="shell">
|
||
|
||
<!-- RAIL -->
|
||
<aside class="rail">
|
||
<div class="rail__brand">
|
||
<div class="rail__logo"></div>
|
||
<div class="rail__brand-text">
|
||
<b>Notify Bridge</b><br>
|
||
<small>v0.5.2</small>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="rail__group">
|
||
<div class="rail__label">Overview</div>
|
||
<nav class="rail__nav">
|
||
<a href="#" class="rail__link is-active">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6"><path d="M3 12h4l3-9 4 18 3-9h4"/></svg>
|
||
Signal
|
||
<span class="count">live</span>
|
||
</a>
|
||
<a href="#" class="rail__link">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6"><rect x="3" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="3" width="7" height="7" rx="1.5"/><rect x="3" y="14" width="7" height="7" rx="1.5"/><rect x="14" y="14" width="7" height="7" rx="1.5"/></svg>
|
||
Providers
|
||
<span class="count">04</span>
|
||
</a>
|
||
</nav>
|
||
</div>
|
||
|
||
<div class="rail__group">
|
||
<div class="rail__label">Routing</div>
|
||
<nav class="rail__nav">
|
||
<a href="#" class="rail__link">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6"><circle cx="12" cy="12" r="3"/><circle cx="12" cy="12" r="8"/><path d="M12 4v3M12 17v3M4 12h3M17 12h3"/></svg>
|
||
Trackers <span class="count">12</span>
|
||
</a>
|
||
<a href="#" class="rail__link">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6"><path d="M4 6h16M4 12h16M4 18h10"/></svg>
|
||
Templates <span class="count">28</span>
|
||
</a>
|
||
<a href="#" class="rail__link">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6"><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18"/></svg>
|
||
Targets <span class="count">19</span>
|
||
</a>
|
||
<a href="#" class="rail__link">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6"><path d="M5 12l3 3 6-6 6 6"/></svg>
|
||
Actions <span class="count">07</span>
|
||
</a>
|
||
</nav>
|
||
</div>
|
||
|
||
<div class="rail__group">
|
||
<div class="rail__label">Operators</div>
|
||
<nav class="rail__nav">
|
||
<a href="#" class="rail__link">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6"><rect x="4" y="4" width="16" height="16" rx="3"/><circle cx="9" cy="10" r="1.2"/><circle cx="15" cy="10" r="1.2"/><path d="M8 16c1.5 1 6.5 1 8 0"/></svg>
|
||
Bots <span class="count">06</span>
|
||
</a>
|
||
<a href="#" class="rail__link">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6"><path d="M4 18l5-5 5 5 5-9"/></svg>
|
||
Commands <span class="count">14</span>
|
||
</a>
|
||
</nav>
|
||
</div>
|
||
|
||
<div class="rail__foot">
|
||
<div class="rail__op">
|
||
<div class="rail__avatar">A</div>
|
||
<div>
|
||
<div class="rail__op-name">alexei</div>
|
||
<div class="rail__op-role">administrator</div>
|
||
</div>
|
||
<div class="rail__chip"></div>
|
||
</div>
|
||
</div>
|
||
</aside>
|
||
|
||
<!-- MAIN -->
|
||
<main class="main stagger">
|
||
|
||
<!-- TOP BAR -->
|
||
<div class="topbar">
|
||
<div class="search">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4.3-4.3"/></svg>
|
||
Search trackers, templates, targets…
|
||
<span class="kbd">⌘K</span>
|
||
</div>
|
||
<button class="icon-btn" title="Notifications">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10 21a2 2 0 0 0 4 0"/></svg>
|
||
</button>
|
||
<button class="icon-btn" title="Theme">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg>
|
||
</button>
|
||
<button class="new-btn">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M5 12h14M12 5v14"/></svg>
|
||
New tracker
|
||
</button>
|
||
</div>
|
||
|
||
<!-- HERO -->
|
||
<section class="panel hero">
|
||
<div class="hero__row">
|
||
<div>
|
||
<div class="hero__crumb">
|
||
<span>Control</span><span>·</span><span class="live">Live</span>
|
||
</div>
|
||
<h1 class="hero__title">Tonight, <em>everything</em><br>is flowing.</h1>
|
||
<p class="hero__sub">
|
||
Four providers are listening, twelve trackers are armed, and the bridge has dispatched
|
||
<b>2 814</b> messages across nineteen targets in the last 24 hours.
|
||
Nothing is queued. Nothing is failing.
|
||
</p>
|
||
</div>
|
||
<div class="hero__meter">
|
||
<div class="hero__meter-label">throughput · 24h</div>
|
||
<div class="hero__meter-value">2 814<sup>msgs</sup></div>
|
||
<div class="hero__meter-row">
|
||
<span class="pill"><span class="dot" style="background: var(--mint)"></span><b>99.7%</b> delivered</span>
|
||
<span class="pill"><span class="dot" style="background: var(--sky)"></span><b>148ms</b> p50</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- STATS -->
|
||
<section class="stats">
|
||
<div class="stat" style="--orb-color: var(--lavender)">
|
||
<div class="stat__head">
|
||
<div class="stat__icon">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><rect x="3" y="3" width="7" height="7" rx="2"/><rect x="14" y="3" width="7" height="7" rx="2"/><rect x="3" y="14" width="7" height="7" rx="2"/><rect x="14" y="14" width="7" height="7" rx="2"/></svg>
|
||
</div>
|
||
<span class="stat__delta">+1 wk</span>
|
||
</div>
|
||
<div class="stat__value">04</div>
|
||
<div class="stat__caption">Providers · <b>Immich</b>, Gitea, GitHub, RSS</div>
|
||
</div>
|
||
|
||
<div class="stat" style="--orb-color: var(--sky)">
|
||
<div class="stat__head">
|
||
<div class="stat__icon">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><circle cx="12" cy="12" r="3"/><circle cx="12" cy="12" r="8"/><path d="M12 4v3M12 17v3M4 12h3M17 12h3"/></svg>
|
||
</div>
|
||
<span class="stat__delta">+3 24h</span>
|
||
</div>
|
||
<div class="stat__value">12<span class="frac">/14</span></div>
|
||
<div class="stat__caption">Trackers armed · <b>2 paused</b> awaiting credentials</div>
|
||
</div>
|
||
|
||
<div class="stat" style="--orb-color: var(--mint)">
|
||
<div class="stat__head">
|
||
<div class="stat__icon">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18"/></svg>
|
||
</div>
|
||
<span class="stat__delta">+2 wk</span>
|
||
</div>
|
||
<div class="stat__value">19</div>
|
||
<div class="stat__caption">Targets · <b>5 channels</b> · TG, Matrix, Mail, ntfy, Discord</div>
|
||
</div>
|
||
|
||
<div class="stat" style="--orb-color: var(--coral)">
|
||
<div class="stat__head">
|
||
<div class="stat__icon">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><circle cx="12" cy="12" r="9"/><path d="M12 7v6M12 16v0.5"/></svg>
|
||
</div>
|
||
<span class="stat__delta down">−4 wk</span>
|
||
</div>
|
||
<div class="stat__value">02</div>
|
||
<div class="stat__caption">Failures · <b>auto-recovered</b> on Fastmail SMTP</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- SIGNAL + PROVIDERS -->
|
||
<section class="row">
|
||
|
||
<!-- SIGNAL STREAM -->
|
||
<div class="panel">
|
||
<div class="panel__head">
|
||
<h2 class="panel__title">Signal <em>stream</em></h2>
|
||
<div class="panel__meta">
|
||
<div class="seg">
|
||
<button class="is-active">All</button>
|
||
<button>Assets</button>
|
||
<button>Sharing</button>
|
||
<button>Commands</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="stream">
|
||
<div class="signal" style="--avatar-from: var(--lavender); --avatar-to: var(--orchid)">
|
||
<div class="signal__avatar">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="3" y="5" width="18" height="14" rx="2.5"/><circle cx="9" cy="11" r="2"/><path d="M3 17l5-5 4 4 3-3 6 6"/></svg>
|
||
</div>
|
||
<div>
|
||
<div class="signal__head"><span class="verb">Immich added</span> <b>14 assets</b> <span class="verb">to</span> <b>«Семейный 2025»</b></div>
|
||
<div class="signal__sub">
|
||
<span class="ch">family-photos</span><span class="arrow">→</span>
|
||
<span class="ch">@family · telegram</span><span class="arrow">→</span>
|
||
<span class="ch">assets_added.ru</span>
|
||
</div>
|
||
</div>
|
||
<div class="signal__when"><b>02:14</b>just now</div>
|
||
</div>
|
||
|
||
<div class="signal" style="--avatar-from: var(--mint); --avatar-to: var(--sky)">
|
||
<div class="signal__avatar">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="12" r="9"/><path d="M9 8l3-3 3 3M9 16l3 3 3-3M5 12h14"/></svg>
|
||
</div>
|
||
<div>
|
||
<div class="signal__head"><span class="verb">Gitea pushed</span> <b>3 commits</b> <span class="verb">to</span> <b>notify-bridge/main</b></div>
|
||
<div class="signal__sub">
|
||
<span class="ch">repo-changes</span><span class="arrow">→</span>
|
||
<span class="ch">#dev-room · matrix</span>
|
||
</div>
|
||
</div>
|
||
<div class="signal__when"><b>02:13</b>17s ago</div>
|
||
</div>
|
||
|
||
<div class="signal" style="--avatar-from: var(--citrus); --avatar-to: var(--coral)">
|
||
<div class="signal__avatar">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M22 2L11 13M22 2l-7 20-4-9-9-4z"/></svg>
|
||
</div>
|
||
<div>
|
||
<div class="signal__head"><span class="verb">Telegram bot received</span> <b>/recent</b> <span class="verb">from</span> <b>@alex</b></div>
|
||
<div class="signal__sub">
|
||
<span class="ch">@notifybridge_bot</span><span class="arrow">→</span>
|
||
<span class="ch">resolved 5 · 142ms</span>
|
||
</div>
|
||
</div>
|
||
<div class="signal__when"><b>02:13</b>36s ago</div>
|
||
</div>
|
||
|
||
<div class="signal" style="--avatar-from: var(--orchid); --avatar-to: var(--lavender)">
|
||
<div class="signal__avatar">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><path d="M8.5 13.5l7 4M8.5 10.5l7-4"/></svg>
|
||
</div>
|
||
<div>
|
||
<div class="signal__head"><span class="verb">Immich shared link</span> <b>expired</b> <span class="verb">on</span> <b>«Прага 24»</b></div>
|
||
<div class="signal__sub">
|
||
<span class="ch">link-watch</span><span class="arrow">→</span>
|
||
<span class="ch">photos@dolgolyov.dev · email</span>
|
||
</div>
|
||
</div>
|
||
<div class="signal__when"><b>02:12</b>1m ago</div>
|
||
</div>
|
||
|
||
<div class="signal" style="--avatar-from: var(--coral); --avatar-to: var(--orchid)">
|
||
<div class="signal__avatar">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M22 4H2l8 9v7l4 2v-9z"/></svg>
|
||
</div>
|
||
<div>
|
||
<div class="signal__head"><span class="verb">Email</span> · <b>SMTP retry</b> <span class="verb">on</span> <b>smtp.fastmail.com</b></div>
|
||
<div class="signal__sub">
|
||
<span class="ch">backoff 2.4s</span><span class="arrow">→</span>
|
||
<span class="ch">delivered on attempt 2/3</span>
|
||
</div>
|
||
</div>
|
||
<div class="signal__when"><b>02:11</b>3m ago</div>
|
||
</div>
|
||
|
||
<div class="signal" style="--avatar-from: var(--sky); --avatar-to: var(--mint)">
|
||
<div class="signal__avatar">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="3" y="4" width="18" height="16" rx="3"/><path d="M3 9h18M8 14h2M14 14h2"/></svg>
|
||
</div>
|
||
<div>
|
||
<div class="signal__head"><span class="verb">Action</span> <b>memory_dispatch</b> <span class="verb">ran for</span> <b>«Один год назад»</b></div>
|
||
<div class="signal__sub">
|
||
<span class="ch">6 recipients</span><span class="arrow">→</span>
|
||
<span class="ch">2 telegram · 3 matrix · 1 ntfy</span>
|
||
</div>
|
||
</div>
|
||
<div class="signal__when"><b>02:09</b>4m ago</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- PROVIDERS -->
|
||
<div class="panel">
|
||
<div class="panel__head">
|
||
<h2 class="panel__title">On <em>watch</em></h2>
|
||
<div class="panel__meta"><b>04</b>providers</div>
|
||
</div>
|
||
<div class="providers">
|
||
<div class="provider" style="--prov-color: var(--lavender); --prov-color-2: var(--orchid)">
|
||
<div class="provider__icon">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><rect x="3" y="5" width="18" height="14" rx="2.5"/><circle cx="9" cy="11" r="2"/><path d="M3 17l5-5 4 4 3-3 6 6"/></svg>
|
||
</div>
|
||
<div>
|
||
<div class="provider__name"><span class="pulse"></span>Immich</div>
|
||
<div class="provider__sub">photos.dolgolyov.dev · 8 trackers</div>
|
||
</div>
|
||
<div class="provider__bar">
|
||
<div class="provider__num">1 942</div>
|
||
<div class="provider__bar-track"><div class="provider__bar-fill" style="width: 100%"></div></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="provider" style="--prov-color: var(--mint); --prov-color-2: var(--sky)">
|
||
<div class="provider__icon">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><circle cx="12" cy="12" r="9"/><path d="M9 8l3-3 3 3M9 16l3 3 3-3M5 12h14"/></svg>
|
||
</div>
|
||
<div>
|
||
<div class="provider__name"><span class="pulse"></span>Gitea</div>
|
||
<div class="provider__sub">git.dolgolyov-family.by · 2 trackers</div>
|
||
</div>
|
||
<div class="provider__bar">
|
||
<div class="provider__num">368</div>
|
||
<div class="provider__bar-track"><div class="provider__bar-fill" style="width: 19%"></div></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="provider" style="--prov-color: var(--citrus); --prov-color-2: var(--coral)">
|
||
<div class="provider__icon">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><path d="M12 2a10 10 0 0 0-3 19.5c.5 0 .7-.3.7-.6v-2c-2.7.6-3.3-1.3-3.3-1.3-.4-1-1-1.3-1-1.3-1-.7.1-.7.1-.7 1 .1 1.5 1 1.5 1 1 1.6 2.5 1.2 3.1.9.1-.7.4-1.2.7-1.5-2.1-.2-4.4-1-4.4-4.7 0-1 .4-1.9 1-2.6-.1-.3-.4-1.2.1-2.6 0 0 .8-.3 2.7 1 .8-.2 1.6-.3 2.5-.3.8 0 1.7.1 2.5.3 1.9-1.3 2.7-1 2.7-1 .5 1.4.2 2.3.1 2.6.6.7 1 1.6 1 2.6 0 3.7-2.3 4.5-4.4 4.7.4.3.7 1 .7 1.9v2.8c0 .3.2.6.7.6A10 10 0 0 0 12 2z"/></svg>
|
||
</div>
|
||
<div>
|
||
<div class="provider__name"><span class="pulse warn"></span>GitHub</div>
|
||
<div class="provider__sub">anthropics/claude-code · 1 tracker</div>
|
||
</div>
|
||
<div class="provider__bar">
|
||
<div class="provider__num">88</div>
|
||
<div class="provider__bar-track"><div class="provider__bar-fill" style="width: 5%"></div></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="provider" style="--prov-color: var(--sky); --prov-color-2: var(--lavender)">
|
||
<div class="provider__icon">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><path d="M5 19V5l8 7-8 7zM13 5l8 7-8 7"/></svg>
|
||
</div>
|
||
<div>
|
||
<div class="provider__name"><span class="pulse idle"></span>RSS · 7 feeds</div>
|
||
<div class="provider__sub">last seen 14m ago · idle</div>
|
||
</div>
|
||
<div class="provider__bar">
|
||
<div class="provider__num">416</div>
|
||
<div class="provider__bar-track"><div class="provider__bar-fill" style="width: 21%"></div></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="provider" style="opacity: 0.55; cursor: pointer">
|
||
<div class="provider__icon" style="background: transparent; border-style: dashed">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><path d="M12 5v14M5 12h14"/></svg>
|
||
</div>
|
||
<div>
|
||
<div class="provider__name" style="color: var(--fg-dim)">Add provider</div>
|
||
<div class="provider__sub">9 integrations available</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- WAVE + CHANNELS -->
|
||
<section class="row-3">
|
||
<div class="panel">
|
||
<div class="panel__head">
|
||
<h2 class="panel__title">Pulse · <em>last seven days</em></h2>
|
||
<div class="panel__meta">
|
||
<div class="seg">
|
||
<button>24h</button>
|
||
<button class="is-active">7d</button>
|
||
<button>30d</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="wave-wrap">
|
||
<svg class="wave" viewBox="0 0 700 180" preserveAspectRatio="none">
|
||
<defs>
|
||
<linearGradient id="g1" x1="0" x2="0" y1="0" y2="1">
|
||
<stop offset="0%" stop-color="#b8a7ff" stop-opacity="0.5"/>
|
||
<stop offset="100%" stop-color="#b8a7ff" stop-opacity="0"/>
|
||
</linearGradient>
|
||
<linearGradient id="g2" x1="0" x2="0" y1="0" y2="1">
|
||
<stop offset="0%" stop-color="#ff9ec4" stop-opacity="0.5"/>
|
||
<stop offset="100%" stop-color="#ff9ec4" stop-opacity="0"/>
|
||
</linearGradient>
|
||
<linearGradient id="g3" x1="0" x2="0" y1="0" y2="1">
|
||
<stop offset="0%" stop-color="#7ee8c4" stop-opacity="0.5"/>
|
||
<stop offset="100%" stop-color="#7ee8c4" stop-opacity="0"/>
|
||
</linearGradient>
|
||
</defs>
|
||
<!-- back wave (assets) -->
|
||
<path fill="url(#g1)" d="M0 140 C 70 100, 140 110, 200 80 S 320 60, 380 90 S 500 50, 570 70 S 660 100, 700 80 L 700 180 L 0 180 Z"/>
|
||
<path fill="none" stroke="#b8a7ff" stroke-width="2" d="M0 140 C 70 100, 140 110, 200 80 S 320 60, 380 90 S 500 50, 570 70 S 660 100, 700 80"/>
|
||
<!-- mid wave (commands) -->
|
||
<path fill="url(#g2)" d="M0 160 C 80 140, 160 145, 240 130 S 360 120, 420 135 S 540 110, 610 125 S 680 145, 700 135 L 700 180 L 0 180 Z"/>
|
||
<path fill="none" stroke="#ff9ec4" stroke-width="2" d="M0 160 C 80 140, 160 145, 240 130 S 360 120, 420 135 S 540 110, 610 125 S 680 145, 700 135"/>
|
||
<!-- front wave (sharing) -->
|
||
<path fill="url(#g3)" d="M0 170 C 90 162, 170 168, 260 158 S 380 150, 440 160 S 560 145, 620 152 S 680 165, 700 160 L 700 180 L 0 180 Z"/>
|
||
<path fill="none" stroke="#7ee8c4" stroke-width="2" d="M0 170 C 90 162, 170 168, 260 158 S 380 150, 440 160 S 560 145, 620 152 S 680 165, 700 160"/>
|
||
<!-- vertical highlight -->
|
||
<line x1="500" y1="20" x2="500" y2="180" stroke="rgba(255,255,255,0.18)" stroke-dasharray="3 3"/>
|
||
<circle cx="500" cy="62" r="5" fill="#b8a7ff" stroke="white" stroke-width="2"/>
|
||
<text x="510" y="40" fill="#f3f1ff" font-size="11" font-family="Geist Mono">Sun 21:00 · 312/h</text>
|
||
</svg>
|
||
<div class="wave-legend">
|
||
<span><i style="background:#b8a7ff"></i>Assets · 1 942</span>
|
||
<span><i style="background:#ff9ec4"></i>Commands · 488</span>
|
||
<span><i style="background:#7ee8c4"></i>Sharing · 312</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="panel">
|
||
<div class="panel__head">
|
||
<h2 class="panel__title">Active <em>wires</em></h2>
|
||
<div class="panel__meta"><b>09</b>routes</div>
|
||
</div>
|
||
<div class="channels">
|
||
<div class="channel">
|
||
<div class="channel__from">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><rect x="3" y="5" width="18" height="14" rx="2"/><circle cx="9" cy="11" r="2"/></svg>
|
||
<div>
|
||
<div class="channel__name">Immich · family</div>
|
||
<div class="channel__sub">family-photos</div>
|
||
</div>
|
||
</div>
|
||
<div class="channel__wire"><span class="channel__count">1 942</span></div>
|
||
<div class="channel__to">
|
||
<div>
|
||
<div class="channel__name">@family</div>
|
||
<div class="channel__sub">telegram · group</div>
|
||
</div>
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><path d="M22 2L11 13M22 2l-7 20-4-9-9-4z"/></svg>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="channel">
|
||
<div class="channel__from">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><circle cx="12" cy="12" r="9"/></svg>
|
||
<div>
|
||
<div class="channel__name">Gitea · main</div>
|
||
<div class="channel__sub">repo-changes</div>
|
||
</div>
|
||
</div>
|
||
<div class="channel__wire"><span class="channel__count">368</span></div>
|
||
<div class="channel__to">
|
||
<div>
|
||
<div class="channel__name">#dev-room</div>
|
||
<div class="channel__sub">matrix</div>
|
||
</div>
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><path d="M4 4h16v16H4z"/></svg>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="channel">
|
||
<div class="channel__from">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><rect x="3" y="5" width="18" height="14" rx="2"/></svg>
|
||
<div>
|
||
<div class="channel__name">Immich · share-watch</div>
|
||
<div class="channel__sub">link-watch</div>
|
||
</div>
|
||
</div>
|
||
<div class="channel__wire"><span class="channel__count">312</span></div>
|
||
<div class="channel__to">
|
||
<div>
|
||
<div class="channel__name">photos@dolgolyov.dev</div>
|
||
<div class="channel__sub">email</div>
|
||
</div>
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><path d="M4 4h16v16H4zM4 4l8 8 8-8"/></svg>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="channel">
|
||
<div class="channel__from">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><path d="M5 19V5l8 7-8 7z"/></svg>
|
||
<div>
|
||
<div class="channel__name">RSS · Hacker News</div>
|
||
<div class="channel__sub">daily-digest</div>
|
||
</div>
|
||
</div>
|
||
<div class="channel__wire"><span class="channel__count">88</span></div>
|
||
<div class="channel__to">
|
||
<div>
|
||
<div class="channel__name">notify · ntfy.sh</div>
|
||
<div class="channel__sub">personal</div>
|
||
</div>
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"><path d="M12 2a7 7 0 0 0-7 7v5l-2 3h18l-2-3V9a7 7 0 0 0-7-7z"/></svg>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- COMPOSE -->
|
||
<section class="panel compose" style="background: linear-gradient(135deg, rgba(126,232,196,0.08), rgba(142,201,255,0.10), rgba(184,167,255,0.10)), var(--glass);">
|
||
<div>
|
||
<h3 class="compose__title">Pick a source. Choose a channel. <em>Compose the wire.</em></h3>
|
||
<p class="compose__sub">
|
||
The composer walks you from provider → tracker → template → target in four steps,
|
||
with live preview at every stage. Or paste a webhook URL and we'll infer the rest.
|
||
</p>
|
||
</div>
|
||
<div class="compose__cta">
|
||
<button class="ghost-btn">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/></svg>
|
||
Import JSON
|
||
</button>
|
||
<button class="new-btn" style="height: 40px">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
|
||
Start composing
|
||
</button>
|
||
</div>
|
||
</section>
|
||
|
||
<div class="stamp">Notify Bridge · v0.5.2 · build 770c198</div>
|
||
</main>
|
||
</div>
|
||
|
||
<script>
|
||
function setTheme(t) {
|
||
document.documentElement.setAttribute('data-theme', t);
|
||
document.getElementById('t-dark').classList.toggle('is-active', t === 'dark');
|
||
document.getElementById('t-pearl').classList.toggle('is-active', t === 'pearl');
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|