design: overhaul pass — amber accent, home recomposition, motion layer

Lever 1 (color): Replace #5B7CFA AI-blue with electric amber #E8821C across
all accent tokens, tile tones, logo glows, and hardcoded rgba values. Dark
text on amber primary buttons for WCAG AA contrast.

Lever 2 (home): Collapse centered logo hero into compact left-aligned header.
Split tile grid into primary ops row (Library, Recorders, Playout) + secondary
4-col row (Downloads, Jobs, Dashboard, Settings) with reduced visual weight.

Lever 3 (typography): Remove v1.2.0 from sidebar. Fix em-dashes to hyphens or
periods across all visible UI strings (option labels, body copy, error messages).
Topbar height 56px -> 48px.

Lever 4 (motion): Staggered entry animation for launcher tiles
(prefers-reduced-motion gated). Tactile scale(0.97) on primary/record buttons.
Smooth 150ms nav active-item transitions.

Lever 5 (blocks): Jobs stats row semantic card variants (amber glow when
active, red border when failed, quiet muted style for Total).

Lever 6 (spacing): Topbar 48px, launcher inner gap tightened, status left-aligned.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
Zac Gaetano 2026-06-02 08:20:15 -04:00
parent b875376887
commit e54b8403e7
10 changed files with 196 additions and 136 deletions

1
.claude/launch.json Normal file
View file

@ -0,0 +1 @@
{"version":"0.0.1","configurations":[{"name":"web-ui","runtimeExecutable":"npx","runtimeArgs":["serve","services/web-ui/public","--listen","47434","--no-clipboard"],"port":47434}]}

View file

