A free, open, self-hosted app platform (GNU AGPLv3): one-click app deploys, Traefik reverse proxy with automatic SSL, rootless Docker support, gluetun VPN routing, and a web dashboard to manage it all. Free & open forever to self-host; optional paid hosted services fund it. See PROMISE.md. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Signed-off-by: librelad <librelad@digitalangels.vip>
1043 lines
25 KiB
CSS
1043 lines
25 KiB
CSS
|
|
|
|
/* Form fields, inputs, checkboxes/tickboxes, input groups. Extracted from style.css. */
|
|
|
|
/* Form Fields */
|
|
.form-field {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 6px;
|
|
min-width: 0;
|
|
}
|
|
|
|
.form-label {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
font-weight: 500;
|
|
color: var(--text-primary, #fff);
|
|
font-size: 13px;
|
|
}
|
|
|
|
.required {
|
|
color: var(--danger-color);
|
|
font-weight: bold;
|
|
}
|
|
|
|
.help-icon {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 14px;
|
|
height: 14px;
|
|
background: var(--primary-color);
|
|
color: var(--text-primary);
|
|
border-radius: 50%;
|
|
font-size: 9px;
|
|
font-weight: bold;
|
|
cursor: help;
|
|
position: relative;
|
|
}
|
|
|
|
.help-icon:hover::after {
|
|
content: attr(title);
|
|
position: absolute;
|
|
bottom: 100%;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
background: var(--tooltip-bg);
|
|
color: var(--tooltip-text);
|
|
padding: 4px 8px;
|
|
border-radius: 4px;
|
|
font-size: 11px;
|
|
white-space: nowrap;
|
|
z-index: 1000;
|
|
margin-bottom: 6px;
|
|
min-width: 180px;
|
|
text-align: center;
|
|
font-weight: normal;
|
|
}
|
|
|
|
.form-input,
|
|
.form-select,
|
|
.form-textarea {
|
|
width: 100%;
|
|
padding: 10px;
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 6px;
|
|
color: var(--text-primary, #fff);
|
|
font-size: 13px;
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
.form-input:focus,
|
|
.form-select:focus,
|
|
.form-textarea:focus {
|
|
outline: none;
|
|
border-color: var(--primary-color);
|
|
box-shadow: 0 0 0 2px rgba(74, 144, 226, 0.1);
|
|
}
|
|
|
|
.form-textarea {
|
|
resize: vertical;
|
|
min-height: 80px;
|
|
font-family: inherit;
|
|
}
|
|
|
|
.form-help {
|
|
color: var(--text-secondary, #ccc);
|
|
font-size: 11px;
|
|
font-style: italic;
|
|
margin-top: 4px;
|
|
}
|
|
|
|
.checkbox-label.disabled {
|
|
opacity: 0.6;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.checkbox-label.disabled .checkbox-custom {
|
|
background: var(--hover-bg);
|
|
border-color: var(--border-color);
|
|
opacity: 0.5;
|
|
}
|
|
|
|
.checkbox-label.disabled .checkbox-text {
|
|
color: var(--text-secondary, #ccc);
|
|
}
|
|
|
|
.form-label.disabled {
|
|
color: var(--text-secondary, #ccc);
|
|
opacity: 0.6;
|
|
}
|
|
|
|
.form-input.disabled {
|
|
background: var(--hover-bg);
|
|
border-color: var(--border-color);
|
|
opacity: 0.6;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
/* Input Group for Number + Unit */
|
|
.input-group {
|
|
display: flex;
|
|
align-items: stretch;
|
|
}
|
|
|
|
.input-group .form-control {
|
|
border-top-right-radius: 0;
|
|
border-bottom-right-radius: 0;
|
|
border-right: none;
|
|
}
|
|
|
|
.input-group .form-control:focus {
|
|
border-right: none;
|
|
border-color: var(--primary-color);
|
|
box-shadow: 0 0 0 0.2rem rgba(var(--accent-rgb), 0.25);
|
|
}
|
|
|
|
.input-group-text {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 10px 12px;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
line-height: 1.5;
|
|
color: var(--text-color);
|
|
text-align: center;
|
|
white-space: nowrap;
|
|
background-color: var(--input-bg);
|
|
border: 1px solid var(--border-color);
|
|
border-top-right-radius: 6px;
|
|
border-bottom-right-radius: 6px;
|
|
min-width: 50px;
|
|
justify-content: center;
|
|
}
|
|
|
|
.master-toggle .checkbox-label,
|
|
.git-master-toggle .checkbox-label {
|
|
display: flex;
|
|
align-items: center;
|
|
font-weight: 600;
|
|
font-size: 16px;
|
|
cursor: pointer;
|
|
width: 100%;
|
|
}
|
|
|
|
/* .master-toggle and .git-master-toggle are section-enable controls
|
|
(e.g. backup remote 1, mail config). They inherit the default toggle
|
|
visual from .checkbox-custom — no overrides needed here. The
|
|
.git-master-toggle block lower in this file remains for legacy
|
|
layout (font-weight/sizing on the surrounding label). */
|
|
|
|
.section-content.disabled .field-group {
|
|
opacity: 0.6;
|
|
}
|
|
|
|
/* Themed <select> dropdown. Native rendering on Linux/Firefox honors
|
|
the OS dark theme and paints the control black regardless of our
|
|
CSS, so we strip the native appearance and supply our own glass
|
|
surface + chevron. */
|
|
select.form-control {
|
|
-webkit-appearance: none;
|
|
-moz-appearance: none;
|
|
appearance: none;
|
|
background-color: rgba(var(--text-rgb), 0.06);
|
|
color: var(--text-primary);
|
|
border: 1px solid rgba(var(--text-rgb), 0.20);
|
|
/* Inline SVG chevron, recoloured via theme text-rgb so it stays
|
|
readable on every theme. */
|
|
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='rgba(255,255,255,0.7)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
|
|
background-repeat: no-repeat;
|
|
background-position: right 14px center;
|
|
background-size: 16px 16px;
|
|
padding-right: 40px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
/* <option> elements are drawn by the OS popup, not the page surface.
|
|
We can only nudge their colors; the popup chrome itself is platform-
|
|
native. Use the theme's solid surface so options stay readable. */
|
|
select.form-control option {
|
|
background-color: var(--surface-bg-solid);
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
select.form-control:hover {
|
|
border-color: rgba(var(--accent-rgb), 0.40);
|
|
}
|
|
|
|
select.form-control:focus {
|
|
outline: none;
|
|
border-color: var(--accent);
|
|
background-color: rgba(var(--text-rgb), 0.10);
|
|
box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.20);
|
|
}
|
|
|
|
.field-group {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
margin-bottom: 6px;
|
|
}
|
|
|
|
.field-group label {
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
color: var(--text-secondary, #ccc);
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.form-control {
|
|
width: 100%;
|
|
padding: 12px 16px;
|
|
border: 1px solid rgba(var(--text-rgb), 0.2);
|
|
border-radius: 8px;
|
|
background: rgba(var(--text-rgb), 0.05);
|
|
color: var(--text-primary, #fff);
|
|
font-size: 14px;
|
|
font-family: inherit;
|
|
transition: all 0.2s ease;
|
|
resize: vertical;
|
|
min-height: 44px;
|
|
}
|
|
|
|
.form-control:focus {
|
|
outline: none;
|
|
border-color: var(--primary-color, var(--accent));
|
|
background: rgba(var(--text-rgb), 0.08);
|
|
box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.1);
|
|
}
|
|
|
|
.form-control::placeholder {
|
|
color: var(--text-secondary, #ccc);
|
|
opacity: 0.7;
|
|
}
|
|
|
|
.password-field .form-control {
|
|
padding-right: 48px;
|
|
}
|
|
|
|
.field-group label {
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
color: var(--text-secondary, #ccc);
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
/* Git Configuration Master Toggle */
|
|
|
|
/* INSTALL_MODE dropdown: narrower than full-width. Both selectors
|
|
below are needed because the custom-select.js component wraps the
|
|
native <select> in a .custom-select div — we have to constrain the
|
|
wrapper (which is what's visually rendered) AND the native select
|
|
(which is what's there when JS hasn't enhanced it yet, or when
|
|
custom-select is opted out via data-no-enhance). */
|
|
.git-master-toggle select.form-control,
|
|
.git-master-toggle .custom-select {
|
|
width: auto !important;
|
|
max-width: 33%;
|
|
}
|
|
|
|
.git-master-toggle .checkbox-text {
|
|
font-weight: 600;
|
|
font-size: 16px;
|
|
margin-left: 8px;
|
|
}
|
|
|
|
.git-master-toggle .checkbox-label {
|
|
display: flex;
|
|
align-items: center;
|
|
cursor: pointer;
|
|
padding: 0;
|
|
border-radius: 0;
|
|
transition: none;
|
|
background: transparent;
|
|
}
|
|
|
|
.git-master-toggle .checkbox-label:hover {
|
|
background: transparent;
|
|
}
|
|
|
|
/* Toggle Switch Design - Complete override */
|
|
.git-master-toggle .checkbox-label input[type="checkbox"] {
|
|
display: none !important;
|
|
opacity: 0 !important;
|
|
position: absolute !important;
|
|
}
|
|
|
|
.git-master-toggle .checkbox-custom {
|
|
position: relative;
|
|
width: 48px;
|
|
height: 24px;
|
|
background: var(--border-strong);
|
|
border-radius: 24px;
|
|
margin-right: 12px;
|
|
transition: all 0.3s ease;
|
|
cursor: pointer;
|
|
border: none !important;
|
|
}
|
|
|
|
.git-master-toggle .checkbox-custom::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 2px;
|
|
left: 2px;
|
|
width: 20px;
|
|
height: 20px;
|
|
background: white;
|
|
border-radius: 50%;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.git-master-toggle .checkbox-label input[type="checkbox"]:checked + .checkbox-custom {
|
|
background: var(--primary-color) !important;
|
|
border-color: var(--primary-color) !important;
|
|
}
|
|
|
|
.git-master-toggle .checkbox-label input[type="checkbox"]:checked + .checkbox-custom::before {
|
|
transform: translateX(24px);
|
|
}
|
|
|
|
.git-master-toggle .checkbox-label input[type="checkbox"]:hover + .checkbox-custom {
|
|
}
|
|
|
|
.git-master-toggle .checkbox-text {
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
color: var(--text-primary);
|
|
line-height: 1.4;
|
|
user-select: none;
|
|
}
|
|
|
|
/* Config page checkboxes */
|
|
.checkbox-label {
|
|
display: flex;
|
|
align-items: center;
|
|
cursor: pointer;
|
|
padding: 10px 16px;
|
|
min-height: 44px;
|
|
border-radius: 10px;
|
|
transition: background 0.18s ease, border-color 0.18s ease;
|
|
background: rgba(var(--text-rgb), 0.04);
|
|
border: 1px solid rgba(var(--text-rgb), 0.10);
|
|
}
|
|
|
|
/* In a multi-column config row the toggle cell only contains the
|
|
toggle (no label/help text above it like the input cells do), so
|
|
it sits at the top of the row while the inputs underneath their
|
|
labels run lower. align-self: end drops the toggle to the bottom
|
|
of the grid row so its row aligns with the inputs' row, not the
|
|
inputs' labels. */
|
|
.config-fields > .checkbox-field {
|
|
align-self: end;
|
|
}
|
|
|
|
.checkbox-label:hover {
|
|
background: rgba(var(--text-rgb), 0.07);
|
|
border-color: rgba(var(--accent-rgb), 0.35);
|
|
}
|
|
|
|
.checkbox-label.disabled {
|
|
opacity: 0.6;
|
|
cursor: not-allowed;
|
|
background: transparent;
|
|
border-color: var(--border-color);
|
|
}
|
|
|
|
.checkbox-label.disabled .checkbox-custom {
|
|
background: var(--hover-bg);
|
|
border-color: var(--border-color);
|
|
}
|
|
|
|
.checkbox-label input[type="checkbox"] {
|
|
display: none !important;
|
|
opacity: 0 !important;
|
|
position: absolute !important;
|
|
cursor: pointer;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------
|
|
Default checkbox visual = TOGGLE SWITCH.
|
|
Most app/config options are binary data settings (Enable X, Auto-Y)
|
|
for which a sliding switch reads as "on/off".
|
|
|
|
The TICKBOX variant (square with check) lives further down, scoped
|
|
to .toggle-section and .advanced-toggle-field — those host the
|
|
"Show Advanced / Show Unused" UI preferences where a tickbox better
|
|
signals "this affects what you see, not the data".
|
|
------------------------------------------------------------------ */
|
|
.checkbox-custom {
|
|
position: relative;
|
|
width: 48px;
|
|
height: 24px;
|
|
flex-shrink: 0;
|
|
margin-right: 12px;
|
|
background: rgba(var(--text-rgb), 0.20);
|
|
border: none;
|
|
border-radius: 24px;
|
|
cursor: pointer;
|
|
transition: background 0.18s ease, box-shadow 0.18s ease;
|
|
}
|
|
|
|
.checkbox-custom::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 2px;
|
|
left: 2px;
|
|
width: 20px;
|
|
height: 20px;
|
|
background: var(--text-primary);
|
|
border-radius: 50%;
|
|
transition: transform 0.18s ease;
|
|
}
|
|
|
|
.checkbox-label input[type="checkbox"]:hover + .checkbox-custom {
|
|
box-shadow: 0 0 0 4px rgba(var(--accent-rgb), 0.10);
|
|
}
|
|
|
|
.checkbox-label input[type="checkbox"]:checked + .checkbox-custom {
|
|
background: var(--accent);
|
|
}
|
|
|
|
.checkbox-label input[type="checkbox"]:checked + .checkbox-custom::before {
|
|
transform: translateX(24px);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------
|
|
TICKBOX variant — used for "this changes what you see" preferences
|
|
like Show Advanced Options / Show Unused Options / Show advanced
|
|
settings. Scoped by parent class so the toggle stays the default
|
|
everywhere else.
|
|
------------------------------------------------------------------ */
|
|
.toggle-section .checkbox-custom,
|
|
.advanced-toggle-field .checkbox-custom {
|
|
width: 20px;
|
|
height: 20px;
|
|
margin-right: 10px;
|
|
background: rgba(var(--text-rgb), 0.05);
|
|
border: 1.5px solid rgba(var(--text-rgb), 0.40);
|
|
border-radius: 4px;
|
|
box-shadow: none;
|
|
}
|
|
|
|
.toggle-section .checkbox-custom::before,
|
|
.advanced-toggle-field .checkbox-custom::before {
|
|
content: none;
|
|
}
|
|
|
|
.toggle-section .checkbox-custom::after,
|
|
.advanced-toggle-field .checkbox-custom::after {
|
|
content: '';
|
|
position: absolute;
|
|
top: 2px;
|
|
left: 6px;
|
|
width: 5px;
|
|
height: 10px;
|
|
border: solid var(--text-on-accent, #ffffff);
|
|
border-width: 0 2px 2px 0;
|
|
transform: rotate(45deg);
|
|
opacity: 0;
|
|
transition: opacity 0.15s ease;
|
|
}
|
|
|
|
.toggle-section .checkbox-label input[type="checkbox"]:hover + .checkbox-custom,
|
|
.advanced-toggle-field .checkbox-label input[type="checkbox"]:hover + .checkbox-custom {
|
|
border-color: var(--accent);
|
|
box-shadow: none;
|
|
}
|
|
|
|
.toggle-section .checkbox-label input[type="checkbox"]:checked + .checkbox-custom,
|
|
.advanced-toggle-field .checkbox-label input[type="checkbox"]:checked + .checkbox-custom {
|
|
background: var(--accent);
|
|
border-color: var(--accent);
|
|
}
|
|
|
|
.toggle-section .checkbox-label input[type="checkbox"]:checked + .checkbox-custom::after,
|
|
.advanced-toggle-field .checkbox-label input[type="checkbox"]:checked + .checkbox-custom::after {
|
|
opacity: 1;
|
|
}
|
|
|
|
.checkbox-text {
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
color: var(--text-primary);
|
|
line-height: 1.4;
|
|
user-select: none;
|
|
}
|
|
|
|
.group-fields .form-field {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.group-fields .form-field:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.app-config .form-field {
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.app-config .form-label {
|
|
display: block;
|
|
margin-bottom: 6px;
|
|
font-weight: 500;
|
|
color: var(--text-color);
|
|
font-size: 14px;
|
|
}
|
|
|
|
/* App-config inputs share the .form-control recipe used by the standard
|
|
config page — translucent theme-aware border + glass fill — so app
|
|
config fields stop reading as a near-black slab against the nebula
|
|
gradient. */
|
|
.app-config .form-input,
|
|
.app-config .form-select,
|
|
.app-config .config-input,
|
|
.config-input {
|
|
width: 100%;
|
|
padding: 10px 12px;
|
|
border: 1px solid rgba(var(--text-rgb), 0.20);
|
|
border-radius: 8px;
|
|
background: rgba(var(--text-rgb), 0.05);
|
|
color: var(--text-primary, #fff);
|
|
font-size: 14px;
|
|
font-family: inherit;
|
|
transition: border-color 0.2s ease, background 0.2s ease;
|
|
}
|
|
|
|
.app-config .form-input:focus,
|
|
.app-config .form-select:focus,
|
|
.app-config .config-input:focus,
|
|
.config-input:focus {
|
|
outline: none;
|
|
border-color: var(--accent);
|
|
background: rgba(var(--text-rgb), 0.08);
|
|
}
|
|
|
|
.app-config .form-input::placeholder,
|
|
.app-config .config-input::placeholder,
|
|
.config-input::placeholder {
|
|
color: var(--text-secondary, #ccc);
|
|
opacity: 0.7;
|
|
}
|
|
|
|
.app-config .form-help {
|
|
display: block;
|
|
margin-top: 4px;
|
|
font-size: 12px;
|
|
color: var(--text-muted);
|
|
line-height: 1.4;
|
|
}
|
|
|
|
/* App Config Checkboxes - EXACT copy of global toggle switch styling */
|
|
.app-config .checkbox-label {
|
|
display: flex;
|
|
align-items: center;
|
|
cursor: pointer;
|
|
padding: 4px;
|
|
border-radius: 6px;
|
|
transition: all 0.2s ease;
|
|
background: #0000;
|
|
border: 1px solid transparent;
|
|
}
|
|
|
|
.app-config .checkbox-label:hover {
|
|
/* background: rgba(var(--text-rgb),0.05); */
|
|
}
|
|
|
|
.app-config .checkbox-label.disabled {
|
|
opacity: 0.6;
|
|
cursor: not-allowed;
|
|
background: transparent;
|
|
border-color: var(--border-color);
|
|
}
|
|
|
|
.app-config .checkbox-label.disabled .checkbox-custom {
|
|
background: var(--hover-bg);
|
|
border-color: var(--border-color);
|
|
}
|
|
|
|
.app-config .checkbox-label.disabled .checkbox-text {
|
|
color: var(--text-secondary, #ccc);
|
|
}
|
|
|
|
.app-config .checkbox-label:hover .checkbox-text {
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.app-config .checkbox-text {
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
color: var(--text-primary);
|
|
line-height: 1.4;
|
|
user-select: none;
|
|
}
|
|
|
|
.app-config .checkbox-label input[type="checkbox"] {
|
|
display: none !important;
|
|
opacity: 0 !important;
|
|
position: absolute !important;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.app-config .checkbox-custom {
|
|
position: relative;
|
|
width: 48px;
|
|
height: 24px;
|
|
background: var(--border-strong);
|
|
border-radius: 24px;
|
|
margin-right: 12px;
|
|
transition: all 0.3s ease;
|
|
cursor: pointer;
|
|
border: none;
|
|
}
|
|
|
|
.app-config .checkbox-custom::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 2px;
|
|
left: 2px;
|
|
width: 20px;
|
|
height: 20px;
|
|
background: white;
|
|
border-radius: 50%;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.app-config .checkbox-label input[type="checkbox"]:hover + .checkbox-custom {
|
|
}
|
|
|
|
.app-config .checkbox-label input[type="checkbox"]:checked + .checkbox-custom {
|
|
background: var(--primary-color);
|
|
border-color: var(--primary-color);
|
|
}
|
|
|
|
.app-config .checkbox-label input[type="checkbox"]:checked + .checkbox-custom::before {
|
|
transform: translateX(24px);
|
|
}
|
|
|
|
/* Field Group Styling for Domain Blocks */
|
|
.domain-building-block .field-group {
|
|
margin: 0;
|
|
width: 100%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.domain-building-block .field-label {
|
|
display: block;
|
|
margin-bottom: 6px;
|
|
font-weight: 500;
|
|
color: var(--text-color);
|
|
font-size: 14px;
|
|
}
|
|
|
|
.domain-building-block .form-control {
|
|
width: 100%;
|
|
padding: 8px 12px;
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 4px;
|
|
background: var(--input-bg);
|
|
color: var(--text-color);
|
|
font-size: 14px;
|
|
transition: border-color 0.3s ease;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.domain-building-block .form-control:focus {
|
|
outline: none;
|
|
border-color: var(--primary-color, var(--accent));
|
|
box-shadow: 0 0 0 2px rgba(var(--accent-rgb), 0.25);
|
|
}
|
|
|
|
/* Toggle-specific spacing */
|
|
.checkbox-label.master-toggle {
|
|
padding: 20px;
|
|
}
|
|
|
|
/* Enhanced field styling */
|
|
.form-field {
|
|
position: relative;
|
|
}
|
|
|
|
.form-label {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
font-weight: 500;
|
|
margin-bottom: 6px;
|
|
}
|
|
|
|
.required {
|
|
color: var(--error-color);
|
|
font-weight: 600;
|
|
}
|
|
|
|
.help-icon {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 16px;
|
|
height: 16px;
|
|
background: var(--primary-color);
|
|
color: var(--text-primary);
|
|
border-radius: 50%;
|
|
font-size: 10px;
|
|
font-weight: bold;
|
|
cursor: help;
|
|
margin-left: 4px;
|
|
}
|
|
|
|
.form-help {
|
|
display: block;
|
|
margin-top: 4px;
|
|
font-size: 11px;
|
|
color: var(--text-secondary);
|
|
font-style: italic;
|
|
}
|
|
|
|
/* "Show Advanced / Show Unused" UI-preference wrapper. Glass card that
|
|
reads on every theme — replaces an older recipe that hardcoded
|
|
var(--surface-bg-solid) (solid dark navy on nebula) plus a near-white
|
|
#f9f9f9 hover, which made the label invisible on dark themes. */
|
|
.toggle-section .checkbox-label {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
cursor: pointer;
|
|
width: 100%;
|
|
padding: 14px 16px;
|
|
background: rgba(var(--text-rgb), 0.05);
|
|
border: 1px solid rgba(var(--text-rgb), 0.10);
|
|
border-radius: 10px;
|
|
box-shadow: inset 0 1px 0 rgba(var(--text-rgb), 0.04);
|
|
transition: background 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease;
|
|
}
|
|
|
|
.toggle-section .checkbox-label:hover {
|
|
background: rgba(var(--text-rgb), 0.08);
|
|
border-color: rgba(var(--accent-rgb), 0.40);
|
|
box-shadow: inset 0 1px 0 rgba(var(--text-rgb), 0.06);
|
|
}
|
|
|
|
/* .toggle-section .checkbox-custom intentionally NOT redefined here.
|
|
The shared tickbox styling earlier in this file already scopes the
|
|
square-check visual to .toggle-section, so adding another rule here
|
|
would just override it. */
|
|
|
|
.toggle-section .checkbox-text {
|
|
font-weight: 600;
|
|
color: var(--text-primary);
|
|
font-size: 16px;
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.toggle-section .checkbox-description {
|
|
font-size: 13px;
|
|
color: var(--text-secondary);
|
|
line-height: 1.4;
|
|
}
|
|
|
|
/* Red-flash highlight for required-but-empty fields when the user clicks
|
|
Install. Cleared on the next input/change. Native title attribute on the
|
|
element provides the "Required" tooltip on hover. */
|
|
.field-required-error {
|
|
border: 1.5px solid var(--status-danger) !important;
|
|
box-shadow: 0 0 0 3px rgba(var(--status-danger-rgb), 0.20);
|
|
animation: requiredFlash 0.6s ease-in-out 2;
|
|
background: rgba(var(--status-danger-rgb), 0.06) !important;
|
|
}
|
|
|
|
/* ============================================================
|
|
Custom <select> component (js/system/custom-select.js).
|
|
|
|
Native <select> popups are drawn by the OS and ignore CSS for the
|
|
popup chrome. This component visually replaces the native select
|
|
while keeping it in the DOM for form submission / change events.
|
|
============================================================ */
|
|
.custom-select {
|
|
position: relative;
|
|
width: 100%;
|
|
display: inline-block;
|
|
}
|
|
|
|
/* The native <select> stays in the DOM (so forms submit + JS reading
|
|
.value continues to work) but is hidden behind the styled button. */
|
|
.custom-select-native {
|
|
position: absolute;
|
|
inset: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
z-index: -1;
|
|
}
|
|
|
|
.custom-select-button {
|
|
width: 100%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 12px;
|
|
padding: 12px 16px;
|
|
background: rgba(var(--text-rgb), 0.06);
|
|
border: 1px solid rgba(var(--text-rgb), 0.20);
|
|
border-radius: 8px;
|
|
color: var(--text-primary);
|
|
font-size: 14px;
|
|
font-family: inherit;
|
|
text-align: left;
|
|
cursor: pointer;
|
|
transition: background 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease;
|
|
}
|
|
|
|
.custom-select-button:hover:not(:disabled) {
|
|
border-color: rgba(var(--accent-rgb), 0.40);
|
|
}
|
|
|
|
.custom-select-button:disabled {
|
|
opacity: 0.6;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.custom-select.is-open .custom-select-button {
|
|
border-color: var(--accent);
|
|
background: rgba(var(--text-rgb), 0.10);
|
|
box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.20);
|
|
}
|
|
|
|
.custom-select-label {
|
|
flex: 1;
|
|
text-align: left;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.custom-select-arrow {
|
|
display: inline-flex;
|
|
flex-shrink: 0;
|
|
color: rgba(var(--text-rgb), 0.70);
|
|
transition: transform 0.18s ease;
|
|
}
|
|
|
|
.custom-select.is-open .custom-select-arrow {
|
|
transform: rotate(180deg);
|
|
color: var(--accent);
|
|
}
|
|
|
|
/* Popup — fixed-positioned (top/left/width set by JS in
|
|
custom-select.js > positionPopup) so the dropdown escapes any
|
|
ancestor with overflow: hidden, e.g. .eo-modal-content. z-index
|
|
is above eo-modal (1100) so it floats over modals too. */
|
|
.custom-select-popup {
|
|
position: fixed;
|
|
z-index: 1200;
|
|
max-height: 280px;
|
|
overflow-y: auto;
|
|
padding: 6px;
|
|
background: var(--surface-bg-solid, var(--bg-primary));
|
|
border: 1px solid rgba(var(--text-rgb), 0.16);
|
|
border-radius: 10px;
|
|
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.35), inset 0 1px 0 rgba(var(--text-rgb), 0.06);
|
|
backdrop-filter: blur(14px) saturate(140%);
|
|
-webkit-backdrop-filter: blur(14px) saturate(140%);
|
|
animation: customSelectIn 0.12s ease-out;
|
|
}
|
|
|
|
.custom-select.flip-up .custom-select-popup {
|
|
transform-origin: bottom center;
|
|
}
|
|
|
|
@keyframes customSelectIn {
|
|
from { opacity: 0; transform: translateY(-4px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
|
|
.custom-select.flip-up .custom-select-popup {
|
|
animation-name: customSelectInUp;
|
|
}
|
|
|
|
@keyframes customSelectInUp {
|
|
from { opacity: 0; transform: translateY(4px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
|
|
.custom-select-option {
|
|
padding: 9px 12px;
|
|
border-radius: 6px;
|
|
cursor: pointer;
|
|
color: var(--text-primary);
|
|
font-size: 14px;
|
|
line-height: 1.3;
|
|
user-select: none;
|
|
transition: background 0.12s ease, color 0.12s ease;
|
|
position: relative;
|
|
}
|
|
|
|
.custom-select-option:hover,
|
|
.custom-select-option.is-focused {
|
|
background: rgba(var(--accent-rgb), 0.15);
|
|
color: var(--accent);
|
|
}
|
|
|
|
.custom-select-option.is-selected {
|
|
background: rgba(var(--accent-rgb), 0.22);
|
|
color: var(--accent);
|
|
font-weight: 600;
|
|
}
|
|
|
|
.custom-select-option.is-selected::after {
|
|
content: '';
|
|
position: absolute;
|
|
top: 50%;
|
|
right: 12px;
|
|
width: 4px;
|
|
height: 8px;
|
|
margin-top: -6px;
|
|
border: solid var(--accent);
|
|
border-width: 0 2px 2px 0;
|
|
transform: rotate(45deg);
|
|
}
|
|
|
|
.custom-select-option.is-disabled {
|
|
opacity: 0.4;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
/* Themed scrollbar for the popup. */
|
|
.custom-select-popup::-webkit-scrollbar {
|
|
width: 8px;
|
|
}
|
|
.custom-select-popup::-webkit-scrollbar-track {
|
|
background: transparent;
|
|
}
|
|
.custom-select-popup::-webkit-scrollbar-thumb {
|
|
background: rgba(var(--text-rgb), 0.20);
|
|
border-radius: 4px;
|
|
}
|
|
.custom-select-popup::-webkit-scrollbar-thumb:hover {
|
|
background: rgba(var(--accent-rgb), 0.40);
|
|
}
|
|
|
|
/* ============================================================
|
|
Custom number stepper (js/system/custom-number.js).
|
|
|
|
Replaces the OS spin-buttons on <input type="number"> with themed
|
|
up/down chevrons that read consistently across browsers/themes.
|
|
============================================================ */
|
|
|
|
/* Hide native browser spin buttons. */
|
|
.custom-number-input::-webkit-outer-spin-button,
|
|
.custom-number-input::-webkit-inner-spin-button {
|
|
-webkit-appearance: none;
|
|
margin: 0;
|
|
}
|
|
.custom-number-input {
|
|
-moz-appearance: textfield;
|
|
}
|
|
|
|
.custom-number {
|
|
position: relative;
|
|
display: inline-flex;
|
|
width: 100%;
|
|
align-items: stretch;
|
|
}
|
|
|
|
/* The input itself stretches to fill, leaving room for the controls. */
|
|
.custom-number .custom-number-input {
|
|
flex: 1;
|
|
min-width: 0;
|
|
padding-right: 36px;
|
|
}
|
|
|
|
.custom-number.is-disabled {
|
|
opacity: 0.6;
|
|
}
|
|
|
|
.custom-number-controls {
|
|
position: absolute;
|
|
top: 4px;
|
|
right: 4px;
|
|
bottom: 4px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
width: 24px;
|
|
border-radius: 6px;
|
|
overflow: hidden;
|
|
background: rgba(var(--text-rgb), 0.05);
|
|
}
|
|
|
|
.custom-number-btn {
|
|
flex: 1;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: transparent;
|
|
border: none;
|
|
color: rgba(var(--text-rgb), 0.65);
|
|
cursor: pointer;
|
|
padding: 0;
|
|
margin: 0;
|
|
transition: background 0.12s ease, color 0.12s ease;
|
|
}
|
|
|
|
.custom-number-btn:hover:not(:disabled) {
|
|
background: rgba(var(--accent-rgb), 0.20);
|
|
color: var(--accent);
|
|
}
|
|
|
|
.custom-number-btn:active:not(:disabled) {
|
|
background: rgba(var(--accent-rgb), 0.35);
|
|
}
|
|
|
|
.custom-number-btn:disabled,
|
|
.custom-number-btn.is-disabled {
|
|
opacity: 0.30;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
/* Subtle divider between up/down. */
|
|
.custom-number-up {
|
|
border-bottom: 1px solid rgba(var(--text-rgb), 0.08);
|
|
}
|