Files
alexei.dolgolyov 1e357244e1 chore(design): add aurora redesign mockups + chooser
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.
2026-04-25 01:11:42 +03:00

1411 lines
52 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" 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&thinsp;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&thinsp;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>