Files
Password-Manager/css/style-legacy.css
T
Zaki 40b3154a34 feat: MFA tools, single-instance, tray polish, prefs persistence
Session highlights:

- feat(prefs): DPAPI-backed key/value store (PM.UserPrefs) — fixes
  rememberedUsername being lost across reboots due to the random
  ephemeral HTTP port changing the localStorage origin every launch.
  Bridge cmd://prefs/{get,set} round-trips through Delphi.

- feat(tray): icon visible from startup (NIM_ADD at constructor, not
  at first minimize). Tray context menu themed via uxtheme!135
  SetPreferredAppMode so it follows the app's dark/light setting.

- feat(single-instance): named mutex + RegisterWindowMessage broadcast.
  Second launch posts WM_PMSHOW to HWND_BROADCAST and exits; the
  running bridge restores the window from tray. Mutex lives in Local\
  namespace so distinct Windows users can still each run one.

- feat(mfa): Authenticator sidebar view (live TOTP codes for every
  entry with a secret) + standalone TOTP generator modal (paste
  base32 / otpauth:// URI, or generate a random 20-byte secret).

- feat(sidebar): Folders / Tags / Tools sections collapsible with
  chevron toggle. Badge counts stay visible when collapsed. State
  persisted in settings_json (synced across devices).

- feat(autofill): hotkey when vault is locked now restores the app
  and focuses the master password input instead of no-op'ing
  silently. Cleaner UX for the common "I hit Ctrl+Shift+L but the
  vault was locked" path.

- feat(quick-unlock): when enabled, skip lockVault on Windows lock /
  sleep. Rationale: the DPAPI blob already gates access via the
  Windows account, so re-locking on top of the OS lock is redundant.
  Idle auto-lock still fires (separate opt-in).

- fix(quick-unlock): re-sync state.quickUnlockEnabled from DPAPI
  source-of-truth at boot, instead of trusting (now-volatile)
  localStorage.

- docs: CLAUDE.md updated with all new modules, bridge commands,
  and the port-ephemeral pitfall.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-08 21:31:39 +01:00

343 lines
22 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
:root {
--bg: #0e1015; --bg2: #191d27; --text: #e2e6ea; --text2: #7f8a98;
--accent: #6b7280; --accent-rgb: 107,114,128;
--danger: #ef4444; --success: #22c55e; --warning: #f59e0b;
--border: rgba(255,255,255,0.12); --card: #212734;
--input: #11141a; --vault-bg: rgba(255,255,255,0.06);
}
.light {
--bg: #eef0f4; --bg2: #e2e5eb; --text: #161b24; --text2: #5f6a7a;
--accent: #6b7280; --accent-rgb: 107,114,128;
--card: #ffffff; --input: #ffffff;
--border: rgba(0,0,0,0.16); --vault-bg: rgba(255,255,255,0.7);
}
* { margin:0; padding:0; box-sizing:border-box; }
body {
background: linear-gradient(145deg, var(--bg) 0%, var(--bg2) 100%);
font-family: 'Segoe UI', system-ui, sans-serif;
min-height: 100vh; display: flex; justify-content: center;
align-items: flex-start; padding: 1.2rem; color: var(--text);
transition: background 0.3s, color 0.3s;
}
.toast-container { position:fixed; top:1rem; right:1rem; z-index:9999; display:flex; flex-direction:column; gap:0.5rem; }
.toast { padding:0.7rem 1.2rem; border-radius:0.8rem; font-size:0.85rem; animation:slideIn 0.3s ease; color:#fff; }
.toast.error { background:var(--danger); }
.toast.success { background:var(--success); }
.toast { display:flex; align-items:center; gap:0.6rem; max-width:400px; }
.toast-action { background:rgba(0,0,0,0.3); border:1px solid rgba(255,255,255,0.4); color:#fff; font-weight:700; font-size:0.8rem; padding:0.25rem 0.7rem; border-radius:1rem; cursor:pointer; white-space:nowrap; flex-shrink:0; transition:0.15s; }
.toast-action:hover { background:rgba(0,0,0,0.5); }
@keyframes slideIn { from { transform:translateX(100%); opacity:0; } to { transform:translateX(0); opacity:1; } }
.vault {
width:96%; max-width:1700px; background:var(--vault-bg);
backdrop-filter:blur(20px); border:1px solid var(--border);
border-radius:2rem; padding:1.8rem; margin:0.5rem auto;
box-shadow:0 30px 50px rgba(0,0,0,0.4);
user-select:none; -webkit-user-drag:none;
}
h1 { font-size:2rem; margin-bottom:0.6rem; display:flex; align-items:center; gap:0.6rem; flex-wrap:wrap; }
h1 span { background:var(--accent); padding:0.2rem 0.7rem; border-radius:3rem; font-size:0.8rem; color:#fff; }
/* Auth */
.auth-section { background:rgba(0,0,0,0.25); border-radius:1.5rem; padding:1.3rem; margin-bottom:1rem; border:1px solid var(--border); }
.auth-tabs { display:flex; gap:1rem; margin-bottom:0.8rem; }
.auth-tab { background:none; border:none; color:var(--text2); padding:0.4rem 0.8rem; cursor:pointer; border-bottom:2px solid transparent; font-size:0.9rem; }
.auth-tab.active { color:var(--accent); border-bottom-color:var(--accent); }
input, select, textarea {
flex:1; min-width:130px; background:var(--input); border:1px solid #2d3748;
padding:0.65rem 1rem; border-radius:2rem; color:var(--text); font-size:0.85rem;
outline:none; font-family:inherit;
}
input:focus, select:focus { border-color:var(--accent); }
.btn {
background:linear-gradient(135deg, #6b7280, #4b5563); border:none; color:#fff;
font-weight:600; padding:0.65rem 1.3rem; border-radius:2rem; cursor:pointer;
font-size:0.85rem; transition:0.2s; white-space:nowrap;
}
.btn:hover { filter:brightness(1.15); transform:scale(1.02); }
.btn:disabled { opacity:0.5; cursor:not-allowed; transform:none; }
.btn-outline { background:transparent; border:1px solid #475569; color:var(--text2); }
.btn-sm { padding:0.35rem 0.9rem; font-size:0.75rem; }
.btn-xs { padding:0.2rem 0.5rem; font-size:0.7rem; }
.btn-danger { background:var(--danger); }
.input-group { display:flex; gap:0.5rem; margin:0.7rem 0; flex-wrap:wrap; align-items:center; }
/* Toolbar */
.toolbar { display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap; gap:0.5rem; margin-bottom:0.7rem; }
.view-dropdown { position:relative; }
.view-dropdown-menu { position:absolute; top:100%; right:0; margin-top:4px; min-width:155px; background:var(--bg2); border:1px solid var(--border); border-radius:0.6rem; padding:0.3rem; box-shadow:0 8px 24px rgba(0,0,0,0.3); z-index:100; display:flex; flex-direction:column; gap:2px; }
.view-opt { background:none; border:none; color:var(--text); padding:0.45rem 0.8rem; border-radius:0.4rem; cursor:pointer; font-size:0.8rem; text-align:left; transition:0.12s; white-space:nowrap; }
.view-opt:hover { background:var(--accent); color:#fff; }
.view-opt.active { background:var(--accent); color:#fff; font-weight:600; }
.toggles-row { display:flex; gap:1.2rem; align-items:center; flex-wrap:wrap; }
.toggle-item { display:flex; align-items:center; gap:0.4rem; font-size:0.75rem; color:var(--text2); }
.toggle-switch { position:relative; width:36px; height:20px; background:#334155; border-radius:10px; cursor:pointer; transition:0.2s; }
.toggle-switch.active { background:var(--accent); }
.toggle-switch::after { content:''; position:absolute; top:2px; left:2px; width:16px; height:16px; background:#fff; border-radius:50%; transition:0.2s; }
.toggle-switch.active::after { left:18px; }
.status-badge { background:var(--bg2); padding:0.25rem 0.8rem; border-radius:2rem; font-size:0.75rem; white-space:nowrap; }
.hidden { display:none !important; }
/* Strength */
.strength-bar { height:4px; border-radius:2px; transition:0.3s; margin-top:0.2rem; }
.s0 { background:var(--danger); width:20%; }
.s1 { background:var(--warning); width:40%; }
.s2 { background:#eab308; width:60%; }
.s3 { background:#84cc16; width:80%; }
.s4 { background:var(--success); width:100%; }
/* ========== SEARCH BOX WITH CLEAR BUTTON (FIXED) ========== */
.search-box {
position: relative;
width: 240px; /* fixed width adjust if needed */
flex-shrink: 0;
}
.search-box input {
width: 100%;
box-sizing: border-box;
padding-right: 30px; /* space for the ✕ button */
}
.clear-search-btn {
position: absolute;
right: 6px;
top: 50%;
transform: translateY(-50%);
background: none;
border: none;
color: var(--text2);
cursor: pointer;
font-size: 0.9rem;
line-height: 1;
padding: 6px;
display: none;
}
/* Folders */
.folders-bar { display:flex; gap:0.4rem; margin-bottom:0.8rem; flex-wrap:wrap; align-items:center; padding:0.4rem 0.6rem; background:rgba(0,0,0,0.2); border-radius:1rem; }
.folder-chip { background:var(--bg2); border:1px solid var(--border); color:var(--text2); padding:0.3rem 0.8rem; border-radius:2rem; cursor:pointer; font-size:0.78rem; transition:0.2s; white-space:nowrap; }
.folder-chip:hover { background:var(--chip-color, var(--accent)); color:#fff; border-color:var(--chip-color, var(--accent)); }
.folder-chip.active { background:var(--chip-color, var(--accent)); color:#fff; border-color:var(--chip-color, var(--accent)); }
.folder-chip.drag-over { border-color:var(--accent)!important; box-shadow:0 0 12px rgba(var(--accent-rgb),0.5); transform:scale(1.05); }
.pw-display.pw-hover { cursor:pointer; }
.folder-count { background:rgba(0,0,0,0.3); padding:0.1rem 0.4rem; border-radius:1rem; margin-left:0.3rem; font-size:0.7rem; }
.folder-delete-btn { background:transparent; border:none; color:var(--danger); cursor:pointer; font-size:0.7rem; margin-left:0.2rem; opacity:0.7; }
.folder-delete-btn:hover { opacity:1; }
.folder-add-btn { background:transparent; border:1px dashed #475569; color:var(--text2); padding:0.3rem 0.6rem; border-radius:2rem; cursor:pointer; font-size:0.75rem; transition:0.2s; }
.folder-add-btn:hover { border-color:var(--accent); color:var(--accent); }
.light .entry-card, .light .entry-row, .light .entry-compact { box-shadow:0 1px 4px rgba(0,0,0,0.08); }
#addFolderSelect { min-width:110px; max-width:150px; background:var(--input); border:1px solid #2d3748; color:var(--text); padding:0.5rem 0.8rem; border-radius:2rem; font-size:0.8rem; cursor:pointer; }
/* Entries */
#entriesContainer { user-select:none; -webkit-user-drag:none; }
#entriesContainer.grid-view { display:grid; grid-template-columns: repeat(auto-fill, minmax(180px,1fr)); gap:0.6rem; }
#entriesContainer.list-view { display:flex; flex-direction:column; gap:0.4rem; }
#entriesContainer.compact-view { display:flex; flex-direction:column; gap:0.2rem; }
#entriesContainer.table-view { overflow-x:auto; }
#entriesContainer.table-view table { width:100%; border-collapse:collapse; }
#entriesContainer.table-view th { text-align:left; padding:0.4rem 0.6rem; color:var(--text2); font-size:0.75rem; border-bottom:1px solid var(--border); }
#entriesContainer.table-view td { padding:0.4rem 0.6rem; font-size:0.8rem; border-bottom:1px solid rgba(255,255,255,0.03); }
.entry-card, .entry-row, .entry-compact, .table-row-drag { cursor:pointer; user-select:none; }
.entry-card.drag-over, .entry-row.drag-over, .entry-compact.drag-over, .table-row-drag.drag-over { border-color:var(--accent)!important; box-shadow:0 0 15px rgba(var(--accent-rgb),0.3); }
.entry-card.selected { border-color:var(--accent)!important; box-shadow:0 0 0 2px var(--accent),0 0 18px rgba(var(--accent-rgb),0.35); background:rgba(var(--accent-rgb),0.18); }
.entry-row.selected, .entry-compact.selected { border-color:var(--accent)!important; box-shadow:0 0 0 2px var(--accent),0 0 18px rgba(var(--accent-rgb),0.35); background:rgba(var(--accent-rgb),0.14); border-left:3px solid var(--accent); }
.table-row-drag.selected td { background:rgba(var(--accent-rgb),0.14)!important; box-shadow:inset 0 0 0 1px var(--accent); border-bottom:1px solid var(--accent); }
.entry-card { background:var(--card); border:1px solid var(--border); border-radius:0.8rem; padding:0.9rem 2.8rem 0.9rem 0.9rem; transition:0.2s; position:relative; word-break:break-word; box-shadow:0 2px 6px rgba(0,0,0,0.2); }
.entry-card:hover { border-color:rgba(255,255,255,0.15); transform:translateY(-2px); box-shadow:0 4px 12px rgba(0,0,0,0.3); }
.card-site { font-weight:700; font-size:0.9rem; color:var(--text); margin-bottom:0.2rem; }
.card-user { color:var(--text2); font-size:0.75rem; margin-bottom:0.3rem; }
.card-folder { font-size:0.65rem; color:var(--accent); margin-bottom:0.3rem; background:rgba(var(--accent-rgb),0.15); display:inline-block; padding:0.1rem 0.5rem; border-radius:1rem; }
.card-password { background:var(--bg2); padding:0.3rem 0.5rem; border-radius:0.6rem; display:flex; align-items:center; justify-content:space-between; font-family:monospace; font-size:0.75rem; gap:0.2rem; }
#entriesContainer.card-view .entry-card { padding:1.2rem 3rem 1.2rem 1.2rem; }
#entriesContainer.card-view .card-site { font-size:1.05rem; }
#entriesContainer.card-view .card-password { font-size:0.9rem; }
#entriesContainer.card-view .entry-card:hover { transform:translateY(-3px); }
.entry-row { background:var(--card); border:1px solid var(--border); border-radius:0.8rem; padding:0.7rem 2.8rem 0.7rem 0.9rem; display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap; gap:0.4rem; position:relative; box-shadow:0 2px 6px rgba(0,0,0,0.15); }
.entry-compact { display:flex; align-items:center; gap:0.5rem; padding:0.35rem 2.8rem 0.35rem 0.7rem; background:var(--card); border-radius:0.5rem; border:1px solid var(--border); font-size:0.8rem; position:relative; box-shadow:0 1px 4px rgba(0,0,0,0.12); }
.action-btns { position:absolute; top:4px; right:6px; display:flex; gap:4px; z-index:1; }
.delete-btn { width:20px; height:20px; background:transparent; color:var(--danger); border:none; cursor:pointer; font-size:0.9rem; font-weight:700; display:flex; align-items:center; justify-content:center; }
.delete-btn:hover { color:#fff; transform:scale(1.2); }
.light .delete-btn:hover { color:#000!important; }
.edit-btn { width:20px; height:20px; background:transparent; color:var(--accent); border:none; cursor:pointer; font-size:0.75rem; display:flex; align-items:center; justify-content:center; }
.edit-btn:hover { color:#fff; transform:scale(1.2); }
.light .edit-btn:hover { color:#000!important; }
.star-btn { width:20px; height:20px; background:transparent; border:none; cursor:pointer; font-size:0.85rem; display:flex; align-items:center; justify-content:center; color:var(--text2); }
.star-btn:hover { transform:scale(1.3); }
.entry-card.favorite { border-color:rgba(255,200,0,0.3); background:rgba(255,200,0,0.05); }
.entry-row.favorite { border-color:rgba(255,200,0,0.3); background:rgba(255,200,0,0.05); }
.entry-compact.favorite { border-color:rgba(255,200,0,0.3); background:rgba(255,200,0,0.05); }
.table-row-drag.favorite td { background:rgba(255,200,0,0.05); }
/* Grouped view */
#entriesContainer.grouped-view { display:flex; flex-direction:column; gap:0.15rem; }
.grouped-header { position:sticky; top:0; z-index:2; background:var(--bg); padding:0.55rem 0.8rem; border-radius:0.5rem; font-weight:600; font-size:0.9rem; color:var(--accent); display:flex; align-items:center; gap:0.5rem; border-bottom:2px solid var(--accent); margin-top:0.4rem; }
.grouped-header:first-child { margin-top:0; }
#entriesContainer.grouped-view .entry-row { border-left:3px solid transparent; transition:0.15s; padding-left:1rem; }
#entriesContainer.grouped-view .entry-row:hover { border-left-color:var(--accent); }
/* Detail view */
#entriesContainer.detail-view { display:flex; flex-direction:column; gap:1rem; }
.detail-nav { display:flex; align-items:center; justify-content:center; gap:1rem; padding:0.5rem 0; position:sticky; top:0; z-index:2; background:var(--bg); }
.detail-nav button { background:var(--bg2); border:1px solid var(--border); color:var(--text); padding:0.4rem 1rem; border-radius:0.5rem; cursor:pointer; transition:0.15s; font-size:0.85rem; }
.detail-nav button:hover { border-color:var(--accent); color:var(--accent); }
.detail-nav button:disabled { opacity:0.4; cursor:default; }
.detail-nav .detail-counter { font-size:0.8rem; color:var(--text2); min-width:80px; text-align:center; }
.detail-card { background:var(--bg2); border:1px solid var(--border); border-radius:1rem; padding:1.5rem; display:flex; flex-direction:column; gap:0.9rem; max-width:520px; margin:0 auto; width:100%; }
.detail-field { display:flex; flex-direction:column; gap:0.15rem; }
.detail-label { font-size:0.7rem; color:var(--text2); text-transform:uppercase; letter-spacing:0.5px; }
.detail-value { font-size:1.2rem; word-break:break-all; }
.detail-value.pw-display { font-family:monospace; letter-spacing:2px; font-size:1.3rem; cursor:default; }
.detail-folder { display:flex; gap:0.5rem; align-items:center; }
.detail-actions { display:flex; gap:0.5rem; margin-top:0.3rem; }
.detail-actions button { flex:1; padding:0.5rem; border-radius:0.5rem; border:1px solid var(--border); background:var(--bg); cursor:pointer; transition:0.15s; font-size:0.85rem; }
.detail-actions button:hover { border-color:var(--accent); background:var(--bg2); }
.entry-info { display:flex; gap:0.5rem; align-items:center; flex-wrap:wrap; flex:1; }
.entry-site { font-weight:700; color:var(--text); }
.entry-user { color:var(--text2); }
.entry-folder { font-size:0.7rem; color:var(--accent); background:rgba(var(--accent-rgb),0.15); padding:0.1rem 0.5rem; border-radius:1rem; }
.password-field { display:flex; align-items:center; gap:0.3rem; background:var(--bg2); padding:0.2rem 0.5rem; border-radius:2rem; }
.password-text { font-family:monospace; color:var(--text2); font-size:0.8rem; }
.icon-btn { background:none; border:1px solid #475569; color:var(--text2); border-radius:2rem; padding:0.2rem 0.5rem; font-size:0.65rem; cursor:pointer; }
.icon-btn:hover { background:rgba(var(--accent-rgb),0.2); }
/* Settings Dropdown */
.settings-dropdown { position:relative; display:inline-block; }
.settings-menu { position:absolute; top:100%; right:0; background:var(--bg2); border:1px solid var(--border); border-radius:0.8rem; padding:0.5rem; min-width:200px; z-index:1000; box-shadow:0 10px 25px rgba(0,0,0,0.4); }
.settings-item { display:flex; justify-content:space-between; align-items:center; padding:0.4rem 0.6rem; border-radius:0.5rem; font-size:0.8rem; color:var(--text2); cursor:pointer; }
.settings-item:hover { background:rgba(255,255,255,0.05); }
.settings-item select { background:var(--input); border:1px solid #334155; color:var(--text); padding:0.2rem 0.5rem; border-radius:1rem; font-size:0.75rem; }
/* Trash */
.trash-badge { background:var(--danger); color:#fff; padding:0.15rem 0.5rem; border-radius:1rem; font-size:0.65rem; margin-left:0.3rem; }
.trash-info { font-size:0.7rem; color:var(--text2); margin-top:0.2rem; }
.restore-btn { background:none; color:#fff; border:none; padding:0.2rem 0.6rem; border-radius:1.5rem; cursor:pointer; font-size:0.7rem; }
.restore-btn:hover { filter:brightness(1.2); }
.empty-trash-btn { background:var(--danger); color:#fff; border:none; padding:0.3rem 0.8rem; border-radius:1.5rem; cursor:pointer; font-size:0.75rem; }
.empty-trash-btn:hover { filter:brightness(1.2); }
/* Edit Modal */
.edit-modal { position:fixed; top:0; left:0; right:0; bottom:0; background:rgba(0,0,0,0.7); display:none; justify-content:center; align-items:center; z-index:1001; }
.edit-modal.show { display:flex; }
.edit-box { background:var(--bg2); border-radius:1.5rem; padding:1.5rem; min-width:380px; max-width:90%; }
.edit-box h3 { margin-bottom:1rem; color:var(--text); }
.edit-box label { color:var(--text2); font-size:0.8rem; display:block; margin-bottom:0.2rem; }
.edit-box input, .edit-box select { width:100%; margin-bottom:0.5rem; }
/* Custom modals */
.custom-modal-overlay { position:fixed; top:0; left:0; right:0; bottom:0; background:rgba(0,0,0,0.6); display:flex; justify-content:center; align-items:center; z-index:10001; display:none; }
.custom-modal-overlay.show { display:flex; }
.custom-modal { background:var(--bg2); border:1px solid var(--border); border-radius:1.2rem; padding:1.5rem; min-width:300px; max-width:90%; }
.custom-modal h3 { margin-bottom:1rem; color:var(--text); }
.custom-modal input { width:100%; margin-bottom:1rem; }
.custom-modal .modal-actions { display:flex; gap:0.5rem; justify-content:flex-end; }
/* Confirm popup near element */
.custom-confirm { position:fixed; background:var(--bg2); border:1px solid var(--accent); border-radius:0.8rem; padding:0.7rem 1rem; z-index:9999; box-shadow:0 10px 30px rgba(0,0,0,0.5); display:none; font-size:0.8rem; color:var(--text); white-space:nowrap; }
.custom-confirm.show { display:block; }
.custom-confirm .confirm-text { margin-bottom:0.5rem; }
.custom-confirm .confirm-btns { display:flex; gap:0.4rem; }
.custom-confirm .confirm-yes { background:var(--danger); color:#fff; border:none; padding:0.3rem 0.8rem; border-radius:1.5rem; cursor:pointer; font-size:0.75rem; }
.custom-confirm .confirm-no { background:#334155; color:#fff; border:none; padding:0.3rem 0.8rem; border-radius:1.5rem; cursor:pointer; font-size:0.75rem; }
/* Zigzag toast */
.toast-zigzag { position:fixed; z-index:9998; padding:0.4rem 0.7rem; border-radius:0.5rem; font-size:0.72rem; font-weight:600; pointer-events:none; animation:zigzagUp 1.2s ease-out forwards; white-space:nowrap; }
.toast-zigzag.success { background:rgba(34,197,94,0.95); color:#fff; }
.toast-zigzag.error { background:rgba(239,68,68,0.95); color:#fff; }
@keyframes zigzagUp { 0%{opacity:1;transform:translate(0,0) scale(1)} 20%{opacity:0.9;transform:translate(-10px,-14px) scale(0.9)} 40%{opacity:0.65;transform:translate(12px,-28px) scale(0.75)} 70%{opacity:0.3;transform:translate(-8px,-42px) scale(0.6)} 100%{opacity:0;transform:translate(0,-60px) scale(0.4)} }
.idle-warning { position:fixed; top:50%; left:50%; transform:translate(-50%,-50%); background:rgba(0,0,0,0.95); color:#fff; padding:2rem; border-radius:2rem; z-index:10000; text-align:center; display:none; }
.idle-warning.show { display:block; }
@media(max-width:700px) {
.vault { padding:1rem; border-radius:1.5rem; width:98%; }
.entry-row { flex-direction:column; }
#entriesContainer.grid-view { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
.toolbar { flex-direction:column; }
.folders-bar { flex-direction:column; align-items:stretch; }
}
/* Floating action button */
.fab {
position: fixed; bottom: 2rem; right: 2rem;
width: 56px; height: 56px; border-radius: 50%;
background: var(--accent); color: #fff; border: none;
font-size: 1.8rem; cursor: pointer; box-shadow: 0 8px 20px rgba(0,0,0,0.4);
display: flex; align-items: center; justify-content: center;
transition: 0.2s; z-index: 100;
}
.fab:hover { transform: scale(1.1); filter: brightness(1.1); }
.fab-trash { left: 2rem; right: auto; font-size:1.4rem; background:transparent; box-shadow:none; }
.fab-trash.active { box-shadow:0 0 18px rgba(var(--accent-rgb),0.5); }
/* Generic modal overlay (if you dont already have it) */
.modal-overlay {
position: fixed; top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0,0,0,0.7); display: none;
justify-content: center; align-items: center; z-index: 1001;
}
.modal-overlay.show { display: flex; }
/* Modal box (used by both edit and add modals) */
.modal-box {
background: var(--bg2); border-radius: 1.5rem; padding: 1.5rem;
min-width: 380px; max-width: 90%;
}
.modal-box h3 { margin-bottom: 1rem; color: var(--text); }
.modal-box label { color: var(--text2); font-size: 0.8rem; display: block; margin-bottom: 0.2rem; }
.modal-box input, .modal-box select { width: 100%; margin-bottom: 0.5rem; }
/* Batch select */
.select-checkbox {
position: absolute;
top: 6px;
left: 6px;
z-index: 2;
accent-color: var(--accent);
}
.batch-actions {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
background: var(--bg2);
border: 1px solid var(--accent);
border-radius: 2rem;
padding: 0.5rem 1.5rem;
display: flex;
gap: 0.8rem;
align-items: center;
z-index: 1000;
box-shadow: 0 10px 25px rgba(0,0,0,0.5);
font-size: 0.85rem;
color: var(--text);
}
.batch-actions button {
font-size: 0.78rem;
padding: 0.35rem 0.8rem;
}
.selected-count {
font-weight: 600;
color: var(--accent);
}
#rectSelect {
position:fixed; pointer-events:none; z-index:999;
border:1px solid var(--accent);
background:rgba(var(--accent-rgb),0.1);
display:none;
}
#trashBtn.drag-over {
border-color:var(--danger)!important;
box-shadow:0 0 15px rgba(239,68,68,0.4);
background:rgba(239,68,68,0.15);
}
/* Batch confirm modal backdrop */
.batch-confirm-overlay { background:rgba(0,0,0,0.3); }