/* Kgyoka Finance — the accounting house skin.
 *
 * Owner rulings: "make the UI match the old demo / Kgyoka accounting. I don't
 * like the Kgyoka One UI" (2026-08-05), then "look at the UI from accounting
 * app and use that exact UI" — palette, type, layout AND chrome.
 *
 * Loaded ONLY by finance.html, after base.css. Not a fork and not a second
 * design system: the white-label skin contract used the way it was designed —
 * override the tokens and everything else re-derives. base.css is untouched;
 * Kgyoka One and every org skin are unaffected.
 *
 * Every value below is lifted from accounting/kgyoka_*.html's :root and
 * component block, including its dark theme, which is why the two apps now
 * read as one product.
 */

:root {
  /* ── the 8-token contract ── */
  --skin-accent: #1f7a4d;         /* accounting --green */
  --skin-accent-vivid: #4fbe89;
  --skin-on-accent: #ffffff;
  --skin-shell: #10502f;          /* accounting --green-deep */
  --skin-shell-ink: #ffffff;
  --skin-display: 'Fraunces';
  --skin-display-case: none;      /* a serif sets sentence case, never Oswald's caps */
  --skin-radius: 10px;

  /* ── accounting's own variables, named as it names them ── */
  --fin-surface: #ffffff;         /* panels, sidebar, topbar  (accounting #fff) */
  --fin-head: #10502f;            /* headings + active nav ink (--green-deep) */
  --fin-soft: #e8f3ec;            /* hover/active wash        (--green-soft) */

  --font-body: 'Outfit', 'IBM Plex Sans Arabic', ui-sans-serif, system-ui, sans-serif;
  --track-display: -.02em;        /* serif tracking, not condensed-caps tracking */

  --line: rgba(16, 80, 47, .13);
  --line-strong: rgba(16, 80, 47, .13);
  --paper: #ffffff;
  --surface-app: #fbfdfb;
  --surface-sunken: #fbfdfb;      /* body carries .app-bg, which paints this */
  --ink-1: #13211a;
  --ink-2: #54635a;
  --ink-3: #54635a;
  --danger: #A32D2D;
  --danger-text: #A32D2D;
}

/* ── DARK ── accounting's shipped dark theme, same `kgyoka-theme` key and the
 * same [data-theme="dark"] switch, so the toggle feels identical in both apps.
 * Deliberately NOT tied to prefers-color-scheme: accounting defaults to light
 * and remembers an explicit choice, and the two must not disagree. */
:root[data-theme="dark"] {
  --skin-accent: #2ecc82;
  --skin-accent-vivid: #2ecc82;
  --skin-on-accent: #0f1a13;
  --skin-shell-ink: #e8f0eb;

  --fin-surface: #1a2e1f;
  --fin-head: #a8f0cc;
  --fin-soft: rgba(46, 204, 130, .12);

  --line: rgba(168, 240, 204, .12);
  --line-strong: rgba(168, 240, 204, .12);
  --paper: #1a2e1f;
  --surface-app: #0f1a13;
  --surface-sunken: #0f1a13;
  --ink-1: #e8f0eb;
  --ink-2: #7a9e87;
  --ink-3: #7a9e87;
  --danger: #ff6b6b;
  --danger-text: #ff6b6b;
  --accent-soft: rgba(46, 204, 130, .12);
  --accent-text: #2ecc82;
  --ok: #2ecc82;
  --ok-text: #2ecc82;
  --warn: #ffd54f;
  --warn-text: #ffd54f;
  /* The three status WASHES, not just their ink. base.css's *-soft values are
     opaque near-white (#e4f3ea / #faeedd / #fbe7e7); overriding only *-text
     left every .chip.green/.gold/.red painting bright text on a white pill in
     dark — illegible, and it reached Team, Payroll, Profile and Settings'
     "Saved" chip alike. Translucent, like --accent-soft above, so one value is
     right on both dark surfaces. */
  --ok-soft: rgba(46, 204, 130, .14);
  --warn-soft: rgba(255, 213, 79, .14);
  --danger-soft: rgba(255, 107, 107, .14);
}

/* ── BLACK ── the standalone demo's third mode, ported 1:1: pure-OLED neutral
 * surfaces, same accents as dark. Stored under the shared `kgyoka-theme` key;
 * accounting has no black theme and simply renders a stored 'black' as light. */
