/* =====================================================================
   Report dashboard styles
   Loaded by reports/templates/reports/report.html
   ===================================================================== */

/* The tab strip and the panel under it moved to tabs.css, which layout.html
   loads on every page: the strip is rendered from the shared navigation now
   and appears on pages that never load this file. */



/* Descendant, not `> .rpt-panel`: the credits tabs nest their cards one
   level deeper, inside <section class="toggle-section">, so a child
   combinator matched products but silently missed credits — the case this
   was written for.

   The tray alone is not quite enough on a dark theme, where --surface
   (#0f172a) and --surface-sunken (#020617) sit close together. A hairline
   edge makes each card read as a card in both themes. Scoped to tab panels
   so panels on ordinary pages keep their borderless, shadow-only look. */
/* Anchor-scrolling to a tab section must not put its top under the chrome.

   Several sidebar links carry a fragment — Customers goes to
   credits_main#customers-section, Reconciliation to #reconciliation. The
   browser scrolls that element to the very top of the viewport, where the
   fixed topbar and the sticky tab strip sit on top of it, hiding the first
   thing inside the section. That first thing is the filter strip, so the
   page opened looking as though it had no filter at all.

   .tab-pane already had this. .toggle-section is the same idea with a
   different name — the credits tabs and the admin dashboard use it — and
   it was missed, which is why Customers jumped and Reconciliation did not. */
.tab-pane,
.toggle-section,
.rpt-anchor { scroll-margin-top: 130px; }   /* .rpt-anchor: a panel other pages link to by #id */



/* ─── Reconciliation calendar strip (shared with sellers' view) ── */
.recon-calendar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.recon-day {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 50px;
  height: 54px;
  border-radius: var(--radius-tile);
  border: 1px solid transparent;
  color: var(--text);
  font-size: var(--text-body);
  line-height: 1.1;
  background: var(--surface-hover);
  cursor: default;
}
.recon-day .dow { opacity: .65; font-size: var(--text-meta); }
.recon-day .dom { font-weight: 700; font-size: var(--text-section); }
.recon-day.is-green  { background: var(--success-bg); color: var(--success-fg); }
.recon-day.is-orange { background: var(--warn-bg); color: var(--warn-fg); }
.recon-day.is-red    { background: var(--danger-bg); color: var(--danger-fg); }
.recon-day.is-grey   { background: var(--surface-hover); color: var(--text-muted); }
.legend-dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; vertical-align: middle; margin-right: 4px; }

/* ─── Tap-aware popover for a calendar tile (mobile-friendly) ─── */
.recon-day { cursor: pointer; }
.recon-day:focus-visible,
.recon-day.is-active {
  outline: 2px solid #1f2937;
  outline-offset: 1px;
}
.recon-popover {
  position: fixed;
  z-index: 1080;
  min-width: 220px;
  max-width: 280px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-btn);
  box-shadow: 0 10px 25px rgba(15, 23, 42, 0.18);
  padding: var(--space-4) var(--space-4) var(--space-3);
  font-size: var(--text-support);
  color: var(--text);
}
.recon-popover[hidden] { display: none; }
.recon-popover-close {
  position: absolute;
  top: 4px;
  right: 6px;
  border: 0;
  background: transparent;
  font-size: var(--text-title);
  line-height: 1;
  color: var(--text-subtle);
  cursor: pointer;
}
.recon-popover-close:hover { color: #1f2937; }
.recon-popover-date {
  font-weight: 600;
  color: var(--text);
  margin-bottom: var(--space-3);
  padding-right: var(--space-5);
}
.recon-popover-row {
  display: flex;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-1) 0;
}
.recon-popover-row .lbl { color: var(--text-muted); }
.recon-popover-row .val { font-weight: 600; }
.recon-popover-var-row {
  border-top: 1px dashed var(--border);
  margin-top: var(--space-1);
  padding-top: var(--space-2);
}
.recon-popover.is-green  .recon-popover-variance { color: var(--success); }
.recon-popover.is-orange .recon-popover-variance { color: var(--warn); }
.recon-popover.is-red    .recon-popover-variance { color: var(--danger); }
.recon-popover.is-grey   .recon-popover-variance { color: var(--text-muted); }

/* =====================================================================
   Report design system v2 — borderless panels, hero metric,
   flow bars, insight strips, and ranked leaderboards.
   Shared by Sales Summary and Reconciliation tabs.
   ===================================================================== */

