coolify/resources/css/app.css

4887 lines
136 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';
@custom-variant dark (&:where(.dark, .dark *));
/* MapleDeploy branding: Canadian red accent, stone greys */
@theme {
--font-sans: Inter, sans-serif;
--font-display: 'Overlock', sans-serif;
--font-mono: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
--font-logs: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
--color-base: oklch(17.3% 0.005 52); /* MapleDeploy branding: stone re-tint (was oklch(17.3% 0 0)) */
--color-warning: #fde047; /* MapleDeploy branding: warning palette */
--color-warning-50: #fefce8;
--color-warning-100: #fef9c3;
--color-warning-200: #fef08a;
--color-warning-300: #fde047;
--color-warning-400: #facc15;
--color-warning-500: #eab308;
--color-warning-600: #ca8a04;
--color-warning-700: #a16207;
--color-warning-800: #854d0e;
--color-warning-900: #713f12;
--color-success: oklch(72.27% 0.192 149.58);
--color-error: #dc281c; /* MapleDeploy branding: red palette hue-normalized */
--color-coollabs-50: #fef3f1; /* MapleDeploy branding: coollabs -> red */
--color-coollabs: #d52b1f;
--color-coollabs-100: #f34d3d;
--color-coollabs-200: #bc251a;
--color-coollabs-300: #9c2117;
--color-coolgray-100: oklch(20.9% 0.006 55); /* MapleDeploy branding: stone re-tint */
--color-coolgray-200: oklch(24.35% 0.007 45); /* MapleDeploy branding: stone re-tint */
--color-coolgray-300: oklch(26.03% 0.007 38); /* MapleDeploy branding: stone re-tint */
--color-coolgray-400: oklch(27.68% 0.007 37); /* MapleDeploy branding: stone re-tint */
--color-coolgray-500: oklch(31.71% 0.008 50); /* MapleDeploy branding: stone re-tint */
/* 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.004 49); /* MapleDeploy branding: stone re-tint */
/* 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.005 53); /* MapleDeploy branding: stone re-tint */
/* 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.005 54); /* MapleDeploy branding: stone re-tint */
--color-raised: oklch(22.64% 0.006 52); /* MapleDeploy branding: stone re-tint */
--color-selected: oklch(26.86% 0.007 34); /* MapleDeploy branding: stone re-tint */
--color-fg: oklch(96.12% 0.001 106); /* MapleDeploy branding: stone re-tint */
--color-fg-dim: oklch(76.99% 0.008 56); /* MapleDeploy branding: stone re-tint */
/* 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.013 58); /* MapleDeploy branding: stone re-tint */
--color-accent: #d52b1f; /* MapleDeploy branding: accent -> red-600 */
--color-accent-foreground: oklch(100.0% 0 0);
--color-hairline: oklch(100.0% 0 0 / 0.08);
--color-nav-text: oklch(43.86% 0.011 73); /* MapleDeploy branding: stone re-tint */
--color-nav-muted: oklch(51.03% 0.012 64); /* MapleDeploy branding: stone re-tint */
--color-nav-active: oklch(20.46% 0.006 55); /* MapleDeploy branding: stone re-tint */
--color-log: oklch(15.91% 0.004 50); /* MapleDeploy branding: stone re-tint */
--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);
/* MapleDeploy branding: override Tailwind's red scale so red-* utilities match the brand hue */
--color-red-50: #fef1ef;
--color-red-100: #ffe3df;
--color-red-200: #ffcec5;
--color-red-300: #feaa9d;
--color-red-400: #fb7968;
--color-red-500: #f34d3d;
--color-red-600: #d52b1f;
--color-red-700: #bc251a;
--color-red-800: #9c2117;
--color-red-900: #812219;
--color-red-950: #460d08;
/* MapleDeploy branding: remap neutral and gray -> stone for warm tone consistency with marketing/dashboard.
Upstream uses neutral for text/borders and gray for backgrounds/surfaces. Remapping both to stone
warms the entire UI to match our design system, covering ~1000 class references without touching
any Blade templates. */
--color-neutral-50: oklch(98.5% 0.001 106.423);
--color-neutral-100: oklch(97% 0.001 106.424);
--color-neutral-200: oklch(92.3% 0.003 48.717);
--color-neutral-300: oklch(86.9% 0.005 56.366);
--color-neutral-400: oklch(70.9% 0.01 56.259);
--color-neutral-500: oklch(55.3% 0.013 58.071);
--color-neutral-600: oklch(44.4% 0.011 73.639);
--color-neutral-700: oklch(37.4% 0.01 67.558);
--color-neutral-800: oklch(26.8% 0.007 34.298);
--color-neutral-900: oklch(21.6% 0.006 56.043);
--color-neutral-950: oklch(14.7% 0.004 49.25);
--color-gray-50: oklch(98.5% 0.001 106.423);
--color-gray-100: oklch(97% 0.001 106.424);
--color-gray-200: oklch(92.3% 0.003 48.717);
--color-gray-300: oklch(86.9% 0.005 56.366);
--color-gray-400: oklch(70.9% 0.01 56.259);
--color-gray-500: oklch(55.3% 0.013 58.071);
--color-gray-600: oklch(44.4% 0.011 73.639);
--color-gray-700: oklch(37.4% 0.01 67.558);
--color-gray-800: oklch(26.8% 0.007 34.298);
--color-gray-900: oklch(21.6% 0.006 56.043);
--color-gray-950: oklch(14.7% 0.004 49.25);
}
/* 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: color-mix(in oklab, var(--color-coollabs) 52%, black);
}
.dark .button:not(.button-highlighted):not(.button-error):not([isHighlighted]):not(:disabled) {
--button-depth-color: rgb(255 255 255 / 0.08);
}
.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);
}
/* 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;
}
.button {
transition-duration: 120ms, 120ms, 120ms, 80ms, 80ms;
}
.icon-button:not(:disabled):active,
.app-tab:active,
.split-action-main:not(:disabled):active,
.split-action-caret:not(:disabled):active {
transform: scale(0.97);
}
.button:not(:disabled):active {
transform: translateY(2px);
box-shadow: none;
}
/* 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;
}
/*
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;
background:
linear-gradient(var(--color-warning), var(--color-warning)),
linear-gradient(var(--color-warning), var(--color-warning)),
linear-gradient(var(--color-warning), var(--color-warning)),
linear-gradient(var(--color-warning), var(--color-warning));
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::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 {
/* MapleDeploy branding: text-stone-800 body text matches marketing/dashboard */
@apply w-full min-h-full text-stone-800 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 {
/* MapleDeploy branding: font-display (Overlock) on large semantic headings.
h4 (16px, body size) deliberately stays on the body font. */
@apply text-[24px] leading-7 font-semibold tracking-tight font-display dark:text-white;
}
h2 {
@apply text-xl font-bold font-display dark:text-white;
}
h3 {
@apply text-lg font-bold font-display dark:text-white;
}
h4 {
@apply text-base font-bold dark:text-white;
}
/* MapleDeploy branding: Overlock is a display face and only reads well at
heading sizes (>= 18px). Headings downsized below that via text-size
utilities (e.g. 13-15px card and section titles) fall back to the body
font instead of inheriting font-display from the base heading styles. */
:is(h1, h2, h3):is(
[class*="text-[10"], [class*="text-[11"], [class*="text-[12"],
[class*="text-[13"], [class*="text-[14"], [class*="text-[15"],
[class*="text-[16"], [class*="text-[17"],
[class*="text-xs"], [class*="text-sm"], [class*="text-base"]) {
font-family: var(--font-sans);
}
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-warning 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-black dark:text-white;
}
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;
}
.lds-heart {
animation: lds-heart 1.2s infinite cubic-bezier(0.215, 0.61, 0.355, 1);
}
/* Loading feedback uses the higher-contrast warning accent in dark mode. */
.dark .animate-spin {
color: var(--color-warning) !important;
}
.dark #nprogress .bar {
background: var(--color-warning) !important;
}
.dark #nprogress .peg {
box-shadow:
0 0 10px var(--color-warning),
0 0 5px var(--color-warning) !important;
}
.dark #nprogress .spinner-icon {
border-top-color: var(--color-warning) !important;
border-left-color: var(--color-warning) !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: #f2f2f1; /* MapleDeploy branding: stone remap (was #f2f2f2) */
background-color: #141210; /* MapleDeploy branding: stone remap (was #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.005 52); /* MapleDeploy branding: stone re-tint (was 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);
}
.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: rgb(0 0 0 / 0.18);
border-color: rgb(255 255 255 / 0.1);
backdrop-filter: blur(16px);
}
/* 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);
}
.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);
/* 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);
}
.dark {
--color-accent: #fde047; /* MapleDeploy branding: dark-mode accent -> warning */
/* 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). */
--coollabs-canvas: oklch(14.48% 0.004 49); /* MapleDeploy branding: stone re-tint */
--coollabs-elevated: oklch(20.02% 0.005 54); /* MapleDeploy branding: stone re-tint */
--coollabs-recessed: oklch(25.2% 0.007 42); /* MapleDeploy branding: stone re-tint */
--coollabs-base: oklch(22.64% 0.006 52); /* MapleDeploy branding: stone re-tint */
--coollabs-fill: oklch(29.31% 0.008 44); /* MapleDeploy branding: stone re-tint */
--coollabs-line: oklch(32% 0.0085 50.61); /* MapleDeploy branding: stone re-tint */
/* Crisped from 26.9% (1.36:1 on canvas) to 32% (1.63:1) so dark cards
and tables show a visible edge instead of blending into the canvas. */
--coollabs-hairline: oklch(32% 0.0085 50.61); /* MapleDeploy branding: stone re-tint */
--coollabs-subtle: oklch(70.8% 0.01 56.27); /* MapleDeploy branding: stone re-tint */
/* 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.009 56); /* MapleDeploy branding: stone re-tint */
/* 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.012 58); /* MapleDeploy branding: stone re-tint */
--color-nav-active: oklch(96.12% 0.001 106); /* MapleDeploy branding: stone re-tint */
/* 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.012 59); /* MapleDeploy branding: stone re-tint */
/* 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 29.38); /* MapleDeploy branding: brand red hue */
}
/* 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 29.38); /* MapleDeploy branding: brand red hue */
}
/* Theme surfaces are derived from one brand color. Changing
--theme-base-color is enough to generate a complete dark surface ladder. */
html[data-theme="custom"] {
--theme-base-color: #d52b1f; /* MapleDeploy branding: default custom-theme base -> red-600 */
--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));
--theme-border-color: color-mix(in oklab, var(--theme-base-color) 42%, oklch(44.19% 0.0146 285.79));
/* 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);
/* Placeholders read as a light, near-neutral hint of the hue - never the raw
(possibly dark) base color, which is invisible on the dark input. */
--theme-placeholder-color: oklch(from var(--theme-base-color) 0.62 min(c, 0.03) 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);
--color-accent-foreground: var(--theme-accent-foreground);
--color-app: color-mix(in oklab, var(--theme-base-color) 12%, oklch(14.03% 0.0022 286.11));
--color-panel: color-mix(in oklab, var(--theme-base-color) 14%, oklch(15.48% 0.0021 286.15));
--color-surface: color-mix(in oklab, var(--theme-base-color) 18%, oklch(17.35% 0.0020 286.18));
--color-raised: color-mix(in oklab, var(--theme-base-color) 24%, oklch(19.21% 0.0040 286.02));
--color-selected: color-mix(in oklab, var(--theme-base-color) 32%, oklch(20.99% 0.0039 286.06));
--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(--theme-base-color) 28%, oklch(22.34% 0.0058 285.92));
--color-coolgray-500: color-mix(in oklab, var(--theme-base-color) 32%, oklch(24.52% 0.0075 285.83));
--coollabs-canvas: var(--color-app);
--coollabs-elevated: var(--color-surface);
--coollabs-recessed: var(--color-raised);
--coollabs-base: color-mix(in oklab, var(--theme-base-color) 22%, oklch(17.81% 0.0020 286.19));
--color-content-surface: var(--coollabs-base);
--coollabs-fill: color-mix(in oklab, var(--theme-base-color) 32%, oklch(20.99% 0.0039 286.06));
--coollabs-line: var(--theme-border-color);
--coollabs-hairline: color-mix(in srgb, var(--theme-border-color) 55%, var(--color-panel));
--color-nav-text: oklch(91.08% 0.0157 306.4);
--color-nav-muted: oklch(87.24% 0.0188 306.63);
--color-nav-active: oklch(100.0% 0 0);
--color-log: color-mix(in oklab, var(--theme-base-color) 20%, oklch(13.49% 0.0024 286.07));
--color-log-toolbar: color-mix(in oklab, var(--theme-base-color) 26%, oklch(17.35% 0.0020 286.18));
--color-fg-dim: oklch(91.08% 0.0157 306.4);
--color-fg-faint: oklch(87.24% 0.0188 306.63);
--coollabs-subtle: oklch(87.24% 0.0188 306.63);
}
/* Light custom theme: the same single brand color, but surfaces tint toward
white and every foreground token flips to a dark, readable band. This selector
outranks the plain html:not(.dark) light palette, so it wins where they meet.
Accent fills (--theme-bright-color, --color-warning/coollabs) are inherited from
the block above so the JS-computed --theme-accent-foreground stays consistent. */
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);
/* Placeholder: medium-dark, near-neutral hint of the hue. */
--theme-placeholder-color: oklch(from var(--theme-base-color) 0.50 min(c, 0.03) h);
/* Surfaces hold a fixed high lightness (stays light, keeps dark text readable)
while injecting the base hue's chroma, so the tint is actually visible.
color-mix into white coupled lightness to the base and washed the tint out
(a 4% mix of any color into near-white is imperceptible). */
--color-app: oklch(from var(--theme-base-color) 96.5% min(c, 0.035) h);
--color-panel: oklch(from var(--theme-base-color) 98.5% min(c, 0.025) h);
--color-surface: oklch(from var(--theme-base-color) 98% min(c, 0.03) h);
--color-raised: oklch(from var(--theme-base-color) 95.5% min(c, 0.04) h);
--color-selected: oklch(from var(--theme-base-color) 92% min(c, 0.05) h);
--color-coolgray-100: var(--color-surface);
--color-coolgray-200: var(--color-raised);
--color-coolgray-300: var(--color-selected);
--color-coolgray-400: oklch(from var(--theme-base-color) 90% min(c, 0.05) h);
--color-coolgray-500: oklch(from var(--theme-base-color) 87% min(c, 0.055) h);
--coollabs-canvas: var(--color-app);
--coollabs-elevated: oklch(from var(--theme-base-color) 98.5% min(c, 0.025) h);
--coollabs-recessed: var(--color-raised);
--coollabs-base: oklch(from var(--theme-base-color) 99.3% min(c, 0.018) h);
--color-content-surface: var(--coollabs-base);
--coollabs-fill: oklch(from var(--theme-base-color) 91% min(c, 0.05) h);
--coollabs-line: oklch(from var(--theme-base-color) 82% min(c, 0.055) h);
--coollabs-hairline: oklch(from var(--theme-base-color) 88% min(c, 0.04) h);
--color-nav-text: oklch(from var(--theme-base-color) 0.40 min(c, 0.04) h);
--color-nav-muted: oklch(from var(--theme-base-color) 0.50 min(c, 0.03) h);
--color-nav-active: oklch(from var(--theme-base-color) 0.22 min(c, 0.05) h);
--color-log: oklch(from var(--theme-base-color) 96.5% min(c, 0.035) h);
--color-log-toolbar: oklch(from var(--theme-base-color) 93% min(c, 0.045) h);
--color-fg: oklch(from var(--theme-base-color) 0.22 min(c, 0.04) h);
--color-fg-dim: oklch(from var(--theme-base-color) 0.40 min(c, 0.04) h);
--color-fg-faint: oklch(from var(--theme-base-color) 0.50 min(c, 0.03) h);
--coollabs-subtle: oklch(from var(--theme-base-color) 0.50 min(c, 0.03) h);
}
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"]
) {
color: var(--theme-fg-on-surface) !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-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-*/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. */
html[data-theme="custom"]:not(.dark) :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"] {
background-color: var(--coollabs-canvas) !important;
}
html[data-theme="custom"]:not(.dark) :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"] {
border-color: var(--coollabs-hairline) !important;
}
html[data-theme="custom"]:not(.dark) :is([class~="border-neutral-300"], [class~="border-neutral-100"]) {
border-color: var(--coollabs-line) !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-warning);
}
.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: elevated shell with a hairline ring. overflow stays
visible so floating panels (listbox, tooltips) can escape the card. */
.application-settings-section {
margin: 0;
display: flex;
width: 100%;
flex-direction: column;
border-radius: 8px;
border: none;
background: var(--coollabs-elevated);
/* crisp ring + a restrained lift so the card separates from the canvas
(shadcn-style), not a heavy floating shadow */
box-shadow: 0 0 0 1px var(--coollabs-hairline), 0 1px 2px 0 rgb(0 0 0 / 0.05);
/* keep anchored scrolls (scrollIntoView) clear of the fixed topbar + layer-2 nav */
scroll-margin-top: 7rem;
}
/* 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 but size to content on large screens */
@media (min-width: 1024px) {
.application-settings-section.application-settings-form {
width: auto;
}
}
/* Layer card header: compact strip on the shell, subtle title color */
.application-settings-section > :is(header, .application-settings-section-header) {
display: flex;
min-height: 3rem;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 0.5rem;
padding: 0.5rem 0.5rem 0.5rem 1rem;
border-radius: 8px 8px 0 0;
background: var(--coollabs-elevated);
font-weight: 500;
color: var(--coollabs-subtle);
}
.application-settings-section > :is(header, .application-settings-section-header) :is(h2, h3) {
font-size: 0.875rem;
font-weight: 500;
line-height: 1.25rem;
letter-spacing: 0;
/* Card title is a heading, so give it a readable strength instead of the
muted --coollabs-subtle (5.66:1 light / 6.98:1 dark). ~8.7:1 in both
modes (comfortable AAA) while descriptions/labels stay muted. */
color: oklch(40% 0 0);
}
.dark .application-settings-section > :is(header, .application-settings-section-header) :is(h2, h3) {
color: var(--color-fg-dim);
}
.application-settings-section > .application-settings-section-header p {
margin-top: 0.125rem;
font-size: 0.75rem;
line-height: 1rem;
color: var(--coollabs-subtle);
}
/* Layer card body: nested rounded panel with base bg. Ring uses the same
--coollabs-hairline as the card shell (line ~1456) so the body edge and the
header edge read as one continuous border instead of two different grays
(fill was lighter than hairline, so the header sides and body sides did not
match). */
.application-settings-section-body {
position: relative;
border-radius: 8px;
background: var(--coollabs-base);
box-shadow: 0 0 0 1px var(--coollabs-hairline);
padding: 1rem;
}
.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;
}
@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);
}
/* 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: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;
}
/* 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, #0b0a09) 96.5%, white); /* MapleDeploy branding: stone remap */
}
.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, #a8a29e); /* MapleDeploy branding: stone remap */
background: var(--color-raised, #191615); /* MapleDeploy branding: stone remap */
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, #fafaf9); /* MapleDeploy branding: stone remap */
background: var(--color-raised, #191615); /* MapleDeploy branding: stone remap */
}
.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, #d52b1f) 45%, transparent), /* MapleDeploy branding */
0 0 0 1px rgba(0, 0, 0, 0.08);
}
.resource-heading-menus,
.resource-heading-actions {
overflow: visible;
}
.resource-heading-overflow {
display: inline-flex;
align-items: center;
}
.resource-heading-overflow-items {
display: flex;
align-items: center;
gap: 0.125rem;
}
.resource-heading-overflow-items.is-measuring {
display: flex !important;
visibility: hidden !important;
position: absolute !important;
inset: auto auto 0 0 !important;
flex-direction: row !important;
width: max-content !important;
height: auto !important;
max-width: none !important;
max-height: none !important;
overflow: visible !important;
pointer-events: none !important;
padding: 0 !important;
border: 0 !important;
background: transparent !important;
box-shadow: none !important;
}
.resource-heading-overflow.is-collapsed .resource-heading-overflow-items > .button,
.resource-heading-overflow.is-collapsed .resource-heading-overflow-items > a.button {
width: 100%;
justify-content: flex-start;
height: auto;
min-height: 2rem;
padding: 0.375rem 0.625rem;
}
/* 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: 1.75rem;
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-warning/15, text-warning, 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-warning) 15%, transparent);
color: var(--color-warning);
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-warning) 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-warning) 20%, transparent);
color: var(--color-warning);
}
.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;
}
.split-action-main:focus-visible,
.split-action-caret:focus-visible {
outline: none;
position: relative;
z-index: 1;
box-shadow: 0 0 0 3px color-mix(in oklab, var(--color-accent) 40%, transparent);
}
.application-heading-actions .split-action-main,
.application-heading-actions .split-action-caret {
height: 1.75rem;
}
/* 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: 2.25rem;
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, #78716c); /* MapleDeploy branding: stone remap */
}
.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, #78716c); /* MapleDeploy branding: stone remap */
}
/* Flush layer-card body (tables and other full-bleed content) */
.application-settings-section-body.is-flush {
padding: 0;
}
/* 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;
}
.empty-state {
background: var(--coollabs-base);
}
.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%;
}
.validation-installation-logs {
border: 1px solid var(--coollabs-fill);
}
.checkpoint-scroll-fade::after {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
z-index: 1;
width: 2rem;
background: linear-gradient(to left, var(--coollabs-base), transparent);
pointer-events: none;
}
@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);
}
.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;
}
.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-row.service-backup-table-grid {
background: var(--coollabs-base);
border-bottom: 1px solid var(--coollabs-fill);
}
.data-table-row.service-backup-table-grid:last-child {
border-bottom: 0;
}
.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(12rem, 1.5fr) minmax(8rem, 1fr) minmax(8rem, 1fr) 5rem;
}
.volumes-table-grid {
grid-template-columns: minmax(10rem, 1.4fr) minmax(6rem, 1fr) minmax(6rem, 1fr) 5rem 12rem;
}
.volumes-table-grid-with-pr {
grid-template-columns: minmax(9rem, 1.2fr) minmax(5.5rem, 0.85fr) minmax(5.5rem, 0.85fr) 9.25rem 5rem 12rem;
}
.volumes-mobile-label {
display: none;
}
/*
* 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;
}
@media (max-width: 1100px) {
.volumes-table-grid {
grid-template-columns: minmax(9rem, 1.2fr) minmax(6rem, 1fr) 5rem 12rem;
}
.volumes-table-grid > .volumes-col-source,
.data-table-header.volumes-table-grid > .volumes-col-source {
display: none;
}
.volumes-table-grid-with-pr {
grid-template-columns: minmax(9rem, 1.1fr) minmax(6rem, 1fr) 8.5rem 5rem 12rem;
}
.volumes-table-grid-with-pr > .volumes-col-source,
.data-table-header.volumes-table-grid-with-pr > .volumes-col-source {
display: none;
}
.volumes-table-grid-readonly {
grid-template-columns: minmax(10rem, 1.4fr) minmax(8rem, 1fr) 5rem;
}
.volumes-table-grid-readonly > .volumes-col-source,
.data-table-header.volumes-table-grid-readonly > .volumes-col-source {
display: none;
}
}
/* 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 {
display: none;
}
.data-table-row.volumes-table-grid,
.data-table-row.volumes-table-grid-with-pr,
.data-table-row.volumes-table-grid-readonly {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 0.625rem;
padding: 0.875rem 1rem;
min-height: 0;
}
.data-table-row.volumes-table-grid > .volumes-col-source,
.data-table-row.volumes-table-grid-with-pr > .volumes-col-source,
.data-table-row.volumes-table-grid-with-pr > .volumes-col-pr,
.data-table-row.volumes-table-grid-readonly > .volumes-col-source {
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 > * {
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-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;
}
}
.dashboard-deployment-table-grid {
grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr) minmax(6.25rem, 0.75fr) 10rem 8rem;
}
.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;
}
.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);
}
.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) 7rem 5rem 4rem minmax(8rem, 1fr) 5rem;
gap: 0.75rem;
min-width: 66rem;
}
.backup-executions-table-scroll {
overflow-x: auto;
}
.backup-execution-row:last-child > .data-table-row {
border-bottom: 0;
}
.volume-backup-executions-grid {
grid-template-columns: 7.5rem minmax(0, 1fr) 8.5rem 7rem 9rem minmax(9rem, 0.7fr);
min-width: 50rem;
}
.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;
}
.scheduled-executions-table-grid {
grid-template-columns: 5.5rem minmax(8rem, 1fr) 8.5rem minmax(10rem, 1.3fr);
}
.scheduled-executions-table-grid > :nth-child(3),
.scheduled-executions-table-grid > :nth-child(5) {
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) 8rem 6rem minmax(9rem, auto);
}
.backup-executions-table-grid > :nth-child(6),
.backup-executions-table-grid > :nth-child(7) {
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;
}
.scheduled-executions-table-grid {
grid-template-columns: 5.5rem minmax(0, 1fr) 8.5rem;
}
.scheduled-executions-table-grid > :nth-child(6) {
display: none;
}
.scheduler-runs-table-grid {
grid-template-columns: 8.5rem minmax(0, 1fr) 5.5rem;
}
.scheduler-runs-table-grid > :nth-child(3),
.scheduler-runs-table-grid > :nth-child(4) {
display: none;
}
.skipped-jobs-table-grid {
grid-template-columns: 8.5rem minmax(0, 1fr);
}
.skipped-jobs-table-grid > :nth-child(2),
.skipped-jobs-table-grid > :nth-child(4) {
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 {
grid-template-columns: 7.5rem 9rem minmax(14rem, 1fr) minmax(7rem, auto);
min-width: 42rem;
}
.backup-executions-table-grid > :nth-child(4),
.backup-executions-table-grid > :nth-child(5),
.backup-executions-table-grid > :nth-child(6),
.backup-executions-table-grid > :nth-child(7) {
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). */
.logs-viewer {
background: var(--coollabs-base);
color: #262626;
}
.dark .logs-viewer {
background: var(--color-log);
color: #f5f5f4; /* MapleDeploy branding: stone remap (stone-100) */
}
.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, #756d67); /* MapleDeploy branding: stone remap */
}
.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;
}
/* Compact badge that fits the dark logs chrome */
.logs-viewer-status-badge {
height: 1.5rem !important;
flex-shrink: 0;
border-color: var(--color-neutral-200) !important;
background: white !important;
padding-inline: 0.5rem !important;
font-size: 0.6875rem !important;
font-weight: 500 !important;
color: var(--color-neutral-700) !important;
}
.dark .logs-viewer-status-badge {
border-color: rgba(255, 255, 255, 0.1) !important;
background: rgba(255, 255, 255, 0.05) !important;
color: #d6d3d1 !important; /* MapleDeploy branding: stone remap (stone-300) */
}
/* 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: #a8a29e; /* MapleDeploy branding: stone remap (stone-400) */
}
.dark .logs-viewer-btn:hover {
background: rgba(255, 255, 255, 0.06);
color: #f5f5f4; /* MapleDeploy branding: stone remap (stone-100) */
}
.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;
}
.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;
}
.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);
}
/* 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(220px, 1.7fr)
minmax(100px, 0.65fr)
minmax(90px, 0.6fr)
minmax(220px, 1.5fr)
6rem;
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(220px, 1fr) 7rem 6rem 6rem;
}
.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: 36rem;
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;
}
.command-palette-shortcuts {
display: flex;
flex-shrink: 0;
align-items: center;
gap: 0.375rem;
}
.command-palette-kbd {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 1.5rem;
height: 1.375rem;
padding: 0 0.375rem;
border-radius: 0.375rem;
border: 1px solid transparent;
background: var(--coollabs-recessed);
color: var(--coollabs-subtle);
font-size: 0.6875rem;
font-weight: 500;
line-height: 1;
font-family: inherit;
}
.dark .command-palette-kbd {
border-color: rgb(255 255 255 / 0.05);
background: rgb(255 255 255 / 0.06);
}
button.command-palette-kbd {
cursor: pointer;
transition: color 0.15s ease, background 0.15s ease;
}
button.command-palette-kbd:hover {
color: #171717;
background: var(--coollabs-fill);
}
.dark button.command-palette-kbd:hover {
color: var(--color-fg);
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;
}
}