:root[data-theme="black"] {
  --skin-accent: #2ecc82;
  --skin-accent-vivid: #2ecc82;
  --skin-on-accent: #0f1a13;
  --skin-shell-ink: #ededec;

  --fin-surface: #141414;
  --fin-head: #a8f0cc;
  --fin-soft: rgba(46, 204, 130, .12);

  --line: rgba(255, 255, 255, .12);
  --line-strong: rgba(255, 255, 255, .12);
  --paper: #141414;
  --surface-app: #000000;
  --surface-sunken: #0c0c0c;
  --ink-1: #ededec;
  --ink-2: #9a9a96;
  --ink-3: #9a9a96;
  --danger: #ff6b6b;
  --danger-text: #ff6b6b;
  --accent-soft: rgba(46, 204, 130, .12);
  --accent-text: #2ecc82;
  --ok: #2ecc82;
  --ok-text: #2ecc82;
  --warn: #ffd54f;
  --warn-text: #ffd54f;
  /* same three washes as dark, and for the same reason — see the note there */
  --ok-soft: rgba(46, 204, 130, .14);
  --warn-soft: rgba(255, 213, 79, .14);
  --danger-soft: rgba(255, 107, 107, .14);
}

/* ── USER ACCENTS ── ported 1:1 from the demo's tokens/colors.css data-accent
 * axis. Forest is the :root default above (no attribute set). Light swaps the
 * greens wholesale; in dark/black the demo's brightened accents are right for
 * text and headings but too pastel for solid fills, so indigo/plum/rose keep
 * its saturated fill values as --skin-accent with the pastel on --accent-text.
 * Money/status colours are semantic and never follow the accent. */
:root[data-accent="indigo"] {
  --skin-accent: #3450a8; --skin-accent-vivid: #7d99f2; --skin-on-accent: #ffffff;
  --skin-shell: #1f3373;
  --fin-head: #1f3373; --fin-soft: #e9edf9;
}
:root[data-theme="dark"][data-accent="indigo"], :root[data-theme="black"][data-accent="indigo"] {
  --skin-accent: #4a63c4; --skin-accent-vivid: #7d99f2; --skin-on-accent: #ffffff;
  --fin-head: #a8bcf8; --fin-soft: rgba(125, 153, 242, .15);
  --accent-soft: rgba(125, 153, 242, .15); --accent-text: #7d99f2;
}
:root[data-theme="dark"][data-accent="indigo"] {
  --line: rgba(168, 188, 248, .12); --line-strong: rgba(168, 188, 248, .12);
}

:root[data-accent="plum"] {
  --skin-accent: #7c3f87; --skin-accent-vivid: #c98ad6; --skin-on-accent: #ffffff;
  --skin-shell: #4e2359;
  --fin-head: #4e2359; --fin-soft: #f3eaf6;
}
:root[data-theme="dark"][data-accent="plum"], :root[data-theme="black"][data-accent="plum"] {
  --skin-accent: #9c4bb0; --skin-accent-vivid: #c98ad6; --skin-on-accent: #ffffff;
  --fin-head: #ddb3e6; --fin-soft: rgba(201, 138, 214, .15);
  --accent-soft: rgba(201, 138, 214, .15); --accent-text: #c98ad6;
}
:root[data-theme="dark"][data-accent="plum"] {
  --line: rgba(221, 179, 230, .12); --line-strong: rgba(221, 179, 230, .12);
}

:root[data-accent="rose"] {
  --skin-accent: #a83a5e; --skin-accent-vivid: #e585a6; --skin-on-accent: #ffffff;
  --skin-shell: #6e1f3a;
  --fin-head: #6e1f3a; --fin-soft: #f8e9ee;
}
:root[data-theme="dark"][data-accent="rose"], :root[data-theme="black"][data-accent="rose"] {
  --skin-accent: #bf4370; --skin-accent-vivid: #e585a6; --skin-on-accent: #ffffff;
  --fin-head: #f0b3c8; --fin-soft: rgba(229, 133, 166, .15);
  --accent-soft: rgba(229, 133, 166, .15); --accent-text: #e585a6;
}
:root[data-theme="dark"][data-accent="rose"] {
  --line: rgba(240, 179, 200, .12); --line-strong: rgba(240, 179, 200, .12);
}

:root[data-accent="teal"] {
  --skin-accent: #17767a; --skin-accent-vivid: #4ecdd3; --skin-on-accent: #ffffff;
  --skin-shell: #0c4d50;
  --fin-head: #0c4d50; --fin-soft: #e5f2f2;
}
:root[data-theme="dark"][data-accent="teal"], :root[data-theme="black"][data-accent="teal"] {
  --skin-accent: #4ecdd3; --skin-accent-vivid: #4ecdd3; --skin-on-accent: #082b2d;
  --fin-head: #8adfe3; --fin-soft: rgba(78, 205, 211, .15);
  --accent-soft: rgba(78, 205, 211, .15); --accent-text: #4ecdd3;
}
:root[data-theme="dark"][data-accent="teal"] {
  --line: rgba(138, 223, 227, .12); --line-strong: rgba(138, 223, 227, .12);
}

