


[data-theme='dark'] {
  --bg: #0b1020; --bg-2: #070b16;
  --surface: #131a2e; --surface-2: #1a2237;
  --line: #253049; --line-2: #33405e;
  --ink: #f1f5f9; --ink-2: #b6c2d6; --ink-3: #7c8aa3;
  --up-bg: #05372a; --down-bg: #4c0d1c; --warn-bg: #422006; --info-bg: #082f49;
  --a-indigo-s:#1e1b4b; --a-emerald-s:#022c22; --a-amber-s:#451a03; --a-rose-s:#4c0519;
  --a-sky-s:#082f49; --a-violet-s:#2e1065; --a-cyan-s:#083344; --a-pink-s:#500724;
  --a-fuchsia-s:#4a044e; --a-teal-s:#042f2e; --a-red-s:#450a0a; --a-orange-s:#431407;
  --a-slate-s:#1e293b;
  --shadow-sm: 0 1px 3px rgba(0,0,0,.5);
  --shadow: 0 4px 14px rgba(0,0,0,.5);
  --shadow-lg: 0 24px 48px -12px rgba(0,0,0,.7);}

* { box-sizing: border-box;}
html, body { height: 100%;}
body {
  margin: 0; color: var(--ink);
  background-attachment: fixed;}
a { text-decoration: none;}
a:hover { text-decoration: underline;}
h1,h2,h3,h4 { margin: 0; font-weight: 700; letter-spacing: -.01em;}
::-webkit-scrollbar { width: 10px; height: 10px;}
::-webkit-scrollbar-track { background: transparent;}
::-webkit-scrollbar-thumb { background: var(--line-2); border-radius: 10px; border: 2px solid var(--bg);}
::-webkit-scrollbar-thumb:hover { background: var(--ink-3);}

/* ─────────────────────────────────────────────────────────── login */
.login-wrap {
  min-height: 100vh; display: grid; place-items: center; padding: 24px;}
.login-card {
  width: 100%; max-width: 420px; background: var(--surface);
  border-radius: 24px; padding: 40px; box-shadow: var(--shadow-lg);}
.login-card .brand { display: flex; align-items: center; gap: 12px; margin-bottom: 6px;}
.login-card .brand-mark {
  width: 44px; height: 44px; border-radius: 13px; display: grid; place-items: center;
  background: linear-gradient(135deg, var(--brand-1), var(--brand-3));
  color: #fff; font-size: 22px; box-shadow: 0 6px 16px rgba(99,102,241,.4);}
.login-card h1 { font-size: 22px;}
.login-card .muted { color: var(--ink-2); margin: 0 0 26px; font-size: 13px;}

/* ─────────────────────────────────────────────────────────── shell */
.shell { display: grid; grid-template-columns: var(--sidebar-w) 1fr; min-height: 100vh;}

.sidebar {
  position: sticky; top: 0; height: 100vh;}

/* ── navigation position (Appearance setting) ─────────────────────────────
   The shell is a two-column grid, so moving the navigation is a matter of
   reordering the tracks — no markup changes, and the sidebar keeps its own
   styling in every position. */
/* Explicit tracks, not `order`. Ordering relies on auto-placement, and with the
   sidebar declared first in the markup it kept taking the wide column — the
   navigation rendered 1184px wide with the content squeezed into 246px. */
[data-navpos="right"] .shell { grid-template-columns: 1fr var(--sidebar-w);}
[data-navpos="right"] .sidebar { grid-column: 2; grid-row: 1;}
[data-navpos="right"] .main { grid-column: 1; grid-row: 1;}

/* Top bar: one column, and the nav becomes a horizontal strip that scrolls
   sideways. Group headings are dropped rather than stacked — a heading above a
   horizontal row of items points at nothing. */
/* align-content must be pinned. The shell carries min-height:100vh, and a grid
   with auto rows shares the leftover height out between them — so a 66px bar
   was being stretched to 240px on a tall viewport. `start` makes each row take
   only what its content needs; `1fr` on the second gives the rest to content. */
[data-navpos="top"] .shell {
  grid-template-columns: 1fr; grid-template-rows: auto 1fr;
  align-content: start;}
[data-navpos="top"] .sidebar {
  position: sticky; top: 0; height: auto; z-index: 40;
  /* flex-direction must be stated: the base .sidebar is a COLUMN flex, so
     without this the header, nav and footer stack and the "bar" comes out
     169px tall. */
  display: flex; flex-direction: row; align-items: center; gap: 14px;
  padding: 8px 16px; border-right: 0; border-bottom: 1px solid var(--line);}
[data-navpos="top"] .sb-head { flex: 0 0 auto; padding: 0; border-bottom: 0;}
[data-navpos="top"] .sb-nav {
  flex: 1 1 auto; display: flex; flex-wrap: nowrap; align-items: center; gap: 4px;
  /* nowrap is load-bearing: 24 links wrapped onto six rows and the "bar" came
     out 370px tall. They are meant to overflow sideways, not stack.
     The bar scrolls sideways. It was briefly set to `overflow: visible` so the
     dropdowns would not be clipped by it — but eight group buttons plus the
     brand and the search box do not fit in 1440px either, so that just moved
     the last two groups off the right edge where they could not be reached at
     all. The dropdown escapes this box by being `position: fixed` instead
     (see below), which is the only way to have both. */
  overflow-x: auto; overflow-y: hidden; padding: 0; scrollbar-width: thin;
  min-width: 0;}
/* Grouping survives the move to a horizontal bar — as dropdowns.

   The first version dissolved the wrappers with `display: contents` and hid the
   headings, because the sidebar's collapse machinery (which hides a closed
   group with `visibility`) left 23 of 24 links invisible with no way to open
   them. That worked, but it threw the grouping away: twenty-four flat links
   scrolling sideways, with Markets, Money and Academy indistinguishable.

   So the wrappers stay, and each group becomes a button in the bar whose items
   drop below it. The collapse state is deliberately NOT consulted here — a bar
   has no room for eight open panels, and `S.navOpen` is about the vertical
   sidebar. Hover and focus-within drive it instead, which keeps it keyboard
   reachable and needs no JavaScript at all. Below 1024px the sidebar is a
   drawer regardless of this setting, so there is no touch-only device that
   depends on hover. */
[data-navpos="top"] .sb-group-wrap {
  position: relative; flex: 0 0 auto; margin: 0;}
[data-navpos="top"] .sb-group {
  display: flex; align-items: center; gap: 6px; margin: 0;
  padding: 7px 11px; white-space: nowrap; border-radius: 8px; opacity: 1;}
[data-navpos="top"] .sb-group .chev { transform: rotate(0deg);}
[data-navpos="top"] .sb-group-wrap:has(.sb-item.active) .sb-group {
  background: var(--surface-2); color: var(--fg);}

/* The panel. `visibility` rather than `display` so the base sidebar's
   transition still applies and nothing has to be re-declared. */
/* `fixed`, not `absolute`: an absolutely-positioned panel is still clipped by
   the scrolling bar above it. Fixed elements escape overflow entirely, so the
   bar can scroll AND the menu can hang below it. Top and left are measured in
   keepMenuOnScreen() because a fixed element has no useful default here. */
:root[data-navpos="top"] .sb-group-wrap > .sb-items {
  position: fixed; z-index: 60;
  display: block; grid-template-rows: none;
  min-width: 208px; max-height: min(70vh, 520px); overflow-y: auto;
  padding: 6px; border: 1px solid var(--line); border-radius: 12px;
  background: var(--surface-1); box-shadow: 0 18px 44px rgb(0 0 0 / .34);
  visibility: hidden; opacity: 0; transform: translateY(-4px);
  transition: opacity .13s ease, transform .13s ease, visibility .13s;}
/* Opened by CLICK — `.is-open` is set in app.js — with :focus-within kept so
   keyboard users get the same menu. Hover deliberately does NOT open it: the
   group button rebuilds its own node when clicked, so a hover-driven menu
   disappeared the moment you tried to use it, and a trackpad tap never opened
   one at all. */
:root[data-navpos="top"] .sb-group-wrap.is-open > .sb-items,
:root[data-navpos="top"] .sb-group-wrap:focus-within > .sb-items {
  visibility: visible; opacity: 1; transform: none;}
:root[data-navpos="top"] .sb-group-wrap.is-open > .sb-group { background: var(--surface-2); color: var(--fg);}
:root[data-navpos="top"] .sb-group-wrap.is-open .chev { transform: rotate(90deg);}
[data-navpos="top"] .sb-items-inner { display: block;}
[data-motion='off'] [data-navpos="top"] .sb-items { transition: none;}

/* Inside the dropdown the items are a normal vertical list again; only the
   group buttons sit in the bar itself. */
[data-navpos="top"] .sb-item {
  display: flex; flex: 0 0 auto; margin: 0; padding: 7px 10px; white-space: nowrap;}
[data-navpos="top"] .sb-item.active::before { display: none;}
[data-navpos="top"] .sb-item.active { background: var(--surface-2);}
[data-navpos="top"] .sb-foot { display: none;}

/* Below 1024px the sidebar is already a slide-over drawer (mobile.css), and
   that is the right pattern whichever position is chosen — so the desktop
   positions stand down rather than fight it. The breakpoint must stay in step
   with mobile.css: at 900px these rules kept re-styling a drawer that was
   sitting off-canvas at x=-310. */
@media (max-width: 1024px) {
  [data-navpos="right"] .shell, [data-navpos="top"] .shell { grid-template-columns: 1fr;}
  [data-navpos="right"] .sidebar, [data-navpos="right"] .main { order: initial;}
  [data-navpos="top"] .sidebar {
    position: fixed; height: 100%; display: block;
    padding: 0; border-bottom: 0;}
  [data-navpos="top"] .sb-nav { display: block; overflow-y: auto;}
  [data-navpos="top"] .sb-group, [data-navpos="top"] .sb-foot { display: block;}
  [data-navpos="top"] .sb-group-wrap, [data-navpos="top"] .sb-items-inner { display: block;}
  [data-navpos="top"] .sb-items { display: grid; visibility: inherit !important;}
  [data-navpos="top"] .sb-item { display: flex;}
}
.sidebar::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;}
.sb-head { position: relative; z-index: 1;}
.sb-title { font-weight: 800; letter-spacing: .2px;}