/* Borderless white surface with a soft shadow — the "plain nice card". */
.rpt-panel {
  background: var(--surface);
  /* A card is white on a near-white page (--surface-sunken is slate-50),
     so on the light theme the shadow alone was carrying the whole edge and
     the sections ran together. --border rather than --border-strong: a
     card edge is long, so it reads at a weight that would be too heavy
     around a small control. */
  border: 1px solid var(--border);
  border-radius: var(--radius-btn);
  padding: var(--space-6) var(--space-7);
  margin-bottom: var(--space-5);
  box-shadow:
    0 1px 2px rgba(15, 23, 42, .04),
    0 1px 3px rgba(15, 23, 42, .05);
}
@media (max-width: 640px) {
  .rpt-panel { padding: var(--space-5); border-radius: var(--radius-btn); }
}

/* A panel inside the tab canvas is a card, exactly as it is anywhere else.

   This block used to strip the background, shadow and padding from any
   .rpt-panel inside .rpt-tab-panel, on the reasoning that "the tab canvas is
   already the surface, so nested cards would fight each other". That held
   while the canvas was var(--surface) — a card on a card does look wrong.

   The canvas is now a recessed tray (var(--surface-sunken), see above), so
   the premise no longer holds: the tray is the PAGE surface, not the card
   surface. Keeping the flattening is what made tabbed pages look unlike
   every other page — sections running edge to edge, divided by hairlines,
   where an untabbed page shows separated cards.

   Panels inside the tray therefore inherit .rpt-panel untouched. The only
   thing kept here is the last-child margin, which stops the tray ending in
   a band of empty padding. */
.rpt-tab-panel .rpt-panel:last-child { margin-bottom: 0; }

/* ─── Hero metric strip ─────────────────────────────────────────── */
.rpt-hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--space-7);
  align-items: start;
}
.rpt-hero-eyebrow {
  font-size: var(--text-meta);
  font-weight: 600;
  letter-spacing: .1em;
  color: var(--text-muted);
  margin-bottom: var(--space-3);
}

/* Back arrow sharing the eyebrow line, for a detail page.

   A detail page's Back used to get a row of its own above the card, which
   spent a whole band of vertical space on one 30px button. On the eyebrow
   it sits next to the thing it returns from, and the card keeps the top of
   the page. Every detail page needs this, so it is a class rather than
   another arrangement invented per page.

   The arrow is a real control at text scale, hence the negative inline
   start: without it the label no longer lines up with the figure below. */
.rpt-hero-eyebrow.has-back {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-inline-start: calc(var(--space-2) * -1);
  letter-spacing: normal;
}

.rpt-hero-eyebrow.has-back .rpt-btn.icon-only {
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
}

