summaryrefslogtreecommitdiff
path: root/player-server/web/css/layout.css
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-17 15:25:52 +0300
committerPaul Buetow <paul@buetow.org>2026-05-17 15:25:52 +0300
commit914bd7cd6aa14e839332a98d91c30b19865b0cf2 (patch)
tree02b11537237a204048589e14ed24938b805e42f7 /player-server/web/css/layout.css
parent3b24f0e1be832584d6550e8cc3e5d24329f66f90 (diff)
Restructure repo: move Go server into player-server/
Diffstat (limited to 'player-server/web/css/layout.css')
-rw-r--r--player-server/web/css/layout.css729
1 files changed, 729 insertions, 0 deletions
diff --git a/player-server/web/css/layout.css b/player-server/web/css/layout.css
new file mode 100644
index 0000000..bc71bdd
--- /dev/null
+++ b/player-server/web/css/layout.css
@@ -0,0 +1,729 @@
+/* Reset & base */
+*, *::before, *::after { box-sizing: border-box; }
+html, body {
+ height: 100%;
+}
+body {
+ margin: 0;
+ font-family: var(--font-sans);
+ background: var(--bg-body);
+ color: var(--text-primary);
+ line-height: 1.45;
+}
+
+/* Header */
+.site-header {
+ position: fixed;
+ inset: 0 0 auto 0;
+ height: var(--header-height);
+ background: var(--bg-surface);
+ border-bottom: 1px solid var(--border);
+ display: flex;
+ align-items: center;
+ gap: var(--gap);
+ padding: 0 1rem;
+ z-index: 50;
+ transform: translateY(calc(-100% + 0.45rem));
+ transition: transform var(--transition-base);
+}
+.site-header::after {
+ content: "";
+ position: absolute;
+ left: 0;
+ right: 0;
+ bottom: -0.45rem;
+ height: 0.45rem;
+}
+.site-header:hover,
+.site-header:focus-within {
+ transform: translateY(0);
+}
+.header-main {
+ display: flex;
+ align-items: center;
+ gap: var(--gap);
+ flex: 1 1 auto;
+ min-width: 0;
+}
+.site-header .logo {
+ font-weight: 700;
+ letter-spacing: 0.04em;
+ color: var(--text-primary);
+ text-decoration: none;
+ display: inline-flex;
+ align-items: center;
+ gap: 0.35rem;
+}
+.site-header .logo-img {
+ height: 1.5rem;
+ width: auto;
+ display: block;
+}
+.site-header .spacer { flex: 1 1 auto; }
+
+.scan-indicator {
+ position: fixed;
+ top: 0.75rem;
+ right: 0.75rem;
+ z-index: 120;
+ display: inline-flex;
+ align-items: center;
+ gap: 0.4rem;
+ max-width: min(22rem, calc(100vw - 1.5rem));
+ padding: 0.25rem 0.55rem;
+ border: 1px solid var(--border);
+ border-radius: var(--radius-sm);
+ background: var(--bg-elevated);
+ color: var(--text-secondary);
+ box-shadow: var(--shadow-sm);
+ font-size: 0.78rem;
+ line-height: 1.2;
+ pointer-events: none;
+ white-space: nowrap;
+}
+.scan-indicator.hidden {
+ display: none;
+}
+.scan-indicator span:last-child {
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.scan-dot {
+ width: 0.5rem;
+ height: 0.5rem;
+ flex: 0 0 auto;
+ border-radius: 50%;
+ background: var(--accent);
+ box-shadow: 0 0 0 0 var(--accent-soft);
+ animation: scanPulse 1.4s ease-out infinite;
+}
+@keyframes scanPulse {
+ 0% { box-shadow: 0 0 0 0 var(--accent-soft); }
+ 100% { box-shadow: 0 0 0 0.45rem transparent; }
+}
+
+/* Sidebar */
+.sidebar {
+ position: fixed;
+ inset: var(--header-height) auto 0 0;
+ width: var(--sidebar-width);
+ background: var(--bg-surface);
+ border-right: 1px solid var(--border);
+ overflow: auto;
+ padding: 0.75rem 0.5rem;
+ display: none; /* hidden by default */
+ flex-direction: column;
+ gap: 0.25rem;
+ z-index: 40;
+}
+.sidebar.open {
+ display: flex;
+}
+.sidebar h2 {
+ font-size: 0.75rem;
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ color: var(--text-muted);
+ margin: 0.5rem 0.5rem 0.25rem;
+ padding-bottom: 0.25rem;
+ border-bottom: 1px solid var(--border);
+}
+.sidebar .set-item {
+ display: block;
+ padding: 0.4rem 0.6rem;
+ border-radius: var(--radius-sm);
+ color: var(--text-secondary);
+ text-decoration: none;
+ cursor: pointer;
+ user-select: none;
+}
+.sidebar .set-item:hover,
+.sidebar .set-item:focus-visible {
+ background: var(--bg-surface-hover);
+ color: var(--text-primary);
+ outline: none;
+}
+.sidebar .set-item.active {
+ background: var(--accent-soft);
+ color: var(--accent);
+ font-weight: 600;
+}
+.sidebar .set-item.selected {
+ border: 2px solid var(--accent);
+ background: var(--bg-elevated);
+ color: var(--text-primary);
+ font-weight: 600;
+}
+.sidebar-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+/* Main layout */
+.page {
+ padding-top: var(--header-height);
+ min-height: 100vh;
+}
+.page.has-sidebar {
+ padding-left: var(--sidebar-width);
+}
+.main-content {
+ padding: var(--gap);
+ max-width: 100%;
+}
+
+/* Breadcrumb bar */
+.breadcrumb-bar {
+ display: flex;
+ align-items: center;
+ gap: 0.35rem;
+ flex-wrap: wrap;
+ margin-bottom: 0.75rem;
+ font-size: 0.85rem;
+}
+.breadcrumb-bar.hidden { display: none !important; }
+.breadcrumb-bar button {
+ background: transparent;
+ border: none;
+ color: var(--accent);
+ padding: 0.15rem 0.25rem;
+ cursor: pointer;
+ font: inherit;
+ border-radius: var(--radius-sm);
+}
+.breadcrumb-bar button:hover { background: var(--accent-soft); }
+.breadcrumb-bar .breadcrumb-sep { color: var(--text-muted); user-select: none; }
+
+/* Folder card placeholder */
+.folder-card .thumb-wrap .placeholder { font-size: 3rem; }
+.set-card .thumb-wrap .placeholder { font-size: 3rem; }
+.set-card .thumb-wrap img + .placeholder { display: none; }
+
+/* Media grid */
+.media-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
+ gap: var(--gap);
+}
+.media-pager {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 0.75rem;
+ padding: 0.5rem 0.65rem;
+ border: 1px solid var(--border);
+ border-radius: var(--radius-sm);
+ background: var(--bg-surface);
+}
+.media-pager-top {
+ margin-bottom: 0.1rem;
+}
+.media-pager-bottom {
+ margin-top: 0.1rem;
+}
+.media-page-summary {
+ color: var(--text-secondary);
+ font-size: 0.85rem;
+}
+.media-card {
+ position: relative;
+ background: var(--bg-surface);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-md);
+ overflow: hidden;
+ cursor: pointer;
+ transition: transform var(--transition-fast), box-shadow var(--transition-fast), border-color var(--transition-fast);
+}
+.media-card:hover {
+ box-shadow: var(--shadow-md);
+ border-color: var(--border-strong);
+}
+.media-card.selected {
+ border-color: var(--selected-border);
+ background: var(--selected-bg);
+}
+.media-card.playing {
+ border-color: var(--playing-border);
+ background: var(--playing-bg);
+}
+.media-card .thumb-wrap {
+ position: relative;
+ aspect-ratio: 16 / 9;
+ background: var(--bg-elevated);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ overflow: hidden;
+}
+.media-card .thumb-wrap img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ display: block;
+}
+.media-card .thumb-wrap .placeholder {
+ color: var(--text-muted);
+ font-size: 0.85rem;
+}
+.media-card .meta {
+ padding: 0.55rem 0.65rem;
+ display: flex;
+ flex-direction: column;
+ gap: 0.15rem;
+ min-height: 3.5rem;
+}
+.media-card .title {
+ font-size: 0.9rem;
+ font-weight: 600;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.media-card .subtitle {
+ font-size: 0.78rem;
+ color: var(--text-secondary);
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.media-card .badge {
+ position: absolute;
+ top: 0.35rem;
+ right: 0.35rem;
+ font-size: 0.65rem;
+ padding: 0.15rem 0.4rem;
+ border-radius: var(--radius-sm);
+ background: var(--bg-overlay);
+ color: var(--text-primary);
+}
+
+.media-card .card-actions {
+ position: absolute;
+ bottom: 0.35rem;
+ left: 0;
+ right: 0;
+ display: flex;
+ gap: 0.25rem;
+ justify-content: center;
+ opacity: 0;
+ transition: opacity var(--transition-fast);
+ z-index: 2;
+}
+.media-card:hover .card-actions,
+.media-card.selected .card-actions,
+.media-card.playing .card-actions {
+ opacity: 1;
+}
+
+/* Audio row variant */
+.media-row {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+ padding: 0.5rem 0.75rem;
+ background: var(--bg-surface);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-md);
+ cursor: pointer;
+ transition: border-color var(--transition-fast), background var(--transition-fast);
+}
+.media-row:hover {
+ background: var(--bg-surface-hover);
+ border-color: var(--border-strong);
+}
+.media-row.selected {
+ border-color: var(--selected-border);
+ background: var(--selected-bg);
+}
+.media-row.playing {
+ border-color: var(--playing-border);
+ background: var(--playing-bg);
+}
+.media-row .row-icon {
+ color: var(--text-muted);
+ font-size: 1.1rem;
+}
+.media-row .row-body {
+ min-width: 0;
+ flex: 1 1 auto;
+}
+.media-row .row-title {
+ font-size: 0.9rem;
+ font-weight: 600;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.media-row .row-meta {
+ font-size: 0.78rem;
+ color: var(--text-secondary);
+}
+.media-row .row-duration {
+ font-size: 0.78rem;
+ color: var(--text-muted);
+ white-space: nowrap;
+}
+
+.media-row .row-cover {
+ width: 2.8rem;
+ height: 2.8rem;
+ object-fit: cover;
+ border-radius: var(--radius-sm);
+ flex-shrink: 0;
+ background: var(--bg-elevated);
+}
+
+/* Hide row action buttons unless hovered/selected/playing */
+.media-row button[data-action] {
+ opacity: 0;
+ transition: opacity var(--transition-fast);
+}
+.media-row:hover button[data-action],
+.media-row.selected button[data-action],
+.media-row.playing button[data-action] {
+ opacity: 1;
+}
+
+/* Modal */
+.modal-overlay {
+ position: fixed;
+ inset: 0;
+ background: var(--bg-overlay);
+ display: grid;
+ place-items: center;
+ z-index: 100;
+ opacity: 0;
+ visibility: hidden;
+ transition: opacity var(--transition-base), visibility var(--transition-base);
+}
+.modal-overlay.open {
+ opacity: 1;
+ visibility: visible;
+}
+.modal {
+ background: var(--bg-surface);
+ border: 1px solid var(--border-strong);
+ border-radius: var(--radius-lg);
+ box-shadow: var(--shadow-lg);
+ width: min(40rem, 92vw);
+ max-height: 86vh;
+ overflow: auto;
+ padding: 1.25rem;
+}
+.modal-header {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ margin-bottom: 0.75rem;
+}
+.modal-header h3 { margin: 0; font-size: 1.1rem; }
+.modal-header .spacer { flex: 1 1 auto; }
+
+/* Help modal */
+.help-modal {
+ width: min(64rem, 96vw);
+}
+.help-content {
+ overflow: auto;
+}
+.help-shortcuts {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
+ gap: 0.35rem 0.75rem;
+ font-size: 0.88rem;
+}
+.help-row {
+ display: grid;
+ grid-template-columns: minmax(4.6rem, auto) 1fr;
+ align-items: center;
+ gap: 0.5rem;
+ min-height: 2rem;
+ padding: 0.25rem 0.35rem;
+ border-bottom: 1px solid var(--border);
+}
+.help-row > span:first-child {
+ white-space: nowrap;
+ font-family: var(--font-mono);
+}
+.help-content kbd {
+ display: inline-block;
+ padding: 0.15rem 0.4rem;
+ border-radius: var(--radius-sm);
+ background: var(--bg-elevated);
+ border: 1px solid var(--border-strong);
+ font-size: 0.8rem;
+ min-width: 1.6rem;
+ text-align: center;
+}
+.help-shares {
+ line-height: 1.7;
+}
+
+/* Media info modal */
+.media-info-table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: 0.9rem;
+}
+.media-info-table th,
+.media-info-table td {
+ padding: 0.45rem 0.5rem;
+ border-bottom: 1px solid var(--border);
+ vertical-align: top;
+}
+.media-info-table th {
+ width: 11rem;
+ color: var(--text-secondary);
+ font-weight: 600;
+ text-align: left;
+}
+.media-info-table td {
+ color: var(--text-primary);
+ overflow-wrap: anywhere;
+}
+.media-info-raw {
+ margin-top: 1rem;
+ font-size: 0.85rem;
+}
+.media-info-raw summary {
+ cursor: pointer;
+ color: var(--text-secondary);
+}
+.media-info-raw pre {
+ max-height: 18rem;
+ overflow: auto;
+ padding: 0.75rem;
+ border: 1px solid var(--border);
+ border-radius: var(--radius-sm);
+ background: var(--bg-elevated);
+ color: var(--text-primary);
+ white-space: pre-wrap;
+ overflow-wrap: anywhere;
+}
+
+/* Responsive */
+@media (max-width: 860px) {
+ :root {
+ --header-height: 3rem;
+ --sidebar-width: 0;
+ }
+ .site-header {
+ gap: 0.35rem;
+ padding: 0 0.5rem;
+ }
+ .header-main {
+ gap: 0.35rem;
+ }
+ .search-bar {
+ max-width: none;
+ min-width: 0;
+ flex: 1 1 auto;
+ }
+ .sidebar {
+ position: fixed;
+ inset: var(--header-height) auto auto 0;
+ width: 100%;
+ height: auto;
+ max-height: 70vh;
+ border-right: none;
+ border-bottom: 1px solid var(--border);
+ display: none;
+ }
+ .sidebar.open { display: flex; }
+ .page { padding-left: 0; }
+ .page.has-sidebar { padding-left: 0; }
+ .player { inset: auto 0 0 0 !important; }
+ .toolbar {
+ flex-wrap: wrap;
+ gap: 0.35rem;
+ }
+ .toolbar .input-sm {
+ min-width: 4.5rem;
+ max-width: none;
+ flex: 1 1 4.5rem;
+ }
+ .site-header .logo {
+ font-size: 0.9rem;
+ }
+ #theme-toggle,
+ #user-name {
+ display: none;
+ }
+ #logout-btn {
+ padding: 0.3rem 0.5rem;
+ font-size: 0.75rem;
+ }
+ .media-grid { grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr)); }
+}
+
+@media (max-width: 520px) {
+ .media-grid { grid-template-columns: repeat(2, 1fr); }
+ .toolbar .input-sm {
+ min-width: 3.5rem;
+ }
+}
+
+/* Utilities */
+.hidden { display: none !important; }
+.overflow-hidden { overflow: hidden; }
+
+/* Layout helpers */
+.flex { display: flex; }
+.flex-wrap { flex-wrap: wrap; }
+.justify-end { justify-content: flex-end; }
+.align-center { align-items: center; }
+.gap-1 { gap: 0.25rem; }
+.gap-2 { gap: 0.5rem; }
+.gap-3 { gap: 0.75rem; }
+.gap-4 { gap: 1rem; }
+.mt-1 { margin-top: 0.25rem; }
+.mt-2 { margin-top: 0.5rem; }
+.mt-3 { margin-top: 0.75rem; }
+.my-1 { margin: 0.25rem 0; }
+.mb-2 { margin-bottom: 0.5rem; }
+.py-1 { padding-top: 0.25rem; padding-bottom: 0.25rem; }
+.border-b { border-bottom: 1px solid var(--border); }
+.text-sm { font-size: 0.85rem; }
+.text-xs { font-size: 0.8rem; }
+.text-75 { font-size: 0.75rem; }
+.text-90 { font-size: 0.9rem; }
+.text-muted { color: var(--text-muted); }
+.text-danger { color: var(--danger); }
+.flex-1-18 { flex: 1 1 18rem; }
+.flex-col { flex-direction: column; }
+.truncate {
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+.set-row {
+ display: flex;
+ align-items: center;
+ gap: 0.25rem;
+ padding-right: 0.25rem;
+}
+.tag-chip {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.2rem;
+ padding: 0.2rem 0.5rem;
+ border-radius: var(--radius-sm);
+ background: var(--accent-soft);
+ color: var(--accent);
+ font-size: 0.78rem;
+}
+.grid-full { grid-column: 1 / -1; }
+
+/* Toolbar */
+.toolbar {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ margin-bottom: 0.75rem;
+}
+
+/* Empty state hint */
+.empty-hint {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ min-height: 50vh;
+ gap: 0.5rem;
+ text-align: center;
+}
+.empty-hint p {
+ margin: 0;
+ font-size: 1.1rem;
+ color: var(--text-secondary);
+}
+.empty-hint kbd {
+ display: inline-block;
+ padding: 0.15rem 0.4rem;
+ border-radius: var(--radius-sm);
+ background: var(--bg-elevated);
+ border: 1px solid var(--border-strong);
+ font-family: var(--font-mono);
+ font-size: 0.85rem;
+}
+.empty-hint.hidden { display: none !important; }
+
+/* Modal sizes */
+.modal-wide { width: min(56rem, 96vw); }
+
+/* Admin lists */
+.admin-list {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ display: flex;
+ flex-direction: column;
+ gap: 0.35rem;
+}
+.admin-list li {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0.25rem 0.35rem;
+ border-radius: var(--radius-sm);
+}
+.admin-list li.is-admin {
+ background: var(--accent-soft);
+}
+
+#admin-permissions {
+ max-width: 100%;
+ overflow-x: auto;
+}
+
+.admin-table {
+ width: max-content;
+ min-width: 100%;
+ border-collapse: collapse;
+ font-size: 0.82rem;
+}
+
+.admin-table th,
+.admin-table td {
+ padding: 0.35rem 0.45rem;
+ border-bottom: 1px solid var(--border);
+ text-align: left;
+ vertical-align: middle;
+ white-space: nowrap;
+}
+
+.admin-table th:first-child,
+.admin-table td:first-child {
+ max-width: 12rem;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.admin-table .perm-select {
+ min-width: 5.5rem;
+}
+
+.podcast-feed-row {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.35rem 0;
+ border-bottom: 1px solid var(--border);
+}
+
+.podcast-feed-cover {
+ width: 2.5rem;
+ height: 2.5rem;
+ border-radius: var(--radius-sm);
+ object-fit: cover;
+ background: var(--bg-elevated);
+ border: 1px solid var(--border);
+ flex: 0 0 auto;
+}
+
+.btn-download-episode {
+ align-self: flex-start;
+ margin-top: 0.2rem;
+}