v3: follows, notifications, invite-only registration, unread markers

This commit is contained in:
khannurien
2026-03-21 18:42:47 +00:00
parent 7c098e7c4c
commit 608c6bc6a8
55 changed files with 4743 additions and 884 deletions

View File

@@ -13,8 +13,12 @@
.md a:hover {
text-decoration: none;
}
.md strong { font-weight: 700; }
.md em { font-style: italic; }
.md strong {
font-weight: 700;
}
.md em {
font-style: italic;
}
.md code {
font-family: monospace;
background: var(--color-bg);
@@ -40,15 +44,16 @@
padding-left: 1.5em;
margin: 0.4em 0;
}
.md li { margin: 0.15em 0; }
.md li {
margin: 0.15em 0;
}
.md blockquote {
border-left: 3px solid var(--color-border);
margin: 0.5em 0;
padding: 0.2em 0.75em;
opacity: 0.75;
}
.md h1, .md h2, .md h3,
.md h4, .md h5, .md h6 {
.md h1, .md h2, .md h3, .md h4, .md h5, .md h6 {
margin: 0.6em 0 0.2em;
font-weight: 700;
line-height: 1.25;
@@ -62,9 +67,13 @@
.md--inline blockquote {
margin: 0;
}
.md--inline li { margin: 0; }
.md--inline li {
margin: 0;
}
.md--inline ul,
.md--inline ol { padding-left: 1.2em; }
.md--inline ol {
padding-left: 1.2em;
}
/* ── Dump detail page ── */
.dump-detail {
@@ -101,15 +110,6 @@
justify-self: center;
}
.dump-header-info {
display: flex;
flex-direction: column;
gap: 0.3rem;
flex: 1;
min-width: 0;
}
.dump-title {
margin: 0;
font-size: 1.5rem;
@@ -631,32 +631,6 @@
background: var(--color-soundcloud);
}
.rich-content-embed {
width: 100%;
display: block;
border: 2px solid var(--color-border);
border-radius: 10px;
overflow: hidden;
margin-top: 0.75rem;
}
.rich-content-embed iframe {
width: 100%;
border: none;
display: block;
}
.embed-youtube {
aspect-ratio: 16/9;
}
.embed-youtube iframe {
height: 100%;
}
.embed-soundcloud {
height: 166px;
}
.embed-bandcamp {
height: 120px;
}
/* ── Global persistent player ── */
.global-player {
position: fixed;
@@ -862,14 +836,6 @@ body.has-player .fab-new {
opacity: 0.6;
}
/* ── Online users ── */
.online-users {
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
margin-bottom: 1rem;
}
.avatar-img {
object-fit: cover;
border: 2px solid var(--color-surface);
@@ -1087,19 +1053,6 @@ body.has-player .fab-new {
}
/* ── Profile (own) page ── */
.profile-avatar-section {
display: flex;
align-items: center;
gap: 1.5rem;
margin-bottom: 2rem;
}
.profile-avatar-upload {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.profile-username {
margin: 0;
font-size: 1.25rem;
@@ -1110,6 +1063,132 @@ body.has-player .fab-new {
margin-top: 0.5rem;
}
.profile-header .follow-btn {
margin-top: 0.5rem;
}
.profile-invited-by {
font-size: 0.78rem;
color: var(--color-text-muted);
margin: 0.15rem 0 0.4rem;
}
.profile-invited-by--founding {
font-style: italic;
}
.profile-invited-by-link {
color: var(--color-text-muted);
text-decoration: none;
font-weight: 600;
}
.profile-invited-by-link:hover {
color: var(--color-accent);
}
.profile-own-actions {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-top: 0.5rem;
align-items: center;
}
.profile-own-actions .logout-btn {
margin-top: 0;
}
.invite-generate {
display: contents;
}
.invite-btn {
padding: 0.3rem 0.9rem;
border: 1.5px solid var(--color-accent);
border-radius: 6px;
background: transparent;
color: var(--color-accent);
font-size: 0.85rem;
cursor: pointer;
transition: background 0.15s, color 0.15s;
}
.invite-btn:hover {
background: var(--color-accent);
color: var(--color-on-accent);
}
.invite-result {
display: flex;
align-items: center;
gap: 0.5rem;
background: var(--color-surface);
border: 1px solid var(--color-border-subtle);
border-radius: 6px;
padding: 0.3rem 0.5rem 0.3rem 0.75rem;
max-width: 480px;
}
.invite-url {
font-size: 0.75rem;
font-family: monospace;
color: var(--color-text-muted);
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.invite-copy-btn {
padding: 0.2rem 0.65rem;
border: 1px solid var(--color-border-subtle);
border-radius: 4px;
background: transparent;
color: var(--color-text);
font-size: 0.75rem;
cursor: pointer;
white-space: nowrap;
flex-shrink: 0;
transition: background 0.12s;
}
.invite-copy-btn:hover {
background: var(--color-accent);
color: var(--color-on-accent);
border-color: var(--color-accent);
}
/* ── Profile sub-pages (dumps / upvoted / playlists) ── */
.profile-subpage-back {
display: inline-block;
font-size: 0.85rem;
color: var(--color-text-muted);
text-decoration: none;
margin-bottom: 0.75rem;
}
.profile-subpage-back:hover {
color: var(--color-text);
}
.profile-subpage-title-row {
display: flex;
align-items: center;
gap: 0.75rem;
}
.profile-subpage-title {
margin: 0;
font-size: 1.4rem;
font-weight: 700;
flex: 1;
}
/* ── Profile "View all" link ── */
.profile-view-all {
display: inline-block;
margin-top: 0.75rem;
font-size: 0.85rem;
color: var(--color-text-muted);
text-decoration: none;
}
.profile-view-all:hover {
color: var(--color-accent);
}
.logout-btn {
padding: 0.3rem 0.9rem;
border: 1.5px solid var(--color-border);
@@ -1126,23 +1205,6 @@ body.has-player .fab-new {
color: var(--color-danger);
}
.avatar-upload-label {
display: inline-block;
padding: 0.4rem 1rem;
border: 2px solid var(--color-accent);
border-radius: 6px;
color: var(--color-accent);
font-size: 0.9rem;
font-weight: 500;
cursor: pointer;
transition: background 0.15s, color 0.15s;
}
.avatar-upload-label:hover {
background: var(--color-accent);
color: var(--color-on-accent);
}
.form-error {
color: var(--color-danger);
margin: 0;
@@ -1601,10 +1663,6 @@ body.has-player .fab-new {
gap: 0.4rem;
}
.feed-header {
padding: 0.5rem 0 0;
}
.feed-sort-btn {
padding: 0.25rem 0.8rem;
border-radius: 6px;
@@ -1751,6 +1809,20 @@ body.has-player .fab-new {
color: var(--color-accent);
}
/* ── Unread dot ── */
.unread-dot {
display: inline-block;
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--color-accent);
vertical-align: baseline;
position: relative;
top: -0.15em;
margin-right: 0.4em;
flex-shrink: 0;
}
/* ── Shared card description / comment ── */
.dump-card-comment,
.playlist-card-description {
@@ -1833,6 +1905,14 @@ body.has-player .fab-new {
.playlist-card-count {
opacity: 0.7;
}
.playlist-card-owner {
color: inherit;
text-decoration: none;
font-weight: 600;
}
.playlist-card-owner:hover {
color: var(--color-accent);
}
/* ── Playlist card delete button ── */
.playlist-card-delete-btn {
@@ -2060,8 +2140,20 @@ body.has-player .fab-new {
border: 1px solid var(--color-border);
}
.playlist-detail-title-row {
display: flex;
align-items: center;
gap: 0.75rem;
flex-wrap: wrap;
}
.playlist-detail-title-row .playlist-edit-input {
flex: 1;
min-width: 0;
}
.playlist-detail-title {
margin: 0 0 0.25rem;
margin: 0;
font-size: 1.5rem;
font-weight: 700;
word-break: break-word;
@@ -2080,6 +2172,15 @@ body.has-player .fab-new {
font-size: 0.82rem;
opacity: 0.6;
}
.playlist-detail-owner {
color: inherit;
text-decoration: none;
font-weight: 600;
}
.playlist-detail-owner:hover {
color: var(--color-accent);
opacity: 1;
}
/* ── Playlist header inline edit ── */
.playlist-detail-content {
@@ -2090,14 +2191,6 @@ body.has-player .fab-new {
gap: 0.4rem;
}
.playlist-header-actions {
flex-shrink: 0;
display: flex;
flex-direction: column;
align-items: stretch;
gap: 0.4rem;
}
.playlist-edit-btn {
background: none;
border: 1px solid var(--color-border-subtle);
@@ -2276,73 +2369,6 @@ body.has-player .fab-new {
opacity: 0.75;
}
/* ── Public/Private toggle ── */
.toggle-row {
display: flex;
align-items: center;
gap: 0.6rem;
cursor: pointer;
margin-bottom: 0.5rem;
}
.toggle-label {
font-size: 0.9rem;
color: var(--color-text);
user-select: none;
}
.toggle-hint {
font-size: 0.8rem;
color: var(--color-text-muted);
user-select: none;
}
.toggle-switch {
position: relative;
display: inline-flex;
align-items: center;
width: 2.4rem;
height: 1.3rem;
flex-shrink: 0;
}
.toggle-switch input {
opacity: 0;
width: 0;
height: 0;
position: absolute;
}
.toggle-thumb {
position: absolute;
inset: 0;
border-radius: 999px;
background: var(--color-text-muted);
transition: background 0.2s;
cursor: pointer;
}
.toggle-thumb::after {
content: "";
position: absolute;
left: 0.15rem;
top: 50%;
transform: translateY(-50%);
width: 1rem;
height: 1rem;
border-radius: 50%;
background: #fff;
transition: left 0.2s;
}
.toggle-switch input:checked + .toggle-thumb {
background: var(--color-accent);
}
.toggle-switch input:checked + .toggle-thumb::after {
left: calc(100% - 1.15rem);
}
/* ── Dump card comment count ── */
.dump-card-comment-count {
font-size: 0.72rem;
@@ -2401,7 +2427,11 @@ body.has-player .fab-new {
}
.comment-node-inner:hover {
background: color-mix(in srgb, var(--color-surface) 80%, var(--color-accent) 20%);
background: color-mix(
in srgb,
var(--color-surface) 80%,
var(--color-accent) 20%
);
}
.comment-avatar {
@@ -2477,7 +2507,8 @@ body.has-player .fab-new {
padding-left: 1.25rem;
margin-left: 1.1rem;
margin-top: 0.35rem;
border-left: 2px solid color-mix(in srgb, var(--color-accent) 30%, transparent);
border-left: 2px solid
color-mix(in srgb, var(--color-accent) 30%, transparent);
display: flex;
flex-direction: column;
gap: 0.35rem;
@@ -2516,7 +2547,8 @@ body.has-player .fab-new {
.comment-reply-textarea:focus {
outline: none;
border-color: var(--color-accent);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-accent) 20%, transparent);
box-shadow: 0 0 0 3px
color-mix(in srgb, var(--color-accent) 20%, transparent);
}
.comment-form-actions {
@@ -2574,3 +2606,324 @@ body.has-player .fab-new {
margin: 0;
padding: 0.2rem 0;
}
/* ── Follow button ── */
.follow-btn {
padding: 0.25rem 0.9rem;
border-radius: 6px;
border: 2px solid var(--color-accent);
background: transparent;
color: var(--color-accent);
cursor: pointer;
font-size: 0.8rem;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
font-family: inherit;
transition: background 0.15s, color 0.15s;
}
.follow-btn:hover {
background: var(--color-accent);
color: var(--color-on-accent, #fff);
}
.follow-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.follow-btn--following {
background: var(--color-accent);
color: var(--color-on-accent, #fff);
}
.follow-btn--following:hover {
background: transparent;
color: var(--color-accent);
}
/* ── Followed feed layout ── */
.followed-feed {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
}
.followed-sub-nav {
padding: 0.75rem 1.25rem;
align-self: center;
}
.followed-feed .dump-feed {
padding-top: 0;
}
.followed-feed .index-status {
padding-top: 0.75rem;
padding-bottom: 0.75rem;
}
/* ── Notification bell ── */
@keyframes bell-ring {
0% {
transform: rotate(0deg);
}
12% {
transform: rotate(-20deg);
}
28% {
transform: rotate(20deg);
}
42% {
transform: rotate(-14deg);
}
56% {
transform: rotate(10deg);
}
70% {
transform: rotate(-6deg);
}
84% {
transform: rotate(3deg);
}
100% {
transform: rotate(0deg);
}
}
.notification-bell {
position: relative;
background: var(--color-header-user-bg);
border: none;
cursor: pointer;
font-size: 0.95rem;
padding: 0.35rem 0.85rem;
border-radius: 8px;
transition: background 0.15s;
display: inline-flex;
align-items: center;
}
.notification-bell:hover {
background: var(--color-header-user-bg-hover);
}
.notification-bell-icon {
display: inline-block;
transform-origin: 50% 10%;
}
.notification-bell--ringing .notification-bell-icon {
animation: bell-ring 0.65s cubic-bezier(0.36, 0.07, 0.19, 0.97);
}
.notification-badge {
position: absolute;
top: -3px;
right: -3px;
background: var(--color-danger);
color: #fff;
font-size: 0.6rem;
font-weight: 700;
min-width: 16px;
height: 16px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
padding: 0 3px;
line-height: 1;
pointer-events: none;
box-shadow: 0 0 0 2px var(--color-bg);
}
/* ── Notifications page ── */
.notifications-page {
max-width: 680px;
margin: 0 auto;
padding: 1.5rem 1rem 3rem;
}
.notifications-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
margin-bottom: 1.75rem;
}
.notifications-title {
font-size: 1.4rem;
font-weight: 700;
margin: 0;
display: flex;
align-items: center;
gap: 0.5rem;
}
.notifications-title-bell {
font-size: 1.2rem;
}
.notifications-unread-pill {
font-size: 0.75rem;
font-weight: 700;
background: color-mix(in srgb, var(--color-danger) 18%, transparent);
color: var(--color-danger);
border: 1px solid color-mix(in srgb, var(--color-danger) 35%, transparent);
border-radius: 12px;
padding: 0.2rem 0.65rem;
white-space: nowrap;
}
.notifications-empty {
text-align: center;
padding: 3rem 1rem;
color: var(--color-text-muted);
}
.notifications-empty-icon {
font-size: 2.5rem;
display: block;
margin-bottom: 0.75rem;
opacity: 0.5;
}
.notifications-empty p {
margin: 0.25rem 0;
}
.notifications-empty-hint {
font-size: 0.85rem;
max-width: 340px;
margin: 0.5rem auto 0 !important;
line-height: 1.5;
}
.notif-group {
margin-bottom: 2rem;
}
.notif-group-label {
font-size: 0.68rem;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--color-text-muted);
margin: 0 0 0.6rem;
display: flex;
align-items: center;
gap: 0.6rem;
}
.notif-group-label::after {
content: "";
flex: 1;
height: 1px;
background: var(--color-border-subtle);
}
.notification-list {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
gap: 1rem;
}
.notification-item {
display: flex;
align-items: center;
gap: 0.875rem;
padding: 0.875rem 1rem;
background: var(--color-surface);
border-radius: 10px;
border: 1px solid var(--color-border-subtle);
border-left: 3px solid transparent;
transition: background 0.12s, border-color 0.12s;
}
.notification-item:hover {
background: color-mix(
in srgb,
var(--color-surface) 80%,
var(--color-text) 8%
);
}
.notification-item--unread {
border-left-color: var(--color-accent);
background: color-mix(in srgb, var(--color-accent) 9%, var(--color-surface));
}
.notification-item--unread:hover {
background: color-mix(in srgb, var(--color-accent) 14%, var(--color-surface));
}
.notif-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--color-accent);
box-shadow: 0 0 5px color-mix(in srgb, var(--color-accent) 70%, transparent);
flex-shrink: 0;
align-self: center;
}
.notif-icon {
width: 36px;
height: 36px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.9rem;
flex-shrink: 0;
}
.notif-icon--upvote {
background: color-mix(in srgb, #f59e0b 30%, transparent);
color: #f59e0b;
}
.notif-icon--follow {
background: color-mix(in srgb, #8b5cf6 30%, transparent);
color: #8b5cf6;
}
.notif-icon--dump {
background: color-mix(in srgb, #3b82f6 30%, transparent);
color: #3b82f6;
}
.notif-icon--playlist {
background: color-mix(in srgb, #10b981 30%, transparent);
color: #10b981;
}
.notification-body {
flex: 1;
display: flex;
align-items: baseline;
gap: 0.75rem;
min-width: 0;
}
.notification-content {
flex: 1;
font-size: 0.875rem;
line-height: 1.5;
min-width: 0;
}
.notification-time {
font-size: 0.72rem;
color: var(--color-text-muted);
white-space: nowrap;
flex-shrink: 0;
}
.notif-link {
color: var(--color-text);
text-decoration: none;
font-weight: 600;
}
.notif-link:hover {
color: var(--color-accent);
text-decoration: underline;
}
.load-more-btn {
display: block;
margin: 1.5rem auto 0;
padding: 0.5rem 1.5rem;
background: none;
border: 1px solid var(--color-border-subtle);
border-radius: 6px;
cursor: pointer;
font-size: 0.875rem;
color: var(--color-text-muted);
transition: border-color 0.15s, color 0.15s;
}
.load-more-btn:hover:not(:disabled) {
border-color: var(--color-accent);
color: var(--color-accent);
}
.load-more-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}