Replace the frontpage liquid-fill disk circle with a real donut split into Apps · Docker · Other · Free, keeping disk % used in the centre. Apps come from the per-app generator (root-device bytes), Docker from system df; both are clamped within "used" so skew can't overflow. Live disk ticks redraw it, and the card now clicks through to the full Storage breakdown. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Signed-off-by: librelad <librelad@digitalangels.vip>
3885 lines
81 KiB
CSS
Executable File
3885 lines
81 KiB
CSS
Executable File
|
||
|
||
/* Reset */
|
||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||
|
||
/* ----------------------------------------------------------------------
|
||
Global themed scrollbars.
|
||
|
||
- Firefox uses scrollbar-color (and scrollbar-width: thin).
|
||
- WebKit/Blink uses the ::-webkit-scrollbar pseudo elements.
|
||
|
||
The thumb's transparent border + background-clip: padding-box gives
|
||
the thumb breathing room (the visible thumb is thinner than the
|
||
8px channel) so it feels less heavy. Hover swaps the thumb to the
|
||
theme accent. Track is transparent so the scrollbar floats over
|
||
whatever surface it's on, matching the cosmic glass elsewhere.
|
||
---------------------------------------------------------------------- */
|
||
* {
|
||
scrollbar-width: thin;
|
||
scrollbar-color: rgba(var(--text-rgb), 0.20) transparent;
|
||
}
|
||
|
||
*::-webkit-scrollbar {
|
||
width: 10px;
|
||
height: 10px;
|
||
}
|
||
|
||
*::-webkit-scrollbar-track {
|
||
background: transparent;
|
||
}
|
||
|
||
*::-webkit-scrollbar-thumb {
|
||
background: rgba(var(--text-rgb), 0.20);
|
||
border: 2px solid transparent;
|
||
background-clip: padding-box;
|
||
border-radius: 8px;
|
||
transition: background-color 0.18s ease;
|
||
}
|
||
|
||
*::-webkit-scrollbar-thumb:hover {
|
||
background: rgba(var(--accent-rgb), 0.55);
|
||
background-clip: padding-box;
|
||
}
|
||
|
||
*::-webkit-scrollbar-thumb:active {
|
||
background: rgba(var(--accent-rgb), 0.75);
|
||
background-clip: padding-box;
|
||
}
|
||
|
||
*::-webkit-scrollbar-corner {
|
||
background: transparent;
|
||
}
|
||
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
|
||
display: flex;
|
||
flex-direction: column;
|
||
min-height: 100vh;
|
||
padding-top: 60px;
|
||
background: var(--surface-bg);
|
||
background-attachment: fixed;
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
/* Nebula body — same recipe as .aurora-bg.aurora-static used on the
|
||
loading + login screens, so the chrome and main content share one
|
||
atmosphere. Three layers cover the viewport: the base radial +
|
||
linear gradient on html, a static cyan-blob plume on ::before
|
||
(mirrors aurora-bg::after), and the star-particle pattern on
|
||
::after (mirrors .aurora-stars::before). No animation. */
|
||
html[data-theme="nebula"] {
|
||
background:
|
||
radial-gradient(ellipse at 20% 30%, var(--gradient-mid) 0%, transparent 55%),
|
||
radial-gradient(ellipse at 80% 70%, var(--gradient-to) 0%, transparent 55%),
|
||
linear-gradient(135deg, var(--gradient-from) 0%, var(--gradient-from) 40%, var(--gradient-mid) 100%);
|
||
background-attachment: fixed;
|
||
}
|
||
|
||
html[data-theme="nebula"] body {
|
||
background: transparent;
|
||
}
|
||
|
||
html[data-theme="nebula"]::before {
|
||
content: '';
|
||
position: fixed;
|
||
inset: -10%;
|
||
z-index: -2;
|
||
background:
|
||
/* Warm cosmic accents — magenta + violet bloom for nebula richness */
|
||
radial-gradient(circle at 12% 88%, rgba(180, 90, 220, 0.32) 0%, transparent 42%),
|
||
radial-gradient(circle at 88% 12%, rgba(255, 120, 180, 0.22) 0%, transparent 38%),
|
||
/* Cyan accent plumes (theme accent colour) */
|
||
radial-gradient(circle at 18% 22%, rgba(var(--accent-rgb), 0.42) 0%, transparent 45%),
|
||
radial-gradient(circle at 78% 18%, rgba(var(--accent-rgb), 0.34) 0%, transparent 42%),
|
||
radial-gradient(circle at 30% 78%, rgba(var(--accent-rgb), 0.30) 0%, transparent 48%),
|
||
radial-gradient(circle at 82% 80%, rgba(var(--accent-rgb), 0.40) 0%, transparent 46%),
|
||
radial-gradient(circle at 50% 50%, rgba(var(--accent-rgb), 0.14) 0%, transparent 60%);
|
||
pointer-events: none;
|
||
}
|
||
|
||
html[data-theme="nebula"]::after {
|
||
content: '';
|
||
position: fixed;
|
||
inset: 0;
|
||
z-index: -1;
|
||
background-image:
|
||
radial-gradient(1.5px 1.5px at 12px 18px, rgba(var(--text-rgb), 0.9), transparent 60%),
|
||
radial-gradient(1px 1px at 47px 92px, rgba(var(--accent-rgb), 0.85), transparent 60%),
|
||
radial-gradient(1.2px 1.2px at 110px 40px, rgba(var(--text-rgb), 0.75), transparent 60%),
|
||
radial-gradient(1px 1px at 165px 130px, rgba(var(--accent-rgb), 0.70), transparent 60%);
|
||
background-size: 200px 200px;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.mobile-menu-toggle {
|
||
display: none;
|
||
background: none;
|
||
border: none;
|
||
color: inherit;
|
||
cursor: pointer;
|
||
padding: 8px;
|
||
border-radius: 6px;
|
||
transition: background 0.2s;
|
||
}
|
||
|
||
.mobile-menu-toggle:hover {
|
||
background: rgba(var(--text-rgb), 0.1);
|
||
}
|
||
|
||
.theme-selector {
|
||
padding: 6px 12px;
|
||
border: 1px solid;
|
||
border-radius: 4px;
|
||
font-size: 14px;
|
||
cursor: pointer;
|
||
}
|
||
|
||
/* Layout — config page (and any other page using .container/.main) uses
|
||
a viewport-locked flex row so the sidebar paints its background the
|
||
full column height and the main pane scrolls independently. Same
|
||
pattern as .tasks-layout and .apps-layout. */
|
||
.container {
|
||
display: flex;
|
||
width: 100%;
|
||
height: calc(100vh - 60px);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.mobile-overlay {
|
||
display: none;
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: rgba(var(--bg-rgb), 0.5);
|
||
z-index: 99;
|
||
opacity: 0;
|
||
transition: opacity 0.3s ease;
|
||
}
|
||
|
||
.mobile-overlay.active {
|
||
display: block;
|
||
opacity: 1;
|
||
}
|
||
|
||
/* Main content — fills the remaining width inside .container/.apps-layout
|
||
and scrolls internally so the sidebar can stay locked at viewport
|
||
height. */
|
||
.main {
|
||
flex: 1;
|
||
height: 100%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
padding: 0px;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.advanced-field.is-hidden {
|
||
display: none;
|
||
}
|
||
|
||
.mullvad-generate-field .mullvad-generate-actions {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
flex-wrap: wrap;
|
||
}
|
||
.mullvad-generate-field .mullvad-generate-btn { margin: 0; }
|
||
.mullvad-generate-status {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
font-size: 12px;
|
||
padding: 4px 10px;
|
||
border-radius: 12px;
|
||
border: 1px solid rgba(var(--text-rgb), 0.15);
|
||
background: rgba(var(--text-rgb), 0.05);
|
||
color: rgba(var(--text-rgb), 0.6);
|
||
}
|
||
.mullvad-generate-status.is-configured {
|
||
background: rgba(var(--status-success-rgb), 0.12);
|
||
border-color: rgba(var(--status-success-rgb), 0.35);
|
||
color: var(--status-success);
|
||
}
|
||
.mullvad-generate-status .mullvad-generate-tick {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 14px;
|
||
height: 14px;
|
||
border-radius: 3px;
|
||
border: 1px solid currentColor;
|
||
font-size: 11px;
|
||
line-height: 1;
|
||
}
|
||
.mullvad-generate-status.is-configured .mullvad-generate-tick {
|
||
background: var(--status-success);
|
||
border-color: var(--status-success);
|
||
color: #0b3d1c;
|
||
}
|
||
|
||
.gluetun-countries-field {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
}
|
||
.gluetun-countries-display {
|
||
flex: 1;
|
||
min-width: 0;
|
||
display: flex;
|
||
flex-wrap: nowrap;
|
||
gap: 6px;
|
||
height: 32px;
|
||
padding: 6px 10px;
|
||
border: 1px solid var(--border-color);
|
||
border-radius: 6px;
|
||
background: var(--card-bg);
|
||
overflow: hidden;
|
||
white-space: nowrap;
|
||
-webkit-mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent);
|
||
mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent);
|
||
}
|
||
.gluetun-country-chip { flex-shrink: 0; }
|
||
.gluetun-country-chip {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
padding: 2px 10px;
|
||
border-radius: 12px;
|
||
background: rgba(52, 152, 219, 0.15);
|
||
border: 1px solid rgba(52, 152, 219, 0.3);
|
||
color: var(--text-color);
|
||
font-size: 12px;
|
||
}
|
||
.gluetun-flag {
|
||
font-size: 14px;
|
||
line-height: 1;
|
||
font-family: 'Twemoji Country Flags', 'Apple Color Emoji', 'Segoe UI Emoji', 'Noto Color Emoji', sans-serif;
|
||
}
|
||
.gluetun-country-empty {
|
||
color: var(--text-secondary, #888);
|
||
font-style: italic;
|
||
font-size: 12px;
|
||
}
|
||
.gluetun-countries-edit { flex-shrink: 0; }
|
||
.gluetun-modal .modal-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 16px 20px;
|
||
border-bottom: 1px solid var(--border-color);
|
||
}
|
||
.gluetun-modal .modal-close {
|
||
background: none;
|
||
border: none;
|
||
font-size: 24px;
|
||
color: var(--text-color);
|
||
cursor: pointer;
|
||
padding: 0;
|
||
}
|
||
.gluetun-modal .modal-body {
|
||
padding: 20px;
|
||
overflow-y: auto;
|
||
}
|
||
.gluetun-modal .modal-footer {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
gap: 12px;
|
||
padding: 16px 20px;
|
||
border-top: 1px solid var(--border-color);
|
||
}
|
||
.gluetun-provider-card {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 12px 14px;
|
||
background: rgba(56, 189, 248, 0.10);
|
||
border: 1px solid rgba(56, 189, 248, 0.30);
|
||
border-radius: 10px;
|
||
margin-bottom: 14px;
|
||
}
|
||
.gluetun-provider-icon-wrap {
|
||
width: 44px;
|
||
height: 44px;
|
||
border-radius: 9px;
|
||
background: var(--surface-bg-solid);
|
||
border: 1px solid rgba(var(--text-rgb), 0.10);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-shrink: 0;
|
||
overflow: hidden;
|
||
}
|
||
.gluetun-provider-icon { width: 32px; height: 32px; object-fit: contain; }
|
||
.gluetun-provider-text { flex: 1; min-width: 0; }
|
||
.gluetun-provider-label { margin: 0; font-size: 12px; color: rgba(var(--text-rgb), 0.60); text-transform: uppercase; letter-spacing: 0.5px; }
|
||
.gluetun-provider-name { margin: 2px 0 0 0; font-size: 16px; font-weight: 600; color: var(--text-primary); text-transform: capitalize; }
|
||
|
||
.gluetun-search-card {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 10px;
|
||
padding: 10px 12px;
|
||
background: rgba(var(--text-rgb), 0.03);
|
||
border: 1px solid rgba(var(--text-rgb), 0.08);
|
||
border-radius: 10px;
|
||
margin-bottom: 12px;
|
||
}
|
||
.gluetun-search-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 6px 10px;
|
||
background: rgba(var(--text-rgb), 0.04);
|
||
border: 1px solid rgba(var(--text-rgb), 0.10);
|
||
border-radius: 8px;
|
||
transition: border-color 0.15s ease, box-shadow 0.15s ease;
|
||
}
|
||
.gluetun-search-row:focus-within {
|
||
border-color: rgba(56, 189, 248, 0.55);
|
||
box-shadow: 0 0 0 3px rgba(56, 189, 248, 0.12);
|
||
}
|
||
.gluetun-search-icon { color: rgba(var(--text-rgb), 0.55); flex-shrink: 0; }
|
||
.gluetun-country-search {
|
||
flex: 1;
|
||
background: transparent;
|
||
border: none;
|
||
outline: none;
|
||
color: var(--text-primary);
|
||
font-size: 14px;
|
||
padding: 2px 0;
|
||
}
|
||
.gluetun-search-actions {
|
||
display: flex;
|
||
gap: 8px;
|
||
}
|
||
.gluetun-search-actions .btn { flex: 1 1 0; }
|
||
|
||
.gluetun-modal .modal-footer {
|
||
display: flex;
|
||
gap: 12px;
|
||
}
|
||
.gluetun-modal .modal-footer .btn { flex: 1 1 0; }
|
||
|
||
.gluetun-country-list {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
||
gap: 6px 14px;
|
||
max-height: 45vh;
|
||
overflow-y: auto;
|
||
padding: 4px 2px;
|
||
}
|
||
.gluetun-country-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
padding: 8px 10px;
|
||
background: rgba(var(--text-rgb), 0.03);
|
||
border: 1px solid rgba(var(--text-rgb), 0.08);
|
||
border-radius: 10px;
|
||
cursor: pointer;
|
||
transition: background 0.15s ease, border-color 0.15s ease;
|
||
}
|
||
.gluetun-country-item:hover {
|
||
background: rgba(var(--text-rgb), 0.06);
|
||
border-color: rgba(56, 189, 248, 0.25);
|
||
}
|
||
.gluetun-country-item:has(input:checked) {
|
||
background: rgba(56, 189, 248, 0.10);
|
||
border-color: rgba(56, 189, 248, 0.45);
|
||
}
|
||
|
||
.gluetun-country-item input[type=checkbox] {
|
||
-webkit-appearance: none;
|
||
appearance: none;
|
||
width: 18px;
|
||
height: 18px;
|
||
flex-shrink: 0;
|
||
cursor: pointer;
|
||
border-radius: 5px;
|
||
background: rgba(var(--text-rgb), 0.04);
|
||
border: 1.5px solid rgba(var(--text-rgb), 0.18);
|
||
box-shadow: inset 0 0 0 1px rgba(var(--text-rgb), 0.02);
|
||
position: relative;
|
||
transition: background 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease, transform 0.12s ease;
|
||
margin: 0;
|
||
}
|
||
.gluetun-country-item:hover input[type=checkbox] {
|
||
border-color: rgba(56, 189, 248, 0.55);
|
||
box-shadow: 0 0 0 3px rgba(56, 189, 248, 0.08);
|
||
}
|
||
.gluetun-country-item input[type=checkbox]:focus-visible {
|
||
outline: none;
|
||
border-color: rgba(56, 189, 248, 0.85);
|
||
box-shadow: 0 0 0 3px rgba(56, 189, 248, 0.20);
|
||
}
|
||
.gluetun-country-item input[type=checkbox]:checked {
|
||
background: linear-gradient(135deg, #38bdf8, #818cf8);
|
||
border-color: rgba(56, 189, 248, 0.9);
|
||
box-shadow: 0 0 0 1px rgba(56, 189, 248, 0.35);
|
||
}
|
||
.gluetun-country-item input[type=checkbox]:checked::after {
|
||
content: '';
|
||
position: absolute;
|
||
inset: 0;
|
||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'><polyline points='20 6 9 17 4 12'/></svg>");
|
||
background-repeat: no-repeat;
|
||
background-position: center;
|
||
background-size: 13px 13px;
|
||
animation: gluetunCheckPop 0.22s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||
}
|
||
@keyframes gluetunCheckPop {
|
||
0% { transform: scale(0.4); opacity: 0; }
|
||
100% { transform: scale(1); opacity: 1; }
|
||
}
|
||
|
||
.gluetun-country-name {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
font-size: 15px;
|
||
color: var(--text-primary);
|
||
font-weight: 500;
|
||
}
|
||
.gluetun-country-name .gluetun-flag {
|
||
font-size: 18px;
|
||
line-height: 1;
|
||
}
|
||
.gluetun-country-empty-msg {
|
||
grid-column: 1 / -1;
|
||
color: var(--text-secondary, #888);
|
||
font-style: italic;
|
||
}
|
||
|
||
/* Output console */
|
||
.console {
|
||
background: var(--console-bg);
|
||
color: var(--console-text);
|
||
border: 1px solid var(--border);
|
||
border-radius: 12px;
|
||
padding: 5px; /* Further reduced to 5px */
|
||
height: 125px; /* Reduced from 250px to half */
|
||
overflow-y: auto;
|
||
white-space: pre-wrap;
|
||
font-size: 14px;
|
||
font-family: 'Courier New', monospace;
|
||
margin: 0;
|
||
position: relative; /* Ensure proper positioning */
|
||
top: 0; /* Force to top */
|
||
}
|
||
|
||
.console-section {
|
||
margin-top: 20px;
|
||
}
|
||
|
||
/* Remove gaps in console output */
|
||
.log-entry {
|
||
margin: 0;
|
||
padding: 2px 8px;
|
||
border-radius: 4px;
|
||
display: block;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.log-entry:first-child {
|
||
padding-top: 0;
|
||
margin-top: 0;
|
||
border-top: none;
|
||
}
|
||
|
||
.log-entry:last-child {
|
||
padding-bottom: 0;
|
||
}
|
||
|
||
.log-timestamp {
|
||
color: rgba(var(--text-rgb), 0.5);
|
||
font-size: 10px;
|
||
margin-right: 8px;
|
||
display: inline;
|
||
}
|
||
|
||
.tabs-wrapper {
|
||
display: block;
|
||
width: 100%;
|
||
}
|
||
|
||
.tabs-list {
|
||
display: flex;
|
||
background: var(--hover-bg);
|
||
border-bottom: 1px solid var(--border-color);
|
||
padding: 0;
|
||
margin: 0;
|
||
width: 100%;
|
||
overflow-x: auto;
|
||
scrollbar-color: rgba(var(--text-rgb), 0.4) rgba(var(--text-rgb), 0.08);
|
||
}
|
||
|
||
/* Tabs inside .tabs-wrapper or .tab-navigation share the row evenly so
|
||
the bar fills its container instead of leaving empty space on the right.
|
||
Children also get centered so labels (and any leading icon/emoji) sit
|
||
in the middle of each tab. */
|
||
.tabs-wrapper .tabs-list .tab-button,
|
||
.tab-navigation .tab-button {
|
||
flex: 1 1 0;
|
||
min-width: 0;
|
||
text-align: center;
|
||
white-space: nowrap;
|
||
justify-content: center;
|
||
}
|
||
|
||
/* Task Status Indicator */
|
||
.task-status-indicator {
|
||
position: fixed;
|
||
top: 20px;
|
||
right: 20px;
|
||
background: rgba(33, 150, 243, 0.95);
|
||
color: var(--text-primary);
|
||
padding: 12px 16px;
|
||
border-radius: 8px;
|
||
border: 1px solid rgba(76, 175, 80, 0.3);
|
||
z-index: 1000;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
backdrop-filter: blur(10px);
|
||
animation: slideIn 0.3s ease-out;
|
||
}
|
||
|
||
.task-status-content {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.spinner-small {
|
||
width: 16px;
|
||
height: 16px;
|
||
border: 2px solid rgba(var(--text-rgb), 0.3);
|
||
border-top: 2px solid #4CAF50;
|
||
border-radius: 50%;
|
||
animation: spin 1s linear infinite;
|
||
}
|
||
|
||
@keyframes spin {
|
||
0% { transform: rotate(0deg); }
|
||
100% { transform: rotate(360deg); }
|
||
}
|
||
|
||
/* Button States */
|
||
.task-running {
|
||
opacity: 0.7;
|
||
cursor: not-allowed !important;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.task-running::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: -100%;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: linear-gradient(90deg, transparent, rgba(76, 175, 80, 0.1));
|
||
animation: loadingShimmer 1.5s infinite;
|
||
}
|
||
|
||
@keyframes loadingShimmer {
|
||
0% { left: -100%; }
|
||
50% { left: 100%; }
|
||
100% { left: 100%; }
|
||
}
|
||
|
||
/* App Header Enhancement */
|
||
.app-header {
|
||
position: relative;
|
||
}
|
||
|
||
.task-highlighted {
|
||
background: linear-gradient(135deg, rgba(76, 175, 80, 0.2), rgba(33, 150, 243, 0.2));
|
||
border: 2px solid #4CAF50;
|
||
border-radius: 8px;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.task-highlighted:hover {
|
||
background: linear-gradient(135deg, rgba(76, 175, 80, 0.3), rgba(33, 150, 243, 0.3));
|
||
}
|
||
|
||
/* Clean scrollbar from scratch - higher specificity */
|
||
.tabs-wrapper .tabs-list::-webkit-scrollbar {
|
||
height: 12px !important;
|
||
}
|
||
|
||
.tabs-wrapper .tabs-list::-webkit-scrollbar-track {
|
||
background: rgba(var(--text-rgb), 0.08) !important;
|
||
border-radius: 9px !important;
|
||
}
|
||
|
||
.tabs-wrapper .tabs-list::-webkit-scrollbar-thumb {
|
||
background: rgba(var(--text-rgb), 0.4) !important;
|
||
border-radius: 9px !important;
|
||
border: none !important;
|
||
}
|
||
|
||
.tabs-wrapper .tabs-list::-webkit-scrollbar-thumb:hover {
|
||
background: rgba(var(--text-rgb), 0.5) !important;
|
||
}
|
||
|
||
/* Dynamic scrollbar enhancement for when tabs-list exists - higher specificity */
|
||
.tabs-wrapper .tabs-list[data-scrollable="true"]::-webkit-scrollbar {
|
||
height: 16px !important;
|
||
}
|
||
|
||
.tabs-wrapper .tabs-list[data-scrollable="true"]::-webkit-scrollbar-track {
|
||
background: rgba(var(--text-rgb), 0.1) !important;
|
||
border-radius: 10px !important;
|
||
margin: 15px 0 8px 0 !important;
|
||
}
|
||
|
||
.tabs-wrapper .tabs-list[data-scrollable="true"]::-webkit-scrollbar-thumb {
|
||
background: rgba(var(--text-rgb), 0.5) !important;
|
||
border-radius: 10px !important;
|
||
border: none !important;
|
||
}
|
||
|
||
.tabs-wrapper .tabs-list[data-scrollable="true"]::-webkit-scrollbar-thumb:hover {
|
||
background: rgba(var(--text-rgb), 0.6) !important;
|
||
}
|
||
|
||
.tabs-wrapper .tabs-list[data-scrollable="true"]::-webkit-scrollbar-thumb:hover::-webkit-scrollbar {
|
||
height: 11px !important; /* 16px * 2/3 = ~11px */
|
||
}
|
||
|
||
.tab-emoji {
|
||
font-size: 14px;
|
||
/* Coerce the OS to render these as text-presentation glyphs rather
|
||
than colour-emoji bitmaps (⚙ instead of ⚙️ etc.). Result is a
|
||
monochrome glyph we can theme with `color`, which reads way
|
||
better against the dark cosmic gradient than the platform's
|
||
greyish emoji bitmaps did. */
|
||
font-variant-emoji: text;
|
||
color: var(--accent);
|
||
line-height: 1;
|
||
}
|
||
|
||
/* Active tab uses text-primary so the icon doesn't disappear behind
|
||
the accent-tinted pill background. */
|
||
.tab-button.active .tab-emoji,
|
||
.tab-button.nav-active .tab-emoji,
|
||
.main-tab-button.active .tab-emoji,
|
||
.main-tab-button.nav-active .tab-emoji {
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
.tab-name {
|
||
font-weight: 500;
|
||
}
|
||
|
||
.tabs-content {
|
||
display: block;
|
||
width: 100%;
|
||
background: var(--card-bg);
|
||
padding: 30px 10px 20px 10px;
|
||
border-radius: 0px 0px 12px 12px;
|
||
}
|
||
|
||
.tab-panel {
|
||
display: none;
|
||
padding: 5px 24px 5px 24px;
|
||
min-height: auto;
|
||
animation: fadeIn 0.2s ease;
|
||
}
|
||
|
||
.tab-panel.active {
|
||
display: block;
|
||
}
|
||
|
||
.panel-header {
|
||
margin-bottom: 20px;
|
||
padding-bottom: 12px;
|
||
border-bottom: 1px solid var(--border-color);
|
||
display: none; /* Hide duplicate headers */
|
||
}
|
||
|
||
.panel-header h4 {
|
||
margin: 0 0 6px 0;
|
||
color: var(--text-primary, #fff);
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.panel-header p {
|
||
margin: 0;
|
||
color: var(--text-secondary, #ccc);
|
||
font-size: 13px;
|
||
}
|
||
|
||
.panel-fields {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||
gap: 16px;
|
||
}
|
||
|
||
@keyframes configDirtyIn {
|
||
from { opacity: 0; transform: translateY(6px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
|
||
/* "Dependency required" card — e.g. "Enable Whitelist — Traefik needs
|
||
to be installed" on the Features tab. Two-row grid so the action
|
||
button always lives in its own full-width footer row, regardless of
|
||
how narrow the card column is. The old single-row flex layout broke
|
||
in 3-column grids: the body squashed into a 30-char column and the
|
||
button stretched vertically as the only flex item with breathing
|
||
room. Grid keeps icon + body on one row and the button on the next,
|
||
so the visual reads top-to-bottom: who is this for → what's missing
|
||
→ fix it. */
|
||
.dep-required-card {
|
||
display: grid;
|
||
grid-template-columns: auto 1fr;
|
||
grid-template-rows: auto auto;
|
||
gap: 10px 12px;
|
||
padding: 14px;
|
||
background: rgba(245, 158, 11, 0.08);
|
||
border: 1px solid rgba(245, 158, 11, 0.30);
|
||
border-radius: 10px;
|
||
margin-bottom: 10px;
|
||
}
|
||
.dep-required-icon {
|
||
grid-row: 1;
|
||
grid-column: 1;
|
||
width: 40px;
|
||
height: 40px;
|
||
border-radius: 8px;
|
||
object-fit: contain;
|
||
background: rgba(var(--text-rgb), 0.04);
|
||
padding: 4px;
|
||
}
|
||
.dep-required-body {
|
||
grid-row: 1;
|
||
grid-column: 2;
|
||
min-width: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 2px;
|
||
align-self: center;
|
||
}
|
||
.dep-required-title {
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: var(--text-primary);
|
||
}
|
||
.dep-required-reason {
|
||
font-size: 12px;
|
||
color: rgba(var(--text-rgb), 0.70);
|
||
line-height: 1.4;
|
||
}
|
||
.dep-required-action {
|
||
grid-row: 2;
|
||
grid-column: 1 / -1;
|
||
width: 100%;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 6px;
|
||
margin-top: 2px;
|
||
}
|
||
|
||
.nav-button {
|
||
margin-left: auto;
|
||
padding: 4px 8px;
|
||
background: var(--primary-color);
|
||
color: var(--text-primary);
|
||
border: none;
|
||
border-radius: 4px;
|
||
font-size: 10px;
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.nav-button:hover {
|
||
background: var(--accent-hover);
|
||
transform: translateY(-1px);
|
||
}
|
||
|
||
.nav-button svg {
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.nav-button.install-button {
|
||
background: var(--status-success);
|
||
}
|
||
|
||
.nav-button.install-button:hover {
|
||
background: var(--status-success-hover);
|
||
transform: translateY(-1px);
|
||
}
|
||
|
||
/* Confirmation Dialog - Simple Working Version */
|
||
.confirmation-overlay {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
background: rgba(var(--bg-rgb), 0.7);
|
||
backdrop-filter: blur(8px);
|
||
-webkit-backdrop-filter: blur(8px);
|
||
z-index: 99999;
|
||
display: none;
|
||
opacity: 0;
|
||
transition: opacity 0.3s ease;
|
||
}
|
||
|
||
.confirmation-overlay.active {
|
||
display: block;
|
||
opacity: 1;
|
||
}
|
||
|
||
.confirmation-dialog {
|
||
position: fixed;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
background: var(--surface-bg-solid);
|
||
background: var(--bg-primary, #1a1a1a);
|
||
border: 2px solid var(--border-strong);
|
||
border: 2px solid var(--border-color, #444);
|
||
border-radius: 8px;
|
||
max-width: 400px;
|
||
width: 90%;
|
||
z-index: 100000;
|
||
opacity: 1;
|
||
transition: all 0.3s ease;
|
||
display: none;
|
||
}
|
||
|
||
.confirmation-overlay.active .confirmation-dialog {
|
||
display: block;
|
||
}
|
||
|
||
.confirmation-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 15px 20px;
|
||
border-bottom: 1px solid var(--border-strong);
|
||
border-bottom: 1px solid var(--border-color, #444);
|
||
}
|
||
|
||
.confirmation-header h3 {
|
||
margin: 0;
|
||
color: var(--text-primary);
|
||
color: var(--text-primary, #fff);
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.confirmation-close {
|
||
background: none;
|
||
border: none;
|
||
color: var(--text-secondary);
|
||
color: var(--text-secondary, #ccc);
|
||
font-size: 20px;
|
||
cursor: pointer;
|
||
padding: 0;
|
||
width: 24px;
|
||
height: 24px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.confirmation-close:hover {
|
||
color: var(--text-primary);
|
||
color: var(--text-primary, #fff);
|
||
}
|
||
|
||
.confirmation-body {
|
||
padding: 20px;
|
||
}
|
||
|
||
.confirmation-content {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 12px;
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.confirmation-icon {
|
||
font-size: 20px;
|
||
flex-shrink: 0;
|
||
margin-top: 2px;
|
||
}
|
||
|
||
.confirmation-text {
|
||
color: var(--text-primary);
|
||
color: var(--text-primary, #fff);
|
||
line-height: 1.4;
|
||
flex: 1;
|
||
}
|
||
|
||
.confirmation-checkbox {
|
||
padding-top: 15px;
|
||
border-top: 1px solid var(--border-strong);
|
||
border-top: 1px solid var(--border-color, #444);
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.confirmation-checkbox label {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
color: var(--text-primary);
|
||
color: var(--text-primary, #fff);
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
order: 1;
|
||
}
|
||
|
||
.confirmation-checkbox input {
|
||
width: 16px;
|
||
height: 16px;
|
||
}
|
||
|
||
.confirmation-footer {
|
||
display: flex;
|
||
gap: 10px;
|
||
justify-content: flex-end;
|
||
padding: 15px 20px;
|
||
}
|
||
|
||
.confirmation-btn {
|
||
padding: 8px 16px;
|
||
border: none;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.confirmation-btn-cancel {
|
||
background: var(--text-muted);
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
.confirmation-btn-cancel:hover {
|
||
background: var(--text-secondary);
|
||
}
|
||
|
||
.confirmation-btn-ticked {
|
||
background: var(--status-success) !important;
|
||
color: #ffffff !important;
|
||
}
|
||
|
||
.confirmation-btn-ticked:hover {
|
||
background: var(--status-success-hover) !important;
|
||
}
|
||
|
||
.confirmation-btn:disabled {
|
||
opacity: 0.5;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.tab-panel#panel-advanced .panel-header {
|
||
background: rgba(255, 107, 53, 0.1);
|
||
border-left: 4px solid var(--status-warning);
|
||
border-bottom: 1px solid var(--border-color);
|
||
}
|
||
|
||
.tab-panel#panel-advanced .panel-header h4 {
|
||
color: var(--status-warning);
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.tab-panel#panel-advanced .panel-header p {
|
||
color: #d84315;
|
||
font-style: italic;
|
||
font-size: 12px;
|
||
margin: 4px 0 0 0;
|
||
}
|
||
|
||
.no-fields {
|
||
text-align: center;
|
||
padding: 32px;
|
||
color: var(--text-secondary, #ccc);
|
||
font-style: italic;
|
||
background: var(--hover-bg);
|
||
border-radius: 6px;
|
||
border: 1px dashed var(--border-color);
|
||
}
|
||
|
||
/* Responsive Design */
|
||
@media (max-width: 768px) {
|
||
.tab-button {
|
||
flex: 1;
|
||
min-width: 60px;
|
||
justify-content: center;
|
||
font-size: 11px;
|
||
padding: 8px 12px;
|
||
}
|
||
|
||
.main-tab-button {
|
||
flex: 1;
|
||
min-width: 60px;
|
||
justify-content: center;
|
||
font-size: 11px;
|
||
padding: 8px 12px;
|
||
}
|
||
|
||
.tab-emoji {
|
||
font-size: 12px;
|
||
}
|
||
|
||
.tab-name {
|
||
display: none;
|
||
}
|
||
|
||
.config-title {
|
||
padding: 16px;
|
||
}
|
||
|
||
.tab-panel {
|
||
padding: 24px 16px 16px 16px;
|
||
min-height: auto;
|
||
}
|
||
|
||
.panel-fields {
|
||
grid-template-columns: 1fr;
|
||
gap: 12px;
|
||
}
|
||
|
||
.form-field {
|
||
gap: 4px;
|
||
}
|
||
|
||
.form-input,
|
||
.form-select,
|
||
.form-textarea {
|
||
padding: 8px;
|
||
font-size: 16px; /* Prevent zoom on iOS */
|
||
}
|
||
}
|
||
|
||
.timeout {
|
||
font-weight: bold;
|
||
}
|
||
|
||
/* App Configuration Page */
|
||
.app-header {
|
||
background: var(--card-bg);
|
||
border: 1px solid var(--border-color);
|
||
border-radius: 12px;
|
||
padding: 24px;
|
||
margin-bottom: 30px;
|
||
}
|
||
|
||
.app-info {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 20px;
|
||
}
|
||
|
||
.app-details h1 {
|
||
font-size: 28px;
|
||
font-weight: 700;
|
||
margin-bottom: 8px;
|
||
color: var(--text-color);
|
||
}
|
||
|
||
.app-details h2 {
|
||
font-size: 24px;
|
||
font-weight: 600;
|
||
color: var(--text-color);
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.app-details .app-long-description {
|
||
font-size: 14px;
|
||
color: var(--text-secondary, #ccc);
|
||
line-height: 1.4;
|
||
}
|
||
|
||
/* Responsive config field rules moved to config.css (where the base
|
||
.config-fields { repeat(3, 1fr) } lives — config.css loads after
|
||
style.css so keeping these here got overridden by the unscoped
|
||
base rule). */
|
||
|
||
/* Section Toggle Functionality */
|
||
.hidden {
|
||
display: none !important;
|
||
}
|
||
|
||
.section-content {
|
||
transition: opacity 0.3s ease, transform 0.3s ease;
|
||
}
|
||
|
||
.section-content.disabled {
|
||
opacity: 0.5;
|
||
pointer-events: none;
|
||
}
|
||
|
||
/* App Header with Actions */
|
||
.app-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: flex-start;
|
||
margin-bottom: 30px;
|
||
padding: 20px;
|
||
background: rgba(var(--text-rgb), 0.05);
|
||
border-radius: 12px;
|
||
border: 1px solid rgba(var(--text-rgb), 0.1);
|
||
}
|
||
|
||
.app-info {
|
||
display: flex;
|
||
align-items: center;
|
||
flex: 1;
|
||
}
|
||
|
||
.backup-btn, .uninstall-btn {
|
||
padding: 8px 16px;
|
||
border: 1px solid rgba(var(--text-rgb), 0.3);
|
||
background: transparent;
|
||
color: rgba(var(--text-rgb), 0.9);
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
font-size: 13px;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.backup-btn:hover, .uninstall-btn:hover {
|
||
background: rgba(var(--text-rgb), 0.1);
|
||
transform: translateY(-1px);
|
||
}
|
||
|
||
.password-field {
|
||
position: relative;
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
/* Old notification styles removed - using newer notification system */
|
||
|
||
/* REMOVED OLD CHECKBOX STYLES - REPLACED WITH TOGGLE SWITCH */
|
||
|
||
/* Info tooltip badge. Markup is <span class="tooltip" title="…">ℹ️</span>.
|
||
The ℹ️ emoji is drawn by the OS as a multi-color bitmap that doesn't
|
||
match our theme. We clip the host (overflow:hidden + text-indent to
|
||
push it off-screen) so the emoji is invisible no matter how the
|
||
platform fonts behave, then draw a clean italic 'i' via ::after
|
||
absolutely positioned inside the cyan circle. */
|
||
.tooltip {
|
||
display: inline-block;
|
||
width: 16px;
|
||
height: 16px;
|
||
background: var(--primary-color, var(--accent));
|
||
border-radius: 50%;
|
||
cursor: help;
|
||
position: relative;
|
||
margin-left: 4px;
|
||
flex-shrink: 0;
|
||
overflow: hidden;
|
||
text-indent: 100%;
|
||
white-space: nowrap;
|
||
font-size: 0;
|
||
color: transparent;
|
||
vertical-align: middle;
|
||
/* Sits below where it looks aligned with adjacent label text. */
|
||
margin-top: -2px;
|
||
transition: background 0.18s ease, transform 0.18s ease;
|
||
}
|
||
|
||
.tooltip::after {
|
||
content: 'i';
|
||
position: absolute;
|
||
inset: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-family: Georgia, 'Times New Roman', serif;
|
||
font-style: italic;
|
||
font-weight: 700;
|
||
font-size: 11px;
|
||
line-height: 1;
|
||
color: var(--text-on-accent, #ffffff);
|
||
text-indent: 0;
|
||
/* Drops the 'i' inside the circle so it doesn't sit too high.
|
||
Tuned with the host's -2px margin-top above. */
|
||
padding-bottom: 0;
|
||
}
|
||
|
||
.tooltip:hover {
|
||
background: var(--accent-hover, var(--primary-color-hover, #4169e1));
|
||
transform: scale(1.08);
|
||
}
|
||
|
||
.tooltip::before {
|
||
content: attr(title);
|
||
position: absolute;
|
||
bottom: 125%;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
background: rgba(var(--bg-rgb), 0.9);
|
||
color: var(--text-primary);
|
||
padding: 8px 12px;
|
||
border-radius: 6px;
|
||
font-size: 12px;
|
||
white-space: normal;
|
||
z-index: 1000;
|
||
opacity: 0;
|
||
visibility: hidden;
|
||
transition: opacity 0.3s, visibility 0.3s;
|
||
max-width: 300px;
|
||
word-wrap: break-word;
|
||
min-width: 200px;
|
||
}
|
||
|
||
.tooltip:hover::before {
|
||
opacity: 1;
|
||
visibility: visible;
|
||
z-index: 99999;
|
||
}
|
||
|
||
/* REMOVED OLD CHECKBOX STYLES - REPLACED WITH TOGGLE SWITCH */
|
||
|
||
/* REMOVED OLD CHECKBOX STYLES - REPLACED WITH TOGGLE SWITCH */
|
||
|
||
#hidden-options-content {
|
||
padding: 20px;
|
||
}
|
||
|
||
#hidden-options-content h3 {
|
||
color: var(--text-secondary, #ccc);
|
||
font-size: 16px;
|
||
margin-bottom: 16px;
|
||
font-style: italic;
|
||
}
|
||
|
||
.install-btn {
|
||
background: var(--status-success);
|
||
color: #ffffff;
|
||
border: 1px solid var(--status-success);
|
||
padding: 14px 28px;
|
||
border-radius: 12px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.install-btn:hover {
|
||
background: var(--status-success-hover);
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
/* Base Button Styles */
|
||
.btn {
|
||
padding: 12px 24px;
|
||
border: none;
|
||
border-radius: 8px;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.log-entry {
|
||
font-family: 'Courier New', monospace;
|
||
font-size: 12px;
|
||
line-height: 1.4;
|
||
margin-bottom: 5px;
|
||
padding: 5px;
|
||
border-radius: 4px;
|
||
background: rgba(var(--bg-rgb), 0.2);
|
||
color: rgba(var(--text-rgb), 0.8);
|
||
word-break: break-all;
|
||
}
|
||
|
||
.log-entry.error {
|
||
background: rgba(var(--status-danger-rgb), 0.2);
|
||
color: var(--status-danger);
|
||
}
|
||
|
||
.log-entry.success {
|
||
background: rgba(var(--status-success-rgb), 0.2);
|
||
color: #51cf66;
|
||
}
|
||
|
||
.log-entry.warning {
|
||
background: rgba(var(--status-warning-rgb), 0.2);
|
||
color: #ffd43b;
|
||
}
|
||
|
||
.log-entry.info {
|
||
background: rgba(var(--accent-rgb), 0.2);
|
||
color: #74c0fc;
|
||
}
|
||
|
||
/* Error state */
|
||
.error {
|
||
padding: 20px;
|
||
border-radius: 8px;
|
||
background: #f8d7da;
|
||
color: #721c24;
|
||
border: 1px solid #f5c6cb;
|
||
margin: 20px;
|
||
}
|
||
|
||
.tab-content {
|
||
}
|
||
|
||
.tab-pane {
|
||
display: none;
|
||
animation: fadeIn 0.3s ease-in-out;
|
||
}
|
||
|
||
.tab-pane.active {
|
||
display: block;
|
||
}
|
||
|
||
.tab-pane h4 {
|
||
margin: 0 0 20px 0;
|
||
color: rgba(var(--text-rgb), 0.9);
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
padding-bottom: 10px;
|
||
border-bottom: 1px solid rgba(var(--text-rgb), 0.1);
|
||
}
|
||
|
||
/* Responsive Design */
|
||
@media (max-width: 1024px) {
|
||
.apps-section {
|
||
--app-min: 280px;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
/* Mobile menu toggle */
|
||
.mobile-menu-toggle {
|
||
display: block;
|
||
}
|
||
|
||
/* Sidebar mobile styles */
|
||
.sidebar {
|
||
position: fixed;
|
||
top: 60px;
|
||
left: 0;
|
||
height: calc(100vh - 60px);
|
||
transform: translateX(-100%);
|
||
z-index: 100;
|
||
}
|
||
|
||
.sidebar.mobile-open {
|
||
transform: translateX(0);
|
||
}
|
||
|
||
/* App Configuration Page Styles */
|
||
.app-info {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 24px;
|
||
padding: 32px;
|
||
background: var(--card-bg);
|
||
border: 1px solid var(--border-color);
|
||
border-radius: 12px;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.app-info:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: var(--card-shadow-hover);
|
||
border-color: var(--primary-color);
|
||
}
|
||
|
||
.app-icon {
|
||
width: 64px;
|
||
height: 64px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 12px;
|
||
flex-shrink: 0;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.app-icon img {
|
||
width: 48px;
|
||
height: 48px;
|
||
object-fit: contain;
|
||
transition: transform 0.2s ease;
|
||
}
|
||
|
||
.app-icon:hover img {
|
||
transform: scale(1.1);
|
||
}
|
||
|
||
.app-details {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
|
||
.app-details h2 {
|
||
font-size: 24px;
|
||
font-weight: 600;
|
||
color: var(--text-color);
|
||
margin: 0 0 8px 0;
|
||
line-height: 1.2;
|
||
}
|
||
|
||
.app-description {
|
||
font-size: 16px;
|
||
color: var(--text-secondary, #ccc);
|
||
margin: 0 0 16px 0;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.app-meta {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
margin-top: 16px;
|
||
}
|
||
|
||
.category-tag {
|
||
background: var(--primary-color);
|
||
color: var(--text-primary);
|
||
padding: 4px 8px;
|
||
border-radius: 6px;
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.status-tag {
|
||
padding: 4px 8px;
|
||
border-radius: 6px;
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.status-tag.installed {
|
||
background: var(--status-success);
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
.status-tag.available {
|
||
background: var(--text-muted);
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
.app-not-found {
|
||
text-align: center;
|
||
padding: 60px 20px;
|
||
color: var(--text-color);
|
||
}
|
||
|
||
.app-not-found h2 {
|
||
font-size: 24px;
|
||
font-weight: 600;
|
||
margin: 0 0 16px 0;
|
||
}
|
||
|
||
.config-section {
|
||
background: var(--card-bg);
|
||
border: 1px solid var(--border-color);
|
||
border-radius: 12px;
|
||
padding: 22px;
|
||
margin-top: 24px;
|
||
}
|
||
|
||
.config-placeholder {
|
||
text-align: center;
|
||
}
|
||
|
||
.config-placeholder h3 {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
color: var(--text-color);
|
||
margin: 0 0 16px 0;
|
||
}
|
||
|
||
.config-placeholder p {
|
||
color: var(--text-secondary, #ccc);
|
||
margin: 0 0 24px 0;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.config-actions {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 12px;
|
||
margin-top: 24px;
|
||
}
|
||
|
||
.btn {
|
||
padding: 12px 24px;
|
||
border: none;
|
||
border-radius: 8px;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
text-decoration: none;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.btn-primary {
|
||
background: var(--primary-color);
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
.btn-primary:hover {
|
||
background: var(--primary-hover);
|
||
transform: translateY(-1px);
|
||
}
|
||
|
||
.btn-secondary {
|
||
color: var(--text-color);
|
||
border: 1px solid var(--border-color);
|
||
}
|
||
|
||
.btn-secondary:hover {
|
||
background: var(--border-color);
|
||
transform: translateY(-1px);
|
||
}
|
||
|
||
/* Main content mobile — go edge-to-edge so config/forms inside
|
||
don't get squeezed by stacked padding from .main + .config-section. */
|
||
.main {
|
||
padding: 0;
|
||
}
|
||
|
||
/* App cards mobile */
|
||
.apps-section {
|
||
grid-template-columns: 1fr;
|
||
gap: 16px;
|
||
}
|
||
|
||
.app-card {
|
||
flex-direction: column;
|
||
padding: 16px;
|
||
gap: 12px;
|
||
}
|
||
|
||
.app-card-top {
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 12px;
|
||
}
|
||
|
||
.app-card-icon {
|
||
width: 80px;
|
||
height: 80px;
|
||
align-self: center;
|
||
}
|
||
|
||
.app-card-content {
|
||
text-align: center;
|
||
width: 100%;
|
||
}
|
||
|
||
.app-card-actions {
|
||
width: 100%;
|
||
min-width: auto;
|
||
flex-direction: row;
|
||
}
|
||
|
||
.app-card-actions button {
|
||
flex: 1;
|
||
width: 50%;
|
||
}
|
||
|
||
.app-card-title {
|
||
font-size: 16px;
|
||
white-space: normal;
|
||
line-height: 1.3;
|
||
}
|
||
|
||
.app-card-description {
|
||
font-size: 13px;
|
||
}
|
||
|
||
.app-card button {
|
||
width: 100%;
|
||
padding: 14px 20px;
|
||
font-size: 15px;
|
||
min-height: 48px;
|
||
}
|
||
|
||
/* Topbar mobile */
|
||
.topbar {
|
||
padding: 0 16px;
|
||
}
|
||
|
||
.topbar-controls {
|
||
gap: 8px;
|
||
}
|
||
|
||
.topbar-nav {
|
||
display: none;
|
||
}
|
||
|
||
.theme-selector {
|
||
font-size: 12px;
|
||
padding: 4px 8px;
|
||
}
|
||
|
||
.donate-btn {
|
||
padding: 6px 12px;
|
||
font-size: 12px;
|
||
}
|
||
|
||
/* Category tags mobile */
|
||
.app-card-tags {
|
||
justify-content: center;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.app-tag {
|
||
font-size: 11px;
|
||
padding: 3px 6px;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 480px) {
|
||
/* Extra small screens */
|
||
.topbar {
|
||
padding: 0 12px;
|
||
}
|
||
|
||
.main {
|
||
padding: 0;
|
||
}
|
||
|
||
.app-card {
|
||
padding: 12px;
|
||
}
|
||
|
||
.app-card-icon {
|
||
width: 50px;
|
||
height: 50px;
|
||
}
|
||
|
||
.app-card-title {
|
||
font-size: 15px;
|
||
}
|
||
|
||
.app-card-description {
|
||
font-size: 12px;
|
||
}
|
||
|
||
.donate-btn {
|
||
display: none;
|
||
}
|
||
|
||
.logo {
|
||
font-size: 18px;
|
||
}
|
||
}
|
||
|
||
.dashboard-stats {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||
gap: 20px;
|
||
padding: 22px;
|
||
}
|
||
|
||
.dashboard-content {
|
||
/* Remove background - let main container show through */
|
||
border-radius: 12px;
|
||
border: 1px solid var(--border-color);
|
||
padding: 24px;
|
||
margin-bottom: 16px; /* Reduced from 32px to reduce gap */
|
||
}
|
||
|
||
/* Dashboard Front Page Installed Apps */
|
||
.frontpage-apps-section {
|
||
padding: 0 22px 22px;
|
||
}
|
||
|
||
.frontpage-apps-grid {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 20px;
|
||
}
|
||
|
||
.frontpage-app-tile {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 10px;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.frontpage-app-icon-wrap {
|
||
position: relative;
|
||
width: 132px;
|
||
height: 132px;
|
||
background: rgba(var(--text-rgb), 0.08);
|
||
border-radius: 22px;
|
||
border: 1px solid rgba(var(--text-rgb), 0.15);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 20px;
|
||
transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.frontpage-app-tile:hover .frontpage-app-icon-wrap {
|
||
transform: translateY(-4px);
|
||
border-color: rgba(var(--text-rgb), 0.25);
|
||
}
|
||
|
||
.frontpage-app-icon-wrap img {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: contain;
|
||
}
|
||
|
||
/* Overlay that appears on hover when services are available — frosted
|
||
veil so the icon underneath still reads through, not a black slab. */
|
||
.frontpage-app-overlay {
|
||
display: none;
|
||
position: absolute;
|
||
inset: 0;
|
||
background: rgba(var(--bg-rgb), 0.45);
|
||
border-radius: 18px;
|
||
backdrop-filter: blur(8px) saturate(140%);
|
||
-webkit-backdrop-filter: blur(8px) saturate(140%);
|
||
flex-direction: column;
|
||
gap: 6px;
|
||
padding: 10px;
|
||
overflow-y: auto;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.frontpage-app-icon-wrap:hover .frontpage-app-overlay {
|
||
display: flex;
|
||
}
|
||
|
||
.frontpage-app-overlay a {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
padding: 6px 8px;
|
||
border-radius: 5px;
|
||
background: rgba(var(--status-success-rgb), 0.15);
|
||
border: 1px solid rgba(var(--status-success-rgb), 0.3);
|
||
color: var(--text-primary);
|
||
text-decoration: none;
|
||
font-size: 10px;
|
||
font-weight: 500;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
transition: background 0.15s ease;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.frontpage-app-overlay a:hover {
|
||
background: rgba(var(--status-success-rgb), 0.3);
|
||
}
|
||
|
||
.frontpage-app-overlay a svg {
|
||
flex-shrink: 0;
|
||
opacity: 0.8;
|
||
width: 11px;
|
||
height: 11px;
|
||
}
|
||
|
||
.frontpage-app-name {
|
||
display: none;
|
||
}
|
||
|
||
.frontpage-app-manage-btn {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 8px 12px;
|
||
border-radius: 6px;
|
||
background: var(--accent);
|
||
border: 1px solid var(--accent);
|
||
color: var(--text-primary);
|
||
text-decoration: none;
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
transition: background 0.15s ease;
|
||
flex-shrink: 0;
|
||
cursor: pointer;
|
||
/* Pin to the bottom of the overlay even when no service buttons are above it.
|
||
With justify-content: space-between, a single child sticks to the top —
|
||
margin-top: auto consumes the free space and pushes the manage button down. */
|
||
margin-top: auto;
|
||
}
|
||
|
||
.frontpage-app-manage-btn:hover {
|
||
background: var(--accent-hover);
|
||
border-color: var(--accent-hover);
|
||
}
|
||
|
||
.install-btn {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 10px 16px;
|
||
background: var(--status-success);
|
||
color: #ffffff;
|
||
border: 1px solid var(--status-success);
|
||
border-radius: 8px;
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.install-btn:hover {
|
||
background: var(--status-success-hover);
|
||
transform: translateY(-1px);
|
||
}
|
||
|
||
.stat-card {
|
||
background: var(--card-bg);
|
||
border: 1px solid rgba(var(--text-rgb), 0.15);
|
||
border-radius: 12px;
|
||
padding: 24px;
|
||
text-align: center;
|
||
transition: transform 0.2s ease;
|
||
min-height: 120px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
}
|
||
|
||
.stat-number {
|
||
font-size: 36px;
|
||
font-weight: bold;
|
||
color: var(--primary-color);
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.stat-label {
|
||
font-size: 14px;
|
||
color: var(--text-color);
|
||
opacity: 0.8;
|
||
}
|
||
|
||
.disk-chart {
|
||
position: relative;
|
||
display: inline-block;
|
||
}
|
||
|
||
/* Remove old disk chart styles that might conflict */
|
||
#disk-circle {
|
||
display: none; /* Hide old SVG circle */
|
||
}
|
||
|
||
.disk-circle-container {
|
||
width: 60px;
|
||
height: 60px;
|
||
border-radius: 50%;
|
||
background: rgba(var(--text-rgb), 0.1);
|
||
position: relative;
|
||
overflow: hidden;
|
||
border: 2px solid rgba(var(--text-rgb), 0.2);
|
||
}
|
||
|
||
.disk-circle-fill {
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
background: var(--status-success);
|
||
transition: height 0.5s ease-in-out;
|
||
border-radius: 0 0 50% 50%;
|
||
}
|
||
|
||
.disk-percentage {
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
font-size: 12px;
|
||
font-weight: bold;
|
||
color: var(--text-primary);
|
||
pointer-events: none;
|
||
z-index: 10;
|
||
}
|
||
|
||
.chart-label {
|
||
position: relative;
|
||
text-align: center;
|
||
margin-top: 10px;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.chart-text {
|
||
font-size: 10px;
|
||
color: var(--text-color);
|
||
opacity: 0.7;
|
||
margin-top: 2px;
|
||
}
|
||
|
||
.system-info-card {
|
||
text-align: left;
|
||
padding: 20px;
|
||
position: relative;
|
||
}
|
||
|
||
.system-details {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
}
|
||
|
||
.system-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
align-items: center;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.system-label {
|
||
color: var(--text-color);
|
||
opacity: 0.7;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.system-refresh-btn {
|
||
position: absolute;
|
||
top: 12px;
|
||
right: 12px;
|
||
background: rgba(var(--text-rgb), 0.1);
|
||
border: 1px solid rgba(var(--text-rgb), 0.15);
|
||
border-radius: 6px;
|
||
width: 32px;
|
||
height: 32px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: rgba(var(--text-rgb), 0.7);
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.system-refresh-btn:hover {
|
||
background: rgba(var(--text-rgb), 0.15);
|
||
color: rgba(var(--text-rgb), 0.9);
|
||
}
|
||
|
||
.system-refresh-btn svg {
|
||
width: 16px;
|
||
height: 16px;
|
||
stroke-width: 2;
|
||
}
|
||
|
||
.system-refresh-tooltip {
|
||
position: absolute;
|
||
top: 100%;
|
||
right: 0;
|
||
margin-top: 8px;
|
||
background: rgba(var(--bg-rgb), 0.8);
|
||
color: rgba(var(--text-rgb), 0.9);
|
||
padding: 6px 10px;
|
||
border-radius: 4px;
|
||
font-size: 11px;
|
||
white-space: nowrap;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transition: opacity 0.2s ease;
|
||
z-index: 10;
|
||
}
|
||
|
||
.system-refresh-btn:hover .system-refresh-tooltip {
|
||
opacity: 1;
|
||
}
|
||
|
||
/* Category descriptions */
|
||
.category-description {
|
||
font-size: 14px;
|
||
color: var(--text-secondary, #888);
|
||
margin: 8px 0 16px 0;
|
||
line-height: 1.4;
|
||
font-weight: 400;
|
||
}
|
||
|
||
.git-section-content {
|
||
transition: all 0.3s ease;
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
margin-top: 14px;
|
||
}
|
||
|
||
.git-section-content.hidden {
|
||
max-height: 0;
|
||
padding: 0;
|
||
margin: 0;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.info-card {
|
||
background: var(--card-bg);
|
||
border: 1px solid var(--border-color);
|
||
border-radius: 8px;
|
||
padding: 20px;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.info-card h5 {
|
||
margin: 0 0 12px 0;
|
||
color: var(--primary-color);
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.info-card p {
|
||
margin: 0;
|
||
color: var(--text-secondary);
|
||
line-height: 1.5;
|
||
}
|
||
|
||
/* Domain Building Blocks */
|
||
.domains-wrapper {
|
||
margin-bottom: 0px;
|
||
}
|
||
|
||
.domains-header {
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: flex-start;
|
||
align-items: flex-start;
|
||
gap: 12px;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.domains-header h3 {
|
||
margin: 0;
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
color: var(--text-primary, #fff);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
width: 100%;
|
||
}
|
||
|
||
.domains-divider {
|
||
width: 100%;
|
||
height: 1px;
|
||
background: rgba(var(--text-rgb), 0.08);
|
||
margin-bottom: 18px;
|
||
}
|
||
|
||
.add-domain-btn {
|
||
gap: 8px;
|
||
padding: 8px 20px;
|
||
margin-top: 12px;
|
||
margin-bottom: 12px;
|
||
min-width: 140px;
|
||
background: var(--primary-color);
|
||
color: var(--text-primary);
|
||
border: none;
|
||
border-radius: 6px;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
@media (max-width: 1600px) {
|
||
.domain-building-blocks {
|
||
grid-template-columns: repeat(2, 1fr);
|
||
}
|
||
}
|
||
|
||
@media (max-width: 800px) {
|
||
.domain-building-blocks {
|
||
grid-template-columns: 1fr;
|
||
gap: 16px;
|
||
}
|
||
}
|
||
|
||
.delete-domain-btn {
|
||
background: var(--status-danger);
|
||
color: #ffffff;
|
||
border: none;
|
||
border-radius: 4px;
|
||
padding: 4px 8px;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.delete-domain-btn:hover {
|
||
background: var(--status-danger-hover);
|
||
transform: scale(1.05);
|
||
}
|
||
|
||
.delete-domain-btn.disabled {
|
||
background: var(--text-muted);
|
||
color: #adb5bd;
|
||
cursor: not-allowed;
|
||
opacity: 0.6;
|
||
transform: none;
|
||
}
|
||
|
||
.delete-domain-btn.disabled:hover {
|
||
background: var(--text-muted);
|
||
transform: none;
|
||
}
|
||
|
||
.delete-icon {
|
||
font-size: 16px;
|
||
font-weight: bold;
|
||
line-height: 1;
|
||
}
|
||
|
||
/* Reusable spacer component */
|
||
.spacer {
|
||
display: block;
|
||
width: 100%;
|
||
}
|
||
.spacer-lg { height: 22px; }
|
||
|
||
/* Mail Configuration Master Toggle */
|
||
.mail-master-toggle {
|
||
margin-bottom: 0;
|
||
border-radius: 8px;
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
/* Generic Configuration Master Toggle */
|
||
.generic-master-toggle {
|
||
margin-bottom: 20px;
|
||
border-radius: 8px;
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.add-domain-btn {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
padding: 8px 20px;
|
||
min-width: 140px;
|
||
background: var(--status-success);
|
||
color: #ffffff;
|
||
border: none;
|
||
border-radius: 6px;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.add-domain-btn.disabled {
|
||
background: var(--text-muted);
|
||
cursor: not-allowed;
|
||
opacity: 0.7;
|
||
}
|
||
|
||
.add-domain-btn.disabled:hover {
|
||
background: var(--text-muted);
|
||
transform: none;
|
||
}
|
||
|
||
.add-domain-btn:hover {
|
||
background: var(--status-success-hover);
|
||
transform: translateY(-1px);
|
||
}
|
||
|
||
.add-icon {
|
||
font-size: 18px;
|
||
font-weight: bold;
|
||
}
|
||
|
||
/* Flash animation for empty domain warning */
|
||
@keyframes flash {
|
||
0%, 100% {
|
||
background-color: transparent;
|
||
border-color: var(--border-color);
|
||
}
|
||
25%, 75% {
|
||
background-color: rgba(var(--status-warning-rgb), 0.1);
|
||
border-color: var(--status-warning);
|
||
}
|
||
50% {
|
||
background-color: rgba(var(--status-warning-rgb), 0.2);
|
||
border-color: var(--status-warning);
|
||
}
|
||
}
|
||
|
||
/* Password input styling */
|
||
.password-input {
|
||
display: flex;
|
||
gap: 8px;
|
||
}
|
||
|
||
.password-input input {
|
||
flex: 1;
|
||
}
|
||
|
||
.password-toggle {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 32px;
|
||
height: 32px;
|
||
background: var(--hover-bg);
|
||
border: 1px solid var(--border-color);
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.password-toggle:hover {
|
||
background: var(--primary-color);
|
||
border-color: var(--primary-color);
|
||
}
|
||
|
||
.password-toggle:hover svg {
|
||
stroke: white;
|
||
}
|
||
|
||
.password-field-wrapper {
|
||
position: relative;
|
||
display: block;
|
||
}
|
||
|
||
.password-field-wrapper .password-field {
|
||
width: 100%;
|
||
padding-right: 40px;
|
||
}
|
||
|
||
.password-field-wrapper .password-toggle {
|
||
position: absolute;
|
||
top: 50%;
|
||
right: 6px;
|
||
transform: translateY(-50%);
|
||
width: 28px;
|
||
height: 28px;
|
||
background: transparent;
|
||
border: none;
|
||
padding: 0;
|
||
}
|
||
|
||
.password-field-wrapper .password-toggle:hover {
|
||
background: rgba(var(--text-rgb), 0.08);
|
||
border: none;
|
||
}
|
||
|
||
.password-toggle-icon {
|
||
font-size: 14px;
|
||
line-height: 1;
|
||
user-select: none;
|
||
color: var(--text-secondary, #a0a0a0);
|
||
}
|
||
|
||
/* Responsive design */
|
||
@media (max-width: 768px) {
|
||
.info-card {
|
||
padding: 16px;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.group-header {
|
||
padding: 12px 16px;
|
||
}
|
||
|
||
.group-fields {
|
||
padding: 16px;
|
||
}
|
||
|
||
.field-group {
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.group-fields .form-field {
|
||
margin-bottom: 16px;
|
||
}
|
||
}
|
||
|
||
.system-value {
|
||
color: var(--text-color);
|
||
font-weight: 600;
|
||
}
|
||
|
||
.install-btn {
|
||
background: var(--status-success);
|
||
border: 1px solid var(--status-success);
|
||
border-radius: 8px;
|
||
padding: 10px 16px;
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: var(--text-primary);
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
transition: all 0.2s ease;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.install-btn:hover {
|
||
background: var(--status-success-hover);
|
||
border-color: var(--status-success-hover);
|
||
transform: translateY(-1px);
|
||
}
|
||
|
||
.action-btn {
|
||
background: var(--card-bg);
|
||
border: 1px solid var(--border-color);
|
||
border-radius: 8px;
|
||
padding: 12px 20px;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
color: var(--text-color);
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.action-btn:hover {
|
||
background: var(--hover-bg);
|
||
transform: translateY(-1px);
|
||
}
|
||
|
||
.action-btn.primary {
|
||
background: var(--primary-color);
|
||
color: var(--text-primary);
|
||
border-color: var(--primary-color);
|
||
}
|
||
|
||
.action-btn.primary:hover {
|
||
background: var(--accent-hover);
|
||
border-color: var(--accent-hover);
|
||
}
|
||
|
||
.action-btn.secondary {
|
||
background: rgba(var(--text-rgb), 0.1);
|
||
border-color: rgba(var(--text-rgb), 0.2);
|
||
}
|
||
|
||
.action-btn.secondary:hover {
|
||
background: rgba(var(--text-rgb), 0.2);
|
||
}
|
||
|
||
.installed-apps {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||
gap: 20px;
|
||
/* Remove background - let main container show through */
|
||
}
|
||
|
||
.empty-state {
|
||
grid-column: 1 / -1;
|
||
text-align: center;
|
||
padding: 60px 20px;
|
||
color: var(--text-color);
|
||
}
|
||
|
||
.empty-state svg {
|
||
margin-bottom: 16px;
|
||
opacity: 0.5;
|
||
}
|
||
|
||
.empty-state h3 {
|
||
font-size: 20px;
|
||
font-weight: 600;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.empty-state p {
|
||
font-size: 16px;
|
||
opacity: 0.8;
|
||
}
|
||
|
||
.empty-state a {
|
||
color: var(--primary-color);
|
||
text-decoration: none;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.empty-state a:hover {
|
||
text-decoration: underline;
|
||
}
|
||
|
||
.status-indicator {
|
||
width: 8px;
|
||
height: 8px;
|
||
border-radius: 50%;
|
||
display: inline-block;
|
||
}
|
||
|
||
.status-indicator.running {
|
||
background: var(--status-success);
|
||
}
|
||
|
||
.status-indicator.stopped {
|
||
background: var(--status-danger);
|
||
}
|
||
|
||
.status-text {
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
/* Dashboard Mobile Responsive */
|
||
@media (max-width: 768px) {
|
||
.dashboard-stats {
|
||
grid-template-columns: repeat(2, 1fr);
|
||
}
|
||
|
||
.dashboard-actions {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.action-btn {
|
||
width: 100%;
|
||
justify-content: center;
|
||
}
|
||
|
||
.section-header {
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
gap: 12px;
|
||
}
|
||
|
||
.filter-controls {
|
||
width: 100%;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.search-input {
|
||
min-width: auto;
|
||
width: 100%;
|
||
}
|
||
|
||
.installed-apps {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
|
||
@media (max-width: 480px) {
|
||
.dashboard-stats {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.stat-number {
|
||
font-size: 24px;
|
||
}
|
||
}
|
||
|
||
/* Disk donut (frontpage) — apps / docker / other / free, % used in centre. */
|
||
.disk-stat-card {
|
||
cursor: pointer;
|
||
gap: 4px;
|
||
transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
|
||
}
|
||
.disk-stat-card:hover {
|
||
border-color: rgba(var(--accent-rgb), 0.4);
|
||
box-shadow: 0 6px 22px rgba(var(--accent-rgb), 0.10);
|
||
}
|
||
.disk-donut-wrap {
|
||
position: relative;
|
||
width: 104px;
|
||
height: 104px;
|
||
margin: 0 auto;
|
||
}
|
||
.disk-donut, .disk-donut-svg {
|
||
width: 104px;
|
||
height: 104px;
|
||
display: block;
|
||
}
|
||
.disk-stat-card .disk-percentage { font-size: 20px; }
|
||
.disk-legend {
|
||
display: grid;
|
||
grid-template-columns: auto 1fr auto;
|
||
gap: 3px 8px;
|
||
align-items: center;
|
||
max-width: 200px;
|
||
margin: 12px auto 0;
|
||
text-align: left;
|
||
font-size: 0.78rem;
|
||
}
|
||
.disk-leg-row { display: contents; }
|
||
.disk-leg-dot {
|
||
width: 10px;
|
||
height: 10px;
|
||
border-radius: 3px;
|
||
}
|
||
.disk-leg-dot.apps { background: var(--accent); }
|
||
.disk-leg-dot.docker { background: var(--status-info); }
|
||
.disk-leg-dot.other { background: rgba(var(--text-rgb), 0.35); }
|
||
.disk-leg-dot.free { background: rgba(var(--text-rgb), 0.18); }
|
||
.disk-leg-k { color: rgba(var(--text-rgb), 0.7); }
|
||
.disk-leg-v {
|
||
color: var(--text-primary);
|
||
font-variant-numeric: tabular-nums;
|
||
text-align: right;
|
||
}
|
||
|
||
/* New disk circle chart styles */
|
||
.disk-chart {
|
||
position: relative;
|
||
display: inline-block;
|
||
vertical-align: middle;
|
||
}
|
||
|
||
.disk-circle-container {
|
||
width: 60px;
|
||
height: 60px;
|
||
border-radius: 50%;
|
||
background: rgba(var(--text-rgb), 0.1);
|
||
position: relative;
|
||
overflow: hidden;
|
||
border: 2px solid rgba(var(--text-rgb), 0.2);
|
||
margin: 0 auto; /* Center horizontally */
|
||
}
|
||
|
||
.disk-circle-fill {
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
background: var(--status-success);
|
||
transition: height 0.5s ease-in-out;
|
||
border-radius: 0 0 50% 50%; /* Rounded bottom only */
|
||
}
|
||
|
||
.disk-percentage {
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
font-size: 12px;
|
||
font-weight: bold;
|
||
color: var(--text-primary);
|
||
pointer-events: none; /* Prevent mouse interactions */
|
||
z-index: 10; /* Ensure it's on top */
|
||
}
|
||
|
||
/* LibrePortal logo styles */
|
||
.libreportal-logo {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-right: 15px;
|
||
padding: 4px;
|
||
border-radius: 6px;
|
||
background: rgba(var(--text-rgb), 0.1);
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.libreportal-logo:hover {
|
||
background: rgba(var(--text-rgb), 0.2);
|
||
transform: scale(1.05);
|
||
}
|
||
|
||
.libreportal-logo img {
|
||
width: 32px;
|
||
height: 32px;
|
||
}
|
||
|
||
/* Notification container positioning - ensure bottom-right */
|
||
.notification-container {
|
||
position: fixed !important;
|
||
bottom: 20px !important;
|
||
right: 20px !important;
|
||
top: auto !important;
|
||
left: auto !important;
|
||
z-index: 10000 !important;
|
||
pointer-events: none;
|
||
display: flex;
|
||
flex-direction: column-reverse;
|
||
gap: 10px;
|
||
}
|
||
|
||
.notification {
|
||
background: var(--card-bg, #2d3748);
|
||
border: 1px solid var(--border-color, #4a5568);
|
||
border-radius: 8px;
|
||
padding: 16px;
|
||
width: auto;
|
||
min-width: 350px;
|
||
max-width: 700px;
|
||
pointer-events: all;
|
||
transform: translateY(100%);
|
||
opacity: 0;
|
||
transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||
}
|
||
|
||
/* Dynamic width based on content */
|
||
.notification[data-has-app="true"] {
|
||
min-width: 450px;
|
||
max-width: 650px;
|
||
}
|
||
|
||
.notification[data-has-action="true"] {
|
||
min-width: 500px;
|
||
max-width: 700px;
|
||
}
|
||
|
||
.notification[data-has-app="true"][data-has-action="true"] {
|
||
min-width: 550px;
|
||
max-width: 750px;
|
||
}
|
||
|
||
.notification-show {
|
||
transform: translateY(0);
|
||
opacity: 1;
|
||
}
|
||
|
||
.notification-hide {
|
||
transform: translateY(100%);
|
||
opacity: 0;
|
||
}
|
||
|
||
.notification-content {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 12px;
|
||
}
|
||
|
||
.notification-app-icon {
|
||
flex-shrink: 0;
|
||
margin-right: 12px;
|
||
width: 36px;
|
||
height: 36px;
|
||
border-radius: 6px;
|
||
overflow: hidden;
|
||
background: rgba(var(--text-rgb), 0.15);
|
||
border: 1px solid rgba(var(--text-rgb), 0.25);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.notification-app-icon img {
|
||
width: 28px;
|
||
height: 28px;
|
||
object-fit: contain;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.notification-icon {
|
||
flex-shrink: 0;
|
||
/* Container uses align-items: flex-start so the message stays top-aligned
|
||
for multi-line text. Override here so the status icon (20px) sits
|
||
vertically centered against the 36px app icon next to it. */
|
||
align-self: center;
|
||
}
|
||
|
||
.notification-icon svg {
|
||
display: block;
|
||
}
|
||
|
||
.notification-message {
|
||
flex: 1;
|
||
color: var(--text-color, #e2e8f0);
|
||
font-size: 14px;
|
||
line-height: 1.4;
|
||
margin-top: 2px;
|
||
}
|
||
|
||
.notification-action-btn {
|
||
background: var(--primary-color, var(--accent));
|
||
color: #ffffff;
|
||
border: none;
|
||
padding: 8px 16px;
|
||
border-radius: 6px;
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
flex-shrink: 0;
|
||
margin-left: 12px;
|
||
/* Container is `align-items: flex-start` so multi-line message text stays
|
||
anchored at the top; override here so the button lines up with the
|
||
status / app icons next to it. */
|
||
align-self: center;
|
||
}
|
||
|
||
.notification-action-btn:hover {
|
||
background: var(--primary-hover, #3182ce);
|
||
transform: translateY(-1px);
|
||
}
|
||
|
||
.notification-close {
|
||
background: none;
|
||
border: none;
|
||
color: var(--text-muted, #a0aec0);
|
||
cursor: pointer;
|
||
padding: 4px;
|
||
border-radius: 4px;
|
||
transition: all 0.2s ease;
|
||
flex-shrink: 0;
|
||
/* Same reasoning as `.notification-action-btn` above — center against the
|
||
icons rather than top-aligning with the message. */
|
||
align-self: center;
|
||
}
|
||
|
||
.notification-close:hover {
|
||
background: rgba(var(--text-rgb), 0.1);
|
||
color: var(--text-color, #e2e8f0);
|
||
}
|
||
|
||
.notification-success .notification-icon {
|
||
color: #48bb78;
|
||
}
|
||
|
||
.notification-error .notification-icon {
|
||
color: var(--status-danger);
|
||
}
|
||
|
||
.notification-warning .notification-icon {
|
||
color: var(--status-warning);
|
||
}
|
||
|
||
.notification-info .notification-icon {
|
||
color: var(--accent);
|
||
}
|
||
|
||
.notification-uninstall .notification-icon {
|
||
color: var(--status-danger);
|
||
}
|
||
|
||
/* Mobile positioning */
|
||
@media (max-width: 768px) {
|
||
.notification-container {
|
||
bottom: 10px !important;
|
||
right: 10px !important;
|
||
left: 10px !important;
|
||
}
|
||
}
|
||
|
||
/* Tablet positioning */
|
||
@media (max-width: 1024px) and (min-width: 769px) {
|
||
.notification-container {
|
||
bottom: 15px !important;
|
||
right: 15px !important;
|
||
top: auto !important;
|
||
}
|
||
}
|
||
|
||
.notification-container .notification {
|
||
pointer-events: auto;
|
||
}
|
||
|
||
/* Hide all content (icons, text, etc.) for install, manage, and uninstall buttons when loading */
|
||
.btn-loading.btn-install,
|
||
.btn-loading.btn-manage,
|
||
.btn-loading.btn-uninstall,
|
||
.btn-loading.manage-btn {
|
||
color: transparent !important;
|
||
}
|
||
|
||
.btn-loading.btn-install *,
|
||
.btn-loading.btn-manage *,
|
||
.btn-loading.btn-uninstall *,
|
||
.btn-loading.manage-btn * {
|
||
opacity: 0 !important;
|
||
visibility: hidden !important;
|
||
}
|
||
|
||
/* Show only spinner for install, manage, and uninstall buttons */
|
||
.btn-loading.btn-install::after,
|
||
.btn-loading.btn-manage::after,
|
||
.btn-loading.btn-uninstall::after,
|
||
.btn-loading.manage-btn::after,
|
||
.app-card-actions .btn-loading.manage-btn::after {
|
||
content: '';
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
width: 16px;
|
||
height: 16px;
|
||
margin: -8px 0 0 -8px;
|
||
border: 2px solid transparent;
|
||
border-top: 2px solid currentColor;
|
||
border-radius: 50%;
|
||
animation: spin 1s linear infinite;
|
||
color: var(--text-primary) !important;
|
||
opacity: 1 !important;
|
||
visibility: visible !important;
|
||
}
|
||
|
||
/* Loading text for non-install buttons */
|
||
.btn-loading:not(.btn-install):not(.btn-manage):not(.btn-uninstall):not(.manage-btn)::before {
|
||
content: attr(data-loading-text);
|
||
position: absolute;
|
||
top: 50%;
|
||
left: 50%;
|
||
transform: translate(-50%, -50%);
|
||
color: inherit;
|
||
font-size: inherit;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.loading-initial .loading-spinner {
|
||
width: 60px;
|
||
height: 60px;
|
||
border: 4px solid rgba(var(--text-rgb), 0.1);
|
||
border-top: 4px solid var(--primary-color, var(--accent));
|
||
border-radius: 50%;
|
||
animation: spin 1s linear infinite;
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
.loading-initial .loading-subtitle {
|
||
font-size: 14px;
|
||
color: var(--text-secondary, #ccc);
|
||
font-weight: normal;
|
||
margin-top: 8px;
|
||
opacity: 0.8;
|
||
}
|
||
|
||
/* Loading spinner styles */
|
||
.loading-categories .loading-spinner,
|
||
.loading-apps .loading-spinner {
|
||
width: 20px;
|
||
height: 20px;
|
||
border: 2px solid #e3e3e3;
|
||
border-top: 2px solid var(--accent);
|
||
border-radius: 50%;
|
||
animation: spin 1s linear infinite;
|
||
margin: 0 auto 10px;
|
||
}
|
||
|
||
/* Loading containers */
|
||
.loading-categories,
|
||
.loading-apps {
|
||
text-align: center;
|
||
padding: 20px;
|
||
color: var(--text-color, #666);
|
||
}
|
||
|
||
.loading-categories p,
|
||
.loading-apps p {
|
||
margin: 0;
|
||
font-size: 14px;
|
||
}
|
||
|
||
/* Update needed and warning styles */
|
||
.warning-banner {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 12px 16px;
|
||
margin-bottom: 20px;
|
||
background: #fff3cd;
|
||
border: 1px solid #ffeaa7;
|
||
border-radius: 6px;
|
||
color: #856404;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.warning-banner svg {
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.warning-banner span {
|
||
flex: 1;
|
||
}
|
||
|
||
.warning-banner .btn-small {
|
||
flex-shrink: 0;
|
||
padding: 4px 12px;
|
||
font-size: 12px;
|
||
background: var(--status-warning);
|
||
color: #212529;
|
||
border: 1px solid var(--status-warning);
|
||
}
|
||
|
||
.warning-banner .btn-small:hover {
|
||
background: #e0a800;
|
||
border-color: #e0a800;
|
||
}
|
||
|
||
.btn-copy.copied {
|
||
background: var(--status-success);
|
||
border-color: var(--status-success);
|
||
}
|
||
|
||
.toggle-content {
|
||
display: flex;
|
||
flex-direction: column;
|
||
margin-left: 12px;
|
||
flex: 1;
|
||
}
|
||
|
||
.toggle-section input[type="checkbox"] {
|
||
display: none;
|
||
}
|
||
|
||
/* Section Dividers */
|
||
.section-divider {
|
||
margin: 32px 0 24px 0;
|
||
padding: 16px 0;
|
||
border-bottom: 2px solid var(--border-color);
|
||
}
|
||
|
||
.section-divider h3 {
|
||
margin: 0 0 8px 0;
|
||
color: var(--text-primary);
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.section-divider p {
|
||
margin: 0;
|
||
color: var(--text-secondary);
|
||
font-size: 14px;
|
||
font-style: italic;
|
||
}
|
||
|
||
/* Advanced and Unused Sections */
|
||
.advanced-sections,
|
||
.unused-sections {
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
.advanced-section {
|
||
border-left: 4px solid #f39c12;
|
||
padding-left: 16px;
|
||
padding-right: 16px;
|
||
padding-top: 22px;
|
||
}
|
||
|
||
.unused-section {
|
||
border-left: 4px solid #e74c3c;
|
||
padding-left: 16px;
|
||
padding-right: 16px;
|
||
opacity: 0.8;
|
||
}
|
||
|
||
.advanced-section h3,
|
||
.unused-section h3 {
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
/* Mail Connection Test Button */
|
||
.test-connection-btn {
|
||
background: var(--accent);
|
||
color: #ffffff;
|
||
border: 1px solid var(--accent);
|
||
padding: 8px 16px;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
font-size: 14px;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.test-connection-btn:hover:not(:disabled) {
|
||
background: var(--accent-hover);
|
||
border-color: var(--accent-hover);
|
||
}
|
||
|
||
.test-connection-btn:disabled {
|
||
background: var(--text-muted);
|
||
border-color: var(--text-muted);
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.test-icon {
|
||
font-size: 16px;
|
||
}
|
||
|
||
.test-text {
|
||
font-weight: 500;
|
||
}
|
||
|
||
/* Test Result Display */
|
||
.test-result {
|
||
margin-top: 8px;
|
||
padding: 8px 12px;
|
||
border-radius: 4px;
|
||
font-size: 13px;
|
||
font-weight: 500;
|
||
display: block;
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.test-result.testing {
|
||
background: #fff3cd;
|
||
border: 1px solid #ffeaa7;
|
||
color: #856404;
|
||
}
|
||
|
||
.test-result.success {
|
||
background: #d4edda;
|
||
border: 1px solid #c3e6cb;
|
||
color: #155724;
|
||
}
|
||
|
||
.test-result.error {
|
||
background: #f8d7da;
|
||
border: 1px solid #f5c6cb;
|
||
color: #721c24;
|
||
}
|
||
|
||
/* ==============================================
|
||
TABBED INTERFACE STYLES
|
||
============================================== */
|
||
|
||
/* Tabbed Interface Container */
|
||
.tabbed-interface {
|
||
margin-top: 20px;
|
||
}
|
||
|
||
/* Tab Navigation */
|
||
.tab-navigation {
|
||
display: flex;
|
||
border-bottom: 1px solid rgba(var(--text-rgb), 0.10);
|
||
background: rgba(var(--text-rgb), 0.04);
|
||
backdrop-filter: blur(10px);
|
||
-webkit-backdrop-filter: blur(10px);
|
||
border-radius: 12px 12px 0px 0px;
|
||
}
|
||
|
||
.tab-button {
|
||
background: transparent;
|
||
border: none;
|
||
padding: 12px 16px;
|
||
cursor: pointer;
|
||
border-radius: 8px 8px 0px 0px;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
color: rgba(var(--text-rgb), 0.7);
|
||
transition: all 0.3s ease;
|
||
border-bottom: 2px solid transparent;
|
||
}
|
||
|
||
.tab-button:hover {
|
||
color: var(--accent);
|
||
background: rgba(var(--accent-rgb), 0.1);
|
||
}
|
||
|
||
.tab-button.active {
|
||
color: var(--accent);
|
||
border-bottom-color: var(--accent);
|
||
background: rgba(var(--accent-rgb), 0.1);
|
||
}
|
||
|
||
/* Flatten the .tab-button default 8px-on-both-top-corners inside any
|
||
tab container — we want only the FIRST tab's top-left and LAST
|
||
tab's top-right to follow the strip's curve. Without the reset,
|
||
the inner corner of the active tab still picks up the default
|
||
8px and looks rounded against an unrounded sibling. */
|
||
.tab-navigation > .tab-button,
|
||
.tabs-wrapper .tabs-list .tab-button {
|
||
border-radius: 0;
|
||
}
|
||
|
||
/* When the first / last tab is the active one, its filled background
|
||
should follow the parent strip's 12px top-corner radius. Without
|
||
this, the active tab squares off in the corner of the strip,
|
||
leaving an L-shaped notch against the rounded chrome. */
|
||
.tab-navigation > .tab-button:first-child,
|
||
.tab-navigation > .main-tab-button:first-child,
|
||
.tabs-wrapper .tabs-list .tab-button:first-child {
|
||
border-top-left-radius: 12px;
|
||
}
|
||
|
||
.tab-navigation > .tab-button:last-child,
|
||
.tab-navigation > .main-tab-button:last-child,
|
||
.tabs-wrapper .tabs-list .tab-button:last-child {
|
||
border-top-right-radius: 12px;
|
||
}
|
||
|
||
.main-tab-button {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 6px;
|
||
padding: 12px 20px;
|
||
background: transparent;
|
||
border: none;
|
||
cursor: pointer;
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
color: var(--text-secondary, #ccc);
|
||
transition: all 0.2s ease;
|
||
white-space: nowrap;
|
||
border-bottom: 2px solid transparent;
|
||
flex: 1;
|
||
}
|
||
|
||
.main-tab-button:hover {
|
||
color: var(--accent);
|
||
background: rgba(var(--accent-rgb), 0.1);
|
||
}
|
||
|
||
.main-tab-button.active {
|
||
color: var(--accent);
|
||
border-bottom-color: var(--accent);
|
||
background: rgba(var(--accent-rgb), 0.1);
|
||
}
|
||
|
||
.tab-button svg {
|
||
width: 16px;
|
||
height: 16px;
|
||
}
|
||
|
||
/* Tab Content */
|
||
.tab-content {
|
||
min-height: 400px;
|
||
}
|
||
|
||
.tab-pane {
|
||
display: none;
|
||
padding: 20px;
|
||
background: rgba(var(--text-rgb), 0.04);
|
||
border: 1px solid rgba(var(--text-rgb), 0.10);
|
||
border-top: none;
|
||
border-radius: 0px 0px 12px 12px;
|
||
box-shadow: inset 0 1px 0 rgba(var(--text-rgb), 0.04);
|
||
}
|
||
|
||
.tab-pane.active {
|
||
display: block;
|
||
}
|
||
|
||
.backups-section h3 {
|
||
color: var(--accent);
|
||
margin-bottom: 15px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.backups-section p {
|
||
color: var(--text-muted);
|
||
font-style: italic;
|
||
}
|
||
|
||
/* Padding stays at 0 so .tasks-title's own 20px provides the inset —
|
||
same recipe as .services-section / .config-section. */
|
||
.tasks-section {
|
||
display: flex;
|
||
flex-direction: column;
|
||
padding: 0;
|
||
}
|
||
|
||
.tasks-container {
|
||
/* Make app tasks look like main tasks page */
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
padding: 16px;
|
||
margin: 16px;
|
||
background: rgba(var(--bg-rgb), 0.2);
|
||
border-radius: 8px;
|
||
}
|
||
|
||
/* Hide scrollbar when not needed, show only when scrolling */
|
||
.tasks-container::-webkit-scrollbar {
|
||
width: 8px;
|
||
}
|
||
|
||
.tasks-container::-webkit-scrollbar-track {
|
||
background: transparent;
|
||
}
|
||
|
||
.tasks-container::-webkit-scrollbar-thumb {
|
||
background: var(--input-bg);
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.tasks-container::-webkit-scrollbar-thumb:hover {
|
||
background: var(--text-secondary);
|
||
}
|
||
|
||
/* ==============================================
|
||
TASK ITEM AND DETAILS STYLING
|
||
============================================== */
|
||
|
||
.task-item {
|
||
background: rgba(var(--text-rgb), 0.05);
|
||
border: 1px solid rgba(var(--text-rgb), 0.1);
|
||
border-radius: 8px;
|
||
margin-bottom: 12px;
|
||
overflow: hidden;
|
||
transition: all 0.2s ease;
|
||
padding: 0px;
|
||
}
|
||
|
||
.task-item:hover {
|
||
background: rgba(var(--text-rgb), 0.08);
|
||
border-color: rgba(var(--text-rgb), 0.15);
|
||
transform: translateY(-1px);
|
||
}
|
||
|
||
.task-details {
|
||
display: none;
|
||
background: rgba(var(--bg-rgb), 0.2);
|
||
border-top: 1px solid rgba(var(--text-rgb), 0.1);
|
||
padding: 16px;
|
||
}
|
||
|
||
.task-details.task-details-open {
|
||
display: block;
|
||
}
|
||
|
||
.task-meta {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||
gap: 12px;
|
||
margin-bottom: 16px;
|
||
padding-bottom: 12px;
|
||
border-bottom: 1px solid rgba(var(--text-rgb), 0.1);
|
||
}
|
||
|
||
.meta-item {
|
||
font-size: 12px;
|
||
color: var(--text-muted);
|
||
}
|
||
|
||
.meta-item strong {
|
||
color: var(--text-primary);
|
||
font-weight: 500;
|
||
}
|
||
|
||
.task-id-link,
|
||
.task-app-link {
|
||
color: inherit;
|
||
text-decoration: none;
|
||
}
|
||
|
||
.task-id-link:hover,
|
||
.task-app-link:hover {
|
||
color: var(--accent);
|
||
text-decoration: underline;
|
||
}
|
||
|
||
.task-logs h4,
|
||
.task-output h4,
|
||
.task-error h4,
|
||
.task-running h4 {
|
||
color: var(--text-primary);
|
||
font-size: 14px;
|
||
margin: 16px 0 8px 0;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.task-output .output-content,
|
||
.task-error .error-content {
|
||
background: rgba(var(--text-rgb), 0.04);
|
||
border: 1px solid rgba(var(--text-rgb), 0.10);
|
||
box-shadow: inset 0 1px 0 rgba(var(--text-rgb), 0.04);
|
||
border-radius: 10px;
|
||
padding: 12px;
|
||
font-family: 'Courier New', monospace;
|
||
font-size: 12px;
|
||
line-height: 1.4;
|
||
white-space: pre-wrap;
|
||
overflow-x: auto;
|
||
}
|
||
|
||
.task-error .error-content {
|
||
color: var(--status-danger);
|
||
border-color: rgba(var(--status-danger-rgb), 0.3);
|
||
}
|
||
|
||
.task-running .running-indicator {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
color: var(--status-info);
|
||
font-style: italic;
|
||
}
|
||
|
||
.task-running .spinner {
|
||
width: 16px;
|
||
height: 16px;
|
||
border: 2px solid rgba(23, 162, 184, 0.3);
|
||
border-top: 2px solid var(--status-info);
|
||
border-radius: 50%;
|
||
animation: spin 1s linear infinite;
|
||
}
|
||
|
||
/* ==============================================
|
||
TASK HEADER ENHANCEMENTS
|
||
============================================== */
|
||
|
||
.task-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
width: 100%;
|
||
padding: 12px;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.task-info {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
flex: 1;
|
||
}
|
||
|
||
.task-app-icon {
|
||
width: 32px;
|
||
height: 32px;
|
||
border-radius: 8px;
|
||
object-fit: cover;
|
||
border: 1px solid #f0f0f03d;
|
||
background: #f8f9fa24;
|
||
padding: 3px;
|
||
}
|
||
|
||
.task-type-icon {
|
||
font-size: 16px;
|
||
margin-left: 6px;
|
||
margin-right: 6px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.task-title {
|
||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||
font-weight: 400;
|
||
color: #c5c8ca;
|
||
font-size: 15px;
|
||
line-height: 1.2;
|
||
}
|
||
|
||
.task-status {
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
padding: 4px 8px;
|
||
border-radius: 4px;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
text-transform: uppercase !important;
|
||
}
|
||
|
||
.status-queued {
|
||
background: rgba(255, 189, 46, 0.2);
|
||
color: #ffbd2e;
|
||
border: 1px solid rgba(255, 189, 46, 0.3);
|
||
text-transform: uppercase !important;
|
||
}
|
||
|
||
.status-running {
|
||
background: rgba(40, 202, 66, 0.2);
|
||
color: #28ca42;
|
||
border: 1px solid rgba(40, 202, 66, 0.3);
|
||
text-transform: uppercase !important;
|
||
}
|
||
|
||
.status-completed {
|
||
background: rgba(0, 255, 0, 0.2);
|
||
color: #00ff00;
|
||
border: 1px solid rgba(0, 255, 0, 0.3);
|
||
text-transform: uppercase !important;
|
||
}
|
||
|
||
.status-failed {
|
||
background: rgba(var(--status-danger-rgb), 0.2);
|
||
color: var(--status-danger);
|
||
border: 1px solid rgba(var(--status-danger-rgb), 0.3);
|
||
text-transform: uppercase !important;
|
||
}
|
||
|
||
.status-cancelled {
|
||
background: rgba(var(--text-rgb), 0.2);
|
||
color: var(--text-muted);
|
||
border: 1px solid rgba(var(--text-rgb), 0.3);
|
||
text-transform: uppercase !important;
|
||
}
|
||
|
||
.task-time {
|
||
font-size: 11px;
|
||
color: var(--text-muted);
|
||
margin-left: auto;
|
||
margin-right: 8px;
|
||
}
|
||
|
||
.task-duration {
|
||
font-size: 11px;
|
||
color: var(--text-muted);
|
||
margin-right: 8px;
|
||
}
|
||
|
||
.task-title,
|
||
.task-time,
|
||
.task-duration {
|
||
color: var(--text-muted);
|
||
}
|
||
|
||
.task-app-icon {
|
||
border-color: var(--border);
|
||
background: var(--surface-elevated);
|
||
}
|
||
|
||
/* Mirrors .config-title — see config.css. */
|
||
.tasks-title {
|
||
padding: 20px;
|
||
background: transparent;
|
||
border-bottom: 1px solid var(--border-color);
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.tasks-title h3 {
|
||
margin: 0 0 8px 0;
|
||
color: var(--text-primary, #fff);
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.tasks-title p {
|
||
margin: 0;
|
||
color: var(--text-secondary, #ccc);
|
||
font-size: 13px;
|
||
}
|
||
|
||
/* ==============================================
|
||
TASK ACTIONS STYLING
|
||
============================================== */
|
||
|
||
.task-actions {
|
||
display: flex;
|
||
gap: 12px;
|
||
align-items: center;
|
||
}
|
||
|
||
.task-btn {
|
||
background: rgba(var(--text-rgb), 0.1);
|
||
border: 1px solid rgba(var(--text-rgb), 0.2);
|
||
color: var(--text-muted);
|
||
padding: 6px 10px;
|
||
border-radius: 6px;
|
||
font-size: 11px;
|
||
cursor: pointer;
|
||
transition: all 0.2s ease;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
}
|
||
|
||
.task-btn:hover {
|
||
background: rgba(var(--text-rgb), 0.2);
|
||
border-color: rgba(var(--text-rgb), 0.3);
|
||
transform: translateY(-1px);
|
||
}
|
||
|
||
.task-btn.retry {
|
||
background: rgba(var(--status-danger-rgb), 0.1);
|
||
border-color: rgba(var(--status-danger-rgb), 0.2);
|
||
color: var(--status-danger);
|
||
}
|
||
|
||
.task-btn.retry:hover {
|
||
background: rgba(var(--status-danger-rgb), 0.2);
|
||
border-color: rgba(var(--status-danger-rgb), 0.3);
|
||
}
|
||
|
||
.task-btn.view-logs {
|
||
background: rgba(var(--status-info-rgb), 0.12);
|
||
border-color: rgba(var(--status-info-rgb), 0.30);
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
.task-btn.view-logs:hover {
|
||
background: rgba(var(--status-info-rgb), 0.22);
|
||
border-color: rgba(var(--status-info-rgb), 0.50);
|
||
}
|
||
|
||
.task-btn.toggle-details {
|
||
background: rgba(var(--text-rgb), 0.06);
|
||
border-color: rgba(var(--text-rgb), 0.18);
|
||
color: var(--text-secondary);
|
||
}
|
||
|
||
.task-btn.toggle-details:hover {
|
||
background: rgba(var(--text-rgb), 0.12);
|
||
border-color: rgba(var(--text-rgb), 0.32);
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
.task-btn.toggle-details.expanded {
|
||
background: rgba(var(--text-rgb), 0.14);
|
||
border-color: rgba(var(--text-rgb), 0.36);
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
.task-btn.delete {
|
||
background: rgba(var(--status-danger-rgb), 0.14);
|
||
border-color: rgba(var(--status-danger-rgb), 0.40);
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
.task-btn.delete:hover {
|
||
background: rgba(var(--status-danger-rgb), 0.28);
|
||
border-color: rgba(var(--status-danger-rgb), 0.65);
|
||
color: var(--text-primary);
|
||
}
|
||
|
||
/* Text label sitting next to the SVG inside any .task-btn (Restart,
|
||
Logs, Delete, etc.). Buttons that only carry an icon don't include
|
||
the span, so they stay icon-only. */
|
||
.task-btn .task-btn-label {
|
||
margin-left: 4px;
|
||
font-size: 11px;
|
||
font-weight: 500;
|
||
line-height: 1;
|
||
}
|
||
|
||
.task-btn:has(.task-btn-label) {
|
||
padding-right: 12px;
|
||
}
|
||
|
||
/* ==============================================
|
||
TERMINAL STYLING FOR TASK LOGS
|
||
============================================== */
|
||
|
||
.terminal-style {
|
||
background: rgba(var(--text-rgb), 0.04);
|
||
border: 1px solid rgba(var(--text-rgb), 0.10);
|
||
box-shadow: inset 0 1px 0 rgba(var(--text-rgb), 0.04);
|
||
color: var(--text-primary);
|
||
font-family: 'Courier New', 'Monaco', 'Menlo', 'Consolas', monospace;
|
||
font-size: 15px;
|
||
line-height: 1.3;
|
||
border-radius: 10px;
|
||
padding: 16px;
|
||
overflow: auto;
|
||
white-space: pre;
|
||
word-wrap: normal;
|
||
overflow-wrap: normal;
|
||
/* Browsers only honour `resize` when overflow != visible. Vertical
|
||
only — drag the bottom handle down to extend the log viewport. */
|
||
resize: vertical;
|
||
min-height: 120px;
|
||
max-height: none;
|
||
}
|
||
|
||
.terminal-style .log-line {
|
||
margin: 0;
|
||
padding: 0;
|
||
line-height: 1.3;
|
||
height: auto;
|
||
margin-bottom: 4px !important;
|
||
}
|
||
|
||
.terminal-style .log-entry {
|
||
margin: 0;
|
||
padding: 0;
|
||
line-height: 1.3;
|
||
height: auto;
|
||
margin-bottom: 4px !important;
|
||
}
|
||
|
||
.terminal-style div {
|
||
margin: 0;
|
||
padding: 0;
|
||
line-height: 1.3;
|
||
height: auto;
|
||
}
|
||
|
||
/* Force remove any default spacing but allow gap */
|
||
.terminal-style * {
|
||
margin: 0;
|
||
padding: 0;
|
||
line-height: 1.3;
|
||
}
|
||
|
||
.terminal-style .log-line,
|
||
.terminal-style .log-entry {
|
||
margin-bottom: 4px !important;
|
||
}
|
||
|
||
/* ANSI color styles for terminal */
|
||
.terminal-style span[style*="color: green"] {
|
||
color: #00ff00 !important;
|
||
}
|
||
|
||
.terminal-style span[style*="color: red"] {
|
||
color: var(--status-danger) !important;
|
||
}
|
||
|
||
.terminal-style span[style*="color: yellow"] {
|
||
color: #ffd93d !important;
|
||
}
|
||
|
||
.terminal-style span[style*="color: blue"] {
|
||
color: #6bb6ff !important;
|
||
}
|
||
|
||
.terminal-style span[style*="color: cyan"] {
|
||
color: #4ecdc4 !important;
|
||
}
|
||
|
||
.terminal-style span[style*="color: magenta"] {
|
||
color: #ff6ec7 !important;
|
||
}
|
||
|
||
.terminal-style span[style*="color: white"] {
|
||
color: var(--text-primary) !important;
|
||
}
|
||
|
||
.terminal-style span[style*="color: black"] {
|
||
color: #000000 !important;
|
||
}
|
||
|
||
.terminal-style span[style*="background-color: black"] {
|
||
background-color: #000000 !important;
|
||
padding: 0 2px;
|
||
}
|
||
|
||
.terminal-style span[style*="background-color: red"] {
|
||
background-color: #ff0000 !important;
|
||
padding: 0 2px;
|
||
}
|
||
|
||
.terminal-style span[style*="background-color: green"] {
|
||
background-color: #00ff00 !important;
|
||
padding: 0 2px;
|
||
}
|
||
|
||
.terminal-style span[style*="background-color: yellow"] {
|
||
background-color: #ffff00 !important;
|
||
padding: 0 2px;
|
||
}
|
||
|
||
.terminal-style span[style*="background-color: blue"] {
|
||
background-color: #0000ff !important;
|
||
padding: 0 2px;
|
||
}
|
||
|
||
.terminal-style span[style*="background-color: cyan"] {
|
||
background-color: #00ffff !important;
|
||
padding: 0 2px;
|
||
}
|
||
|
||
.terminal-style span[style*="background-color: magenta"] {
|
||
background-color: #ff00ff !important;
|
||
padding: 0 2px;
|
||
}
|
||
|
||
.terminal-style span[style*="background-color: white"] {
|
||
background-color: var(--text-primary) !important;
|
||
color: #000000 !important;
|
||
padding: 0 2px;
|
||
}
|
||
|
||
/* Modal log viewer enhancements */
|
||
.task-logs-modal .log-viewer.terminal-style {
|
||
max-height: 400px;
|
||
overflow-y: auto;
|
||
overflow-x: auto;
|
||
border: 1px solid var(--border-strong);
|
||
white-space: pre;
|
||
word-wrap: normal;
|
||
overflow-wrap: normal;
|
||
}
|
||
|
||
.task-logs-modal .log-viewer.terminal-style::-webkit-scrollbar {
|
||
width: 8px;
|
||
}
|
||
|
||
.task-logs-modal .log-viewer.terminal-style::-webkit-scrollbar-track {
|
||
background: var(--surface-elevated);
|
||
}
|
||
|
||
.task-logs-modal .log-viewer.terminal-style::-webkit-scrollbar-thumb {
|
||
background: var(--text-secondary);
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.task-logs-modal .log-viewer.terminal-style::-webkit-scrollbar-thumb:hover {
|
||
background: #777;
|
||
}
|
||
|
||
/* Task preview log container styling — initial height is set inline
|
||
(200px) so the user lands on a familiar size, but `resize: vertical`
|
||
from .terminal-style lets them drag the bottom handle down to grow
|
||
it. We deliberately don't set a max-height here. */
|
||
.task-logs .log-container.terminal-style {
|
||
overflow-y: auto;
|
||
border: 1px solid var(--border);
|
||
margin: 8px 0;
|
||
}
|
||
|
||
.task-logs .log-container.terminal-style::-webkit-scrollbar {
|
||
width: 6px;
|
||
}
|
||
|
||
.task-logs .log-container.terminal-style::-webkit-scrollbar-track {
|
||
background: var(--surface-elevated);
|
||
}
|
||
|
||
.task-logs .log-container.terminal-style::-webkit-scrollbar-thumb {
|
||
background: var(--text-secondary);
|
||
border-radius: 3px;
|
||
}
|
||
|
||
.task-logs .log-container.terminal-style::-webkit-scrollbar-thumb:hover {
|
||
background: #777;
|
||
}
|
||
|
||
.task-logs .log-entry {
|
||
margin-bottom: 0;
|
||
line-height: 1.2;
|
||
}
|
||
|
||
.task-output .output-content.terminal-style {
|
||
max-height: 150px;
|
||
overflow-y: auto;
|
||
border: 1px solid var(--border);
|
||
margin: 8px 0;
|
||
}
|
||
|
||
/* Update Indicator Animations */
|
||
@keyframes slideIn {
|
||
from {
|
||
transform: translateX(100%);
|
||
opacity: 0;
|
||
}
|
||
to {
|
||
transform: translateX(0);
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
@keyframes fadeIn {
|
||
from {
|
||
opacity: 0;
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
@keyframes fadeOut {
|
||
from {
|
||
opacity: 1;
|
||
}
|
||
to {
|
||
opacity: 0;
|
||
}
|
||
}
|
||
|
||
@keyframes requiredFlash {
|
||
0%, 100% { box-shadow: 0 0 0 3px rgba(var(--status-danger-rgb), 0.20); }
|
||
50% { box-shadow: 0 0 0 6px rgba(var(--status-danger-rgb), 0.35); }
|
||
}
|
||
|
||
/* Welcome chip styles live in css/service-buttons.css. */
|
||
|
||
/* ===== Mobile (≤768px) — safety net + app detail page ===== */
|
||
@media (max-width: 768px) {
|
||
/* Keep the page from ever scrolling horizontally on mobile —
|
||
long URLs, wide tables, or rogue inline widths still scroll
|
||
inside their own containers if they need to. */
|
||
html, body {
|
||
overflow-x: hidden;
|
||
max-width: 100%;
|
||
}
|
||
|
||
/* Stack the app header so service buttons fall below app info. */
|
||
.app-header {
|
||
flex-direction: column;
|
||
align-items: stretch;
|
||
padding: 16px;
|
||
gap: 16px;
|
||
}
|
||
|
||
.app-header .app-info {
|
||
flex-direction: column;
|
||
align-items: center;
|
||
text-align: center;
|
||
gap: 12px;
|
||
padding: 0;
|
||
background: transparent;
|
||
border: none;
|
||
}
|
||
|
||
.app-header .app-info .app-card-icon {
|
||
align-self: center;
|
||
}
|
||
|
||
.app-header .app-details {
|
||
width: 100%;
|
||
}
|
||
|
||
.app-header .app-details h2 {
|
||
font-size: 20px;
|
||
}
|
||
|
||
.app-header .app-description {
|
||
font-size: 14px;
|
||
}
|
||
|
||
.app-header .app-meta {
|
||
justify-content: center;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
}
|
||
|
||
/* Service buttons stack vertically full-width below app info. */
|
||
.service-buttons-container {
|
||
flex-direction: column;
|
||
width: 100%;
|
||
margin-top: 0;
|
||
align-items: stretch;
|
||
}
|
||
|
||
.service-buttons-container .service-button {
|
||
width: 100%;
|
||
justify-content: center;
|
||
}
|
||
|
||
.service-buttons-container .service-trigger,
|
||
.service-buttons-container .service-trigger-icon {
|
||
width: 100%;
|
||
}
|
||
}
|
||
|
||
/* ============================================================
|
||
Global UI-mode gates — driven by window.LpUi (see js/utils/lp-ui.js).
|
||
Any DOM tagged with .lp-advanced is hidden unless body has the
|
||
lp-ui--advanced class; .lp-dev is hidden unless lp-ui--dev.
|
||
Use these classes on per-element surfaces (a button, a fieldset, a
|
||
tile, a column header) so beginners aren't shown operator detail and
|
||
non-devs aren't shown dev-only knobs. Pair with the per-page Advanced
|
||
toggle (also wired to LpUi.advanced) so flipping it is global.
|
||
============================================================ */
|
||
body:not(.lp-ui--advanced) .lp-advanced { display: none !important; }
|
||
body:not(.lp-ui--dev) .lp-dev { display: none !important; }
|