38686-vm/static/css/custom.css
Konrad du Plessis b0d382987b Theme Choices.js to match app dark/light themes
Code review of Task 7 showed Choices.js shipping with white-bg +
light-grey-text defaults, which were unreadable in dark mode and
clashed with the app's premium aesthetic. The design doc section 10
scoped these overrides into the CSS work but they hadn't been written.

Adds ~70 lines to custom.css re-themeing every .choices__ selector to
use existing design tokens (--bg-card, --bg-inset, --text-primary,
--accent, --border-default, etc.). No hardcoded colours; both :root
and :root.light themes work automatically.

Key visual changes:
- Dropdown popup: dark-card background with shadow
- Options: primary text colour, accent hover highlight
- Selected chips: orange accent pill with white text
- Focus ring: 0.15rem accent glow on the input

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 23:23:38 +02:00

1643 lines
43 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* ===================================================================
FoxFitt LabourPay v5 — Premium Orange Theme
Dark-first design system with warm amber/orange accents.
Top bar navigation on desktop, bottom tab bar on mobile.
All colours are CSS variables — the theme toggle switches them.
=================================================================== */
/* === DARK MODE (default — premium dark-first design) === */
:root {
/* Brand — warm orange/amber accent */
--accent: #e8851a;
--accent-hover: #f59e0b;
--accent-subtle: rgba(232, 133, 26, 0.12);
--accent-text: #f59e0b;
--accent-glow: rgba(232, 133, 26, 0.25);
/* Surfaces — very dark, charcoal-navy */
--bg-body: #0c0e14;
--bg-card: #161921;
--bg-card-hover: #1c2029;
--bg-elevated: #1c2029;
--bg-inset: #111318;
--bg-input: #1a1d26;
--bg-nav: #0c0e14;
--bg-sidebar: #111318;
/* Borders — subtle with slight warm tint */
--border-default: rgba(255, 255, 255, 0.08);
--border-subtle: rgba(255, 255, 255, 0.04);
--border-strong: rgba(255, 255, 255, 0.15);
--border-accent: rgba(232, 133, 26, 0.3);
/* Text — softened white (~85% brightness) for easier reading on dark backgrounds */
--text-primary: #d8d8d8;
--text-secondary: #9ca3af;
--text-tertiary: #6b7280;
--text-on-accent: #ffffff;
--text-on-nav: #d8d8d8;
--text-on-nav-muted: #6b7280;
--text-link: #e8851a;
/* Override Bootstrap */
--bs-body-color: #d8d8d8;
--bs-body-bg: #0c0e14;
--bs-border-color: rgba(255, 255, 255, 0.08);
/* Shadows — deep with orange tint */
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.4);
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.5);
--shadow-lg: 0 10px 25px rgba(0, 0, 0, 0.5);
--shadow-xl: 0 20px 40px rgba(0, 0, 0, 0.6);
--shadow-glow: 0 0 20px rgba(232, 133, 26, 0.15);
/* Status colours */
--color-success: #22c55e;
--color-success-bg: rgba(34, 197, 94, 0.12);
--color-danger: #ef4444;
--color-danger-bg: rgba(239, 68, 68, 0.12);
--color-warning: #f59e0b;
--color-warning-bg: rgba(245, 158, 11, 0.12);
--color-info: #3b82f6;
--color-info-bg: rgba(59, 130, 246, 0.12);
/* Stat card accent colours */
--stat-1: #e8851a;
--stat-2: #22c55e;
--stat-3: #f59e0b;
--stat-4: #3b82f6;
/* Misc */
--radius-sm: 0.5rem;
--radius-md: 0.625rem;
--radius-lg: 0.875rem;
--radius-xl: 1.25rem;
--transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
--transition-normal: 250ms cubic-bezier(0.4, 0, 0.2, 1);
/* Layout dimensions */
--bottom-nav-height: 64px;
}
/* === LIGHT MODE === */
[data-theme="light"] {
--bs-body-color: #1a1a2e;
--bs-body-bg: #f4f4f8;
--bs-border-color: #e0e0e8;
/* Brand tweaks for light */
--accent: #d97706;
--accent-hover: #b45309;
--accent-subtle: rgba(217, 119, 6, 0.08);
--accent-text: #92400e;
--accent-glow: rgba(217, 119, 6, 0.15);
/* Surfaces */
--bg-body: #f4f4f8;
--bg-card: #ffffff;
--bg-card-hover: #fafafa;
--bg-elevated: #ffffff;
--bg-inset: #f0f0f5;
--bg-input: #ffffff;
--bg-nav: #1a1a2e;
--bg-sidebar: #1a1a2e;
/* Borders */
--border-default: #e0e0e8;
--border-subtle: #f0f0f5;
--border-strong: #c8c8d4;
--border-accent: rgba(217, 119, 6, 0.3);
/* Text */
--text-primary: #1a1a2e;
--text-secondary: #4a4a5a;
--text-tertiary: #8a8a9a;
--text-on-nav: #f0f0f0;
--text-on-nav-muted: #6b7280;
--text-link: #d97706;
/* Shadows */
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.06);
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08);
--shadow-lg: 0 10px 25px rgba(0, 0, 0, 0.1);
--shadow-xl: 0 20px 40px rgba(0, 0, 0, 0.12);
--shadow-glow: 0 0 20px rgba(217, 119, 6, 0.08);
/* Status colours */
--color-success: #16a34a;
--color-success-bg: #ecfdf5;
--color-danger: #dc2626;
--color-danger-bg: #fef2f2;
--color-warning: #d97706;
--color-warning-bg: #fffbeb;
--color-info: #2563eb;
--color-info-bg: #eff6ff;
}
/* ===================================================================
GLOBAL RESET & BASE STYLES
=================================================================== */
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
background-color: var(--bg-body);
color: var(--text-primary);
--bs-body-color: var(--text-primary);
--bs-body-bg: var(--bg-body);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
margin: 0;
padding: 0;
}
h1, h2, h3, h4, h5, h6 {
font-family: 'Poppins', sans-serif;
color: var(--text-primary);
font-weight: 600;
}
a {
color: var(--text-link);
transition: color var(--transition-fast);
}
a:hover {
color: var(--accent-hover);
}
/* ===================================================================
APP LAYOUT — top bar + main content area
=================================================================== */
/* Wrapper for the whole app (topbar + content stacked vertically) */
.app-layout {
display: flex;
flex-direction: column;
min-height: 100vh;
}
/* === TOP BAR (always visible — horizontal nav on desktop, brand-only on mobile) === */
.app-topbar {
background: var(--bg-sidebar);
border-bottom: 1px solid var(--border-default);
position: sticky;
top: 0;
z-index: 1030;
padding: 0 1rem;
}
/* Inner flexbox container for topbar items */
.topbar-inner {
display: flex;
align-items: center;
height: 52px;
gap: 0.75rem;
max-width: 1400px;
margin: 0 auto;
width: 100%;
}
/* Brand logo + text */
.topbar-brand {
display: flex;
align-items: center;
gap: 0.5rem;
text-decoration: none;
flex-shrink: 0;
}
.topbar-brand:hover {
text-decoration: none;
}
/* Bolt icon box (also reused on login page) */
.sidebar-brand__icon {
width: 30px;
height: 30px;
background: linear-gradient(135deg, #e8851a 0%, #f59e0b 100%);
border-radius: var(--radius-sm);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 0.85rem;
flex-shrink: 0;
}
.topbar-brand__text {
font-family: 'Poppins', sans-serif;
font-weight: 700;
font-size: 1.1rem;
color: var(--text-on-nav);
letter-spacing: -0.02em;
}
.topbar-brand__text span {
color: var(--accent);
}
/* Horizontal nav links in topbar — centred between brand and actions */
.topbar-nav {
display: flex;
align-items: center;
justify-content: center;
gap: 0.15rem;
flex: 1;
}
.topbar-nav__link {
display: flex;
align-items: center;
gap: 0.35rem;
padding: 0.4rem 0.7rem;
border-radius: var(--radius-sm);
color: var(--text-on-nav-muted);
font-size: 0.8rem;
font-weight: 500;
text-decoration: none;
transition: all var(--transition-fast);
white-space: nowrap;
}
.topbar-nav__link:hover {
color: var(--text-on-nav);
background: rgba(255, 255, 255, 0.06);
text-decoration: none;
}
.topbar-nav__link.active {
color: var(--accent);
background: rgba(232, 133, 26, 0.1);
font-weight: 600;
}
.topbar-nav__link i {
font-size: 0.8rem;
width: 1rem;
text-align: center;
}
/* Right side of topbar: theme toggle + user avatar + logout */
.topbar-actions {
display: flex;
align-items: center;
gap: 0.5rem;
flex-shrink: 0;
margin-left: auto;
}
/* User avatar + name in topbar */
.topbar-user {
display: flex;
align-items: center;
gap: 0.4rem;
}
.topbar-user__avatar {
width: 28px;
height: 28px;
border-radius: 50%;
background: var(--accent);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: 600;
font-size: 0.7rem;
flex-shrink: 0;
}
.topbar-user__name {
color: var(--text-on-nav);
font-size: 0.78rem;
font-weight: 500;
}
/* === MAIN CONTENT AREA === */
.app-main {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
/* NO position/z-index here — avoids trapping Bootstrap modals in a stacking context */
}
/* Decorative gradient glows — separate fixed element, not on .app-main */
.app-glow {
position: fixed;
inset: 0;
pointer-events: none;
z-index: 0;
overflow: hidden;
}
.app-glow::before {
content: '';
position: absolute;
top: -200px;
right: -100px;
width: 600px;
height: 600px;
background: radial-gradient(ellipse, var(--accent-glow) 0%, transparent 70%);
}
.app-glow::after {
content: '';
position: absolute;
bottom: -300px;
left: 0;
width: 500px;
height: 500px;
background: radial-gradient(ellipse, rgba(232, 133, 26, 0.06) 0%, transparent 70%);
}
/* === BOTTOM TAB BAR (mobile only) === */
.app-bottom-nav {
display: none; /* hidden on desktop */
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: var(--bg-nav);
border-top: 1px solid var(--border-default);
z-index: 1030;
padding: 0.25rem 0;
padding-bottom: calc(0.25rem + env(safe-area-inset-bottom, 0px));
}
.bottom-nav-inner {
display: flex;
justify-content: space-around;
align-items: center;
}
.bottom-nav__link {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.15rem;
padding: 0.4rem 0.75rem;
color: var(--text-on-nav-muted);
text-decoration: none;
font-size: 0.65rem;
font-weight: 500;
transition: color var(--transition-fast);
border-radius: var(--radius-sm);
min-width: 56px;
}
.bottom-nav__link i {
font-size: 1.15rem;
}
.bottom-nav__link:hover,
.bottom-nav__link.active {
color: var(--accent);
text-decoration: none;
}
.bottom-nav__link.active {
font-weight: 600;
}
/* Main content inner */
.app-content {
flex-grow: 1;
/* NO position/z-index — lets Bootstrap modals escape to body-level stacking */
}
/* Footer inside main content */
.app-footer {
padding: 1.25rem 0;
margin-top: auto;
border-top: 1px solid var(--border-default);
font-size: 0.8rem;
color: var(--text-tertiary);
}
/* === HAMBURGER BUTTON (mobile only — hidden on desktop via d-lg-none) === */
.hamburger-btn {
background: none;
border: none;
color: var(--text-on-nav);
font-size: 1.2rem;
padding: 0.35rem 0.5rem;
border-radius: var(--radius-sm);
cursor: pointer;
transition: all var(--transition-fast);
}
.hamburger-btn:hover {
color: var(--accent);
background: rgba(255, 255, 255, 0.06);
}
/* === MOBILE MENU (slides down from topbar when hamburger is tapped) === */
/* Fixed below the topbar so it stays visible regardless of scroll position */
.mobile-menu {
position: fixed;
top: 52px; /* matches topbar-inner height */
left: 0;
right: 0;
z-index: 1029; /* just below topbar (1030) */
background: var(--bg-sidebar);
border-bottom: 1px solid var(--border-default);
max-height: 0;
overflow: hidden;
transition: max-height 300ms ease, opacity 200ms ease;
opacity: 0;
box-shadow: var(--shadow-lg);
}
/* Open state — toggled by JS */
.mobile-menu.open {
max-height: calc(100vh - 52px); /* never taller than remaining screen */
opacity: 1;
overflow-y: auto;
}
.mobile-menu__nav {
display: flex;
flex-direction: column;
padding: 0.5rem 1rem;
gap: 0.15rem;
}
.mobile-menu__link {
display: flex;
align-items: center;
gap: 0.6rem;
padding: 0.65rem 0.75rem;
border-radius: var(--radius-sm);
color: var(--text-on-nav-muted);
font-size: 0.85rem;
font-weight: 500;
text-decoration: none;
transition: all var(--transition-fast);
}
.mobile-menu__link:hover {
color: var(--text-on-nav);
background: rgba(255, 255, 255, 0.06);
text-decoration: none;
}
.mobile-menu__link.active {
color: var(--accent);
background: rgba(232, 133, 26, 0.1);
font-weight: 600;
}
.mobile-menu__link i {
width: 1.25rem;
text-align: center;
font-size: 0.9rem;
}
.mobile-menu__footer {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.75rem 1rem;
border-top: 1px solid var(--border-default);
margin-top: 0.25rem;
}
/* Semi-transparent backdrop behind the menu — tapping it closes the menu */
.mobile-menu-backdrop {
display: none;
position: fixed;
inset: 0;
top: 52px;
background: rgba(0, 0, 0, 0.5);
z-index: 1028; /* below menu (1029) and topbar (1030) */
}
.mobile-menu-backdrop.open {
display: block;
}
/* === RESPONSIVE: Mobile layout === */
@media (max-width: 991.98px) {
/* Hide desktop nav links — hamburger menu handles navigation on mobile */
.topbar-nav {
display: none;
}
/* Hide user name on mobile — just show avatar */
.topbar-user__name {
display: none;
}
/* Bottom tab bar hidden — replaced by hamburger menu */
.app-bottom-nav {
display: none;
}
}
/* ===================================================================
CARDS — glass-morphism with subtle borders
=================================================================== */
.card {
background: var(--bg-card);
border: 1px solid var(--border-default);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-sm);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
transition: background-color var(--transition-normal),
border-color var(--transition-normal),
box-shadow var(--transition-normal);
}
.card:hover {
box-shadow: var(--shadow-md);
}
.card-header {
background: transparent;
border-bottom: 1px solid var(--border-default);
}
/* === STAT CARDS — with orange accent line on left === */
.stat-card {
background: var(--bg-card);
border: 1px solid var(--border-default);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-sm);
transition: all var(--transition-normal);
position: relative;
overflow: hidden;
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
}
/* Left accent bar instead of top */
.stat-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
bottom: 0;
width: 3px;
background: var(--stat-accent, var(--accent));
}
.stat-card:hover {
box-shadow: var(--shadow-md);
border-color: var(--border-strong);
transform: translateY(-1px);
}
/* Per-card accent colours */
.stat-card--danger { --stat-accent: var(--color-danger); }
.stat-card--success { --stat-accent: var(--color-success); }
.stat-card--warning { --stat-accent: var(--color-warning); }
.stat-card--info { --stat-accent: var(--color-info); }
.stat-card--accent { --stat-accent: var(--accent); }
.stat-card--purple { --stat-accent: #8b5cf6; }
/* Stat card label */
.stat-label {
font-size: 0.7rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--text-secondary);
margin-bottom: 0.25rem;
}
/* Stat card value */
.stat-value {
font-size: 1.35rem;
font-weight: 700;
color: var(--text-primary);
font-family: 'Poppins', sans-serif;
}
/* Stat card icon circle */
.stat-icon {
width: 2.75rem;
height: 2.75rem;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.1rem;
flex-shrink: 0;
}
.stat-icon--danger { background: var(--color-danger-bg); color: var(--color-danger); }
.stat-icon--success { background: var(--color-success-bg); color: var(--color-success); }
.stat-icon--warning { background: var(--color-warning-bg); color: var(--color-warning); }
.stat-icon--info { background: var(--color-info-bg); color: var(--color-info); }
.stat-icon--accent { background: var(--accent-subtle); color: var(--accent); }
.stat-icon--purple { background: rgba(139, 92, 246, 0.1); color: #8b5cf6; }
/* ===================================================================
BUTTONS
=================================================================== */
.btn {
font-weight: 500;
font-size: 0.875rem;
border-radius: var(--radius-sm);
transition: all var(--transition-fast);
letter-spacing: 0.01em;
}
/* Primary accent button — orange */
.btn-accent {
background: linear-gradient(135deg, #e8851a 0%, #f59e0b 100%);
color: var(--text-on-accent);
font-weight: 600;
border: none;
box-shadow: 0 2px 8px rgba(232, 133, 26, 0.3);
}
.btn-accent:hover {
background: linear-gradient(135deg, #f59e0b 0%, #fbbf24 100%);
color: var(--text-on-accent);
box-shadow: 0 4px 16px rgba(232, 133, 26, 0.4);
transform: translateY(-1px);
}
.btn-accent:active {
transform: translateY(0);
box-shadow: 0 1px 4px rgba(232, 133, 26, 0.3);
}
/* btn-primary — dark slate in dark mode, darker slate in light mode */
.btn-primary {
background-color: #2a2d3a;
border-color: #3a3d4a;
color: #d8d8d8;
}
.btn-primary:hover,
.btn-primary:focus {
background-color: #353849;
border-color: #4a4d5a;
color: #ffffff;
}
.btn-primary:active,
.btn-primary.active {
background-color: #1e2130;
border-color: #3a3d4a;
color: #ffffff;
}
/* Light mode btn-primary — dark navy for good contrast */
[data-theme="light"] .btn-primary {
background-color: #1e293b;
border-color: #1e293b;
color: #ffffff;
}
[data-theme="light"] .btn-primary:hover,
[data-theme="light"] .btn-primary:focus {
background-color: #334155;
border-color: #334155;
color: #ffffff;
}
/* Dark mode outline button fixes */
[data-theme="dark"] .btn-outline-secondary,
:root .btn-outline-secondary {
color: var(--text-secondary);
border-color: var(--border-strong);
}
[data-theme="dark"] .btn-outline-secondary:hover,
:root .btn-outline-secondary:hover {
background-color: var(--bg-elevated);
color: var(--text-primary);
border-color: var(--text-secondary);
}
.btn-outline-info {
color: var(--color-info);
border-color: var(--color-info);
}
.btn-outline-warning {
color: var(--color-warning);
border-color: var(--color-warning);
}
.btn-outline-danger {
color: var(--color-danger);
border-color: var(--color-danger);
}
[data-theme="light"] .btn-outline-secondary {
color: var(--text-secondary);
border-color: var(--border-default);
}
[data-theme="light"] .btn-outline-secondary:hover {
background-color: var(--bg-inset);
color: var(--text-primary);
}
/* ===================================================================
TABLES — compact text for data-dense views
=================================================================== */
.table {
color: var(--text-primary);
--bs-table-bg: transparent;
--bs-table-color: var(--text-primary);
font-size: 0.78rem;
}
.table > thead {
background-color: var(--bg-inset);
}
.table-light {
--bs-table-bg: var(--bg-inset);
--bs-table-color: var(--text-secondary);
}
.table > tbody > tr {
border-color: var(--border-subtle);
transition: background-color var(--transition-fast);
}
.table-hover > tbody > tr:hover {
background-color: var(--bg-card-hover);
--bs-table-hover-bg: var(--bg-card-hover);
--bs-table-hover-color: var(--text-primary);
color: var(--text-primary);
}
.table th {
font-weight: 600;
font-size: 0.68rem;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--text-secondary);
border-bottom-width: 1px;
}
/* ===================================================================
FORMS
=================================================================== */
.form-control,
.form-select {
background-color: var(--bg-input);
border-color: var(--border-default);
color: var(--text-primary);
border-radius: var(--radius-sm);
transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}
/* Placeholder text — visible but subtle */
.form-control::placeholder,
.form-select::placeholder {
color: var(--text-tertiary);
opacity: 1;
}
.form-control:focus,
.form-select:focus {
background-color: var(--bg-input);
border-color: var(--accent);
color: var(--text-primary);
box-shadow: 0 0 0 3px var(--accent-subtle);
}
.form-label {
font-weight: 500;
font-size: 0.875rem;
color: var(--text-secondary);
margin-bottom: 0.375rem;
}
/* === NATIVE DATE/MONTH PICKER ICONS (Chromium) ===
The browser paints a small calendar icon on the right of
<input type="date"> and <input type="month"> via the pseudo-element
::-webkit-calendar-picker-indicator. On dark backgrounds the default
black icon is nearly invisible. CSS can't set its fill directly, so
we use a filter chain to tint it toward our amber accent (#e8851a).
Firefox doesn't render this indicator so the rule is a no-op there. */
input[type="date"]::-webkit-calendar-picker-indicator,
input[type="month"]::-webkit-calendar-picker-indicator {
cursor: pointer;
opacity: 0.9;
filter: invert(58%) sepia(89%) saturate(862%) hue-rotate(357deg) brightness(93%) contrast(92%);
}
input[type="date"]::-webkit-calendar-picker-indicator:hover,
input[type="month"]::-webkit-calendar-picker-indicator:hover {
opacity: 1;
}
.form-check-input:checked {
background-color: var(--accent);
border-color: var(--accent);
}
/* === FORMSET ROW — MARKED FOR DELETION ===
When a user clicks the trash button on a certification/warning row,
JS adds `.row-marked-delete` to that row. These styles fade the row
and strike through its inputs so it's visually obvious the row will
be removed on save. The "Undo" link restores everything. */
.formset-row.row-marked-delete {
opacity: 0.55;
background: rgba(239, 68, 68, 0.06);
border-color: rgba(239, 68, 68, 0.3) !important;
}
.formset-row.row-marked-delete .form-control,
.formset-row.row-marked-delete .form-select,
.formset-row.row-marked-delete textarea {
text-decoration: line-through;
pointer-events: none; /* can't edit a row you're removing */
background: var(--bg-inset, #f0f0f5);
}
.formset-row.row-marked-delete .form-label {
text-decoration: line-through;
}
.input-group-text {
background-color: var(--bg-inset);
border-color: var(--border-default);
color: var(--text-secondary);
}
/* === BOOTSTRAP TOOLTIPS — themed for dark/light modes ===
Bootstrap 5.3's default tooltip uses `--bs-body-color` as background
and `--bs-body-bg` as text colour. In dark mode that produces a light
tooltip with dark text, which clashes with the rest of the UI and can
be unreadable when the body/bg values are very close.
Override the tooltip CSS variables to use our elevated-surface colours
(same palette as cards on hover) — readable on both dark and light. */
.tooltip {
--bs-tooltip-bg: var(--bg-card-hover);
--bs-tooltip-color: var(--text-primary);
--bs-tooltip-opacity: 1;
}
.tooltip .tooltip-inner {
border: 1px solid var(--border-default);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
font-size: 0.8rem;
max-width: 280px;
padding: 6px 10px;
font-weight: 500;
}
/* The arrow inherits its color from --bs-tooltip-bg automatically, but
we give it a matching border so it stays connected visually. */
/* ===================================================================
MODALS
=================================================================== */
.modal-content {
background-color: var(--bg-elevated);
border: 1px solid var(--border-default);
border-radius: var(--radius-xl);
box-shadow: var(--shadow-xl);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
}
.modal-header {
border-bottom: 1px solid var(--border-default);
padding: 1.25rem 1.5rem;
}
.modal-title {
font-size: 1.1rem;
font-weight: 600;
}
.modal-body {
padding: 1.5rem;
}
.modal-footer {
border-top: 1px solid var(--border-default);
padding: 1rem 1.5rem;
}
/* ===================================================================
BADGES (status indicators)
=================================================================== */
.badge {
font-weight: 500;
font-size: 0.7rem;
letter-spacing: 0.02em;
padding: 0.35em 0.65em;
border-radius: var(--radius-sm);
}
/* ===================================================================
NAV TABS (Payroll dashboard tabs)
=================================================================== */
.nav-tabs {
border-bottom: 2px solid var(--border-default);
}
.nav-tabs .nav-link {
color: var(--text-secondary);
font-weight: 500;
font-size: 0.875rem;
border: none;
border-bottom: 2px solid transparent;
margin-bottom: -2px;
padding: 0.75rem 1.25rem;
transition: all var(--transition-fast);
}
.nav-tabs .nav-link:hover {
color: var(--text-primary);
border-bottom-color: var(--border-strong);
background: none;
}
.nav-tabs .nav-link.active {
color: var(--accent);
border-bottom-color: var(--accent);
background: none;
font-weight: 600;
}
/* ===================================================================
THEME TOGGLE BUTTON (in topbar)
=================================================================== */
.theme-toggle {
background: rgba(255, 255, 255, 0.06);
border: 1px solid var(--border-default);
color: var(--text-on-nav-muted);
cursor: pointer;
padding: 0.4rem;
border-radius: var(--radius-sm);
font-size: 0.85rem;
transition: all var(--transition-fast);
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
}
.theme-toggle:hover {
color: var(--accent);
background: rgba(232, 133, 26, 0.1);
border-color: var(--accent);
}
/* ===================================================================
DASHBOARD HEADER — gradient banner with orange accents
=================================================================== */
.dashboard-header {
background: linear-gradient(135deg, #111318 0%, #1a1d26 50%, #2a1a0a 100%);
color: white;
padding: 2rem 2.5rem;
margin-bottom: -3rem;
border-radius: var(--radius-xl);
position: relative;
overflow: hidden;
border: 1px solid var(--border-default);
}
/* Orange glow decoration on dashboard header */
.dashboard-header::before {
content: '';
position: absolute;
top: -60%;
right: -15%;
width: 350px;
height: 350px;
background: radial-gradient(circle, rgba(232, 133, 26, 0.2) 0%, transparent 65%);
pointer-events: none;
}
.dashboard-header::after {
content: '';
position: absolute;
bottom: -40%;
left: 10%;
width: 200px;
height: 200px;
background: radial-gradient(circle, rgba(245, 158, 11, 0.1) 0%, transparent 70%);
pointer-events: none;
}
/* Light mode dashboard header */
[data-theme="light"] .dashboard-header {
background: linear-gradient(135deg, #1a1a2e 0%, #2d2d4a 50%, #3d2a10 100%);
}
/* ===================================================================
PAGE-LEVEL UTILITIES
=================================================================== */
.page-header {
margin-bottom: 1.5rem;
}
.page-title {
font-size: 1.5rem;
font-weight: 700;
color: var(--text-primary);
margin-bottom: 0;
}
/* === PAYROLL ACTION BUTTONS — 2x2 grid on mobile, row on desktop === */
@media (max-width: 767.98px) {
.payroll-actions {
display: grid !important;
grid-template-columns: 1fr 1fr;
gap: 0.5rem;
width: 100%;
}
.payroll-actions .btn {
font-size: 0.75rem;
padding: 0.45rem 0.6rem;
white-space: nowrap;
}
}
/* On desktop, restore normal button size (undo btn-sm) */
@media (min-width: 768px) {
.btn-md-normal {
font-size: 0.875rem !important;
padding: 0.375rem 0.75rem !important;
}
}
.text-muted {
color: var(--text-secondary) !important;
}
.text-dark {
color: var(--text-primary) !important;
}
.border-bottom {
border-color: var(--border-default) !important;
}
/* ===================================================================
ALERTS
=================================================================== */
.alert {
border-radius: var(--radius-md);
border: none;
font-size: 0.875rem;
}
/* ===================================================================
FOOTER (inside app-main)
=================================================================== */
.footer {
background-color: transparent;
color: var(--text-tertiary);
padding: 1.25rem 0;
margin-top: auto;
border-top: 1px solid var(--border-default);
font-size: 0.8rem;
}
/* ===================================================================
SCROLLBAR (premium dark)
=================================================================== */
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: var(--border-strong);
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--text-tertiary);
}
/* ===================================================================
QUICK ACTION CARD (dashboard)
=================================================================== */
.quick-action {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 1.25rem 1rem;
border-radius: var(--radius-lg);
border: 1px solid var(--border-default);
background: var(--bg-card);
color: var(--text-primary);
text-decoration: none;
transition: all var(--transition-fast);
flex: 1;
min-width: 120px;
}
.quick-action:hover {
background: var(--accent-subtle);
border-color: var(--accent);
color: var(--accent);
transform: translateY(-2px);
box-shadow: var(--shadow-glow);
text-decoration: none;
}
.quick-action i {
font-size: 1.5rem;
margin-bottom: 0.5rem;
color: var(--accent);
}
.quick-action span {
font-size: 0.8rem;
font-weight: 600;
}
/* ===================================================================
LIST GROUP (themed)
=================================================================== */
.list-group-item {
background-color: var(--bg-card);
border-color: var(--border-subtle);
color: var(--text-primary);
transition: background-color var(--transition-normal);
}
.list-group-item:hover {
background-color: var(--bg-card-hover);
}
/* ===================================================================
RESOURCE TOGGLE PANEL (dashboard)
=================================================================== */
.resource-hidden { display: none !important; }
.resource-row {
border-color: var(--border-subtle) !important;
transition: background-color var(--transition-fast);
}
.resource-row:hover {
background-color: var(--bg-card-hover);
}
/* ===================================================================
WORKER LOOKUP LINK (clickable names)
=================================================================== */
.worker-lookup-link {
color: var(--text-primary) !important;
text-decoration: none !important;
border-bottom: 1px dashed var(--border-strong);
transition: all var(--transition-fast);
}
.worker-lookup-link:hover {
color: var(--accent) !important;
border-bottom-color: var(--accent);
}
/* ===================================================================
CALENDAR VIEW (work history)
=================================================================== */
.cal-day {
min-height: 90px;
padding: 6px 8px;
border: 1px solid var(--border-default);
border-radius: var(--radius-sm);
background: var(--bg-card);
transition: all var(--transition-fast);
cursor: default;
}
.cal-day--other {
opacity: 0.4;
background: var(--bg-inset);
}
.cal-day--today {
border-color: var(--accent);
border-width: 2px;
}
.cal-day--today .cal-day__number {
color: var(--accent);
font-weight: 700;
}
.cal-day--has-logs {
cursor: pointer;
}
.cal-day--has-logs:hover {
background: var(--accent-subtle);
border-color: var(--accent);
box-shadow: var(--shadow-sm);
}
.cal-day--selected {
background: var(--accent-subtle);
border-color: var(--accent);
border-width: 2px;
box-shadow: 0 0 0 3px var(--accent-subtle);
}
.cal-day__number {
font-size: 0.85rem;
font-weight: 500;
color: var(--text-primary);
}
.cal-entry {
font-size: 0.72rem;
line-height: 1.3;
color: var(--text-secondary);
}
@media (max-width: 767.98px) {
.cal-day { min-height: 55px; padding: 4px; }
.cal-entry { display: none; }
}
/* ===================================================================
DARK MODE — Bootstrap utility overrides
Bootstrap's utility classes (.text-dark, .bg-warning, .table-light,
.form-control:disabled, etc.) use hardcoded colours that look wrong
on dark backgrounds. These overrides fix contrast issues.
=================================================================== */
/* .text-dark — Bootstrap uses #212529, unreadable on dark bg */
:root .text-dark,
:root .text-dark.ms-1 {
color: var(--text-primary) !important;
}
/* Loan badge: yellow bg + dark text for contrast */
.badge.bg-warning {
color: #000 !important;
}
/* table-light (used in thead) — fix for dark mode */
:root .table-light {
--bs-table-bg: var(--bg-inset);
--bs-table-color: var(--text-secondary);
--bs-table-border-color: var(--border-default);
}
[data-theme="light"] .table-light {
--bs-table-bg: #f0f0f5;
--bs-table-color: #4a4a5a;
}
/* Disabled form controls — visible text in dark mode */
.form-control:disabled,
.form-select:disabled {
background-color: var(--bg-inset);
color: var(--text-secondary);
opacity: 0.7;
border-color: var(--border-default);
}
/* form-select option dropdown — ensure readable in dark mode */
.form-select option {
background-color: var(--bg-card);
color: var(--text-primary);
}
/* btn-close — visible X button on dark backgrounds */
:root .btn-close {
filter: invert(1) grayscale(100%) brightness(200%);
}
[data-theme="light"] .btn-close {
filter: none;
}
/* Bootstrap text utilities that clash with dark mode */
:root .text-primary {
color: var(--color-info) !important;
}
:root .text-success {
color: var(--color-success) !important;
}
:root .text-danger {
color: var(--color-danger) !important;
}
:root .text-warning {
color: var(--color-warning) !important;
}
:root .text-info {
color: var(--color-info) !important;
}
:root .text-muted {
color: var(--text-secondary) !important;
}
/* btn-secondary in modals — visible in dark mode */
.btn-secondary {
background-color: var(--bg-elevated);
border-color: var(--border-strong);
color: var(--text-primary);
}
.btn-secondary:hover {
background-color: var(--border-default);
border-color: var(--border-strong);
color: var(--text-primary);
}
/* ===================================================================
PRINT STYLES — ensure payslips print as black text on white page
=================================================================== */
@media print {
/* Override ALL CSS variables to light/print-friendly values */
:root, [data-theme="dark"], [data-theme="light"] {
--bg-body: #ffffff !important;
--bg-card: #ffffff !important;
--bg-card-hover: #ffffff !important;
--bg-elevated: #ffffff !important;
--bg-inset: #f5f5f5 !important;
--bg-input: #ffffff !important;
--text-primary: #000000 !important;
--text-secondary: #333333 !important;
--text-tertiary: #666666 !important;
--text-on-accent: #000000 !important;
--text-link: #000000 !important;
--border-default: #cccccc !important;
--border-subtle: #dddddd !important;
--border-strong: #999999 !important;
--accent: #d97706 !important;
--color-success: #16a34a !important;
--color-danger: #dc2626 !important;
--color-warning: #d97706 !important;
--color-info: #2563eb !important;
--color-success-bg: #ecfdf5 !important;
--color-danger-bg: #fef2f2 !important;
--color-warning-bg: #fffbeb !important;
--color-info-bg: #eff6ff !important;
--bs-body-color: #000000 !important;
--bs-body-bg: #ffffff !important;
}
body {
background: white !important;
color: black !important;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
/* Hide navigation and non-print elements */
.app-topbar, .app-bottom-nav,
.app-footer, .app-glow, .d-print-none { display: none !important; }
/* Cards: clean white with thin border, no blur */
.card {
background: white !important;
border: 1px solid #ddd !important;
box-shadow: none !important;
backdrop-filter: none !important;
-webkit-backdrop-filter: none !important;
}
/* Stat cards: remove glass effect */
.stat-card {
background: white !important;
backdrop-filter: none !important;
-webkit-backdrop-filter: none !important;
box-shadow: none !important;
}
/* Tables: black text on white */
.table, .table th, .table td {
color: #000 !important;
background: white !important;
border-color: #ccc !important;
font-size: 11pt !important;
}
.table > thead, .table-light {
background-color: #f0f0f0 !important;
}
/* Headings and text: all black */
h1, h2, h3, h4, h5, h6, p, span, div, td, th, a {
color: #000 !important;
}
/* Badges: print-friendly */
.badge {
border: 1px solid #999 !important;
background: #f5f5f5 !important;
color: #000 !important;
}
/* Ensure the stat-label text prints as dark grey */
.stat-label {
color: #555 !important;
}
/* Links: no colour, no underline */
a { color: #000 !important; text-decoration: none !important; }
}
/* ===================================================================
SMOOTH TRANSITIONS for theme switch
=================================================================== */
body, .card, .modal-content, .form-control, .form-select,
.table, .btn, .alert, .badge,
.input-group-text, .stat-card, .cal-day,
.app-topbar, .app-bottom-nav {
transition: background-color var(--transition-normal),
color var(--transition-normal),
border-color var(--transition-normal),
box-shadow var(--transition-normal);
}
/* === Work log payroll: clickable row hover === */
/* Applied only by base.html / templates that add class="work-log-row" */
/* (admin-only; supervisors never get the class so hover doesn't apply). */
.work-log-row {
transition: background-color 120ms ease-in-out;
}
.work-log-row:hover td {
background: var(--bg-card-hover);
}
/* === Report filter pills === */
.filter-pill {
display: inline-flex;
align-items: center;
padding: 0.35rem 0.75rem;
font-size: 0.825rem;
background: var(--bg-inset);
color: var(--text-primary);
border: 1px solid var(--border-default);
border-radius: 999px;
line-height: 1.2;
}
.filter-pill i {
color: var(--accent);
font-size: 0.75rem;
}
.filter-pill__x {
margin-left: 0.5rem;
padding: 0 0.35rem;
color: var(--text-tertiary);
text-decoration: none;
font-weight: 600;
border-radius: 50%;
transition: color 120ms, background-color 120ms;
}
.filter-pill__x:hover {
color: var(--text-primary);
background: var(--bg-card-hover);
text-decoration: none;
}
/* === Choices.js theme overrides (dark + light, executive report modal) === */
/*
Choices.js ships with a white-bg, light-grey-text default that clashes with
the app's dark theme. These overrides replace those defaults with the app's
own design tokens so the multi-select picker matches every other card and
input on the page. All tokens auto-switch between dark (:root) and light
(:root.light) themes — no duplicate blocks needed.
*/
/* Container — the outer wrapper that replaces the native <select> */
.choices {
margin-bottom: 0;
}
/* Closed-state input area (where chips and the placeholder/search sit) */
.choices__inner {
background: var(--bg-inset);
color: var(--text-primary);
border: 1px solid var(--border-default);
border-radius: 0.5rem;
padding: 0.4rem 0.55rem;
min-height: 2.55rem;
font-size: 0.925rem;
}
.choices.is-focused .choices__inner,
.choices.is-open .choices__inner {
border-color: var(--accent);
box-shadow: 0 0 0 0.15rem rgba(232, 133, 26, 0.18);
}
/* The cloned search input typed into when the dropdown is open */
.choices__input {
background: transparent !important;
color: var(--text-primary) !important;
font-size: 0.925rem;
}
.choices__input::placeholder {
color: var(--text-tertiary);
}
/* Dropdown popup — the list of choices */
.choices__list--dropdown,
.choices__list[aria-expanded] {
background: var(--bg-card);
border: 1px solid var(--border-default);
border-radius: 0.5rem;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.28);
margin-top: 4px;
z-index: 2000;
}
/* Individual option rows in the dropdown */
.choices__list--dropdown .choices__item,
.choices__list[aria-expanded] .choices__item {
color: var(--text-primary);
padding: 0.5rem 0.75rem;
font-size: 0.9rem;
}
/* Hovered / keyboard-highlighted option */
.choices__list--dropdown .choices__item--selectable.is-highlighted,
.choices__list[aria-expanded] .choices__item--selectable.is-highlighted {
background: var(--bg-card-hover);
color: var(--text-primary);
}
/* The trailing "Press to select" hint */
.choices__list--dropdown .choices__item--selectable.is-highlighted::after,
.choices__list[aria-expanded] .choices__item--selectable.is-highlighted::after {
color: var(--accent);
opacity: 0.9;
}
/* Disabled / placeholder-style rows (e.g. "No matches found") */
.choices__list--dropdown .choices__item--disabled,
.choices__list[aria-expanded] .choices__item--disabled {
color: var(--text-tertiary);
}
/* Placeholder text in the input area when nothing is selected */
.choices__placeholder {
color: var(--text-tertiary);
opacity: 1;
}
/* Selected chips in multi-select mode (visible when items are chosen) */
.choices__list--multiple .choices__item {
background: var(--accent);
border: 1px solid var(--accent);
color: #fff;
font-size: 0.82rem;
font-weight: 500;
padding: 0.2rem 0.6rem;
margin: 0.15rem 0.25rem 0.15rem 0;
border-radius: 999px;
}
.choices__list--multiple .choices__item.is-highlighted {
background: var(--accent-hover);
border-color: var(--accent-hover);
}
/* The × button on each selected chip */
.choices__list--multiple .choices__button {
border-left: 1px solid rgba(255, 255, 255, 0.4);
margin: 0 0 0 0.5rem;
padding-left: 0.5rem;
opacity: 0.85;
}
.choices__list--multiple .choices__button:hover {
opacity: 1;
}
/* No-results / no-choices message */
.choices__list .choices__item--no-results,
.choices__list .choices__item--no-choices {
color: var(--text-tertiary);
font-style: italic;
}