diff --git a/src/router-public.ts b/src/router-public.ts index f11fae1..0d4235c 100644 --- a/src/router-public.ts +++ b/src/router-public.ts @@ -66,6 +66,15 @@ function handleNwFavicon(): Response { }); } +function handleMissingWebsiteIcon(): Response { + return new Response(null, { + status: 404, + headers: { + 'Cache-Control': 'public, max-age=300', + }, + }); +} + function buildIconServiceBase(origin: string): string { return `${origin}/icons`; } @@ -127,9 +136,9 @@ function normalizeIconHost(rawHost: string): string | null { } } -async function handleWebsiteIcon(host: string): Promise { +async function handleWebsiteIcon(host: string, fallbackMode: 'default' | 'not-found' = 'default'): Promise { const normalizedHost = normalizeIconHost(host); - if (!normalizedHost) return handleNwFavicon(); + if (!normalizedHost) return fallbackMode === 'not-found' ? handleMissingWebsiteIcon() : handleNwFavicon(); const encodedHost = encodeURIComponent(normalizedHost); const requestHeaders = { 'User-Agent': 'NodeWarden/1.0' }; @@ -172,9 +181,9 @@ async function handleWebsiteIcon(host: string): Promise { }); } - return handleNwFavicon(); + return fallbackMode === 'not-found' ? handleMissingWebsiteIcon() : handleNwFavicon(); } catch { - return handleNwFavicon(); + return fallbackMode === 'not-found' ? handleMissingWebsiteIcon() : handleNwFavicon(); } } @@ -221,7 +230,8 @@ export async function handlePublicRoute( const iconMatch = path.match(/^\/icons\/([^/]+)\/icon\.png$/i); if (iconMatch && method === 'GET') { - return handleWebsiteIcon(iconMatch[1]); + const fallbackMode = new URL(request.url).searchParams.get('fallback') === '404' ? 'not-found' : 'default'; + return handleWebsiteIcon(iconMatch[1], fallbackMode); } const publicAttachmentMatch = path.match(/^\/api\/attachments\/([a-f0-9-]+)\/([a-f0-9-]+)$/i); diff --git a/webapp/src/components/TotpCodesPage.tsx b/webapp/src/components/TotpCodesPage.tsx index a3b5a85..85ea016 100644 --- a/webapp/src/components/TotpCodesPage.tsx +++ b/webapp/src/components/TotpCodesPage.tsx @@ -65,6 +65,10 @@ function TotpListIcon({ cipher }: { cipher: Cipher }) { const uri = firstCipherUri(cipher); const host = hostFromUri(uri); const [errored, setErrored] = useState(() => (host ? failedIconHosts.has(host) : false)); + useEffect(() => { + setErrored(host ? failedIconHosts.has(host) : false); + }, [host]); + if (host && !errored) { return ( (host ? failedIconHosts.has(host) : false)); + useEffect(() => { + setErrored(host ? failedIconHosts.has(host) : false); + }, [host]); + if (host && !errored) { return ( { - const resp = await authedFetch('/api/sync'); + const resp = await authedFetch('/api/sync', { + cache: 'no-store', + headers: { + 'Cache-Control': 'no-cache', + Pragma: 'no-cache', + }, + }); if (!resp.ok) throw new Error('Failed to load vault'); const body = await parseJson(resp); return body || {}; diff --git a/webapp/src/styles.css b/webapp/src/styles.css index f1a8e30..b5acc8b 100644 --- a/webapp/src/styles.css +++ b/webapp/src/styles.css @@ -1,5086 +1,12 @@ -:root { - --bg: #e7edf8; - --bg-accent: #e7edf8; - --panel: #f9fbff; - --panel-soft: #f2f6fd; - --panel-muted: #e8eff9; - --line: rgba(128, 152, 192, 0.32); - --line-soft: rgba(143, 167, 206, 0.18); - --text: #0b1730; - --text-soft: #1a2d52; - --muted: #60708b; - --muted-strong: #334765; - --primary: #2563eb; - --primary-hover: #1d4ed8; - --primary-strong: #0f3f98; - --primary-soft: rgba(37, 99, 235, 0.12); - --danger: #d92d57; - --danger-hover: #b81f47; - --overlay: rgba(15, 23, 42, 0.4); - --overlay-strong: rgba(15, 23, 42, 0.56); - --shadow-sm: 0 10px 22px rgba(13, 31, 68, 0.045); - --shadow-md: 0 22px 48px rgba(13, 31, 68, 0.08); - --shadow-lg: 0 28px 76px rgba(13, 31, 68, 0.11); - --radius: 16px; - --ease-out-strong: cubic-bezier(0.22, 1, 0.36, 1); - --ease-out-soft: cubic-bezier(0.24, 0.8, 0.32, 1); - --ease-smooth: cubic-bezier(0.4, 0, 0.2, 1); - --dur-micro: 140ms; - --dur-fast: 180ms; - --dur-medium: 240ms; - --dur-panel: 280ms; - --actions-gap: clamp(0px, calc((100vw - 520px) * 1), 10px); -} - -:root[data-theme='dark'] { - --bg: #06111d; - --bg-accent: #06111d; - --panel: #0d192b; - --panel-soft: #112136; - --panel-muted: #0a1626; - --line: rgba(108, 141, 190, 0.28); - --line-soft: rgba(120, 152, 198, 0.16); - --text: #edf4ff; - --text-soft: #d9e6fb; - --muted: #8fa6c6; - --muted-strong: #c3d5ef; - --primary: #84b6ff; - --primary-hover: #a6ccff; - --primary-strong: #f3f8ff; - --primary-soft: rgba(132, 182, 255, 0.18); - --danger: #ff8ba8; - --danger-hover: #ffabc0; - --overlay: rgba(2, 8, 20, 0.68); - --overlay-strong: rgba(2, 8, 20, 0.84); - --shadow-sm: 0 14px 28px rgba(1, 7, 18, 0.24); - --shadow-md: 0 24px 52px rgba(1, 7, 18, 0.36); - --shadow-lg: 0 34px 88px rgba(1, 7, 18, 0.46); -} - -* { - box-sizing: border-box; -} - -html, -body, -#root { - margin: 0; - padding: 0; - width: 100%; - height: 100%; - color: var(--text); - background: var(--bg-accent); - font-family: 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', 'Noto Sans SC', sans-serif; -} - -body { - position: relative; - transition: - background-color var(--dur-medium) var(--ease-smooth), - color var(--dur-medium) var(--ease-smooth); -} - -body.dialog-open { - overflow: hidden; - overscroll-behavior: contain; -} - -body::before { - content: none; -} - -:root[data-theme='dark'] body::before { - content: none; -} - -.loading-screen { - height: 100%; - display: grid; - place-items: center; - color: var(--muted); - font-size: 18px; - animation: fade-in-up var(--dur-panel) var(--ease-out-strong) both; -} - -.auth-page { - min-height: 100%; - display: grid; - place-items: center; - padding: 24px; - position: relative; - background: transparent; -} - -.public-send-page { - min-height: 80vh; - align-items: center; - justify-items: center; -} - -.auth-card { - width: 100%; - position: relative; - background: var(--panel); - border: 1px solid var(--line); - border-radius: 24px; - box-shadow: var(--shadow-lg); - padding: 30px; - overflow: hidden; - transform-origin: 50% 24%; - animation: surface-enter 520ms var(--ease-out-strong) both; -} - -.auth-card::before { - content: none; -} - -.auth-card h1 { - margin: 0 0 4px 0; - text-align: center; -} - -.standalone-shell { - width: min(640px, 100%); - display: grid; - gap: 14px; - animation: fade-in-up 420ms var(--ease-out-strong) both; -} - -.standalone-brand { - display: inline-flex; - align-items: center; - gap: 14px; - margin-bottom: 12px; -} - -.standalone-brand-outside { - justify-content: center; - width: 100%; - margin-bottom: 2px; -} - -.standalone-brand-logo { - width: 56px; - height: 56px; - object-fit: contain; - flex-shrink: 0; - filter: drop-shadow(0 8px 18px rgba(43, 102, 217, 0.22)); -} - -.standalone-brand-title { - font-size: 40px; - font-weight: 800; - line-height: 0.96; - letter-spacing: -0.04em; - color: var(--text); - text-shadow: 0 10px 22px rgba(255, 255, 255, 0.26); -} - -.standalone-brand-wordmark { - display: block; - height: auto; - width: clamp(200px, 30vw, 360px); - max-width: 100%; - filter: drop-shadow(0 10px 22px rgba(43, 102, 217, 0.18)); -} - -.standalone-title { - margin: 0 0 4px 0; - text-align: left; - font-size: 31px; - line-height: 1.15; - letter-spacing: -0.035em; -} - -.standalone-muted { - text-align: left; -} - -.jwt-warning-head { - display: flex; - align-items: center; - justify-content: center; - gap: 10px; - margin-bottom: 10px; - color: #b45309; - text-align: center; -} - -.jwt-warning-box { - border: 1px solid #f1d8a5; - border-radius: 12px; - background: #fffaf0; - padding: 12px 14px; -} - -.jwt-warning-label { - font-size: 13px; - font-weight: 700; - color: #92400e; - margin-bottom: 6px; -} - -.jwt-warning-copy { - margin: 0 0 14px; - color: #475569; - line-height: 1.6; -} - -.jwt-warning-list { - margin: 0; - padding-left: 18px; - color: #334155; - line-height: 1.55; -} - -.jwt-inline-link { - color: #1d4ed8; - font-weight: 700; - text-decoration: none; -} - -.jwt-inline-link:hover { - text-decoration: underline; -} - -.jwt-secret-fields { - margin-top: 8px; - display: grid; - gap: 6px; -} - -.jwt-secret-row { - display: grid; - grid-template-columns: 88px minmax(0, 1fr); - gap: 8px; - align-items: start; -} - -.jwt-secret-row > span { - color: #64748b; -} - -.jwt-generator { - margin-top: 14px; -} - -.jwt-generator-actions { - margin-top: 10px; - display: flex; - align-items: center; - gap: 10px; - flex-wrap: wrap; -} - -.jwt-copy-hint { - color: #15803d; - font-size: 13px; - font-weight: 700; -} - -.standalone-footer { - width: 100%; - text-align: center; - font-size: 13px; - color: #64748b; -} - -.standalone-footer a { - color: #1d4ed8; - font-weight: 700; - text-decoration: none; -} - -.standalone-footer a:hover { - text-decoration: underline; -} - -.standalone-version { - font-weight: 700; - color: #1d4ed8; -} - -.muted { - margin: 0 0 16px 0; - text-align: center; - color: var(--muted); - line-height: 1.65; -} - -.field { - display: block; - margin-bottom: 14px; -} - -.field > span { - display: block; - margin-bottom: 8px; - font-size: 14px; - font-weight: 600; -} - -.input { - width: 100%; - height: 48px; - border: 1px solid rgba(74, 103, 150, 0.42); - border-radius: 14px; - padding: 10px 14px; - font-size: 16px; - outline: none; - color: var(--text); - background: var(--panel); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9); - transition: - border-color var(--dur-fast) var(--ease-smooth), - box-shadow var(--dur-fast) var(--ease-out-soft), - background-color var(--dur-fast) var(--ease-smooth), - transform var(--dur-fast) var(--ease-out-soft); -} - -select.input { - appearance: none; - -webkit-appearance: none; - -moz-appearance: none; - padding-right: 42px; - background-image: - linear-gradient(45deg, transparent 50%, #365fa8 50%), - linear-gradient(135deg, #365fa8 50%, transparent 50%); - background-position: - calc(100% - 18px) calc(50% - 3px), - calc(100% - 12px) calc(50% - 3px); - background-size: 6px 6px, 6px 6px; - background-repeat: no-repeat; -} - -input[type='file'].input { - height: auto; - min-height: 48px; - padding: 8px 10px; - font-size: 14px; - line-height: 1.4; -} - -input[type='file'].input::file-selector-button { - height: 32px; - border: 1px solid #3f5b9e; - border-radius: 999px; - padding: 0 12px; - background: #eef4ff; - color: #1f4ea0; - font-weight: 700; - cursor: pointer; - margin-right: 10px; -} - -input[type='file'].input::file-selector-button:hover { - background: #dfeaff; - border-color: #2f5fd8; -} - -.textarea { - min-height: 110px; - height: auto; - resize: vertical; -} - -.input:focus { - border-color: rgba(43, 102, 217, 0.6); - background-color: #fbfdff; - box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.11), 0 10px 20px rgba(37, 99, 235, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.95); - transform: translateY(-1px); -} - -.input-readonly { - background: #eef2f7; - color: #475569; -} - -.input:disabled { - background: #e2e8f0; - border-color: #cbd5e1; - color: #94a3b8; - cursor: not-allowed; -} - -.password-wrap { - position: relative; -} - -.password-wrap .input { - padding-right: 44px; -} - -.password-toggle { - position: absolute; - right: 8px; - top: 50%; - transform: translateY(-50%); - border: none; - background: transparent; - color: #275ac2; - cursor: pointer; - display: grid; - place-items: center; - transition: color var(--dur-fast) var(--ease-smooth), transform var(--dur-fast) var(--ease-out-soft); -} - -.eye-btn { - position: absolute; - right: 10px; - bottom: 9px; - width: 30px; - height: 30px; - border: none; - background: transparent; - cursor: pointer; - display: grid; - place-items: center; - color: #334155; - transition: color var(--dur-fast) var(--ease-smooth), transform var(--dur-fast) var(--ease-out-soft); -} - -.password-toggle:hover, -.eye-btn:hover { - color: var(--primary); - transform: translateY(-1px) scale(1.04); -} - -.btn { - height: 36px; - border: 1px solid transparent; - border-radius: 999px; - padding: 0 16px; - font-size: 15px; - font-weight: 700; - cursor: pointer; - display: inline-flex; - align-items: center; - justify-content: center; - gap: 6px; - text-decoration: none; - transition: - transform var(--dur-fast) var(--ease-out-soft), - box-shadow var(--dur-fast) var(--ease-out-soft), - background-color var(--dur-fast) var(--ease-smooth), - border-color var(--dur-fast) var(--ease-smooth), - color var(--dur-fast) var(--ease-smooth), - opacity var(--dur-fast) var(--ease-smooth); -} - -.topbar-actions .btn, -.user-chip, -.side-link, -.mobile-tab { - --mag-x: 0px; - --mag-y: 0px; - position: relative; - overflow: hidden; -} - -.topbar-actions .btn::before, -.user-chip::before, -.side-link::before, -.mobile-tab::before { - content: ''; - position: absolute; - left: var(--mx, 50%); - top: var(--my, 50%); - width: 110px; - height: 110px; - border-radius: 999px; - background: radial-gradient(circle, rgba(255, 255, 255, 0.36), rgba(255, 255, 255, 0.08) 42%, transparent 72%); - transform: translate(-50%, -50%) scale(0.68); - opacity: 0; - pointer-events: none; - transition: - opacity var(--dur-fast) var(--ease-smooth), - transform var(--dur-medium) var(--ease-out-soft); -} - -.topbar-actions .btn:hover::before, -.user-chip:hover::before, -.side-link:hover::before, -.mobile-tab:hover::before { - opacity: 1; - transform: translate(-50%, -50%) scale(1); -} - -.btn:hover:not(:disabled) { - transform: translateY(-2px) scale(1.01); -} - -.btn:active:not(:disabled) { - transform: translateY(0) scale(0.985); -} - -.btn-icon { - flex-shrink: 0; -} - -.btn.full { - width: 100%; - height: 50px; - font-size: 22px; - margin: 10px 0; -} - -.btn-primary { - background: linear-gradient(135deg, #2563eb, #3b82f6 72%); - border-color: rgba(15, 63, 152, 0.32); - color: #fff; - box-shadow: 0 14px 28px rgba(37, 99, 235, 0.24); -} - -.btn-primary:hover { - background: linear-gradient(135deg, #1d4ed8, #3377f0 72%); - border-color: rgba(15, 63, 152, 0.38); - box-shadow: 0 18px 34px rgba(37, 99, 235, 0.28); -} - -.btn-secondary { - background: var(--panel); - border-color: rgba(37, 99, 235, 0.22); - color: var(--primary-strong); - box-shadow: 0 8px 18px rgba(13, 31, 68, 0.05); -} - -.btn-secondary:hover { - background: #f4f8ff; - border-color: rgba(37, 99, 235, 0.34); -} - -.btn-danger { - background: rgba(255, 255, 255, 0.8); - border-color: rgba(217, 45, 87, 0.28); - color: var(--danger); -} - -.btn-danger:hover { - background: rgba(255, 241, 242, 0.96); - border-color: rgba(217, 45, 87, 0.38); -} - -.btn:disabled { - background: #e2e8f0; - border-color: #cbd5e1; - color: #94a3b8; - cursor: not-allowed; -} - -.or { - text-align: center; - margin: 10px 0; - color: #334155; -} - -.field-help { - margin-top: 8px; - font-size: 13px; - line-height: 1.5; - color: #667085; -} - -.auth-support-row { - display: flex; - align-items: center; - justify-content: space-between; - gap: 10px; - margin: -2px 0 12px; -} - -.auth-link-btn { - border: none; - background: transparent; - padding: 0; - color: #1d4ed8; - font-size: 13px; - font-weight: 700; - cursor: pointer; - transition: color var(--dur-fast) var(--ease-smooth), transform var(--dur-fast) var(--ease-out-soft), opacity var(--dur-fast) var(--ease-smooth); -} - -.auth-link-btn:hover { - text-decoration: underline; - transform: translateX(2px); -} - -.auth-link-btn:disabled { - color: #94a3b8; - cursor: not-allowed; - text-decoration: none; -} - -.app-page { - min-height: 100%; - padding: 20px; - position: relative; - background: transparent; -} - -.app-shell { - height: calc(100vh - 40px); - max-width: 1600px; - margin: 0 auto; - position: relative; - background: var(--panel-soft); - border: 1px solid var(--line); - border-radius: 28px; - box-shadow: var(--shadow-lg); - display: flex; - flex-direction: column; - overflow: hidden; - animation: shell-enter 560ms var(--ease-out-strong) both; -} - -.app-shell::before { - content: none; -} - -.topbar { - height: 58px; - border-bottom: 1px solid var(--line-soft); - color: #0f172a; - background: rgba(244, 248, 255, 0.72); - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 18px; - transition: background-color var(--dur-fast) var(--ease-smooth), border-color var(--dur-fast) var(--ease-smooth); -} - -.brand { - display: inline-flex; - align-items: center; - gap: 8px; - font-size: 34px; - font-weight: 800; - color: var(--text); -} - -.brand-name { - display: inline; - letter-spacing: -0.045em; - text-shadow: 0 12px 24px rgba(43, 102, 217, 0.14); -} - -.brand-wordmark { - display: block; - height: auto; - width: clamp(210px, 20vw, 290px); - max-width: 100%; - filter: drop-shadow(0 12px 24px rgba(43, 102, 217, 0.12)); -} - -.mobile-page-title { - display: none; - min-width: 0; - max-width: min(58vw, 240px); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 19px; - line-height: 1.2; - font-weight: 800; - color: #0f172a; -} - -.brand-logo { - width: 42px; - height: 42px; - object-fit: contain; - filter: drop-shadow(0 10px 22px rgba(43, 102, 217, 0.22)); - transition: transform var(--dur-medium) var(--ease-out-soft), filter var(--dur-medium) var(--ease-out-soft); -} - -.topbar-actions { - display: flex; - align-items: center; - gap: 10px; -} - -.mobile-tabbar { - display: none; -} - -.mobile-sidebar-toggle { - display: none; -} - -.mobile-lock-btn { - display: none; -} - -.mobile-theme-btn { - display: none; -} - -.theme-switch-wrap { - display: inline-flex; - align-items: center; - justify-content: center; -} - -.theme-switch { - position: relative; - display: inline-block; - width: 56px; - height: 32px; -} - -.theme-switch-input { - opacity: 0; - width: 0; - height: 0; -} - -.theme-switch-slider { - position: absolute; - cursor: pointer; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: linear-gradient(180deg, #dceaff, #c8dcff); - border: 1px solid #9dbbec; - transition: - background var(--dur-medium) var(--ease-out-soft), - border-color var(--dur-medium) var(--ease-smooth), - box-shadow var(--dur-fast) var(--ease-out-soft), - transform var(--dur-fast) var(--ease-out-soft); - border-radius: 999px; -} - -.theme-switch-slider::before { - position: absolute; - content: ''; - height: 26px; - width: 26px; - border-radius: 999px; - left: 2px; - bottom: 2px; - z-index: 2; - background: linear-gradient(180deg, #ffffff, #edf4ff); - box-shadow: 0 2px 8px rgba(15, 23, 42, 0.14); - transition: - transform var(--dur-medium) var(--ease-out-strong), - box-shadow var(--dur-fast) var(--ease-out-soft), - background var(--dur-fast) var(--ease-smooth); -} - -.theme-switch .sun svg { - position: absolute; - top: 6px; - left: 32px; - z-index: 1; - width: 18px; - height: 18px; - opacity: 0.95; - transition: transform var(--dur-medium) var(--ease-out-soft), opacity var(--dur-fast) var(--ease-smooth); -} - -.theme-switch .moon svg { - fill: #5b86d6; - position: absolute; - top: 7px; - left: 7px; - z-index: 1; - width: 16px; - height: 16px; - opacity: 0.88; - transition: transform var(--dur-medium) var(--ease-out-soft), opacity var(--dur-fast) var(--ease-smooth); -} - -.theme-switch-input:checked + .theme-switch-slider { - background: linear-gradient(180deg, #173150, #122742); - border-color: #35527a; -} - -.theme-switch-input:focus + .theme-switch-slider { - box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.22); -} - -.theme-switch-input:checked + .theme-switch-slider::before { - transform: translateX(24px); -} - -.theme-switch:hover .theme-switch-slider { - transform: scale(1.02); -} - -.theme-switch:hover .sun svg, -.theme-switch:hover .moon svg { - transform: scale(1.08); -} - -.topbar-actions .btn { - height: 34px; - border-radius: 12px; - padding: 0 12px; - font-size: 13px; - font-weight: 600; - transform: translate3d(var(--mag-x), var(--mag-y), 0); - transition-duration: 220ms; -} - -.topbar-actions .btn:hover:not(:disabled) { - transform: translate3d(var(--mag-x), calc(var(--mag-y) - 2px), 0) scale(1.02); -} - -.user-chip { - display: inline-flex; - align-items: center; - gap: 6px; - height: 34px; - border-radius: 999px; - padding: 0 12px; - border: 1px solid rgba(148, 163, 184, 0.3); - background: rgba(249, 251, 255, 0.92); - color: var(--muted-strong); - font-size: 14px; - font-weight: 600; - box-shadow: 0 10px 18px rgba(13, 31, 68, 0.05); - transform: translate3d(var(--mag-x), var(--mag-y), 0); - transition: - transform 220ms var(--ease-out-soft), - box-shadow var(--dur-fast) var(--ease-out-soft), - border-color var(--dur-fast) var(--ease-smooth), - background-color var(--dur-fast) var(--ease-smooth); -} - -.user-chip:hover { - transform: translate3d(var(--mag-x), calc(var(--mag-y) - 1px), 0); - box-shadow: 0 16px 28px rgba(15, 23, 42, 0.08); -} - -.app-main { - flex: 1; - min-height: 0; - display: grid; - grid-template-columns: 200px 1fr; -} - -.app-side { - border-right: 1px solid var(--line-soft); - padding: 16px 12px; - display: flex; - flex-direction: column; - gap: 8px; -} - -.side-link { - display: flex; - align-items: center; - gap: 10px; - padding: 11px 12px; - border-radius: 14px; - color: var(--muted-strong); - text-decoration: none; - border: 1px solid transparent; - font-weight: 600; - font-size: 14px; - transition: - background-color var(--dur-fast) var(--ease-smooth), - border-color var(--dur-fast) var(--ease-smooth), - color var(--dur-fast) var(--ease-smooth), - transform var(--dur-fast) var(--ease-out-soft), - box-shadow var(--dur-fast) var(--ease-out-soft); -} - -.side-link:hover { - background: #ffffff; - border-color: rgba(128, 152, 192, 0.18); - color: var(--text); - transform: translate3d(calc(var(--mag-x) + 3px), var(--mag-y), 0); - box-shadow: 0 14px 24px rgba(15, 23, 42, 0.05); -} - -.side-link.active { - background: linear-gradient(135deg, rgba(37, 99, 235, 0.18), rgba(59, 130, 246, 0.08)); - border-color: rgba(37, 99, 235, 0.28); - color: var(--primary-strong); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.64), 0 10px 18px rgba(37, 99, 235, 0.1); -} - -.side-spacer { - flex: 1; -} - -.side-add-btn, -.side-lock-btn { - width: 100%; -} - -.side-add-btn { - justify-content: flex-start; - margin-top: 8px; -} - -.side-add-btn.btn-primary { - background: #1e4f95; - border-color: #1e4f95; -} - -.side-add-btn.btn-primary:hover { - background: #1b4888; - border-color: #1b4888; -} - -.content { - min-height: 0; - padding: 14px; - overflow: hidden; -} - -.route-stage { - height: 100%; - min-height: 0; - overflow: auto; -} - -@media (min-width: 901px) { - .route-stage { - animation: route-stage-in 240ms var(--ease-out-soft) both; - } -} - -.mobile-sidebar-mask { - position: fixed; - inset: 0; - background: rgba(15, 23, 42, 0.36); - z-index: 54; - opacity: 0; - visibility: hidden; - pointer-events: none; - transition: - opacity 220ms var(--ease-smooth), - visibility 220ms var(--ease-smooth); -} - -.mobile-sidebar-mask.open { - opacity: 1; - visibility: visible; - pointer-events: auto; -} - -.mobile-sidebar-head { - display: none; -} - -.vault-grid { - display: grid; - grid-template-columns: 240px minmax(420px, 46%) minmax(575px, 1fr); - gap: 12px; - height: 100%; - min-height: 0; - padding: 2px; -} - -.sidebar, -.list-panel, -.card { - background: var(--panel); - border: 1px solid var(--line); - border-radius: 18px; - box-shadow: var(--shadow-sm); -} - -.sidebar { - padding: 0; - overflow: auto; - border: none; - box-shadow: none; - background: transparent; -} - -.sidebar-block { - border: 1px solid var(--line); - border-radius: 19px; - padding: 12px; - margin-bottom: 8px; - background: var(--panel); -} - -.sidebar-title { - font-size: 13px; - font-weight: 700; - color: #344054; - margin-bottom: 8px; -} - -.sidebar-title-row { - display: flex; - align-items: center; - justify-content: space-between; - padding-bottom: 8px; -} - -.sidebar-title-row .sidebar-title { - margin-bottom: 0; -} - -.folder-title-actions { - display: inline-flex; - align-items: center; - gap: 8px; -} - -.folder-add-btn { - border: none; - background: transparent; - color: #334155; - display: inline-flex; - align-items: center; - justify-content: center; - cursor: pointer; - padding: 0; - line-height: 1; -} - -.folder-add-btn:hover { - color: #1d4ed8; -} - -.search-input { - width: 100%; - height: 48px; - border: 1px solid rgba(74, 103, 150, 0.42); - border-radius: 14px; - padding: 10px 14px; - font-size: 16px; - outline: none; - color: var(--text); - background: var(--panel); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9); - transition: - border-color var(--dur-fast) var(--ease-smooth), - box-shadow var(--dur-fast) var(--ease-out-soft), - background-color var(--dur-fast) var(--ease-smooth), - transform var(--dur-fast) var(--ease-out-soft); -} - -.search-input-wrap { - position: relative; - flex: 1 1 auto; - min-width: 0; -} - -.search-input:focus { - border-color: rgba(43, 102, 217, 0.6); - background-color: #fbfdff; - box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.11), 0 10px 20px rgba(37, 99, 235, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.95); - transform: translateY(-1px); -} - -.search-input-wrap .search-input { - padding-right: 42px; -} - -.search-clear-btn { - position: absolute; - top: 50%; - right: 9px; - width: 22px; - height: 22px; - display: inline-flex; - align-items: center; - justify-content: center; - border: none; - border-radius: 999px; - background: rgba(148, 163, 184, 0.18); - color: var(--muted); - cursor: pointer; - transform: translateY(-50%); - transition: background-color var(--dur-fast) var(--ease-out-soft), color var(--dur-fast) var(--ease-out-soft), transform var(--dur-fast) var(--ease-out-soft); -} - -.search-clear-btn:hover { - background: rgba(59, 130, 246, 0.18); - color: var(--brand); - transform: translateY(-50%) scale(1.04); -} - -.search-clear-btn:focus-visible { - outline: none; - box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.16); -} - -.tree-btn { - width: 100%; - min-width: 0; - border: none; - background: transparent; - text-align: left; - border-radius: 8px; - padding: 8px 10px; - margin-bottom: 4px; - cursor: pointer; - display: flex; - align-items: center; - gap: 8px; - transition: - background-color var(--dur-fast) var(--ease-smooth), - color var(--dur-fast) var(--ease-smooth), - transform var(--dur-fast) var(--ease-out-soft), - box-shadow var(--dur-fast) var(--ease-out-soft); -} - -.tree-btn:hover { - transform: translateX(2px); -} - -.tree-btn.active { - background: rgba(37, 99, 235, 0.09); - color: var(--primary-strong); - font-weight: 700; -} - -.tree-icon { - flex-shrink: 0; -} - -.tree-label { - min-width: 0; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.folder-row { - display: flex; - align-items: center; - gap: 6px; -} - -.folder-row .tree-btn { - margin-bottom: 0; -} - -.folder-delete-btn { - border: none; - background: transparent; - color: #64748b; - width: 24px; - height: 24px; - padding: 0; - cursor: pointer; - flex-shrink: 0; - display: inline-flex; - align-items: center; - justify-content: center; - border-radius: 6px; - transition: - color var(--dur-fast) var(--ease-smooth), - background-color var(--dur-fast) var(--ease-smooth), - transform var(--dur-fast) var(--ease-out-soft); -} - -.folder-delete-btn:hover { - color: #b91c1c; - background: #fee2e2; - transform: scale(1.06); -} - -.folder-edit-btn:hover { - color: #1d4ed8; - background: #dbeafe; -} - -.list-col { - display: flex; - flex-direction: column; - min-width: 0; - min-height: 0; - max-width: 540px; -} - -.toolbar { - margin: 0 0 8px 0; -} - -.toolbar.actions { - justify-content: flex-end; - gap: var(--actions-gap); -} - -.actions { - gap: var(--actions-gap); -} - -.toolbar .btn.small { - height: 30px; - border-radius: 999px; - font-size: 12px; -} - -.list-head { - display: flex; - align-items: center; - gap: 10px; - margin-bottom: 8px; -} - -.list-head .search-input-wrap { - flex: 1 1 auto; - min-width: 0; -} - -.list-head .search-input { - height: 42px; -} - -.list-head .btn { - white-space: nowrap; -} - -.list-count { - flex: 0 0 auto; - color: var(--text-muted); - font-size: 12px; - white-space: nowrap; -} - -.list-icon-btn { - white-space: nowrap; -} - -.sort-menu-wrap { - position: relative; - flex: 0 0 auto; -} - -.sort-trigger { - min-width: 36px; - width: 36px; - padding: 0; - justify-content: center; - gap: 0; -} - -.sort-trigger.active { - background: #e9f1ff; - border-color: #a9c2ee; - color: #175ddc; -} - -.sort-menu { - position: absolute; - top: calc(100% + 6px); - right: 0; - z-index: 30; - min-width: 156px; - padding: 6px; - border: 1px solid var(--line); - border-radius: 16px; - background: var(--panel); - box-shadow: var(--shadow-md); - transform-origin: top right; - animation: menu-in 190ms var(--ease-out-strong) both; -} - -.sort-menu-item { - width: 100%; - border: none; - background: transparent; - border-radius: 10px; - padding: 9px 10px; - display: flex; - align-items: center; - justify-content: space-between; - gap: 10px; - color: #0f172a; - font-size: 13px; - text-align: left; - cursor: pointer; - transition: - background-color var(--dur-fast) var(--ease-smooth), - color var(--dur-fast) var(--ease-smooth), - transform var(--dur-fast) var(--ease-out-soft); -} - -.sort-menu-item:hover { - background: #f2f7ff; - transform: translateX(2px); -} - -.sort-menu-item.active { - background: rgba(37, 99, 235, 0.1); - color: var(--primary-strong); - font-weight: 700; -} - -.sort-menu-check-placeholder { - width: 14px; - height: 14px; - flex: 0 0 14px; -} - -.list-panel { - overflow: auto; - min-height: 0; - padding: 8px; -} - -.list-item { - width: 100%; - background: rgba(249, 251, 255, 0.88); - border: 1px solid var(--line); - border-radius: 14px; - padding: 10px 12px; - display: flex; - align-items: center; - gap: 10px; - margin-bottom: 8px; - min-height: 66px; - box-sizing: border-box; - position: relative; - cursor: pointer; - overflow: hidden; - transform-origin: 50% 50%; - transition: - background-color var(--dur-fast) var(--ease-smooth), - border-color var(--dur-fast) var(--ease-smooth), - box-shadow var(--dur-fast) var(--ease-out-soft), - transform var(--dur-fast) var(--ease-out-soft); - contain: paint; -} - -.list-item::before { - content: ''; - position: absolute; - inset: 0; - background: - linear-gradient(90deg, rgba(43, 102, 217, 0.06), transparent 24%, transparent 76%, rgba(14, 165, 233, 0.05)), - radial-gradient(circle at 18px 50%, rgba(255, 255, 255, 0.28), transparent 44%); - opacity: 0; - transition: - opacity var(--dur-fast) var(--ease-smooth), - transform 320ms var(--ease-out-soft); - transform: translateX(-8px); - pointer-events: none; -} - -.stagger-item { - opacity: 0; - animation: stagger-rise 520ms var(--ease-out-strong) both; -} - -.list-item:hover { - background: #fcfdff; - border-color: rgba(148, 163, 184, 0.26); - box-shadow: 0 16px 28px rgba(15, 23, 42, 0.06); - transform: translate3d(0, -2px, 0) scale(1.008); -} - -.list-item:hover::before { - opacity: 1; - transform: translateX(0); -} - -.list-item.active { - background: rgba(37, 99, 235, 0.1); - border-color: rgba(43, 102, 217, 0.26); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.42), 0 14px 24px rgba(43, 102, 217, 0.08); - transform: translate3d(0, -1px, 0) scale(1.004); -} - -.list-item.active::before { - opacity: 1; - transform: translateX(0); -} - -.row-check { - width: 16px; - height: 16px; - position: relative; - z-index: 2; - cursor: pointer; -} - -.row-main { - flex: 1; - min-width: 0; - border: none; - background: transparent; - padding: 0; - display: flex; - align-items: center; - gap: 10px; - text-align: left; - cursor: pointer; - position: relative; - z-index: 1; - transition: transform 220ms var(--ease-out-soft); -} - -.list-item:hover .row-main, -.list-item.active .row-main { - transform: translateX(2px); -} - -.list-icon-wrap { - width: 24px; - height: 24px; - display: grid; - place-items: center; - flex-shrink: 0; - transition: transform 240ms var(--ease-out-soft), filter 240ms var(--ease-out-soft); -} - -.list-icon { - width: 24px; - height: 24px; - border-radius: 6px; -} - -.list-icon-fallback { - display: grid; - place-items: center; - color: #64748b; - transition: color var(--dur-fast) var(--ease-smooth), transform 240ms var(--ease-out-soft); -} - -.list-icon-fallback svg { - width: 24px; - height: 24px; -} - -.list-text { - flex: 1; - min-width: 0; - overflow: hidden; - transition: transform 220ms var(--ease-out-soft); -} - -.list-title { - display: flex; - align-items: center; - gap: 6px; - color: var(--primary-strong); - font-size: 15px; - font-weight: 700; - min-width: 0; - transition: color var(--dur-fast) var(--ease-smooth), letter-spacing 220ms var(--ease-out-soft); -} - -.list-title-text { - min-width: 0; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.list-badge { - display: inline-flex; - align-items: center; - justify-content: center; - padding: 2px 6px; - border-radius: 999px; - font-size: 11px; - font-weight: 700; - line-height: 1; - color: #475569; - background: #e8eef8; - flex-shrink: 0; -} - -.list-badge.danger { - color: #fff; - background: var(--danger); -} - -.list-sub { - display: block; - color: #5f6f85; - margin-top: 2px; - font-size: 13px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - transition: color var(--dur-fast) var(--ease-smooth), transform 220ms var(--ease-out-soft), opacity var(--dur-fast) var(--ease-smooth); -} - -.list-item:hover .list-icon-wrap, -.list-item.active .list-icon-wrap { - transform: translateX(1px) scale(1.04); -} - -.list-item:hover .list-icon-fallback, -.list-item.active .list-icon-fallback { - color: var(--primary-strong); - transform: scale(1.04); -} - -.list-item:hover .list-text, -.list-item.active .list-text { - transform: translateX(1px); -} - -.list-item:hover .list-title, -.list-item.active .list-title { - letter-spacing: -0.012em; -} - -.list-item:hover .list-sub, -.list-item.active .list-sub { - transform: translateX(1px); - opacity: 0.92; -} - -.detail-col { - overflow: auto; - min-height: 0; -} - -.mobile-panel-head { - display: none; -} - -.card { - padding: 16px 18px; - margin-bottom: 10px; -} - -.detail-col > .card { - opacity: 1; - animation: none; -} - -.detail-col > .card:nth-of-type(1) { animation-delay: 0ms; } -.detail-col > .card:nth-of-type(2) { animation-delay: 0ms; } -.detail-col > .card:nth-of-type(3) { animation-delay: 0ms; } -.detail-col > .card:nth-of-type(4) { animation-delay: 0ms; } -.detail-col > .card:nth-of-type(5) { animation-delay: 0ms; } - -.detail-switch-stage { - animation: none; -} - -.detail-switch-stage > .card { - opacity: 1; - animation: none; -} - -.card h4 { - margin-top: 0; - margin-bottom: 12px; -} - -.detail-title { - margin: 0; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.detail-sub { - color: #667085; - margin-top: 8px; -} - -.password-history-link { - margin-top: 10px; - padding: 0; - border: none; - background: transparent; - color: var(--primary); - font: inherit; - font-weight: 700; - cursor: pointer; -} - -.password-history-link:hover { - color: var(--primary-hover); - text-decoration: underline; -} - -.kv-line { - display: flex; - justify-content: space-between; - align-items: center; - gap: 10px; - border-bottom: 1px solid rgba(154, 172, 205, 0.22); - padding: 10px 0; -} - -.kv-line:last-child { - border-bottom: none; -} - -.kv-line > span { - color: #64748b; -} - -.kv-row { - display: grid; - grid-template-columns: minmax(0px, 80px) minmax(0, 1fr) auto; - align-items: center; - gap: 10px; - border-bottom: 1px solid rgba(154, 172, 205, 0.22); - padding: 10px 0; -} - -.kv-row:last-child { - border-bottom: none; -} - -.password-history-dialog { - width: min(560px, calc(100vw - 32px)); -} - -.password-history-head { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - margin-bottom: 12px; -} - -.password-history-head .dialog-title { - margin: 0; -} - -.password-history-close { - display: inline-flex; - align-items: center; - justify-content: center; - width: 34px; - height: 34px; - border: none; - border-radius: 999px; - background: transparent; - color: var(--muted-strong); - cursor: pointer; -} - -.password-history-close:hover { - background: var(--panel-soft); - color: var(--text); -} - -.password-history-list { - display: grid; - gap: 12px; - margin: 10px 0 18px; -} - -.password-history-item { - position: relative; - border: 1px solid var(--line); - border-radius: 14px; - background: var(--panel-soft); - padding: 16px 54px 14px 16px; - box-shadow: var(--shadow-sm); -} - -.password-history-value { - color: var(--primary); - font-size: 22px; - line-height: 1.15; - letter-spacing: 0.01em; - word-break: break-all; -} - -.password-history-time { - margin-top: 8px; - color: var(--muted); -} - -.password-history-copy { - position: absolute; - top: 12px; - right: 12px; -} - -.password-history-copy-btn { - min-width: 36px; - padding: 0; - width: 36px; - height: 36px; -} - -.kv-label { - color: #64748b; - min-width: 0; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.kv-main { - display: flex; - align-items: center; - gap: 10px; - justify-content: flex-start; - min-width: 0; -} - -.kv-main > strong { - min-width: 0; -} - -.totp-inline { - display: inline-flex; - align-items: center; - gap: 10px; - min-width: 0; -} - -.totp-timer { - width: 30px; - height: 30px; - position: relative; - display: inline-grid; - place-items: center; - flex-shrink: 0; -} - -.totp-ring { - width: 30px; - height: 30px; - transform: rotate(-90deg); -} - -.totp-ring-track, -.totp-ring-progress { - fill: none; - stroke-width: 3; -} - -.totp-ring-track { - stroke: #d9e2ef; -} - -.totp-ring-progress { - stroke: #2563eb; - stroke-linecap: round; - transition: stroke-dashoffset 260ms linear, stroke 200ms ease; -} - -.totp-timer-value { - position: absolute; - inset: 0; - display: grid; - place-items: center; - font-size: 11px; - font-weight: 700; - color: #0f172a; -} - -.totp-codes-page { - display: flex; - flex-direction: column; - min-height: 100%; -} - -.totp-codes-list { - display: grid; - gap: 10px; - grid-template-columns: repeat(var(--totp-columns, 1), minmax(320px, 1fr)); - align-items: start; - width: 100%; -} - -.totp-code-row { - display: grid; - grid-template-columns: auto minmax(0, 1fr) auto; - align-items: center; - gap: 10px; - padding: 12px; - border: 1px solid #e2e8f0; - border-radius: 12px; - background: #f8fafc; - width: 100%; - min-width: 0; - max-width: none; - transition: - transform 220ms var(--ease-out-soft), - box-shadow var(--dur-fast) var(--ease-out-soft), - border-color var(--dur-fast) var(--ease-smooth), - background-color var(--dur-fast) var(--ease-smooth), - opacity var(--dur-fast) var(--ease-smooth); -} - -.totp-code-row.is-dragging { - z-index: 2; - border-color: rgba(37, 99, 235, 0.3); - background: color-mix(in srgb, var(--panel) 88%, white 12%); - box-shadow: 0 18px 36px rgba(15, 23, 42, 0.14); -} - -.totp-code-info { - display: flex; - align-items: center; - gap: 10px; - min-width: 0; -} - -.totp-drag-btn { - min-width: 24px; - width: 24px; - height: 34px; - padding: 0; - gap: 0; - color: var(--muted); - cursor: grab; - align-self: center; - touch-action: none; - -webkit-user-select: none; - user-select: none; - border-color: transparent; - background: transparent; - box-shadow: none; - border-radius: 10px; - position: relative; - overflow: visible; - opacity: 0.82; -} - -.totp-drag-btn:hover { - color: var(--primary-strong); - border-color: transparent; - background: transparent; - box-shadow: none; - opacity: 1; -} - -.totp-drag-btn:active { - cursor: grabbing; - border-color: transparent; - background: transparent; - box-shadow: none; -} - -.totp-drag-btn::before { - content: ''; - position: absolute; - inset: -10px; - border-radius: 12px; -} - -.totp-drag-btn .btn-icon { - opacity: 0.9; -} - -.totp-code-main { - display: flex; - align-items: center; - gap: 6px; - min-width: 0; - flex-shrink: 0; -} - -.totp-code-main strong { - font-size: 22px; - line-height: 1; - letter-spacing: 0.04em; - white-space: nowrap; -} - -.totp-code-meta { - min-width: 0; -} - -.totp-code-name, -.totp-code-username { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.totp-code-name { - font-size: 15px; - font-weight: 700; - color: #0f172a; -} - -.totp-code-username { - margin-top: 2px; - font-size: 13px; - color: #64748b; -} - -.totp-copy-btn { - min-width: 28px; - width: 28px; - height: 28px; - padding: 0; - border-radius: 999px; - flex-shrink: 0; - gap: 0; -} - -.value-ellipsis { - display: block; - max-width: 100%; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.kv-actions { - display: flex; - align-items: center; - justify-content: flex-end; - gap: 8px; - flex-wrap: wrap; - flex-shrink: 0; -} - -.exposed-status { - color: #475569; -} - -.exposed-status.danger { - color: var(--danger); -} - -.attachment-list { - display: grid; - gap: 0; -} - -.attachment-head { - margin-bottom: 8px; -} - -.attachment-head h4 { - margin-bottom: 0; -} - -.attachment-add-btn { - min-width: 32px; - padding: 0 8px; -} - -.attachment-file-input { - display: none; -} - -.attachment-row { - display: flex; - align-items: center; - justify-content: space-between; - gap: 10px; - border-bottom: 1px solid #ecf0f5; - padding: 10px 0; -} - -.attachment-row:last-child { - border-bottom: none; -} - -.attachment-main { - display: flex; - align-items: center; - gap: 8px; - min-width: 0; -} - -.attachment-text { - min-width: 0; - display: grid; - gap: 2px; -} - -.attachment-text span { - color: #64748b; - font-size: 12px; -} - -.attachment-row.is-removed { - opacity: 0.6; -} - -.attachment-row.is-removed .attachment-text strong { - text-decoration: line-through; -} - -.attachment-queue-title { - font-size: 12px; - color: #64748b; - font-weight: 700; - padding: 8px 0 2px; -} - -.boolean-main { - min-width: 0; - gap: 8px; -} - -.boolean-text { - min-width: 0; -} - -.custom-field-card { - display: grid; - gap: 8px; - padding: 10px 0; - border-bottom: 1px solid #ecf0f5; -} - -.custom-field-card:last-child { - border-bottom: none; - padding-bottom: 0; -} - -.custom-field-label { - display: block; - color: #64748b; - font-size: 12px; - font-weight: 700; - line-height: 1.2; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.custom-field-body { - display: grid; - grid-template-columns: minmax(0, 1fr) auto; - gap: 10px; - align-items: center; -} - -.custom-field-value { - min-width: 0; -} - -.custom-field-value > .input { - width: 100%; -} - -.custom-field-check { - margin-bottom: 0; - display: inline-flex; - align-items: center; - gap: 8px; -} - -.custom-field-check span { - color: #334155; - font-size: 14px; - font-weight: 600; -} - -.custom-field-remove { - white-space: nowrap; -} - -.notes { - white-space: pre-wrap; - overflow-wrap: anywhere; - word-break: break-word; - color: #334155; - min-height: 48px; -} - -.empty { - color: #667085; - display: grid; - place-items: center; - min-height: 120px; -} - -.stack { - display: grid; - gap: 12px; -} - -.import-export-page { - display: grid; - gap: 12px; -} - -.import-export-hero { - margin-bottom: 0; -} - -.import-export-hero h3 { - margin: 0 0 8px 0; -} - -.import-export-hero-sub { - margin: 0; - color: #5f6f85; - line-height: 1.5; -} - -.import-export-feature-grid { - margin-top: 12px; - display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 10px; -} - -.import-export-feature-item { - border: 1px solid #d9e4f2; - border-radius: 10px; - background: #f7faff; - padding: 10px; - display: flex; - align-items: flex-start; - gap: 10px; - min-width: 0; -} - -.import-export-feature-icon { - width: 28px; - height: 28px; - border-radius: 8px; - border: 1px solid #cbdcf7; - background: #e9f1ff; - color: #1d4ed8; - display: inline-grid; - place-items: center; - flex-shrink: 0; -} - -.import-export-feature-item strong { - display: block; - font-size: 14px; - line-height: 1.35; -} - -.import-export-feature-item p { - margin: 4px 0 0 0; - color: #64748b; - font-size: 13px; - line-height: 1.45; -} - -.import-export-panels { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 12px; - align-items: start; -} - -.backup-grid { - display: grid; - grid-template-columns: 280px 280px minmax(0, 1fr); - gap: 12px; - align-items: start; - padding: 2px; -} - -.backup-operations-sidebar, -.backup-destination-sidebar, -.backup-detail-panel { - min-width: 0; - background: #fff; - border: 1px solid #d8dee8; - border-radius: 12px; - box-shadow: 0 1px 2px rgba(15, 23, 42, 0.05); - padding: 12px; -} - -.backup-actions-stack { - display: grid; - gap: 10px; -} - -.backup-option-field { - display: inline-flex; - align-items: center; - gap: 8px; -} - -.backup-option-label { - display: inline-flex; - align-items: center; - gap: 8px; - margin: 0; - font-size: 15px; - font-weight: 700; - color: #0f172a; - cursor: pointer; -} - -.backup-option-label input[type='checkbox'] { - width: 22px; - height: 22px; - margin: 0; - flex-shrink: 0; -} - -.backup-help-wrap { - position: relative; - display: inline-flex; - align-items: center; -} - -.backup-help-trigger { - width: 22px; - height: 22px; - border: 1px solid #bfd1f3; - border-radius: 999px; - padding: 0; - background: #eef4ff; - color: #1d4ed8; - font-size: 13px; - font-weight: 800; - line-height: 1; - display: inline-flex; - align-items: center; - justify-content: center; - cursor: pointer; - flex-shrink: 0; -} - -.backup-help-trigger:hover, -.backup-help-trigger:focus-visible { - border-color: #7ea4ef; - background: #e1ecff; - outline: none; -} - -.backup-help-bubble { - position: absolute; - left: 50%; - top: calc(100% + 10px); - z-index: 30; - width: min(320px, calc(100vw - 40px)); - padding: 10px 12px; - border: 1px solid #d5dce7; - border-radius: 12px; - background: #ffffff; - box-shadow: 0 16px 38px rgba(15, 23, 42, 0.14); - color: #475467; - font-size: 13px; - line-height: 1.55; - transform: translate(-50%, -4px); - opacity: 0; - visibility: hidden; - pointer-events: none; - transition: opacity 140ms ease, transform 140ms ease, visibility 140ms ease; -} - -.backup-help-bubble::before { - content: ''; - position: absolute; - left: 50%; - top: -6px; - width: 10px; - height: 10px; - background: #ffffff; - border-left: 1px solid #d5dce7; - border-top: 1px solid #d5dce7; - transform: translateX(-50%) rotate(45deg); -} - -.backup-help-wrap:hover .backup-help-bubble, -.backup-help-wrap:focus-within .backup-help-bubble, -.backup-help-wrap.open .backup-help-bubble { - opacity: 1; - visibility: visible; - pointer-events: auto; - transform: translate(-50%, 0); -} - -.backup-manual-inline-actions { - grid-template-columns: repeat(2, minmax(0, 1fr)); -} - -.backup-schedule-list { - display: grid; - gap: 8px; -} - -.backup-recommendation-list { - display: grid; - gap: 8px; -} - -.backup-recommendation-group + .backup-recommendation-group { - margin-top: 12px; -} - -.backup-recommendation-group-title { - margin: 0 0 8px; - font-size: 14px; - font-weight: 700; - color: #0f172a; -} - -.backup-recommendation-row { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; -} - -.backup-recommendation-linked { - display: grid; - gap: 4px; -} - -.backup-recommendation-linked-item { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - font-size: 12px; - color: #475467; -} - -.backup-recommendation-card { - border: 1px solid var(--line); - border-radius: 12px; - background: #f8fbff; - padding: 14px; - display: grid; - gap: 12px; -} - -.backup-recommendation-header { - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 12px; -} - -.backup-recommendation-actions { - display: flex; - flex-wrap: wrap; - gap: 8px; -} - -.backup-recommendation-steps { - display: grid; - gap: 8px; -} - -.backup-recommendation-step { - color: #475467; - line-height: 1.5; -} - -.backup-recommendation-inline-note { - color: #475467; - line-height: 1.5; -} - -.backup-recommendation-dav-list { - display: grid; - gap: 8px; -} - -.backup-recommendation-dav-item { - display: grid; - gap: 4px; - padding: 10px 12px; - border: 1px solid var(--line); - border-radius: 10px; - background: #fff; -} - -.backup-recommendation-dav-item code { - overflow-wrap: anywhere; -} - -.backup-recommendation-referral { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 8px; - color: #475467; -} - -.backup-destination-list { - display: grid; - gap: 8px; -} - -.backup-destination-item { - width: 100%; - border: 1px solid var(--line); - border-radius: 12px; - background: #fff; - padding: 12px; - text-align: left; - display: grid; - gap: 6px; - cursor: pointer; - transition: border-color 0.15s ease, background 0.15s ease, box-shadow 0.15s ease; -} - -.backup-destination-item:hover { - border-color: #93c5fd; - background: #f8fbff; -} - -.backup-destination-item.active { - border-color: var(--primary); - background: #eff6ff; - box-shadow: 0 0 0 1px rgba(37, 99, 235, 0.08); -} - -.backup-destination-top { - display: flex; - justify-content: space-between; - gap: 12px; - align-items: center; -} - -.backup-destination-name { - font-weight: 700; - color: #0f172a; - overflow-wrap: anywhere; -} - -.backup-destination-type { - border-radius: 999px; - padding: 2px 8px; - background: #e2e8f0; - color: #334155; - font-size: 12px; - white-space: nowrap; -} - -.backup-destination-meta { - color: #64748b; - font-size: 13px; - line-height: 1.4; -} - -.backup-destination-addbar { - margin-top: 10px; -} - -.backup-add-chooser { - display: flex; - flex-wrap: wrap; - gap: 8px; - margin-top: 10px; -} - -.backup-schedule-current { - display: grid; - gap: 4px; - margin-bottom: 12px; - padding: 10px 12px; - border: 1px solid var(--line); - border-radius: 12px; - background: #f8fafc; - color: #475467; -} - -.backup-schedule-current strong { - color: #0f172a; -} - -.backup-name-row { - display: grid; - grid-template-columns: repeat(4, minmax(0, 1fr)); - gap: 10px; - align-items: end; - margin-bottom: 8px; -} - -.backup-name-field { - margin: 0; - grid-column: 1 / span 3; -} - -.backup-type-field { - margin: 0; - grid-column: 4; -} - -.backup-detail-schedule-grid { - grid-template-columns: repeat(4, minmax(0, 1fr)) !important; -} - -.backup-interval-row { - display: grid; - grid-template-columns: minmax(0, 1fr) 86px; - gap: 10px; - align-items: start; -} - -.backup-interval-presets { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 3px; -} - -.backup-interval-preset { - height: 22px; - border: 1px solid #cdd7e6; - border-radius: 999px; - background: #f8fafc; - color: #475569; - font-size: 12px; - font-weight: 700; - cursor: pointer; - transition: - border-color var(--dur-fast) var(--ease-smooth), - background-color var(--dur-fast) var(--ease-smooth), - color var(--dur-fast) var(--ease-smooth), - transform var(--dur-fast) var(--ease-out-soft), - box-shadow var(--dur-fast) var(--ease-out-soft); -} - -.backup-interval-preset:hover:not(:disabled) { - border-color: #2563eb; - color: #2563eb; - background: #eff6ff; -} - -.backup-interval-preset.active { - border-color: #2563eb; - background: #2563eb; - color: #fff; -} - -.backup-interval-preset:disabled { - cursor: not-allowed; - opacity: 0.55; -} - -.backup-retention-input { - display: grid; - grid-template-columns: minmax(0, 1fr) auto; - align-items: center; - gap: 8px; - width: 100%; -} - -.backup-retention-input .input { - min-width: 0; - width: 100%; -} - -.backup-inline-suffix-wrap { - position: relative; - width: 100%; -} - -.backup-inline-suffix { - position: absolute; - right: 12px; - top: 50%; - transform: translateY(-50%); - color: #64748b; - font-size: 13px; - font-weight: 700; - pointer-events: none; -} - -.backup-schedule-attachments-row { - margin-bottom: 4px; -} - - -.backup-retention-suffix { - color: #475467; - white-space: nowrap; -} - -.backup-combined-grid { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 12px; - align-items: start; -} - -.backup-status-card { - border: 1px solid var(--line); - border-radius: 12px; - padding: 12px; - background: #f8fbff; - margin-bottom: 12px; -} - -.backup-status-grid { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 10px 14px; - color: #475467; - line-height: 1.45; -} - -.backup-status-grid strong { - display: block; - margin-bottom: 4px; - color: #0f172a; -} - -.backup-status-error { - margin-top: 12px; -} - -.backup-divider { - height: 1px; - background: var(--line); - margin: 14px 0; -} - -.backup-remote-panel { - margin-top: 0; -} - -.backup-browser-path { - display: flex; - gap: 8px; - align-items: center; - padding: 10px 12px; - border: 1px solid var(--line); - border-radius: 10px; - background: #f8fafc; - margin-bottom: 10px; - overflow-wrap: anywhere; -} - -.backup-browser-path strong { - color: #0f172a; -} - -.backup-browser-nav { - margin-bottom: 10px; -} - -.backup-browser-list { - border: 1px solid var(--line); - border-radius: 12px; - overflow: hidden; - background: #fff; -} - -.backup-browser-pagination { - display: flex; - align-items: center; - justify-content: flex-end; - gap: 10px; - margin-top: 10px; -} - -.backup-browser-page-indicator { - min-width: 48px; - text-align: center; - color: #64748b; - font-size: 13px; - font-weight: 700; -} - -.backup-browser-row + .backup-browser-row { - border-top: 1px solid var(--line); -} - -.backup-browser-row { - display: grid; - grid-template-columns: minmax(0, 1fr) auto auto; - gap: 10px; - align-items: center; - padding: 10px 12px; -} - -.backup-browser-entry { - border: none; - background: transparent; - text-align: left; - display: inline-flex; - align-items: center; - gap: 8px; - padding: 0; - color: #0f172a; - cursor: pointer; -} - -.backup-browser-entry.file { - cursor: default; -} - -.backup-browser-name { - font-weight: 700; - overflow-wrap: anywhere; -} - -.backup-browser-meta { - display: grid; - justify-items: end; - gap: 4px; - color: #64748b; - font-size: 13px; - text-align: right; -} - -.backup-browser-actions { - justify-content: flex-end; -} - -.backup-browser-empty { - border: 1px dashed var(--line); - border-radius: 12px; - padding: 18px 14px; - text-align: center; - color: #64748b; -} - -.backup-list { - margin: 12px 0 14px; - padding-left: 18px; - color: #475467; - line-height: 1.55; -} - -.backup-list li + li { - margin-top: 6px; -} - -.backup-file-meta { - margin: -2px 0 12px; - color: #64748b; - font-size: 13px; - line-height: 1.45; - word-break: break-word; -} - -.backup-inline-note { - margin: 0 0 12px; - color: #64748b; - line-height: 1.5; -} - -.import-export-panel h3 { - margin: 0 0 6px 0; -} - -.field-grid { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 10px; -} - -.field-span-2 { - grid-column: 1 / -1; -} - -.totp-grid { - display: grid; - grid-template-columns: 220px 1fr; - gap: 14px; - margin-bottom: 14px; -} - -.totp-qr { - border: 1px solid var(--line); - border-radius: 10px; - background: #fff; - display: grid; - place-items: center; - min-height: 220px; - padding: 8px; -} - -.totp-qr svg { - width: 180px; - height: 180px; -} - -.totp-qr img { - width: 180px; - height: 180px; - background: #fff; - border-radius: 8px; -} - -.section-head { - display: flex; - align-items: center; - justify-content: space-between; - margin-bottom: 10px; -} - -.actions { - display: flex; - gap: 8px; - flex-wrap: wrap; -} - -.muted-inline { - color: var(--muted); - align-self: center; - font-size: 14px; -} - -.create-menu-wrap { - position: relative; -} - -.create-menu { - position: absolute; - left: 0; - top: calc(100% + 6px); - width: 220px; - background: #fff; - border: 1px solid var(--line); - border-radius: 12px; - box-shadow: 0 12px 28px rgba(15, 23, 42, 0.18); - overflow: hidden; - z-index: 20; - transform-origin: bottom left; - animation: menu-in 190ms var(--ease-out-strong) both; -} - -.create-menu-item { - width: 100%; - border: none; - background: #fff; - text-align: left; - padding: 11px 12px; - cursor: pointer; - font-weight: 600; - display: flex; - align-items: center; - gap: 8px; - transition: - background-color var(--dur-fast) var(--ease-smooth), - color var(--dur-fast) var(--ease-smooth), - transform var(--dur-fast) var(--ease-out-soft); -} - -.create-menu-item:hover { - background: #f1f5f9; - transform: translateX(2px); -} - -.uri-row { - display: grid; - grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto; - gap: 8px; - margin-bottom: 8px; -} - -.website-row { - display: grid; - grid-template-columns: auto minmax(0, 1fr) minmax(130px, 160px) auto; - gap: 8px; - margin-bottom: 8px; - align-items: center; - padding: 6px; - border: 1px solid transparent; - border-radius: 18px; - background: color-mix(in srgb, var(--panel) 84%, transparent); - transition: - border-color var(--dur-fast) var(--ease-smooth), - background-color var(--dur-fast) var(--ease-smooth), - box-shadow var(--dur-fast) var(--ease-out-soft), - transform 220ms var(--ease-out-soft), - opacity var(--dur-fast) var(--ease-smooth); -} - -.website-row.is-dragging { - opacity: 0.48; - border-color: rgba(37, 99, 235, 0.24); - background: color-mix(in srgb, var(--panel-soft) 92%, white 8%); - box-shadow: var(--shadow-sm); -} - -.website-row.is-shift-up { - transform: translateY(calc(-100% - 8px)); -} - -.website-row.is-shift-down { - transform: translateY(calc(100% + 8px)); -} - -.website-row.is-drop-target { - border-color: rgba(37, 99, 235, 0.34); - background: rgba(37, 99, 235, 0.08); - box-shadow: 0 16px 28px rgba(37, 99, 235, 0.1); -} - -.website-drag-btn { - min-width: 28px; - width: 28px; - height: 48px; - padding: 0; - gap: 0; - cursor: grab; - color: var(--muted); - border-color: transparent; - background: transparent; - box-shadow: none; - border-radius: 10px; - position: relative; - overflow: visible; - opacity: 0.82; - touch-action: none; - -webkit-user-select: none; - user-select: none; -} - -.website-drag-btn:hover { - color: var(--primary-strong); - border-color: transparent; - background: transparent; - box-shadow: none; - opacity: 1; -} - -.website-drag-btn:active { - cursor: grabbing; - border-color: transparent; - background: transparent; - box-shadow: none; -} - -.website-drag-btn::before { - content: ''; - position: absolute; - inset: -8px; - border-radius: 12px; -} - -.website-drag-btn .btn-icon { - opacity: 0.9; -} - -.website-match-select { - height: 48px; - font-size: 13px; - line-height: 1.2; - padding-top: 10px; - padding-bottom: 10px; - padding-right: 38px; -} - -.website-match-select option { - font-size: 13px; -} - -.website-row .btn { - justify-self: start; - width: auto; -} - -@media (max-width: 760px) { - .website-row { - grid-template-columns: auto minmax(0, 1fr) auto; - align-items: start; - } - - .website-row > :nth-child(1) { - grid-column: 1; - grid-row: 1; - align-self: center; - } - - .website-row > :nth-child(2) { - grid-column: 2 / span 2; - grid-row: 1; - } - - .website-row > :nth-child(3) { - grid-column: 1 / span 2; - grid-row: 2; - } - - .website-row > :nth-child(4) { - grid-column: 3; - grid-row: 2; - justify-self: start; - } -} - -.cf-check { - margin-bottom: 0; -} - -.cf-check.view { - margin: 0; -} - -.cf-check input[type='checkbox'] { - width: 22px; - height: 22px; -} - -.field-type-pill { - align-self: center; - height: 34px; - line-height: 34px; - border-radius: 999px; - background: #eef4ff; - color: #175ddc; - font-size: 12px; - font-weight: 700; - padding: 0 10px; -} - -.star-on { - background: #eef4ff; -} - -.detail-actions { - display: flex; - justify-content: space-between; - align-items: center; - margin: 12px 0; -} - -.detail-delete-btn { - margin-left: auto; -} - -.send-options { - display: grid; - gap: 8px; - color: #3a4a64; -} - -.send-options label { - display: inline-flex; - align-items: center; - gap: 8px; -} - -.local-error { - margin-top: 10px; - color: #b42318; - font-weight: 600; -} - -.status-ok { - margin: 2px 0 10px 0; - color: #0f766e; - font-weight: 700; -} - -.restore-progress-card { - margin: 8px 0 12px; - padding: 14px 16px; - border-radius: 12px; - border: 1px solid #d7e2f1; - background: #ffffff; - box-shadow: 0 8px 20px rgba(15, 23, 42, 0.10); -} - -.restore-progress-overlay { - position: fixed; - inset: 0; - z-index: 1250; - display: grid; - place-items: center; - padding: 20px; - background: rgba(15, 23, 42, 0.30); -} - -.restore-progress-modal { - width: min(520px, 100%); - margin: 0; -} - -.restore-progress-head { - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: 12px; - margin-bottom: 12px; -} - -.restore-progress-kicker { - font-size: 12px; - font-weight: 600; - letter-spacing: 0.02em; - color: #64748b; -} - -.restore-progress-title { - margin: 4px 0 2px; - font-size: 20px; - line-height: 1.2; -} - -.restore-progress-subtitle { - margin: 0; - color: #6b7280; - font-size: 13px; -} - -.restore-progress-elapsed { - flex: 0 0 auto; - min-width: 88px; - padding: 6px 8px; - border-radius: 10px; - background: #f8fbff; - border: 1px solid #d7e2f1; - color: #475569; - font-weight: 600; - font-size: 13px; - text-align: center; -} - -.restore-progress-meter { - height: 6px; - border-radius: 999px; - background: #e7eef8; - overflow: hidden; -} - -.restore-progress-meter-bar { - display: block; - height: 100%; - border-radius: inherit; - background: #3a71d8; - transition: width 280ms ease; -} - -.restore-progress-current { - margin-top: 12px; - padding: 10px 12px; - border-radius: 10px; - background: #f8fbff; - border: 1px solid #d7e2f1; -} - -.restore-progress-current strong { - display: block; - color: #0f172a; - font-size: 14px; -} - -.restore-progress-current p { - margin: 4px 0 0; - color: #64748b; - line-height: 1.45; - font-size: 13px; -} - -.restore-progress-list { - list-style: none; - margin: 12px 0 0; - padding: 0; - display: grid; - gap: 6px; -} - -.restore-progress-item { - display: flex; - align-items: center; - gap: 8px; - min-height: 30px; - color: #64748b; - font-weight: 500; - font-size: 13px; -} - -.restore-progress-item.active { - color: #1d4ed8; -} - -.restore-progress-item.done { - color: #475569; -} - -.restore-progress-dot { - width: 8px; - height: 8px; - border-radius: 999px; - background: #cbd5e1; - flex: 0 0 auto; -} - -.restore-progress-item.active .restore-progress-dot { - background: #1d4ed8; -} - -.restore-progress-item.done .restore-progress-dot { - background: #94a3b8; -} - -.kv-line strong { - overflow-wrap: anywhere; -} - -.check-line { - display: flex; - align-items: center; - gap: 8px; - margin-bottom: 12px; - color: #334155; -} - -.table { - width: 100%; - border-collapse: collapse; -} - -.table th, -.table td { - text-align: left; - border-bottom: 1px solid var(--line); - padding: 10px 8px; - font-size: 14px; -} - -.table td::before { - display: none; -} - -.table th { - color: #667085; -} - -.input.small { - width: 120px; -} - -.invite-toolbar { - display: flex; - justify-content: space-between; - align-items: center; - gap: 10px; - flex-wrap: wrap; - margin-bottom: 10px; -} - -.invite-create-group { - align-items: flex-end; -} - -.invite-hours-field { - margin: 0; -} - -.invite-hours-field > span { - margin-bottom: 6px; - color: #5f6f85; - font-size: 12px; - font-weight: 600; -} - -.trusted-cell { - display: inline-flex; - align-items: center; - gap: 6px; -} - -.device-status-pill { - display: inline-flex; - align-items: center; - justify-content: center; - min-width: 58px; - height: 26px; - padding: 0 10px; - border-radius: 999px; - font-size: 12px; - font-weight: 700; - line-height: 1; -} - -.device-status-pill.online { - background: #dcfce7; - color: #166534; -} - -.device-status-pill.offline { - background: #e2e8f0; - color: #475569; -} - -.dialog-mask { - position: fixed; - inset: 0; - width: 100vw; - height: 100dvh; - background: rgba(15, 23, 42, 0.5); - display: grid; - place-items: center; - z-index: 1200; - padding: 20px; - opacity: 0; - animation: fade-in var(--dur-medium) var(--ease-smooth) both; - backdrop-filter: blur(6px); - -webkit-backdrop-filter: blur(6px); -} - -.dialog-card { - width: min(460px, 100%); - background: #fff; - border-radius: 20px; - border: 1px solid var(--line); - box-shadow: 0 20px 50px rgba(15, 23, 42, 0.2); - padding: 20px; - text-align: center; - transform-origin: 50% 30%; - animation: dialog-in 240ms var(--ease-out-strong) both; -} - -.dialog-mask.warning { - background: - radial-gradient(circle at top, rgba(255, 237, 213, 0.32), transparent 34%), - linear-gradient(180deg, rgba(127, 29, 29, 0.36), rgba(15, 23, 42, 0.72)); - backdrop-filter: blur(10px); - -webkit-backdrop-filter: blur(10px); -} - -.dialog-card.warning { - width: min(520px, 100%); - border: 1px solid rgba(220, 38, 38, 0.22); - background: - linear-gradient(180deg, rgba(255, 246, 246, 0.98), rgba(255, 255, 255, 0.99)); - box-shadow: - 0 36px 90px rgba(69, 10, 10, 0.28), - 0 0 0 1px rgba(255, 255, 255, 0.7) inset; -} - -.dialog-warning-head { - display: flex; - align-items: center; - justify-content: center; - gap: 12px; - margin-bottom: 8px; -} - -.dialog-warning-badge { - width: 48px; - height: 48px; - display: inline-flex; - align-items: center; - justify-content: center; - border-radius: 16px; - background: linear-gradient(180deg, #fff1f2, #ffe4e6); - color: #dc2626; - box-shadow: - 0 12px 30px rgba(220, 38, 38, 0.18), - 0 0 0 1px rgba(220, 38, 38, 0.08) inset; -} - -.dialog-warning-kicker { - font-size: 12px; - font-weight: 800; - letter-spacing: 0.16em; - text-transform: uppercase; - color: #b91c1c; -} - -.dialog-mask.closing { - animation: fade-out 220ms var(--ease-smooth) both; -} - -.dialog-card.closing { - animation: dialog-out 220ms var(--ease-smooth) both; -} - -.dialog-card .field { - text-align: left; -} - -.dialog-icon { - font-size: 34px; - color: #f59e0b; -} - -.dialog-title { - margin: 6px 0; - font-size: 30px; -} - -.dialog-message { - color: #475467; - margin-bottom: 10px; -} - -.dialog-card.warning .dialog-title { - color: #7f1d1d; - margin-bottom: 10px; -} - -.dialog-message.warning { - margin-bottom: 16px; - padding: 14px 16px; - border-radius: 16px; - border: 1px solid rgba(220, 38, 38, 0.16); - background: linear-gradient(180deg, rgba(255, 241, 242, 0.94), rgba(255, 247, 237, 0.9)); - color: #7a2832; - line-height: 1.65; - box-shadow: 0 10px 28px rgba(248, 113, 113, 0.08) inset; -} - -.dialog-btn { - width: 100%; - height: 50px; - font-size: 20px; - margin-top: 8px; -} - -.dialog-extra { - margin-top: 8px; -} - -.dialog-divider { - height: 1px; - background: var(--line); - margin: 8px 0 10px; -} - -.import-summary-dialog { - max-width: 520px; - text-align: left; - position: relative; - padding-top: 16px; -} - -.import-summary-close { - position: absolute; - top: 10px; - right: 10px; - border: none; - background: transparent; - color: #64748b; - font-size: 24px; - line-height: 1; - cursor: pointer; -} - -.import-summary-close:hover { - color: #0f172a; -} - -.import-summary-table-wrap { - margin-top: 8px; - border: 1px solid var(--line); - border-radius: 10px; - overflow: hidden; -} - -.import-summary-table { - width: 100%; - border-collapse: collapse; - font-size: 14px; -} - -.import-summary-table th, -.import-summary-table td { - padding: 10px 12px; - border-bottom: 1px solid var(--line); -} - -.import-summary-table th { - text-align: left; - color: #475467; - background: #f8fafc; -} - -.import-summary-table td:last-child, -.import-summary-table th:last-child { - text-align: right; - width: 96px; -} - -.import-summary-table tbody tr:last-child td { - border-bottom: none; -} - -.import-summary-failed-list { - margin-top: 10px; - padding: 10px 12px; - border: 1px solid #fecaca; - border-radius: 10px; - background: #fef2f2; - color: #991b1b; - font-size: 13px; -} - -.import-summary-failed-title { - font-weight: 700; - margin-bottom: 6px; -} - -.import-summary-failed-list ul { - margin: 0; - padding-left: 18px; -} - -.import-summary-failed-list li + li { - margin-top: 4px; -} - -.settings-twofactor-grid { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 12px; -} - -.settings-subcard { - border: 1px solid var(--line); - border-radius: 12px; - padding: 12px; - background: #fff; -} - -.settings-subcard h3 { - margin-top: 0; - margin-bottom: 10px; -} - -.toast-stack { - position: fixed; - top: 16px; - right: 16px; - z-index: 1400; - width: min(420px, calc(100vw - 20px)); - list-style: none; - margin: 0; - padding: 0; - display: grid; - gap: 10px; -} - -.toast-item { - position: relative; - border-radius: 10px; - border: 1px solid #bbdfc6; - background: #dff4e5; - color: #0f5132; - padding: 12px 14px; - box-shadow: 0 10px 24px rgba(15, 23, 42, 0.12); - overflow: hidden; - display: flex; - justify-content: space-between; - align-items: center; - animation: toast-in 240ms var(--ease-out-strong) both; -} - -.toast-item.error { - border-color: #f2b8c1; - background: #fde7eb; - color: #9f1239; -} - -.toast-item.warning { - border-color: #f2b8c1; - background: #fde7eb; - color: #9f1239; -} - -.toast-text { - font-weight: 700; - padding-right: 10px; -} - -.toast-close { - border: none; - background: transparent; - cursor: pointer; - font-size: 20px; - color: inherit; - transition: transform var(--dur-fast) var(--ease-out-soft), opacity var(--dur-fast) var(--ease-smooth); -} - -.toast-close:hover { - transform: scale(1.08); - opacity: 0.84; -} - -.toast-progress { - position: absolute; - left: 0; - bottom: 0; - width: 100%; - height: 3px; - background: rgba(15, 23, 42, 0.2); - animation: toast-life 4.5s linear forwards; -} - -@keyframes toast-life { - from { - transform: scaleX(1); - transform-origin: left center; - } - to { - transform: scaleX(0); - transform-origin: left center; - } -} - -@keyframes fade-in { - from { - opacity: 0; - } - to { - opacity: 1; - } -} - -@keyframes fade-in-up { - from { - opacity: 0; - transform: translate3d(0, 16px, 0); - } - to { - opacity: 1; - transform: translate3d(0, 0, 0); - } -} - -@keyframes shell-enter { - from { - opacity: 0; - transform: translate3d(0, 18px, 0) scale(0.992); - } - to { - opacity: 1; - transform: translate3d(0, 0, 0) scale(1); - } -} - -@keyframes surface-enter { - from { - opacity: 0; - transform: translate3d(0, 20px, 0) scale(0.985); - } - to { - opacity: 1; - transform: translate3d(0, 0, 0) scale(1); - } -} - -@keyframes menu-in { - from { - opacity: 0; - transform: translate3d(0, 10px, 0) scale(0.96); - } - to { - opacity: 1; - transform: translate3d(0, 0, 0) scale(1); - } -} - -@keyframes dialog-in { - from { - opacity: 0; - transform: translate3d(0, 18px, 0) scale(0.96); - } - to { - opacity: 1; - transform: translate3d(0, 0, 0) scale(1); - } -} - -@keyframes toast-in { - from { - opacity: 0; - transform: translate3d(18px, 0, 0) scale(0.97); - } - to { - opacity: 1; - transform: translate3d(0, 0, 0) scale(1); - } -} - -@keyframes stagger-rise { - from { - opacity: 0; - transform: translate3d(0, 18px, 0) scale(0.985); - } - to { - opacity: 1; - transform: translate3d(0, 0, 0) scale(1); - } -} - -@keyframes fade-out { - from { - opacity: 1; - } - to { - opacity: 0; - } -} - -@keyframes dialog-out { - from { - opacity: 1; - transform: translate3d(0, 0, 0) scale(1); - } - to { - opacity: 0; - transform: translate3d(0, 10px, 0) scale(0.972); - } -} - -@keyframes route-stage-in { - from { - opacity: 0; - transform: translate3d(0, 14px, 0); - } - to { - opacity: 1; - transform: translate3d(0, 0, 0); - } -} - -@media (max-width: 1180px) { - .app-page { - padding: 8px; - } - - .app-shell { - height: calc(100vh - 16px); - border-radius: 12px; - } - - .app-main { - grid-template-columns: 1fr; - } - - .app-side { - border-right: none; - border-bottom: 1px solid #d9e0ea; - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - align-items: start; - align-self: start; - height: fit-content; - gap: 8px; - } - - .app-side > .side-link { - min-height: 0; - } - - .side-spacer { - display: none; - } - - .side-add-btn, - .side-lock-btn { - grid-column: span 1; - } - - .vault-grid { - grid-template-columns: 1fr; - height: auto; - } - .sidebar { - max-height: 280px; - } - .totp-grid, - .field-grid { - grid-template-columns: 1fr; - } - - .totp-copy-btn { - justify-self: start; - } - - .import-export-feature-grid, - .import-export-panels, - .backup-combined-grid, - .backup-status-grid, - .backup-browser-row { - grid-template-columns: 1fr; - } - - .uri-row { - grid-template-columns: 1fr; - } - - .settings-twofactor-grid { - grid-template-columns: 1fr; - } - - .standalone-title { - font-size: 24px; - } - - .standalone-brand-title { - font-size: 32px; - } - - .standalone-footer { - font-size: 12px; - line-height: 1.4; - } -} - -@media (max-width: 900px) { - .auth-page { - padding: 14px; - align-items: start; - } - - .standalone-shell { - width: 100%; - max-width: 460px; - gap: 10px; - padding-top: 12px; - } - - .standalone-brand-outside { - justify-content: flex-start; - } - - .standalone-brand-logo { - width: 44px; - height: 44px; - } - - .standalone-brand-title { - font-size: 28px; - } - - .standalone-title { - font-size: 24px; - } - - .auth-card { - padding: 20px 16px; - border-radius: 18px; - } - - .btn.full { - height: 48px; - font-size: 18px; - } - - .auth-support-row { - align-items: center; - flex-direction: row; - } - - .app-page { - padding: 0; - background: transparent; - } - - .app-shell { - --mobile-topbar-height: 58px; - --mobile-tabbar-height: 70px; - height: 100dvh; - max-width: none; - border: none; - border-radius: 0; - box-shadow: none; - } - - .topbar { - height: var(--mobile-topbar-height); - padding: 0 12px; - position: relative; - z-index: 20; - } - - .brand { - min-width: 0; - gap: 10px; - font-size: 18px; - } - - .brand-logo { - width: 34px; - height: 34px; - } - - .brand-name { - display: none; - } - - .mobile-page-title { - display: inline; - } - - .topbar-actions .user-chip, - .topbar-actions > .btn:not(.mobile-sidebar-toggle):not(.mobile-lock-btn), - .topbar-actions > .theme-switch-wrap { - display: none; - } - - .mobile-sidebar-toggle, - .mobile-lock-btn { - display: inline-flex; - width: 36px; - min-width: 36px; - height: 36px; - padding: 0; - justify-content: center; - font-size: 0; - gap: 0; - } - - .mobile-sidebar-toggle .btn-icon, - .mobile-lock-btn .btn-icon { - margin: 0; - } - - .mobile-theme-btn { - display: inline-flex; - align-items: center; - } - - .mobile-theme-btn .theme-switch { - transform: scale(0.8); - transform-origin: center; - } - - .app-main { - display: flex; - flex-direction: column; - min-height: 0; - } - - .app-side { - display: none; - } - - .content { - flex: 1; - min-height: 0; - -webkit-overflow-scrolling: touch; - } - - .mobile-tabbar { - display: grid; - grid-template-columns: repeat(4, minmax(0, 1fr)); - align-items: center; - gap: 6px; - min-height: var(--mobile-tabbar-height); - padding: 8px 10px calc(8px + env(safe-area-inset-bottom)); - border-top: 1px solid var(--line); - background: rgba(248, 251, 255, 0.92); - } - - .mobile-tab { - display: grid; - justify-items: center; - gap: 4px; - color: #64748b; - text-decoration: none; - font-size: 11px; - font-weight: 700; - padding: 6px 4px; - border-radius: 12px; - transition: - transform 220ms var(--ease-out-soft), - background-color var(--dur-fast) var(--ease-smooth), - color var(--dur-fast) var(--ease-smooth); - } - - .mobile-tab:hover { - transform: translate3d(var(--mag-x), calc(var(--mag-y) - 1px), 0); - } - - .mobile-tab.active { - color: var(--primary-strong); - background: linear-gradient(135deg, rgba(37, 99, 235, 0.16), rgba(59, 130, 246, 0.08)); - box-shadow: 0 8px 18px rgba(37, 99, 235, 0.08); - } - - .vault-grid { - gap: 10px; - padding: 0; - } - - .sidebar { - display: none; - } - - .mobile-sidebar-sheet { - display: block; - position: fixed; - left: 10px; - right: 10px; - top: calc(var(--mobile-topbar-height) + 10px); - bottom: auto; - max-height: calc(100dvh - 145px); - z-index: 55; - overflow: auto; - border: 1px solid #d8dee8; - border-radius: 18px; - background: #fff; - padding: 12px; - box-shadow: 0 18px 40px rgba(15, 23, 42, 0.16); - opacity: 0; - visibility: hidden; - pointer-events: none; - transform: translate3d(0, 10px, 0) scale(0.98); - transition: - opacity 220ms var(--ease-smooth), - transform 240ms var(--ease-out-soft), - visibility 220ms var(--ease-smooth); - } - - .mobile-sidebar-sheet.open { - opacity: 1; - visibility: visible; - pointer-events: auto; - transform: translate3d(0, 0, 0) scale(1); - } - - .mobile-sidebar-head { - display: flex; - align-items: center; - justify-content: space-between; - gap: 10px; - margin-bottom: 10px; - } - - .mobile-sidebar-title { - font-size: 16px; - font-weight: 800; - color: #0f172a; - } - - .mobile-sidebar-close { - width: 34px; - height: 34px; - border: 1px solid #d7dde6; - border-radius: 999px; - background: #fff; - color: #0f172a; - display: inline-grid; - place-items: center; - cursor: pointer; - padding: 0; - transition: - transform var(--dur-fast) var(--ease-out-soft), - background-color var(--dur-fast) var(--ease-smooth), - border-color var(--dur-fast) var(--ease-smooth); - } - - .mobile-sidebar-close:hover { - transform: scale(1.05); - } - - .mobile-sidebar-sheet .sidebar-block { - margin: 0; - padding: 0; - border: none; - border-radius: 0; - background: transparent; - box-shadow: none; - } - - .mobile-sidebar-sheet .tree-btn { - margin-bottom: 2px; - } - - .mobile-sidebar-sheet .folder-row { - align-items: stretch; - gap: 4px; - } - - .mobile-sidebar-sheet .folder-row .tree-btn { - min-height: 42px; - } - - .mobile-sidebar-sheet .sidebar-title, - .mobile-sidebar-sheet .sidebar-title-row { - padding-bottom: 6px; - margin-bottom: 0; - } - - .mobile-sidebar-sheet .tree-btn { - padding-left: 8px; - padding-right: 8px; - border-radius: 10px; - } - - .mobile-sidebar-sheet .tree-btn.active { - background: #eef4ff; - } - - .mobile-sidebar-sheet .folder-delete-btn { - width: 28px; - height: 42px; - border-radius: 8px; - } - - .list-col { - max-width: none; - } - - .list-head { - display: grid; - grid-template-columns: minmax(0, 1fr) auto auto auto; - gap: 8px; - align-items: center; - } - - .list-count { - grid-column: auto; - width: auto; - font-size: 12px; - white-space: nowrap; - } - - .list-head .search-input-wrap { - width: 100%; - min-width: 0; - } - - .list-head .search-input { - width: 100%; - min-width: 0; - height: 42px; - border-radius: 14px; - } - - .list-icon-btn { - width: auto; - min-width: 0; - padding: 0 12px; - font-size: 13px; - gap: 6px; - white-space: nowrap; - } - - .toolbar.actions { - justify-content: flex-end; - flex-wrap: unset; - gap: var(--actions-gap); - overflow: visible; - padding-bottom: 2px; - } - - .actions { - gap: var(--actions-gap); - } - - .toolbar.actions .btn.small { - width: auto; - min-width: 0; - height: 34px; - padding: 0 12px; - font-size: 13px; - gap: 6px; - border-radius: 999px; - white-space: nowrap; - } - - .mobile-fab-wrap { - position: fixed; - right: 14px; - bottom: calc(14px + var(--mobile-tabbar-height) + env(safe-area-inset-bottom)); - z-index: 45; - } - - .mobile-fab-trigger { - width: 36px; - height: 56px; - padding: 0; - border-radius: 999px; - font-size: 0; - gap: 0; - box-shadow: 0 14px 30px rgba(37, 99, 235, 0.28); - } - - .mobile-fab-trigger .btn-icon { - margin: 0; - width: 20px; - height: 20px; - } - - .mobile-fab-wrap .create-menu { - left: auto; - right: 0; - top: auto; - bottom: calc(100% + 10px); - } - - .list-panel { - border-radius: 16px; - overflow: visible; - } - - .list-item { - padding: 12px; - border-radius: 14px; - } - - .row-check { - width: 18px; - height: 18px; - } - - .vault-grid.mobile-panel-detail .sidebar, - .vault-grid.mobile-panel-detail .list-col, - .vault-grid.mobile-panel-edit .sidebar, - .vault-grid.mobile-panel-edit .list-col { - display: none; - } - - .mobile-detail-sheet { - display: block; - position: fixed; - left: 0; - right: 0; - top: calc(var(--mobile-topbar-height) + env(safe-area-inset-top)); - bottom: calc(var(--mobile-tabbar-height) + env(safe-area-inset-bottom)); - z-index: 35; - overflow: auto; - background: transparent; - padding: 0 0 18px; - opacity: 0; - visibility: hidden; - pointer-events: none; - transform: translate3d(0, 18px, 0); - transition: - opacity 220ms var(--ease-smooth), - transform 260ms var(--ease-out-soft), - visibility 220ms var(--ease-smooth); - } - - .mobile-detail-sheet.open { - opacity: 1; - visibility: visible; - pointer-events: auto; - transform: translate3d(0, 0, 0); - } - - .mobile-panel-head { - display: flex; - align-items: center; - margin: 0 10px 10px; - } - - .mobile-panel-back { - min-height: 40px; - } - - .mobile-detail-sheet > .detail-switch-stage, - .mobile-detail-sheet > .card, - .mobile-detail-sheet > .empty { - margin-left: 10px; - margin-right: 10px; - } - - .detail-col .card, - .import-export-panel, - .backup-panel, - .settings-subcard { - border-radius: 16px; - } - - .card { - padding: 14px 14px; - box-shadow: 0px 0px 22px rgba(13, 31, 68, .06) !important; - } - - .section-head { - align-items: flex-start; - gap: 10px; - flex-direction: column; - } - - .detail-actions { - flex-direction: column; - align-items: stretch; - gap: 10px; - } - - .detail-actions .actions { - width: 100%; - } - - .detail-actions .actions .btn, - .detail-delete-btn { - width: 100%; - } - - .kv-row { - grid-template-columns: minmax(64px, 80px) minmax(0, 1fr) auto; - align-items: center; - } - - .kv-line { - flex-direction: row; - align-items: center; - justify-content: space-between; - } - - .kv-actions { - width: auto; - justify-content: flex-end; - flex-wrap: nowrap; - } - - .kv-actions .btn.small { - width: 34px; - min-width: 34px; - height: 34px; - padding: 0; - font-size: 0; - gap: 0; - border-radius: 999px; - } - - .kv-actions .btn.small .btn-icon { - margin: 0; - } - - .import-export-panels, - .backup-combined-grid, - .backup-status-grid, - .settings-twofactor-grid { - gap: 10px; - } - - .import-export-panel .actions .btn, - .backup-panel .actions .btn, - .settings-subcard .actions .btn, - .section-head .actions .btn { - width: 100%; - } - - .totp-grid { - gap: 10px; - } - - .totp-qr { - min-height: 180px; - } - - .totp-qr svg, - .totp-qr img { - width: 160px; - height: 160px; - } - - .invite-toolbar { - align-items: stretch; - } - - .mobile-settings-card { - min-height: calc(100dvh - 170px); - display: flex; - flex-direction: column; - gap: 12px; - } - - .mobile-settings-subhead { - display: flex; - align-items: center; - } - - .mobile-settings-back { - min-height: 38px; - } - - .mobile-settings-links { - display: grid; - gap: 8px; - align-content: start; - } - - .mobile-settings-link { - display: flex; - align-items: center; - gap: 10px; - min-height: 46px; - padding: 0 12px; - border: 1px solid #dbe2ec; - border-radius: 14px; - background: #f8fafc; - color: #0f172a; - text-decoration: none; - font-weight: 700; - } - - .mobile-settings-link.active { - background: #e8f0ff; - border-color: #b9cff6; - color: #175ddc; - } - - .mobile-settings-logout { - width: 100%; - margin-top: auto; - } - - .stack, - .import-export-page, - .totp-codes-page, - .detail-col { - min-height: auto; - } - - .invite-create-group { - align-items: stretch; - width: 100%; - } - - .input.small { - width: 100%; - } - - .table, - .table tbody, - .table tr, - .table td { - display: block; - width: 100%; - } - - .table thead { - display: none; - } - - .table tr { - border: 1px solid var(--line); - border-radius: 14px; - background: #fff; - padding: 10px 12px; - margin-bottom: 10px; - } - - .table td { - border-bottom: 1px solid #edf1f6; - padding: 10px 0; - } - - .table td:last-child { - border-bottom: none; - padding-bottom: 0; - } - - .table td::before { - display: block; - content: attr(data-label); - margin-bottom: 4px; - color: #64748b; - font-size: 12px; - font-weight: 700; - } - - .dialog-mask { - align-items: center; - justify-items: center; - padding: 16px; - } - - .dialog-card { - width: 90%; - max-width: 460px; - max-height: calc(100dvh - 10px); - overflow: auto; - border-radius: 22px; - padding: 18px 16px calc(18px + env(safe-area-inset-bottom)); - } - - .dialog-card.warning { - max-width: 520px; - } - - .dialog-warning-strip { - margin: -18px -16px 16px; - } - - .dialog-title { - font-size: 24px; - } - - .dialog-btn { - height: 46px; - font-size: 16px; - } - -.toast-stack { - top: 10px; - left: 10px; - right: 10px; - width: auto; -} -} - -@media (max-width: 900px) { - .backup-grid { - grid-template-columns: 1fr; - } - - .backup-operations-sidebar, - .backup-destination-sidebar { - position: static; - } -} - -@media (max-width: 640px) { - .backup-interval-row { - grid-template-columns: 1fr; - } - - .backup-status-grid, - .backup-browser-row, - .field-grid { - grid-template-columns: 1fr; - } - - .backup-destination-top { - align-items: flex-start; - flex-direction: column; - } - - .backup-add-chooser { - flex-direction: column; - } - - .backup-name-row { - grid-template-columns: 1fr; - } - - .backup-option-field { - align-items: flex-start; - } - - .backup-help-bubble { - left: 0; - transform: translate(0, -4px); - } - - .backup-help-bubble::before { - left: 16px; - transform: rotate(45deg); - } - - .backup-help-wrap:hover .backup-help-bubble, - .backup-help-wrap:focus-within .backup-help-bubble, - .backup-help-wrap.open .backup-help-bubble { - transform: translate(0, 0); - } -} - -:root[data-theme='dark'] body, -:root[data-theme='dark'] #root, -:root[data-theme='dark'] .app-page, -:root[data-theme='dark'] .auth-page { - background: transparent; - color: var(--text); -} - -:root[data-theme='dark'] .app-shell, -:root[data-theme='dark'] .auth-card, -:root[data-theme='dark'] .dialog, -:root[data-theme='dark'] .standalone-frame, -:root[data-theme='dark'] .jwt-warning-box, -:root[data-theme='dark'] .backup-operations-sidebar, -:root[data-theme='dark'] .backup-destination-sidebar, -:root[data-theme='dark'] .backup-detail-panel, -:root[data-theme='dark'] .settings-subcard, -:root[data-theme='dark'] .backup-recommendation, -:root[data-theme='dark'] .backup-browser-card, -:root[data-theme='dark'] .backup-settings-card, -:root[data-theme='dark'] .backup-destination-card, -:root[data-theme='dark'] .backup-operation-card, -:root[data-theme='dark'] .list-panel, -:root[data-theme='dark'] .card, -:root[data-theme='dark'] .sidebar-block, -:root[data-theme='dark'] .send-detail-card, -:root[data-theme='dark'] .admin-card, -:root[data-theme='dark'] .empty { - background: var(--panel); - border-color: var(--line); - color: var(--text); - box-shadow: var(--shadow-lg); -} - -:root[data-theme='dark'] .topbar, -:root[data-theme='dark'] .mobile-tabbar, -:root[data-theme='dark'] .sort-menu, -:root[data-theme='dark'] .create-menu, -:root[data-theme='dark'] .dialog-card, -:root[data-theme='dark'] .mobile-sidebar-sheet, -:root[data-theme='dark'] .mobile-detail-sheet, -:root[data-theme='dark'] .mobile-editor-sheet { - background: var(--panel-soft); - border-color: var(--line); - color: var(--text); -} - -:root[data-theme='dark'] .dialog-card.warning { - border-color: rgba(248, 113, 113, 0.36); - background: linear-gradient(180deg, rgba(39, 16, 16, 0.98), rgba(27, 12, 12, 0.98)); - box-shadow: - 0 36px 90px rgba(5, 5, 5, 0.56), - 0 0 0 1px rgba(248, 113, 113, 0.12) inset; -} - -:root[data-theme='dark'] .dialog-mask.warning { - background: - radial-gradient(circle at top, rgba(127, 29, 29, 0.28), transparent 34%), - linear-gradient(180deg, rgba(20, 12, 12, 0.64), rgba(2, 6, 23, 0.82)); -} - -:root[data-theme='dark'] .dialog-warning-badge { - background: linear-gradient(180deg, rgba(127, 29, 29, 0.8), rgba(69, 10, 10, 0.86)); - color: #fda4af; - box-shadow: - 0 12px 30px rgba(0, 0, 0, 0.32), - 0 0 0 1px rgba(248, 113, 113, 0.14) inset; -} - -:root[data-theme='dark'] .dialog-warning-kicker, -:root[data-theme='dark'] .dialog-card.warning .dialog-title { - color: #fecaca; -} - -:root[data-theme='dark'] .dialog-message.warning { - border-color: rgba(248, 113, 113, 0.18); - background: linear-gradient(180deg, rgba(69, 10, 10, 0.54), rgba(67, 20, 7, 0.46)); - color: #fecdd3; - box-shadow: 0 10px 28px rgba(0, 0, 0, 0.18) inset; -} - -:root[data-theme='dark'] .app-side, -:root[data-theme='dark'] .sidebar, -:root[data-theme='dark'] .mobile-sidebar-sheet .sidebar-block { - background: var(--panel-muted); - border-color: var(--line); -} - -:root[data-theme='dark'] .app-shell::before { - content: none; -} - -:root[data-theme='dark'] .auth-card { - background: var(--panel); -} - -:root[data-theme='dark'] .auth-card::before { - content: none; -} - -:root[data-theme='dark'] .brand, -:root[data-theme='dark'] .brand-name, -:root[data-theme='dark'] .mobile-page-title, -:root[data-theme='dark'] .detail-title, -:root[data-theme='dark'] .dialog-title, -:root[data-theme='dark'] .standalone-title, -:root[data-theme='dark'] .standalone-brand-title, -:root[data-theme='dark'] .kv-main strong, -:root[data-theme='dark'] .list-title, -:root[data-theme='dark'] .sidebar-title, -:root[data-theme='dark'] .backup-title, -:root[data-theme='dark'] .backup-section-title, -:root[data-theme='dark'] h1, -:root[data-theme='dark'] h2, -:root[data-theme='dark'] h3, -:root[data-theme='dark'] h4 { - color: var(--text); -} - -:root[data-theme='dark'] .standalone-brand-title, -:root[data-theme='dark'] .brand-name, -:root[data-theme='dark'] .standalone-brand-wordmark, -:root[data-theme='dark'] .brand-wordmark { - text-shadow: 0 16px 28px rgba(2, 6, 23, 0.32); -} - -:root[data-theme='dark'] .muted, -:root[data-theme='dark'] .detail-sub, -:root[data-theme='dark'] .field-help, -:root[data-theme='dark'] .list-sub, -:root[data-theme='dark'] .kv-label, -:root[data-theme='dark'] .standalone-muted, -:root[data-theme='dark'] .standalone-footer, -:root[data-theme='dark'] .backup-inline-note, -:root[data-theme='dark'] .backup-meta, -:root[data-theme='dark'] .backup-browser-empty, -:root[data-theme='dark'] .dialog-copy, -:root[data-theme='dark'] .or, -:root[data-theme='dark'] .txt-muted, -:root[data-theme='dark'] .mobile-tab, -:root[data-theme='dark'] .side-link, -:root[data-theme='dark'] .user-chip, -:root[data-theme='dark'] .folder-meta, -:root[data-theme='dark'] .list-count { - color: var(--muted); -} - -:root[data-theme='dark'] .user-chip { - background: rgba(17, 34, 56, 0.94); - border-color: var(--line); - box-shadow: 0 12px 24px rgba(1, 7, 18, 0.24); -} - -:root[data-theme='dark'] .side-link:hover, -:root[data-theme='dark'] .mobile-tab:hover { - background: rgba(132, 182, 255, 0.11); -} - -:root[data-theme='dark'] .side-link.active, -:root[data-theme='dark'] .mobile-tab.active, -:root[data-theme='dark'] .sort-menu-item.active, -:root[data-theme='dark'] .filter-link.active, -:root[data-theme='dark'] .folder-link.active, -:root[data-theme='dark'] .list-item.active, -:root[data-theme='dark'] .backup-mode-pill.active, -:root[data-theme='dark'] .segmented-item.active { - background: linear-gradient(135deg, rgba(132, 182, 255, 0.2), rgba(56, 189, 248, 0.08)); - border-color: rgba(132, 182, 255, 0.28); - color: var(--primary); -} - -:root[data-theme='dark'] .input, -:root[data-theme='dark'] .textarea, -:root[data-theme='dark'] select.input, -:root[data-theme='dark'] .dialog input, -:root[data-theme='dark'] .dialog textarea, -:root[data-theme='dark'] .dialog select { - background: rgba(13, 24, 40, 0.94); - border-color: rgba(103, 136, 186, 0.36); - color: var(--text); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03); -} - -:root[data-theme='dark'] .input::placeholder, -:root[data-theme='dark'] .textarea::placeholder, -:root[data-theme='dark'] input::placeholder, -:root[data-theme='dark'] textarea::placeholder { - color: #7488a8; -} - -:root[data-theme='dark'] .input:focus, -:root[data-theme='dark'] .textarea:focus, -:root[data-theme='dark'] .search-input:focus, -:root[data-theme='dark'] .dialog input:focus, -:root[data-theme='dark'] .dialog textarea:focus, -:root[data-theme='dark'] .dialog select:focus { - border-color: rgba(132, 182, 255, 0.54); - background-color: rgba(16, 30, 49, 0.98); - box-shadow: 0 0 0 4px rgba(132, 182, 255, 0.12), 0 10px 22px rgba(5, 13, 28, 0.24), inset 0 1px 0 rgba(255, 255, 255, 0.04); -} - -:root[data-theme='dark'] select.input { - background-image: - linear-gradient(45deg, transparent 50%, #8fb8ff 50%), - linear-gradient(135deg, #8fb8ff 50%, transparent 50%); -} - -:root[data-theme='dark'] .input-readonly { - background: #0f1b2d; - color: var(--muted-strong); -} - -:root[data-theme='dark'] .input:disabled, -:root[data-theme='dark'] .btn:disabled { - background: #132033; - border-color: #22334c; - color: #70829d; -} - -:root[data-theme='dark'] .btn-secondary { - background: linear-gradient(180deg, rgba(22, 41, 66, 0.98), rgba(16, 31, 52, 0.98)); - border-color: rgba(132, 182, 255, 0.22); - color: #a9cdff; - box-shadow: 0 12px 22px rgba(1, 7, 18, 0.18); -} - -:root[data-theme='dark'] .btn-secondary:hover { - background: linear-gradient(180deg, rgba(26, 49, 79, 0.98), rgba(19, 37, 61, 0.98)); - border-color: rgba(132, 182, 255, 0.3); -} - -:root[data-theme='dark'] .btn-danger { - background: linear-gradient(180deg, rgba(45, 23, 33, 0.98), rgba(35, 18, 28, 0.98)); - border-color: rgba(255, 139, 168, 0.38); - color: #ff9bb0; -} - -:root[data-theme='dark'] .btn-danger:hover { - background: linear-gradient(180deg, rgba(56, 27, 40, 0.98), rgba(41, 19, 31, 0.98)); - border-color: rgba(255, 171, 192, 0.42); -} - -:root[data-theme='dark'] .btn-primary { - background: linear-gradient(135deg, #79acff, #57c2ff 76%); - border-color: rgba(176, 214, 255, 0.22); - color: #061120; - box-shadow: 0 18px 32px rgba(10, 26, 52, 0.34); -} - -:root[data-theme='dark'] .btn-primary:hover { - background: linear-gradient(135deg, #90bcff, #6accff 76%); - box-shadow: 0 22px 36px rgba(10, 26, 52, 0.38); -} - -:root[data-theme='dark'] .toolbar.actions, -:root[data-theme='dark'] .list-head, -:root[data-theme='dark'] .mobile-panel-head, -:root[data-theme='dark'] .backup-recommendation-header, -:root[data-theme='dark'] .backup-operations-sidebar, -:root[data-theme='dark'] .backup-destination-sidebar, -:root[data-theme='dark'] .backup-detail-panel, -:root[data-theme='dark'] .dialog-actions, -:root[data-theme='dark'] .detail-actions, -:root[data-theme='dark'] .topbar, -:root[data-theme='dark'] .app-side, -:root[data-theme='dark'] .kv-row, -:root[data-theme='dark'] .attachment-row, -:root[data-theme='dark'] .backup-browser-row, -:root[data-theme='dark'] .admin-row, -:root[data-theme='dark'] .send-row { - border-color: var(--line); -} - -:root[data-theme='dark'] .input, -:root[data-theme='dark'] .search-input, -:root[data-theme='dark'] .list-item, -:root[data-theme='dark'] .sidebar-block { - background: rgba(15, 28, 45, 0.94); -} - -:root[data-theme='dark'] .sidebar, -:root[data-theme='dark'] .content, -:root[data-theme='dark'] .list-col, -:root[data-theme='dark'] .detail-col { - color: var(--text); -} - -:root[data-theme='dark'] .mobile-sidebar-mask, -:root[data-theme='dark'] .dialog-mask, -:root[data-theme='dark'] .modal-mask { - background: var(--overlay-strong); -} - -:root[data-theme='dark'] .toast { - background: linear-gradient(180deg, rgba(19, 34, 54, 0.98), rgba(14, 26, 42, 0.98)); - border-color: #263a57; - color: var(--text); -} - -:root[data-theme='dark'] .toast.success { - background: #0f2a1f; - border-color: #1f5b44; - color: #9be2bd; -} - -:root[data-theme='dark'] .toast.error { - background: #2a1720; - border-color: #6c2b41; - color: #ffb1c0; -} - -:root[data-theme='dark'] .toast.warning { - background: #2d2413; - border-color: #7b6230; - color: #f7d48b; -} - -:root[data-theme='dark'] .jwt-warning-head, -:root[data-theme='dark'] .jwt-warning-label, -:root[data-theme='dark'] .jwt-warning-copy, -:root[data-theme='dark'] .jwt-warning-list { - color: #f4d48a; -} - -:root[data-theme='dark'] .theme-switch-input:focus + .theme-switch-slider { - box-shadow: 0 0 0 2px rgba(132, 182, 255, 0.24); -} - -:root[data-theme='dark'] .search-input, -:root[data-theme='dark'] .list-head .search-input, -:root[data-theme='dark'] .mobile-settings-card, -:root[data-theme='dark'] .mobile-settings-link, -:root[data-theme='dark'] .table tr, -:root[data-theme='dark'] .settings-subcard, -:root[data-theme='dark'] .backup-operations-sidebar, -:root[data-theme='dark'] .backup-destination-sidebar, -:root[data-theme='dark'] .backup-detail-panel, -:root[data-theme='dark'] .dialog-card, -:root[data-theme='dark'] .backup-browser-path, -:root[data-theme='dark'] .backup-browser-list, -:root[data-theme='dark'] .backup-schedule-current, -:root[data-theme='dark'] .backup-status-card, -:root[data-theme='dark'] .create-menu, -:root[data-theme='dark'] .create-menu-item, -:root[data-theme='dark'] .sort-menu, -:root[data-theme='dark'] .sort-menu-item, -:root[data-theme='dark'] .import-export-feature-item, -:root[data-theme='dark'] .import-export-feature-icon, -:root[data-theme='dark'] .backup-recommendation-card, -:root[data-theme='dark'] .backup-recommendation-dav-item, -:root[data-theme='dark'] .backup-destination-item, -:root[data-theme='dark'] .totp-code-row, -:root[data-theme='dark'] .list-item { - background: - linear-gradient(180deg, rgba(18, 32, 52, 0.92), rgba(14, 26, 42, 0.92)); - border-color: var(--line); - color: var(--text); -} - -:root[data-theme='dark'] .list-item:hover, -:root[data-theme='dark'] .sort-menu-item:hover, -:root[data-theme='dark'] .create-menu-item:hover, -:root[data-theme='dark'] .mobile-settings-link:hover, -:root[data-theme='dark'] .backup-destination-item:hover, -:root[data-theme='dark'] .import-export-feature-item:hover { - background: - linear-gradient(180deg, rgba(24, 44, 70, 0.96), rgba(16, 31, 51, 0.96)); - border-color: rgba(118, 150, 197, 0.32); -} - -:root[data-theme='dark'] .list-item.active { - background: linear-gradient(135deg, rgba(132, 182, 255, 0.2), rgba(56, 189, 248, 0.1)); - border-color: rgba(122, 176, 255, 0.34); - box-shadow: inset 0 0 0 1px rgba(200, 225, 255, 0.06), 0 12px 24px rgba(5, 13, 28, 0.18); -} - -:root[data-theme='dark'] .list-item::before { - background: - linear-gradient(90deg, rgba(132, 182, 255, 0.08), transparent 24%, transparent 76%, rgba(56, 189, 248, 0.08)), - radial-gradient(circle at 18px 50%, rgba(255, 255, 255, 0.06), transparent 44%); -} - -:root[data-theme='dark'] .backup-destination-item.active, -:root[data-theme='dark'] .backup-interval-preset.active, -:root[data-theme='dark'] .mobile-settings-link.active, -:root[data-theme='dark'] .tree-btn.active { - background: linear-gradient(135deg, rgba(132, 182, 255, 0.2), rgba(56, 189, 248, 0.1)); - border-color: rgba(132, 182, 255, 0.34); - color: #f4f8ff; -} - -:root[data-theme='dark'] .theme-switch-slider { - background: linear-gradient(180deg, #1d3659, #142845); - border-color: rgba(120, 152, 198, 0.34); -} - -:root[data-theme='dark'] .theme-switch-slider::before { - background: linear-gradient(180deg, #f8fbff, #dce9ff); - box-shadow: 0 3px 10px rgba(2, 8, 20, 0.28); -} - -:root[data-theme='dark'] .theme-switch .moon svg { - fill: #8db6ff; -} - -:root[data-theme='dark'] .theme-switch .sun svg { - opacity: 0.82; -} - -:root[data-theme='dark'] .totp-code-name, -:root[data-theme='dark'] .backup-destination-name, -:root[data-theme='dark'] .backup-browser-entry, -:root[data-theme='dark'] .mobile-settings-link, -:root[data-theme='dark'] .backup-browser-path strong, -:root[data-theme='dark'] .backup-status-grid strong, -:root[data-theme='dark'] .backup-option-label, -:root[data-theme='dark'] .sort-menu-item, -:root[data-theme='dark'] .create-menu-item, -:root[data-theme='dark'] .tree-btn, -:root[data-theme='dark'] .folder-add-btn, -:root[data-theme='dark'] .list-icon-fallback, -:root[data-theme='dark'] .totp-code-main strong, -:root[data-theme='dark'] .totp-timer-value { - color: var(--text); -} - -:root[data-theme='dark'] .totp-code-username, -:root[data-theme='dark'] .backup-destination-meta, -:root[data-theme='dark'] .backup-browser-meta, -:root[data-theme='dark'] .table td::before, -:root[data-theme='dark'] .backup-file-meta, -:root[data-theme='dark'] .backup-list, -:root[data-theme='dark'] .backup-recommendation-step, -:root[data-theme='dark'] .backup-recommendation-inline-note, -:root[data-theme='dark'] .backup-recommendation-linked-item, -:root[data-theme='dark'] .backup-recommendation-referral, -:root[data-theme='dark'] .backup-retention-suffix, -:root[data-theme='dark'] .backup-inline-suffix, -:root[data-theme='dark'] .folder-delete-btn, -:root[data-theme='dark'] .folder-add-btn:hover, -:root[data-theme='dark'] .tree-label, -:root[data-theme='dark'] .list-sub { - color: var(--muted); -} - -:root[data-theme='dark'] .import-export-feature-item p, -:root[data-theme='dark'] .import-export-hero-sub, -:root[data-theme='dark'] .import-export-panel p, -:root[data-theme='dark'] .dialog-message, -:root[data-theme='dark'] .local-error, -:root[data-theme='dark'] .status-ok { - color: var(--muted); -} - -:root[data-theme='dark'] .backup-destination-type { - background: #1d3048; - color: #c9d8eb; -} - -:root[data-theme='dark'] .backup-help-trigger { - border-color: #38618f; - background: #173150; - color: #9ec5ff; -} - -:root[data-theme='dark'] .backup-help-trigger:hover, -:root[data-theme='dark'] .backup-help-trigger:focus-visible { - border-color: #5f92d7; - background: #20426a; -} - -:root[data-theme='dark'] .backup-help-bubble { - background: var(--panel); - border-color: var(--line); - color: var(--text); -} - -:root[data-theme='dark'] .backup-help-bubble::before { - background: var(--panel); - border-left-color: var(--line); - border-top-color: var(--line); -} - -:root[data-theme='dark'] .table td { - border-bottom-color: #203047; -} - -:root[data-theme='dark'] .dialog-icon { - color: #f7d48b; -} - -:root[data-theme='dark'] .local-error { - color: #ff9bb0; -} - -:root[data-theme='dark'] .status-ok { - color: #9be2bd; -} - -:root[data-theme='dark'] .totp-qr { - background: #ffffff; - border-color: rgba(15, 23, 42, 0.12); -} - -:root[data-theme='dark'] .totp-qr svg, -:root[data-theme='dark'] .totp-qr img { - background: #ffffff; - border-radius: 8px; -} - -@media (prefers-reduced-motion: reduce) { - html { - scroll-behavior: auto; - } - - *, - *::before, - *::after { - animation-duration: 1ms !important; - animation-iteration-count: 1 !important; - transition-duration: 1ms !important; - scroll-behavior: auto !important; - } - - .btn:hover:not(:disabled), - .btn:active:not(:disabled), - .side-link:hover, - .tree-btn:hover, - .list-item:hover, - .list-item.active, - .search-input:focus, - .input:focus, - .password-toggle:hover, - .eye-btn:hover, - .auth-link-btn:hover, - .sort-menu-item:hover, - .create-menu-item:hover, - .toast-close:hover, - .mobile-sidebar-close:hover { - transform: none !important; - } -} +@import './styles/tokens.css'; +@import './styles/base.css'; +@import './styles/auth.css'; +@import './styles/forms.css'; +@import './styles/shell.css'; +@import './styles/vault.css'; +@import './styles/management.css'; +@import './styles/overlays.css'; +@import './styles/motion.css'; +@import './styles/responsive.css'; +@import './styles/dark.css'; +@import './styles/reduced-motion.css'; diff --git a/webapp/src/styles/auth.css b/webapp/src/styles/auth.css new file mode 100644 index 0000000..6af30d3 --- /dev/null +++ b/webapp/src/styles/auth.css @@ -0,0 +1,193 @@ +.loading-screen { + height: 100%; + display: grid; + place-items: center; + color: var(--muted); + font-size: 18px; + animation: fade-in-up var(--dur-panel) var(--ease-out-strong) both; +} + +.auth-page { + min-height: 100%; + display: grid; + place-items: center; + padding: 24px; + position: relative; + background: transparent; +} + +.public-send-page { + min-height: 80vh; + align-items: center; + justify-items: center; +} + +.auth-card { + width: 100%; + position: relative; + background: var(--panel); + border: 1px solid var(--line); + border-radius: 24px; + box-shadow: var(--shadow-lg); + padding: 30px; + overflow: hidden; + transform-origin: 50% 24%; + animation: surface-enter 520ms var(--ease-out-strong) both; +} + +.auth-card h1 { + margin: 0 0 4px 0; + text-align: center; +} + +.standalone-shell { + width: min(640px, 100%); + display: grid; + gap: 14px; + animation: fade-in-up 420ms var(--ease-out-strong) both; +} + +.standalone-brand { + display: inline-flex; + align-items: center; + gap: 14px; + margin-bottom: 12px; +} + +.standalone-brand-outside { + justify-content: center; + width: 100%; + margin-bottom: 2px; +} + +.standalone-brand-logo { + width: 56px; + height: 56px; + object-fit: contain; + flex-shrink: 0; + filter: drop-shadow(0 8px 18px rgba(43, 102, 217, 0.22)); +} + +.standalone-brand-wordmark { + display: block; + height: auto; + width: clamp(200px, 30vw, 360px); + max-width: 100%; + filter: drop-shadow(0 10px 22px rgba(43, 102, 217, 0.18)); +} + +.standalone-title { + margin: 0 0 4px 0; + text-align: left; + font-size: 31px; + line-height: 1.15; + letter-spacing: -0.035em; +} + +.standalone-muted { + text-align: left; +} + +.jwt-warning-head { + display: flex; + align-items: center; + justify-content: center; + gap: 10px; + margin-bottom: 10px; + color: #b45309; + text-align: center; +} + +.jwt-warning-box { + border: 1px solid #f1d8a5; + border-radius: 12px; + background: #fffaf0; + padding: 12px 14px; +} + +.jwt-warning-label { + font-size: 13px; + font-weight: 700; + color: #92400e; + margin-bottom: 6px; +} + +.jwt-warning-copy { + margin: 0 0 14px; + color: #475569; + line-height: 1.6; +} + +.jwt-warning-list { + margin: 0; + padding-left: 18px; + color: #334155; + line-height: 1.55; +} + +.jwt-inline-link { + color: #1d4ed8; + font-weight: 700; + text-decoration: none; +} + +.jwt-inline-link:hover { + text-decoration: underline; +} + +.jwt-secret-fields { + margin-top: 8px; + display: grid; + gap: 6px; +} + +.jwt-secret-row { + display: grid; + grid-template-columns: 88px minmax(0, 1fr); + gap: 8px; + align-items: start; +} + +.jwt-secret-row > span { + color: #64748b; +} + +.jwt-generator { + margin-top: 14px; +} + +.jwt-generator-actions { + margin-top: 10px; + display: flex; + align-items: center; + gap: 10px; + flex-wrap: wrap; +} + +.jwt-copy-hint { + color: #15803d; + font-size: 13px; + font-weight: 700; +} + +.standalone-footer { + width: 100%; + text-align: center; + font-size: 13px; + color: #64748b; +} + +.standalone-footer a { + color: #1d4ed8; + font-weight: 700; + text-decoration: none; +} + +.standalone-footer a:hover { + text-decoration: underline; +} + +.standalone-version { + font-weight: 700; + color: #1d4ed8; +} diff --git a/webapp/src/styles/base.css b/webapp/src/styles/base.css new file mode 100644 index 0000000..1707d52 --- /dev/null +++ b/webapp/src/styles/base.css @@ -0,0 +1,27 @@ +* { + box-sizing: border-box; +} + +html, +body, +#root { + margin: 0; + padding: 0; + width: 100%; + height: 100%; + color: var(--text); + background: var(--bg-accent); + font-family: 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', 'Noto Sans SC', sans-serif; +} + +body { + position: relative; + transition: + background-color var(--dur-medium) var(--ease-smooth), + color var(--dur-medium) var(--ease-smooth); +} + +body.dialog-open { + overflow: hidden; + overscroll-behavior: contain; +} diff --git a/webapp/src/styles/dark.css b/webapp/src/styles/dark.css new file mode 100644 index 0000000..16359c9 --- /dev/null +++ b/webapp/src/styles/dark.css @@ -0,0 +1,458 @@ +:root[data-theme='dark'] body, +:root[data-theme='dark'] #root, +:root[data-theme='dark'] .app-page, +:root[data-theme='dark'] .auth-page { + background: transparent; + color: var(--text); +} + +:root[data-theme='dark'] .app-shell, +:root[data-theme='dark'] .auth-card, +:root[data-theme='dark'] .dialog, +:root[data-theme='dark'] .jwt-warning-box, +:root[data-theme='dark'] .backup-operations-sidebar, +:root[data-theme='dark'] .backup-destination-sidebar, +:root[data-theme='dark'] .backup-detail-panel, +:root[data-theme='dark'] .settings-subcard, +:root[data-theme='dark'] .list-panel, +:root[data-theme='dark'] .card, +:root[data-theme='dark'] .sidebar-block, +:root[data-theme='dark'] .empty { + background: var(--panel); + border-color: var(--line); + color: var(--text); + box-shadow: var(--shadow-lg); +} + +:root[data-theme='dark'] .topbar, +:root[data-theme='dark'] .mobile-tabbar, +:root[data-theme='dark'] .sort-menu, +:root[data-theme='dark'] .create-menu, +:root[data-theme='dark'] .dialog-card, +:root[data-theme='dark'] .mobile-sidebar-sheet, +:root[data-theme='dark'] .mobile-detail-sheet { + background: var(--panel-soft); + border-color: var(--line); + color: var(--text); +} + +:root[data-theme='dark'] .dialog-card.warning { + border-color: rgba(248, 113, 113, 0.36); + background: linear-gradient(180deg, rgba(39, 16, 16, 0.98), rgba(27, 12, 12, 0.98)); + box-shadow: + 0 36px 90px rgba(5, 5, 5, 0.56), + 0 0 0 1px rgba(248, 113, 113, 0.12) inset; +} + +:root[data-theme='dark'] .dialog-mask.warning { + background: + radial-gradient(circle at top, rgba(127, 29, 29, 0.28), transparent 34%), + linear-gradient(180deg, rgba(20, 12, 12, 0.64), rgba(2, 6, 23, 0.82)); +} + +:root[data-theme='dark'] .dialog-warning-badge { + background: linear-gradient(180deg, rgba(127, 29, 29, 0.8), rgba(69, 10, 10, 0.86)); + color: #fda4af; + box-shadow: + 0 12px 30px rgba(0, 0, 0, 0.32), + 0 0 0 1px rgba(248, 113, 113, 0.14) inset; +} + +:root[data-theme='dark'] .dialog-warning-kicker, +:root[data-theme='dark'] .dialog-card.warning .dialog-title { + color: #fecaca; +} + +:root[data-theme='dark'] .dialog-message.warning { + border-color: rgba(248, 113, 113, 0.18); + background: linear-gradient(180deg, rgba(69, 10, 10, 0.54), rgba(67, 20, 7, 0.46)); + color: #fecdd3; + box-shadow: 0 10px 28px rgba(0, 0, 0, 0.18) inset; +} + +:root[data-theme='dark'] .app-side, +:root[data-theme='dark'] .sidebar, +:root[data-theme='dark'] .mobile-sidebar-sheet .sidebar-block { + background: var(--panel-muted); + border-color: var(--line); +} + +:root[data-theme='dark'] .auth-card { + background: var(--panel); +} + +:root[data-theme='dark'] .brand, +:root[data-theme='dark'] .mobile-page-title, +:root[data-theme='dark'] .detail-title, +:root[data-theme='dark'] .dialog-title, +:root[data-theme='dark'] .standalone-title, +:root[data-theme='dark'] .kv-main strong, +:root[data-theme='dark'] .list-title, +:root[data-theme='dark'] .sidebar-title, +:root[data-theme='dark'] h1, +:root[data-theme='dark'] h2, +:root[data-theme='dark'] h3, +:root[data-theme='dark'] h4 { + color: var(--text); +} + +:root[data-theme='dark'] .standalone-brand-wordmark, +:root[data-theme='dark'] .brand-wordmark { + text-shadow: 0 16px 28px rgba(2, 6, 23, 0.32); +} + +:root[data-theme='dark'] .muted, +:root[data-theme='dark'] .detail-sub, +:root[data-theme='dark'] .field-help, +:root[data-theme='dark'] .list-sub, +:root[data-theme='dark'] .kv-label, +:root[data-theme='dark'] .standalone-muted, +:root[data-theme='dark'] .standalone-footer, +:root[data-theme='dark'] .backup-inline-note, +:root[data-theme='dark'] .backup-browser-empty, +:root[data-theme='dark'] .or, +:root[data-theme='dark'] .mobile-tab, +:root[data-theme='dark'] .side-link, +:root[data-theme='dark'] .user-chip, +:root[data-theme='dark'] .list-count { + color: var(--muted); +} + +:root[data-theme='dark'] .user-chip { + background: rgba(17, 34, 56, 0.94); + border-color: var(--line); + box-shadow: 0 12px 24px rgba(1, 7, 18, 0.24); +} + +:root[data-theme='dark'] .side-link:hover, +:root[data-theme='dark'] .mobile-tab:hover { + background: rgba(132, 182, 255, 0.11); +} + +:root[data-theme='dark'] .side-link.active, +:root[data-theme='dark'] .mobile-tab.active, +:root[data-theme='dark'] .sort-menu-item.active, +:root[data-theme='dark'] .list-item.active { + background: linear-gradient(135deg, rgba(132, 182, 255, 0.2), rgba(56, 189, 248, 0.08)); + border-color: rgba(132, 182, 255, 0.28); + color: var(--primary); +} + +:root[data-theme='dark'] .input, +:root[data-theme='dark'] .textarea, +:root[data-theme='dark'] select.input, +:root[data-theme='dark'] .dialog input, +:root[data-theme='dark'] .dialog textarea, +:root[data-theme='dark'] .dialog select { + background: rgba(13, 24, 40, 0.94); + border-color: rgba(103, 136, 186, 0.36); + color: var(--text); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03); +} + +:root[data-theme='dark'] .input::placeholder, +:root[data-theme='dark'] .textarea::placeholder, +:root[data-theme='dark'] input::placeholder, +:root[data-theme='dark'] textarea::placeholder { + color: #7488a8; +} + +:root[data-theme='dark'] .input:focus, +:root[data-theme='dark'] .textarea:focus, +:root[data-theme='dark'] .search-input:focus, +:root[data-theme='dark'] .dialog input:focus, +:root[data-theme='dark'] .dialog textarea:focus, +:root[data-theme='dark'] .dialog select:focus { + border-color: rgba(132, 182, 255, 0.54); + background-color: rgba(16, 30, 49, 0.98); + box-shadow: 0 0 0 4px rgba(132, 182, 255, 0.12), 0 10px 22px rgba(5, 13, 28, 0.24), inset 0 1px 0 rgba(255, 255, 255, 0.04); +} + +:root[data-theme='dark'] select.input { + background-image: + linear-gradient(45deg, transparent 50%, #8fb8ff 50%), + linear-gradient(135deg, #8fb8ff 50%, transparent 50%); +} + +:root[data-theme='dark'] .input-readonly { + background: #0f1b2d; + color: var(--muted-strong); +} + +:root[data-theme='dark'] .input:disabled, +:root[data-theme='dark'] .btn:disabled { + background: #132033; + border-color: #22334c; + color: #70829d; +} + +:root[data-theme='dark'] .btn-secondary { + background: linear-gradient(180deg, rgba(22, 41, 66, 0.98), rgba(16, 31, 52, 0.98)); + border-color: rgba(132, 182, 255, 0.22); + color: #a9cdff; + box-shadow: 0 12px 22px rgba(1, 7, 18, 0.18); +} + +:root[data-theme='dark'] .btn-secondary:hover { + background: linear-gradient(180deg, rgba(26, 49, 79, 0.98), rgba(19, 37, 61, 0.98)); + border-color: rgba(132, 182, 255, 0.3); +} + +:root[data-theme='dark'] .btn-danger { + background: linear-gradient(180deg, rgba(45, 23, 33, 0.98), rgba(35, 18, 28, 0.98)); + border-color: rgba(255, 139, 168, 0.38); + color: #ff9bb0; +} + +:root[data-theme='dark'] .btn-danger:hover { + background: linear-gradient(180deg, rgba(56, 27, 40, 0.98), rgba(41, 19, 31, 0.98)); + border-color: rgba(255, 171, 192, 0.42); +} + +:root[data-theme='dark'] .btn-primary { + background: linear-gradient(135deg, #79acff, #57c2ff 76%); + border-color: rgba(176, 214, 255, 0.22); + color: #061120; + box-shadow: 0 18px 32px rgba(10, 26, 52, 0.34); +} + +:root[data-theme='dark'] .btn-primary:hover { + background: linear-gradient(135deg, #90bcff, #6accff 76%); + box-shadow: 0 22px 36px rgba(10, 26, 52, 0.38); +} + +:root[data-theme='dark'] .toolbar.actions, +:root[data-theme='dark'] .list-head, +:root[data-theme='dark'] .mobile-panel-head, +:root[data-theme='dark'] .backup-recommendation-header, +:root[data-theme='dark'] .backup-operations-sidebar, +:root[data-theme='dark'] .backup-destination-sidebar, +:root[data-theme='dark'] .backup-detail-panel, +:root[data-theme='dark'] .detail-actions, +:root[data-theme='dark'] .topbar, +:root[data-theme='dark'] .app-side, +:root[data-theme='dark'] .kv-row, +:root[data-theme='dark'] .attachment-row, +:root[data-theme='dark'] .backup-browser-row { + border-color: var(--line); +} + +:root[data-theme='dark'] .input, +:root[data-theme='dark'] .search-input, +:root[data-theme='dark'] .list-item, +:root[data-theme='dark'] .sidebar-block { + background: rgba(15, 28, 45, 0.94); +} + +:root[data-theme='dark'] .sidebar, +:root[data-theme='dark'] .content, +:root[data-theme='dark'] .list-col, +:root[data-theme='dark'] .detail-col { + color: var(--text); +} + +:root[data-theme='dark'] .mobile-sidebar-mask, +:root[data-theme='dark'] .dialog-mask { + background: var(--overlay-strong); +} + +:root[data-theme='dark'] .toast { + background: linear-gradient(180deg, rgba(19, 34, 54, 0.98), rgba(14, 26, 42, 0.98)); + border-color: #263a57; + color: var(--text); +} + +:root[data-theme='dark'] .toast.success { + background: #0f2a1f; + border-color: #1f5b44; + color: #9be2bd; +} + +:root[data-theme='dark'] .toast.error { + background: #2a1720; + border-color: #6c2b41; + color: #ffb1c0; +} + +:root[data-theme='dark'] .toast.warning { + background: #2d2413; + border-color: #7b6230; + color: #f7d48b; +} + +:root[data-theme='dark'] .jwt-warning-head, +:root[data-theme='dark'] .jwt-warning-label, +:root[data-theme='dark'] .jwt-warning-copy, +:root[data-theme='dark'] .jwt-warning-list { + color: #f4d48a; +} + +:root[data-theme='dark'] .theme-switch-input:focus + .theme-switch-slider { + box-shadow: 0 0 0 2px rgba(132, 182, 255, 0.24); +} + +:root[data-theme='dark'] .search-input, +:root[data-theme='dark'] .list-head .search-input, +:root[data-theme='dark'] .mobile-settings-card, +:root[data-theme='dark'] .mobile-settings-link, +:root[data-theme='dark'] .table tr, +:root[data-theme='dark'] .settings-subcard, +:root[data-theme='dark'] .backup-operations-sidebar, +:root[data-theme='dark'] .backup-destination-sidebar, +:root[data-theme='dark'] .backup-detail-panel, +:root[data-theme='dark'] .dialog-card, +:root[data-theme='dark'] .backup-browser-path, +:root[data-theme='dark'] .backup-browser-list, +:root[data-theme='dark'] .create-menu, +:root[data-theme='dark'] .create-menu-item, +:root[data-theme='dark'] .sort-menu, +:root[data-theme='dark'] .sort-menu-item, +:root[data-theme='dark'] .backup-recommendation-card, +:root[data-theme='dark'] .backup-recommendation-dav-item, +:root[data-theme='dark'] .backup-destination-item, +:root[data-theme='dark'] .totp-code-row, +:root[data-theme='dark'] .list-item { + background: + linear-gradient(180deg, rgba(18, 32, 52, 0.92), rgba(14, 26, 42, 0.92)); + border-color: var(--line); + color: var(--text); +} + +:root[data-theme='dark'] .list-item:hover, +:root[data-theme='dark'] .sort-menu-item:hover, +:root[data-theme='dark'] .create-menu-item:hover, +:root[data-theme='dark'] .mobile-settings-link:hover, +:root[data-theme='dark'] .backup-destination-item:hover { + background: + linear-gradient(180deg, rgba(24, 44, 70, 0.96), rgba(16, 31, 51, 0.96)); + border-color: rgba(118, 150, 197, 0.32); +} + +:root[data-theme='dark'] .list-item.active { + background: linear-gradient(135deg, rgba(132, 182, 255, 0.2), rgba(56, 189, 248, 0.1)); + border-color: rgba(122, 176, 255, 0.34); + box-shadow: inset 0 0 0 1px rgba(200, 225, 255, 0.06), 0 12px 24px rgba(5, 13, 28, 0.18); +} + +:root[data-theme='dark'] .list-item::before { + background: + linear-gradient(90deg, rgba(132, 182, 255, 0.08), transparent 24%, transparent 76%, rgba(56, 189, 248, 0.08)), + radial-gradient(circle at 18px 50%, rgba(255, 255, 255, 0.06), transparent 44%); +} + +:root[data-theme='dark'] .backup-destination-item.active, +:root[data-theme='dark'] .backup-interval-preset.active, +:root[data-theme='dark'] .mobile-settings-link.active, +:root[data-theme='dark'] .tree-btn.active { + background: linear-gradient(135deg, rgba(132, 182, 255, 0.2), rgba(56, 189, 248, 0.1)); + border-color: rgba(132, 182, 255, 0.34); + color: #f4f8ff; +} + +:root[data-theme='dark'] .theme-switch-slider { + background: linear-gradient(180deg, #1d3659, #142845); + border-color: rgba(120, 152, 198, 0.34); +} + +:root[data-theme='dark'] .theme-switch-slider::before { + background: linear-gradient(180deg, #f8fbff, #dce9ff); + box-shadow: 0 3px 10px rgba(2, 8, 20, 0.28); +} + +:root[data-theme='dark'] .theme-switch .moon svg { + fill: #8db6ff; +} + +:root[data-theme='dark'] .theme-switch .sun svg { + opacity: 0.82; +} + +:root[data-theme='dark'] .totp-code-name, +:root[data-theme='dark'] .backup-destination-name, +:root[data-theme='dark'] .backup-browser-entry, +:root[data-theme='dark'] .mobile-settings-link, +:root[data-theme='dark'] .backup-browser-path strong, +:root[data-theme='dark'] .backup-option-label, +:root[data-theme='dark'] .sort-menu-item, +:root[data-theme='dark'] .create-menu-item, +:root[data-theme='dark'] .tree-btn, +:root[data-theme='dark'] .folder-add-btn, +:root[data-theme='dark'] .list-icon-fallback, +:root[data-theme='dark'] .totp-code-main strong, +:root[data-theme='dark'] .totp-timer-value { + color: var(--text); +} + +:root[data-theme='dark'] .totp-code-username, +:root[data-theme='dark'] .backup-destination-meta, +:root[data-theme='dark'] .backup-browser-meta, +:root[data-theme='dark'] .table td::before, +:root[data-theme='dark'] .backup-recommendation-step, +:root[data-theme='dark'] .backup-recommendation-inline-note, +:root[data-theme='dark'] .backup-recommendation-linked-item, +:root[data-theme='dark'] .backup-inline-suffix, +:root[data-theme='dark'] .folder-delete-btn, +:root[data-theme='dark'] .folder-add-btn:hover, +:root[data-theme='dark'] .tree-label, +:root[data-theme='dark'] .list-sub { + color: var(--muted); +} + +:root[data-theme='dark'] .import-export-panel p, +:root[data-theme='dark'] .dialog-message, +:root[data-theme='dark'] .local-error, +:root[data-theme='dark'] .status-ok { + color: var(--muted); +} + +:root[data-theme='dark'] .backup-destination-type { + background: #1d3048; + color: #c9d8eb; +} + +:root[data-theme='dark'] .backup-help-trigger { + border-color: #38618f; + background: #173150; + color: #9ec5ff; +} + +:root[data-theme='dark'] .backup-help-trigger:hover, +:root[data-theme='dark'] .backup-help-trigger:focus-visible { + border-color: #5f92d7; + background: #20426a; +} + +:root[data-theme='dark'] .backup-help-bubble { + background: var(--panel); + border-color: var(--line); + color: var(--text); +} + +:root[data-theme='dark'] .backup-help-bubble::before { + background: var(--panel); + border-left-color: var(--line); + border-top-color: var(--line); +} + +:root[data-theme='dark'] .table td { + border-bottom-color: #203047; +} + +:root[data-theme='dark'] .local-error { + color: #ff9bb0; +} + +:root[data-theme='dark'] .status-ok { + color: #9be2bd; +} + +:root[data-theme='dark'] .totp-qr { + background: #ffffff; + border-color: rgba(15, 23, 42, 0.12); +} + +:root[data-theme='dark'] .totp-qr svg, +:root[data-theme='dark'] .totp-qr img { + background: #ffffff; + border-radius: 8px; +} diff --git a/webapp/src/styles/forms.css b/webapp/src/styles/forms.css new file mode 100644 index 0000000..1a076c9 --- /dev/null +++ b/webapp/src/styles/forms.css @@ -0,0 +1,309 @@ +.muted { + margin: 0 0 16px 0; + text-align: center; + color: var(--muted); + line-height: 1.65; +} + +.field { + display: block; + margin-bottom: 14px; +} + +.field > span { + display: block; + margin-bottom: 8px; + font-size: 14px; + font-weight: 600; +} + +.input { + width: 100%; + height: 48px; + border: 1px solid rgba(74, 103, 150, 0.42); + border-radius: 14px; + padding: 10px 14px; + font-size: 16px; + outline: none; + color: var(--text); + background: var(--panel); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9); + transition: + border-color var(--dur-fast) var(--ease-smooth), + box-shadow var(--dur-fast) var(--ease-out-soft), + background-color var(--dur-fast) var(--ease-smooth), + transform var(--dur-fast) var(--ease-out-soft); +} + +select.input { + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + padding-right: 42px; + background-image: + linear-gradient(45deg, transparent 50%, #365fa8 50%), + linear-gradient(135deg, #365fa8 50%, transparent 50%); + background-position: + calc(100% - 18px) calc(50% - 3px), + calc(100% - 12px) calc(50% - 3px); + background-size: 6px 6px, 6px 6px; + background-repeat: no-repeat; +} + +input[type='file'].input { + height: auto; + min-height: 48px; + padding: 8px 10px; + font-size: 14px; + line-height: 1.4; +} + +input[type='file'].input::file-selector-button { + height: 32px; + border: 1px solid #3f5b9e; + border-radius: 999px; + padding: 0 12px; + background: #eef4ff; + color: #1f4ea0; + font-weight: 700; + cursor: pointer; + margin-right: 10px; +} + +input[type='file'].input::file-selector-button:hover { + background: #dfeaff; + border-color: #2f5fd8; +} + +.textarea { + min-height: 110px; + height: auto; + resize: vertical; +} + +.input:focus { + border-color: rgba(43, 102, 217, 0.6); + background-color: #fbfdff; + box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.11), 0 10px 20px rgba(37, 99, 235, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.95); + transform: translateY(-1px); +} + +.input-readonly { + background: #eef2f7; + color: #475569; +} + +.input:disabled { + background: #e2e8f0; + border-color: #cbd5e1; + color: #94a3b8; + cursor: not-allowed; +} + +.password-wrap { + position: relative; +} + +.password-wrap .input { + padding-right: 44px; +} + +.password-toggle { + position: absolute; + right: 8px; + top: 50%; + transform: translateY(-50%); + border: none; + background: transparent; + color: #275ac2; + cursor: pointer; + display: grid; + place-items: center; + transition: color var(--dur-fast) var(--ease-smooth), transform var(--dur-fast) var(--ease-out-soft); +} + +.eye-btn { + position: absolute; + right: 10px; + bottom: 9px; + width: 30px; + height: 30px; + border: none; + background: transparent; + cursor: pointer; + display: grid; + place-items: center; + color: #334155; + transition: color var(--dur-fast) var(--ease-smooth), transform var(--dur-fast) var(--ease-out-soft); +} + +.password-toggle:hover, +.eye-btn:hover { + color: var(--primary); + transform: translateY(-1px) scale(1.04); +} + +.btn { + height: 36px; + border: 1px solid transparent; + border-radius: 999px; + padding: 0 16px; + font-size: 15px; + font-weight: 700; + cursor: pointer; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 6px; + text-decoration: none; + transition: + transform var(--dur-fast) var(--ease-out-soft), + box-shadow var(--dur-fast) var(--ease-out-soft), + background-color var(--dur-fast) var(--ease-smooth), + border-color var(--dur-fast) var(--ease-smooth), + color var(--dur-fast) var(--ease-smooth), + opacity var(--dur-fast) var(--ease-smooth); +} + +.topbar-actions .btn, +.user-chip, +.side-link, +.mobile-tab { + --mag-x: 0px; + --mag-y: 0px; + position: relative; + overflow: hidden; +} + +.topbar-actions .btn::before, +.user-chip::before, +.side-link::before, +.mobile-tab::before { + content: ''; + position: absolute; + left: var(--mx, 50%); + top: var(--my, 50%); + width: 110px; + height: 110px; + border-radius: 999px; + background: radial-gradient(circle, rgba(255, 255, 255, 0.36), rgba(255, 255, 255, 0.08) 42%, transparent 72%); + transform: translate(-50%, -50%) scale(0.68); + opacity: 0; + pointer-events: none; + transition: + opacity var(--dur-fast) var(--ease-smooth), + transform var(--dur-medium) var(--ease-out-soft); +} + +.topbar-actions .btn:hover::before, +.user-chip:hover::before, +.side-link:hover::before, +.mobile-tab:hover::before { + opacity: 1; + transform: translate(-50%, -50%) scale(1); +} + +.btn:hover:not(:disabled) { + transform: translateY(-2px) scale(1.01); +} + +.btn:active:not(:disabled) { + transform: translateY(0) scale(0.985); +} + +.btn-icon { + flex-shrink: 0; +} + +.btn.full { + width: 100%; + height: 50px; + font-size: 22px; + margin: 10px 0; +} + +.btn-primary { + background: linear-gradient(135deg, #2563eb, #3b82f6 72%); + border-color: rgba(15, 63, 152, 0.32); + color: #fff; + box-shadow: 0 14px 28px rgba(37, 99, 235, 0.24); +} + +.btn-primary:hover { + background: linear-gradient(135deg, #1d4ed8, #3377f0 72%); + border-color: rgba(15, 63, 152, 0.38); + box-shadow: 0 18px 34px rgba(37, 99, 235, 0.28); +} + +.btn-secondary { + background: var(--panel); + border-color: rgba(37, 99, 235, 0.22); + color: var(--primary-strong); + box-shadow: 0 8px 18px rgba(13, 31, 68, 0.05); +} + +.btn-secondary:hover { + background: #f4f8ff; + border-color: rgba(37, 99, 235, 0.34); +} + +.btn-danger { + background: rgba(255, 255, 255, 0.8); + border-color: rgba(217, 45, 87, 0.28); + color: var(--danger); +} + +.btn-danger:hover { + background: rgba(255, 241, 242, 0.96); + border-color: rgba(217, 45, 87, 0.38); +} + +.btn:disabled { + background: #e2e8f0; + border-color: #cbd5e1; + color: #94a3b8; + cursor: not-allowed; +} + +.or { + text-align: center; + margin: 10px 0; + color: #334155; +} + +.field-help { + margin-top: 8px; + font-size: 13px; + line-height: 1.5; + color: #667085; +} + +.auth-support-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + margin: -2px 0 12px; +} + +.auth-link-btn { + border: none; + background: transparent; + padding: 0; + color: #1d4ed8; + font-size: 13px; + font-weight: 700; + cursor: pointer; + transition: color var(--dur-fast) var(--ease-smooth), transform var(--dur-fast) var(--ease-out-soft), opacity var(--dur-fast) var(--ease-smooth); +} + +.auth-link-btn:hover { + text-decoration: underline; + transform: translateX(2px); +} + +.auth-link-btn:disabled { + color: #94a3b8; + cursor: not-allowed; + text-decoration: none; +} diff --git a/webapp/src/styles/management.css b/webapp/src/styles/management.css new file mode 100644 index 0000000..7fb231e --- /dev/null +++ b/webapp/src/styles/management.css @@ -0,0 +1,1001 @@ +.stack { + display: grid; + gap: 12px; +} + +.import-export-page { + display: grid; + gap: 12px; +} + +.import-export-panels { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 12px; + align-items: start; +} + +.backup-grid { + display: grid; + grid-template-columns: 280px 280px minmax(0, 1fr); + gap: 12px; + align-items: start; + padding: 2px; +} + +.backup-operations-sidebar, +.backup-destination-sidebar, +.backup-detail-panel { + min-width: 0; + background: #fff; + border: 1px solid #d8dee8; + border-radius: 12px; + box-shadow: 0 1px 2px rgba(15, 23, 42, 0.05); + padding: 12px; +} + +.backup-actions-stack { + display: grid; + gap: 10px; +} + +.backup-option-field { + display: inline-flex; + align-items: center; + gap: 8px; +} + +.backup-option-label { + display: inline-flex; + align-items: center; + gap: 8px; + margin: 0; + font-size: 15px; + font-weight: 700; + color: #0f172a; + cursor: pointer; +} + +.backup-option-label input[type='checkbox'] { + width: 22px; + height: 22px; + margin: 0; + flex-shrink: 0; +} + +.backup-help-wrap { + position: relative; + display: inline-flex; + align-items: center; +} + +.backup-help-trigger { + width: 22px; + height: 22px; + border: 1px solid #bfd1f3; + border-radius: 999px; + padding: 0; + background: #eef4ff; + color: #1d4ed8; + font-size: 13px; + font-weight: 800; + line-height: 1; + display: inline-flex; + align-items: center; + justify-content: center; + cursor: pointer; + flex-shrink: 0; +} + +.backup-help-trigger:hover, +.backup-help-trigger:focus-visible { + border-color: #7ea4ef; + background: #e1ecff; + outline: none; +} + +.backup-help-bubble { + position: absolute; + left: 50%; + top: calc(100% + 10px); + z-index: 30; + width: min(320px, calc(100vw - 40px)); + padding: 10px 12px; + border: 1px solid #d5dce7; + border-radius: 12px; + background: #ffffff; + box-shadow: 0 16px 38px rgba(15, 23, 42, 0.14); + color: #475467; + font-size: 13px; + line-height: 1.55; + transform: translate(-50%, -4px); + opacity: 0; + visibility: hidden; + pointer-events: none; + transition: opacity 140ms ease, transform 140ms ease, visibility 140ms ease; +} + +.backup-help-bubble::before { + content: ''; + position: absolute; + left: 50%; + top: -6px; + width: 10px; + height: 10px; + background: #ffffff; + border-left: 1px solid #d5dce7; + border-top: 1px solid #d5dce7; + transform: translateX(-50%) rotate(45deg); +} + +.backup-help-wrap:hover .backup-help-bubble, +.backup-help-wrap:focus-within .backup-help-bubble, +.backup-help-wrap.open .backup-help-bubble { + opacity: 1; + visibility: visible; + pointer-events: auto; + transform: translate(-50%, 0); +} + +.backup-recommendation-list { + display: grid; + gap: 8px; +} + +.backup-recommendation-group + .backup-recommendation-group { + margin-top: 12px; +} + +.backup-recommendation-group-title { + margin: 0 0 8px; + font-size: 14px; + font-weight: 700; + color: #0f172a; +} + +.backup-recommendation-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; +} + +.backup-recommendation-linked { + display: grid; + gap: 4px; +} + +.backup-recommendation-linked-item { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + font-size: 12px; + color: #475467; +} + +.backup-recommendation-card { + border: 1px solid var(--line); + border-radius: 12px; + background: #f8fbff; + padding: 14px; + display: grid; + gap: 12px; +} + +.backup-recommendation-header { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 12px; +} + +.backup-recommendation-actions { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.backup-recommendation-steps { + display: grid; + gap: 8px; +} + +.backup-recommendation-step { + color: #475467; + line-height: 1.5; +} + +.backup-recommendation-inline-note { + color: #475467; + line-height: 1.5; +} + +.backup-recommendation-dav-list { + display: grid; + gap: 8px; +} + +.backup-recommendation-dav-item { + display: grid; + gap: 4px; + padding: 10px 12px; + border: 1px solid var(--line); + border-radius: 10px; + background: #fff; +} + +.backup-recommendation-dav-item code { + overflow-wrap: anywhere; +} + +.backup-destination-list { + display: grid; + gap: 8px; +} + +.backup-destination-item { + width: 100%; + border: 1px solid var(--line); + border-radius: 12px; + background: #fff; + padding: 12px; + text-align: left; + display: grid; + gap: 6px; + cursor: pointer; + transition: border-color 0.15s ease, background 0.15s ease, box-shadow 0.15s ease; +} + +.backup-destination-item:hover { + border-color: #93c5fd; + background: #f8fbff; +} + +.backup-destination-item.active { + border-color: var(--primary); + background: #eff6ff; + box-shadow: 0 0 0 1px rgba(37, 99, 235, 0.08); +} + +.backup-destination-top { + display: flex; + justify-content: space-between; + gap: 12px; + align-items: center; +} + +.backup-destination-name { + font-weight: 700; + color: #0f172a; + overflow-wrap: anywhere; +} + +.backup-destination-type { + border-radius: 999px; + padding: 2px 8px; + background: #e2e8f0; + color: #334155; + font-size: 12px; + white-space: nowrap; +} + +.backup-destination-meta { + color: #64748b; + font-size: 13px; + line-height: 1.4; +} + +.backup-destination-addbar { + margin-top: 10px; +} + +.backup-add-chooser { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-top: 10px; +} + +.backup-name-row { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 10px; + align-items: end; + margin-bottom: 8px; +} + +.backup-name-field { + margin: 0; + grid-column: 1 / span 3; +} + +.backup-type-field { + margin: 0; + grid-column: 4; +} + +.backup-detail-schedule-grid { + grid-template-columns: repeat(4, minmax(0, 1fr)); +} + +.backup-interval-row { + display: grid; + grid-template-columns: minmax(0, 1fr) 86px; + gap: 10px; + align-items: start; +} + +.backup-interval-presets { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 3px; +} + +.backup-interval-preset { + height: 22px; + border: 1px solid #cdd7e6; + border-radius: 999px; + background: #f8fafc; + color: #475569; + font-size: 12px; + font-weight: 700; + cursor: pointer; + transition: + border-color var(--dur-fast) var(--ease-smooth), + background-color var(--dur-fast) var(--ease-smooth), + color var(--dur-fast) var(--ease-smooth), + transform var(--dur-fast) var(--ease-out-soft), + box-shadow var(--dur-fast) var(--ease-out-soft); +} + +.backup-interval-preset:hover:not(:disabled) { + border-color: #2563eb; + color: #2563eb; + background: #eff6ff; +} + +.backup-interval-preset.active { + border-color: #2563eb; + background: #2563eb; + color: #fff; +} + +.backup-interval-preset:disabled { + cursor: not-allowed; + opacity: 0.55; +} + +.backup-inline-suffix-wrap { + position: relative; + width: 100%; +} + +.backup-inline-suffix { + position: absolute; + right: 12px; + top: 50%; + transform: translateY(-50%); + color: #64748b; + font-size: 13px; + font-weight: 700; + pointer-events: none; +} + +.backup-schedule-attachments-row { + margin-bottom: 4px; +} + +.backup-divider { + height: 1px; + background: var(--line); + margin: 14px 0; +} + +.backup-browser-path { + display: flex; + gap: 8px; + align-items: center; + padding: 10px 12px; + border: 1px solid var(--line); + border-radius: 10px; + background: #f8fafc; + margin-bottom: 10px; + overflow-wrap: anywhere; +} + +.backup-browser-path strong { + color: #0f172a; +} + +.backup-browser-nav { + margin-bottom: 10px; +} + +.backup-browser-list { + border: 1px solid var(--line); + border-radius: 12px; + overflow: hidden; + background: #fff; +} + +.backup-browser-pagination { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 10px; + margin-top: 10px; +} + +.backup-browser-page-indicator { + min-width: 48px; + text-align: center; + color: #64748b; + font-size: 13px; + font-weight: 700; +} + +.backup-browser-row + .backup-browser-row { + border-top: 1px solid var(--line); +} + +.backup-browser-row { + display: grid; + grid-template-columns: minmax(0, 1fr) auto auto; + gap: 10px; + align-items: center; + padding: 10px 12px; +} + +.backup-browser-entry { + border: none; + background: transparent; + text-align: left; + display: inline-flex; + align-items: center; + gap: 8px; + padding: 0; + color: #0f172a; + cursor: pointer; +} + +.backup-browser-entry.file { + cursor: default; +} + +.backup-browser-name { + font-weight: 700; + overflow-wrap: anywhere; +} + +.backup-browser-meta { + display: grid; + justify-items: end; + gap: 4px; + color: #64748b; + font-size: 13px; + text-align: right; +} + +.backup-browser-actions { + justify-content: flex-end; +} + +.backup-browser-empty { + border: 1px dashed var(--line); + border-radius: 12px; + padding: 18px 14px; + text-align: center; + color: #64748b; +} + +.backup-inline-note { + margin: 0 0 12px; + color: #64748b; + line-height: 1.5; +} + +.import-export-panel h3 { + margin: 0 0 6px 0; +} + +.field-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; +} + +.field-span-2 { + grid-column: 1 / -1; +} + +.totp-grid { + display: grid; + grid-template-columns: 220px 1fr; + gap: 14px; + margin-bottom: 14px; +} + +.totp-qr { + border: 1px solid var(--line); + border-radius: 10px; + background: #fff; + display: grid; + place-items: center; + min-height: 220px; + padding: 8px; +} + +.totp-qr svg { + width: 180px; + height: 180px; +} + +.totp-qr img { + width: 180px; + height: 180px; + background: #fff; + border-radius: 8px; +} + +.section-head { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 10px; +} + +.actions { + display: flex; + gap: 8px; + flex-wrap: wrap; +} + +.muted-inline { + color: var(--muted); + align-self: center; + font-size: 14px; +} + +.create-menu-wrap { + position: relative; +} + +.create-menu { + position: absolute; + left: 0; + top: calc(100% + 6px); + width: 220px; + background: #fff; + border: 1px solid var(--line); + border-radius: 12px; + box-shadow: 0 12px 28px rgba(15, 23, 42, 0.18); + overflow: hidden; + z-index: 20; + transform-origin: bottom left; + animation: menu-in 190ms var(--ease-out-strong) both; +} + +.create-menu-item { + width: 100%; + border: none; + background: #fff; + text-align: left; + padding: 11px 12px; + cursor: pointer; + font-weight: 600; + display: flex; + align-items: center; + gap: 8px; + transition: + background-color var(--dur-fast) var(--ease-smooth), + color var(--dur-fast) var(--ease-smooth), + transform var(--dur-fast) var(--ease-out-soft); +} + +.create-menu-item:hover { + background: #f1f5f9; + transform: translateX(2px); +} + +.website-row { + display: grid; + grid-template-columns: auto minmax(0, 1fr) minmax(130px, 160px) auto; + gap: 8px; + margin-bottom: 8px; + align-items: center; + padding: 6px; + border: 1px solid transparent; + border-radius: 18px; + background: color-mix(in srgb, var(--panel) 84%, transparent); + transition: + border-color var(--dur-fast) var(--ease-smooth), + background-color var(--dur-fast) var(--ease-smooth), + box-shadow var(--dur-fast) var(--ease-out-soft), + transform 220ms var(--ease-out-soft), + opacity var(--dur-fast) var(--ease-smooth); +} + +.website-row.is-dragging { + opacity: 0.48; + border-color: rgba(37, 99, 235, 0.24); + background: color-mix(in srgb, var(--panel-soft) 92%, white 8%); + box-shadow: var(--shadow-sm); +} + +.website-drag-btn { + min-width: 28px; + width: 28px; + height: 48px; + padding: 0; + gap: 0; + cursor: grab; + color: var(--muted); + border-color: transparent; + background: transparent; + box-shadow: none; + border-radius: 10px; + position: relative; + overflow: visible; + opacity: 0.82; + touch-action: none; + -webkit-user-select: none; + user-select: none; +} + +.website-drag-btn:hover { + color: var(--primary-strong); + border-color: transparent; + background: transparent; + box-shadow: none; + opacity: 1; +} + +.website-drag-btn:active { + cursor: grabbing; + border-color: transparent; + background: transparent; + box-shadow: none; +} + +.website-drag-btn::before { + content: ''; + position: absolute; + inset: -8px; + border-radius: 12px; +} + +.website-drag-btn .btn-icon { + opacity: 0.9; +} + +.website-match-select { + height: 48px; + font-size: 13px; + line-height: 1.2; + padding-top: 10px; + padding-bottom: 10px; + padding-right: 38px; +} + +.website-match-select option { + font-size: 13px; +} + +.website-row .btn { + justify-self: start; + width: auto; +} + +@media (max-width: 760px) { + .website-row { + grid-template-columns: auto minmax(0, 1fr) auto; + align-items: start; + } + + .website-row > :nth-child(1) { + grid-column: 1; + grid-row: 1; + align-self: center; + } + + .website-row > :nth-child(2) { + grid-column: 2 / span 2; + grid-row: 1; + } + + .website-row > :nth-child(3) { + grid-column: 1 / span 2; + grid-row: 2; + } + + .website-row > :nth-child(4) { + grid-column: 3; + grid-row: 2; + justify-self: start; + } +} + +.cf-check { + margin-bottom: 0; +} + +.cf-check.view { + margin: 0; +} + +.cf-check input[type='checkbox'] { + width: 22px; + height: 22px; +} + +.star-on { + background: #eef4ff; +} + +.detail-actions { + display: flex; + justify-content: space-between; + align-items: center; + margin: 12px 0; +} + +.detail-delete-btn { + margin-left: auto; +} + +.send-options { + display: grid; + gap: 8px; + color: #3a4a64; +} + +.send-options label { + display: inline-flex; + align-items: center; + gap: 8px; +} + +.local-error { + margin-top: 10px; + color: #b42318; + font-weight: 600; +} + +.status-ok { + margin: 2px 0 10px 0; + color: #0f766e; + font-weight: 700; +} + +.restore-progress-card { + margin: 8px 0 12px; + padding: 14px 16px; + border-radius: 12px; + border: 1px solid #d7e2f1; + background: #ffffff; + box-shadow: 0 8px 20px rgba(15, 23, 42, 0.10); +} + +.restore-progress-overlay { + position: fixed; + inset: 0; + z-index: 1250; + display: grid; + place-items: center; + padding: 20px; + background: rgba(15, 23, 42, 0.30); +} + +.restore-progress-modal { + width: min(520px, 100%); + margin: 0; +} + +.restore-progress-head { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 12px; + margin-bottom: 12px; +} + +.restore-progress-kicker { + font-size: 12px; + font-weight: 600; + letter-spacing: 0.02em; + color: #64748b; +} + +.restore-progress-title { + margin: 4px 0 2px; + font-size: 20px; + line-height: 1.2; +} + +.restore-progress-subtitle { + margin: 0; + color: #6b7280; + font-size: 13px; +} + +.restore-progress-elapsed { + flex: 0 0 auto; + min-width: 88px; + padding: 6px 8px; + border-radius: 10px; + background: #f8fbff; + border: 1px solid #d7e2f1; + color: #475569; + font-weight: 600; + font-size: 13px; + text-align: center; +} + +.restore-progress-meter { + height: 6px; + border-radius: 999px; + background: #e7eef8; + overflow: hidden; +} + +.restore-progress-meter-bar { + display: block; + height: 100%; + border-radius: inherit; + background: #3a71d8; + transition: width 280ms ease; +} + +.restore-progress-current { + margin-top: 12px; + padding: 10px 12px; + border-radius: 10px; + background: #f8fbff; + border: 1px solid #d7e2f1; +} + +.restore-progress-current strong { + display: block; + color: #0f172a; + font-size: 14px; +} + +.restore-progress-current p { + margin: 4px 0 0; + color: #64748b; + line-height: 1.45; + font-size: 13px; +} + +.restore-progress-list { + list-style: none; + margin: 12px 0 0; + padding: 0; + display: grid; + gap: 6px; +} + +.restore-progress-item { + display: flex; + align-items: center; + gap: 8px; + min-height: 30px; + color: #64748b; + font-weight: 500; + font-size: 13px; +} + +.restore-progress-item.active { + color: #1d4ed8; +} + +.restore-progress-item.done { + color: #475569; +} + +.restore-progress-dot { + width: 8px; + height: 8px; + border-radius: 999px; + background: #cbd5e1; + flex: 0 0 auto; +} + +.restore-progress-item.active .restore-progress-dot { + background: #1d4ed8; +} + +.restore-progress-item.done .restore-progress-dot { + background: #94a3b8; +} + +.kv-line strong { + overflow-wrap: anywhere; +} + +.check-line { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 12px; + color: #334155; +} + +.table { + width: 100%; + border-collapse: collapse; +} + +.table th, +.table td { + text-align: left; + border-bottom: 1px solid var(--line); + padding: 10px 8px; + font-size: 14px; +} + +.table td::before { + display: none; +} + +.table th { + color: #667085; +} + +.input.small { + width: 120px; +} + +.invite-toolbar { + display: flex; + justify-content: space-between; + align-items: center; + gap: 10px; + flex-wrap: wrap; + margin-bottom: 10px; +} + +.invite-create-group { + align-items: flex-end; +} + +.invite-hours-field { + margin: 0; +} + +.invite-hours-field > span { + margin-bottom: 6px; + color: #5f6f85; + font-size: 12px; + font-weight: 600; +} + +.trusted-cell { + display: inline-flex; + align-items: center; + gap: 6px; +} + +.device-status-pill { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 58px; + height: 26px; + padding: 0 10px; + border-radius: 999px; + font-size: 12px; + font-weight: 700; + line-height: 1; +} + +.device-status-pill.online { + background: #dcfce7; + color: #166534; +} + +.device-status-pill.offline { + background: #e2e8f0; + color: #475569; +} diff --git a/webapp/src/styles/motion.css b/webapp/src/styles/motion.css new file mode 100644 index 0000000..ab6299c --- /dev/null +++ b/webapp/src/styles/motion.css @@ -0,0 +1,127 @@ +@keyframes toast-life { + from { + transform: scaleX(1); + transform-origin: left center; + } + to { + transform: scaleX(0); + transform-origin: left center; + } +} + +@keyframes fade-in { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes fade-in-up { + from { + opacity: 0; + transform: translate3d(0, 16px, 0); + } + to { + opacity: 1; + transform: translate3d(0, 0, 0); + } +} + +@keyframes shell-enter { + from { + opacity: 0; + transform: translate3d(0, 18px, 0) scale(0.992); + } + to { + opacity: 1; + transform: translate3d(0, 0, 0) scale(1); + } +} + +@keyframes surface-enter { + from { + opacity: 0; + transform: translate3d(0, 20px, 0) scale(0.985); + } + to { + opacity: 1; + transform: translate3d(0, 0, 0) scale(1); + } +} + +@keyframes menu-in { + from { + opacity: 0; + transform: translate3d(0, 10px, 0) scale(0.96); + } + to { + opacity: 1; + transform: translate3d(0, 0, 0) scale(1); + } +} + +@keyframes dialog-in { + from { + opacity: 0; + transform: translate3d(0, 18px, 0) scale(0.96); + } + to { + opacity: 1; + transform: translate3d(0, 0, 0) scale(1); + } +} + +@keyframes toast-in { + from { + opacity: 0; + transform: translate3d(18px, 0, 0) scale(0.97); + } + to { + opacity: 1; + transform: translate3d(0, 0, 0) scale(1); + } +} + +@keyframes stagger-rise { + from { + opacity: 0; + transform: translate3d(0, 18px, 0) scale(0.985); + } + to { + opacity: 1; + transform: translate3d(0, 0, 0) scale(1); + } +} + +@keyframes fade-out { + from { + opacity: 1; + } + to { + opacity: 0; + } +} + +@keyframes dialog-out { + from { + opacity: 1; + transform: translate3d(0, 0, 0) scale(1); + } + to { + opacity: 0; + transform: translate3d(0, 10px, 0) scale(0.972); + } +} + +@keyframes route-stage-in { + from { + opacity: 0; + transform: translate3d(0, 14px, 0); + } + to { + opacity: 1; + transform: translate3d(0, 0, 0); + } +} diff --git a/webapp/src/styles/overlays.css b/webapp/src/styles/overlays.css new file mode 100644 index 0000000..e1f8dbf --- /dev/null +++ b/webapp/src/styles/overlays.css @@ -0,0 +1,299 @@ +.dialog-mask { + position: fixed; + inset: 0; + width: 100vw; + height: 100dvh; + background: rgba(15, 23, 42, 0.5); + display: grid; + place-items: center; + z-index: 1200; + padding: 20px; + opacity: 0; + animation: fade-in var(--dur-medium) var(--ease-smooth) both; + backdrop-filter: blur(6px); + -webkit-backdrop-filter: blur(6px); +} + +.dialog-card { + width: min(460px, 100%); + background: #fff; + border-radius: 20px; + border: 1px solid var(--line); + box-shadow: 0 20px 50px rgba(15, 23, 42, 0.2); + padding: 20px; + text-align: center; + transform-origin: 50% 30%; + animation: dialog-in 240ms var(--ease-out-strong) both; +} + +.dialog-mask.warning { + background: + radial-gradient(circle at top, rgba(255, 237, 213, 0.32), transparent 34%), + linear-gradient(180deg, rgba(127, 29, 29, 0.36), rgba(15, 23, 42, 0.72)); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); +} + +.dialog-card.warning { + width: min(520px, 100%); + border: 1px solid rgba(220, 38, 38, 0.22); + background: + linear-gradient(180deg, rgba(255, 246, 246, 0.98), rgba(255, 255, 255, 0.99)); + box-shadow: + 0 36px 90px rgba(69, 10, 10, 0.28), + 0 0 0 1px rgba(255, 255, 255, 0.7) inset; +} + +.dialog-warning-head { + display: flex; + align-items: center; + justify-content: center; + gap: 12px; + margin-bottom: 8px; +} + +.dialog-warning-badge { + width: 48px; + height: 48px; + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 16px; + background: linear-gradient(180deg, #fff1f2, #ffe4e6); + color: #dc2626; + box-shadow: + 0 12px 30px rgba(220, 38, 38, 0.18), + 0 0 0 1px rgba(220, 38, 38, 0.08) inset; +} + +.dialog-warning-kicker { + font-size: 12px; + font-weight: 800; + letter-spacing: 0.16em; + text-transform: uppercase; + color: #b91c1c; +} + +.dialog-mask.closing { + animation: fade-out 220ms var(--ease-smooth) both; +} + +.dialog-card.closing { + animation: dialog-out 220ms var(--ease-smooth) both; +} + +.dialog-card .field { + text-align: left; +} + +.dialog-title { + margin: 6px 0; + font-size: 30px; +} + +.dialog-message { + color: #475467; + margin-bottom: 10px; +} + +.dialog-card.warning .dialog-title { + color: #7f1d1d; + margin-bottom: 10px; +} + +.dialog-message.warning { + margin-bottom: 16px; + padding: 14px 16px; + border-radius: 16px; + border: 1px solid rgba(220, 38, 38, 0.16); + background: linear-gradient(180deg, rgba(255, 241, 242, 0.94), rgba(255, 247, 237, 0.9)); + color: #7a2832; + line-height: 1.65; + box-shadow: 0 10px 28px rgba(248, 113, 113, 0.08) inset; +} + +.dialog-btn { + width: 100%; + height: 50px; + font-size: 20px; + margin-top: 8px; +} + +.dialog-extra { + margin-top: 8px; +} + +.dialog-divider { + height: 1px; + background: var(--line); + margin: 8px 0 10px; +} + +.import-summary-dialog { + max-width: 520px; + text-align: left; + position: relative; + padding-top: 16px; +} + +.import-summary-close { + position: absolute; + top: 10px; + right: 10px; + border: none; + background: transparent; + color: #64748b; + font-size: 24px; + line-height: 1; + cursor: pointer; +} + +.import-summary-close:hover { + color: #0f172a; +} + +.import-summary-table-wrap { + margin-top: 8px; + border: 1px solid var(--line); + border-radius: 10px; + overflow: hidden; +} + +.import-summary-table { + width: 100%; + border-collapse: collapse; + font-size: 14px; +} + +.import-summary-table th, +.import-summary-table td { + padding: 10px 12px; + border-bottom: 1px solid var(--line); +} + +.import-summary-table th { + text-align: left; + color: #475467; + background: #f8fafc; +} + +.import-summary-table td:last-child, +.import-summary-table th:last-child { + text-align: right; + width: 96px; +} + +.import-summary-table tbody tr:last-child td { + border-bottom: none; +} + +.import-summary-failed-list { + margin-top: 10px; + padding: 10px 12px; + border: 1px solid #fecaca; + border-radius: 10px; + background: #fef2f2; + color: #991b1b; + font-size: 13px; +} + +.import-summary-failed-title { + font-weight: 700; + margin-bottom: 6px; +} + +.import-summary-failed-list ul { + margin: 0; + padding-left: 18px; +} + +.import-summary-failed-list li + li { + margin-top: 4px; +} + +.settings-twofactor-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 12px; +} + +.settings-subcard { + border: 1px solid var(--line); + border-radius: 12px; + padding: 12px; + background: #fff; +} + +.settings-subcard h3 { + margin-top: 0; + margin-bottom: 10px; +} + +.toast-stack { + position: fixed; + top: 16px; + right: 16px; + z-index: 1400; + width: min(420px, calc(100vw - 20px)); + list-style: none; + margin: 0; + padding: 0; + display: grid; + gap: 10px; +} + +.toast-item { + position: relative; + border-radius: 10px; + border: 1px solid #bbdfc6; + background: #dff4e5; + color: #0f5132; + padding: 12px 14px; + box-shadow: 0 10px 24px rgba(15, 23, 42, 0.12); + overflow: hidden; + display: flex; + justify-content: space-between; + align-items: center; + animation: toast-in 240ms var(--ease-out-strong) both; +} + +.toast-item.error { + border-color: #f2b8c1; + background: #fde7eb; + color: #9f1239; +} + +.toast-item.warning { + border-color: #f2b8c1; + background: #fde7eb; + color: #9f1239; +} + +.toast-text { + font-weight: 700; + padding-right: 10px; +} + +.toast-close { + border: none; + background: transparent; + cursor: pointer; + font-size: 20px; + color: inherit; + transition: transform var(--dur-fast) var(--ease-out-soft), opacity var(--dur-fast) var(--ease-smooth); +} + +.toast-close:hover { + transform: scale(1.08); + opacity: 0.84; +} + +.toast-progress { + position: absolute; + left: 0; + bottom: 0; + width: 100%; + height: 3px; + background: rgba(15, 23, 42, 0.2); + animation: toast-life 4.5s linear forwards; +} diff --git a/webapp/src/styles/reduced-motion.css b/webapp/src/styles/reduced-motion.css new file mode 100644 index 0000000..dc2ddb6 --- /dev/null +++ b/webapp/src/styles/reduced-motion.css @@ -0,0 +1,32 @@ +@media (prefers-reduced-motion: reduce) { + html { + scroll-behavior: auto; + } + + *, + *::before, + *::after { + animation-duration: 1ms !important; + animation-iteration-count: 1 !important; + transition-duration: 1ms !important; + scroll-behavior: auto !important; + } + + .btn:hover:not(:disabled), + .btn:active:not(:disabled), + .side-link:hover, + .tree-btn:hover, + .list-item:hover, + .list-item.active, + .search-input:focus, + .input:focus, + .password-toggle:hover, + .eye-btn:hover, + .auth-link-btn:hover, + .sort-menu-item:hover, + .create-menu-item:hover, + .toast-close:hover, + .mobile-sidebar-close:hover { + transform: none !important; + } +} diff --git a/webapp/src/styles/responsive.css b/webapp/src/styles/responsive.css new file mode 100644 index 0000000..32119fa --- /dev/null +++ b/webapp/src/styles/responsive.css @@ -0,0 +1,790 @@ +@media (max-width: 1180px) { + .app-page { + padding: 8px; + } + + .app-shell { + height: calc(100vh - 16px); + border-radius: 12px; + } + + .app-main { + grid-template-columns: 1fr; + } + + .app-side { + border-right: none; + border-bottom: 1px solid #d9e0ea; + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + align-items: start; + align-self: start; + height: fit-content; + gap: 8px; + } + + .app-side > .side-link { + min-height: 0; + } + + .vault-grid { + grid-template-columns: 1fr; + height: auto; + } + .sidebar { + max-height: 280px; + } + .totp-grid, + .field-grid { + grid-template-columns: 1fr; + } + + .totp-copy-btn { + justify-self: start; + } + + .import-export-panels, + .backup-browser-row { + grid-template-columns: 1fr; + } + + .settings-twofactor-grid { + grid-template-columns: 1fr; + } + + .standalone-title { + font-size: 24px; + } + + .standalone-footer { + font-size: 12px; + line-height: 1.4; + } +} + +@media (max-width: 900px) { + .auth-page { + padding: 14px; + align-items: start; + } + + .standalone-shell { + width: 100%; + max-width: 460px; + gap: 10px; + padding-top: 12px; + } + + .standalone-brand-outside { + justify-content: flex-start; + } + + .standalone-brand-logo { + width: 44px; + height: 44px; + } + + .auth-card { + padding: 20px 16px; + border-radius: 18px; + } + + .btn.full { + height: 48px; + font-size: 18px; + } + + .auth-support-row { + align-items: center; + flex-direction: row; + } + + .app-page { + padding: 0; + background: transparent; + } + + .app-shell { + --mobile-topbar-height: 58px; + --mobile-tabbar-height: 70px; + height: 100dvh; + max-width: none; + border: none; + border-radius: 0; + box-shadow: none; + } + + .topbar { + height: var(--mobile-topbar-height); + padding: 0 12px; + position: relative; + z-index: 20; + } + + .brand { + min-width: 0; + gap: 10px; + font-size: 18px; + } + + .brand-logo { + width: 34px; + height: 34px; + } + + .mobile-page-title { + display: inline; + } + + .topbar-actions .user-chip, + .topbar-actions > .btn:not(.mobile-sidebar-toggle):not(.mobile-lock-btn), + .topbar-actions > .theme-switch-wrap { + display: none; + } + + .mobile-sidebar-toggle, + .mobile-lock-btn { + display: inline-flex; + width: 36px; + min-width: 36px; + height: 36px; + padding: 0; + justify-content: center; + font-size: 0; + gap: 0; + } + + .mobile-sidebar-toggle .btn-icon, + .mobile-lock-btn .btn-icon { + margin: 0; + } + + .mobile-theme-btn { + display: inline-flex; + align-items: center; + } + + .mobile-theme-btn .theme-switch { + transform: scale(0.8); + transform-origin: center; + } + + .app-main { + display: flex; + flex-direction: column; + min-height: 0; + } + + .app-side { + display: none; + } + + .content { + flex: 1; + min-height: 0; + -webkit-overflow-scrolling: touch; + } + + .mobile-tabbar { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + align-items: center; + gap: 6px; + min-height: var(--mobile-tabbar-height); + padding: 8px 10px calc(8px + env(safe-area-inset-bottom)); + border-top: 1px solid var(--line); + background: rgba(248, 251, 255, 0.92); + } + + .mobile-tab { + display: grid; + justify-items: center; + gap: 4px; + color: #64748b; + text-decoration: none; + font-size: 11px; + font-weight: 700; + padding: 6px 4px; + border-radius: 12px; + transition: + transform 220ms var(--ease-out-soft), + background-color var(--dur-fast) var(--ease-smooth), + color var(--dur-fast) var(--ease-smooth); + } + + .mobile-tab:hover { + transform: translate3d(var(--mag-x), calc(var(--mag-y) - 1px), 0); + } + + .mobile-tab.active { + color: var(--primary-strong); + background: linear-gradient(135deg, rgba(37, 99, 235, 0.16), rgba(59, 130, 246, 0.08)); + box-shadow: 0 8px 18px rgba(37, 99, 235, 0.08); + } + + .vault-grid { + gap: 10px; + padding: 0; + } + + .sidebar { + display: none; + } + + .mobile-sidebar-sheet { + display: block; + position: fixed; + left: 10px; + right: 10px; + top: calc(var(--mobile-topbar-height) + 10px); + bottom: auto; + max-height: calc(100dvh - 145px); + z-index: 55; + overflow: auto; + border: 1px solid #d8dee8; + border-radius: 18px; + background: #fff; + padding: 12px; + box-shadow: 0 18px 40px rgba(15, 23, 42, 0.16); + opacity: 0; + visibility: hidden; + pointer-events: none; + transform: translate3d(0, 10px, 0) scale(0.98); + transition: + opacity 220ms var(--ease-smooth), + transform 240ms var(--ease-out-soft), + visibility 220ms var(--ease-smooth); + } + + .mobile-sidebar-sheet.open { + opacity: 1; + visibility: visible; + pointer-events: auto; + transform: translate3d(0, 0, 0) scale(1); + } + + .mobile-sidebar-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + margin-bottom: 10px; + } + + .mobile-sidebar-title { + font-size: 16px; + font-weight: 800; + color: #0f172a; + } + + .mobile-sidebar-close { + width: 34px; + height: 34px; + border: 1px solid #d7dde6; + border-radius: 999px; + background: #fff; + color: #0f172a; + display: inline-grid; + place-items: center; + cursor: pointer; + padding: 0; + transition: + transform var(--dur-fast) var(--ease-out-soft), + background-color var(--dur-fast) var(--ease-smooth), + border-color var(--dur-fast) var(--ease-smooth); + } + + .mobile-sidebar-close:hover { + transform: scale(1.05); + } + + .mobile-sidebar-sheet .sidebar-block { + margin: 0; + padding: 0; + border: none; + border-radius: 0; + background: transparent; + box-shadow: none; + } + + .mobile-sidebar-sheet .tree-btn { + margin-bottom: 2px; + } + + .mobile-sidebar-sheet .folder-row { + align-items: stretch; + gap: 4px; + } + + .mobile-sidebar-sheet .folder-row .tree-btn { + min-height: 42px; + } + + .mobile-sidebar-sheet .sidebar-title, + .mobile-sidebar-sheet .sidebar-title-row { + padding-bottom: 6px; + margin-bottom: 0; + } + + .mobile-sidebar-sheet .tree-btn { + padding-left: 8px; + padding-right: 8px; + border-radius: 10px; + } + + .mobile-sidebar-sheet .tree-btn.active { + background: #eef4ff; + } + + .mobile-sidebar-sheet .folder-delete-btn { + width: 28px; + height: 42px; + border-radius: 8px; + } + + .list-col { + max-width: none; + } + + .list-head { + display: grid; + grid-template-columns: minmax(0, 1fr) auto auto auto; + gap: 8px; + align-items: center; + } + + .list-count { + grid-column: auto; + width: auto; + font-size: 12px; + white-space: nowrap; + } + + .list-head .search-input-wrap { + width: 100%; + min-width: 0; + } + + .list-head .search-input { + width: 100%; + min-width: 0; + height: 42px; + border-radius: 14px; + } + + .list-icon-btn { + width: auto; + min-width: 0; + padding: 0 12px; + font-size: 13px; + gap: 6px; + white-space: nowrap; + } + + .toolbar.actions { + justify-content: flex-end; + flex-wrap: unset; + gap: var(--actions-gap); + overflow: visible; + padding-bottom: 2px; + } + + .actions { + gap: var(--actions-gap); + } + + .toolbar.actions .btn.small { + width: auto; + min-width: 0; + height: 34px; + padding: 0 12px; + font-size: 13px; + gap: 6px; + border-radius: 999px; + white-space: nowrap; + } + + .mobile-fab-wrap { + position: fixed; + right: 14px; + bottom: calc(14px + var(--mobile-tabbar-height) + env(safe-area-inset-bottom)); + z-index: 45; + } + + .mobile-fab-trigger { + width: 36px; + height: 56px; + padding: 0; + border-radius: 999px; + font-size: 0; + gap: 0; + box-shadow: 0 14px 30px rgba(37, 99, 235, 0.28); + } + + .mobile-fab-trigger .btn-icon { + margin: 0; + width: 20px; + height: 20px; + } + + .mobile-fab-wrap .create-menu { + left: auto; + right: 0; + top: auto; + bottom: calc(100% + 10px); + } + + .list-panel { + border-radius: 16px; + overflow: visible; + } + + .list-item { + padding: 12px; + border-radius: 14px; + } + + .row-check { + width: 18px; + height: 18px; + } + + .vault-grid.mobile-panel-detail .sidebar, + .vault-grid.mobile-panel-detail .list-col, + .vault-grid.mobile-panel-edit .sidebar, + .vault-grid.mobile-panel-edit .list-col { + display: none; + } + + .mobile-detail-sheet { + display: block; + position: fixed; + left: 0; + right: 0; + top: calc(var(--mobile-topbar-height) + env(safe-area-inset-top)); + bottom: calc(var(--mobile-tabbar-height) + env(safe-area-inset-bottom)); + z-index: 35; + overflow: auto; + background: transparent; + padding: 0 0 18px; + opacity: 0; + visibility: hidden; + pointer-events: none; + transform: translate3d(0, 18px, 0); + transition: + opacity 220ms var(--ease-smooth), + transform 260ms var(--ease-out-soft), + visibility 220ms var(--ease-smooth); + } + + .mobile-detail-sheet.open { + opacity: 1; + visibility: visible; + pointer-events: auto; + transform: translate3d(0, 0, 0); + } + + .mobile-panel-head { + display: flex; + align-items: center; + margin: 0 10px 10px; + } + + .mobile-panel-back { + min-height: 40px; + } + + .mobile-detail-sheet > .detail-switch-stage, + .mobile-detail-sheet > .card, + .mobile-detail-sheet > .empty { + margin-left: 10px; + margin-right: 10px; + } + + .detail-col .card, + .import-export-panel, + .settings-subcard { + border-radius: 16px; + } + + .card { + padding: 14px 14px; + } + + .section-head { + align-items: flex-start; + gap: 10px; + flex-direction: column; + } + + .detail-actions { + flex-direction: column; + align-items: stretch; + gap: 10px; + } + + .detail-actions .actions { + width: 100%; + } + + .detail-actions .actions .btn, + .detail-delete-btn { + width: 100%; + } + + .kv-row { + grid-template-columns: minmax(64px, 80px) minmax(0, 1fr) auto; + align-items: center; + } + + .kv-line { + flex-direction: row; + align-items: center; + justify-content: space-between; + } + + .kv-actions { + width: auto; + justify-content: flex-end; + flex-wrap: nowrap; + } + + .kv-actions .btn.small { + width: 34px; + min-width: 34px; + height: 34px; + padding: 0; + font-size: 0; + gap: 0; + border-radius: 999px; + } + + .kv-actions .btn.small .btn-icon { + margin: 0; + } + + .import-export-panels, + .settings-twofactor-grid { + gap: 10px; + } + + .import-export-panel .actions .btn, + .settings-subcard .actions .btn, + .section-head .actions .btn { + width: 100%; + } + + .totp-grid { + gap: 10px; + } + + .totp-qr { + min-height: 180px; + } + + .totp-qr svg, + .totp-qr img { + width: 160px; + height: 160px; + } + + .invite-toolbar { + align-items: stretch; + } + + .mobile-settings-card { + min-height: calc(100dvh - 170px); + display: flex; + flex-direction: column; + gap: 12px; + } + + .mobile-settings-subhead { + display: flex; + align-items: center; + } + + .mobile-settings-back { + min-height: 38px; + } + + .mobile-settings-links { + display: grid; + gap: 8px; + align-content: start; + } + + .mobile-settings-link { + display: flex; + align-items: center; + gap: 10px; + min-height: 46px; + padding: 0 12px; + border: 1px solid #dbe2ec; + border-radius: 14px; + background: #f8fafc; + color: #0f172a; + text-decoration: none; + font-weight: 700; + } + + .mobile-settings-link.active { + background: #e8f0ff; + border-color: #b9cff6; + color: #175ddc; + } + + .mobile-settings-logout { + width: 100%; + margin-top: auto; + } + + .stack, + .import-export-page, + .totp-codes-page, + .detail-col { + min-height: auto; + } + + .invite-create-group { + align-items: stretch; + width: 100%; + } + + .input.small { + width: 100%; + } + + .table, + .table tbody, + .table tr, + .table td { + display: block; + width: 100%; + } + + .table thead { + display: none; + } + + .table tr { + border: 1px solid var(--line); + border-radius: 14px; + background: #fff; + padding: 10px 12px; + margin-bottom: 10px; + } + + .table td { + border-bottom: 1px solid #edf1f6; + padding: 10px 0; + } + + .table td:last-child { + border-bottom: none; + padding-bottom: 0; + } + + .table td::before { + display: block; + content: attr(data-label); + margin-bottom: 4px; + color: #64748b; + font-size: 12px; + font-weight: 700; + } + + .dialog-mask { + align-items: center; + justify-items: center; + padding: 16px; + } + + .dialog-card { + width: 90%; + max-width: 460px; + max-height: calc(100dvh - 10px); + overflow: auto; + border-radius: 22px; + padding: 18px 16px calc(18px + env(safe-area-inset-bottom)); + } + + .dialog-card.warning { + max-width: 520px; + } + + .dialog-warning-strip { + margin: -18px -16px 16px; + } + + .dialog-title { + font-size: 24px; + } + + .dialog-btn { + height: 46px; + font-size: 16px; + } + +.toast-stack { + top: 10px; + left: 10px; + right: 10px; + width: auto; +} +} + +@media (max-width: 900px) { + .backup-grid { + grid-template-columns: 1fr; + } + + .backup-operations-sidebar, + .backup-destination-sidebar { + position: static; + } +} + +@media (max-width: 640px) { + .backup-interval-row { + grid-template-columns: 1fr; + } + + .backup-browser-row, + .field-grid { + grid-template-columns: 1fr; + } + + .backup-destination-top { + align-items: flex-start; + flex-direction: column; + } + + .backup-add-chooser { + flex-direction: column; + } + + .backup-name-row { + grid-template-columns: 1fr; + } + + .backup-option-field { + align-items: flex-start; + } + + .backup-help-bubble { + left: 0; + transform: translate(0, -4px); + } + + .backup-help-bubble::before { + left: 16px; + transform: rotate(45deg); + } + + .backup-help-wrap:hover .backup-help-bubble, + .backup-help-wrap:focus-within .backup-help-bubble, + .backup-help-wrap.open .backup-help-bubble { + transform: translate(0, 0); + } +} diff --git a/webapp/src/styles/shell.css b/webapp/src/styles/shell.css new file mode 100644 index 0000000..da3271e --- /dev/null +++ b/webapp/src/styles/shell.css @@ -0,0 +1,321 @@ +.app-page { + min-height: 100%; + padding: 20px; + position: relative; + background: transparent; +} + +.app-shell { + height: calc(100vh - 40px); + max-width: 1600px; + margin: 0 auto; + position: relative; + background: var(--panel-soft); + border: 1px solid var(--line); + border-radius: 28px; + box-shadow: var(--shadow-lg); + display: flex; + flex-direction: column; + overflow: hidden; + animation: shell-enter 560ms var(--ease-out-strong) both; +} + +.topbar { + height: 58px; + border-bottom: 1px solid var(--line-soft); + color: #0f172a; + background: rgba(244, 248, 255, 0.72); + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 18px; + transition: background-color var(--dur-fast) var(--ease-smooth), border-color var(--dur-fast) var(--ease-smooth); +} + +.brand { + display: inline-flex; + align-items: center; + gap: 8px; + font-size: 34px; + font-weight: 800; + color: var(--text); +} + +.brand-wordmark { + display: block; + height: auto; + width: clamp(210px, 20vw, 290px); + max-width: 100%; + filter: drop-shadow(0 12px 24px rgba(43, 102, 217, 0.12)); +} + +.mobile-page-title { + display: none; + min-width: 0; + max-width: min(58vw, 240px); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 19px; + line-height: 1.2; + font-weight: 800; + color: #0f172a; +} + +.brand-logo { + width: 42px; + height: 42px; + object-fit: contain; + filter: drop-shadow(0 10px 22px rgba(43, 102, 217, 0.22)); + transition: transform var(--dur-medium) var(--ease-out-soft), filter var(--dur-medium) var(--ease-out-soft); +} + +.topbar-actions { + display: flex; + align-items: center; + gap: 10px; +} + +.mobile-tabbar { + display: none; +} + +.mobile-sidebar-toggle { + display: none; +} + +.mobile-lock-btn { + display: none; +} + +.mobile-theme-btn { + display: none; +} + +.theme-switch-wrap { + display: inline-flex; + align-items: center; + justify-content: center; +} + +.theme-switch { + position: relative; + display: inline-block; + width: 56px; + height: 32px; +} + +.theme-switch-input { + opacity: 0; + width: 0; + height: 0; +} + +.theme-switch-slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(180deg, #dceaff, #c8dcff); + border: 1px solid #9dbbec; + transition: + background var(--dur-medium) var(--ease-out-soft), + border-color var(--dur-medium) var(--ease-smooth), + box-shadow var(--dur-fast) var(--ease-out-soft), + transform var(--dur-fast) var(--ease-out-soft); + border-radius: 999px; +} + +.theme-switch-slider::before { + position: absolute; + content: ''; + height: 26px; + width: 26px; + border-radius: 999px; + left: 2px; + bottom: 2px; + z-index: 2; + background: linear-gradient(180deg, #ffffff, #edf4ff); + box-shadow: 0 2px 8px rgba(15, 23, 42, 0.14); + transition: + transform var(--dur-medium) var(--ease-out-strong), + box-shadow var(--dur-fast) var(--ease-out-soft), + background var(--dur-fast) var(--ease-smooth); +} + +.theme-switch .sun svg { + position: absolute; + top: 6px; + left: 32px; + z-index: 1; + width: 18px; + height: 18px; + opacity: 0.95; + transition: transform var(--dur-medium) var(--ease-out-soft), opacity var(--dur-fast) var(--ease-smooth); +} + +.theme-switch .moon svg { + fill: #5b86d6; + position: absolute; + top: 7px; + left: 7px; + z-index: 1; + width: 16px; + height: 16px; + opacity: 0.88; + transition: transform var(--dur-medium) var(--ease-out-soft), opacity var(--dur-fast) var(--ease-smooth); +} + +.theme-switch-input:checked + .theme-switch-slider { + background: linear-gradient(180deg, #173150, #122742); + border-color: #35527a; +} + +.theme-switch-input:focus + .theme-switch-slider { + box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.22); +} + +.theme-switch-input:checked + .theme-switch-slider::before { + transform: translateX(24px); +} + +.theme-switch:hover .theme-switch-slider { + transform: scale(1.02); +} + +.theme-switch:hover .sun svg, +.theme-switch:hover .moon svg { + transform: scale(1.08); +} + +.topbar-actions .btn { + height: 34px; + border-radius: 12px; + padding: 0 12px; + font-size: 13px; + font-weight: 600; + transform: translate3d(var(--mag-x), var(--mag-y), 0); + transition-duration: 220ms; +} + +.topbar-actions .btn:hover:not(:disabled) { + transform: translate3d(var(--mag-x), calc(var(--mag-y) - 2px), 0) scale(1.02); +} + +.user-chip { + display: inline-flex; + align-items: center; + gap: 6px; + height: 34px; + border-radius: 999px; + padding: 0 12px; + border: 1px solid rgba(148, 163, 184, 0.3); + background: rgba(249, 251, 255, 0.92); + color: var(--muted-strong); + font-size: 14px; + font-weight: 600; + box-shadow: 0 10px 18px rgba(13, 31, 68, 0.05); + transform: translate3d(var(--mag-x), var(--mag-y), 0); + transition: + transform 220ms var(--ease-out-soft), + box-shadow var(--dur-fast) var(--ease-out-soft), + border-color var(--dur-fast) var(--ease-smooth), + background-color var(--dur-fast) var(--ease-smooth); +} + +.user-chip:hover { + transform: translate3d(var(--mag-x), calc(var(--mag-y) - 1px), 0); + box-shadow: 0 16px 28px rgba(15, 23, 42, 0.08); +} + +.app-main { + flex: 1; + min-height: 0; + display: grid; + grid-template-columns: 200px 1fr; +} + +.app-side { + border-right: 1px solid var(--line-soft); + padding: 16px 12px; + display: flex; + flex-direction: column; + gap: 8px; +} + +.side-link { + display: flex; + align-items: center; + gap: 10px; + padding: 11px 12px; + border-radius: 14px; + color: var(--muted-strong); + text-decoration: none; + border: 1px solid transparent; + font-weight: 600; + font-size: 14px; + transition: + background-color var(--dur-fast) var(--ease-smooth), + border-color var(--dur-fast) var(--ease-smooth), + color var(--dur-fast) var(--ease-smooth), + transform var(--dur-fast) var(--ease-out-soft), + box-shadow var(--dur-fast) var(--ease-out-soft); +} + +.side-link:hover { + background: #ffffff; + border-color: rgba(128, 152, 192, 0.18); + color: var(--text); + transform: translate3d(calc(var(--mag-x) + 3px), var(--mag-y), 0); + box-shadow: 0 14px 24px rgba(15, 23, 42, 0.05); +} + +.side-link.active { + background: linear-gradient(135deg, rgba(37, 99, 235, 0.18), rgba(59, 130, 246, 0.08)); + border-color: rgba(37, 99, 235, 0.28); + color: var(--primary-strong); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.64), 0 10px 18px rgba(37, 99, 235, 0.1); +} + +.content { + min-height: 0; + padding: 14px; + overflow: hidden; +} + +.route-stage { + height: 100%; + min-height: 0; + overflow: auto; +} + +@media (min-width: 901px) { + .route-stage { + animation: route-stage-in 240ms var(--ease-out-soft) both; + } +} + +.mobile-sidebar-mask { + position: fixed; + inset: 0; + background: rgba(15, 23, 42, 0.36); + z-index: 54; + opacity: 0; + visibility: hidden; + pointer-events: none; + transition: + opacity 220ms var(--ease-smooth), + visibility 220ms var(--ease-smooth); +} + +.mobile-sidebar-mask.open { + opacity: 1; + visibility: visible; + pointer-events: auto; +} + +.mobile-sidebar-head { + display: none; +} diff --git a/webapp/src/styles/tokens.css b/webapp/src/styles/tokens.css new file mode 100644 index 0000000..138a646 --- /dev/null +++ b/webapp/src/styles/tokens.css @@ -0,0 +1,46 @@ +:root { + --bg-accent: #e7edf8; + --panel: #f9fbff; + --panel-soft: #f2f6fd; + --panel-muted: #e8eff9; + --line: rgba(128, 152, 192, 0.32); + --line-soft: rgba(143, 167, 206, 0.18); + --text: #0b1730; + --muted: #60708b; + --muted-strong: #334765; + --primary: #2563eb; + --primary-hover: #1d4ed8; + --primary-strong: #0f3f98; + --danger: #d92d57; + --overlay-strong: rgba(15, 23, 42, 0.56); + --shadow-sm: 0 10px 22px rgba(13, 31, 68, 0.045); + --shadow-md: 0 22px 48px rgba(13, 31, 68, 0.08); + --shadow-lg: 0 28px 76px rgba(13, 31, 68, 0.11); + --ease-out-strong: cubic-bezier(0.22, 1, 0.36, 1); + --ease-out-soft: cubic-bezier(0.24, 0.8, 0.32, 1); + --ease-smooth: cubic-bezier(0.4, 0, 0.2, 1); + --dur-fast: 180ms; + --dur-medium: 240ms; + --dur-panel: 280ms; + --actions-gap: clamp(0px, calc((100vw - 520px) * 1), 10px); +} + +:root[data-theme='dark'] { + --bg-accent: #06111d; + --panel: #0d192b; + --panel-soft: #112136; + --panel-muted: #0a1626; + --line: rgba(108, 141, 190, 0.28); + --line-soft: rgba(120, 152, 198, 0.16); + --text: #edf4ff; + --muted: #8fa6c6; + --muted-strong: #c3d5ef; + --primary: #84b6ff; + --primary-hover: #a6ccff; + --primary-strong: #f3f8ff; + --danger: #ff8ba8; + --overlay-strong: rgba(2, 8, 20, 0.84); + --shadow-sm: 0 14px 28px rgba(1, 7, 18, 0.24); + --shadow-md: 0 24px 52px rgba(1, 7, 18, 0.36); + --shadow-lg: 0 34px 88px rgba(1, 7, 18, 0.46); +} diff --git a/webapp/src/styles/vault.css b/webapp/src/styles/vault.css new file mode 100644 index 0000000..88972bc --- /dev/null +++ b/webapp/src/styles/vault.css @@ -0,0 +1,1097 @@ +.vault-grid { + display: grid; + grid-template-columns: 240px minmax(420px, 46%) minmax(575px, 1fr); + gap: 12px; + height: 100%; + min-height: 0; + padding: 2px; +} + +.sidebar, +.list-panel, +.card { + background: var(--panel); + border: 1px solid var(--line); + border-radius: 18px; + box-shadow: var(--shadow-sm); +} + +.sidebar { + padding: 0; + overflow: auto; + border: none; + box-shadow: none; + background: transparent; +} + +.sidebar-block { + border: 1px solid var(--line); + border-radius: 19px; + padding: 12px; + margin-bottom: 8px; + background: var(--panel); +} + +.sidebar-title { + font-size: 13px; + font-weight: 700; + color: #344054; + margin-bottom: 8px; +} + +.sidebar-title-row { + display: flex; + align-items: center; + justify-content: space-between; + padding-bottom: 8px; +} + +.sidebar-title-row .sidebar-title { + margin-bottom: 0; +} + +.folder-title-actions { + display: inline-flex; + align-items: center; + gap: 8px; +} + +.folder-add-btn { + border: none; + background: transparent; + color: #334155; + display: inline-flex; + align-items: center; + justify-content: center; + cursor: pointer; + padding: 0; + line-height: 1; +} + +.folder-add-btn:hover { + color: #1d4ed8; +} + +.search-input { + width: 100%; + height: 48px; + border: 1px solid rgba(74, 103, 150, 0.42); + border-radius: 14px; + padding: 10px 14px; + font-size: 16px; + outline: none; + color: var(--text); + background: var(--panel); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.9); + transition: + border-color var(--dur-fast) var(--ease-smooth), + box-shadow var(--dur-fast) var(--ease-out-soft), + background-color var(--dur-fast) var(--ease-smooth), + transform var(--dur-fast) var(--ease-out-soft); +} + +.search-input-wrap { + position: relative; + flex: 1 1 auto; + min-width: 0; +} + +.search-input:focus { + border-color: rgba(43, 102, 217, 0.6); + background-color: #fbfdff; + box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.11), 0 10px 20px rgba(37, 99, 235, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.95); + transform: translateY(-1px); +} + +.search-input-wrap .search-input { + padding-right: 42px; +} + +.search-clear-btn { + position: absolute; + top: 50%; + right: 9px; + width: 22px; + height: 22px; + display: inline-flex; + align-items: center; + justify-content: center; + border: none; + border-radius: 999px; + background: rgba(148, 163, 184, 0.18); + color: var(--muted); + cursor: pointer; + transform: translateY(-50%); + transition: background-color var(--dur-fast) var(--ease-out-soft), color var(--dur-fast) var(--ease-out-soft), transform var(--dur-fast) var(--ease-out-soft); +} + +.search-clear-btn:hover { + background: rgba(59, 130, 246, 0.18); + color: var(--brand); + transform: translateY(-50%) scale(1.04); +} + +.search-clear-btn:focus-visible { + outline: none; + box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.16); +} + +.tree-btn { + width: 100%; + min-width: 0; + border: none; + background: transparent; + text-align: left; + border-radius: 8px; + padding: 8px 10px; + margin-bottom: 4px; + cursor: pointer; + display: flex; + align-items: center; + gap: 8px; + transition: + background-color var(--dur-fast) var(--ease-smooth), + color var(--dur-fast) var(--ease-smooth), + transform var(--dur-fast) var(--ease-out-soft), + box-shadow var(--dur-fast) var(--ease-out-soft); +} + +.tree-btn:hover { + transform: translateX(2px); +} + +.tree-btn.active { + background: rgba(37, 99, 235, 0.09); + color: var(--primary-strong); + font-weight: 700; +} + +.tree-icon { + flex-shrink: 0; +} + +.tree-label { + min-width: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.folder-row { + display: flex; + align-items: center; + gap: 6px; +} + +.folder-row .tree-btn { + margin-bottom: 0; +} + +.folder-delete-btn { + border: none; + background: transparent; + color: #64748b; + width: 24px; + height: 24px; + padding: 0; + cursor: pointer; + flex-shrink: 0; + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 6px; + transition: + color var(--dur-fast) var(--ease-smooth), + background-color var(--dur-fast) var(--ease-smooth), + transform var(--dur-fast) var(--ease-out-soft); +} + +.folder-delete-btn:hover { + color: #b91c1c; + background: #fee2e2; + transform: scale(1.06); +} + +.folder-edit-btn:hover { + color: #1d4ed8; + background: #dbeafe; +} + +.list-col { + display: flex; + flex-direction: column; + min-width: 0; + min-height: 0; + max-width: 540px; +} + +.toolbar { + margin: 0 0 8px 0; +} + +.toolbar.actions { + justify-content: flex-end; + gap: var(--actions-gap); +} + +.toolbar .btn.small { + height: 30px; + border-radius: 999px; + font-size: 12px; +} + +.list-head { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 8px; +} + +.list-head .search-input-wrap { + flex: 1 1 auto; + min-width: 0; +} + +.list-head .search-input { + height: 42px; +} + +.list-head .btn { + white-space: nowrap; +} + +.list-count { + flex: 0 0 auto; + color: var(--text-muted); + font-size: 12px; + white-space: nowrap; +} + +.list-icon-btn { + white-space: nowrap; +} + +.sort-menu-wrap { + position: relative; + flex: 0 0 auto; +} + +.sort-trigger { + min-width: 36px; + width: 36px; + padding: 0; + justify-content: center; + gap: 0; +} + +.sort-trigger.active { + background: #e9f1ff; + border-color: #a9c2ee; + color: #175ddc; +} + +.sort-menu { + position: absolute; + top: calc(100% + 6px); + right: 0; + z-index: 30; + min-width: 156px; + padding: 6px; + border: 1px solid var(--line); + border-radius: 16px; + background: var(--panel); + box-shadow: var(--shadow-md); + transform-origin: top right; + animation: menu-in 190ms var(--ease-out-strong) both; +} + +.sort-menu-item { + width: 100%; + border: none; + background: transparent; + border-radius: 10px; + padding: 9px 10px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + color: #0f172a; + font-size: 13px; + text-align: left; + cursor: pointer; + transition: + background-color var(--dur-fast) var(--ease-smooth), + color var(--dur-fast) var(--ease-smooth), + transform var(--dur-fast) var(--ease-out-soft); +} + +.sort-menu-item:hover { + background: #f2f7ff; + transform: translateX(2px); +} + +.sort-menu-item.active { + background: rgba(37, 99, 235, 0.1); + color: var(--primary-strong); + font-weight: 700; +} + +.sort-menu-check-placeholder { + width: 14px; + height: 14px; + flex: 0 0 14px; +} + +.list-panel { + overflow: auto; + min-height: 0; + padding: 8px; +} + +.list-item { + width: 100%; + background: rgba(249, 251, 255, 0.88); + border: 1px solid var(--line); + border-radius: 14px; + padding: 10px 12px; + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 8px; + min-height: 66px; + box-sizing: border-box; + position: relative; + cursor: pointer; + overflow: hidden; + transform-origin: 50% 50%; + transition: + background-color var(--dur-fast) var(--ease-smooth), + border-color var(--dur-fast) var(--ease-smooth), + box-shadow var(--dur-fast) var(--ease-out-soft), + transform var(--dur-fast) var(--ease-out-soft); + contain: paint; +} + +.list-item::before { + content: ''; + position: absolute; + inset: 0; + background: + linear-gradient(90deg, rgba(43, 102, 217, 0.06), transparent 24%, transparent 76%, rgba(14, 165, 233, 0.05)), + radial-gradient(circle at 18px 50%, rgba(255, 255, 255, 0.28), transparent 44%); + opacity: 0; + transition: + opacity var(--dur-fast) var(--ease-smooth), + transform 320ms var(--ease-out-soft); + transform: translateX(-8px); + pointer-events: none; +} + +.stagger-item { + opacity: 0; + animation: stagger-rise 520ms var(--ease-out-strong) both; +} + +.list-item:hover { + background: #fcfdff; + border-color: rgba(148, 163, 184, 0.26); + box-shadow: 0 16px 28px rgba(15, 23, 42, 0.06); + transform: translate3d(0, -2px, 0) scale(1.008); +} + +.list-item:hover::before { + opacity: 1; + transform: translateX(0); +} + +.list-item.active { + background: rgba(37, 99, 235, 0.1); + border-color: rgba(43, 102, 217, 0.26); + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.42), 0 14px 24px rgba(43, 102, 217, 0.08); + transform: translate3d(0, -1px, 0) scale(1.004); +} + +.list-item.active::before { + opacity: 1; + transform: translateX(0); +} + +.row-check { + width: 16px; + height: 16px; + position: relative; + z-index: 2; + cursor: pointer; +} + +.row-main { + flex: 1; + min-width: 0; + border: none; + background: transparent; + padding: 0; + display: flex; + align-items: center; + gap: 10px; + text-align: left; + cursor: pointer; + position: relative; + z-index: 1; + transition: transform 220ms var(--ease-out-soft); +} + +.list-item:hover .row-main, +.list-item.active .row-main { + transform: translateX(2px); +} + +.list-icon-wrap { + width: 24px; + height: 24px; + display: grid; + place-items: center; + flex-shrink: 0; + transition: transform 240ms var(--ease-out-soft), filter 240ms var(--ease-out-soft); +} + +.list-icon { + width: 24px; + height: 24px; + border-radius: 6px; +} + +.list-icon-fallback { + display: grid; + place-items: center; + color: #64748b; + transition: color var(--dur-fast) var(--ease-smooth), transform 240ms var(--ease-out-soft); +} + +.list-icon-fallback svg { + width: 24px; + height: 24px; +} + +.list-text { + flex: 1; + min-width: 0; + overflow: hidden; + transition: transform 220ms var(--ease-out-soft); +} + +.list-title { + display: flex; + align-items: center; + gap: 6px; + color: var(--primary-strong); + font-size: 15px; + font-weight: 700; + min-width: 0; + transition: color var(--dur-fast) var(--ease-smooth), letter-spacing 220ms var(--ease-out-soft); +} + +.list-title-text { + min-width: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.list-badge { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 2px 6px; + border-radius: 999px; + font-size: 11px; + font-weight: 700; + line-height: 1; + color: #475569; + background: #e8eef8; + flex-shrink: 0; +} + +.list-badge.danger { + color: #fff; + background: var(--danger); +} + +.list-sub { + display: block; + color: #5f6f85; + margin-top: 2px; + font-size: 13px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + transition: color var(--dur-fast) var(--ease-smooth), transform 220ms var(--ease-out-soft), opacity var(--dur-fast) var(--ease-smooth); +} + +.list-item:hover .list-icon-wrap, +.list-item.active .list-icon-wrap { + transform: translateX(1px) scale(1.04); +} + +.list-item:hover .list-icon-fallback, +.list-item.active .list-icon-fallback { + color: var(--primary-strong); + transform: scale(1.04); +} + +.list-item:hover .list-text, +.list-item.active .list-text { + transform: translateX(1px); +} + +.list-item:hover .list-title, +.list-item.active .list-title { + letter-spacing: -0.012em; +} + +.list-item:hover .list-sub, +.list-item.active .list-sub { + transform: translateX(1px); + opacity: 0.92; +} + +.detail-col { + overflow: auto; + min-height: 0; +} + +.mobile-panel-head { + display: none; +} + +.card { + padding: 16px 18px; + margin-bottom: 10px; +} + +.detail-col > .card { + opacity: 1; + animation: none; +} + +.detail-col > .card:nth-of-type(1) { animation-delay: 0ms; } +.detail-col > .card:nth-of-type(2) { animation-delay: 0ms; } +.detail-col > .card:nth-of-type(3) { animation-delay: 0ms; } +.detail-col > .card:nth-of-type(4) { animation-delay: 0ms; } +.detail-col > .card:nth-of-type(5) { animation-delay: 0ms; } + +.detail-switch-stage { + animation: none; +} + +.detail-switch-stage > .card { + opacity: 1; + animation: none; +} + +.card h4 { + margin-top: 0; + margin-bottom: 12px; +} + +.detail-title { + margin: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.detail-sub { + color: #667085; + margin-top: 8px; +} + +.password-history-link { + margin-top: 10px; + padding: 0; + border: none; + background: transparent; + color: var(--primary); + font: inherit; + font-weight: 700; + cursor: pointer; +} + +.password-history-link:hover { + color: var(--primary-hover); + text-decoration: underline; +} + +.kv-line { + display: flex; + justify-content: space-between; + align-items: center; + gap: 10px; + border-bottom: 1px solid rgba(154, 172, 205, 0.22); + padding: 10px 0; +} + +.kv-line:last-child { + border-bottom: none; +} + +.kv-line > span { + color: #64748b; +} + +.kv-row { + display: grid; + grid-template-columns: minmax(0px, 80px) minmax(0, 1fr) auto; + align-items: center; + gap: 10px; + border-bottom: 1px solid rgba(154, 172, 205, 0.22); + padding: 10px 0; +} + +.kv-row:last-child { + border-bottom: none; +} + +.password-history-dialog { + width: min(560px, calc(100vw - 32px)); +} + +.password-history-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + margin-bottom: 12px; +} + +.password-history-head .dialog-title { + margin: 0; +} + +.password-history-close { + display: inline-flex; + align-items: center; + justify-content: center; + width: 34px; + height: 34px; + border: none; + border-radius: 999px; + background: transparent; + color: var(--muted-strong); + cursor: pointer; +} + +.password-history-close:hover { + background: var(--panel-soft); + color: var(--text); +} + +.password-history-list { + display: grid; + gap: 12px; + margin: 10px 0 18px; +} + +.password-history-item { + position: relative; + border: 1px solid var(--line); + border-radius: 14px; + background: var(--panel-soft); + padding: 16px 54px 14px 16px; + box-shadow: var(--shadow-sm); +} + +.password-history-value { + color: var(--primary); + font-size: 22px; + line-height: 1.15; + letter-spacing: 0.01em; + word-break: break-all; +} + +.password-history-time { + margin-top: 8px; + color: var(--muted); +} + +.password-history-copy { + position: absolute; + top: 12px; + right: 12px; +} + +.password-history-copy-btn { + min-width: 36px; + padding: 0; + width: 36px; + height: 36px; +} + +.kv-label { + color: #64748b; + min-width: 0; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.kv-main { + display: flex; + align-items: center; + gap: 10px; + justify-content: flex-start; + min-width: 0; +} + +.kv-main > strong { + min-width: 0; +} + +.totp-inline { + display: inline-flex; + align-items: center; + gap: 10px; + min-width: 0; +} + +.totp-timer { + width: 30px; + height: 30px; + position: relative; + display: inline-grid; + place-items: center; + flex-shrink: 0; +} + +.totp-ring { + width: 30px; + height: 30px; + transform: rotate(-90deg); +} + +.totp-ring-track, +.totp-ring-progress { + fill: none; + stroke-width: 3; +} + +.totp-ring-track { + stroke: #d9e2ef; +} + +.totp-ring-progress { + stroke: #2563eb; + stroke-linecap: round; + transition: stroke-dashoffset 260ms linear, stroke 200ms ease; +} + +.totp-timer-value { + position: absolute; + inset: 0; + display: grid; + place-items: center; + font-size: 11px; + font-weight: 700; + color: #0f172a; +} + +.totp-codes-page { + display: flex; + flex-direction: column; + min-height: 100%; +} + +.totp-codes-list { + display: grid; + gap: 10px; + grid-template-columns: repeat(var(--totp-columns, 1), minmax(320px, 1fr)); + align-items: start; + width: 100%; +} + +.totp-code-row { + display: grid; + grid-template-columns: auto minmax(0, 1fr) auto; + align-items: center; + gap: 10px; + padding: 12px; + border: 1px solid #e2e8f0; + border-radius: 12px; + background: #f8fafc; + width: 100%; + min-width: 0; + max-width: none; + transition: + transform 220ms var(--ease-out-soft), + box-shadow var(--dur-fast) var(--ease-out-soft), + border-color var(--dur-fast) var(--ease-smooth), + background-color var(--dur-fast) var(--ease-smooth), + opacity var(--dur-fast) var(--ease-smooth); +} + +.totp-code-row.is-dragging { + z-index: 2; + border-color: rgba(37, 99, 235, 0.3); + background: color-mix(in srgb, var(--panel) 88%, white 12%); + box-shadow: 0 18px 36px rgba(15, 23, 42, 0.14); +} + +.totp-code-info { + display: flex; + align-items: center; + gap: 10px; + min-width: 0; +} + +.totp-drag-btn { + min-width: 24px; + width: 24px; + height: 34px; + padding: 0; + gap: 0; + color: var(--muted); + cursor: grab; + align-self: center; + touch-action: none; + -webkit-user-select: none; + user-select: none; + border-color: transparent; + background: transparent; + box-shadow: none; + border-radius: 10px; + position: relative; + overflow: visible; + opacity: 0.82; +} + +.totp-drag-btn:hover { + color: var(--primary-strong); + border-color: transparent; + background: transparent; + box-shadow: none; + opacity: 1; +} + +.totp-drag-btn:active { + cursor: grabbing; + border-color: transparent; + background: transparent; + box-shadow: none; +} + +.totp-drag-btn::before { + content: ''; + position: absolute; + inset: -10px; + border-radius: 12px; +} + +.totp-drag-btn .btn-icon { + opacity: 0.9; +} + +.totp-code-main { + display: flex; + align-items: center; + gap: 6px; + min-width: 0; + flex-shrink: 0; +} + +.totp-code-main strong { + font-size: 22px; + line-height: 1; + letter-spacing: 0.04em; + white-space: nowrap; +} + +.totp-code-meta { + min-width: 0; +} + +.totp-code-name, +.totp-code-username { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.totp-code-name { + font-size: 15px; + font-weight: 700; + color: #0f172a; +} + +.totp-code-username { + margin-top: 2px; + font-size: 13px; + color: #64748b; +} + +.totp-copy-btn { + min-width: 28px; + width: 28px; + height: 28px; + padding: 0; + border-radius: 999px; + flex-shrink: 0; + gap: 0; +} + +.value-ellipsis { + display: block; + max-width: 100%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.kv-actions { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 8px; + flex-wrap: wrap; + flex-shrink: 0; +} + +.attachment-list { + display: grid; + gap: 0; +} + +.attachment-head { + margin-bottom: 8px; +} + +.attachment-head h4 { + margin-bottom: 0; +} + +.attachment-add-btn { + min-width: 32px; + padding: 0 8px; +} + +.attachment-file-input { + display: none; +} + +.attachment-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + border-bottom: 1px solid #ecf0f5; + padding: 10px 0; +} + +.attachment-row:last-child { + border-bottom: none; +} + +.attachment-main { + display: flex; + align-items: center; + gap: 8px; + min-width: 0; +} + +.attachment-text { + min-width: 0; + display: grid; + gap: 2px; +} + +.attachment-text span { + color: #64748b; + font-size: 12px; +} + +.attachment-row.is-removed { + opacity: 0.6; +} + +.attachment-row.is-removed .attachment-text strong { + text-decoration: line-through; +} + +.attachment-queue-title { + font-size: 12px; + color: #64748b; + font-weight: 700; + padding: 8px 0 2px; +} + +.boolean-text { + min-width: 0; +} + +.custom-field-card { + display: grid; + gap: 8px; + padding: 10px 0; + border-bottom: 1px solid #ecf0f5; +} + +.custom-field-card:last-child { + border-bottom: none; + padding-bottom: 0; +} + +.custom-field-label { + display: block; + color: #64748b; + font-size: 12px; + font-weight: 700; + line-height: 1.2; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.custom-field-body { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: 10px; + align-items: center; +} + +.custom-field-value { + min-width: 0; +} + +.custom-field-value > .input { + width: 100%; +} + +.custom-field-check { + margin-bottom: 0; + display: inline-flex; + align-items: center; + gap: 8px; +} + +.custom-field-check span { + color: #334155; + font-size: 14px; + font-weight: 600; +} + +.custom-field-remove { + white-space: nowrap; +} + +.notes { + white-space: pre-wrap; + overflow-wrap: anywhere; + word-break: break-word; + color: #334155; + min-height: 48px; +} + +.empty { + color: #667085; + display: grid; + place-items: center; + min-height: 120px; +}