/* Material Symbols is a ligature font: when it fails to load, the browser
 * renders the ligature NAME as text and the UI fills with "receipt_long".
 * finance.html sets .kf-noicons when the face is genuinely unavailable, so a
 * missing icon costs an icon and never a wall of code words. The nav, topbar
 * and quick-add carry emoji instead — the same glyphs accounting uses — so the
 * chrome stays legible either way. */
.kf-noicons .material-symbols-rounded { display: none !important; }

/* ══ THE ACCOUNTING SHELL ═════════════════════════════════════════════════
 * Same markup, different clothes. finance.js keeps rendering the platform's
 * classes (.topbar / #finNav / .pcard / .rowcard / .btn / .stat); everything
 * below re-dresses them as accounting's (.sidebar / .panel / .btn +
 * .btn-primary / .met). A restyle survives the Business-tier port; a re-render
 * would have to be redone module by module.
 * ════════════════════════════════════════════════════════════════════════ */

/* ── page header (accounting .page-title) ────────────────────────────────── */
.topbar {
  background: var(--fin-surface);
  color: var(--ink-1);
  border-bottom: 1px solid var(--line);
  padding: 14px 20px;
}
.topbar .nm {
  font-family: var(--font-display);
  font-size: 1.35rem;
  font-weight: 600;
  color: var(--fin-head);
  letter-spacing: -.02em;
}
.topbar .btn.ghost,
.topbar .btn.ghost:hover:not(:disabled) {
  background: var(--fin-surface);
  color: var(--ink-1);
  border: 1px solid var(--line);
}
.topbar .btn.ghost:hover:not(:disabled) { background: var(--fin-soft); }
.topbar select { background: var(--fin-surface); color: var(--ink-1); border-color: var(--line); }

/* ── buttons ─────────────────────────────────────────────────────────────
 * Platform: .btn filled, .btn.ghost outline. Accounting: .btn outline,
 * .btn-primary filled. Same two roles — the mapping is one-to-one. */
