Files
PassKeeper/app/static/css/app.css
T

2466 lines
46 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.
/* ── Reset & base ──────────────────────────────────────────────────── */
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--color-bg: #f4f5f7;
--color-surface: #ffffff;
--color-primary: #d0021b;
--color-primary-dark: #a80016;
--color-text: #1a1a2e;
--color-text-muted: #6b7280;
--color-border: #e2e4e9;
--color-sidebar-bg: #1e2235;
--color-sidebar-text: #c8ccd8;
--color-sidebar-hover: #2d3250;
--color-sidebar-active: #d0021b;
--radius: 8px;
--shadow: 0 1px 4px rgba(0, 0, 0, 0.08), 0 2px 12px rgba(0, 0, 0, 0.05);
--transition: 150ms ease;
}
html,
body {
height: 100%;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
font-size: 14px;
color: var(--color-text);
background: var(--color-bg);
}
a {
color: var(--color-primary);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
button {
cursor: pointer;
font: inherit;
border: none;
background: none;
}
input,
select,
textarea {
font: inherit;
}
ul {
list-style: none;
}
/* ── Utilities ───────────────────────────────────────────────────── */
.hidden {
display: none !important;
}
/* ── Auth pages ──────────────────────────────────────────────────── */
.auth-page {
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
background: var(--color-bg);
}
.auth-container {
width: 100%;
max-width: 420px;
padding: 16px;
}
.auth-card {
background: var(--color-surface);
border-radius: var(--radius);
box-shadow: var(--shadow);
padding: 40px 36px;
}
@media (max-width: 480px) {
.auth-container {
padding: 12px;
}
.auth-card {
padding: 28px 20px;
border-radius: 12px;
}
.auth-title {
font-size: 20px;
}
}
.auth-logo {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 28px;
}
.auth-logo .logo-icon {
font-size: 28px;
}
.auth-logo .logo-text {
font-size: 20px;
font-weight: 700;
color: var(--color-text);
}
.auth-title {
font-size: 22px;
font-weight: 600;
margin-bottom: 24px;
}
.auth-footer {
margin-top: 20px;
text-align: center;
color: var(--color-text-muted);
}
/* ── Form elements ──────────────────────────────────────────────── */
.form-group {
margin-bottom: 16px;
}
.form-group label {
display: block;
margin-bottom: 6px;
font-weight: 500;
color: var(--color-text);
}
.form-group label .hint {
font-weight: 400;
color: var(--color-text-muted);
font-size: 12px;
}
.form-group input,
.form-group select,
.form-group textarea {
width: 100%;
padding: 9px 12px;
border: 1px solid var(--color-border);
border-radius: var(--radius);
background: var(--color-surface);
color: var(--color-text);
transition: border-color var(--transition);
}
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 3px rgba(208, 2, 27, 0.12);
}
/* Prevent iOS viewport auto-zoom on input focus (requires font-size >= 16px) */
@media (max-width: 768px) {
.form-group input,
.form-group select,
.form-group textarea {
font-size: 16px;
}
}
.input-with-toggle {
position: relative;
}
.input-with-toggle input {
padding-right: 40px;
}
.btn-show-pass {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
background: none;
border: none;
cursor: pointer;
font-size: 16px;
opacity: 0.6;
transition: opacity var(--transition);
}
.btn-show-pass:hover {
opacity: 1;
}
.form-error {
color: #c00;
background: #fff0f0;
border: 1px solid #fcc;
border-radius: var(--radius);
padding: 8px 12px;
margin-bottom: 12px;
font-size: 13px;
}
/* Buttons */
.btn-primary {
display: inline-flex;
align-items: center;
justify-content: center;
background: var(--color-primary);
color: #fff;
padding: 10px 20px;
border-radius: var(--radius);
font-weight: 600;
transition: background var(--transition);
}
.btn-primary:hover {
background: var(--color-primary-dark);
}
.btn-primary:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.btn-full {
width: 100%;
}
.btn-secondary {
display: inline-flex;
align-items: center;
justify-content: center;
background: var(--color-border);
color: var(--color-text);
padding: 10px 20px;
border-radius: var(--radius);
font-weight: 500;
transition: background var(--transition);
}
.btn-secondary:hover {
background: #d1d5dc;
}
.btn-text {
background: none;
border: none;
color: inherit;
padding: 0;
}
/* Notices */
.notice-success {
background: #e8f5e9;
border: 1px solid #a5d6a7;
color: #2e7d32;
border-radius: var(--radius);
padding: 10px 14px;
margin-bottom: 16px;
font-size: 13px;
}
.notice-info {
background: #e3f2fd;
border: 1px solid #90caf9;
color: #1565c0;
border-radius: var(--radius);
padding: 10px 14px;
margin-bottom: 16px;
font-size: 13px;
}
/* ── Password Generator modal ────────────────────────────────────────── */
.gen-opt {
display: flex;
align-items: center;
gap: 6px;
cursor: pointer;
font-size: 13px;
}
.gen-opt input[type="checkbox"] {
width: 15px;
height: 15px;
accent-color: var(--color-primary);
cursor: pointer;
}
#gen-output {
letter-spacing: 0.5px;
font-family: "Courier New", monospace;
}
/* Launch (open URL) button — distinct colour so it reads as navigation */
.btn-icon[data-action="launch"] {
color: #1a73e8;
}
.btn-icon[data-action="launch"]:hover {
background: #e8f0fe;
color: #1558b0;
}
/* ── Vault list items ────────────────────────────────────────────── */
.vault-item {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 14px;
border-bottom: 1px solid var(--color-border);
cursor: default;
transition: background var(--transition);
}
.vault-item:last-child {
border-bottom: none;
}
.vault-item:hover {
background: #f9fafb;
}
.item-icon {
font-size: 20px;
flex-shrink: 0;
}
.item-info {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 2px;
}
.item-name {
font-size: 14px;
font-weight: 600;
color: var(--color-text);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.item-sub {
font-size: 12px;
color: var(--color-text-muted);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.item-actions {
display: flex;
align-items: center;
gap: 2px;
flex-shrink: 0;
}
/* ── TOTP live display in vault item ─────────────────────────────── */
.item-totp {
display: inline-flex;
align-items: center;
gap: 0;
font-size: 12px;
font-weight: 700;
font-family: "Courier New", Courier, monospace;
color: #1a73e8;
margin-top: 1px;
letter-spacing: 0.5px;
}
.totp-timer {
font-size: 11px;
font-weight: 400;
color: #9ca3af;
font-family: inherit;
}
/* Copy TOTP button — distinct teal so it reads as 2FA, not password */
.btn-icon[data-action="copy-totp"] {
color: #0891b2;
}
.btn-icon[data-action="copy-totp"]:hover {
background: #e0f2fe;
color: #0369a1;
}
/* Password strength */
.password-strength {
margin-top: 6px;
font-size: 12px;
font-weight: 500;
}
.strength-1 {
color: #c62828;
}
.strength-2 {
color: #e65100;
}
.strength-3 {
color: #f57f17;
}
.strength-4 {
color: #2e7d32;
}
.strength-5 {
color: #1b5e20;
}
/* ── App layout (vault) ──────────────────────────────────────────── */
.vault-page {
height: 100vh;
height: -webkit-fill-available;
height: calc(var(--vh, 1vh) * 100);
overflow: hidden;
}
.app-layout {
display: flex;
height: 100%;
}
/* ── Vault main ──────────────────────────────────────────────────── */
.vault-main {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
background: var(--color-bg);
}
#view-vault,
#view-security {
display: flex;
flex-direction: column;
flex: 1;
overflow: hidden;
min-height: 0;
position: relative;
}
#view-vault.hidden,
#view-security.hidden {
display: none;
}
.vault-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 24px;
background: var(--color-surface);
border-bottom: 1px solid var(--color-border);
gap: 16px;
flex-shrink: 0;
}
.vault-header-inner {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
gap: 16px;
}
.vault-title {
font-size: 18px;
font-weight: 600;
white-space: nowrap;
}
.vault-toolbar {
display: flex;
align-items: center;
gap: 12px;
flex: 1;
justify-content: flex-end;
}
.search-wrapper {
display: flex;
align-items: center;
background: var(--color-bg);
border: 1px solid var(--color-border);
border-radius: 20px;
padding: 6px 14px;
gap: 8px;
flex: 1;
max-width: 360px;
}
.search-icon {
color: var(--color-text-muted);
}
#search-input {
border: none;
background: none;
outline: none;
flex: 1;
font-size: 14px;
}
/* ── Sort control ────────────────────────────────────────────────── */
.sort-wrapper select {
padding: 6px 10px;
border: 1px solid var(--color-border);
border-radius: 20px;
background: var(--color-bg);
color: var(--color-text);
font-size: 13px;
cursor: pointer;
outline: none;
}
.sort-wrapper select:focus {
border-color: var(--color-primary);
}
.btn-fab {
width: 40px;
height: 40px;
border-radius: 50%;
background: var(--color-primary);
color: #fff;
font-size: 24px;
font-weight: 300;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 8px rgba(208, 2, 27, 0.35);
transition: background var(--transition), box-shadow var(--transition);
flex-shrink: 0;
}
.btn-fab:hover {
background: var(--color-primary-dark);
box-shadow: 0 4px 14px rgba(208, 2, 27, 0.45);
}
/* ── Vault list ──────────────────────────────────────────────────── */
.vault-list {
flex: 1;
overflow-y: auto;
padding: 12px 24px 24px;
}
.vault-list-inner {
max-width: 900px;
}
.vault-group-header {
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--color-text-muted);
padding: 16px 0 6px;
border-bottom: 1px solid var(--color-border);
margin-bottom: 4px;
}
.vault-empty {
text-align: center;
color: var(--color-text-muted);
padding: 60px 0;
font-size: 15px;
}
.btn-icon {
width: 30px;
height: 30px;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
font-size: 15px;
color: var(--color-text-muted);
transition: background var(--transition), color var(--transition);
flex-shrink: 0;
}
.btn-icon:hover {
background: var(--color-bg);
color: var(--color-text);
}
/* ── Spinner ─────────────────────────────────────────────────────── */
.spinner-overlay {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.7);
z-index: 10;
}
.spinner-overlay.hidden {
display: none;
}
.spinner {
width: 36px;
height: 36px;
border: 3px solid var(--color-border);
border-top-color: var(--color-primary);
border-radius: 50%;
animation: spin 0.7s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
/* ── Modal ───────────────────────────────────────────────────────── */
.modal-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.45);
display: none;
align-items: center;
justify-content: center;
z-index: 100;
padding: 16px;
}
.modal-overlay.open {
display: flex;
}
.modal {
background: var(--color-surface);
border-radius: var(--radius);
box-shadow: 0 8px 40px rgba(0, 0, 0, 0.2);
width: 100%;
max-width: 480px;
max-height: 90vh;
overflow-y: auto;
padding: 28px;
}
.modal-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20px;
}
.modal-header h3 {
font-size: 18px;
font-weight: 600;
}
.btn-close {
font-size: 18px;
color: var(--color-text-muted);
padding: 4px;
border-radius: 4px;
}
.btn-close:hover {
background: var(--color-bg);
color: var(--color-text);
}
.modal-actions {
display: flex;
gap: 10px;
justify-content: flex-end;
margin-top: 20px;
}
.required {
color: var(--color-primary);
}
/* ── Modal item (wider for address/card forms) ───────────────────── */
.modal-item {
max-width: 540px;
}
/* ── Multi-column form rows ──────────────────────────────────────── */
.form-row {
display: flex;
gap: 12px;
}
.form-row .form-group {
flex: 1;
}
/* ── Btn small ───────────────────────────────────────────────────── */
.btn-sm {
padding: 5px 12px;
font-size: 12px;
border-radius: 6px;
}
/* ── Toast ───────────────────────────────────────────────────────── */
.toast {
position: fixed;
bottom: 24px;
left: 50%;
transform: translateX(-50%) translateY(20px);
background: #1a1a2e;
color: #fff;
padding: 10px 20px;
border-radius: 20px;
font-size: 14px;
font-weight: 500;
opacity: 0;
transition: opacity 0.25s, transform 0.25s;
pointer-events: none;
z-index: 200;
white-space: nowrap;
}
.toast.show {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
.toast.toast-error {
background: #c00;
}
/* ── Unlock overlay ──────────────────────────────────────────────── */
.modal-unlock {
max-width: 380px;
}
.unlock-logo {
margin-bottom: 16px;
}
.unlock-title {
margin-bottom: 4px;
}
.unlock-subtitle {
color: var(--color-text-muted);
font-size: 13px;
margin-bottom: 20px;
}
.btn-link {
padding: 10px 20px;
border-radius: var(--radius);
text-decoration: none;
}
/* ── Auth step hint ─────────────────────────────────────────────── */
.auth-step-hint {
color: var(--color-text-muted);
font-size: 13px;
margin-bottom: 16px;
}
#mfa-code {
font-size: 22px;
letter-spacing: 0.2em;
text-align: center;
}
/* ── Panel body (sharing / emergency) ───────────────────────────── */
.panel-body {
flex: 1;
overflow-y: auto;
padding: 20px 24px;
}
/* ── Tabs ────────────────────────────────────────────────────────── */
.panel-tabs {
display: flex;
gap: 4px;
margin-bottom: 16px;
border-bottom: 2px solid var(--color-border);
padding-bottom: 0;
}
.tab-btn {
padding: 8px 16px;
border-radius: 6px 6px 0 0;
font-size: 13px;
font-weight: 500;
color: var(--color-text-muted);
border-bottom: 2px solid transparent;
margin-bottom: -2px;
transition: color var(--transition), border-color var(--transition);
}
.tab-btn:hover {
color: var(--color-text);
}
.tab-btn.active {
color: var(--color-primary);
border-bottom-color: var(--color-primary);
}
/* ── Share / Emergency lists ────────────────────────────────────── */
.share-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.share-item {
display: flex;
align-items: center;
gap: 12px;
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius);
padding: 12px 14px;
}
.share-icon {
font-size: 22px;
flex-shrink: 0;
}
.share-info {
flex: 1;
min-width: 0;
}
.share-name {
display: block;
font-weight: 500;
}
.share-meta {
display: block;
font-size: 12px;
color: var(--color-text-muted);
margin-top: 2px;
}
.share-actions {
display: flex;
gap: 6px;
align-items: center;
flex-shrink: 0;
}
/* ── Badges ──────────────────────────────────────────────────────── */
.badge {
display: inline-block;
padding: 3px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: 500;
}
.badge-warn {
background: #fff8e1;
color: #e65100;
border: 1px solid #ffb74d;
}
.badge-info {
background: #e3f2fd;
color: #1565c0;
border: 1px solid #90caf9;
}
/* ── Settings modal ──────────────────────────────────────────────── */
.modal-settings {
max-width: 520px;
}
.settings-section {
padding: 16px 0;
border-bottom: 1px solid var(--color-border);
}
.settings-section:last-child {
border-bottom: none;
}
.settings-section-title {
font-size: 15px;
font-weight: 600;
margin-bottom: 8px;
}
.settings-desc {
font-size: 13px;
color: var(--color-text-muted);
margin-bottom: 10px;
line-height: 1.5;
}
.mfa-qr {
display: block;
margin: 12px auto;
max-width: 180px;
border: 1px solid var(--color-border);
border-radius: 4px;
}
.mfa-secret-text {
font-family: monospace;
font-size: 12px;
}
.mfa-secret-text code {
background: var(--color-bg);
padding: 2px 6px;
border-radius: 4px;
user-select: all;
}
/* ── Security dashboard ──────────────────────────────────────────── */
.security-dashboard {
flex: 1;
overflow-y: auto;
padding: 24px;
display: flex;
flex-direction: column;
gap: 24px;
}
.sec-notice {
text-align: center;
color: var(--color-text-muted);
padding: 40px 0;
font-size: 15px;
}
.security-summary {
display: flex;
gap: 20px;
align-items: center;
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius);
padding: 24px;
flex-wrap: wrap;
}
.sec-score-card {
text-align: center;
flex-shrink: 0;
}
.sec-score {
font-size: 56px;
font-weight: 700;
line-height: 1;
}
.sec-score.score-good {
color: #2e7d32;
}
.sec-score.score-fair {
color: #e65100;
}
.sec-score.score-poor {
color: #c62828;
}
.sec-score-label {
font-size: 16px;
font-weight: 600;
margin-top: 4px;
}
.sec-score-desc {
font-size: 12px;
color: var(--color-text-muted);
margin-top: 2px;
}
.sec-stats {
display: flex;
gap: 16px;
flex-wrap: wrap;
}
.sec-stat {
text-align: center;
background: var(--color-bg);
border: 1px solid var(--color-border);
border-radius: var(--radius);
padding: 12px 20px;
min-width: 80px;
}
.sec-stat-warn {
border-color: #ffb74d;
background: #fff8e1;
}
.sec-stat-info {
border-color: #90caf9;
background: #e3f2fd;
}
.sec-stat-ok {
border-color: #a5d6a7;
background: #e8f5e9;
}
.sec-stat-num {
display: block;
font-size: 28px;
font-weight: 700;
}
.sec-stat-label {
display: block;
font-size: 12px;
color: var(--color-text-muted);
margin-top: 2px;
}
.sec-section {
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius);
overflow: hidden;
}
.sec-section-header {
display: flex;
align-items: flex-start;
gap: 12px;
padding: 16px 20px;
border-bottom: 1px solid var(--color-border);
background: var(--color-bg);
}
.sec-section-icon {
font-size: 20px;
flex-shrink: 0;
}
.sec-section-title {
font-weight: 600;
font-size: 15px;
}
.sec-section-desc {
font-size: 12px;
color: var(--color-text-muted);
margin-top: 2px;
}
.sec-item-list {
padding: 0;
}
.sec-item {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 20px;
border-bottom: 1px solid var(--color-border);
}
.sec-item:last-child {
border-bottom: none;
}
.sec-item-name {
font-weight: 500;
flex: 1;
}
.sec-item-sub {
font-size: 12px;
color: var(--color-text-muted);
flex: 1;
}
/* ── Item Detail Modal ───────────────────────────────────────────── */
.modal-detail {
max-height: 80vh;
display: flex;
flex-direction: column;
}
.detail-body {
flex: 1;
overflow-y: auto;
padding: 4px 0;
}
.detail-field {
display: flex;
align-items: flex-start;
gap: 12px;
padding: 10px 0;
border-bottom: 1px solid var(--color-border);
}
.detail-field:last-child {
border-bottom: none;
}
.detail-label {
font-size: 12px;
color: var(--color-text-muted);
width: 140px;
flex-shrink: 0;
padding-top: 4px;
}
.detail-val-row {
display: flex;
align-items: center;
gap: 6px;
flex: 1;
flex-wrap: wrap;
}
.detail-val {
font-size: 14px;
word-break: break-all;
}
.detail-sensitive {
display: inline-flex;
align-items: center;
font-family: monospace;
font-size: 14px;
}
.detail-masked {
letter-spacing: 2px;
}
.detail-actual {
word-break: break-all;
}
.btn-reveal,
.btn-copy-field {
background: none;
border: none;
cursor: pointer;
padding: 2px 4px;
font-size: 14px;
color: var(--color-text-muted);
flex-shrink: 0;
}
.btn-reveal:hover,
.btn-copy-field:hover {
color: var(--color-primary);
}
/* ── Emergency Vault Panel ───────────────────────────────────────── */
.em-vault-panel {
padding: 8px 0;
}
.em-vault-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 4px 0 16px;
border-bottom: 2px solid var(--color-border);
margin-bottom: 8px;
}
.em-vault-header h4 {
margin: 0;
font-size: 15px;
font-weight: 600;
}
.em-vault-list {
list-style: none;
padding: 0;
margin: 0;
}
.em-vault-item {
border-bottom: 1px solid var(--color-border);
}
.em-vault-item:last-child {
border-bottom: none;
}
.em-vault-item-header {
display: flex;
align-items: center;
gap: 10px;
width: 100%;
background: none;
border: none;
padding: 12px 4px;
cursor: pointer;
text-align: left;
}
.em-vault-item-header:hover {
background: var(--color-bg);
border-radius: var(--radius);
}
.em-vault-icon {
font-size: 18px;
flex-shrink: 0;
}
.em-vault-name {
font-weight: 500;
flex: 1;
}
.em-vault-type {
font-size: 12px;
color: var(--color-text-muted);
}
.em-vault-chevron {
font-size: 12px;
color: var(--color-text-muted);
flex-shrink: 0;
}
.em-vault-item-body {
padding: 0 8px 12px 36px;
}
.em-detail-body .detail-field {
padding: 8px 0;
}
/* ── Sidebar ─────────────────────────────────────────────────────── */
/* ── Base sidebar styles (shared by mobile and desktop) ─────────── */
.sidebar {
background: var(--color-sidebar-bg);
color: var(--color-sidebar-text);
display: flex;
flex-direction: column;
}
.sidebar-header {
display: flex;
align-items: center;
gap: 8px;
padding: 14px 12px;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
flex-shrink: 0;
min-height: 56px;
}
.sidebar-header .logo-icon {
font-size: 22px;
flex-shrink: 0;
}
.sidebar-header .logo-text {
font-size: 16px;
font-weight: 700;
color: #fff;
flex: 1;
white-space: nowrap;
overflow: hidden;
}
.sidebar-nav {
flex: 1;
padding: 8px 0;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
.sidebar-section-header {
padding: 12px 16px 4px;
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.08em;
color: rgba(255, 255, 255, 0.35);
white-space: nowrap;
overflow: hidden;
}
.sidebar-item {
display: flex;
align-items: center;
gap: 10px;
padding: 9px 16px;
color: var(--color-sidebar-text);
cursor: pointer;
transition: background var(--transition);
font-size: 14px;
width: 100%;
text-align: left;
position: relative;
white-space: nowrap;
overflow: hidden;
}
.sidebar-item:hover {
background: var(--color-sidebar-hover);
}
.sidebar-item.active {
background: var(--color-sidebar-active);
color: #fff;
}
.sidebar-icon {
font-size: 20px;
flex-shrink: 0;
width: 22px;
text-align: center;
}
.sidebar-label {
white-space: nowrap;
overflow: hidden;
}
.sidebar-footer {
padding: 12px 0;
border-top: 1px solid rgba(255, 255, 255, 0.08);
}
/* ── DESKTOP sidebar (body.is-desktop) ───────────────────────────── */
body.is-desktop .sidebar {
width: 240px;
flex-shrink: 0;
overflow: hidden;
/* Transition only on desktop so display:none on mobile is instant */
transition: width 0.22s ease;
}
body.is-desktop .sidebar.collapsed {
width: 72px;
}
body.is-desktop .sidebar.collapsed .sidebar-nav,
body.is-desktop .sidebar.collapsed .sidebar-footer {
overflow: visible;
}
body.is-desktop .sidebar.collapsed .sidebar-header {
justify-content: center;
padding: 14px 0;
overflow: visible;
}
body.is-desktop .sidebar-header .logo-icon {
transition: opacity 0.15s ease;
}
body.is-desktop .sidebar-header .logo-text {
flex: 1;
transition: opacity 0.15s ease;
}
body.is-desktop .sidebar.collapsed .sidebar-header .logo-icon,
body.is-desktop .sidebar.collapsed .sidebar-header .logo-text {
opacity: 0;
width: 0;
pointer-events: none;
}
body.is-desktop .btn-sidebar-toggle {
background: none;
border: none;
color: rgba(255, 255, 255, 0.5);
font-size: 14px;
cursor: pointer;
padding: 6px 8px;
border-radius: 4px;
line-height: 1;
flex-shrink: 0;
transition:
color var(--transition),
transform 0.22s ease;
margin-left: auto;
}
body.is-desktop .btn-sidebar-toggle:hover {
color: #fff;
background: rgba(255, 255, 255, 0.12);
}
body.is-desktop .sidebar.collapsed .btn-sidebar-toggle {
transform: rotate(180deg);
margin-left: 0;
}
body.is-desktop .sidebar-section-header {
transition: opacity 0.15s ease;
}
body.is-desktop .sidebar.collapsed .sidebar-section-header {
opacity: 0;
pointer-events: none;
height: 0;
padding: 0;
}
body.is-desktop .sidebar-label {
transition:
opacity 0.15s ease,
max-width 0.22s ease;
max-width: 200px;
}
body.is-desktop .sidebar.collapsed .sidebar-label {
opacity: 0;
max-width: 0;
}
body.is-desktop .sidebar.collapsed .sidebar-item {
justify-content: center;
padding: 10px 0;
overflow: visible;
}
body.is-desktop .sidebar.collapsed .sidebar-item::after {
content: attr(data-tooltip);
position: fixed;
left: 80px;
background: #111827;
color: #fff;
font-size: 12px;
padding: 5px 10px;
border-radius: 5px;
white-space: nowrap;
pointer-events: none;
opacity: 0;
transition: opacity 0.12s ease;
z-index: 1000;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
}
body.is-desktop .sidebar.collapsed .sidebar-item:hover::after {
opacity: 1;
}
/* ── MOBILE sidebar (body.is-mobile) ─────────────────────────────── */
body.is-mobile .sidebar {
/* Hidden by default — no layout space, no transitions to fight */
display: none;
position: fixed;
top: 0;
left: 0;
width: 280px;
height: 100%;
z-index: 90;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
/* No width/opacity transitions on mobile */
}
body.is-mobile .sidebar.mobile-open {
display: flex;
flex-direction: column;
}
/* Mobile sidebar always shows full labels regardless of collapsed state */
body.is-mobile .sidebar .sidebar-label {
opacity: 1;
max-width: none;
}
body.is-mobile .sidebar .sidebar-section-header {
opacity: 1;
height: auto;
padding: 12px 16px 4px;
pointer-events: auto;
}
body.is-mobile .sidebar .sidebar-item {
justify-content: flex-start;
padding: 9px 16px;
}
body.is-mobile .sidebar .sidebar-header {
justify-content: flex-start;
padding: 14px 12px;
}
body.is-mobile .sidebar .sidebar-header .logo-icon,
body.is-mobile .sidebar .sidebar-header .logo-text {
opacity: 1;
width: auto;
pointer-events: auto;
}
body.is-mobile .sidebar .sidebar-item::after {
display: none;
}
body.is-mobile .btn-sidebar-toggle {
display: none;
}
/* ── Mobile top bar — only on body.is-mobile ─────────────────────── */
.mobile-topbar {
display: none;
align-items: center;
gap: 12px;
padding: 0 16px;
height: 54px;
background: var(--color-sidebar-bg);
color: #fff;
flex-shrink: 0;
position: sticky;
top: 0;
z-index: 50;
}
body.is-mobile .mobile-topbar {
display: flex;
}
.btn-mobile-menu {
font-size: 22px;
color: rgba(255, 255, 255, 0.8);
background: none;
border: none;
padding: 4px 6px;
cursor: pointer;
flex-shrink: 0;
}
.btn-mobile-menu:hover {
color: #fff;
}
.mobile-logo {
flex: 1;
font-size: 16px;
font-weight: 700;
color: #fff;
}
.btn-fab-mobile {
width: 36px;
height: 36px;
font-size: 20px;
flex-shrink: 0;
}
/* Sidebar backdrop */
.sidebar-backdrop {
display: none;
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 89;
}
.sidebar-backdrop.open {
display: block;
}
/* ── App layout ──────────────────────────────────────────────────── */
body.is-desktop .app-layout {
display: flex;
height: 100vh;
height: calc(var(--vh, 1vh) * 100);
}
body.is-mobile .app-layout {
display: flex;
flex-direction: column;
height: 100vh;
height: -webkit-fill-available;
height: calc(var(--vh, 1vh) * 100);
overflow: hidden;
}
/* ── Folder items ────────────────────────────────────────────────── */
.folder-item {
justify-content: space-between;
}
.folder-label {
display: flex;
align-items: center;
gap: 10px;
flex: 1;
cursor: pointer;
}
.btn-delete-folder {
background: none;
border: none;
cursor: pointer;
font-size: 13px;
opacity: 0;
color: rgba(255, 255, 255, 0.6);
transition:
opacity var(--transition),
color var(--transition);
padding: 2px 4px;
border-radius: 4px;
flex-shrink: 0;
}
.folder-item:hover .btn-delete-folder {
opacity: 1;
}
.btn-delete-folder:hover {
color: #ff6b6b;
}
.btn-new-folder {
float: right;
margin-right: 4px;
background: none;
border: none;
color: rgba(255, 255, 255, 0.5);
font-size: 16px;
line-height: 1;
cursor: pointer;
padding: 0 4px;
transition: color var(--transition);
}
.btn-new-folder:hover {
color: #fff;
}
.new-folder-row {
padding: 4px 10px 4px 16px;
}
.new-folder-row form {
display: flex;
align-items: center;
gap: 4px;
}
.new-folder-row input {
flex: 1;
padding: 5px 8px;
border-radius: 4px;
border: 1px solid rgba(255, 255, 255, 0.2);
background: rgba(255, 255, 255, 0.1);
color: #fff;
font-size: 13px;
}
.new-folder-row input::placeholder {
color: rgba(255, 255, 255, 0.4);
}
.new-folder-row input:focus {
outline: none;
border-color: rgba(255, 255, 255, 0.5);
}
.new-folder-row .btn-icon {
color: rgba(255, 255, 255, 0.7);
font-size: 14px;
}
.new-folder-row .btn-icon:hover {
color: #fff;
background: rgba(255, 255, 255, 0.1);
}
/* ── Breakpoint-driven padding adjustments ───────────────────────── */
/* Tablet (7691024px) */
@media (min-width: 769px) and (max-width: 1024px) {
body.is-desktop .sidebar:not(.collapsed) {
width: 200px;
}
.vault-header {
padding: 14px 18px;
}
.vault-list {
padding: 10px 18px 18px;
}
.panel-body {
padding: 16px 18px;
}
.security-dashboard {
padding: 16px;
gap: 16px;
}
.search-wrapper {
max-width: 280px;
}
.security-summary {
flex-wrap: wrap;
gap: 12px;
}
.sec-stats {
flex-wrap: wrap;
gap: 10px;
}
.modal {
max-width: 460px;
}
}
/* Large PC (> 1440px) */
@media (min-width: 1441px) {
body.is-desktop .sidebar:not(.collapsed) {
width: 260px;
}
.vault-header {
padding: 18px 40px;
}
.vault-list {
padding: 16px 40px 40px;
}
.vault-list-inner {
max-width: 1100px;
}
.vault-header-inner {
max-width: 1100px;
}
.panel-body {
padding: 24px 40px;
}
.security-dashboard {
padding: 24px 40px;
gap: 28px;
}
.search-wrapper {
max-width: 480px;
}
.modal {
max-width: 520px;
}
.modal-settings {
max-width: 580px;
}
}
/* Mobile padding and layout overrides */
body.is-mobile .vault-main {
flex: 1;
overflow: hidden;
min-height: 0;
}
body.is-mobile .vault-header {
padding: 12px 16px;
gap: 8px;
}
body.is-mobile .vault-title {
font-size: 16px;
}
body.is-mobile .vault-list {
padding: 8px 12px 80px;
}
body.is-mobile .panel-body {
padding: 16px;
}
body.is-mobile .security-dashboard {
padding: 12px;
gap: 16px;
}
body.is-mobile .vault-toolbar {
gap: 8px;
}
body.is-mobile .search-wrapper {
max-width: none;
}
body.is-mobile .sort-wrapper {
display: none;
}
body.is-mobile .vault-header .btn-fab {
display: none;
}
body.is-mobile .form-row {
flex-direction: column;
gap: 0;
}
body.is-mobile .modal-actions {
flex-wrap: wrap;
}
body.is-mobile .modal-overlay {
padding: 0;
align-items: flex-end;
}
body.is-mobile .modal {
max-width: 100%;
max-height: calc(var(--vh, 1vh) * 92);
border-radius: 16px 16px 0 0;
padding: 20px 16px;
}
body.is-mobile .security-summary {
padding: 16px;
gap: 12px;
flex-wrap: wrap;
}
body.is-mobile .sec-score {
font-size: 40px;
}
body.is-mobile .sec-stats {
gap: 8px;
flex-wrap: wrap;
}
body.is-mobile .sec-stat {
padding: 8px 12px;
min-width: 68px;
}
body.is-mobile .sec-stat-num {
font-size: 22px;
}
body.is-mobile .detail-field {
flex-direction: column;
gap: 4px;
}
body.is-mobile .detail-label {
width: auto;
}
/* Auth page — phone */
@media (max-width: 480px) {
.auth-container {
padding: 12px;
}
.auth-card {
padding: 28px 20px;
border-radius: 12px;
}
.auth-title {
font-size: 20px;
}
}
/* iOS input font-size — prevent auto-zoom on focus */
body.is-mobile .form-group input,
body.is-mobile .form-group select,
body.is-mobile .form-group textarea {
font-size: 16px;
}
/* ── Settings — new sections (Phase 6) ──────────────────────────── */
.settings-section-danger {
border-top: 2px solid var(--color-danger, #e53e3e);
margin-top: 8px;
padding-top: 16px;
}
.settings-section-danger .settings-section-title {
color: var(--color-danger, #e53e3e);
}
.btn-danger {
background: var(--color-danger, #e53e3e);
color: #fff;
border: none;
border-radius: var(--radius);
padding: 8px 18px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: opacity 0.15s;
}
.btn-danger:hover {
opacity: 0.85;
}
.btn-danger:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.recovery-code-box {
display: flex;
align-items: center;
gap: 8px;
background: var(--color-bg-secondary, #f7f7f7);
border: 1px solid var(--color-border);
border-radius: var(--radius);
padding: 12px 14px;
margin: 8px 0;
}
.recovery-code-box code {
font-family: monospace;
font-size: 15px;
letter-spacing: 0.08em;
word-break: break-all;
flex: 1;
}
.warning-text {
color: var(--color-danger, #e53e3e);
font-size: 13px;
font-weight: 500;
}
/* ── CSP-safe replacements for former inline styles ─────────────────── */
/* TOTP timer: turns red when ≤5 s remain (toggled via JS class) */
.totp-timer--expiring {
color: #dc2626;
}
/* Generate-password button injected below password field */
.btn-generate {
margin-top: 6px;
}
/* Scroll-lock applied to <html> when mobile sidebar is open */
html.sidebar-open {
overflow: hidden;
}
/* Password Generator modal max-width */
.gen-modal-inner {
max-width: 420px;
}
/* Generator length slider */
#gen-length {
width: 100%;
margin-top: 4px;
}
/* Generator options row */
.gen-options {
display: flex;
gap: 16px;
flex-wrap: wrap;
margin-bottom: 16px;
}
/* Generator form-group spacing */
.gen-form-group {
margin-bottom: 8px;
}
/* Generator input-with-toggle wrapper */
.gen-input-wrap {
margin-bottom: 4px;
}
/* Generator copy button */
#btn-gen-copy {
font-size: 14px;
}
/* Generator output field */
#gen-output {
padding-right: 40px;
}
/* Form field label — "optional" badge */
.field-label-optional {
font-size: 11px;
color: #9ca3af;
font-weight: 400;
}
/* Form field hint text below inputs */
.field-hint {
font-size: 11px;
color: #6b7280;
margin-top: 4px;
}
/* Monospace input — used for recovery codes and other token fields */
.input-monospace {
font-family: monospace;
letter-spacing: 0.05em;
}
/* ── Import / Export view ──────────────────────────────────────────────────── */
.import-export-section {
background: #fff;
border: 1px solid #e5e7eb;
border-radius: 10px;
padding: 24px;
margin-bottom: 20px;
max-width: 640px;
}
.import-export-heading {
font-size: 15px;
font-weight: 700;
color: #111827;
margin: 0 0 8px;
}
.import-export-desc {
font-size: 13px;
color: #6b7280;
margin: 0 0 16px;
line-height: 1.55;
}
.import-export-row {
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
}
.import-file-label {
display: inline-flex;
align-items: center;
cursor: pointer;
padding: 7px 14px;
border-radius: 7px;
font-size: 13px;
}
.import-file-name {
font-size: 13px;
color: #6b7280;
}
.import-preview {
margin: 14px 0 0;
font-size: 13px;
color: #374151;
background: #f9fafb;
border: 1px solid #e5e7eb;
border-radius: 7px;
padding: 12px 14px;
line-height: 1.6;
}
.import-preview-list {
margin: 6px 0 0 16px;
padding: 0;
}
.import-note {
color: #6b7280;
font-size: 12px;
margin: 4px 0 0;
}
.import-error {
color: #dc2626;
}
.import-result {
margin-top: 14px;
font-size: 13px;
padding: 10px 14px;
border-radius: 7px;
}
.import-result-ok {
background: #f0fdf4;
color: #15803d;
border: 1px solid #bbf7d0;
}
.import-result-err {
background: #fef2f2;
color: #dc2626;
border: 1px solid #fecaca;
}
/* ── Collapsible vault folder groups ───────────────────────────────────────── */
.vault-group-header {
display: flex;
align-items: center;
justify-content: space-between;
cursor: pointer;
user-select: none;
transition: color 0.12s;
}
.vault-group-header:hover { color: var(--color-primary); }
.group-name { flex: 1; }
.group-meta {
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
}
.group-count {
background: #e5e7eb;
color: #374151;
border-radius: 999px;
padding: 1px 7px;
font-size: 11px;
font-weight: 600;
}
.group-chevron {
font-size: 10px;
color: var(--color-text-muted);
width: 14px;
text-align: center;
transition: transform 0.15s;
}
.vault-group-header.collapsed .group-chevron { color: var(--color-primary); }
/* ── Vault item tags ────────────────────────────────────────────────────────── */
.item-tags {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-top: 3px;
}
.item-tag {
display: inline-block;
background: #ede9fe;
color: #6d28d9;
font-size: 10px;
font-weight: 600;
padding: 1px 6px;
border-radius: 999px;
letter-spacing: 0.2px;
}
.tag-preview {
display: flex;
flex-wrap: wrap;
gap: 4px;
margin-top: 6px;
min-height: 20px;
}
/* ── Sidebar tags ────────────────────────────────────────────────────────────── */
.sidebar-tag-list {
list-style: none;
padding: 0;
margin: 0 0 4px;
}
.sidebar-tag-empty {
font-size: 12px;
color: var(--color-text-muted);
padding: 4px 16px 8px;
}
.sidebar-tag-item .sidebar-icon { font-size: 14px; }
/* ── Advanced Settings (collapsible, inside password modal) ─────────── */
.adv-section {
border: 1px solid var(--color-border);
border-radius: var(--radius);
overflow: hidden;
margin-top: 4px;
margin-bottom: 8px;
background: var(--color-surface);
}
.adv-section-header {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: 11px 14px;
background: none;
border: none;
cursor: pointer;
font-size: 14px;
font-weight: 500;
color: var(--color-text);
text-align: left;
transition: background var(--transition);
gap: 8px;
}
.adv-section-header:hover {
background: var(--color-bg);
}
.adv-section-title {
flex: 1;
}
.adv-section-chevron {
font-size: 16px;
color: var(--color-text-muted);
line-height: 1;
transition: transform 0.18s ease;
user-select: none;
}
/* Rotate chevron when expanded */
.adv-section-header[aria-expanded="true"] .adv-section-chevron {
transform: rotate(180deg);
}
.adv-section-body {
padding: 4px 14px 10px;
border-top: 1px solid var(--color-border);
display: flex;
flex-direction: column;
gap: 0;
animation: adv-body-in 0.15s ease;
}
.adv-section-body.hidden {
display: none;
}
@keyframes adv-body-in {
from { opacity: 0; transform: translateY(-4px); }
to { opacity: 1; transform: translateY(0); }
}
.adv-checkbox-row {
display: flex;
align-items: flex-start;
gap: 10px;
padding: 10px 0;
cursor: pointer;
border-bottom: 1px solid var(--color-border);
}
.adv-checkbox-row:last-child {
border-bottom: none;
}
.adv-checkbox-row input[type="checkbox"] {
margin-top: 2px;
width: 15px;
height: 15px;
flex-shrink: 0;
accent-color: var(--color-primary);
cursor: pointer;
}
.adv-checkbox-text {
display: flex;
flex-direction: column;
gap: 2px;
flex: 1;
min-width: 0;
}
.adv-checkbox-label {
font-size: 14px;
font-weight: 500;
color: var(--color-text);
line-height: 1.3;
}
.adv-checkbox-desc {
font-size: 12px;
color: var(--color-text-muted);
line-height: 1.45;
}
/* ── Favorite star button ───────────────────────────────────────────────── */
/* Outline star — subtle, not distracting */
.btn-favorite {
color: #d1d5db;
font-size: 16px;
transition:
color var(--transition),
transform 0.12s ease;
}
.btn-favorite:hover {
color: #f59e0b;
background: #fef9c3;
transform: scale(1.15);
}
/* Filled gold star — item is favorited */
.btn-favorite.is-favorite {
color: #f59e0b;
}
.btn-favorite.is-favorite:hover {
color: #d97706;
background: #fef3c7;
}
/* Brief pop animation when toggling */
.btn-favorite:active {
transform: scale(0.88);
}
/* ── Passkey form info callout ──────────────────────────────────────────────── */
.passkey-info-callout {
background: #eff6ff;
border: 1px solid #bfdbfe;
border-radius: var(--radius);
color: #1d4ed8;
font-size: 12px;
line-height: 1.5;
padding: 10px 12px;
margin-bottom: 14px;
}
/* ── Passkey management (settings modal) ────────────────────────────────────── */
.passkeys-list {
margin-bottom: 12px;
}
.passkey-item {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
padding: 10px 0;
border-bottom: 1px solid var(--border);
}
.passkey-item:last-child {
border-bottom: none;
}
.passkey-info {
display: flex;
flex-direction: column;
gap: 2px;
min-width: 0;
}
.passkey-name {
font-size: 14px;
font-weight: 600;
color: var(--text);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.passkey-meta {
font-size: 12px;
color: var(--text-muted);
}
.passkey-actions {
display: flex;
gap: 8px;
flex-shrink: 0;
}
.btn-danger-text {
background: none;
border: none;
cursor: pointer;
color: var(--danger);
font-size: 12px;
padding: 2px 4px;
}
.btn-danger-text:hover {
text-decoration: underline;
}
.passkey-register-row {
display: flex;
gap: 8px;
align-items: center;
margin-top: 10px;
}
.passkey-name-input {
flex: 1;
padding: 7px 10px;
border: 1px solid var(--border);
border-radius: var(--radius);
font-size: 13px;
color: var(--text);
background: var(--surface);
}
.passkey-name-input:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(192, 57, 43, 0.15);
}
/* Login page divider */
.auth-divider {
display: flex;
align-items: center;
gap: 10px;
margin: 16px 0;
color: var(--text-muted);
font-size: 12px;
}
.auth-divider::before,
.auth-divider::after {
content: "";
flex: 1;
height: 1px;
background: var(--border);
}