/* Tailwind CSS directives - required for framework */ @tailwind base; @tailwind components; @tailwind utilities; @layer base { /* * Default :root mirrors the Tokyo Night theme so the palette is correct * on first paint, before the theme class is applied to (kills the * theme-flash on load). */ :root { --bg-primary: #1a1b26; --bg-secondary: #16161e; --text-primary: #c0caf5; --text-secondary: #9aa5ce; --accent: #7aa2f7; --border: #2a2e42; /* status */ --status-success: #9ece6a; --status-warning: #e0af68; --status-danger: #f7768e; --status-info: #7aa2f7; } .theme-tokyo-night { --bg-primary: #1a1b26; --bg-secondary: #16161e; --text-primary: #c0caf5; --text-secondary: #9aa5ce; --accent: #7aa2f7; --border: #2a2e42; } .theme-dracula { --bg-primary: #282a36; --bg-secondary: #21222c; --text-primary: #f8f8f2; --text-secondary: #bfbfbf; --accent: #bd93f9; --border: #3a3d4d; } .theme-nord { --bg-primary: #2e3440; --bg-secondary: #3b4252; --text-primary: #eceff4; --text-secondary: #b7c0d2; --accent: #88c0d0; --border: #46577a; } .theme-solarized-dark { --bg-primary: #002b36; --bg-secondary: #073642; --text-primary: #c5d3d3; --text-secondary: #93a1a1; --accent: #2aa198; --border: #1d4b58; } .theme-monokai { --bg-primary: #272822; --bg-secondary: #1e1f1c; --text-primary: #f8f8f2; --text-secondary: #c8c8b8; --accent: #a6e22e; --border: #3e3d32; } .theme-one-dark-pro { --bg-primary: #282c34; --bg-secondary: #21252b; --text-primary: #d7dbe0; --text-secondary: #9da5b4; --accent: #61dafb; --border: #3a3f4b; } .theme-material-dark { --bg-primary: #263238; --bg-secondary: #1e282d; --text-primary: #eeffff; --text-secondary: #b0bec5; --accent: #80cbc4; --border: #37474f; } .theme-catppuccin-mocha { --bg-primary: #1e1e2e; --bg-secondary: #181825; --text-primary: #cdd6f4; --text-secondary: #a6adc8; --accent: #f38ba8; --border: #313244; } .theme-catppuccin-macchiato { --bg-primary: #24273a; --bg-secondary: #1e2030; --text-primary: #cad3f5; --text-secondary: #a5adcb; --accent: #c6a0f6; --border: #363a4f; } .theme-catppuccin-frappe { --bg-primary: #303446; --bg-secondary: #292c3c; --text-primary: #c6d0f5; --text-secondary: #a5adce; --accent: #ca9ee6; --border: #414559; } .theme-catppuccin-latte { --bg-primary: #eff1f5; --bg-secondary: #ffffff; --text-primary: #4c4f69; --text-secondary: #6c6f85; --accent: #d20f39; --border: #dcdfe6; } /* * Derived tokens — computed once on from the active theme vars so * they adapt automatically to every theme without 11x repetition. */ body { background-color: var(--bg-primary); color: var(--text-primary); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; /* hover/overlay surfaces */ --surface-hover: color-mix(in srgb, var(--text-primary) 9%, transparent); --surface-overlay: rgba(0, 0, 0, 0.55); /* stronger hairline */ --border-strong: color-mix(in srgb, var(--text-primary) 24%, var(--border)); /* soft accent tint (chips, active nav, subtle highlights) */ --accent-muted: color-mix(in srgb, var(--accent) 14%, var(--bg-secondary)); /* elevation */ --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.10), 0 1px 3px rgba(0, 0, 0, 0.08); --shadow-card-hover: 0 6px 16px rgba(0, 0, 0, 0.22), 0 2px 6px rgba(0, 0, 0, 0.12); --shadow-pop: 0 12px 40px rgba(0, 0, 0, 0.34), 0 4px 12px rgba(0, 0, 0, 0.20); --shadow-bar: 0 1px 0 rgba(0, 0, 0, 0.18), 0 4px 16px rgba(0, 0, 0, 0.16); } /* Slightly softer shadows on the lone light theme */ .theme-catppuccin-latte { --shadow-card: 0 1px 2px rgba(76, 79, 105, 0.08), 0 1px 3px rgba(76, 79, 105, 0.06); --shadow-card-hover: 0 6px 16px rgba(76, 79, 105, 0.16), 0 2px 6px rgba(76, 79, 105, 0.08); --shadow-pop: 0 12px 40px rgba(76, 79, 105, 0.22), 0 4px 12px rgba(76, 79, 105, 0.12); --shadow-bar: 0 1px 0 rgba(76, 79, 105, 0.08), 0 4px 16px rgba(76, 79, 105, 0.10); } /* App-wide keyboard focus affordance (accessibility) */ :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; } /* Hide Alpine-controlled elements until Alpine initialises */ [x-cloak] { display: none !important; } /* Header is sticky on every authenticated page, plus docs. */ .header-nav { position: sticky; top: 0; z-index: 50; } /* Smooth, theme-aware scrollbars */ * { scrollbar-color: color-mix(in srgb, var(--text-primary) 22%, transparent) transparent; } } @layer components { /* ---------- Surfaces ---------- */ .card { background-color: var(--bg-secondary); border: 1px solid color-mix(in srgb, var(--text-primary) 7%, transparent); border-radius: 0.75rem; box-shadow: var(--shadow-card); } /* ---------- Buttons ---------- */ .btn { display: inline-flex; align-items: center; justify-content: center; gap: 0.5rem; padding: 0.5rem 1rem; border-radius: 0.625rem; font-size: 0.875rem; font-weight: 500; line-height: 1.25rem; transition: background-color 0.15s ease, color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease, border-color 0.15s ease; cursor: pointer; white-space: nowrap; } .btn:active { transform: translateY(1px); } .btn:disabled { opacity: 0.5; cursor: not-allowed; } .btn-primary { background-color: var(--accent); color: var(--bg-primary); box-shadow: var(--shadow-card); } .btn-primary:hover { background-color: color-mix(in srgb, var(--accent) 88%, white); box-shadow: var(--shadow-card-hover); } .btn-secondary { background-color: transparent; color: var(--text-primary); border: 1px solid var(--border-strong); } .btn-secondary:hover { background-color: var(--surface-hover); border-color: var(--accent); color: var(--text-primary); } .btn-ghost { background-color: transparent; color: var(--text-secondary); } .btn-ghost:hover { background-color: var(--surface-hover); color: var(--text-primary); } .btn-danger { background-color: var(--status-danger); color: #fff; } .btn-danger:hover { background-color: color-mix(in srgb, var(--status-danger) 88%, white); } /* ---------- Icon button (square, subtle) ---------- */ .icon-btn { display: inline-flex; align-items: center; justify-content: center; width: 2.25rem; height: 2.25rem; border-radius: 0.625rem; color: var(--text-secondary); transition: background-color 0.15s ease, color 0.15s ease; cursor: pointer; } .icon-btn:hover { background-color: var(--surface-hover); color: var(--text-primary); } /* ---------- Inputs ---------- */ .input { width: 100%; background-color: var(--bg-primary); color: var(--text-primary); border: 1px solid var(--border); border-radius: 0.625rem; padding: 0.5rem 0.75rem; font-size: 0.875rem; transition: border-color 0.15s ease, box-shadow 0.15s ease; } .input::placeholder { color: color-mix(in srgb, var(--text-secondary) 75%, transparent); } .input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 25%, transparent); outline: none; } /* ---------- Chips & badges ---------- */ .chip { display: inline-flex; align-items: center; gap: 0.375rem; padding: 0.25rem 0.75rem; border-radius: 9999px; font-size: 0.75rem; font-weight: 500; background-color: var(--accent-muted); color: var(--text-primary); } .badge { display: inline-flex; align-items: center; padding: 0.125rem 0.5rem; border-radius: 0.375rem; font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.01em; } /* ---------- Book card (the atomic unit) ---------- */ .book-card { background-color: var(--bg-secondary); border: 1px solid color-mix(in srgb, var(--text-primary) 6%, transparent); border-radius: 0.625rem; overflow: hidden; box-shadow: var(--shadow-card); transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease; } .book-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-card-hover); border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); } .book-card-meta { background-color: var(--bg-secondary); padding: 0.5rem 0.625rem; } /* ---------- TriState Button (has_cover filter) ---------- */ .tristate-btn { background-color: var(--bg-primary); border: 1px solid var(--border); } .tristate-btn.state-null { color: var(--text-secondary); } .tristate-btn.state-true { background-color: var(--accent-muted); border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); color: var(--accent); } .tristate-btn.state-false { background-color: var(--surface-hover); color: var(--text-secondary); } .loading-spinner { display: inline-block; width: 40px; height: 40px; border: 3px solid var(--border); border-top-color: var(--accent); border-radius: 50%; animation: spin 0.8s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } } /* ---------- Status / priority semantics ---------- */ .status-pending { background-color: color-mix(in srgb, var(--status-warning) 20%, transparent); color: var(--status-warning); } .status-processing { background-color: color-mix(in srgb, var(--status-info) 20%, transparent); color: var(--status-info); } .status-completed { background-color: color-mix(in srgb, var(--status-success) 20%, transparent); color: var(--status-success); } .status-failed { background-color: color-mix(in srgb, var(--status-danger) 20%, transparent); color: var(--status-danger); } .priority-1 { background-color: color-mix(in srgb, var(--status-danger) 22%, transparent); color: var(--status-danger); } .priority-2 { background-color: color-mix(in srgb, #f97316 22%, transparent); color: #fb923c; } .priority-3 { background-color: color-mix(in srgb, var(--status-warning) 22%, transparent); color: var(--status-warning); } .priority-5 { background-color: color-mix(in srgb, var(--status-success) 22%, transparent); color: var(--status-success); } .priority-7 { background-color: var(--surface-hover); color: var(--text-secondary); } .priority-10 { background-color: var(--surface-hover); color: var(--text-secondary); } /* ---------- Docs / highlight.js overrides ---------- */ .prose pre code.hljs { background-color: transparent !important; } .prose code.hljs { background-color: var(--bg-primary) !important; padding: 0.2em 0.4em; } .prose pre { background-color: var(--bg-secondary) !important; } .prose .hljs { background-color: transparent !important; color: var(--text-primary) !important; } /* ---------- Theme / wood picker active states ---------- */ .bg-theme-active, .bg-wood-active { background-color: var(--bg-primary) !important; box-shadow: inset 0 0 0 2px var(--accent); } .bg-theme-inactive, .bg-wood-inactive { background-color: var(--bg-secondary) !important; } /* ---------- Wood paneling (optional bookshelf background) ---------- */ .bg-wood-dark, .bg-wood-light, .bg-wood-mahogany { background-attachment: fixed; background-position: center; background-size: cover; } body.bg-wood-dark, body.bg-wood-light, body.bg-wood-mahogany, #collections-container.bg-wood-dark, #collections-container.bg-wood-light, #collections-container.bg-wood-mahogany { background-attachment: fixed; background-position: center; background-size: cover; } /* Smart text colors for wood paneling backgrounds */ #collections-container[data-wood="wood-light"] { --wood-text-primary: #1a1a1a; --wood-text-secondary: #4a4a4a; --wood-border: #8b5a2b; } #collections-container[data-wood="wood-dark"] { --wood-text-primary: #f5f5f5; --wood-text-secondary: #c0c0c0; --wood-border: #7a5228; } #collections-container[data-wood="wood-mahogany"] { --wood-text-primary: #f5f5f5; --wood-text-secondary: #c0c0c0; --wood-border: #8b3a3a; } #collections-container[data-wood] h1, #collections-container[data-wood] h2, #collections-container[data-wood] h3, #collections-container[data-wood] p, #collections-container[data-wood] span, #collections-container[data-wood] a, #collections-container[data-wood] button { color: var(--wood-text-primary) !important; } #collections-container[data-wood] .text-secondary { color: var(--wood-text-secondary) !important; } #collections-container[data-wood="wood-light"] a { color: #0066cc !important; } #collections-container[data-wood="wood-dark"] a, #collections-container[data-wood="wood-mahogany"] a { color: #66ccff !important; } #collections-container[data-wood="wood-light"] .carousel-nav-left { background: linear-gradient(to right, rgba(0, 0, 0, 0.6), transparent); } #collections-container[data-wood="wood-light"] .carousel-nav-right { background: linear-gradient(to left, rgba(0, 0, 0, 0.6), transparent); } #collections-container[data-wood="wood-dark"] .carousel-nav-left, #collections-container[data-wood="wood-mahogany"] .carousel-nav-left { background: linear-gradient(to right, rgba(0, 0, 0, 0.3), transparent); } #collections-container[data-wood="wood-dark"] .carousel-nav-right, #collections-container[data-wood="wood-mahogany"] .carousel-nav-right { background: linear-gradient(to left, rgba(0, 0, 0, 0.3), transparent); } #collections-container[data-wood] .book-card { border: 1px solid var(--wood-border); } /* ---------- Reader ---------- */ .reader-icon { display: block; fill: none; stroke: currentColor; stroke-width: 2px; stroke-linecap: round; stroke-linejoin: round; } .dockable-panel { background-color: var(--bg-secondary); border-bottom: 1px solid var(--border); transition: max-height 0.2s ease-out; overflow: hidden; } .dockable-panel.panel-collapsed .panel-content { display: none; } .panel-header { user-select: none; } .panel-header:hover { background-color: var(--surface-hover); } .panel-container { background-color: var(--bg-secondary); border-right: 1px solid var(--border); width: 320px; max-height: calc(100vh - 8rem); overflow-y: auto; } .panel-container[data-side="right"] { border-right: none; border-left: 1px solid var(--border); } /* ---------- Series stacked covers ---------- */ .stacked-covers { position: relative; width: 100%; aspect-ratio: 2 / 3; overflow: hidden; } .stacked-covers .cover-img { position: absolute; width: 70%; height: 90%; object-fit: cover; border-radius: 4px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4); } .stacked-covers.cover-count-1 .cover-0 { top: 5%; left: 15%; } .stacked-covers.cover-count-2 .cover-0 { top: 8%; left: 5%; transform: rotate(-6deg); z-index: 1; } .stacked-covers.cover-count-2 .cover-1 { top: 2%; left: 25%; transform: rotate(3deg); z-index: 2; } .stacked-covers.cover-count-3 .cover-0 { top: 12%; left: 2%; transform: rotate(-8deg); z-index: 1; } .stacked-covers.cover-count-3 .cover-1 { top: 6%; left: 15%; transform: rotate(-2deg); z-index: 2; } .stacked-covers.cover-count-3 .cover-2 { top: 0; left: 30%; transform: rotate(4deg); z-index: 3; } .stacked-covers.cover-count-4 .cover-0 { top: 14%; left: 0; width: 65%; height: 85%; transform: rotate(-10deg); z-index: 1; } .stacked-covers.cover-count-4 .cover-1 { top: 10%; left: 12%; width: 65%; height: 85%; transform: rotate(-4deg); z-index: 2; } .stacked-covers.cover-count-4 .cover-2 { top: 5%; left: 22%; width: 65%; height: 85%; transform: rotate(2deg); z-index: 3; } .stacked-covers.cover-count-4 .cover-3 { top: 0; left: 32%; width: 65%; height: 85%; transform: rotate(6deg); z-index: 4; } .stacked-covers.cover-count-5 .cover-0, .stacked-covers.cover-count-6 .cover-0, .stacked-covers.cover-count-7 .cover-0 { top: 16%; left: -2%; width: 62%; height: 82%; transform: rotate(-12deg); z-index: 1; } .stacked-covers.cover-count-5 .cover-1, .stacked-covers.cover-count-6 .cover-1, .stacked-covers.cover-count-7 .cover-1 { top: 12%; left: 8%; width: 62%; height: 82%; transform: rotate(-6deg); z-index: 2; } .stacked-covers.cover-count-5 .cover-2, .stacked-covers.cover-count-6 .cover-2, .stacked-covers.cover-count-7 .cover-2 { top: 8%; left: 17%; width: 62%; height: 82%; transform: rotate(-1deg); z-index: 3; } .stacked-covers.cover-count-5 .cover-3, .stacked-covers.cover-count-6 .cover-3, .stacked-covers.cover-count-7 .cover-3 { top: 4%; left: 26%; width: 62%; height: 82%; transform: rotate(4deg); z-index: 4; } .stacked-covers.cover-count-5 .cover-4, .stacked-covers.cover-count-6 .cover-4, .stacked-covers.cover-count-7 .cover-4 { top: 0; left: 35%; width: 62%; height: 82%; transform: rotate(9deg); z-index: 5; } .stacked-covers.cover-count-6 .cover-5, .stacked-covers.cover-count-7 .cover-5 { top: -2%; left: 30%; width: 60%; height: 80%; transform: rotate(13deg); z-index: 6; } .stacked-covers.cover-count-7 .cover-6 { top: -4%; left: 25%; width: 58%; height: 78%; transform: rotate(16deg); z-index: 7; } .series-card { transition: transform 0.2s ease, box-shadow 0.2s ease; } .series-card:hover { transform: translateY(-4px); } /* ---------- Skeleton loading shimmer ---------- */ .skeleton { position: relative; overflow: hidden; background-color: var(--surface-hover); border-radius: 0.5rem; } .skeleton::after { content: ""; position: absolute; inset: 0; transform: translateX(-100%); background: linear-gradient( 90deg, transparent, color-mix(in srgb, var(--text-primary) 8%, transparent), transparent ); animation: skeleton-shimmer 1.4s infinite; } @keyframes skeleton-shimmer { 100% { transform: translateX(100%); } } }