diff --git a/ui_kits/web/HistoryScreen.jsx b/ui_kits/web/HistoryScreen.jsx
new file mode 100644
index 0000000..684af88
--- /dev/null
+++ b/ui_kits/web/HistoryScreen.jsx
@@ -0,0 +1,245 @@
+// HistoryScreen — append-only audit log
+
+(function () {
+ const KIND_META = {
+ import: { label: "IMPORT", glyph: "↓", color: "var(--fg-strong)" },
+ poll: { label: "POLL", glyph: "↻", color: "var(--fg-muted)" },
+ approve: { label: "APPROVE", glyph: "✓", color: "var(--conf-rules)" },
+ override: { label: "OVERRIDE", glyph: "✎", color: "var(--conf-high)" },
+ skip: { label: "SKIP", glyph: "↩", color: "var(--conf-mid)" },
+ rule: { label: "RULE", glyph: "§", color: "var(--conf-high)" },
+ commit: { label: "COMMIT", glyph: "●", color: "var(--fg-strong)" },
+ fx: { label: "FX", glyph: "◇", color: "var(--fg-muted)" },
+ categorize: { label: "AGENT", glyph: "△", color: "var(--conf-high)" },
+ error: { label: "ERROR", glyph: "✕", color: "var(--conf-low)" },
+ };
+ const ACTOR_LABEL = { you: "you", system: "system", agent: "agent" };
+ const entityShort = { all: "ALL", personal: "P/", tfox: "9T/", finacode: "FC/" };
+ const entityClass = { personal: "personal", tfox: "tfox", finacode: "finacode" };
+
+ function HistoryFilters({ activeKinds, toggleKind, actor, setActor, range, setRange }) {
+ const kinds = Object.keys(KIND_META);
+ return (
+
+
+
KIND
+
+
+ {kinds.map(k => (
+
+ ))}
+
+
+
+
ACTOR
+
+ {["all", "you", "system", "agent"].map(a => (
+
+ ))}
+
+
RANGE
+
+ {[
+ { id: "24h", label: "24H" },
+ { id: "7d", label: "7D" },
+ { id: "30d", label: "30D" },
+ { id: "all", label: "ALL" },
+ ].map(r => (
+
+ ))}
+
+
+
+ );
+ }
+
+ // Group log entries by date
+ function groupByDay(rows) {
+ const groups = new Map();
+ rows.forEach(r => {
+ const day = r.at.split(" ")[0];
+ if (!groups.has(day)) groups.set(day, []);
+ groups.get(day).push(r);
+ });
+ return [...groups.entries()];
+ }
+
+ function dayLabel(iso) {
+ const d = new Date(iso + "T00:00:00");
+ const today = new Date("2026-03-08T00:00:00");
+ const diff = Math.round((today - d) / (24 * 3600 * 1000));
+ if (diff === 0) return "TODAY";
+ if (diff === 1) return "YESTERDAY";
+ const wd = d.toLocaleDateString("en-US", { weekday: "short" }).toUpperCase();
+ const md = d.toLocaleDateString("en-US", { month: "short", day: "numeric" }).toUpperCase();
+ return `${wd} · ${md}`;
+ }
+
+ function HistoryRow({ row }) {
+ const meta = KIND_META[row.kind];
+ return (
+
+
+ {row.at.split(" ")[1]}
+
+
+ {meta.glyph}
+ {meta.label}
+
+
+ {ACTOR_LABEL[row.actor]}
+
+
+ {entityShort[row.entity]}
+
+
+
{row.summary}
+
{row.detail}
+
+
{row.ref}
+
+ );
+ }
+
+ function HistoryScreen({ entity }) {
+ const all = window.AKEFIN_DATA.history;
+ const [activeKinds, setActiveKinds] = React.useState(new Set());
+ const [actor, setActor] = React.useState("all");
+ const [range, setRange] = React.useState("7d");
+
+ const toggleKind = (k) => {
+ setActiveKinds(prev => {
+ const next = new Set(prev);
+ if (next.has(k)) next.delete(k); else next.add(k);
+ return next;
+ });
+ };
+
+ // Range filter (anchored to 2026-03-08 — the demo's "today")
+ const TODAY = new Date("2026-03-08T23:59:59");
+ const RANGE_DAYS = { "24h": 1, "7d": 7, "30d": 30, "all": 99999 };
+ const cutoff = new Date(TODAY.getTime() - RANGE_DAYS[range] * 24 * 3600 * 1000);
+
+ const rows = all.filter(r => {
+ if (entity !== "all" && r.entity !== entity && r.entity !== "all") return false;
+ if (activeKinds.size > 0 && !activeKinds.has(r.kind)) return false;
+ if (actor !== "all" && r.actor !== actor) return false;
+ const d = new Date(r.at.replace(" ", "T"));
+ if (d < cutoff) return false;
+ return true;
+ });
+
+ // Aggregates for header strip
+ const counts = rows.reduce((acc, r) => {
+ acc.total++;
+ acc.byActor[r.actor] = (acc.byActor[r.actor] || 0) + 1;
+ acc.byKind[r.kind] = (acc.byKind[r.kind] || 0) + 1;
+ return acc;
+ }, { total: 0, byActor: {}, byKind: {} });
+
+ const grouped = groupByDay(rows);
+
+ return (
+
+
+
+
HISTORY · {entity === "all" ? "ALL ENTITIES" : entity.toUpperCase()}
+
Audit log
+
+ {counts.total} events · append-only · backed by git on main
+
+
+
+
+
+
+
+
+ {/* Summary strip */}
+
+
+
EVENTS
+
{counts.total}
+
+
+
BY YOU
+
{counts.byActor.you || 0}
+
+
+
BY SYSTEM
+
{counts.byActor.system || 0}
+
+
+
BY AGENT
+
{counts.byActor.agent || 0}
+
+
+
COMMITS
+
{counts.byKind.commit || 0}
+
+
+
ERRORS
+
0 ? "neg" : ""}`}>{counts.byKind.error || 0}
+
+
+
+
+
+
+
+
TIME
+
KIND
+
ACTOR
+
ENT
+
EVENT
+
REF
+
+
+ {grouped.length === 0 && (
+
+
NO EVENTS MATCH
+
Try widening the range or clearing filters.
+
+ )}
+
+ {grouped.map(([day, dayRows]) => (
+
+
+ {dayLabel(day)}
+ {day}
+ {dayRows.length} events
+
+
+
+ {dayRows.map(r => )}
+
+
+ ))}
+
+
+ );
+ }
+
+ Object.assign(window, { HistoryScreen });
+})();
diff --git a/ui_kits/web/dashboard.css b/ui_kits/web/dashboard.css
index a501fa1..9495aa0 100644
--- a/ui_kits/web/dashboard.css
+++ b/ui_kits/web/dashboard.css
@@ -1969,3 +1969,601 @@ button { font-family: inherit; cursor: pointer; }
.dash-log-rows { color: var(--fg-on-ink-muted); font-variant-numeric: tabular-nums; }
.dash-log-msg { color: var(--fg-on-ink-muted); }
+/* =========================================================================
+ ACCOUNTS SCREEN
+ ========================================================================= */
+.accounts-screen {
+ padding: 24px;
+ overflow-y: auto;
+ height: 100%;
+}
+
+.acct-tabs {
+ display: flex; gap: 0;
+ border-bottom: 1px solid var(--rule);
+ margin-bottom: 20px;
+ margin-top: 4px;
+}
+.acct-tab {
+ background: transparent;
+ border: none;
+ border-bottom: 2px solid transparent;
+ padding: 10px 16px 10px 0;
+ margin-right: 24px;
+ font-family: var(--font-mono);
+ font-size: 11px;
+ font-weight: 500;
+ letter-spacing: 0.16em;
+ color: var(--fg-muted);
+ display: inline-flex; align-items: center; gap: 8px;
+}
+.acct-tab.active { color: var(--fg-strong); border-bottom-color: var(--fg-strong); }
+.acct-tab:hover { color: var(--fg-strong); }
+.acct-tab-count {
+ background: var(--rule);
+ color: var(--fg-strong);
+ font-size: 10px;
+ letter-spacing: 0.04em;
+ padding: 1px 6px;
+ border-radius: 999px;
+}
+
+/* ---- Layout: cards grid + detail ----------------------------------- */
+.acct-layout {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) 360px;
+ gap: 16px;
+ align-items: start;
+}
+.acct-src-grid {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 12px;
+}
+
+/* ---- Source card --------------------------------------------------- */
+.acct-src-card {
+ text-align: left;
+ background: var(--paper);
+ border: 1px solid var(--rule);
+ border-radius: var(--r-md);
+ padding: 14px 16px;
+ display: flex; flex-direction: column;
+ gap: 8px;
+ position: relative;
+ transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
+}
+.acct-src-card:hover {
+ border-color: var(--fg-muted);
+}
+.acct-src-card.selected {
+ border-color: var(--fg-strong);
+ background: color-mix(in srgb, var(--paper) 96%, var(--fg-strong) 4%);
+}
+.acct-src-card.selected::before {
+ content: "";
+ position: absolute;
+ left: -1px; top: -1px; bottom: -1px;
+ width: 3px;
+ background: var(--fg-strong);
+ border-radius: 1px 0 0 1px;
+}
+.acct-src-head {
+ display: flex; align-items: center; justify-content: space-between;
+}
+.acct-src-name {
+ font-family: var(--font-sans-kr);
+ font-size: 14.5px;
+ font-weight: 500;
+ color: var(--fg-strong);
+}
+.acct-src-meta {
+ font-family: var(--font-mono);
+ font-size: 11px;
+ letter-spacing: 0.04em;
+ color: var(--fg-subtle);
+ display: flex; gap: 6px;
+}
+.acct-src-balance {
+ font-family: var(--font-mono);
+ font-size: 22px;
+ font-weight: 500;
+ font-variant-numeric: tabular-nums;
+ color: var(--fg-strong);
+ letter-spacing: -0.01em;
+ margin-top: 2px;
+}
+.acct-src-balance .ccy {
+ color: var(--fg-subtle);
+ font-size: 12px;
+ font-weight: 400;
+}
+.acct-src-foot {
+ display: flex; align-items: center; justify-content: space-between;
+ padding-top: 8px;
+ border-top: 1px dashed var(--rule);
+ font-family: var(--font-mono);
+ font-size: 10px;
+ letter-spacing: 0.14em;
+ color: var(--fg-subtle);
+}
+.acct-src-sync { font-variant-numeric: tabular-nums; }
+.acct-method {
+ display: inline-block;
+ padding: 2px 6px;
+ border: 1px solid var(--rule);
+ border-radius: 2px;
+ font-family: var(--font-mono);
+ font-size: 9.5px;
+ letter-spacing: 0.16em;
+ color: var(--fg-muted);
+}
+
+/* ---- Status pill --------------------------------------------------- */
+.acct-pill {
+ display: inline-flex; align-items: center; gap: 5px;
+ height: 18px;
+ padding: 0 7px;
+ border-radius: 2px;
+ font-family: var(--font-mono);
+ font-size: 9.5px;
+ font-weight: 500;
+ letter-spacing: 0.16em;
+}
+.acct-pill-dot {
+ width: 5px; height: 5px;
+ border-radius: 50%;
+}
+.acct-pill-ok { background: color-mix(in srgb, var(--conf-rules) 14%, transparent); color: var(--conf-rules); }
+.acct-pill-ok .acct-pill-dot { background: var(--conf-rules); }
+.acct-pill-warn { background: color-mix(in srgb, var(--conf-mid) 16%, transparent); color: var(--conf-mid); }
+.acct-pill-warn .acct-pill-dot { background: var(--conf-mid); }
+.acct-pill-fail { background: color-mix(in srgb, var(--conf-low) 14%, transparent); color: var(--conf-low); }
+.acct-pill-fail .acct-pill-dot { background: var(--conf-low); }
+.acct-pill-idle { background: var(--rule); color: var(--fg-muted); }
+.acct-pill-idle .acct-pill-dot { background: var(--fg-muted); }
+
+/* ---- Detail panel -------------------------------------------------- */
+.acct-detail {
+ background: var(--paper);
+ border: 1px solid var(--rule);
+ border-radius: var(--r-md);
+ padding: 18px 20px;
+ display: flex; flex-direction: column;
+ gap: 18px;
+ position: sticky;
+ top: 0;
+}
+.acct-detail-empty {
+ align-items: center; justify-content: center;
+ text-align: center;
+ min-height: 240px;
+ color: var(--fg-subtle);
+}
+.acct-detail-empty-lbl {
+ font-family: var(--font-mono);
+ font-size: 10px;
+ letter-spacing: 0.18em;
+ color: var(--fg-muted);
+ margin-bottom: 8px;
+}
+.acct-detail-empty-sub {
+ font-size: 12.5px;
+ max-width: 240px;
+ line-height: 1.5;
+}
+.acct-detail-head {
+ display: flex; align-items: flex-start; justify-content: space-between;
+ gap: 12px;
+}
+.acct-detail-eyebrow {
+ font-family: var(--font-mono);
+ font-size: 10px;
+ letter-spacing: 0.16em;
+ color: var(--fg-subtle);
+ margin-bottom: 4px;
+}
+.acct-detail-title {
+ margin: 0;
+ font-family: var(--font-sans-kr);
+ font-size: 18px;
+ font-weight: 500;
+ color: var(--fg-strong);
+}
+.acct-detail-sub {
+ font-size: 11.5px;
+ color: var(--fg-muted);
+ letter-spacing: 0.04em;
+ margin-top: 4px;
+}
+.acct-detail-stat-grid {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 0;
+ border: 1px solid var(--rule);
+ border-radius: var(--r-sm);
+}
+.acct-detail-stat {
+ padding: 10px 12px;
+ border-right: 1px solid var(--rule);
+ border-bottom: 1px solid var(--rule);
+}
+.acct-detail-stat:nth-child(2n) { border-right: none; }
+.acct-detail-stat:nth-last-child(-n+2) { border-bottom: none; }
+.acct-detail-stat .lbl {
+ font-family: var(--font-mono);
+ font-size: 9.5px;
+ letter-spacing: 0.16em;
+ color: var(--fg-subtle);
+ margin-bottom: 4px;
+}
+.acct-detail-stat .val {
+ font-family: var(--font-mono);
+ font-size: 16px;
+ font-weight: 500;
+ font-variant-numeric: tabular-nums;
+ color: var(--fg-strong);
+}
+.acct-detail-stat .val .ccy { color: var(--fg-subtle); font-weight: 400; font-size: 11px; }
+.acct-detail-stat .sub {
+ font-family: var(--font-mono);
+ font-size: 10px;
+ color: var(--fg-subtle);
+ letter-spacing: 0.04em;
+ margin-top: 2px;
+}
+.acct-detail-section { display: flex; flex-direction: column; gap: 0; }
+.acct-detail-section-head {
+ font-family: var(--font-mono);
+ font-size: 10px;
+ letter-spacing: 0.18em;
+ color: var(--fg-subtle);
+ margin-bottom: 8px;
+}
+.acct-kv-row {
+ display: flex; justify-content: space-between; align-items: center;
+ padding: 7px 0;
+ border-bottom: 1px dashed var(--rule);
+ font-size: 12.5px;
+}
+.acct-kv-row:last-child { border-bottom: none; }
+.acct-kv-row .k {
+ font-family: var(--font-mono);
+ font-size: 10px;
+ letter-spacing: 0.14em;
+ color: var(--fg-subtle);
+}
+.acct-kv-row .v { color: var(--fg); display: inline-flex; align-items: center; gap: 4px; }
+.acct-mini-log {
+ background: var(--surface);
+ border: 1px solid var(--rule-ink);
+ border-radius: var(--r-sm);
+ padding: 4px 0;
+}
+.acct-mini-row {
+ display: grid;
+ grid-template-columns: 60px 1fr;
+ gap: 10px;
+ padding: 7px 12px;
+ font-family: var(--font-mono);
+ font-size: 11.5px;
+ color: var(--fg-on-ink);
+ border-bottom: 1px dashed var(--rule-ink);
+}
+.acct-mini-row:last-child { border-bottom: none; }
+.acct-mini-row .t { color: var(--fg-on-ink-muted); }
+.acct-mini-row.fail .msg { color: var(--conf-low); }
+.acct-mini-row.warn .msg { color: var(--conf-mid); }
+.acct-detail-actions {
+ display: flex; flex-wrap: wrap; gap: 6px;
+ margin-top: 4px;
+}
+.acct-detail-actions .btn { flex: 1; min-width: 110px; }
+.acct-detail-actions .btn.ghost { color: var(--conf-low); border-color: color-mix(in srgb, var(--conf-low) 30%, var(--rule)); }
+.acct-detail-actions .btn.ghost:hover { background: color-mix(in srgb, var(--conf-low) 8%, transparent); }
+
+/* ---- Chart of accounts -------------------------------------------- */
+.acct-coa {
+ display: flex; flex-direction: column;
+ gap: 20px;
+}
+.acct-coa-group {
+ background: var(--paper);
+ border: 1px solid var(--rule);
+ border-radius: var(--r-md);
+ overflow: hidden;
+}
+.acct-coa-head {
+ display: flex; align-items: center; gap: 10px;
+ padding: 12px 16px;
+ border-bottom: 1px solid var(--rule);
+ background: color-mix(in srgb, var(--paper) 94%, black 6%);
+}
+.acct-coa-title {
+ font-family: var(--font-sans-kr);
+ font-size: 14px;
+ font-weight: 500;
+ color: var(--fg-strong);
+}
+.acct-coa-count {
+ margin-left: auto;
+ font-family: var(--font-mono);
+ font-size: 10px;
+ letter-spacing: 0.14em;
+ color: var(--fg-subtle);
+}
+.acct-coa-rows { padding: 4px 0; }
+.acct-coa-row {
+ display: grid;
+ grid-template-columns: 1fr 2fr auto;
+ gap: 16px;
+ align-items: center;
+ padding: 7px 16px;
+ border-bottom: 1px solid var(--rule);
+ font-size: 12.5px;
+}
+.acct-coa-row:last-child { border-bottom: none; }
+.acct-coa-row.branch {
+ background: color-mix(in srgb, var(--paper) 96%, black 4%);
+}
+.acct-coa-row.branch .acct-coa-leaf {
+ font-weight: 500;
+ color: var(--fg-strong);
+}
+.acct-coa-tree {
+ display: inline-flex; align-items: center; gap: 6px;
+}
+.acct-coa-glyph {
+ font-family: var(--font-mono);
+ color: var(--fg-subtle);
+ width: 12px;
+ text-align: center;
+}
+.acct-coa-leaf {
+ font-family: var(--font-sans-kr);
+ color: var(--fg);
+}
+.acct-coa-path {
+ font-size: 11px;
+ color: var(--fg-subtle);
+ letter-spacing: 0.02em;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.acct-coa-amt {
+ font-size: 13px;
+ font-variant-numeric: tabular-nums;
+ color: var(--fg-strong);
+ text-align: right;
+}
+.acct-coa-amt.pos { color: var(--conf-rules); }
+.acct-coa-amt.neg { color: var(--fg-strong); }
+.acct-coa-amt .ccy { color: var(--fg-subtle); font-weight: 400; }
+
+/* =========================================================================
+ HISTORY SCREEN
+ ========================================================================= */
+.history-screen {
+ padding: 24px;
+ overflow-y: auto;
+ height: 100%;
+}
+
+/* Summary strip */
+.hist-summary-strip {
+ display: grid;
+ grid-template-columns: repeat(6, 1fr);
+ background: var(--paper);
+ border: 1px solid var(--rule);
+ border-radius: var(--r-md);
+ margin-bottom: 16px;
+ overflow: hidden;
+}
+.hist-summary-cell {
+ padding: 12px 16px;
+ border-right: 1px solid var(--rule);
+ display: flex; flex-direction: column;
+ gap: 4px;
+}
+.hist-summary-cell:last-child { border-right: none; }
+.hist-summary-cell .lbl {
+ font-family: var(--font-mono);
+ font-size: 9.5px;
+ letter-spacing: 0.16em;
+ color: var(--fg-subtle);
+}
+.hist-summary-cell .val {
+ font-family: var(--font-mono);
+ font-size: 20px;
+ font-weight: 500;
+ font-variant-numeric: tabular-nums;
+ color: var(--fg-strong);
+ line-height: 1;
+}
+.hist-summary-cell .val.neg { color: var(--conf-low); }
+
+/* Filters */
+.hist-filters {
+ display: flex; flex-direction: column;
+ gap: 10px;
+ padding: 12px 16px;
+ background: var(--paper);
+ border: 1px solid var(--rule);
+ border-radius: var(--r-md);
+ margin-bottom: 16px;
+}
+.hist-filter-row {
+ display: flex; align-items: center; gap: 12px;
+ flex-wrap: wrap;
+}
+.hist-filter-lbl {
+ font-family: var(--font-mono);
+ font-size: 10px;
+ letter-spacing: 0.16em;
+ color: var(--fg-subtle);
+ min-width: 48px;
+}
+.hist-filter-chips { display: flex; flex-wrap: wrap; gap: 4px; }
+.hist-chip {
+ display: inline-flex; align-items: center; gap: 6px;
+ height: 24px;
+ padding: 0 10px;
+ background: transparent;
+ border: 1px solid var(--rule);
+ border-radius: 2px;
+ font-family: var(--font-mono);
+ font-size: 10px;
+ font-weight: 500;
+ letter-spacing: 0.14em;
+ color: var(--fg-muted);
+ text-transform: uppercase;
+}
+.hist-chip:hover { color: var(--fg-strong); border-color: var(--fg-muted); }
+.hist-chip.active {
+ background: var(--fg-strong);
+ border-color: var(--fg-strong);
+ color: var(--bg);
+}
+.hist-chip.active .hist-chip-glyph { color: inherit !important; }
+.hist-chip-glyph { font-size: 12px; }
+
+/* Table */
+.hist-table {
+ background: var(--paper);
+ border: 1px solid var(--rule);
+ border-radius: var(--r-md);
+ overflow: hidden;
+}
+.hist-table-head {
+ display: grid;
+ grid-template-columns: 78px 110px 80px 60px 1fr 140px;
+ gap: 14px;
+ align-items: center;
+ padding: 10px 16px;
+ border-bottom: 1px solid var(--rule);
+ background: color-mix(in srgb, var(--paper) 94%, black 6%);
+ font-family: var(--font-mono);
+ font-size: 9.5px;
+ letter-spacing: 0.18em;
+ color: var(--fg-subtle);
+}
+
+.hist-day { display: flex; flex-direction: column; }
+.hist-day-head {
+ display: flex; align-items: center; gap: 12px;
+ padding: 14px 16px 6px;
+ border-bottom: 1px solid var(--rule);
+}
+.hist-day-lbl {
+ font-family: var(--font-mono);
+ font-size: 11px;
+ font-weight: 500;
+ letter-spacing: 0.18em;
+ color: var(--fg-strong);
+}
+.hist-day-date {
+ font-size: 11px;
+ letter-spacing: 0.06em;
+ color: var(--fg-subtle);
+}
+.hist-day-count {
+ margin-left: auto;
+ font-family: var(--font-mono);
+ font-size: 10px;
+ letter-spacing: 0.14em;
+ color: var(--fg-subtle);
+}
+
+.hist-day-rows { display: flex; flex-direction: column; }
+.hist-row {
+ display: grid;
+ grid-template-columns: 78px 110px 80px 60px 1fr 140px;
+ gap: 14px;
+ align-items: center;
+ padding: 11px 16px;
+ border-bottom: 1px solid var(--rule);
+}
+.hist-row:last-child { border-bottom: none; }
+.hist-row:hover { background: color-mix(in srgb, var(--paper) 96%, var(--fg-strong) 4%); }
+.hist-time {
+ font-size: 11.5px;
+ color: var(--fg-muted);
+ font-variant-numeric: tabular-nums;
+ letter-spacing: 0.02em;
+}
+.hist-col-kind {
+ display: inline-flex; align-items: center; gap: 6px;
+}
+.hist-glyph {
+ font-family: var(--font-mono);
+ font-size: 13px;
+ line-height: 1;
+ width: 14px;
+ text-align: center;
+}
+.hist-kind-lbl {
+ font-family: var(--font-mono);
+ font-size: 10px;
+ font-weight: 500;
+ letter-spacing: 0.14em;
+ color: var(--fg-strong);
+}
+.hist-actor {
+ display: inline-block;
+ padding: 1px 6px;
+ border-radius: 2px;
+ font-family: var(--font-mono);
+ font-size: 10px;
+ letter-spacing: 0.1em;
+ font-weight: 500;
+}
+.hist-actor-you { background: var(--fg-strong); color: var(--bg); }
+.hist-actor-system { background: var(--rule); color: var(--fg); }
+.hist-actor-agent { background: color-mix(in srgb, var(--conf-high) 18%, transparent); color: var(--conf-high); }
+
+.hist-col-summary { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
+.hist-summary {
+ font-size: 13px;
+ color: var(--fg);
+ font-family: var(--font-sans-kr);
+}
+.hist-detail {
+ font-size: 11px;
+ color: var(--fg-subtle);
+ letter-spacing: 0.02em;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+.hist-col-ref {
+ font-size: 11px;
+ color: var(--fg-muted);
+ letter-spacing: 0.02em;
+ text-align: right;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.hist-empty {
+ padding: 48px 16px;
+ text-align: center;
+ color: var(--fg-subtle);
+}
+.hist-empty-lbl {
+ font-family: var(--font-mono);
+ font-size: 11px;
+ letter-spacing: 0.18em;
+ color: var(--fg-muted);
+ margin-bottom: 6px;
+}
+.hist-empty-sub { font-size: 12.5px; }
+
+/* "all entity" badge (used in history for system events) */
+.entity-badge.all {
+ background: var(--rule);
+ color: var(--fg-muted);
+}
+.entity-badge.all::before { background: var(--fg-muted); }
+
diff --git a/ui_kits/web/data.js b/ui_kits/web/data.js
index decfa9c..eaa0e08 100644
--- a/ui_kits/web/data.js
+++ b/ui_kits/web/data.js
@@ -50,6 +50,37 @@ window.AKEFIN_DATA = (() => {
],
};
+ // Connected data sources (banks, exchanges, file drops)
+ const connectedAccounts = [
+ { id: "src01", name: "Toss · 신한 입출금", kind: "bank", country: "KR", entity: "personal", maskedNo: "•••• 4823", balance: 14200000, ccy: "KRW", lastSync: "2026-03-08 09:14", method: "csv-poll", status: "ok", nextRun: "in 4h", txCount30d: 247 },
+ { id: "src02", name: "Kakao Bank · 9TFox", kind: "bank", country: "KR", entity: "tfox", maskedNo: "•••• 1102", balance: 18400000, ccy: "KRW", lastSync: "2026-03-07 18:02", method: "csv-poll", status: "ok", nextRun: "in 12h", txCount30d: 62 },
+ { id: "src03", name: "Wise · multi-ccy", kind: "exchange", country: "BE", entity: "personal", maskedNo: "USR-93281", balance: 2840, ccy: "EUR", lastSync: "2026-03-07 12:48", method: "api", status: "ok", nextRun: "in 1h", txCount30d: 41 },
+ { id: "src04", name: "Wise · 9TFox", kind: "exchange", country: "BE", entity: "tfox", maskedNo: "USR-93281-B", balance: 176.40, ccy: "EUR", lastSync: "2026-03-07 12:48", method: "api", status: "ok", nextRun: "in 1h", txCount30d: 14 },
+ { id: "src05", name: "Garanti BBVA", kind: "bank", country: "TR", entity: "personal", maskedNo: "•••• 7012", balance: 62400, ccy: "TRY", lastSync: "2026-03-06 22:01", method: "csv-drop", status: "warn", nextRun: "manual", txCount30d: 89 },
+ { id: "src06", name: "Finacode · partner", kind: "manual", country: "EU", entity: "finacode", maskedNo: "—", balance: 6320, ccy: "EUR", lastSync: "2026-03-03 11:20", method: "manual", status: "idle", nextRun: "—", txCount30d: 3 },
+ { id: "src07", name: "MUFG · JPY savings", kind: "bank", country: "JP", entity: "personal", maskedNo: "•••• 9981", balance: 148000, ccy: "JPY", lastSync: "2026-03-05 03:11", method: "csv-drop", status: "fail", nextRun: "manual", txCount30d: 8 },
+ ];
+
+ // Append-only audit history — most recent first
+ const history = [
+ { id: "h001", at: "2026-03-08 11:42:18", actor: "you", kind: "commit", entity: "all", summary: "Committed ledger to git", detail: "e4a82c1 · 23 entries · +1.2k −0.1k", ref: "main" },
+ { id: "h002", at: "2026-03-08 11:38:02", actor: "you", kind: "approve", entity: "personal", summary: "Approved 18 transactions ≥ 0.85 confidence", detail: "batch via ⌘⇧A", ref: "t14…t31" },
+ { id: "h003", at: "2026-03-08 11:36:40", actor: "you", kind: "override", entity: "personal", summary: "Overrode category on 1 transaction", detail: "GS25 역삼점 → Personal:Expenses:Convenience", ref: "t05" },
+ { id: "h004", at: "2026-03-08 11:30:09", actor: "you", kind: "rule", entity: "personal", summary: "Promoted rule · 스타벅스 *", detail: "→ Personal:Expenses:Food:Coffee · 14 matches", ref: "r01" },
+ { id: "h005", at: "2026-03-08 09:14:00", actor: "system", kind: "import", entity: "personal", summary: "Imported Toss · 신한 입출금 CSV", detail: "247 rows · 153 auto · 44 high · 35 review · 15 failed", ref: "i01" },
+ { id: "h006", at: "2026-03-08 09:11:22", actor: "system", kind: "poll", entity: "personal", summary: "Polled Toss for new transactions", detail: "247 new rows · password decrypt ok", ref: "src01" },
+ { id: "h007", at: "2026-03-08 06:00:01", actor: "system", kind: "fx", entity: "all", summary: "FX rates refreshed", detail: "KRW=1 · EUR=1456.20 · TRY=41.32 · JPY=9.21", ref: "fx-snap" },
+ { id: "h008", at: "2026-03-07 22:48:50", actor: "agent", kind: "categorize", entity: "tfox", summary: "Categorized 12 transactions", detail: "avg confidence 0.89 · 1 unmatched", ref: "agent-run-128" },
+ { id: "h009", at: "2026-03-07 18:02:00", actor: "system", kind: "import", entity: "tfox", summary: "Imported Kakao Bank · 9TFox CSV", detail: "62 rows · 58 auto · 3 high · 1 review", ref: "i02" },
+ { id: "h010", at: "2026-03-07 12:48:00", actor: "system", kind: "import", entity: "tfox", summary: "Imported Wise · 9TFox via API", detail: "14 rows · 12 auto · 2 high", ref: "i03" },
+ { id: "h011", at: "2026-03-07 09:14:30", actor: "you", kind: "skip", entity: "personal", summary: "Skipped & re-queued 1 transaction", detail: "ATM 출금 강남역 · low confidence (0.42)", ref: "t13" },
+ { id: "h012", at: "2026-03-06 22:01:00", actor: "system", kind: "import", entity: "personal", summary: "Imported Garanti BBVA CSV", detail: "89 rows · 61 auto · 14 high · 11 review · 3 failed", ref: "i04" },
+ { id: "h013", at: "2026-03-06 20:55:14", actor: "you", kind: "rule", entity: "tfox", summary: "Edited rule · Hetzner *", detail: "target → 9TFox:Expenses:Infrastructure", ref: "r03" },
+ { id: "h014", at: "2026-03-06 14:12:08", actor: "you", kind: "commit", entity: "all", summary: "Committed ledger to git", detail: "3a17fb9 · 47 entries · pushed to origin/main", ref: "main" },
+ { id: "h015", at: "2026-03-05 03:11:00", actor: "system", kind: "error", entity: "personal", summary: "MUFG · JPY savings — CSV parse failed", detail: "Encoding mismatch · expected Shift-JIS, got UTF-8", ref: "src07" },
+ { id: "h016", at: "2026-03-04 18:32:11", actor: "you", kind: "approve", entity: "tfox", summary: "Approved 7 transactions", detail: "manual review queue", ref: "t44…t50" },
+ ];
+
const importRuns = [
{ id: "i01", at: "2026-03-08 09:14", source: "Toss · 신한 입출금", rows: 247, auto: 153, high: 44, review: 35, failed: 15, entity: "personal" },
{ id: "i02", at: "2026-03-07 18:02", source: "Kakao Bank · 9TFox", rows: 62, auto: 58, high: 3, review: 1, failed: 0, entity: "tfox" },
@@ -110,5 +141,5 @@ window.AKEFIN_DATA = (() => {
"Income:Consulting", "Income:PartnerShare", "Cash",
];
- return { transactions, ruleSuggestions, accountsByEntity, importRuns, entities, allAccounts, fx, convert, commands, categories };
+ return { transactions, ruleSuggestions, accountsByEntity, importRuns, entities, allAccounts, fx, convert, commands, categories, connectedAccounts, history };
})();
diff --git a/ui_kits/web/index.html b/ui_kits/web/index.html
index 644f719..8e7a10a 100644
--- a/ui_kits/web/index.html
+++ b/ui_kits/web/index.html
@@ -25,6 +25,8 @@
+
+