forked from Keelrleek/Harbor
Opening the chat froze the machine: the log rendered all ~800 loaded messages (1.1 M characters, ~10,000 nodes, a 440,000 px column) and rebuilt the whole log on every new message, receipt change and mark refresh - about 1.8 s of blocked main thread each time. Now: - the log keeps the newest 150 rows; "Load earlier" adds 150 more (from memory, then the server) and keeps the reader's place; switching person or thread starts again at 150 - new messages are appended; a changed receipt, pin, correction or answer repaints only its row - off-screen rows skip layout and paint (content-visibility); the Intl date formatters are made once - polls go through every(): no overlapping requests; while the tab is hidden messages poll every 10 s (sound and notifications still work), people every 60 s, the overview every 30 s, and the marks refresh and board panels pause - removed the backdrop-filter blurs on the sidebar, top bar and composer, and the live dot's endless box-shadow animation (now opacity) Measured: forced full render 1,600-1,850 ms -> 16-21 ms (hidden pane, render + layout); load long task 1,806 ms -> 56 ms; DOM 13,318 -> 5,282 nodes (log 9,870 -> 1,806); a new message appends 2 nodes with no long task; hidden-tab traffic ~37 requests / ~2 MB per 30 s -> 4 requests / 156 KB.
350 lines
24 KiB
CSS
350 lines
24 KiB
CSS
/* Harbor design system: tokens, themes and components shared by the chat page and the board. The colour themes
|
|
from BroadWorks Enhancer live in themes.css and override these tokens; this file is Harbor's own default look. */
|
|
|
|
@font-face {
|
|
font-family: "Inter"; src: url("InterVariable.woff2") format("woff2"); /* OFL, Inter-OFL.txt */
|
|
font-weight: 100 900; font-style: normal; font-display: swap;
|
|
}
|
|
|
|
:root {
|
|
--font: "Inter", "Segoe UI Variable Text", "Segoe UI", system-ui, -apple-system, "Helvetica Neue", sans-serif;
|
|
--font-display: "Inter", "Segoe UI Variable Display", "Segoe UI", system-ui, sans-serif;
|
|
--mono: "Cascadia Code", "Cascadia Mono", ui-monospace, "SF Mono", Consolas, monospace;
|
|
|
|
--bg: #f2f4f8;
|
|
--bg-tint: radial-gradient(1200px 600px at 100% -10%, #e6ebff 0%, transparent 60%), radial-gradient(900px 500px at -10% 110%, #e8f6f1 0%, transparent 55%);
|
|
--surface: #ffffff;
|
|
--surface-2: #f7f9fc;
|
|
--surface-3: #eef2f8;
|
|
--text: #111a2c;
|
|
--text-2: #364259;
|
|
--muted: #687489;
|
|
--faint: #9aa4b6;
|
|
--line: #e3e8f0;
|
|
--line-2: #d2d9e5;
|
|
|
|
--accent: #4361ee;
|
|
--accent-2: #3650d6;
|
|
--accent-soft: #edf0ff;
|
|
--accent-ink: #2c3fb5;
|
|
--action: #4361ee; --action-2: #3650d6; --on-action: #ffffff; /* filled buttons, the send button, your bubbles */
|
|
--on-green: #ffffff; --on-red: #ffffff;
|
|
--green: #097a5a; --green-soft: #e3f5ee;
|
|
--amber: #b97710; --amber-soft: #fcf1de;
|
|
--red: #d63f36; --red-soft: #fdebea;
|
|
--violet: #7356f0; --violet-soft: #f0ecff;
|
|
--blue: #2a78e4; --blue-soft: #e6f0fd;
|
|
|
|
--shadow-1: 0 1px 2px rgba(17, 26, 44, .05);
|
|
--shadow-2: 0 1px 2px rgba(17, 26, 44, .04), 0 8px 24px -10px rgba(17, 26, 44, .14);
|
|
--shadow-3: 0 24px 60px -16px rgba(17, 26, 44, .32);
|
|
--ring: 0 0 0 3px rgba(67, 97, 238, .28);
|
|
|
|
--r-1: 8px; --r-2: 12px; --r-3: 16px; --r-4: 22px;
|
|
--green-ink: color-mix(in oklab, var(--green) 66%, var(--text)); --red-ink: color-mix(in oklab, var(--red) 66%, var(--text));
|
|
--amber-ink: color-mix(in oklab, var(--amber) 66%, var(--text)); --violet-ink: color-mix(in oklab, var(--violet) 66%, var(--text));
|
|
--blue-ink: color-mix(in oklab, var(--blue) 66%, var(--text));
|
|
|
|
/* the top bar and the sidebar; a theme can colour them apart from the page */
|
|
--header: color-mix(in srgb, var(--surface) 58%, transparent);
|
|
--side: color-mix(in srgb, var(--surface) 72%, transparent); --side-solid: var(--surface);
|
|
--side-text: var(--text); --side-muted: var(--muted); --side-hover: var(--surface-3); --side-line: var(--line);
|
|
--side-active: var(--accent-soft); --side-active-text: var(--accent-ink); --side-accent: var(--accent);
|
|
--side-green: color-mix(in oklab, var(--green) 34%, var(--side-text));
|
|
--side-green-soft: color-mix(in srgb, var(--side-green) 16%, var(--side-solid));
|
|
--ease: cubic-bezier(.2, .8, .2, 1);
|
|
color-scheme: light;
|
|
}
|
|
|
|
@media (prefers-color-scheme: dark) {
|
|
:root:not([data-theme="light"]) {
|
|
--bg: #0b0f17;
|
|
--bg-tint: radial-gradient(1200px 600px at 100% -10%, rgba(76, 99, 230, .16) 0%, transparent 60%), radial-gradient(900px 500px at -10% 110%, rgba(30, 160, 120, .08) 0%, transparent 55%);
|
|
--surface: #121825; --surface-2: #161d2b; --surface-3: #1c2435;
|
|
--text: #e9eef7; --text-2: #c3cbdb; --muted: #8b96ac; --faint: #5d687e;
|
|
--line: #222b3c; --line-2: #2c374a;
|
|
--accent: #7189ff; --accent-2: #8a9dff; --accent-soft: rgba(113, 137, 255, .14); --accent-ink: #b9c5ff;
|
|
--action: #4f6af0; --action-2: #6079f5; --on-green: #0b0f17; --on-red: #0b0f17;
|
|
--green: #33c995; --green-soft: rgba(51, 201, 149, .13);
|
|
--amber: #f1b44c; --amber-soft: rgba(241, 180, 76, .13);
|
|
--red: #ff6b61; --red-soft: rgba(255, 107, 97, .13);
|
|
--violet: #a68cff; --violet-soft: rgba(166, 140, 255, .14);
|
|
--blue: #5ba3ff; --blue-soft: rgba(91, 163, 255, .13);
|
|
--shadow-1: 0 1px 2px rgba(0, 0, 0, .35);
|
|
--shadow-2: 0 1px 2px rgba(0, 0, 0, .3), 0 10px 30px -12px rgba(0, 0, 0, .55);
|
|
--shadow-3: 0 30px 70px -20px rgba(0, 0, 0, .7);
|
|
--ring: 0 0 0 3px rgba(113, 137, 255, .35);
|
|
color-scheme: dark;
|
|
}
|
|
}
|
|
:root[data-theme="dark"] {
|
|
--bg: #0b0f17;
|
|
--bg-tint: radial-gradient(1200px 600px at 100% -10%, rgba(76, 99, 230, .16) 0%, transparent 60%), radial-gradient(900px 500px at -10% 110%, rgba(30, 160, 120, .08) 0%, transparent 55%);
|
|
--surface: #121825; --surface-2: #161d2b; --surface-3: #1c2435;
|
|
--text: #e9eef7; --text-2: #c3cbdb; --muted: #8b96ac; --faint: #5d687e;
|
|
--line: #222b3c; --line-2: #2c374a;
|
|
--accent: #7189ff; --accent-2: #8a9dff; --accent-soft: rgba(113, 137, 255, .14); --accent-ink: #b9c5ff;
|
|
--action: #4f6af0; --action-2: #6079f5; --on-green: #0b0f17; --on-red: #0b0f17;
|
|
--green: #33c995; --green-soft: rgba(51, 201, 149, .13);
|
|
--amber: #f1b44c; --amber-soft: rgba(241, 180, 76, .13);
|
|
--red: #ff6b61; --red-soft: rgba(255, 107, 97, .13);
|
|
--violet: #a68cff; --violet-soft: rgba(166, 140, 255, .14);
|
|
--blue: #5ba3ff; --blue-soft: rgba(91, 163, 255, .13);
|
|
--shadow-1: 0 1px 2px rgba(0, 0, 0, .35);
|
|
--shadow-2: 0 1px 2px rgba(0, 0, 0, .3), 0 10px 30px -12px rgba(0, 0, 0, .55);
|
|
--shadow-3: 0 30px 70px -20px rgba(0, 0, 0, .7);
|
|
--ring: 0 0 0 3px rgba(113, 137, 255, .35);
|
|
color-scheme: dark;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------------------------------ base */
|
|
*, *::before, *::after { box-sizing: border-box; }
|
|
html, body { margin: 0; height: 100%; }
|
|
body {
|
|
font: 14px/1.5 var(--font); color: var(--text); font-feature-settings: "cv11", "ss03";
|
|
background-color: var(--bg); background-image: var(--bg-tint); background-attachment: fixed;
|
|
-webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility;
|
|
}
|
|
button, input, select, textarea { font: inherit; color: inherit; }
|
|
button { cursor: pointer; }
|
|
a { color: var(--accent); text-decoration: none; }
|
|
a:hover { text-decoration: underline; }
|
|
code, .mono { font-family: var(--mono); font-size: .92em; }
|
|
code { background: var(--surface-3); padding: 1px 5px; border-radius: 5px; }
|
|
::selection { background: var(--accent-soft); }
|
|
:focus-visible { outline: none; box-shadow: var(--ring); border-radius: var(--r-1); }
|
|
* { scrollbar-width: thin; scrollbar-color: var(--line-2) transparent; }
|
|
[hidden] { display: none !important; }
|
|
svg.i { width: 16px; height: 16px; flex-shrink: 0; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
|
|
svg.i.lg { width: 20px; height: 20px; }
|
|
svg.i.sm { width: 14px; height: 14px; }
|
|
svg.i { pointer-events: none; }
|
|
.muted { color: var(--muted); } .faint { color: var(--faint); }
|
|
.note { font-size: 12px; color: var(--muted); }
|
|
.err { color: var(--red); }
|
|
.grow { flex: 1; min-width: 0; }
|
|
.row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
|
|
.stack-v { display: flex; flex-direction: column; gap: 4px; }
|
|
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
|
|
|
|
/* ------------------------------------------------------------------------------------------------ buttons */
|
|
.btn {
|
|
display: inline-flex; align-items: center; justify-content: center; gap: 6px; height: 34px; padding: 0 13px;
|
|
border: 1px solid var(--line-2); background: var(--surface); color: var(--text-2); border-radius: var(--r-1);
|
|
font-size: 13px; font-weight: 600; white-space: nowrap; box-shadow: var(--shadow-1);
|
|
transition: background .15s, border-color .15s, color .15s, transform .08s, box-shadow .15s;
|
|
}
|
|
.btn:hover { background: var(--surface-2); border-color: var(--faint); color: var(--text); }
|
|
.btn:active { transform: translateY(1px); }
|
|
.btn:disabled { opacity: .45; cursor: default; transform: none; }
|
|
.btn.small, .btn.sm { height: 28px; padding: 0 10px; font-size: 12px; border-radius: 7px; }
|
|
.btn.primary { background: var(--action); border-color: var(--action); color: var(--on-action); }
|
|
.btn.primary:hover { background: var(--action-2); border-color: var(--action-2); color: var(--on-action); }
|
|
.btn.go { background: var(--green); border-color: var(--green); color: var(--on-green); }
|
|
.btn.go:hover { background: var(--green); filter: brightness(1.07); border-color: var(--green); color: var(--on-green); }
|
|
.btn.stop, .btn.danger { background: var(--red); border-color: var(--red); color: var(--on-red); }
|
|
.btn.stop:hover, .btn.danger:hover { background: var(--red); filter: brightness(1.07); border-color: var(--red); color: var(--on-red); }
|
|
.btn.ghost { background: transparent; border-color: transparent; box-shadow: none; color: var(--muted); }
|
|
.btn.ghost:hover { background: var(--surface-3); color: var(--text); }
|
|
.btn.icon { width: 34px; padding: 0; }
|
|
.btn.icon.sm { width: 28px; }
|
|
.btn[aria-pressed="true"] { background: var(--accent-soft); border-color: transparent; color: var(--accent-ink); }
|
|
|
|
/* ------------------------------------------------------------------------------------------------ tags and pills */
|
|
.tag {
|
|
display: inline-flex; align-items: center; gap: 5px; height: 21px; padding: 0 8px; border-radius: 999px;
|
|
font-size: 11px; font-weight: 650; letter-spacing: .1px; white-space: nowrap; border: 0;
|
|
background: var(--surface-3); color: var(--muted);
|
|
}
|
|
.tag.green { background: var(--green-soft); color: var(--green-ink); }
|
|
.tag.red { background: var(--red-soft); color: var(--red-ink); }
|
|
.tag.amber { background: var(--amber-soft); color: var(--amber-ink); }
|
|
.tag.violet { background: var(--violet-soft); color: var(--violet-ink); }
|
|
.tag.blue { background: var(--blue-soft); color: var(--blue-ink); }
|
|
.tag.solid-red { background: var(--red); color: var(--on-red); }
|
|
.tag .dot, .dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; display: inline-block; flex-shrink: 0; }
|
|
button.tag { cursor: pointer; }
|
|
button.tag:hover { filter: brightness(.96); color: var(--accent); }
|
|
.count-badge {
|
|
min-width: 20px; height: 20px; padding: 0 6px; border-radius: 999px; display: inline-grid; place-items: center;
|
|
font-size: 11px; font-weight: 700; background: var(--surface-3); color: var(--muted);
|
|
}
|
|
.count-badge.hot { background: var(--red); color: var(--on-red); }
|
|
.count-badge.accent { background: var(--action); color: var(--on-action); }
|
|
.count-badge.green { background: var(--green-soft); color: var(--green-ink); }
|
|
a.tag:hover { text-decoration: none; filter: brightness(.96); }
|
|
|
|
/* ------------------------------------------------------------------------------------------------ live dot */
|
|
.live { display: inline-flex; align-items: center; gap: 7px; font-size: 12px; color: var(--muted); white-space: nowrap; }
|
|
.live .dot { width: 8px; height: 8px; background: var(--amber); box-shadow: 0 0 0 3px var(--amber-soft); }
|
|
.live.ok .dot, .connection.connected .dot { background: var(--green); box-shadow: 0 0 0 3px var(--green-soft); animation: pulse 2.4s ease-in-out infinite; } /* opacity only: an endless box-shadow animation repainted every frame */
|
|
.live.bad .dot, .connection.failed .dot { background: var(--red); box-shadow: 0 0 0 3px var(--red-soft); }
|
|
@keyframes pulse { 0% { opacity: 1; } 50% { opacity: .35; } 100% { opacity: 1; } }
|
|
@keyframes rise { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
|
|
|
|
/* ------------------------------------------------------------------------------------------------ avatars */
|
|
.avatar {
|
|
--avatar: #7b8797; position: relative; width: 36px; height: 36px; flex-shrink: 0; display: grid; place-items: center;
|
|
border-radius: 12px; font-size: 11.5px; font-weight: 750; letter-spacing: .2px; color: #fff;
|
|
background: linear-gradient(145deg, color-mix(in srgb, var(--avatar) 82%, #fff 18%), var(--avatar));
|
|
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .14), var(--shadow-1);
|
|
}
|
|
.avatar.sm { width: 28px; height: 28px; border-radius: 9px; font-size: 10px; }
|
|
.avatar.lg { width: 44px; height: 44px; border-radius: 14px; font-size: 13px; }
|
|
.avatar.xs { width: 22px; height: 22px; border-radius: 7px; font-size: 8.5px; letter-spacing: 0; }
|
|
.avatar.xs[data-state]::after { width: 8px; height: 8px; border-width: 2px; right: -2px; bottom: -2px; }
|
|
.avatar[data-state]::after {
|
|
content: ""; position: absolute; right: -3px; bottom: -3px; width: 12px; height: 12px; border-radius: 50%;
|
|
border: 2.5px solid var(--surface); background: var(--faint);
|
|
}
|
|
.avatar[data-state="working"]::after { background: var(--green); animation: pulse 1.6s infinite; }
|
|
.avatar[data-state="online"]::after { background: var(--green); }
|
|
.avatar[data-state="stopped"]::after { background: var(--red); }
|
|
.avatar[data-state="trial"]::after { background: var(--amber); }
|
|
.claude { --avatar: #cc8a65; } .codex { --avatar: #4688e6; } .deepseek { --avatar: #8071ce; }
|
|
.opencode { --avatar: #39a48d; } .kimi { --avatar: #b45b9b; } .user { --avatar: #536070; }
|
|
|
|
/* ------------------------------------------------------------------------------------------------ cards */
|
|
.card {
|
|
background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-3); box-shadow: var(--shadow-2);
|
|
min-width: 0; animation: rise .35s var(--ease) both;
|
|
}
|
|
.card > header {
|
|
display: flex; align-items: center; gap: 10px; padding: 14px 18px 12px; border-bottom: 1px solid var(--line);
|
|
}
|
|
.card > header h2 { margin: 0; font: 650 13.5px/1.3 var(--font-display); letter-spacing: -.1px; display: flex; align-items: center; gap: 8px; }
|
|
.card > header h2 svg.i { color: var(--muted); }
|
|
.card > header .count { font-size: 12px; color: var(--muted); }
|
|
.card .body { padding: 14px 18px 16px; }
|
|
.card .body.flush { padding: 0; }
|
|
.scroll { overflow-x: auto; }
|
|
|
|
/* ------------------------------------------------------------------------------------------------ forms */
|
|
input[type=text], input[type=search], select, textarea {
|
|
background: var(--surface-2); border: 1px solid var(--line-2); border-radius: var(--r-1); padding: 7px 10px;
|
|
font-size: 13px; min-width: 0; color: var(--text); transition: border-color .15s, box-shadow .15s, background .15s;
|
|
}
|
|
input[type=text]:focus, input[type=search]:focus, select:focus, textarea:focus {
|
|
outline: none; border-color: var(--accent); box-shadow: var(--ring); background: var(--surface);
|
|
}
|
|
select { padding-right: 26px; appearance: none; background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%), linear-gradient(135deg, var(--muted) 50%, transparent 50%); background-position: calc(100% - 14px) 52%, calc(100% - 9px) 52%; background-size: 5px 5px; background-repeat: no-repeat; }
|
|
textarea { width: 100%; resize: vertical; min-height: 72px; line-height: 1.5; }
|
|
input::placeholder, textarea::placeholder { color: var(--faint); }
|
|
.check { display: inline-flex; align-items: center; gap: 7px; font-size: 12.5px; color: var(--text-2); cursor: pointer; user-select: none; }
|
|
.check input { accent-color: var(--action); width: 15px; height: 15px; margin: 0; }
|
|
.switch { position: relative; display: inline-flex; align-items: center; gap: 9px; cursor: pointer; font-size: 12.5px; color: var(--text-2); user-select: none; }
|
|
.switch input { appearance: none; width: 34px; height: 20px; border-radius: 999px; background: var(--line-2); position: relative; margin: 0; cursor: pointer; transition: background .2s; }
|
|
.switch input::after { content: ""; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%; background: #fff; box-shadow: var(--shadow-1); transition: transform .2s var(--ease); }
|
|
.switch input:checked { background: var(--action); }
|
|
.switch input:checked::after { transform: translateX(14px); }
|
|
.toggle-chip { position: relative; display: inline-flex; }
|
|
.toggle-chip input { position: absolute; opacity: 0; inset: 0; cursor: pointer; margin: 0; }
|
|
.toggle-chip span { display: inline-flex; align-items: center; gap: 5px; height: 26px; padding: 0 10px; border-radius: 999px; border: 1px solid var(--line-2); font-size: 11.5px; font-weight: 600; color: var(--muted); background: var(--surface); transition: all .15s; }
|
|
.toggle-chip input:checked + span { background: var(--accent-soft); color: var(--accent-ink); border-color: transparent; }
|
|
.toggle-chip input:focus-visible + span { box-shadow: var(--ring); }
|
|
.toggle-chip.danger input:checked + span { background: var(--red-soft); color: var(--red); }
|
|
|
|
/* ------------------------------------------------------------------------------------------------ tables */
|
|
table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
|
th { text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: .7px; color: var(--faint); font-weight: 650; padding: 8px 12px; border-bottom: 1px solid var(--line); background: var(--surface-2); }
|
|
td { padding: 10px 12px; border-top: 1px solid var(--line); vertical-align: top; overflow-wrap: anywhere; }
|
|
tbody tr:first-child td { border-top: 0; }
|
|
tr.click { cursor: pointer; }
|
|
tr.click:hover td, tbody tr:hover td { background: var(--surface-2); }
|
|
td.mono { white-space: nowrap; color: var(--muted); }
|
|
|
|
/* ------------------------------------------------------------------------------------------------ empty states */
|
|
.empty { display: grid; justify-items: center; gap: 6px; text-align: center; color: var(--muted); padding: 22px 12px; font-size: 13px; }
|
|
.empty strong { color: var(--text-2); font-size: 14px; }
|
|
.empty svg.i { width: 26px; height: 26px; color: var(--faint); stroke-width: 1.5; }
|
|
|
|
/* ------------------------------------------------------------------------------------------------ dialogs */
|
|
dialog {
|
|
border: 1px solid var(--line); border-radius: var(--r-4); background: var(--surface); color: var(--text);
|
|
padding: 0; width: min(760px, calc(100vw - 32px)); box-shadow: var(--shadow-3); max-height: 86vh;
|
|
}
|
|
dialog[open] { animation: rise .25s var(--ease); }
|
|
dialog::backdrop { background: rgba(8, 12, 20, .5); backdrop-filter: blur(3px); }
|
|
dialog > header, dialog form > header { display: flex; align-items: center; gap: 10px; padding: 16px 20px; border-bottom: 1px solid var(--line); }
|
|
dialog h2 { margin: 0; font: 650 15px/1.3 var(--font-display); flex: 1; min-width: 0; overflow-wrap: anywhere; }
|
|
dialog .body { padding: 16px 20px 20px; overflow: auto; }
|
|
dialog footer { display: flex; justify-content: flex-end; gap: 8px; padding: 14px 20px; border-top: 1px solid var(--line); background: var(--surface-2); border-radius: 0 0 var(--r-4) var(--r-4); }
|
|
|
|
/* ------------------------------------------------------------------------------------------------ segmented radio */
|
|
.segmented { display: inline-flex; padding: 3px; background: var(--surface-3); border-radius: 10px; gap: 2px; }
|
|
.segmented label { position: relative; }
|
|
.segmented input { position: absolute; opacity: 0; inset: 0; margin: 0; cursor: pointer; }
|
|
.segmented span { display: flex; align-items: center; gap: 6px; padding: 6px 12px; border-radius: 8px; font-size: 12.5px; font-weight: 600; color: var(--muted); transition: all .15s; }
|
|
.segmented input:checked + span { background: var(--surface); color: var(--text); box-shadow: var(--shadow-1); }
|
|
.segmented input:focus-visible + span { box-shadow: var(--ring); }
|
|
|
|
/* ------------------------------------------------------------------------------------------------ availability pill */
|
|
.avail {
|
|
display: inline-flex; align-items: center; gap: 8px; height: 34px; padding: 0 14px 0 12px; border-radius: 999px; white-space: nowrap;
|
|
border: 1px solid var(--line-2); background: var(--surface); color: var(--text-2); font-size: 12.5px; font-weight: 650; box-shadow: var(--shadow-1);
|
|
transition: background .15s, border-color .15s;
|
|
}
|
|
.avail::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--green); box-shadow: 0 0 0 3px var(--green-soft); flex-shrink: 0; }
|
|
.avail:hover { background: var(--surface-2); border-color: var(--faint); }
|
|
.avail.away { color: var(--amber); border-color: color-mix(in srgb, var(--amber) 45%, var(--line-2)); }
|
|
.avail.away::before { background: var(--amber); box-shadow: 0 0 0 3px var(--amber-soft); }
|
|
.avail.dnd { color: var(--red); border-color: color-mix(in srgb, var(--red) 45%, var(--line-2)); }
|
|
.avail.dnd::before { background: var(--red); box-shadow: 0 0 0 3px var(--red-soft); }
|
|
|
|
/* ------------------------------------------------------------------------------------------------ misc shared */
|
|
.working { display: inline-flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--green); font-weight: 650; }
|
|
.working::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--green); animation: pulse 1.4s infinite; }
|
|
.kbd { font: 600 11px var(--mono); padding: 1px 6px; border: 1px solid var(--line-2); border-bottom-width: 2px; border-radius: 5px; color: var(--muted); background: var(--surface); }
|
|
.divider { height: 1px; background: var(--line); margin: 12px 0; }
|
|
|
|
/* ------------------------------------------------------------------------------------------------ the sidebar's colours */
|
|
/* Inside a sidebar the general tokens point at the theme's side colours, so everything in it follows the theme. */
|
|
.side, .sidebar {
|
|
--text: var(--side-text); --text-2: var(--side-text); --muted: var(--side-muted); --faint: var(--side-muted);
|
|
--line: var(--side-line); --line-2: var(--side-line);
|
|
--surface: var(--side-solid); --surface-2: var(--side-hover); --surface-3: var(--side-hover);
|
|
--accent: var(--side-accent); --accent-soft: var(--side-active); --accent-ink: var(--side-active-text);
|
|
--green: var(--side-green); --green-soft: var(--side-green-soft);
|
|
color: var(--text); background: var(--side);
|
|
}
|
|
.side .segmented input:checked + span, .sidebar .segmented input:checked + span { background: var(--side-active); color: var(--side-active-text); box-shadow: none; }
|
|
.theme-btn {
|
|
display: inline-flex; align-items: center; gap: 8px; height: 32px; padding: 0 11px; min-width: 0; border-radius: 9px;
|
|
border: 1px solid var(--line-2); background: var(--surface-2); color: var(--text-2); font-size: 12.5px; font-weight: 600;
|
|
white-space: nowrap; overflow: hidden; text-overflow: ellipsis; transition: background .15s, border-color .15s;
|
|
}
|
|
.theme-btn:hover { background: var(--surface-3); border-color: var(--accent); }
|
|
.theme-btn svg.i { color: var(--accent); }
|
|
|
|
/* ------------------------------------------------------------------------------------------------ the theme picker */
|
|
.theme-dialog { width: min(860px, calc(100vw - 32px)); }
|
|
.theme-row { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin-bottom: 18px; }
|
|
.theme-row .field-label { margin: 0; }
|
|
.theme-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 172px), 1fr)); gap: 12px; }
|
|
.tcard {
|
|
display: flex; flex-direction: column; gap: 9px; padding: 8px 8px 10px; text-align: left; color: var(--text);
|
|
border: 1px solid var(--line); border-radius: var(--r-2); background: var(--surface-2);
|
|
transition: border-color .15s, transform .15s var(--ease), box-shadow .15s;
|
|
}
|
|
.tcard:hover { border-color: var(--line-2); transform: translateY(-2px); box-shadow: var(--shadow-2); }
|
|
.tcard[aria-pressed="true"] { border-color: var(--accent); box-shadow: var(--ring); }
|
|
.tname { display: flex; align-items: baseline; justify-content: space-between; gap: 6px; padding: 0 3px; font-size: 13px; font-weight: 650; }
|
|
.tprev {
|
|
display: grid; grid-template-columns: 24% 1fr; grid-template-rows: 13px 1fr; height: 82px; overflow: hidden;
|
|
border-radius: 8px; border: 1px solid var(--p-line); background: var(--p-bg);
|
|
}
|
|
.tprev i, .tprev b, .tprev em { display: block; font-style: normal; }
|
|
.tprev .ps { grid-row: 1 / 3; background: var(--p-side); padding: 16px 6px 0; display: flex; flex-direction: column; gap: 6px; }
|
|
.tprev .ps b { height: 4px; border-radius: 2px; background: var(--p-side-accent); opacity: .45; }
|
|
.tprev .ps b:first-child { opacity: 1; }
|
|
.tprev .ph { background: var(--p-header); border-bottom: 1px solid var(--p-line); }
|
|
.tprev .pb { padding: 8px; display: flex; }
|
|
.tprev .pc { flex: 1; display: flex; flex-direction: column; gap: 5px; padding: 7px; border-radius: 6px; background: var(--p-surface); border: 1px solid var(--p-line); }
|
|
.tprev .pc b { height: 4px; width: 78%; border-radius: 2px; background: var(--p-text); opacity: .75; }
|
|
.tprev .pc b + b { width: 52%; opacity: .4; }
|
|
.tprev .pc em { margin-top: auto; height: 10px; width: 40%; border-radius: 5px; background: var(--p-action); }
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
*, *::before, *::after { animation: none !important; transition: none !important; }
|
|
}
|