68a6acfa24
Add a fail-open Python sidecar (livekit/voice-limit-guard.py) that fronts lk-jwt-service to enforce per-room voice participant caps for ALL Matrix clients, not just Lotus Chat: - lk-jwt-service moved to :8071 (systemd drop-in), guard owns :8070 so NPM's existing /sfu/get + /get_token proxy targets are unchanged - guard reads io.lotus.voice_limit.max_users (Synapse admin API, cached), forwards to lk-jwt-service, and on an issued token decodes the LiveKit alias + requester, counts distinct Matrix users via LiveKit ListParticipants, and returns 403 when the room is full (rejoins/extra devices allowed) - any error fails open (returns upstream response) so calls never break - systemd/voice-limit-guard.service; README documents ports, setup, revert Also update landing page: voice limit is now server-enforced for all clients. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1358 lines
77 KiB
HTML
1358 lines
77 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Lotus Guild Matrix</title>
|
||
<style>
|
||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
|
||
body {
|
||
background: #0a0a0a;
|
||
color: #e0e0e0;
|
||
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
|
||
min-height: 100vh;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 20px 16px 40px;
|
||
}
|
||
|
||
body::before {
|
||
content: '';
|
||
position: fixed;
|
||
top: 50%;
|
||
left: 50%;
|
||
width: 900px;
|
||
height: 900px;
|
||
transform: translate(-50%, -50%);
|
||
background: radial-gradient(circle, rgba(152, 0, 0, 0.07) 0%, transparent 65%);
|
||
pointer-events: none;
|
||
z-index: 0;
|
||
}
|
||
|
||
.container {
|
||
position: relative;
|
||
z-index: 1;
|
||
text-align: center;
|
||
width: 100%;
|
||
max-width: 900px;
|
||
}
|
||
|
||
.logo {
|
||
width: 140px;
|
||
height: 140px;
|
||
margin: 0 auto 28px;
|
||
border-radius: 50%;
|
||
filter: drop-shadow(0 0 24px rgba(152, 0, 0, 0.35));
|
||
animation: float 6s ease-in-out infinite;
|
||
}
|
||
|
||
@keyframes float {
|
||
0%, 100% { transform: translateY(0); }
|
||
50% { transform: translateY(-8px); }
|
||
}
|
||
|
||
h1 {
|
||
font-size: 2rem;
|
||
font-weight: 300;
|
||
letter-spacing: 0.15em;
|
||
text-transform: uppercase;
|
||
color: #fff;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
h1 span { color: #980000; font-weight: 600; }
|
||
|
||
.subtitle {
|
||
font-size: 0.85rem;
|
||
color: #555;
|
||
letter-spacing: 0.3em;
|
||
text-transform: uppercase;
|
||
margin-bottom: 36px;
|
||
}
|
||
|
||
/* ─── Cards / Panels ─── */
|
||
.card {
|
||
background: linear-gradient(145deg, rgba(22,22,22,0.95), rgba(14,14,14,0.98));
|
||
border: 1px solid rgba(152,0,0,0.2);
|
||
border-radius: 16px;
|
||
padding: 32px;
|
||
max-width: 560px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.card h2 {
|
||
font-size: 0.9rem;
|
||
font-weight: 500;
|
||
color: #980000;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.15em;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
/* ─── Steps ─── */
|
||
.steps { list-style: none; text-align: left; margin-bottom: 28px; }
|
||
|
||
.steps li {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 14px;
|
||
padding: 12px 0;
|
||
border-bottom: 1px solid rgba(255,255,255,0.04);
|
||
}
|
||
|
||
.steps li:last-child { border-bottom: none; }
|
||
|
||
.step-num {
|
||
flex-shrink: 0;
|
||
width: 28px; height: 28px;
|
||
background: rgba(152,0,0,0.12);
|
||
border: 1px solid rgba(152,0,0,0.35);
|
||
border-radius: 50%;
|
||
display: flex; align-items: center; justify-content: center;
|
||
font-size: 0.8rem; font-weight: 600; color: #c44;
|
||
}
|
||
|
||
.step-text { padding-top: 3px; font-size: 0.95rem; line-height: 1.5; color: #bbb; }
|
||
.step-text strong { color: #e0e0e0; }
|
||
|
||
.homeserver {
|
||
display: inline-block;
|
||
background: rgba(152,0,0,0.1);
|
||
border: 1px solid rgba(152,0,0,0.25);
|
||
color: #e88;
|
||
font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
|
||
font-size: 0.85rem;
|
||
padding: 3px 10px;
|
||
border-radius: 6px;
|
||
}
|
||
|
||
a { color: #c44; text-decoration: none; }
|
||
.step-text a, .option-block a {
|
||
border-bottom: 1px solid rgba(204,68,68,0.3);
|
||
transition: border-color 0.2s;
|
||
}
|
||
.step-text a:hover, .option-block a:hover { border-bottom-color: #c44; }
|
||
|
||
/* ─── Or divider ─── */
|
||
.or-divider {
|
||
display: flex; align-items: center; gap: 10px;
|
||
margin: 4px 0 10px 42px;
|
||
color: #444; font-size: 0.78rem; letter-spacing: 0.1em; text-transform: uppercase;
|
||
}
|
||
.or-divider::before, .or-divider::after {
|
||
content: ''; flex: 1; height: 1px; background: rgba(255,255,255,0.06);
|
||
}
|
||
|
||
.option-block {
|
||
margin-left: 42px;
|
||
padding: 12px 14px;
|
||
background: rgba(255,255,255,0.03);
|
||
border: 1px solid rgba(255,255,255,0.06);
|
||
border-radius: 8px;
|
||
text-align: left; font-size: 0.88rem; color: #888; line-height: 1.5;
|
||
}
|
||
|
||
.divider { height: 1px; background: rgba(152,0,0,0.15); margin: 24px 0; }
|
||
|
||
/* ─── Tags ─── */
|
||
.tag {
|
||
font-size: 0.65rem;
|
||
background: rgba(255,255,255,0.08);
|
||
border: 1px solid rgba(255,255,255,0.1);
|
||
padding: 2px 8px; border-radius: 4px;
|
||
text-transform: uppercase; letter-spacing: 0.05em; color: #bbb;
|
||
white-space: nowrap;
|
||
}
|
||
.tag.voice { background: rgba(0,180,120,0.2); border-color: rgba(0,180,120,0.4); color: #5effc4; }
|
||
.tag.beta { background: rgba(255,180,0,0.15); border-color: rgba(255,180,0,0.3); color: #ffcc55; }
|
||
.tag.dev { background: rgba(160,80,255,0.15); border-color: rgba(160,80,255,0.3); color: #cc88ff; }
|
||
.tag.rust { background: rgba(80,140,255,0.15); border-color: rgba(80,140,255,0.3); color: #88aaff; }
|
||
.tag.warn { background: rgba(255,140,0,0.15); border-color: rgba(255,140,0,0.3); color: #ffaa44; }
|
||
.tag.dim { background: rgba(255,255,255,0.04); border-color: rgba(255,255,255,0.08); color: #666; }
|
||
|
||
/* ─── Featured client ─── */
|
||
.client-featured { margin-bottom: 16px; }
|
||
|
||
.client-featured a {
|
||
display: flex; flex-direction: column; align-items: center; gap: 6px;
|
||
background: linear-gradient(135deg, rgba(152,0,0,0.25), rgba(120,0,0,0.15));
|
||
border: 1px solid rgba(152,0,0,0.55);
|
||
color: #fff; text-decoration: none;
|
||
padding: 18px 24px; border-radius: 12px;
|
||
transition: all 0.25s ease;
|
||
box-shadow: 0 0 20px rgba(152,0,0,0.1);
|
||
}
|
||
.client-featured a:hover {
|
||
background: linear-gradient(135deg, rgba(152,0,0,0.38), rgba(120,0,0,0.25));
|
||
border-color: rgba(152,0,0,0.8);
|
||
box-shadow: 0 0 32px rgba(152,0,0,0.25);
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
.client-name { font-size: 1.15rem; font-weight: 600; letter-spacing: 0.05em; }
|
||
.client-desc { font-size: 0.82rem; color: #ccc; }
|
||
|
||
.tag-row { display: flex; gap: 6px; flex-wrap: wrap; justify-content: center; margin-top: 2px; }
|
||
|
||
/* Also-available note */
|
||
.also-available {
|
||
font-size: 0.78rem; color: #555; margin-top: 8px; line-height: 1.6;
|
||
}
|
||
.also-available a { color: #666; border-bottom: 1px solid rgba(102,102,102,0.3); transition: color 0.2s; }
|
||
.also-available a:hover { color: #999; }
|
||
|
||
/* ─── Space join ─── */
|
||
.space-join {
|
||
margin-top: 20px; padding: 16px 20px;
|
||
background: rgba(152,0,0,0.06);
|
||
border: 1px solid rgba(152,0,0,0.2); border-radius: 10px;
|
||
}
|
||
.space-join p { font-size: 0.82rem; color: #666; margin-bottom: 10px; }
|
||
.space-join a {
|
||
display: inline-block;
|
||
background: rgba(152,0,0,0.15);
|
||
border: 1px solid rgba(152,0,0,0.35);
|
||
color: #c44; text-decoration: none;
|
||
padding: 8px 20px; border-radius: 8px; font-size: 0.88rem;
|
||
transition: all 0.25s ease;
|
||
}
|
||
.space-join a:hover { background: rgba(152,0,0,0.25); color: #fff; }
|
||
|
||
/* ─── Secondary client cards ─── */
|
||
.clients-section h3 {
|
||
font-size: 0.8rem; font-weight: 500; color: #666;
|
||
text-transform: uppercase; letter-spacing: 0.12em; margin-bottom: 14px;
|
||
}
|
||
|
||
.client-group { margin-bottom: 18px; }
|
||
|
||
.client-group-label {
|
||
font-size: 0.75rem; color: #555;
|
||
text-transform: uppercase; letter-spacing: 0.1em;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.client-cards { display: flex; flex-direction: column; gap: 8px; }
|
||
|
||
.client-card {
|
||
display: flex; flex-direction: column; gap: 6px;
|
||
background: rgba(255,255,255,0.025);
|
||
border: 1px solid rgba(255,255,255,0.06);
|
||
border-radius: 10px; padding: 12px 14px; text-align: left;
|
||
transition: border-color 0.2s;
|
||
}
|
||
.client-card:hover { border-color: rgba(152,0,0,0.3); }
|
||
|
||
.client-card-top {
|
||
display: flex; align-items: center;
|
||
justify-content: space-between; gap: 10px; flex-wrap: wrap;
|
||
}
|
||
|
||
.client-card-name {
|
||
font-size: 0.95rem; font-weight: 600; color: #e0e0e0;
|
||
text-decoration: none;
|
||
border-bottom: 1px solid rgba(204,68,68,0.2);
|
||
transition: color 0.2s, border-color 0.2s;
|
||
}
|
||
.client-card-name:hover { color: #c44; border-bottom-color: #c44; }
|
||
|
||
.client-card-tags { display: flex; gap: 5px; flex-wrap: wrap; }
|
||
.client-card-desc { font-size: 0.8rem; color: #666; line-height: 1.45; }
|
||
|
||
/* ─── Comparison table ─── */
|
||
.comparison-section {
|
||
margin-top: 32px;
|
||
background: linear-gradient(145deg, rgba(22,22,22,0.95), rgba(14,14,14,0.98));
|
||
border: 1px solid rgba(152,0,0,0.2);
|
||
border-radius: 16px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.comparison-title {
|
||
font-size: 0.78rem; font-weight: 500; color: #980000;
|
||
text-transform: uppercase; letter-spacing: 0.15em;
|
||
padding: 16px 20px 12px;
|
||
border-bottom: 1px solid rgba(152,0,0,0.1);
|
||
}
|
||
|
||
.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
|
||
|
||
table {
|
||
width: 100%; border-collapse: collapse;
|
||
font-size: 0.78rem; min-width: 760px;
|
||
}
|
||
|
||
thead tr { border-bottom: 1px solid rgba(152,0,0,0.15); }
|
||
|
||
th {
|
||
padding: 10px 8px; text-align: center;
|
||
font-size: 0.7rem; font-weight: 600;
|
||
color: #888; text-transform: uppercase; letter-spacing: 0.08em;
|
||
background: rgba(255,255,255,0.02);
|
||
}
|
||
th:first-child { text-align: left; padding-left: 16px; min-width: 140px; }
|
||
th.ours { color: #c66; }
|
||
|
||
th small { display: block; font-size: 0.6rem; font-weight: 400; color: #555; margin-top: 2px; text-transform: none; letter-spacing: 0; }
|
||
|
||
tr { border-bottom: 1px solid rgba(255,255,255,0.03); }
|
||
tr:last-child { border-bottom: none; }
|
||
|
||
tr.section-header td {
|
||
background: rgba(152,0,0,0.06);
|
||
color: #770000; font-weight: 600;
|
||
font-size: 0.65rem; text-transform: uppercase; letter-spacing: 0.12em;
|
||
padding: 6px 8px 5px 16px; text-align: left;
|
||
}
|
||
|
||
td {
|
||
padding: 8px 8px; text-align: center; vertical-align: middle;
|
||
color: #888; line-height: 1.3;
|
||
}
|
||
td:first-child {
|
||
text-align: left; padding-left: 16px;
|
||
color: #aaa; font-size: 0.78rem;
|
||
}
|
||
td small { display: block; font-size: 0.68rem; color: #555; margin-top: 1px; }
|
||
|
||
.yes { color: #5effc4; font-size: 1rem; }
|
||
.part { color: #ffcc55; font-size: 0.9rem; }
|
||
.no { color: #444; font-size: 1rem; }
|
||
|
||
/* highlight our hosted client column */
|
||
th.ours, td.ours { background: rgba(152,0,0,0.04); }
|
||
|
||
/* ─── Legend ─── */
|
||
.legend {
|
||
display: flex; gap: 18px; justify-content: center; flex-wrap: wrap;
|
||
padding: 10px 16px 14px;
|
||
border-top: 1px solid rgba(255,255,255,0.04);
|
||
font-size: 0.72rem; color: #555;
|
||
}
|
||
.legend span { display: flex; align-items: center; gap: 5px; }
|
||
|
||
/* ─── Security note ─── */
|
||
.security-note {
|
||
margin: 0 20px 16px;
|
||
padding: 10px 14px;
|
||
background: rgba(80,140,255,0.04);
|
||
border: 1px solid rgba(80,140,255,0.12);
|
||
border-radius: 8px;
|
||
font-size: 0.76rem; color: #556; line-height: 1.55; text-align: left;
|
||
}
|
||
.security-note strong { color: #88aaff; }
|
||
|
||
.all-clients { margin-top: 14px; }
|
||
.all-clients a {
|
||
font-size: 0.78rem; color: #555;
|
||
border-bottom: 1px solid rgba(85,85,85,0.3);
|
||
transition: color 0.2s, border-color 0.2s;
|
||
}
|
||
.all-clients a:hover { color: #888; border-bottom-color: #888; }
|
||
|
||
/* ─── Server info ─── */
|
||
.server-info {
|
||
margin-top: 24px;
|
||
background: linear-gradient(145deg, rgba(22,22,22,0.95), rgba(14,14,14,0.98));
|
||
border: 1px solid rgba(152,0,0,0.2);
|
||
border-radius: 12px; overflow: hidden;
|
||
}
|
||
.server-info-title {
|
||
font-size: 0.78rem; font-weight: 500; color: #980000;
|
||
text-transform: uppercase; letter-spacing: 0.15em;
|
||
padding: 14px 20px 10px;
|
||
border-bottom: 1px solid rgba(152,0,0,0.1);
|
||
}
|
||
.info-grid { display: grid; grid-template-columns: 1fr 1fr; }
|
||
.info-item {
|
||
padding: 12px 18px;
|
||
border-bottom: 1px solid rgba(255,255,255,0.03);
|
||
border-right: 1px solid rgba(255,255,255,0.03);
|
||
text-align: left;
|
||
}
|
||
.info-item:nth-child(even) { border-right: none; }
|
||
.info-item:nth-last-child(-n+2) { border-bottom: none; }
|
||
.info-label { font-size: 0.7rem; color: #555; text-transform: uppercase; letter-spacing: 0.1em; margin-bottom: 3px; }
|
||
.info-value { font-size: 0.88rem; color: #ccc; }
|
||
|
||
.privacy-strip {
|
||
padding: 10px 18px;
|
||
border-top: 1px solid rgba(152,0,0,0.1);
|
||
display: flex; gap: 18px; justify-content: center; flex-wrap: wrap;
|
||
}
|
||
.privacy-badge { font-size: 0.75rem; color: #5effc4; display: flex; align-items: center; gap: 5px; }
|
||
.privacy-badge::before { content: '✓'; font-weight: 700; }
|
||
|
||
/* ─── Legal / contact / footer ─── */
|
||
.legal-note {
|
||
margin-top: 24px; padding: 12px 18px;
|
||
background: rgba(255,255,255,0.02);
|
||
border: 1px solid rgba(255,255,255,0.05);
|
||
border-radius: 8px; font-size: 0.75rem; color: #444;
|
||
line-height: 1.6; text-align: left;
|
||
}
|
||
.legal-note a { color: #555; border-bottom: 1px solid rgba(85,85,85,0.3); }
|
||
.legal-note a:hover { color: #888; }
|
||
|
||
.contact {
|
||
margin-top: 24px; padding: 16px;
|
||
background: linear-gradient(145deg, rgba(22,22,22,0.95), rgba(14,14,14,0.98));
|
||
border: 1px solid rgba(152,0,0,0.2);
|
||
border-radius: 12px; text-align: center;
|
||
}
|
||
|
||
.footer {
|
||
margin-top: 20px; font-size: 0.72rem; color: #383838;
|
||
letter-spacing: 0.04em;
|
||
display: flex; justify-content: center; gap: 14px; flex-wrap: wrap;
|
||
}
|
||
.footer a { color: #444; transition: color 0.2s; }
|
||
.footer a:hover { color: #777; }
|
||
|
||
/* ─── Sticky first table column (all screen sizes) ─── */
|
||
td:first-child {
|
||
position: sticky;
|
||
left: 0;
|
||
z-index: 1;
|
||
background: #0d0d0d;
|
||
}
|
||
th:first-child {
|
||
position: sticky;
|
||
left: 0;
|
||
z-index: 2;
|
||
background: #111;
|
||
}
|
||
tr.section-header td {
|
||
/* section headers span full width — override sticky bg */
|
||
position: static;
|
||
background: rgba(152,0,0,0.06);
|
||
}
|
||
|
||
/* Scroll hint — visible only on mobile via JS class */
|
||
.scroll-hint {
|
||
display: none;
|
||
font-size: 0.72rem;
|
||
color: #3a3a3a;
|
||
padding: 0 16px 10px;
|
||
letter-spacing: 0.05em;
|
||
}
|
||
|
||
/* ─── Tablet (≤ 700px) ─── */
|
||
@media (max-width: 700px) {
|
||
body { align-items: flex-start; }
|
||
}
|
||
|
||
/* ─── Mobile (≤ 540px) ─── */
|
||
@media (max-width: 540px) {
|
||
body { padding: 20px 12px 40px; }
|
||
|
||
.logo { width: 100px; height: 100px; margin-bottom: 20px; }
|
||
h1 { font-size: 1.5rem; letter-spacing: 0.1em; }
|
||
.subtitle { font-size: 0.78rem; letter-spacing: 0.18em; margin-bottom: 26px; }
|
||
|
||
.card { padding: 22px 16px; }
|
||
.step-text { font-size: 0.88rem; }
|
||
.homeserver { font-size: 0.75rem; word-break: break-all; }
|
||
.or-divider, .option-block { margin-left: 0; }
|
||
|
||
.client-card-top { flex-direction: column; align-items: flex-start; gap: 6px; }
|
||
.client-card-desc { font-size: 0.79rem; }
|
||
.client-card-tags { gap: 4px; }
|
||
|
||
.also-available { font-size: 0.75rem; }
|
||
|
||
/* Table */
|
||
.scroll-hint { display: block; }
|
||
table { font-size: 0.71rem; min-width: 620px; }
|
||
th { padding: 8px 5px; font-size: 0.6rem; }
|
||
th:first-child { min-width: 100px; padding-left: 10px; }
|
||
td { padding: 7px 5px; }
|
||
td:first-child { font-size: 0.71rem; padding-left: 10px; }
|
||
td small, th small { font-size: 0.58rem; }
|
||
.yes { font-size: 0.88rem; }
|
||
.part { font-size: 0.82rem; }
|
||
.no { font-size: 0.88rem; }
|
||
.comparison-title { font-size: 0.7rem; padding: 12px 14px 8px; letter-spacing: 0.1em; }
|
||
.security-note { margin: 10px 12px 4px; font-size: 0.71rem; }
|
||
.legend { padding: 8px 12px 12px; gap: 12px; font-size: 0.68rem; }
|
||
|
||
/* Server info */
|
||
.server-info-title { font-size: 0.7rem; padding: 12px 14px 8px; }
|
||
.info-item { padding: 10px 14px; }
|
||
.info-label { font-size: 0.65rem; }
|
||
.info-value { font-size: 0.82rem; }
|
||
.privacy-strip { flex-direction: column; align-items: center; gap: 8px; padding: 10px 14px; }
|
||
.privacy-badge { font-size: 0.71rem; }
|
||
|
||
/* Legal / footer */
|
||
.legal-note { font-size: 0.72rem; padding: 11px 14px; }
|
||
.contact { padding: 14px; }
|
||
.footer { gap: 8px; font-size: 0.68rem; }
|
||
}
|
||
|
||
/* ─── Very small (≤ 380px) ─── */
|
||
@media (max-width: 380px) {
|
||
h1 { font-size: 1.3rem; }
|
||
.logo { width: 84px; height: 84px; }
|
||
.card { padding: 18px 12px; }
|
||
table { font-size: 0.66rem; min-width: 580px; }
|
||
th { font-size: 0.55rem; padding: 7px 4px; }
|
||
td { padding: 6px 4px; }
|
||
td:first-child { font-size: 0.66rem; min-width: 90px; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
|
||
<img class="logo" src="https://photos.lotusguild.org/api/assets/3c4eb2da-0d06-407f-bdb7-c9e4cf795f0a/thumbnail?key=4aoZxX5-FHE3m_Ywwz1uGo3iNW53kmFztxfUw91PdOgphPNxayLFicNuxPvit1OYTpY&size=preview&c=jUqDBQAWF9iId3J%2FyAeIcIAICEd4d3BzSA%3D%3D" alt="Lotus Guild">
|
||
|
||
<h1><span>Lotus</span> Guild</h1>
|
||
<p class="subtitle">Private Communications</p>
|
||
|
||
<!-- ── How to Join ── -->
|
||
<div class="card">
|
||
<h2>How to Join</h2>
|
||
<ol class="steps">
|
||
<li>
|
||
<span class="step-num">1</span>
|
||
<span class="step-text">Open <a href="https://chat.lotusguild.org" target="_blank" rel="noopener">chat.lotusguild.org</a> — your homeserver is pre-configured</span>
|
||
</li>
|
||
<li>
|
||
<span class="step-num">2</span>
|
||
<span class="step-text">Register with a <strong>token from Jared</strong> — <code class="homeserver">@jared:matrix.lotusguild.org</code></span>
|
||
</li>
|
||
<li>
|
||
<span class="step-num">3</span>
|
||
<span class="step-text">Join the <a href="https://matrix.to/#/!-1ZBnAH-JiCOV8MGSKN77zDGTuI3pgSdy8Unu_DrDyc?via=matrix.lotusguild.org&via=matrix.org" target="_blank" rel="noopener">Lotus Guild Space</a> to access all rooms</span>
|
||
</li>
|
||
</ol>
|
||
|
||
<div class="or-divider">or join from another server</div>
|
||
<div class="option-block">
|
||
Already have a Matrix account? Sign up free at <a href="https://app.element.io/#/register?hs_url=https://mozilla.modular.im" target="_blank" rel="noopener">mozilla.org</a> or <a href="https://app.element.io/#/register" target="_blank" rel="noopener">matrix.org</a>, then <a href="https://matrix.to/#/!-1ZBnAH-JiCOV8MGSKN77zDGTuI3pgSdy8Unu_DrDyc?via=matrix.lotusguild.org&via=matrix.org" target="_blank" rel="noopener">join our space</a>.
|
||
</div>
|
||
|
||
<div class="divider"></div>
|
||
|
||
<!-- ── Recommended client ── -->
|
||
<div class="clients-section">
|
||
<h3>Recommended Client</h3>
|
||
|
||
<div class="client-featured">
|
||
<a href="https://chat.lotusguild.org" target="_blank" rel="noopener">
|
||
<span class="client-name">Lotus Guild Chat</span>
|
||
<span class="client-desc">Hosted Cinny — homeserver pre-configured, no setup</span>
|
||
<span class="tag-row">
|
||
<span class="tag dim">Desktop & Web</span>
|
||
<span class="tag dev">Lotus Fork</span>
|
||
<span class="tag voice">Voice & Video Rooms</span>
|
||
<span class="tag">Discord-like UI</span>
|
||
</span>
|
||
</a>
|
||
</div>
|
||
<p class="also-available">
|
||
Our Lotus Guild fork of Cinny adds: voice message recording & playback with 0.75×/1×/1.5×/2× speed control (MSC3245, E2EE), device verification fix (cross-client SAS emoji + inline cards), per-member device session panel with per-device verify buttons, full Discord-style presence tracking (online on startup, idle/away after 10 min inactivity, unavailable when tab hidden, offline on close — with a “Hide Online Status” privacy toggle), presence status indicators (online/busy/away dots) in member lists, incoming call ring + Answer/Decline (DMs & group chats), GIF picker (Giphy), emoji & sticker picker (custom packs, stickers send as <code style="font-size:0.8em;color:#e88;">m.sticker</code> events), pinned messages panel (pin icon in room header, pin/unpin from message menu), who-reacted viewer (hover any reaction for a name tooltip; right-click for a full avatar list), draggable+resizable picture-in-picture call window, poll creation & voting (single or multiple choice, 2–10 options), message forwarding, image/video captions, location sharing (map view + send), deleted message placeholders, per-message read receipt avatars (click for full list with timestamps), private read receipts toggle (Settings → Privacy), screenshare fullscreen button, screenshare audio mute (mute a screenshare’s audio without leaving the call), PTT (Push-to-Talk with configurable hold key), push-to-deafen (<kbd style="font-size:0.8em;background:rgba(255,255,255,0.06);border:1px solid rgba(255,255,255,0.12);border-radius:3px;padding:1px 5px;">M</kbd> key, configurable in Settings → Calls), custom status messages with emoji picker + auto-clear timer (30 min – 7 days) shown below usernames, encrypted room search via local cache scan with per-room “Load more” history buttons, a dedicated Privacy settings section (hide typing, hide online status), sidebar room filter (search rooms by name in Home and DMs tabs), favorite rooms (star any room, syncs across devices via m.favourite tag), media gallery drawer (browse all images/videos/files shared in a room), invite link + QR code (in both invite modal and room settings), knock-to-join support (Request to Join button + admin Approve/Deny panel), code syntax highlighting in Lotus Terminal mode (keywords, strings, numbers, comments, functions), night light / blue light filter (warm orange overlay with adjustable intensity in Settings → Appearance), message length counter in the composer, and the Lotus Terminal design theme (with TDS-styled orange typing indicator dots).
|
||
Also added in June 2026: message scheduling (MSC4140, datetime picker, cancel tray), saved messages / bookmarks (right-click any message, sidebar panel, syncs across devices), room history export (txt/json/html, date range, E2EE-aware), room activity & mod log (joins, kicks, bans, power level changes), server ACL editor (allow/deny lists, wildcard validation, power-level gated), room stats panel (top members, top reactions, media breakdown, 24h activity heatmap), opt-in image compression (Canvas API at 0.82 quality, shows before/after sizes), 13 domain-specific URL preview cards (YouTube, Vimeo, Twitch, Reddit, X/Twitter, Spotify, Steam, IMDb, Wikipedia, GitHub, Discord, npm, Stack Overflow), inline GIF preview (Giphy & Tenor share links auto-embed as animated GIFs via the homeserver proxy), policy list viewer (admin panel for <code style="font-size:0.8em;color:#e88;">m.policy.rule.*</code> ban list rooms — complements Draupnir), collapsible long messages (auto-collapse > 20 lines with “Read more” toggle, threshold configurable), message send animation (0.15 s fade+scale on own messages, respects prefers-reduced-motion), right-click room context menu improvements (Mute with duration submenu 15 min–indefinite, Copy Room Link, Mark as Read, Leave Room, Room Settings), quick emoji reactions directly on message hover (3 most-recent emoji in the hover bar, single click to react), in-app notification toasts (TDS-styled slide-in card from bottom-right when the window is focused, 4 s auto-dismiss, click to navigate), presence avatar border ring (2px green/yellow/red ring on user avatars in the timeline, members list, @mention autocomplete, and notifications), room emoji prefix support (leading emoji renders at 1.15× in the sidebar; emoji picker button on all room name inputs), glassmorphism sidebar toggle (Settings → Appearance, off by default; frosted blur effect lets chat backgrounds show through the sidebar — fixed in June 2026 to mirror the background onto <code style="font-size:0.8em;color:#e88;">document.body</code> so the blur has content to work through), and 5 CSS-only animated chat backgrounds: Digital Rain (two-layer vertical stripe scroll with parallax), Star Drift (three-layer radial-gradient dots drifting diagonally), Grid Pulse (neon grid lines expanding/contracting), Aurora Flow (sweeping radial gradient ellipses on a 200% canvas), and Fireflies (three layers of glowing dots drifting). All respect <code style="font-size:0.8em;color:#e88;">prefers-reduced-motion</code> and include a "Pause Background Animations" toggle in Settings → Appearance. Also added: AFK auto-mute for voice calls (mic silenced after a configurable idle timeout of 1–30 min detected via Web Audio AnalyserNode; in-app toast confirms the action; toggle + duration selector in Settings → Calls), knock-to-join admin badge (a live warning badge on the Members button in the room header counts pending knock requests in real time, visible only to users with sufficient invite permissions), voice channel user limit (admins set a max-participant cap per room via the <code style="font-size:0.8em;color:#e88;">io.lotus.voice_limit</code> state event; enforced server-side for <em>every</em> Matrix client by a guard that fronts the LiveKit JWT issuer and refuses tokens once a room is full, with a “Channel Full (N/N)” message and disabled Join button in Lotus Chat, while members already in the call can always rejoin), and custom call join/leave sound effects (a local cue plays when someone enters or leaves a call you’re in — tracked via MatrixRTC membership changes, synthesized in-browser with the Web Audio API so no assets are bundled; choose Chime, Soft, Retro, or off in Settings → Calls).
|
||
Prefer the unmodified upstream? <a href="https://cinny.in" target="_blank" rel="noopener">cinny.in</a> works with our homeserver — set it to <code style="font-size:0.8em;color:#e88;">matrix.lotusguild.org</code>.
|
||
</p>
|
||
|
||
<div class="space-join">
|
||
<p>Already signed in? Jump straight into the community:</p>
|
||
<a href="https://matrix.to/#/!-1ZBnAH-JiCOV8MGSKN77zDGTuI3pgSdy8Unu_DrDyc?via=matrix.lotusguild.org&via=matrix.org" target="_blank" rel="noopener">Join Lotus Guild Space →</a>
|
||
</div>
|
||
|
||
<div class="divider"></div>
|
||
|
||
<!-- ── Other clients ── -->
|
||
<div class="clients-section">
|
||
<h3>Other Clients</h3>
|
||
|
||
<div class="client-group">
|
||
<p class="client-group-label">Web & Desktop — Official Cinny</p>
|
||
<div class="client-cards">
|
||
<div class="client-card">
|
||
<div class="client-card-top">
|
||
<a href="https://cinny.in" target="_blank" rel="noopener" class="client-card-name">Cinny</a>
|
||
<div class="client-card-tags">
|
||
<span class="tag dim">Web</span>
|
||
<span class="tag dim">Desktop</span>
|
||
</div>
|
||
</div>
|
||
<p class="client-card-desc">The upstream open-source Cinny client. Same Discord-like UI and best-in-class space navigation as Lotus Chat, but without our custom additions (no GIF picker, no call ring notification, no PiP window, no message forwarding). Set homeserver to <code style="font-size:0.85em;color:#e88;background:rgba(152,0,0,0.08);padding:1px 5px;border-radius:4px;">matrix.lotusguild.org</code>.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="client-group">
|
||
<p class="client-group-label">Mobile — iOS & Android</p>
|
||
<div class="client-cards">
|
||
<div class="client-card">
|
||
<div class="client-card-top">
|
||
<a href="https://element.io/element-x" target="_blank" rel="noopener" class="client-card-name">Element X</a>
|
||
<div class="client-card-tags">
|
||
<span class="tag dim">iOS</span>
|
||
<span class="tag dim">Android</span>
|
||
<span class="tag rust">Rust SDK</span>
|
||
<span class="tag voice">Voice, Video & Screenshare</span>
|
||
</div>
|
||
</div>
|
||
<p class="client-card-desc">Fastest Matrix client — instant load via Sliding Sync. Native MatrixRTC calls (voice-only + video), screenshare, DM voice calls, live location sharing, media captions. Full space create & management. Rust encryption (Vodozemac).</p>
|
||
</div>
|
||
<div class="client-card">
|
||
<div class="client-card-top">
|
||
<a href="https://fluffychat.im/" target="_blank" rel="noopener" class="client-card-name">FluffyChat</a>
|
||
<div class="client-card-tags">
|
||
<span class="tag dim">iOS</span>
|
||
<span class="tag dim">Android</span>
|
||
<span class="tag dim">Desktop</span>
|
||
<span class="tag dim">Web</span>
|
||
<span class="tag dim">Dart SDK</span>
|
||
<span class="tag warn">Calls Experimental</span>
|
||
</div>
|
||
</div>
|
||
<p class="client-card-desc">Simple, beginner-friendly, fully cross-platform. Dart SDK with Vodozemac encryption. Unique: supports image captions (text + image as one event). Voice/video calls are experimental — reliability varies by homeserver and platform.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="client-group">
|
||
<p class="client-group-label">Discord-like — Android, Windows, macOS & Linux</p>
|
||
<div class="client-cards">
|
||
<div class="client-card">
|
||
<div class="client-card-top">
|
||
<a href="https://commet.chat/" target="_blank" rel="noopener" class="client-card-name">Commet</a>
|
||
<div class="client-card-tags">
|
||
<span class="tag dim">Android</span>
|
||
<span class="tag dim">Windows</span>
|
||
<span class="tag dim">macOS</span>
|
||
<span class="tag dim">Linux</span>
|
||
<span class="tag beta">Beta</span>
|
||
<span class="tag voice">Voice & Video</span>
|
||
</div>
|
||
</div>
|
||
<p class="client-card-desc">Most Discord-like client overall. Multi-account support, built-in GIF search (privacy proxy — uploads to homeserver), shared calendars & photo albums, polls, screenshare on browsers & Android. Available on Android, Windows, macOS, Linux, and Web. Active development — iOS planned.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="client-group">
|
||
<p class="client-group-label">Desktop — Windows, macOS & Linux</p>
|
||
<div class="client-cards">
|
||
<div class="client-card">
|
||
<div class="client-card-top">
|
||
<a href="https://element.io/download" target="_blank" rel="noopener" class="client-card-name">Element</a>
|
||
<div class="client-card-tags">
|
||
<span class="tag dim">Windows</span>
|
||
<span class="tag dim">macOS</span>
|
||
<span class="tag dim">Linux</span>
|
||
<span class="tag dim">Web</span>
|
||
<span class="tag voice">Voice, Video & Screenshare</span>
|
||
</div>
|
||
</div>
|
||
<p class="client-card-desc">Most feature-complete Matrix client — every spec feature including polls, widgets, threads, screenshare. Heavier on resources. Encrypted message search on desktop only (not web).</p>
|
||
</div>
|
||
<div class="client-card">
|
||
<div class="client-card-top">
|
||
<a href="https://nheko-reborn.github.io/" target="_blank" rel="noopener" class="client-card-name">Nheko</a>
|
||
<div class="client-card-tags">
|
||
<span class="tag dim">Windows</span>
|
||
<span class="tag dim">macOS</span>
|
||
<span class="tag dim">Linux</span>
|
||
<span class="tag voice">Voice & Video</span>
|
||
</div>
|
||
</div>
|
||
<p class="client-card-desc">Native C++/Qt client — lightest desktop option, no Electron. Deep KDE/Plasma integration. No threads, no mobile or web. Best for Linux power users who want minimal resource usage.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<p class="all-clients"><a href="https://matrix.org/ecosystem/clients/" target="_blank" rel="noopener">View all Matrix clients →</a></p>
|
||
</div>
|
||
</div>
|
||
</div><!-- end .card -->
|
||
|
||
<!-- ── Feature Comparison Table ── -->
|
||
<div class="comparison-section">
|
||
<p class="comparison-title">Client Feature Comparison — June 2026 (latest)</p>
|
||
<p class="scroll-hint" id="scrollHint">← swipe to compare →</p>
|
||
<div class="table-wrap">
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th></th>
|
||
<th class="ours">Lotus Chat<small>chat.lotusguild.org<br>Our Cinny fork</small></th>
|
||
<th>Cinny<small>cinny.in<br>Official</small></th>
|
||
<th>Element X<small>iOS & Android</small></th>
|
||
<th>FluffyChat<small>All platforms</small></th>
|
||
<th>Commet<small>Android / Win / macOS / Linux</small></th>
|
||
<th>Element<small>Web & Desktop</small></th>
|
||
<th>Nheko<small>Desktop only</small></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
|
||
<!-- Platform -->
|
||
<tr class="section-header"><td colspan="8">Platform</td></tr>
|
||
<tr>
|
||
<td>iOS</td>
|
||
<td class="ours"><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Android</td>
|
||
<td class="ours"><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Desktop app</td>
|
||
<td class="ours"><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span><small>Cinny Desktop</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span><small>Win, macOS & Linux</small></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Web browser</td>
|
||
<td class="ours"><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Performance</td>
|
||
<td class="ours">Light</td>
|
||
<td>Light</td>
|
||
<td>Very fast<small>Sliding Sync</small></td>
|
||
<td>Moderate</td>
|
||
<td>Moderate</td>
|
||
<td>Heavy<small>Electron / JS</small></td>
|
||
<td>Light<small>Native C++</small></td>
|
||
</tr>
|
||
|
||
<!-- Security -->
|
||
<tr class="section-header"><td colspan="8">Security & Encryption</td></tr>
|
||
<tr>
|
||
<td>E2EE</td>
|
||
<td class="ours"><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Vodozemac encryption<small>stronger crypto track record</small></td>
|
||
<td class="ours"><span class="no">✗</span><small>js-sdk</small></td>
|
||
<td><span class="no">✗</span><small>js-sdk</small></td>
|
||
<td><span class="yes">✓</span><small>Rust SDK</small></td>
|
||
<td><span class="yes">✓</span><small>dart-sdk</small></td>
|
||
<td><span class="yes">✓</span><small>dart-sdk</small></td>
|
||
<td><span class="no">✗</span><small>js-sdk, migrating</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Device verification</td>
|
||
<td class="ours"><span class="yes">✓</span><small>SAS emoji, cross-client requests show inline card</small></td>
|
||
<td><span class="part">~</span><small>cross-client requests<br>may show unsupported</small></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
</tr>
|
||
|
||
<!-- Calling -->
|
||
<tr class="section-header"><td colspan="8">Voice & Video</td></tr>
|
||
<tr>
|
||
<td>Voice & video calls</td>
|
||
<td class="ours"><span class="yes">✓</span><small>rooms & DMs, group calls,<br>screenshare via Element Call</small></td>
|
||
<td><span class="part">~</span><small>Element Call embed;<br>no ring notification</small></td>
|
||
<td><span class="yes">✓</span><small>MatrixRTC</small></td>
|
||
<td><span class="part">~</span><small>experimental, varies<br>by homeserver</small></td>
|
||
<td><span class="part">~</span><small>1:1 + group,<br>no E2EE voice rooms</small></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Screenshare</td>
|
||
<td class="ours"><span class="yes">✓</span><small>via Element Call; fullscreen button,<br>independent audio mute</small></td>
|
||
<td><span class="yes">✓</span><small>via Element Call embed</small></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span><small>XDG portals & native</small></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Push-to-Talk<small>hold key to transmit</small></td>
|
||
<td class="ours"><span class="yes">✓</span><small>configurable hold key,<br>PTT badge in call bar;<br>M = push-to-deafen</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>AFK auto-mute<small>silence mic after idle timeout</small></td>
|
||
<td class="ours"><span class="yes">✓</span><small>1–30 min timeout,<br>toast notification</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Voice channel user limit<small>admin-set max participants</small></td>
|
||
<td class="ours"><span class="yes">✓</span><small>per-room cap,<br>server-enforced<br>for all clients</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Call join/leave sounds<small>cue when others come & go</small></td>
|
||
<td class="ours"><span class="yes">✓</span><small>3 styles,<br>Web Audio synth</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Group calls</td>
|
||
<td class="ours"><span class="yes">✓</span><small>via Element Call embed</small></td>
|
||
<td><span class="yes">✓</span><small>via Element Call embed</small></td>
|
||
<td><span class="yes">✓</span><small>MatrixRTC</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="part">~</span><small>no E2EE voice rooms</small></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="part">~</span><small>partial</small></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Incoming call notification<small>ring + Answer/Decline</small></td>
|
||
<td class="ours"><span class="yes">✓</span><small>DMs & group chats;<br>ring tone, auto-dismiss</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="yes">✓</span><small>native push</small></td>
|
||
<td><span class="part">~</span><small>experimental</small></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span><small>desktop</small></td>
|
||
<td><span class="part">~</span><small>partial</small></td>
|
||
</tr>
|
||
|
||
<!-- Core features -->
|
||
<tr class="section-header"><td colspan="8">Core Features</td></tr>
|
||
<tr>
|
||
<td>Spaces</td>
|
||
<td class="ours"><span class="yes">✓</span><small>best-in-class sub-space nav</small></td>
|
||
<td><span class="yes">✓</span><small>best-in-class sub-space nav</small></td>
|
||
<td><span class="yes">✓</span><small>dedicated spaces tab,<br>full management</small></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span><small>Discord-like categories</small></td>
|
||
<td><span class="yes">✓</span><small>sub-spaces clunky</small></td>
|
||
<td><span class="part">~</span><small>limited</small></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Threads</td>
|
||
<td class="ours"><span class="part">~</span><small>basic, no dedicated view</small></td>
|
||
<td><span class="part">~</span><small>basic, no dedicated view</small></td>
|
||
<td><span class="part">~</span><small>Labs flag</small></td>
|
||
<td><span class="no">✗</span><small>shows as regular messages</small></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Polls</td>
|
||
<td class="ours"><span class="yes">✓</span><small>create, vote & display;<br>single or multiple choice</small></td>
|
||
<td><span class="part">~</span><small>display only</small></td>
|
||
<td><span class="yes">✓</span><small>create, vote & end</small></td>
|
||
<td><span class="yes">✓</span><small>added v2.3.0</small></td>
|
||
<td><span class="yes">✓</span><small>added v0.4.2</small></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Voice messages<small>record & play</small></td>
|
||
<td class="ours"><span class="yes">✓</span><small>record & play, E2EE;<br>0.75×/1×/1.5×/2× speed</small></td>
|
||
<td><span class="part">~</span><small>playback only</small></td>
|
||
<td><span class="yes">✓</span><small>record, play, variable speed,<br>reply with voice</small></td>
|
||
<td><span class="yes">✓</span><small>record & play,<br>pause support</small></td>
|
||
<td><span class="part">~</span><small>playback confirmed;<br>recording unclear</small></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Pinned messages</td>
|
||
<td class="ours"><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span><small>dedicated pins view</small></td>
|
||
<td><span class="part">~</span><small>view only</small></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Custom emoji & stickers</td>
|
||
<td class="ours"><span class="yes">✓</span><small>best-in-class</small></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="part">~</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span><small>bulk upload, Signal packs</small></td>
|
||
<td><span class="part">~</span><small>sometimes shows mxc:// URLs</small></td>
|
||
<td><span class="yes">✓</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>GIF search / picker</td>
|
||
<td class="ours"><span class="yes">✓</span><small>Giphy, Terminal TDS themed,<br>click-outside to close</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="yes">✓</span><small>privacy proxy,<br>uploads to homeserver</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Message search (encrypted)</td>
|
||
<td class="ours"><span class="yes">✓</span><small>local cache scan; load-more<br>buttons extend history per room</small></td>
|
||
<td><span class="part">~</span><small>server search only;<br>no E2EE support</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="part">~</span><small>cache scan only</small></td>
|
||
<td><span class="yes">✓</span><small>client-side; auto-fetches +<br>decrypts history during search</small></td>
|
||
<td><span class="part">~</span><small>desktop only, not web</small></td>
|
||
<td><span class="part">~</span><small>local DB scan</small></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Widgets</td>
|
||
<td class="ours"><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
|
||
<!-- UX / extras -->
|
||
<tr class="section-header"><td colspan="8">UX & Extras</td></tr>
|
||
<tr>
|
||
<td>Multi-account</td>
|
||
<td class="ours"><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="part">~</span><small>supported but clunky</small></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span><small>CLI profiles only</small></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Image captions<small>(text + image as one event)</small></td>
|
||
<td class="ours"><span class="yes">✓</span><small>caption field on<br>image & video upload</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="yes">✓</span><small>scrollable captions<br>on media (v26.04.2)</small></td>
|
||
<td><span class="yes">✓</span><small>unique feature</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="part">~</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Discord-like UI</td>
|
||
<td class="ours"><span class="yes">✓</span><small>best-in-class for web</small></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span><small>WhatsApp/Telegram style</small></td>
|
||
<td><span class="yes">✓</span><small>most Discord-like overall</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Shared calendars & albums</td>
|
||
<td class="ours"><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="yes">✓</span><small>unique to Commet</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Night Light / Blue Light Filter<small>warm overlay, adjustable intensity</small></td>
|
||
<td class="ours"><span class="yes">✓</span><small>5–80% intensity slider,<br>Settings → Appearance</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Animated chat backgrounds<small>CSS-only, GPU-accelerated</small></td>
|
||
<td class="ours"><span class="yes">✓</span><small>5 animated wallpapers (rain,<br>stars, grid pulse, aurora,<br>fireflies); pause toggle</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Deleted message visibility<small>(redacted events)</small></td>
|
||
<td class="ours"><span class="yes">✓</span><small>"Message deleted" with reason<br>if provided</small></td>
|
||
<td><span class="no">✗</span><small>redacted messages hidden</small></td>
|
||
<td><span class="yes">✓</span><small>"Message deleted"<br>placeholder shown</small></td>
|
||
<td><span class="yes">✓</span><small>shows redaction notice</small></td>
|
||
<td><span class="yes">✓</span><small>shows redaction notice</small></td>
|
||
<td><span class="yes">✓</span><small>"Message deleted"<br>placeholder shown</small></td>
|
||
<td><span class="yes">✓</span><small>shows redaction notice</small></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Location sharing</td>
|
||
<td class="ours"><span class="part">~</span><small>map embed view +<br>static share button</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="yes">✓</span><small>live & static,<br>map rendering</small></td>
|
||
<td><span class="part">~</span><small>basic static</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Message forwarding</td>
|
||
<td class="ours"><span class="yes">✓</span><small>forward to any room</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="part">~</span><small>via share menu</small></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="part">~</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Per-message read receipts<small>who read each message</small></td>
|
||
<td class="ours"><span class="yes">✓</span><small>avatar pill below message,<br>click for list + timestamps</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="yes">✓</span><small>avatar thumbnails</small></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span><small>avatar row below message</small></td>
|
||
<td><span class="yes">✓</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Who reacted<small>see who reacted with each emoji</small></td>
|
||
<td class="ours"><span class="yes">✓</span><small>hover tooltip with names;<br>right-click for full avatar modal</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="part">~</span><small>tap reaction chip</small></td>
|
||
<td><span class="part">~</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span><small>tooltip + click for list</small></td>
|
||
<td><span class="yes">✓</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Edit history viewer<small>see all prior versions of an edited message</small></td>
|
||
<td class="ours"><span class="yes">✓</span><small>click "(edited)" label → modal<br>with all versions + timestamps</small></td>
|
||
<td><span class="no">✗</span><small>label shown, not clickable</small></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Custom status message<small>shown below username</small></td>
|
||
<td class="ours"><span class="yes">✓</span><small>emoji picker, auto-clear timer<br>(30 min – 7 days), 64-char limit</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="yes">✓</span><small>via profile settings</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Online presence tracking<small>idle/away auto-detection</small></td>
|
||
<td class="ours"><span class="yes">✓</span><small>online on start, idle after 10 min,<br>unavailable when tab hidden,<br>offline on close; hide-status toggle</small></td>
|
||
<td><span class="part">~</span><small>manual AFK only; auto idle<br>detection PR open, unmerged</small></td>
|
||
<td><span class="part">~</span><small>basic online/offline only</small></td>
|
||
<td><span class="part">~</span><small>basic online/offline only</small></td>
|
||
<td><span class="part">~</span><small>inactivity monitor shipped Oct 2025;<br>no tab/close detection confirmed</small></td>
|
||
<td><span class="part">~</span><small>3-min idle timer (shipped Nov 2023);<br>no tab detection, no offline on close,<br>no user-facing hide toggle</small></td>
|
||
<td><span class="part">~</span><small>basic online/offline only</small></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Media Gallery<small>browse room images/videos/files</small></td>
|
||
<td class="ours"><span class="yes">✓</span><small>right-side drawer;<br>Images/Videos/Files tabs</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Sidebar room filter<small>filter by name in real time</small></td>
|
||
<td class="ours"><span class="yes">✓</span><small>Home & DMs tabs</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Favorite rooms<small>starred section in sidebar</small></td>
|
||
<td class="ours"><span class="yes">✓</span><small>m.favourite tag, syncs<br>across devices</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="yes">✓</span><small>favourites section</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Invite link + QR code</td>
|
||
<td class="ours"><span class="yes">✓</span><small>matrix.to URL + QR code<br>in invite modal & room settings</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Custom notification sounds<small>per-category sound selector</small></td>
|
||
<td class="ours"><span class="yes">✓</span><small>per-category sound selector; ▶ preview</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Notification quiet hours<small>silence notifs in a time window</small></td>
|
||
<td class="ours"><span class="yes">✓</span><small>silence all notifs in a time window; overnight support</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Room sort order<small>sort sidebar rooms by activity, name, or unread</small></td>
|
||
<td class="ours"><span class="yes">✓</span><small>Recent / A→Z / Unread First</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="part">~</span><small>sort by activity only</small></td>
|
||
<td><span class="part">~</span><small>alphabetical only</small></td>
|
||
<td><span class="yes">✓</span><small>multiple sort options</small></td>
|
||
<td><span class="part">~</span><small>limited</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Push notifications</td>
|
||
<td class="ours"><span class="part">~</span><small>web push only</small></td>
|
||
<td><span class="part">~</span><small>web push only</small></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="yes">✓</span><small>desktop</small></td>
|
||
<td><span class="yes">✓</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Export room history<small>download messages as file</small></td>
|
||
<td class="ours"><span class="yes">✓</span><small>txt/json/html, date range,<br>E2EE-aware</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="yes">✓</span><small>has export</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Message scheduling<small>send later with datetime picker</small></td>
|
||
<td class="ours"><span class="yes">✓</span><small>MSC4140, datetime picker,<br>cancel tray above composer</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="part">~</span><small>partial / labs</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Message bookmarks / saved messages</td>
|
||
<td class="ours"><span class="yes">✓</span><small>right-click bookmark,<br>sidebar panel, syncs across devices</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="yes">✓</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Collapsible long messages<small>auto-collapse >20 lines</small></td>
|
||
<td class="ours"><span class="yes">✓</span><small>"Read more" toggle,<br>threshold configurable</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Inline GIF preview<small>Giphy / Tenor links auto-embed</small></td>
|
||
<td class="ours"><span class="yes">✓</span><small>via homeserver proxy,<br>no direct CDN contact</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Presence avatar border ring<small>colored ring shows online status</small></td>
|
||
<td class="ours"><span class="yes">✓</span><small>green/yellow/red ring;<br>timeline, members, mentions</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Room emoji prefix<small>🎮 renders larger in sidebar</small></td>
|
||
<td class="ours"><span class="yes">✓</span><small>1.15× size; emoji picker<br>in all name inputs</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>Quick emoji reactions on hover<small>1-click react, no menu needed</small></td>
|
||
<td class="ours"><span class="yes">✓</span><small>3 recent emoji in hover bar;<br>dismisses emoji picker</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="part">~</span><small>hover bar, no recents</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
<tr>
|
||
<td>In-app notification toasts<small>TDS-styled, when window focused</small></td>
|
||
<td class="ours"><span class="yes">✓</span><small>slides in bottom-right;<br>4 s auto-dismiss, click to jump</small></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
<td><span class="no">✗</span></td>
|
||
</tr>
|
||
|
||
</tbody>
|
||
</table>
|
||
</div><!-- table-wrap -->
|
||
|
||
<div class="security-note" style="margin:14px 16px 6px;">
|
||
<strong>Encryption architecture:</strong> Element X uses the native <strong>matrix-rust-sdk</strong> with Vodozemac. FluffyChat and Commet use <strong>matrix-dart-sdk</strong>, which also wraps Vodozemac for its crypto — all three benefit from Vodozemac's stronger security track record over the older JavaScript SDK. Cinny and Element Web/Desktop currently use <strong>matrix-js-sdk</strong>; Element is actively migrating to the Rust SDK. Clients using Vodozemac were not affected by historical js-sdk vulnerabilities.
|
||
</div>
|
||
|
||
<div class="legend">
|
||
<span><span class="yes">✓</span> Full support</span>
|
||
<span><span class="part">~</span> Partial / experimental</span>
|
||
<span><span class="no">✗</span> Not supported</span>
|
||
</div>
|
||
|
||
</div><!-- comparison-section -->
|
||
|
||
<!-- ── Server Details ── -->
|
||
<div class="server-info">
|
||
<p class="server-info-title">Server Details</p>
|
||
<div class="info-grid">
|
||
<div class="info-item">
|
||
<div class="info-label">Access</div>
|
||
<div class="info-value">Invite-only</div>
|
||
</div>
|
||
<div class="info-item">
|
||
<div class="info-label">Max Upload</div>
|
||
<div class="info-value">200 MB / file</div>
|
||
</div>
|
||
<div class="info-item">
|
||
<div class="info-label">Message History</div>
|
||
<div class="info-value">Kept indefinitely</div>
|
||
</div>
|
||
<div class="info-item">
|
||
<div class="info-label">Media Retention</div>
|
||
<div class="info-value">3 yr local · 1 yr remote</div>
|
||
</div>
|
||
<div class="info-item">
|
||
<div class="info-label">Federation</div>
|
||
<div class="info-value">Fully federated</div>
|
||
</div>
|
||
<div class="info-item">
|
||
<div class="info-label">Minimum Age</div>
|
||
<div class="info-value">13+ (COPPA)</div>
|
||
</div>
|
||
</div>
|
||
<div class="privacy-strip">
|
||
<span class="privacy-badge">No ads or tracking</span>
|
||
<span class="privacy-badge">No data sold</span>
|
||
<span class="privacy-badge">E2EE — server cannot read encrypted rooms</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="legal-note">
|
||
This service is provided "as-is" with no uptime guarantee. Not for emergency use — do not use to contact emergency services (e.g. 911). Use is governed by our <a href="https://wiki.lotusguild.org/en/Legal/terms-of-service" target="_blank" rel="noopener">Terms of Service</a> and <a href="https://wiki.lotusguild.org/en/Legal/privacy-policy" target="_blank" rel="noopener">Privacy Policy</a>. Governing law: State of Ohio, United States.
|
||
</div>
|
||
|
||
<div class="contact">
|
||
<p style="font-size:0.85rem;color:#777;margin-bottom:4px;">Questions or need a registration token?</p>
|
||
<p>Reach out to <code class="homeserver">@jared:matrix.lotusguild.org</code></p>
|
||
</div>
|
||
|
||
<p class="footer">
|
||
<a href="https://wiki.lotusguild.org/en/Services/service-matrix" target="_blank" rel="noopener">Wiki & Setup Guide</a>
|
||
<span>·</span>
|
||
<a href="https://wiki.lotusguild.org/en/Legal/privacy-policy" target="_blank" rel="noopener">Privacy Policy</a>
|
||
<span>·</span>
|
||
<a href="https://wiki.lotusguild.org/en/Legal/terms-of-service" target="_blank" rel="noopener">Terms of Service</a>
|
||
<span>·</span>
|
||
<span>Powered by Matrix · E2E Encrypted</span>
|
||
</p>
|
||
|
||
</div><!-- container -->
|
||
<script>
|
||
// Show scroll hint only on touch devices; hide once table is scrolled
|
||
if ('ontouchstart' in window || navigator.maxTouchPoints > 0) {
|
||
var hint = document.getElementById('scrollHint');
|
||
if (hint) hint.style.display = 'block';
|
||
var wrap = hint && hint.nextElementSibling;
|
||
if (wrap) wrap.addEventListener('scroll', function hide() {
|
||
hint.style.display = 'none';
|
||
wrap.removeEventListener('scroll', hide);
|
||
}, { passive: true });
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|