.rpt-hero-eyebrow.has-back > span {
  letter-spacing: .1em;
  min-width: 0;
}
.rpt-hero-value {
  font-size: 2.6rem;
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.025em;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.rpt-hero-value.tone-ok    { color: var(--success-fg); }
.rpt-hero-value.tone-over  { color: var(--warn); }
.rpt-hero-value.tone-under { color: var(--danger-fg); }
.rpt-hero-value.tone-info  { color: var(--text-muted); }
.rpt-hero-caption {
  margin-top: var(--space-3);
  font-size: var(--text-row);
  color: var(--text-muted);
  line-height: 1.5;
}
.rpt-hero-caption strong { color: var(--text); font-weight: 600; }

.rpt-hero-meta {
  margin-top: var(--space-5);
  display: grid;
  /* Wider min-track so a currency-prefixed value ("UGX 2,607,700") stays on
     one line under the new 1.4rem m-val size. Falls back to fewer columns
     rather than wrapping the value across two lines. */
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: var(--space-4) var(--space-7);
  max-width: 640px;
}
.rpt-hero-meta .m-lbl {
  font-size: var(--text-meta);
  font-weight: 600;
  color: var(--text-subtle);
  margin-bottom: var(--space-1);
}
.rpt-hero-meta .m-val {
  /* Spec: a Stat figure (26) steps down to the Card figure (20) below a 13rem
     tile. This grid is minmax(160px) = 10rem, so it takes the smaller role —
     a step between two roles in the table, never a new size. */
  font-size: var(--text-card-fig);
  line-height: 1.15;
  font-weight: 700;
  letter-spacing: -0.015em;
  color: var(--text);
  font-variant-numeric: tabular-nums;
  /* Keep "UGX 2,607,700" on one line. If the number outgrows its column, the
     grid re-flows (fewer columns per row) rather than breaking the value in
     half — a wrapped currency prefix reads worse than a wider tile. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
@media (max-width: 480px) {
  .rpt-hero-meta .m-val { font-size: var(--text-title); }
}
.rpt-hero-meta .m-val.tone-ok    { color: var(--success-fg); }
.rpt-hero-meta .m-val.tone-over  { color: var(--warn); }
.rpt-hero-meta .m-val.tone-under { color: var(--danger-fg); }
.rpt-hero-meta .m-val.tone-info  { color: var(--text-muted); }

.rpt-hero-verdict {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-5);
  border-radius: var(--radius-pill);
  font-size: var(--text-body);
  font-weight: 600;
  white-space: nowrap;
}
.rpt-hero-verdict.tone-ok    { background: var(--success-bg); color: var(--success-fg); }
.rpt-hero-verdict.tone-over  { background: var(--warn-bg); color: var(--warn-fg); }
.rpt-hero-verdict.tone-under { background: var(--danger-bg); color: var(--danger-fg); }
.rpt-hero-verdict.tone-info  { background: var(--surface-hover); color: var(--text); }
.rpt-hero-verdict .dot { width: 8px; height: 8px; border-radius: 50%; }
.rpt-hero-verdict.tone-ok    .dot { background: var(--success); }
.rpt-hero-verdict.tone-over  .dot { background: var(--warn); }
.rpt-hero-verdict.tone-under .dot { background: var(--danger); }
.rpt-hero-verdict.tone-info  .dot { background: #94a3b8; }

@media (max-width: 640px) {
  .rpt-hero { grid-template-columns: 1fr; gap: var(--space-4); }
  .rpt-hero-value { font-size: var(--text-hero); }
  .rpt-hero-meta { grid-template-columns: repeat(2, 1fr); }
}

/* ─── Flow bar visualization ────────────────────────────────────── */
.rpt-flow-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-4);
  margin-bottom: var(--space-2);
}
.rpt-flow-header h6 {
  font-size: var(--text-row);
  font-weight: 600;
  color: var(--text);
  margin: 0 0 2px;
}
.rpt-flow-header small { color: var(--text-muted); font-size: var(--text-body); }

