mirror of
https://github.com/coollabsio/coolify.git
synced 2026-10-28 17:44:36 +08:00
6865 lines
218 KiB
CSS
6865 lines
218 KiB
CSS
@import "./fonts.css" layer(base);
|
||
|
||
@import "tailwindcss";
|
||
@import "tw-animate-css";
|
||
|
||
@import "./utilities.css";
|
||
|
||
@plugin 'tailwind-scrollbar';
|
||
@plugin '@tailwindcss/typography';
|
||
@plugin '@tailwindcss/forms';
|
||
|
||
|
||
@source '../../storage/framework/views/*.php';
|
||
|
||
/* A custom theme can give the sidebar the other scheme than the page
|
||
(data-sidebar-scheme on <html>). dark: utilities follow the sidebar there. */
|
||
@custom-variant dark (&:where(.dark, .dark *, [data-sidebar-scheme="dark"] :is(.app-sidebar, .app-topbar-brand, .mobile-sidebar-sheet [role="dialog"]), [data-sidebar-scheme="dark"] :is(.app-sidebar, .app-topbar-brand, .mobile-sidebar-sheet [role="dialog"]) *):not(:where([data-sidebar-scheme="light"] :is(.app-sidebar, .app-topbar-brand, .mobile-sidebar-sheet [role="dialog"]), [data-sidebar-scheme="light"] :is(.app-sidebar, .app-topbar-brand, .mobile-sidebar-sheet [role="dialog"]) *)));
|
||
|
||
@theme {
|
||
--font-sans: 'Geist Sans', Inter, sans-serif;
|
||
--font-mono: 'Geist Mono', 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
|
||
--font-geist-sans: 'Geist Sans', Inter, sans-serif;
|
||
--font-logs: 'Geist Mono', 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
|
||
|
||
--color-base: oklch(17.3% 0 0);
|
||
--color-warning: oklch(88.13% 0.1507 91.7);
|
||
--color-warning-50: oklch(98.73% 0.0262 102.21);
|
||
--color-warning-100: oklch(97.29% 0.0693 103.19);
|
||
--color-warning-200: oklch(94.51% 0.1243 101.54);
|
||
--color-warning-300: oklch(90.52% 0.1657 98.11);
|
||
--color-warning-400: oklch(88.13% 0.1507 91.7);
|
||
--color-warning-500: oklch(86.06% 0.1731 91.94);
|
||
--color-warning-600: oklch(68.06% 0.1423 75.83);
|
||
--color-warning-700: oklch(55.38% 0.1207 66.44);
|
||
--color-warning-800: oklch(47.62% 0.1034 61.91);
|
||
--color-warning-900: oklch(42.1% 0.0897 57.71);
|
||
--color-success: oklch(72.27% 0.192 149.58);
|
||
--color-error: oklch(57.71% 0.2152 27.33);
|
||
--color-coollabs-50: oklch(96.33% 0.0206 301.15);
|
||
--color-coollabs: oklch(49.65% 0.2709 289.33);
|
||
--color-coollabs-100: oklch(52.34% 0.287 289.16);
|
||
--color-coollabs-200: oklch(43.76% 0.2369 289.82);
|
||
--color-coollabs-300: oklch(38.04% 0.2031 290.47);
|
||
/* coolLabs brand purple. Primary actions, focus, and active navigation
|
||
use these in both themes. Warning yellow is not an accent. */
|
||
--color-brand: #6b16ed;
|
||
--color-brand-hover: #7317ff;
|
||
--color-brand-press: #5a12c7;
|
||
--color-brand-depth: #4a0fa3;
|
||
--color-brand-light: #a391ff;
|
||
--color-coolgray-100: oklch(20.9% 0 0);
|
||
--color-coolgray-200: oklch(24.35% 0 0);
|
||
--color-coolgray-300: oklch(26.03% 0 0);
|
||
--color-coolgray-400: oklch(27.68% 0 0);
|
||
--color-coolgray-500: oklch(31.71% 0 0);
|
||
|
||
/* Graphite design language (ported from ref/frontend). Layered neutral
|
||
surfaces + translucent hairlines. See DESIGN.md. */
|
||
/* Content canvas: the darkest shell layer (sRGB ~10). Hex, not oklch:
|
||
oklch lightness compresses to near-black below ~15%, so oklch(7.5%)
|
||
renders as sRGB 1 with no visible step. */
|
||
--color-app: oklch(14.48% 0 0);
|
||
/* Sidebar + topbar chrome (sRGB ~20): a clear step lighter than the content
|
||
canvas so the shell chrome separates from the content area. */
|
||
--color-panel: oklch(19.13% 0 0);
|
||
/* Pure neutral (r=g=b). Were cool-tinted (#161618/#1c1c1e/#26262a, b>r),
|
||
which clashed with the neutral panel ladder. */
|
||
--color-surface: oklch(20.02% 0 0);
|
||
--color-raised: oklch(22.64% 0 0);
|
||
--color-selected: oklch(26.86% 0 0);
|
||
--color-fg: oklch(96.12% 0 0);
|
||
--color-fg-dim: oklch(76.99% 0 0);
|
||
/* Tertiary text. Raised from #6e6e74 (3.78:1 on base, failed WCAG AA)
|
||
to #7e7e84 so 13-14px text clears 4.5:1 on the dark surface ladder. */
|
||
--color-fg-faint: oklch(59.31% 0 0);
|
||
--color-accent: oklch(49.65% 0.2709 289.33);
|
||
--color-accent-foreground: oklch(100.0% 0 0);
|
||
--color-hairline: oklch(100.0% 0 0 / 0.08);
|
||
--color-nav-text: oklch(43.86% 0 0);
|
||
--color-nav-muted: oklch(51.03% 0 0);
|
||
--color-nav-active: oklch(20.46% 0 0);
|
||
--color-log: oklch(15.91% 0 0);
|
||
|
||
--shadow-modal: 0 24px 64px rgba(0, 0, 0, 0.55), 0 4px 16px rgba(0, 0, 0, 0.4);
|
||
/* One restrained lift shared by every dropdown/menu/listbox panel and the
|
||
command palette. Lighter and consistent, replacing the old scattered
|
||
0.45 / 0.35 / shadow-lg / shadow-sm mix. Modals keep --shadow-modal. */
|
||
--shadow-dropdown: 0 4px 12px rgb(0 0 0 / 0.12), 0 2px 4px rgb(0 0 0 / 0.08);
|
||
|
||
/* Shared motion curves (stronger than the built-in CSS easings). Use these
|
||
for consistency: --ease-out for UI enter/press, --ease-drawer for
|
||
drawers/sheets, --ease-in-out for on-screen movement. */
|
||
--ease-out: cubic-bezier(0.23, 1, 0.32, 1);
|
||
--ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
|
||
--ease-drawer: cubic-bezier(0.32, 0.72, 0, 1);
|
||
}
|
||
|
||
/* Standard buttons have a compact physical edge. Hover lifts the face by 1px;
|
||
pressing nudges it down while the depth disappears. */
|
||
.button:not(:disabled) {
|
||
--button-depth-color: rgb(0 0 0 / 0.22);
|
||
--button-depth: 0 2px 0 var(--button-depth-color);
|
||
--button-depth-hover: 0 3px 0 var(--button-depth-color);
|
||
|
||
box-shadow: var(--button-depth);
|
||
}
|
||
|
||
.button.button-highlighted:not(:disabled),
|
||
.button[isHighlighted]:not(:disabled) {
|
||
--button-depth-color: var(--color-coollabs-300);
|
||
}
|
||
|
||
.dark .button:not(.button-highlighted):not(.button-error):not([isHighlighted]):not(:disabled) {
|
||
--button-depth-color: color-mix(in srgb, var(--color-brand) 35%, transparent);
|
||
background-color: color-mix(in srgb, var(--color-brand) 14%, transparent);
|
||
border-color: color-mix(in srgb, var(--color-brand-light) 24%, transparent);
|
||
color: oklch(from var(--color-brand-light) 93.5% 0.03 h);
|
||
}
|
||
|
||
.dark .button:not(.button-highlighted):not(.button-error):not([isHighlighted]):not(:disabled):hover {
|
||
background-color: color-mix(in srgb, var(--color-brand) 18%, transparent);
|
||
border-color: color-mix(in srgb, var(--color-brand-light) 34%, transparent);
|
||
}
|
||
|
||
.dark .button:not(.button-highlighted):not(.button-error):not([isHighlighted]):not(:disabled) svg {
|
||
color: var(--color-brand-light);
|
||
opacity: 1;
|
||
}
|
||
|
||
.dark .button.button-highlighted:not(:disabled),
|
||
.dark .button[isHighlighted]:not(:disabled) {
|
||
background: var(--color-brand);
|
||
border-color: transparent;
|
||
color: var(--color-brand-foreground);
|
||
--button-depth-color: var(--color-brand-depth);
|
||
}
|
||
|
||
.dark .button.button-highlighted:not(:disabled):hover,
|
||
.dark .button[isHighlighted]:not(:disabled):hover {
|
||
background: var(--color-brand-hover);
|
||
}
|
||
|
||
.button.button-error:not(:disabled) {
|
||
--button-depth-color: var(--color-red-300);
|
||
}
|
||
|
||
.dark .button.button-error:not(:disabled) {
|
||
--button-depth-color: var(--color-red-800);
|
||
}
|
||
|
||
/* Keep the shared focus indicator when the depth shadow owns box-shadow. */
|
||
.button:not(:disabled):focus-visible {
|
||
box-shadow: var(--button-depth), 0 0 0 1px var(--color-accent);
|
||
}
|
||
|
||
.button:not(:disabled):hover {
|
||
transform: translateY(-1px);
|
||
box-shadow: var(--button-depth-hover);
|
||
}
|
||
|
||
.button:not(:disabled):hover:focus-visible {
|
||
box-shadow: var(--button-depth-hover), 0 0 0 1px var(--color-accent);
|
||
}
|
||
|
||
/* Destructive buttons keep a focus indicator in their own red instead of the accent. */
|
||
.button.button-error:not(:disabled):focus-visible {
|
||
box-shadow: var(--button-depth), 0 0 0 1px var(--color-red-500);
|
||
}
|
||
|
||
.button.button-error:not(:disabled):hover:focus-visible {
|
||
box-shadow: var(--button-depth-hover), 0 0 0 1px var(--color-red-500);
|
||
}
|
||
|
||
/* Highlighted buttons share the accent hue, so a flush ring reads as a stray
|
||
border. Offset the indicator with an outline to leave a clear gap. */
|
||
.button.button-highlighted:not(:disabled):focus-visible,
|
||
.button[isHighlighted]:not(:disabled):focus-visible {
|
||
box-shadow: var(--button-depth);
|
||
outline: 1px solid var(--color-accent);
|
||
outline-offset: 2px;
|
||
}
|
||
|
||
.button.button-highlighted:not(:disabled):hover:focus-visible,
|
||
.button[isHighlighted]:not(:disabled):hover:focus-visible {
|
||
box-shadow: var(--button-depth-hover);
|
||
}
|
||
|
||
/* Other button-like controls retain the lighter scale feedback. */
|
||
.button,
|
||
.icon-button,
|
||
.app-tab,
|
||
.split-action-main,
|
||
.split-action-caret {
|
||
transition-property: color, background-color, border-color, box-shadow, transform;
|
||
transition-timing-function: var(--ease-out);
|
||
transition-duration: 120ms;
|
||
}
|
||
|
||
.icon-button:not(:disabled):active,
|
||
.app-tab:active {
|
||
transform: scale(0.97);
|
||
}
|
||
|
||
.button,
|
||
.split-action-main,
|
||
.split-action-caret {
|
||
transition-duration: 120ms, 120ms, 120ms, 80ms, 80ms;
|
||
}
|
||
|
||
.button:not(:disabled):active,
|
||
.split-action-main:not(:disabled):active,
|
||
.split-action-caret:not(:disabled):active {
|
||
transform: translateY(2px);
|
||
box-shadow: none;
|
||
}
|
||
|
||
/* Appearance → Style → Flat (data-nav-items="flat") removes the raised edge
|
||
everywhere, not only in the sidebar: the depth color goes transparent (the
|
||
!important beats the theme overrides of the variable) and the press uses
|
||
the scale feedback of .icon-button instead of the lift and sink. */
|
||
html[data-nav-items="flat"] :is(.button, .split-action-main, .split-action-caret, .toast-icon) {
|
||
--button-depth-color: transparent !important;
|
||
}
|
||
|
||
html[data-nav-items="flat"] :is(.button, .split-action-main, .split-action-caret):not(:disabled):hover {
|
||
transform: none;
|
||
}
|
||
|
||
html[data-nav-items="flat"] :is(.button, .split-action-main, .split-action-caret):not(:disabled):active {
|
||
transform: scale(0.97);
|
||
}
|
||
|
||
/* Focus border/ring on any form control should ease, not snap. The accent
|
||
focus indicator is a border-color and/or box-shadow (ring) change, which the
|
||
utilities' `transition-colors` did not fully animate. Target the elements
|
||
themselves (covers bespoke search fields too) plus the div-based controls. */
|
||
input,
|
||
textarea,
|
||
select,
|
||
.listbox-trigger,
|
||
.chip-input {
|
||
transition-property: color, background-color, border-color, box-shadow;
|
||
transition-timing-function: var(--ease-out);
|
||
transition-duration: 120ms;
|
||
}
|
||
|
||
/*
|
||
Traffic-analytics chart tokens (light defaults; dark overrides below).
|
||
One source of truth shared by ApexCharts donuts, the geo choropleth SVG,
|
||
and proportional bars — JS reads them at runtime via getComputedStyle.
|
||
Palette validated with the dataviz skill; contrast ratios are recorded in
|
||
the PR description. See resources/views/livewire/traffic/_geo.blade.php.
|
||
*/
|
||
:root {
|
||
/* Categorical HTTP status palette (labelled 2xx/3xx/4xx/5xx in every legend). */
|
||
--chart-status-2xx: #15803d;
|
||
--chart-status-3xx: #2563eb;
|
||
--chart-status-4xx: #d97706;
|
||
--chart-status-5xx: #dc2626;
|
||
|
||
/* KPI sparkline accent for Bandwidth (violet — distinct from the status hues). */
|
||
--chart-spark-bandwidth: #7c3aed;
|
||
|
||
/* Sequential 5-step geo ramp (low -> high traffic) + neutral empty. */
|
||
--chart-geo-1: #3b82f6;
|
||
--chart-geo-2: #2563eb;
|
||
--chart-geo-3: #1d4ed8;
|
||
--chart-geo-4: #1e40af;
|
||
--chart-geo-5: #172554;
|
||
--chart-geo-empty: #e5e7eb;
|
||
--chart-geo-stroke: #ffffff;
|
||
}
|
||
|
||
.dark {
|
||
--chart-status-2xx: #22c55e;
|
||
--chart-status-3xx: #3b82f6;
|
||
--chart-status-4xx: #f59e0b;
|
||
--chart-status-5xx: #ef4444;
|
||
|
||
--chart-spark-bandwidth: #a78bfa;
|
||
|
||
--chart-geo-1: #2563eb;
|
||
--chart-geo-2: #3b82f6;
|
||
--chart-geo-3: #60a5fa;
|
||
--chart-geo-4: #93c5fd;
|
||
--chart-geo-5: #bfdbfe;
|
||
--chart-geo-empty: #262626;
|
||
--chart-geo-stroke: #101010;
|
||
}
|
||
|
||
/*
|
||
The default border color has changed to `currentcolor` in Tailwind CSS v4,
|
||
so we've added these compatibility styles to make sure everything still
|
||
looks the same as it did with Tailwind CSS v3.
|
||
|
||
If we ever want to remove these styles, we need to add an explicit border
|
||
color utility to any element that depends on these defaults.
|
||
*/
|
||
|
||
@layer components {
|
||
.terminal-mobile-key {
|
||
@apply min-h-8 shrink-0 rounded-full border bg-transparent px-3 py-1 text-sm font-medium text-neutral-300 active:text-white;
|
||
border-color: color-mix(in srgb, var(--terminal-scrollbar, #fff) 24%, transparent);
|
||
}
|
||
|
||
.terminal-key-row {
|
||
border: 1px solid color-mix(in srgb, var(--terminal-scrollbar, #fff) 22%, transparent);
|
||
background: transparent;
|
||
}
|
||
|
||
/* Active state is a solid fill only (no accent rail / border). */
|
||
.menu-item-active .menu-item-icon,
|
||
.menu-subitem-active .menu-item-icon {
|
||
opacity: 1;
|
||
}
|
||
|
||
/* Kill any legacy accent wash or rail; fill is solid via menu-item-active utility. */
|
||
.menu-item-active,
|
||
.menu-subitem-active,
|
||
.dark .menu-item-active,
|
||
.dark .menu-subitem-active {
|
||
background-image: none;
|
||
}
|
||
|
||
.menu-item-active::before,
|
||
.menu-subitem-active::before {
|
||
content: none;
|
||
display: none;
|
||
}
|
||
|
||
/* vertical connector line for a nested nav group */
|
||
.nav-children {
|
||
position: relative;
|
||
margin-left: 1.1rem;
|
||
padding-left: 0.25rem;
|
||
border-left: 1px solid var(--color-hairline);
|
||
}
|
||
}
|
||
|
||
@layer base {
|
||
|
||
html {
|
||
color-scheme: light;
|
||
}
|
||
|
||
html.dark {
|
||
color-scheme: dark;
|
||
}
|
||
|
||
*,
|
||
::after,
|
||
::before,
|
||
::backdrop,
|
||
::file-selector-button {
|
||
border-color: var(--color-coolgray-200, currentcolor);
|
||
}
|
||
}
|
||
|
||
@keyframes lds-heart {
|
||
0% {
|
||
transform: scale(1);
|
||
}
|
||
|
||
5% {
|
||
transform: scale(1.2);
|
||
}
|
||
|
||
39% {
|
||
transform: scale(0.85);
|
||
}
|
||
|
||
45% {
|
||
transform: scale(1);
|
||
}
|
||
|
||
60% {
|
||
transform: scale(0.95);
|
||
}
|
||
|
||
100% {
|
||
transform: scale(0.9);
|
||
}
|
||
}
|
||
|
||
@keyframes coolbox-border-track {
|
||
0% {
|
||
background-position: 0 0, 100% 0, 100% 100%, 0 100%;
|
||
background-size: 35% 2px, 0 0, 0 0, 0 0;
|
||
}
|
||
|
||
33% {
|
||
background-position: 100% 0, 100% 0, 100% 100%, 0 100%;
|
||
background-size: 35% 2px, 0 0, 0 0, 0 0;
|
||
}
|
||
|
||
34% {
|
||
background-position: 100% 0, 100% 0, 100% 100%, 0 100%;
|
||
background-size: 0 0, 2px 35%, 0 0, 0 0;
|
||
}
|
||
|
||
50% {
|
||
background-position: 100% 0, 100% 100%, 100% 100%, 0 100%;
|
||
background-size: 0 0, 2px 35%, 0 0, 0 0;
|
||
}
|
||
|
||
51% {
|
||
background-position: 0 0, 100% 0, 100% 100%, 0 100%;
|
||
background-size: 0 0, 0 0, 35% 2px, 0 0;
|
||
}
|
||
|
||
84% {
|
||
background-position: 0 0, 100% 0, 0 100%, 0 100%;
|
||
background-size: 0 0, 0 0, 35% 2px, 0 0;
|
||
}
|
||
|
||
85% {
|
||
background-position: 0 0, 100% 0, 100% 100%, 0 100%;
|
||
background-size: 0 0, 0 0, 0 0, 2px 35%;
|
||
}
|
||
|
||
100% {
|
||
background-position: 0 0, 100% 0, 100% 100%, 0 0;
|
||
background-size: 0 0, 0 0, 0 0, 2px 35%;
|
||
}
|
||
}
|
||
|
||
@layer components {
|
||
.coolbox-loading {
|
||
@apply overflow-hidden border-transparent;
|
||
isolation: isolate;
|
||
}
|
||
|
||
.coolbox-loading::before {
|
||
content: "";
|
||
position: absolute;
|
||
inset: 0;
|
||
border-radius: inherit;
|
||
--coolbox-loading-color: var(--color-coollabs);
|
||
background:
|
||
linear-gradient(var(--coolbox-loading-color), var(--coolbox-loading-color)),
|
||
linear-gradient(var(--coolbox-loading-color), var(--coolbox-loading-color)),
|
||
linear-gradient(var(--coolbox-loading-color), var(--coolbox-loading-color)),
|
||
linear-gradient(var(--coolbox-loading-color), var(--coolbox-loading-color));
|
||
background-repeat: no-repeat;
|
||
animation: coolbox-border-track 2400ms linear infinite;
|
||
pointer-events: none;
|
||
z-index: 0;
|
||
}
|
||
|
||
.coolbox-loading::after {
|
||
content: "";
|
||
position: absolute;
|
||
inset: 2px;
|
||
border-radius: calc(0.25rem - 2px);
|
||
background: white;
|
||
pointer-events: none;
|
||
z-index: 1;
|
||
}
|
||
|
||
.dark .coolbox-loading::before {
|
||
--coolbox-loading-color: var(--color-brand-light);
|
||
}
|
||
|
||
.dark .coolbox-loading::after {
|
||
background: var(--color-coolgray-100);
|
||
}
|
||
|
||
.coolbox-loading > * {
|
||
position: relative;
|
||
z-index: 2;
|
||
}
|
||
|
||
@media (max-width: 767px) {
|
||
:root input,
|
||
:root textarea,
|
||
:root select {
|
||
font-size: 16px !important;
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Base styles
|
||
*/
|
||
html,
|
||
body {
|
||
@apply w-full min-h-full bg-neutral-50 dark:bg-app dark:text-fg-dim;
|
||
}
|
||
|
||
body {
|
||
/* overflow-x: clip (not hidden) — hidden creates a scroll containment that
|
||
breaks position:sticky on settings sidebars and other in-page pins. */
|
||
min-height: 100dvh;
|
||
@apply text-sm font-sans antialiased scrollbar overflow-x-clip;
|
||
}
|
||
|
||
.coolify-monaco-editor {
|
||
@apply min-w-0 w-full;
|
||
overflow: hidden;
|
||
border-radius: 10px;
|
||
border: 1px solid var(--coollabs-line, #e5e5e5);
|
||
}
|
||
|
||
.coolify-monaco-editor .monaco-editor,
|
||
.coolify-monaco-editor .overflow-guard {
|
||
max-width: 100%;
|
||
}
|
||
|
||
option {
|
||
@apply dark:text-white dark:bg-coolgray-100;
|
||
}
|
||
|
||
.button.button-error {
|
||
@apply text-red-800 dark:text-red-300 bg-red-50 dark:bg-red-900/30 border-red-300 dark:border-red-800;
|
||
}
|
||
|
||
.button.button-error:disabled {
|
||
@apply text-red-800 dark:text-red-300 bg-red-50 dark:bg-red-900/30 border-red-300 dark:border-red-800;
|
||
}
|
||
|
||
.button-error:not(:disabled) {
|
||
@apply hover:bg-red-100 hover:text-red-900 dark:hover:bg-red-700 dark:hover:text-white;
|
||
}
|
||
|
||
button[isHighlighted]:not(:disabled) {
|
||
@apply button-highlighted;
|
||
}
|
||
|
||
h1 {
|
||
@apply text-[24px] leading-7 font-semibold tracking-tight dark:text-white;
|
||
}
|
||
|
||
h2 {
|
||
@apply text-xl font-bold dark:text-white;
|
||
}
|
||
|
||
h3 {
|
||
@apply text-lg font-bold dark:text-white;
|
||
}
|
||
|
||
h4 {
|
||
@apply text-base font-bold dark:text-white;
|
||
}
|
||
|
||
a {
|
||
@apply hover:text-black dark:hover:text-white;
|
||
}
|
||
|
||
button:focus-visible,
|
||
a:focus-visible {
|
||
@apply outline-none ring-2 ring-coollabs dark:ring-1 dark:ring-brand-light ring-offset-2 dark:ring-offset-coolgray-100;
|
||
}
|
||
|
||
label {
|
||
@apply dark:text-neutral-400;
|
||
}
|
||
|
||
table {
|
||
@apply min-w-full divide-y dark:divide-coolgray-200 divide-neutral-300;
|
||
}
|
||
|
||
thead {
|
||
@apply uppercase;
|
||
}
|
||
|
||
tbody {
|
||
@apply divide-y dark:divide-coolgray-200 divide-neutral-300;
|
||
}
|
||
|
||
tr {
|
||
@apply text-black dark:text-neutral-400 dark:hover:bg-coolgray-300 hover:bg-neutral-100;
|
||
}
|
||
|
||
tr th {
|
||
@apply px-3 py-3.5 text-left text-neutral-500 dark:text-fg-faint;
|
||
}
|
||
|
||
tr th:first-child {
|
||
@apply py-3.5 pr-3 pl-4 sm:pl-6;
|
||
}
|
||
|
||
tr td {
|
||
@apply px-3 py-4 whitespace-nowrap;
|
||
}
|
||
|
||
tr td:first-child {
|
||
@apply pr-3 pl-4 font-bold sm:pl-6;
|
||
}
|
||
|
||
/*
|
||
* Utility classes
|
||
*/
|
||
/* Reserve space for the visibility toggle. Use a class (not [type=password]) so padding
|
||
remains when Alpine reveals the value as type=text, and so settings-workspace input
|
||
padding-right overrides cannot collapse the field into the eye icon. */
|
||
.input.input-with-password-toggle,
|
||
.input[type="password"] {
|
||
padding-right: 2.5rem;
|
||
}
|
||
|
||
/* Same durable clearance for copy-affordance inputs (utility pr-* loses to settings CSS). */
|
||
.input.input-with-copy-button {
|
||
padding-right: 2.5rem;
|
||
}
|
||
|
||
/* Number fields use the brand −/+ stepper (components/forms/input) instead of the native spinner. */
|
||
.input.input-with-stepper {
|
||
padding-right: 4.25rem;
|
||
appearance: textfield;
|
||
-moz-appearance: textfield;
|
||
}
|
||
|
||
.input.input-with-stepper::-webkit-outer-spin-button,
|
||
.input.input-with-stepper::-webkit-inner-spin-button {
|
||
margin: 0;
|
||
-webkit-appearance: none;
|
||
}
|
||
|
||
.number-stepper {
|
||
@apply absolute inset-y-px right-px flex;
|
||
}
|
||
|
||
.number-stepper button {
|
||
@apply flex w-8 cursor-pointer items-center justify-center border-l border-neutral-200 text-neutral-500 transition-colors last:rounded-r-[5px] hover:bg-neutral-100 hover:text-black active:bg-neutral-200 disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:bg-transparent dark:border-white/[0.08] dark:text-fg-dim dark:hover:bg-white/[0.06] dark:hover:text-white dark:active:bg-white/[0.1];
|
||
}
|
||
|
||
.number-stepper button svg {
|
||
@apply transition-transform duration-150 ease-out;
|
||
}
|
||
|
||
.number-stepper button:active:not(:disabled) svg {
|
||
@apply scale-75;
|
||
}
|
||
|
||
/* Room for both the peek eye and the copy button on maskable read-only fields. */
|
||
.input.input-with-copy-and-peek {
|
||
padding-right: 4.25rem;
|
||
}
|
||
|
||
.lds-heart {
|
||
animation: lds-heart 1.2s infinite cubic-bezier(0.215, 0.61, 0.355, 1);
|
||
}
|
||
|
||
/* Loading feedback uses the brand-light accent in dark mode. */
|
||
.dark .animate-spin {
|
||
color: var(--color-brand-light) !important;
|
||
}
|
||
|
||
.dark #nprogress .bar {
|
||
background: var(--color-brand-light) !important;
|
||
}
|
||
|
||
.dark #nprogress .peg {
|
||
box-shadow:
|
||
0 0 10px var(--color-brand-light),
|
||
0 0 5px var(--color-brand-light) !important;
|
||
}
|
||
|
||
.dark #nprogress .spinner-icon {
|
||
border-top-color: var(--color-brand-light) !important;
|
||
border-left-color: var(--color-brand-light) !important;
|
||
}
|
||
|
||
html[data-theme="custom"] .loading-indicator,
|
||
html[data-theme="custom"] .animate-spin {
|
||
color: var(--theme-bright-color) !important;
|
||
}
|
||
|
||
/* Opt out of the brand spinner when the surrounding surface is a selected/neutral control
|
||
or a highlighted button, whose accent surface would camouflage a brand-colored spinner. */
|
||
.dark .animate-spin.spinner-current,
|
||
html[data-theme="custom"] .animate-spin.spinner-current,
|
||
html[data-theme="custom"] .button-highlighted .animate-spin,
|
||
html[data-theme="custom"] button[isHighlighted] .animate-spin {
|
||
color: inherit !important;
|
||
}
|
||
|
||
html[data-theme="custom"] #nprogress .bar {
|
||
background: var(--theme-bright-color) !important;
|
||
}
|
||
|
||
html[data-theme="custom"] #nprogress .peg {
|
||
box-shadow:
|
||
0 0 10px var(--theme-bright-color),
|
||
0 0 5px var(--theme-bright-color) !important;
|
||
}
|
||
|
||
html[data-theme="custom"] #nprogress .spinner-icon {
|
||
border-top-color: var(--theme-bright-color) !important;
|
||
border-left-color: var(--theme-bright-color) !important;
|
||
}
|
||
|
||
.log-highlight {
|
||
background-color: rgba(234, 179, 8, 0.4);
|
||
border-radius: 2px;
|
||
box-decoration-break: clone;
|
||
-webkit-box-decoration-break: clone;
|
||
}
|
||
|
||
.dark .log-highlight {
|
||
background-color: rgba(234, 179, 8, 0.3);
|
||
}
|
||
|
||
/* Wave-style application console: transparent viewport and an overlay
|
||
scrollbar that stays out of the terminal content until it is used. */
|
||
#terminal.terminal-host,
|
||
#terminal.terminal-host .xterm {
|
||
width: 100%;
|
||
height: 100%;
|
||
max-height: 100%;
|
||
}
|
||
|
||
#terminal.terminal-host .xterm {
|
||
padding: 0;
|
||
}
|
||
|
||
#terminal.terminal-host .xterm-viewport {
|
||
overflow-y: auto !important;
|
||
}
|
||
|
||
#terminal[data-terminal-style="application"] .xterm-viewport {
|
||
background-color: transparent !important;
|
||
scrollbar-width: thin;
|
||
scrollbar-color: rgba(255, 255, 255, 0.15) transparent;
|
||
}
|
||
|
||
#terminal[data-terminal-style="application"] .xterm-viewport::-webkit-scrollbar {
|
||
width: 4px;
|
||
}
|
||
|
||
#terminal[data-terminal-style="application"] .xterm-viewport::-webkit-scrollbar-thumb {
|
||
border-radius: 4px;
|
||
background: rgba(255, 255, 255, 0.15);
|
||
}
|
||
|
||
#terminal[data-terminal-style="application"] .xterm-viewport::-webkit-scrollbar-track {
|
||
background: transparent;
|
||
}
|
||
|
||
.application-console-shell,
|
||
.terminal-fullscreen-shell {
|
||
position: relative;
|
||
isolation: isolate;
|
||
color: #f2f2f2;
|
||
background-color: #121214;
|
||
}
|
||
|
||
.application-console-shell {
|
||
box-shadow:
|
||
inset 0 0 0 1px var(--console-theme-border, rgb(255 255 255 / 0.08)),
|
||
0 18px 50px rgb(0 0 0 / 0.18);
|
||
}
|
||
|
||
/* Fullscreen is portaled to body — keep position:fixed from utilities. */
|
||
.terminal-fullscreen-shell {
|
||
position: fixed;
|
||
}
|
||
|
||
.application-console-shell::before,
|
||
.terminal-fullscreen-shell::before {
|
||
position: absolute;
|
||
z-index: 0;
|
||
inset: 0;
|
||
border-radius: inherit;
|
||
background: var(--console-theme-background);
|
||
opacity: var(--console-theme-opacity);
|
||
content: "";
|
||
pointer-events: none;
|
||
transition:
|
||
background 220ms ease,
|
||
opacity 220ms ease;
|
||
}
|
||
|
||
.application-console-shell[data-console-theme="system"],
|
||
.terminal-fullscreen-shell[data-console-theme="system"] {
|
||
--console-theme-background: oklch(100.0% 0 0);
|
||
--console-theme-border: oklch(87.11% 0.0055 286.29);
|
||
--console-theme-opacity: 1;
|
||
}
|
||
|
||
/* Session toolbar (selected-target trigger + label) sits over the white
|
||
system console in light mode. Without this its text-white/* stayed white on
|
||
white and the selected server was invisible (issue #11532). */
|
||
html:not(.dark) .application-console-shell[data-console-theme="system"] .terminal-session-toolbar[class*="text-white"],
|
||
html:not(.dark) .application-console-shell[data-console-theme="system"] .terminal-session-toolbar [class*="text-white"],
|
||
html:not(.dark) .terminal-fullscreen-shell[data-console-theme="system"] .terminal-session-toolbar[class*="text-white"],
|
||
html:not(.dark) .terminal-fullscreen-shell[data-console-theme="system"] .terminal-session-toolbar [class*="text-white"] {
|
||
color: #52525b !important;
|
||
}
|
||
|
||
html.dark .application-console-shell[data-console-theme="system"],
|
||
html.dark .terminal-fullscreen-shell[data-console-theme="system"] {
|
||
--console-theme-background: oklch(18.31% 0.004 285.99);
|
||
--console-theme-border: rgb(255 255 255 / 0.08);
|
||
}
|
||
|
||
html[data-theme="custom"] .application-console-shell[data-console-theme="system"],
|
||
html[data-theme="custom"] .terminal-fullscreen-shell[data-console-theme="system"] {
|
||
--console-theme-background: var(--color-log);
|
||
--console-theme-border: var(--coollabs-line);
|
||
}
|
||
|
||
/* Docked System console only: the fill behind the terminal panel is the
|
||
elevated (header) surface, so the panel's rounded-corner notch reads as the
|
||
header colour — the card's figure/ground. The panel paints its own base
|
||
surface over this fill, so only the notch shows it. The fullscreen shell has
|
||
no panel, so it is intentionally excluded and keeps its plain terminal fill. */
|
||
.application-console-shell[data-console-theme="system"] {
|
||
--console-theme-background: var(--coollabs-elevated);
|
||
}
|
||
|
||
html.dark .application-console-shell[data-console-theme="system"] {
|
||
--console-theme-background: var(--coollabs-elevated);
|
||
}
|
||
|
||
/* Dark mode: the docked System console uses the dark card colors (darker outer
|
||
header, --color-card body, faint white ring), so it matches the cards around it. */
|
||
html.dark .application-console-shell[data-console-theme="system"] {
|
||
--coollabs-elevated: color-mix(in srgb, var(--color-card) 66%, black);
|
||
--coollabs-base: var(--color-card);
|
||
--coollabs-hairline: rgb(255 255 255 / 8%);
|
||
}
|
||
|
||
.console-theme-selector {
|
||
scrollbar-color: rgb(161 161 170) transparent;
|
||
scrollbar-width: thin;
|
||
}
|
||
|
||
.console-theme-selector::-webkit-scrollbar {
|
||
width: 8px;
|
||
}
|
||
|
||
.console-theme-selector::-webkit-scrollbar-thumb {
|
||
border: 2px solid transparent;
|
||
border-radius: 9999px;
|
||
background: rgb(161 161 170);
|
||
background-clip: padding-box;
|
||
}
|
||
|
||
.console-theme-selector::-webkit-scrollbar-track {
|
||
background: transparent;
|
||
}
|
||
|
||
html.dark .console-theme-selector {
|
||
scrollbar-color: rgb(255 255 255 / 0.28) transparent;
|
||
}
|
||
|
||
html.dark .console-theme-selector::-webkit-scrollbar-thumb {
|
||
background: rgb(255 255 255 / 0.28);
|
||
background-clip: padding-box;
|
||
}
|
||
|
||
.application-console-shell[data-console-theme="shadows-midnight"],
|
||
.terminal-fullscreen-shell[data-console-theme="shadows-midnight"] {
|
||
--console-theme-background: linear-gradient(135deg, #2a3b4c, rgb(42 59 76 / 0.4));
|
||
--console-theme-opacity: 0.9;
|
||
}
|
||
|
||
.application-console-shell[data-console-theme="shadows-golden-hour"],
|
||
.terminal-fullscreen-shell[data-console-theme="shadows-golden-hour"] {
|
||
--console-theme-background: linear-gradient(135deg, #d58a42, rgb(213 138 66 / 0.4));
|
||
--console-theme-opacity: 0.4;
|
||
}
|
||
|
||
.application-console-shell[data-console-theme="shadows-cosmic-purple"],
|
||
.terminal-fullscreen-shell[data-console-theme="shadows-cosmic-purple"] {
|
||
--console-theme-background: linear-gradient(135deg, #5d3e66, rgb(93 62 102 / 0.4));
|
||
--console-theme-opacity: 0.8;
|
||
}
|
||
|
||
.application-console-shell[data-console-theme="shadows-neon-glow"],
|
||
.terminal-fullscreen-shell[data-console-theme="shadows-neon-glow"] {
|
||
--console-theme-background: linear-gradient(135deg, #f300a6, rgb(243 0 166 / 0.3));
|
||
--console-theme-opacity: 0.45;
|
||
}
|
||
|
||
.application-console-shell[data-console-theme="shadows-icy-mist"],
|
||
.terminal-fullscreen-shell[data-console-theme="shadows-icy-mist"] {
|
||
--console-theme-background: linear-gradient(135deg, #d0d8e2, rgb(208 216 226 / 0.2));
|
||
--console-theme-opacity: 0.5;
|
||
}
|
||
|
||
.application-console-shell[data-console-theme="shadows-tropical-storm"],
|
||
.terminal-fullscreen-shell[data-console-theme="shadows-tropical-storm"] {
|
||
--console-theme-background: linear-gradient(135deg, #00b894, #1fa771, #2ecc71, #27ae60);
|
||
--console-theme-opacity: 0.3;
|
||
}
|
||
|
||
.application-console-shell[data-console-theme="shadows-golden-nebula"],
|
||
.terminal-fullscreen-shell[data-console-theme="shadows-golden-nebula"] {
|
||
--console-theme-background: linear-gradient(135deg, #ffd700, #ff6347, #d4a20e, #ffcc00, #1f3d6f);
|
||
--console-theme-opacity: 0.44;
|
||
}
|
||
|
||
.application-console-shell[data-console-theme="shadows-cosmic-lagoon"],
|
||
.terminal-fullscreen-shell[data-console-theme="shadows-cosmic-lagoon"] {
|
||
--console-theme-background: linear-gradient(135deg, #1d2b64, #2f4f96, #00b5b8, #9c27b0, #8e24aa);
|
||
--console-theme-opacity: 0.59;
|
||
}
|
||
|
||
.application-console-shell[data-console-theme="shadows-neon-nebula"],
|
||
.terminal-fullscreen-shell[data-console-theme="shadows-neon-nebula"] {
|
||
--console-theme-background: linear-gradient(135deg, #00d9d9, #ff55aa, #1e1e2f, #2f3b57, #ff99ff);
|
||
--console-theme-opacity: 0.6;
|
||
}
|
||
|
||
.application-console-shell[data-console-theme="shadows-transparent"],
|
||
.terminal-fullscreen-shell[data-console-theme="shadows-transparent"] {
|
||
--console-theme-background: rgb(0 0 0 / 0.7);
|
||
--console-theme-opacity: 0.7;
|
||
}
|
||
|
||
/*
|
||
* Terminal page: lock to the viewport so the shell fills remaining height
|
||
* without document scroll. Offsets match layouts/app.blade.php chrome:
|
||
* mobile sticky top bar (~3.5rem) + main py-6 (3rem)
|
||
* desktop fixed top bar accounted in main lg:pt-[calc(3rem+1.75rem)] + lg:pb-10
|
||
*/
|
||
.terminal-page {
|
||
display: flex;
|
||
flex-direction: column;
|
||
width: 100%;
|
||
min-width: 0;
|
||
min-height: 0;
|
||
overflow: hidden;
|
||
height: calc(100dvh - 6.75rem);
|
||
max-height: calc(100dvh - 6.75rem);
|
||
}
|
||
|
||
@media (min-width: 1024px) {
|
||
.terminal-page {
|
||
height: calc(100dvh - 7.25rem);
|
||
max-height: calc(100dvh - 7.25rem);
|
||
}
|
||
}
|
||
|
||
.terminal-page-header {
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
.terminal-page-console {
|
||
display: flex;
|
||
min-height: 0;
|
||
flex: 1 1 0%;
|
||
flex-direction: column;
|
||
overflow: hidden;
|
||
/* Breathing room so the console's box-shadow ring + lift are not clipped by
|
||
this overflow-hidden, viewport-height container (the card gets this from
|
||
the scrollable page around it). */
|
||
padding: 6px;
|
||
}
|
||
|
||
.terminal-page-console > .application-console-shell {
|
||
min-height: 0;
|
||
flex: 1 1 0%;
|
||
}
|
||
|
||
/* Fullscreen: freeze the page; only xterm scrollback may move. */
|
||
html.terminal-is-fullscreen,
|
||
html.terminal-is-fullscreen body {
|
||
overflow: hidden !important;
|
||
overscroll-behavior: none !important;
|
||
touch-action: none;
|
||
height: 100% !important;
|
||
max-height: 100% !important;
|
||
}
|
||
|
||
html.terminal-is-fullscreen body {
|
||
/* position:fixed is also applied inline for scroll-lock */
|
||
width: 100% !important;
|
||
}
|
||
|
||
html.terminal-is-fullscreen main {
|
||
overflow: hidden !important;
|
||
overscroll-behavior: none !important;
|
||
}
|
||
|
||
/* Fullscreen terminal must win against layout chrome (header z-50, sidebar z-40). */
|
||
html.terminal-is-fullscreen .terminal-fullscreen-shell,
|
||
body.terminal-is-fullscreen .terminal-fullscreen-shell {
|
||
position: fixed !important;
|
||
inset: 0 !important;
|
||
z-index: 100000 !important;
|
||
width: 100vw !important;
|
||
height: auto !important;
|
||
max-width: none !important;
|
||
margin: 0 !important;
|
||
overflow: hidden !important;
|
||
overscroll-behavior: none !important;
|
||
touch-action: none;
|
||
}
|
||
|
||
/* Re-enable pan/scroll only inside the xterm buffer viewport. */
|
||
html.terminal-is-fullscreen .terminal-fullscreen-shell .xterm-viewport,
|
||
body.terminal-is-fullscreen .terminal-fullscreen-shell .xterm-viewport {
|
||
overscroll-behavior: contain !important;
|
||
touch-action: pan-y;
|
||
}
|
||
|
||
html.terminal-is-fullscreen .terminal-fullscreen-shell .terminal-fullscreen-btn,
|
||
html.terminal-is-fullscreen .terminal-fullscreen-shell [data-terminal-mobile-toolbar],
|
||
body.terminal-is-fullscreen .terminal-fullscreen-shell .terminal-fullscreen-btn,
|
||
body.terminal-is-fullscreen .terminal-fullscreen-shell [data-terminal-mobile-toolbar] {
|
||
touch-action: manipulation;
|
||
}
|
||
|
||
/* Session header band: an attached card-style header on top of the console,
|
||
mirroring the page card header (elevated surface + hairline divider). The
|
||
System theme uses the neutral card surface in both modes; colorful themes
|
||
fall back to a translucent dark band so the white controls stay legible. */
|
||
.terminal-session-toolbar {
|
||
position: relative;
|
||
z-index: 2;
|
||
flex-shrink: 0;
|
||
min-height: 3rem;
|
||
padding: 0.5rem 0.75rem 0.5rem 1rem;
|
||
/* No border-radius: the shell is overflow-hidden + rounded and clips the
|
||
header's top corners, so rounding here only risks a sub-pixel seam. */
|
||
border-bottom: 1px solid var(--terminal-header-border, rgb(255 255 255 / 0.1));
|
||
background: var(--terminal-header-bg, rgb(0 0 0 / 0.28));
|
||
-webkit-backdrop-filter: blur(12px);
|
||
backdrop-filter: blur(12px);
|
||
}
|
||
|
||
/* System header has no bottom border: the body panel's ring is the divider, and
|
||
its rounded top corners tuck under the header — the same merge the
|
||
target-picker card uses (elevated header meeting a base body panel). */
|
||
.application-console-shell[data-console-theme="system"] .terminal-session-toolbar {
|
||
--terminal-header-bg: var(--coollabs-elevated);
|
||
--terminal-header-border: transparent;
|
||
/* Shell no longer clips (overflow: visible), so the header rounds its own
|
||
top corners — matching the card header. */
|
||
border-radius: 8px 8px 0 0;
|
||
/* Drop the z-index:2 stacking context: it painted the header OVER the body
|
||
panel, hiding the panel's ring (the divider). The header only had z-2 to
|
||
beat ::before, but its dropdowns sit in their own .relative wrappers, so
|
||
z:auto is safe and lets the body panel's ring paint over the header —
|
||
exactly how the card body overlaps the card header. */
|
||
z-index: auto;
|
||
-webkit-backdrop-filter: none;
|
||
backdrop-filter: none;
|
||
}
|
||
|
||
/* System console body: the terminal panel, a full rounded box-shadow ring like
|
||
the card body. Its base surface is the terminal fill; the elevated surface
|
||
behind it (::before) shows only through the rounded-corner notch, reading as
|
||
the header colour — the card's figure/ground. */
|
||
.application-console-shell[data-console-theme="system"] .terminal-session-panel {
|
||
background: var(--coollabs-base);
|
||
/* The body panel is a full rounded ring (like the card body). position:
|
||
relative puts it (and its ring) above ::before AND, because it comes
|
||
after the now-z:auto header in the DOM, above the header — so the ring's
|
||
top edge is the divider and its rounded top corners connect down into the
|
||
side ring, which coincides with the shell's ring (one hairline, no
|
||
doubling). A box-shadow ring rounds and connects cleanly; a border-top
|
||
cannot (square corners, or a disconnected transparent edge). */
|
||
position: relative;
|
||
border-radius: 8px;
|
||
box-shadow: 0 0 0 1px var(--coollabs-hairline);
|
||
}
|
||
|
||
/* Connected System console frame = the target-picker card recipe: a hairline
|
||
box-shadow ring + a restrained lift. overflow stays visible so the ring is
|
||
not clipped (the .terminal-page-console padding gives it room inside the
|
||
viewport-locked, overflow-hidden page). Colorful themes keep the themed,
|
||
overflow-hidden shell. */
|
||
.application-console-shell[data-console-theme="system"] {
|
||
overflow: visible;
|
||
box-shadow:
|
||
0 0 0 1px var(--coollabs-hairline),
|
||
0 1px 2px 0 rgb(0 0 0 / 0.05);
|
||
}
|
||
|
||
.terminal-loading-label {
|
||
font-size: 0.875rem;
|
||
font-weight: 500;
|
||
color: rgb(255 255 255 / 0.75);
|
||
}
|
||
|
||
html:not(.dark) .application-console-shell[data-console-theme="system"] .terminal-loading-label {
|
||
color: #52525b;
|
||
}
|
||
|
||
.terminal-target-picker {
|
||
color: rgb(255 255 255 / 0.75);
|
||
background: oklch(18.31% 0.004 285.99);
|
||
border-color: rgb(255 255 255 / 0.1);
|
||
}
|
||
|
||
/* Pre-session target list: a normal page card, not the themed console canvas. */
|
||
.terminal-target-card .application-settings-section-body {
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* The header inset is tuned for 32px buttons. A full-width filter needs the
|
||
same gutter on both sides once the actions row wraps below the title.
|
||
Matches the base header selector's specificity so the shorthand cannot win. */
|
||
@media (max-width: 640px) {
|
||
.application-settings-section.terminal-target-card > :is(header, .application-settings-section-header) {
|
||
padding-right: 1rem;
|
||
}
|
||
}
|
||
|
||
.terminal-target-card-list {
|
||
max-height: min(70vh, 34rem);
|
||
overflow-y: auto;
|
||
scrollbar-width: thin;
|
||
scrollbar-color: var(--coollabs-fill) transparent;
|
||
}
|
||
|
||
.terminal-target-card-list::-webkit-scrollbar {
|
||
width: 8px;
|
||
}
|
||
|
||
.terminal-target-card-list::-webkit-scrollbar-track {
|
||
background: transparent;
|
||
}
|
||
|
||
.terminal-target-card-list::-webkit-scrollbar-thumb {
|
||
border: 2px solid transparent;
|
||
border-radius: 9999px;
|
||
background-color: var(--coollabs-fill);
|
||
background-clip: padding-box;
|
||
}
|
||
|
||
/* Group headers stay readable while scrolling long container lists. */
|
||
.terminal-target-group-label {
|
||
display: flex;
|
||
position: sticky;
|
||
z-index: 1;
|
||
top: 0;
|
||
align-items: center;
|
||
gap: 0.375rem;
|
||
padding: 0.5rem 0.5rem 0.375rem;
|
||
background: var(--coollabs-base);
|
||
font-size: 0.6875rem;
|
||
font-weight: 500;
|
||
letter-spacing: 0.01em;
|
||
color: var(--coollabs-subtle);
|
||
}
|
||
|
||
.terminal-target-group-count {
|
||
border-radius: 9999px;
|
||
background: var(--coollabs-fill);
|
||
padding: 0 0.375rem;
|
||
font-size: 0.625rem;
|
||
line-height: 1rem;
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
|
||
/* The server column only earns its space once the row is wide enough. */
|
||
.terminal-target-item-server {
|
||
display: none;
|
||
max-width: 14rem;
|
||
flex-shrink: 0;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
font-size: 0.6875rem;
|
||
line-height: 1rem;
|
||
color: var(--coollabs-subtle);
|
||
}
|
||
|
||
@media (min-width: 640px) {
|
||
.terminal-target-item-server {
|
||
display: block;
|
||
}
|
||
}
|
||
|
||
.terminal-session-panel {
|
||
border: 0;
|
||
border-radius: 0.75rem;
|
||
background: transparent;
|
||
box-shadow: none;
|
||
}
|
||
|
||
html:not(.dark) .application-console-shell[data-console-theme="system"] .terminal-session-panel {
|
||
background: var(--coollabs-base);
|
||
}
|
||
|
||
/* Console (attach) command box. Colours come from the active terminal theme's
|
||
foreground (--console-fg), so the input is readable on every console theme. */
|
||
.terminal-console-input {
|
||
border-color: color-mix(in srgb, var(--console-fg, #fff) 10%, transparent);
|
||
}
|
||
|
||
.console-command-row {
|
||
box-shadow: inset 0 0 0 1px transparent;
|
||
transition:
|
||
background-color 150ms ease,
|
||
box-shadow 150ms ease;
|
||
}
|
||
|
||
.console-command-row:hover {
|
||
background-color: color-mix(in srgb, var(--console-fg, #fff) 3%, transparent);
|
||
}
|
||
|
||
.console-command-row:focus-within {
|
||
background-color: color-mix(in srgb, var(--console-fg, #fff) 6%, transparent);
|
||
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--console-fg, #fff) 22%, transparent);
|
||
}
|
||
|
||
.console-command-field {
|
||
color: var(--console-fg, #fff) !important;
|
||
caret-color: var(--console-fg, #fff);
|
||
}
|
||
|
||
.console-command-field::placeholder {
|
||
color: color-mix(in srgb, var(--console-fg, #fff) 40%, transparent);
|
||
}
|
||
|
||
.console-command-row:focus-within .console-command-field::placeholder {
|
||
color: color-mix(in srgb, var(--console-fg, #fff) 28%, transparent);
|
||
}
|
||
|
||
.console-command-prompt {
|
||
color: color-mix(in srgb, var(--console-fg, #fff) 40%, transparent);
|
||
transition: color 200ms ease;
|
||
}
|
||
|
||
.console-command-row:focus-within .console-command-prompt {
|
||
color: var(--console-fg, #fff);
|
||
}
|
||
|
||
.console-command-prompt[data-sent] {
|
||
color: #fcd452 !important;
|
||
}
|
||
|
||
.console-command-pending {
|
||
color: color-mix(in srgb, var(--console-fg, #fff) 60%, transparent);
|
||
}
|
||
|
||
.console-command-send {
|
||
color: color-mix(in srgb, var(--console-fg, #fff) 60%, transparent);
|
||
outline-color: color-mix(in srgb, var(--console-fg, #fff) 60%, transparent);
|
||
}
|
||
|
||
.console-command-send:hover {
|
||
color: var(--console-fg, #fff);
|
||
background-color: color-mix(in srgb, var(--console-fg, #fff) 8%, transparent);
|
||
}
|
||
|
||
.terminal-target-list {
|
||
scrollbar-width: thin;
|
||
scrollbar-color: var(--terminal-scrollbar, rgb(255 255 255 / 0.3)) transparent;
|
||
}
|
||
|
||
.terminal-target-list::-webkit-scrollbar {
|
||
width: 8px;
|
||
}
|
||
|
||
.terminal-target-list::-webkit-scrollbar-thumb {
|
||
border: 2px solid transparent;
|
||
border-radius: 9999px;
|
||
background: var(--terminal-scrollbar, rgb(255 255 255 / 0.3));
|
||
background-clip: padding-box;
|
||
}
|
||
|
||
.terminal-target-list::-webkit-scrollbar-track {
|
||
background: transparent;
|
||
}
|
||
|
||
html:not(.dark) .application-console-shell[data-console-theme="system"] .terminal-target-picker {
|
||
color: #52525b;
|
||
background: #fff;
|
||
border-color: #d4d4d8;
|
||
box-shadow: var(--shadow-dropdown);
|
||
}
|
||
|
||
html:not(.dark) .application-console-shell[data-console-theme="system"] .terminal-target-picker [class*="text-white"] {
|
||
color: #52525b !important;
|
||
}
|
||
|
||
html:not(.dark) .application-console-shell[data-console-theme="system"] .terminal-target-picker input {
|
||
color: #27272a !important;
|
||
background: #f4f4f5 !important;
|
||
border-color: #d4d4d8 !important;
|
||
}
|
||
|
||
html:not(.dark) .application-console-shell[data-console-theme="system"] .terminal-target-picker input::placeholder {
|
||
color: #71717a !important;
|
||
}
|
||
|
||
html:not(.dark) .application-console-shell[data-console-theme="system"] .terminal-target-picker button:hover {
|
||
color: #18181b !important;
|
||
background: #f4f4f5;
|
||
}
|
||
|
||
html:not(.dark) .application-console-shell[data-console-theme="system"] .terminal-theme-trigger {
|
||
color: #52525b;
|
||
}
|
||
|
||
html:not(.dark) .application-console-shell[data-console-theme="system"] .terminal-session-target-trigger {
|
||
color: #52525b;
|
||
}
|
||
|
||
html:not(.dark) .application-console-shell[data-console-theme="system"] .terminal-theme-trigger:hover,
|
||
html:not(.dark) .application-console-shell[data-console-theme="system"] .terminal-session-target-trigger:hover {
|
||
color: #18181b;
|
||
background: rgb(0 0 0 / 0.06);
|
||
}
|
||
|
||
.application-console-block {
|
||
position: relative;
|
||
z-index: 1;
|
||
isolation: isolate;
|
||
}
|
||
|
||
.terminal-fullscreen-btn {
|
||
display: flex;
|
||
width: 1.75rem;
|
||
height: 1.75rem;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 0.375rem;
|
||
border: 0;
|
||
background: transparent;
|
||
color: var(--terminal-scrollbar, rgb(255 255 255 / 0.65));
|
||
transition:
|
||
background-color 150ms ease,
|
||
color 150ms ease,
|
||
opacity 150ms ease;
|
||
}
|
||
|
||
.terminal-fullscreen-btn:hover {
|
||
background: color-mix(in srgb, var(--terminal-scrollbar, #fff) 18%, transparent);
|
||
color: var(--terminal-scrollbar, #fff);
|
||
}
|
||
|
||
/*
|
||
* Coollabs layer-card settings surfaces.
|
||
* Anatomy: root shell = elevated bg + hairline ring; the header is a compact
|
||
* strip on the shell; the body is a nested rounded panel with base bg + fill
|
||
* ring. Depth comes from the neutral oklch surface ladder below.
|
||
* Deliberately unlayered plain CSS: the global element rules above (h3, h4,
|
||
* section, label) and the .input/.button utilities would otherwise override
|
||
* utility classes placed in the card markup.
|
||
*/
|
||
:root {
|
||
/* Page canvas is a faint off-white so white/elevated cards lift off it
|
||
(shadcn pattern), instead of card and page reading as one flat color. */
|
||
--coollabs-canvas: oklch(97% 0 0);
|
||
--coollabs-elevated: oklch(98% 0 0);
|
||
--coollabs-recessed: oklch(96% 0 0);
|
||
--coollabs-base: oklch(100.0% 0 0);
|
||
--coollabs-fill: oklch(92.2% 0 0);
|
||
--coollabs-line: oklch(14.5% 0 0 / 0.1);
|
||
/* Card/table edge ring. Crisped from 93.5% (1.17:1 on canvas, invisible)
|
||
to 85.5% (1.51:1) so surfaces read as bordered cards, not flat fills. */
|
||
--coollabs-hairline: oklch(85.5% 0 0);
|
||
/* Row and section separators inside cards. Softer than the card edge.
|
||
Translucent, so it stays visible on any card or theme surface. */
|
||
--coollabs-divider: oklch(0% 0 0 / 0.04);
|
||
/* Muted labels/titles. Darkened from 55.6% (failed WCAG AA on elevated
|
||
4.46, recessed 4.24, fill 3.76) to 50% so muted text clears 4.5:1 on
|
||
every light surface above white. Dark stays 70.8% (already passes). */
|
||
--coollabs-subtle: oklch(50% 0 0);
|
||
/* Border of brand fills and the text on them. A custom theme overrides both. */
|
||
--color-brand-ring: #8a4dff;
|
||
--color-brand-foreground: #fff;
|
||
}
|
||
|
||
.dark {
|
||
/* Purple is the accent in dark mode too. Warning yellow stays on
|
||
--color-warning for real warnings only (update available, attention). */
|
||
--color-accent: var(--color-brand);
|
||
/* Cards sit one step above the #0a0a0a canvas. The top bar uses the canvas,
|
||
not this color. The sidebar uses --color-sidebar. */
|
||
--color-card: #121212;
|
||
--color-sidebar: #120e1a;
|
||
/* Dark surface ladder in hex (sRGB), not oklch: oklch lightness compresses
|
||
to near-black below ~15%, so oklch(15%) rendered as sRGB 11 and cards
|
||
could not lift off the content canvas (sRGB 10). These give clear,
|
||
visible steps: content 10 -> elevated 22 -> base 28 -> recessed 34.
|
||
Pure neutral (r=g=b), matching the neutral borders/text and the light
|
||
ladder, so every panel shares one temperature (no blue cast). */
|
||
--color-app: oklch(14.48% 0 0);
|
||
--color-panel: oklch(19.13% 0 0);
|
||
--coollabs-canvas: var(--color-app);
|
||
--coollabs-elevated: oklch(20.02% 0 0);
|
||
--coollabs-recessed: oklch(25.2% 0 0);
|
||
--coollabs-base: oklch(22.64% 0 0);
|
||
--coollabs-fill: oklch(29.31% 0 0);
|
||
--color-surface: oklch(20.02% 0 0);
|
||
--color-raised: oklch(22.64% 0 0);
|
||
--color-selected: oklch(26.86% 0 0);
|
||
--color-log: oklch(15.91% 0 0);
|
||
--coollabs-line: oklch(36% 0 0);
|
||
/* Kept about 1.6:1 on the canvas so dark cards
|
||
and tables show a visible edge instead of blending into the canvas. */
|
||
--coollabs-hairline: oklch(36% 0 0);
|
||
--coollabs-divider: oklch(100% 0 0 / 0.045);
|
||
--coollabs-subtle: oklch(70.8% 0 0);
|
||
/* Neutral (was #a8a8b0 / #7a7a84, both cool-tinted). Sidebar text now shares
|
||
the same neutral temperature as the content text, so the sidebar no longer
|
||
reads cooler than the panels/cards. */
|
||
--color-nav-text: oklch(73.8% 0 0);
|
||
/* Raised from 58.97% (4.47:1 on panel, just under AA) to 61% (4.86:1) so the
|
||
11px sidebar section labels clear 4.5:1. */
|
||
--color-nav-muted: oklch(61% 0 0);
|
||
--color-nav-active: oklch(96.12% 0 0);
|
||
/* Tertiary text. Base token (59.31%) only clears 4.5:1 on app/panel; it
|
||
failed on surface/raised/selected (4.46/4.20/3.73). Raised to 64% so the
|
||
313 dark:text-fg-faint usages clear AA across the whole surface ladder,
|
||
while staying clearly dimmer than fg-dim (76.99%). */
|
||
--color-fg-faint: oklch(65% 0 0);
|
||
/* Error text. The base error red (57.71%) failed on every dark surface
|
||
(4.10/3.75/3.53). Brightened for dark mode so text-error clears AA on the
|
||
card ladder while staying unmistakably red. */
|
||
--color-error: oklch(66% 0.2 27.33);
|
||
}
|
||
|
||
/* Light-mode semantic text.
|
||
warning / success / error double as the bright brand accent and as solid
|
||
fills, which only appear under `dark:` — so in light mode these tokens are
|
||
used almost entirely as text, translucent tints (bg-warning/10) and borders.
|
||
The bright values (yellow 88%, green 72%) and the base red (57.71%) fail AA
|
||
as text on white and the light surface ladder (warning 1.43, success 2.28,
|
||
error 4.43 on canvas). Scoped to `:not(.dark)` so dark keeps the brand
|
||
colors and the custom theme keeps its own overrides. Every value clears
|
||
4.5:1 on white, canvas, elevated, recessed and fill, and is in sRGB gamut. */
|
||
html:not(.dark) {
|
||
--color-warning: oklch(50% 0.105 80);
|
||
--color-success: oklch(48% 0.135 149.58);
|
||
--color-error: oklch(52% 0.2 27.33);
|
||
}
|
||
|
||
/* Custom theme. applyCustomTheme() in base.blade.php sets four colors and the
|
||
text color that is readable on each. The page and the sidebar each derive
|
||
one surface ladder from three variables:
|
||
--t-bg the background color
|
||
--t-text the text color on it
|
||
--t-light 1 for a light background, 0 for a dark one
|
||
Light schemes put white cards on a slightly darker canvas. Dark schemes lift
|
||
cards toward the text color. */
|
||
html[data-theme="custom"] {
|
||
--t-bg: var(--theme-bg);
|
||
--t-text: var(--theme-text);
|
||
--t-light: var(--theme-light);
|
||
--theme-base-color: oklch(49.65% 0.2709 289.33);
|
||
--theme-bright-color: color-mix(in srgb, var(--theme-base-color) 85%, oklch(100% 0 0));
|
||
--theme-scrollbar-thumb: color-mix(in srgb, var(--theme-bright-color) 70%, var(--theme-accent-foreground));
|
||
/* Accent text/icons sit directly on dark surfaces. Force the lightness into a
|
||
readable band (and tame chroma) so any picked hue keeps enough contrast. */
|
||
--theme-fg-on-surface: oklch(from var(--theme-base-color) clamp(0.72, l, 0.86) min(c, 0.13) h);
|
||
--color-accent: var(--theme-bright-color);
|
||
--color-coollabs: var(--theme-bright-color);
|
||
--color-coollabs-100: color-mix(in oklab, var(--theme-bright-color) 88%, oklch(100% 0 0));
|
||
--color-coollabs-200: color-mix(in oklab, var(--theme-bright-color) 88%, oklch(0% 0 0));
|
||
--color-coollabs-300: color-mix(in oklab, var(--theme-bright-color) 72%, oklch(0% 0 0));
|
||
/* Legacy dark-theme accent utilities use warning as the brand color. */
|
||
--color-warning: var(--theme-bright-color);
|
||
/* Brand fills, tints, and accent text follow the picked color. Text on a
|
||
brand fill uses the computed foreground, so light colors stay readable. */
|
||
--color-brand: var(--theme-bright-color);
|
||
--color-brand-hover: color-mix(in oklab, var(--theme-bright-color) 88%, oklch(100% 0 0));
|
||
--color-brand-press: color-mix(in oklab, var(--theme-bright-color) 85%, oklch(0% 0 0));
|
||
--color-brand-depth: color-mix(in oklab, var(--theme-bright-color) 65%, oklch(0% 0 0));
|
||
--color-brand-ring: color-mix(in oklab, var(--theme-bright-color) 75%, oklch(100% 0 0));
|
||
--color-brand-foreground: var(--theme-accent-foreground);
|
||
--color-brand-light: var(--theme-fg-on-surface);
|
||
--color-accent-foreground: var(--theme-accent-foreground);
|
||
--color-sidebar: var(--theme-sidebar);
|
||
}
|
||
|
||
html[data-theme="custom"]:not(.dark) {
|
||
--theme-scrollbar-thumb: color-mix(in srgb, var(--theme-base-color) 45%, oklch(60% 0 0));
|
||
/* Accent text/icons on light surfaces: force lightness into a dark band. */
|
||
--theme-fg-on-surface: oklch(from var(--theme-base-color) clamp(0.30, l, 0.48) min(c, 0.16) h);
|
||
}
|
||
|
||
html[data-theme="custom"] :is(.app-sidebar, .app-topbar-brand, .mobile-sidebar-sheet [role="dialog"]) {
|
||
--t-bg: var(--theme-sidebar);
|
||
--t-text: var(--theme-sidebar-text);
|
||
--t-light: var(--theme-sidebar-light);
|
||
}
|
||
|
||
html[data-theme="custom"],
|
||
html[data-theme="custom"] :is(.app-sidebar, .app-topbar-brand, .mobile-sidebar-sheet [role="dialog"]) {
|
||
--color-app: color-mix(in oklab, var(--t-text) calc(var(--t-light) * 3%), var(--t-bg));
|
||
--color-panel: color-mix(in oklab, var(--t-text) calc((1 - var(--t-light)) * 3%), var(--t-bg));
|
||
--color-surface: color-mix(in oklab, var(--t-text) calc((1 - var(--t-light)) * 4%), var(--t-bg));
|
||
--color-raised: color-mix(in oklab, var(--t-text) calc(4% + (1 - var(--t-light)) * 2%), var(--t-bg));
|
||
--color-selected: color-mix(in oklab, var(--t-text) calc(8% + (1 - var(--t-light)) * 2%), var(--t-bg));
|
||
--color-card: var(--color-surface);
|
||
--color-coolgray-100: var(--color-surface);
|
||
--color-coolgray-200: var(--color-raised);
|
||
--color-coolgray-300: var(--color-selected);
|
||
--color-coolgray-400: color-mix(in oklab, var(--t-text) calc(9% + (1 - var(--t-light)) * 3%), var(--t-bg));
|
||
--color-coolgray-500: color-mix(in oklab, var(--t-text) calc(12% + (1 - var(--t-light)) * 3%), var(--t-bg));
|
||
--coollabs-canvas: var(--color-app);
|
||
--coollabs-elevated: var(--color-surface);
|
||
--coollabs-recessed: var(--color-raised);
|
||
--coollabs-base: color-mix(in oklab, var(--t-text) calc((1 - var(--t-light)) * 5%), var(--t-bg));
|
||
--color-content-surface: var(--coollabs-base);
|
||
--coollabs-fill: color-mix(in oklab, var(--t-text) calc(8% + (1 - var(--t-light)) * 4%), var(--t-bg));
|
||
--coollabs-line: color-mix(in oklab, var(--t-text) calc(16% + (1 - var(--t-light)) * 6%), var(--t-bg));
|
||
--coollabs-hairline: color-mix(in oklab, var(--t-text) calc(11% + (1 - var(--t-light)) * 3%), var(--t-bg));
|
||
--coollabs-divider: color-mix(in oklab, var(--t-text) calc(5% + (1 - var(--t-light)) * 1%), transparent);
|
||
--color-hairline: color-mix(in oklab, var(--t-text) 8%, transparent);
|
||
--color-fg: var(--t-text);
|
||
--color-fg-dim: color-mix(in oklab, var(--t-text) 74%, var(--t-bg));
|
||
--color-fg-faint: color-mix(in oklab, var(--t-text) 60%, var(--t-bg));
|
||
--coollabs-subtle: var(--color-fg-faint);
|
||
--theme-placeholder-color: color-mix(in oklab, var(--t-text) 50%, var(--t-bg));
|
||
--color-nav-text: color-mix(in oklab, var(--t-text) 74%, var(--t-bg));
|
||
--color-nav-muted: color-mix(in oklab, var(--t-text) 60%, var(--t-bg));
|
||
--color-nav-active: var(--t-text);
|
||
/* Logs sit a little deeper than the canvas. */
|
||
--color-log: color-mix(in oklab, oklch(0% 0 0) calc((1 - var(--t-light)) * 20%), var(--color-app));
|
||
--color-log-toolbar: var(--color-surface);
|
||
}
|
||
|
||
|
||
html[data-theme="custom"] .control-selected,
|
||
html[data-theme="custom"] .logs-viewer-btn-active {
|
||
color: var(--color-accent-foreground);
|
||
}
|
||
|
||
html[data-theme="custom"] * {
|
||
scrollbar-color: var(--theme-scrollbar-thumb) var(--color-panel);
|
||
}
|
||
|
||
html[data-theme="custom"] *::-webkit-scrollbar-thumb {
|
||
border-radius: 9999px;
|
||
background: var(--theme-scrollbar-thumb);
|
||
}
|
||
|
||
html[data-theme="custom"] *::-webkit-scrollbar-thumb:hover {
|
||
background: var(--theme-bright-color);
|
||
}
|
||
|
||
html[data-theme="custom"] *::-webkit-scrollbar-track {
|
||
background: var(--color-panel);
|
||
}
|
||
|
||
html[data-theme="custom"] .application-settings-navigation {
|
||
overflow-y: scroll;
|
||
scrollbar-gutter: stable;
|
||
scrollbar-width: thin;
|
||
}
|
||
|
||
html[data-theme="custom"] .application-settings-navigation::-webkit-scrollbar {
|
||
width: 8px;
|
||
}
|
||
|
||
/* Use the same surface recipe as application settings cards for legacy
|
||
translucent cards and tables across the application. */
|
||
html[data-theme="custom"] .data-table,
|
||
html[data-theme="custom"] .box,
|
||
html[data-theme="custom"] .coolbox,
|
||
html[data-theme="custom"] .resource-view,
|
||
html[data-theme="custom"] [class~="bg-surface"],
|
||
html[data-theme="custom"] [class~="dark:bg-surface"],
|
||
html[data-theme="custom"] [class*="dark:bg-white/[0.025]"] {
|
||
background-color: var(--color-content-surface) !important;
|
||
}
|
||
|
||
.dark [class~="dark:border-white/[0.07]"],
|
||
.dark [class~="dark:border-white/[0.08]"] {
|
||
border-color: var(--coollabs-line) !important;
|
||
}
|
||
|
||
.dark [class~="dark:border-white/[0.06]"] {
|
||
border-color: var(--coollabs-hairline) !important;
|
||
}
|
||
|
||
html[data-theme="custom"] input::placeholder,
|
||
html[data-theme="custom"] textarea::placeholder {
|
||
color: var(--theme-placeholder-color) !important;
|
||
/* The token is already a readable light tone; no extra opacity dampening,
|
||
which would drop it back below the contrast floor. */
|
||
opacity: 1;
|
||
}
|
||
|
||
/* Accent text/icons (links, badges, "update available" pill, check marks) use the
|
||
brand color as foreground on dark surfaces. Route them to the clamped, always
|
||
readable token; fills (bg-*) keep the brighter accent + its computed foreground. */
|
||
html[data-theme="custom"] :is(
|
||
[class~="text-coollabs"],
|
||
[class~="text-warning"],
|
||
[class~="text-accent"],
|
||
[class~="dark:text-coollabs"],
|
||
[class~="dark:text-warning"],
|
||
[class~="dark:text-brand-light"]
|
||
) {
|
||
color: var(--theme-fg-on-surface) !important;
|
||
}
|
||
|
||
html.dark[data-theme="custom"] [class~="dark:bg-brand"] {
|
||
color: var(--theme-accent-foreground) !important;
|
||
}
|
||
|
||
html[data-theme="custom"] [class~="hover:text-coollabs"]:hover,
|
||
html[data-theme="custom"] [class~="hover:text-warning"]:hover,
|
||
html[data-theme="custom"] [class~="dark:hover:text-warning"]:hover,
|
||
html[data-theme="custom"] [class~="dark:hover:text-brand-light"]:hover,
|
||
html[data-theme="custom"] [class~="dark:hover:text-coollabs"]:hover {
|
||
color: var(--theme-fg-on-surface) !important;
|
||
}
|
||
|
||
/* Light custom theme reaches raw neutral utilities too. Dark mode tints
|
||
everything through the pervasive dark:bg-* and dark:border-* utilities (which
|
||
resolve to --color-* vars the block overrides); light mode has no equivalent,
|
||
so components fall back to hardcoded neutrals. Remap the dominant light
|
||
surface/border utilities to the tinted custom vars so the color reaches the
|
||
whole UI, not just design-system classes. A light sidebar on a dark page
|
||
gets the same remap. */
|
||
html[data-theme="custom"]:not(.dark) :is([class~="bg-white"], [class~="bg-white/95"], [class~="bg-white/80"]),
|
||
html[data-sidebar-scheme="light"] :where(.app-sidebar, .app-topbar-brand, .mobile-sidebar-sheet [role="dialog"]) :is([class~="bg-white"], [class~="bg-white/95"], [class~="bg-white/80"]) {
|
||
background-color: var(--coollabs-elevated) !important;
|
||
}
|
||
|
||
html[data-theme="custom"]:not(.dark) [class~="bg-neutral-50"],
|
||
html[data-sidebar-scheme="light"] :where(.app-sidebar, .app-topbar-brand, .mobile-sidebar-sheet [role="dialog"]) [class~="bg-neutral-50"] {
|
||
background-color: var(--coollabs-canvas) !important;
|
||
}
|
||
|
||
html[data-theme="custom"]:not(.dark) :is([class~="bg-neutral-100"], [class~="bg-neutral-200"]),
|
||
html[data-sidebar-scheme="light"] :where(.app-sidebar, .app-topbar-brand, .mobile-sidebar-sheet [role="dialog"]) :is([class~="bg-neutral-100"], [class~="bg-neutral-200"]) {
|
||
background-color: var(--coollabs-fill) !important;
|
||
}
|
||
|
||
html[data-theme="custom"]:not(.dark) [class~="border-neutral-200"],
|
||
html[data-sidebar-scheme="light"] :where(.app-sidebar, .app-topbar-brand, .mobile-sidebar-sheet [role="dialog"]) [class~="border-neutral-200"] {
|
||
border-color: var(--coollabs-hairline) !important;
|
||
}
|
||
|
||
html[data-theme="custom"]:not(.dark) :is([class~="border-neutral-300"], [class~="border-neutral-100"]),
|
||
html[data-sidebar-scheme="light"] :where(.app-sidebar, .app-topbar-brand, .mobile-sidebar-sheet [role="dialog"]) :is([class~="border-neutral-300"], [class~="border-neutral-100"]) {
|
||
border-color: var(--coollabs-line) !important;
|
||
}
|
||
|
||
/* Row and section separators (one-sided borders and divide-y) use the softer
|
||
divider token, so lists read as one card instead of a stack of boxes. Card
|
||
edges (full borders) keep the hairline/line tokens. */
|
||
html:not(.dark) :is([class~="border-b"], [class~="border-t"], [class~="border-y"])[class~="border-neutral-200"],
|
||
html:not(.dark) [class~="divide-neutral-200"] > :not(:last-child),
|
||
.dark :is([class~="border-b"], [class~="border-t"], [class~="border-y"]):is([class~="dark:border-white/[0.06]"], [class~="dark:border-white/[0.07]"], [class~="dark:border-white/[0.08]"]),
|
||
.dark :is([class~="dark:divide-white/[0.06]"], [class~="dark:divide-white/[0.07]"], [class~="dark:divide-white/[0.08]"]) > :not(:last-child) {
|
||
border-color: var(--coollabs-divider) !important;
|
||
}
|
||
|
||
html[data-theme="custom"] input:read-only,
|
||
html[data-theme="custom"] textarea:read-only,
|
||
html[data-theme="custom"] input:disabled,
|
||
html[data-theme="custom"] textarea:disabled {
|
||
color: var(--color-fg-dim) !important;
|
||
}
|
||
|
||
/* Public authentication surfaces */
|
||
.auth-shell {
|
||
min-height: 100dvh;
|
||
display: flex;
|
||
background:
|
||
radial-gradient(circle at 50% 0%, color-mix(in oklab, var(--color-coollabs) 9%, transparent), transparent 34rem),
|
||
var(--coollabs-canvas);
|
||
color: #171717;
|
||
}
|
||
|
||
.dark .auth-shell {
|
||
color: var(--color-fg);
|
||
}
|
||
|
||
.auth-shell-content {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
min-height: 100dvh;
|
||
width: 100%;
|
||
padding: 2rem 1rem;
|
||
}
|
||
|
||
.auth-card {
|
||
width: min(100%, 27rem);
|
||
overflow: hidden;
|
||
border-radius: 10px;
|
||
background: var(--coollabs-elevated);
|
||
box-shadow: 0 0 0 1px var(--coollabs-hairline), 0 20px 60px rgb(0 0 0 / 0.14);
|
||
}
|
||
|
||
.auth-card-heading {
|
||
padding: 1.25rem 1.25rem 1rem;
|
||
}
|
||
|
||
.auth-card-heading h1 {
|
||
margin: 0;
|
||
font-size: 1.25rem;
|
||
line-height: 1.75rem;
|
||
font-weight: 600;
|
||
letter-spacing: -0.015em;
|
||
color: #171717;
|
||
}
|
||
|
||
.dark .auth-card-heading h1 {
|
||
color: var(--color-fg);
|
||
}
|
||
|
||
.auth-card-heading p {
|
||
margin-top: 0.25rem;
|
||
max-width: 38ch;
|
||
font-size: 0.8125rem;
|
||
line-height: 1.25rem;
|
||
color: var(--coollabs-subtle);
|
||
}
|
||
|
||
.auth-card-body {
|
||
border-radius: 10px;
|
||
padding: 1.25rem;
|
||
background: var(--coollabs-base);
|
||
box-shadow: 0 0 0 1px var(--coollabs-fill);
|
||
}
|
||
|
||
.auth-card-footer {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 0.375rem;
|
||
min-height: 3rem;
|
||
padding: 0.75rem 1.25rem;
|
||
font-size: 0.8125rem;
|
||
color: var(--coollabs-subtle);
|
||
}
|
||
|
||
.auth-text-link {
|
||
font-size: 0.8125rem;
|
||
font-weight: 500;
|
||
color: #525252;
|
||
text-underline-offset: 3px;
|
||
transition: color 0.15s ease;
|
||
}
|
||
|
||
.dark .auth-text-link {
|
||
color: var(--color-fg-dim);
|
||
}
|
||
|
||
.auth-text-link:hover {
|
||
color: var(--color-accent);
|
||
text-decoration: underline;
|
||
}
|
||
|
||
.auth-divider {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.75rem;
|
||
color: var(--coollabs-subtle);
|
||
font-size: 0.75rem;
|
||
}
|
||
|
||
.auth-divider::before,
|
||
.auth-divider::after {
|
||
content: '';
|
||
height: 1px;
|
||
flex: 1;
|
||
background: var(--coollabs-fill);
|
||
}
|
||
|
||
.auth-guidance {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 0.5rem;
|
||
border-radius: 8px;
|
||
padding: 0.625rem 0.75rem;
|
||
background: var(--coollabs-recessed);
|
||
color: var(--coollabs-subtle);
|
||
font-size: 0.75rem;
|
||
line-height: 1rem;
|
||
}
|
||
|
||
/* Public error surfaces (401/404/500/…). Match auth shell canvas + accent. */
|
||
.error-page-body {
|
||
width: 100vw;
|
||
height: 100dvh;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.error-shell {
|
||
height: 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 100%;
|
||
padding: 2rem 1rem;
|
||
background:
|
||
radial-gradient(circle at 50% 0%, color-mix(in oklab, var(--color-coollabs) 9%, transparent), transparent 34rem),
|
||
var(--coollabs-canvas);
|
||
color: #171717;
|
||
}
|
||
|
||
.dark .error-shell {
|
||
color: var(--color-fg);
|
||
}
|
||
|
||
.error-shell-content {
|
||
width: min(100%, 32rem);
|
||
}
|
||
|
||
.error-code {
|
||
margin: 0;
|
||
font-family: var(--font-mono);
|
||
font-size: clamp(3.5rem, 12vw, 5.5rem);
|
||
font-weight: 600;
|
||
line-height: 1;
|
||
letter-spacing: -0.03em;
|
||
color: var(--color-coollabs);
|
||
}
|
||
|
||
.dark .error-code {
|
||
color: var(--color-brand-light);
|
||
}
|
||
|
||
.error-code-danger {
|
||
color: var(--color-error);
|
||
}
|
||
|
||
.dark .error-code-danger {
|
||
color: var(--color-error);
|
||
}
|
||
|
||
.error-title {
|
||
margin: 1rem 0 0;
|
||
font-size: 1.25rem;
|
||
line-height: 1.75rem;
|
||
font-weight: 600;
|
||
letter-spacing: -0.015em;
|
||
color: #171717;
|
||
}
|
||
|
||
.dark .error-title {
|
||
color: var(--color-fg);
|
||
}
|
||
|
||
.error-description {
|
||
margin: 0.375rem 0 0;
|
||
max-width: 42ch;
|
||
font-size: 0.875rem;
|
||
line-height: 1.375rem;
|
||
color: var(--coollabs-subtle);
|
||
}
|
||
|
||
.error-extra {
|
||
margin-top: 1.25rem;
|
||
}
|
||
|
||
.error-extra .error-message {
|
||
margin: 0;
|
||
padding: 0.75rem 0.875rem;
|
||
border-radius: 8px;
|
||
background: color-mix(in oklab, var(--color-error) 8%, transparent);
|
||
color: var(--color-error);
|
||
font-size: 0.8125rem;
|
||
line-height: 1.25rem;
|
||
word-break: break-word;
|
||
}
|
||
|
||
.error-extra details {
|
||
margin: 0;
|
||
font-size: 0.8125rem;
|
||
line-height: 1.25rem;
|
||
color: var(--coollabs-subtle);
|
||
}
|
||
|
||
.error-extra details summary {
|
||
cursor: pointer;
|
||
font-weight: 500;
|
||
color: #525252;
|
||
list-style: none;
|
||
}
|
||
|
||
.dark .error-extra details summary {
|
||
color: var(--color-fg-dim);
|
||
}
|
||
|
||
.error-extra details summary::-webkit-details-marker {
|
||
display: none;
|
||
}
|
||
|
||
.error-extra details summary:hover {
|
||
color: var(--color-accent);
|
||
}
|
||
|
||
.error-extra details ul,
|
||
.error-proxy-help ul {
|
||
margin: 0.5rem 0 0;
|
||
padding-left: 1.125rem;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.375rem;
|
||
}
|
||
|
||
.error-extra details code,
|
||
.error-proxy-help code {
|
||
font-family: var(--font-mono);
|
||
font-size: 0.75rem;
|
||
padding: 0.05rem 0.3rem;
|
||
border-radius: 4px;
|
||
background: var(--coollabs-recessed);
|
||
}
|
||
|
||
.error-actions {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
margin-top: 1.75rem;
|
||
}
|
||
|
||
/* Layer card root: soft outer box around a darker inset well (the body).
|
||
overflow stays visible so floating panels (listbox, tooltips) can escape
|
||
the card. */
|
||
.application-settings-section,
|
||
.application-settings-section-body,
|
||
.empty-state {
|
||
--card-bg: rgb(0 0 0 / 0.75%);
|
||
/* The page color under a card. The card tints are translucent, so
|
||
top-level cards paint them over this color (see below). */
|
||
--card-underlay: var(--color-neutral-50);
|
||
--card-border: rgb(0 0 0 / 9%);
|
||
--well-bg: #ffffff;
|
||
--well-border: rgb(0 0 0 / 8%);
|
||
--well-shadow: 0 1px 2px rgb(0 0 0 / 6%);
|
||
/* Light mode has no dim/faint fg tokens; these keep WCAG AA on the card. */
|
||
--card-fg: oklch(14.5% 0 0);
|
||
--card-fg-dim: oklch(40% 0 0);
|
||
--card-fg-faint: var(--coollabs-subtle);
|
||
}
|
||
|
||
/* Layout only for cards: wells set their own display (often a Tailwind grid). */
|
||
.application-settings-section,
|
||
.empty-state {
|
||
margin: 0;
|
||
display: flex;
|
||
width: 100%;
|
||
flex-direction: column;
|
||
border: 1px solid var(--card-border);
|
||
border-radius: 0.75rem;
|
||
padding: 0.375rem;
|
||
background: var(--card-bg);
|
||
box-shadow: 0 1px 2px rgb(0 0 0 / 5%);
|
||
/* keep anchored scrolls (scrollIntoView) clear of the fixed topbar and page padding */
|
||
scroll-margin-top: 7rem;
|
||
}
|
||
|
||
.dark .application-settings-section,
|
||
.dark .application-settings-section-body,
|
||
.dark .empty-state {
|
||
--card-bg: rgb(255 255 255 / 6%);
|
||
--card-underlay: var(--color-app);
|
||
--card-border: rgb(255 255 255 / 8%);
|
||
--well-bg: rgb(0 0 0 / 34%);
|
||
--well-border: rgb(255 255 255 / 7%);
|
||
--well-shadow: 0 1px 2px rgb(0 0 0 / 25%);
|
||
--card-fg: var(--color-fg);
|
||
--card-fg-dim: var(--color-fg-dim);
|
||
--card-fg-faint: var(--color-fg-faint);
|
||
}
|
||
|
||
html[data-theme="custom"]:not(.dark) :is(.application-settings-section, .application-settings-section-body, .empty-state) {
|
||
--card-underlay: var(--coollabs-canvas);
|
||
}
|
||
|
||
/* Top-level cards and wells sit on the page: paint the translucent tint over
|
||
the page color, so a page backdrop (the floating navigation dots) does not
|
||
show through. Nested ones keep the tint on top of their parent. */
|
||
:is(.application-settings-section, .empty-state):not(:is(.application-settings-section, .application-settings-section-body) *) {
|
||
background: linear-gradient(var(--card-bg), var(--card-bg)), var(--card-underlay);
|
||
}
|
||
|
||
.application-settings-section-body:not(.application-settings-section *) {
|
||
background: linear-gradient(var(--well-bg), var(--well-bg)), var(--card-underlay);
|
||
}
|
||
|
||
/* Dark mode: top-level cards are a near-canvas shade (#121212 under 34% black),
|
||
and their well is #121212, a step above it. The top bar matches the canvas
|
||
(see the brand shell below). */
|
||
.dark :is(.application-settings-section, .empty-state):not(:is(.application-settings-section, .application-settings-section-body) *) {
|
||
background: color-mix(in srgb, var(--color-card) 66%, black);
|
||
box-shadow: none;
|
||
}
|
||
|
||
.dark :is(.application-settings-section, .empty-state):not(:is(.application-settings-section, .application-settings-section-body) *) > :is(.application-settings-section-body, .empty-state-well) {
|
||
--well-bg: var(--color-card);
|
||
--well-shadow: none;
|
||
}
|
||
|
||
/* Dark mode: collection tiles, pagination bars, and tables match the cards.
|
||
A tile is #121212, like a card well. A table (a shell with a header row) has
|
||
#121212 rows and a header row in the darker card outer color. */
|
||
.dark [class~="rounded-xl"][class~="dark:bg-white/[0.05]"] {
|
||
background-color: var(--color-card) !important;
|
||
border-color: rgb(255 255 255 / 0.08) !important;
|
||
box-shadow: none;
|
||
}
|
||
|
||
.dark [class~="rounded-xl"][class~="dark:bg-white/[0.05]"] > [class~="border-b"]:first-child {
|
||
background: color-mix(in srgb, var(--color-card) 66%, black) !important;
|
||
}
|
||
|
||
/* Brief accent ring when a settings nav sub-item scrolls a section into view.
|
||
Use a real border on ::after (not animated multi-layer box-shadow) so the
|
||
ring is the same weight on every side — box-shadow rings look thicker on
|
||
the header edge next to the elevated strip. */
|
||
@keyframes application-settings-section-highlight {
|
||
0% {
|
||
opacity: 0;
|
||
}
|
||
|
||
15%,
|
||
60% {
|
||
opacity: 1;
|
||
}
|
||
|
||
100% {
|
||
opacity: 0;
|
||
}
|
||
}
|
||
|
||
.application-settings-section.is-section-highlight {
|
||
position: relative;
|
||
}
|
||
|
||
.application-settings-section.is-section-highlight::after {
|
||
content: '';
|
||
position: absolute;
|
||
inset: 0;
|
||
z-index: 5;
|
||
border-radius: inherit;
|
||
border: 0.5px solid var(--color-accent);
|
||
pointer-events: none;
|
||
animation: application-settings-section-highlight 500ms ease-out forwards;
|
||
}
|
||
|
||
/* Modals reuse the layer-card shell. The card tint is translucent, so paint it
|
||
over the page canvas to keep the page behind the modal hidden. */
|
||
.application-settings-section.application-settings-form {
|
||
background: linear-gradient(var(--card-bg), var(--card-bg)), var(--coollabs-canvas);
|
||
}
|
||
|
||
/* Dark mode: the modal markup adds an inline hairline ring outside the card
|
||
border, which reads as a light double edge. Keep only the card border. */
|
||
.dark .application-settings-section.application-settings-form {
|
||
box-shadow: var(--shadow-modal) !important;
|
||
}
|
||
|
||
/* Modals size to content on large screens */
|
||
@media (min-width: 1024px) {
|
||
.application-settings-section.application-settings-form {
|
||
width: auto;
|
||
}
|
||
}
|
||
|
||
/* Layer card header: small title row on the outer box */
|
||
.application-settings-section > :is(header, .application-settings-section-header) {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 1rem;
|
||
padding: 0.25rem 0.5rem 0.625rem;
|
||
font-weight: 500;
|
||
color: var(--card-fg-faint);
|
||
}
|
||
|
||
.application-settings-section > :is(header, .application-settings-section-header) :is(h2, h3) {
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
line-height: 1.25rem;
|
||
letter-spacing: 0;
|
||
color: var(--card-fg-dim);
|
||
}
|
||
|
||
.application-settings-section > :is(header, .application-settings-section-header) :is(h2, h3) .info-helper {
|
||
color: inherit;
|
||
}
|
||
|
||
.application-settings-section > .application-settings-section-header p {
|
||
margin-top: 0.125rem;
|
||
font-size: 11px;
|
||
line-height: 1rem;
|
||
color: var(--card-fg-faint);
|
||
}
|
||
|
||
.application-settings-section > :is(header, .application-settings-section-header) > div:nth-child(2) > a:not(.button) {
|
||
font-size: 11px;
|
||
font-weight: 500;
|
||
color: var(--card-fg-faint);
|
||
}
|
||
|
||
.application-settings-section > :is(header, .application-settings-section-header) > div:nth-child(2) > a:not(.button):hover {
|
||
color: var(--card-fg);
|
||
}
|
||
|
||
/* Layer card body: the inset well */
|
||
.application-settings-section-body {
|
||
position: relative;
|
||
border: 1px solid var(--well-border);
|
||
border-radius: 0.5rem;
|
||
background: var(--well-bg);
|
||
box-shadow: var(--well-shadow);
|
||
padding: 1rem;
|
||
}
|
||
|
||
/* The dark well is darker than the canvas, so controls inside it use a darker,
|
||
translucent fill and border (like haruvo) instead of the global opaque ones. */
|
||
.dark .application-settings-section-body {
|
||
--coollabs-recessed: rgb(255 255 255 / 3%);
|
||
--coollabs-line: rgb(255 255 255 / 8%);
|
||
}
|
||
|
||
/* A well without a card around it sits straight on the canvas. In dark mode it
|
||
matches a top-level card: #121212 content, and a table header row in the
|
||
darker card outer color. */
|
||
.dark .application-settings-section-body:not(.application-settings-section *) {
|
||
--well-bg: var(--color-card);
|
||
--well-border: var(--card-border);
|
||
--well-shadow: none;
|
||
}
|
||
|
||
.dark .application-settings-section-body:not(.application-settings-section *) .data-table-header {
|
||
background: color-mix(in srgb, var(--color-card) 66%, black);
|
||
}
|
||
|
||
/* Floating panels in the well must stay opaque: restore the global dark values. */
|
||
.dark .application-settings-section-body :is(.listbox-panel, .searchable-listbox-panel, [role="listbox"], [role="menu"]) {
|
||
--coollabs-recessed: oklch(25.2% 0 0);
|
||
--coollabs-line: oklch(36% 0 0);
|
||
}
|
||
|
||
.settings-mobile-header {
|
||
margin-bottom: 1.25rem;
|
||
}
|
||
|
||
.settings-mobile-title {
|
||
font-size: 1.5rem;
|
||
font-weight: 600;
|
||
line-height: 1.75rem;
|
||
letter-spacing: -0.025em;
|
||
color: #000000;
|
||
}
|
||
|
||
.dark .settings-mobile-title {
|
||
color: #ffffff;
|
||
}
|
||
|
||
.settings-mobile-description {
|
||
margin-top: 0.25rem;
|
||
font-size: 0.8125rem;
|
||
line-height: 1.25rem;
|
||
color: #737373;
|
||
}
|
||
|
||
.dark .settings-mobile-description {
|
||
color: var(--coollabs-subtle);
|
||
}
|
||
|
||
/* All settings sidebars share one alignment rule. Keep the sticky offset equal
|
||
to the desktop main-content top padding so the menu does not jump upward
|
||
when it changes from its natural position to sticky positioning. */
|
||
.server-settings-workspace > :not(.application-settings-navigation) {
|
||
min-width: 0;
|
||
}
|
||
|
||
/* Below xl the rail is a grid above the content; a canvas has no room for it. */
|
||
@media (max-width: 1279.98px) {
|
||
.app-main-full .application-settings-navigation {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
/* A resource menu that the main sidebar (and the mobile drawer) shows instead
|
||
(navbar #main-nav-context-{depth}). */
|
||
.application-settings-navigation.in-main-nav {
|
||
display: none;
|
||
}
|
||
|
||
.application-settings-navigation.in-main-nav ~ * {
|
||
grid-column: 1 / -1;
|
||
}
|
||
|
||
@media (min-width: 1280px) {
|
||
/* Fixed like the main sidebar (3rem → bottom); left tracks its width (--sidebar-w).
|
||
Fixed leaves the grid, so the content sibling is pinned to column 2 below. */
|
||
.application-settings-navigation {
|
||
position: fixed;
|
||
top: 3rem;
|
||
left: var(--sidebar-w, 14rem);
|
||
width: calc(210px + 2.5rem);
|
||
height: calc(100dvh - 3rem);
|
||
overflow-x: hidden;
|
||
overflow-y: auto;
|
||
overscroll-behavior: contain;
|
||
scrollbar-width: thin;
|
||
scrollbar-color: var(--coollabs-fill) transparent;
|
||
scrollbar-gutter: stable;
|
||
--rail-fill: var(--coollabs-elevated);
|
||
padding: 0.5rem;
|
||
background: var(--rail-fill);
|
||
border-right: 1px solid var(--coollabs-hairline);
|
||
transition: left 200ms ease;
|
||
}
|
||
|
||
.application-settings-navigation ~ * {
|
||
grid-column: 2;
|
||
}
|
||
|
||
html:not(.dark) .application-settings-navigation {
|
||
--rail-fill: var(--coollabs-base);
|
||
}
|
||
|
||
.application-settings-navigation::-webkit-scrollbar {
|
||
width: 6px;
|
||
}
|
||
|
||
.application-settings-navigation::-webkit-scrollbar-track {
|
||
background: transparent;
|
||
}
|
||
|
||
.application-settings-navigation::-webkit-scrollbar-thumb {
|
||
border-radius: 9999px;
|
||
background: var(--coollabs-fill);
|
||
}
|
||
|
||
.application-settings-navigation::-webkit-scrollbar-thumb:hover {
|
||
background: var(--coollabs-line);
|
||
}
|
||
|
||
/* Canvas pages with a rail (server canvas): the rail stays above the canvas,
|
||
and the canvas starts after the rail. */
|
||
.app-main-full .application-settings-navigation {
|
||
z-index: 20;
|
||
}
|
||
|
||
.app-main-full .application-settings-navigation:not(.in-main-nav) ~ [data-environment-canvas] {
|
||
margin-left: calc(210px + 2.5rem);
|
||
}
|
||
|
||
/* Embeds: keep the surface, drop the fixed positioning. */
|
||
.application-settings-navigation.is-flush {
|
||
position: static;
|
||
inset: auto;
|
||
width: auto;
|
||
height: auto;
|
||
overflow: visible;
|
||
transition: none;
|
||
}
|
||
}
|
||
|
||
/* Honor reduced-motion for the settings nav disclosure/accordion and the mobile
|
||
sidebar sheet: keep open/close functional but drop the height/slide/fade. */
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.application-settings-navigation,
|
||
.application-settings-navigation *,
|
||
.mobile-sidebar-sheet * {
|
||
transition-duration: 0.01ms !important;
|
||
animation-duration: 0.01ms !important;
|
||
}
|
||
}
|
||
|
||
/* Field labels */
|
||
.application-settings-workspace label,
|
||
.application-settings-form label {
|
||
margin-bottom: 0.375rem;
|
||
font-size: 13px;
|
||
font-weight: 500;
|
||
color: var(--coollabs-subtle);
|
||
}
|
||
|
||
/*
|
||
* Labels that sit in a fixed-height control row (with optional helper icon) must not
|
||
* add their own bottom margin — the row wrapper owns spacing so side-by-side inputs align.
|
||
*/
|
||
.application-settings-workspace .mb-0\!,
|
||
.application-settings-form .mb-0\!,
|
||
.application-settings-workspace label.mb-0\!,
|
||
.application-settings-form label.mb-0\! {
|
||
margin-bottom: 0 !important;
|
||
}
|
||
|
||
/* Inputs & selects: recessed fill + line border (surface hierarchy) */
|
||
.application-settings-workspace .input,
|
||
.application-settings-workspace .select,
|
||
.application-settings-workspace .listbox-trigger,
|
||
.application-settings-form .input,
|
||
.application-settings-form .select,
|
||
.application-settings-form .listbox-trigger {
|
||
height: 2rem;
|
||
border-radius: 8px;
|
||
border-color: var(--coollabs-line);
|
||
background-color: var(--coollabs-recessed);
|
||
padding-left: 0.75rem;
|
||
padding-right: 0.75rem;
|
||
font-size: 0.875rem;
|
||
box-shadow: none !important;
|
||
}
|
||
|
||
/* Reserve the trailing area used by the native select chevron. */
|
||
.application-settings-workspace .select,
|
||
.application-settings-form .select {
|
||
padding-right: 2.5rem;
|
||
}
|
||
|
||
@media (max-width: 767px) {
|
||
.application-settings-workspace .select,
|
||
.application-settings-form .select {
|
||
font-size: 0.75rem !important;
|
||
line-height: 1.25rem;
|
||
}
|
||
}
|
||
|
||
/* Keep password toggle / copy button clear of the value inside denser settings inputs */
|
||
.application-settings-workspace .input.input-with-password-toggle,
|
||
.application-settings-workspace .input[type="password"],
|
||
.application-settings-workspace .input.input-with-copy-button,
|
||
.application-settings-form .input.input-with-password-toggle,
|
||
.application-settings-form .input[type="password"],
|
||
.application-settings-form .input.input-with-copy-button {
|
||
padding-right: 2.5rem;
|
||
}
|
||
|
||
.application-settings-workspace .input.input-with-copy-and-peek,
|
||
.application-settings-form .input.input-with-copy-and-peek {
|
||
padding-right: 4.25rem;
|
||
}
|
||
|
||
.application-settings-workspace .input.input-with-stepper,
|
||
.application-settings-form .input.input-with-stepper {
|
||
padding-right: 4.25rem;
|
||
}
|
||
|
||
.application-settings-workspace .number-stepper button:last-child,
|
||
.application-settings-form .number-stepper button:last-child {
|
||
border-radius: 0 7px 7px 0;
|
||
}
|
||
|
||
.application-settings-workspace .input:focus-visible,
|
||
.application-settings-workspace .select:focus-visible,
|
||
.application-settings-form .input:focus-visible,
|
||
.application-settings-form .select:focus-visible {
|
||
border-color: var(--color-accent);
|
||
box-shadow: none !important;
|
||
}
|
||
|
||
.application-settings-workspace textarea.input,
|
||
.application-settings-form textarea.input {
|
||
height: auto;
|
||
min-height: 5rem;
|
||
padding-top: 0.5rem;
|
||
padding-bottom: 0.5rem;
|
||
line-height: 1.25rem;
|
||
}
|
||
|
||
.application-settings-workspace .form-control,
|
||
.application-settings-form .form-control {
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.dark .application-settings-workspace .form-control:hover,
|
||
.dark .application-settings-form .form-control:hover {
|
||
background: transparent;
|
||
}
|
||
|
||
/* Main sidebar: only the menu scrolls on short or zoomed-in screens; the
|
||
footer stays visible. Hide the scrollbar so it does not take width from the
|
||
narrow collapsed rail and push the icons aside. */
|
||
.sidebar-menu {
|
||
scrollbar-width: none; /* Firefox */
|
||
}
|
||
|
||
.sidebar-menu::-webkit-scrollbar {
|
||
display: none; /* Chrome/Safari */
|
||
}
|
||
|
||
/* Unified resource heading bar: hide tab-strip scrollbars; menus/actions stay overflow-visible for dropdowns */
|
||
.resource-heading-navbar {
|
||
overflow: visible;
|
||
}
|
||
|
||
.resource-heading-tabs {
|
||
scrollbar-width: none; /* Firefox */
|
||
-ms-overflow-style: none; /* IE/legacy Edge */
|
||
}
|
||
|
||
.resource-heading-tabs::-webkit-scrollbar {
|
||
display: none; /* Chrome/Safari */
|
||
width: 0;
|
||
height: 0;
|
||
}
|
||
|
||
/*
|
||
* Kumo-style overflow controls for resource heading tabs.
|
||
* Circular chevrons sit over gradient fades when more tabs are off-screen.
|
||
*/
|
||
.resource-heading-tabs-scroller {
|
||
--resource-heading-tabs-fade: oklch(97.02% 0 0); /* neutral-100, matches pill track */
|
||
}
|
||
|
||
.dark .resource-heading-tabs-scroller {
|
||
/* Matches dark:bg-white/[0.035] pill track + panel so fade blends */
|
||
--resource-heading-tabs-fade: color-mix(in srgb, var(--color-panel, #0a0a0a) 96.5%, white);
|
||
}
|
||
|
||
.resource-heading-tabs-control {
|
||
position: absolute;
|
||
inset-block: 0;
|
||
z-index: 3;
|
||
display: flex;
|
||
align-items: center;
|
||
width: 2.5rem;
|
||
margin: 0;
|
||
padding: 0;
|
||
border: 0;
|
||
pointer-events: none;
|
||
opacity: 0;
|
||
transition: opacity 0.15s ease;
|
||
}
|
||
|
||
.resource-heading-tabs-control.is-visible {
|
||
pointer-events: auto;
|
||
opacity: 1;
|
||
}
|
||
|
||
.resource-heading-tabs-control.is-start {
|
||
left: 0;
|
||
justify-content: flex-start;
|
||
background: linear-gradient(
|
||
to right,
|
||
var(--resource-heading-tabs-fade) 35%,
|
||
color-mix(in srgb, var(--resource-heading-tabs-fade) 85%, transparent) 70%,
|
||
transparent
|
||
);
|
||
}
|
||
|
||
.resource-heading-tabs-control.is-end {
|
||
right: 0;
|
||
justify-content: flex-end;
|
||
background: linear-gradient(
|
||
to left,
|
||
var(--resource-heading-tabs-fade) 35%,
|
||
color-mix(in srgb, var(--resource-heading-tabs-fade) 85%, transparent) 70%,
|
||
transparent
|
||
);
|
||
}
|
||
|
||
.resource-heading-tabs-control-icon {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 1.5rem;
|
||
height: 1.5rem;
|
||
margin-inline: 0.25rem;
|
||
border-radius: 9999px;
|
||
color: #737373;
|
||
background: var(--coollabs-base);
|
||
box-shadow:
|
||
0 1px 2px rgba(0, 0, 0, 0.06),
|
||
0 0 0 1px rgba(0, 0, 0, 0.08);
|
||
transition:
|
||
background-color 0.15s ease,
|
||
color 0.15s ease,
|
||
box-shadow 0.15s ease;
|
||
}
|
||
|
||
.dark .resource-heading-tabs-control-icon {
|
||
color: var(--color-fg-dim, #a3a3a3);
|
||
background: var(--color-raised, #171717);
|
||
box-shadow:
|
||
0 1px 2px rgba(0, 0, 0, 0.35),
|
||
0 0 0 1px rgba(255, 255, 255, 0.1);
|
||
}
|
||
|
||
.resource-heading-tabs-control:hover .resource-heading-tabs-control-icon,
|
||
.resource-heading-tabs-control:focus-visible .resource-heading-tabs-control-icon {
|
||
color: #000000;
|
||
background: #ffffff;
|
||
}
|
||
|
||
.dark .resource-heading-tabs-control:hover .resource-heading-tabs-control-icon,
|
||
.dark .resource-heading-tabs-control:focus-visible .resource-heading-tabs-control-icon {
|
||
color: var(--color-fg, #fafafa);
|
||
background: var(--color-raised, #171717);
|
||
}
|
||
|
||
.resource-heading-tabs-control:focus {
|
||
outline: none;
|
||
}
|
||
|
||
.resource-heading-tabs-control:focus-visible .resource-heading-tabs-control-icon {
|
||
box-shadow:
|
||
0 0 0 2px color-mix(in srgb, var(--color-coollabs, #6b16ed) 45%, transparent),
|
||
0 0 0 1px rgba(0, 0, 0, 0.08);
|
||
}
|
||
|
||
.resource-heading-menus,
|
||
.resource-heading-actions {
|
||
overflow: visible;
|
||
}
|
||
|
||
/* Heading action group: buttons + dropdown triggers styled like the tabs */
|
||
.application-heading-actions .button,
|
||
.application-heading-actions .app-tab,
|
||
.application-heading-actions .relative > button[x-ref='trigger'] {
|
||
height: 2rem;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.375rem;
|
||
border: none;
|
||
background: transparent;
|
||
box-shadow: none;
|
||
padding: 0 0.625rem;
|
||
border-radius: 6px;
|
||
font-size: 13px;
|
||
font-weight: 500;
|
||
color: #737373;
|
||
min-width: 0;
|
||
white-space: nowrap;
|
||
transition: background-color 0.15s, color 0.15s;
|
||
}
|
||
|
||
.dark .application-heading-actions .button,
|
||
.dark .application-heading-actions .app-tab,
|
||
.dark .application-heading-actions .relative > button[x-ref='trigger'] {
|
||
color: var(--color-fg-dim);
|
||
}
|
||
|
||
.application-heading-actions .button:hover:not(:disabled),
|
||
.application-heading-actions .app-tab:hover,
|
||
.application-heading-actions .relative > button[x-ref='trigger']:hover {
|
||
background: rgba(0, 0, 0, 0.05);
|
||
color: #000000;
|
||
}
|
||
|
||
.dark .application-heading-actions .button:hover:not(:disabled),
|
||
.dark .application-heading-actions .app-tab:hover,
|
||
.dark .application-heading-actions .relative > button[x-ref='trigger']:hover {
|
||
background: rgba(255, 255, 255, 0.06);
|
||
color: var(--color-fg);
|
||
}
|
||
|
||
.application-heading-actions .button-highlighted,
|
||
.dark .application-heading-actions .button-highlighted,
|
||
.application-heading-actions .button-highlighted:hover:not(:disabled),
|
||
.dark .application-heading-actions .button-highlighted:hover:not(:disabled) {
|
||
@apply button-highlighted;
|
||
}
|
||
|
||
/*
|
||
* Active primary tab styles.
|
||
* The base rules above set background/color/box-shadow with higher specificity
|
||
* than Tailwind utilities (bg-brand/15, text-brand-light, ring-*), so active
|
||
* tabs need an explicit override or they look identical to inactive ones.
|
||
*/
|
||
.application-heading-actions .app-tab[aria-current='page'],
|
||
.application-heading-actions .app-tab.app-tab-active {
|
||
background: color-mix(in srgb, var(--color-coollabs) 10%, transparent);
|
||
color: var(--color-coollabs);
|
||
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-coollabs) 25%, transparent);
|
||
}
|
||
|
||
.application-heading-actions .app-tab[aria-current='page']:hover,
|
||
.application-heading-actions .app-tab.app-tab-active:hover {
|
||
background: color-mix(in srgb, var(--color-coollabs) 15%, transparent);
|
||
color: var(--color-coollabs);
|
||
}
|
||
|
||
.dark .application-heading-actions .app-tab[aria-current='page'],
|
||
.dark .application-heading-actions .app-tab.app-tab-active {
|
||
background: color-mix(in srgb, var(--color-brand) 15%, transparent);
|
||
color: var(--color-brand-light);
|
||
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-brand-light) 25%, transparent);
|
||
}
|
||
|
||
.dark .application-heading-actions .app-tab[aria-current='page']:hover,
|
||
.dark .application-heading-actions .app-tab.app-tab-active:hover {
|
||
background: color-mix(in srgb, var(--color-brand) 20%, transparent);
|
||
color: var(--color-brand-light);
|
||
}
|
||
|
||
.application-heading-actions .relative > button[x-ref='trigger'] {
|
||
padding-right: 0.625rem;
|
||
}
|
||
|
||
/* dropdown chevron: inline instead of absolutely positioned */
|
||
.application-heading-actions .relative > button[x-ref='trigger'] > svg {
|
||
position: static;
|
||
margin: 0;
|
||
width: 0.875rem;
|
||
height: 0.875rem;
|
||
flex-shrink: 0;
|
||
opacity: 0.6;
|
||
}
|
||
|
||
/* dropdown label: kill the h-full/flex-col title wrapper so text centers
|
||
on the same baseline as the sibling buttons */
|
||
.application-heading-actions .relative > button[x-ref='trigger'] > span {
|
||
display: inline-flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
height: auto;
|
||
line-height: 1.25;
|
||
}
|
||
|
||
.application-heading-actions .button svg,
|
||
.application-heading-actions .app-tab svg {
|
||
width: 0.875rem;
|
||
height: 0.875rem;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.split-action {
|
||
display: inline-flex;
|
||
}
|
||
|
||
.split-action-main,
|
||
.split-action-caret {
|
||
@apply button-highlighted;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 0.375rem;
|
||
height: 2rem;
|
||
font-size: 13px;
|
||
font-weight: 500;
|
||
white-space: nowrap;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.split-action-main {
|
||
flex: 1 1 auto;
|
||
min-width: 0;
|
||
padding: 0 0.625rem;
|
||
border-radius: 6px 0 0 6px;
|
||
}
|
||
|
||
.split-action-caret {
|
||
flex-shrink: 0;
|
||
width: 1.75rem;
|
||
border-left: 1px solid rgba(255, 255, 255, 0.25);
|
||
border-radius: 0 6px 6px 0;
|
||
}
|
||
|
||
.split-action > .split-action-main:only-of-type {
|
||
border-radius: 6px;
|
||
}
|
||
|
||
.split-action-main:disabled,
|
||
.split-action-caret:disabled {
|
||
cursor: not-allowed;
|
||
opacity: 0.45;
|
||
}
|
||
|
||
/* Same physical edge as highlighted .button: hover lifts, press sinks. */
|
||
.split-action-main:not(:disabled),
|
||
.split-action-caret:not(:disabled) {
|
||
--button-depth-color: var(--color-coollabs-300);
|
||
box-shadow: 0 2px 0 var(--button-depth-color);
|
||
}
|
||
|
||
.dark .split-action-main:not(:disabled),
|
||
.dark .split-action-caret:not(:disabled) {
|
||
--button-depth-color: var(--color-brand-depth);
|
||
}
|
||
|
||
.split-action-main:not(:disabled):hover,
|
||
.split-action-caret:not(:disabled):hover {
|
||
transform: translateY(-1px);
|
||
box-shadow: 0 3px 0 var(--button-depth-color);
|
||
}
|
||
|
||
.split-action-main:focus-visible,
|
||
.split-action-caret:focus-visible {
|
||
outline: 1px solid var(--color-accent);
|
||
outline-offset: 2px;
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
/* Custom listbox (replaces native <select>) */
|
||
.listbox-trigger {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 0.5rem;
|
||
width: 100%;
|
||
min-width: 0;
|
||
height: 2rem;
|
||
padding: 0 0.625rem 0 0.75rem;
|
||
overflow: hidden;
|
||
border-radius: 8px;
|
||
border: 1px solid var(--coollabs-line);
|
||
background: var(--coollabs-recessed);
|
||
font-size: 0.875rem;
|
||
color: #000000;
|
||
text-align: left;
|
||
cursor: pointer;
|
||
}
|
||
|
||
/* Selected value: fill remaining space and ellipsize long labels
|
||
(multi-select summaries, long option names, timezone paths, etc.). */
|
||
.listbox-trigger-label {
|
||
min-width: 0;
|
||
flex: 1 1 0%;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
text-align: left;
|
||
}
|
||
|
||
.dark .listbox-trigger {
|
||
color: var(--color-fg);
|
||
}
|
||
|
||
.listbox-trigger:disabled {
|
||
cursor: not-allowed;
|
||
background-color: var(--color-neutral-100);
|
||
color: var(--color-neutral-400);
|
||
}
|
||
|
||
.dark .listbox-trigger:disabled {
|
||
background-color: color-mix(in oklab, var(--color-white) 3%, transparent);
|
||
color: var(--color-fg-faint);
|
||
}
|
||
|
||
.listbox-trigger:focus-visible {
|
||
outline: none;
|
||
border-color: var(--color-accent);
|
||
box-shadow: none;
|
||
}
|
||
|
||
.listbox-panel {
|
||
position: absolute;
|
||
top: calc(100% + 0.25rem);
|
||
left: 0;
|
||
z-index: 30;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 1px;
|
||
/* At least as wide as the trigger, but never narrower than a readable
|
||
menu. Unlayered so it wins over layered Tailwind min-w-* utilities —
|
||
floating action menus should still set an explicit width with ! when
|
||
they need a fixed size (e.g. w-52!). */
|
||
min-width: max(100%, 13rem);
|
||
width: max-content;
|
||
max-width: min(24rem, calc(100vw - 1.5rem));
|
||
max-height: 16rem;
|
||
overflow-x: hidden;
|
||
overflow-y: auto;
|
||
padding: 0.25rem;
|
||
border-radius: 10px;
|
||
border: 1px solid var(--coollabs-line);
|
||
background: var(--coollabs-recessed);
|
||
box-shadow: var(--shadow-dropdown);
|
||
}
|
||
|
||
@media (max-width: 767px) {
|
||
/* The mobile top bar's backdrop filter makes it the containing block for
|
||
fixed descendants. Keep this menu anchored below its trigger instead
|
||
of centering it within the short top bar. */
|
||
.listbox-panel.top-user-menu-panel {
|
||
position: absolute !important;
|
||
top: calc(100% + 0.25rem) !important;
|
||
right: 0 !important;
|
||
left: auto !important;
|
||
max-height: calc(100dvh - 4.5rem) !important;
|
||
overflow-y: auto !important;
|
||
transform: none !important;
|
||
}
|
||
}
|
||
|
||
.listbox-option {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 0.5rem;
|
||
width: 100%;
|
||
min-height: 2rem;
|
||
height: auto;
|
||
padding: 0.375rem 0.5rem;
|
||
border-radius: 6px;
|
||
font-size: 0.875rem;
|
||
line-height: 1.25rem;
|
||
color: #000000;
|
||
text-align: left;
|
||
cursor: pointer;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.dark .listbox-option {
|
||
color: var(--color-fg);
|
||
}
|
||
|
||
.listbox-option:hover {
|
||
background: var(--coollabs-fill);
|
||
}
|
||
|
||
.data-table-row-active {
|
||
background: var(--coollabs-fill);
|
||
color: var(--coollabs-fg);
|
||
}
|
||
|
||
.listbox-option-disabled {
|
||
cursor: not-allowed;
|
||
opacity: 0.4;
|
||
}
|
||
|
||
.listbox-option-disabled:hover {
|
||
background: transparent;
|
||
}
|
||
|
||
/* Searchable listbox: sticky search field + scrollable options */
|
||
.searchable-listbox-panel {
|
||
max-height: 18rem;
|
||
overflow: hidden;
|
||
padding: 0;
|
||
gap: 0;
|
||
}
|
||
|
||
.searchable-listbox-search {
|
||
position: relative;
|
||
flex-shrink: 0;
|
||
border-bottom: 1px solid var(--coollabs-line);
|
||
padding: 0.375rem;
|
||
}
|
||
|
||
.searchable-listbox-search-input {
|
||
width: 100%;
|
||
height: 2rem;
|
||
border-radius: 6px;
|
||
border: 1px solid var(--coollabs-line);
|
||
background: var(--coollabs-recessed);
|
||
padding: 0 0.5rem 0 2rem;
|
||
font-size: 0.8125rem;
|
||
color: #000000;
|
||
box-shadow: none;
|
||
}
|
||
|
||
.dark .searchable-listbox-search-input {
|
||
color: var(--color-fg);
|
||
}
|
||
|
||
.searchable-listbox-search-input::placeholder {
|
||
color: var(--color-fg-faint, #737373);
|
||
}
|
||
|
||
.searchable-listbox-search-input:focus {
|
||
outline: none;
|
||
border-color: var(--color-accent);
|
||
box-shadow: none;
|
||
}
|
||
|
||
/* Hide native WebKit search clear so custom clear buttons are not doubled */
|
||
input[type="search"]::-webkit-search-decoration,
|
||
input[type="search"]::-webkit-search-cancel-button,
|
||
input[type="search"]::-webkit-search-results-button,
|
||
input[type="search"]::-webkit-search-results-decoration {
|
||
-webkit-appearance: none;
|
||
appearance: none;
|
||
display: none;
|
||
}
|
||
|
||
.searchable-listbox-options {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 1px;
|
||
max-height: 14rem;
|
||
overflow-x: hidden;
|
||
overflow-y: auto;
|
||
padding: 0.25rem;
|
||
}
|
||
|
||
.searchable-listbox-empty {
|
||
padding: 0.75rem 0.5rem;
|
||
text-align: center;
|
||
font-size: 0.75rem;
|
||
color: var(--color-fg-dim, #737373);
|
||
}
|
||
|
||
/* Flush layer-card body (tables and other full-bleed content) */
|
||
.application-settings-section-body.is-flush {
|
||
padding: 0;
|
||
}
|
||
|
||
/* The well does not clip (floating panels must escape it), so a filled table
|
||
header row needs the well's inner top radius, or its corners poke out. */
|
||
.application-settings-section-body.is-flush > :first-child,
|
||
.application-settings-section-body.is-flush > :first-child > :first-child {
|
||
border-top-left-radius: calc(0.5rem - 1px);
|
||
border-top-right-radius: calc(0.5rem - 1px);
|
||
}
|
||
|
||
/* Empty states inside cards keep the same inset as other card content. */
|
||
.application-settings-section-body:has(> .empty-state:only-child),
|
||
.application-settings-section-body.is-flush:has(> .empty-state:only-child),
|
||
.application-settings-section-body.is-flush > div:has(> .empty-state:only-child) {
|
||
padding: 1rem;
|
||
}
|
||
|
||
/* Standalone empty state: the same outer box and inset well as a layer card */
|
||
.empty-state {
|
||
border: 1px solid var(--card-border);
|
||
border-radius: 0.75rem;
|
||
padding: 0.375rem;
|
||
background: var(--card-bg);
|
||
box-shadow: 0 1px 2px rgb(0 0 0 / 5%);
|
||
}
|
||
|
||
.empty-state-well {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border: 1px solid var(--well-border);
|
||
border-radius: 0.5rem;
|
||
background: var(--well-bg);
|
||
box-shadow: var(--well-shadow);
|
||
padding: 2rem 1.5rem;
|
||
text-align: center;
|
||
}
|
||
|
||
.empty-state-title {
|
||
font-size: 0.875rem;
|
||
font-weight: 500;
|
||
line-height: 1.25rem;
|
||
color: var(--card-fg);
|
||
}
|
||
|
||
.empty-state-description {
|
||
margin-top: 0.25rem;
|
||
max-width: 28rem;
|
||
line-height: 1.25rem;
|
||
color: var(--coollabs-subtle);
|
||
}
|
||
|
||
/* Inside a layer card or a well, that box is the frame: show only the centered text. */
|
||
:is(.application-settings-section, .application-settings-section-body) .empty-state,
|
||
:is(.application-settings-section, .application-settings-section-body) .empty-state-well {
|
||
border: 0;
|
||
border-radius: 0;
|
||
padding-inline: 0;
|
||
background: none;
|
||
box-shadow: none;
|
||
}
|
||
|
||
:is(.application-settings-section, .application-settings-section-body) .empty-state {
|
||
padding: 0;
|
||
}
|
||
|
||
.loading-state-card {
|
||
border: 1px dashed var(--coollabs-line);
|
||
border-radius: 12px;
|
||
background: var(--coollabs-base);
|
||
}
|
||
|
||
.application-settings-section-body.is-flush .empty-state,
|
||
.application-settings-section-body:has(> .empty-state:only-child) .empty-state,
|
||
.application-settings-section > .empty-state {
|
||
margin: 0;
|
||
width: 100%;
|
||
max-width: none;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
/* Centered process dialog (startup / activity logs) — full-height log body.
|
||
Override application-settings-form's `width: auto` so empty/waiting states
|
||
keep a usable panel instead of shrinking to the title. */
|
||
.process-dialog {
|
||
width: 100% !important;
|
||
min-height: min(70dvh, 28rem);
|
||
}
|
||
|
||
.process-dialog-body {
|
||
border-radius: 0 0 8px 8px;
|
||
min-height: 0;
|
||
flex: 1 1 auto;
|
||
}
|
||
|
||
.process-dialog-body > * {
|
||
min-height: 0;
|
||
flex: 1 1 auto;
|
||
width: 100%;
|
||
}
|
||
|
||
/* The dialog body is already the well: the log sits in it directly, without
|
||
its own box. */
|
||
.process-dialog-body .activity-monitor-log {
|
||
padding: 0;
|
||
border: 0;
|
||
border-radius: 0;
|
||
background: transparent;
|
||
}
|
||
|
||
/* Show the status of the activity monitor next to the dialog title. */
|
||
.process-dialog-status {
|
||
display: none;
|
||
}
|
||
|
||
.process-dialog:has([data-activity-status="running"]) .process-dialog-status-running,
|
||
.process-dialog:has([data-activity-status="finished"]) .process-dialog-status-finished,
|
||
.process-dialog:has([data-activity-status="failed"]) .process-dialog-status-failed {
|
||
display: inline-flex;
|
||
}
|
||
|
||
.validation-installation-logs {
|
||
border: 1px solid var(--coollabs-fill);
|
||
}
|
||
|
||
@media (max-width: 639px) {
|
||
.process-dialog-mobile-fullscreen {
|
||
height: 100dvh !important;
|
||
min-height: 100dvh;
|
||
max-height: 100dvh;
|
||
border-radius: 0;
|
||
box-shadow: inset 0 0 0 1px var(--coollabs-hairline) !important;
|
||
}
|
||
|
||
.process-dialog-mobile-fullscreen .process-dialog-body {
|
||
border-radius: 0;
|
||
}
|
||
}
|
||
|
||
/* Data table (layer-card body, full-bleed) */
|
||
.data-table {
|
||
min-width: 0;
|
||
max-width: 100%;
|
||
overflow-x: auto;
|
||
overscroll-behavior-x: contain;
|
||
-webkit-overflow-scrolling: touch;
|
||
}
|
||
|
||
.data-table-header {
|
||
display: grid;
|
||
align-items: center;
|
||
gap: 1rem;
|
||
padding: 0 1rem;
|
||
height: 2.5rem;
|
||
font-size: 13px;
|
||
font-weight: 500;
|
||
color: var(--coollabs-subtle);
|
||
background: rgba(0, 0, 0, 0.02);
|
||
border-bottom: 1px solid var(--coollabs-fill);
|
||
border-radius: 8px 8px 0 0;
|
||
}
|
||
|
||
.dark .data-table-header {
|
||
background: rgba(255, 255, 255, 0.02);
|
||
color: var(--color-fg-faint);
|
||
}
|
||
|
||
.data-table > .env-table-item {
|
||
border-bottom: 1px solid var(--coollabs-fill);
|
||
}
|
||
|
||
.data-table > .env-table-item:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.data-table-row {
|
||
display: grid;
|
||
align-items: center;
|
||
gap: 1rem;
|
||
padding: 0.625rem 1rem;
|
||
min-height: 3rem;
|
||
transition: background-color 0.15s;
|
||
}
|
||
|
||
.data-table-row:hover {
|
||
background: rgba(0, 0, 0, 0.02);
|
||
}
|
||
|
||
.dark .data-table-row:hover {
|
||
background: rgba(255, 255, 255, 0.02);
|
||
}
|
||
|
||
.env-table-grid {
|
||
grid-template-columns: minmax(14rem, 2.5fr) 4.8rem 6rem 4rem 4.5rem 4.8rem 4.2rem 3rem;
|
||
}
|
||
|
||
/* File browser: icon, name, perms, size, modified, actions.
|
||
Every column is a fixed width except the flexible name column, so the
|
||
header grid and each row grid resolve identical tracks and stay aligned
|
||
(an `auto` column would size to its own content per row and drift). */
|
||
.file-table-grid {
|
||
grid-template-columns: 1.75rem minmax(10rem, 1fr) 5rem 6rem 6rem 5rem 11rem 10rem;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.data-table-header.file-table-grid {
|
||
display: none;
|
||
}
|
||
|
||
.data-table-row.file-table-grid {
|
||
grid-template-columns: 1.75rem minmax(0, 1fr) 10rem;
|
||
gap: 0.75rem;
|
||
}
|
||
|
||
/* Hide perms, owner, group, size and modified columns on small screens */
|
||
.data-table-row.file-table-grid > :nth-child(3),
|
||
.data-table-row.file-table-grid > :nth-child(4),
|
||
.data-table-row.file-table-grid > :nth-child(5),
|
||
.data-table-row.file-table-grid > :nth-child(6),
|
||
.data-table-row.file-table-grid > :nth-child(7) {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
/* File browser Monaco editor container: fixed viewport-relative height with a
|
||
rounded, hairline-framed surface that matches the rest of the settings UI. */
|
||
.file-editor-monaco {
|
||
height: 60vh;
|
||
min-height: 320px;
|
||
overflow: hidden;
|
||
border-radius: 8px;
|
||
box-shadow: inset 0 0 0 1px var(--coollabs-line);
|
||
}
|
||
|
||
.environment-table-scroll {
|
||
overflow-x: auto;
|
||
overscroll-behavior-x: contain;
|
||
-webkit-overflow-scrolling: touch;
|
||
}
|
||
|
||
.environment-table-scroll .env-table-grid {
|
||
min-width: 53rem;
|
||
}
|
||
|
||
.env-table-grid.env-table-grid-no-type {
|
||
grid-template-columns: minmax(14rem, 2.5fr) 4.8rem 4rem 4.5rem 4.8rem 4.2rem 3rem;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.environment-table-scroll .env-table-grid {
|
||
min-width: 0;
|
||
}
|
||
|
||
.data-table-header.env-table-grid {
|
||
display: none;
|
||
}
|
||
|
||
.data-table-row.env-table-grid {
|
||
grid-template-columns: minmax(0, 1fr) auto;
|
||
gap: 0.75rem;
|
||
padding: 0.75rem 1rem;
|
||
}
|
||
|
||
.data-table-row.env-table-grid > :not(:first-child):not(:last-child) {
|
||
display: none;
|
||
}
|
||
|
||
.data-table-row.env-table-grid > :last-child {
|
||
display: block;
|
||
justify-self: end;
|
||
}
|
||
}
|
||
|
||
/* Shared variables only store value shape (multiline), not per-resource flags. */
|
||
.env-table-grid-shared {
|
||
grid-template-columns: minmax(0, 1.6fr) 6rem minmax(0, 1fr) 4.5rem 3rem;
|
||
}
|
||
|
||
/* Shared env vars collapse secondary columns on tablet and use cards on phone. */
|
||
@media (max-width: 1100px) {
|
||
.env-table-grid-shared {
|
||
grid-template-columns: minmax(0, 1.4fr) 6rem minmax(0, 1fr) 3rem;
|
||
gap: 0.75rem;
|
||
}
|
||
|
||
/* Hide Multiline (4 of 5) */
|
||
.env-table-grid-shared > :nth-child(4) {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 900px) {
|
||
.env-table-grid-shared {
|
||
grid-template-columns: minmax(0, 1fr) 6rem 3rem;
|
||
}
|
||
|
||
.env-table-grid-shared > :nth-child(3) {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 640px) {
|
||
.data-table-header.env-table-grid-shared {
|
||
display: none;
|
||
}
|
||
|
||
.data-table-row.env-table-grid-shared {
|
||
display: grid;
|
||
grid-template-columns: minmax(0, 1fr) auto;
|
||
grid-template-areas:
|
||
'name actions'
|
||
'meta actions';
|
||
gap: 0.35rem 0.75rem;
|
||
align-items: start;
|
||
padding: 0.875rem 1rem;
|
||
min-height: 0;
|
||
}
|
||
|
||
/* Name */
|
||
.data-table-row.env-table-grid-shared > :nth-child(1) {
|
||
grid-area: name;
|
||
min-width: 0;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.data-table-row.env-table-grid-shared > :nth-child(1) .env-key-label {
|
||
white-space: normal;
|
||
overflow-wrap: anywhere;
|
||
word-break: break-word;
|
||
}
|
||
|
||
/* Type column */
|
||
.data-table-row.env-table-grid-shared > :nth-child(2) {
|
||
display: none !important;
|
||
}
|
||
|
||
/* Comment / flags already hidden; keep meta area for optional second line */
|
||
.data-table-row.env-table-grid-shared > :nth-child(3),
|
||
.data-table-row.env-table-grid-shared > :nth-child(4) {
|
||
display: none !important;
|
||
}
|
||
|
||
/* Actions */
|
||
.data-table-row.env-table-grid-shared > :nth-child(5) {
|
||
grid-area: actions;
|
||
align-self: center;
|
||
justify-self: end;
|
||
}
|
||
|
||
}
|
||
|
||
.env-managed-desktop {
|
||
display: flex;
|
||
}
|
||
|
||
.domains-table-grid {
|
||
grid-template-columns: minmax(0, 1.8fr) 8.5rem 10rem 11rem 6.5rem;
|
||
}
|
||
|
||
.domains-table-grid-service {
|
||
grid-template-columns: minmax(0, 1.8fr) 8.5rem 10rem 6.5rem;
|
||
}
|
||
|
||
.domains-table-grid-compose {
|
||
grid-template-columns: minmax(0, 1.6fr) minmax(6rem, 0.8fr) 8.5rem 10rem 11rem 6.5rem;
|
||
}
|
||
|
||
.domains-mobile-label {
|
||
display: none;
|
||
}
|
||
|
||
/* Domains table: collapse secondary columns on tablet/phone */
|
||
@media (max-width: 900px) {
|
||
.domains-table-grid {
|
||
grid-template-columns: minmax(0, 1fr) 8.25rem 9rem 10rem 5.5rem;
|
||
gap: 0.75rem;
|
||
}
|
||
|
||
.domains-table-grid-service {
|
||
grid-template-columns: minmax(0, 1fr) 8.25rem 9rem 5.5rem;
|
||
gap: 0.75rem;
|
||
}
|
||
|
||
.domains-table-grid-compose {
|
||
grid-template-columns: minmax(0, 1fr) 8.25rem 9rem 10rem 5.5rem;
|
||
gap: 0.75rem;
|
||
}
|
||
|
||
/* Hide "Service" */
|
||
.domains-table-grid-compose > :nth-child(2) {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.data-table-header.domains-table-grid,
|
||
.data-table-header.domains-table-grid-service,
|
||
.data-table-header.domains-table-grid-compose {
|
||
display: none;
|
||
}
|
||
|
||
.data-table-row.domains-table-grid,
|
||
.data-table-row.domains-table-grid-service,
|
||
.data-table-row.domains-table-grid-compose {
|
||
display: grid;
|
||
grid-template-columns: minmax(0, 1fr) auto;
|
||
grid-template-areas:
|
||
'domain domain'
|
||
'meta actions'
|
||
'indexing indexing'
|
||
'direction direction';
|
||
gap: 0.5rem 0.75rem;
|
||
align-items: start;
|
||
padding: 0.75rem;
|
||
min-height: 0;
|
||
}
|
||
|
||
.data-table-row.domains-row-without-direction {
|
||
grid-template-areas:
|
||
'domain domain'
|
||
'meta actions'
|
||
'indexing indexing';
|
||
}
|
||
|
||
.data-table-row.domains-table-grid.domains-row-without-direction > :nth-child(4),
|
||
.data-table-row.domains-table-grid-compose.domains-row-without-direction > :nth-child(5) {
|
||
display: none;
|
||
}
|
||
|
||
/* Domain cell */
|
||
.data-table-row.domains-table-grid > :nth-child(1),
|
||
.data-table-row.domains-table-grid-service > :nth-child(1),
|
||
.data-table-row.domains-table-grid-compose > :nth-child(1) {
|
||
grid-area: domain;
|
||
min-width: 0;
|
||
}
|
||
|
||
.data-table-row.domains-table-grid > :nth-child(1) a,
|
||
.data-table-row.domains-table-grid-service > :nth-child(1) a,
|
||
.data-table-row.domains-table-grid-compose > :nth-child(1) a {
|
||
white-space: normal;
|
||
overflow-wrap: anywhere;
|
||
word-break: break-word;
|
||
}
|
||
|
||
/* Non-compose: 1 Domain, 2 DNS, 3 Indexing, 4 Direction, 5 Actions */
|
||
.data-table-row.domains-table-grid > :nth-child(2) {
|
||
grid-area: meta;
|
||
display: flex !important;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
gap: 0.375rem;
|
||
}
|
||
|
||
.data-table-row.domains-table-grid > :nth-child(3) {
|
||
grid-area: indexing;
|
||
}
|
||
|
||
.data-table-row.domains-table-grid > :nth-child(4) {
|
||
grid-area: direction;
|
||
}
|
||
|
||
.data-table-row.domains-table-grid > :nth-child(5) {
|
||
grid-area: actions;
|
||
align-self: center;
|
||
}
|
||
|
||
/* Compose service group: 1 Domain, 2 DNS, 3 Indexing, 4 Actions */
|
||
.data-table-row.domains-table-grid-service > :nth-child(2) {
|
||
grid-area: meta;
|
||
display: flex !important;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
gap: 0.375rem;
|
||
}
|
||
|
||
.data-table-row.domains-table-grid-service > :nth-child(3) {
|
||
grid-area: indexing;
|
||
}
|
||
|
||
.data-table-row.domains-table-grid-service > :nth-child(4) {
|
||
grid-area: actions;
|
||
align-self: center;
|
||
}
|
||
|
||
/* Compose: 1 Domain, 2 Service, 3 DNS, 4 Indexing, 5 Direction, 6 Actions */
|
||
.data-table-row.domains-table-grid-compose > :nth-child(2) {
|
||
display: none !important;
|
||
}
|
||
|
||
.data-table-row.domains-table-grid-compose > :nth-child(3) {
|
||
grid-area: meta;
|
||
display: flex !important;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
gap: 0.375rem;
|
||
}
|
||
|
||
.data-table-row.domains-table-grid-compose > :nth-child(4) {
|
||
grid-area: indexing;
|
||
}
|
||
|
||
.data-table-row.domains-table-grid-compose > :nth-child(5) {
|
||
grid-area: direction;
|
||
}
|
||
|
||
.data-table-row.domains-table-grid-compose > :nth-child(6) {
|
||
grid-area: actions;
|
||
align-self: center;
|
||
}
|
||
|
||
.domains-mobile-label {
|
||
display: block;
|
||
margin-bottom: 0.25rem;
|
||
font-size: 0.6875rem;
|
||
font-weight: 500;
|
||
line-height: 1rem;
|
||
color: var(--coollabs-subtle);
|
||
}
|
||
|
||
.data-table-row.domains-table-grid .listbox-trigger,
|
||
.data-table-row.domains-table-grid-compose .listbox-trigger {
|
||
height: 2rem;
|
||
font-size: 0.8125rem;
|
||
}
|
||
|
||
/* Service name as badge under domain on mobile (compose only) */
|
||
.data-table-row.domains-table-grid-compose .domains-service-mobile {
|
||
display: inline-flex !important;
|
||
}
|
||
}
|
||
|
||
.domains-service-mobile {
|
||
display: none;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.domains-service-desktop {
|
||
display: none !important;
|
||
}
|
||
}
|
||
|
||
.backup-table-grid {
|
||
grid-template-columns: minmax(10rem, 1.7fr) 6rem minmax(7rem, 0.8fr) 7.5rem 6.5rem minmax(8rem, 1fr) 5rem;
|
||
min-width: 50rem;
|
||
}
|
||
|
||
.service-backup-table-grid {
|
||
grid-template-columns: minmax(10rem, 1.7fr) 6rem minmax(7rem, 0.8fr) 7.5rem 6.5rem minmax(8rem, 1fr) 12.5rem;
|
||
width: 100%;
|
||
}
|
||
|
||
.data-table-header.service-backup-table-grid {
|
||
border-bottom: 0;
|
||
}
|
||
|
||
/* Rows are reordered with CSS order, so dividers must not depend on DOM position. */
|
||
.data-table-row.service-backup-table-grid {
|
||
background: var(--coollabs-base);
|
||
border-top: 1px solid var(--coollabs-fill);
|
||
}
|
||
|
||
.data-table-row.service-backup-table-grid:hover {
|
||
background: color-mix(in srgb, var(--coollabs-base) 98%, black);
|
||
}
|
||
|
||
.dark .data-table-row.service-backup-table-grid:hover {
|
||
background: color-mix(in srgb, var(--coollabs-base) 98%, white);
|
||
}
|
||
|
||
/* Persistent storage volumes: Name | [Source] | Destination | [PR suffix] | Backup | [Actions] */
|
||
.volumes-table-grid-readonly {
|
||
grid-template-columns: minmax(10rem, 1.4fr) minmax(8rem, 1fr) 5rem;
|
||
}
|
||
|
||
.volumes-table-grid {
|
||
grid-template-columns: minmax(9rem, 1.2fr) minmax(6rem, 1fr) 5rem 15rem;
|
||
}
|
||
|
||
.volumes-table-grid-with-pr {
|
||
grid-template-columns: minmax(9rem, 1.1fr) minmax(6rem, 1fr) 8.5rem 5rem 15rem;
|
||
}
|
||
|
||
.volumes-table-grid-readonly.has-source {
|
||
grid-template-columns: minmax(10rem, 1.2fr) minmax(10rem, 1.2fr) minmax(8rem, 1fr) 5rem;
|
||
}
|
||
|
||
.volumes-table-grid.has-source {
|
||
grid-template-columns: minmax(9rem, 1.1fr) minmax(14rem, 1.5fr) minmax(6rem, 1fr) 5rem 15rem;
|
||
}
|
||
|
||
.volumes-table-grid-with-pr.has-source {
|
||
grid-template-columns: minmax(9rem, 1fr) minmax(14rem, 1.4fr) minmax(6rem, 1fr) 8.5rem 5rem 15rem;
|
||
}
|
||
|
||
.volumes-mobile-label {
|
||
display: none;
|
||
}
|
||
|
||
/* External Compose volumes (read-only): Volume | Docker volume | Mount path | Service */
|
||
.external-volumes-table-grid {
|
||
grid-template-columns: minmax(8rem, 1fr) minmax(9rem, 1.2fr) minmax(8rem, 1fr) minmax(6rem, 0.8fr);
|
||
}
|
||
|
||
/*
|
||
* Same tokens as .application-settings-form label (13px / medium / subtle).
|
||
* Do not use text-sm (14px) — settings labels override Tailwind to 13px.
|
||
*/
|
||
.volumes-mobile-label.is-visible,
|
||
.volumes-field-label {
|
||
font-size: 13px;
|
||
font-weight: 500;
|
||
line-height: 1rem;
|
||
color: var(--coollabs-subtle);
|
||
}
|
||
|
||
/* Compact inputs inside volume table rows (desktop) */
|
||
.data-table-row.volumes-table-grid .input,
|
||
.data-table-row.volumes-table-grid-with-pr .input,
|
||
.data-table-row.volumes-table-grid .listbox-trigger,
|
||
.data-table-row.volumes-table-grid-with-pr .listbox-trigger {
|
||
min-height: 2rem;
|
||
height: 2rem;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.data-table-row.volumes-table-grid .listbox-trigger,
|
||
.data-table-row.volumes-table-grid-with-pr .listbox-trigger {
|
||
padding-inline: 0.5rem;
|
||
}
|
||
|
||
/* Phone: stacked card rows with per-field labels (table headers hidden) */
|
||
@media (max-width: 768px) {
|
||
.data-table-header.volumes-table-grid,
|
||
.data-table-header.volumes-table-grid-with-pr,
|
||
.data-table-header.volumes-table-grid-readonly,
|
||
.data-table-header.external-volumes-table-grid {
|
||
display: none;
|
||
}
|
||
|
||
.data-table-row.volumes-table-grid,
|
||
.data-table-row.volumes-table-grid-with-pr,
|
||
.data-table-row.volumes-table-grid-readonly,
|
||
.data-table-row.external-volumes-table-grid {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: stretch;
|
||
gap: 0.625rem;
|
||
padding: 0.875rem 1rem;
|
||
min-height: 0;
|
||
}
|
||
|
||
.data-table-row.volumes-table-grid-with-pr > .volumes-col-pr {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.25rem;
|
||
min-width: 0;
|
||
width: 100%;
|
||
}
|
||
|
||
.data-table-row.volumes-table-grid > *,
|
||
.data-table-row.volumes-table-grid-with-pr > *,
|
||
.data-table-row.volumes-table-grid-readonly > *,
|
||
.data-table-row.external-volumes-table-grid > * {
|
||
width: 100%;
|
||
min-width: 0;
|
||
}
|
||
|
||
/* Match .application-settings-form label (13px), not Tailwind text-sm (14px) */
|
||
.volumes-mobile-label {
|
||
display: block;
|
||
font-size: 13px;
|
||
font-weight: 500;
|
||
line-height: 1rem;
|
||
color: var(--coollabs-subtle);
|
||
}
|
||
|
||
.volumes-cell-name,
|
||
.volumes-cell-source,
|
||
.volumes-col-backup,
|
||
.volumes-cell-dest,
|
||
.volumes-cell-actions {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.25rem;
|
||
min-width: 0;
|
||
width: 100%;
|
||
}
|
||
|
||
.volumes-col-backup {
|
||
flex-direction: row;
|
||
align-items: center;
|
||
justify-content: flex-start;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.volumes-cell-actions {
|
||
flex-direction: row;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
justify-content: flex-start;
|
||
gap: 0.5rem;
|
||
padding-top: 0.25rem;
|
||
}
|
||
|
||
.data-table-row.volumes-table-grid .input,
|
||
.data-table-row.volumes-table-grid-with-pr .input,
|
||
.data-table-row.volumes-table-grid .listbox-trigger,
|
||
.data-table-row.volumes-table-grid-with-pr .listbox-trigger {
|
||
min-height: 2.25rem;
|
||
height: 2.25rem;
|
||
font-size: 13px;
|
||
}
|
||
}
|
||
|
||
.deployment-table-scroll {
|
||
overflow-x: auto;
|
||
overscroll-behavior-x: contain;
|
||
-webkit-overflow-scrolling: touch;
|
||
}
|
||
|
||
.deployment-table-grid {
|
||
min-width: 59rem;
|
||
grid-template-columns: 7.5rem minmax(7rem, 0.8fr) minmax(12rem, 1.7fr) minmax(8rem, 0.9fr) 6.5rem minmax(7rem, 0.8fr);
|
||
}
|
||
|
||
@media (min-width: 1024px) {
|
||
.deployment-table-scroll {
|
||
overflow-x: visible;
|
||
}
|
||
|
||
.deployment-table-grid {
|
||
min-width: 0;
|
||
}
|
||
}
|
||
|
||
/* A full-row link sits behind the cells, so gaps open the deployment too; the cells stay on top for their own links and tooltips. */
|
||
.deployment-table-row > :not(.deployment-row-link) {
|
||
position: relative;
|
||
z-index: 1;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.deployment-table-row a:not(.deployment-row-link) {
|
||
pointer-events: auto;
|
||
}
|
||
|
||
/* The embedded history shows the start time and a status dot in the last column. */
|
||
.deployment-table-embedded .deployment-table-grid {
|
||
grid-template-columns: minmax(7rem, 0.8fr) minmax(12rem, 1.7fr) 6.5rem minmax(7rem, 0.8fr) 5rem;
|
||
}
|
||
|
||
@media (min-width: 1536px) {
|
||
.deployment-table-embedded .deployment-table-grid {
|
||
grid-template-columns: minmax(0, 1fr) 5rem;
|
||
}
|
||
|
||
.deployment-table-embedded .deployment-table-grid > :nth-child(1),
|
||
.deployment-table-embedded .deployment-table-grid > :nth-child(3),
|
||
.deployment-table-embedded .deployment-table-grid > :nth-child(4) {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
/* On the deployment page the history matches the logs height; the page size is fitted to it in JS. */
|
||
@media (min-width: 1536px) {
|
||
.deployment-history-fit {
|
||
position: sticky;
|
||
top: 1rem;
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: calc(100dvh - 10rem);
|
||
min-height: 32rem;
|
||
}
|
||
|
||
.deployment-history-fit > .application-settings-section,
|
||
.deployment-history-fit .application-settings-section-body,
|
||
.deployment-history-fit .data-table {
|
||
display: flex;
|
||
flex: 1;
|
||
flex-direction: column;
|
||
min-height: 0;
|
||
}
|
||
|
||
.deployment-history-fit .deployment-table-scroll {
|
||
flex: 1;
|
||
min-height: 0;
|
||
overflow: hidden;
|
||
}
|
||
}
|
||
|
||
/* Status and Started only fit a badge and a short "3 hours ago"; the names get the rest. */
|
||
.dashboard-deployment-table-grid {
|
||
grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr) minmax(0, 0.7fr) 6.5rem 6rem;
|
||
}
|
||
|
||
.team-members-table-grid {
|
||
grid-template-columns: minmax(0, 1.15fr) minmax(0, 1.55fr) 7rem minmax(10rem, 0.9fr);
|
||
}
|
||
|
||
.team-members-table-grid-2fa {
|
||
grid-template-columns: minmax(0, 1.15fr) minmax(0, 1.55fr) 7rem 4rem minmax(10rem, 0.9fr);
|
||
}
|
||
|
||
.admin-users-table-grid {
|
||
grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr) 5rem;
|
||
}
|
||
|
||
.api-tokens-table-grid {
|
||
grid-template-columns: minmax(9rem, 1.15fr) minmax(14rem, 1.9fr) 7rem 6.5rem 6.5rem 5.5rem;
|
||
}
|
||
|
||
.fleet-servers-table-grid {
|
||
grid-template-columns: minmax(9rem, 1.4fr) 8rem 8rem 8rem 5rem 8rem 5.5rem;
|
||
}
|
||
|
||
.fleet-containers-table-grid {
|
||
grid-template-columns: 2.25rem minmax(9rem, 1.7fr) minmax(6rem, 0.9fr) repeat(4, minmax(7rem, 1fr));
|
||
min-width: 54rem;
|
||
}
|
||
|
||
.scheduled-executions-table-grid {
|
||
grid-template-columns: 5.5rem minmax(9rem, 1fr) minmax(7rem, 0.7fr) 8.5rem 5rem minmax(12rem, 1.5fr);
|
||
}
|
||
|
||
.scheduler-runs-table-grid {
|
||
grid-template-columns: 9rem minmax(12rem, 1.5fr) 6rem 6rem 6rem;
|
||
}
|
||
|
||
.skipped-jobs-table-grid {
|
||
grid-template-columns: 9rem 7rem minmax(10rem, 1fr) minmax(12rem, 1.4fr);
|
||
}
|
||
|
||
.server-resources-managed-table-grid {
|
||
grid-template-columns: minmax(10rem, 1.3fr) minmax(8rem, 1fr) minmax(8rem, 1fr) 8rem 9.5rem;
|
||
}
|
||
|
||
.server-resources-unmanaged-table-grid {
|
||
grid-template-columns: minmax(9rem, 1fr) minmax(12rem, 1.5fr) 7rem minmax(9rem, auto);
|
||
}
|
||
|
||
.server-images-table-grid {
|
||
grid-template-columns: minmax(10rem, 1.6fr) minmax(5rem, 0.8fr) 7.5rem 6.5rem 4.5rem minmax(8rem, 1fr) 6.5rem;
|
||
min-width: 55rem;
|
||
}
|
||
|
||
.package-updates-table-grid {
|
||
grid-template-columns: minmax(12rem, 1.4fr) minmax(10rem, 1fr) 6rem;
|
||
}
|
||
|
||
.scheduled-backups-table-grid {
|
||
grid-template-columns: minmax(10rem, 1.25fr) 8rem minmax(9rem, 1fr) 6rem 5rem;
|
||
}
|
||
|
||
.backup-executions-table-grid {
|
||
grid-template-columns: 6.5rem minmax(7rem, 0.8fr) minmax(14rem, 1.5fr) 10rem 5rem minmax(8rem, 1fr) 5rem;
|
||
gap: 0.75rem;
|
||
min-width: 67.5rem;
|
||
}
|
||
|
||
.backup-executions-table-scroll {
|
||
overflow-x: auto;
|
||
}
|
||
|
||
/* Match the grid width so the log output spans the full scrolled row. */
|
||
.backup-execution-row {
|
||
min-width: 67.5rem;
|
||
}
|
||
|
||
.backup-execution-row:last-child > .data-table-row {
|
||
border-bottom: 0;
|
||
}
|
||
|
||
.volume-backup-executions-grid {
|
||
grid-template-columns: 7.5rem minmax(14rem, 1fr) 11rem 7rem 9rem minmax(9rem, 0.7fr);
|
||
min-width: 52.5rem;
|
||
}
|
||
|
||
.clone-destinations-table-grid {
|
||
grid-template-columns: 2rem minmax(10rem, 1fr) minmax(10rem, 1fr);
|
||
}
|
||
|
||
.clone-resources-table-grid {
|
||
grid-template-columns: minmax(10rem, 1fr) 7rem minmax(12rem, 1.5fr);
|
||
}
|
||
|
||
.admin-search-table-grid {
|
||
grid-template-columns: minmax(10rem, 1fr) minmax(12rem, 1.2fr) 8rem 7rem;
|
||
}
|
||
|
||
@media (max-width: 900px) {
|
||
.team-members-table-grid {
|
||
grid-template-columns: minmax(0, 1fr) 7rem 7rem;
|
||
}
|
||
|
||
.team-members-table-grid-2fa {
|
||
grid-template-columns: minmax(0, 1fr) 7rem 4rem 7rem;
|
||
}
|
||
|
||
.team-members-table-grid > :nth-child(2) {
|
||
display: none;
|
||
}
|
||
|
||
.api-tokens-table-grid {
|
||
grid-template-columns: minmax(0, 1fr) minmax(9rem, 1.3fr) 6.5rem 4.5rem;
|
||
}
|
||
|
||
.api-tokens-table-grid > :nth-child(3),
|
||
.api-tokens-table-grid > :nth-child(4) {
|
||
display: none;
|
||
}
|
||
|
||
.server-resources-managed-table-grid {
|
||
grid-template-columns: minmax(9rem, 1fr) minmax(8rem, 0.8fr) 8rem 9.5rem;
|
||
}
|
||
|
||
.server-resources-managed-table-grid > :nth-child(3) {
|
||
display: none;
|
||
}
|
||
|
||
.backup-executions-table-grid {
|
||
grid-template-columns: 7.5rem minmax(9rem, 0.8fr) minmax(14rem, 1.5fr) 10rem minmax(8rem, 1fr) 5rem;
|
||
}
|
||
|
||
.backup-executions-table-grid > :nth-child(5) {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 640px) {
|
||
/* Keep header actions compact; log sections override via .logs-settings-section.
|
||
Only stretch when the actions row needs the full width (multi-control toolbars). */
|
||
.application-settings-section:not(.logs-settings-section) > :is(header, .application-settings-section-header) > div:nth-child(2) {
|
||
width: 100%;
|
||
}
|
||
|
||
.team-members-table-grid {
|
||
grid-template-columns: minmax(0, 1fr) 6.5rem;
|
||
}
|
||
|
||
.team-members-table-grid-2fa {
|
||
grid-template-columns: minmax(0, 1fr) 4rem 6.5rem;
|
||
}
|
||
|
||
.team-members-table-grid > :nth-child(3) {
|
||
display: none;
|
||
}
|
||
|
||
.admin-users-table-grid {
|
||
grid-template-columns: minmax(0, 1fr) 5rem;
|
||
}
|
||
|
||
.admin-users-table-grid > :nth-child(2) {
|
||
display: none;
|
||
}
|
||
|
||
.api-tokens-table-grid {
|
||
grid-template-columns: minmax(0, 1fr) 6.5rem 4.5rem;
|
||
}
|
||
|
||
.api-tokens-table-grid > :nth-child(2) {
|
||
display: none;
|
||
}
|
||
|
||
/* Name + Status only — fixed Type/Status tracks were crushing Name into "NameType". */
|
||
.server-resources-managed-table-grid {
|
||
grid-template-columns: minmax(0, 1fr) auto;
|
||
column-gap: 0.75rem;
|
||
}
|
||
|
||
.server-resources-managed-table-grid > :nth-child(2),
|
||
.server-resources-managed-table-grid > :nth-child(3),
|
||
.server-resources-managed-table-grid > :nth-child(4) {
|
||
display: none;
|
||
}
|
||
|
||
/* Name + Actions only — Status badge + action buttons overcrowded the row. */
|
||
.server-resources-unmanaged-table-grid {
|
||
grid-template-columns: minmax(0, 1fr) auto;
|
||
column-gap: 0.75rem;
|
||
}
|
||
|
||
.server-resources-unmanaged-table-grid > :nth-child(2),
|
||
.server-resources-unmanaged-table-grid > :nth-child(3) {
|
||
display: none;
|
||
}
|
||
|
||
.package-updates-table-grid {
|
||
grid-template-columns: minmax(0, 1fr) 5rem;
|
||
}
|
||
|
||
.package-updates-table-grid > :nth-child(2) {
|
||
display: none;
|
||
}
|
||
|
||
.scheduled-backups-table-grid {
|
||
grid-template-columns: minmax(0, 1fr) 8rem 5rem;
|
||
}
|
||
|
||
.scheduled-backups-table-grid > :nth-child(3),
|
||
.scheduled-backups-table-grid > :nth-child(4) {
|
||
display: none;
|
||
}
|
||
|
||
.backup-executions-table-grid,
|
||
.backup-execution-row {
|
||
min-width: 42rem;
|
||
}
|
||
|
||
.backup-executions-table-grid {
|
||
grid-template-columns: 7.5rem 9rem minmax(14rem, 1fr) minmax(7rem, auto);
|
||
}
|
||
|
||
.backup-executions-table-grid > :nth-child(4),
|
||
.backup-executions-table-grid > :nth-child(5),
|
||
.backup-executions-table-grid > :nth-child(6) {
|
||
display: none;
|
||
}
|
||
|
||
.clone-destinations-table-grid {
|
||
grid-template-columns: 2rem minmax(0, 1fr);
|
||
}
|
||
|
||
.clone-destinations-table-grid > :nth-child(3) {
|
||
display: none;
|
||
}
|
||
|
||
.clone-resources-table-grid {
|
||
grid-template-columns: minmax(0, 1fr) 7rem;
|
||
}
|
||
|
||
.clone-resources-table-grid > :nth-child(3) {
|
||
display: none;
|
||
}
|
||
|
||
.admin-search-table-grid {
|
||
grid-template-columns: minmax(0, 1fr) 7rem;
|
||
}
|
||
|
||
.admin-search-table-grid > :nth-child(2),
|
||
.admin-search-table-grid > :nth-child(3) {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
.data-table-cell-check {
|
||
display: flex;
|
||
justify-content: center;
|
||
color: #171717;
|
||
}
|
||
|
||
.dark .data-table-cell-check {
|
||
color: var(--color-fg);
|
||
}
|
||
|
||
.data-table-cell-dash {
|
||
display: flex;
|
||
justify-content: center;
|
||
color: #a3a3a3;
|
||
}
|
||
|
||
.dark .data-table-cell-dash {
|
||
color: var(--color-fg-faint);
|
||
}
|
||
|
||
/* Shared logs viewer (deployment + runtime) — mobile-first toolbar
|
||
Mobile stacks: search → meta → full-width actions (no side-by-side overlap). */
|
||
/* Same border as the application settings cards (--card-border). */
|
||
.logs-viewer {
|
||
border-color: rgb(0 0 0 / 9%);
|
||
background: var(--coollabs-base);
|
||
color: #262626;
|
||
}
|
||
|
||
.dark .logs-viewer {
|
||
border-color: rgb(255 255 255 / 8%);
|
||
background: var(--color-log);
|
||
color: #f5f5f5;
|
||
}
|
||
|
||
.logs-viewer-toolbar {
|
||
flex-shrink: 0;
|
||
border-bottom: 1px solid var(--color-neutral-200);
|
||
background: var(--color-neutral-50);
|
||
padding: 0.5rem 0.75rem;
|
||
}
|
||
|
||
.dark .logs-viewer-toolbar {
|
||
border-bottom-color: rgba(255, 255, 255, 0.08);
|
||
background: rgba(255, 255, 255, 0.035);
|
||
}
|
||
|
||
html[data-theme="custom"] .logs-viewer-toolbar {
|
||
border-bottom-color: var(--coollabs-line);
|
||
background: var(--color-log-toolbar);
|
||
}
|
||
|
||
html[data-theme="custom"] .logs-viewer-timestamp {
|
||
color: var(--color-fg-faint);
|
||
}
|
||
|
||
.logs-viewer-toolbar-controls {
|
||
display: flex;
|
||
min-width: 0;
|
||
width: 100%;
|
||
flex-direction: column;
|
||
align-items: stretch;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.logs-viewer-primary {
|
||
display: flex;
|
||
min-width: 0;
|
||
width: 100%;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.logs-viewer-primary .logs-viewer-actions {
|
||
width: auto;
|
||
flex: 1 1 auto;
|
||
}
|
||
|
||
.logs-viewer-search {
|
||
position: relative;
|
||
min-width: 0;
|
||
width: 100%;
|
||
}
|
||
|
||
/* Search field uses the same pl-8!/pr-8! + z-10 icon pattern as server/project
|
||
list filters. Avoid .input here — settings form .input padding (0.75rem)
|
||
overrides custom padding and makes the search icon overlap the placeholder. */
|
||
|
||
.logs-viewer-meta {
|
||
display: flex;
|
||
min-width: 0;
|
||
width: 100%;
|
||
flex-shrink: 0;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
/* Compact lines control (runtime/proxy/sentinel logs) */
|
||
.logs-viewer-lines {
|
||
display: inline-flex;
|
||
min-width: 0;
|
||
flex-shrink: 0;
|
||
align-items: center;
|
||
gap: 0.375rem;
|
||
}
|
||
|
||
.logs-viewer-lines-label {
|
||
font-size: 0.6875rem;
|
||
font-weight: 500;
|
||
color: #737373;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.dark .logs-viewer-lines-label {
|
||
color: var(--color-fg-faint, #6e6e74);
|
||
}
|
||
|
||
.logs-viewer-lines-input {
|
||
width: 4.5rem !important;
|
||
min-width: 4.5rem !important;
|
||
max-width: 5rem !important;
|
||
height: 2rem !important;
|
||
padding-left: 0.5rem !important;
|
||
padding-right: 0.375rem !important;
|
||
font-size: 0.75rem !important;
|
||
text-align: center;
|
||
appearance: textfield;
|
||
-moz-appearance: textfield;
|
||
}
|
||
|
||
.logs-viewer-lines-input::-webkit-outer-spin-button,
|
||
.logs-viewer-lines-input::-webkit-inner-spin-button {
|
||
margin: 0;
|
||
-webkit-appearance: none;
|
||
}
|
||
|
||
/* Runtime logs: "Lines", the number field and "All" as one control, styled like the search field. */
|
||
.runtime-log-lines-group {
|
||
height: 2rem;
|
||
gap: 0;
|
||
overflow: hidden;
|
||
border: 1px solid var(--color-neutral-200);
|
||
border-radius: 0.5rem;
|
||
background: #fff;
|
||
}
|
||
|
||
.dark .runtime-log-lines-group {
|
||
border-color: rgba(255, 255, 255, 0.08);
|
||
background: rgba(255, 255, 255, 0.035);
|
||
}
|
||
|
||
.runtime-log-lines-group:has(.logs-viewer-lines-input:focus) {
|
||
border-color: var(--color-coollabs);
|
||
}
|
||
|
||
.runtime-log-lines-group .logs-viewer-lines-label {
|
||
padding: 0 0.5rem 0 0.625rem;
|
||
}
|
||
|
||
.runtime-log-lines-group .logs-viewer-lines-input {
|
||
width: 3.75rem !important;
|
||
min-width: 3.75rem !important;
|
||
height: 100% !important;
|
||
border: 0 !important;
|
||
border-left: 1px solid var(--color-neutral-200) !important;
|
||
border-right: 1px solid var(--color-neutral-200) !important;
|
||
border-radius: 0 !important;
|
||
background: transparent !important;
|
||
box-shadow: none !important;
|
||
outline: none !important;
|
||
}
|
||
|
||
.dark .runtime-log-lines-group .logs-viewer-lines-input {
|
||
border-left-color: rgba(255, 255, 255, 0.08) !important;
|
||
border-right-color: rgba(255, 255, 255, 0.08) !important;
|
||
}
|
||
|
||
.runtime-log-lines-all {
|
||
height: 100%;
|
||
padding: 0 0.625rem;
|
||
font-size: 0.6875rem;
|
||
font-weight: 500;
|
||
color: #737373;
|
||
transition: color 0.15s, background-color 0.15s;
|
||
}
|
||
|
||
.runtime-log-lines-all:hover:not(:disabled, .runtime-log-lines-all-active) {
|
||
background: rgba(0, 0, 0, 0.05);
|
||
color: #171717;
|
||
}
|
||
|
||
.dark .runtime-log-lines-all {
|
||
color: var(--color-fg-faint);
|
||
}
|
||
|
||
.dark .runtime-log-lines-all:hover:not(:disabled, .runtime-log-lines-all-active) {
|
||
background: rgba(255, 255, 255, 0.06);
|
||
color: var(--color-fg);
|
||
}
|
||
|
||
.runtime-log-lines-all.runtime-log-lines-all-active,
|
||
.dark .runtime-log-lines-all.runtime-log-lines-all-active {
|
||
background: var(--color-coollabs);
|
||
color: #fff;
|
||
}
|
||
|
||
.runtime-log-lines-all:disabled {
|
||
cursor: not-allowed;
|
||
opacity: 0.5;
|
||
}
|
||
|
||
/* Section header status (proxy/sentinel) stays inline with title on mobile */
|
||
.logs-settings-section > :is(header, .application-settings-section-header) {
|
||
flex-wrap: nowrap;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.logs-settings-section > :is(header, .application-settings-section-header) > div:first-child {
|
||
min-width: 0;
|
||
flex: 1 1 auto;
|
||
}
|
||
|
||
.logs-settings-section > :is(header, .application-settings-section-header) > div:nth-child(2) {
|
||
width: auto !important;
|
||
flex-shrink: 0;
|
||
margin-left: auto;
|
||
}
|
||
|
||
.logs-section-status-badge {
|
||
height: 1.5rem !important;
|
||
padding-inline: 0.5rem !important;
|
||
font-size: 0.6875rem !important;
|
||
}
|
||
|
||
/* Full-width action strip on mobile — icons never sit beside Lines */
|
||
.logs-viewer-actions {
|
||
display: flex;
|
||
min-width: 0;
|
||
width: 100%;
|
||
flex-shrink: 0;
|
||
align-items: center;
|
||
justify-content: flex-start;
|
||
gap: 0.125rem;
|
||
overflow-x: auto;
|
||
overscroll-behavior-x: contain;
|
||
scrollbar-width: none;
|
||
-webkit-overflow-scrolling: touch;
|
||
}
|
||
|
||
.logs-viewer-actions::-webkit-scrollbar {
|
||
display: none;
|
||
}
|
||
|
||
.logs-viewer-actions > * {
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* Cancel/force-start + search — right-aligned as a group */
|
||
.logs-viewer-end {
|
||
display: flex;
|
||
min-width: 0;
|
||
width: 100%;
|
||
flex-shrink: 0;
|
||
align-items: center;
|
||
justify-content: flex-end;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.logs-viewer-end .logs-viewer-meta {
|
||
width: auto;
|
||
}
|
||
|
||
.runtime-logs-viewer-end {
|
||
align-items: stretch;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.logs-viewer-deployment-actions {
|
||
display: flex;
|
||
min-width: 0;
|
||
flex-shrink: 0;
|
||
align-items: center;
|
||
justify-content: flex-end;
|
||
}
|
||
|
||
.logs-viewer-deployment-actions:empty {
|
||
display: none;
|
||
}
|
||
|
||
.logs-viewer-deployment-btn {
|
||
height: 2rem !important;
|
||
min-height: 2rem !important;
|
||
padding-inline: 0.75rem !important;
|
||
font-size: 0.75rem !important;
|
||
line-height: 1rem !important;
|
||
border-radius: 0.5rem !important;
|
||
}
|
||
|
||
.logs-viewer-cancel-btn {
|
||
border-color: color-mix(in oklab, var(--color-error, #ef4444) 55%, transparent) !important;
|
||
background: color-mix(in oklab, var(--color-error, #ef4444) 10%, transparent) !important;
|
||
color: color-mix(in oklab, var(--color-error, #f87171) 90%, white) !important;
|
||
}
|
||
|
||
.logs-viewer-cancel-btn:hover {
|
||
border-color: color-mix(in oklab, var(--color-error, #ef4444) 75%, transparent) !important;
|
||
background: color-mix(in oklab, var(--color-error, #ef4444) 18%, transparent) !important;
|
||
}
|
||
|
||
.logs-viewer-btn,
|
||
.runtime-log-icon-button {
|
||
position: relative;
|
||
z-index: 0;
|
||
}
|
||
|
||
.logs-viewer-btn {
|
||
display: inline-flex;
|
||
flex-shrink: 0;
|
||
width: 2.25rem;
|
||
height: 2.25rem;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 0.5rem;
|
||
color: var(--color-neutral-500);
|
||
transition: color 0.15s, background-color 0.15s;
|
||
}
|
||
|
||
.logs-viewer-btn:hover {
|
||
background: var(--color-neutral-100);
|
||
color: var(--color-neutral-900);
|
||
}
|
||
|
||
.dark .logs-viewer-btn {
|
||
color: #a3a3a3;
|
||
}
|
||
|
||
.dark .logs-viewer-btn:hover {
|
||
background: rgba(255, 255, 255, 0.06);
|
||
color: #f5f5f5;
|
||
}
|
||
|
||
.logs-viewer-btn-active {
|
||
background: var(--color-coollabs);
|
||
color: #fff;
|
||
}
|
||
|
||
.dark .logs-viewer-btn-active {
|
||
color: #fff;
|
||
}
|
||
|
||
.logs-viewer-viewport {
|
||
min-width: 0;
|
||
padding: 0.5rem 0.75rem 0;
|
||
}
|
||
|
||
/* A flex item is reliably included in the scrollable overflow area, unlike
|
||
bottom padding on overflow containers in some browser/layout combinations. */
|
||
.logs-viewer-viewport::after {
|
||
content: "";
|
||
flex: 0 0 2rem;
|
||
}
|
||
|
||
.logs-viewer-line {
|
||
display: flex;
|
||
min-width: 0;
|
||
flex-direction: column;
|
||
gap: 0.125rem;
|
||
padding-block: 0.125rem;
|
||
}
|
||
|
||
.logs-viewer-line.hidden {
|
||
display: none;
|
||
}
|
||
|
||
.logs-viewer-timestamp {
|
||
flex-shrink: 0;
|
||
font-size: 0.625rem;
|
||
line-height: 1rem;
|
||
color: #737373;
|
||
letter-spacing: 0.01em;
|
||
}
|
||
|
||
.logs-viewer-line-text {
|
||
min-width: 0;
|
||
overflow-wrap: anywhere;
|
||
word-break: break-word;
|
||
white-space: pre-wrap;
|
||
}
|
||
|
||
@media (min-width: 640px) {
|
||
/* Desktop: [actions + status] ... [cancel + search] */
|
||
.logs-viewer-toolbar-controls {
|
||
flex-direction: row;
|
||
flex-wrap: nowrap;
|
||
align-items: center;
|
||
gap: 0.5rem 0.75rem;
|
||
}
|
||
|
||
.logs-viewer-primary {
|
||
width: auto;
|
||
flex: 0 1 auto;
|
||
}
|
||
|
||
.logs-viewer-meta {
|
||
order: 0;
|
||
width: auto;
|
||
flex: 0 0 auto;
|
||
}
|
||
|
||
.logs-viewer-search {
|
||
order: 1;
|
||
width: auto;
|
||
max-width: 16rem;
|
||
flex: 1 1 12rem;
|
||
}
|
||
|
||
.logs-viewer-actions {
|
||
order: 0;
|
||
width: auto;
|
||
flex: 0 0 auto;
|
||
max-width: 100%;
|
||
justify-content: flex-end;
|
||
overflow: visible;
|
||
}
|
||
|
||
.logs-viewer-end {
|
||
order: 1;
|
||
width: auto;
|
||
flex: 0 0 auto;
|
||
margin-left: auto;
|
||
}
|
||
|
||
.logs-viewer-end .logs-viewer-meta {
|
||
order: 2;
|
||
}
|
||
|
||
.runtime-logs-viewer-end {
|
||
align-items: center;
|
||
flex-direction: row;
|
||
}
|
||
|
||
.logs-viewer-deployment-actions {
|
||
width: auto;
|
||
flex: 0 0 auto;
|
||
}
|
||
|
||
.logs-viewer-btn {
|
||
width: 1.75rem;
|
||
height: 1.75rem;
|
||
border-radius: 0.375rem;
|
||
}
|
||
|
||
.logs-viewer-viewport {
|
||
padding: 0.5rem 1rem 0;
|
||
}
|
||
|
||
.logs-viewer-line {
|
||
flex-direction: row;
|
||
gap: 0.5rem;
|
||
padding-block: 0;
|
||
}
|
||
|
||
.logs-viewer-timestamp {
|
||
font-size: 0.75rem;
|
||
line-height: inherit;
|
||
}
|
||
}
|
||
|
||
/* Runtime log viewer */
|
||
.runtime-log-shell {
|
||
overflow: visible;
|
||
border-radius: 8px;
|
||
background: var(--coollabs-elevated);
|
||
box-shadow: 0 0 0 1px var(--coollabs-hairline);
|
||
}
|
||
|
||
.runtime-log-trigger {
|
||
display: flex;
|
||
min-height: 2.75rem;
|
||
cursor: pointer;
|
||
user-select: none;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
border-radius: 8px;
|
||
padding: 0.625rem 0.875rem;
|
||
color: #171717;
|
||
transition: background-color 0.15s;
|
||
}
|
||
|
||
.runtime-log-trigger:hover {
|
||
background: rgba(0, 0, 0, 0.025);
|
||
}
|
||
|
||
.dark .runtime-log-trigger {
|
||
color: var(--color-fg);
|
||
}
|
||
|
||
.dark .runtime-log-trigger:hover {
|
||
background: rgba(255, 255, 255, 0.03);
|
||
}
|
||
|
||
.runtime-log-trigger h4 {
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||
font-size: 0.8125rem;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.runtime-log-panel {
|
||
display: flex;
|
||
min-width: 0;
|
||
flex-direction: column;
|
||
overflow: visible;
|
||
border-radius: 8px;
|
||
background: var(--coollabs-base);
|
||
box-shadow: 0 0 0 1px var(--coollabs-fill);
|
||
}
|
||
|
||
.settings-log-panel .runtime-log-panel {
|
||
box-shadow: none;
|
||
}
|
||
|
||
.runtime-log-toolbar {
|
||
position: relative;
|
||
z-index: 20;
|
||
border-bottom-color: var(--coollabs-fill);
|
||
background: transparent;
|
||
}
|
||
|
||
/* Dark mode: the panel matches the cards. Rows are the card well color, and the
|
||
toolbar and column header are the darker card outer color. */
|
||
html.dark:not([data-theme="custom"]) .runtime-log-panel {
|
||
background: var(--color-card);
|
||
box-shadow: 0 0 0 1px rgb(255 255 255 / 8%);
|
||
}
|
||
|
||
html.dark:not([data-theme="custom"]) .runtime-log-panel :is(.runtime-log-toolbar, .runtime-log-columns),
|
||
html.dark:not([data-theme="custom"]) .logs-viewer > .runtime-log-toolbar {
|
||
border-bottom-color: rgb(255 255 255 / 8%);
|
||
background: color-mix(in srgb, var(--color-card) 66%, black);
|
||
}
|
||
|
||
html.dark:not([data-theme="custom"]) .runtime-log-panel .runtime-log-toolbar {
|
||
border-radius: 8px 8px 0 0;
|
||
}
|
||
|
||
.runtime-log-icon-button {
|
||
display: inline-flex;
|
||
flex-shrink: 0;
|
||
width: 2.25rem;
|
||
height: 2.25rem;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 0.5rem;
|
||
color: #737373;
|
||
transition: color 0.15s, background-color 0.15s;
|
||
}
|
||
|
||
@media (min-width: 640px) {
|
||
.runtime-log-icon-button {
|
||
width: 1.75rem;
|
||
height: 1.75rem;
|
||
border-radius: 6px;
|
||
}
|
||
}
|
||
|
||
.runtime-log-icon-button:hover {
|
||
background: rgba(0, 0, 0, 0.05);
|
||
color: #171717;
|
||
}
|
||
|
||
.dark .runtime-log-icon-button {
|
||
color: var(--color-fg-faint);
|
||
}
|
||
|
||
.dark .runtime-log-icon-button:hover {
|
||
background: rgba(255, 255, 255, 0.06);
|
||
color: var(--color-fg);
|
||
}
|
||
|
||
.runtime-log-icon-button-active {
|
||
background: var(--color-coollabs);
|
||
color: #fff;
|
||
}
|
||
|
||
.dark .runtime-log-icon-button-active {
|
||
background: var(--color-coollabs);
|
||
color: #fff;
|
||
}
|
||
|
||
.runtime-log-menu {
|
||
background: var(--coollabs-elevated) !important;
|
||
opacity: 1 !important;
|
||
}
|
||
|
||
/* View and Export menus sit after the search field and the Lines control. */
|
||
.runtime-log-menus {
|
||
display: flex;
|
||
order: 3;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.runtime-log-menu-label {
|
||
padding: 0.5rem 0.5rem 0.25rem;
|
||
font-size: 0.6875rem;
|
||
font-weight: 500;
|
||
color: #737373;
|
||
}
|
||
|
||
.dark .runtime-log-menu-label {
|
||
color: var(--color-fg-faint);
|
||
}
|
||
|
||
.runtime-log-viewport {
|
||
min-height: 12rem;
|
||
border-radius: 0 0 8px 8px;
|
||
background: var(--coollabs-elevated);
|
||
color: #262626;
|
||
}
|
||
|
||
@media (min-width: 640px) {
|
||
.runtime-log-viewport {
|
||
min-height: 18rem;
|
||
}
|
||
}
|
||
|
||
.dark .runtime-log-viewport {
|
||
background: var(--color-log);
|
||
color: var(--color-fg-dim);
|
||
}
|
||
|
||
.runtime-log-panel {
|
||
--runtime-log-line: rgba(0, 0, 0, 0.08);
|
||
--runtime-log-muted: #66666f;
|
||
--runtime-log-hover: rgba(0, 0, 0, 0.04);
|
||
--runtime-log-detail: rgba(0, 0, 0, 0.03);
|
||
--runtime-log-columns: 12.75rem 5.5rem minmax(0, 1fr);
|
||
}
|
||
|
||
.dark .runtime-log-panel {
|
||
--runtime-log-line: var(--glass-line, rgba(255, 255, 255, 0.065));
|
||
--runtime-log-muted: #a09da5;
|
||
--runtime-log-hover: rgba(255, 255, 255, 0.035);
|
||
--runtime-log-detail: rgba(0, 0, 0, 0.24);
|
||
}
|
||
|
||
.runtime-log-viewport.logs-viewer-viewport {
|
||
container: runtime-log-explorer / inline-size;
|
||
padding: 0;
|
||
}
|
||
|
||
.runtime-log-viewport.logs-viewer-viewport::after {
|
||
display: none;
|
||
}
|
||
|
||
.runtime-log-columns,
|
||
.runtime-log-viewport [data-log-line]:not(.hidden) {
|
||
display: grid;
|
||
grid-template-columns: var(--runtime-log-columns);
|
||
gap: 0.625rem;
|
||
box-sizing: border-box;
|
||
width: 100%;
|
||
align-items: baseline;
|
||
padding: 0.125rem 1.875rem 0.125rem 1rem;
|
||
}
|
||
|
||
.runtime-log-columns {
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 10;
|
||
border-bottom: 1px solid var(--runtime-log-line);
|
||
background: #f0eff2;
|
||
color: var(--runtime-log-muted);
|
||
font-family: ui-sans-serif, system-ui, sans-serif;
|
||
font-size: 0.75rem;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.dark .runtime-log-columns {
|
||
background: var(--coollabs-elevated);
|
||
}
|
||
|
||
html[data-theme="custom"] .runtime-log-columns {
|
||
background: var(--color-log-toolbar);
|
||
}
|
||
|
||
.runtime-log-viewport [data-log-line] {
|
||
position: relative;
|
||
border-bottom: 1px solid var(--runtime-log-line);
|
||
border-radius: 0;
|
||
cursor: default;
|
||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
|
||
font-size: 0.8125rem;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.runtime-log-viewport [data-log-line][data-expandable] {
|
||
cursor: pointer;
|
||
}
|
||
|
||
.runtime-log-viewport [data-log-line]:hover,
|
||
.runtime-log-viewport [data-log-line][aria-expanded="true"] {
|
||
background: var(--runtime-log-hover);
|
||
}
|
||
|
||
.runtime-log-viewport [data-log-line]:focus-visible {
|
||
outline: 2px solid var(--color-accent, #b93642);
|
||
outline-offset: -2px;
|
||
}
|
||
|
||
.runtime-log-viewport [data-log-line]::before {
|
||
content: "[" attr(data-log-level) "]";
|
||
grid-column: 2;
|
||
grid-row: 1;
|
||
color: var(--runtime-log-muted);
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
.runtime-log-viewport [data-log-line]:not([data-expandable], [aria-expanded="true"])::after {
|
||
display: none;
|
||
}
|
||
|
||
.runtime-log-viewport [data-log-line]::after {
|
||
content: "";
|
||
position: absolute;
|
||
top: 0.5rem;
|
||
right: 0.75rem;
|
||
width: 0.375rem;
|
||
height: 0.375rem;
|
||
border-right: 1.5px solid var(--runtime-log-muted);
|
||
border-bottom: 1.5px solid var(--runtime-log-muted);
|
||
transform: rotate(45deg);
|
||
}
|
||
|
||
.runtime-log-viewport [data-log-line][aria-expanded="true"]::after {
|
||
top: 0.6875rem;
|
||
transform: rotate(225deg);
|
||
}
|
||
|
||
.runtime-log-viewport .log-error::before { color: #e55e73; }
|
||
.runtime-log-viewport .log-warning::before { color: #d79945; }
|
||
.runtime-log-viewport .log-debug::before { color: #929099; }
|
||
.runtime-log-viewport .log-info::before { color: #8891f0; }
|
||
|
||
.runtime-log-viewport .logs-viewer-timestamp {
|
||
grid-column: 1;
|
||
grid-row: 1;
|
||
color: var(--runtime-log-muted);
|
||
font-size: 0.8125rem;
|
||
line-height: 1.6;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.runtime-log-viewport [data-line-text] {
|
||
display: flex;
|
||
align-items: baseline;
|
||
gap: 0.75rem;
|
||
grid-column: 3;
|
||
grid-row: 1;
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.runtime-log-message {
|
||
min-width: 0;
|
||
flex: 1 1 auto;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: pre;
|
||
}
|
||
|
||
.runtime-log-source {
|
||
flex: 0 0 8rem;
|
||
width: 8rem;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.runtime-log-detail {
|
||
margin: 0;
|
||
padding: 0.5rem 1rem;
|
||
border-bottom: 1px solid var(--runtime-log-line);
|
||
background: var(--runtime-log-detail);
|
||
color: inherit;
|
||
overflow-wrap: anywhere;
|
||
white-space: pre-wrap;
|
||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
|
||
font-size: 0.8125rem;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.dark .runtime-log-detail {
|
||
color: #adbdc9;
|
||
}
|
||
|
||
.runtime-log-virtual-list {
|
||
position: relative;
|
||
width: 100%;
|
||
}
|
||
|
||
.runtime-log-virtual-row {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
}
|
||
|
||
.runtime-log-without-time {
|
||
--runtime-log-columns: 5.5rem minmax(0, 1fr);
|
||
}
|
||
|
||
.runtime-log-without-time [data-log-line]::before {
|
||
grid-column: 1;
|
||
}
|
||
|
||
.runtime-log-without-time [data-line-text] {
|
||
grid-column: 2;
|
||
}
|
||
|
||
/* Text view: one compact, wrapped text line per log entry, like the old viewer. */
|
||
.runtime-log-text-view {
|
||
padding-block: 0;
|
||
}
|
||
|
||
.runtime-log-viewport .runtime-log-text-view [data-log-line]:not(.hidden) {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
gap: 0.125rem;
|
||
min-height: 0;
|
||
padding: 0 0.75rem;
|
||
border-bottom: 0;
|
||
cursor: text;
|
||
font-size: 0.75rem;
|
||
line-height: 1.45;
|
||
text-align: left;
|
||
}
|
||
|
||
.runtime-log-viewport .runtime-log-text-view [data-log-line]::before,
|
||
.runtime-log-viewport .runtime-log-text-view [data-log-line]::after {
|
||
display: none;
|
||
}
|
||
|
||
.runtime-log-viewport .runtime-log-text-view .logs-viewer-timestamp {
|
||
font-size: inherit;
|
||
line-height: inherit;
|
||
}
|
||
|
||
.runtime-log-viewport .runtime-log-text-view [data-line-text] {
|
||
align-items: flex-start;
|
||
flex: 1 1 auto;
|
||
overflow: visible;
|
||
overflow-wrap: anywhere;
|
||
white-space: normal;
|
||
}
|
||
|
||
.runtime-log-viewport .runtime-log-text-view .runtime-log-message {
|
||
overflow: visible;
|
||
overflow-wrap: anywhere;
|
||
text-overflow: unset;
|
||
white-space: pre-wrap;
|
||
}
|
||
|
||
@media (min-width: 640px) {
|
||
.runtime-log-viewport .runtime-log-text-view [data-log-line]:not(.hidden) {
|
||
flex-direction: row;
|
||
gap: 0.5rem;
|
||
}
|
||
}
|
||
|
||
.runtime-logs-workspace {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.75rem;
|
||
}
|
||
|
||
.runtime-logs-fill,
|
||
.runtime-logs-fill > #screen,
|
||
.runtime-logs-fill .runtime-logs-stream,
|
||
.runtime-logs-fill .runtime-log-panel {
|
||
display: flex;
|
||
min-width: 0;
|
||
min-height: 0;
|
||
flex: 1 1 auto;
|
||
flex-direction: column;
|
||
}
|
||
|
||
/* Comes after the shared min-height: 0 so a phone still gets a tall stream. */
|
||
.runtime-logs-fill {
|
||
min-height: 70dvh;
|
||
}
|
||
|
||
/* The page fills the area inside <main>. Those paddings live in layouts/app.blade.php:
|
||
lg:pt-[calc(3rem+1.75rem)] and lg:pb-10. Width stays on that layout's pageWidth wrapper. */
|
||
.runtime-logs-page {
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.runtime-logs-fill .runtime-log-loading,
|
||
.runtime-logs-fill .runtime-log-empty {
|
||
position: absolute;
|
||
inset: 0;
|
||
min-height: 0;
|
||
}
|
||
|
||
@media (min-width: 1280px) {
|
||
.runtime-logs-page {
|
||
height: calc(100dvh - 4.75rem - 2.5rem);
|
||
}
|
||
|
||
.runtime-logs-page > .application-settings-workspace {
|
||
display: flex;
|
||
min-height: 0;
|
||
flex: 1 1 auto;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.runtime-logs-page > .application-settings-workspace > .grid {
|
||
flex: 1 1 auto;
|
||
min-height: 0;
|
||
grid-template-rows: minmax(0, 1fr);
|
||
}
|
||
|
||
.runtime-logs-column {
|
||
display: flex;
|
||
min-height: 0;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.runtime-logs-column > .w-full {
|
||
display: flex;
|
||
min-height: 0;
|
||
flex: 1 1 auto;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.runtime-logs-workspace {
|
||
height: auto;
|
||
min-height: 0;
|
||
flex: 1 1 auto;
|
||
}
|
||
|
||
.runtime-logs-fill {
|
||
min-height: 0;
|
||
}
|
||
}
|
||
|
||
.deployment-badge {
|
||
@apply inline-flex shrink-0 items-center rounded-md px-1.5 py-0.5 text-[10px] font-semibold tracking-wide;
|
||
}
|
||
|
||
.deployment-badge-production {
|
||
@apply bg-success/10 text-success uppercase ring-1 ring-success/20;
|
||
}
|
||
|
||
.deployment-badge-preview {
|
||
@apply bg-coollabs/10 text-coollabs ring-1 ring-coollabs/20 dark:bg-brand/15 dark:text-brand-light dark:ring-brand-light/25;
|
||
}
|
||
|
||
.runtime-logs-fill .runtime-log-viewport {
|
||
min-height: 0;
|
||
flex: 1 1 auto;
|
||
max-height: none;
|
||
}
|
||
|
||
.runtime-log-viewport .runtime-log-text-view .log-error [data-line-text] { color: #e55e73; }
|
||
.runtime-log-viewport .runtime-log-text-view .log-warning [data-line-text] { color: #d79945; }
|
||
.runtime-log-viewport .runtime-log-text-view .log-debug [data-line-text] { color: #929099; }
|
||
|
||
@container runtime-log-explorer (max-width: 650px) {
|
||
.runtime-log-columns,
|
||
.runtime-log-viewport [data-log-line]:not(.hidden) {
|
||
grid-template-columns: minmax(0, 1fr) 5.5rem;
|
||
gap: 0.1875rem 0.5rem;
|
||
}
|
||
|
||
.runtime-log-columns > :last-child,
|
||
.runtime-log-viewport [data-line-text] {
|
||
grid-column: 1 / -1;
|
||
grid-row: 2;
|
||
}
|
||
|
||
.runtime-log-without-time [data-line-text] {
|
||
grid-column: 1 / -1;
|
||
}
|
||
}
|
||
|
||
.runtime-log-empty {
|
||
display: flex;
|
||
min-height: 18rem;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 2rem;
|
||
color: var(--runtime-log-muted);
|
||
text-align: center;
|
||
}
|
||
|
||
.runtime-log-loading {
|
||
min-height: 18rem;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 0.625rem;
|
||
padding: 2rem;
|
||
color: var(--runtime-log-muted);
|
||
font-size: 0.8125rem;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.runtime-log-empty p {
|
||
color: inherit;
|
||
font-size: 0.8125rem;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.runtime-log-empty div > span {
|
||
display: block;
|
||
margin-top: 0.125rem;
|
||
font-size: 0.75rem;
|
||
line-height: 1.25rem;
|
||
}
|
||
|
||
.env-table-detail {
|
||
padding: 0.25rem 1rem 1.25rem;
|
||
}
|
||
|
||
/* Small pill badges for table cells */
|
||
.table-badge {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.25rem;
|
||
height: 20px;
|
||
padding: 0 0.5rem;
|
||
border-radius: 9999px;
|
||
font-size: 11px;
|
||
font-weight: 500;
|
||
white-space: nowrap;
|
||
background: var(--coollabs-fill);
|
||
color: #52525b;
|
||
}
|
||
|
||
.dark .table-badge {
|
||
color: var(--color-fg-dim);
|
||
}
|
||
|
||
.table-badge-danger {
|
||
background: rgba(248, 113, 113, 0.14);
|
||
color: #dc2626;
|
||
}
|
||
|
||
.dark .table-badge-danger {
|
||
color: #f87171;
|
||
}
|
||
|
||
.table-badge-warning {
|
||
background: rgba(245, 158, 11, 0.16);
|
||
color: #b45309;
|
||
}
|
||
|
||
.dark .table-badge-warning {
|
||
background: rgba(245, 158, 11, 0.14);
|
||
color: #fbbf24;
|
||
}
|
||
|
||
.table-badge-success {
|
||
background: rgba(16, 185, 129, 0.14);
|
||
color: #047857;
|
||
}
|
||
|
||
.dark .table-badge-success {
|
||
background: rgba(16, 185, 129, 0.16);
|
||
color: #34d399;
|
||
}
|
||
|
||
/* Suggested / not-yet-configured domain rows — distinct from real FQDNs */
|
||
.domains-row-suggested {
|
||
background: rgba(245, 158, 11, 0.05);
|
||
box-shadow: inset 3px 0 0 0 rgba(245, 158, 11, 0.55);
|
||
}
|
||
|
||
.dark .domains-row-suggested {
|
||
background: rgba(245, 158, 11, 0.07);
|
||
box-shadow: inset 3px 0 0 0 rgba(251, 191, 36, 0.5);
|
||
}
|
||
|
||
/* Chip/tag input (comma-free multi-value entry, e.g. Domains) */
|
||
.chip-input {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
gap: 0.375rem;
|
||
min-height: 2rem;
|
||
width: 100%;
|
||
/* 3px block padding: 24px chip row + 6px + 2px border = 32px, matching .input/.button */
|
||
padding: 0.1875rem 0.375rem;
|
||
border-radius: 8px;
|
||
border: 1px solid var(--coollabs-line);
|
||
background: var(--coollabs-recessed);
|
||
cursor: text;
|
||
}
|
||
|
||
.chip-input:focus-within {
|
||
border-color: var(--color-accent);
|
||
box-shadow: none;
|
||
}
|
||
|
||
.chip-input input {
|
||
flex: 1 1 10rem;
|
||
min-width: 10rem;
|
||
height: 1.5rem;
|
||
margin: 0;
|
||
padding: 0 0.25rem;
|
||
border: none;
|
||
border-radius: 0;
|
||
background: transparent;
|
||
font-size: 0.875rem;
|
||
color: #000000;
|
||
outline: none;
|
||
box-shadow: none;
|
||
}
|
||
|
||
.dark .chip-input input {
|
||
color: var(--color-fg);
|
||
}
|
||
|
||
.chip-input input:focus,
|
||
.chip-input input:focus-visible {
|
||
outline: none;
|
||
box-shadow: none;
|
||
border: none;
|
||
}
|
||
|
||
.chip-input input::placeholder {
|
||
color: var(--coollabs-subtle);
|
||
opacity: 0.7;
|
||
}
|
||
|
||
.chip {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 0.375rem;
|
||
height: 1.5rem;
|
||
max-width: 100%;
|
||
padding: 0 0.375rem 0 0.625rem;
|
||
border-radius: 6px;
|
||
border: none;
|
||
background: var(--coollabs-fill);
|
||
font-size: 0.8125rem;
|
||
color: #000000;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.dark .chip {
|
||
color: var(--color-fg);
|
||
}
|
||
|
||
.chip > span {
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
.chip-remove {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 1rem;
|
||
height: 1rem;
|
||
border-radius: 3px;
|
||
color: var(--coollabs-subtle);
|
||
transition: color 0.15s;
|
||
}
|
||
|
||
.chip-remove:hover {
|
||
color: inherit;
|
||
}
|
||
|
||
/* In-card sub-headings (e.g. "Container labels", "Docker Compose") */
|
||
.application-settings-form h4,
|
||
.application-settings-form .application-settings-section-body h3 {
|
||
font-size: 0.875rem;
|
||
font-weight: 600;
|
||
}
|
||
|
||
/* Compact resource table on the project environment page. */
|
||
.environment-resource-grid {
|
||
display: grid;
|
||
grid-template-columns:
|
||
minmax(240px, 2fr)
|
||
minmax(100px, 0.7fr)
|
||
minmax(110px, 0.8fr)
|
||
minmax(180px, 1.2fr)
|
||
minmax(120px, 0.8fr)
|
||
minmax(140px, 1fr);
|
||
column-gap: 1rem;
|
||
}
|
||
|
||
.environment-resource-grid .mobile-resource-domain {
|
||
display: none;
|
||
}
|
||
|
||
.projects-table-grid {
|
||
display: grid;
|
||
grid-template-columns:
|
||
minmax(200px, 1.7fr)
|
||
8rem
|
||
7rem
|
||
minmax(200px, 1.6fr)
|
||
5rem;
|
||
column-gap: 1rem;
|
||
}
|
||
|
||
.tags-table-grid {
|
||
display: grid;
|
||
grid-template-columns:
|
||
minmax(220px, 1.7fr)
|
||
minmax(100px, 0.65fr)
|
||
minmax(110px, 0.7fr)
|
||
minmax(90px, 0.55fr);
|
||
column-gap: 1rem;
|
||
}
|
||
|
||
.environments-table-grid {
|
||
display: grid;
|
||
grid-template-columns:
|
||
minmax(220px, 1.5fr)
|
||
minmax(90px, 0.55fr)
|
||
minmax(240px, 1.6fr)
|
||
6rem;
|
||
column-gap: 1rem;
|
||
}
|
||
|
||
@media (max-width: 900px) {
|
||
.environments-table-grid {
|
||
grid-template-columns: minmax(220px, 1fr) 6rem 6rem;
|
||
}
|
||
|
||
.environments-table-grid .environment-description {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 640px) {
|
||
.environments-table-grid {
|
||
grid-template-columns: minmax(0, 1fr) 5rem;
|
||
column-gap: 0.75rem;
|
||
}
|
||
|
||
.environments-table-grid .environment-resource-count {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 1050px) {
|
||
.projects-table-grid {
|
||
grid-template-columns: minmax(200px, 1fr) 8rem 7rem 5rem;
|
||
}
|
||
|
||
.projects-table-grid .project-description {
|
||
display: none;
|
||
}
|
||
|
||
.tags-table-grid {
|
||
grid-template-columns: minmax(220px, 1fr) 7rem 6rem 3rem;
|
||
}
|
||
|
||
.tags-table-grid .tag-services {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 640px) {
|
||
.projects-table-grid {
|
||
grid-template-columns: minmax(0, 1fr) 5rem;
|
||
column-gap: 0.75rem;
|
||
}
|
||
|
||
.projects-table-grid > :nth-child(2),
|
||
.projects-table-grid > :nth-child(3) {
|
||
display: none;
|
||
}
|
||
|
||
.tags-table-grid {
|
||
grid-template-columns: minmax(0, 1fr) 5rem 3rem;
|
||
column-gap: 0.75rem;
|
||
}
|
||
|
||
.tags-table-grid > :nth-child(3),
|
||
.tags-table-grid > :nth-child(4) {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 1200px) {
|
||
.environment-resource-grid {
|
||
grid-template-columns:
|
||
minmax(220px, 2fr)
|
||
minmax(100px, 0.8fr)
|
||
minmax(110px, 0.8fr)
|
||
minmax(120px, 1fr);
|
||
}
|
||
|
||
.environment-resource-grid .resource-domain,
|
||
.environment-resource-grid .resource-tags {
|
||
display: none;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.environment-resource-grid {
|
||
grid-template-columns: minmax(0, 1fr) 6.5rem;
|
||
column-gap: 0.75rem;
|
||
}
|
||
|
||
.environment-resource-grid .resource-type,
|
||
.environment-resource-grid .resource-server {
|
||
display: none;
|
||
}
|
||
|
||
.environment-resource-grid .mobile-resource-domain {
|
||
display: block;
|
||
}
|
||
}
|
||
|
||
/* Command palette (global search) */
|
||
.command-palette {
|
||
display: flex;
|
||
width: 100%;
|
||
max-width: 42rem;
|
||
flex-direction: column;
|
||
overflow: hidden;
|
||
border-radius: 10px;
|
||
background: var(--coollabs-elevated);
|
||
box-shadow: 0 0 0 1px var(--coollabs-hairline), var(--shadow-dropdown);
|
||
}
|
||
|
||
.command-palette-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.625rem;
|
||
min-height: 2.75rem;
|
||
padding: 0 0.75rem 0 0.875rem;
|
||
background: var(--coollabs-elevated);
|
||
}
|
||
|
||
.command-palette-header-icon {
|
||
display: flex;
|
||
width: 1rem;
|
||
height: 1rem;
|
||
flex-shrink: 0;
|
||
color: var(--coollabs-subtle);
|
||
}
|
||
|
||
.command-palette-header-icon.is-loading {
|
||
color: var(--color-accent);
|
||
}
|
||
|
||
.command-palette-input {
|
||
min-width: 0;
|
||
flex: 1;
|
||
height: 2.75rem;
|
||
border: 0;
|
||
background: transparent;
|
||
font-size: 0.875rem;
|
||
line-height: 1.25rem;
|
||
color: #171717;
|
||
outline: none;
|
||
box-shadow: none !important;
|
||
}
|
||
|
||
.dark .command-palette-input {
|
||
color: var(--color-fg);
|
||
}
|
||
|
||
.command-palette-input::placeholder {
|
||
color: var(--coollabs-subtle);
|
||
}
|
||
|
||
.command-palette-input:focus,
|
||
.command-palette-input:focus-visible {
|
||
outline: none !important;
|
||
box-shadow: none !important;
|
||
border: 0 !important;
|
||
}
|
||
|
||
/* Search scope switch (Global / This page), shown on resource settings pages */
|
||
.command-palette-scope {
|
||
display: inline-flex;
|
||
flex-shrink: 0;
|
||
align-items: center;
|
||
gap: 0.125rem;
|
||
padding: 0.125rem;
|
||
border-radius: 0.5rem;
|
||
border: 1px solid var(--coollabs-hairline);
|
||
}
|
||
|
||
.command-palette-scope-option {
|
||
height: 1.375rem;
|
||
padding: 0 0.5rem;
|
||
border-radius: 0.375rem;
|
||
color: var(--coollabs-subtle);
|
||
font-size: 0.75rem;
|
||
font-weight: 500;
|
||
white-space: nowrap;
|
||
cursor: pointer;
|
||
transition: color 0.15s ease, background 0.15s ease;
|
||
}
|
||
|
||
.command-palette-scope-option:hover {
|
||
color: #171717;
|
||
}
|
||
|
||
.command-palette-scope-option[aria-pressed='true'] {
|
||
color: #171717;
|
||
background: var(--coollabs-fill);
|
||
}
|
||
|
||
.dark .command-palette-scope-option:hover,
|
||
.dark .command-palette-scope-option[aria-pressed='true'] {
|
||
color: var(--color-fg);
|
||
}
|
||
|
||
.dark .command-palette-scope-option[aria-pressed='true'] {
|
||
background: rgb(255 255 255 / 0.1);
|
||
}
|
||
|
||
.command-palette-body {
|
||
max-height: min(60vh, 28rem);
|
||
overflow-y: auto;
|
||
border-radius: 8px;
|
||
background: var(--coollabs-base);
|
||
box-shadow: 0 0 0 1px var(--coollabs-fill);
|
||
scrollbar-width: thin;
|
||
scrollbar-color: var(--coollabs-fill) transparent;
|
||
}
|
||
|
||
.command-palette-body::-webkit-scrollbar {
|
||
width: 8px;
|
||
}
|
||
|
||
.command-palette-body::-webkit-scrollbar-track {
|
||
background: transparent;
|
||
}
|
||
|
||
.command-palette-body::-webkit-scrollbar-thumb {
|
||
border-radius: 9999px;
|
||
border: 2px solid transparent;
|
||
background-clip: padding-box;
|
||
background-color: var(--coollabs-fill);
|
||
}
|
||
|
||
.command-palette-section {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 1px;
|
||
padding: 0.25rem;
|
||
}
|
||
|
||
.command-palette-section + .command-palette-section {
|
||
border-top: 1px solid var(--coollabs-fill);
|
||
margin-top: 0;
|
||
padding-top: 0.25rem;
|
||
}
|
||
|
||
.command-palette-group-label {
|
||
padding: 0.375rem 0.5rem 0.25rem;
|
||
font-size: 0.6875rem;
|
||
font-weight: 500;
|
||
letter-spacing: 0.01em;
|
||
color: var(--coollabs-subtle);
|
||
}
|
||
|
||
/* Result rows: inset listbox-style pills. Kill the global button/a
|
||
focus-visible ring so keyboard selection is a soft fill, not yellow bars. */
|
||
.command-palette-item {
|
||
display: flex;
|
||
position: relative;
|
||
width: 100%;
|
||
min-height: 2.5rem;
|
||
align-items: center;
|
||
gap: 0.625rem;
|
||
padding: 0.4375rem 0.5rem;
|
||
border-radius: 6px;
|
||
text-align: left;
|
||
color: inherit;
|
||
transition: background 0.1s ease, box-shadow 0.1s ease;
|
||
outline: none !important;
|
||
border: 0;
|
||
box-shadow: none !important;
|
||
--tw-ring-shadow: 0 0 #0000 !important;
|
||
--tw-ring-offset-shadow: 0 0 #0000 !important;
|
||
--tw-ring-offset-width: 0px !important;
|
||
}
|
||
|
||
a.command-palette-item,
|
||
button.command-palette-item {
|
||
text-decoration: none;
|
||
background: transparent;
|
||
border: 0;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.command-palette-item:hover {
|
||
background: var(--coollabs-fill);
|
||
}
|
||
|
||
/* Keyboard / programmatic focus = active selection */
|
||
.command-palette-item:focus,
|
||
.command-palette-item:focus-visible,
|
||
button.command-palette-item:focus-visible,
|
||
a.command-palette-item:focus-visible {
|
||
outline: none !important;
|
||
background: color-mix(in oklab, var(--color-accent) 12%, transparent);
|
||
box-shadow: none !important;
|
||
--tw-ring-shadow: 0 0 #0000 !important;
|
||
--tw-ring-offset-shadow: 0 0 #0000 !important;
|
||
--tw-ring-offset-width: 0px !important;
|
||
}
|
||
|
||
/* Soft left accent rail on focused row (matches sidebar language) */
|
||
.command-palette-item:focus::before,
|
||
.command-palette-item:focus-visible::before {
|
||
content: "";
|
||
position: absolute;
|
||
top: 6px;
|
||
bottom: 6px;
|
||
left: 0;
|
||
width: 2px;
|
||
border-radius: 9999px;
|
||
background: var(--color-accent);
|
||
pointer-events: none;
|
||
}
|
||
|
||
.command-palette-item-main {
|
||
min-width: 0;
|
||
flex: 1;
|
||
}
|
||
|
||
.command-palette-item-title {
|
||
display: flex;
|
||
min-width: 0;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
gap: 0.375rem 0.5rem;
|
||
}
|
||
|
||
.command-palette-item-name {
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
font-size: 0.8125rem;
|
||
font-weight: 500;
|
||
line-height: 1.25rem;
|
||
color: #171717;
|
||
}
|
||
|
||
.dark .command-palette-item-name {
|
||
color: var(--color-fg);
|
||
}
|
||
|
||
.command-palette-item:focus .command-palette-item-name,
|
||
.command-palette-item:focus-visible .command-palette-item-name {
|
||
color: #171717;
|
||
}
|
||
|
||
.dark .command-palette-item:focus .command-palette-item-name,
|
||
.dark .command-palette-item:focus-visible .command-palette-item-name {
|
||
color: var(--color-fg);
|
||
}
|
||
|
||
.command-palette-item-meta {
|
||
margin-top: 0.0625rem;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
font-size: 0.6875rem;
|
||
line-height: 1rem;
|
||
color: var(--coollabs-subtle);
|
||
}
|
||
|
||
.command-palette-type-badge {
|
||
display: inline-flex;
|
||
flex-shrink: 0;
|
||
align-items: center;
|
||
height: 1.25rem;
|
||
padding: 0 0.4375rem;
|
||
border-radius: 9999px;
|
||
background: var(--coollabs-recessed);
|
||
color: var(--coollabs-subtle);
|
||
font-size: 0.625rem;
|
||
font-weight: 500;
|
||
line-height: 1;
|
||
}
|
||
|
||
.command-palette-item:focus .command-palette-type-badge,
|
||
.command-palette-item:focus-visible .command-palette-type-badge {
|
||
background: color-mix(in oklab, var(--color-accent) 14%, transparent);
|
||
color: var(--color-accent);
|
||
}
|
||
|
||
/* Shortcut hint chip next to create action titles */
|
||
.command-palette-quickcommand {
|
||
display: inline-flex;
|
||
flex-shrink: 0;
|
||
align-items: center;
|
||
height: 1.25rem;
|
||
padding: 0 0.375rem;
|
||
border-radius: 0.25rem;
|
||
background: var(--coollabs-recessed);
|
||
color: var(--coollabs-subtle);
|
||
font-size: 0.625rem;
|
||
font-weight: 500;
|
||
line-height: 1;
|
||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||
}
|
||
|
||
.command-palette-item:focus .command-palette-quickcommand,
|
||
.command-palette-item:focus-visible .command-palette-quickcommand {
|
||
background: color-mix(in oklab, var(--color-accent) 12%, transparent);
|
||
color: color-mix(in oklab, var(--color-accent) 85%, var(--coollabs-subtle));
|
||
}
|
||
|
||
/* Leading icon tile — neutral by default; accent only when the row is active */
|
||
.command-palette-item-icon {
|
||
display: flex;
|
||
width: 1.75rem;
|
||
height: 1.75rem;
|
||
flex-shrink: 0;
|
||
align-items: center;
|
||
justify-content: center;
|
||
overflow: hidden;
|
||
border-radius: 6px;
|
||
background: var(--coollabs-recessed);
|
||
box-shadow: inset 0 0 0 1px var(--coollabs-fill);
|
||
color: var(--coollabs-subtle);
|
||
transition: background 0.1s ease, color 0.1s ease, box-shadow 0.1s ease;
|
||
}
|
||
|
||
.command-palette-item-icon img {
|
||
width: 1.125rem;
|
||
height: 1.125rem;
|
||
object-fit: contain;
|
||
}
|
||
|
||
.command-palette-item-icon.is-create {
|
||
background: var(--coollabs-recessed);
|
||
color: var(--coollabs-subtle);
|
||
box-shadow: inset 0 0 0 1px var(--coollabs-fill);
|
||
}
|
||
|
||
.command-palette-item-icon.is-create svg {
|
||
width: 0.875rem;
|
||
height: 0.875rem;
|
||
}
|
||
|
||
.command-palette-item:hover .command-palette-item-icon.is-create {
|
||
color: #171717;
|
||
}
|
||
|
||
.dark .command-palette-item:hover .command-palette-item-icon.is-create {
|
||
color: var(--color-fg);
|
||
}
|
||
|
||
.command-palette-item:focus .command-palette-item-icon,
|
||
.command-palette-item:focus-visible .command-palette-item-icon,
|
||
.command-palette-item:focus .command-palette-item-icon.is-create,
|
||
.command-palette-item:focus-visible .command-palette-item-icon.is-create {
|
||
background: color-mix(in oklab, var(--color-accent) 16%, transparent);
|
||
color: var(--color-accent);
|
||
box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--color-accent) 28%, transparent);
|
||
}
|
||
|
||
.command-palette-item-chevron {
|
||
width: 0.875rem;
|
||
height: 0.875rem;
|
||
flex-shrink: 0;
|
||
color: var(--coollabs-subtle);
|
||
opacity: 0.4;
|
||
transition: opacity 0.1s ease, color 0.1s ease;
|
||
}
|
||
|
||
.command-palette-item:hover .command-palette-item-chevron {
|
||
opacity: 0.7;
|
||
}
|
||
|
||
.command-palette-item:focus .command-palette-item-chevron,
|
||
.command-palette-item:focus-visible .command-palette-item-chevron {
|
||
opacity: 1;
|
||
color: var(--color-accent);
|
||
}
|
||
|
||
.command-palette-step-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.625rem;
|
||
margin-bottom: 0.125rem;
|
||
padding: 0.25rem 0.375rem;
|
||
}
|
||
|
||
.command-palette-step-back {
|
||
display: inline-flex;
|
||
width: 1.75rem;
|
||
height: 1.75rem;
|
||
flex-shrink: 0;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 0.375rem;
|
||
color: var(--coollabs-subtle);
|
||
transition: background 0.1s ease, color 0.1s ease;
|
||
}
|
||
|
||
.command-palette-step-back:hover {
|
||
background: var(--coollabs-recessed);
|
||
color: #171717;
|
||
}
|
||
|
||
.dark .command-palette-step-back:hover {
|
||
color: var(--color-fg);
|
||
}
|
||
|
||
.command-palette-step-title {
|
||
font-size: 0.8125rem;
|
||
font-weight: 500;
|
||
line-height: 1.25rem;
|
||
color: #171717;
|
||
}
|
||
|
||
.dark .command-palette-step-title {
|
||
color: var(--color-fg);
|
||
}
|
||
|
||
.command-palette-step-subtitle {
|
||
font-size: 0.6875rem;
|
||
line-height: 1rem;
|
||
color: var(--coollabs-subtle);
|
||
}
|
||
|
||
.command-palette-status {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.625rem;
|
||
margin: 0.125rem 0;
|
||
padding: 0.625rem 0.75rem;
|
||
border-radius: 6px;
|
||
background: var(--coollabs-recessed);
|
||
color: var(--coollabs-subtle);
|
||
font-size: 0.75rem;
|
||
line-height: 1rem;
|
||
}
|
||
|
||
.command-palette-status.is-error {
|
||
background: color-mix(in oklab, #ef4444 10%, transparent);
|
||
color: #b91c1c;
|
||
}
|
||
|
||
.dark .command-palette-status.is-error {
|
||
color: #fca5a5;
|
||
}
|
||
|
||
.command-palette-empty {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 0.25rem;
|
||
padding: 2rem 1.25rem;
|
||
text-align: center;
|
||
}
|
||
|
||
.command-palette-empty-title {
|
||
font-size: 0.8125rem;
|
||
font-weight: 500;
|
||
color: #171717;
|
||
}
|
||
|
||
.dark .command-palette-empty-title {
|
||
color: var(--color-fg);
|
||
}
|
||
|
||
.command-palette-empty-desc {
|
||
max-width: 28ch;
|
||
font-size: 0.75rem;
|
||
line-height: 1.125rem;
|
||
color: var(--coollabs-subtle);
|
||
}
|
||
|
||
.command-palette-arch-badge {
|
||
display: inline-flex;
|
||
flex-shrink: 0;
|
||
align-items: center;
|
||
height: 1.25rem;
|
||
padding: 0 0.4375rem;
|
||
border-radius: 9999px;
|
||
background: color-mix(in oklab, #f59e0b 16%, transparent);
|
||
color: #b45309;
|
||
font-size: 0.625rem;
|
||
font-weight: 500;
|
||
line-height: 1;
|
||
}
|
||
|
||
.dark .command-palette-arch-badge {
|
||
color: #fcd34d;
|
||
}
|
||
|
||
/* Service domains prioritize public addresses; configuration lives in settings. */
|
||
#service-domains-section,
|
||
.domains-overview-container {
|
||
container: service-domains / inline-size;
|
||
}
|
||
|
||
.service-domains-overview-grid {
|
||
grid-template-columns: minmax(0, 1fr) 7.25rem 7.5rem 5.5rem 6.5rem 8rem 6.5rem;
|
||
column-gap: 0.75rem;
|
||
}
|
||
|
||
.data-table-row.service-domains-overview-grid {
|
||
padding-block: 0.5rem;
|
||
}
|
||
|
||
.service-domain-detail {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
min-width: 0;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.service-domains-overview-grid > span:not(:first-child):not(:last-child) {
|
||
text-align: center;
|
||
}
|
||
|
||
.service-domain-detail-label {
|
||
display: none;
|
||
}
|
||
|
||
.service-domain-mobile-summary {
|
||
display: none;
|
||
}
|
||
|
||
.service-domains-https .listbox-trigger {
|
||
min-width: 7rem;
|
||
}
|
||
|
||
@container service-domains (max-width: 980px) {
|
||
.data-table-header.service-domains-overview-grid {
|
||
display: none;
|
||
}
|
||
|
||
.data-table-row.service-domains-overview-grid {
|
||
grid-template-columns: minmax(0, 1fr) auto auto;
|
||
grid-template-areas: "domain dns actions" "summary summary summary";
|
||
gap: 0.625rem 0.75rem;
|
||
padding: 0.75rem;
|
||
}
|
||
|
||
.data-table-row.service-domains-overview-grid > :first-child {
|
||
grid-area: domain;
|
||
}
|
||
|
||
.service-domain-detail,
|
||
.domains-service-desktop {
|
||
display: none;
|
||
}
|
||
|
||
.service-domain-mobile-summary {
|
||
display: flex;
|
||
grid-area: summary;
|
||
flex-wrap: wrap;
|
||
align-items: center;
|
||
gap: 0.375rem 0.75rem;
|
||
color: var(--coollabs-fg-dim);
|
||
font-size: 12px;
|
||
line-height: 1.25rem;
|
||
}
|
||
|
||
.service-domain-mobile-summary > span:not(:last-child)::after {
|
||
margin-left: 0.75rem;
|
||
color: var(--coollabs-line);
|
||
content: "·";
|
||
}
|
||
|
||
.service-domain-dns {
|
||
grid-area: dns;
|
||
justify-self: end;
|
||
}
|
||
|
||
.service-domain-actions {
|
||
grid-area: actions;
|
||
justify-self: end;
|
||
}
|
||
}
|
||
|
||
@container service-domains (max-width: 600px) {
|
||
.data-table-row.service-domains-overview-grid {
|
||
grid-template-columns: minmax(0, 1fr) auto;
|
||
grid-template-areas: "domain domain" "summary summary" "dns actions";
|
||
gap: 0.625rem 0.75rem;
|
||
padding: 0.875rem;
|
||
}
|
||
|
||
.data-table-row.service-domains-overview-grid > :first-child {
|
||
grid-area: domain;
|
||
}
|
||
|
||
.data-table-row.service-domains-overview-grid > :first-child a,
|
||
.data-table-row.service-domains-overview-grid > :first-child span[title] {
|
||
overflow: visible;
|
||
white-space: normal;
|
||
overflow-wrap: anywhere;
|
||
line-height: 1.35;
|
||
}
|
||
|
||
.service-domain-dns {
|
||
grid-area: dns;
|
||
justify-self: start;
|
||
}
|
||
|
||
.service-domain-actions {
|
||
grid-area: actions;
|
||
justify-self: end;
|
||
}
|
||
|
||
.service-domain-actions .icon-button {
|
||
width: 2.5rem;
|
||
height: 2.5rem;
|
||
}
|
||
}
|
||
|
||
/* Navigation items: each sidebar icon sits in a framed 30px tile with the 3D
|
||
depth of .button, like the canvas tool rail. Only the tile reacts; the row
|
||
has no background. Applies to both navigation styles. Appearance →
|
||
Style → Flat sets data-nav-items="flat" on <html> and turns these
|
||
rules off, which brings back the plain rows (see also the .button rules). */
|
||
html:where(:not([data-nav-items="flat"])) .menu-item:not(.sidebar-toggle) {
|
||
height: 2.25rem;
|
||
padding-left: 0.25rem;
|
||
}
|
||
|
||
/* In-page section rows have no icon tile, so they stay compact. */
|
||
html:where(:not([data-nav-items="flat"])) .menu-subitem {
|
||
height: 1.75rem;
|
||
}
|
||
|
||
/* Only the icon tile reacts: no row background on hover or for the current
|
||
item; the current item shows through its tile and its brighter label.
|
||
In the utilities layer so it can beat the search row's !bg-* utilities
|
||
(a layered !important wins over an unlayered one). */
|
||
@layer utilities {
|
||
/* The submenu Back row (navbar) keeps its fill. */
|
||
html:where(:not([data-nav-items="flat"])) :is(.menu-item, .menu-subitem):not(.sidebar-toggle, .menu-back),
|
||
html:where(:not([data-nav-items="flat"])) :is(.menu-item, .menu-subitem):not(.sidebar-toggle, .menu-back):hover {
|
||
background-color: transparent !important;
|
||
}
|
||
|
||
/* The expanded Search field keeps its input fill (see .menu-search below). */
|
||
html:where(:not([data-nav-items="flat"])) nav:not(.sidebar-collapsed) .menu-search.menu-item {
|
||
background-color: var(--color-neutral-100) !important;
|
||
}
|
||
|
||
html:where(:not([data-nav-items="flat"])) nav:not(.sidebar-collapsed) .menu-search.menu-item:hover {
|
||
background-color: var(--color-neutral-200) !important;
|
||
}
|
||
|
||
html.dark:where(:not([data-nav-items="flat"])) nav:not(.sidebar-collapsed) .menu-search.menu-item {
|
||
background-color: oklch(100% 0 0 / 0.04) !important;
|
||
}
|
||
|
||
html.dark:where(:not([data-nav-items="flat"])) nav:not(.sidebar-collapsed) .menu-search.menu-item:hover {
|
||
background-color: oklch(100% 0 0 / 0.07) !important;
|
||
}
|
||
|
||
/* The collapsed desktop rail shows Search as a tile only, without the row fill. */
|
||
html .app-sidebar nav.sidebar-collapsed .menu-search.menu-item,
|
||
html .app-sidebar nav.sidebar-collapsed .menu-search.menu-item:hover {
|
||
background-color: transparent !important;
|
||
}
|
||
|
||
@media (max-width: 1023.98px) {
|
||
html:where(:not([data-nav-items="flat"])) nav.sidebar-collapsed .menu-search.menu-item {
|
||
background-color: var(--color-neutral-100) !important;
|
||
}
|
||
|
||
html.dark:where(:not([data-nav-items="flat"])) nav.sidebar-collapsed .menu-search.menu-item {
|
||
background-color: oklch(100% 0 0 / 0.04) !important;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* The current item's tile takes the accent, like the active tab pill. */
|
||
html:where(:not([data-nav-items="flat"])) :is(.menu-item-active, .menu-subitem-active) .menu-item-icon {
|
||
color: var(--color-coollabs);
|
||
border-color: color-mix(in oklab, var(--color-coollabs) 45%, transparent) !important;
|
||
--button-depth-color: color-mix(in oklab, var(--color-coollabs) 45%, transparent) !important;
|
||
}
|
||
|
||
html.dark:where(:not([data-nav-items="flat"])) :is(.menu-item-active, .menu-subitem-active) .menu-item-icon:not(.text-error) {
|
||
color: var(--color-brand-foreground);
|
||
border-color: var(--color-brand-ring) !important;
|
||
background: linear-gradient(180deg, var(--color-brand-hover) 0%, var(--color-brand-press) 100%) !important;
|
||
--button-depth-color: var(--color-brand-depth) !important;
|
||
box-shadow: 0 2px 0 var(--color-brand-depth) !important;
|
||
}
|
||
|
||
html.dark:where(:not([data-nav-items="flat"])) :is(.menu-item-active, .menu-subitem-active):active .menu-item-icon:not(.text-error) {
|
||
transform: translateY(2px);
|
||
box-shadow: none !important;
|
||
}
|
||
|
||
/* Danger Zone icons (<x-reicon name="danger">) stay red, also when active. */
|
||
html:where(:not([data-nav-items="flat"])) :is(.menu-item-active, .menu-subitem-active) .menu-item-icon.text-error {
|
||
color: var(--color-error);
|
||
border-color: color-mix(in oklab, var(--color-error) 45%, transparent) !important;
|
||
--button-depth-color: color-mix(in oklab, var(--color-error) 40%, transparent) !important;
|
||
}
|
||
|
||
/* In dark mode the Danger Zone tile is red-tinted, not the white or purple tile of other items. */
|
||
html.dark:where(:not([data-nav-items="flat"])) :is(.menu-item, .menu-subitem):not(.sidebar-toggle) .menu-item-icon.text-error {
|
||
color: var(--color-error) !important;
|
||
border-color: color-mix(in oklab, var(--color-error) 25%, transparent) !important;
|
||
background: color-mix(in oklab, var(--color-error) 8%, transparent) !important;
|
||
--button-depth-color: color-mix(in oklab, var(--color-error) 20%, transparent) !important;
|
||
}
|
||
|
||
html.dark:where(:not([data-nav-items="flat"])) :is(.menu-item, .menu-subitem):not(.sidebar-toggle):is(:hover, .menu-item-active, .menu-subitem-active) .menu-item-icon.text-error {
|
||
border-color: color-mix(in oklab, var(--color-error) 45%, transparent) !important;
|
||
background: color-mix(in oklab, var(--color-error) 15%, transparent) !important;
|
||
--button-depth-color: color-mix(in oklab, var(--color-error) 35%, transparent) !important;
|
||
}
|
||
|
||
html:where(:not([data-nav-items="flat"])) :is(.menu-item, .menu-subitem):not(.sidebar-toggle):focus-visible {
|
||
outline: none;
|
||
}
|
||
|
||
html:where(:not([data-nav-items="flat"])) :is(.menu-item, .menu-subitem):not(.sidebar-toggle):focus-visible .menu-item-icon {
|
||
outline: 1px solid var(--color-accent);
|
||
outline-offset: 2px;
|
||
}
|
||
|
||
/* Sidebar menu swap: a deeper menu (submenu, resource menu) enters from the right, a lower one from the left.
|
||
CSS animations restart when an element is shown again, so x-show replays them on each swap. */
|
||
@keyframes nav-swap-forward {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateX(0.5rem);
|
||
}
|
||
}
|
||
|
||
@keyframes nav-swap-back {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateX(-0.5rem);
|
||
}
|
||
}
|
||
|
||
.nav-swap-forward {
|
||
animation: nav-swap-forward 150ms ease-out;
|
||
}
|
||
|
||
.nav-swap-back {
|
||
animation: nav-swap-back 150ms ease-out;
|
||
}
|
||
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.nav-swap-forward,
|
||
.nav-swap-back {
|
||
animation: none;
|
||
}
|
||
}
|
||
|
||
/* Submenu Back chevron box: one icon wide (flat), or one icon tile wide (16px + padding + border). */
|
||
.menu-back-icon {
|
||
width: 1rem;
|
||
height: 1rem;
|
||
}
|
||
|
||
html:where(:not([data-nav-items="flat"])) .menu-back-icon {
|
||
width: calc(1rem + 0.75rem + 2px);
|
||
}
|
||
|
||
html:where(:not([data-nav-items="flat"])) :is(.menu-item, .menu-subitem):not(.sidebar-toggle) .menu-item-icon {
|
||
box-sizing: content-box;
|
||
width: 1rem;
|
||
height: 1rem;
|
||
padding: 0.375rem;
|
||
border: 1px solid oklch(0% 0 0 / 0.09);
|
||
border-radius: 8px;
|
||
background: #fafafa;
|
||
opacity: 1;
|
||
/* Same 3D depth as .button: rests on a 2px ledge, lifts on hover, presses on click. */
|
||
--button-depth-color: rgb(0 0 0 / 0.22);
|
||
box-shadow: 0 2px 0 var(--button-depth-color);
|
||
transition: border-color 120ms, background-color 120ms, box-shadow 80ms, transform 80ms;
|
||
}
|
||
|
||
html.dark:where(:not([data-nav-items="flat"])) :is(.menu-item, .menu-subitem):not(.sidebar-toggle) .menu-item-icon {
|
||
--button-depth-color: rgb(255 255 255 / 0.08);
|
||
}
|
||
|
||
html:where(:not([data-nav-items="flat"])) :is(.menu-item, .menu-subitem):not(.sidebar-toggle):hover .menu-item-icon {
|
||
transform: translateY(-1px);
|
||
box-shadow: 0 3px 0 var(--button-depth-color);
|
||
}
|
||
|
||
html:where(:not([data-nav-items="flat"])) :is(.menu-item, .menu-subitem):not(.sidebar-toggle):active .menu-item-icon {
|
||
transform: translateY(2px);
|
||
box-shadow: none;
|
||
}
|
||
|
||
html.dark:where(:not([data-nav-items="flat"])) :is(.app-sidebar, .mobile-sidebar-sheet) :is(.menu-item, .menu-subitem):not(.sidebar-toggle):not(.menu-search):not(.menu-item-active):not(.menu-subitem-active) .menu-item-icon {
|
||
border-color: rgb(255 255 255 / 0.07);
|
||
background: rgb(255 255 255 / 0.03);
|
||
color: oklch(72% 0 0);
|
||
--button-depth-color: rgb(255 255 255 / 0.05);
|
||
}
|
||
|
||
html:where(:not([data-nav-items="flat"])) :is(.menu-item, .menu-subitem):not(.sidebar-toggle):hover .menu-item-icon,
|
||
html:where(:not([data-nav-items="flat"])) :is(.menu-item-active, .menu-subitem-active) .menu-item-icon {
|
||
border-color: oklch(0% 0 0 / 0.18);
|
||
background: #fff;
|
||
}
|
||
|
||
html.dark:where(:not([data-nav-items="flat"])) :is(.menu-item, .menu-subitem):not(.sidebar-toggle):not(.menu-item-active):not(.menu-subitem-active):hover .menu-item-icon {
|
||
border-color: rgb(255 255 255 / 0.14);
|
||
background: rgb(255 255 255 / 0.06);
|
||
color: #fff;
|
||
}
|
||
|
||
/* Search keeps its original input look while the sidebar is expanded (always on
|
||
mobile, where the rail never collapses). Only the collapsed rail shows the 3D tile.
|
||
The icon resets use .menu-search.menu-item to beat the html.dark tile rules. */
|
||
html:where(:not([data-nav-items="flat"])) nav:not(.sidebar-collapsed) .menu-search {
|
||
height: 2rem;
|
||
padding-left: 0.625rem;
|
||
}
|
||
|
||
html:where(:not([data-nav-items="flat"])) nav:not(.sidebar-collapsed) .menu-search.menu-item .menu-item-icon,
|
||
html:where(:not([data-nav-items="flat"])) nav:not(.sidebar-collapsed) .menu-search.menu-item:is(:hover, :active, :focus-visible) .menu-item-icon {
|
||
box-sizing: border-box;
|
||
width: 1rem;
|
||
height: 1rem;
|
||
padding: 0;
|
||
border: 0;
|
||
background: transparent;
|
||
box-shadow: none;
|
||
transform: none;
|
||
outline: none;
|
||
opacity: 0.9;
|
||
}
|
||
|
||
html:where(:not([data-nav-items="flat"])) nav:not(.sidebar-collapsed) .menu-search:focus-visible {
|
||
outline: 1px solid var(--color-accent);
|
||
}
|
||
|
||
@media (max-width: 1023.98px) {
|
||
html:where(:not([data-nav-items="flat"])) nav.sidebar-collapsed .menu-search {
|
||
height: 2rem;
|
||
padding-left: 0.625rem;
|
||
}
|
||
|
||
html:where(:not([data-nav-items="flat"])) nav.sidebar-collapsed .menu-search.menu-item .menu-item-icon,
|
||
html:where(:not([data-nav-items="flat"])) nav.sidebar-collapsed .menu-search.menu-item:is(:hover, :active, :focus-visible) .menu-item-icon {
|
||
box-sizing: border-box;
|
||
width: 1rem;
|
||
height: 1rem;
|
||
padding: 0;
|
||
border: 0;
|
||
background: transparent;
|
||
box-shadow: none;
|
||
transform: none;
|
||
outline: none;
|
||
opacity: 0.9;
|
||
}
|
||
}
|
||
|
||
@media (min-width: 1024px) {
|
||
/* The collapsed rail is 64px wide with 40px of content: center the 30px tile. */
|
||
html:where(:not([data-nav-items="flat"])) .sidebar-collapsed .menu-item:not(.sidebar-toggle) {
|
||
padding-left: 0.3125rem;
|
||
padding-right: 0.3125rem;
|
||
}
|
||
}
|
||
|
||
/* Secondary navigation (settings rails): text-only rows with a filled pill for
|
||
the current item, like Sentry. Only the main sidebar keeps the icon tiles. */
|
||
html .application-settings-navigation :is(.menu-item, .menu-subitem) {
|
||
height: 2rem;
|
||
padding-left: 0.625rem;
|
||
}
|
||
|
||
html .application-settings-navigation .menu-item-icon {
|
||
display: none;
|
||
}
|
||
|
||
/* In the utilities layer to beat the transparent row rule above. */
|
||
@layer utilities {
|
||
html .application-settings-navigation :is(.menu-item, .menu-subitem):hover {
|
||
background-color: oklch(0% 0 0 / 0.04) !important;
|
||
}
|
||
|
||
html.dark .application-settings-navigation :is(.menu-item, .menu-subitem):hover {
|
||
background-color: oklch(100% 0 0 / 0.05) !important;
|
||
}
|
||
|
||
html .application-settings-navigation :is(.menu-item-active, .menu-subitem-active),
|
||
html .application-settings-navigation :is(.menu-item-active, .menu-subitem-active):hover {
|
||
background-color: oklch(0% 0 0 / 0.06) !important;
|
||
}
|
||
|
||
html.dark .application-settings-navigation :is(.menu-item-active, .menu-subitem-active),
|
||
html.dark .application-settings-navigation :is(.menu-item-active, .menu-subitem-active):hover {
|
||
background-color: oklch(100% 0 0 / 0.08) !important;
|
||
}
|
||
}
|
||
|
||
/* setCanvasDots() writes these on the canvas root on every pan and zoom step, and
|
||
only the root reads them. Inherited, each write restyles every card in the canvas. */
|
||
@property --canvas-dots-size {
|
||
syntax: '*';
|
||
inherits: false;
|
||
}
|
||
|
||
@property --canvas-dots-position {
|
||
syntax: '*';
|
||
inherits: false;
|
||
}
|
||
|
||
/* Canvases at full zoom: the grid dots turn into small Coolify logos, one
|
||
per 20px tile. setCanvasDots() (environment-canvas.js) sets .canvas-zoom-max. */
|
||
.canvas-zoom-max {
|
||
--canvas-dots-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath transform='translate(8 8) scale(0.0125)' d='M64 256H0V64h64Zm0-192V0h256v64Zm0 192h256v64H64Z' fill='rgb(0 0 0 / 0.13)'/%3E%3C/svg%3E");
|
||
}
|
||
|
||
html.dark .canvas-zoom-max {
|
||
--canvas-dots-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath transform='translate(8 8) scale(0.0125)' d='M64 256H0V64h64Zm0-192V0h256v64Zm0 192h256v64H64Z' fill='rgb(255 255 255 / 0.09)'/%3E%3C/svg%3E");
|
||
}
|
||
|
||
.canvas-zoom-max [data-ec-viewport] {
|
||
background-image: var(--canvas-dots-image);
|
||
}
|
||
|
||
/* A pan (canvas-core panZoom), also a middle-button pan that started on a card. */
|
||
:is([data-ec-viewport], .cv-viewport).panning,
|
||
:is([data-ec-viewport], .cv-viewport).panning * {
|
||
cursor: grabbing !important;
|
||
}
|
||
|
||
/* "Floating" navigation (Appearance → Navigation style): the top bar, sidebar,
|
||
tab strip, and settings rail float as framed panels over a dotted backdrop,
|
||
like the canvas islands. Set on <html> by applyStoredTheme() from
|
||
localStorage.navStyle; the default style keeps edge-to-edge bars. */
|
||
/* Width of the collapsed desktop sidebar and the brand cell above it. */
|
||
html {
|
||
--sidebar-collapsed-w: 4rem;
|
||
}
|
||
|
||
@media (min-width: 1024px) {
|
||
html[data-nav-style="floating"] {
|
||
/* The framed rail needs no edge spacing: 1px border + 5px padding + 40px content. */
|
||
--sidebar-collapsed-w: 3.25rem;
|
||
--floating-gap: 0.5rem;
|
||
--floating-radius: 8px;
|
||
--floating-line: oklch(0% 0 0 / 0.09);
|
||
/* Bottom edge of the top bar panel plus one gap. */
|
||
--floating-top: calc(3rem + var(--floating-gap) * 2);
|
||
}
|
||
|
||
html.dark[data-nav-style="floating"] {
|
||
--floating-line: oklch(100% 0 0 / 0.08);
|
||
--floating-dot: rgb(255 255 255 / 0.09);
|
||
}
|
||
|
||
/* Dotted backdrop behind the floating panels and in the gaps between them,
|
||
like the canvas stage. Fixed so the dots stay still while content scrolls. */
|
||
html[data-nav-style="floating"] .app-shell {
|
||
background-image: radial-gradient(circle, var(--floating-dot, rgb(0 0 0 / 0.13)) 1px, transparent 1.3px);
|
||
background-size: 20px 20px;
|
||
background-attachment: fixed;
|
||
}
|
||
|
||
html[data-nav-style="floating"] .app-main {
|
||
background-color: transparent;
|
||
}
|
||
|
||
html[data-nav-style="floating"] .app-topbar {
|
||
background-color: #fff;
|
||
}
|
||
|
||
html.dark[data-nav-style="floating"] .app-topbar {
|
||
background-color: var(--color-panel);
|
||
}
|
||
|
||
html[data-nav-style="floating"] :is(.app-topbar, .app-sidebar > div > nav) {
|
||
border: 1px solid var(--floating-line);
|
||
border-radius: var(--floating-radius);
|
||
}
|
||
|
||
/* The top bar hugs its content (brand, breadcrumb, docked actions)
|
||
instead of spanning an empty full-width panel. */
|
||
html[data-nav-style="floating"] .app-topbar {
|
||
top: var(--floating-gap);
|
||
left: var(--floating-gap);
|
||
right: auto;
|
||
max-width: calc(100vw - var(--floating-gap) * 2);
|
||
}
|
||
|
||
/* The bar is opaque here, so it needs no blur. Without the backdrop filter
|
||
the bar is no longer the containing block for fixed children, which lets
|
||
the resource actions panel below sit against the screen edge. */
|
||
html[data-nav-style="floating"] .app-topbar {
|
||
backdrop-filter: none;
|
||
}
|
||
|
||
/* The panel border replaces the inner dividers of the top bar. */
|
||
html[data-nav-style="floating"] .app-topbar > div {
|
||
border-bottom-width: 0;
|
||
}
|
||
|
||
html[data-nav-style="floating"] .app-topbar > .app-topbar-brand {
|
||
border-radius: calc(var(--floating-radius) - 1px) 0 0 calc(var(--floating-radius) - 1px);
|
||
}
|
||
|
||
html[data-nav-style="floating"] .app-sidebar {
|
||
top: var(--floating-top);
|
||
bottom: var(--floating-gap);
|
||
left: var(--floating-gap);
|
||
}
|
||
|
||
html[data-nav-style="floating"] .app-sidebar > div > nav > .sidebar-footer {
|
||
border-radius: 0 0 var(--floating-radius) var(--floating-radius);
|
||
}
|
||
|
||
html[data-nav-style="floating"] .app-sidebar nav.sidebar-collapsed {
|
||
padding-left: 0.3125rem;
|
||
padding-right: 0.3125rem;
|
||
}
|
||
|
||
html[data-nav-style="floating"] .app-sidebar nav.sidebar-collapsed > .sidebar-footer {
|
||
margin-left: -0.3125rem;
|
||
margin-right: -0.3125rem;
|
||
padding-left: 0.3125rem;
|
||
padding-right: 0.3125rem;
|
||
}
|
||
|
||
html[data-nav-style="floating"] .app-main {
|
||
margin-left: calc(var(--sidebar-w, 14rem) + var(--floating-gap));
|
||
/* The content top lines up with the sidebar and settings rail panels.
|
||
The cards draw their frame as a 1px ring outside the box, while the
|
||
panels use a 1px border inside it, so add 1px to match the edges. */
|
||
padding-top: calc(var(--floating-top) + 1px);
|
||
}
|
||
|
||
/* Full-bleed pages (canvases): no top padding gap, and the same gap as the panels. */
|
||
html[data-nav-style="floating"] .app-main.app-main-full {
|
||
margin-left: calc(var(--sidebar-w, 14rem) + var(--floating-gap) * 2);
|
||
padding: var(--floating-top) var(--floating-gap) var(--floating-gap) 0;
|
||
}
|
||
|
||
/* Canvases fill the space under the top bar too, and the canvas root
|
||
paints the canvas dots (set by the canvas JS) behind the top bar, so
|
||
the dots are the same there and on the canvas. */
|
||
html[data-nav-style="floating"] .app-main.app-main-full:has(:is([data-ec-root], .cv-root)) {
|
||
padding-top: 0;
|
||
}
|
||
|
||
/* The resource canvases fill the whole screen behind the floating panels
|
||
(top bar, toolbar, sidebar), so the panels do not cut off the cards. */
|
||
html[data-nav-style="floating"] .app-main.app-main-full:has([data-ec-root]) {
|
||
margin-left: 0;
|
||
padding: 0;
|
||
}
|
||
|
||
html[data-nav-style="floating"] .app-main-full [data-ec-root] {
|
||
padding: 0 !important;
|
||
}
|
||
|
||
html[data-nav-style="floating"] .app-main-full :is([data-ec-root], .cv-root) {
|
||
padding-top: calc(var(--floating-top) + 0.5rem);
|
||
background-image: var(--canvas-dots-image, radial-gradient(circle, var(--floating-dot, rgb(0 0 0 / 0.13)) 1px, transparent 1px));
|
||
background-size: var(--canvas-dots-size, 20px 20px);
|
||
background-position: var(--canvas-dots-position, 0 0);
|
||
}
|
||
|
||
/* The deploy canvas Steps panel starts at the same height as the sidebar. */
|
||
html[data-nav-style="floating"] .app-main-full .cv-root {
|
||
padding-top: var(--floating-top);
|
||
}
|
||
|
||
html[data-nav-style="floating"] .app-main-full [data-ec-viewport] {
|
||
background-image: none;
|
||
}
|
||
|
||
/* Full page width: the content is one gap from the sidebar and the screen edge. */
|
||
html[data-nav-style="floating"] .app-main.app-main-wide {
|
||
padding-right: var(--floating-gap);
|
||
padding-left: var(--floating-gap);
|
||
padding-bottom: var(--floating-gap);
|
||
}
|
||
|
||
/* Dashboard canvas Deployments panel: one gap from the floating sidebar and the screen bottom. */
|
||
/* Bottom-left zoom controls and deployments: the canvas fills the screen behind the sidebar, so start one gap after it. */
|
||
html[data-nav-style="floating"] .app-main-full [data-ec-dock] {
|
||
left: calc(var(--sidebar-w, 14rem) + var(--floating-gap) * 2);
|
||
bottom: var(--floating-gap);
|
||
}
|
||
}
|
||
|
||
@media (min-width: 1280px) {
|
||
/* Resource actions (Links, Deploy, …) float as their own panel at the top
|
||
right. The top bar panel stops one gap before it (the header publishes
|
||
--resource-actions-w). */
|
||
html[data-nav-style="floating"] #resource-action-hud-slot:not(:empty) {
|
||
position: fixed;
|
||
top: var(--floating-gap);
|
||
right: var(--floating-gap);
|
||
height: 3rem;
|
||
padding: 0 0.5rem;
|
||
border: 1px solid var(--floating-line);
|
||
border-radius: var(--floating-radius);
|
||
background-color: #fff;
|
||
}
|
||
|
||
html.dark[data-nav-style="floating"] #resource-action-hud-slot:not(:empty) {
|
||
background-color: var(--color-panel);
|
||
}
|
||
|
||
html[data-nav-style="floating"] .app-topbar:has(#resource-action-hud-slot:not(:empty)) {
|
||
max-width: calc(100vw - var(--floating-gap) * 3 - var(--resource-actions-w, 0px));
|
||
}
|
||
|
||
html[data-nav-style="floating"] .application-settings-navigation {
|
||
top: var(--floating-top);
|
||
left: calc(var(--sidebar-w, 14rem) + var(--floating-gap) * 2);
|
||
/* Hug the menu items; long menus scroll inside the viewport. */
|
||
height: auto;
|
||
max-height: calc(100dvh - var(--floating-top) - var(--floating-gap));
|
||
border: 1px solid var(--floating-line);
|
||
border-radius: var(--floating-radius);
|
||
}
|
||
|
||
/* Full page width: the content starts one gap after the settings rail. */
|
||
html[data-nav-style="floating"] .app-main-wide :has(> .application-settings-navigation) {
|
||
grid-template-columns: calc(210px + 2.5rem) minmax(0, 1fr);
|
||
column-gap: var(--floating-gap);
|
||
}
|
||
|
||
/* The canvas fills the screen behind the rail too; the canvas JS keeps cards clear of it. */
|
||
html[data-nav-style="floating"] .app-main-full .application-settings-navigation ~ [data-environment-canvas] {
|
||
margin-left: 0;
|
||
}
|
||
}
|
||
|
||
/* Brand shell (dark): black canvas and top bar, sidebar, scroll hairline.
|
||
The default dark theme tints the sidebar purple (below); a custom theme gets
|
||
these neutral rules. Light mode keeps the neutral shell. Floating navigation
|
||
keeps its own frame. */
|
||
html.dark .app-sidebar nav.sidebar-nav,
|
||
html.dark .mobile-sidebar-sheet [role="dialog"] {
|
||
background-color: var(--color-sidebar);
|
||
border-right-color: rgb(255 255 255 / 0.06);
|
||
}
|
||
|
||
/* Same color as the sidebar, so the brand and sidebar read as one surface. */
|
||
html.dark .app-topbar-brand {
|
||
background-color: var(--color-sidebar);
|
||
border-right-color: rgb(255 255 255 / 0.06);
|
||
}
|
||
|
||
/* The drawer carries the brand surface; its nav must not paint over it. */
|
||
html.dark .mobile-sidebar-sheet nav.sidebar-nav {
|
||
background-color: transparent;
|
||
}
|
||
|
||
html.dark :is(.app-sidebar, .mobile-sidebar-sheet) .sidebar-footer {
|
||
background-color: transparent;
|
||
}
|
||
|
||
html.dark :is(.app-sidebar, .mobile-sidebar-sheet) .menu-search {
|
||
box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.06) !important;
|
||
}
|
||
|
||
/* The account button is a .button, which is purple in dark mode; keep it neutral in a custom theme sidebar. */
|
||
html.dark[data-theme="custom"] :is(.app-sidebar, .mobile-sidebar-sheet) .sidebar-footer .button:not(.button-highlighted):not(.button-error):not([isHighlighted]):not(:disabled) {
|
||
--button-depth-color: rgb(255 255 255 / 0.05);
|
||
background-color: rgb(255 255 255 / 0.04);
|
||
border-color: rgb(255 255 255 / 0.08);
|
||
color: var(--color-nav-text);
|
||
}
|
||
|
||
html.dark[data-theme="custom"] :is(.app-sidebar, .mobile-sidebar-sheet) .sidebar-footer .button:not(.button-highlighted):not(.button-error):not([isHighlighted]):not(:disabled):hover {
|
||
background-color: rgb(255 255 255 / 0.07);
|
||
border-color: rgb(255 255 255 / 0.12);
|
||
}
|
||
|
||
html.dark[data-theme="custom"] :is(.app-sidebar, .mobile-sidebar-sheet) .sidebar-footer .button:not(.button-highlighted):not(.button-error):not([isHighlighted]):not(:disabled) svg {
|
||
color: var(--color-nav-muted);
|
||
}
|
||
|
||
/* The collapsed rail shows Search as a tile like the other items: no row frame (the fill is
|
||
removed in the utilities layer above), the dark tile. */
|
||
html.dark .app-sidebar nav.sidebar-collapsed .menu-search {
|
||
box-shadow: none !important;
|
||
}
|
||
|
||
html.dark:where(:not([data-nav-items="flat"])) .app-sidebar nav.sidebar-collapsed .menu-search.menu-item .menu-item-icon {
|
||
border-color: rgb(255 255 255 / 0.07);
|
||
background: rgb(255 255 255 / 0.03);
|
||
color: oklch(72% 0 0);
|
||
--button-depth-color: rgb(255 255 255 / 0.05);
|
||
}
|
||
|
||
html.dark :is(.app-sidebar, .mobile-sidebar-sheet) nav:not(.sidebar-collapsed) .nav-section {
|
||
font-family: var(--font-mono);
|
||
font-size: 10.5px;
|
||
font-weight: 500;
|
||
letter-spacing: 0.08em;
|
||
text-transform: uppercase;
|
||
color: var(--color-nav-muted);
|
||
opacity: 1;
|
||
}
|
||
|
||
html.dark .app-sidebar .nav-chevron {
|
||
transition: opacity 120ms ease;
|
||
}
|
||
|
||
html.dark .app-sidebar .menu-item:not(:hover):not(:focus-visible):not(.menu-item-active) > .nav-chevron {
|
||
opacity: 0;
|
||
}
|
||
|
||
/* Default dark theme sidebar: purple-black with a faint brand wash and lavender
|
||
tiles, text, and edges. A custom theme keeps the neutral rules above. */
|
||
html.dark:not([data-theme="custom"]) .app-sidebar nav.sidebar-nav,
|
||
html.dark:not([data-theme="custom"]) .mobile-sidebar-sheet [role="dialog"] {
|
||
background-image: linear-gradient(180deg, rgb(107 22 237 / 0.09), transparent 520px);
|
||
border-right-color: rgb(163 145 255 / 0.10);
|
||
--color-nav-text: oklch(80% 0.02 300);
|
||
--color-nav-muted: oklch(58% 0.04 300);
|
||
}
|
||
|
||
/* Same color as the top of the sidebar gradient, so the brand and sidebar read as one surface. */
|
||
html.dark:not([data-theme="custom"]) .app-topbar-brand {
|
||
background-image: linear-gradient(rgb(107 22 237 / 0.09), rgb(107 22 237 / 0.09));
|
||
border-right-color: rgb(163 145 255 / 0.10);
|
||
}
|
||
|
||
html.dark:not([data-theme="custom"]) :is(.app-sidebar, .mobile-sidebar-sheet) .menu-search {
|
||
background-color: rgb(163 145 255 / 0.06) !important;
|
||
box-shadow: inset 0 0 0 1px rgb(163 145 255 / 0.10) !important;
|
||
}
|
||
|
||
html.dark:not([data-theme="custom"]) :is(.app-sidebar, .mobile-sidebar-sheet) .menu-search:hover {
|
||
background-color: rgb(163 145 255 / 0.10) !important;
|
||
}
|
||
|
||
html.dark:not([data-theme="custom"]) :is(.app-sidebar, .mobile-sidebar-sheet) .menu-search kbd {
|
||
border-color: rgb(163 145 255 / 0.18) !important;
|
||
}
|
||
|
||
/* Group dividers use the lavender sidebar edge: the neutral hairline looks olive on the purple-black sidebar. */
|
||
html.dark:not([data-theme="custom"]) :is(.app-sidebar, .mobile-sidebar-sheet) .sidebar-nav :is([class~="dark:border-white/[0.06]"], [class~="dark:border-white/[0.07]"], [class~="dark:border-white/[0.08]"]) {
|
||
border-color: rgb(163 145 255 / 0.10) !important;
|
||
}
|
||
|
||
/* Admin keeps the neutral tile unless it is the current page. */
|
||
html.dark:not([data-theme="custom"]):where(:not([data-nav-items="flat"])) :is(.app-sidebar, .mobile-sidebar-sheet) :is(.menu-item, .menu-subitem):not(.sidebar-toggle):not(.menu-search):not(.nav-admin):not(.menu-item-active):not(.menu-subitem-active) .menu-item-icon,
|
||
html.dark:not([data-theme="custom"]):where(:not([data-nav-items="flat"])) .app-sidebar nav.sidebar-collapsed .menu-search.menu-item .menu-item-icon {
|
||
border-color: rgb(163 145 255 / 0.13);
|
||
background: rgb(107 22 237 / 0.07);
|
||
color: #b9adff;
|
||
}
|
||
|
||
html.dark:not([data-theme="custom"]) :is(.app-sidebar, .mobile-sidebar-sheet) nav:not(.sidebar-collapsed) .nav-section {
|
||
color: rgb(163 145 255 / 0.55);
|
||
}
|
||
|
||
/* Sidebar account menu: the purple tint of the sidebar it opens from. */
|
||
html.dark:not([data-theme="custom"]) .listbox-panel.top-user-menu-panel-sidebar {
|
||
background: color-mix(in oklab, #6b16ed 10%, var(--coollabs-elevated));
|
||
border-color: rgb(163 145 255 / 0.22);
|
||
}
|
||
|
||
html.dark:not([data-theme="custom"]) .top-user-menu-panel-sidebar .listbox-option:hover {
|
||
background: rgb(163 145 255 / 0.10);
|
||
}
|
||
|
||
/* Custom theme sidebar. These rules come after the dark sidebar rules above, so they win. */
|
||
/* !important: the light custom theme maps bg-neutral-50 to the canvas with !important. */
|
||
html[data-theme="custom"] :is(.app-sidebar nav.sidebar-nav, .app-topbar-brand, .mobile-sidebar-sheet [role="dialog"]) {
|
||
background-color: var(--theme-sidebar) !important;
|
||
}
|
||
|
||
html[data-theme="custom"] .mobile-sidebar-sheet nav.sidebar-nav {
|
||
background-color: transparent !important;
|
||
}
|
||
|
||
/* The current page in the sidebar uses the Selected color. */
|
||
html[data-theme="custom"] :is(.app-sidebar, .mobile-sidebar-sheet) :is(.menu-item-active, .menu-subitem-active) {
|
||
background-color: var(--theme-selected);
|
||
color: var(--theme-selected-text);
|
||
}
|
||
|
||
/* The 3D style shows the current page as an accent tile, like the Dark theme, on a light or dark sidebar. */
|
||
html[data-theme="custom"]:where(:not([data-nav-items="flat"])) :is(.app-sidebar, .mobile-sidebar-sheet) :is(.menu-item-active, .menu-subitem-active) .menu-item-icon:not(.text-error) {
|
||
color: var(--color-brand-foreground);
|
||
border-color: var(--color-brand-ring) !important;
|
||
background: linear-gradient(180deg, var(--color-brand-hover) 0%, var(--color-brand-press) 100%) !important;
|
||
--button-depth-color: var(--color-brand-depth) !important;
|
||
box-shadow: 0 2px 0 var(--color-brand-depth) !important;
|
||
}
|
||
|
||
/* 3D icon tiles and the search field take their colors from the sidebar text,
|
||
so they fit a light or a dark sidebar. */
|
||
html[data-theme="custom"]:where(:not([data-nav-items="flat"])) :is(.app-sidebar, .mobile-sidebar-sheet) :is(.menu-item, .menu-subitem):not(.sidebar-toggle):not(.menu-search):not(.menu-item-active):not(.menu-subitem-active) .menu-item-icon {
|
||
border-color: color-mix(in oklab, var(--t-text) 15%, transparent);
|
||
background: color-mix(in oklab, var(--t-text) 5%, transparent);
|
||
color: var(--color-nav-text);
|
||
--button-depth-color: color-mix(in oklab, var(--t-text) 12%, transparent);
|
||
}
|
||
|
||
html[data-theme="custom"]:where(:not([data-nav-items="flat"])) :is(.app-sidebar, .mobile-sidebar-sheet) :is(.menu-item, .menu-subitem):not(.sidebar-toggle):not(.menu-search):not(.menu-item-active):not(.menu-subitem-active):hover .menu-item-icon {
|
||
border-color: color-mix(in oklab, var(--t-text) 20%, transparent);
|
||
background: color-mix(in oklab, var(--t-text) 9%, transparent);
|
||
color: var(--color-nav-active);
|
||
}
|
||
|
||
@layer utilities {
|
||
html[data-theme="custom"]:where(:not([data-nav-items="flat"])) :is(.app-sidebar, .mobile-sidebar-sheet) nav:not(.sidebar-collapsed) .menu-search.menu-item {
|
||
background-color: color-mix(in oklab, var(--t-text) 6%, transparent) !important;
|
||
}
|
||
|
||
html[data-theme="custom"]:where(:not([data-nav-items="flat"])) :is(.app-sidebar, .mobile-sidebar-sheet) nav:not(.sidebar-collapsed) .menu-search.menu-item:hover {
|
||
background-color: color-mix(in oklab, var(--t-text) 10%, transparent) !important;
|
||
}
|
||
}
|
||
|
||
html[data-theme="custom"] :is(.app-sidebar, .mobile-sidebar-sheet) .sidebar-footer .button:not(.button-highlighted):not(.button-error):not([isHighlighted]):not(:disabled) {
|
||
--button-depth-color: color-mix(in oklab, var(--t-text) 8%, transparent);
|
||
background-color: color-mix(in oklab, var(--t-text) 5%, transparent);
|
||
border-color: color-mix(in oklab, var(--t-text) 12%, transparent);
|
||
color: var(--color-nav-text);
|
||
}
|
||
|
||
html[data-theme="custom"] :is(.app-sidebar, .mobile-sidebar-sheet) .sidebar-footer .button:not(.button-highlighted):not(.button-error):not([isHighlighted]):not(:disabled):hover {
|
||
background-color: color-mix(in oklab, var(--t-text) 9%, transparent);
|
||
border-color: color-mix(in oklab, var(--t-text) 18%, transparent);
|
||
}
|
||
|
||
html.dark:not([data-nav-style="floating"]) .app-topbar {
|
||
background-color: var(--color-app);
|
||
backdrop-filter: none;
|
||
transition: background-color 180ms ease;
|
||
}
|
||
|
||
html.dark:not([data-nav-style="floating"]) .app-topbar > div:not(.app-topbar-brand) {
|
||
border-bottom-color: transparent;
|
||
transition: border-color 180ms ease, box-shadow 180ms ease, background-color 180ms ease;
|
||
}
|
||
|
||
html.dark.is-scrolled:not([data-nav-style="floating"]) .app-topbar {
|
||
background-color: color-mix(in srgb, var(--color-app) 72%, transparent);
|
||
backdrop-filter: blur(12px) saturate(140%);
|
||
}
|
||
|
||
html.dark.is-scrolled:not([data-nav-style="floating"]) .app-topbar > .app-topbar-brand {
|
||
background-color: var(--color-sidebar);
|
||
}
|
||
|
||
html.dark.is-scrolled:not([data-nav-style="floating"]) .app-topbar > div:not(.app-topbar-brand) {
|
||
border-bottom-color: rgb(255 255 255 / 0.06);
|
||
box-shadow: 0 10px 30px -14px rgb(0 0 0 / 0.95);
|
||
}
|
||
|
||
/* Canvases (default navigation): the canvas runs under a clear top bar, and the
|
||
canvas root paints the dots (set by the canvas JS) behind it, like the
|
||
floating navigation does. */
|
||
@media (min-width: 1024px) {
|
||
html:not([data-nav-style="floating"]) .app-main.app-main-full:has(:is([data-ec-root], .cv-root)) {
|
||
padding-top: 0;
|
||
}
|
||
|
||
/* The deploy canvas Steps panel starts below the top bar, and its canvas
|
||
runs up under the top bar. */
|
||
html:not([data-nav-style="floating"]) .app-main-full .cv-root {
|
||
padding-top: calc(3rem + 0.5rem);
|
||
}
|
||
|
||
html:not([data-nav-style="floating"]) .app-main-full .cv-root.cv-docked .cv-stage {
|
||
margin-top: calc(-3rem - 0.5rem);
|
||
}
|
||
|
||
/* The resource canvases fill the space under the top bar, so the top bar
|
||
does not cut off the cards (fit keeps the cards below it). */
|
||
html:not([data-nav-style="floating"]) .app-main-full [data-ec-root] {
|
||
padding: 0 !important;
|
||
}
|
||
|
||
/* The deploy canvas (.cv-root) paints its own dots. */
|
||
html:not([data-nav-style="floating"]) .app-main-full [data-ec-root] {
|
||
background-image: var(--canvas-dots-image, radial-gradient(circle, rgb(0 0 0 / 0.13) 1px, transparent 1px));
|
||
background-size: var(--canvas-dots-size, 20px 20px);
|
||
background-position: var(--canvas-dots-position, 0 0);
|
||
}
|
||
|
||
html.dark:not([data-nav-style="floating"]) .app-main-full [data-ec-root] {
|
||
background-image: var(--canvas-dots-image, radial-gradient(circle, rgb(255 255 255 / 0.09) 1px, transparent 1px));
|
||
}
|
||
|
||
html:not([data-nav-style="floating"]) .app-main-full [data-ec-viewport] {
|
||
background-image: none;
|
||
}
|
||
|
||
/* A layout class, not html:has(): an html-level :has() restyles the whole
|
||
page on every DOM change, which made chart renders take seconds. */
|
||
html:not([data-nav-style="floating"]) .app-shell-canvas > .app-topbar {
|
||
background-color: transparent;
|
||
backdrop-filter: none;
|
||
}
|
||
|
||
html:not([data-nav-style="floating"]) .app-shell-canvas > .app-topbar > div:not(.app-topbar-brand) {
|
||
border-bottom-color: transparent;
|
||
}
|
||
}
|
||
|
||
html.dark .view-toggle {
|
||
gap: 2px;
|
||
padding: 2px;
|
||
background-color: color-mix(in srgb, var(--color-brand) 6%, transparent);
|
||
border-color: color-mix(in srgb, var(--color-brand-light) 16%, transparent);
|
||
}
|
||
|
||
html.dark .view-toggle :is(a, button) {
|
||
color: oklch(from var(--color-brand-light) 80% 0.02 h);
|
||
}
|
||
|
||
html.dark .view-toggle :is(a, button) svg {
|
||
color: var(--color-brand-light);
|
||
}
|
||
|
||
html.dark .view-toggle :is(a, button):not(.is-selected):hover {
|
||
background-color: color-mix(in srgb, var(--color-brand) 14%, transparent);
|
||
color: #fff;
|
||
}
|
||
|
||
html.dark .view-toggle > .is-selected {
|
||
background: linear-gradient(180deg, var(--color-brand-hover), var(--color-brand-press));
|
||
color: var(--color-brand-foreground);
|
||
box-shadow: 0 1px 0 var(--color-brand-depth), inset 0 0 0 1px var(--color-brand-ring);
|
||
}
|
||
|
||
html.dark .view-toggle > .is-selected svg {
|
||
color: var(--color-brand-foreground);
|
||
}
|
||
|
||
html.dark .view-toggle .bg-coollabs {
|
||
background-color: color-mix(in srgb, var(--color-brand-light) 18%, transparent);
|
||
color: oklch(from var(--color-brand-light) 87.5% 0.06 h);
|
||
}
|
||
|
||
/* Toasts: a faint edge that blends into the toast background. */
|
||
html.dark .toast-card {
|
||
box-shadow: 0 0 0 1px rgb(255 255 255 / 0.05), var(--shadow-dropdown);
|
||
}
|
||
|
||
/* Toast icon tile: the 3D depth of the sidebar tiles, tinted with the icon color of the toast type. */
|
||
.toast-icon {
|
||
--button-depth-color: color-mix(in oklab, currentColor 30%, transparent);
|
||
border: 1px solid color-mix(in oklab, currentColor 22%, transparent);
|
||
box-shadow: 0 2px 0 var(--button-depth-color);
|
||
}
|
||
|
||
/* Command palette: the purple brand tint of the dark shell. */
|
||
html.dark .command-palette {
|
||
background: color-mix(in oklab, var(--color-brand) 10%, var(--coollabs-elevated));
|
||
box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-brand-light) 22%, transparent), var(--shadow-dropdown);
|
||
}
|
||
|
||
html.dark .command-palette-header {
|
||
background: transparent;
|
||
}
|
||
|
||
html.dark .command-palette-header-icon:not(.is-loading) {
|
||
color: var(--color-brand-light);
|
||
}
|
||
|
||
html.dark .command-palette-input::placeholder {
|
||
color: oklch(from var(--color-brand-light) 72% 0.05 h);
|
||
}
|
||
|
||
html.dark .command-palette-body {
|
||
background: color-mix(in oklab, var(--color-brand) 6%, var(--coollabs-base));
|
||
box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-brand-light) 12%, transparent);
|
||
}
|
||
|
||
html.dark .command-palette-item:is(:focus, :focus-visible) :is(.command-palette-quickcommand, .command-palette-type-badge, .command-palette-item-chevron, .command-palette-item-icon) {
|
||
color: #fff;
|
||
}
|
||
|
||
html.dark .resource-glyph {
|
||
border-color: color-mix(in srgb, var(--color-brand-light) 20%, transparent);
|
||
background: color-mix(in srgb, var(--color-brand) 10%, transparent);
|
||
color: var(--color-brand-light);
|
||
}
|
||
|
||
html.dark .card-icon-button {
|
||
color: var(--color-brand-light);
|
||
}
|
||
|
||
html.dark .card-icon-button:hover {
|
||
background-color: color-mix(in srgb, var(--color-brand) 16%, transparent);
|
||
color: #fff;
|
||
}
|
||
|
||
html.dark .app-main :is(article.group, a.group).rounded-xl {
|
||
background-color: var(--color-card) !important;
|
||
border-color: rgb(255 255 255 / 0.08) !important;
|
||
box-shadow: none;
|
||
}
|
||
|
||
html.dark .app-main :is(article.group, a.group).rounded-xl:hover {
|
||
border-color: color-mix(in srgb, var(--color-brand-light) 30%, transparent) !important;
|
||
box-shadow: none;
|
||
}
|
||
|
||
.project-card-stats {
|
||
color: oklch(40% 0 0);
|
||
font-size: 11.5px;
|
||
font-weight: 500;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
.project-card-stats b {
|
||
color: oklch(20% 0 0);
|
||
font-weight: 600;
|
||
}
|
||
|
||
.project-card-stats-dot {
|
||
margin-inline: 5px;
|
||
color: oklch(70% 0 0);
|
||
}
|
||
|
||
html.dark .project-card-stats {
|
||
color: oklch(72% 0 0);
|
||
}
|
||
|
||
html.dark .project-card-stats b {
|
||
color: #f2f2f2;
|
||
}
|
||
|
||
html.dark .project-card-stats-dot {
|
||
color: oklch(45% 0 0);
|
||
}
|
||
|
||
.dark .application-settings-form :is(.input, select, textarea):focus,
|
||
.dark .application-settings-form :is(.input, select, textarea):focus-visible,
|
||
.dark .application-settings-workspace :is(.input, select, textarea):focus,
|
||
.dark .application-settings-workspace :is(.input, select, textarea):focus-visible {
|
||
border-color: var(--color-brand);
|
||
box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-brand) 25%, transparent) !important;
|
||
}
|
||
|
||
/* One page of results: the bar is noise. !important beats the flex utility and Alpine x-show. */
|
||
.pager[hidden] {
|
||
display: none !important;
|
||
}
|
||
|
||
/* Dark mode (both navigation styles): the floating dotted backdrop shows through
|
||
see-through surfaces (dark:bg-white/[x] with or without `!`, .button, …). Give the
|
||
outermost ones the same color as an opaque mix with the app background, like the
|
||
canvas cards. The normal navigation uses the same colors so both styles match. Nested
|
||
surfaces keep their tint on top, also inside opaque CSS-class panels
|
||
(settings sections, boxes, tables, …) that are not app-colored. Generated from the dark:bg-white|black/x
|
||
utilities used in resources/views and resources/js; in the utilities layer
|
||
with !important so it beats the !bg-* utilities. */
|
||
@layer utilities {
|
||
html.dark .app-main [class*="dark:bg-black/10"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *) { background-color: color-mix(in oklab, #000 10%, var(--color-app)) !important; }
|
||
html.dark .app-main [class*="dark:bg-black/15"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *) { background-color: color-mix(in oklab, #000 15%, var(--color-app)) !important; }
|
||
html.dark .app-main [class*="dark:bg-black/20"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *) { background-color: color-mix(in oklab, #000 20%, var(--color-app)) !important; }
|
||
html.dark .app-main [class*="dark:bg-black/30"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *) { background-color: color-mix(in oklab, #000 30%, var(--color-app)) !important; }
|
||
html.dark .app-main [class*="dark:bg-black/40"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *) { background-color: color-mix(in oklab, #000 40%, var(--color-app)) !important; }
|
||
html.dark .app-main [class*="dark:bg-black/55"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *) { background-color: color-mix(in oklab, #000 55%, var(--color-app)) !important; }
|
||
html.dark .app-main [class*="dark:bg-black/60"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *) { background-color: color-mix(in oklab, #000 60%, var(--color-app)) !important; }
|
||
html.dark .app-main [class*="dark:bg-white/10"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *) { background-color: color-mix(in oklab, #fff 10%, var(--color-app)) !important; }
|
||
html.dark .app-main [class*="dark:bg-white/40"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *) { background-color: color-mix(in oklab, #fff 40%, var(--color-app)) !important; }
|
||
html.dark .app-main [class*="dark:bg-white/[0.025]"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *) { background-color: color-mix(in oklab, #fff 2.5%, var(--color-app)) !important; }
|
||
html.dark .app-main [class*="dark:bg-white/[0.02]"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *) { background-color: color-mix(in oklab, #fff 2%, var(--color-app)) !important; }
|
||
html.dark .app-main [class*="dark:bg-white/[0.035]"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *) { background-color: color-mix(in oklab, #fff 3.5%, var(--color-app)) !important; }
|
||
html.dark .app-main [class*="dark:bg-white/[0.03]"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *) { background-color: color-mix(in oklab, #fff 3%, var(--color-app)) !important; }
|
||
html.dark .app-main [class*="dark:bg-white/[0.045]"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *) { background-color: color-mix(in oklab, #fff 4.5%, var(--color-app)) !important; }
|
||
html.dark .app-main [class*="dark:bg-white/[0.04]"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *) { background-color: color-mix(in oklab, #fff 4%, var(--color-app)) !important; }
|
||
html.dark .app-main [class*="dark:bg-white/[0.05]"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *) { background-color: color-mix(in oklab, #fff 5%, var(--color-app)) !important; }
|
||
html.dark .app-main [class*="dark:bg-white/[0.06]"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *) { background-color: color-mix(in oklab, #fff 6%, var(--color-app)) !important; }
|
||
html.dark .app-main [class*="dark:bg-white/[0.07]"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *) { background-color: color-mix(in oklab, #fff 7%, var(--color-app)) !important; }
|
||
html.dark .app-main [class*="dark:bg-white/[0.08]"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *) { background-color: color-mix(in oklab, #fff 8%, var(--color-app)) !important; }
|
||
html.dark .app-main [class*="dark:bg-white/[0.09]"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *) { background-color: color-mix(in oklab, #fff 9%, var(--color-app)) !important; }
|
||
html.dark .app-main [class*="dark:bg-white/[0.14]"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *) { background-color: color-mix(in oklab, #fff 14%, var(--color-app)) !important; }
|
||
html.dark .app-main [class*="dark:bg-white/[0.1]"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *) { background-color: color-mix(in oklab, #fff 10%, var(--color-app)) !important; }
|
||
html.dark .app-main .button:not(.button-highlighted):not(.button-error):not([isHighlighted]):not(:disabled):not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *) { background-color: color-mix(in srgb, var(--color-brand) 14%, var(--color-app)) !important; border-color: color-mix(in srgb, var(--color-brand-light) 24%, transparent) !important; color: oklch(from var(--color-brand-light) 93.5% 0.03 h) !important; }
|
||
html.dark .app-main .view-toggle:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *) { background-color: color-mix(in srgb, var(--color-brand) 6%, var(--color-app)) !important; border-color: color-mix(in srgb, var(--color-brand-light) 16%, transparent) !important; }
|
||
html.dark .app-main [class*="dark:hover:bg-white/10"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *):hover { background-color: color-mix(in oklab, #fff 10%, var(--color-app)) !important; }
|
||
html.dark .app-main [class*="dark:hover:bg-white/[0.025]"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *):hover { background-color: color-mix(in oklab, #fff 2.5%, var(--color-app)) !important; }
|
||
html.dark .app-main [class*="dark:hover:bg-white/[0.035]"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *):hover { background-color: color-mix(in oklab, #fff 3.5%, var(--color-app)) !important; }
|
||
html.dark .app-main [class*="dark:hover:bg-white/[0.03]"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *):hover { background-color: color-mix(in oklab, #fff 3%, var(--color-app)) !important; }
|
||
html.dark .app-main [class*="dark:hover:bg-white/[0.04]"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *):hover { background-color: color-mix(in oklab, #fff 4%, var(--color-app)) !important; }
|
||
html.dark .app-main [class*="dark:hover:bg-white/[0.05]"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *):hover { background-color: color-mix(in oklab, #fff 5%, var(--color-app)) !important; }
|
||
html.dark .app-main [class*="dark:hover:bg-white/[0.06]"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *):hover { background-color: color-mix(in oklab, #fff 6%, var(--color-app)) !important; }
|
||
html.dark .app-main [class*="dark:hover:bg-white/[0.07]"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *):hover { background-color: color-mix(in oklab, #fff 7%, var(--color-app)) !important; }
|
||
html.dark .app-main [class*="dark:hover:bg-white/[0.08]"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *):hover { background-color: color-mix(in oklab, #fff 8%, var(--color-app)) !important; }
|
||
html.dark .app-main [class*="dark:hover:bg-white/[0.12]"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *):hover { background-color: color-mix(in oklab, #fff 12%, var(--color-app)) !important; }
|
||
html.dark .app-main [class*="dark:hover:bg-white/[0.1]"]:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *):hover { background-color: color-mix(in oklab, #fff 10%, var(--color-app)) !important; }
|
||
html.dark .app-main .button:not(.button-highlighted):not(.button-error):not([isHighlighted]):not(:disabled):not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *):hover { background-color: color-mix(in srgb, var(--color-brand) 18%, var(--color-app)) !important; border-color: color-mix(in srgb, var(--color-brand-light) 34%, transparent) !important; }
|
||
|
||
/* Collection shells are #121212. Same selector as the 5% mix, plus .rounded-xl, so it wins. */
|
||
html.dark .app-main [class*="dark:bg-white/[0.05]"].rounded-xl:not(:is([class*="dark:bg-"]:not(.app-main), .button, .view-toggle, .application-settings-section, .application-settings-section-body, .box, .coolbox, .data-table, .resource-view, .empty-state, .loading-state-card, .runtime-log-shell, .logs-viewer) *) {
|
||
background-color: var(--color-card) !important;
|
||
border-color: rgb(255 255 255 / 0.08) !important;
|
||
}
|
||
}
|
||
|
||
/* Resource menu in the collapsed desktop sidebar: icons only (the mobile drawer never collapses). */
|
||
.app-sidebar .sidebar-collapsed .nav-context-menu :is(.menu-item-label, .nav-children, .menu-item > :not(.menu-item-icon)) {
|
||
display: none !important;
|
||
}
|
||
|
||
.app-sidebar .sidebar-collapsed .nav-context-menu .menu-item {
|
||
justify-content: center;
|
||
padding-left: 0;
|
||
padding-right: 0;
|
||
}
|
||
|
||
/* A page menu's own "Back to ..." item: the main sidebar copy has the back row instead (components/main-nav-menu). */
|
||
.nav-context-menu [data-page-only] {
|
||
display: none !important;
|
||
}
|