fix(decnet_web/css): sweep rgba colour literals to tokens app-wide

Pre-this-commit, ~80 rgba() literals across 24 files were
hardcoding alert-red, warn-amber, info-cyan, panel-dark, and
white-text-with-alpha shades that bypassed the token cascade.
Net effect in light mode: the .eml/SESSREC drawers, AttackerDetail
verdict pills, MazeNET net-box headers, OPEN/REPLAY action
buttons, threat-intel cards, and all the dim 'whitish' overlays
stayed on their dark-mode hex values, producing the unreadable
panels in the screenshots.

Sweep maps each rgba colour family onto the existing token by
alpha bucket — rgba(13,17,23,*) -> var(--panel),
rgba(255,65,65,*) -> var(--alert)/-tint-10,
rgba(255,170,0,*) and rgba(224,160,64,*) -> var(--warn)/-tint-10,
rgba(0,200,255,*) -> var(--info)/-tint-10,
rgba(255,255,255,*) -> var(--fg-N)/var(--matrix-tint-N) by alpha.

VERDICT_TONE in AttackerDetail (MALICIOUS/SUSPICIOUS/BENIGN/
NO SIGNAL) was the worst offender — string literals
'#ff4d4d'/'#ffae42'/'#5fd07a'/rgba(255,255,255,0.4) baked into
inline JS styles. Now resolves at render time via var(--alert)/
var(--warn)/var(--ok)/var(--fg-4).

New tokens in :root:
 - --bg-color (alias of --bg) — drawers used this name with
   #0d1117 fallback that fired in every browser because nothing
   defined --bg-color. Adding the alias makes drawers re-tone.
 - --info / --info-tint-10 / --info-tint-30 — REPLAY buttons and
   any future neutral-secondary use.
 - --ok — semantic alias for 'verified good' (matrix in dark,
   emerald in light) so BENIGN pills stay readable across themes.

Login.css left intentionally — pre-auth surface, not themed.
This commit is contained in:
2026-05-09 03:48:05 -04:00
parent 11b2da7d54
commit aa0b22aacb
26 changed files with 142 additions and 119 deletions

View File