.btn {
  font-size: .85rem;
  font-weight: 600;
  padding: 9px 18px;
  min-height: 0;
  border-radius: 10px;
  border: 1px solid var(--accent);
  background: var(--accent);
  color: var(--on-accent);
  transition: all .15s;
}
.btn:hover:not(:disabled) { background: var(--fin-head); border-color: var(--fin-head); color: var(--fin-surface); filter: none; }
.btn.ghost { background: var(--fin-surface); color: var(--ink-1); border: 1px solid var(--line); }
.btn.ghost:hover:not(:disabled) { background: var(--fin-soft); color: var(--ink-1); }
.btn.danger, .btn.ghost.danger {
  background: var(--fin-surface);
  color: var(--danger);
  border-color: rgba(163, 45, 45, .3);
}
.btn.danger:hover:not(:disabled) { background: #FCEBEB; color: var(--danger); }
:root[data-theme="dark"] .btn.danger:hover:not(:disabled),
:root[data-theme="black"] .btn.danger:hover:not(:disabled) { background: rgba(255, 107, 107, .12); }
.btn.sm { padding: 7px 12px; font-size: .8rem; }

/* ── panels (accounting .panel) ──────────────────────────────────────────── */
.pcard, .card {
  background: var(--fin-surface);
  border: 1px solid var(--line);
  border-radius: 16px;
  padding: 22px;
  box-shadow: none;
}
.rowcard {
  background: var(--fin-surface);
  border: 1px solid var(--line);
  border-radius: 16px;
  box-shadow: none;
}
.sec-head .k-overline { font-size: .72rem; letter-spacing: .07em; color: var(--ink-2); font-weight: 600; }

/* ── metric tiles (accounting .met) ──────────────────────────────────────── */
.stat {
  background: var(--fin-surface);
  border: 1px solid var(--line);
  border-inline-start: 1px solid var(--line);   /* accounting has no accent edge */
  border-radius: 14px;
  padding: 18px;
  box-shadow: none;
}
.stat .num, .stat .val {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.7rem;
  color: var(--fin-head);
  letter-spacing: -.02em;
}
.stat .lbl { font-size: .75rem; font-weight: 500; letter-spacing: .06em; color: var(--ink-2); }
/* A tile is a LABEL OVER A NUMBER, and nothing was making it one. base.css
   styles both parts but leaves them inline spans, so on a 375px phone a
   nowrap label (117px) and its amount (88px) shared one line inside a 165px
   tile — the amount ran 58px past the tile AND past the viewport, and since
   the page itself does not scroll horizontally the end of the figure was
   simply unreachable. Seen as "$84." on Approvals; latent everywhere else,
   waiting for a value wide enough. Fixed here rather than per module because
   the tiers disagree on which classes they use (.lbl/.num vs the newer
   .k-overline/.k-num) — both are addressed, so no tab can miss the fix or
   reintroduce it. Finance-only: base.css stays untouched for Kgyoka One. */
.stat > .lbl, .stat > .num, .stat > .k-overline, .stat > .k-num { display: block; }
.stat > .num, .stat > .k-num { overflow-wrap: anywhere; }
.stat { min-width: 0; }

/* ── tables (accounting's, verbatim) ─────────────────────────────────────── */
table { font-size: .85rem; }
thead th {
  text-align: start;
  padding: 10px 12px;
  font-size: .72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .07em;
  color: var(--ink-2);
  border-bottom: 1px solid var(--line);
}
tbody tr { border-bottom: 1px solid var(--line); transition: background .1s; }
tbody tr:last-child { border-bottom: none; }
tbody tr:hover { background: var(--fin-soft); }
tbody td { padding: 12px; color: var(--ink-1); }

/* ── inputs ──────────────────────────────────────────────────────────────── */
input, select, textarea {
  border-radius: 10px;
  border-color: var(--line);
  background: var(--fin-surface);
  color: var(--ink-1);
}
input:focus, select:focus, textarea:focus { border-color: var(--accent); box-shadow: none; }
label { color: var(--ink-2); letter-spacing: .05em; }
.chip { font-size: .72rem; font-weight: 600; padding: 3px 10px; }

/* ══ SIDEBAR CHROME (standalone only — finance.js renders these) ══════════
 * accounting's .sb-logo / .sb-label / .sb-footer, so the sidebar is a real
 * sidebar with a brand, grouped sections and the signed-in user at the bottom
 * — not a bare list of links. */
.fin-brand {
  /* accounting's .sb-logo, matched exactly: padding 10px 20px, a bottom rule,
     and the mark alone. The "Finance" word that used to sit under it was an
     addition accounting does not have — a second element the eye has to
     balance against the first, above a left-aligned nav, which is what kept
     reading as crooked however precisely the box was centred. */
  padding: 10px 20px;
  border-bottom: 1px solid var(--line);
  flex: none;
}
/* accounting's .sb-logo img, and the same brightness lift in dark — without it
   the dark logo sinks into the sidebar.
   max-width is not decoration: the source is 360px wide, so if this rule ever
   fails to load the image renders at natural size and bursts out of the 240px
   sidebar across the page. That happened once, because the hosting no-cache
   header covered html|js|json but not css and a stale stylesheet was served. */
.fin-logo {
  display: block;
  width: 80px;             /* accounting's exact figure */
  max-width: 100%;
  height: auto;
  object-fit: contain;
  margin: 0 auto;          /* accounting's margin:0 auto — dead centre */
}
:root[data-theme="dark"] .fin-logo,
:root[data-theme="black"] .fin-logo { filter: brightness(1.3); }
.fin-grp {
  font-size: 10px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-2);
  font-weight: 600;
  padding: 18px 12px 6px;
  margin: 0;
}
.fin-ico { width: 18px; text-align: center; flex: none; font-size: 1rem; line-height: 1; }
.fin-foot {
  padding: 14px;
  border-top: 1px solid var(--line);
  flex: none;
  display: flex;
  flex-direction: column;
  gap: 9px;
}
.fin-user { display: flex; align-items: center; gap: 10px; min-width: 0; }
.fin-avatar {
  width: 32px; height: 32px; border-radius: 50%;
  background: var(--fin-soft); color: var(--fin-head);
  display: flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 600; flex: none;
}
.fin-nm { font-size: .82rem; font-weight: 500; color: var(--ink-1); }
.fin-rl {
  font-size: .75rem; color: var(--ink-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* sign-out and the theme toggle sit in the topbar, so they stay reachable when
   the sidebar collapses to a bottom bar on a phone */
#finTheme { padding-inline: 9px; font-size: .95rem; line-height: 1; }

/* ── NAV: accounting's 240px sidebar on desktop ──────────────────────────── */
@media (min-width: 900px) {
  .app-col.wide { padding-inline-start: 240px; }
  #finNav {
    position: fixed;
    inset-block: 0;
    inset-inline-start: 0;
    width: 240px;
    margin: 0;
    padding: 0;
    background: var(--fin-surface);
    border: none;
    border-inline-end: 1px solid var(--line);
    border-radius: 0;
    box-shadow: none;
    flex-direction: column;
    flex-wrap: nowrap;
    align-items: stretch;
    gap: 0;
    z-index: 60;
  }
  .fin-navlist { flex: 1; min-height: 0; overflow-y: auto; padding: 4px 10px 10px; }
  #finNav .tab {
    display: flex;
    width: 100%;
    flex: 0 0 auto;
    min-width: 0;
    justify-content: flex-start;
    align-items: center;
    gap: 10px;
    padding: 9px 12px;
    border-radius: 10px;
    font-size: .88rem;
    font-weight: 500;
    color: var(--ink-1);
    background: none;
    border: none;
    text-align: start;
  }
  #finNav .tab:hover, #finNav .tab.active { background: var(--fin-soft); color: var(--fin-head); }
  #finNav .tab .material-symbols-rounded { font-size: 18px; }
  /* accounting's main column: one stack of full-width panels, not a masonry */
  .app-col.wide .screen { display: block; max-width: 1100px; padding: 28px 32px 32px; }
  .app-col.wide .screen > * { margin-bottom: 20px; }
}

