This repository has been archived on 2026-05-31. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
imagerepo/app/static/style.css
T

2808 lines
58 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* /app/static/style.css
==============================================================================
ImageRepo Global Styles
- Theme tokens
- Base & layout
- Header / Navbar
- Flash messages
- Pagination
- Gallery
- Modal viewer
- Tag overlays & editor
- Tag/Artist cards (tags_list.html)
- Settings
- Index / Landing
- Forms
==============================================================================*/
/*------------------------------------------------------------------------------
Theme tokens - Dark theme matching showcase
------------------------------------------------------------------------------*/
:root {
/* surfaces */
--bg: #0a0a0a;
--bg-elevated: #141414;
--panel: #1a1a1a;
--panel-alt: rgba(20, 20, 20, 0.95);
/* nav */
--nav-bg: rgba(0, 0, 0, 0.8);
--nav-border: rgba(255, 255, 255, 0.08);
/* text */
--text: #ffffff;
--text-dim: rgba(255, 255, 255, 0.7);
--text-muted: rgba(255, 255, 255, 0.5);
/* accents */
--link: #a78bfa;
--link-hover: #c4b5fd;
/* shadows */
--shadow-1: 0 2px 8px rgba(0, 0, 0, 0.4);
--shadow-2: 0 4px 12px rgba(0, 0, 0, 0.5);
--shadow-3: 0 8px 24px rgba(0, 0, 0, 0.6);
/* buttons */
--btn-primary: #6366f1;
--btn-primary-hover: #4f46e5;
--btn-danger: #dc2626;
--btn-danger-hover: #b91c1c;
/* flash */
--flash-info: #3b82f6;
--flash-success: #22c55e;
--flash-warning: #f59e0b;
--flash-danger: #ef4444;
/* filter pills */
--filter-bg: rgba(255, 255, 255, 0.08);
--filter-fg: #e2e8f0;
--filter-border: rgba(255, 255, 255, 0.15);
--filter-active-bg: #6366f1;
--filter-active-fg: #ffffff;
--filter-active-border: #818cf8;
}
/*------------------------------------------------------------------------------
Base & layout
------------------------------------------------------------------------------*/
* { box-sizing: border-box; }
body {
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
margin: 0;
padding: 0;
color: var(--text);
background-color: var(--bg);
min-height: 100vh;
}
.content {
padding: 1rem;
max-width: 1800px;
margin: 0 auto;
}
.mainview {
max-width: 100%;
margin: 0 auto;
background-color: transparent;
min-height: 100vh;
}
h1 {
font-weight: 500;
letter-spacing: -0.02em;
}
hr {
border: none;
border-top: 1px solid rgba(255, 255, 255, 0.1);
margin: 0;
}
/*------------------------------------------------------------------------------
Header / Navbar (sticky with gradient fade)
------------------------------------------------------------------------------*/
header {
position: sticky;
top: 0;
z-index: 1000;
background: linear-gradient(to bottom, rgba(0,0,0,0.85) 0%, rgba(0,0,0,0.6) 70%, rgba(0,0,0,0) 100%);
padding-bottom: 1rem;
}
.navbar {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 0.25rem;
padding: 0.75rem 1rem;
background: transparent;
border: none;
}
.nav-button {
display: inline-block;
padding: 0.5rem 1rem;
color: var(--text);
text-decoration: none;
border-radius: 6px;
font-weight: 500;
font-size: 1rem;
transition: background 0.2s ease, transform 0.1s ease;
text-shadow: 0 1px 3px rgba(0,0,0,0.5);
}
.nav-button:hover {
background: rgba(255,255,255,0.15);
transform: translateY(-1px);
}
.nav-button:active {
background: rgba(255,255,255,0.1);
transform: translateY(0);
}
/* Nav dropdown */
.nav-dropdown {
position: relative;
display: inline-block;
}
.nav-dropdown-trigger {
cursor: pointer;
border: none;
background: transparent;
}
.nav-dropdown-trigger::after {
content: " \25BC";
font-size: 0.65em;
opacity: 0.7;
}
.nav-dropdown-menu {
display: none;
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
min-width: 150px;
background: rgba(0, 0, 0, 0.7);
border: 1px solid rgba(255, 255, 255, 0.5);
border-radius: 6px;
box-shadow: 0 4px 12px rgba(0,0,0,0.5);
z-index: 1001;
padding: 0.5rem 0;
}
.nav-dropdown-menu a {
display: block;
padding: 0.5rem 1rem;
color: var(--text);
text-decoration: none;
font-size: 0.95rem;
transition: background 0.15s ease;
}
.nav-dropdown-menu a:hover {
background: rgba(255, 255, 255, 0.15);
}
.nav-dropdown:hover .nav-dropdown-menu,
.nav-dropdown:focus-within .nav-dropdown-menu {
display: block;
}
/*------------------------------------------------------------------------------
Flash messages
------------------------------------------------------------------------------*/
.flash-container {
max-width: 800px;
margin: 1rem auto;
padding: 0 1rem;
}
.flash {
padding: 1rem;
margin-bottom: 1rem;
border-radius: 6px;
font-weight: 500;
color: #fff;
box-shadow: var(--shadow-1);
}
.flash-info { background-color: var(--flash-info); }
.flash-message,
.flash-success { background-color: var(--flash-success); }
.flash-warning { background-color: var(--flash-warning); }
.flash-danger,
.flash-error { background-color: var(--flash-danger); }
/*------------------------------------------------------------------------------
Pagination - Floating minimal bar
------------------------------------------------------------------------------*/
.floating-pagination {
position: fixed;
bottom: 1.5rem;
left: 50%;
transform: translateX(-50%);
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 0.75rem;
background: rgba(20, 20, 20, 0.9);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 999px;
box-shadow: var(--shadow-3);
z-index: 100;
}
.fp-btn {
display: flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.08);
color: var(--text);
text-decoration: none;
font-size: 1.25rem;
transition: all 0.2s ease;
}
.fp-btn:hover:not(.disabled) {
background: rgba(255, 255, 255, 0.15);
color: white;
}
.fp-btn.disabled {
opacity: 0.3;
cursor: not-allowed;
pointer-events: none;
}
.fp-indicator {
display: flex;
align-items: center;
gap: 0.25rem;
padding: 0 0.5rem;
font-size: 0.9rem;
color: var(--text-dim);
}
.fp-current {
color: var(--text);
font-weight: 500;
}
.fp-sep {
color: var(--text-muted);
}
.fp-total {
color: var(--text-muted);
}
/* Legacy pagination (kept for other pages if needed) */
.pagination-container {
text-align: center;
margin: 2rem 0;
}
.pagination-link {
display: inline-block;
margin: 0 0.2rem;
padding: 0.5rem 1rem;
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 6px;
color: var(--text-dim);
text-decoration: none;
background: rgba(255, 255, 255, 0.05);
transition: all 0.2s ease;
}
.pagination-link:hover {
background: rgba(255, 255, 255, 0.12);
border-color: rgba(255, 255, 255, 0.25);
color: var(--text);
}
.pagination-link.active {
background: var(--btn-primary);
color: white;
border-color: var(--btn-primary);
}
.pagination-ellipsis { padding: 0 0.5rem; color: var(--text-muted); }
/*------------------------------------------------------------------------------
Gallery - Seamless grid with overlaid metadata
------------------------------------------------------------------------------*/
.gallery-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 4px;
padding: 4px;
}
.gallery-item {
position: relative;
height: 320px;
background-color: var(--panel);
overflow: hidden;
cursor: pointer;
}
.gallery-thumb {
position: absolute;
inset: 0;
background-size: cover;
background-position: center;
transition: transform 0.3s ease, filter 0.3s ease;
}
.gallery-item:hover .gallery-thumb {
transform: scale(1.03);
filter: brightness(1.1);
}
/* Date overlay at bottom */
.image-date {
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding: 6px 8px;
font-size: 0.75rem;
color: rgba(255, 255, 255, 0.9);
background: linear-gradient(to top, rgba(0,0,0,0.6) 0%, rgba(0,0,0,0) 100%);
text-shadow: 0 1px 2px rgba(0,0,0,0.5);
z-index: 2;
pointer-events: none;
opacity: 0.5;
transition: opacity 0.2s ease;
}
.gallery-item:hover .image-date {
opacity: 1;
}
.gallery-item a {
color: var(--link);
font-weight: 500;
text-decoration: none;
font-size: 0.9rem;
}
.gallery-item a:hover { text-decoration: underline; color: var(--link-hover); }
/* video play glyph sits under tags, above image */
.play-overlay {
position: absolute;
inset: 0;
display: grid;
place-items: center;
z-index: 1;
pointer-events: none;
}
.play-overlay::before {
content: '▶';
font-size: 2rem;
color: rgba(255, 255, 255, 0.85);
text-shadow: 0 0 12px rgba(0,0,0,0.9), 0 2px 6px rgba(0,0,0,0.8);
width: 52px;
height: 52px;
border-radius: 50%;
background: rgba(0, 0, 0, 0.45);
display: flex;
align-items: center;
justify-content: center;
padding-left: 4px; /* optical centering of triangle */
}
/*------------------------------------------------------------------------------
Modal viewer
------------------------------------------------------------------------------*/
.modal {
display: none;
position: fixed;
inset: 0;
z-index: 1000;
background: rgba(0, 0, 0, 0.92);
justify-content: center;
align-items: center;
backdrop-filter: blur(4px);
-webkit-backdrop-filter: blur(4px);
}
.modal.active { display: flex; }
.modal-content {
position: relative;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 1rem;
}
/* Modal body - horizontal layout with image on left, tags on right */
.modal-body {
display: flex;
flex-direction: row;
align-items: flex-start;
gap: 1rem;
width: 100%;
max-width: 1600px;
max-height: 100%;
}
/* On smaller screens, stack vertically */
@media (max-width: 900px) {
.modal-body {
flex-direction: column;
align-items: center;
height: 100%;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
}
/* Mobile landscape: prioritize image, minimize tag editor */
@media (max-width: 900px) and (orientation: landscape) {
.modal-body {
flex-direction: row;
align-items: stretch;
}
.modal-image-wrapper {
max-width: calc(100% - 200px);
max-height: calc(100vh - 2rem);
height: calc(100vh - 2rem);
}
.tag-editor {
width: 180px;
min-width: 180px;
max-height: calc(100vh - 2rem);
overflow-y: auto;
}
}
/* Image wrapper & zoom */
.modal-image-wrapper {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
max-width: calc(100% - 300px); /* Account for tag editor */
height: calc(100vh - 140px);
max-height: calc(100vh - 140px);
overflow: hidden;
cursor: default;
}
.modal-image-wrapper.zoomed {
overflow: auto;
cursor: grab;
/* Enable scrolling by removing flex centering when zoomed */
display: block;
}
.modal-image-wrapper.zoomed img {
width: auto;
height: auto;
max-width: none;
max-height: none;
cursor: grab;
}
.modal-image-wrapper img {
max-width: 100%;
max-height: calc(100vh - 140px);
object-fit: contain;
border-radius: 4px;
}
.modal-image-wrapper video {
max-width: 100%;
max-height: calc(100vh - 140px);
border-radius: 4px;
}
/* Tall images */
.modal-image-wrapper.too-tall { overflow-y: auto; }
.modal-image-wrapper.too-tall img {
width: 90vw; height: auto; max-height: none;
}
/* On smaller screens (portrait), full width for image wrapper */
@media (max-width: 900px) and (orientation: portrait) {
.modal-image-wrapper {
max-width: 100%;
height: auto;
max-height: 55vh;
flex-shrink: 0;
}
.modal-image-wrapper img,
.modal-image-wrapper video {
max-height: 55vh;
}
}
/* Modal navigation buttons - cleaner pill style */
.modal-button {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 48px;
height: 48px;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.15);
color: rgba(255, 255, 255, 0.8);
font-size: 1.25rem;
cursor: pointer;
z-index: 1001;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: all 0.2s ease;
border-radius: 50%;
}
.modal-button:hover {
background: rgba(255, 255, 255, 0.2);
color: white;
border-color: rgba(255, 255, 255, 0.3);
}
.modal-content:hover .modal-button { opacity: 1; }
.modal-prev { left: 1rem; }
.modal-next { right: 1rem; }
/* Mobile: always show nav buttons, larger touch targets */
@media (max-width: 900px) {
.modal-button {
opacity: 0.8;
width: 44px;
height: 44px;
}
.modal-prev { left: 0.5rem; }
.modal-next { right: 0.5rem; }
}
/* Close button */
.modal-close-btn {
position: absolute;
top: 1rem;
right: 1rem;
width: 40px;
height: 40px;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 50%;
color: rgba(255, 255, 255, 0.8);
font-size: 1.5rem;
cursor: pointer;
z-index: 1002;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
line-height: 1;
}
.modal-close-btn:hover {
background: rgba(255, 255, 255, 0.2);
color: white;
border-color: rgba(255, 255, 255, 0.3);
}
/* Close hint - hidden on mobile since we have a button */
.modal-close-hint {
position: absolute;
top: 1.5rem;
right: 4rem;
color: rgba(255, 255, 255, 0.4);
font-size: 0.8rem;
pointer-events: none;
}
@media (max-width: 640px) {
.modal-close-hint { display: none; }
}
/* Modal sidebar sections */
.modal-sidebar-section {
padding: 0.75rem 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.07);
}
.modal-sidebar-section:last-child {
border-bottom: none;
}
/* Series toggle button (for "Add to Series" collapsed state) */
.modal-series-toggle {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
background: none;
border: none;
color: var(--text-muted);
font-size: 0.85rem;
cursor: pointer;
padding: 0.25rem 0;
transition: color 0.15s ease;
}
.modal-series-toggle:hover {
color: var(--text-dim);
}
.modal-series-toggle .toggle-arrow {
transition: transform 0.15s ease;
font-size: 0.9rem;
}
.modal-series-toggle.open .toggle-arrow {
transform: rotate(90deg);
}
.add-series-body {
margin-top: 0.5rem;
}
/* In-context feedback label */
.tag-feedback {
font-size: 0.75rem;
color: var(--flash-success);
opacity: 0;
transition: opacity 0.2s ease;
display: inline-block;
margin-left: 0.4rem;
}
.tag-feedback.visible {
opacity: 1;
}
.tag-feedback-inline {
display: block;
margin-left: 0;
margin-top: 0.3rem;
min-height: 1.1em;
}
/* Tag chip remove button in modal — hidden at rest, visible on hover */
.tag-editor .tag-chip .x {
opacity: 0;
transition: opacity 0.15s ease;
margin-left: 4px;
}
.tag-editor .tag-chip:hover .x {
opacity: 1;
}
/*------------------------------------------------------------------------------
Tag overlays & editor
------------------------------------------------------------------------------*/
/* Pills used inside overlays and editors */
.tags {
margin-top: .4rem;
display: flex;
flex-wrap: wrap;
gap: .25rem;
}
/* Tag chips (truncate long names nicely) */
.tag-chip {
display: inline-block; /* required for ellipsis */
max-width: clamp(8rem, 22vw, 14rem); /* responsive cap */
padding: 2px 8px;
border-radius: 999px;
background: rgba(255,255,255,0.92);
color: #334155;
text-decoration: none;
border: 1px solid rgba(199,210,254,0.9);
white-space: nowrap; /* single line */
overflow: hidden; /* hide overflow */
text-overflow: ellipsis; /* … */
vertical-align: middle;
font: 12px/1.6 system-ui, sans-serif;
}
.tag-chip:hover { background: rgba(255,255,255,1); }
/* Modal tag editor - side panel layout */
.tag-editor {
width: 280px;
min-width: 280px;
max-height: calc(100vh - 40px);
overflow-y: auto;
background: rgba(255, 255, 255, 0.05);
border-radius: 8px;
padding: 0.75rem;
border: 1px solid rgba(255, 255, 255, 0.1);
flex-shrink: 0;
}
/* On smaller screens (portrait), make tag editor full width and scrollable */
@media (max-width: 900px) and (orientation: portrait) {
.tag-editor {
width: 100%;
max-width: 500px;
min-width: unset;
max-height: 35vh;
overflow-y: auto;
flex-shrink: 0;
}
}
/* Series info in modal tag editor */
.modal-series-info {
margin-bottom: 0.75rem;
padding-bottom: 0.75rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.series-info-row {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.4rem;
}
.series-info-row:last-child {
margin-bottom: 0;
}
.series-info-label {
color: var(--text-muted);
font-size: 0.85rem;
}
.series-info-link {
color: var(--accent);
text-decoration: none;
font-size: 0.9rem;
}
.series-info-link:hover {
text-decoration: underline;
}
.series-page-input {
width: 60px;
padding: 0.25rem 0.4rem;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 4px;
color: var(--text);
font-size: 0.85rem;
}
.btn-small {
padding: 0.25rem 0.5rem;
background: var(--btn-secondary);
border: none;
border-radius: 4px;
color: var(--text);
font-size: 0.8rem;
cursor: pointer;
}
.btn-small:hover {
background: var(--btn-secondary-hover);
}
.btn-small:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.page-update-status {
font-size: 0.8rem;
color: var(--text-muted);
}
/* Add to series section in modal */
.modal-add-to-series {
margin-bottom: 0.75rem;
padding-bottom: 0.75rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.add-series-header {
margin-bottom: 0.5rem;
}
.add-series-controls {
display: flex;
align-items: center;
gap: 0.5rem;
flex-wrap: wrap;
}
.series-select {
flex: 1;
min-width: 120px;
padding: 0.3rem 0.5rem;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 4px;
color: var(--text);
font-size: 0.85rem;
cursor: pointer;
}
.series-select option {
background: var(--panel);
color: var(--text);
}
.tag-editor .tags {
margin: 0 0 0.5rem 0;
min-height: 28px;
}
/* Tag chips in modal - with remove button */
.tag-editor .tag-chip {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 4px 8px;
background: rgba(255, 255, 255, 0.12);
border: 1px solid rgba(255, 255, 255, 0.15);
color: var(--text);
}
.tag-editor .tag-chip:hover {
background: rgba(255, 255, 255, 0.18);
}
.tag-editor .tag-chip .x {
background: none;
border: none;
color: rgba(255, 255, 255, 0.5);
cursor: pointer;
padding: 0;
margin-left: 2px;
font-size: 1rem;
line-height: 1;
}
.tag-editor .tag-chip .x:hover {
color: #ef4444;
}
/* Tag form with autocomplete */
.tag-form {
display: flex;
gap: 0.5rem;
position: relative;
}
.tag-form-wrapper {
flex: 1;
position: relative;
}
.tag-form input {
width: 100%;
padding: 0.5rem 0.75rem;
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 6px;
background: rgba(255, 255, 255, 0.08);
color: var(--text);
font-size: 16px; /* Must be 16px+ to prevent iOS auto-zoom on focus */
}
.tag-form input::placeholder {
color: var(--text-muted);
}
.tag-form input:focus {
outline: none;
border-color: var(--btn-primary);
background: rgba(255, 255, 255, 0.12);
}
.tag-form button {
padding: 0.5rem 1rem;
border: none;
border-radius: 6px;
background: var(--btn-primary);
color: white;
cursor: pointer;
font-size: 0.9rem;
transition: background 0.2s ease;
white-space: nowrap;
}
.tag-form button:hover {
background: var(--btn-primary-hover);
}
/* Autocomplete dropdown - uses fixed positioning to escape overflow containers */
.tag-autocomplete {
position: fixed;
background: var(--panel);
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 6px;
max-height: 240px;
overflow-y: auto;
z-index: 10100;
display: none;
box-shadow: var(--shadow-3);
min-width: 200px;
max-width: calc(100vw - 16px);
box-sizing: border-box;
}
.tag-autocomplete.active {
display: block;
}
.tag-autocomplete-item {
padding: 0.5rem 0.75rem;
cursor: pointer;
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.9rem;
color: var(--text);
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
.tag-autocomplete-item:last-child {
border-bottom: none;
}
.tag-autocomplete-item:hover,
.tag-autocomplete-item.selected {
background: rgba(255, 255, 255, 0.1);
}
.tag-autocomplete-item .tag-kind {
font-size: 0.75rem;
color: var(--text-muted);
margin-left: auto;
}
.tag-autocomplete-empty {
padding: 0.5rem 0.75rem;
color: var(--text-muted);
font-size: 0.85rem;
font-style: italic;
}
/* Tag overlay bar at the top of the image */
.tag-overlay {
position: absolute;
top: 0; left: 0; right: 0;
display: flex;
flex-wrap: wrap;
gap: 4px;
padding: 6px;
z-index: 2;
background: linear-gradient(to bottom, rgba(0,0,0,0.45), rgba(0,0,0,0));
}
/*------------------------------------------------------------------------------
Tag cards (tags_list.html) - New design
------------------------------------------------------------------------------*/
.tag-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 1rem;
padding: 0.5rem;
}
.tag-card {
position: relative;
background: var(--panel);
border-radius: 8px;
overflow: hidden;
box-shadow: var(--shadow-2);
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.tag-card:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-3);
}
.tag-card-link {
text-decoration: none;
color: inherit;
display: block;
}
.tag-preview {
display: flex;
gap: 2px;
height: 140px;
overflow: hidden;
}
.tag-thumb {
flex: 1;
min-width: 0;
height: 100%;
object-fit: cover;
background-color: var(--bg-elevated);
transition: filter 0.2s ease;
}
.tag-card:hover .tag-thumb {
filter: brightness(1.08);
}
.tag-thumb-empty {
background: linear-gradient(135deg, var(--bg-elevated) 0%, var(--panel) 100%);
}
.tag-card-info {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.75rem 1rem;
background: linear-gradient(to top, rgba(0,0,0,0.4) 0%, rgba(0,0,0,0.2) 100%);
}
.tag-icon {
font-size: 1.1rem;
flex-shrink: 0;
}
.tag-name {
flex: 1;
font-weight: 500;
font-size: 0.95rem;
color: var(--text);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.tag-count {
font-size: 0.8rem;
color: var(--text-muted);
background: rgba(255,255,255,0.1);
padding: 0.2rem 0.5rem;
border-radius: 999px;
flex-shrink: 0;
}
.tag-edit-btn {
position: absolute;
top: 0.5rem;
right: 0.5rem;
width: 32px;
height: 32px;
border-radius: 50%;
background: rgba(0,0,0,0.6);
border: 1px solid rgba(255,255,255,0.1);
color: var(--text-dim);
cursor: pointer;
opacity: 0;
transition: all 0.2s ease;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.9rem;
}
.tag-card:hover .tag-edit-btn {
opacity: 1;
}
.tag-edit-btn:hover {
background: rgba(0,0,0,0.8);
color: var(--text);
border-color: rgba(255,255,255,0.2);
}
.empty-state {
text-align: center;
color: var(--text-muted);
padding: 3rem 1rem;
font-size: 1.1rem;
}
/* Tag edit modal specific */
.tag-edit-modal-content {
max-width: 400px;
padding: 2rem;
background: var(--panel);
border-radius: 12px;
border: 1px solid rgba(255,255,255,0.1);
}
.tag-edit-modal-content h2 {
margin: 0 0 1.5rem 0;
text-align: center;
color: var(--text);
}
.tag-edit-form .form-group {
margin-bottom: 1rem;
}
.tag-edit-form .form-actions {
display: flex;
gap: 0.75rem;
margin-top: 1.5rem;
}
.tag-edit-form .form-actions .btn {
flex: 1;
}
/* Legacy artist grid (kept for backward compatibility) */
.artist-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 6px;
padding: 0.5rem;
}
.artist-card-link {
text-decoration: none;
color: inherit;
display: block;
transition: transform 0.3s ease;
}
.artist-card-link:hover .artist-card {
transform: scale(1.02);
box-shadow: var(--shadow-3);
}
.artist-card {
background-color: var(--panel);
padding: 0;
border-radius: 4px;
overflow: hidden;
box-shadow: var(--shadow-2);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.artist-card h3 {
margin: 0;
padding: 0.75rem;
text-align: center;
color: var(--text);
font-weight: 500;
font-size: 0.95rem;
background: rgba(0,0,0,0.3);
}
.artist-preview {
display: flex;
gap: 2px;
justify-content: center;
overflow: hidden;
}
.artist-thumb {
flex: 1;
height: 280px;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
transition: transform 0.3s ease, filter 0.3s ease;
}
.artist-card-link:hover .artist-thumb {
filter: brightness(1.1);
}
/* Tag search bar */
.tag-search-bar {
display: flex;
align-items: center;
gap: 1rem;
max-width: 500px;
margin: 0 auto 1rem;
}
.tag-search-input {
flex: 1;
padding: 0.6rem 1rem;
border: 1px solid var(--border);
border-radius: 6px;
background: var(--surface);
color: var(--text);
font-size: 1rem;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.tag-search-input:focus {
outline: none;
border-color: var(--accent);
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.2);
}
.tag-search-input::placeholder {
color: var(--muted);
}
.tag-search-count {
color: var(--muted);
font-size: 0.9rem;
white-space: nowrap;
}
/* Filter pills (top of tags_list) */
.filter-bar {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin: 0.75rem 0 1.5rem;
justify-content: center;
}
.filter-btn {
display: inline-flex;
align-items: center;
gap: 0.4rem;
padding: 0.5rem 1rem;
border-radius: 999px;
border: 1px solid var(--filter-border);
background: var(--filter-bg);
color: var(--filter-fg);
text-decoration: none;
font: 14px/1.2 system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
transition: all 0.2s ease;
}
.filter-btn:hover {
background: rgba(255, 255, 255, 0.15);
border-color: rgba(255, 255, 255, 0.25);
}
.filter-btn.is-active {
background: var(--filter-active-bg);
color: var(--filter-active-fg);
border-color: var(--filter-active-border);
}
.filter-btn:focus-visible {
outline: 2px solid #6366f1;
outline-offset: 2px;
}
/*------------------------------------------------------------------------------
Settings
------------------------------------------------------------------------------*/
/* Settings page title */
.settings-title {
text-align: center;
margin-top: 1rem;
margin-bottom: 1rem;
}
/* Settings tab bar */
.settings-tab-bar {
display: flex;
gap: 0.5rem;
margin-bottom: 1.5rem;
flex-wrap: wrap;
max-width: 1600px;
margin-left: auto;
margin-right: auto;
padding: 0 1.5rem;
}
.settings-tab-btn {
padding: 0.4rem 1.1rem;
border-radius: 999px;
border: 1px solid var(--filter-border);
background: var(--filter-bg);
color: var(--filter-fg);
font-size: 0.9rem;
cursor: pointer;
transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.settings-tab-btn:hover {
background: rgba(255, 255, 255, 0.12);
}
.settings-tab-btn.is-active {
background: var(--filter-active-bg);
border-color: var(--filter-active-border);
color: var(--filter-active-fg);
}
/* Settings tab panels */
.settings-tab-panel {
display: none;
}
.settings-tab-panel.is-active {
display: block;
}
/* Maintenance tab — visually subdued */
.settings-tab-panel[data-tab="maintenance"] .settings-container {
opacity: 0.8;
}
.settings-tab-panel[data-tab="maintenance"] .settings-container:hover {
opacity: 1;
transition: opacity 0.2s ease;
}
/* Two-column layout for Import tab */
.settings-two-col {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
align-items: start;
}
@media (max-width: 900px) {
.settings-two-col {
grid-template-columns: 1fr;
}
}
.settings-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 1.5rem;
max-width: 1600px;
margin: 1.5rem auto;
padding: 0 1.5rem;
align-items: start;
}
.settings-grid-full {
max-width: 1600px;
margin: 1.5rem auto;
padding: 0 1.5rem;
}
.settings-column {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
@media (max-width: 768px) {
.settings-grid {
grid-template-columns: 1fr;
padding: 0 1rem;
}
.settings-grid-full {
padding: 0 1rem;
}
}
.settings-container {
padding: 1.5rem;
background: var(--panel);
border-radius: 8px;
box-shadow: var(--shadow-2);
border: 1px solid rgba(255, 255, 255, 0.08);
}
.settings-section h2,
.settings-info h2 {
margin-bottom: 1rem;
text-align: center;
color: var(--text);
font-weight: 500;
}
.settings-actions {
display: flex;
flex-direction: column;
gap: 1rem;
align-items: center;
}
.btn {
padding: 0.75rem 2rem;
font-size: 1rem;
border: none;
border-radius: 6px;
cursor: pointer;
transition: all 0.2s ease;
font-weight: 500;
min-width: 200px;
}
.btn:hover {
transform: translateY(-1px);
}
.primary-btn { background-color: var(--btn-primary); color: #fff; }
.primary-btn:hover { background-color: var(--btn-primary-hover); }
.warning-btn { background-color: var(--flash-warning); color: #fff; }
.warning-btn:hover { background-color: #d97706; }
.danger-btn { background-color: var(--btn-danger); color: #fff; }
.danger-btn:hover { background-color: var(--btn-danger-hover); }
.settings-info {
font-size: 0.9rem;
color: var(--text-muted);
text-align: center;
line-height: 1.5;
}
/*------------------------------------------------------------------------------
Showcase - Full-bleed Masonry Collage
------------------------------------------------------------------------------*/
.showcase-body {
margin: 0;
padding: 0;
background: #0a0a0a;
min-height: 100vh;
overflow-x: hidden;
}
/* Floating navbar overlay */
.showcase-nav {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
display: flex;
flex-direction: column;
align-items: center;
padding: 0.75rem 1rem;
background: linear-gradient(to bottom, rgba(0,0,0,0.85) 0%, rgba(0,0,0,0.6) 70%, rgba(0,0,0,0) 100%);
pointer-events: none;
padding-bottom: 1rem;
}
.showcase-nav-links {
display: flex;
justify-content: center;
gap: 0.25rem;
pointer-events: auto;
}
.showcase-nav .nav-link {
pointer-events: auto;
color: #fff;
text-decoration: none;
padding: 0.5rem 1rem;
font-size: 1rem;
font-weight: 500;
border-radius: 6px;
transition: background 0.2s ease;
text-shadow: 0 1px 3px rgba(0,0,0,0.5);
}
.showcase-nav .nav-link:hover {
background: rgba(255,255,255,0.15);
}
/* Dropdown within showcase nav */
.showcase-nav .nav-dropdown {
position: relative;
display: inline-block;
pointer-events: auto;
}
.showcase-nav .nav-dropdown-trigger {
cursor: pointer;
border: none;
background: transparent;
font-family: inherit;
}
.showcase-nav .nav-dropdown-menu {
left: 50%;
transform: translateX(-50%);
}
.showcase-nav .nav-hint {
opacity: 0;
transition: opacity 0.2s ease;
font-size: 0.8rem;
color: var(--text-muted);
margin-top: 0.25rem;
pointer-events: none;
}
.showcase-nav:hover .nav-hint {
opacity: 1;
}
/* Masonry container - JS-managed columns */
.masonry-container {
display: flex;
gap: 4px;
padding: 60px 4px 4px 4px; /* top padding for navbar */
min-height: 100vh;
align-items: flex-start;
max-width: 2200px; /* prevent columns from getting too wide on ultrawide */
margin: 0 auto;
}
.masonry-column {
flex: 1;
display: flex;
flex-direction: column;
gap: 4px;
}
/* Masonry item */
.masonry-item {
position: relative;
cursor: pointer;
overflow: hidden;
background: #1a1a1a;
}
.masonry-item img {
display: block;
width: 100%;
height: auto;
transition: transform 0.3s ease, filter 0.3s ease;
}
.masonry-item:hover img {
transform: scale(1.03);
filter: brightness(1.1);
}
/* Tag overlay on masonry items */
.masonry-item .tag-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
padding: 6px;
background: linear-gradient(to bottom, rgba(0,0,0,0.6) 0%, rgba(0,0,0,0) 100%);
display: flex;
flex-wrap: wrap;
gap: 4px;
z-index: 2;
}
/* Ghost tag count badge on showcase items */
.tag-count-badge {
position: absolute;
bottom: 8px;
right: 8px;
padding: 2px 7px;
border-radius: 999px;
background: rgba(0, 0, 0, 0.45);
border: 1px solid rgba(255, 255, 255, 0.15);
color: rgba(255, 255, 255, 0.55);
font-size: 0.7rem;
font-weight: 500;
z-index: 2;
pointer-events: none;
opacity: 0;
transition: opacity 0.2s ease;
}
.masonry-item:hover .tag-count-badge {
opacity: 1;
}
/* Touch devices: always show badge */
@media (hover: none) {
.tag-count-badge {
opacity: 1;
}
}
/* Loading state for shuffle */
.masonry-container.loading {
opacity: 0.5;
pointer-events: none;
}
.masonry-container {
transition: opacity 0.3s ease;
}
/* Stagger entry animation for showcase items */
@keyframes itemFadeIn {
from {
opacity: 0;
transform: translateY(12px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.masonry-item.animating-in {
animation: itemFadeIn 0.25s ease forwards;
animation-delay: calc(var(--stagger-index, 0) * 60ms);
opacity: 0; /* Start hidden until animation fires */
}
@media (prefers-reduced-motion: reduce) {
.masonry-item.animating-in {
animation: none;
opacity: 1;
}
}
/*------------------------------------------------------------------------------
Forms
------------------------------------------------------------------------------*/
.form-container {
max-width: 400px;
margin: 2rem auto;
padding: 2rem;
background-color: var(--panel);
border-radius: 8px;
box-shadow: var(--shadow-2);
border: 1px solid rgba(255, 255, 255, 0.08);
}
.form-card { display: flex; flex-direction: column; gap: 1rem; }
.form-group { display: flex; flex-direction: column; }
.form-label { margin-bottom: 0.5rem; font-weight: 500; color: var(--text); }
.form-input {
padding: 0.75rem;
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 6px;
background: rgba(255, 255, 255, 0.08);
color: var(--text);
font-size: 1rem;
}
.form-input:focus {
outline: none;
border-color: var(--btn-primary);
background: rgba(255, 255, 255, 0.12);
}
/* Select dropdowns - ensure dark theme for options */
select.form-input {
cursor: pointer;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23ffffff' d='M6 8L1 3h10z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 0.75rem center;
padding-right: 2.5rem;
}
select.form-input option,
select.form-input optgroup {
background: #1a1a1a;
color: #ffffff;
}
select.form-input optgroup {
font-weight: 600;
color: #a0a0a0;
}
.form-button {
padding: 0.75rem;
background-color: var(--btn-primary);
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: 500;
transition: background 0.2s ease;
}
.form-button:hover { background-color: var(--btn-primary-hover); }
.form-footer-text { margin-top: 1rem; text-align: center; color: var(--text-dim); }
/* Fixed-height, 2-line title area so all cards match height */
.card-title {
/* reserve exactly two lines of space for the title */
line-height: 1.3;
min-height: calc(1.3em * 2);
max-height: calc(1.3em * 2);
overflow: hidden;
margin-bottom: 0.5rem;
}
/* Multi-line clamp with ellipsis (where supported) */
.card-title-text {
margin: 0;
font-size: 1.1rem;
line-height: 1.3;
display: -webkit-box;
-webkit-line-clamp: 2; /* clamp to 2 lines */
-webkit-box-orient: vertical;
overflow: hidden;
/* good fallbacks for very long single words / no spaces */
overflow-wrap: anywhere; /* allows breaking long tokens */
word-break: break-word;
}
/*------------------------------------------------------------------------------
Gallery Infinite Scroll Layout
------------------------------------------------------------------------------*/
.gallery-infinite-container {
display: flex;
min-height: 100vh;
max-width: 1800px;
margin: 0 auto;
position: relative;
}
/* Timeline Sidebar - positioned relative to gallery container */
.timeline-sidebar {
position: sticky;
top: 60px;
height: calc(100vh - 60px);
width: 70px;
min-width: 70px;
background: rgba(0,0,0,0.3);
border-right: 1px solid rgba(255,255,255,0.08);
z-index: 50;
display: flex;
flex-direction: column;
padding: 0.5rem 0;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: rgba(255,255,255,0.2) transparent;
}
.timeline-sidebar::-webkit-scrollbar {
width: 4px;
}
.timeline-sidebar::-webkit-scrollbar-track {
background: transparent;
}
.timeline-sidebar::-webkit-scrollbar-thumb {
background: rgba(255,255,255,0.2);
border-radius: 2px;
}
.timeline-track {
display: flex;
flex-direction: column;
padding: 0 6px;
}
.timeline-loading {
font-size: 0.7rem;
color: var(--text-muted);
padding: 0.5rem;
text-align: center;
}
.timeline-year-group {
margin-bottom: 0.5rem;
}
.timeline-year {
font-size: 0.8rem;
font-weight: 600;
color: var(--text);
padding: 0.5rem 0.25rem 0.25rem;
text-align: center;
position: sticky;
top: 0;
background: linear-gradient(to bottom, rgba(10,10,10,0.95), rgba(10,10,10,0.8));
z-index: 1;
border-bottom: 1px solid rgba(255,255,255,0.1);
}
.timeline-months {
display: flex;
flex-direction: column;
gap: 2px;
padding-top: 4px;
}
.timeline-month {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.35rem 0.5rem;
font-size: 0.7rem;
color: var(--text-muted);
border-radius: 4px;
cursor: pointer;
transition: all 0.15s ease;
text-decoration: none;
}
.timeline-month:hover {
background: rgba(255,255,255,0.12);
color: var(--text);
}
.timeline-month.active {
background: var(--btn-primary);
color: white;
}
.timeline-month.loaded {
color: var(--text-dim);
}
.timeline-month-label {
font-weight: 500;
}
.timeline-month-count {
font-size: 0.6rem;
opacity: 0.7;
}
/* Main gallery area - flex child next to timeline */
.gallery-main {
flex: 1;
padding: 0 0.5rem;
min-width: 0;
}
.gallery-header {
display: flex;
flex-direction: column;
align-items: center;
padding: 0.5rem 0 1rem;
gap: 0.5rem;
}
.gallery-header h1 {
margin: 0;
font-size: 1.5rem;
}
.active-filter {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
background: rgba(255,255,255,0.08);
border-radius: 999px;
font-size: 0.9rem;
color: var(--text-dim);
}
.filter-tag {
color: var(--link);
font-weight: 500;
}
.clear-filter {
display: flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
border-radius: 50%;
background: rgba(255,255,255,0.1);
color: var(--text-muted);
text-decoration: none;
font-size: 0.9rem;
line-height: 1;
transition: all 0.15s ease;
}
.clear-filter:hover {
background: rgba(255,255,255,0.2);
color: var(--text);
}
/* Post Info Header (shown when filtering by post tag) */
.post-info-header {
background: var(--panel);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
margin: 0 0 1.5rem 0;
overflow: hidden;
}
.post-info-content {
padding: 1.25rem 1.5rem;
}
.post-title {
margin: 0 0 0.75rem 0;
font-size: 1.35rem;
font-weight: 600;
color: var(--text);
line-height: 1.3;
}
.post-meta {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5rem 1rem;
margin-bottom: 0.75rem;
font-size: 0.875rem;
color: var(--text-dim);
}
.post-artist {
font-weight: 500;
color: var(--link);
}
.post-platform {
display: inline-flex;
align-items: center;
padding: 0.2rem 0.6rem;
border-radius: 4px;
font-size: 0.75rem;
font-weight: 500;
text-transform: capitalize;
background: rgba(255, 255, 255, 0.1);
color: var(--text-dim);
}
.post-platform-patreon {
background: rgba(255, 66, 77, 0.2);
color: #ff6b6b;
}
.post-platform-subscribestar {
background: rgba(255, 153, 0, 0.2);
color: #ffaa33;
}
.post-platform-hentaifoundry {
background: rgba(139, 92, 246, 0.2);
color: #a78bfa;
}
.post-date {
color: var(--text-muted);
}
.post-count {
color: var(--text-muted);
}
.post-count::before {
content: "•";
margin-right: 0.5rem;
}
.post-description {
margin: 0.75rem 0;
padding: 1rem;
background: rgba(0, 0, 0, 0.2);
border-radius: 8px;
font-size: 0.9rem;
line-height: 1.6;
color: var(--text-dim);
max-height: 200px;
overflow-y: auto;
}
.post-description:empty {
display: none;
}
/* Style any HTML content in descriptions */
.post-description a {
color: var(--link);
}
.post-description a:hover {
color: var(--link-hover);
}
.post-description p {
margin: 0 0 0.75rem 0;
}
.post-description p:last-child {
margin-bottom: 0;
}
.post-description img {
max-width: 100%;
height: auto;
border-radius: 4px;
}
.post-source-link {
display: inline-flex;
align-items: center;
gap: 0.4rem;
margin-top: 0.5rem;
padding: 0.5rem 1rem;
background: rgba(99, 102, 241, 0.15);
border: 1px solid rgba(99, 102, 241, 0.3);
border-radius: 6px;
color: var(--link);
text-decoration: none;
font-size: 0.85rem;
font-weight: 500;
transition: all 0.2s ease;
}
.post-source-link:hover {
background: rgba(99, 102, 241, 0.25);
border-color: rgba(99, 102, 241, 0.5);
color: var(--link-hover);
}
.post-source-link::after {
content: "↗";
font-size: 0.9em;
}
/* Series Header (shown when filtering by series tag) */
.series-header {
background: var(--panel);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
margin: 0 0 1.5rem 0;
overflow: hidden;
}
.series-header-content {
padding: 1rem 1.25rem;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 1rem;
}
.series-header-left {
display: flex;
align-items: center;
gap: 0.75rem;
}
.series-title {
margin: 0;
font-size: 1.25rem;
font-weight: 600;
color: var(--text);
}
.series-page-count {
color: var(--text-muted);
font-size: 0.9rem;
background: rgba(255, 255, 255, 0.08);
padding: 0.25rem 0.6rem;
border-radius: 4px;
}
.series-header-actions {
display: flex;
gap: 0.5rem;
}
/* Series Editor Panel */
.series-editor-panel {
padding: 1rem 1.25rem;
border-top: 1px solid rgba(255, 255, 255, 0.1);
background: rgba(0, 0, 0, 0.2);
}
.series-editor-info {
margin-bottom: 0.75rem;
}
.series-editor-info p {
margin: 0;
color: var(--text-dim);
font-size: 0.85rem;
}
.series-editor-controls {
display: flex;
align-items: flex-end;
gap: 1rem;
flex-wrap: wrap;
}
.series-editor-input {
display: flex;
flex-direction: column;
gap: 0.35rem;
}
.series-editor-input label {
font-size: 0.8rem;
color: var(--text-muted);
}
.series-editor-input input {
width: 100px;
padding: 0.4rem 0.6rem;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 6px;
color: var(--text);
font-size: 0.9rem;
}
.series-editor-feedback {
margin-top: 0.75rem;
}
.series-editor-feedback .feedback-success {
padding: 0.5rem 0.75rem;
background: rgba(34, 197, 94, 0.15);
border: 1px solid rgba(34, 197, 94, 0.3);
border-radius: 6px;
color: #22c55e;
font-size: 0.85rem;
}
.series-editor-feedback .feedback-error {
padding: 0.5rem 0.75rem;
background: rgba(239, 68, 68, 0.15);
border: 1px solid rgba(239, 68, 68, 0.3);
border-radius: 6px;
color: #ef4444;
font-size: 0.85rem;
}
#seriesEditModeBtn.active {
background: var(--btn-primary);
color: white;
}
@media (max-width: 768px) {
.series-header-content {
flex-direction: column;
align-items: flex-start;
}
.series-editor-controls {
flex-direction: column;
align-items: stretch;
}
.series-editor-input {
flex-direction: row;
align-items: center;
gap: 0.5rem;
}
}
@media (max-width: 768px) {
.post-info-content {
padding: 1rem;
}
.post-title {
font-size: 1.15rem;
}
.post-meta {
font-size: 0.8rem;
}
.post-description {
max-height: 150px;
font-size: 0.85rem;
}
}
/* Gallery content area */
.gallery-infinite {
min-height: 50vh;
}
.gallery-empty {
text-align: center;
padding: 4rem 1rem;
color: var(--text-muted);
}
/* Date sections */
.date-section {
margin-bottom: 1.5rem;
}
.date-divider {
display: flex;
align-items: center;
gap: 1rem;
padding: 0.75rem 0.5rem;
margin: 0 0 0.5rem 0;
border-bottom: 1px solid rgba(255,255,255,0.1);
position: sticky;
top: 50px;
background: linear-gradient(to bottom, var(--bg) 0%, var(--bg) 85%, transparent 100%);
z-index: 10;
}
.date-divider-text {
font-size: 1.1rem;
font-weight: 500;
color: var(--text);
}
.date-divider-count {
font-size: 0.8rem;
color: var(--text-muted);
background: rgba(255,255,255,0.08);
padding: 0.2rem 0.6rem;
border-radius: 999px;
}
/* Loading indicator */
.gallery-loading {
display: flex;
align-items: center;
justify-content: center;
gap: 0.75rem;
padding: 2rem;
color: var(--text-muted);
}
.loading-spinner {
display: inline-block;
width: 20px;
height: 20px;
border: 2px solid rgba(255,255,255,0.2);
border-top-color: var(--btn-primary);
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.gallery-end {
text-align: center;
padding: 2rem;
color: var(--text-muted);
font-style: italic;
border-top: 1px solid rgba(255,255,255,0.05);
}
/* Infinite scroll loading indicator */
.loading-indicator {
display: flex;
align-items: center;
justify-content: center;
gap: 0.75rem;
padding: 2rem;
color: var(--text-muted);
}
.loading-indicator .spinner {
display: inline-block;
width: 20px;
height: 20px;
border: 2px solid rgba(255,255,255,0.2);
border-top-color: var(--btn-primary);
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
/* Scroll sentinel for IntersectionObserver */
.scroll-sentinel {
height: 1px;
width: 100%;
}
/* Mobile: hide timeline sidebar */
@media (max-width: 768px) {
.timeline-sidebar {
display: none;
}
.date-divider {
top: 45px;
}
}
/*------------------------------------------------------------------------------
Floating Select Button (Gallery)
------------------------------------------------------------------------------*/
.floating-select-btn {
position: fixed;
top: 0.75rem;
right: 1rem;
z-index: 1001;
}
.floating-select-btn .btn {
box-shadow: var(--shadow-2);
}
@media (max-width: 640px) {
.floating-select-btn {
top: 0.5rem;
right: 0.5rem;
}
}
/*------------------------------------------------------------------------------
Bulk Select Mode
------------------------------------------------------------------------------*/
/* Select mode body state */
body.select-mode .img-clickable {
cursor: pointer;
position: relative;
}
/* Selection checkbox overlay */
body.select-mode .img-clickable::before {
content: '';
position: absolute;
top: 8px;
left: 8px;
width: 24px;
height: 24px;
border: 2px solid rgba(255, 255, 255, 0.8);
border-radius: 4px;
background: rgba(0, 0, 0, 0.4);
z-index: 10;
transition: all 0.15s ease;
}
body.select-mode .img-clickable.selected::before {
background: var(--btn-primary);
border-color: var(--btn-primary);
}
body.select-mode .img-clickable.selected::after {
content: '\2713';
position: absolute;
top: 8px;
left: 8px;
width: 24px;
height: 24px;
color: white;
font-size: 14px;
font-weight: bold;
display: flex;
align-items: center;
justify-content: center;
z-index: 11;
}
/* Selected image highlight */
body.select-mode .img-clickable.selected {
outline: 3px solid var(--btn-primary);
outline-offset: -3px;
}
/* Prevent hover effects in select mode */
body.select-mode .gallery-item:hover {
transform: none;
}
/* Select button active state */
#selectModeBtn.active {
background: var(--btn-primary);
color: white;
}
/* Push gallery content left when bulk editor panel is visible */
body.select-mode .gallery-infinite-container {
margin-right: 320px;
transition: margin-right 0.3s ease;
}
@media (max-width: 768px) {
/* On mobile, don't shift content - panel will overlay */
body.select-mode .gallery-infinite-container {
margin-right: 0;
}
}
/*------------------------------------------------------------------------------
Bulk Editor Panel (slide-in sidebar)
------------------------------------------------------------------------------*/
.bulk-editor-panel {
position: fixed;
top: 0;
right: 0;
width: 320px;
height: 100vh;
background: var(--panel);
border-left: 1px solid rgba(255, 255, 255, 0.1);
z-index: 1100;
transform: translateX(100%);
transition: transform 0.3s ease;
display: flex;
flex-direction: column;
box-shadow: var(--shadow-3);
overflow-y: auto;
overflow-x: hidden;
}
.bulk-editor-panel.active {
transform: translateX(0);
}
.bulk-editor-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.bulk-editor-header h3 {
margin: 0;
font-size: 1.1rem;
}
.bulk-editor-stats {
padding: 0.75rem 1rem;
background: rgba(255, 255, 255, 0.05);
font-size: 0.9rem;
color: var(--text-dim);
}
.bulk-editor-section {
padding: 1rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
.bulk-editor-section h4 {
margin: 0 0 0.75rem 0;
font-size: 0.9rem;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.bulk-editor-section .form-help {
margin: 0 0 0.5rem 0;
font-size: 0.8rem;
color: var(--text-muted);
}
.bulk-editor-section .tags {
min-height: 32px;
}
.bulk-editor-actions {
padding: 1rem;
margin-top: auto;
}
/* Removable tag chips in bulk editor */
.bulk-editor-section .tag-chip.removable {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 4px 8px;
background: rgba(255, 255, 255, 0.12);
border: 1px solid rgba(255, 255, 255, 0.15);
color: var(--text);
cursor: default;
}
.bulk-editor-section .tag-chip.removable:hover {
background: rgba(255, 255, 255, 0.18);
}
.bulk-editor-section .tag-chip .x {
background: none;
border: none;
color: rgba(255, 255, 255, 0.5);
cursor: pointer;
padding: 0;
margin-left: 2px;
font-size: 1rem;
line-height: 1;
}
.bulk-editor-section .tag-chip .x:hover {
color: #ef4444;
}
/* Series section in bulk editor */
.bulk-editor-series {
background: rgba(100, 100, 255, 0.05);
border-top: 1px solid rgba(100, 100, 255, 0.15);
}
.bulk-editor-series h4 {
color: var(--text);
}
.bulk-editor-series .series-editor-controls {
margin-top: 0.5rem;
}
.bulk-editor-series .series-editor-feedback {
margin-top: 0.5rem;
}
/* Text muted helper */
.text-muted {
color: var(--text-muted);
font-size: 0.85rem;
}
/* Selection order badge on gallery items */
.selection-order-badge {
position: absolute;
top: 4px;
left: 4px;
width: 24px;
height: 24px;
background: var(--accent);
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.75rem;
font-weight: bold;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
z-index: 10;
pointer-events: none;
}
/* Mobile: full-width panel from bottom */
@media (max-width: 640px) {
.bulk-editor-panel {
width: 100%;
height: auto;
max-height: 60vh;
top: auto;
bottom: 0;
transform: translateY(100%);
border-left: none;
border-top: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 16px 16px 0 0;
}
.bulk-editor-panel.active {
transform: translateY(0);
}
}
/*------------------------------------------------------------------------------
Series Reader View
------------------------------------------------------------------------------*/
.reader-container {
display: flex;
flex-direction: column;
height: calc(100vh - 60px);
background: var(--bg);
margin: -1rem;
}
.reader-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.75rem 1rem;
background: var(--panel);
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
flex-shrink: 0;
transition: opacity 0.3s ease, transform 0.3s ease;
}
.reader-header.hidden {
opacity: 0;
pointer-events: none;
transform: translateY(-4px);
}
.reader-header-left {
display: flex;
align-items: center;
gap: 0.75rem;
}
.reader-back-btn {
display: flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
background: rgba(255, 255, 255, 0.1);
border-radius: 8px;
color: var(--text);
text-decoration: none;
font-size: 1.2rem;
transition: all 0.2s ease;
}
.reader-back-btn:hover {
background: rgba(255, 255, 255, 0.2);
}
.reader-title {
margin: 0;
font-size: 1.1rem;
font-weight: 600;
color: var(--text);
}
.reader-page-count {
color: var(--text-muted);
font-size: 0.85rem;
}
.reader-header-right {
display: flex;
align-items: center;
gap: 0.75rem;
}
.reader-jump {
display: flex;
align-items: center;
gap: 0.5rem;
}
.reader-jump label {
color: var(--text-muted);
font-size: 0.85rem;
}
.reader-jump-select {
padding: 0.4rem 0.75rem;
background: var(--surface);
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 6px;
color: var(--text);
font-size: 0.85rem;
cursor: pointer;
}
.reader-jump-select option {
background: var(--surface);
color: var(--text);
}
.reader-toggle-nav {
display: flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
background: rgba(255, 255, 255, 0.1);
border: none;
border-radius: 8px;
color: var(--text);
font-size: 1.1rem;
cursor: pointer;
transition: all 0.2s ease;
}
.reader-toggle-nav:hover {
background: rgba(255, 255, 255, 0.2);
}
.reader-body {
display: flex;
flex: 1;
overflow: hidden;
position: relative;
}
/* Navigation Sidebar */
.reader-nav {
position: fixed;
top: 0;
left: 0;
width: 200px;
height: 100vh;
background: var(--panel);
border-right: 1px solid rgba(255, 255, 255, 0.1);
transform: translateX(-100%);
transition: transform 0.3s ease;
z-index: 1000;
display: flex;
flex-direction: column;
}
.reader-nav.open {
transform: translateX(0);
}
.reader-nav-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.reader-nav-header h3 {
margin: 0;
font-size: 1rem;
font-weight: 500;
}
.reader-nav-close {
width: 28px;
height: 28px;
background: rgba(255, 255, 255, 0.1);
border: none;
border-radius: 6px;
color: var(--text);
font-size: 1.2rem;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.reader-nav-close:hover {
background: rgba(255, 255, 255, 0.2);
}
.reader-nav-thumbs {
flex: 1;
overflow-y: auto;
padding: 0.5rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.reader-nav-thumb {
position: relative;
border-radius: 6px;
overflow: hidden;
transition: opacity 0.2s ease;
cursor: pointer;
border: 2px solid transparent;
transition: all 0.2s ease;
}
.reader-nav-thumb img {
width: 100%;
height: auto;
display: block;
}
.thumb-page-num {
position: absolute;
bottom: 4px;
right: 4px;
background: rgba(0, 0, 0, 0.75);
color: white;
padding: 2px 6px;
border-radius: 4px;
font-size: 0.7rem;
font-weight: 600;
}
/* Main Reader Content */
.reader-main {
flex: 1;
display: flex;
flex-direction: column;
position: relative;
}
.reader-content {
flex: 1;
overflow-y: auto;
scroll-behavior: smooth;
padding: 0.25rem;
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
background: var(--bg);
}
.reader-page {
position: relative;
width: 100%;
display: flex;
justify-content: center;
}
.reader-page img {
max-width: min(100%, 1200px);
height: auto;
display: block;
}
.page-indicator {
position: absolute;
bottom: 0.5rem;
right: 0.5rem;
background: rgba(0, 0, 0, 0.75);
color: white;
padding: 0.25rem 0.6rem;
border-radius: 4px;
font-size: 0.75rem;
opacity: 0;
transition: opacity 0.2s ease;
}
.reader-page:hover .page-indicator {
opacity: 1;
}
.reader-empty {
text-align: center;
padding: 4rem 1rem;
color: var(--text-muted);
}
/* Reading progress bar */
.reader-progress-bar {
position: fixed;
top: 0;
left: 0;
height: 2px;
width: 0%;
background: var(--btn-primary);
z-index: 1100;
transition: width 0.1s linear;
pointer-events: none;
}
/* Active nav thumbnail */
.reader-nav-thumb.active {
box-shadow: inset 3px 0 0 var(--btn-primary);
opacity: 1;
}
.reader-nav-thumb:not(.active) {
opacity: 0.5;
}
.reader-nav-thumb:hover {
opacity: 1;
}
/* Floating page indicator */
.floating-page-indicator {
position: fixed;
bottom: 1.5rem;
right: 1.5rem;
padding: 4px 10px;
background: rgba(0, 0, 0, 0.6);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 999px;
font-size: 0.8rem;
color: var(--text-muted);
z-index: 100;
pointer-events: none;
backdrop-filter: blur(4px);
-webkit-backdrop-filter: blur(4px);
}
/* Quick nav buttons */
.reader-quick-nav {
position: fixed;
right: 1.5rem;
bottom: 1.5rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
z-index: 100;
}
.reader-quick-btn {
padding: 0.5rem 0.75rem;
background: var(--panel);
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 6px;
color: var(--text-dim);
font-size: 0.8rem;
cursor: pointer;
transition: all 0.2s ease;
}
.reader-quick-btn:hover {
background: rgba(255, 255, 255, 0.1);
color: var(--text);
}
/* Mobile adjustments */
@media (max-width: 768px) {
.reader-header {
flex-wrap: wrap;
gap: 0.5rem;
}
.reader-header-left {
flex: 1;
}
.reader-title {
font-size: 0.95rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 150px;
}
.reader-jump label {
display: none;
}
.reader-nav {
width: 100%;
max-width: 280px;
}
.reader-content {
padding: 0.5rem;
}
.floating-page-indicator {
bottom: 1rem;
font-size: 0.8rem;
}
.reader-quick-nav {
right: 1rem;
bottom: 4rem;
}
}