@ -432,10 +432,10 @@ function NewRecorderModal({ open, onClose }) {
<select className="field-input" value={growingOn ? 'h264_growing' : recCodec}
onChange={e => setRecCodec(e.target.value)} disabled={growingOn}
style={{ appearance: 'auto', opacity: growingOn ? 0.6 : 1 }}>
{growingOn && <option value="h264_growing">XDCAM HD422 (MXF OP1a) growing</option>}
<option value="hevc_nvenc">All-Intra HEVC (NVENC) GPU, growing</option>
<option value="h264_nvenc">H.264 (NVENC) GPU</option>
<option value="prores_hq">ProRes 422 HQ 4:2:2 CPU</option>
{growingOn && <option value="h264_growing">XDCAM HD422 (MXF OP1a, growing)</option>}
<option value="hevc_nvenc">All-Intra HEVC (NVENC, GPU, growing)</option>
<option value="h264_nvenc">H.264 (NVENC, GPU)</option>
<option value="prores_hq">ProRes 422 HQ (4:2:2, CPU)</option>
<option value="prores">ProRes 422</option>
<option value="prores_lt">ProRes 422 LT</option>
<option value="prores_proxy">ProRes 422 Proxy</option>
@ -457,8 +457,8 @@ function NewRecorderModal({ open, onClose }) {
) : (
<Field label="Bitrate" value="Quality-based (profile)" select />
)}
<Field label="Resolution" value="Auto — from source" select />
<Field label="Framerate" value="Auto — from source" select />
<Field label="Resolution" value="Auto (from source)" select />
<Field label="Framerate" value="Auto (from source)" select />
{/* #3: warn when the configured bitrate exceeds the probed source
bitrate re-encoding above source adds storage, not quality. */}
{codecUsesBitrate && (() => {

View file

@ -439,7 +439,7 @@ function UserPolicyRow({ user: u, expanded, onToggle, onChangeRole, onResetTotp
{!loading && !accessErr && (u.role === 'admin') && (
<div style={{ padding: '12px 0', fontSize: 12.5, color: 'var(--text-3)', display: 'flex', alignItems: 'center', gap: 6 }}>
<Icon name="check" size={12} style={{ color: 'var(--success)' }} />
Admin full access to every project.
Admin: full access to every project.
</div>
)}
{!loading && !accessErr && u.role !== 'admin' && (
@ -1883,7 +1883,7 @@ function AddNodeModal({ onClose }) {
<React.Fragment>
<div style={{ marginBottom: 10, padding: 10, border: '1px solid var(--accent)', background: 'var(--accent-soft)', borderRadius: 6 }}>
<div style={{ fontSize: 11, fontWeight: 600, color: 'var(--accent-text)' }}>
This token is shown only once copy the command now.
This token is shown only once. Copy the command now.
</div>
</div>
<code className="mono" style={{ display: 'block', background: 'var(--bg-2)', padding: 12, borderRadius: 6, fontSize: 11.5, lineHeight: 1.5, wordBreak: 'break-all', whiteSpace: 'pre-wrap' }}>{command}</code>
@ -2060,7 +2060,7 @@ function TotpSection() {
</div>
<div>
<div style={{ fontSize: 12, color: 'var(--text-2)', marginBottom: 8 }}>
Scan the QR with Google Authenticator, Authy, or 1Password or enter this secret manually:
Scan the QR with Google Authenticator, Authy, or 1Password, or enter this secret manually:
</div>
<div className="mono" style={{ fontSize: 12, background: 'var(--bg-2)', padding: '6px 10px', borderRadius: 5, wordBreak: 'break-all', marginBottom: 12 }}>{enroll.secret}</div>
<div className="field">
@ -2264,7 +2264,7 @@ function StorageWarningBanner() {
}}>
<Icon name="alert" size={20} style={{ color: 'var(--danger)', flexShrink: 0, marginTop: 1 }} />
<div style={{ fontSize: 13, fontWeight: 700, letterSpacing: '0.02em', lineHeight: 1.5, color: 'var(--text-1)' }}>
WARNING THESE SETTINGS ARE MEANT TO BE SET ONCE AT INITIAL DEPLOYMENT.
WARNING: THESE SETTINGS ARE MEANT TO BE SET ONCE AT INITIAL DEPLOYMENT.
CHANGING STORAGE PATHS MID-CYCLE CAN CAUSE DATABASE CORRUPTION AND FILE LOSS.
PLEASE USE WITH CAUTION.
</div>

View file

@ -76,7 +76,7 @@
style={{
width: '100%',
background: disabled ? 'var(--bg-3)' : 'var(--accent)',
color: '#fff',
color: disabled ? 'var(--text-3)' : '#0a0c10',
border: 'none',
borderRadius: 4,
padding: '9px',
@ -210,7 +210,7 @@
// An expired/used ticket means the user must start over.
if (r.status === 401 && /ticket/.test(body.error || '')) {
setTicket(null); setCode(''); setPassword('');
setError('Session timed out — please sign in again.');
setError('Session timed out. Please sign in again.');
} else {
setError(body.error || ('Verification failed: ' + r.status));
}

View file

@ -134,7 +134,6 @@ function Home({ navigate }) {
<div className="launcher-inner">
<div className="launcher-hero">
<span className="launcher-logo-wrap">
<span className="launcher-logo-pulse" aria-hidden="true" />
<img
className="launcher-logo"
src="img/dragon-logo.png"
@ -142,15 +141,17 @@ function Home({ navigate }) {
draggable="false"
/>
</span>
<h1 className="launcher-wordmark">DRAGONFLIGHT</h1>
<p className="launcher-kicker">Let's Create</p>
<p className="launcher-tagline">
Media Asset Management &amp; Production Platform
</p>
<div className="launcher-hero-text">
<h1 className="launcher-wordmark">DRAGONFLIGHT</h1>
<p className="launcher-tagline">
Media Asset Management &amp; Production Platform
</p>
</div>
</div>
{/* Primary ops: Library, Recorders, Playout */}
<div className="launcher-grid">
{tiles.map(t => (
{tiles.slice(0, 3).map(t => (
<button
key={t.id}
className={'launcher-tile tone-' + t.tone}
@ -167,38 +168,55 @@ function Home({ navigate }) {
</span>
</button>
))}
</div>
{/* Secondary: Downloads, Jobs, Dashboard, Settings — smaller tiles */}
<div className="launcher-settings-row">
{tiles.slice(3).map(t => (
<button
key={t.id}
className={'launcher-tile tone-' + t.tone + ' launcher-tile-secondary'}
onClick={() => t.id === '__downloads' ? setShowDownloads(true) : navigate(t.id)}
>
<span className="launcher-tile-icon">
<Icon name={t.icon} size={20} />
</span>
<span className="launcher-tile-label">{t.label}</span>
<span className="launcher-tile-sub">{t.sub}</span>
<span className="launcher-tile-desc">{t.desc}</span>
<span className="launcher-tile-arrow">
<Icon name="arrowRight" size={13} />
</span>
</button>
))}
<button
className="launcher-tile tone-ghost launcher-tile-secondary"
onClick={() => navigate('dashboard')}
>
<span className="launcher-tile-icon">
<Icon name="layout" size={22} />
<Icon name="layout" size={20} />
</span>
<span className="launcher-tile-label">Dashboard</span>
<span className="launcher-tile-sub">Operations view</span>
<span className="launcher-tile-desc">
Recent activity, job queue, cluster health.
Live recorders, job queue, cluster health.
</span>
<span className="launcher-tile-arrow">
<Icon name="arrowRight" size={14} />
<Icon name="arrowRight" size={13} />
</span>
</button>
</div>
<div className="launcher-settings-row">
<button
className={'launcher-tile tone-' + settingsTile.tone + ' launcher-tile-settings'}
className={'launcher-tile tone-' + settingsTile.tone + ' launcher-tile-secondary launcher-tile-settings'}
onClick={() => navigate(settingsTile.id)}
>
<span className="launcher-tile-icon">
<Icon name={settingsTile.icon} size={26} />
<Icon name={settingsTile.icon} size={20} />
</span>
<span className="launcher-tile-label">{settingsTile.label}</span>
<span className="launcher-tile-sub">{settingsTile.sub}</span>
<span className="launcher-tile-desc">{settingsTile.desc}</span>
<span className="launcher-tile-arrow">
<Icon name="arrowRight" size={14} />
<Icon name="arrowRight" size={13} />
</span>
</button>
</div>
@ -340,7 +358,7 @@ function DownloadsModal({ onClose }) {
<span className="btn primary sm" aria-disabled="true" style={{ opacity: 0.5, pointerEvents: 'none' }}>
<Icon name="download" />Teams ISO (.exe)
</span>
<span style={{ fontSize: 11.5, color: 'var(--text-3)' }}>coming soon file pending</span>
<span style={{ fontSize: 11.5, color: 'var(--text-3)' }}>coming soon, file pending</span>
</>
)}
</div>
@ -540,7 +558,7 @@ function Dashboard({ navigate }) {
const alerts = [
...erroredRecorders.map(r => ({
key: 'r-' + r.id, sev: 'danger',
title: r.name + ' recorder error',
title: r.name + ': recorder error',
meta: r.error_message || r.url || 'signal lost',
action: 'Reconnect', to: 'recorders',
})),
@ -795,7 +813,7 @@ function OnAirEmpty({ sources, onStart }) {
<span className="onair-empty-icon"><Icon name="record" size={18} /></span>
<div className="onair-empty-copy">
<div className="onair-empty-title">Nothing on air</div>
<div className="onair-empty-sub">All recorders are idle start a source to begin capturing.</div>
<div className="onair-empty-sub">All recorders are idle. Start a source to begin capturing.</div>
</div>
<button className="btn primary" onClick={onStart}><Icon name="plus" size={14} />Start a recorder</button>
</div>

View file

@ -172,26 +172,26 @@ function Jobs({ navigate }) {
<div className="page-body">
<div className="jobs-stats">
<div className="stat-card">
<div className={'stat-card' + (counts.running > 0 ? ' stat-card--active' : '')}>
<div className="label">Running</div>
<div className="value">{counts.running}</div>
<div className="delta">{counts.queued} queued</div>
</div>
<div className="stat-card">
<div className="label">Completed</div>
<div className="value">{counts.done}</div>
<div className="value stat-value--muted">{counts.done}</div>
<div className="delta">Total done</div>
</div>
<div className="stat-card">
<div className={'stat-card' + (counts.failed > 0 ? ' stat-card--failed' : '')}>
<div className="label">Failed</div>
<div className="value">{counts.failed}</div>
<div className={'value' + (counts.failed > 0 ? ' stat-value--danger' : ' stat-value--muted')}>{counts.failed}</div>
<div className={'delta' + (counts.failed > 0 ? ' delta-warn' : '')}>
{counts.failed > 0 ? 'Needs attention' : 'All clear'}
</div>
</div>
<div className="stat-card">
<div className="stat-card stat-card--quiet">
<div className="label">Total jobs</div>
<div className="value">{counts.all}</div>
<div className="value stat-value--muted">{counts.all}</div>
<div className="delta muted delta-tiny">Updated {Math.round((Date.now()-lastFetch)/1000)}s ago</div>
</div>
</div>

View file

@ -167,7 +167,6 @@ function Sidebar({ active, onNavigate, me, collapsed, onToggle }) {
>
<img className="brand-logo" src="img/dragon-logo.png" alt="Dragonflight" draggable="false" />
<div className="brand-name">Dragonflight</div>
<div className="brand-sub">v1.2.0</div>
</div>
<button
className="icon-btn sidebar-toggle"

View file

@ -253,10 +253,10 @@
/* Convert the dark logo to white so it pops on the dark sidebar.
brightness(0) collapses everything to black, invert(1) flips to white.
Works on both the original dark PNG and any transparent white PNG. */
filter: brightness(0) invert(1) drop-shadow(0 0 6px rgba(91, 124, 250, 0.25));
filter: brightness(0) invert(1) drop-shadow(0 0 6px rgba(232, 130, 28, 0.30));
}
.sidebar-header:hover .brand-logo {
filter: brightness(0) invert(1) drop-shadow(0 0 10px rgba(91, 124, 250, 0.45));
filter: brightness(0) invert(1) drop-shadow(0 0 10px rgba(232, 130, 28, 0.55));
}
/* ============================================================
@ -268,139 +268,132 @@
overflow-y: auto;
overflow-x: hidden;
background:
radial-gradient(1100px 600px at 50% 0%, rgba(91, 124, 250, 0.10), transparent 65%),
radial-gradient(900px 600px at 50% 100%, rgba(181, 124, 250, 0.06), transparent 60%),
radial-gradient(1100px 500px at 50% -10%, rgba(232, 130, 28, 0.07), transparent 60%),
var(--bg-0);
display: flex;
align-items: flex-start;
justify-content: center;
padding: 48px 32px 64px;
padding: 40px 32px 64px;
}
.launcher-inner {
width: 100%;
max-width: 1160px;
display: flex;
flex-direction: column;
align-items: center;
gap: 40px;
align-items: stretch;
gap: 32px;
}
.launcher-hero {
display: flex;
flex-direction: column;
flex-direction: row;
align-items: center;
gap: 14px;
text-align: center;
margin-top: 8px;
gap: 18px;
text-align: left;
padding-bottom: 24px;
border-bottom: 1px solid var(--border);
}
/* Logo wrapper holds the animated pulse halo behind the image. */
/* Logo wrapper — compact, left-aligned mark. */
.launcher-logo-wrap {
position: relative;
display: inline-grid;
place-items: center;
width: 180px;
height: 180px;
}
.launcher-logo-pulse {
position: absolute;
left: 50%;
top: 50%;
width: 200px;
height: 200px;
transform: translate(-50%, -50%) scale(0.85);
border-radius: 50%;
pointer-events: none;
z-index: 0;
background: radial-gradient(
circle at center,
color-mix(in srgb, var(--accent) 55%, transparent) 0%,
color-mix(in srgb, var(--accent) 22%, transparent) 38%,
transparent 68%
);
filter: blur(2px);
animation: launcherLogoPulse 3.4s ease-in-out infinite;
}
@keyframes launcherLogoPulse {
0%, 100% { transform: translate(-50%, -50%) scale(0.82); opacity: 0.45; }
50% { transform: translate(-50%, -50%) scale(1.08); opacity: 0.9; }
width: 52px;
height: 52px;
flex-shrink: 0;
}
.launcher-logo-pulse { display: none; }
.launcher-logo {
position: relative;
z-index: 1;
width: 180px;
height: 180px;
width: 52px;
height: 52px;
object-fit: contain;
/* Convert to white - same approach as .brand-logo. */
filter:
brightness(0) invert(1)
drop-shadow(0 0 24px rgba(91, 124, 250, 0.28))
drop-shadow(0 0 48px rgba(91, 124, 250, 0.18));
animation: launcherLogoIn 600ms cubic-bezier(0.2, 0.7, 0.2, 1) both;
drop-shadow(0 0 8px rgba(232, 130, 28, 0.35));
animation: launcherLogoIn 400ms cubic-bezier(0.2, 0.7, 0.2, 1) both;
}
@keyframes launcherLogoIn {
from { opacity: 0; transform: translateY(8px) scale(0.96); }
to { opacity: 1; transform: translateY(0) scale(1); }
from { opacity: 0; transform: scale(0.88); }
to { opacity: 1; transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
.launcher-logo-pulse { animation: none; opacity: 0.5; }
.launcher-logo { animation: none; }
}
.launcher-hero-text {
display: flex;
flex-direction: column;
gap: 3px;
flex: 1;
min-width: 0;
}
.launcher-wordmark {
margin: 0;
font-size: 44px;
font-size: 22px;
font-weight: 700;
letter-spacing: 0.12em;
letter-spacing: 0.08em;
line-height: 1;
color: var(--text-1);
text-shadow: 0 0 32px rgba(91, 124, 250, 0.15);
}
.launcher-kicker {
margin: 2px 0 0;
color: var(--accent);
font-size: 12px;
font-weight: 600;
letter-spacing: 0.22em;
text-transform: uppercase;
text-shadow: none;
}
.launcher-kicker { display: none; }
.launcher-tagline {
margin: 0;
color: var(--text-3);
font-size: 13.5px;
letter-spacing: 0.02em;
white-space: nowrap;
}
@media (max-width: 480px) {
.launcher-tagline { font-size: 11.5px; letter-spacing: 0; }
}
.launcher-tagline-motto {
margin-top: 6px;
color: var(--accent);
font-style: italic;
font-size: 15px;
letter-spacing: 0.04em;
font-size: 12.5px;
letter-spacing: 0.01em;
}
.launcher-tagline-motto { display: none; }
.launcher-grid {
width: 100%;
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 14px;
gap: 12px;
}
@media (max-width: 960px) { .launcher-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 620px) { .launcher-grid { grid-template-columns: 1fr; } }
/* Settings sits on its own centered row beneath the main grid. */
/* Secondary tiles: smaller vertical footprint, quieter treatment. */
.launcher-tile-secondary {
min-height: 120px;
padding: 16px 18px 18px;
}
.launcher-tile-secondary .launcher-tile-icon {
width: 34px;
height: 34px;
border-radius: 8px;
margin-bottom: 4px;
}
.launcher-tile-secondary .launcher-tile-icon svg {
width: 17px;
height: 17px;
}
.launcher-tile-secondary .launcher-tile-label {
font-size: 15px;
}
.launcher-tile-secondary .launcher-tile-desc {
font-size: 12px;
}
/* Settings row: holds the 4 secondary tiles. */
.launcher-settings-row {
width: 100%;
display: flex;
justify-content: center;
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 12px;
}
.launcher-tile-settings {
width: 100%;
max-width: calc((100% - 28px) / 3);
max-width: 100%;
}
@media (max-width: 960px) {
.launcher-settings-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 620px) {
.launcher-settings-row { grid-template-columns: 1fr; }
}
@media (max-width: 960px) { .launcher-tile-settings { max-width: calc((100% - 14px) / 2); } }
@media (max-width: 620px) { .launcher-tile-settings { max-width: 100%; } }
.launcher-tile {
position: relative;
@ -507,10 +500,10 @@
/* Tone variants - colour the icon tile + halo, leave the body text
neutral so the tile reads as a button, not a banner. */
.launcher-tile.tone-accent {
--tile-tint: rgba(91, 124, 250, 0.18);
--tile-tint: rgba(232, 130, 28, 0.15);
--tile-icon-bg: var(--accent-soft);
--tile-icon-fg: var(--accent-text);
--tile-icon-border: rgba(91, 124, 250, 0.30);
--tile-icon-border: rgba(232, 130, 28, 0.28);
}
.launcher-tile.tone-live {
--tile-tint: rgba(255, 59, 48, 0.18);
@ -557,7 +550,7 @@
gap: 16px;
align-items: center;
flex-wrap: wrap;
justify-content: center;
justify-content: flex-start;
margin-top: 4px;
}
.launcher-status-pip {
@ -585,12 +578,46 @@
.launcher-footer {
margin-top: 20px;
text-align: center;
text-align: left;
font-size: 11px;
letter-spacing: 0.04em;
color: var(--text-4);
}
/* ============================================================
Motion layer entry stagger for launcher tiles.
Respects prefers-reduced-motion.
============================================================ */
@media (prefers-reduced-motion: no-preference) {
.launcher-tile {
animation: tileEnter 360ms cubic-bezier(0.16, 1, 0.3, 1) both;
}
.launcher-grid .launcher-tile:nth-child(1) { animation-delay: 60ms; }
.launcher-grid .launcher-tile:nth-child(2) { animation-delay: 110ms; }
.launcher-grid .launcher-tile:nth-child(3) { animation-delay: 160ms; }
.launcher-grid .launcher-tile:nth-child(4) { animation-delay: 210ms; }
.launcher-grid .launcher-tile:nth-child(5) { animation-delay: 250ms; }
.launcher-grid .launcher-tile:nth-child(6) { animation-delay: 290ms; }
.launcher-settings-row .launcher-tile { animation-delay: 320ms; }
@keyframes tileEnter {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
}
/* Tactile press feedback on high-stakes operational buttons. */
.btn-record:active,
button.btn.primary:active {
transform: scale(0.97);
transition: transform 80ms ease-out;
}
/* Smooth active-item transition in sidebar nav. */
.nav-item {
transition: background 150ms ease-out, color 150ms ease-out;
}
/* ============================================================
Recorder row - signal indicator with a pulsing dot when
actually receiving frames. Closes part of #2.
@ -640,7 +667,7 @@
stroke-width: 1.5;
}
.bmd-card-trace {
stroke: rgba(91, 124, 250, 0.12);
stroke: rgba(232, 130, 28, 0.10);
stroke-width: 0.5;
fill: none;
}

View file

@ -171,7 +171,7 @@
gap: 20px;
align-items: stretch;
background:
radial-gradient(ellipse at top, rgba(91,124,250,0.05), transparent 60%),
radial-gradient(ellipse at top, rgba(232,130,28,0.05), transparent 60%),
var(--bg-0);
}
.decklink-card-face {
@ -971,7 +971,7 @@
border-bottom: 1px solid var(--border);
}
.epg-week-day:last-child { border-bottom: 0; }
.epg-week-day.today { background: linear-gradient(180deg, rgba(91,124,250,0.04) 0%, transparent 80%); }
.epg-week-day.today { background: linear-gradient(180deg, rgba(232,130,28,0.04) 0%, transparent 80%); }
.epg-week-dayhead {
display: flex; align-items: baseline; gap: 10px;
padding: 10px 20px 6px;
@ -1060,6 +1060,20 @@
line-height: 1.1;
}
/* Stat card semantic variants */
.stat-card--active {
border-color: var(--accent-soft-2);
background: linear-gradient(180deg, var(--accent-soft) 0%, var(--bg-1) 60%);
}
.stat-card--active .value { color: var(--accent); }
.stat-card--failed {
border-color: rgba(255,91,91,0.25);
background: linear-gradient(180deg, var(--danger-soft) 0%, var(--bg-1) 60%);
}
.stat-card--quiet { opacity: 0.75; }
.stat-value--muted { color: var(--text-2); }
.stat-value--danger { color: var(--danger); }
/* ========== Settings ========== */
.settings-nav {
display: flex; flex-direction: column; gap: 2px;
@ -1222,7 +1236,7 @@
.token-tier.popular {
border-color: var(--accent);
background: linear-gradient(180deg, var(--accent-soft) 0%, var(--bg-1) 40%);
box-shadow: 0 0 24px rgba(91,124,250,0.10);
box-shadow: 0 0 24px rgba(232,130,28,0.12);
}
.token-tier-badge {
position: absolute;
@ -1345,7 +1359,7 @@
border-radius: 99px;
flex-shrink: 0;
}
.search-result-kind.kind-asset { color: #B4C3FF; border-color: rgba(91,124,250,0.25); }
.search-result-kind.kind-asset { color: #FECF8A; border-color: rgba(232,130,28,0.25); }
.search-result-kind.kind-project { color: #FFD89B; border-color: rgba(245,166,35,0.25); }
.search-result-kind.kind-recorder { color: #FFAFAF; border-color: rgba(255,91,91,0.25); }
.search-result-kind.kind-job { color: #9EE7D2; border-color: rgba(45,212,168,0.25); }

View file

@ -18,12 +18,12 @@
--text-3: #8B92A0; /* WCAG AA (#133) - was #6B7280, 4.06:1 vs --bg-0; now ~7.5:1 */
--text-4: #6B7280;
/* accent (blue, frame.io-ish) */
--accent: #5B7CFA;
--accent-hover: #6E8BFF;
--accent-soft: rgba(91, 124, 250, 0.14);
--accent-soft-2: rgba(91, 124, 250, 0.22);
--accent-text: #B4C3FF;
/* accent — electric amber (broadcast tally) */
--accent: #E8821C;
--accent-hover: #F09030;
--accent-soft: rgba(232, 130, 28, 0.14);
--accent-soft-2: rgba(232, 130, 28, 0.22);
--accent-text: #FECF8A;
/* status */
--success: #2DD4A8;
@ -51,7 +51,7 @@
/* sizing (density tweakable) */
--sidebar-w: 232px;
--row-h: 44px;
--topbar-h: 56px;
--topbar-h: 48px;
--gap: 16px;
/* fonts */
@ -244,10 +244,11 @@ a { color: inherit; text-decoration: none; }
.avatar {
width: 28px; height: 28px;
border-radius: 50%;
background: linear-gradient(135deg, #5B7CFA, #B57CFA);
background: var(--bg-3);
border: 1px solid var(--border-strong);
display: grid; place-items: center;
font-weight: 600; font-size: 11px;
color: white;
font-weight: 700; font-size: 11px;
color: var(--accent);
flex-shrink: 0;
}
.user-meta { display: flex; flex-direction: column; min-width: 0; flex: 1; }
@ -350,7 +351,7 @@ a { color: inherit; text-decoration: none; }
transition: background 80ms, border 80ms, color 80ms;
white-space: nowrap;
}
.btn.primary { background: var(--accent); color: white; }
.btn.primary { background: var(--accent); color: #0a0c10; }
.btn.primary:hover { background: var(--accent-hover); }
.btn.ghost { background: transparent; color: var(--text-2); }
.btn.ghost:hover { background: var(--hover); color: var(--text-1); }