@@ -27,7 +27,7 @@ function decodeMeta(fields: Record<string, any>): Record<string, any> | null {
}
const Row: React.FC<{ label: string; value: React.ReactNode }> = ({ label, value }) => (
<div style={{ display: 'flex', gap: '12px', padding: '6px 0', borderBottom: '1px solid rgba(255,255,255,0.05)' }}>
<div style={{ display: 'flex', gap: '12px', padding: '6px 0', borderBottom: '1px solid var(--matrix-tint-5)' }}>
<div style={{ minWidth: '140px', color: 'var(--dim-color)', fontSize: '0.75rem', textTransform: 'uppercase' }}>{label}</div>
<div style={{ flex: 1, fontSize: '0.85rem', wordBreak: 'break-all' }}>{value ?? <span style={{ opacity: 0.4 }}></span>}</div>
</div>
@@ -120,9 +120,9 @@ const ArtifactDrawer: React.FC<ArtifactDrawerProps> = ({ decky, storedAs, fields
<div style={{
display: 'flex', alignItems: 'center', gap: '8px',
padding: '8px 12px', marginBottom: '16px',
border: '1px solid rgba(255, 170, 0, 0.3)',
backgroundColor: 'rgba(255, 170, 0, 0.05)',
fontSize: '0.75rem', color: '#ffaa00',
border: '1px solid var(--warn)',
backgroundColor: 'var(--warn-tint-10)',
fontSize: '0.75rem', color: 'var(--warn)',
}}>
<AlertTriangle size={14} />
Attacker-controlled content. Download at your own risk.
@@ -176,7 +176,7 @@ const ArtifactDrawer: React.FC<ArtifactDrawerProps> = ({ decky, storedAs, fields
<h3 style={{ fontSize: '0.8rem', letterSpacing: '0.1em', color: 'var(--dim-color)', marginBottom: '8px' }}>
CONCURRENT SESSIONS ({concurrent.length})
</h3>
<pre style={{ fontSize: '0.75rem', background: 'rgba(255,255,255,0.03)', padding: '8px', overflowX: 'auto' }}>
<pre style={{ fontSize: '0.75rem', background: 'var(--matrix-tint-5)', padding: '8px', overflowX: 'auto' }}>
{JSON.stringify(concurrent, null, 2)}
</pre>
</section>
@@ -187,7 +187,7 @@ const ArtifactDrawer: React.FC<ArtifactDrawerProps> = ({ decky, storedAs, fields
<h3 style={{ fontSize: '0.8rem', letterSpacing: '0.1em', color: 'var(--dim-color)', marginBottom: '8px' }}>
SS SNAPSHOT ({ssSnapshot.length})
</h3>
<pre style={{ fontSize: '0.75rem', background: 'rgba(255,255,255,0.03)', padding: '8px', overflowX: 'auto' }}>
<pre style={{ fontSize: '0.75rem', background: 'var(--matrix-tint-5)', padding: '8px', overflowX: 'auto' }}>
{JSON.stringify(ssSnapshot, null, 2)}
</pre>
</section>

View File

@@ -1292,10 +1292,10 @@ type IntelRow = {
};
const VERDICT_TONE: Record<string, { color: string; label: string }> = {
malicious: { color: '#ff4d4d', label: 'MALICIOUS' },
suspicious: { color: '#ffae42', label: 'SUSPICIOUS' },
benign: { color: '#5fd07a', label: 'BENIGN' },
unknown: { color: 'rgba(255,255,255,0.4)', label: 'NO SIGNAL' },
malicious: { color: 'var(--alert)', label: 'MALICIOUS' },
suspicious: { color: 'var(--warn)', label: 'SUSPICIOUS' },
benign: { color: 'var(--ok)', label: 'BENIGN' },
unknown: { color: 'var(--fg-4)', label: 'NO SIGNAL' },
};
const fmtTs = (iso?: string | null): string => {
@@ -1317,7 +1317,7 @@ const ProviderRow: React.FC<{
gridTemplateColumns: '160px 1fr auto',
gap: '12px',
padding: '10px 16px',
borderTop: '1px solid rgba(255,255,255,0.05)',
borderTop: '1px solid var(--matrix-tint-5)',
alignItems: 'center',
fontSize: '0.85rem',
}}>
@@ -1391,7 +1391,7 @@ const IntelPanel: React.FC<{ uuid: string }> = ({ uuid }) => {
alignItems: 'center',
gap: '12px',
padding: '14px 16px',
borderBottom: '1px solid rgba(255,255,255,0.05)',
borderBottom: '1px solid var(--matrix-tint-5)',
}}>
<Shield size={16} style={{ color: tone.color }} />
<span style={{
@@ -2310,11 +2310,11 @@ const AttackerDetail: React.FC = () => {
style={{
display: 'flex', alignItems: 'center', gap: '6px',
fontSize: '0.7rem',
backgroundColor: 'rgba(255, 170, 0, 0.1)',
backgroundColor: 'var(--warn-tint-10)',
padding: '2px 8px',
borderRadius: '4px',
border: '1px solid rgba(255, 170, 0, 0.5)',
color: '#ffaa00',
border: '1px solid var(--warn)',
color: 'var(--warn)',
cursor: 'pointer',
}}
>
@@ -2449,11 +2449,11 @@ const AttackerDetail: React.FC = () => {
style={{
display: 'flex', alignItems: 'center', gap: '6px',
fontSize: '0.7rem',
backgroundColor: 'rgba(255, 170, 0, 0.1)',
backgroundColor: 'var(--warn-tint-10)',
padding: '2px 8px',
borderRadius: '4px',
border: '1px solid rgba(255, 170, 0, 0.5)',
color: '#ffaa00',
border: '1px solid var(--warn)',
color: 'var(--warn)',
cursor: 'pointer',
}}
>
@@ -2532,11 +2532,11 @@ const AttackerDetail: React.FC = () => {
style={{
display: 'flex', alignItems: 'center', gap: '6px',
fontSize: '0.7rem',
backgroundColor: 'rgba(0, 200, 255, 0.1)',
backgroundColor: 'var(--info-tint-10)',
padding: '2px 8px',
borderRadius: '4px',
border: '1px solid rgba(0, 200, 255, 0.5)',
color: '#00c8ff',
border: '1px solid var(--info)',
color: 'var(--info)',
cursor: 'pointer',
}}
>

View File

@@ -44,7 +44,7 @@ interface Props {
}
const Row: React.FC<{ label: string; value: React.ReactNode }> = ({ label, value }) => (
<div style={{ display: 'flex', gap: '12px', padding: '6px 0', borderBottom: '1px solid rgba(255,255,255,0.05)' }}>
<div style={{ display: 'flex', gap: '12px', padding: '6px 0', borderBottom: '1px solid var(--matrix-tint-5)' }}>
<div style={{ minWidth: '140px', color: 'var(--dim-color)', fontSize: '0.75rem', textTransform: 'uppercase' }}>{label}</div>
<div style={{ flex: 1, fontSize: '0.85rem', wordBreak: 'break-all' }}>{value ?? <span style={{ opacity: 0.4 }}></span>}</div>
</div>
@@ -291,8 +291,8 @@ const CanaryTokenDrawer: React.FC<Props> = ({ token, onClose, onRevoked }) => {
key={t.uuid}
style={{
padding: '8px 12px',
border: '1px solid rgba(255,255,255,0.08)',
background: 'rgba(255,255,255,0.02)',
border: '1px solid var(--matrix-tint-5)',
background: 'var(--matrix-tint-5)',
fontSize: '0.8rem',
}}
>

View File

@@ -458,9 +458,9 @@ const UploadModal: React.FC<UploadModalProps> = ({ onClose, onUploaded }) => {
<div style={{
display: 'flex', alignItems: 'center', gap: '8px',
padding: '8px 12px', marginBottom: '16px',
border: '1px solid rgba(255, 170, 0, 0.3)',
backgroundColor: 'rgba(255, 170, 0, 0.05)',
fontSize: '0.75rem', color: '#ffaa00',
border: '1px solid var(--warn)',
backgroundColor: 'var(--warn-tint-10)',
fontSize: '0.75rem', color: 'var(--warn)',
}}>
<AlertTriangle size={14} />
DECNET injects the callback server-side; the original bytes stay on the master.
@@ -811,9 +811,9 @@ const FileDropModal: React.FC<FileDropModalProps> = ({ deckies, topologies, onCl
<div style={{
display: 'flex', alignItems: 'center', gap: '8px',
padding: '8px 12px', marginBottom: '16px',
border: '1px solid rgba(255, 170, 0, 0.3)',
backgroundColor: 'rgba(255, 170, 0, 0.05)',
fontSize: '0.7rem', color: '#ffaa00',
border: '1px solid var(--warn)',
backgroundColor: 'var(--warn-tint-10)',
fontSize: '0.7rem', color: 'var(--warn)',
}}>
<AlertTriangle size={14} />
File drops bypass canary instrumentation bytes land verbatim. The list below is local only.
@@ -1052,7 +1052,7 @@ const CanaryTokens: React.FC = () => {
alignItems: 'center', gap: '12px',
padding: '10px 14px',
border: '1px solid var(--border-color, #30363d)',
background: 'rgba(255,255,255,0.02)',
background: 'var(--matrix-tint-5)',
color: 'var(--text-color)',
cursor: 'pointer',
textAlign: 'left',
@@ -1114,7 +1114,7 @@ const CanaryTokens: React.FC = () => {
alignItems: 'center', gap: '12px',
padding: '10px 14px',
border: '1px solid var(--border-color, #30363d)',
background: 'rgba(255,255,255,0.02)',
background: 'var(--matrix-tint-5)',
fontSize: '0.8rem',
}}
>
@@ -1189,7 +1189,7 @@ const CanaryTokens: React.FC = () => {
alignItems: 'center', gap: '12px',
padding: '10px 14px',
border: '1px solid var(--border-color, #30363d)',
background: 'rgba(255,255,255,0.02)',
background: 'var(--matrix-tint-5)',
fontSize: '0.8rem',
}}
>
@@ -1281,7 +1281,7 @@ const INPUT_STYLE: React.CSSProperties = {
width: '100%',
padding: '8px 10px',
marginBottom: '12px',
background: 'rgba(255,255,255,0.03)',
background: 'var(--matrix-tint-5)',
border: '1px solid var(--border-color, #30363d)',
color: 'var(--text-color)',
fontSize: '0.85rem',
@@ -1324,7 +1324,7 @@ const Stat: React.FC<{ label: string; value: number | string; color: string }> =
flex: '1 1 120px',
padding: '12px 16px',
border: '1px solid var(--border-color, #30363d)',
background: 'rgba(255,255,255,0.02)',
background: 'var(--matrix-tint-5)',
}}>
<div style={{ fontSize: '0.7rem', color: 'var(--dim-color)', letterSpacing: '0.1em' }}>{label}</div>
<div style={{ fontSize: '1.4rem', fontWeight: 'bold', color, marginTop: '4px' }}>{value}</div>

View File

@@ -165,7 +165,7 @@
.action-btn.danger:hover {
background: #ff4141;
color: var(--background-color);
box-shadow: 0 0 10px rgba(255, 65, 65, 0.5);
box-shadow: 0 0 10px var(--alert);
}
/* Add User Form */
@@ -260,7 +260,7 @@
font-size: 0.75rem;
padding: 6px 12px;
border: 1px solid #ff4141;
background: rgba(255, 65, 65, 0.1);
background: var(--alert-tint-10);
display: inline-block;
}

View File

@@ -635,7 +635,7 @@ const RealismBadge: React.FC<{
const breaker = realism.llm_breaker_state ?? 'closed';
const breakerColor =
breaker === 'open' ? '#ff5555'
: breaker === 'half_open' ? '#ffaa00'
: breaker === 'half_open' ? 'var(--warn)'
: 'var(--matrix)';
const tooltip = [
`Backend: ${realism.llm_backend ?? '?'}`,
@@ -793,8 +793,8 @@ const WorkersPanel: React.FC<WorkersPanelProps> = ({ pushToast }) => {
margin: '16px 20px 0',
padding: '10px 14px',
border: '1px solid #ffaa00',
background: 'rgba(255, 170, 0, 0.08)',
color: '#ffaa00',
background: 'var(--warn-tint-10)',
color: 'var(--warn)',
fontSize: '0.72rem',
letterSpacing: 1,
lineHeight: 1.5,
@@ -906,8 +906,8 @@ const WorkersPanel: React.FC<WorkersPanelProps> = ({ pushToast }) => {
</td>
<td style={{
color: w.status === 'ok' ? 'var(--matrix)'
: w.status === 'stale' ? '#ffaa00'
: 'rgba(255,255,255,0.4)',
: w.status === 'stale' ? 'var(--warn)'
: 'var(--fg-4)',
letterSpacing: 1,
}}>
{w.status.toUpperCase()}
@@ -927,8 +927,8 @@ const WorkersPanel: React.FC<WorkersPanelProps> = ({ pushToast }) => {
alignItems: 'center',
justifyContent: 'center',
gap: 4,
color: canStop ? '#ff4d4d' : '#ff4d4d',
borderColor: canStop ? '#ff4d4d' : 'rgba(255, 77, 77, 0.4)',
color: canStop ? 'var(--alert)' : 'var(--alert)',
borderColor: canStop ? 'var(--alert)' : 'rgba(255, 77, 77, 0.4)',
opacity: canStop ? 1 : 0.3,
cursor: canStop ? 'pointer' : 'not-allowed',
}}

View File

@@ -75,7 +75,7 @@
.chip.alert-chip {
border-color: var(--alert);
color: var(--alert);
background: rgba(255, 65, 65, 0.1);
background: var(--alert-tint-10);
}
/* Key-value chips in the live-feed event column. Values are unbounded
@@ -96,7 +96,7 @@
/* Breach banner */
.breach-banner {
background: rgba(255, 65, 65, 0.1);
background: var(--alert-tint-10);
border: 1px solid var(--alert);
padding: 12px 20px;
display: flex;
@@ -128,7 +128,7 @@
}
.breach-banner button:hover {
background: rgba(255, 65, 65, 0.15);
background: var(--alert-tint-10);
}
/* Stats */
@@ -155,7 +155,7 @@
}
.stat-card.alert {
border-color: rgba(255, 65, 65, 0.4);
border-color: var(--alert);
}
.stat-card .row {
@@ -370,7 +370,7 @@
.status-dot.warn {
background: var(--warn);
box-shadow: 0 0 6px rgba(255, 170, 0, 0.6);
box-shadow: 0 0 6px var(--warn);
}
.status-dot.hot {

View File

@@ -399,11 +399,11 @@ const Dashboard: React.FC<DashboardProps> = ({ searchQuery }) => {
style={{
display: 'inline-flex', alignItems: 'center', gap: 4,
fontSize: '0.62rem',
backgroundColor: 'rgba(255, 170, 0, 0.1)',
backgroundColor: 'var(--warn-tint-10)',
padding: '2px 8px',
borderRadius: 4,
border: '1px solid rgba(255, 170, 0, 0.5)',
color: '#ffaa00',
border: '1px solid var(--warn)',
color: 'var(--warn)',
cursor: 'pointer',
letterSpacing: 1,
}}

View File

@@ -56,7 +56,7 @@
.fleet-root .btn.violet { border-color: var(--violet); color: var(--violet); }
.fleet-root .btn.violet:hover { background: var(--violet); color: var(--bg); box-shadow: var(--violet-glow); }
.fleet-root .btn.alert { border-color: var(--alert); color: var(--alert); }
.fleet-root .btn.alert:hover { background: var(--alert); color: var(--bg); box-shadow: 0 0 10px rgba(255, 65, 65, 0.5); }
.fleet-root .btn.alert:hover { background: var(--alert); color: var(--bg); box-shadow: 0 0 10px var(--alert); }
.fleet-root .btn.ghost { border-color: var(--border); color: var(--matrix); opacity: 0.7; }
.fleet-root .btn.ghost:hover { color: var(--matrix); opacity: 1; border-color: var(--matrix); box-shadow: var(--matrix-glow); background: transparent; }
.fleet-root .btn.small { padding: 4px 10px; font-size: 0.68rem; }
@@ -137,7 +137,7 @@
line-height: 1;
padding: 2px 8px;
border-color: var(--border);
color: var(--text-dim, rgba(255,255,255,0.5));
color: var(--text-dim, var(--fg-3));
letter-spacing: 0;
}
.tarpit-menu-btn:hover {
@@ -173,14 +173,14 @@
.tarpit-dropdown-item:last-child { border-bottom: none; }
.tarpit-dropdown-item:hover { background: rgba(57,255,20,0.08); }
.tarpit-dropdown-item.alert { color: var(--alert); }
.tarpit-dropdown-item.alert:hover { background: rgba(255,65,65,0.08); }
.tarpit-dropdown-item.alert:hover { background: var(--alert-tint-10); }
/* Tarpit enable form — rendered below the card footer */
.tarpit-form {
margin-top: 8px;
padding: 10px;
border: 1px solid var(--alert);
background: rgba(255,65,65,0.04);
background: var(--alert-tint-10);
display: flex;
flex-direction: column;
gap: 8px;
@@ -194,7 +194,7 @@
.tarpit-form input.input {
font-size: 0.72rem;
padding: 4px 6px;
background: rgba(255,255,255,0.04);
background: var(--matrix-tint-10);
border: 1px solid var(--border);
color: var(--text-color);
}
@@ -204,7 +204,7 @@
Modal portal; PersonaGeneration.css scopes its own copy under
.persona-gen-root, which doesn't match the modal's DOM location. */
.info-banner {
background: rgba(255, 255, 255, 0.02);
background: var(--matrix-tint-5);
border: 1px solid var(--border);
border-left: 3px solid var(--violet);
padding: 10px 14px;
@@ -222,7 +222,7 @@
display: flex; align-items: center; gap: 8px;
width: 100%;
padding: 8px 12px;
background: rgba(255, 255, 255, 0.02);
background: var(--matrix-tint-5);
border: none;
color: inherit;
font-family: var(--font-mono);
@@ -232,7 +232,7 @@
cursor: pointer;
text-align: left;
}
.wizard-svc-toggle:hover { background: rgba(255, 255, 255, 0.05); }
.wizard-svc-toggle:hover { background: var(--matrix-tint-10); }
.wizard-svc-toggle.open { border-bottom: 1px solid var(--border); }
.wizard-svc-caret { color: var(--violet); width: 10px; }
.wizard-svc-name { color: var(--matrix); flex: 1; }

View File

@@ -519,7 +519,7 @@ const DeckyCard: React.FC<DeckyCardProps> = ({
onChange={(e) => setAddSlug(e.target.value)}
style={{
flex: 1, fontSize: '0.75rem', padding: '4px 6px',
background: 'rgba(255,255,255,0.04)',
background: 'var(--matrix-tint-10)',
border: '1px solid var(--border-color, #30363d)',
color: 'var(--text-color)',
}}

View File

@@ -94,7 +94,7 @@
color: var(--alert);
border: 1px solid var(--alert);
padding: 1px 5px;
background: rgba(255, 65, 65, 0.1);
background: var(--alert-tint-10);
letter-spacing: 1px;
margin-left: auto;
}
@@ -286,7 +286,7 @@
padding: 6px 10px;
border: 1px solid var(--alert);
color: var(--alert);
background: rgba(255, 65, 65, 0.08);
background: var(--alert-tint-10);
text-transform: uppercase;
}

View File

@@ -137,15 +137,15 @@
font-size: 0.68rem;
padding: 2px 8px;
border-radius: 3px;
background: rgba(255, 170, 0, 0.1);
border: 1px solid rgba(255, 170, 0, 0.5);
background: var(--warn-tint-10);
border: 1px solid var(--warn);
color: var(--warn);
cursor: pointer;
font-family: inherit;
}
.logs-root .artifact-btn:hover {
background: rgba(255, 170, 0, 0.2);
box-shadow: 0 0 8px rgba(255, 170, 0, 0.4);
background: var(--warn-tint-10);
box-shadow: 0 0 8px var(--warn);
}
/* Empty state */

View File

@@ -31,7 +31,7 @@ function parseAttachments(fields: Record<string, any>): AttachmentManifest[] {
}
const Row: React.FC<{ label: string; value: React.ReactNode }> = ({ label, value }) => (
<div style={{ display: 'flex', gap: '12px', padding: '6px 0', borderBottom: '1px solid rgba(255,255,255,0.05)' }}>
<div style={{ display: 'flex', gap: '12px', padding: '6px 0', borderBottom: '1px solid var(--matrix-tint-5)' }}>
<div style={{ minWidth: '140px', color: 'var(--dim-color)', fontSize: '0.75rem', textTransform: 'uppercase' }}>{label}</div>
<div style={{ flex: 1, fontSize: '0.85rem', wordBreak: 'break-all' }}>{value ?? <span style={{ opacity: 0.4 }}></span>}</div>
</div>
@@ -125,9 +125,9 @@ const MailDrawer: React.FC<MailDrawerProps> = ({ decky, storedAs, fields, onClos
<div style={{
display: 'flex', alignItems: 'center', gap: '8px',
padding: '8px 12px', marginBottom: '16px',
border: '1px solid rgba(255, 170, 0, 0.3)',
backgroundColor: 'rgba(255, 170, 0, 0.05)',
fontSize: '0.75rem', color: '#ffaa00',
border: '1px solid var(--warn)',
backgroundColor: 'var(--warn-tint-10)',
fontSize: '0.75rem', color: 'var(--warn)',
}}>
<AlertTriangle size={14} />
Attacker-controlled content. Phishing kits / malware likely.
@@ -184,8 +184,8 @@ const MailDrawer: React.FC<MailDrawerProps> = ({ decky, storedAs, fields, onClos
key={idx}
style={{
padding: '8px 12px',
border: '1px solid rgba(255,255,255,0.08)',
background: 'rgba(255,255,255,0.02)',
border: '1px solid var(--matrix-tint-5)',
background: 'var(--matrix-tint-5)',
fontSize: '0.8rem',
}}
>

View File

@@ -211,7 +211,7 @@ const Inspector: React.FC<Props> = ({
onChange={(e) => setAddSlug(e.target.value)}
style={{
flex: 1, fontSize: '0.75rem', padding: '4px 6px',
background: 'rgba(255,255,255,0.04)',
background: 'var(--matrix-tint-10)',
border: '1px solid var(--border-color, #30363d)',
color: 'var(--text-color)',
}}

View File

@@ -151,7 +151,7 @@ body.maze-fullscreen .maze-shell {
.maze-net-box {
position: absolute;
border: 1px dashed var(--border);
background: rgba(13, 17, 23, 0.6);
background: var(--panel);
padding: 32px 16px 16px;
min-width: 220px; min-height: 140px;
transition: border-color 0.2s;
@@ -165,14 +165,14 @@ body.maze-fullscreen .maze-shell {
border-color: var(--matrix); background: var(--matrix-tint-5);
}
.maze-net-box.internet {
border-color: var(--alert); background: rgba(255, 65, 65, 0.04);
border-color: var(--alert); background: var(--alert-tint-10);
}
.maze-net-box.dmz {
border-color: var(--alert); background: rgba(255, 65, 65, 0.06);
border-color: var(--alert); background: var(--alert-tint-10);
border-style: dashed;
}
.maze-net-box.dmz .maze-net-box-head {
color: var(--alert); border-bottom-color: rgba(255, 65, 65, 0.45);
color: var(--alert); border-bottom-color: var(--alert);
}
/* Deployed: topology is active/degraded — make it visually unmistakable.
* Subnet LANs glow matrix-green; DMZ stays hot red (and gets a stronger
@@ -188,42 +188,42 @@ body.maze-fullscreen .maze-shell {
}
.maze-net-box.deployed.dmz {
border-color: var(--alert);
background: rgba(255, 65, 65, 0.09);
box-shadow: 0 0 0 1px rgba(255, 65, 65, 0.35) inset,
0 0 16px rgba(255, 65, 65, 0.5);
background: var(--alert-tint-10);
box-shadow: 0 0 0 1px var(--alert) inset,
0 0 16px var(--alert);
}
.maze-net-box.deployed.dmz .maze-net-box-head {
color: var(--alert); border-bottom-color: rgba(255, 65, 65, 0.55);
color: var(--alert); border-bottom-color: var(--alert);
}
.maze-net-box.inactive {
opacity: 0.42; filter: grayscale(0.7); border-style: dotted;
}
.maze-net-box.inactive .maze-net-box-head { color: rgba(255, 255, 255, 0.5); }
.maze-net-box.inactive .maze-net-box-head { color: var(--fg-3); }
/* Optimistic placeholder for an enqueued LAN-add. Amber tint matches
* the REAP button voice — clearly "in-flight, not committed" without
* collapsing into the dimmed-out 'inactive' style which means the
* opposite (no traffic on a deployed LAN). */
.maze-net-box.pending {
border-color: var(--warn, #e0a040);
background: rgba(224, 160, 64, 0.04);
background: var(--warn-tint-10);
border-style: dashed;
filter: none; opacity: 1;
}
.maze-net-box.pending .maze-net-box-head {
color: var(--warn, #e0a040);
border-bottom-color: rgba(224, 160, 64, 0.45);
border-bottom-color: var(--warn);
}
.maze-net-box.pending .cidr { color: rgba(224, 160, 64, 0.7); }
.maze-net-box.pending .cidr { color: var(--warn); }
.maze-net-box-head {
position: absolute; top: 0; left: 0; right: 0;
padding: 6px 12px; border-bottom: 1px dashed var(--border);
display: flex; align-items: center; gap: 8px; justify-content: space-between;
font-size: 0.65rem; letter-spacing: 1.5px; opacity: 0.8;
background: rgba(13, 17, 23, 0.8); cursor: move;
background: var(--panel); cursor: move;
}
.maze-net-box-head .cidr { opacity: 0.5; font-size: 0.6rem; letter-spacing: 1px; }
.maze-net-box.internet .maze-net-box-head {
color: var(--alert); border-bottom-color: rgba(255, 65, 65, 0.4);
color: var(--alert); border-bottom-color: var(--alert);
}
/* ── Network resize handles ─────────────────── */
@@ -271,8 +271,8 @@ body.maze-fullscreen .maze-shell {
.maze-node.deployed .mn-head { color: var(--matrix); }
.maze-node.deployed.dmz-gateway {
border-color: var(--alert);
box-shadow: 0 0 12px rgba(255, 65, 65, 0.55);
background: rgba(255, 65, 65, 0.06);
box-shadow: 0 0 12px var(--alert);
background: var(--alert-tint-10);
}
.maze-node.deployed.dmz-gateway .mn-head { color: var(--alert); }
.maze-node .mn-head {
@@ -322,7 +322,7 @@ body.maze-fullscreen .maze-shell {
}
.maze-legend {
position: absolute; bottom: 12px; right: 12px; z-index: 5;
background: rgba(13, 17, 23, 0.85); border: 1px solid var(--border);
background: var(--panel); border: 1px solid var(--border);
padding: 8px 10px; font-size: 0.6rem; letter-spacing: 1px;
display: flex; flex-direction: column; gap: 4px;
}
@@ -425,7 +425,7 @@ body.maze-fullscreen .maze-shell {
.ghost-edge.snap { stroke: var(--matrix); opacity: 0.9; }
.ctx-item:hover { background: var(--violet-tint-10); color: var(--violet); }
.ctx-item.danger { color: var(--alert); }
.ctx-item.danger:hover { background: rgba(255, 65, 65, 0.12); }
.ctx-item.danger:hover { background: var(--alert-tint-10); }
.ctx-item.disabled { opacity: 0.35; cursor: not-allowed; }
.ctx-item.disabled:hover { background: transparent; color: inherit; }
.ctx-divider { height: 1px; background: var(--border); margin: 4px 0; }
@@ -453,7 +453,7 @@ body.maze-fullscreen .maze-shell {
.inspector-close-btn {
background: transparent;
border: 1px solid var(--border);
color: rgba(255, 255, 255, 0.5);
color: var(--fg-3);
padding: 3px 5px;
cursor: pointer;
display: flex;
@@ -550,7 +550,7 @@ body.maze-fullscreen .maze-shell {
padding: 1px 5px;
border: 1px solid var(--border);
letter-spacing: 1px;
color: rgba(255, 255, 255, 0.55);
color: var(--fg-3);
}
/* Inspector buttons */
@@ -563,7 +563,7 @@ body.maze-fullscreen .maze-shell {
.maze-btn.alert:hover {
background: var(--alert);
color: var(--bg);
box-shadow: 0 0 10px rgba(255, 65, 65, 0.5);
box-shadow: 0 0 10px var(--alert);
opacity: 1;
}
@@ -606,8 +606,8 @@ body.maze-fullscreen .maze-shell {
flex-direction: column;
gap: 8px;
padding: 8px;
background: rgba(255, 65, 65, 0.04);
border: 1px solid rgba(255, 65, 65, 0.3);
background: var(--alert-tint-10);
border: 1px solid var(--alert);
}
.inspector-tarpit-field {
display: flex;
@@ -617,7 +617,7 @@ body.maze-fullscreen .maze-shell {
.maze-input {
font-size: 0.72rem;
padding: 4px 6px;
background: rgba(255, 255, 255, 0.04);
background: var(--matrix-tint-5);
border: 1px solid var(--border);
color: var(--text-color, #e0e0e0);
font-family: inherit;

View File

@@ -56,7 +56,7 @@ const NetBox: React.FC<Props> = ({
<span>{net.label}</span>
{inactive && !net.pending && (
<span className="chip-mini"
style={{ marginLeft: 4, borderColor: 'var(--border)', color: 'rgba(255,255,255,0.45)' }}>
style={{ marginLeft: 4, borderColor: 'var(--border)', color: 'var(--fg-4)' }}>
INACTIVE
</span>
)}

View File

@@ -81,7 +81,7 @@
letter-spacing: 1.5px;
border: 1px solid var(--alert);
color: var(--alert);
background: rgba(255, 65, 65, 0.06);
background: var(--alert-tint-10);
text-transform: uppercase;
}
@@ -192,9 +192,9 @@
}
.orchestrator-root .logs-table tr:hover td { background: var(--matrix-tint-5); }
.orchestrator-root .logs-table tr.fail td {
background: rgba(255, 65, 65, 0.05);
background: var(--alert-tint-10);
}
.orchestrator-root .logs-table tr.fail:hover td { background: rgba(255, 65, 65, 0.08); }
.orchestrator-root .logs-table tr.fail:hover td { background: var(--alert-tint-10); }
/* Live-prepended row tint — fades back to neutral after a moment via opacity. */
.orchestrator-root .logs-table tr.fresh td {

View File

@@ -6,7 +6,7 @@
/* Info banner explaining scope (non-MazeNET) + showing pool path. */
.persona-gen-root .info-banner {
background: rgba(255, 255, 255, 0.02);
background: var(--matrix-tint-5);
border: 1px solid var(--border);
border-left: 3px solid var(--violet);
padding: 10px 14px;

View File

@@ -5,7 +5,7 @@
.realism-config-root .mono { font-family: var(--font-mono); }
.realism-config-root .info-banner {
background: rgba(255, 255, 255, 0.02);
background: var(--matrix-tint-5);
border: 1px solid var(--border);
border-left: 3px solid var(--violet);
padding: 10px 14px;

View File

@@ -29,7 +29,7 @@ interface TranscriptPage {
const PAGE_SIZE = 500;
const Row: React.FC<{ label: string; value: React.ReactNode }> = ({ label, value }) => (
<div style={{ display: 'flex', gap: '12px', padding: '6px 0', borderBottom: '1px solid rgba(255,255,255,0.05)' }}>
<div style={{ display: 'flex', gap: '12px', padding: '6px 0', borderBottom: '1px solid var(--matrix-tint-5)' }}>
<div style={{ minWidth: '140px', color: 'var(--dim-color)', fontSize: '0.75rem', textTransform: 'uppercase' }}>{label}</div>
<div style={{ flex: 1, fontSize: '0.85rem', wordBreak: 'break-all' }}>{value ?? <span style={{ opacity: 0.4 }}></span>}</div>
</div>
@@ -204,9 +204,9 @@ const SessionDrawer: React.FC<SessionDrawerProps> = ({ decky, sid, fields, onClo
<div style={{
display: 'flex', alignItems: 'center', gap: '8px',
padding: '8px 12px', marginBottom: '16px',
border: '1px solid rgba(255, 170, 0, 0.3)',
backgroundColor: 'rgba(255, 170, 0, 0.05)',
fontSize: '0.75rem', color: '#ffaa00',
border: '1px solid var(--warn)',
backgroundColor: 'var(--warn-tint-10)',
fontSize: '0.75rem', color: 'var(--warn)',
}}>
<AlertTriangle size={14} />
Session exceeded 10 MB cap playback is truncated.

View File

@@ -31,7 +31,7 @@
justify-content: space-between;
gap: 12px;
padding: 4px 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.04);
border-bottom: 1px solid var(--matrix-tint-10);
font-size: 0.8rem;
}
@@ -50,8 +50,8 @@
font-size: 0.7rem;
letter-spacing: 0.05em;
padding: 2px 6px;
border: 1px solid rgba(255, 255, 255, 0.12);
background: rgba(255, 255, 255, 0.03);
border: 1px solid var(--matrix-tint-10);
background: var(--matrix-tint-10);
color: var(--text-color);
border-radius: 3px;
min-width: 20px;

View File

@@ -5,7 +5,7 @@
.synthetic-files-root .mono { font-family: var(--font-mono); }
.synthetic-files-root .info-banner {
background: rgba(255, 255, 255, 0.02);
background: var(--matrix-tint-5);
border: 1px solid var(--border);
border-left: 3px solid var(--violet);
padding: 10px 14px;
@@ -69,7 +69,7 @@
text-transform: uppercase;
}
.synthetic-files-root .files-table tbody tr {
border-bottom: 1px solid rgba(255, 255, 255, 0.04);
border-bottom: 1px solid var(--matrix-tint-5);
cursor: pointer;
transition: background 0.1s;
}
@@ -194,7 +194,7 @@
font-family: var(--font-mono);
white-space: pre-wrap;
word-break: break-word;
background: rgba(255, 255, 255, 0.03);
background: var(--matrix-tint-5);
border: 1px solid var(--border);
padding: 12px;
font-size: 0.78rem;

View File

@@ -80,7 +80,7 @@
display: flex;
flex-direction: column;
gap: 8px;
background: rgba(255, 255, 255, 0.015);
background: var(--matrix-tint-5);
}
.ttp-tag-card .ttp-card-head {

View File

@@ -57,7 +57,7 @@
.tlist-btn.danger:hover { background: var(--alert, #e74c3c); color: var(--bg); box-shadow: 0 0 10px rgba(231, 76, 60, 0.5); }
.tlist-btn.danger.armed { background: var(--alert, #e74c3c); color: #000; }
.tlist-btn.warn { border-color: var(--warn, #e0a040); color: var(--warn, #e0a040); }
.tlist-btn.warn:hover { background: var(--warn, #e0a040); color: var(--bg); box-shadow: 0 0 10px rgba(224, 160, 64, 0.5); }
.tlist-btn.warn:hover { background: var(--warn, #e0a040); color: var(--bg); box-shadow: 0 0 10px var(--warn); }
.tlist-btn.warn.armed { background: var(--warn, #e0a040); color: #000; }
.tlist-create-row {

View File

@@ -57,7 +57,7 @@
.webhooks-root .btn.alert { border-color: var(--alert, #ff4d4d); color: var(--alert, #ff4d4d); }
.webhooks-root .btn.alert:hover { background: var(--alert, #ff4d4d); color: var(--bg); box-shadow: 0 0 10px rgba(255, 77, 77, 0.5); }
.webhooks-root .btn.warn { border-color: var(--warn, #e0a040); color: var(--warn, #e0a040); }
.webhooks-root .btn.warn:hover { background: var(--warn, #e0a040); color: var(--bg); box-shadow: 0 0 10px rgba(224, 160, 64, 0.5); }
.webhooks-root .btn.warn:hover { background: var(--warn, #e0a040); color: var(--bg); box-shadow: 0 0 10px var(--warn); }
.webhooks-root .btn.ghost { border-color: var(--border-color); color: var(--matrix); opacity: 0.7; }
.webhooks-root .btn.ghost:hover { opacity: 1; border-color: var(--matrix); background: transparent; box-shadow: var(--matrix-glow); }
.webhooks-root .btn:disabled { opacity: 0.3; cursor: not-allowed; }
@@ -67,7 +67,7 @@
}
.webhooks-root .webhooks-warning-banner {
background: rgba(224, 160, 64, 0.08);
background: var(--warn-tint-10);
border: 1px solid var(--warn, #e0a040);
color: var(--warn, #e0a040);
padding: 10px 14px;
@@ -167,7 +167,7 @@
.webhooks-root .wh-chip.status-warn {
border-color: var(--warn, #e0a040);
background: rgba(224, 160, 64, 0.1);
background: var(--warn-tint-10);
color: var(--warn, #e0a040);
}

View File

@@ -14,11 +14,24 @@
/* Back-compat names used across the codebase */
--background-color: var(--bg);
--bg-color: var(--bg); /* used by drawers and other inline styles */
--text-color: var(--matrix);
--accent-color: var(--violet);
--secondary-color: var(--panel);
--border-color: var(--border);
/* Cyan/info — REPLAY buttons, neutral status that's neither
* "good/active" (matrix) nor "stop" (alert). */
--info: #22d3ee;
--info-tint-10: rgba(34, 211, 238, 0.10);
--info-tint-30: rgba(34, 211, 238, 0.30);
/* Benign/healthy — semantic alias for "verified good". Stays
* matrix in dark, picks up a darker emerald in light so a
* BENIGN pill still reads as "green check" without the dark-mode
* neon. */
--ok: var(--matrix);
/* ── Foreground opacities ──────────────────────── */
--fg-1: var(--matrix);
--fg-2: rgba(0, 255, 65, 0.80);
@@ -151,6 +164,16 @@ html[data-theme="light"] {
--warn-tint-10: rgba(180, 83, 9, 0.12);
--crit-tint-10: rgba(185, 28, 28, 0.10);
/* Cyan/info dims to slate in light mode so REPLAY buttons read
* as a calm secondary against cream rather than an electric pop. */
--info: #155e75;
--info-tint-10: rgba(21, 94, 117, 0.10);
--info-tint-30: rgba(21, 94, 117, 0.20);
/* Benign in light mode — emerald, the one "happy green" we keep,
* tuned dark enough to read on cream. */
--ok: #047857;
/* Glows no-op'd — kept defined so .btn:hover etc. don't
* break, just produce no halo. */
--matrix-glow: none;