/* ======================================== Users Page - Scoped Component Styles ======================================== Inherits from app.css: - .access-denied, .empty-icon, .access-denied-icon (section 19) - .alert, .alert-danger/success/warning (section 7) - .modal-backdrop, .app-modal, .modal-dialog/content/header/body/footer (section 10b) - .modal-body .form-label, .required, .mb-3 (section 10b) - .form-control, .form-select, .form-label (section 4) - .btn, .btn-primary/secondary/danger, .btn-sm, .btn-close (section 5) - .spinner-border (section 11) - .invalid-feedback → validation-message (section 12) - Reduced motion (section 17) Only page-specific additions and overrides below. ======================================== */ /* ======================================== Page Container ======================================== */ .users-container { padding: var(--space-6) var(--space-page-x); max-width: 1400px; margin: 0 auto; } @media (min-width: 1024px) { .users-container { padding: var(--space-8) var(--space-12); } } @media (min-width: 1440px) { .users-container { padding: var(--space-10) 4rem; } } /* ======================================== Header ======================================== */ .header-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: var(--space-8); gap: var(--space-6); } .header-info h1 { margin: 0; font-family: var(--font-family-heading); font-size: clamp(1.5rem, 3vw, 2rem); color: var(--text-primary); letter-spacing: -0.02em; } .header-subtitle { margin: var(--space-2) 0 0 0; font-family: var(--font-family-base); font-size: var(--text-sm); color: var(--text-tertiary); } .header-actions { display: flex; gap: var(--space-3); align-items: center; } .header-actions .btn { white-space: nowrap; } /* ======================================== Loading & Empty States ======================================== */ .loading-spinner { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: var(--space-10) var(--space-8); color: var(--text-tertiary); font-family: var(--font-family-base); } .loading-spinner .spinner-border { width: 3rem; height: 3rem; color: var(--color-primary-light); margin-bottom: var(--space-6); } .empty-state { text-align: center; padding: var(--space-10) var(--space-8); color: var(--text-tertiary); } .empty-state h3 { margin: 0 0 var(--space-3) 0; color: var(--text-primary); font-family: var(--font-family-base); font-size: var(--text-xl); } .empty-state p { margin: 0; font-family: var(--font-family-base); } /* ======================================== Alerts (page-specific overrides) ======================================== */ .alert { justify-content: space-between; margin-bottom: var(--space-6); } .alert-dismiss { background: none; border: none; color: inherit; font-size: var(--text-2xl); cursor: pointer; padding: 0 0 0 var(--space-4); opacity: 0.7; transition: opacity var(--duration-fast) var(--ease-default); line-height: 1; flex-shrink: 0; } .alert-dismiss:hover { opacity: 1; } /* ======================================== Users Table ======================================== */ .users-table { background: var(--surface-elevated); border-radius: var(--radius-xl); border: 0.5px solid var(--border-default); overflow: hidden; box-shadow: var(--shadow-md); } .table-header { display: grid; grid-template-columns: 1.5fr 1.5fr 0.8fr 0.7fr 0.7fr 1fr 140px; padding: var(--space-4) var(--space-6); background: rgba(0, 0, 0, 0.3); border-bottom: 0.5px solid var(--border-light); font-family: var(--font-family-base); font-weight: var(--font-weight-semibold); font-size: var(--text-xs); color: var(--text-tertiary); text-transform: uppercase; letter-spacing: 0.08em; gap: var(--space-4); } .table-body { max-height: 65vh; overflow-y: auto; -webkit-overflow-scrolling: touch; scrollbar-width: thin; scrollbar-color: rgba(255, 255, 255, 0.15) transparent; } .table-body::-webkit-scrollbar { width: 8px; } .table-body::-webkit-scrollbar-track { background: rgba(0, 0, 0, 0.2); } .table-body::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.15); border-radius: 4px; } .table-body::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.25); } .table-row { display: grid; grid-template-columns: 1.5fr 1.5fr 0.8fr 0.7fr 0.7fr 1fr 140px; padding: var(--space-5) var(--space-6); border-bottom: 0.5px solid var(--border-light); align-items: center; gap: var(--space-4); transition: background var(--duration-fast) var(--ease-out); } .table-row:last-child { border-bottom: none; } .table-row:hover { background: rgba(255, 255, 255, 0.04); } .table-row.inactive { opacity: 0.5; } .table-row.current-user { background: rgba(220, 38, 38, 0.06); } .table-row.current-user:hover { background: rgba(220, 38, 38, 0.1); } /* ======================================== Table Columns ======================================== */ .col-username, .col-displayname, .col-lastlogin { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .col-username { font-family: var(--font-family-base); font-weight: var(--font-weight-semibold); font-size: var(--text-base); color: var(--text-primary); display: flex; align-items: center; gap: var(--space-2); } .col-displayname { color: var(--text-secondary); font-family: var(--font-family-base); font-size: var(--text-sm); } .col-role, .col-status, .col-2fa { display: flex; align-items: center; } .col-lastlogin { color: var(--text-placeholder); font-size: var(--text-sm); font-family: var(--font-family-base); } .col-actions { display: flex; gap: var(--space-2); justify-content: flex-end; flex-wrap: wrap; } /* ======================================== Badges ======================================== */ /* Current User */ .current-user-badge { display: inline-flex; align-items: center; padding: var(--space-1) var(--space-2); background: rgba(220, 38, 38, 0.15); color: var(--color-error-light); border-radius: var(--radius-full); font-size: var(--text-xs); font-weight: var(--font-weight-semibold); text-transform: uppercase; letter-spacing: 0.04em; flex-shrink: 0; } /* Roles */ .role-badge { display: inline-flex; align-items: center; padding: var(--space-1) var(--space-3); border-radius: var(--radius-full); font-size: var(--text-xs); font-family: var(--font-family-base); font-weight: var(--font-weight-semibold); white-space: nowrap; } .role-badge.role-admin { background: rgba(220, 38, 38, 0.15); color: var(--color-error-light); } .role-badge.role-worker { background: rgba(59, 130, 246, 0.15); color: var(--color-info); } .role-badge.role-user { background: rgba(107, 114, 128, 0.15); color: var(--text-tertiary); } /* Status */ .status-badge { display: inline-flex; align-items: center; padding: var(--space-1) var(--space-2); border-radius: var(--radius-sm); font-size: var(--text-xs); font-family: var(--font-family-base); font-weight: var(--font-weight-semibold); white-space: nowrap; } .status-badge.active { background: rgba(34, 197, 94, 0.15); color: var(--color-success-light); } .status-badge.inactive { background: rgba(107, 114, 128, 0.15); color: var(--text-tertiary); } /* 2FA */ .tfa-badge { display: inline-flex; align-items: center; gap: var(--space-1); padding: var(--space-1) var(--space-2); border-radius: var(--radius-md); font-size: var(--text-xs); font-weight: var(--font-weight-semibold); white-space: nowrap; } .tfa-badge.tfa-on { background: rgba(34, 197, 94, 0.15); color: var(--color-success); border: 0.5px solid rgba(34, 197, 94, 0.3); } .tfa-badge.tfa-off { background: rgba(255, 255, 255, 0.05); color: var(--text-placeholder); border: 0.5px solid var(--border-default); } /* ======================================== Action Buttons ======================================== */ .btn-icon-sm { width: var(--touch-target-sm); height: var(--touch-target-sm); border: none; background: rgba(255, 255, 255, 0.08); color: var(--text-tertiary); border-radius: var(--radius-md); cursor: pointer; transition: all var(--duration-fast) var(--ease-out); display: flex; align-items: center; justify-content: center; flex-shrink: 0; -webkit-tap-highlight-color: transparent; } .btn-icon-sm:hover { background: rgba(255, 255, 255, 0.15); color: var(--text-primary); transform: translateY(-1px); } .btn-icon-sm:active { transform: scale(0.92); } .btn-icon-sm.danger:hover { background: rgba(220, 38, 38, 0.2); color: var(--color-error-light); } .btn-icon-sm.warning { color: var(--color-warning); } .btn-icon-sm.warning:hover { background: rgba(245, 158, 11, 0.15); } /* ======================================== Modal Content (page-specific additions) ======================================== */ /* Modal form controls — slightly larger padding than global default */ .modal-body .form-control, .modal-body .form-select { padding: var(--space-3) var(--space-5); border-radius: var(--radius-lg); } .modal-body .form-select option { background: var(--surface-primary); color: var(--text-primary); padding: var(--space-2); } .modal-body .form-text { display: block; margin-top: var(--space-2); font-size: var(--text-sm); color: var(--text-muted); font-family: var(--font-family-base); } .modal-body .form-text.text-warning { color: var(--color-warning); } .invalid-feedback { display: block; margin-top: var(--space-2); font-size: var(--text-sm); color: var(--color-error-light); font-family: var(--font-family-base); } /* Checkbox */ .form-check { display: flex; align-items: center; gap: var(--space-3); cursor: pointer; padding: var(--space-2) 0; } .form-check-input { width: 22px; height: 22px; accent-color: var(--color-primary); border-radius: var(--radius-sm); cursor: pointer; flex-shrink: 0; } .form-check-label { font-family: var(--font-family-base); color: var(--text-secondary); cursor: pointer; font-size: var(--text-base); } /* User Info Box (password modal) */ .user-info-box { display: flex; align-items: center; gap: var(--space-2); padding: var(--space-3) var(--space-5); background: rgba(0, 0, 0, 0.2); border-radius: var(--radius-lg); font-family: var(--font-family-base); } .user-info-box .label { color: var(--text-muted); } .user-info-box .value { font-weight: var(--font-weight-semibold); color: var(--text-primary); } /* Delete Warning */ .delete-warning { text-align: center; padding: var(--space-6) 0; } .warning-icon { font-family: var(--font-family-emoji); font-size: 3rem; margin-bottom: var(--space-5); line-height: 1; } .warning-text { font-family: var(--font-family-base); font-size: var(--text-lg); color: var(--text-secondary); margin-bottom: var(--space-3); } .warning-subtext { font-family: var(--font-family-base); font-size: var(--text-sm); color: var(--text-muted); } /* ======================================== 2FA Setup Modal ======================================== */ .tfa-setup-instructions { font-family: var(--font-family-base); color: var(--text-secondary); font-size: var(--text-sm); margin-bottom: var(--space-5); line-height: var(--line-height-relaxed); } .tfa-setup-instructions p { margin: 0; } .tfa-qr-container { display: flex; justify-content: center; padding: var(--space-6); margin-bottom: var(--space-5); background: rgba(255, 255, 255, 0.04); border-radius: var(--radius-xl); border: 0.5px solid var(--border-light); } .tfa-qr-image { width: 220px; height: 220px; max-width: 100%; border-radius: var(--radius-md); image-rendering: pixelated; } .tfa-manual-key { display: flex; flex-direction: column; gap: var(--space-2); padding: var(--space-3) var(--space-5); background: rgba(0, 0, 0, 0.25); border-radius: var(--radius-md); border: 0.5px solid var(--border-light); margin-bottom: var(--space-6); } .tfa-manual-key .label { font-family: var(--font-family-base); font-size: var(--text-xs); color: var(--text-placeholder); text-transform: uppercase; letter-spacing: 0.04em; } .tfa-manual-key .secret-key { font-family: 'Courier New', monospace; font-size: var(--text-base); color: var(--text-primary); letter-spacing: 0.15em; word-break: break-all; user-select: all; background: none; padding: 0; } .tfa-confirm-section { margin-top: var(--space-2); } .tfa-confirm-input { font-family: var(--font-family-base); font-size: var(--text-xl); text-align: center; letter-spacing: 0.3em; font-weight: var(--font-weight-semibold); } /* ======================================== Responsive — Mobile Card Layout ======================================== */ @media (max-width: 900px) { .users-container { padding: var(--space-4) var(--space-3); padding-bottom: calc(var(--space-4) + var(--safe-area-bottom)); } .header-row { flex-direction: column; align-items: stretch; gap: var(--space-4); } .header-actions { width: 100%; } .header-actions .btn { flex: 1; justify-content: center; } /* Hide grid header; rows become cards */ .table-header { display: none; } .users-table { border-radius: var(--radius-lg); } .table-row { display: flex; flex-wrap: wrap; gap: var(--space-2); padding: var(--space-5) var(--space-4); } .col-username { flex: 1 1 100%; font-size: var(--text-lg); margin-bottom: 2px; white-space: normal; } .col-displayname { flex: 1 1 100%; font-size: var(--text-sm); margin-bottom: var(--space-2); color: var(--text-tertiary); white-space: normal; } .col-role, .col-status, .col-2fa { flex: 0 1 auto; } .col-lastlogin { flex: 1 1 100%; order: 10; margin-top: var(--space-2); padding-top: var(--space-2); border-top: 0.5px solid var(--border-light); font-size: var(--text-sm); white-space: normal; } .col-actions { width: auto; flex: 0 1 auto; order: 5; margin-left: auto; } } /* ======================================== Large Desktop Optimizations ======================================== */ @media (min-width: 1200px) { .table-header { grid-template-columns: 1.8fr 1.5fr 0.8fr 0.7fr 0.7fr 1fr 160px; } .table-row { grid-template-columns: 1.8fr 1.5fr 0.8fr 0.7fr 0.7fr 1fr 160px; } .col-actions { gap: var(--space-2); } .btn-icon-sm { width: 40px; height: 40px; } }