/* ── NAV: accounting's bottom bar on a phone (and in the Play shell) ─────── */
@media (max-width: 899px) {
  #finNav {
    position: fixed;
    inset-inline: 0;
    bottom: 0;
    margin: 0;
    padding: 0;
    background: var(--fin-surface);
    border: none;
    border-top: 1px solid var(--line);
    border-radius: 0;
    box-shadow: none;
    z-index: 60;
  }
  .fin-brand, .fin-grp, .fin-foot { display: none; }   /* sidebar-only chrome */
  .fin-navlist {
    display: flex;
    flex-wrap: nowrap;
    overflow-x: auto;
    padding: 8px 4px calc(8px + env(safe-area-inset-bottom, 0px));
  }
  #finNav .tab {
    flex: 1 0 auto;
    min-width: 62px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 3px;
    padding: 6px 4px;
    border-radius: 8px;
    font-size: .6rem;
    font-weight: 500;
    color: var(--ink-2);
    background: none;
    border: none;
  }
  #finNav .tab.active, #finNav .tab:hover { color: var(--fin-head); background: var(--fin-soft); }
  #finNav .tab .material-symbols-rounded { font-size: 1.2rem; }
  .fin-ico { font-size: 1.15rem; width: auto; }
  /* !important is load-bearing: finance.js writes padding-bottom:24px inline,
     which would leave the last rows sitting under the bottom bar */
  .app-main { padding-bottom: calc(84px + env(safe-area-inset-bottom, 0px)) !important; }
  .app-col.wide .screen { padding: 16px 16px 20px; }
  #finFab { bottom: calc(80px + env(safe-area-inset-bottom, 0px)); }
  /* The BUSINESS tier's nav (#fbNav) is a different element from the personal
     tier's bottom bar (#finNav) above, and the skin had only ever dressed the
     latter — so the business tabs were the raw platform .tab: 23px tall with
     no padding at all, against a 44px touch minimum. Eighteen of them, three
     to a row. Height only; the wrap and the ordering are left exactly as the
     tier renders them, because how a phone should present eighteen tabs is a
     design call for whoever owns that surface, not something to change from
     inside a sizing fix. */
  #fbNav .tab { min-height: 44px; padding: 6px 8px; }
  /* one row, not three: the workspace name can be long, so the title truncates
     rather than pushing the actions onto their own lines */
  .topbar { padding: 10px 14px; flex-wrap: nowrap; gap: 6px; }
  .topbar .nm {
    font-size: 1.05rem;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  #finSignOut { padding-inline: 10px; }
}

/* ── appearance controls (account screen, Settings → Theme in the old demo) ──
 * Mode tiles carry a small surface preview; swatches are the accent's light
 * value with a check on the active one. */
.fin-modetile {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  flex: 1; min-width: 90px; padding: 10px 12px;
  border: 1.5px solid var(--line); border-radius: var(--radius-md);
  background: var(--paper); color: var(--ink-1);
  font-family: var(--font-body); font-size: var(--text-sm); cursor: pointer;
}
.fin-modetile.on { border-color: var(--accent); background: var(--accent-soft); }
.fin-modetile .prev {
  width: 100%; height: 34px; border-radius: 6px;
  border: 1px solid var(--line-strong); pointer-events: none;
}
.fin-accsw {
  width: 30px; height: 30px; border-radius: 50%;
  border: 2px solid transparent; padding: 0; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  color: #fff; font-size: 14px; line-height: 1;
}
.fin-accsw.on { border-color: var(--ink-1); }

