194 lines
10 KiB
CSS
194 lines
10 KiB
CSS
/* money-trace · dark finance-terminal aesthetic */
|
|
:root {
|
|
--bg: #090b0f;
|
|
--bg-grid: rgba(255,255,255,0.022);
|
|
--panel: #10141b;
|
|
--panel-2: #161b24;
|
|
--line: rgba(255,255,255,0.075);
|
|
--line-2: rgba(255,255,255,0.13);
|
|
--ink: #eaeff7;
|
|
--ink-mute: #8b95a6;
|
|
--ink-dim: #545d6d;
|
|
|
|
--c-income: #2ee6a6; /* real money in */
|
|
--c-spend: #ff5f73; /* real money out */
|
|
--c-person: #b98cff; /* inter-person */
|
|
--c-fee: #ffc24b; /* fees & interest*/
|
|
--c-internal:#4b5466; /* cycles / noise */
|
|
|
|
--glow: 1; /* 0..1, multiplied into drop-shadows */
|
|
--link-op: 0.62; /* base opacity of real links */
|
|
--font-ui: 'Space Grotesk', system-ui, sans-serif;
|
|
--font-num: 'IBM Plex Mono', ui-monospace, monospace;
|
|
}
|
|
|
|
* { box-sizing: border-box; }
|
|
html, body { margin: 0; height: 100%; }
|
|
body {
|
|
background:
|
|
radial-gradient(1200px 700px at 78% -8%, rgba(46,230,166,0.05), transparent 60%),
|
|
radial-gradient(900px 600px at 6% 110%, rgba(185,140,255,0.045), transparent 55%),
|
|
linear-gradient(0deg, var(--bg-grid) 1px, transparent 1px) 0 0 / 100% 34px,
|
|
var(--bg);
|
|
color: var(--ink);
|
|
font-family: var(--font-ui);
|
|
-webkit-font-smoothing: antialiased;
|
|
overflow: hidden;
|
|
}
|
|
.num { font-family: var(--font-num); font-feature-settings: 'tnum' 1; letter-spacing: -0.01em; }
|
|
|
|
/* ---- app frame ---- */
|
|
#app { display: flex; flex-direction: column; height: 100vh; }
|
|
|
|
/* ---- header / summary bar ---- */
|
|
header.bar {
|
|
display: flex; align-items: stretch; gap: 0;
|
|
padding: 14px 22px 13px; border-bottom: 1px solid var(--line);
|
|
background: linear-gradient(180deg, rgba(255,255,255,0.018), transparent);
|
|
}
|
|
.brand { display: flex; flex-direction: column; justify-content: center; min-width: 168px; }
|
|
.brand .mark {
|
|
font-weight: 600; font-size: 15px; letter-spacing: 0.26em;
|
|
text-transform: uppercase; color: var(--ink);
|
|
}
|
|
.brand .mark b { color: var(--c-income); font-weight: 600; }
|
|
.brand .sub { font-size: 11px; color: var(--ink-dim); letter-spacing: 0.04em; margin-top: 3px; }
|
|
|
|
.stats { display: flex; gap: 30px; margin-left: auto; align-items: center; }
|
|
.stat { display: flex; flex-direction: column; gap: 4px; min-width: 132px; }
|
|
.stat .lab {
|
|
font-size: 10.5px; letter-spacing: 0.14em; text-transform: uppercase;
|
|
color: var(--ink-mute); display: flex; align-items: center; gap: 6px;
|
|
}
|
|
.stat .lab i { width: 7px; height: 7px; border-radius: 2px; display: inline-block; }
|
|
.stat .val { font-size: 27px; font-weight: 500; line-height: 1; }
|
|
.stat .val small { font-size: 13px; color: var(--ink-dim); margin-left: 2px; }
|
|
.stat.income .val { color: var(--c-income); }
|
|
.stat.spend .val { color: var(--c-spend); }
|
|
.stat.internal .val { color: var(--ink-mute); }
|
|
.stat.net .val { color: var(--ink); }
|
|
.stat .delta { font-size: 11px; color: var(--ink-dim); }
|
|
|
|
/* ---- controls strip ---- */
|
|
.controls {
|
|
display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
|
|
padding: 11px 22px; border-bottom: 1px solid var(--line);
|
|
background: var(--panel);
|
|
}
|
|
.seg { display: inline-flex; background: var(--bg); border: 1px solid var(--line); border-radius: 9px; padding: 3px; gap: 2px; }
|
|
.seg button {
|
|
font-family: var(--font-ui); font-size: 12.5px; color: var(--ink-mute);
|
|
background: transparent; border: 0; padding: 7px 15px; border-radius: 6px;
|
|
cursor: pointer; display: flex; align-items: center; gap: 7px; transition: .16s;
|
|
}
|
|
.seg button:hover { color: var(--ink); }
|
|
.seg button.on { background: var(--panel-2); color: var(--ink); box-shadow: inset 0 0 0 1px var(--line-2); }
|
|
.seg button svg { width: 14px; height: 14px; }
|
|
|
|
.toggle { display: inline-flex; align-items: center; gap: 9px; cursor: pointer; user-select: none; }
|
|
.toggle .track { width: 38px; height: 21px; border-radius: 20px; background: var(--bg); border: 1px solid var(--line-2); position: relative; transition: .18s; }
|
|
.toggle .track::after { content: ''; position: absolute; top: 2px; left: 2px; width: 15px; height: 15px; border-radius: 50%; background: var(--ink-mute); transition: .18s; }
|
|
.toggle.on .track { background: rgba(46,230,166,0.22); border-color: var(--c-income); }
|
|
.toggle.on .track::after { left: 19px; background: var(--c-income); }
|
|
.toggle .lbl { font-size: 12.5px; color: var(--ink); }
|
|
.toggle .hint { font-size: 11px; color: var(--ink-dim); }
|
|
|
|
.ctl-label { font-size: 10.5px; letter-spacing: 0.13em; text-transform: uppercase; color: var(--ink-dim); margin-right: -8px; }
|
|
|
|
/* month scrubber */
|
|
.scrub { display: flex; align-items: center; gap: 12px; flex: 1; min-width: 280px; max-width: 560px; }
|
|
.scrub .play { width: 30px; height: 30px; border-radius: 8px; border: 1px solid var(--line); background: var(--bg); color: var(--ink); cursor: pointer; display: grid; place-items: center; }
|
|
.scrub .play:hover { border-color: var(--line-2); }
|
|
.scrub .track-wrap { flex: 1; }
|
|
.scrub .months { display: flex; justify-content: space-between; font-size: 10px; color: var(--ink-dim); margin-bottom: 5px; }
|
|
.scrub .months b { color: var(--c-income); font-weight: 500; font-family: var(--font-num); }
|
|
.bars { display: flex; gap: 2px; align-items: flex-end; height: 30px; }
|
|
.bars .b { flex: 1; background: var(--line); border-radius: 2px 2px 0 0; cursor: pointer; transition: .14s; min-height: 2px; }
|
|
.bars .b:hover { background: var(--ink-dim); }
|
|
.bars .b.active { background: linear-gradient(180deg, var(--c-income), rgba(46,230,166,0.35)); }
|
|
|
|
/* bank filter */
|
|
.dropdown { position: relative; }
|
|
.dropdown > .btn {
|
|
font-family: var(--font-ui); font-size: 12.5px; color: var(--ink); background: var(--bg);
|
|
border: 1px solid var(--line); border-radius: 8px; padding: 8px 13px; cursor: pointer;
|
|
display: flex; align-items: center; gap: 8px;
|
|
}
|
|
.dropdown > .btn .cnt { color: var(--ink-dim); font-family: var(--font-num); font-size: 11px; }
|
|
.dropdown .menu {
|
|
position: absolute; top: calc(100% + 6px); right: 0; z-index: 40;
|
|
background: var(--panel-2); border: 1px solid var(--line-2); border-radius: 10px;
|
|
padding: 8px; width: 220px; box-shadow: 0 18px 50px rgba(0,0,0,0.55); display: none;
|
|
max-height: 360px; overflow: auto;
|
|
}
|
|
.dropdown.open .menu { display: block; }
|
|
.dropdown .menu .row { display: flex; align-items: center; gap: 9px; padding: 7px 9px; border-radius: 7px; cursor: pointer; font-size: 12.5px; }
|
|
.dropdown .menu .row:hover { background: var(--panel); }
|
|
.dropdown .menu .row .ck { width: 15px; height: 15px; border-radius: 4px; border: 1px solid var(--line-2); display: grid; place-items: center; color: var(--bg); }
|
|
.dropdown .menu .row.on .ck { background: var(--c-income); border-color: var(--c-income); }
|
|
.dropdown .menu .row .n { color: var(--ink-dim); margin-left: auto; font-family: var(--font-num); font-size: 11px; }
|
|
.dropdown .menu .sep { height: 1px; background: var(--line); margin: 6px 4px; }
|
|
.dropdown .menu .all { font-size: 11px; color: var(--c-income); padding: 6px 9px; cursor: pointer; }
|
|
|
|
/* ---- legend ---- */
|
|
.legend { display: flex; align-items: center; gap: 16px; margin-left: auto; }
|
|
.legend .item { display: flex; align-items: center; gap: 7px; font-size: 11.5px; color: var(--ink-mute); }
|
|
.legend .item i { width: 16px; height: 3px; border-radius: 2px; display: inline-block; }
|
|
.legend .item.dash i { height: 0; border-top: 2px dashed var(--c-internal); width: 18px; }
|
|
|
|
/* ---- view area ---- */
|
|
.view { position: relative; flex: 1; overflow: hidden; }
|
|
.view svg { width: 100%; height: 100%; display: block; }
|
|
|
|
/* sankey */
|
|
.node-rect { transition: opacity .18s; }
|
|
.node-label { fill: var(--ink); font-size: 12.5px; font-family: var(--font-ui); }
|
|
.node-sub { fill: var(--ink-dim); font-size: 10px; font-family: var(--font-num); }
|
|
.node-val { fill: var(--ink-mute); font-size: 10.5px; font-family: var(--font-num); }
|
|
.colhead { fill: var(--ink-dim); font-size: 10.5px; letter-spacing: 0.16em; text-transform: uppercase; font-family: var(--font-ui); }
|
|
|
|
.link { fill: none; transition: opacity .18s, stroke-opacity .18s; }
|
|
.link.real { opacity: var(--link-op); }
|
|
.link.internal { opacity: 0.4; }
|
|
.link.income { stroke: var(--c-income); }
|
|
.link.inter_in, .link.inter_out { stroke: var(--c-person); }
|
|
.link.expense { stroke: var(--c-spend); }
|
|
.link.fee { stroke: var(--c-fee); }
|
|
.link.internal_card, .link.internal_line, .link.internal_self { stroke: var(--c-internal); stroke-dasharray: 5 5; }
|
|
.glow-real { filter: drop-shadow(0 0 calc(5px * var(--glow)) currentColor); }
|
|
|
|
.dim { opacity: 0.07 !important; }
|
|
.hl { opacity: 0.95 !important; }
|
|
|
|
/* ---- tooltip ---- */
|
|
#tip {
|
|
position: fixed; z-index: 80; pointer-events: none; max-width: 320px;
|
|
background: rgba(13,17,23,0.96); border: 1px solid var(--line-2); border-radius: 11px;
|
|
padding: 12px 13px; box-shadow: 0 16px 44px rgba(0,0,0,0.6); backdrop-filter: blur(8px);
|
|
opacity: 0; transform: translateY(4px); transition: opacity .12s, transform .12s; font-size: 12.5px;
|
|
}
|
|
#tip.show { opacity: 1; transform: translateY(0); }
|
|
#tip .t-head { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
|
|
#tip .t-dot { width: 9px; height: 9px; border-radius: 3px; }
|
|
#tip .t-title { font-weight: 600; font-size: 13px; }
|
|
#tip .t-amt { font-family: var(--font-num); font-size: 19px; margin: 2px 0 9px; }
|
|
#tip .t-meta { color: var(--ink-mute); font-size: 11px; margin-bottom: 9px; }
|
|
#tip .t-rows { display: flex; flex-direction: column; gap: 5px; border-top: 1px solid var(--line); padding-top: 9px; max-height: 200px; overflow: hidden; }
|
|
#tip .t-row { display: flex; gap: 10px; font-size: 11.5px; align-items: baseline; }
|
|
#tip .t-row .d { color: var(--ink-dim); font-family: var(--font-num); font-size: 10.5px; width: 42px; flex: none; }
|
|
#tip .t-row .desc { color: var(--ink-mute); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
#tip .t-row .a { font-family: var(--font-num); color: var(--ink); }
|
|
#tip .t-more { color: var(--ink-dim); font-size: 10.5px; margin-top: 7px; }
|
|
|
|
/* orbit view */
|
|
.orbit-ring { fill: none; stroke: var(--line); stroke-width: 1; }
|
|
.orbit-node-label { fill: var(--ink); font-size: 11.5px; font-family: var(--font-ui); }
|
|
.orbit-sub { fill: var(--ink-dim); font-size: 9.5px; font-family: var(--font-num); }
|
|
.orbit-center-lab { fill: var(--ink); font-size: 14px; font-family: var(--font-ui); font-weight: 600; }
|
|
|
|
/* empty state */
|
|
.empty { position: absolute; inset: 0; display: grid; place-items: center; color: var(--ink-dim); font-size: 14px; }
|
|
|
|
/* loading */
|
|
#loading { position: fixed; inset: 0; display: grid; place-items: center; background: var(--bg); z-index: 200; color: var(--ink-mute); font-family: var(--font-num); font-size: 13px; letter-spacing: 0.1em; }
|