/* Every colour is a Telegram theme variable with a fallback: the client injects the
   user's own theme, and a page that paints its own palette looks like a web page inside
   an app rather than part of it. */
:root {
  --bg: var(--tg-theme-bg-color, #ffffff);
  --card: var(--tg-theme-secondary-bg-color, #f2f2f7);
  --text: var(--tg-theme-text-color, #000000);
  --hint: var(--tg-theme-hint-color, #8e8e93);
  --link: var(--tg-theme-link-color, #2481cc);
  --button: var(--tg-theme-button-color, #2481cc);
  --button-text: var(--tg-theme-button-text-color, #ffffff);
  --expense: #e8534f;
  --income: #34a853;
  --transfer: #3b82f6;

  /* The eight slots the category pie paints with, assigned in this fixed order and never
     cycled: a colour belongs to a category, not to its rank, so a month in which Кафе
     overtakes Продукты does not repaint both. Past the eighth the tail folds into
     «Прочее» rather than inventing a ninth hue — which is also where a pie stops being
     readable. Checked for colour-vision separation against both surfaces; the three
     lightest steps sit under 3:1 on a light background, which is why every slice is also
     named and priced in the legend under the chart and never identified by colour alone. */
  --series-1: #2a78d6;
  --series-2: #eb6834;
  --series-3: #1baf7a;
  --series-4: #eda100;
  --series-5: #e87ba4;
  --series-6: #008300;
  --series-7: #4a3aa7;
  --series-8: #e34948;
}

/* The same eight hues re-stepped for a dark surface, not flipped: a light-surface step
   on a dark one is either glare or mud. Telegram tells the page which theme it is in and
   `applyColorScheme` writes it onto <html>; the media query is for a plain browser, where
   nothing does. The data-theme block comes last so the client's answer wins over the OS. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --series-1: #3987e5;
    --series-2: #d95926;
    --series-3: #199e70;
    --series-4: #c98500;
    --series-5: #d55181;
    --series-6: #008300;
    --series-7: #9085e9;
    --series-8: #e66767;
  }
}
:root[data-theme="dark"] {
  --series-1: #3987e5;
  --series-2: #d95926;
  --series-3: #199e70;
  --series-4: #c98500;
  --series-5: #d55181;
  --series-6: #008300;
  --series-7: #9085e9;
  --series-8: #e66767;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  /* Clears the fixed bottom bar, whose own padding already carries the safe area. */
  padding: 8px 12px calc(96px + env(safe-area-inset-bottom));
  background: var(--bg);
  color: var(--text);
  font: 15px/1.35 -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, sans-serif;
  -webkit-tap-highlight-color: transparent;
}

/* --- bottom bar --------------------------------------------------------------- */
/* The two questions this app answers, plus the one thing it can send. At the bottom
   because that is where a thumb is on a phone, and because the filter rows own the top of
   the screen — a tab strip above them competed for the same glance. It is fixed rather
   than sticky: the history scrolls for a long time, and the way to analytics should not
   be a scroll away. */
.tabbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px calc(6px + env(safe-area-inset-bottom));
  background: var(--bg);
  box-shadow: inset 0 0.5px 0 rgba(128, 128, 128, 0.35);
}
.tab {
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  border: 0;
  border-radius: 10px;
  padding: 6px 4px;
  font: inherit;
  font-size: 11px;
  font-weight: 600;
  color: var(--hint);
  background: none;
  cursor: pointer;
}
.tab-icon { font-size: 19px; line-height: 1; }
/* The active tab is coloured, not filled: a filled pill at this size reads as a button
   that does something rather than as where you already are. */
.tab[aria-pressed="true"] { color: var(--link); }

/* The one control that writes. Round and raised because it is not a third tab — it does
   not switch a view, it starts something — and every phone user already knows this shape. */
.fab {
  flex: 0 0 auto;
  width: 52px;
  height: 52px;
  margin-top: -18px;
  border: 3px solid var(--bg);
  border-radius: 50%;
  font: inherit;
  font-size: 28px;
  font-weight: 300;
  line-height: 1;
  color: var(--button-text);
  background: var(--button);
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.22);
  cursor: pointer;
}
.fab:active { transform: scale(0.94); }

/* --- the new-operation sheet ---------------------------------------------------- */
.backdrop {
  position: fixed;
  inset: 0;
  z-index: 6;
  background: rgba(0, 0, 0, 0.45);
}
.sheet {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 7;
  padding: 8px 16px calc(16px + env(safe-area-inset-bottom));
  border-radius: 16px 16px 0 0;
  background: var(--bg);
  box-shadow: 0 -4px 24px rgba(0, 0, 0, 0.25);
}
.sheet-grip {
  width: 36px;
  height: 4px;
  margin: 0 auto 12px;
  border-radius: 999px;
  background: rgba(128, 128, 128, 0.4);
}
.sheet h2 { margin: 0 0 4px; font-size: 17px; }
.sheet-hint { margin: 0 0 12px; font-size: 13px; color: var(--hint); }
.sheet textarea {
  display: block;
  width: 100%;
  padding: 11px 13px;
  border: 0;
  border-radius: 12px;
  font: inherit;
  color: var(--text);
  background: var(--card);
  resize: none;
}
.sheet textarea:focus { outline: 2px solid var(--button); }
.sheet-send {
  display: block;
  width: 100%;
  margin-top: 12px;
  padding: 13px;
  border: 0;
  border-radius: 12px;
  font: inherit;
  font-weight: 600;
  color: var(--button-text);
  background: var(--button);
  cursor: pointer;
}
.sheet-send:disabled { opacity: 0.55; cursor: default; }
.sheet-error { margin-top: 10px; font-size: 13px; color: var(--expense); }

/* --- filters ------------------------------------------------------------------ */
.filters {
  position: sticky;
  top: 0;
  z-index: 2;
  padding: 8px 0 6px;
  background: var(--bg);
}
.row {
  display: flex;
  gap: 6px;
  overflow-x: auto;
  scrollbar-width: none;
  padding-bottom: 6px;
}
.row::-webkit-scrollbar { display: none; }
.chip {
  flex: 0 0 auto;
  border: 0;
  border-radius: 999px;
  padding: 7px 13px;
  font: inherit;
  font-size: 14px;
  white-space: nowrap;
  color: var(--text);
  background: var(--card);
  cursor: pointer;
}
.chip[aria-pressed="true"] {
  background: var(--button);
  color: var(--button-text);
}

/* --- list --------------------------------------------------------------------- */
.day {
  padding: 14px 4px 6px;
  font-size: 13px;
  font-weight: 600;
  color: var(--hint);
  text-transform: lowercase;
}
.ops {
  background: var(--card);
  border-radius: 12px;
  overflow: hidden;
}
.op {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 11px 13px;
}
.op + .op { box-shadow: inset 0 0.5px 0 rgba(128, 128, 128, 0.35); }
/* An operation row opens its card, so it is a real button — reset to look exactly like
   the div it used to be, with the pressed state as the only sign it can be tapped. The
   comparison rows in analytics reuse `.op` and stay divs, which is why this is scoped. */
button.op {
  width: 100%;
  border: 0;
  font: inherit;
  text-align: left;
  color: inherit;
  background: none;
  cursor: pointer;
}
button.op:active { background: rgba(128, 128, 128, 0.14); }
.op-body { flex: 1 1 auto; min-width: 0; }
.op-title {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.op-sub {
  margin-top: 2px;
  font-size: 13px;
  color: var(--hint);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.op-amount {
  flex: 0 0 auto;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  white-space: nowrap;
}
.expense .op-amount { color: var(--expense); }
.income .op-amount { color: var(--income); }
.transfer .op-amount { color: var(--transfer); }
/* The arrival amount of a cross-currency transfer, under the amount that left. Smaller and
   muted: the departure is the figure the row is sorted and read by, the arrival is context. */
.op-arrival {
  font-size: 12px;
  font-weight: 500;
  color: var(--hint);
  text-align: right;
}

/* --- balances ----------------------------------------------------------------- */
.balances { margin-top: 22px; }
/* «Главная» opens on these blocks, so the first one needs no gap above it. */
#home > .balances:first-child { margin-top: 8px; }
.balances h2 {
  margin: 0 4px 6px;
  font-size: 13px;
  font-weight: 600;
  color: var(--hint);
  text-transform: lowercase;
}
.balance {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 13px;
}
.balance + .balance { box-shadow: inset 0 0.5px 0 rgba(128, 128, 128, 0.35); }
.balance-amount { font-variant-numeric: tabular-nums; font-weight: 600; }
/* On «Главная» a row is a button that opens its account's card, so it sheds the button
   chrome and keeps the look of a row — the chevron is what says it leads somewhere. */
.balance-open {
  align-items: center;
  width: 100%;
  border: 0;
  font: inherit;
  text-align: left;
  color: var(--text);
  background: none;
  cursor: pointer;
}
.balance-open:active { background: rgba(128, 128, 128, 0.14); }
.balance-open .balance-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.balance-open .balance-amount { flex: 0 0 auto; }
/* What the side holds in all, on the heading rather than in a row of its own — the same
   place the bot puts it. `text-transform: none` because the heading is lower-cased and a
   currency code is not: "100 400 rsd" is not what the ledger says. */
.head-sum {
  text-transform: none;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

/* --- analytics ---------------------------------------------------------------- */
/* The month stepper. Its middle button is wide and its arrows are not: the label is the
   thing being read, the arrows are two targets beside it. */
.stepper { justify-content: center; align-items: center; }
.stepper .chip { min-width: 44px; text-align: center; }
.stepper .chip.current { flex: 1 1 auto; max-width: 190px; }

/* A view is a heading you open, not a chip you switch to. Three of them stand one under
   the other and each keeps its own state, so «по месяцам» and «категории» can be read
   together — with chips that was a tap back and forth, and the answer to «на что ушло»
   lives next to the answer to «сколько», not instead of it. */
.fold { margin-top: 10px; }
.fold-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  padding: 12px 13px;
  border: 0;
  border-radius: 12px;
  font: inherit;
  font-size: 15px;
  font-weight: 600;
  text-align: left;
  color: var(--text);
  background: var(--card);
  cursor: pointer;
}
.fold-head:active { opacity: 0.7; }
/* Pointing down when open and at the section when shut — one glyph rotated, so the two
   states cannot drift apart the way two different characters can. */
.fold-mark {
  display: inline-block;
  flex: 0 0 auto;
  font-size: 13px;
  color: var(--hint);
  transition: transform 0.15s ease;
}
.fold-head[aria-expanded="false"] .fold-mark { transform: rotate(-90deg); }

.block-head {
  padding: 16px 4px 6px;
  font-size: 13px;
  font-weight: 600;
  color: var(--hint);
}
.metric { padding: 11px 13px; }
.metric + .metric { box-shadow: inset 0 0.5px 0 rgba(128, 128, 128, 0.35); }
.metric-line {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}
.metric-amount { font-variant-numeric: tabular-nums; font-weight: 600; }
/* Here the bar is real CSS rather than the block characters the bot has to use — but it
   is scaled the same way, against the largest month in its own currency block. A EUR bar
   and an RSD bar measure different money, and one shared scale would draw 40 EUR as a
   rounding error beside 84 000 RSD. */
.bar {
  height: 6px;
  margin-top: 7px;
  border-radius: 999px;
  background: rgba(128, 128, 128, 0.2);
  overflow: hidden;
}
.bar > i {
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--button);
}
.expense-bar > i { background: var(--expense); }
.income-bar > i { background: var(--income); }
.metric-foot {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  margin-top: 6px;
  font-size: 12px;
  color: var(--hint);
}
/* Up is not automatically bad — it is growth on the income side — so the colour follows
   the direction and the caller says which side it is read from. */
.up { color: var(--expense); }
.down { color: var(--income); }
.up.good { color: var(--income); }
.down.bad { color: var(--expense); }
.flat { color: var(--hint); }
.delta { font-size: 12px; font-weight: 500; text-align: right; white-space: nowrap; }
.summary {
  padding: 10px 4px 0;
  font-size: 13px;
  color: var(--hint);
  font-variant-numeric: tabular-nums;
}
/* A sign has one meaning here and it does not depend on which side is being read:
   money left over is good, a month that spent more than it earned is not. That is a
   different rule from `.up`/`.down`, which colour a *change* and flip with the kind. */
.plus { color: var(--income); }
.minus { color: var(--expense); }
/* A line of context under a block: what the kind chips do not reach, what a figure is
   measured against, what a list leaves out. Small and muted — read once, not every time. */
.section-note {
  padding: 8px 4px 0;
  font-size: 12px;
  color: var(--hint);
}

/* The summary over every section: three tiles side by side — расходы, доходы, разница —
   with the pace under them. Tiles rather than rows because the three are read together,
   and a row each would push the first section below the fold on a phone. The figure uses
   proportional digits: it stands alone, and tabular ones space «121» out at this size. */
.summary-block { margin-top: 10px; }
.tiles {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 6px;
}
.tile {
  min-width: 0;
  padding: 10px 11px;
  border-radius: 12px;
  background: var(--card);
}
.tile-label {
  font-size: 12px;
  color: var(--hint);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* 17px is the ceiling; `fitTileRow` steps it down until the widest figure of the row fits. */
.tile-value {
  margin-top: 4px;
  font-size: 17px;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* Sized off the figure, so it shrinks with it when a long number is fitted to its tile. */
.tile-approx {
  margin-right: 2px;
  font-size: 0.76em;
  font-weight: 400;
  color: var(--hint);
}
.tile-foot {
  margin-top: 2px;
  font-size: 12px;
  color: var(--hint);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The range switch inside «Динамика»: the same chips as the filters, sitting in the body
   because it changes only this section. */
.row.range { padding: 10px 0 0; }
.metric-name {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* A difference is signed, so its bar grows out of the middle rather than off the left
   edge: the side a month fell on is then the first thing seen, before the figure is read.
   One recessive centre line and nothing behind the bars, like the «Динамика» columns. */
.diverge {
  display: flex;
  align-items: stretch;
  height: 8px;
  margin-top: 7px;
}
.diverge-side { flex: 1 1 0; display: flex; }
.diverge-side.left {
  justify-content: flex-end;
  border-right: 1px solid rgba(128, 128, 128, 0.45);
}
.diverge i { display: block; height: 100%; }
.diverge i.minus-fill { background: var(--expense); border-radius: 3px 0 0 3px; }
.diverge i.plus-fill { background: var(--income); border-radius: 0 3px 3px 0; }

/* «Динамика»: a column per month with its figure written over it. Bars and one recessive
   axis, and nothing behind them — a full-height track per slot reads as a second bar at
   this size. The top padding is the room the tallest column's figure stands in. */
.chart { margin: 4px 0 2px; }
.cols {
  box-sizing: border-box;
  display: flex;
  align-items: flex-end;
  gap: 4px;
  height: 150px;
  padding: 18px 2px 0;
  border-bottom: 1px solid rgba(128, 128, 128, 0.3);
}
.col {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  height: 100%;
}
.col > i {
  display: block;
  flex: none;
  width: 100%;
  border-radius: 4px 4px 0 0;
  background: var(--button);
}
.chart.expense-bar .col > i { background: var(--expense); }
.chart.income-bar .col > i { background: var(--income); }
/* The month still running: paler, so its short column reads as unfinished. */
.col.partial > i { opacity: 0.45; }
/* Centred even when it overflows its column, so a figure a little wider than its bar
   spills evenly to both sides instead of off to the right. */
.col-value {
  display: flex;
  justify-content: center;
  padding-bottom: 3px;
  font-size: 10px;
  line-height: 12px;
  color: var(--text);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.col-labels {
  display: flex;
  gap: 4px;
  padding: 5px 2px 0;
  font-size: 10px;
  color: var(--hint);
}
.col-labels > span { flex: 1 1 0; min-width: 0; text-align: center; }
/* A month this money has no row for is dimmed rather than left looking like a month in
   which nothing happened to be spent — a real zero keeps its full label and its «0». */
.col-labels > span.out { opacity: 0.35; }
.col-years { padding-top: 1px; font-size: 9px; opacity: 0.8; }

/* The donut and its legend. The chart answers «на что» at a glance; the legend under it
   is the same numbers as a table, which is what makes the shares readable for someone who
   cannot separate two of the hues — and on a phone, where a slice is too small to label. */
.pie-card {
  display: flex;
  justify-content: center;
  padding: 14px 0 10px;
}
.pie { width: 208px; height: 208px; }
.pie-total { font-weight: 600; fill: var(--text); }
.pie-currency { font-size: 11px; fill: var(--hint); }
/* A row is a button: it opens the operations behind its share. Reset to look exactly like
   the text row it used to be — the chevron and the pressed state are the only things that
   say it can be tapped, which is as much as a list this dense can spare. */
.slice {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 10px 13px;
  border: 0;
  font: inherit;
  text-align: left;
  color: var(--text);
  background: none;
  cursor: pointer;
}
.slice:active { background: rgba(128, 128, 128, 0.14); }
.slice + .slice { box-shadow: inset 0 0.5px 0 rgba(128, 128, 128, 0.35); }
.slice-go { flex: 0 0 auto; color: var(--hint); }
.dot {
  flex: 0 0 auto;
  width: 10px;
  height: 10px;
  border-radius: 3px;
}
/* Two lines a side: the name over its share and what it cost before, the amount over its
   change. The comparison that used to be a section of its own is this second line. */
.slice-body {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.slice-name {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.slice-sub {
  margin-top: 2px;
  font-size: 12px;
  color: var(--hint);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.slice-right {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
}
.slice-amount {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* The operations behind one slice. A sheet rather than a jump to the history tab: the
   question is asked from the chart and is answered without leaving it, and the same list
   on another screen would arrive filtered by controls that do not show why. */
.detail {
  display: flex;
  flex-direction: column;
  max-height: 84vh;
}
.detail h2 { margin: 0 0 2px; }
.detail-scroll {
  margin-top: 6px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

/* --- one operation: its card, and the editor over it ---------------------------- */
/* The card is the Mini App's answer to the bot's operation card, and the fields are the
   bot's «✏️ Изменить» menu folded into it: in a message the values and the buttons that
   change them have to be two screens, because a Telegram message cannot make a line
   tappable. Here a row is both, so the extra step buys nothing. */
.card-scroll {
  margin-top: 6px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}
.field {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 11px 13px;
  border: 0;
  font: inherit;
  text-align: left;
  color: var(--text);
  background: none;
  cursor: pointer;
}
.field + .field { box-shadow: inset 0 0.5px 0 rgba(128, 128, 128, 0.35); }
.field:active { background: rgba(128, 128, 128, 0.14); }
.field-label { flex: 0 0 auto; color: var(--hint); }
.field-value {
  flex: 1 1 auto;
  min-width: 0;
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.field-go { flex: 0 0 auto; color: var(--hint); }

.card-note {
  padding: 8px 4px 0;
  font-size: 12px;
  color: var(--hint);
}
.card-actions { padding-top: 14px; }
/* Deleting is the one thing here that cannot be undone, so it is the one control painted
   in the warning colour — and it asks first, exactly as the bot does. */
.danger {
  display: block;
  width: 100%;
  padding: 13px;
  border: 0;
  border-radius: 12px;
  font: inherit;
  font-weight: 600;
  color: var(--expense);
  background: var(--card);
  cursor: pointer;
}
.danger.filled { color: var(--button-text); background: var(--expense); }
.danger:disabled { opacity: 0.55; cursor: default; }
.confirm-text {
  padding: 0 4px 10px;
  font-size: 13px;
  color: var(--hint);
}
.confirm-row { display: flex; gap: 8px; }
/* The account card's buttons: two flags and a delete, one under the other. */
.action-stack { display: flex; flex-direction: column; gap: 8px; }
.confirm-row > button { flex: 1 1 0; }
.plain {
  display: block;
  width: 100%;
  padding: 13px;
  border: 0;
  border-radius: 12px;
  font: inherit;
  font-weight: 600;
  color: var(--text);
  background: var(--card);
  cursor: pointer;
}

/* One field, one screen — the same shape as the «+» sheet, because it is the same act:
   type or pick a value and send it. A picked value saves on the tap; a typed one needs
   its own button, since there is no moment a keyboard says "done". */
.field-input {
  display: block;
  width: 100%;
  padding: 11px 13px;
  border: 0;
  border-radius: 12px;
  font: inherit;
  color: var(--text);
  background: var(--card);
}
.field-input:focus { outline: 2px solid var(--button); }
.pick-list { max-height: 46vh; overflow-y: auto; }
.pick {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 12px 13px;
  border: 0;
  font: inherit;
  text-align: left;
  color: var(--text);
  background: none;
  cursor: pointer;
}
.pick + .pick { box-shadow: inset 0 0.5px 0 rgba(128, 128, 128, 0.35); }
.pick:active { background: rgba(128, 128, 128, 0.14); }
.pick-name {
  flex: 1 1 auto;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.pick-mark { flex: 0 0 auto; color: var(--link); }
.sheet-actions { display: flex; gap: 8px; margin-top: 12px; }
.sheet-actions > button { flex: 1 1 0; }
.day-chips { display: flex; gap: 6px; margin-top: 10px; }

/* «Новый счёт»: one form rather than one field, so each input carries a label over it. */
.form-label {
  display: block;
  margin: 14px 2px 6px;
  font-size: 13px;
  font-weight: 600;
  color: var(--hint);
}
.card-scroll > .form-label:first-child { margin-top: 4px; }
.form-optional { font-weight: 400; }
.row.wrap { flex-wrap: wrap; overflow-x: visible; padding-bottom: 0; }
.other-currency { margin-top: 10px; }

/* --- misc --------------------------------------------------------------------- */
.more {
  display: block;
  width: 100%;
  margin-top: 14px;
  padding: 12px;
  border: 0;
  border-radius: 12px;
  font: inherit;
  color: var(--link);
  background: var(--card);
  cursor: pointer;
}
/* «🏷 Категории» sits right under «➕ Новый счёт»: two of the same kind, so less air. */
#open-categories { margin-top: 8px; }
/* The list of categories under its filter chips, and the muted synonyms on each row. */
#cat-kinds { padding: 4px 0 2px; }
#cat-list { margin-bottom: 4px; }
.balance-name small {
  display: block;
  margin-top: 2px;
  font-size: 12px;
  color: var(--hint);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.more.subtle {
  margin-top: 18px;
  background: none;
  color: var(--hint);
  font-size: 14px;
}
/* Where the home-screen offer explains itself when it cannot be made. Muted and small:
   it is an answer to "почему нет кнопки", not something to read every session. */
.hint {
  margin-top: 12px;
  font-size: 12px;
  line-height: 1.4;
  color: var(--hint);
  text-align: center;
}
.hint a { color: inherit; }
.notice {
  padding: 26px 12px;
  text-align: center;
  color: var(--hint);
}
.notice.error { color: var(--expense); }
.hidden { display: none; }
.total {
  padding: 2px 4px 0;
  font-size: 13px;
  color: var(--hint);
}

/* --- drafts ------------------------------------------------------------------------ */
/* A draft row is the history's row with a 🗑 beside it: the row opens the card, the bin
   takes the operation out — two targets, because they are two different answers. */
.draft-row { display: flex; align-items: stretch; }
.draft-row + .draft-row { box-shadow: inset 0 0.5px 0 rgba(128, 128, 128, 0.35); }
.draft-row > .op { flex: 1 1 auto; min-width: 0; }
.draft-drop {
  flex: 0 0 auto;
  width: 48px;
  border: 0;
  font: inherit;
  font-size: 17px;
  background: transparent;
  cursor: pointer;
}
.draft-drop:disabled { opacity: 0.45; cursor: default; }
/* An item that will not be saved — not understood, or no amount — is drawn, never hidden,
   and dimmed with the reason under it, as the chat's card marks it ⚠️. */
.draft-row.skip .op { opacity: 0.6; }
.draft-warning { color: var(--expense); }
.draft-actions { display: flex; gap: 8px; }
.draft-actions > button { flex: 1 1 0; margin-top: 0; }
.draft-actions > .sheet-send { flex: 1.6 1 0; }

.toast {
  position: fixed;
  left: 16px;
  right: 16px;
  bottom: calc(84px + env(safe-area-inset-bottom));
  z-index: 8;
  padding: 12px 14px;
  border-radius: 12px;
  font-size: 14px;
  text-align: center;
  color: var(--bg);
  background: var(--text);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
}


/* The phrase and the notes under a card title, one per line. */
#card-sub { white-space: pre-line; }