/* ══ FUTURE-UI LAYER ══════════════════════════════════════════════════════
 * Additive polish over the accounting skin: aurora canvas, glass chrome,
 * micro-interactions, skeletons, command palette, view-transition mapping,
 * shared tooltip + trend bars, Fraunces optical sizing. Every colour below
 * DERIVES from tokens already in this file or base.css (--accent /
 * --accent-vivid / --fin-surface / --fin-soft / --fin-head / --line / --ok /
 * --danger) so all 3 themes x 5 accents resolve with zero per-combo rules;
 * motion rides base.css's --dur-1/2/3 + --ease-out/--ease-spring and the
 * whole layer sits under the reduced-motion kill-switch at the end of this
 * file. Loaded ONLY by finance.html — Kgyoka One and org skins unreachable.
 * ════════════════════════════════════════════════════════════════════════ */

/* ── aurora wash (P9): fixed accent-derived mesh behind everything ────────
 * body::before at z-index:-1 paints above the propagated canvas background
 * and below all content. Accents recolor it automatically via --accent /
 * --accent-vivid. Deliberately static — no drift animation, no battery cost,
 * no reduced-motion surface. */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(38rem 26rem at 8% -6%, color-mix(in srgb, var(--accent) 9%, transparent), transparent 60%),
    radial-gradient(30rem 22rem at 105% 12%, color-mix(in srgb, var(--accent-vivid) 7%, transparent), transparent 55%),
    radial-gradient(26rem 26rem at 55% 110%, color-mix(in srgb, var(--accent) 5%, transparent), transparent 60%);
}
:root[data-theme="dark"] body::before {
  background:
    radial-gradient(38rem 26rem at 8% -6%, color-mix(in srgb, var(--accent) 6%, transparent), transparent 60%),
    radial-gradient(30rem 22rem at 105% 12%, color-mix(in srgb, var(--accent-vivid) 5%, transparent), transparent 55%),
    radial-gradient(26rem 26rem at 55% 110%, color-mix(in srgb, var(--accent) 4%, transparent), transparent 60%);
}
/* pure OLED black is that theme's whole point — never dilute it */
:root[data-theme="black"] body::before { display: none; }
/* base.css paints .app-col opaque --surface-app edge to edge, which would
 * hide the mesh (and give the glass chrome nothing to blur). body already
 * paints the same token, so going transparent is visually free. */
.app-col.wide { background: transparent; }

/* ── glass chrome (P5): sticky translucent topbar + sidebar/bottom bar ────
 * The overflow pair moves scrolling from .app-main to the document so
 * content genuinely slides under the sticky bar. Progressive: browsers
 * without overflow:clip drop that declaration, base.css's overflow-x:hidden
 * survives, overflow-y:visible computes back to auto — i.e. today's inner
 * scroll, today's look. The 1px var(--line) hairline already on both bars is
 * what sells the glass edge; tint is 78% of the SAME surface the text was
 * contrast-tested on (88% in black so OLED stays deep). */
.app-col.wide .app-main { overflow-x: clip; overflow-y: visible; }
.topbar { position: sticky; top: 0; z-index: 50; }   /* below #finNav (60) + overlays */
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .topbar, #finNav {
    background: color-mix(in srgb, var(--fin-surface) 78%, transparent);
    -webkit-backdrop-filter: blur(14px) saturate(1.5);
    backdrop-filter: blur(14px) saturate(1.5);
  }
  :root[data-theme="black"] .topbar,
  :root[data-theme="black"] #finNav {
    background: color-mix(in srgb, var(--fin-surface) 88%, transparent);
  }
}

/* ── micro-interactions (P8) ──────────────────────────────────────────────
 * Hovers gated behind (hover:hover) so touch never gets sticky states;
 * presses (:active) work everywhere. Scoped to #finBody/#fbBody so the nav
 * rowcard and modal cards never lift. Accent-tinted shadows re-derive per
 * accent automatically. */
#finBody .pcard, #fbBody .pcard,
#finBody .rowcard, #fbBody .rowcard,
#finBody .stat, #fbBody .stat {
  transition: transform var(--dur-1) var(--ease-out), box-shadow var(--dur-1) var(--ease-out);
}
@media (hover: hover) {
  #finBody .pcard:hover, #fbBody .pcard:hover,
  #finBody .rowcard:hover, #fbBody .rowcard:hover,
  #finBody .stat:hover, #fbBody .stat:hover {
    transform: translateY(-2px);
    box-shadow: 0 8px 24px -8px color-mix(in srgb, var(--accent) 18%, transparent);
  }
  /* !important: finance.js writes the resting shadow inline on the FAB */
  #finFab:hover {
    transform: scale(1.06);
    box-shadow: 0 10px 26px -6px color-mix(in srgb, var(--accent) 45%, transparent) !important;
  }
}
.btn {
  transition: filter var(--dur-1) var(--ease-out), background var(--dur-1) var(--ease-out),
    border-color var(--dur-1) var(--ease-out), color var(--dur-1) var(--ease-out),
    transform var(--dur-1) var(--ease-spring);
}
.btn:active:not(:disabled) { transform: scale(.97); }
#finFab { transition: transform var(--dur-1) var(--ease-spring), box-shadow var(--dur-1) var(--ease-out); }
#finFab:active { transform: scale(.94); }
/* sidebar tabs: the active wash gets a small accent bar that scales in from
 * the inline-start edge instead of appearing flat-instant. Desktop only —
 * the bottom bar's tabs are vertical icon stacks where a side bar reads as
 * clutter. */