.sb-nav { position: relative; z-index: 1;}
.sb-nav::-webkit-scrollbar-thumb { background: rgba(255,255,255,.18); border-color: transparent;}
.sb-group { opacity: .85;}
.sb-item { cursor: pointer; margin-bottom: 2px;}
.sb-item:hover { text-decoration: none;}
.sb-item.active::before { transform: translateY(-50%); height: 22px;}
.sb-foot { position: relative; z-index: 1;}

/* ─────────────────────────────────────────────────────────── main */
.main { display: flex; flex-direction: column; min-width: 0;}
.topbar .spacer { flex: 1;}
.content { padding: 24px 26px 60px; max-width: 1680px; width: 100%;}

/* ─────────────────────────────────────────────────────── components */
.btn {
  font-family: inherit; white-space: nowrap;}
.btn:hover { background: var(--surface-2); border-color: var(--ink-3); transform: translateY(-1px); text-decoration: none;}
.btn:active { transform: translateY(0);}
.btn.primary:hover { box-shadow: 0 7px 20px rgba(99,102,241,.45); background: linear-gradient(135deg, #4f46e5, #7c3aed);}
.btn.ghost:hover { background: var(--surface-2);}
.btn.danger:hover { background: var(--down); color: #fff;}
.btn.sm { border-radius: 8px;}
.btn[disabled] { opacity: .5; cursor: not-allowed; transform: none;}

.input, select.input, textarea.input {
  width: 100%; padding: 9px 12px; border-radius: 10px; border: 1px solid var(--line-2);
  background: var(--surface); color: var(--ink); font-family: inherit; font-size: 13.5px;
  transition: border-color .14s, box-shadow .14s;}
textarea.input { min-height: 76px; resize: vertical;}
label.lbl { display: block; font-size: 12px; font-weight: 700; color: var(--ink-2); margin-bottom: 5px;}
.hint { font-size: 11.5px; color: var(--ink-3); margin-top: 4px;}
.card-head .spacer { flex: 1;}
@keyframes pulse { 0%,100%{opacity:1} 50%{opacity:.25} }
.mono { font-family: var(--mono); font-variant-numeric: tabular-nums;}
.num  { font-variant-numeric: tabular-nums; text-align: right;}
.kpi { cursor: pointer;}
.kpi:hover { box-shadow: var(--shadow);}
.kpi::before {
  content: ''; position: absolute; inset: 0 0 auto 0;}
.kpi::after {
  content: ''; position: absolute; right: -30px; top: -30px; width: 120px; height: 120px;
  border-radius: 50%; background: var(--acc-soft); opacity: .8; pointer-events: none;}
.kpi > * { position: relative; z-index: 1;}
.kpi-ic {
  width: 32px; height: 32px; border-radius: 9px; display: grid; place-items: center; background: var(--acc-soft); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--acc) 22%, transparent);}
.kpi-delta { display: inline-flex; align-items: center; gap: 4px;}
.kpi-badge { z-index: 2;}
.kpi-basis { display: none;}
.kpi.show-basis .kpi-basis { display: block;}

/* ────────────────────────────────────────────────────────── alerts */
.alert { font-weight: 550; cursor: pointer; transition: transform .14s;}
.alert:hover { transform: translateX(3px);}
.alert.danger { color: #9f1239; border-color: var(--down);}
.alert.warn   { color: #92400e; border-color: var(--warn);}
.alert.info   { color: #075985; border-color: var(--info);}
[data-theme='dark'] .alert.danger { color: #fda4af;}
[data-theme='dark'] .alert.warn   { color: #fcd34d;}
[data-theme='dark'] .alert.info   { color: #7dd3fc;}

/* ────────────────────────────────────────────────────────── tables */
.toolbar {
  border-radius: var(--radius) var(--radius) 0 0;}
.toolbar .search { position: relative; flex: 1; min-width: 190px; max-width: 340px;}
.toolbar .search input { padding-left: 33px;}
.toolbar .search .mag { position: absolute; left: 11px; top: 50%; transform: translateY(-50%); color: var(--ink-3); font-size: 13px;}
table.tbl { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 13px;}
table.tbl thead th {
  position: sticky; top: 0; z-index: 5; background: var(--surface-2);
  text-align: left; padding: 10px 13px; font-size: 11px; font-weight: 800;
  color: var(--ink-2); text-transform: uppercase; letter-spacing: .05em;
  border-bottom: 2px solid var(--line); white-space: nowrap; cursor: pointer;
  user-select: none;}
table.tbl thead th:hover { color: var(--brand-1);}
table.tbl thead th .arrow { opacity: .3; margin-left: 4px; font-size: 9px;}
table.tbl thead th.sorted { color: var(--brand-1);}
table.tbl thead th.sorted .arrow { opacity: 1;}
table.tbl thead th.num, table.tbl td.num { text-align: right;}
table.tbl tbody td {
  padding: 9px 13px; border-bottom: 1px solid var(--line); vertical-align: middle; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;}
table.tbl tbody tr:hover { background: var(--surface-2);}
table.tbl tbody tr.clickable { cursor: pointer;}
table.tbl tfoot td {
  padding: 10px 13px; font-weight: 800; background: var(--surface-2);
  border-top: 2px solid var(--line); position: sticky; bottom: 0;}
.row-actions { display: flex; gap: 5px; opacity: 0; transition: opacity .12s;}
tr:hover .row-actions { opacity: 1;}
.icon-btn.del:hover { border-color: var(--down);}
.pager .spacer { flex: 1;}

/* ────────────────────────────────────────────────────────── tabs */
.tabs { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 18px;}
.tab {
  padding: 8px 16px; border-radius: 10px; font-weight: 650; font-size: 13px; cursor: pointer;
  border: 1px solid var(--line); background: var(--surface); color: var(--ink-2); transition: all .14s;}
.tab:hover { background: var(--surface-2); color: var(--ink);}
.tab.active {
  background: linear-gradient(135deg, var(--brand-1), var(--brand-2));
  border-color: transparent; color: #fff; box-shadow: 0 4px 12px rgba(99,102,241,.32);}

/* ───────────────────────────── the grouped, collapsing tab strip

   Used by the FTTH cockpit (27 tabs) and CM (10). Two levels, two rows, and
   the height does not grow with the tab count:

       [ 🏠 Home 2 ][ 👷 BBM 4 ][ 🤝 TIP 2 ][ 🗼 Network / OLT 8 ] …
        ⤷ OLT Health   OLT Provisioning   RED OLTs   …

   Laying all 27 out under headings at once was findable but 175 px tall — a
   quarter of a laptop screen spent before the first figure.

   TWO DIFFERENT "SELECTED" STATES, because there are two different questions.
   `.open` is the group you are LOOKING AT: filled, raised, its pills showing.
   `.here` is the group your current tab actually lives in: an accent dot. They
   are usually the same group and occasionally not, and collapsing them into
   one style is what would make browsing feel like it had moved you.

   NO DRAGGING, NO SELECTING. The pills are anchors, so the browser gives each
   one native drag-and-drop — a press and a few pixels lifts a translucent ghost
   of the tab across the page — and dragging over the strip smears a text
   selection through the labels. Both read as the interface coming apart under
   the cursor. Switched off on the strip only, never globally: a table of
   figures has to stay selectable to be copied. */
.tabs2 {
  display: block; margin-bottom: 18px;
  -webkit-user-select: none; user-select: none;}
.tabs2 .tab, .tabs2 .tab-grp { -webkit-tap-highlight-color: transparent;}
.tabs2 .tab { -webkit-user-drag: none; user-drag: none;}

/* The group row — a segmented control, so it reads as one object you are
   choosing within rather than as eight loose buttons. */
.tab-groups {
  display: flex; flex-wrap: wrap; gap: 3px;
  padding: 4px; border-radius: 13px;
  background: rgba(120,120,130,.07);
  border: 1px solid rgba(120,120,130,.16);}
.tab-grp {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 7px 13px; border: 0; border-radius: 10px;
  background: transparent; cursor: pointer;
  font: inherit; font-size: 12.5px; font-weight: 650; letter-spacing: -.005em;
  color: var(--ink-2, #6b7280); white-space: nowrap;
  transition: background .15s, color .15s, box-shadow .15s;}
.tab-grp:hover {
  background: rgba(120,120,130,.09);
  background: color-mix(in srgb, currentColor 7%, transparent);
  color: var(--ink, inherit);}
.tab-grp .tab-grp-ic { font-size: 13px; line-height: 1;}
/* The count is the whole reason the collapsed row still tells you where things
   are: "Network / OLT 8" says the estate lives there without opening it. */
.tab-grp .tab-grp-n {
  min-width: 17px; padding: 1px 5px; border-radius: 999px;
  background: rgba(120,120,130,.18);
  background: color-mix(in srgb, currentColor 16%, transparent);
  font-size: 10px; font-weight: 700; text-align: center;}
/* SELECTED STATES ARE DRAWN WITHOUT THEME VARIABLES, deliberately.
   --surface, --line and --brand-2 are declared on the app's themed wrapper and
   not at :root, and a custom property that is declared EMPTY defeats the
   fallback in var(--x, #fff) — the declaration is invalid at computed-value
   time and the property falls back to `transparent`, not to the colour you
   wrote. Measured: the open group came out fully transparent and the selected
   group was invisible. currentColor is inherited ink, so it is correct in
   light and dark alike and cannot be undefined. */
.tab-grp.open {
  background: rgba(120,120,130,.14);
  background: color-mix(in srgb, currentColor 12%, transparent);
  color: var(--ink, inherit); font-weight: 750;
  box-shadow: inset 0 0 0 1px rgba(120,120,130,.2);}
.tab-grp.open .tab-grp-n {
  background: rgba(120,120,130,.34);
  background: color-mix(in srgb, currentColor 34%, transparent);}
/* Where you actually ARE, as against what you are merely browsing. */
.tab-grp.here::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: currentColor; opacity: .85; flex: 0 0 auto;}
.tab-grp:focus-visible { outline: 2px solid currentColor; outline-offset: 1px;}

/* The member tabs of whichever group is open. Indented under the row above so
   the relationship is visible without a connector. */
.tab-pills {
  display: flex; flex-wrap: wrap; gap: 5px;
  margin: 9px 0 0; padding-left: 6px;}
.tab-pills[hidden] { display: none;}
.tabs2 .tab { padding: 7px 13px; font-size: 12.5px; border-radius: 9px;}

/* ACTIONS GET THEIR OWN LINE. Sharing a row with navigation put "Report
   Builder" (a page) beside "Re-import" (an action that changes data) in one
   undifferentiated run of buttons — same size, same shape, same spacing, and
   the eye cannot separate them. A hairline and a row of their own is the whole
   difference between a strip you scan and one you have to read. */
.tab-bar-end {
  margin-top: 10px; padding-top: 9px;
  display: flex; align-items: center; justify-content: flex-end; gap: 8px; flex-wrap: wrap;
  border-top: 1px solid var(--line, rgba(128,128,128,.22));}
.tab-bar-end:empty { display: none;}

@media (max-width: 720px) {
  /* The group row scrolls sideways rather than wrapping to three lines: on a
     phone the vertical space is the scarce one, and a horizontal swipe over a
     segmented control is a familiar gesture. */
  .tab-groups { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;}
  .tab-groups::-webkit-scrollbar { display: none;}
  .tab-grp { padding: 7px 11px;}
  .tab-bar-end { justify-content: flex-start;}
}

/* ────────────────────────────────────────────────────────── modal */
.modal-back {
  position: fixed; inset: 0;
  z-index: 100; display: grid; place-items: center; padding: 24px; overflow-y: auto;}
.modal {
  width: 100%; max-width: 780px; max-height: 90vh; display: flex; flex-direction: column;}
.modal.wide { max-width: 1080px;}
.modal.narrow { max-width: 460px;}
@keyframes pop { from { opacity: 0; transform: translateY(14px) scale(.98); } }
.modal-head h3 { font-size: 17px;}
.modal-body { overflow-y: auto; flex: 1;}
.modal-foot {
  border-radius: 0 0 20px 20px;}
.form-grid { display: grid; gap: 15px;}
.form-grid .full { grid-column: 1 / -1;}
.form-section { margin-top: 6px; padding-top: 13px; border-top: 1px solid var(--line);}
.form-section:first-child { margin-top: 0; padding-top: 0; border-top: none;}
.chart-box { min-height: 240px;}
.legend { display: flex; flex-direction: column; gap: 7px; font-size: 12.5px;}
.legend-row { display: flex; align-items: center;}
.legend-dot { width: 10px; height: 10px; border-radius: 3px; flex-shrink: 0;}
.legend-row .nm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap;}
.legend-row .vl { font-weight: 700; font-variant-numeric: tabular-nums;}
.legend-row .pc { color: var(--ink-3); font-size: 11.5px; text-align: right;}
.donut-wrap { display: grid; gap: 18px; align-items: center;}
@media (max-width: 560px) { .donut-wrap { grid-template-columns: 1fr;} }

.bar-row { display: grid; gap: 11px; align-items: center; margin-bottom: 9px; font-size: 12.5px;}
.bar-track { height: 22px; background: var(--surface-2); border-radius: 6px; overflow: hidden;}
.bar-fill { height: 100%; transition: width .5s cubic-bezier(.4,0,.2,1);}
.bar-row .lb { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600;}
.bar-row .vl { text-align: right; font-weight: 700; font-variant-numeric: tabular-nums;}

/* progress + misc */
.prog { height: 7px; border-radius: 99px; background: var(--surface-2); overflow: hidden;}
.prog > i { display: block; height: 100%; border-radius: 99px; background: linear-gradient(90deg, var(--brand-1), var(--brand-3));}
.stat .v { margin-top: 3px;}

.frame { width: 100%; height: 74vh; border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface);}
.perm-tbl td, .perm-tbl th { padding: 8px 11px; font-size: 12.5px;}
.seg { display: inline-flex; border: 1px solid var(--line-2); border-radius: 9px; overflow: hidden;}
.seg button {
  padding: 5px 12px; border: none; background: var(--surface); font-size: 12px; font-weight: 650;
  cursor: pointer; color: var(--ink-2); font-family: inherit;}
.seg button.on { background: var(--brand-1); color: #fff;}
.seg button + button { border-left: 1px solid var(--line-2);}

.toast-wrap { position: fixed; bottom: 22px; right: 22px; z-index: 200; display: flex; flex-direction: column; gap: 9px;}
.toast { font-weight: 600;
  display: flex; align-items: center; gap: 9px; max-width: 380px;}
.toast.ok  { background: linear-gradient(135deg, #059669, #10b981);}
.toast.err { background: linear-gradient(135deg, #be123c, #f43f5e);}
@keyframes slide { from { opacity: 0; transform: translateX(24px); } }

.spinner {
  width: 17px; height: 17px; border: 2.5px solid var(--line-2);
  border-radius: 50%; animation: spin .7s linear infinite; display: inline-block;}
@keyframes spin { to { transform: rotate(360deg); } }
.loading { display: grid; place-items: center; padding: 70px; gap: 12px; color: var(--ink-2);}

/* mapping grid for the importer */
.map-grid { display: grid; grid-template-columns: 1fr 26px 1fr; gap: 9px 12px; align-items: center;}
.map-grid .arrow { text-align: center; color: var(--ink-3);}
.map-head { font-size: 11px; font-weight: 800; text-transform: uppercase; color: var(--ink-3); letter-spacing: .05em;}

@media (max-width: 1024px) {
  .shell { grid-template-columns: 1fr;}
}
@media (min-width: 1025px) { .menu-btn { display: none;} }

/* ===========================================================================
   MOTION — a living background, page transitions and card shine.
   Everything here is decorative and respects prefers-reduced-motion.
   =========================================================================== */

/* ── Aurora background ─────────────────────────────────────────────────── */
#aurora {
  position: fixed; inset: 0; z-index: -2; pointer-events: none; overflow: hidden;}
#aurora i {
  position: absolute; display: block; border-radius: 50%;
  filter: blur(70px); opacity: .38; mix-blend-mode: multiply;
  animation: drift var(--dur, 26s) ease-in-out infinite alternate;}
[data-theme='dark'] #aurora i { mix-blend-mode: screen; opacity: .30; filter: blur(90px);}
#aurora i:nth-child(1) { width: 46vw; height: 46vw; left: -12vw; top: -14vw; --dur: 24s;}
#aurora i:nth-child(2) { width: 40vw; height: 40vw; right: -10vw; top: -8vw; --dur: 31s; animation-delay: -6s;}
#aurora i:nth-child(3) { width: 44vw; height: 44vw; left: 22vw; bottom: -22vw; --dur: 28s; animation-delay: -12s;}
#aurora i:nth-child(4) { width: 32vw; height: 32vw; right: 6vw; bottom: -14vw; --dur: 35s; animation-delay: -3s;}
@keyframes drift {
  0%   { transform: translate3d(0,0,0) scale(1); }
  50%  { transform: translate3d(4vw, 3vh, 0) scale(1.12); }
  100% { transform: translate3d(-3vw, -4vh, 0) scale(.94); }
}

/* A slow shimmer that sweeps across the whole page, left to right and back. */
#aurora::after {
  content: ''; position: absolute; inset: -20% -60%;
  background: linear-gradient(105deg, transparent 38%,
    color-mix(in srgb, #ffffff 55%, transparent) 47%,
    color-mix(in srgb, #ffffff 70%, transparent) 50%,
    color-mix(in srgb, #ffffff 55%, transparent) 53%, transparent 62%);
  opacity: .5; animation: sweep 11s ease-in-out infinite alternate;}
[data-theme='dark'] #aurora::after { opacity: .06;}
@keyframes sweep { 0% { transform: translateX(-42%); } 100% { transform: translateX(42%); } }

body { background: transparent;}

/* ── Page transition ───────────────────────────────────────────────────── */
.content { animation: viewIn .34s cubic-bezier(.22,.9,.3,1) both;}
@keyframes viewIn { from { opacity: 0; transform: translateY(10px); } }

/* Stagger the KPI cards in. */
.kpi { animation: cardIn .45s cubic-bezier(.22,.9,.3,1) both;}
.kpi:nth-child(1){animation-delay:.02s} .kpi:nth-child(2){animation-delay:.06s}
.kpi:nth-child(3){animation-delay:.10s} .kpi:nth-child(4){animation-delay:.14s}
.kpi:nth-child(5){animation-delay:.18s} .kpi:nth-child(6){animation-delay:.22s}
.kpi:nth-child(7){animation-delay:.26s} .kpi:nth-child(8){animation-delay:.30s}
.kpi:nth-child(9){animation-delay:.34s} .kpi:nth-child(10){animation-delay:.38s}
.kpi:nth-child(n+11){animation-delay:.42s}
@keyframes cardIn { from { opacity: 0; transform: translateY(14px) scale(.97); } }

.card { animation: cardIn .4s cubic-bezier(.22,.9,.3,1) both;}
.stat-strip { animation: cardIn .4s cubic-bezier(.22,.9,.3,1) both;}

/* ── The travelling flash across the KPI cards ─────────────────────────── */
.kpi-grid { position: relative;}
.kpi { isolation: isolate;}
.kpi .shine {
  content: ''; position: absolute; inset: 0; z-index: 3; pointer-events: none;
  border-radius: inherit; overflow: hidden;}
.kpi .shine::before {
  content: ''; position: absolute; top: -60%; bottom: -60%; width: 55%;
  background: linear-gradient(100deg, transparent, rgba(255,255,255,.85), transparent);
  transform: translateX(-180%) rotate(8deg);
  animation: flash 5.5s ease-in-out infinite;
  animation-delay: var(--flash-delay, 0s);}
[data-theme='dark'] .kpi .shine::before {
  background: linear-gradient(100deg, transparent, rgba(255,255,255,.13), transparent);}
@keyframes flash {
  0%, 62%  { transform: translateX(-180%) rotate(8deg); }
  78%      { transform: translateX(320%)  rotate(8deg); }
  100%     { transform: translateX(320%)  rotate(8deg); }
}

/* Accent bar on top of each card gets a matching pulse. */
.kpi::before { background-size: 200% 100%; animation: slide-accent 6s linear infinite;}
@keyframes slide-accent { to { background-position: 200% 0; } }

/* ── Live-value ticks ──────────────────────────────────────────────────── */
.kpi-val { transition: color .25s;}
.kpi-val.bump { animation: bump .5s cubic-bezier(.3,1.4,.5,1);}
@keyframes bump { 0% { transform: scale(1); } 40% { transform: scale(1.06); } 100% { transform: scale(1); } }

/* Alerts slide in one after another. */
.alert { animation: alertIn .38s cubic-bezier(.22,.9,.3,1) both;}
.alert:nth-child(2){animation-delay:.05s} .alert:nth-child(3){animation-delay:.1s}
.alert:nth-child(4){animation-delay:.15s} .alert:nth-child(n+5){animation-delay:.2s}
@keyframes alertIn { from { opacity: 0; transform: translateX(-14px); } }

/* Danger alerts get a soft heartbeat so they can't be missed. */
.alert.danger { animation: alertIn .38s cubic-bezier(.22,.9,.3,1) both, heartbeat 2.6s ease-in-out 1s infinite;}
@keyframes heartbeat {
  0%, 100% { box-shadow: 0 0 0 0 rgba(225,29,72,0); }
  45%      { box-shadow: 0 0 0 5px rgba(225,29,72,.14); }
}

/* ── Sidebar polish ────────────────────────────────────────────────────── */
.sb-item { overflow: hidden;}
.sb-item::after {
  content: ''; position: absolute; inset: 0; border-radius: inherit;
  background: linear-gradient(100deg, transparent, rgba(255,255,255,.16), transparent);
  transform: translateX(-140%); transition: transform .55s ease;}
.sb-item:hover::after { transform: translateX(140%);}
.sb-mark { animation: breathe 5s ease-in-out infinite;}
@keyframes breathe {
  0%, 100% { box-shadow: 0 6px 18px rgba(0,0,0,.35), 0 0 0 0 rgba(240,171,252,.5); }
  50%      { box-shadow: 0 6px 22px rgba(0,0,0,.4), 0 0 0 9px rgba(240,171,252,0); }
}

/* Buttons get a subtle lift + sheen. */
.btn.primary { background-size: 180% 100%; transition: background-position .45s, box-shadow .2s, transform .14s;}
.btn.primary:hover { background-position: 100% 0;}

/* Loading spinner gets a coloured trail. */
.spinner { border-top-color: var(--brand-1); border-right-color: var(--brand-3);}

/* ── Respect the user's motion preference ──────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  #aurora i, #aurora::after, .kpi .shine::before, .kpi::before,
  .sb-mark, .alert.danger { animation: none !important;}
  .content, .kpi, .card, .stat-strip, .alert, table.tbl tbody tr { animation-duration: .01ms !important;}
  .bar-fill { animation: none !important; transform: none !important;}
}
/* And a hard off-switch the user controls. */
[data-motion='off'] #aurora i, [data-motion='off'] #aurora::after,
[data-motion='off'] .kpi .shine, [data-motion='off'] .kpi::before,
[data-motion='off'] .sb-mark, [data-motion='off'] .alert.danger { animation: none !important;}
[data-motion='off'] .content, [data-motion='off'] .kpi, [data-motion='off'] .card,
[data-motion='off'] .stat-strip, [data-motion='off'] .alert,
[data-motion='off'] table.tbl tbody tr { animation: none !important;}
@keyframes rowIn { from { opacity: .25; } }

/* ── Horizontal bars: proportional fills that actually animate ──────────── */
.bar-track { position: relative;}
.bar-fill {
  position: absolute; top: 0; bottom: 0; left: 0;
  width: var(--w, 0%);
  border-radius: 6px;
  transform-origin: left center;
  animation: growW .75s cubic-bezier(.22,.9,.3,1) both;}
@keyframes growW { from { transform: scaleX(0); } to { transform: scaleX(1); } }

/* Signed charts grow out from a centre line. */
.bar-track.split { background: var(--surface-2);}
.bar-track.split .zero {
  position: absolute; left: 50%; top: 2px; bottom: 2px; width: 1px;
  background: var(--line-2); z-index: 2;}
.bar-fill.right {
  left: 50%; width: var(--w, 0%);
  border-radius: 0 6px 6px 0; transform-origin: left center;}
.bar-fill.left {
  left: auto; right: 50%; width: var(--w, 0%);
  border-radius: 6px 0 0 6px; transform-origin: right center;}
.bar-row.signed .vl { min-width: 84px;}
.bar-row .vl .pc { color: var(--ink-3); font-weight: 600; font-size: 11px; margin-left: 4px;}
.bar-track:hover .bar-fill { filter: brightness(1.08);}

/* ===========================================================================
   MOTION, part 2 — every tab, table, modal and control.
   =========================================================================== */

/* Tab bar: the active pill glides, and each tab lifts on hover. */
.tabs { perspective: 800px;}
.tab { position: relative; transform-origin: center bottom;}
.tab:hover { transform: translateY(-2px);}
.tab::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(100deg, transparent, rgba(255,255,255,.35), transparent);
  transform: translateX(-140%); transition: transform .5s ease;}
.tab:hover::after { transform: translateX(140%);}
.tab.active { animation: tabPop .3s cubic-bezier(.3,1.3,.5,1);}
@keyframes tabPop { 0% { transform: scale(.94); } 60% { transform: scale(1.03); } 100% { transform: scale(1); } }

/* Tabs and stat cards deal in with a stagger. */
.tabs .tab { animation: cardIn .32s cubic-bezier(.22,.9,.3,1) both;}
.tabs .tab:nth-child(1){animation-delay:.02s} .tabs .tab:nth-child(2){animation-delay:.05s}
.tabs .tab:nth-child(3){animation-delay:.08s} .tabs .tab:nth-child(4){animation-delay:.11s}
.tabs .tab:nth-child(5){animation-delay:.14s} .tabs .tab:nth-child(n+6){animation-delay:.17s}

.stat { animation: cardIn .38s cubic-bezier(.22,.9,.3,1) both;}
.stat:nth-child(1){animation-delay:.02s} .stat:nth-child(2){animation-delay:.05s}
.stat:nth-child(3){animation-delay:.08s} .stat:nth-child(4){animation-delay:.11s}
.stat:nth-child(5){animation-delay:.14s} .stat:nth-child(6){animation-delay:.17s}
.stat:nth-child(7){animation-delay:.20s} .stat:nth-child(n+8){animation-delay:.23s}
.stat:hover { transform: translateY(-2px);}
.stat .v { transition: transform .2s;}
.stat:hover .v { transform: scale(1.03);}

/* Cards in a grid stagger too. */
.chart-grid > .card:nth-child(1){animation-delay:.03s} .chart-grid > .card:nth-child(2){animation-delay:.08s}
.chart-grid > .card:nth-child(3){animation-delay:.13s} .chart-grid > .card:nth-child(4){animation-delay:.18s}
.chart-grid > .card:nth-child(5){animation-delay:.23s} .chart-grid > .card:nth-child(n+6){animation-delay:.28s}

/* Table rows cascade in, and lift slightly under the cursor. */
table.tbl tbody tr { animation: rowSlide .34s cubic-bezier(.22,.9,.3,1) both;}
table.tbl tbody tr:nth-child(1){animation-delay:.01s} table.tbl tbody tr:nth-child(2){animation-delay:.025s}
table.tbl tbody tr:nth-child(3){animation-delay:.04s} table.tbl tbody tr:nth-child(4){animation-delay:.055s}
table.tbl tbody tr:nth-child(5){animation-delay:.07s} table.tbl tbody tr:nth-child(6){animation-delay:.085s}
table.tbl tbody tr:nth-child(7){animation-delay:.1s}  table.tbl tbody tr:nth-child(8){animation-delay:.115s}
table.tbl tbody tr:nth-child(9){animation-delay:.13s} table.tbl tbody tr:nth-child(10){animation-delay:.145s}
table.tbl tbody tr:nth-child(n+11){animation-delay:.16s}
@keyframes rowSlide { from { opacity: 0; transform: translateY(6px); } }
table.tbl tbody tr.clickable:hover { transform: translateX(3px); box-shadow: inset 3px 0 0 var(--brand-1);}
table.tbl thead th { transition: color .14s, background .14s;}

/* Donut segments draw themselves in. */
.chart-box svg path, .chart-box svg circle { animation: fadeScale .55s cubic-bezier(.22,.9,.3,1) both; transform-origin: center;}
.chart-box svg path:nth-child(1){animation-delay:.04s} .chart-box svg path:nth-child(2){animation-delay:.09s}
.chart-box svg path:nth-child(3){animation-delay:.14s} .chart-box svg path:nth-child(4){animation-delay:.19s}
.chart-box svg path:nth-child(5){animation-delay:.24s} .chart-box svg path:nth-child(n+6){animation-delay:.29s}
@keyframes fadeScale { from { opacity: 0; transform: scale(.86); } }
.chart-box svg path:hover, .chart-box svg rect:hover { filter: brightness(1.1); cursor: pointer;}
.chart-box svg polyline { stroke-dasharray: 3000; animation: draw 1.5s cubic-bezier(.4,0,.2,1) both;}
@keyframes draw { from { stroke-dashoffset: 3000; } to { stroke-dashoffset: 0; } }
.chart-box svg rect { animation: colGrow .6s cubic-bezier(.22,.9,.3,1) both; transform-origin: center bottom;}
@keyframes colGrow { from { transform: scaleY(0); } }

/* Legend rows slide in. */
.legend-row { animation: rowSlide .3s ease both;}
.legend-row:nth-child(2){animation-delay:.04s} .legend-row:nth-child(3){animation-delay:.07s}
.legend-row:nth-child(4){animation-delay:.1s}  .legend-row:nth-child(5){animation-delay:.13s}
.legend-row:nth-child(n+6){animation-delay:.16s}
.bar-row { animation: rowSlide .3s ease both;}
.bar-row:nth-child(2){animation-delay:.04s} .bar-row:nth-child(3){animation-delay:.08s}
.bar-row:nth-child(4){animation-delay:.12s} .bar-row:nth-child(5){animation-delay:.16s}
.bar-row:nth-child(6){animation-delay:.2s}  .bar-row:nth-child(n+7){animation-delay:.24s}

/* Modals: backdrop fades, panel springs. */
.modal-back { animation: fadeIn .18s ease both;}
@keyframes fadeIn { from { opacity: 0; } }
.modal { animation: modalIn .3s cubic-bezier(.3,1.2,.4,1) both;}
@keyframes modalIn { from { opacity: 0; transform: translateY(22px) scale(.96); } }
.form-grid > * { animation: rowSlide .3s ease both;}
.form-grid > *:nth-child(n+4){animation-delay:.05s} .form-grid > *:nth-child(n+8){animation-delay:.1s}

/* Buttons, chips and inputs get life. */
.btn { position: relative; overflow: hidden;}
.btn:not(.ghost)::after {
  content: ''; position: absolute; inset: 0;
  opacity: 0; transition: opacity .25s;}
.btn:not(.ghost):hover::after { opacity: 1;}
.chip { transition: transform .15s, filter .15s;}
.chip:hover { transform: translateY(-1px); filter: brightness(1.05);}
.icon-btn { transition: transform .15s, background .15s, border-color .15s;}
.icon-btn:hover { transform: scale(1.12) rotate(-4deg);}
.input:focus { transform: translateY(-1px);}

/* Progress bar shimmer. */
.prog > i { position: relative; overflow: hidden; transition: width .6s cubic-bezier(.22,.9,.3,1);}
.prog > i::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(100deg, transparent, rgba(255,255,255,.55), transparent);
  transform: translateX(-100%); animation: progShine 2.2s ease-in-out infinite;}
@keyframes progShine { 60%, 100% { transform: translateX(220%); } }

/* Sidebar counters pop when they change. */
.sb-item .cnt { transition: transform .2s, background .2s;}
.sb-item:hover .cnt { transform: scale(1.1); background: rgba(255,255,255,.28);}

/* Loading state feels alive. */
.loading .spinner { animation: spin .7s linear infinite, hue 3s linear infinite;}
@keyframes hue { to { filter: hue-rotate(360deg); } }

/* Toasts bounce in. */
.toast { animation: toastIn .32s cubic-bezier(.3,1.3,.5,1) both;}
@keyframes toastIn { from { opacity: 0; transform: translateX(26px) scale(.94); } }

.warn-text { color: var(--warn);}

/* Everything above obeys the off-switch. */
[data-motion='off'] .tab, [data-motion='off'] .stat, [data-motion='off'] .legend-row,
[data-motion='off'] .bar-row, [data-motion='off'] .modal, [data-motion='off'] .modal-back,
[data-motion='off'] .form-grid > *, [data-motion='off'] .chart-box svg path,
[data-motion='off'] .chart-box svg rect, [data-motion='off'] .chart-box svg circle,
[data-motion='off'] .chart-box svg polyline, [data-motion='off'] .toast,
[data-motion='off'] .prog > i::after, [data-motion='off'] .loading .spinner {
  animation: none !important;}
[data-motion='off'] .chart-box svg polyline { stroke-dasharray: none !important;}
@media (prefers-reduced-motion: reduce) {
  .chart-box svg polyline { stroke-dasharray: none !important; animation: none !important;}
  .tab, .stat, .legend-row, .bar-row, .modal, .form-grid > *,
  .chart-box svg path, .chart-box svg rect, .toast { animation-duration: .01ms !important;}
}

/* ===========================================================================
   LAYOUT SAFETY — nothing may escape its box.
   Long rupee figures, 60-character fund names and 12-column tables all have to
   sit inside cards that shrink on a laptop screen. These rules are deliberately
   broad: every flex/grid child gets min-width:0 (without it a child refuses to
   shrink below its content and pushes out of the card), and every value that
   can be long gets an explicit wrap or ellipsis.
   =========================================================================== */

/* The single most common cause of overflow in flex/grid layouts. */
.kpi, .stat, .card, .card-head, .card-pad, .toolbar, .legend, .legend-row,
.bar-row, .chart-box, .form-grid > *, .modal, .modal-body, .banner, .alert,
.sb-item, .topbar, .main, .shell > * { min-width: 0;}
.kpi > *, .stat > *, .banner > *, .alert > * { min-width: 0;}
.kpi-val {
  overflow-wrap: anywhere; word-break: break-word; hyphens: auto;
  padding-right: 4px;}
.kpi.wide .kpi-val { font-size: clamp(21px, 2.4vw, 30px);}
.kpi-lbl { text-overflow: ellipsis;}
.kpi-top { flex-wrap: nowrap;}
.kpi-ic { flex: 0 0 auto;}
.kpi-sub {
  white-space: normal; overflow-wrap: anywhere;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;}
.kpi-badge { max-width: 46%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;}
/* Keep the badge from sitting on top of the label. */
.kpi-top { padding-right: 58px;}
.kpi-basis { overflow-wrap: anywhere;}

/* Stat strip ------------------------------------------------------------ */
.stat { overflow: hidden;}
.stat .v {
  overflow-wrap: anywhere; word-break: break-word;}

/* Chart legends --------------------------------------------------------- */
.legend { width: 100%;}
.legend-row { gap: 7px;}
.legend-row .nm { flex: 1 1 auto; min-width: 0;}
.legend-row .vl { flex: 0 0 auto; white-space: nowrap;}
.legend-row .pc { flex: 0 0 auto; width: auto; min-width: 42px; white-space: nowrap;}
.donut-wrap { grid-template-columns: minmax(120px, 168px) minmax(0, 1fr);}
.donut-wrap > svg { max-width: 100%; height: auto;}

/* Bar rows -------------------------------------------------------------- */
.bar-row { grid-template-columns: minmax(72px, 128px) minmax(0, 1fr) minmax(64px, auto);}
.bar-row .lb { min-width: 0;}
.bar-row .vl { white-space: nowrap;}
.bar-track { min-width: 0;}

/* Tables ---------------------------------------------------------------- */
table.tbl { table-layout: auto;}
table.tbl tbody td { max-width: 260px;}
table.tbl tbody td.num, table.tbl tfoot td.num { max-width: none; white-space: nowrap;}
table.tbl thead th { max-width: 260px; overflow: hidden; text-overflow: ellipsis;}
.tbl-wrap { max-width: 100%;}

/* Cards, banners and alerts --------------------------------------------- */
.card-head h3 { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;}
.card-head { flex-wrap: wrap; row-gap: 8px;}
.banner { flex-wrap: wrap; row-gap: 9px;}
.banner > div { overflow-wrap: anywhere;}
.alert span:last-child { overflow-wrap: anywhere; min-width: 0;}
.chip { max-width: 100%; overflow: hidden; text-overflow: ellipsis;}

/* Toolbars and tab strips wrap rather than push out. */
.toolbar { row-gap: 9px;}
.tabs { row-gap: 6px;}
.tab { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;}

/* Sidebar --------------------------------------------------------------- */
.sb-item > span:nth-child(2) { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;}
.sb-item .cnt { flex: 0 0 auto;}
.sb-user .nm, .sb-user .rl { overflow: hidden; text-overflow: ellipsis; white-space: nowrap;}

/* Top bar --------------------------------------------------------------- */
.topbar h2 { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;}
.topbar .sub { overflow: hidden; text-overflow: ellipsis; white-space: nowrap;}
.topbar > div:not(.spacer) { min-width: 0;}

/* Modals ---------------------------------------------------------------- */
.modal-head h3 { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;}
.modal-head { flex-wrap: wrap; row-gap: 8px;}
.modal-body { overflow-x: auto;}
.form-grid { grid-template-columns: repeat(auto-fit, minmax(min(210px, 100%), 1fr));}

/* Narrow screens -------------------------------------------------------- */
@media (max-width: 1400px) { .kpi.wide { grid-column: span 1;} }
@media (max-width: 820px) {
  .kpi-val { font-size: clamp(18px, 5vw, 24px);}
  .donut-wrap { grid-template-columns: 1fr; justify-items: center;}
  .bar-row { grid-template-columns: minmax(60px, 96px) minmax(0, 1fr) minmax(56px, auto); font-size: 12px;}
  .stat-strip { grid-template-columns: repeat(auto-fit, minmax(min(118px, 100%), 1fr));}
}

/* ── Alert bell ─────────────────────────────────────────────────────────── */
.bell-badge {
  position: absolute; top: -5px; right: -5px; min-width: 18px; height: 18px;
  padding: 0 4px; border-radius: 999px; display: grid; place-items: center;
  font-size: 10px; font-weight: 800; color: #fff; background: var(--info);
  box-shadow: 0 0 0 2px var(--surface);}
.bell-badge.warn { background: var(--warn);}
.bell-badge.danger { background: var(--down); animation: bellPulse 1.8s ease-in-out infinite;}
@keyframes bellPulse {
  0%, 100% { box-shadow: 0 0 0 2px var(--surface), 0 0 0 0 rgba(225,29,72,.5); }
  55%      { box-shadow: 0 0 0 2px var(--surface), 0 0 0 6px rgba(225,29,72,0); }
}
#bellBtn.has-alerts { animation: bellShake 3.5s ease-in-out infinite;}
@keyframes bellShake {
  0%, 88%, 100% { transform: rotate(0); }
  90% { transform: rotate(-11deg); } 93% { transform: rotate(9deg); }
  96% { transform: rotate(-5deg); }
}
.bell-panel {
  position: absolute; right: 0; top: calc(100% + 10px); width: min(420px, 88vw);
  background: var(--surface); border: 1px solid var(--line); border-radius: 14px;
  box-shadow: var(--shadow-lg); z-index: 60; overflow: hidden;
  animation: cardIn .22s cubic-bezier(.22,.9,.3,1) both;}
.bell-head { display: flex; align-items: center; gap: 7px; padding: 12px 15px; border-bottom: 1px solid var(--line); background: var(--surface-2);}
.bell-body { max-height: 56vh; overflow-y: auto; padding: 11px 13px;}
.bell-body .alert { cursor: pointer;}
.bell-foot { display: flex; align-items: center; gap: 8px; padding: 9px 13px; border-top: 1px solid var(--line); background: var(--surface-2);}
[data-motion='off'] .bell-badge.danger, [data-motion='off'] #bellBtn.has-alerts { animation: none !important;}

/* ── Row selection & bulk actions ───────────────────────────────────────── */
.selcol { width: 34px; text-align: center;}
.selcol input[type=checkbox] { width: 15px; height: 15px; accent-color: var(--brand-1); cursor: pointer;}
table.tbl tbody tr.row-selected { background: var(--a-indigo-s); box-shadow: inset 3px 0 0 var(--brand-1);}
table.tbl tbody tr.row-selected:hover { background: color-mix(in srgb, var(--a-indigo-s) 80%, var(--brand-1) 8%);}

/* Every figure gets tabular numerals so columns of digits align to the pixel.
   This one property does more for a financial table than any amount of colour. */
.mono, .num, .tbl td, .tbl th, .stat .v, .kpi-val, input[type='number'] {
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;}

h1, h2, h3 { letter-spacing: -0.018em; font-weight: 650;}

/* ── Cards ──────────────────────────────────────────────────────────────── */
.card {
  transition: box-shadow 0.2s ease;}
.card:hover { box-shadow: var(--sh-3);}
.card-head h3 { margin: 0;}

/* ── KPI strip ──────────────────────────────────────────────────────────── */
.stat-strip {
  gap: 1px;}
.stat {
  background: var(--card);
  transition: background 0.16s ease;}
.tbl thead th {
  backdrop-filter: blur(8px);}

.tbl tbody td {
  font-size: var(--fs-sm);}
.fin {
  transition: border-color 0.15s ease, box-shadow 0.15s ease;}
.fin::placeholder { opacity: 0.65;}

/* ── Buttons ────────────────────────────────────────────────────────────── */
.btn {
  letter-spacing: -0.004em;}
.btn:active:not(:disabled) { transform: translateY(0);}
.btn.primary {
  color: #fff;}

/* ── Alerts ─────────────────────────────────────────────────────────────── */
.alert {
  border-left-width: 3px;}
.sb-item.active {
  box-shadow: 0 1px 6px color-mix(in srgb, var(--brand-1) 30%, transparent);}
.input {
  border-radius: var(--r-sm);
  font-size: var(--fs-sm);
  border: 1px solid var(--line);
  transition: border-color 0.15s ease, box-shadow 0.15s ease;}
.input:focus {
  outline: none;}

/* Build stamp — quiet, but the fastest way to tell a cached script from a bug. */
.sb-build {
  font-size: 9.5px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  opacity: 0.42;
  padding: 6px 4px 0;
  text-align: center;
  font-variant-numeric: tabular-nums;}

/* ── Command palette (Ctrl/Cmd+K or /) ─────────────────────────────────── */
.pal-back {
  position: fixed;
  inset: 0;
  z-index: 9000;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding-top: 12vh;
  animation: palIn 0.14s ease-out;}
@keyframes palIn { from { opacity: 0; } to { opacity: 1; } }
.pal {
  width: min(560px, 92vw);
  animation: palUp 0.16s cubic-bezier(0.2, 0.9, 0.3, 1);}
@keyframes palUp { from { transform: translateY(-9px) scale(0.985); } to { transform: none; } }
.pal-in { display: flex; align-items: center; gap: 10px; padding: 14px 18px; border-bottom: 1px solid var(--line);}
.pal-mag { font-size: 15px; color: var(--muted);}
.pal-in input {
  flex: 1;
  border: 0;
  outline: none;
  background: transparent;
  color: var(--fg);
  font: inherit;
  letter-spacing: -0.01em;}
.pal-list { max-height: 46vh; overflow: auto; padding: 6px;}
.pal-row {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 9px 12px;
  text-decoration: none;
  font-size: var(--fs-base);}
.pal-ic { font-size: var(--fs-lg); width: 22px; text-align: center;}
.pal-lbl { flex: 1; font-weight: 560;}
.pal-hint { font-size: var(--fs-micro); text-transform: uppercase; letter-spacing: 0.07em; opacity: 0.6;}
.pal-none { padding: 22px; text-align: center; color: var(--muted); font-size: var(--fs-sm);}
.pal-foot {
  display: flex;
  gap: 16px;
  padding: 9px 18px;
  border-top: 1px solid var(--line);
  font-size: var(--fs-micro);
  color: var(--muted);}
.pal-foot kbd {
  font: inherit;
  padding: 1px 5px;
  margin-right: 3px;
  border-radius: 4px;
  border: 1px solid var(--line);
  background: color-mix(in srgb, var(--fg) 5%, transparent);}

/* ── Extra Income month chart ──────────────────────────────────────────────
   Solid fill is money received, the outline above it is money still owed. One
   glance answers "which months have not been paid". */
.ei-chart {
  display: flex;
  align-items: flex-end;
  gap: 5px;
  height: 170px;
  padding-top: 6px;}
.ei-bar {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  align-items: center;
  height: 100%;
  gap: 5px;}
.ei-col {
  width: 100%;
  max-width: 34px;
  border-radius: 5px 5px 2px 2px;
  border: 1.5px dashed color-mix(in srgb, var(--brand-1) 55%, transparent);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  transition: filter 0.15s ease, transform 0.15s ease;}
.ei-bar:hover .ei-col { filter: brightness(1.08); transform: translateY(-2px);}
.ei-got {
  width: 100%;
  border-radius: 4px 4px 2px 2px;}
.ei-lbl {
  font-size: 9.5px;
  color: var(--muted);
  white-space: nowrap;
  transform: rotate(-45deg);
  transform-origin: center;}
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 14px;}
@media (max-width: 1100px) { .grid-2 { grid-template-columns: 1fr;} }
/* Four abreast on a desktop, two on a laptop, one on a phone — the plan cards
   in the Fee Ledger are the four on the studio's own banner and read as a set.
   The class was already being used elsewhere with nothing behind it, so those
   cards were stacking in a single column. */
.grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px;}
@media (max-width: 1400px) { .grid-4 { grid-template-columns: 1fr 1fr;} }
@media (max-width: 760px) { .grid-4 { grid-template-columns: 1fr;} }

/* ── Live price ticks ──────────────────────────────────────────────────────
   A brief flash on the cell that moved, then back to normal. Enough to notice
   out of the corner of an eye; not enough to be irritating on a busy day. */
.tick-up, .tick-down { animation: tickFlash 1.5s ease-out; border-radius: 5px;}
.tick-up  { animation-name: tickUp;}
.tick-down { animation-name: tickDown;}
@keyframes tickUp {
  0%   { background: color-mix(in srgb, var(--up) 34%, transparent); }
  100% { background: transparent; }
}
@keyframes tickDown {
  0%   { background: color-mix(in srgb, var(--down) 34%, transparent); }
  100% { background: transparent; }
}
@media (prefers-reduced-motion: reduce) {
  .tick-up, .tick-down { animation: none;}
}

/* ── Merged-module tabs ────────────────────────────────────────────────────
   Rendered whenever a module family has children. Looks like a segmented
   control rather than browser tabs, so it reads as one page with facets. */
.mtabs {
  display: flex;
  gap: 5px;
  flex-wrap: wrap;
  margin-bottom: 14px;
  padding: 5px;
  border-radius: 13px;
  background: var(--card);
  border: 1px solid var(--line);
  box-shadow: var(--sh-1, 0 1px 2px rgb(0 0 0 / 5%));}
.mtab {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 8px 14px;
  border-radius: 9px;
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
  text-decoration: none;
  white-space: nowrap;
  transition: background 0.16s ease, color 0.16s ease, transform 0.16s ease;}
.mtab:hover { background: var(--hover, rgb(127 127 127 / 8%)); color: var(--fg);}
.mtab.on {
  background: linear-gradient(135deg, var(--brand-1), var(--brand-2));
  color: #fff;
  box-shadow: 0 2px 9px color-mix(in srgb, var(--brand-1) 34%, transparent);}
h1.brand-word { font-size: 25px; line-height: 1.14;}
.sb-title.brand-word { font-size: 13.5px; letter-spacing: 0.15em;}
@keyframes brandSheen {
  0%, 100% { background-position: 0% center; }
  50%      { background-position: 100% center; }
}
@media (prefers-reduced-motion: reduce) { .brand-word { animation: none;} }

.sel-bar.is-empty { display: none;}
.sel-bar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 11px 16px; margin-bottom: 12px; border-radius: 12px;
  background: linear-gradient(135deg, var(--a-indigo-s), var(--a-violet-s));
  border: 1px solid var(--line); box-shadow: var(--shadow-sm);
  animation: cardIn .22s cubic-bezier(.22,.9,.3,1) both;}
.sel-bar .sel-count {
  min-width: 26px; height: 26px; padding: 0 7px; border-radius: 999px;
  display: grid; place-items: center; font-size: 12px; font-weight: 800;
  background: var(--brand-1); color: #fff;}
.sb-group { margin: 12px 0 3px; font-family: inherit;}
.sb-group .chev { width: 10px;}
.sb-group .gname { flex: 1; text-align: left; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;}
.sb-group .gcount {
  font-size: 9.5px; font-weight: 700; letter-spacing: 0; padding: 1px 6px;
  border-radius: 999px; background: rgba(255,255,255,.12); color: #c7d2fe;}
.sb-items { overflow: hidden;}
/* Exactly one child, so the single 0fr/1fr row track governs the whole group.
   With the links as direct children, only the first was sized by the track and
   the rest landed in auto-sized implicit rows — which is why a collapsed group
   still left a gap. */
.sb-items > .sb-items-inner { min-height: 0; overflow: hidden;}
.sb-group-wrap.open .sb-items { grid-template-rows: 1fr;}
.sb-group-wrap:not(.open) .sb-items { visibility: hidden;}
.sb-group-wrap.open .sb-items { visibility: visible;}
/* No stray margin can survive the collapse either. */
.sb-group-wrap:not(.open) .sb-items-inner > * { margin: 0;}
[data-motion='off'] .sb-items { transition: none;}

/* ── Frozen first column ────────────────────────────────────────────────── */
/* A 14-column holdings table is unreadable once you scroll right and lose the
   symbol. The identifying column (and the select box in front of it) stay put. */
table.tbl.freeze-first thead th:first-child,
table.tbl.freeze-first tbody td:first-child,
table.tbl.freeze-first tfoot td:first-child {
  position: sticky; left: 0; z-index: 4;
  background: var(--surface);}
table.tbl.freeze-first thead th:first-child { z-index: 7; background: var(--surface-2);}
table.tbl.freeze-first tfoot td:first-child { z-index: 6; background: var(--surface-2);}
table.tbl.freeze-first tbody tr:nth-child(even) td:first-child { background: var(--surface);}
table.tbl.freeze-first tbody tr:hover td:first-child { background: var(--surface-2);}
table.tbl.freeze-first tbody tr.row-selected td:first-child { background: var(--a-indigo-s);}

/* When a select column is present, freeze the name column beside it too. */
table.tbl.freeze-first.has-sel thead th:nth-child(2),
table.tbl.freeze-first.has-sel tbody td:nth-child(2),
table.tbl.freeze-first.has-sel tfoot td:nth-child(2) {
  position: sticky; left: 34px; z-index: 4; background: var(--surface);}
table.tbl.freeze-first.has-sel thead th:nth-child(2) { z-index: 7; background: var(--surface-2);}
table.tbl.freeze-first.has-sel tfoot td:nth-child(2) { z-index: 6; background: var(--surface-2);}
table.tbl.freeze-first.has-sel tbody tr:hover td:nth-child(2) { background: var(--surface-2);}
table.tbl.freeze-first.has-sel tbody tr.row-selected td:nth-child(2) { background: var(--a-indigo-s);}

/* A soft edge so it reads as pinned rather than overlapping. */
table.tbl.freeze-first tbody td:first-child,
table.tbl.freeze-first.has-sel tbody td:nth-child(2) { box-shadow: 1px 0 0 var(--line);}
table.tbl.freeze-first thead th:first-child,
table.tbl.freeze-first.has-sel thead th:nth-child(2) { box-shadow: 1px 0 0 var(--line);}

/* The pinned column must not be the widest thing on screen. */
table.tbl.freeze-first tbody td:first-child,
table.tbl.freeze-first.has-sel tbody td:nth-child(2) { max-width: 210px;}

@media (max-width: 620px) {
  table.tbl.freeze-first tbody td:first-child,
  table.tbl.freeze-first.has-sel tbody td:nth-child(2) { max-width: 130px;}
}

/* ===========================================================================
   PALETTES — the whole app reskins from one attribute on <html>.
   Only the brand ramp and the aurora blobs change; the semantic colours
   (up/down/warn) stay put, because green must mean profit in every theme.
   =========================================================================== */

/* Indigo — the default */
[data-palette='indigo'] { --brand-1:#6366f1; --brand-2:#8b5cf6; --brand-3:#d946ef;
  --au-1:#818cf8; --au-2:#f0abfc; --au-3:#67e8f9; --au-4:#6ee7b7;
  --sb-1:#1e1b4b; --sb-2:#312e81; --sb-3:#4c1d95; --sb-glow:rgba(217,70,239,.28); --sb-active:#f0abfc;}

/* Ocean — cool blues and teal, calm for long sessions */
[data-palette='ocean'] { --brand-1:#0284c7; --brand-2:#0891b2; --brand-3:#06b6d4;
  --au-1:#38bdf8; --au-2:#5eead4; --au-3:#818cf8; --au-4:#a5f3fc;
  --sb-1:#082f49; --sb-2:#0c4a6e; --sb-3:#155e75; --sb-glow:rgba(34,211,238,.30); --sb-active:#67e8f9;}

/* Sunset — warm amber into rose */
[data-palette='sunset'] { --brand-1:#ea580c; --brand-2:#e11d48; --brand-3:#db2777;
  --au-1:#fb923c; --au-2:#fda4af; --au-3:#fcd34d; --au-4:#f0abfc;
  --sb-1:#431407; --sb-2:#7c2d12; --sb-3:#9f1239; --sb-glow:rgba(251,146,60,.32); --sb-active:#fdba74;}

/* Forest — greens, easiest on the eye */
[data-palette='forest'] { --brand-1:#059669; --brand-2:#0d9488; --brand-3:#65a30d;
  --au-1:#6ee7b7; --au-2:#bef264; --au-3:#5eead4; --au-4:#a7f3d0;
  --sb-1:#052e16; --sb-2:#064e3b; --sb-3:#065f46; --sb-glow:rgba(110,231,183,.30); --sb-active:#6ee7b7;}

/* Royal — deep purple and gold, the most "control room" of the set */
[data-palette='royal'] { --brand-1:#7c3aed; --brand-2:#a21caf; --brand-3:#f59e0b;
  --au-1:#c4b5fd; --au-2:#f0abfc; --au-3:#fcd34d; --au-4:#a78bfa;
  --sb-1:#2e1065; --sb-2:#4c1d95; --sb-3:#701a75; --sb-glow:rgba(245,158,11,.26); --sb-active:#fcd34d;}

/* Slate — near-monochrome, for when the colour is a distraction */
[data-palette='slate'] { --brand-1:#475569; --brand-2:#64748b; --brand-3:#0ea5e9;
  --au-1:#cbd5e1; --au-2:#94a3b8; --au-3:#bae6fd; --au-4:#e2e8f0;
  --sb-1:#0f172a; --sb-2:#1e293b; --sb-3:#334155; --sb-glow:rgba(14,165,233,.22); --sb-active:#7dd3fc;}
.sidebar::after { background: radial-gradient(500px 300px at 100% 0%, var(--sb-glow), transparent 60%);}
.login-wrap { background: linear-gradient(135deg, var(--brand-1) 0%, var(--brand-2) 45%, var(--brand-3) 100%);}
#aurora i:nth-child(1) { background: var(--au-1);}
#aurora i:nth-child(2) { background: var(--au-2);}
#aurora i:nth-child(3) { background: var(--au-3);}
#aurora i:nth-child(4) { background: var(--au-4);}
#aurora { background:
  radial-gradient(1200px 600px at 50% -10%, color-mix(in srgb, var(--brand-1) 9%, transparent), transparent 65%),
  var(--bg);}
body { background-image:
  radial-gradient(1200px 600px at 100% -5%, color-mix(in srgb, var(--brand-2) 10%, transparent), transparent 60%),
  radial-gradient(900px 500px at -10% 100%, color-mix(in srgb, var(--brand-1) 10%, transparent), transparent 60%);}
.kpi::before { background: linear-gradient(90deg, var(--acc), color-mix(in srgb, var(--acc) 45%, #fff));}

/* Palette picker */
.palette-menu {
  position: absolute; right: 0; top: calc(100% + 10px); width: 236px; z-index: 60; animation: cardIn .2s cubic-bezier(.22,.9,.3,1) both;}
.palette-row {
  font-family: inherit;}
.swatch { display: flex; gap: 3px; flex: 0 0 auto;}
.swatch i { width: 13px; height: 13px; border-radius: 4px; display: block;}
.palette-row .nm { flex: 1;}
.palette-row .tick { font-weight: 800;}

/* A KPI card that needs looking at right now. */
.stat.is-alarm {
  border-color: color-mix(in srgb, var(--down) 55%, transparent);}
@keyframes alarmPulse {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--down) 30%, transparent); }
  50%      { box-shadow: 0 0 0 6px color-mix(in srgb, var(--down) 0%, transparent); }
}
@media (prefers-reduced-motion: reduce) { .stat.is-alarm { animation: none;} }

/* A figure in a table that opens the records behind it. Reads as the number it
   replaces - same font, same alignment, inheriting the cell's rank colouring -
   with only a dotted underline to say it can be clicked. A bare <button> would
   otherwise arrive with its own font and a grey box, and the column would stop
   lining up. */
.lnk-num {
  background: none; border: 0; padding: 0; margin: 0;
  font: inherit; color: inherit; cursor: pointer;
  font-variant-numeric: tabular-nums;
  border-bottom: 1px dotted currentColor;
}
.lnk-num:hover { color: var(--brand-1); }
.lnk-num:focus-visible { outline: 2px solid var(--brand-1); outline-offset: 2px; }

/* The download on a KPI tile. The tile itself is a link, so this sits on top of
   it and has to read as its own control rather than part of the label — faint
   until the tile is hovered, so eight of them do not clutter the row. */
.kpi-dl {
  cursor: pointer; opacity: .35; font-size: 12px; padding: 0 2px;
  border-radius: 4px; line-height: 1;
}
.kpi:hover .kpi-dl { opacity: .8; }
.kpi-dl:hover { opacity: 1; background: var(--surface-2); }
