Files
Siro/dashboard/siro-admin/css/main.css
T
Hamza-AyedandClaude Opus 5 3fb7bc5190 Show the build id, tame long table cells, and add purposeful motion
The build identifier now sits in the sidebar footer. "Is my deploy actually
live?" was only answerable by opening Session & Security, which is the wrong
place for the first question asked after every deploy.

Error-log rows carry stack traces and full URLs. They stretched their row far
past the viewport and pushed the other columns out of view, which is exactly
where the console is least usable. Values over 70 characters truncate and
expand on click.

Motion is limited to two things that mean something: content arriving
(staggered, so a grid resolves instead of snapping), and a fetch being in
flight — the refresh control spins and the status pill pulses, so a section
that legitimately returns nothing still shows that it tried. All of it is
disabled under prefers-reduced-motion.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 17:49:48 +03:00

1594 lines
34 KiB
CSS

/* ==========================================================================
Siro Admin Web Portal - Modern Design System & CSS Stylesheet
========================================================================== */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Outfit:wght@400;500;600;700&display=swap');
:root {
/* Color Tokens */
--bg-dark: #090d16;
--bg-card: rgba(17, 24, 39, 0.7);
--bg-card-hover: rgba(30, 41, 59, 0.8);
--bg-surface: #111827;
--bg-elevated: #1e293b;
--border-color: rgba(255, 255, 255, 0.08);
--border-active: rgba(99, 102, 241, 0.4);
--primary: #6366f1;
--primary-hover: #4f46e5;
--primary-light: rgba(99, 102, 241, 0.15);
--accent-purple: #8b5cf6;
--success: #10b981;
--success-bg: rgba(16, 185, 129, 0.15);
--warning: #f59e0b;
--warning-bg: rgba(245, 158, 11, 0.15);
--danger: #f43f5e;
--danger-bg: rgba(244, 63, 94, 0.15);
--info: #06b6d4;
--info-bg: rgba(6, 182, 212, 0.15);
--text-main: #f8fafc;
--text-muted: #94a3b8;
--text-subtle: #64748b;
--sidebar-width: 260px;
--header-height: 70px;
--radius-sm: 8px;
--radius-md: 12px;
--radius-lg: 18px;
--radius-full: 9999px;
--shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.2);
--shadow-md: 0 8px 24px rgba(0, 0, 0, 0.3);
--shadow-glow: 0 0 25px rgba(99, 102, 241, 0.25);
--transition-fast: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
--transition-smooth: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Reset & Global */
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Inter', system-ui, -apple-system, sans-serif;
background-color: var(--bg-dark);
color: var(--text-main);
min-height: 100vh;
overflow-x: hidden;
line-height: 1.5;
}
h1, h2, h3, h4, h5, h6 {
font-family: 'Outfit', sans-serif;
letter-spacing: -0.02em;
}
/* Custom Scrollbar */
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
background: var(--bg-dark);
}
::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.15);
border-radius: var(--radius-full);
}
::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.3);
}
/* Auth Page Overlay / Container */
.auth-wrapper {
position: fixed;
inset: 0;
z-index: 1000;
background: radial-gradient(circle at 10% 20%, rgba(99, 102, 241, 0.15) 0%, transparent 40%),
radial-gradient(circle at 90% 80%, rgba(139, 92, 246, 0.15) 0%, transparent 40%),
var(--bg-dark);
display: flex;
align-items: center;
justify-content: center;
padding: 1.5rem;
transition: opacity 0.4s ease, visibility 0.4s ease;
}
.auth-wrapper.hidden {
opacity: 0;
visibility: hidden;
pointer-events: none;
}
.auth-card {
width: 100%;
max-width: 440px;
background: var(--bg-card);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid var(--border-color);
border-radius: var(--radius-lg);
padding: 2.5rem;
box-shadow: var(--shadow-md);
position: relative;
overflow: hidden;
}
.auth-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, var(--primary), var(--accent-purple));
}
.auth-header {
text-align: center;
margin-bottom: 2rem;
}
.auth-logo {
display: inline-flex;
align-items: center;
gap: 0.75rem;
font-size: 1.75rem;
font-weight: 700;
color: #fff;
margin-bottom: 0.5rem;
}
.auth-logo i {
color: var(--primary);
filter: drop-shadow(0 0 10px rgba(99, 102, 241, 0.6));
}
.auth-subtitle {
color: var(--text-muted);
font-size: 0.9rem;
}
.form-group {
margin-bottom: 1.25rem;
}
.form-label {
display: block;
font-size: 0.85rem;
font-weight: 500;
color: var(--text-muted);
margin-bottom: 0.5rem;
}
.form-input-group {
position: relative;
}
.form-input-group i {
position: absolute;
left: 1rem;
top: 50%;
transform: translateY(-50%);
color: var(--text-subtle);
font-size: 1.1rem;
transition: var(--transition-fast);
}
.form-input {
width: 100%;
background: rgba(15, 23, 42, 0.6);
border: 1px solid var(--border-color);
border-radius: var(--radius-md);
padding: 0.75rem 1rem 0.75rem 2.8rem;
color: var(--text-main);
font-size: 0.95rem;
outline: none;
transition: var(--transition-fast);
}
.form-input:focus {
border-color: var(--primary);
box-shadow: 0 0 0 3px var(--primary-light);
}
.form-input:focus + i, .form-input-group:focus-within i {
color: var(--primary);
}
.btn-primary {
width: 100%;
padding: 0.85rem;
border-radius: var(--radius-md);
border: none;
background: linear-gradient(135deg, var(--primary), var(--accent-purple));
color: #fff;
font-weight: 600;
font-size: 1rem;
cursor: pointer;
box-shadow: var(--shadow-glow);
transition: var(--transition-fast);
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
}
.btn-primary:hover {
opacity: 0.95;
transform: translateY(-1px);
}
/* App Main Layout */
.app-container {
display: flex;
min-height: 100vh;
}
/* Sidebar */
.sidebar {
width: var(--sidebar-width);
background: rgba(15, 23, 42, 0.95);
border-right: 1px solid var(--border-color);
display: flex;
flex-direction: column;
position: fixed;
top: 0;
bottom: 0;
left: 0;
z-index: 100;
transition: var(--transition-smooth);
backdrop-filter: blur(12px);
}
.sidebar-header {
height: var(--header-height);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 1.5rem;
border-bottom: 1px solid var(--border-color);
}
.brand {
display: flex;
align-items: center;
gap: 0.75rem;
font-size: 1.4rem;
font-weight: 700;
color: var(--text-main);
text-decoration: none;
}
.brand-icon {
width: 36px;
height: 36px;
border-radius: var(--radius-md);
background: linear-gradient(135deg, var(--primary), var(--accent-purple));
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 1.1rem;
box-shadow: var(--shadow-glow);
}
.brand-badge {
font-size: 0.65rem;
padding: 0.15rem 0.4rem;
border-radius: var(--radius-sm);
background: var(--primary-light);
color: var(--primary);
font-weight: 600;
text-transform: uppercase;
}
.sidebar-menu {
padding: 1.25rem 0.75rem;
flex: 1;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 0.35rem;
}
.menu-label {
font-size: 0.75rem;
text-transform: uppercase;
color: var(--text-subtle);
font-weight: 600;
padding: 0.75rem 0.75rem 0.25rem;
letter-spacing: 0.05em;
}
.nav-item {
display: flex;
align-items: center;
gap: 0.85rem;
padding: 0.75rem 1rem;
color: var(--text-muted);
text-decoration: none;
border-radius: var(--radius-md);
font-weight: 500;
font-size: 0.92rem;
transition: var(--transition-fast);
cursor: pointer;
}
.nav-item i {
font-size: 1.1rem;
width: 20px;
text-align: center;
}
.nav-item:hover {
color: var(--text-main);
background: rgba(255, 255, 255, 0.05);
}
.nav-item.active {
color: #fff;
background: linear-gradient(90deg, var(--primary-light), transparent);
border-left: 3px solid var(--primary);
}
.nav-item.active i {
color: var(--primary);
}
.sidebar-footer {
padding: 1rem 1.25rem;
border-top: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: space-between;
}
.user-profile-mini {
display: flex;
align-items: center;
gap: 0.75rem;
}
.avatar {
width: 38px;
height: 38px;
border-radius: var(--radius-full);
background: linear-gradient(135deg, var(--info), var(--primary));
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
color: #fff;
}
.user-info .name {
font-size: 0.88rem;
font-weight: 600;
color: var(--text-main);
}
.user-info .role {
font-size: 0.75rem;
color: var(--text-subtle);
}
.btn-icon {
background: transparent;
border: 1px solid var(--border-color);
color: var(--text-muted);
width: 34px;
height: 34px;
border-radius: var(--radius-md);
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: var(--transition-fast);
}
.btn-icon:hover {
color: var(--text-main);
border-color: rgba(255, 255, 255, 0.2);
background: rgba(255, 255, 255, 0.05);
}
/* Main Content Area */
.main-wrapper {
margin-left: var(--sidebar-width);
flex: 1;
display: flex;
flex-direction: column;
min-width: 0;
}
/* Header */
.top-header {
height: var(--header-height);
background: rgba(15, 23, 42, 0.8);
backdrop-filter: blur(12px);
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 2rem;
position: sticky;
top: 0;
z-index: 90;
}
.header-left {
display: flex;
align-items: center;
gap: 1rem;
}
.toggle-sidebar-btn {
display: none;
}
.search-box {
position: relative;
width: 320px;
}
.search-box input {
width: 100%;
background: rgba(30, 41, 59, 0.5);
border: 1px solid var(--border-color);
border-radius: var(--radius-full);
padding: 0.5rem 1rem 0.5rem 2.5rem;
color: var(--text-main);
font-size: 0.88rem;
outline: none;
transition: var(--transition-fast);
}
.search-box input:focus {
border-color: var(--primary);
background: rgba(30, 41, 59, 0.8);
}
.search-box i {
position: absolute;
left: 0.9rem;
top: 50%;
transform: translateY(-50%);
color: var(--text-subtle);
}
.header-right {
display: flex;
align-items: center;
gap: 1rem;
}
.live-indicator {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.8rem;
color: var(--success);
background: var(--success-bg);
padding: 0.35rem 0.75rem;
border-radius: var(--radius-full);
font-weight: 500;
}
.pulse-dot {
width: 8px;
height: 8px;
border-radius: var(--radius-full);
background: var(--success);
box-shadow: 0 0 8px var(--success);
animation: pulse 1.8s infinite;
}
@keyframes pulse {
0% { opacity: 1; transform: scale(1); }
50% { opacity: 0.4; transform: scale(0.85); }
100% { opacity: 1; transform: scale(1); }
}
/* Page Views */
.content-body {
padding: 2rem;
flex: 1;
}
.page-view {
display: none;
animation: fadeIn 0.3s ease;
}
.page-view.active {
display: block;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(6px); }
to { opacity: 1; transform: translateY(0); }
}
.page-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 1.75rem;
}
.page-title h1 {
font-size: 1.75rem;
font-weight: 700;
color: #fff;
}
.page-title p {
color: var(--text-muted);
font-size: 0.9rem;
margin-top: 0.2rem;
}
.page-actions {
display: flex;
align-items: center;
gap: 0.75rem;
}
/* Cards & Stats Grid */
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 1.25rem;
margin-bottom: 1.75rem;
}
.stat-card {
background: var(--bg-card);
backdrop-filter: blur(16px);
border: 1px solid var(--border-color);
border-radius: var(--radius-lg);
padding: 1.5rem;
position: relative;
overflow: hidden;
transition: var(--transition-smooth);
}
.stat-card:hover {
transform: translateY(-3px);
border-color: rgba(255, 255, 255, 0.15);
box-shadow: var(--shadow-md);
}
.stat-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 1rem;
}
.stat-icon {
width: 44px;
height: 44px;
border-radius: var(--radius-md);
display: flex;
align-items: center;
justify-content: center;
font-size: 1.25rem;
}
.stat-icon.primary { background: var(--primary-light); color: var(--primary); }
.stat-icon.success { background: var(--success-bg); color: var(--success); }
.stat-icon.warning { background: var(--warning-bg); color: var(--warning); }
.stat-icon.info { background: var(--info-bg); color: var(--info); }
.stat-icon.danger { background: var(--danger-bg); color: var(--danger); }
.stat-value {
font-size: 1.85rem;
font-weight: 700;
color: #fff;
line-height: 1.2;
}
.stat-title {
color: var(--text-muted);
font-size: 0.85rem;
font-weight: 500;
margin-top: 0.25rem;
}
.stat-footer {
margin-top: 1rem;
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.8rem;
}
.trend-badge {
display: inline-flex;
align-items: center;
gap: 0.25rem;
padding: 0.15rem 0.5rem;
border-radius: var(--radius-full);
font-weight: 600;
}
.trend-badge.up { background: var(--success-bg); color: var(--success); }
.trend-badge.down { background: var(--danger-bg); color: var(--danger); }
.trend-text {
color: var(--text-subtle);
}
/* Section Grid (Charts & Tables) */
.dashboard-grid {
display: grid;
grid-template-columns: 2fr 1fr;
gap: 1.5rem;
margin-bottom: 1.75rem;
}
@media (max-width: 1200px) {
.dashboard-grid {
grid-template-columns: 1fr;
}
}
.card {
background: var(--bg-card);
backdrop-filter: blur(16px);
border: 1px solid var(--border-color);
border-radius: var(--radius-lg);
padding: 1.5rem;
}
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 1.25rem;
}
.card-title {
font-size: 1.1rem;
font-weight: 600;
color: #fff;
}
/* Custom CSS Charts */
.chart-container {
height: 280px;
width: 100%;
position: relative;
}
/* Data Tables */
.table-responsive {
width: 100%;
overflow-x: auto;
}
.data-table {
width: 100%;
border-collapse: collapse;
text-align: left;
}
.data-table th {
background: rgba(15, 23, 42, 0.5);
color: var(--text-subtle);
font-size: 0.78rem;
text-transform: uppercase;
font-weight: 600;
padding: 0.85rem 1rem;
border-bottom: 1px solid var(--border-color);
letter-spacing: 0.05em;
}
.data-table td {
padding: 0.95rem 1rem;
border-bottom: 1px solid var(--border-color);
color: var(--text-main);
font-size: 0.88rem;
vertical-align: middle;
}
.data-table tbody tr {
transition: var(--transition-fast);
}
.data-table tbody tr:hover {
background: rgba(255, 255, 255, 0.03);
}
/* Badges */
.badge {
display: inline-flex;
align-items: center;
gap: 0.35rem;
padding: 0.25rem 0.65rem;
border-radius: var(--radius-full);
font-size: 0.75rem;
font-weight: 600;
}
.badge-success { background: var(--success-bg); color: var(--success); }
.badge-warning { background: var(--warning-bg); color: var(--warning); }
.badge-danger { background: var(--danger-bg); color: var(--danger); }
.badge-info { background: var(--info-bg); color: var(--info); }
.badge-primary { background: var(--primary-light); color: var(--primary); }
/* Buttons */
.btn {
padding: 0.55rem 1.1rem;
border-radius: var(--radius-md);
font-size: 0.88rem;
font-weight: 500;
cursor: pointer;
border: 1px solid transparent;
display: inline-flex;
align-items: center;
gap: 0.5rem;
transition: var(--transition-fast);
text-decoration: none;
}
.btn-secondary {
background: rgba(255, 255, 255, 0.06);
border-color: var(--border-color);
color: var(--text-main);
}
.btn-secondary:hover {
background: rgba(255, 255, 255, 0.12);
}
.btn-sm {
padding: 0.35rem 0.75rem;
font-size: 0.8rem;
}
/* Modal Overlay */
.modal-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.7);
backdrop-filter: blur(8px);
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
pointer-events: none;
transition: var(--transition-smooth);
}
.modal-overlay.active {
opacity: 1;
pointer-events: auto;
}
.modal-content {
background: var(--bg-card);
border: 1px solid var(--border-color);
border-radius: var(--radius-lg);
width: 90%;
max-width: 550px;
padding: 2rem;
box-shadow: var(--shadow-md);
transform: translateY(20px);
transition: var(--transition-smooth);
}
.modal-overlay.active .modal-content {
transform: translateY(0);
}
/* Filter Bar */
.filter-bar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
background: var(--bg-card);
padding: 1rem 1.25rem;
border-radius: var(--radius-lg);
border: 1px solid var(--border-color);
margin-bottom: 1.5rem;
flex-wrap: wrap;
}
.filter-inputs {
display: flex;
align-items: center;
gap: 0.75rem;
flex-wrap: wrap;
}
.select-input {
background: rgba(15, 23, 42, 0.7);
border: 1px solid var(--border-color);
color: var(--text-main);
padding: 0.5rem 1rem;
border-radius: var(--radius-md);
font-size: 0.88rem;
outline: none;
}
/* Responsive adjustments */
@media (max-width: 992px) {
.sidebar {
transform: translateX(-100%);
}
.sidebar.open {
transform: translateX(0);
}
.main-wrapper {
margin-left: 0;
}
.toggle-sidebar-btn {
display: inline-flex;
}
.search-box {
width: 200px;
}
}
/* ==========================================================================
Console additions — live data states, charts, toasts
========================================================================== */
/* Auth extras */
.secure-strip {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.6rem 0.85rem;
margin-bottom: 1.5rem;
border-radius: var(--radius-md);
background: var(--success-bg);
color: var(--success);
font-size: 0.78rem;
font-weight: 500;
}
.device-note {
display: flex;
align-items: center;
gap: 0.5rem;
margin-top: 1.25rem;
font-size: 0.74rem;
color: var(--text-subtle);
}
.device-note code {
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
color: var(--text-muted);
}
.btn-primary[disabled] {
opacity: 0.6;
cursor: not-allowed;
}
/* OTP modal */
.otp-icon {
width: 56px;
height: 56px;
border-radius: var(--radius-full);
background: var(--success-bg);
color: var(--success);
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 1.75rem;
margin-bottom: 1rem;
}
.otp-input {
text-align: center;
font-size: 1.5rem;
letter-spacing: 0.5rem;
font-weight: 700;
width: 150px;
padding-left: 1.25rem;
}
/* Header / nav */
.live-indicator[data-state="loading"] { color: var(--warning); }
.live-indicator[data-state="loading"] .pulse-dot { background: var(--warning); }
.live-indicator[data-state="error"] { color: var(--danger); }
.live-indicator[data-state="error"] .pulse-dot { background: var(--danger); animation: none; }
.nav-count {
margin-left: auto;
min-width: 20px;
padding: 0 0.4rem;
border-radius: var(--radius-full);
background: var(--danger);
color: #fff;
font-size: 0.7rem;
font-weight: 700;
text-align: center;
line-height: 20px;
}
.stamp {
font-size: 0.76rem;
color: var(--text-subtle);
white-space: nowrap;
}
.card-sub {
font-weight: 400;
font-size: 0.75rem;
color: var(--text-subtle);
margin-left: 0.4rem;
}
/* Table states */
.table-msg {
text-align: center;
padding: 2.25rem 1rem !important;
color: var(--text-subtle);
font-size: 0.85rem;
}
.table-msg.is-error { color: var(--danger); }
.route-cell {
font-size: 0.8rem;
color: var(--text-muted);
white-space: nowrap;
}
.danger-btn:hover {
background: var(--danger-bg);
color: var(--danger);
}
/* Charts */
.donut-wrap {
display: flex;
align-items: center;
justify-content: center;
}
.legend {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
margin-top: 0.75rem;
}
.legend-item {
display: inline-flex;
align-items: center;
gap: 0.4rem;
font-size: 0.78rem;
color: var(--text-muted);
}
.legend-item i {
width: 9px;
height: 9px;
border-radius: 3px;
display: inline-block;
}
.legend-item strong { color: var(--text-main); }
/* Key/value & distribution lists */
.mini-list {
display: flex;
flex-direction: column;
gap: 0.15rem;
}
.kv-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
padding: 0.7rem 0.25rem;
border-bottom: 1px solid var(--border-color);
font-size: 0.85rem;
color: var(--text-muted);
}
.kv-row:last-child { border-bottom: none; }
.kv-row strong { color: var(--text-main); font-weight: 600; text-align: right; }
.mix-row { padding: 0.55rem 0.25rem; }
.mix-head {
display: flex;
justify-content: space-between;
font-size: 0.85rem;
color: var(--text-muted);
margin-bottom: 0.45rem;
}
.mix-head strong { color: var(--text-main); }
.mix-pct { color: var(--text-subtle); font-weight: 400; font-size: 0.78rem; }
.mix-track {
height: 7px;
border-radius: var(--radius-full);
background: rgba(255, 255, 255, 0.06);
overflow: hidden;
}
.mix-fill {
height: 100%;
border-radius: var(--radius-full);
transition: width 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Modal head */
.modal-head {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 1rem;
}
.modal-head h3 { color: var(--text-main); font-family: 'Outfit', sans-serif; }
.modal-content { max-height: 85vh; overflow-y: auto; }
/* Toasts */
.toast {
position: fixed;
bottom: 24px;
right: 24px;
z-index: 9999;
display: flex;
align-items: center;
gap: 0.6rem;
max-width: 380px;
padding: 0.85rem 1.15rem;
border-radius: var(--radius-md);
background: var(--bg-elevated);
border: 1px solid var(--border-color);
border-left: 3px solid var(--primary);
color: var(--text-main);
font-size: 0.86rem;
box-shadow: var(--shadow-md);
animation: toastIn 0.28s cubic-bezier(0.4, 0, 0.2, 1);
}
.toast.leaving { opacity: 0; transform: translateY(8px); transition: var(--transition-smooth); }
.toast i { font-size: 1.15rem; }
.toast-success { border-left-color: var(--success); }
.toast-success i { color: var(--success); }
.toast-danger { border-left-color: var(--danger); }
.toast-danger i { color: var(--danger); }
.toast-warning { border-left-color: var(--warning); }
.toast-warning i { color: var(--warning); }
.toast-info i { color: var(--primary); }
@keyframes toastIn {
from { opacity: 0; transform: translateY(12px); }
to { opacity: 1; transform: translateY(0); }
}
@media (max-width: 560px) {
.toast { left: 16px; right: 16px; max-width: none; }
}
.chart-container canvas {
display: block;
width: 100%;
height: 100%;
}
.data-table td strong,
.data-table .badge {
white-space: nowrap;
}
/* Diagnostics panel */
.card-note {
font-size: 0.82rem;
color: var(--text-muted);
line-height: 1.6;
margin-bottom: 1rem;
}
.api-base-row {
display: flex;
gap: 0.6rem;
flex-wrap: wrap;
align-items: center;
}
.api-base-row .select-input { flex: 1; min-width: 240px; }
.api-base-row .form-input { flex: 1; min-width: 240px; padding-left: 1rem; }
.api-base-row .btn-primary { width: auto; padding: 0.55rem 1.1rem; }
.diagnostics {
margin: 0;
padding: 1rem;
max-height: 420px;
overflow: auto;
border-radius: var(--radius-md);
background: rgba(2, 6, 23, 0.75);
border: 1px solid var(--border-color);
color: var(--text-muted);
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 0.76rem;
line-height: 1.65;
white-space: pre-wrap;
word-break: break-word;
}
/* Generic module pages */
.module-panels {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.panel-body { min-height: 40px; }
.kpi-tiles {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
gap: 0.85rem;
}
.kpi-tile {
padding: 0.95rem 1.1rem;
border-radius: var(--radius-md);
background: rgba(255, 255, 255, 0.03);
border: 1px solid var(--border-color);
}
.kpi-tile-value {
font-family: 'Outfit', sans-serif;
font-size: 1.5rem;
font-weight: 600;
color: var(--text-main);
line-height: 1.2;
word-break: break-word;
}
.kpi-tile-label {
margin-top: 0.3rem;
font-size: 0.78rem;
color: var(--text-muted);
}
.sub-panel { margin-top: 1.4rem; }
.sub-panel-title {
font-size: 0.82rem;
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--text-subtle);
margin-bottom: 0.75rem;
}
/* Inline search + clickable rows */
.search-inline {
display: flex;
gap: 0.5rem;
align-items: center;
flex: 1;
min-width: 260px;
}
.search-inline .form-input {
padding-left: 1rem;
font-size: 0.85rem;
}
.row-clickable { cursor: pointer; }
.row-clickable:hover { background: rgba(99, 102, 241, 0.08) !important; }
.load-more {
display: flex;
justify-content: center;
padding: 1rem 0 0.25rem;
}
.decrypt-area {
padding: 0.85rem 1rem;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 0.8rem;
resize: vertical;
min-height: 84px;
}
.coord-link { color: var(--text-muted); text-decoration: none; }
.coord-link:hover { color: var(--primary); }
/* Tariff editor */
.notice-card {
display: flex;
align-items: flex-start;
gap: 0.7rem;
font-size: 0.85rem;
color: var(--text-muted);
line-height: 1.6;
}
.notice-card i { font-size: 1.2rem; color: var(--info); flex-shrink: 0; }
.notice-danger { border-color: rgba(244, 63, 94, 0.35); }
.notice-danger i { color: var(--danger); }
.notice-card strong { color: var(--text-main); }
.tariff-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 0.9rem;
}
.tariff-field { display: flex; flex-direction: column; gap: 0.35rem; }
.tariff-label {
font-size: 0.78rem;
color: var(--text-muted);
font-weight: 500;
}
.tariff-label em {
font-style: normal;
color: var(--text-subtle);
font-size: 0.72rem;
}
.tariff-field .form-input { padding-left: 1rem; font-size: 0.9rem; }
.tariff-field .form-input:disabled { opacity: 0.65; cursor: not-allowed; }
/* Route approvals */
.stop-list {
margin: 0;
padding-left: 1.2rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
color: var(--text-muted);
font-size: 0.85rem;
}
.stop-list li {
display: flex;
align-items: center;
gap: 0.6rem;
flex-wrap: wrap;
}
.stop-list li span:first-child { color: var(--text-main); }
/* Broadcast preview */
.push-preview {
max-width: 420px;
padding: 1rem 1.15rem;
border-radius: var(--radius-md);
background: rgba(255, 255, 255, 0.06);
border: 1px solid var(--border-color);
box-shadow: var(--shadow-sm);
}
.push-app {
display: flex;
align-items: center;
gap: 0.4rem;
font-size: 0.72rem;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--text-subtle);
margin-bottom: 0.5rem;
}
.push-app i { color: var(--primary); }
.push-title {
font-weight: 600;
color: var(--text-main);
margin-bottom: 0.2rem;
word-break: break-word;
}
.push-body {
font-size: 0.86rem;
color: var(--text-muted);
line-height: 1.5;
white-space: pre-wrap;
word-break: break-word;
}
/* Bidirectional text: names, addresses and messages are often Arabic while the
UI chrome is English. `plaintext` lets each value pick its own direction
from its first strong character instead of inheriting the page's LTR. */
.form-input,
.data-table td,
.push-title,
.push-body,
.kv-row strong,
.stop-list li span:first-child,
.kpi-tile-value {
unicode-bidi: plaintext;
}
textarea.form-input { text-align: start; }
/* Driver document review */
.doc-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 1rem;
}
.doc-card {
margin: 0;
border-radius: var(--radius-md);
border: 1px solid var(--border-color);
background: rgba(255, 255, 255, 0.03);
overflow: hidden;
}
.doc-card img {
display: block;
width: 100%;
height: 150px;
object-fit: cover;
background: rgba(2, 6, 23, 0.6);
transition: var(--transition-fast);
}
.doc-card img:hover { opacity: 0.85; }
.doc-missing {
display: flex;
align-items: center;
justify-content: center;
gap: 0.4rem;
height: 150px;
color: var(--text-subtle);
font-size: 0.8rem;
background: rgba(2, 6, 23, 0.6);
}
.doc-card figcaption {
padding: 0.6rem 0.75rem;
display: flex;
flex-direction: column;
gap: 0.15rem;
font-size: 0.8rem;
color: var(--text-main);
border-top: 1px solid var(--border-color);
}
.doc-card figcaption .stamp { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* .btn-primary is full-width by default (login form); inline uses must not be */
.btn-primary.btn-sm {
width: auto;
padding: 0.4rem 0.9rem;
justify-content: center;
}
.card-header { gap: 1rem; flex-wrap: wrap; }
/* Destructive action button */
.btn-danger {
background: var(--danger-bg);
border-color: rgba(244, 63, 94, 0.45);
color: var(--danger);
font-weight: 600;
}
.btn-danger:hover { background: rgba(244, 63, 94, 0.28); }
/* ==========================================================================
Right-to-left layout (Arabic)
The base layout is written LTR with physical properties, so RTL flips the
fixed sidebar, the content offset and anything anchored to a screen edge.
========================================================================== */
.lang-toggle {
font-weight: 700;
font-size: 0.85rem;
font-family: 'Outfit', sans-serif;
}
[dir="rtl"] .sidebar {
left: auto;
right: 0;
border-right: none;
border-left: 1px solid var(--border-color);
}
[dir="rtl"] .main-wrapper {
margin-left: 0;
margin-right: var(--sidebar-width);
}
[dir="rtl"] .nav-item.active::before,
[dir="rtl"] .nav-item::before {
left: auto;
right: 0;
}
[dir="rtl"] .toast {
right: auto;
left: 24px;
}
/* The icon sits at the inline start; in RTL that is the right edge, and the
input's reserved padding must move with it. */
[dir="rtl"] .form-input-group i {
left: auto;
right: 1rem;
}
[dir="rtl"] .form-input-group .form-input {
padding-left: 1rem;
padding-right: 2.8rem;
}
[dir="rtl"] .search-box i {
left: auto;
right: 0.9rem;
}
[dir="rtl"] .search-box input {
padding-left: 1rem;
padding-right: 2.5rem;
}
[dir="rtl"] .nav-count { margin-left: 0; margin-right: auto; }
[dir="rtl"] .card-sub { margin-left: 0; margin-right: 0.4rem; }
[dir="rtl"] .kv-row strong { text-align: left; }
[dir="rtl"] .stop-list { padding-left: 0; padding-right: 1.2rem; }
/* Directional icons must mirror, plain symbols must not */
[dir="rtl"] .route-cell .ph-arrow-right,
[dir="rtl"] .btn .ph-caret-left,
[dir="rtl"] .btn .ph-caret-right {
transform: scaleX(-1);
}
/* Numbers, code and diagnostics stay LTR even inside an RTL page */
[dir="rtl"] .diagnostics,
[dir="rtl"] .device-note code,
[dir="rtl"] .stat-value,
[dir="rtl"] .kpi-tile-value {
direction: ltr;
text-align: start;
}
@media (max-width: 992px) {
[dir="rtl"] .sidebar { transform: translateX(100%); }
[dir="rtl"] .sidebar.open { transform: translateX(0); }
[dir="rtl"] .main-wrapper { margin-right: 0; }
}
/* Persistent sign-in error */
.login-error {
display: flex;
align-items: flex-start;
gap: 0.6rem;
margin-top: 1.25rem;
padding: 0.8rem 0.95rem;
border-radius: var(--radius-md);
background: var(--danger-bg);
border: 1px solid rgba(244, 63, 94, 0.4);
color: var(--danger);
font-size: 0.82rem;
line-height: 1.55;
}
/* display:flex would otherwise override the [hidden] attribute */
.login-error[hidden] { display: none; }
.login-error i { font-size: 1.1rem; flex-shrink: 0; }
.login-error div { display: flex; flex-direction: column; gap: 0.25rem; min-width: 0; }
.login-error strong { color: var(--text-main); font-weight: 600; }
.login-error span {
color: var(--text-muted);
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 0.72rem;
word-break: break-all;
direction: ltr;
text-align: start;
}
/* Build identifier, always visible in the sidebar footer */
.build-stamp {
padding: 0.45rem 1.25rem 0.75rem;
font-size: 0.68rem;
letter-spacing: 0.04em;
color: var(--text-subtle);
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
direction: ltr;
text-align: center;
border-top: 1px solid var(--border-color);
}
/* Long values in generated tables */
.cell-long {
cursor: pointer;
border-bottom: 1px dotted var(--text-subtle);
}
.cell-long:hover { color: var(--primary); }
.cell-full {
display: block;
max-width: 460px;
white-space: pre-wrap;
word-break: break-word;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 0.74rem;
color: var(--text-muted);
}
.data-table td { max-width: 420px; }
/* ==========================================================================
Motion — used sparingly, and only where it carries meaning:
arrival of content, and the moment data is being fetched.
========================================================================== */
@keyframes riseIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.page-view.active .stat-card,
.page-view.active > .card,
.module-panels > .card {
animation: riseIn 0.34s cubic-bezier(0.22, 1, 0.36, 1) backwards;
}
/* Stagger so a grid resolves left-to-right instead of snapping in at once */
.page-view.active .stats-grid .stat-card:nth-child(1) { animation-delay: 0.00s; }
.page-view.active .stats-grid .stat-card:nth-child(2) { animation-delay: 0.05s; }
.page-view.active .stats-grid .stat-card:nth-child(3) { animation-delay: 0.10s; }
.page-view.active .stats-grid .stat-card:nth-child(4) { animation-delay: 0.15s; }
.module-panels > .card:nth-child(2) { animation-delay: 0.06s; }
.module-panels > .card:nth-child(3) { animation-delay: 0.12s; }
.stat-card, .card { transition: transform 0.25s ease, border-color 0.25s ease; }
.stat-card:hover { transform: translateY(-3px); }
/* Rows settle in rather than appearing fully formed */
.data-table tbody tr { animation: riseIn 0.25s ease backwards; }
.data-table tbody tr:nth-child(1) { animation-delay: 0.02s; }
.data-table tbody tr:nth-child(2) { animation-delay: 0.04s; }
.data-table tbody tr:nth-child(3) { animation-delay: 0.06s; }
.data-table tbody tr:nth-child(4) { animation-delay: 0.08s; }
.data-table tbody tr:nth-child(n+5) { animation-delay: 0.10s; }
/* The refresh control spins while a request is in flight, so a section that
returns nothing still visibly did something. */
@keyframes spin { to { transform: rotate(360deg); } }
.btn-icon.is-busy i { animation: spin 0.8s linear infinite; }
.live-indicator[data-state="loading"] { animation: pulseSoft 1.4s ease-in-out infinite; }
@keyframes pulseSoft { 50% { opacity: 0.55; } }
/* Respect users who ask the system for less motion */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}