mirror of
https://github.com/narrrl/pl8list-rewind.git
synced 2026-11-03 16:41:26 +01:00
The rim hue drifted with play time until it left the card's colour altogether, so a pink card could carry a green drop. It now only sways around the accent. The edge fades into the page, a halo in the accent pulses behind it like the plain artwork's glow, the surface reflects the accent from above, and the title and the rest of the card sit on top where the drop spills over them. Dragging the drop turns it (sideways only on touch, where up and down turn the page). A flick keeps it spinning; it slows down and a weak spring wobbles it back to face the front. Spun fast it flattens a little and sloshes, and the artwork blurs into colour where it is seen from the side. A spin is not a tap, so it does not pause.
724 lines
22 KiB
CSS
724 lines
22 KiB
CSS
/* Design tokens ---------------------------------------------------------- */
|
|
|
|
:root {
|
|
color-scheme: dark;
|
|
--bg: #000;
|
|
--fg: #f2f2f2;
|
|
--muted: #9a9a9a;
|
|
--faint: #666;
|
|
--accent: #ff4d6d;
|
|
--panel: rgba(18, 18, 20, 0.94);
|
|
--line: #ffffff1f;
|
|
--glass: rgba(255, 255, 255, 0.1);
|
|
--glass-strong: rgba(255, 255, 255, 0.2);
|
|
--radius-s: 8px;
|
|
--radius-m: 14px;
|
|
--radius-l: 18px;
|
|
--shadow: 0 16px 40px #000a;
|
|
--font: system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
--font-display: var(--font);
|
|
--ease-out: cubic-bezier(0.2, 0.8, 0.2, 1);
|
|
--fast: 0.15s;
|
|
}
|
|
|
|
* { box-sizing: border-box; }
|
|
|
|
html, body {
|
|
margin: 0;
|
|
height: 100%;
|
|
background: var(--bg);
|
|
color: var(--fg);
|
|
font: 15px/1.4 var(--font);
|
|
overflow: hidden;
|
|
-webkit-tap-highlight-color: transparent;
|
|
}
|
|
|
|
a { color: inherit; }
|
|
|
|
button, input {
|
|
font: inherit;
|
|
color: inherit;
|
|
}
|
|
|
|
button {
|
|
cursor: pointer;
|
|
background: rgba(255, 255, 255, 0.1);
|
|
border: 1px solid rgba(255, 255, 255, 0.15);
|
|
border-radius: 999px;
|
|
padding: 0.35em 0.9em;
|
|
}
|
|
|
|
[hidden] { display: none !important; }
|
|
|
|
.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
|
|
|
|
/* Header and panels ------------------------------------------------------ */
|
|
|
|
#bar {
|
|
position: fixed;
|
|
inset: 0 0 auto 0;
|
|
z-index: 10;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
padding: 0.6rem 1rem;
|
|
padding-top: max(0.6rem, env(safe-area-inset-top));
|
|
background: linear-gradient(#000c, #0000);
|
|
}
|
|
|
|
.logo { font-weight: 600; margin-right: auto; }
|
|
.logo b { color: var(--accent); }
|
|
#stats { color: var(--muted); font-size: 0.8rem; }
|
|
#help-btn { width: 2.2em; padding: 0.35em 0; }
|
|
#account-btn { max-width: 14rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
#filters-btn.active::after {
|
|
content: "";
|
|
display: inline-block;
|
|
width: 0.45em;
|
|
height: 0.45em;
|
|
margin-left: 0.4em;
|
|
border-radius: 50%;
|
|
background: var(--accent);
|
|
vertical-align: middle;
|
|
}
|
|
#bar button[aria-expanded="true"] { background: rgba(255, 255, 255, 0.22); }
|
|
#mode-chip {
|
|
max-width: 14rem;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.45em;
|
|
padding-left: 0.3em;
|
|
white-space: nowrap;
|
|
background: color-mix(in oklab, var(--accent) 40%, transparent);
|
|
}
|
|
#mode-chip .chip-label { overflow: hidden; text-overflow: ellipsis; }
|
|
.chip-avatar {
|
|
flex: none;
|
|
width: 1.5em;
|
|
height: 1.5em;
|
|
border-radius: 50%;
|
|
display: grid;
|
|
place-items: center;
|
|
font-size: 0.8em;
|
|
background: #0006 center / cover no-repeat;
|
|
}
|
|
|
|
.panel {
|
|
position: fixed;
|
|
z-index: 11;
|
|
top: calc(3rem + env(safe-area-inset-top));
|
|
right: 1rem;
|
|
background: var(--panel);
|
|
border: 1px solid #333;
|
|
border-radius: 12px;
|
|
padding: 0.8rem 1rem;
|
|
box-shadow: 0 16px 40px #000a;
|
|
}
|
|
|
|
#filters { display: flex; gap: 0.8rem; align-items: center; flex-wrap: wrap; max-width: 24rem; }
|
|
.modes { display: flex; gap: 0.4rem; flex-wrap: wrap; flex-basis: 100%; margin: 0; padding: 0; border: 0; }
|
|
.modes legend { color: var(--muted); font-size: 0.8rem; padding: 0; margin-bottom: 0.3rem; }
|
|
.modes label { cursor: pointer; }
|
|
.modes input { position: absolute; opacity: 0; }
|
|
.modes label:has(input) {
|
|
padding: 0.25em 0.8em;
|
|
border-radius: 999px;
|
|
border: 1px solid #ffffff40;
|
|
font-size: 0.85rem;
|
|
}
|
|
.modes label:has(input:checked) { background: var(--accent); border-color: transparent; color: #000; }
|
|
.modes label:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
#filters input[type=number] {
|
|
width: 4.5em;
|
|
background: #222;
|
|
border: 1px solid #444;
|
|
border-radius: 6px;
|
|
padding: 0.2em 0.4em;
|
|
}
|
|
|
|
#likes { width: min(26rem, calc(100vw - 2rem)); max-height: 70vh; overflow: auto; }
|
|
#likes h2 { margin: 0 0 0.5rem; font-size: 1rem; }
|
|
#likes ol { margin: 0; padding: 0; list-style: none; }
|
|
#likes li {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.6rem;
|
|
padding: 0.35rem 0;
|
|
border-bottom: 1px solid #ffffff10;
|
|
}
|
|
#likes li:last-child { border-bottom: none; }
|
|
#likes li .meta { min-width: 0; flex: 1; }
|
|
#likes li .meta a { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-decoration: none; }
|
|
#likes li .meta a:hover { text-decoration: underline; }
|
|
#likes li .meta span { color: var(--muted); font-size: 0.8rem; }
|
|
#likes li .play-now {
|
|
flex: none;
|
|
width: 2rem;
|
|
height: 2rem;
|
|
padding: 0;
|
|
display: grid;
|
|
place-items: center;
|
|
}
|
|
#likes li .play-now svg { width: 0.9rem; height: 0.9rem; }
|
|
|
|
#account { width: min(20rem, calc(100vw - 2rem)); display: flex; flex-direction: column; align-items: flex-start; gap: 0.7rem; }
|
|
#account h2 { margin: 0; font-size: 1rem; }
|
|
#account a { font-size: 0.85rem; color: var(--muted); }
|
|
#sync { width: 100%; display: flex; flex-direction: column; align-items: flex-start; gap: 0.5rem;
|
|
padding-bottom: 0.7rem; border-bottom: 1px solid #ffffff14; }
|
|
#sync h3 { margin: 0; font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted); }
|
|
#sync-status { margin: 0; font-size: 0.85rem; color: var(--muted); }
|
|
#sync-status.on::before { content: "● "; color: #3ddc84; }
|
|
#delete-form { display: flex; flex-direction: column; gap: 0.5rem; width: 100%; }
|
|
#delete-form label { color: var(--muted); font-size: 0.85rem; }
|
|
#delete-form input { padding: 0.4em 0.6em; border-radius: 8px; border: 1px solid #444; background: #111; }
|
|
.link.danger { color: var(--accent); }
|
|
button.danger:not(.link) { background: var(--accent); border: none; color: #fff; font-weight: 600; }
|
|
.legal-links a { color: inherit; }
|
|
|
|
/* Feed ------------------------------------------------------------------- */
|
|
|
|
/* The pager (pager.ts) places cards with transforms and sets --away (0 in place,
|
|
1 a full page away); the visualizer sets --energy (0..1, bass level) on the
|
|
playing card; --card-accent comes from the artist image. */
|
|
#feed {
|
|
position: fixed;
|
|
inset: 0;
|
|
overflow: hidden;
|
|
touch-action: none;
|
|
overscroll-behavior: none;
|
|
}
|
|
|
|
.card {
|
|
--card-accent: var(--accent);
|
|
--energy: 0;
|
|
--away: 0;
|
|
position: absolute;
|
|
inset: 0;
|
|
will-change: transform;
|
|
backface-visibility: hidden;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
overflow: hidden;
|
|
touch-action: manipulation;
|
|
user-select: none;
|
|
-webkit-user-select: none;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.bg {
|
|
position: absolute;
|
|
inset: -40px;
|
|
background: #111 center / cover no-repeat;
|
|
filter: blur(40px) brightness(0.45) saturate(1.3);
|
|
}
|
|
|
|
.glow {
|
|
position: absolute;
|
|
inset: 0;
|
|
pointer-events: none;
|
|
background: radial-gradient(55% 40% at 50% 40%, color-mix(in oklab, var(--card-accent) 55%, transparent), transparent 70%);
|
|
opacity: calc(0.18 + var(--energy) * 0.55);
|
|
}
|
|
|
|
.content {
|
|
position: relative;
|
|
/* Cards shrink and dim a little as they leave, for depth while swiping. */
|
|
transform: scale(calc(1 - var(--away) * 0.08));
|
|
opacity: calc(1 - var(--away) * 0.55);
|
|
width: min(34rem, calc(100vw - 5.5rem));
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
text-align: center;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.art {
|
|
position: relative;
|
|
width: min(16rem, 42vh, 70vw);
|
|
aspect-ratio: 1;
|
|
/* Round, so the static artwork matches the liquid drop (blob/) it turns into. */
|
|
border-radius: 50%;
|
|
background: #222 center / cover no-repeat;
|
|
box-shadow:
|
|
0 20px 60px #000a,
|
|
0 0 calc(10px + var(--energy) * 70px) color-mix(in oklab, var(--card-accent) calc(15% + var(--energy) * 45%), transparent);
|
|
display: grid;
|
|
place-items: center;
|
|
margin-bottom: 0.6rem;
|
|
/* Pulse on the beat through `scale`; `transform` is left to the pause animation. */
|
|
scale: calc(1 + var(--energy) * 0.045);
|
|
transition: transform 0.25s var(--ease-out), filter 0.25s ease;
|
|
}
|
|
.initial { font-size: 4rem; font-weight: 700; color: #fff6; }
|
|
|
|
/* The liquid drop spills past the art slot; taps go through it to the card. */
|
|
.liquid-canvas {
|
|
position: absolute;
|
|
inset: -50%;
|
|
width: 200%;
|
|
height: 200%;
|
|
pointer-events: none;
|
|
}
|
|
/* The halo stays behind the drop and pulses with it; the text sits on top where
|
|
the drop spills over it. */
|
|
.card.liquid .art {
|
|
z-index: 0;
|
|
background-image: none !important;
|
|
background-color: transparent;
|
|
box-shadow: 0 0 calc(30px + var(--energy) * 80px) color-mix(in oklab, var(--card-accent) calc(18% + var(--energy) * 40%), transparent);
|
|
scale: 1;
|
|
}
|
|
.card.liquid .content > :not(.art) { position: relative; z-index: 1; }
|
|
.card.liquid .title { text-shadow: 0 2px 16px #000b; }
|
|
.card.liquid .initial { display: none; }
|
|
@media (pointer: fine) {
|
|
.card.liquid .art { cursor: grab; }
|
|
.card.turning, .card.turning .art { cursor: grabbing; }
|
|
}
|
|
.paused-badge, .spinner { z-index: 1; }
|
|
|
|
.audio-debug {
|
|
position: fixed;
|
|
left: 0.7rem;
|
|
bottom: 0.7rem;
|
|
z-index: 30;
|
|
border-radius: var(--radius-s);
|
|
background: #000b;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.card.paused .art { transform: scale(0.96); filter: brightness(0.7); }
|
|
.paused-badge {
|
|
position: absolute;
|
|
inset: 0;
|
|
margin: auto;
|
|
width: 4.5rem;
|
|
height: 4.5rem;
|
|
border-radius: 50%;
|
|
background: #000a;
|
|
display: grid;
|
|
place-items: center;
|
|
opacity: 0;
|
|
transition: opacity 0.2s;
|
|
pointer-events: none;
|
|
}
|
|
.paused-badge svg { width: 2rem; height: 2rem; margin-left: 0.2rem; }
|
|
.card.paused .paused-badge { opacity: 1; }
|
|
|
|
.spinner {
|
|
position: absolute;
|
|
inset: 0;
|
|
margin: auto;
|
|
width: 3rem;
|
|
height: 3rem;
|
|
border-radius: 50%;
|
|
border: 3px solid #ffffff33;
|
|
border-top-color: #fff;
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
animation: spin 0.8s linear infinite;
|
|
}
|
|
.card.buffering .spinner { opacity: 1; }
|
|
.card.buffering .paused-badge { opacity: 0; }
|
|
@keyframes spin { to { transform: rotate(360deg); } }
|
|
|
|
.title {
|
|
font: 800 clamp(1.3rem, 4.2vw, 1.9rem)/1.1 var(--font-display);
|
|
letter-spacing: -0.01em;
|
|
margin: 0;
|
|
overflow-wrap: anywhere;
|
|
text-wrap: balance;
|
|
}
|
|
.why {
|
|
margin: 0;
|
|
min-height: 0;
|
|
font-size: 0.7rem;
|
|
letter-spacing: 0.1em;
|
|
text-transform: uppercase;
|
|
color: color-mix(in oklab, var(--card-accent) 70%, #fff);
|
|
}
|
|
.why:empty { display: none; }
|
|
.byline { display: flex; align-items: center; gap: 0.6rem; }
|
|
.artist { color: var(--muted); text-decoration: none; }
|
|
.artist:hover { text-decoration: underline; }
|
|
.follow, .radio {
|
|
font-size: 0.75rem;
|
|
padding: 0.15em 0.75em;
|
|
border-radius: 999px;
|
|
border: 1px solid #ffffff40;
|
|
background: transparent;
|
|
}
|
|
.follow.on { background: #ffffff1f; border-color: transparent; color: var(--muted); }
|
|
|
|
.chips { display: flex; gap: 0.4rem; flex-wrap: wrap; justify-content: center; }
|
|
.chips span {
|
|
font-size: 0.75rem;
|
|
padding: 0.1em 0.6em;
|
|
border-radius: 999px;
|
|
background: rgba(255, 255, 255, 0.12);
|
|
}
|
|
.chips .free { background: #1f7a4d; }
|
|
.chips span:first-child { background: color-mix(in oklab, var(--card-accent) 35%, transparent); }
|
|
.chips .preview { background: #6a4b16; }
|
|
.chips .tag { background: transparent; box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--card-accent) 60%, transparent); }
|
|
|
|
.desc {
|
|
margin: 0;
|
|
display: -webkit-box;
|
|
-webkit-box-orient: vertical;
|
|
-webkit-line-clamp: 2;
|
|
line-clamp: 2;
|
|
overflow: hidden;
|
|
overflow-wrap: anywhere;
|
|
color: var(--muted);
|
|
font-size: 0.85rem;
|
|
}
|
|
|
|
.wave-wrap { position: relative; width: 100%; margin-top: 0.6rem; }
|
|
.wave { display: block; width: 100%; height: 64px; cursor: pointer; touch-action: none; }
|
|
.wave-tip {
|
|
position: absolute;
|
|
bottom: calc(100% + 4px);
|
|
transform: translateX(-50%);
|
|
padding: 0.1em 0.45em;
|
|
border-radius: 6px;
|
|
background: #000c;
|
|
font-size: 0.75rem;
|
|
pointer-events: none;
|
|
opacity: 0;
|
|
transition: opacity 0.1s;
|
|
}
|
|
.wave-wrap.hover .wave-tip { opacity: 1; }
|
|
.time { width: 100%; display: flex; justify-content: space-between; color: var(--muted); font-size: 0.75rem; }
|
|
.counts { color: var(--muted); font-size: 0.8rem; }
|
|
|
|
.actions {
|
|
position: absolute;
|
|
right: 0.8rem;
|
|
bottom: 18vh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.9rem;
|
|
}
|
|
.actions > * {
|
|
width: 3rem;
|
|
height: 3rem;
|
|
display: grid;
|
|
place-items: center;
|
|
border-radius: 50%;
|
|
background: rgba(255, 255, 255, 0.12);
|
|
border: none;
|
|
text-decoration: none;
|
|
padding: 0;
|
|
transition: background 0.15s, transform 0.15s;
|
|
}
|
|
.actions > *:hover { background: rgba(255, 255, 255, 0.2); }
|
|
.actions > *:active { transform: scale(0.9); }
|
|
.actions svg { width: 1.3rem; height: 1.3rem; }
|
|
.actions .like.on { color: var(--card-accent); background: color-mix(in oklab, var(--card-accent) 22%, transparent); }
|
|
.actions .like.pop svg { animation: pop 0.35s ease; }
|
|
@keyframes pop { 50% { transform: scale(1.4); } }
|
|
|
|
/* Feedback overlays ------------------------------------------------------ */
|
|
|
|
.flash {
|
|
position: absolute;
|
|
z-index: 2;
|
|
top: 50%;
|
|
left: 50%;
|
|
width: 5.5rem;
|
|
height: 5.5rem;
|
|
margin: -2.75rem 0 0 -2.75rem;
|
|
border-radius: 50%;
|
|
background: #000a;
|
|
display: grid;
|
|
place-items: center;
|
|
pointer-events: none;
|
|
animation: flash 0.6s ease forwards;
|
|
}
|
|
.flash svg { width: 2.4rem; height: 2.4rem; }
|
|
@keyframes flash {
|
|
from { opacity: 1; transform: scale(0.7); }
|
|
to { opacity: 0; transform: scale(1.3); }
|
|
}
|
|
|
|
.burst {
|
|
position: absolute;
|
|
z-index: 3;
|
|
width: 6rem;
|
|
height: 6rem;
|
|
margin: -3rem 0 0 -3rem;
|
|
color: var(--card-accent);
|
|
pointer-events: none;
|
|
filter: drop-shadow(0 6px 20px color-mix(in oklab, var(--card-accent) 55%, transparent));
|
|
animation: burst 0.8s cubic-bezier(.2, .9, .3, 1.3) forwards;
|
|
}
|
|
@keyframes burst {
|
|
0% { opacity: 0; transform: scale(0.3) rotate(var(--tilt)); }
|
|
25% { opacity: 1; transform: scale(1.15) rotate(var(--tilt)); }
|
|
60% { opacity: 1; transform: scale(1) rotate(var(--tilt)); }
|
|
100% { opacity: 0; transform: translateY(-3rem) scale(0.9) rotate(var(--tilt)); }
|
|
}
|
|
|
|
#toast {
|
|
position: fixed;
|
|
z-index: 40;
|
|
left: 50%;
|
|
bottom: max(2rem, env(safe-area-inset-bottom));
|
|
transform: translate(-50%, 1rem);
|
|
padding: 0.6em 1.1em;
|
|
border-radius: 999px;
|
|
background: #222e;
|
|
border: 1px solid #ffffff22;
|
|
font-size: 0.9rem;
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
transition: opacity 0.2s, transform 0.2s;
|
|
white-space: nowrap;
|
|
}
|
|
#toast.show { opacity: 1; transform: translate(-50%, 0); }
|
|
|
|
#feed-status {
|
|
position: fixed;
|
|
z-index: 5;
|
|
left: 50%;
|
|
bottom: max(6rem, calc(env(safe-area-inset-bottom) + 5rem));
|
|
transform: translateX(-50%);
|
|
width: min(26rem, calc(100vw - 2rem));
|
|
margin: 0;
|
|
color: var(--muted);
|
|
text-align: center;
|
|
pointer-events: none;
|
|
}
|
|
#feed:empty + #feed-status { bottom: 50%; }
|
|
|
|
#start {
|
|
position: fixed;
|
|
inset: 0;
|
|
z-index: 20;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 1rem;
|
|
background: #000b;
|
|
cursor: pointer;
|
|
}
|
|
#start button { font-size: 1.3rem; padding: 0.7em 1.6em; background: var(--accent); border: none; color: #fff; }
|
|
|
|
/* Radio sheet: a bottom sheet on phones, a centred box elsewhere. */
|
|
#station {
|
|
position: fixed;
|
|
inset: 0;
|
|
z-index: 26;
|
|
display: grid;
|
|
place-items: center;
|
|
padding: 1rem;
|
|
background: #000b;
|
|
animation: fade 0.2s ease;
|
|
}
|
|
.station-box {
|
|
position: relative;
|
|
width: min(26rem, 100%);
|
|
max-height: calc(100dvh - 2rem);
|
|
overflow-y: auto;
|
|
background: var(--panel);
|
|
border: 1px solid #333;
|
|
border-radius: var(--radius-l);
|
|
padding: 1.4rem;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.9rem;
|
|
animation: rise 0.35s var(--ease-out);
|
|
}
|
|
@keyframes rise { from { transform: translateY(2rem); opacity: 0; } }
|
|
#station-back { align-self: flex-start; }
|
|
#station-close { position: absolute; top: 0.8rem; right: 0.8rem; }
|
|
.icon-btn { width: 2.2rem; height: 2.2rem; padding: 0; display: grid; place-items: center; }
|
|
.icon-btn svg { width: 1rem; height: 1rem; }
|
|
.station-head { display: flex; gap: 1rem; align-items: center; }
|
|
.station-head h2 { margin: 0; font: 800 1.4rem/1.1 var(--font-display); overflow-wrap: anywhere; }
|
|
.station-kicker { margin: 0 0 0.2rem; font-size: 0.7rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--accent); }
|
|
#station-sub { margin: 0.3rem 0 0.4rem; color: var(--muted); font-size: 0.85rem; }
|
|
#station-tags { justify-content: flex-start; }
|
|
#station-tags span { background: rgba(255, 255, 255, 0.12); }
|
|
.station-avatar {
|
|
flex: none;
|
|
width: 5.5rem;
|
|
height: 5.5rem;
|
|
border-radius: 50%;
|
|
display: grid;
|
|
place-items: center;
|
|
background: #222 center / cover no-repeat;
|
|
box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent) 60%, transparent), 0 10px 30px #000a;
|
|
}
|
|
.station-avatar .initial { font-size: 2rem; }
|
|
.station-avatar.small { width: 3.4rem; height: 3.4rem; box-shadow: none; }
|
|
.station-avatar.small .initial { font-size: 1.2rem; }
|
|
.station-box h3 { margin: 0; font-size: 0.8rem; font-weight: 600; color: var(--muted); }
|
|
#station-neighbours {
|
|
list-style: none;
|
|
margin: 0 -1.4rem;
|
|
padding: 0 1.4rem 0.3rem;
|
|
display: flex;
|
|
gap: 0.7rem;
|
|
overflow-x: auto;
|
|
scrollbar-width: none;
|
|
color: var(--muted);
|
|
font-size: 0.85rem;
|
|
}
|
|
#station-neighbours li { animation: fade 0.5s ease; }
|
|
#station-neighbours button {
|
|
width: 4.2rem;
|
|
padding: 0;
|
|
border: none;
|
|
background: none;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 0.3rem;
|
|
font-size: 0.72rem;
|
|
color: var(--fg);
|
|
}
|
|
#station-neighbours button > span:last-child { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
#station-neighbours button:hover .station-avatar { box-shadow: 0 0 0 2px var(--accent); }
|
|
#station-exploring { margin: 0; color: var(--muted); font-size: 0.85rem; animation: breathe 1.4s ease-in-out infinite; }
|
|
@keyframes breathe { 50% { opacity: 0.35; } }
|
|
button.primary {
|
|
background: var(--accent);
|
|
border: none;
|
|
color: #fff;
|
|
font-weight: 600;
|
|
padding: 0.7em 1.2em;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 0.4em;
|
|
}
|
|
button.primary svg { width: 1em; height: 1em; }
|
|
|
|
/* The end of an artist radio, over its last card. */
|
|
#radio-end {
|
|
position: fixed;
|
|
z-index: 6;
|
|
left: 50%;
|
|
bottom: max(6rem, calc(env(safe-area-inset-bottom) + 5rem));
|
|
transform: translateX(-50%);
|
|
width: min(24rem, calc(100vw - 2rem));
|
|
padding: 1rem;
|
|
border-radius: var(--radius-m);
|
|
background: var(--panel);
|
|
border: 1px solid #333;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 0.5rem;
|
|
justify-content: center;
|
|
text-align: center;
|
|
animation: rise 0.35s var(--ease-out);
|
|
}
|
|
#radio-end p { flex-basis: 100%; margin: 0 0 0.3rem; color: var(--muted); font-size: 0.9rem; }
|
|
|
|
#help {
|
|
position: fixed;
|
|
inset: 0;
|
|
z-index: 25;
|
|
display: grid;
|
|
place-items: center;
|
|
padding: 1rem;
|
|
background: #000b;
|
|
animation: fade 0.2s ease;
|
|
}
|
|
@keyframes fade { from { opacity: 0; } }
|
|
.help-box {
|
|
width: min(22rem, 100%);
|
|
background: var(--panel);
|
|
border: 1px solid #333;
|
|
border-radius: 16px;
|
|
padding: 1.2rem 1.4rem;
|
|
}
|
|
.help-box h2 { margin: 0 0 0.8rem; font-size: 1.1rem; }
|
|
.help-box dl { display: grid; grid-template-columns: auto 1fr; gap: 0.45rem 1rem; margin: 0; }
|
|
.help-box dt { font-family: ui-monospace, monospace; font-size: 0.85rem; color: var(--accent); }
|
|
.help-box dd { margin: 0; }
|
|
.help-box p { color: var(--muted); font-size: 0.85rem; margin: 1rem 0; }
|
|
.help-box button { width: 100%; background: var(--accent); border: none; color: #fff; padding: 0.6em; font-weight: 600; }
|
|
@media (hover: hover) and (pointer: fine) { .touch-only { display: none !important; } }
|
|
@media not ((hover: hover) and (pointer: fine)) { .pointer-only { display: none !important; } }
|
|
|
|
/* Sign-in ---------------------------------------------------------------- */
|
|
|
|
#login {
|
|
position: fixed;
|
|
inset: 0;
|
|
z-index: 30;
|
|
display: grid;
|
|
place-items: center;
|
|
padding: 1rem;
|
|
background:
|
|
radial-gradient(60rem 40rem at 20% 10%, #ff4d6d33, transparent 60%),
|
|
radial-gradient(50rem 40rem at 90% 90%, #4d6dff2e, transparent 60%),
|
|
#050507;
|
|
}
|
|
|
|
.login-box {
|
|
width: min(24rem, 100%);
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.9rem;
|
|
}
|
|
.login-box h1 { margin: 0; font-size: 2.4rem; }
|
|
.login-box h1 b { color: var(--accent); }
|
|
.tagline { margin: 0 0 0.8rem; color: var(--muted); }
|
|
.login-box form { display: flex; flex-direction: column; gap: 0.6rem; }
|
|
.login-box label { color: var(--muted); font-size: 0.9rem; }
|
|
.login-box input {
|
|
padding: 0.7em 0.9em;
|
|
border-radius: 10px;
|
|
border: 1px solid #333;
|
|
background: #111;
|
|
}
|
|
#code { font-family: ui-monospace, monospace; font-size: 1.4rem; letter-spacing: 0.5em; text-align: center; }
|
|
.login-box button[type=submit] { background: var(--accent); border: none; color: #fff; padding: 0.7em; font-weight: 600; }
|
|
.login-box button[disabled] { opacity: 0.6; cursor: wait; }
|
|
.login-links { display: flex; justify-content: space-between; }
|
|
.login-box label.check { display: flex; gap: 0.6rem; align-items: flex-start; color: var(--fg); cursor: pointer; }
|
|
.check input { margin-top: 0.2rem; accent-color: var(--accent); }
|
|
.check small { display: block; margin-top: 0.2rem; color: #777; font-size: 0.75rem; line-height: 1.4; }
|
|
#login-cancel { align-self: center; }
|
|
.link { background: none; border: none; padding: 0; color: var(--muted); text-decoration: underline; font-size: 0.85rem; }
|
|
#login-error { margin: 0; min-height: 1.2em; color: var(--accent); font-size: 0.9rem; }
|
|
.fineprint { margin: 0; color: #666; font-size: 0.75rem; }
|
|
|
|
/* Phones ----------------------------------------------------------------- */
|
|
|
|
@media (max-width: 640px) {
|
|
#stats { display: none; }
|
|
#bar { gap: 0.4rem; padding-left: 0.7rem; padding-right: 0.7rem; }
|
|
#bar button { padding: 0.3em 0.7em; font-size: 0.85rem; }
|
|
#help-btn { display: none; }
|
|
#account-btn { max-width: 6.5rem; }
|
|
.panel { left: 0.7rem; right: 0.7rem; }
|
|
#station { place-items: end stretch; padding: 0; }
|
|
.station-box { width: 100%; border-radius: var(--radius-l) var(--radius-l) 0 0; padding-bottom: max(1.4rem, env(safe-area-inset-bottom)); }
|
|
#mode-chip { max-width: 9rem; }
|
|
/* A station chip says more than the logo; there is no room for both. */
|
|
#bar:has(#mode-chip:not([hidden])) .logo { display: none; }
|
|
#likes { width: auto; }
|
|
.content { width: calc(100vw - 2rem); margin-bottom: 4.5rem; }
|
|
.actions {
|
|
right: auto;
|
|
left: 50%;
|
|
bottom: max(1.2rem, env(safe-area-inset-bottom));
|
|
transform: translateX(-50%);
|
|
flex-direction: row;
|
|
gap: 0.9rem;
|
|
}
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
*, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
|
|
}
|