Files
bitvid/css/style.css
T

1466 lines
31 KiB
CSS

@import "./tokens.css";
:root {
--scrollbar-width: 0.5rem;
--scrollbar-track: rgba(255, 255, 255, 0.1);
--scrollbar-thumb: rgba(255, 255, 255, 0.3);
--scrollbar-thumb-hover: rgba(255, 255, 255, 0.4);
/* Sidebar stack must remain below modal overlays. */
--z-sidebar-overlay: 10;
--z-sidebar: 20;
--z-modal-root: 100;
--z-modal-overlay: 110;
--z-modal-content: 120;
--z-modal-player: 130;
--sidebar-width-expanded: 16rem;
--sidebar-width-collapsed: 4rem;
--sidebar-width: var(--sidebar-width-expanded);
--sidebar-control-size: 2.75rem;
--sidebar-content-gap: 1rem;
}
html {
font-size: 100%;
}
@media (min-width: 1024px) {
html {
font-size: 75%;
}
}
/* Global scrollbar styling */
* {
scrollbar-width: thin;
scrollbar-color: var(--scrollbar-thumb) transparent;
}
*::-webkit-scrollbar {
width: var(--scrollbar-width);
height: var(--scrollbar-width);
}
*::-webkit-scrollbar-track {
background: var(--scrollbar-track);
}
*::-webkit-scrollbar-thumb {
background: var(--scrollbar-thumb);
border-radius: 0.25rem;
}
*::-webkit-scrollbar-thumb:hover {
background: var(--scrollbar-thumb-hover);
}
/* Core Styles */
body {
font-family: var(--font-family-sans);
background-color: var(--color-page);
color: var(--color-text-strong);
line-height: 1.5;
margin: 0;
padding: 0;
overflow-x: hidden; /* Disable horizontal scrolling */
}
#nip71TagsFieldset > legend,
#editNip71TagsFieldset > legend {
margin-left: 0;
padding: 0;
}
#app {
/*
Keep the primary content surface aligned with the page background so the
sidebar styling does not bleed into the video grid.
*/
background-color: var(--color-page);
color: inherit;
}
header {
margin-bottom: 2rem;
padding: 1rem 0;
display: flex;
justify-content: flex-start;
}
header img {
height: 6rem;
width: auto;
max-width: none;
}
.container {
max-width: 92.5rem;
margin: 0 auto;
padding: 1rem;
}
/* Video Grids */
#videoList {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
gap: 2rem;
padding: 1.5rem 0;
}
/* Subscriptions grid: same pattern as #videoList */
#subscriptionsVideoList {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
gap: 2rem;
padding: 1.5rem 0;
}
/* Now also match for channelVideoList (channel profile) */
#channelVideoList {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
gap: 2rem;
padding: 1.5rem 0;
}
/* --------------------------------------------------------------------------
Legacy component compatibility shims
--------------------------------------------------------------------------
These selectors bridge existing markup to the token-backed primitives while
templates migrate to the new utility classes. Remove once all views are
updated. The shared token mappings now live in `css/tailwind.source.css`
so this file only maintains bespoke overrides.
--------------------------------------------------------------------------- */
.video-card[data-owner-is-viewer="false"][data-url-health-state="offline"][data-stream-health-state="unhealthy"] {
display: none;
}
.video-card.video-card--owner-private {
box-shadow:
0 0 0 0.125rem rgba(168, 85, 247, 0.55),
var(--shadow-md);
}
.video-card.video-card--owner-private:hover {
box-shadow:
0 0 0 0.125rem rgba(192, 132, 252, 0.65),
var(--shadow-lg);
}
.video-card.video-card--nsfw-owner {
background-color: #000;
box-shadow:
0 0 0 0.0625rem rgba(239, 68, 68, 0.4),
var(--shadow-md);
}
.video-card.video-card--nsfw-owner:hover {
box-shadow:
0 0 0 0.0625rem rgba(239, 68, 68, 0.55),
var(--shadow-lg);
}
.video-card img.video-card__thumbnail--blurred {
filter: blur(18px);
transform: scale(1.05);
transition:
filter 200ms ease,
transform 200ms ease;
}
.video-card[data-owner-is-viewer="true"][data-url-health-state="offline"][data-stream-health-state="unhealthy"] {
box-shadow:
0 0 0 0.0625rem rgba(239, 68, 68, 0.35),
var(--shadow-md);
}
.video-card[data-owner-is-viewer="true"][data-url-health-state="offline"][data-stream-health-state="unhealthy"]:hover {
box-shadow:
0 0 0 0.0625rem rgba(239, 68, 68, 0.45),
var(--shadow-lg);
}
.video-card--enter {
opacity: 0;
animation: video-card-fade-in 220ms ease-out forwards;
}
.video-visibility-toggle__option {
min-width: 5.5rem;
letter-spacing: 0.08em;
}
.video-visibility-toggle__option.is-disabled[data-active="true"] {
opacity: 0.65;
}
.video-visibility-toggle__option[data-active="true"] {
background: linear-gradient(
135deg,
rgba(129, 140, 248, 0.4),
rgba(192, 132, 252, 0.32)
);
}
/*
The modal action buttons reuse the shared `.icon-button` primitive which
previously layered Tailwind's `.btn-ghost` helper on top. The helper adds
responsive padding/height rules that collapse when the flex container
tightens, so the glyphs shrank while the back button (a fixed-size button)
stayed stable. Reset every dimension here so the icons always render with
the same 40px square footprint as the back control, regardless of flex
squeezing or inherited ghost styles.
*/
.modal-nav .icon-button {
box-shadow: none;
padding: 0;
gap: 0;
width: 2.5rem;
height: 2.5rem;
min-width: 2.5rem;
min-height: 2.5rem;
border: none;
}
/* Watch history cards */
@keyframes video-card-fade-in {
from {
opacity: 0;
transform: translateY(0.5rem);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/*
Thumbnails start transparent and fade in the first time they finish loading.
`data-thumbnail-loaded` is cleared by js/index.js once the animation completes
so re-rendering the same element later does not replay the transition and cause
a distracting flash.
*/
[data-video-thumbnail][data-thumbnail-loaded="true"] {
animation: video-thumbnail-fade-in 220ms ease-out forwards;
}
@keyframes video-thumbnail-fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
/*
Generic fade-in utility for chrome elements (sidebar, header controls, etc.).
js/index.js listens for the `interface-fade-in` animation finishing and removes
the temporary classes so that once an element has gracefully appeared it stays
opaque even if the DOM is re-used or re-parented later.
*/
.fade-in {
opacity: 0;
animation: interface-fade-in 280ms ease-out forwards;
animation-delay: var(--fade-in-delay, 0ms);
}
.fade-in-delay-100 {
--fade-in-delay: 100ms;
}
.fade-in-delay-200 {
--fade-in-delay: 200ms;
}
.fade-in-delay-300 {
--fade-in-delay: 300ms;
}
.fade-in-delay-400 {
--fade-in-delay: 400ms;
}
@keyframes interface-fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@media (prefers-reduced-motion: reduce) {
.video-card--enter,
.fade-in {
opacity: 1;
animation: none;
}
[data-video-thumbnail][data-thumbnail-loaded="true"] {
animation: none;
opacity: 1;
}
}
.video-card:hover {
transform: translateY(-0.25rem);
box-shadow: var(--shadow-lg);
}
.video-card .thumbnail-container {
position: relative;
padding-top: 56.25%;
background-color: var(--color-surface);
overflow: hidden;
border-top-left-radius: inherit;
border-top-right-radius: inherit;
}
.video-card img.thumbnail {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
.video-card .details {
padding: 1.25rem;
}
.video-card h3 {
font-size: 1.125rem;
font-weight: 600;
color: var(--color-text);
margin-bottom: 0.75rem;
}
/* Ensure modal portals always sit above the sidebar stack */
#modalContainer {
position: fixed;
inset: 0;
z-index: var(--z-modal-root);
pointer-events: none;
isolation: isolate;
}
#modalContainer > * {
pointer-events: auto;
}
#modalContainer > [id$="Modal"]:not(#playerModal) {
z-index: var(--z-modal-root);
}
#modalContainer > [id$="Modal"]:not(#playerModal) > .absolute {
z-index: var(--z-modal-overlay);
}
#modalContainer > [id$="Modal"]:not(#playerModal) .modal-container,
#modalContainer > [id$="Modal"]:not(#playerModal) .modal-content {
z-index: var(--z-modal-content);
}
#modalContainer > #playerModal {
z-index: var(--z-modal-player);
}
#modalContainer > #playerModal .player-modal__container,
#modalContainer > #playerModal .player-modal__content,
#modalContainer > #playerModal .video-modal__container,
#modalContainer > #playerModal .video-modal__panel {
z-index: var(--z-modal-content);
}
/* Modal Player */
#playerModal {
position: fixed;
inset: 0;
z-index: var(--z-modal-player);
display: none;
flex-direction: column;
align-items: stretch;
justify-content: stretch;
width: 100vw;
height: 100vh;
background-color: var(--color-surface);
color: var(--color-subtle-text);
overflow-x: hidden;
overflow-y: auto;
overscroll-behavior: contain;
-webkit-overflow-scrolling: touch;
}
#nostrFormModal,
#profileModal,
#uploadModal {
backdrop-filter: blur(0.625rem);
-webkit-backdrop-filter: blur(0.625rem); /* For Safari */
}
/* If you ever want to show it, add ".flex" class dynamically */
#playerModal.flex {
display: flex;
}
.video-modal__panel,
#playerModal .player-modal__content {
animation: fadeIn var(--modal-motion-duration) ease-out;
}
.video-modal__video,
#playerModal .player-modal__video {
padding: clamp(0.75rem, 3.5vw, 2.5rem);
border-bottom: 0.0625rem solid rgba(148, 163, 184, 0.18);
}
.video-modal__media,
#playerModal .player-modal__video video {
box-shadow: 0 1.125rem 2.75rem rgba(8, 11, 30, 0.55);
max-height: min(80vh, calc(100vw * 9 / 16));
}
.video-modal__info,
#playerModal .player-modal__info {
padding: clamp(1.5rem, 3vw, 2.5rem);
padding-bottom: clamp(2.5rem, 6vw, 4rem);
box-shadow: inset 0 1px 0 rgba(15, 23, 42, 0.35);
}
.modal-meta {
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
.modal-view-count {
display: inline-flex;
align-items: center;
gap: 0.25rem;
font-size: 0.875rem;
line-height: 1.25rem;
color: var(--color-muted-alt);
white-space: nowrap;
}
.view-count-text {
white-space: nowrap;
color: var(--color-muted-strong);
}
html.modal-open {
overflow: hidden;
}
body.modal-open {
overflow: hidden;
}
html.modal-open,
body.modal-open {
height: 100%;
}
body.modal-open #app {
overflow: hidden;
}
body.modal-open #sidebar,
body.modal-open #sidebarOverlay {
pointer-events: none;
opacity: 0;
visibility: hidden;
}
body.modal-open #sidebar {
z-index: var(--z-sidebar-overlay);
}
body.modal-open #sidebarOverlay {
z-index: calc(var(--z-sidebar-overlay) - 1);
}
#sidebar.is-modal-hidden,
#sidebarOverlay.is-modal-hidden {
pointer-events: none;
opacity: 0;
visibility: hidden;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(1.25rem);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@media (prefers-reduced-motion: reduce) {
.video-modal__panel,
#playerModal .player-modal__content {
animation-duration: 1ms;
}
}
/* Modal Video */
#modalVideo {
width: 100%;
aspect-ratio: 16/9;
background-color: black;
}
/* Responsive Adjustments */
@media (min-width: 768px) {
#playerModal .player-modal__content {
max-width: none;
border-radius: 0;
}
}
#modalNav.modal-nav,
.modal-nav,
.video-modal__nav {
background-color: var(--color-bg);
border-bottom: 0.0625rem solid rgba(148, 163, 184, 0.25);
box-shadow: 0 0.75rem 2rem rgba(8, 11, 30, 0.45);
}
.modal-nav .flex {
position: relative;
gap: 0.75rem;
}
[data-modal-zap-wrapper] .zap-popover,
.zap-popover {
position: absolute;
top: calc(100% + 0.5rem);
right: 0;
z-index: 80;
width: max-content;
}
.zap-popover__content {
width: 18rem;
max-width: calc(100vw - 2rem);
background: rgba(11, 17, 32, 0.96);
color: var(--color-subtle);
border: 1px solid rgba(148, 163, 184, 0.35);
box-shadow: 0 22px 48px -18px rgba(8, 14, 28, 0.7);
-webkit-backdrop-filter: blur(12px);
backdrop-filter: blur(12px);
}
.modal-actions__overflow {
position: relative;
}
.modal-actions__overflow,
[data-more-menu-wrapper] {
overflow: visible;
}
[data-more-menu="true"] {
z-index: 70;
}
/* Mobile-specific styles */
@media (max-width: 767px) {
#playerModal {
padding: 0;
}
}
/* Progress Bars */
.progress-bar {
width: 100%;
height: 0.5rem;
background-color: rgb(255 255 255 / 0.1);
border-radius: 9999px;
overflow: hidden;
}
.progress-bar-fill {
height: 100%;
background-color: var(--color-primary);
transition: width 0.3s ease-out;
}
/* Forms & Inputs */
input,
textarea {
width: 100%;
padding: 0.75rem;
background-color: var(--color-bg);
border: 0.0625rem solid rgb(255 255 255 / 0.1);
border-radius: 0.5rem;
color: var(--color-text);
transition: border-color 0.2s;
}
input:focus,
textarea:focus {
outline: none;
border-color: var(--color-primary);
ring: 0.125rem var(--color-primary);
}
/* -------------------------------------------
COMMENTED OUT the 'button:not(.icon-button)'
global rule that overrides your circles
--------------------------------------------
button:not(.icon-button) {
padding: 0.75rem 1.5rem;
background-color: var(--color-primary);
color: white;
border-radius: 0.5rem;
font-weight: 500;
transition: all 0.2s;
}
button:not(.icon-button):hover {
background-color: var(--color-secondary);
transform: translateY(-0.0625rem);
}
button:not(.icon-button):focus {
outline: none;
ring: 0.125rem var(--color-primary);
}
*/
/* Utility Classes */
.line-clamp-2 {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.hidden {
display: none;
}
/* Notifications */
#notificationPortal {
position: fixed;
top: calc(env(safe-area-inset-top, 0px) + 1.5rem);
left: 50%;
transform: translateX(-50%);
z-index: 400; /* Keep notifications above the highest modal overlays */
width: min(90vw, 32rem);
display: flex;
flex-direction: column;
gap: 0.75rem;
pointer-events: none;
}
#notificationPortal > * {
width: 100%;
margin-bottom: 0;
pointer-events: auto;
border-radius: 0.75rem;
box-shadow: 0 1.25rem 3rem rgba(15, 23, 42, 0.3);
}
/* Base styling without a forced display */
#errorContainer,
#successContainer {
border-radius: 0.5rem;
padding: 1rem;
margin-bottom: 1rem;
align-items: center; /* Keep the rest of your styling */
}
/* When not hidden, display as flex */
#errorContainer:not(.hidden),
#successContainer:not(.hidden) {
display: flex;
}
.status-spinner--inline {
animation: spin 1s linear infinite;
border: 0.125rem solid rgba(96, 165, 250, 0.35);
border-radius: 9999px;
border-top-color: var(--color-info);
display: inline-block;
height: 1rem;
width: 1rem;
}
.sidebar-loading-wrapper {
display: flex;
justify-content: center;
align-items: center;
padding: 3rem 0;
width: 100%;
grid-column: 1 / -1;
}
.sidebar-loading-indicator {
display: inline-flex;
align-items: center;
gap: 0.75rem;
color: var(--color-muted-strong);
font-size: 0.875rem;
font-weight: 500;
letter-spacing: 0.01em;
}
.sidebar-loading-text {
white-space: nowrap;
}
.status-banner {
align-items: center;
background-color: rgba(31, 41, 55, 0.9);
border-radius: 0.5rem;
color: var(--color-subtle);
display: flex;
gap: 0.75rem;
margin-bottom: 1rem;
padding: 1rem;
}
.status-banner .status-spinner {
animation: spin 1s linear infinite;
border: 0.125rem solid rgba(229, 231, 235, 0.35);
border-radius: 9999px;
border-top-color: var(--color-page-alt);
height: 1.25rem;
width: 1.25rem;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
/* Responsive Design */
@media (max-width: 640px) {
#videoList {
grid-template-columns: 1fr;
gap: 1rem;
}
.container {
padding: 0.75rem;
}
}
footer {
border-top: 0.0625rem solid rgba(255, 255, 255, 0.1);
margin-top: 4rem;
padding-top: 2rem;
}
footer a {
text-decoration: none;
}
footer a:hover {
text-decoration: underline;
}
/* Disclaimer Modal Styles */
#disclaimerModal {
position: fixed;
inset: 0;
background-color: rgb(0 0 0 / 0.9);
z-index: var(--z-modal-root);
/* remove display: none; */
display: flex;
flex-direction: column;
overflow-y: auto;
overscroll-behavior: contain;
}
#disclaimerModal > .absolute {
z-index: var(--z-modal-overlay);
}
#disclaimerModal .modal-container,
#disclaimerModal .modal-content {
z-index: var(--z-modal-content);
}
#disclaimerModal.hidden {
display: none !important;
}
#disclaimerModal .modal-content {
width: min(60rem, calc(100% - 2rem));
max-height: min(90vh, 51.25rem);
display: flex;
flex-direction: column;
background-color: var(--color-bg);
box-shadow: 0 1.5625rem 3.125rem -0.75rem rgb(0 0 0 / 0.45);
}
#disclaimerModal .modal-header {
min-height: 1.5rem;
}
#disclaimerModal .modal-body {
padding: 1.5rem;
flex: 1;
overflow-y: auto;
}
#disclaimerModal .modal-footer {
padding: 1.25rem 1.5rem 1.75rem;
background-color: var(--color-overlay);
border-top: 0.0625rem solid rgba(255, 255, 255, 0.1);
}
/* Responsive Adjustments for Disclaimer Modal */
@media (min-width: 768px) {
#disclaimerModal {
padding: 2rem;
align-items: center;
justify-content: center;
}
#disclaimerModal .modal-body {
padding: 2rem 2.5rem 1.5rem;
max-height: calc(90vh - 5rem);
}
#disclaimerModal .modal-footer {
padding: 1.5rem 2.5rem 2rem;
}
}
/* Mobile-specific styles for Disclaimer Modal */
@media (max-width: 767px) {
#disclaimerModal {
padding: 0;
}
#disclaimerModal .modal-content {
min-height: 100vh;
border-radius: 0;
max-height: none;
width: 100%;
}
#disclaimerModal .modal-body {
padding: 1.5rem 1.5rem 1rem;
max-height: none;
}
#disclaimerModal .modal-footer {
padding: 1rem 1.5rem 2rem;
}
}
/* Override default button styles for back button */
.back-button {
background: rgba(15, 23, 42, 0.75) !important;
padding: 0 !important;
color: rgb(226 232 240) !important;
transform: none !important;
border: 0.0625rem solid rgba(148, 163, 184, 0.4) !important;
box-shadow: none;
}
.back-button:hover {
background: rgba(51, 65, 85, 0.95) !important;
color: white !important;
border-color: rgba(148, 163, 184, 0.55) !important;
}
/* Modal Container */
.modal-container {
padding: 0;
}
@media (min-width: 768px) {
.modal-container {
padding: 2rem;
}
}
/* Video info cards */
.player-modal__info .bg-gray-800\/50 {
background-color: rgb(31 41 55 / 0.5);
backdrop-filter: blur(0.25rem);
}
/* Circular Icon Buttons */
.icon-button {
display: inline-flex;
align-items: center;
justify-content: center;
width: 2.5rem; /* 40px */
height: 2.5rem; /* 40px */
min-width: 2.5rem; /* Prevent flex shrink */
min-height: 2.5rem; /* Prevent flex shrink */
flex: 0 0 2.5rem;
line-height: 0;
background-color: var(--color-neutral-700); /* Gray 700 */
color: var(--color-white);
border: none;
border-radius: 9999px;
cursor: pointer;
transition:
background-color 0.2s,
box-shadow 0.2s;
}
.icon-button:hover {
background-color: var(--color-neutral-600); /* Gray 600 */
}
.icon-button:focus,
.icon-button:active {
outline: none;
box-shadow: 0 0 0 0.1875rem rgba(220, 38, 38, 0.6);
}
.icon-image {
width: 1.25rem; /* 20px */
height: 1.25rem; /* 20px */
pointer-events: none;
}
/* Ratio 16:9 Container */
.ratio-16-9 {
position: relative;
width: 100%;
padding-top: 56.25%;
background-color: var(--color-card);
}
.ratio-16-9 > img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
.video-card .ratio-16-9 {
border-radius: 0.75rem;
overflow: hidden;
}
.video-card .ratio-16-9 > [data-video-thumbnail] {
border-radius: inherit;
}
/* Sidebar default states */
#sidebarContainer {
position: relative;
overflow: visible;
}
#sidebar {
position: fixed;
top: 0;
left: 0;
width: var(--sidebar-width);
min-width: var(--sidebar-width);
height: 100vh;
height: 100dvh;
background-color: transparent;
color: var(--color-text);
overflow-x: visible;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
overscroll-behavior: contain;
scrollbar-gutter: stable both-edges;
transition:
width 200ms ease,
transform 0.3s ease,
box-shadow 0.3s ease;
/* Keep below modal stacks so dialogs overlay the drawer. */
z-index: var(--z-sidebar);
box-shadow: none;
}
#sidebar .sidebar__inner {
display: flex;
flex-direction: column;
height: 100%;
padding: 1.5rem 1.25rem 1.75rem;
position: relative;
width: 100%;
/*
Allow the gradient panel shadow to breathe outside the rail while a nested
scroll container handles overflow.
*/
overflow: visible;
}
/*
The scroll wrapper keeps nav + footer together while leaving overflow
visible so the gradient card shadow is never clipped.
*/
.sidebar-scroll {
display: flex;
flex-direction: column;
gap: 1.5rem;
width: 100%;
height: 100%;
min-height: 0;
flex: 1 1 auto;
overflow: visible;
}
.sidebar-panel {
background: linear-gradient(
160deg,
rgba(30, 41, 59, 0.92) 0%,
rgba(15, 23, 42, 0.92) 100%
);
border: 1px solid rgba(148, 163, 184, 0.18);
border-radius: 1rem;
box-shadow: 0 1.25rem 2.5rem rgba(15, 23, 42, 0.35);
padding: 1.25rem 1rem;
width: 100%;
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: 1.25rem;
}
.sidebar-panel--primary {
flex: 1 1 auto;
min-height: 0;
}
.sidebar-panel__header {
display: none;
justify-content: flex-end;
margin-bottom: 1rem;
}
.sidebar-collapse-toggle {
display: none;
align-items: center;
justify-content: center;
width: var(--sidebar-control-size);
height: var(--sidebar-control-size);
border-radius: 9999px;
border: 1px solid rgba(148, 163, 184, 0.2);
background: rgba(148, 163, 184, 0.08);
color: inherit;
cursor: pointer;
transition:
background 180ms ease,
color 180ms ease,
border-color 180ms ease;
flex-shrink: 0;
box-sizing: border-box;
}
.sidebar-collapse-toggle:hover {
background: rgba(148, 163, 184, 0.18);
border-color: rgba(148, 163, 184, 0.35);
color: var(--color-text);
}
.sidebar-collapse-toggle:focus-visible {
outline: 0.125rem solid var(--color-secondary);
outline-offset: 0.25rem;
}
.sidebar-collapse-toggle__icon {
width: 1.125rem;
height: 1.125rem;
transition: transform 200ms ease;
}
.sidebar-nav {
display: flex;
flex-direction: column;
gap: 0.25rem;
opacity: 1;
transition: opacity 160ms ease;
width: 100%;
}
.sidebar-nav-link {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.625rem 0.875rem;
min-height: var(--sidebar-control-size);
border-radius: 0.875rem;
font-size: 0.95rem;
font-weight: 600;
color: rgba(226, 232, 240, 0.94);
text-decoration: none;
transition:
background 160ms ease,
color 160ms ease,
transform 160ms ease;
width: 100%;
box-sizing: border-box;
}
.sidebar-nav-link:hover {
background: rgba(148, 163, 184, 0.18);
color: var(--color-text);
}
.sidebar-nav-link:focus-visible {
outline: 0.125rem solid var(--color-secondary);
outline-offset: 0.25rem;
}
.sidebar-nav-link--sub {
font-size: 0.85rem;
font-weight: 500;
color: rgba(226, 232, 240, 0.82);
padding-block: 0.55rem;
}
.sidebar-icon {
/* Lock icon sizing so the glyphs remain steady during collapse/expand. */
width: 1.5rem;
height: 1.5rem;
min-width: 1.5rem;
min-height: 1.5rem;
flex-shrink: 0;
display: block;
}
.sidebar-footer {
display: flex;
flex-direction: column;
gap: 0.75rem;
margin-top: auto;
position: relative;
z-index: 2;
}
.sidebar-dropup-trigger {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
width: 100%;
padding: 0.75rem 0.875rem;
min-height: var(--sidebar-control-size);
border-radius: 0.875rem;
border: 1px solid rgba(148, 163, 184, 0.18);
background: rgba(30, 41, 59, 0.85);
color: rgba(226, 232, 240, 0.9);
font-size: 0.9rem;
font-weight: 600;
cursor: pointer;
transition:
background 160ms ease,
border-color 160ms ease,
color 160ms ease;
box-sizing: border-box;
flex-shrink: 0;
}
.sidebar-dropup-trigger:hover {
background: rgba(51, 65, 85, 0.95);
border-color: rgba(148, 163, 184, 0.32);
color: var(--color-text);
}
.sidebar-dropup-trigger:focus-visible {
outline: 0.125rem solid var(--color-secondary);
outline-offset: 0.25rem;
}
.sidebar-dropup-trigger__glyph {
width: 1.25rem;
height: 1.25rem;
opacity: 0.9;
}
.sidebar-dropup-trigger__chevron {
width: 1.125rem;
height: 1.125rem;
transition: transform 180ms ease;
}
.sidebar-dropup-trigger__chevron.is-rotated,
.sidebar-dropup-trigger[aria-expanded="true"] .sidebar-dropup-trigger__chevron {
transform: rotate(180deg);
}
.sidebar-dropup-panel {
background: rgba(15, 23, 42, 0.88);
border: 1px solid rgba(148, 163, 184, 0.18);
border-radius: 0.875rem;
box-shadow: 0 1.5rem 3rem rgba(15, 23, 42, 0.4);
padding: 0.75rem 0.5rem;
position: absolute;
left: 0;
right: 0;
bottom: calc(100% + 0.75rem);
opacity: 0;
visibility: hidden;
pointer-events: none;
transform: translateY(0.5rem);
transition:
opacity 160ms ease,
transform 160ms ease,
visibility 160ms ease;
z-index: 5;
}
.sidebar-dropup-panel[data-state="expanded"] {
opacity: 1;
visibility: visible;
pointer-events: auto;
transform: translateY(0);
}
.sidebar-dropup-panel__inner {
display: flex;
flex-direction: column;
gap: 0.25rem;
}
#sidebar[data-footer-state="expanded"] .sidebar-nav {
opacity: 0;
pointer-events: none;
}
#sidebar[data-footer-state="collapsed"] .sidebar-nav {
opacity: 1;
}
#sidebar.sidebar-open {
box-shadow: 0 0.75rem 1.5rem rgba(15, 23, 42, 0.45);
}
#sidebarOverlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(15, 23, 42, 0.55);
opacity: 0;
pointer-events: none;
transition: opacity 0.3s ease;
/* Sits above page chrome but below modals and the sidebar itself. */
z-index: var(--z-sidebar-overlay);
}
body.sidebar-open #sidebarOverlay {
opacity: 1;
pointer-events: auto;
}
/* Mobile (max-width: 767px): Hide sidebar by default */
@media (max-width: 767px) {
#sidebar {
transform: translate3d(-100%, 0, 0);
width: min(var(--sidebar-width-expanded), calc(100vw - 3rem));
}
/* When the sidebar-open class is added, slide the sidebar in */
#sidebar.sidebar-open {
transform: translate3d(0, 0, 0);
}
body.sidebar-open {
overflow: hidden;
}
}
/* Desktop (min-width: 768px): Always show the sidebar */
@media (min-width: 768px) {
.sidebar-panel__header {
display: flex;
}
.sidebar-collapse-toggle {
display: inline-flex;
}
#sidebar {
transform: translateX(0) !important;
width: var(--sidebar-width);
overflow: visible;
}
#sidebar.sidebar-open {
box-shadow: none;
}
#sidebarOverlay {
display: none;
}
#app {
box-sizing: border-box;
transition:
margin-left 200ms ease,
width 200ms ease,
max-width 200ms ease;
}
#app[data-sidebar-state="collapsed"] {
margin-left: calc(
var(--sidebar-width-collapsed) + var(--sidebar-content-gap)
);
width: calc(
100vw - var(--sidebar-width-collapsed) - var(--sidebar-content-gap)
);
max-width: calc(
100vw - var(--sidebar-width-collapsed) - var(--sidebar-content-gap)
);
}
#app[data-sidebar-state="expanded"] {
margin-left: calc(
var(--sidebar-width-expanded) + var(--sidebar-content-gap)
);
width: calc(
100vw - var(--sidebar-width-expanded) - var(--sidebar-content-gap)
);
max-width: calc(
100vw - var(--sidebar-width-expanded) - var(--sidebar-content-gap)
);
}
body.sidebar-collapsed,
#sidebar.sidebar-collapsed {
--sidebar-width: var(--sidebar-width-collapsed);
}
body.sidebar-expanded,
#sidebar.sidebar-expanded {
--sidebar-width: var(--sidebar-width-expanded);
}
body.sidebar-collapsed #sidebar .sidebar-nav-link,
#sidebar.sidebar-collapsed .sidebar-nav-link {
/*
Keep icons centered in the collapsed rail without resizing them.
*/
justify-content: center;
width: var(--sidebar-control-size);
padding: 0;
margin-inline: auto;
border-radius: 9999px;
}
body.sidebar-expanded #sidebar .sidebar-nav-link,
#sidebar.sidebar-expanded .sidebar-nav-link {
justify-content: flex-start;
}
body.sidebar-collapsed #sidebar .sidebar-text,
#sidebar.sidebar-collapsed .sidebar-text {
display: none;
}
body.sidebar-collapsed #sidebar .sidebar__inner,
#sidebar.sidebar-collapsed .sidebar__inner {
/*
Keep enough breathing room for the panel shadow while widening the
interior track so var(--sidebar-control-size) controls stay perfectly
round.
*/
padding-inline: 0.25rem;
}
body.sidebar-collapsed #sidebar .sidebar-panel,
#sidebar.sidebar-collapsed .sidebar-panel {
/*
Mirror the outer adjustment so the gradient card retains its rounded
edges without squeezing the icon rail or clipping the control diameter.
*/
padding-inline: 0.375rem;
}
body.sidebar-collapsed #sidebar .sidebar-panel__header,
#sidebar.sidebar-collapsed .sidebar-panel__header {
justify-content: center;
}
body.sidebar-collapsed #sidebar .sidebar-collapse-toggle,
#sidebar.sidebar-collapsed .sidebar-collapse-toggle {
margin-inline: auto;
}
#sidebar .sidebar-toggle-icon--rotated {
transform: rotate(180deg);
}
body.sidebar-collapsed #sidebar .sidebar-toggle-icon--rotated,
#sidebar.sidebar-collapsed .sidebar-toggle-icon--rotated {
transform: rotate(0deg);
}
body.sidebar-collapsed #sidebar .sidebar-dropup-trigger {
/*
Align the "More" control with the centered icon rail while keeping its
padding consistent so the glyph does not scale between states.
*/
justify-content: center;
width: var(--sidebar-control-size);
height: var(--sidebar-control-size);
padding: 0;
margin-inline: auto;
border-radius: 9999px;
}
body.sidebar-collapsed #sidebar .sidebar-dropup-trigger .sidebar-text,
#sidebar.sidebar-collapsed .sidebar-dropup-trigger .sidebar-text {
display: none;
}
body.sidebar-collapsed #sidebar .sidebar-dropup-trigger__chevron {
display: none;
}
body.sidebar-collapsed #sidebar .sidebar-dropup-trigger__glyph,
#sidebar.sidebar-collapsed .sidebar-dropup-trigger__glyph {
margin: 0;
}
}
.edit-field-button {
align-items: center;
background-color: var(--color-panel);
border-radius: 0.5rem;
color: var(--color-subtle-text);
display: inline-flex;
font-size: 0.75rem;
font-weight: 600;
justify-content: center;
padding: 0.5rem 0.85rem;
transition:
background-color 0.2s ease,
color 0.2s ease,
transform 0.2s ease;
}
.edit-field-button:hover,
.edit-field-button:focus {
background-color: var(--color-panel-hover);
color: var(--color-text);
transform: translateY(-0.0625rem);
}
.locked-input {
background-color: rgba(31, 41, 55, 0.75) !important;
border-color: rgba(55, 65, 81, 0.9) !important;
color: var(--color-muted) !important;
cursor: default;
}
.locked-input:focus {
outline: none;
box-shadow: none;
}