@media (min-width: 900px) {
  #finNav .tab { position: relative; }
  #finNav .tab::before {
    content: '';
    position: absolute;
    inset-inline-start: 0;
    top: 22%;
    bottom: 22%;
    width: 3px;
    border-radius: 3px;
    background: var(--accent);
    transform: scaleY(0);
    transform-origin: center;
    transition: transform var(--dur-2) var(--ease-out);
  }
  #finNav .tab.active::before { transform: scaleY(1); }
}

/* ── skeleton loaders (P3): shape-matched shimmer blocks ──────────────────
 * --fin-soft base recolors per theme/accent for free; the sweep highlight is
 * a translucent slice of the panel surface. Reduced-motion fallback: static
 * blocks (see kill-switch + explicit rule there). JS sizes lines with inline
 * width; defaults below are the common case. */
.skel-line, .skel-num {
  position: relative;
  overflow: hidden;
  background: var(--fin-soft);
  border-radius: var(--radius-sm);
  height: 12px;
}
.skel-num { height: 34px; width: 58%; max-width: 220px; border-radius: 8px; }
.skel-line + .skel-line, .skel-num + .skel-line { margin-top: 10px; }
.skel-line::after, .skel-num::after {
  content: '';
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--fin-surface) 60%, transparent), transparent);
  animation: kf-shimmer 1.4s ease-in-out infinite;
}
@keyframes kf-shimmer { to { transform: translateX(100%); } }

/* ── command palette (P1) ─────────────────────────────────────────────────
 * Veil above the nav/quick-add (60) and below #toast (200) so the result-
 * count live region stays visible. Panel is the house pcard recipe with an
 * accent-tinted modal shadow; active row = the same --fin-soft wash the
 * sidebar uses, so keyboard focus reads as "nav". */
.kf-cmdk {
  position: fixed;
  inset: 0;
  z-index: 120;
  background: rgba(0, 0, 0, .45);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: min(18vh, 160px) 16px 16px;
}
.kf-cmdk-card {
  width: 100%;
  max-width: 560px;
  max-height: min(60vh, 480px);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--fin-surface);
  border: 1px solid var(--line);
  border-radius: 16px;
  box-shadow: 0 24px 64px -16px color-mix(in srgb, var(--accent) 30%, rgba(0, 0, 0, .4));
  animation: k-fade-up var(--dur-2) var(--ease-out);
}
.kf-cmdk-input, .kf-cmdk-input:focus {
  border: none;
  border-bottom: 1px solid var(--line);
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  outline: none;
}
.kf-cmdk-input { width: 100%; padding: 14px 16px; font-size: var(--text-md); color: var(--ink-1); }
.kf-cmdk-list { flex: 1; min-height: 0; overflow-y: auto; padding: 6px; margin: 0; list-style: none; }
.kf-cmdk-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 10px 12px;
  border: none;
  border-radius: 10px;
  background: none;
  color: var(--ink-1);
  font-size: var(--text-sm);
  font-weight: 500;
  text-align: start;
  cursor: pointer;
}
.kf-cmdk-item.active, .kf-cmdk-item:hover { background: var(--fin-soft); color: var(--fin-head); }
.kf-cmdk-item mark { background: none; color: var(--accent-text); font-weight: 700; }
.kf-cmdk-item .kf-kbd { margin-inline-start: auto; }
.kf-cmdk-empty { padding: 18px 16px; font-size: var(--text-sm); color: var(--ink-2); }
/* hint chip — also the discoverability chip in the topbar */
.kf-kbd {
  display: inline-flex;
  align-items: center;
  padding: 2px 7px;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: var(--fin-soft);
  color: var(--ink-2);
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: 600;
  line-height: 1.5;
  letter-spacing: .02em;
  white-space: nowrap;
}