.rpt-flow-track {
  position: relative;
  /* 32px, not 44: the bar reads just as well and the table under it comes
     up the screen. The 32px of headroom is only for a marker's floating
     label (reconciliation's "declared"), so only a track with a marker
     reserves it — every other bar sat under a band of empty space. */
  height: 32px;
  background: var(--surface-hover);
  border-radius: var(--radius-btn);
  margin: var(--space-2) 0;
}
.rpt-flow-track:has(.rpt-flow-marker) { margin-top: 32px; }
.rpt-flow-fill {
  position: absolute;
  inset: 0;
  display: flex;
  border-radius: var(--radius-btn);
  overflow: hidden;
}
.rpt-flow-seg {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--text-body);
  font-weight: 600;
  color: #ffffff;
  min-width: 0;
  padding: 0 var(--space-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: filter .12s;
}
.rpt-flow-seg:hover { filter: brightness(1.08); }
.rpt-flow-seg.seg-cash    { background: linear-gradient(180deg, #34d399, var(--success)); }
.rpt-flow-seg.seg-repaid  { background: linear-gradient(180deg, #818cf8, var(--info)); }
.rpt-flow-seg.seg-credit  { background: linear-gradient(180deg, #fbbf24, var(--warn)); }
.rpt-flow-seg.seg-profit  { background: linear-gradient(180deg, #4ade80, var(--success)); }
.rpt-flow-seg.seg-expense { background: linear-gradient(180deg, #fb7185, #e11d48); }
.rpt-flow-seg.seg-restock { background: linear-gradient(180deg, #94a3b8, #64748b); }
.rpt-flow-seg.seg-neutral { background: linear-gradient(180deg, var(--surface-hover), #94a3b8); color: var(--text); }

.rpt-flow-marker {
  position: absolute;
  top: -10px;
  bottom: -10px;
  width: 0;
  /* --text, not #0f172a: that is the dark theme's own page colour, so the
     marker vanished there. */
  border-left: 2px dashed var(--text);
  z-index: 3;
  pointer-events: none;
}
.rpt-flow-marker::before {
  content: "";
  position: absolute;
  top: -4px;
  left: -4px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--text);
}
.rpt-flow-marker .lbl {
  position: absolute;
  top: -26px;
  left: 6px;
  font-size: var(--text-meta);
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  background: var(--surface);
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-tile);
  box-shadow: 0 1px 3px rgba(15, 23, 42, .12);
  font-variant-numeric: tabular-nums;
}
.rpt-flow-marker.right-align .lbl { left: auto; right: 6px; }

.rpt-flow-scale {
  display: flex;
  justify-content: space-between;
  font-size: var(--text-meta);
  color: var(--text-subtle);
  margin-top: var(--space-2);
  font-variant-numeric: tabular-nums;
}

.rpt-flow-legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-6);
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px dashed var(--border);
  font-size: var(--text-body);
}
.rpt-flow-legend .item {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  color: var(--text-muted);
}
.rpt-flow-legend .swatch {
  width: 12px; height: 12px; border-radius: var(--radius-tile);
  flex-shrink: 0;
}
.rpt-flow-legend .swatch.s-cash    { background: linear-gradient(180deg, #34d399, var(--success)); }
.rpt-flow-legend .swatch.s-repaid  { background: linear-gradient(180deg, #818cf8, var(--info)); }
.rpt-flow-legend .swatch.s-credit  { background: linear-gradient(180deg, #fbbf24, var(--warn)); }
.rpt-flow-legend .swatch.s-profit  { background: linear-gradient(180deg, #4ade80, var(--success)); }
.rpt-flow-legend .swatch.s-expense { background: linear-gradient(180deg, #fb7185, #e11d48); }
.rpt-flow-legend .swatch.s-restock { background: linear-gradient(180deg, #94a3b8, #64748b); }
.rpt-flow-legend .swatch.s-declared{ background: var(--text); border-radius: 50%; width: 10px; height: 10px; }
.rpt-flow-legend .amt {
  color: var(--text);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* ─── Insight strip (soft callout, no card) ─────────────────────── */
.rpt-insight {
  display: flex;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-btn);
  background: var(--surface-sunken);
  border-left: 3px solid var(--border);
  margin-bottom: var(--space-5);
  font-size: var(--text-support);
  color: var(--text);
  line-height: 1.5;
}
.rpt-insight.tone-ok    { background: var(--success-bg); border-left-color: var(--success-border); }
.rpt-insight.tone-over  { background: var(--warn-bg); border-left-color: var(--warn-border); }
.rpt-insight.tone-under { background: var(--danger-bg); border-left-color: var(--danger-border); }
.rpt-insight i { margin-top: var(--space-1); opacity: .75; flex-shrink: 0; }
.rpt-insight strong { color: var(--text); font-weight: 600; }

/* ─── Daily compliance ribbon (calendar + sparkline) — recon only ── */
.recon-ribbon-header {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: flex-end;
  gap: var(--space-4);
  margin-bottom: var(--space-4);
}
.recon-ribbon-header h6 {
  font-size: var(--text-row);
  font-weight: 600;
  color: var(--text);
  margin: 0 0 2px;
}
.recon-ribbon-header small { color: var(--text-muted); font-size: var(--text-body); }
.recon-ribbon-legend {
  display: inline-flex;
  gap: var(--space-4);
  font-size: var(--text-body);
  color: var(--text-muted);
  flex-wrap: wrap;
}
.recon-ribbon-legend .item { display: inline-flex; align-items: center; gap: var(--space-1); }

.recon-spark { display: block; width: 100%; height: 48px; margin-top: var(--space-4); }
.recon-spark-baseline { stroke: var(--text-subtle); stroke-dasharray: 3 3; stroke-width: 1; }
.recon-spark-bar { transition: opacity .12s; }
.recon-spark-bar:hover { opacity: .65; }
.recon-spark-caption {
  display: flex;
  justify-content: space-between;
  font-size: var(--text-meta);
  color: var(--text-subtle);
  margin-top: var(--space-1);
}

.recon-ribbon-footer {
  margin-top: var(--space-5);
  padding-top: var(--space-4);
  border-top: 1px dashed var(--border);
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-3);
  font-size: var(--text-support);
  color: var(--text-muted);
}
.recon-ribbon-footer strong { font-variant-numeric: tabular-nums; }
.recon-ribbon-footer strong.tone-ok    { color: var(--success-fg); }
.recon-ribbon-footer strong.tone-over  { color: var(--warn); }
.recon-ribbon-footer strong.tone-under { color: var(--danger-fg); }

/* ─── Ranked leaderboard table ──────────────────────────────────── */
.rpt-board-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-4);
  margin-bottom: var(--space-2);
}
.rpt-board-header h6 {
  /* A section title should outrank the rows beneath it. At --text-row it
     was the same size as the values in the table, so "Product catalog" and
     "10,500" carried equal weight and the sections had no visible heading
     level at all. --text-section is the role named for exactly this. */
  font-size: var(--text-section);
  font-weight: 600;
  color: var(--text);
  margin: 0;
}
.rpt-board-header small { color: var(--text-muted); font-size: var(--text-body); }

.rpt-board-scroll {
  overflow-x: auto;
  margin: 0 -8px;
  padding: 0 var(--space-3);
}
.rpt-board-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: var(--text-support);
}
.rpt-board-table thead th {
  /* Padding restored. The token migration (7586601, 2026-09-20) had no
     token for 12px 10px 10px / 14px 10px and dropped both, so every table
     in the app ran its text into the cell edges. */
  padding: var(--space-4) var(--space-3) var(--space-3);
  /* The table's own size, not --text-meta: at 13px over 15px rows the
     headings read as smaller than the data they label. Weight and the muted
     colour set them apart; inheriting keeps them level on phones too, where
     the table drops to --text-body. */
  font-size: inherit;
  font-weight: 600;
  color: var(--text-muted);
  border-bottom: 1px solid var(--border);
  text-align: left;
  background: var(--surface);
  position: sticky;
  top: 0;
  white-space: nowrap;
}
.rpt-board-table th.num,
.rpt-board-table td.num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.rpt-board-table tbody td {
  padding: var(--space-4) var(--space-3);
  border-bottom: 1px solid var(--border-subtle);
  vertical-align: middle;
  color: var(--text);
}
.rpt-board-table tbody tr:last-child td { border-bottom: 0; }
.rpt-board-table tbody tr:hover td { background: var(--surface-sunken); }
.rpt-board-table tfoot td {
  padding: var(--space-4) var(--space-3);
  border-top: 2px solid var(--border);
  font-weight: 700;
  color: var(--text);
  background: var(--surface-sunken);
  font-variant-numeric: tabular-nums;
}
.rpt-board-table tfoot td.num { text-align: right; }

/* .rpt-board-branch is worn by BOTH <a> and <span> across the credits,
   dashboard and audit tables — it is the table's "primary cell" treatment,
   not a link class. So the base rule styles neither as clickable, and the
   link affordance is scoped to the anchors.

   Before this split it had a bare `:hover { text-decoration: underline }`,
   which meant the dead <span>s (customer names in _customers.html, party
   names in _credit_summary.html, branch names on the owner dashboard, the
   actor in the audit feed) underlined under the cursor and read as links
   that swallowed the click. The anchors had the opposite problem: painted
   var(--text), a real link looked like plain text.

   Scoping by element rather than renaming fixes both halves everywhere at
   once, and keeps working as these spans turn into real links. */
.rpt-board-branch {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  font-weight: 600;
  color: var(--text);
  text-decoration: none;
}

a.rpt-board-branch {
  color: var(--link);
  text-underline-offset: 2px;
}
a.rpt-board-branch:hover {
  color: var(--link-hover);
  text-decoration: underline;
}
a.rpt-board-branch:focus-visible {
  outline: 2px solid var(--link);
  outline-offset: 2px;
  border-radius: var(--radius-tile);
}
.rpt-board-rank {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: var(--radius-tile);
  background: var(--surface-hover);
  color: var(--text-muted);
  font-size: var(--text-body);
  font-weight: 700;
  flex-shrink: 0;
}
.rpt-board-rank.gold   { background: var(--warn-bg); color: var(--warn-fg); }
.rpt-board-rank.silver { background: var(--border-subtle); color: var(--text); }
.rpt-board-rank.bronze { background: var(--warn-bg); color: var(--warn-fg); }

.rpt-match {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-width: 140px;
}
.rpt-match-bar {
  flex: 1;
  height: 8px;
  background: var(--surface-hover);
  border-radius: var(--radius-pill);
  overflow: hidden;
  min-width: 60px;
}
.rpt-match-bar .fill {
  display: block;
  height: 100%;
  background: linear-gradient(90deg, #34d399, var(--success));
  border-radius: var(--radius-pill);
  transition: width .3s ease;
}
.rpt-match-bar .fill.mid  { background: linear-gradient(90deg, #fbbf24, var(--warn)); }
.rpt-match-bar .fill.low  { background: linear-gradient(90deg, #fb7185, #e11d48); }
.rpt-match-bar .fill.info { background: linear-gradient(90deg, var(--surface-hover), #94a3b8); }
.rpt-match-pct {
  font-size: var(--text-body);
  color: var(--text-muted);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  width: 40px;
  text-align: right;
}

/* ── One line per row ─────────────────────────────────────

   A table earns its keep by letting you scan many rows at once. A cell
   that wraps to a second line halves how many fit on a screen, and it
   does that to EVERY row in the table, not only to the one that had
   something extra to say.

   Across the app the same shape kept appearing: a value, and under it a
   muted second line - a person's role under their name, a transaction
   reference under a status badge, a location under a branch. `.cell-meta`
   is that second line, put back on the first one.

   Where the text is genuinely long it is truncated rather than wrapped,
   with the full string on the cell's `title`. Nothing is lost; it just
   stops costing every other row its height. */
.cell-meta {
  margin-left: var(--space-2);
  color: var(--text-subtle);
  font-size: var(--text-meta);
  font-weight: 400;
}
.cell-meta.sep::before { content: "·  "; }

.cell-1line {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The activity feed, which is the worst case: a whole sentence per row. */
.act-when { white-space: nowrap; }
.act-who  { white-space: nowrap; }
.act-what {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 520px;
}

/* The tone used to be a badge wrapped around the whole sentence, which
   is what forced the wrap in the first place. A dot carries the same
   signal in 7px and cannot grow with the text. */
.act-dot {
  display: inline-block;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  margin-right: var(--space-2);
  vertical-align: middle;
  background: var(--text-subtle);
}
.act-dot.tone-ok   { background: var(--success); }
.act-dot.tone-over { background: var(--danger); }
.act-dot.tone-info { background: var(--info); }

/* Who: the name, then the role as a quiet note. These two classes were in
   the markup with no rule at all, so "Ray Okello" and "Owner" ran together
   as "Ray OkelloOwner" — the template trims the whitespace between them. */
.act-role,
.act-why {
  margin-left: var(--space-1);   /* plus the real space the template now keeps */
  color: var(--text-subtle);
  font-size: var(--text-meta);
  font-weight: 400;
}
.act-why::before { content: " b7 a0"; }   /* "· " before the reason */

.act-history {
  padding: var(--space-1) var(--space-3);
  font-size: var(--text-meta);
}

/* The record-history page is the one place a cell may run to several
   lines: it shows a SINGLE record, so there is no list of rows for a tall
   cell to make taller, and the full diff is why somebody opened it. */
.act-detail {
  font-size: var(--text-meta);
  color: var(--text-muted);
  line-height: 1.7;
}
.act-detail strong { color: var(--text); font-weight: 600; margin-right: var(--space-1); }
.act-why-block { margin-top: var(--space-1); color: var(--text-subtle); font-style: italic; }

@media (max-width: 900px) {
  .act-what { max-width: 320px; }
}

.rpt-count {
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
}
.rpt-count.zero { color: var(--text-subtle); }
.rpt-count.hot-green  { color: var(--success-fg); font-weight: 600; }
.rpt-count.hot-orange { color: var(--warn); font-weight: 600; }
.rpt-count.hot-red    { color: var(--danger-fg); font-weight: 600; }
/* Plain value in the text colour. It was semi-bold, which made ordinary
   numbers compete with the row title; the other hot-* tones keep their
   weight because their colour is a warning. */
.rpt-count.hot-grey   { color: var(--text); }

/* The ONE outcome figure of a row (net, profit, balance, variance). 600,
   level with the row title; its tone colour is what sets it apart. At 700
   it out-shouted the title, and tables that used it on two or three
   columns read as all bold. */
.rpt-net {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.rpt-net.tone-ok    { color: var(--success-fg); }
.rpt-net.tone-over  { color: var(--warn); }
.rpt-net.tone-under { color: var(--danger-fg); }
.rpt-net.tone-info  { color: var(--text-subtle); font-weight: 500; }

/* ─── Chip: a status or label INSIDE a table cell ───────────────────
   Sixty-one cells were borrowing .rpt-hero-verdict — the hero's big
   verdict pill — and shrinking it back down with an inline style each
   time. This is that shrunk pill as a class of its own, same tones. */
.rpt-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-pill);
  font-size: var(--text-meta);
  font-weight: 600;
  line-height: var(--leading-single);
  white-space: nowrap;
  background: var(--surface-hover);
  color: var(--text);
}
.rpt-chip.tone-ok    { background: var(--success-bg); color: var(--success-fg); }
.rpt-chip.tone-over  { background: var(--warn-bg);    color: var(--warn-fg); }
.rpt-chip.tone-under { background: var(--danger-bg);  color: var(--danger-fg); }
.rpt-chip.tone-info  { background: var(--info-bg);    color: var(--info-fg); }

.rpt-board-empty {
  text-align: center;
  padding: 32px var(--space-4);
  color: var(--text-subtle);
  font-size: var(--text-row);
}

/* ─── Subtle button system (used inside .rpt-panel tables + filters) ── */
.rpt-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  font-size: var(--text-body);
  font-weight: 600;
  color: var(--text-muted);
  /* A button has an edge.

     This used to be `border: 0; background: transparent`, so a plain
     .rpt-btn rendered as bare text - the Return action in a sales row and
     the Back arrow on a detail page had nothing to say they were
     controls. The tell was in the usage: `.rpt-btn.outline`, the variant
     whose only job was to add the edge back, was worn 82 times against 14
     for the base. When the exception outnumbers the rule five to one, the
     rule is wrong.

     The border lives here now, so every variant inherits it and the
     filled ones below only restate its COLOUR. Geometry is identical
     everywhere, which is what makes a row of mixed buttons line up. */
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-tile);
  cursor: pointer;
  text-decoration: none;
  line-height: 1.2;
  white-space: nowrap;
  transition: background .12s, color .12s;
  font-family: inherit;
}
.rpt-btn:hover     { background: var(--surface-hover); color: var(--text); text-decoration: none; }
.rpt-btn:focus-visible {
  outline: 2px solid var(--border);
  outline-offset: 1px;
}
.rpt-btn:disabled  { opacity: .45; cursor: not-allowed; }
.rpt-btn i         { font-size: var(--text-body); }

/* Icon-only square variant for row actions */
.rpt-btn.icon-only {
  width: 30px; height: 30px; padding: 0;
}

/* Strong = the true primary CTA (Add, Save, Submit). Solid dark. */
/* The primary CTA. Tokens, not hexes — this was:

       background: #0f172a;  color: #ffffff;
       :hover { background: var(--text); color: #ffffff; }

   which broke in both directions on the dark theme. #0f172a IS the dark
   theme's --surface, so the button disappeared into the page at rest; and
   --text there is #e2e8f0, so hovering turned it near-white with white
   text on it. A raw hex cannot flip, which is the whole reason tokens.css
   says colour belongs there. */
.rpt-btn.strong {
  background: var(--brand);
  border-color: var(--brand);
  color: var(--on-solid);
}
.rpt-btn.strong:hover {
  background: var(--brand-hover);
  border-color: var(--brand-hover);
  color: var(--on-solid);
}

/* Soft-primary = secondary CTAs that still want a fill (Repay, Apply). */
.rpt-btn.soft-primary {
  background: var(--info-bg);
  border-color: var(--info-border);
  color: var(--info-fg);
}
/* Hover has to differ from rest, or it is not feedback. This repeated
   the rest state exactly. */
.rpt-btn.soft-primary:hover {
  background: var(--info);
  border-color: var(--info);
  color: var(--on-solid);
}

/* Danger / Success — the edge is tinted to match the label, so an
   Approve and a Reject sitting next to each other are told apart by
   shape as well as by colour. */
.rpt-btn.danger  { color: var(--danger-fg);  border-color: var(--danger-border); }
.rpt-btn.danger:hover  { background: var(--danger-bg);  color: var(--danger-fg); }
.rpt-btn.success { color: var(--success-fg); border-color: var(--success-border); }
.rpt-btn.success:hover { background: var(--success-bg); color: var(--success-fg); }

/* .outline is now what the base already is. Kept as a no-op alias rather
   than edited out of 82 templates: deleting the class would be a large,
   purely cosmetic diff, and the name still reads correctly at the call
   site. Only the hover differs. */
.rpt-btn.outline:hover { background: var(--surface-sunken); color: var(--text); border-color: var(--border); }

/* The one genuine exception: a button that must NOT look like one,
   because it sits inside another control or in dense running text. */
.rpt-btn.bare {
  border-color: transparent;
  background: transparent;
}
.rpt-btn.bare:hover { background: var(--surface-hover); border-color: transparent; }

/* Row action cluster — tightens spacing when several .rpt-btn sit together */
.rpt-actions {
  display: inline-flex;
  gap: var(--space-1);
  justify-content: flex-end;
  flex-wrap: wrap;
}

/* ─── Definitions footer (glossary strip) ───────────────────────── */
.rpt-defs {
  margin-top: var(--space-3);
  padding: var(--space-1) var(--space-1) 0;
}
.rpt-defs-title {
  font-size: var(--text-meta);
  font-weight: 600;
  letter-spacing: .1em;
  color: var(--text-subtle);
  margin: 0 0 10px;
}
.rpt-defs-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--space-4) var(--space-7);
  margin: 0;
}
.rpt-defs-list > div { min-width: 0; }
.rpt-defs-list dt {
  font-size: var(--text-body);
  font-weight: 600;
  color: var(--text);
  margin-bottom: var(--space-1);
}
.rpt-defs-list dd {
  font-size: var(--text-body);
  color: var(--text-muted);
  line-height: 1.5;
  margin: 0;
}

@media (max-width: 720px) {
  /* Keep report tables as tables on phones — horizontal scroll via
     .rpt-board-scroll (overflow-x:auto) instead of collapsing rows into
     stacked cards. Tighter cell padding + font so more columns fit at
     a glance before the user has to scroll sideways. */
  .rpt-board-table { font-size: var(--text-body); }
  .rpt-board-table thead th,
  .rpt-board-table tbody td,
  .rpt-board-table tfoot td { padding: var(--space-3); }
  .rpt-match { min-width: 0; }
}

/* ─── Slim bar inside a hero ───────────────────────────────────────
   For a split the hero's own figures already state (paid / outstanding,
   payables / advances): the bar only has to show the proportion, so it
   lives in the hero as a thin line instead of a card of its own with a
   header and a legend repeating the same two numbers. That card was
   ~230px between the summary and the table. */
.rpt-hero-bar { margin-top: var(--space-5); max-width: 640px; }
.rpt-hero-bar .rpt-flow-track { height: 10px; margin: 0; border-radius: var(--radius-pill); }
.rpt-hero-bar .rpt-flow-fill  { border-radius: var(--radius-pill); }
.rpt-hero-bar .rpt-flow-seg   { padding: 0; }
.rpt-hero-bar-lbl {
  display: flex;
  justify-content: space-between;
  gap: var(--space-4);
  margin-top: var(--space-2);
  font-size: var(--text-meta);
  color: var(--text-muted);
}
.rpt-hero-bar-lbl .swatch {
  display: inline-block;
  width: 8px; height: 8px;
  border-radius: 50%;
  margin-right: var(--space-1);
}
.rpt-hero-bar-lbl .swatch.s-profit  { background: var(--success); }
.rpt-hero-bar-lbl .swatch.s-expense { background: var(--danger); }
.rpt-hero-bar-lbl .swatch.s-credit  { background: var(--warn); }
.rpt-hero-bar-lbl .swatch.s-cash    { background: var(--success); }
.rpt-hero-bar-lbl .swatch.s-gap     { background: var(--text-subtle); }
/* A marker in a slim bar is a plain tick: no dot, no floating label (the
   figure is in the hero), and no headroom reserved above the track. */
.rpt-hero-bar .rpt-flow-track:has(.rpt-flow-marker) { margin-top: 0; }
.rpt-hero-bar .rpt-flow-marker { top: -4px; bottom: -4px; border-left-style: solid; }
.rpt-hero-bar .rpt-flow-marker::before { display: none; }
.rpt-hero-bar-lbl .tick {
  display: inline-block;
  width: 2px; height: 10px;
  background: var(--text);
  margin-right: var(--space-2);
  vertical-align: -1px;
}
