/* Tailwind CSS directives - required for framework */ @tailwind base; @tailwind components; @tailwind utilities; /* * Cross-document view transitions (the optional sweetener): modern * browsers (Chrome 126+, Safari 18+) crossfade same-origin navigations — * sidebar links and the series page's library-switch navigation get a * subtle fade for free, with zero JavaScript. Firefox and older browsers * simply navigate instantly; the rule is pure progressive enhancement. */ @view-transition { navigation: auto; } @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 { /* Native form controls (select dropdown lists, scrollbars, inputs) follow the OS light scheme without this — the stats library switcher's popup rendered white in every dark theme. Dark is the document default (all themes except latte are dark). */ color-scheme: dark; --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; } /* Omarchy dark themes, sorted by background luminance (darkest first). Palettes: themes//colors.toml in basecamp/omarchy. Where the upstream lighter_background matches the background (Last Horizon, Solitude) the card tone is derived a step up instead. */ .theme-vantablack { --bg-primary: #000000; --bg-secondary: #1a1a1a; --text-primary: #ffffff; --text-secondary: #ececec; --accent: #8d8d8d; --border: #1a1a1a; } .theme-last-horizon { --bg-primary: #0c0b0c; --bg-secondary: #171516; --text-primary: #fafcfb; --text-secondary: #cfd3cd; --accent: #b59790; --border: #2b2729; } .theme-ethereal { --bg-primary: #060b1e; --bg-secondary: #131a3a; --text-primary: #ffcead; --text-secondary: #c9b8a6; --accent: #7d82d9; --border: #252e56; } .theme-hackerman { --bg-primary: #0b0c16; --bg-secondary: #151828; --text-primary: #ddf7ff; --text-secondary: #b5c5db; --accent: #82fb9c; --border: #1f253a; } .theme-matte-black { --bg-primary: #121212; --bg-secondary: #1e1e1e; --text-primary: #bebebe; --text-secondary: #8a8a8d; --accent: #e68e0d; --border: #2a2a2a; } .theme-solitude { --bg-primary: #101315; --bg-secondary: #171b1e; --text-primary: #cacccc; --text-secondary: #8a9199; --accent: #798186; --border: #262d31; } .theme-retro-82 { --bg-primary: #05182e; --bg-secondary: #0a2540; --text-primary: #f6dcac; --text-secondary: #a7c9c6; --accent: #faa968; --border: #134e5a; } .theme-osaka-jade { --bg-primary: #111c18; --bg-secondary: #23372b; --text-primary: #c1c497; --text-secondary: #81b8a8; --accent: #509475; --border: #32473b; } /* Omarchy maps Kanagawa's accent to its foreground; the UI uses crystalBlue instead so links/buttons stay distinguishable from text. */ .theme-kanagawa { --bg-primary: #1f1f28; --bg-secondary: #223249; --text-primary: #dcd7ba; --text-secondary: #c8c093; --accent: #7e9cd8; --border: #363646; } .theme-miasma { --bg-primary: #222222; --bg-secondary: #2c2c2c; --text-primary: #c2c2b0; --text-secondary: #8a8a7e; --accent: #78824b; --border: #383838; } .theme-lumon { --bg-primary: #16242d; --bg-secondary: #1b2d40; --text-primary: #d6e2ee; --text-secondary: #4d86b0; --accent: #8bc9eb; --border: #243d56; } .theme-ristretto { --bg-primary: #2c2525; --bg-secondary: #3d2f2a; --text-primary: #e6d9db; --text-secondary: #c3b7b8; --accent: #f38d70; --border: #403e41; } .theme-gruvbox { --bg-primary: #282828; --bg-secondary: #3c3836; --text-primary: #d4be98; --text-secondary: #bdae93; --accent: #7daea3; --border: #504945; } .theme-everforest { --bg-primary: #2d353b; --bg-secondary: #343f44; --text-primary: #d3c6aa; --text-secondary: #9da9a0; --accent: #7fbbb3; --border: #3d484d; } .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 { color-scheme: light; --bg-primary: #eff1f5; --bg-secondary: #ffffff; --text-primary: #4c4f69; --text-secondary: #6c6f85; --accent: #d20f39; --border: #dcdfe6; } /* Light themes. Each carries color-scheme: light so native form controls, scrollbars and UA popups render light. Backgrounds follow each theme's canonical palette; the card tone mirrors what the theme itself does (elevated white for screen-like themes, a darker paper tone for the paper ones — Solarized set that convention). */ .theme-tokyo-night-day { color-scheme: light; --bg-primary: #e1e2e7; --bg-secondary: #ffffff; --text-primary: #3760bf; --text-secondary: #6172b0; --accent: #2e7de9; --border: #c4c8da; } .theme-solarized-light { color-scheme: light; --bg-primary: #fdf6e3; --bg-secondary: #eee8d5; --text-primary: #586e75; --text-secondary: #93a1a1; --accent: #268bd2; --border: #ddd6c1; } .theme-one-light { color-scheme: light; --bg-primary: #fafafa; --bg-secondary: #ffffff; --text-primary: #383a42; --text-secondary: #696c77; --accent: #4078f2; --border: #dfdfdf; } .theme-ayu-light { color-scheme: light; --bg-primary: #fafafa; --bg-secondary: #f3f4f5; --text-primary: #5c6166; --text-secondary: #8a9199; --accent: #ff9933; --border: #e2e3e5; } .theme-github-light { color-scheme: light; --bg-primary: #ffffff; --bg-secondary: #f6f8fa; --text-primary: #24292f; --text-secondary: #57606a; --accent: #0969da; --border: #d0d7de; } .theme-gruvbox-light { color-scheme: light; --bg-primary: #fbf1c7; --bg-secondary: #f2e5bc; --text-primary: #3c3836; --text-secondary: #7c6f64; --accent: #076678; --border: #d5c4a1; } .theme-everforest-light { color-scheme: light; --bg-primary: #fdf6e3; --bg-secondary: #f4f0d9; --text-primary: #5c6a72; --text-secondary: #708089; --accent: #3a94c5; --border: #d5cfaf; } .theme-kanagawa-lotus { color-scheme: light; --bg-primary: #f2ecbc; --bg-secondary: #e5ddb0; --text-primary: #545464; --text-secondary: #716e61; --accent: #4d699b; --border: #d5cea3; } /* Omarchy light themes, sorted by background luminance (darkest first). */ .theme-rose-pine { color-scheme: light; --bg-primary: #faf4ed; --bg-secondary: #fffaf3; --text-primary: #575279; --text-secondary: #797593; --accent: #56949f; --border: #dfdad9; } .theme-lupine { color-scheme: light; --bg-primary: #fafafa; --bg-secondary: #ffffff; --text-primary: #212121; --text-secondary: #757575; --accent: #3264eb; --border: #d0d0d0; } .theme-flexoki-light { color-scheme: light; --bg-primary: #fffcf0; --bg-secondary: #f2efe4; --text-primary: #100f0f; --text-secondary: #6f6e69; --accent: #205ea6; --border: #cecdc3; } /* Omarchy's pure monochrome theme: its accent is deliberately grey. */ .theme-white { color-scheme: light; --bg-primary: #ffffff; --bg-secondary: #f5f5f5; --text-primary: #000000; --text-secondary: #808080; --accent: #6e6e6e; --border: #e8e8e8; } /* * 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); } /* Softer shadows on light themes — dark shadows tuned for dark themes look harsh on paper backgrounds. */ .theme-catppuccin-latte, .theme-tokyo-night-day, .theme-solarized-light, .theme-one-light, .theme-ayu-light, .theme-github-light, .theme-gruvbox-light, .theme-everforest-light, .theme-kanagawa-lotus, .theme-rose-pine, .theme-lupine, .theme-flexoki-light, .theme-white { --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); } /* Select popup entries follow the theme (the closed control is themed by its own classes; the option LIST is what UA-styles light) */ select option { background-color: var(--bg-secondary); color: var(--text-primary); } /* 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; } /* * App shell — a fixed left sidebar with a content area to its right. * The sidebar is off-canvas on mobile (toggled via the `is-open` class, * which Alpine flips through `mobileMenuOpen`) and pinned on >= lg. */ :root { --sidebar-w: 16rem; --topbar-h: 4rem; } .app-sidebar { position: fixed; top: 0; left: 0; bottom: 0; width: var(--sidebar-w); z-index: 60; background-color: var(--bg-secondary); border-right: 1px solid color-mix(in srgb, var(--text-primary) 7%, transparent); display: flex; flex-direction: column; overflow-y: auto; transform: translateX(-100%); transition: transform 0.25s ease; } .app-sidebar.is-open { transform: translateX(0); } .app-sidebar-backdrop { position: fixed; inset: 0; z-index: 55; background: var(--surface-overlay); opacity: 0; pointer-events: none; transition: opacity 0.25s ease; } .app-sidebar-backdrop.is-open { opacity: 1; pointer-events: auto; } /* Grouped panels for the collapsible footer menus (account, appearance). Each menu lives in its own subtly lifted, bordered box so that expanded submenus stay visually contained and one menu never blends into another. */ .sidebar-panel { background-color: color-mix(in srgb, var(--text-primary) 4%, var(--bg-secondary)); border: 1px solid var(--border); border-radius: 0.75rem; padding: 0.375rem; } /* Active half of the Appearance panel's Dark/Light mode filter. */ .mode-toggle-active { background-color: var(--accent-muted); color: var(--text-primary); } /* Desktop: pin the sidebar, drop the backdrop. No unconditional body offset here — the body:has(.app-sidebar) rule below is the only source of the sidebar padding, so pages that never render the sidebar (logged-out, audio player, the reader itself) stay truly centered: an unconditional rule reserved the sidebar's 16rem on every wide viewport as an invisible left margin. */ @media (min-width: 1024px) { .app-sidebar { transform: none; } .app-sidebar-backdrop { display: none !important; } .app-mobile-only { display: none !important; } } @media (max-width: 1023.98px) { .app-desktop-only { display: none !important; } } /* Slim fixed topbar that spans the content area (right of the sidebar). */ .app-topbar { position: fixed; top: 0; left: 0; right: 0; z-index: 40; height: var(--topbar-h); background-color: color-mix(in srgb, var(--bg-secondary) 82%, transparent); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); border-bottom: 1px solid color-mix(in srgb, var(--text-primary) 6%, transparent); } /* * Content offsets — applied only on pages whose Header renders the * fixed sidebar (reader.templ / error.templ have no sidebar, so they are * left untouched). */ body:has(.app-sidebar) { padding-top: var(--topbar-h); } @media (min-width: 1024px) { body:has(.app-sidebar) { padding-left: var(--sidebar-w); } /* On desktop the topbar starts right of the pinned sidebar. */ .app-topbar { left: var(--sidebar-w); } } /* A secondary sticky bar (e.g. library switcher) parks under the topbar. */ .app-subbar { position: sticky; top: var(--topbar-h); z-index: 30; } /* 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) 6%, transparent); border-radius: 1rem; 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 { position: relative; background-color: var(--bg-secondary); border: 1px solid color-mix(in srgb, var(--text-primary) 6%, transparent); border-radius: 0.75rem; 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(-4px); box-shadow: var(--shadow-card-hover); border-color: color-mix(in srgb, var(--accent) 50%, var(--border)); } /* Cinematic cover overlay: a centered quick-action that fades in on hover. */ .book-card-cover { position: relative; } .book-card-cover::after { content: ""; position: absolute; inset: 0; background: linear-gradient(to top, rgba(0, 0, 0, 0.55), transparent 55%); opacity: 0; transition: opacity 0.18s ease; pointer-events: none; } .book-card:hover .book-card-cover::after { opacity: 1; } .book-card-action { position: absolute; top: 0; left: 0; right: 0; aspect-ratio: 2 / 3; display: flex; align-items: center; justify-content: center; opacity: 0; pointer-events: none; z-index: 2; transition: opacity 0.18s ease; } .book-card:hover .book-card-action, .book-card:focus-within .book-card-action { opacity: 1; } .book-card-action-btn { display: inline-flex; align-items: center; justify-content: center; width: 2.75rem; height: 2.75rem; border-radius: 9999px; background-color: color-mix(in srgb, var(--accent) 92%, white); color: #fff; box-shadow: 0 6px 18px rgba(0, 0, 0, 0.4); pointer-events: auto; text-decoration: none; } .book-card-meta { background-color: var(--bg-secondary); padding: 0.5rem 0.625rem; } /* On touch devices there is no hover, so the quick-action stays visible and only the circular button itself is clickable (the container passes clicks through to the detail link). */ @media (hover: none) { .book-card-action { opacity: 1; } } /* ---------- Bold accents ---------- */ /* Signature gradient surface for heroes / primary panels. */ .brand-gradient { background-image: linear-gradient( 135deg, color-mix(in srgb, var(--accent) 28%, var(--bg-secondary)), var(--bg-secondary) 62% ); } /* Blurred cover backdrop used behind the book-detail hero. */ .hero-backdrop { position: absolute; inset: 0; z-index: 0; background-size: cover; background-position: center; filter: blur(28px) saturate(1.4); transform: scale(1.15); opacity: 0.5; } .hero-backdrop::after { content: ""; position: absolute; inset: 0; background: linear-gradient( to bottom, color-mix(in srgb, var(--bg-primary) 35%, transparent), var(--bg-primary) 92% ); } /* Stat tile for analytics / dashboard summaries. */ .stat-card { background-color: var(--bg-secondary); border: 1px solid color-mix(in srgb, var(--text-primary) 6%, transparent); border-radius: 1rem; padding: 1.25rem; box-shadow: var(--shadow-card); } /* ---------- 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; } .bg-wood-dark { background-image: url(/static/textures/wood-dark.png); } .bg-wood-light { background-image: url(/static/textures/wood-light.png); } .bg-wood-mahogany { background-image: url(/static/textures/wood-mahogany.png); } 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 chrome & drawers ---------- */ .reader-icon { display: block; fill: none; stroke: currentColor; stroke-width: 2px; stroke-linecap: round; stroke-linejoin: round; } /* Glass chrome: translucent theme-tinted bars over the edge-to-edge reading surface. Blur + saturate the content behind, hairline border, soft directional shadow. */ .reader-glass { background-color: color-mix(in srgb, var(--bg-primary) 70%, transparent); -webkit-backdrop-filter: blur(18px) saturate(1.4); backdrop-filter: blur(18px) saturate(1.4); border-color: color-mix(in srgb, var(--border) 60%, transparent); } #reader-topbar.reader-glass { border-bottom: 1px solid color-mix(in srgb, var(--border) 60%, transparent); box-shadow: 0 4px 18px rgba(0, 0, 0, 0.16); } #reader-bottombar.reader-glass { border-top: 1px solid color-mix(in srgb, var(--border) 60%, transparent); box-shadow: 0 -4px 18px rgba(0, 0, 0, 0.16); } /* Bars slide away when the chrome hides (opacity handled on #reader-chrome) */ #reader-topbar, #reader-bottombar { transition: transform 0.3s ease, opacity 0.3s ease; } #reader-chrome.chrome-hidden #reader-topbar { transform: translateY(-101%); } #reader-chrome.chrome-hidden #reader-bottombar { transform: translateY(101%); } /* Theme-aware translucent hover pills + focus rings for bar controls */ #reader-topbar button:hover, #reader-bottombar button:hover, #reader-topbar a:hover { background-color: color-mix(in srgb, currentColor 13%, transparent); } #reader-topbar button:focus-visible, #reader-bottombar button:focus-visible, #reader-topbar a:focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; } .reader-sep { background-color: color-mix(in srgb, var(--border) 80%, transparent); } .reader-select { background-color: color-mix(in srgb, var(--bg-primary) 55%, transparent); border: 1px solid color-mix(in srgb, var(--border) 70%, transparent); color: inherit; } .reader-select:hover { border-color: #3b82f6; } /* Progress slider: thin rounded track + floating white thumb */ #reader-bottombar input[type="range"] { -webkit-appearance: none; appearance: none; height: 18px; background: transparent; cursor: pointer; accent-color: #3b82f6; } #reader-bottombar input[type="range"]::-webkit-slider-runnable-track { height: 4px; border-radius: 9999px; background: color-mix(in srgb, currentColor 22%, transparent); } #reader-bottombar input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; margin-top: -5px; width: 14px; height: 14px; border: none; border-radius: 50%; background: #ffffff; box-shadow: 0 1px 4px rgba(0, 0, 0, 0.45); transition: transform 0.12s ease; } #reader-bottombar input[type="range"]::-webkit-slider-thumb:hover { transform: scale(1.15); } #reader-bottombar input[type="range"]::-moz-range-track { height: 4px; border-radius: 9999px; background: color-mix(in srgb, currentColor 22%, transparent); } #reader-bottombar input[type="range"]::-moz-range-thumb { width: 14px; height: 14px; border: none; border-radius: 50%; background: #ffffff; box-shadow: 0 1px 4px rgba(0, 0, 0, 0.45); } .drawer-scrim { position: fixed; inset: 0; background-color: rgba(0, 0, 0, 0.45); z-index: 45; } .reader-drawer { position: fixed; top: 0; bottom: 0; width: 340px; max-width: calc(100vw - 2rem); background-color: var(--bg-secondary); z-index: 50; display: flex; flex-direction: column; padding-top: env(safe-area-inset-top); padding-bottom: env(safe-area-inset-bottom); } .reader-drawer.left { left: 0; border-right: 1px solid var(--border); } .reader-drawer.right { right: 0; border-left: 1px solid var(--border); } /* Mobile: drawers become full-width sheets */ @media (max-width: 640px) { .reader-drawer { width: 100%; max-width: 100%; } } .reader-drawer-header { display: flex; align-items: center; justify-content: space-between; padding: 0.75rem 1rem; border-bottom: 1px solid var(--border); flex-shrink: 0; } .reader-drawer-body { flex: 1; overflow-y: auto; padding: 1rem; } .reader-seg { display: inline-flex; border: 1px solid color-mix(in srgb, var(--border) 70%, transparent); background-color: color-mix(in srgb, var(--bg-primary) 50%, transparent); border-radius: 0.5rem; overflow: hidden; } .reader-seg button { padding: 0.25rem 0.6rem; font-size: 0.75rem; line-height: 1.25rem; } .reader-seg button.active { background-color: #2563eb; color: #ffffff; } .theme-swatch { height: 2.25rem; border-radius: 0.5rem; border: 1px solid var(--border); display: flex; align-items: center; justify-content: center; } .theme-swatch:hover { border-color: #3b82f6; } /* Selection popover & annotations drawer widgets */ .reader-popover { position: fixed; transform: translate(-50%, calc(-100% - 10px)); background-color: color-mix(in srgb, var(--bg-primary) 85%, transparent); -webkit-backdrop-filter: blur(16px) saturate(1.3); backdrop-filter: blur(16px) saturate(1.3); border: 1px solid color-mix(in srgb, var(--border) 70%, transparent); border-radius: 0.75rem; box-shadow: 0 8px 28px rgba(0, 0, 0, 0.35); padding: 0.5rem 0.625rem; z-index: 60; } .color-dot { width: 1.375rem; height: 1.375rem; border-radius: 9999px; border: 2px solid rgba(0, 0, 0, 0.25); transition: transform 0.12s ease; } .color-dot:hover { transform: scale(1.15); } .color-dot.selected { border-color: #ffffff; box-shadow: 0 0 0 2px #3b82f6; } .reader-popover-btn { padding: 0.3rem; border-radius: 0.375rem; color: inherit; } .reader-popover-btn:hover { background-color: color-mix(in srgb, currentColor 13%, transparent); } .reader-popover-btn.danger:hover { background-color: rgba(153, 27, 27, 0.6); } /* Highlight extent-adjustment handles (objective 9): teardrop dots that sit on the range boundary; mirrored pair — start points left, end points right. */ .adjust-handle { position: fixed; width: 26px; height: 26px; /* Standard Android selection teardrops: both points face DOWN toward the text. The square corner is the tip — start's bottom-left corner rotated -45° sits straight down; end's bottom-right +45° likewise. */ border-radius: 50% 50% 50% 0; transform: rotate(-45deg); background: #3b82f6; border: 2px solid #ffffff; box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); pointer-events: auto; cursor: grab; touch-action: none; } .adjust-handle[data-which="end"] { border-radius: 50% 50% 0 50%; transform: rotate(45deg); } .adjust-handle[data-dragging] { cursor: grabbing; scale: 1.15; } /* PDF rect-edge adjust grips (fixed-layout highlights): short edge bars centered on each rect's edge midpoint. Vertical edges drag in x, horizontal edges in y — resize only, one rect per drag. */ .adjust-grip { position: fixed; width: 10px; height: 30px; border-radius: 5px; background: #3b82f6; border: 2px solid #ffffff; box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4); pointer-events: auto; cursor: ew-resize; touch-action: none; } .adjust-grip[data-dir="h"] { width: 30px; height: 10px; cursor: ns-resize; } .adjust-grip[data-dragging] { scale: 1.1; } .reader-note-input { width: 100%; resize: vertical; padding: 0.5rem; border-radius: 0.5rem; font-size: 0.875rem; color: inherit; background-color: color-mix(in srgb, var(--bg-primary) 55%, transparent); border: 1px solid color-mix(in srgb, var(--border) 70%, transparent); } .reader-note-input:focus { outline: 2px solid #3b82f6; outline-offset: 1px; } /* Markdown notes render through renderNoteHtml (markdown.ts) into x-html containers at three sites (reader notes-drawer rows, the 📝 highlight note line, the banner's expanded body). Tailwind preflight zeroes p/blockquote/list margins globally, which flattened paragraphs single-spaced and left blockquotes/heads indistinguishable from body text — this block restores block structure. em-based so it scales with each site's text-xs/text-sm context. */ .note-rich p { margin: 0; } .note-rich p + p { margin-top: 0.6em; } .note-rich h1, .note-rich h2, .note-rich h3, .note-rich h4, .note-rich h5, .note-rich h6 { margin: 0.8em 0 0.4em; font-weight: 600; line-height: 1.3; } .note-rich h1 { font-size: 1.25em; } .note-rich h2 { font-size: 1.15em; } .note-rich h3, .note-rich h4 { font-size: 1.05em; } .note-rich h5, .note-rich h6 { font-size: 1em; } .note-rich blockquote { margin: 0.6em 0; padding: 0.1em 0 0.1em 0.75em; border-left: 3px solid color-mix(in srgb, var(--text-secondary) 55%, transparent); color: var(--text-secondary); } .note-rich ul, .note-rich ol { margin: 0.6em 0; padding-left: 1.4em; } .note-rich ul { list-style: disc; } .note-rich ol { list-style: decimal; } .note-rich li { margin: 0.15em 0; } .note-rich li + li { margin-top: 0.25em; } .note-rich li > p { margin: 0; } .note-rich pre { margin: 0.6em 0; padding: 0.6em 0.75em; overflow-x: auto; border-radius: 0.375rem; background-color: color-mix(in srgb, var(--bg-secondary) 80%, transparent); border: 1px solid color-mix(in srgb, var(--border) 70%, transparent); } .note-rich pre code { background: none; padding: 0; font-size: 0.9em; } .note-rich code { font-size: 0.9em; padding: 0.1em 0.3em; border-radius: 0.25rem; background-color: color-mix(in srgb, var(--bg-secondary) 80%, transparent); } .note-rich a { text-decoration: underline; text-underline-offset: 2px; } .note-rich hr { margin: 0.8em 0; } .note-rich table { margin: 0.6em 0; border-collapse: collapse; font-size: 0.95em; } .note-rich th, .note-rich td { border: 1px solid var(--border); padding: 0.25em 0.5em; text-align: left; } .note-rich th { font-weight: 600; } .reader-tabs { display: flex; border-bottom: 1px solid var(--border); flex-shrink: 0; } .reader-tabs button { flex: 1; padding: 0.5rem 0.25rem; font-size: 0.8125rem; color: var(--text-secondary); border-bottom: 2px solid transparent; } .reader-tabs button.active { color: var(--text-primary); border-bottom-color: #3b82f6; } .reader-tab-count { display: inline-block; min-width: 1.25rem; margin-left: 0.25rem; padding: 0 0.25rem; border-radius: 9999px; font-size: 0.6875rem; line-height: 1.1rem; background-color: color-mix(in srgb, currentColor 13%, transparent); } .reader-hl-row { display: flex; align-items: center; gap: 0.25rem; } /* Fixed-layout toolbar responsiveness is handled with Tailwind responsive utilities in reader.templ (hidden md:flex for the full toolbar, flex md:hidden for the compact row) — custom layer rules here would lose the cascade to the flex utility anyway. */ .reader-tools-popover { position: absolute; bottom: 100%; right: 0.5rem; margin-bottom: 0.5rem; min-width: 16rem; max-width: calc(100vw - 1.5rem); max-height: min(26rem, 65vh); overflow-y: auto; background-color: color-mix(in srgb, var(--bg-primary) 88%, transparent); -webkit-backdrop-filter: blur(18px) saturate(1.3); backdrop-filter: blur(18px) saturate(1.3); border: 1px solid color-mix(in srgb, var(--border) 70%, transparent); border-radius: 0.75rem; box-shadow: 0 -6px 28px rgba(0, 0, 0, 0.35); padding: 0.25rem; } .reader-tools-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 0.5rem 0.75rem; font-size: 0.875rem; border-radius: 0.5rem; } .reader-tools-row + .reader-tools-row { border-top: 1px solid color-mix(in srgb, var(--border) 45%, transparent); } .reader-tools-row:hover { background-color: color-mix(in srgb, currentColor 6%, transparent); } /* Fixed-layout display filters: one var drives the iframe ::part(filter) (comics/PDFs via foliate-view exportparts) and the webtoon page images. */ #reader-view::part(filter) { filter: var(--fx-filter, none); } /* Page thumbnails grid (contents drawer, fixed-layout) */ .reader-thumb-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.5rem; } .reader-thumb { position: relative; border-radius: 0.375rem; overflow: hidden; border: 2px solid transparent; padding: 0; background-color: color-mix(in srgb, currentColor 6%, transparent); } .reader-thumb:hover { border-color: color-mix(in srgb, currentColor 30%, transparent); } .reader-thumb.active { border-color: #3b82f6; } .reader-thumb-img { aspect-ratio: 3 / 4; display: flex; align-items: center; justify-content: center; overflow: hidden; } .reader-thumb-img canvas, .reader-thumb-img img { max-width: 100%; max-height: 100%; object-fit: contain; } .reader-thumb-num { position: absolute; bottom: 0.2rem; right: 0.35rem; font-size: 0.65rem; line-height: 1; padding: 0.15rem 0.3rem; border-radius: 0.25rem; background-color: rgba(0, 0, 0, 0.55); color: #ffffff; } /* Desktop edge page-turn zones: only on hover-capable fine-pointer devices (touch uses tap zones instead). Arrow + subtle edge gradient appear on hover. */ .reader-edge-zone { position: absolute; top: 0; bottom: 0; width: 8%; max-width: 72px; min-width: 44px; z-index: 10; display: none; align-items: center; cursor: pointer; } .reader-edge-zone.left { left: 0; justify-content: flex-start; padding-left: 0.75rem; } .reader-edge-zone.right { right: 0; justify-content: flex-end; padding-right: 0.75rem; } .reader-edge-zone::before { content: ""; position: absolute; inset: 0; opacity: 0; transition: opacity 0.15s ease; } .reader-edge-zone.left::before { background: linear-gradient(to right, rgba(255, 255, 255, 0.08), transparent); } .reader-edge-zone.right::before { background: linear-gradient(to left, rgba(255, 255, 255, 0.08), transparent); } .reader-edge-zone .reader-icon { color: #ffffff; filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.6)); opacity: 0; transition: opacity 0.15s ease; } .reader-edge-zone:hover::before { opacity: 1; } .reader-edge-zone:hover .reader-icon { opacity: 0.85; } @media (hover: hover) and (pointer: fine) { .reader-edge-zone { display: flex; } } /* Shortcuts help modal */ .reader-help-backdrop { position: fixed; inset: 0; background-color: rgba(0, 0, 0, 0.5); z-index: 70; } .reader-help { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: min(34rem, calc(100vw - 2rem)); max-height: min(38rem, calc(100vh - 4rem)); display: flex; flex-direction: column; background-color: color-mix(in srgb, var(--bg-primary) 90%, transparent); -webkit-backdrop-filter: blur(20px) saturate(1.3); backdrop-filter: blur(20px) saturate(1.3); border: 1px solid color-mix(in srgb, var(--border) 70%, transparent); border-radius: 0.875rem; box-shadow: 0 16px 48px rgba(0, 0, 0, 0.45); z-index: 71; } .reader-help-header { display: flex; align-items: center; justify-content: space-between; padding: 0.875rem 1.25rem; border-bottom: 1px solid var(--border); flex-shrink: 0; } .reader-help-body { overflow-y: auto; padding: 1rem 1.25rem; } .reader-help-section { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-secondary); margin: 1.1rem 0 0.4rem; } .reader-help-body .reader-help-section:first-child { margin-top: 0; } .help-row { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; padding: 0.3rem 0; font-size: 0.85rem; } .help-row > span:first-child { color: var(--text-primary); } .help-keys { text-align: right; color: var(--text-secondary); font-size: 0.8rem; white-space: nowrap; } .help-note { color: var(--text-secondary); font-size: 0.75rem; } .kbd { display: inline-block; min-width: 1.4rem; text-align: center; padding: 0.05rem 0.4rem; margin: 0 0.1rem; border-radius: 0.3rem; border: 1px solid color-mix(in srgb, var(--border) 80%, transparent); border-bottom-width: 2px; background-color: color-mix(in srgb, currentColor 8%, transparent); font-family: ui-monospace, monospace; font-size: 0.72rem; line-height: 1.2rem; } /* In-book search */ .reader-search-bar { display: flex; align-items: center; gap: 0.375rem; padding: 0.625rem 1rem; border-bottom: 1px solid var(--border); flex-shrink: 0; } .reader-search-status { padding: 0.375rem 1rem; font-size: 0.75rem; color: var(--text-secondary); border-bottom: 1px solid color-mix(in srgb, var(--border) 50%, transparent); flex-shrink: 0; min-height: 1.75rem; } .search-result { display: block; padding: 0.375rem 0.5rem; border-radius: 0.375rem; font-size: 0.8125rem; line-height: 1.35; } .search-result:hover { background-color: color-mix(in srgb, currentColor 9%, transparent); } .search-result mark { background-color: rgba(255, 213, 79, 0.4); color: inherit; border-radius: 2px; padding: 0 1px; } /* ---------- 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%); } } } /* Touch selection handles (custom gesture — the native ones are disabled) */ #selection-handles { position: fixed; inset: 0; pointer-events: none; z-index: 55; } #selection-handles .sel-handle { position: absolute; top: 0; left: 0; width: 12px; height: 24px; pointer-events: auto; touch-action: none; background: #8ab4f8; border-radius: 6px; box-shadow: 0 1px 4px rgba(0, 0, 0, 0.45); opacity: 0.95; } #selection-handles .sel-handle::before { content: ""; position: absolute; top: -4px; left: 50%; width: 2px; height: 5px; background: inherit; transform: translateX(-50%); } /* ---------- Filter rule builder (filter drawer, Phase 9) ---------- Templ owns the markup; Alpine owns the rule-tree state. Look only: rows indent by group depth, negated operators/chips run red. */ .fb-connective { font-size: 0.65rem; font-weight: 700; letter-spacing: 0.05em; color: var(--text-secondary); min-width: 1.6rem; } .fb-picker { font-size: 0.8rem; padding: 0.3rem 0.6rem; border-radius: 0.5rem; border: 1px solid var(--border); background-color: var(--bg-primary); color: var(--text-primary); cursor: pointer; white-space: nowrap; } .fb-picker:hover { background-color: var(--surface-hover); } .fb-picker-active { border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); } .fb-picker-op { color: var(--text-secondary); } .fb-negated-text { color: var(--status-danger); } .fb-value { width: 9rem; font-size: 0.8rem; padding: 0.3rem 0.5rem; } .fb-menu { position: absolute; z-index: 60; margin-top: 0.25rem; width: 15rem; max-height: 16rem; overflow-y: auto; border-radius: 0.5rem; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25); background-color: var(--bg-primary); border: 1px solid var(--border); } .fb-menu-value { width: 16rem; left: 0; top: 100%; margin-top: 0.25rem; } .fb-menu-group { font-size: 0.65rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.09em; color: var(--text-secondary); padding: 0.4rem 0.75rem 0.3rem; margin-top: 0.3rem; background-color: color-mix(in srgb, var(--text-primary) 5%, transparent); border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); } .fb-menu-filter-wrap { padding: 0.4rem 0.5rem; border-bottom: 1px solid var(--border); background-color: var(--bg-primary); } .fb-menu-filter { width: 100%; font-size: 0.8rem; padding: 0.3rem 0.5rem; border-radius: 0.4rem; border: 1px solid var(--border); background-color: var(--bg-primary); color: var(--text-primary); outline: none; } .fb-menu-filter:focus { border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); } .fb-menu-empty { font-size: 0.78rem; color: var(--text-secondary); padding: 0.6rem 0.75rem; } .fb-menu-item { display: flex; width: 100%; justify-content: space-between; align-items: center; gap: 0.5rem; text-align: left; font-size: 0.8rem; /* tabbed in under the flush-left group headers */ padding: 0.35rem 0.75rem 0.35rem 1.7rem; color: var(--text-primary); cursor: pointer; } .fb-menu-item:hover { background-color: var(--surface-hover); } .fb-menu-item-active { color: var(--accent); } .fb-mode-btn { font-size: 0.65rem; font-weight: 700; letter-spacing: 0.05em; padding: 0.15rem 0.5rem; border-radius: 9999px; border: 1px solid var(--border); background-color: var(--bg-primary); color: var(--text-secondary); cursor: pointer; } .fb-mode-btn.fb-mode-active { background-color: var(--accent-muted); border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); color: var(--accent); } .fb-mini-btn { font-size: 0.7rem; padding: 0.15rem 0.5rem; border-radius: 0.4rem; border: 1px dashed var(--border); background-color: transparent; color: var(--text-secondary); cursor: pointer; white-space: nowrap; } .fb-mini-btn:hover { background-color: var(--surface-hover); color: var(--text-primary); } .fb-mini-btn.fb-danger { border-style: solid; color: var(--status-danger); } .fb-group-end { border-left: 2px solid var(--border); height: 0.5rem; margin-top: -0.25rem; } .fb-nested { border-left: 2px solid var(--border); padding-left: 0.6rem; } .fb-connective-btn { font-size: 0.65rem; font-weight: 700; letter-spacing: 0.05em; color: var(--text-secondary); min-width: 1.9rem; padding: 0.1rem 0.3rem; border: 1px dashed var(--border); border-radius: 0.4rem; background-color: transparent; cursor: pointer; text-align: center; } .fb-connective-btn:hover { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); } .fb-chip-static { border-style: solid; opacity: 0.7; cursor: default; } /* Field menu: the filter input lives OUTSIDE the scroll region so it sticks while the groups scroll beneath it. */ .fb-menu-fields { display: flex; flex-direction: column; max-height: 18rem; overflow: hidden; } .fb-menu-fields .fb-menu-filter-wrap { flex-shrink: 0; border-bottom: 1px solid var(--border); } .fb-menu-fields .fb-menu-scroll { overflow-y: auto; min-height: 0; } .fb-value-cell .fb-menu-empty { padding: 0.5rem 0.75rem; font-size: 0.78rem; color: var(--text-secondary); }