/* ── view transitions (P4): map the native crossfade onto the house motion
 * scale; the active tab highlight glides between nav items (exactly one
 * element ever carries the name, since only one tab is .active). .kf-enter
 * is the Firefox/Safari fallback the JS applies on render completion. */
::view-transition-old(root), ::view-transition-new(root) { animation-duration: var(--dur-2); }
#finNav .tab.active { view-transition-name: fin-tab; }
.kf-enter { animation: k-fade-up var(--dur-2) var(--ease-out) both; }

/* ── shared tooltip + trend bars (P7) ─────────────────────────────────────
 * One fixed .kf-tip positioned by JS, shown on hover AND focus. Trend
 * months are focusable groups; bars grow in from the baseline with a
 * per-month stagger via --i (custom properties inherit, so the group's
 * inline --i reaches its bars). Reveal = .on on the container. */
.kf-tip {
  position: fixed;
  z-index: 150;
  max-width: 260px;
  padding: 8px 11px;
  background: var(--fin-surface);
  border: 1px solid var(--line);
  border-radius: 10px;
  color: var(--ink-1);
  font-size: var(--text-xs);
  line-height: 1.5;
  box-shadow: 0 8px 24px -8px rgba(0, 0, 0, .3);
  pointer-events: none;
}
.kf-tip .kf-tip-in { color: var(--ok-text); font-weight: 600; }
.kf-tip .kf-tip-out { color: var(--danger-text); font-weight: 600; }
.kf-trend {
  display: flex;
  align-items: flex-end;
  gap: 10px;
  height: 140px;
  border-bottom: 1px solid var(--line-strong);
}
.kf-trend-mon {
  position: relative;
  flex: 1;
  min-width: 0;
  height: 100%;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: 3px;
  border-radius: 8px 8px 0 0;
  cursor: default;
}
.kf-bar {
  width: min(14px, 40%);
  border-radius: 3px 3px 0 0;
  transform: scaleY(0);
  transform-origin: bottom;
  transition: transform var(--dur-3) var(--ease-out);
  transition-delay: calc(var(--i, 0) * 40ms);
}
.kf-bar.in { background: var(--ok); }
.kf-bar.out { background: var(--danger); }
.kf-trend.on .kf-bar { transform: scaleY(1); }
@media (hover: hover) {
  .kf-trend-mon:hover { background: var(--fin-soft); }
  .kf-trend-mon:hover .kf-bar { filter: brightness(1.1); }
}
.kf-trend-mon:focus-visible .kf-bar { filter: brightness(1.1); }

/* ── typography (P10): Fraunces optical sizing + gradient hero number ─────
 * The face is loaded with its full 9..144 opsz axis; big money picks up the
 * display cut, small numbers stay sturdy. The hero gradient's light stop is
 * mixed 60/40 toward --fin-head so large-text contrast holds on the light
 * surfaces; solid --fin-head is the declared fallback outside @supports and
 * under forced colors. */
.topbar .nm, .stat .num, .stat .val, .stat .k-num {
  font-optical-sizing: auto;
  font-variation-settings: 'opsz' 40;
}
.k-num { font-variant-numeric: tabular-nums; font-feature-settings: 'tnum' 1; }
.kf-hero-num {
  font-family: var(--font-display);
  font-optical-sizing: auto;
  font-variation-settings: 'opsz' 100;
  font-size: var(--display-lg);
  font-weight: 600;
  letter-spacing: -.03em;
  line-height: 1.05;
  color: var(--fin-head);
  font-variant-numeric: tabular-nums;
}
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .kf-hero-num {
    background: linear-gradient(120deg, var(--fin-head) 30%, color-mix(in srgb, var(--accent-vivid) 60%, var(--fin-head)));
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
  }
}
@media (forced-colors: active) {
  .kf-hero-num { background: none; -webkit-text-fill-color: currentColor; }
}

/* ── visually-hidden utility (skeleton SR text, palette announcements) ──── */
.kf-vh {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ── THE global reduced-motion kill-switch (P8, half 2) ───────────────────
 * base.css honors prefers-reduced-motion at exactly one selector; this
 * covers everything in this app — the layer above, base.css's k-fade-up /
 * toast slide / capmeter fill, and any motion a future module adds. Scoped
 * to Finance by the stylesheet itself (finance.html only), so base.css and
 * Kgyoka One stay untouched per the skin contract. Keep LAST in the file. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    transition-delay: 0ms !important;
    scroll-behavior: auto !important;
  }
  ::view-transition-old(root), ::view-transition-new(root) { animation: none !important; }
  /* static skeletons still communicate loading; bars render fully grown */
  .skel-line::after, .skel-num::after { animation: none; }
  .kf-bar { transform: scaleY(1); }
}
