/* ============================================================================
   Finance Mini App — shadcn/ui neutral light
   Port of the shadcn design language (github.com/shadcn-ui/ui, neutral theme)
   onto this codebase's semantic variables. See skill shadcn-design-language:

     · Surfaces: #ffffff canvas → #ffffff cards → #f5f5f5 muted panels
     · Text:     #171717 headings (weight 600) / #404040 body / #737373 muted
     · Accent:   #2563eb blue — progress, chips, icons, links, focus rings
     · Primary:  filled near-black button with a soft shadow (shadcn default)
     · Radii:    one --radius ladder: 6 / 8 / 12px (shadcn 0.6x/0.8x/1.4x)
     · Elevation: one soft layer — cards shadow-sm, dialogs/popovers deeper
     · Numbers:  tabular-nums everywhere money is shown
     · Tap targets: 44px minimum on every interactive control (host rule)
   ========================================================================== */

:root{
  /* The app is a light theme. Declaring this in CSS (the HTML meta alone is not
     enough inside the Telegram webview) stops the webview from restyling form
     controls for dark mode, which is what turned the text caret white. */
  color-scheme: light;
  /* shadcn/ui neutral light theme, ported onto the app's variable names.
     Sources: apps/v4 registry theme-neutral + globals.css (see skill
     shadcn-design-language). Surfaces are clean white/gray, not cream. */
  --void:#fafafa;      /* page canvas — neutral-50; cards pop on it */
  --carbon:#ffffff;    /* cards — card */
  --obsidian:#f5f5f5;  /* muted panels — hsl(0 0% 96.1%) */
  --slate:#ececec;     /* secondary tint / tracks — hsl(0 0% 92.5%) */

  /* hairlines */
  --graphite:#e5e5e5;  /* border — hsl(0 0% 89.8%) */
  --smoke:#d4d4d4;     /* stronger border on hover */

  /* text ramp */
  --paper:#171717;     /* headings — hsl(0 0% 9%) */
  --bone:#171717;      /* primary control fill */
  --mist:#404040;      /* body text — hsl(0 0% 25%) */
  --fog:#737373;       /* tertiary text — hsl(0 0% 45.1%), 4.6:1 on white */
  --ash:#737373;       /* muted metadata — 4.6:1 on white */

  /* one accent: shadcn chart blue, used sparingly */
  --lime:#2563eb;      /* primary accent — blue */
  --pulse:#15803d;     /* income / positive — green-700, 5:1 on white */
  --coral:#dc2626;     /* errors / overspend — destructive */
  --teal:#0d9488;
  --iris:#4f46e5;
  --lavender:#7c3aed;

  /* radius ladder from one base (shadcn --radius: 10px, adapted for mobile) */
  --r-small:6px;
  --r-badge:6px;
  --r-input:8px;
  --r-card:12px;
  --r-pill:999px;

  --gap:8px;
  --gap-md:12px;
  --pad-card:20px;
  --gap-section:96px;

  /* shadcn elevation: soft single-layer shadows */
  --sh-cta:0 1px 2px rgba(0,0,0,.05);
  --sh-subtle:0 1px 2px rgba(0,0,0,.04);
  --sh-pop:0 8px 24px rgba(0,0,0,.12);

  /* Motion tokens (Emil Kowalski's values — skill emil-motion-craft) */
  --ease:cubic-bezier(.32,.72,0,1);           /* iOS drawer curve — sheets/dialogs */
  --ease-out:cubic-bezier(0.23, 1, 0.32, 1);  /* strong ease-out: entries, press */
  --ease-in-out:cubic-bezier(0.77, 0, 0.175, 1); /* on-screen movement */
  --quick:.16s;

  --tap:44px;
  --safe-top:env(safe-area-inset-top,0px);
  --safe-bottom:env(safe-area-inset-bottom,0px);
}

*{box-sizing:border-box}
[hidden]{display:none!important}
::selection{background:#0007cb;color:#fff}

html{
  background:var(--void);color:var(--mist);
  font-family:Inter,"Inter Variable",system-ui,-apple-system,"Segoe UI",sans-serif;
  font-feature-settings:"cv01" on,"ss03" on,"zero" on;
  -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
  -webkit-tap-highlight-color:transparent;
}
body{margin:0;min-height:100vh;font-size:16px;font-weight:400;line-height:1.5}
/* Kill the iOS double-tap zoom delay: this is a tap-first app (Web Interface
   Guidelines, Touch & Interaction). Pinch zoom stays available. */
html{touch-action:manipulation}
/* Long-press on a control must not select its label or pop the callout
   (mobile-native: native controls never do that; content stays selectable). */
button,[role="button"],.chip,.tabbar button,.fab{
  -webkit-user-select:none;user-select:none;
  -webkit-touch-callout:none;
}
/* The app owns its scroll containers (dialogs): no page-level pull-to-refresh. */
html,body{overscroll-behavior-y:none}
/* Modal surfaces: rubber-band must not scroll the page behind the dialog. */
dialog.finance-dialog{overscroll-behavior:contain}
/* The tour card keeps its own placement rule further down (pinned to the bottom of the
   screen); scroll containment for it is handled by its scrim below. */
.tour-scrim{touch-action:none}
h1,h2{text-wrap:balance}


/* type scale: shadcn anatomy — semibold titles, tight tracking */
h1{font-size:32px;font-weight:600;line-height:1.2;letter-spacing:-.03em;color:var(--paper);margin:0}
h2{font-size:22px;font-weight:600;line-height:1.3;letter-spacing:-.02em;color:var(--paper);margin:0}
h3{font-size:16px;font-weight:600;line-height:1.4;letter-spacing:-.014em;color:var(--paper);margin:0}
small{font-size:13px;line-height:1.45}

/* ---- buttons: neutral by default; lime is reserved for one action ---- */
button{
  min-height:var(--tap);padding:10px 16px;
  border:1px solid var(--graphite);border-radius:var(--r-input);
  background:var(--carbon);color:var(--paper);
  font:500 14px/1.4 inherit;font-feature-settings:inherit;cursor:pointer;
  box-shadow:var(--sh-subtle);
  transition:background var(--quick) ease,border-color var(--quick) ease,color var(--quick) ease,transform 140ms var(--ease-out);
}
/* The invisible full-tile overlay button must stay truly invisible. */
.account-tile .tile-open{box-shadow:none}
button:active{background:var(--slate);box-shadow:none}
/* Press feedback on visible in-flow buttons: confirms every tap. Fixed-position
   controls (.fab/.tabbar) and the invisible .tile-open overlay are excluded —
   transform on fixed/overlay elements was part of the iOS tap-eating bug. */
.dialog-actions button:active,
.button-row button:active,
.account-actions button:active,
.transaction-actions button:active,
.settings-row button:active,
.dialog-extra>button:active,
.empty-actions button:active,
.goal-actions button:active{
  transform:scale(.97);
  transition-duration:120ms;
}
.tabbar button,.fab{transition:none}
button:disabled{opacity:.5;cursor:not-allowed;box-shadow:none}
button:focus-visible{outline:2px solid var(--lime);outline-offset:2px}

.primary{
  background:var(--bone);color:#fafafa;border-color:transparent;
  font-size:14px;font-weight:500;letter-spacing:-.01em;padding:12px 16px;
  box-shadow:var(--sh-cta);
}
@media (hover: hover) and (pointer: fine){
  .primary:hover{background:#000000e6;color:#fff}}
.danger{background:transparent;border-color:var(--graphite);color:var(--coral);box-shadow:none}
@media (hover: hover) and (pointer: fine){
  .danger:hover{background:#fef2f2;border-color:var(--coral);color:var(--coral)}}
.text-button{
  min-height:auto;padding:0;border:0;background:transparent;color:var(--mist);
  font-size:14px;font-weight:400;
}
@media (hover: hover) and (pointer: fine){
  .text-button:hover{background:transparent;color:var(--paper);text-decoration:underline}}
a{color:var(--mist);text-decoration:none}
@media (hover: hover) and (pointer: fine){
  a:hover{color:var(--paper);text-decoration:underline}}

/* ------------------------------- header --------------------------------- */
.app-header{
  position:sticky;z-index:30;top:0;
  display:grid;grid-template-columns:var(--tap) minmax(0,1fr) var(--tap);
  align-items:center;gap:var(--gap-md);
  padding:calc(16px + var(--safe-top)) 16px 12px;
  background:color-mix(in srgb,var(--void) 92%,transparent);
  -webkit-backdrop-filter:saturate(180%) blur(16px);
  backdrop-filter:saturate(180%) blur(16px);
  border-bottom:1px solid transparent;
}
/* Появление хайрлайна только когда контент уехал под хедер — задаёт глубину без
   постоянной серой полосы. */
@supports (backdrop-filter: blur(1px)){
  .app-header.scrolled{border-bottom-color:var(--graphite)}
}
/* Each slot is pinned to its own column. Without this, `hidden` on the back
   button removes it from the grid, the title slides into the 44px column and
   the refresh glyph collides with the heading. */
#backButton{grid-column:1}
.title-stack{grid-column:2}
#refreshButton{grid-column:3;justify-self:end}
/* On the home screen there is no back button, so its 44px column only pushed
   the title away from the left edge. Drop the column entirely in that state. */
.app-header.no-back{grid-template-columns:minmax(0,1fr) var(--tap)}
.app-header.no-back .title-stack{grid-column:1}
.app-header.no-back #refreshButton{grid-column:2}
.icon-button{
  width:var(--tap);height:var(--tap);padding:0;border-radius:var(--r-input);
  display:grid;place-items:center;font-size:16px;color:var(--fog);background:transparent;border:1px solid transparent;
}
@media (hover: hover) and (pointer: fine){
  .icon-button:hover{color:var(--paper)}}
.title-stack{min-width:0}
.title-stack .sub{margin:4px 0 0;color:var(--fog);font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.eyebrow{
  margin:0 0 6px;color:var(--fog);font-size:11px;font-weight:500;
  font-family:ui-monospace,"JetBrains Mono","IBM Plex Mono",SFMono-Regular,monospace;
  letter-spacing:.1em;text-transform:uppercase;
}

main{width:min(100%,680px);margin:auto;padding:32px 24px calc(120px + var(--safe-bottom))}

.state{display:grid;place-items:center;gap:var(--gap-md);min-height:40vh;color:var(--fog);font-size:15px;letter-spacing:-.011em}
.state.error{color:var(--coral)}
.spinner{
  width:20px;height:20px;border:1.5px solid var(--graphite);
  border-top-color:var(--mist);border-radius:50%;animation:spin .8s linear infinite;
}
@keyframes spin{to{transform:rotate(360deg)}}

/* --------------------- pills: book switch + tabs ------------------------ */
/* Three equal scopes: Личное / Бизнес / Семья. Explicit columns keep them the same
   width instead of sizing to their label length. */
/* Вертикальные строки-входы: влезают живые цифры обеих книг (стартовый экран = вся картина). */
.book-switch{display:grid;grid-template-columns:1fr;gap:var(--gap);margin-bottom:24px}
/* These are entry points into two sections, not a toggle. Styling one of them
   `active` made it read as "already selected" while the other looked disabled.
   Both are now equal, obviously tappable cards with an affordance arrow. */
.book-switch button{border-radius:var(--r-input);padding:16px;min-height:56px;
  display:flex;align-items:center;justify-content:space-between;gap:8px;
  background:var(--carbon);border:1px solid var(--graphite);color:var(--paper);font-weight:400;
}
.book-entry{text-align:left}
/* Живые цифры на входе в книгу: −расход +приход, тихий мета-уровень. */
.book-fig{display:flex;flex-direction:column;align-items:flex-end;gap:2px;margin-left:auto;margin-right:8px}
.book-fig b{font-size:13px;font-weight:600;letter-spacing:-.01em;color:var(--mist);font-variant-numeric:tabular-nums}
.book-fig b.in{color:var(--pulse);font-weight:500}
.book-switch button span{color:var(--lime);font-size:16px}
@media (hover: hover) and (pointer: fine){
  .book-switch button:hover{border-color:var(--paper);color:var(--paper)}
}
.book-switch button{transition:transform 140ms var(--ease-out)}
.book-switch button:active{transform:scale(.985)}
.book-switch .active{background:var(--bone);color:#fafafa;border-color:transparent;font-weight:500}
@media (hover: hover) and (pointer: fine){
  .book-switch .active:hover{background:#000000d9;color:#fff}}

.section-tabs,.settings-nav,.tabs{
  display:flex;gap:24px;overflow-x:auto;padding:0 0 0;
  border-bottom:1px solid var(--graphite);
  scrollbar-width:none;-webkit-overflow-scrolling:touch;
}
.section-tabs::-webkit-scrollbar,.settings-nav::-webkit-scrollbar,.tabs::-webkit-scrollbar{display:none}
.section-tabs button,.tabs button,.settings-nav a{
  flex:0 0 auto;min-height:44px;padding:8px 2px;margin-right:0;border-radius:0;
  background:transparent;border:0;border-bottom:2px solid transparent;
  color:var(--ash);font-size:14px;white-space:nowrap;
  display:inline-flex;align-items:center;
}
@media (hover: hover) and (pointer: fine){
  .section-tabs button:hover,.tabs button:hover,.settings-nav a:hover{color:var(--paper);background:transparent;text-decoration:none}}
.section-tabs button.active,.tabs button.active{background:transparent;border-bottom-color:var(--paper);color:var(--paper);font-weight:400}
.period-filter{padding-bottom:var(--gap-md);border-bottom:0}
.period-filter .pf-btn{min-height:44px;padding:8px 16px;font-size:14px}

/* ---------------- cards: hairline border + one soft shadow ---------------- */
/* shadcn card anatomy: rounded-xl border bg-card text-card-foreground shadow.
   White cards are for OBJECTS (accounts, budgets, tasks). Sections themselves are
   open blocks on the white canvas: no border, no box — whitespace does the work. */
.attention-band,.account-tile,.budget-row,
.settings-section,.business-columns>div,.forecast-week,.stat,.task,.experiment-card,
.protocol-panel,.upload-card,.suggestions,.preview-box,.review-row,.chart-series>div,
.forecast-item,.work-links button,.summary-strip>div{
  background:var(--carbon);border:1px solid var(--graphite);border-radius:var(--r-card);
  box-shadow:var(--sh-subtle);
}
.panel-section,.dashboard-grid>article{
  background:transparent;border:0;border-radius:0;
}
.attention-band{
  display:flex;justify-content:space-between;align-items:center;gap:var(--gap-md);
  flex-wrap:wrap;padding:16px;margin-bottom:32px;
  background:var(--carbon);border:1px solid var(--graphite);border-radius:var(--r-card);
  box-shadow:var(--sh-subtle);
}
.attention-band>span,.attention-band>div{min-width:0;flex:1 1 auto;overflow-wrap:anywhere}
@media (hover: hover) and (pointer: fine){
  .attention-band:hover{border-color:var(--smoke)}}
/* Состояние «всё разобрано»: подтверждение, а не полоса с действием. */
.attention-band.calm{cursor:default;background:transparent;border:1px dashed var(--graphite)}
@media (hover: hover) and (pointer: fine){
  .attention-band.calm:hover{border-color:var(--graphite)}}
.attention-band.calm strong{color:var(--pulse)}
.attention-band small,.project-row small,.tx-main small,.goal-row small,.debt-row small,
.settings-row small,.occurrence-row small,.review-row small,.work-links small,.commitment small,
.budget-top small,.suggestions label small{
  display:block;color:var(--fog);margin-top:4px;font-size:13px;
}

.kpi-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--gap-md);margin-top:16px;margin-bottom:16px}
.kpi{padding:var(--pad-card);background:var(--carbon);border:1px solid var(--graphite);border-radius:var(--r-card);box-shadow:var(--sh-subtle)}
.kpi span{display:block;color:var(--fog);font-size:12px;
  font-family:ui-monospace,"JetBrains Mono","IBM Plex Mono",SFMono-Regular,monospace;
  letter-spacing:.08em;text-transform:uppercase}
.kpi strong{
  display:block;margin:10px 0 4px;color:var(--paper);
  font-size:32px;font-weight:600;line-height:1.2;letter-spacing:-.03em;
  font-variant-numeric:tabular-nums;}
.kpi small{color:var(--ash);font-size:12px}
.primary-kpi{background:var(--slate)}

.section-head{display:flex;align-items:flex-end;justify-content:space-between;gap:var(--gap-md);margin:56px 0 16px}
.section-head.compact{margin:0 0 var(--gap-md)}
.panel-section h3{margin:32px 0 var(--gap-md);font-size:12px;font-weight:500;
  font-family:ui-monospace,"JetBrains Mono","IBM Plex Mono",SFMono-Regular,monospace;
  letter-spacing:.1em;text-transform:uppercase;color:var(--fog)}
.summary-strip{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--gap-md);margin-bottom:24px}
.summary-strip>div{padding:var(--gap-md) 16px;background:var(--carbon);border:1px solid var(--graphite);border-radius:var(--r-card);box-shadow:var(--sh-subtle)}
.summary-strip span{display:block;color:var(--fog);font-size:12px}
.summary-strip b{
  display:block;margin-top:var(--gap);color:var(--paper);
  font-size:20px;font-weight:400;letter-spacing:-.012em;font-variant-numeric:tabular-nums;
}

.dashboard-grid{display:grid;grid-template-columns:1fr;gap:64px}
.dashboard-grid>article{padding:0;align-self:start}
.dashboard-grid>.wide{grid-column:auto}
.panel-section{padding:0;margin-bottom:64px}

/* --------------------------- transactions ------------------------------- */
.transaction-actions{display:flex;gap:var(--gap);margin-bottom:24px}
.transaction-actions button{flex:1}
.transaction-list{display:grid}
.transaction{
  display:grid;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:var(--gap-md);
  padding:16px 0;cursor:pointer;
}
/* Hairline между строками, не после последней — «таблица» уходит, остаётся список. */
.transaction:not(:last-child){border-bottom:1px solid var(--graphite)}
@media (hover: hover) and (pointer: fine){
  .transaction:hover{background:var(--obsidian);border-radius:var(--r-small)}}
.transaction:last-child{border-bottom:0}
.tx-mark{
  display:grid;place-items:center;width:36px;height:36px;border-radius:999px;
  background:var(--obsidian);color:var(--mist);font-size:13px;
}
.tx-mark.in{color:var(--pulse);background:color-mix(in srgb,var(--pulse) 10%,transparent)}
.tx-main{min-width:0}
.tx-main b{display:block;color:var(--paper);font-weight:500;font-size:16px;letter-spacing:-.011em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tx-amount{
  white-space:nowrap;color:var(--paper);font-size:16px;font-weight:600;
  letter-spacing:-.02em;font-variant-numeric:tabular-nums;}
.tx-amount.in{color:var(--pulse)}
.row-actions{display:flex;flex-wrap:wrap;justify-content:flex-end;flex:0 0 auto;gap:var(--gap);align-items:center}

/* ------------------------------ accounts -------------------------------- */
.account-grid-inner,.account-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--gap-md)}
.account-tile{padding:var(--pad-card)}
@media (hover: hover) and (pointer: fine){
  .account-tile:hover{border-color:var(--smoke)}}
.account-tile>div{display:flex;align-items:center;gap:var(--gap)}
.account-icon{
  display:grid;place-items:center;width:28px;height:28px;border-radius:8px;
  background:var(--obsidian);color:var(--mist);font-size:12px;
}
/* Тонировка по типу счёта — тихие 12% tint-ы вместо серого «всё одинаково». */
.account-icon.cash{background:color-mix(in srgb,var(--pulse) 12%,transparent);color:var(--pulse)}
.account-icon.card{background:color-mix(in srgb,var(--lime) 12%,transparent);color:var(--lime)}
.account-icon.savings{background:color-mix(in srgb,var(--iris) 12%,transparent);color:var(--iris)}
.account-icon.crypto{background:color-mix(in srgb,var(--lavender) 12%,transparent);color:var(--lavender)}
.account-tile strong{
  display:block;margin:10px 0 4px;color:var(--paper);
  font-size:24px;font-weight:600;letter-spacing:-.025em;font-variant-numeric:tabular-nums;}
.account-tile p{margin:0;color:var(--ash);font-size:12px}
.account-actions,.button-row,.dialog-actions,.report-controls,.coldbrew-actions,
.plan-actions,.forecast-picker{display:flex;flex-wrap:wrap;gap:var(--gap);margin-top:var(--gap-md)}
/* Внутри .section-head отступ сверху не нужен: он опускал пару кнопок относительно заголовка.
   Заголовок при этом не переносится, поэтому на узком экране кнопки жались друг к другу —
   разрешаем перенос и запрещаем сжатие самих кнопок. */
.section-head>.button-row{margin-top:0;justify-content:flex-end;flex:0 0 auto}
.section-head>.button-row>button{flex:0 0 auto}
.section-head{flex-wrap:wrap}

/* ------------------------------- budget --------------------------------- */
.budget-list,.goal-list,.task-list,.experiment-list,.chart-series,.work-links,
.settings-list,.budget-preview{display:grid;gap:var(--gap-md)}
.budget-row{padding:var(--pad-card);cursor:pointer}
@media (hover: hover) and (pointer: fine){
  .budget-row:hover{border-color:var(--smoke)}}
.budget-top{display:flex;justify-content:space-between;gap:var(--gap-md);align-items:flex-start}
.budget-top b{color:var(--paper);font-weight:400;font-size:15px;letter-spacing:-.011em}
.budget-top strong{font-size:15px;font-weight:400;font-variant-numeric:tabular-nums;color:var(--mist)}
.budget-top .ok{color:var(--fog)}
.budget-top .warn{color:var(--lime)}
.budget-top .over{color:var(--coral)}
.budget-row p{margin:var(--gap-md) 0 0;color:var(--ash);font-size:12px}
.progress{height:6px;margin-top:var(--gap-md);background:var(--slate);border-radius:var(--r-pill);overflow:hidden}
.progress i{display:block;height:100%;background:var(--lime);transition:width .35s var(--ease-out)}
.progress i.warn{background:var(--lime)}
.progress i.over{background:var(--coral)}

/* ------------------------------- forms ---------------------------------- */
input,select,textarea{
  width:100%;min-height:var(--tap);margin-top:var(--gap);padding:12px 14px;
  border:1px solid var(--graphite);border-radius:var(--r-input);
  background:#fff;color:var(--paper);
  /* 16px is the threshold: iOS zooms the whole page when a focused field is
     smaller, which is what made the screen jump while typing an invite. Do NOT
     "fix" that with maximum-scale=1 — it kills pinch zoom for everyone. */
  /* NOT the `font:` shorthand: a shorthand component may not be `inherit`, so
     `font:400 16px/1.4 inherit` was invalid and dropped wholesale. Fields were
     therefore inheriting label's 12px, which is why iOS zoomed. Separate
     longhands are required for the size to apply at all. */
  font-family:inherit;font-size:16px;font-weight:400;line-height:1.4;
  font-feature-settings:inherit;outline:none;
  /* The caret colour was never declared, so the webview picked it. With Telegram
     in dark mode that produced a white caret on a white field. Pin both the caret
     and the text fill so dark-mode form styling cannot override them. */
  caret-color:var(--paper);-webkit-text-fill-color:var(--paper);
  transition:border-color var(--quick) var(--ease);
}
input::placeholder,textarea::placeholder{color:var(--fog)}
input:focus-visible,select:focus-visible,textarea:focus-visible{
  border-color:var(--lime);
  outline:2px solid color-mix(in srgb,var(--lime) 25%,transparent);
  outline-offset:1px;
}
select{
  appearance:none;-webkit-appearance:none;padding-right:36px;
  /* flat chevron, no gradient: a 6x4 caret in warm ink */
  background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23737373' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-position:right 12px center;
  background-size:10px 6px;background-repeat:no-repeat;
}
label{display:block;margin:var(--gap-md) 0;color:var(--fog);font-size:12px}
fieldset{margin:var(--gap-md) 0;padding:var(--gap-md);border:1px solid var(--graphite);border-radius:var(--r-input);background:#fff}
legend{padding:0 var(--gap);color:var(--fog);font-size:12px}
.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--gap-md)}
.inline-form{display:grid;grid-template-columns:1fr auto;gap:var(--gap-md);align-items:end}
/* align-items:end выравнивает по MARGIN-боксу, а глобальный label{margin:var(--gap-md) 0} даёт
   подписи нижний отступ — из-за него кнопка вставала ниже своего поля. Отступы внутри формы
   задаёт форма (тот же вывод, что и для карточки импорта). */
.inline-form>label{margin:0}
.inline-form>button{align-self:end}
.statement-extra,.date-range,.week-totals{display:grid;grid-template-columns:1fr 1fr;gap:var(--gap-md)}
/* Элемент сетки по умолчанию не сжимается ниже собственной внутренней ширины, а у input[type=date]
   она задана вместе со значком календаря — поле выходило за границу колонки на узком экране.
   jsdom этого не покажет: у него нет внутренних размеров, поэтому дефект виден только на телефоне. */
.statement-extra>label,.date-range>label,.week-totals>label{min-width:0}
.statement-extra input,.date-range input,.week-totals input{min-width:0;max-width:100%}
/* Значок календаря в WebKit добавляет собственную ширину поверх padding. */
input[type=date]{appearance:none;-webkit-appearance:none;min-width:0;max-width:100%}
input[type=date]::-webkit-calendar-picker-indicator{margin-inline-start:0;padding:0}
.currency-label{margin-top:var(--gap-md)}
.empty{
  padding:var(--pad-card);text-align:center;color:var(--fog);font-size:13px;
  background:var(--obsidian);border:1px dashed var(--graphite);border-radius:var(--r-input);
}
/* An empty list that only says "nothing here" is a dead end. When it carries a way out, the
   button needs to look like one — unstyled markup reads as leftover debug text. */
.empty-actions{display:flex;justify-content:center;margin:var(--gap-md) 0 0}
.empty-actions button{min-height:44px;padding:0 20px}
.data-note{color:var(--ash);font-size:12px;margin:var(--gap) 0 var(--gap-md);line-height:1.4}
/* ---- один ритм «заголовок → описание → содержимое» на всех разделах ------ */
/* Долги, Повторы и Закрытие месяца собирают вступление из разных комбинаций .intro-note и
   .data-note, поэтому расстояние от заголовка до первой строки содержимого получалось разным.
   Отступ сверху задаёт секционный заголовок — он уже имеет margin-bottom. */
.section-head+.intro-note,.section-head+.data-note{margin-top:0}
/* Заголовок сразу после описания не добавляет ещё 24px поверх его нижнего отступа. Именно этот
   случай давал двойной отступ. Заголовки после содержимого («Мне должны») сохраняют широкий
   отступ — :first-of-type для этого не годится: если список пуст, первым становится второй
   заголовок, и он терял отбивку. */
.intro-note+h3,.data-note+h3,.section-head+h3{margin-top:0}
.receipt-warning{
  margin:var(--gap-md) 0;padding:var(--gap-md);
  background:#fef2f2;border:1px solid #fecaca;color:var(--coral);
}
.upload-status{margin:var(--gap-md) 0 0;font-size:12px;color:var(--fog)}

/* ------------------------------ dialogs --------------------------------- */
/* Entry: bottom sheets slide up from their edge (spatial consistency), 280ms
   drawer curve; confirm dialogs rise from center (modals exempt from origin rules).
   @starting-style = pure-CSS entry. Transform+opacity only, from a visible shape. */
.finance-dialog{
  position:fixed;z-index:100;inset:auto 0 0;width:min(100%,600px);max-height:92vh;margin:auto;
  padding:var(--pad-card) var(--pad-card) calc(var(--pad-card) + var(--safe-bottom));
  overflow:auto;border:1px solid var(--graphite);border-bottom:0;
  border-radius:16px 16px 0 0;
  background:var(--carbon);color:var(--mist);
  box-shadow:var(--sh-pop);
}
/* Entry: one-shot animation, NOT a transition. The earlier transition kept a
   compositor layer alive on the Telegram iOS webview and ate taps after the dialog
   was removed (report: «все кнопки перестали работать»). A keyframes run leaves no
   residual transition state on the fixed element — the fix, not the removal. */
@keyframes sheet-up{from{opacity:0;transform:translateY(24px) scale(.985)}}
/* Анимируется ВНУТРЕННИЙ wrapper, не сам <dialog>: fixed dialog с анимацией в
   Telegram iOS webview оставляет фантомный композитный слой, глотающий тапы
   после закрытия. Dialog теперь никогда не анимируется — класс бага устранён. */
.finance-dialog[open] .sheet-body{animation:sheet-up 300ms var(--ease)}
/* Backdrop появляется мгновенно: анимация на ::backdrop в WebKit оставляет
   фантомный слой после удаления dialog — тот самый «всё мёртвое». */
.finance-dialog::backdrop{background:rgba(0,0,0,.4)}
@keyframes scrim-in{from{opacity:0}to{opacity:1}}
.finance-dialog::backdrop{background:rgba(0,0,0,.4)}
.dialog-title{display:flex;justify-content:space-between;gap:var(--gap-md);align-items:flex-start;margin-bottom:var(--gap-md)}
.icon-close{width:44px;height:44px;min-height:44px;padding:0;border-radius:var(--r-input);font-size:16px;line-height:1}
.dialog-actions{position:sticky;bottom:0;padding:var(--gap-md) 0 0;margin-top:24px;background:var(--carbon)}
.dialog-actions>*{flex:1}
/* Destructive and secondary actions live on their own row, away from «Сохранить»:
   four buttons in a three-column grid wrapped 2+2 and put «Удалить» (danger) right next
   to the primary button. */
.dialog-extra{display:flex;gap:var(--gap);margin-top:var(--gap);padding-top:var(--gap);border-top:1px solid var(--graphite)}
.dialog-extra>button{flex:1 1 50%;min-height:44px}
.wizard-dialog{min-height:60vh;display:flex;flex-direction:column}
.wizard-dialog h2{margin:24px 0 var(--gap-md);font-size:30px;font-weight:600;line-height:1.2;letter-spacing:-.03em}
.wizard-dialog>#wizardInput{min-height:56px;font-size:24px;font-weight:400;letter-spacing:-.012em}
.wizard-dialog .dialog-actions{margin-top:auto}
.wizard-progress{display:grid;gap:var(--gap);color:var(--fog);font-size:12px}
.wizard-progress i{height:4px;background:var(--slate);border-radius:var(--r-pill);overflow:hidden}
.wizard-progress b{display:block;height:100%;background:var(--lime);transition:width .3s var(--ease-out)}
.category-grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--gap);margin-top:var(--gap-md)}
.category-choice{min-height:52px;text-align:left;font-size:14px;
  display:flex;align-items:center;gap:8px;padding:8px 12px}
.category-choice>span{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.category-choice.selected{background:var(--slate);color:var(--paper);border-color:var(--smoke);font-weight:500}
@media (hover: hover) and (pointer: fine){
  .category-choice.selected:hover{background:var(--slate);color:var(--paper)}}
.business-dialog{max-height:88vh}
.transaction-dialog .dialog-title p[hidden]{display:none}

/* ------------------------------ settings -------------------------------- */
.settings-page,.import-page,.business-plan,.business-projects{display:block}
.settings-section{padding:var(--pad-card);margin-bottom:var(--gap-md)}
.settings-heading,.settings-row,.project-row,.debt-row,.goal-row,.occurrence-row,.commitment,.task-top{
  display:flex;justify-content:space-between;align-items:center;gap:var(--gap-md);
  padding:var(--gap-md) 0;border-bottom:1px solid var(--graphite);
  /* Строка с четырьмя детьми (текст, сумма и две кнопки) не должна растаскивать кнопки по
     середине: лишнее переносится на следующую строку. */
  flex-wrap:wrap;
}
.settings-heading{padding-top:0;margin-bottom:var(--gap)}
.settings-heading p{margin:4px 0 0;color:var(--fog);font-size:12px}
.settings-row:last-child,.project-row:last-child,.goal-row:last-child,
.debt-row:last-child,.occurrence-row:last-child{border-bottom:0}
.settings-row b,.project-row b,.goal-row b,.debt-row b,.occurrence-row b,.commitment b{
  color:var(--paper);font-weight:400;font-size:15px;letter-spacing:-.011em;
}
.settings-row button,.project-row button,.goal-row button,.debt-row button,
.occurrence-row button,.review-row button{min-height:44px;padding:4px 12px;font-size:12px}
.settings-row strong,.project-row strong,.goal-row strong,.debt-row strong,
.occurrence-row strong,.commitment strong{
  font-size:15px;font-weight:400;font-variant-numeric:tabular-nums;white-space:nowrap;color:var(--mist);
}
.settings-form{padding-top:var(--gap-md)}
/* The submit button followed a full-width block label with no layout of its own,
   so it sat flush against the last field and looked misplaced. Give it its own
   row with a separating rule. */
.settings-form button.primary{
  display:block;width:100%;min-height:44px;margin-top:var(--gap-md);
}
.settings-form.inline-form button.primary{width:auto;margin-top:0}
/* Long settings lists are collapsed by default and load a page at a time. */
.settings-group{margin-top:var(--gap-md);border-top:1px solid var(--graphite)}
.settings-group summary{
  display:flex;justify-content:space-between;align-items:center;gap:var(--gap-md);
  min-height:44px;padding:var(--gap-md) 0;cursor:pointer;color:var(--mist);font-size:13px;
}
.settings-group summary::-webkit-details-marker{display:none}
.settings-group summary b{
  font-weight:400;color:var(--paper);font-variant-numeric:tabular-nums;
  background:var(--carbon);border:1px solid var(--graphite);border-radius:var(--r-badge);padding:2px 8px;font-size:12px;
}
.settings-group[open] summary span::after{content:' ▾'}
.settings-group:not([open]) summary span::after{content:' ▸'}
.settings-more{width:100%;min-height:44px;margin-top:var(--gap);font-size:13px}
.settings-nav{padding-bottom:var(--gap-md)}

/* ------------------------------ business -------------------------------- */
.business-columns{display:grid;grid-template-columns:1fr 1fr;gap:var(--gap-md)}
.business-columns>div{padding:var(--pad-card)}
.forecast-weeks{
  display:grid;grid-auto-flow:column;grid-auto-columns:minmax(200px,1fr);gap:var(--gap-md);
  overflow-x:auto;padding-bottom:var(--gap);scrollbar-width:none;
}
.forecast-weeks::-webkit-scrollbar{display:none}
.forecast-week,.forecast-item{padding:var(--gap-md) 16px}
.forecast-week strong{display:block;color:var(--paper);font-size:18px;font-weight:600;letter-spacing:-.02em;font-variant-numeric:tabular-nums}
.forecast-grid{display:grid;gap:var(--gap-md);margin-top:var(--gap-md)}
.plan-meta{display:flex;flex-wrap:wrap;gap:var(--gap-md);color:var(--fog);font-size:12px;margin:var(--gap) 0 var(--gap-md)}
.status-active{color:var(--pulse)}
.status-frozen,.status-completed{color:var(--fog)}
.status-paused{color:var(--lime)}
.allocation-line{display:grid;grid-template-columns:minmax(0,2fr) 92px minmax(0,1fr) 44px;gap:var(--gap);align-items:end;margin-bottom:var(--gap)}
.check-row{display:flex;align-items:center;gap:var(--gap-md);padding:var(--gap) 0;margin:0;color:var(--mist);font-size:14px;min-height:44px;}
.check-row input{width:16px;min-height:16px;margin:0;flex:0 0 16px}
.in{color:var(--pulse)}
.out{color:var(--mist)}

/* ------------------------------- work ----------------------------------- */
.work-heading{margin-top:48px}
.work-links button{
  display:flex;justify-content:space-between;align-items:center;text-align:left;
  padding:var(--pad-card);border-radius:var(--r-card);min-height:auto;
}
.work-links b{color:var(--paper);font-weight:400;font-size:15px;letter-spacing:-.011em}
.work-links strong{color:var(--fog);font-size:14px;font-weight:400}

pre{
  overflow:auto;padding:var(--gap-md);border-radius:var(--r-input);
  background:var(--obsidian);border:1px solid var(--graphite);color:var(--mist);
  font:400 12px/1.4 ui-monospace,"JetBrains Mono","IBM Plex Mono",SFMono-Regular,monospace;
  font-feature-settings:"cv01" on,"ss03" on;letter-spacing:-.013em;
}
.stats{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--gap-md)}
.stat{padding:var(--gap-md) 16px;background:var(--carbon);border:1px solid var(--graphite);border-radius:var(--r-card);box-shadow:var(--sh-subtle)}
.stat strong{display:block;color:var(--paper);font-size:24px;font-weight:600;letter-spacing:-.03em;font-variant-numeric:tabular-nums}
.stat span{color:var(--fog);font-size:12px}
.controls,.import-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--gap-md);margin:var(--gap-md) 0}
.task,.experiment-card{padding:var(--pad-card)}
@media (hover: hover) and (pointer: fine){
  .task:hover,.experiment-card:hover{border-color:var(--smoke)}}
.task-top{padding-top:0;border-bottom:0;align-items:flex-start}
.task h3,.experiment-card h3{margin:var(--gap) 0}
.task p,.experiment-card p{margin:var(--gap) 0 0;color:var(--fog);font-size:13px}

/* badges: shadcn badge — 6px radius, 12px/500 */
.badge{display:inline-block;padding:2px 8px;border-radius:var(--r-badge);background:var(--slate);color:var(--paper);font:500 12px/1.4 ui-monospace,"JetBrains Mono","IBM Plex Mono",SFMono-Regular,monospace;letter-spacing:.02em}
.tone-0{color:var(--pulse)}
.tone-1{color:var(--iris)}
.tone-2{color:var(--lavender)}

.upload-card{padding:var(--pad-card);display:flex;flex-direction:column;gap:var(--gap);background:var(--carbon);border:1px solid var(--graphite);box-shadow:var(--sh-subtle)}
.upload-card h3{margin:var(--gap) 0 4px}
.upload-card p{margin:0 0 var(--gap-md);color:var(--fog);font-size:13px}
.upload-icon{display:grid;place-items:center;width:32px;height:32px;border-radius:var(--r-input);background:var(--obsidian);color:var(--fog);font-size:16px}
.upload-submit{margin-top:auto}
.file-picker{
  display:block;margin:0 0 var(--gap);padding:var(--gap-md);text-align:center;cursor:pointer;
  border:1px dashed var(--graphite);border-radius:var(--r-input);
  background:#fff;color:var(--fog);font-size:13px;
  transition:border-color var(--quick) var(--ease);
}
@media (hover: hover) and (pointer: fine){
  .file-picker:hover{border-color:var(--mist);color:var(--mist)}}
.file-picker input{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none}
.file-picker span{display:block}

/* ---- Импорт: одна вертикальная сетка на карточку ------------------------- */
/* `.upload-card` — flex-колонка с gap, а глобальный `label` несёт собственный
   `margin:var(--gap-md) 0`. Поле без подписи отбивалось только gap, поле с подписью — gap плюс
   margin, поэтому внутри одной карточки шаг был разный, а соседняя карточка чека вставала иначе.
   Один источник отступа: карточка. */
.upload-card label{display:grid;gap:4px;margin:0}
.upload-card label input,.upload-card label select{margin-top:0}
/* Пунктирный выбор файла — тоже <label>, и он должен остаться блоком со своим отступом. */
.upload-card label.file-picker{display:block;margin:0 0 var(--gap)}
.field-hint{margin:0;color:var(--fog);font-size:12px}

/* Перенос личного лимита в семейный: строки как обычные карточки списка. */
.convert-list{display:block}
.convert-row{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:var(--gap);padding:var(--pad-card);margin:0 0 var(--gap);background:var(--carbon);border:1px solid var(--graphite);border-radius:var(--r-card)}
.convert-row span{min-width:0}
.convert-row b{display:block;font-size:15px}
.convert-row small{display:block;color:var(--fog);font-size:12px}
.budget-plan{padding:var(--pad-card);margin:0 0 var(--gap-md);background:var(--carbon);border:1px solid var(--graphite);border-radius:var(--r-card)}
.budget-plan>strong{display:block;margin:4px 0;color:var(--paper);font-size:28px;font-weight:400;font-variant-numeric:tabular-nums}
.budget-plan>small{display:block;color:var(--fog);font-size:12px;line-height:1.45}
.budget-plan h3{margin:var(--gap-md) 0 var(--gap);font-size:14px}
.budget-plan ul{display:block;margin:0;padding:0;list-style:none;border-top:1px solid var(--graphite)}
.budget-plan li{padding:12px 0;border-bottom:1px solid var(--graphite)}
.budget-plan li:last-child{border-bottom:0}.budget-plan li span{min-width:0}.budget-plan li small{display:block;color:var(--fog);font-size:11px}.budget-plan li b{display:block;color:var(--paper);font-size:14px;font-weight:400}.budget-plan li strong{align-self:center;color:var(--mist);font-size:14px;font-weight:400;white-space:nowrap}.budget-plan .plan-empty{display:block;color:var(--fog);font-size:13px}
.suggestions,.preview-box{padding:var(--pad-card);margin-top:var(--gap-md);background:var(--carbon);border:1px solid var(--graphite);box-shadow:var(--sh-subtle)}
.suggestions ul{margin:var(--gap-md) 0;padding:0;list-style:none;display:grid;gap:var(--gap)}
.suggestions li{
  display:flex;flex-wrap:wrap;gap:var(--gap);align-items:center;padding:var(--gap-md);
  background:#fff;border:1px solid var(--graphite);border-radius:var(--r-input);font-size:13px;
}
.suggestions li input,.suggestions li select{margin-top:0;min-height:44px;width:auto;flex:1 1 110px}
/* Категория слева, сумма справа — в ОДНОЙ строке. Сумма не ниже названия:
   это неудобно и тратит место. Поле занимает всё свободное место справа. */
.suggestions label{display:grid;grid-template-columns:20px minmax(0,1fr);gap:var(--gap-md);
  align-items:start;margin:var(--gap) 0;padding:var(--gap-md);
  background:#fff;border:1px solid var(--graphite);border-radius:var(--r-input);
  color:var(--mist);font-size:14px;min-height:44px;cursor:pointer;}
.suggest-body{min-width:0;display:block}
.suggest-body>.suggest-method{margin-top:var(--gap)}
.suggest-top{display:grid;grid-template-columns:minmax(0,1fr) minmax(128px,1.4fr);
  gap:var(--gap-md);align-items:center}
.suggest-top>b{min-width:0;overflow-wrap:anywhere}
.suggest-edit{display:block;margin:0;min-width:0}
.suggest-edit-field{display:block;position:relative;width:100%}
.suggest-edit input{
  display:block;width:100%;max-width:none;min-height:48px;margin:0;padding:0 56px 0 12px;
  font-size:20px;font-weight:500;font-variant-numeric:tabular-nums;
  color:var(--paper);background:var(--carbon);border-color:var(--paper);
}
.suggest-edit-field small{
  position:absolute;right:12px;top:50%;transform:translateY(-50%);
  color:var(--mist);font-size:14px;pointer-events:none;
}
.suggest-method{display:block;margin:0;padding:0;border:0;color:var(--fog);font-size:12px}
@media (hover: hover) and (pointer: fine){
  .suggestions label:hover{border-color:var(--smoke)}}
.suggestions label>input[type="checkbox"]{width:16px;min-height:16px;margin:2px 0 0;flex:0 0 16px}
.suggestions label b{color:var(--paper);font-weight:400}
.suggestions label small{display:block;color:var(--fog);margin-top:4px}
.suggestions button{margin-top:var(--gap-md)}
.preview-box{font-size:13px}
.preview-box b{color:var(--paper);font-weight:400;font-variant-numeric:tabular-nums}
.preview-box button{margin-top:var(--gap-md);width:100%}

.review-row{display:grid;gap:var(--gap-md);padding:var(--gap-md) 16px}
.review-row b{color:var(--paper);font-weight:400;font-size:15px;letter-spacing:-.011em}
.review-row p{margin:var(--gap) 0 0;color:var(--fog);font-size:12px}
.review-row select{margin-top:0}

.protocol-panel{padding:var(--pad-card);background:var(--carbon);border:1px solid var(--graphite);border-radius:var(--r-card);box-shadow:var(--sh-subtle)}
.protocol-panel h2{margin-bottom:var(--gap)}
.protocol-panel p{margin:0;color:var(--fog);font-size:13px}
.chart-series>div{padding:var(--gap-md) 16px;display:grid;grid-template-columns:minmax(72px,auto) minmax(0,1fr) auto;align-items:center;gap:var(--gap)}
.chart-series span{color:var(--fog);font-size:12px;min-width:0;overflow-wrap:anywhere}
.chart-series i{display:block;height:2px;background:var(--fog);min-width:0;transition:width .35s var(--ease-out)}
.chart-series i.income{background:var(--pulse)}
.chart-series b{color:var(--mist);font-size:12px;font-weight:400;font-variant-numeric:tabular-nums;white-space:nowrap;text-align:right}
.report-controls select{margin-top:0;flex:1 1 140px;width:auto}
.exchange-rows{display:grid;gap:var(--gap-md)}

/* ---------------------------- composition -------------------------------- */
.hero-kpi{padding:16px 0 28px}
/* Топ-категории месяца: строка = тап, бар = доля трат, сумма справа. */
.cat-row{display:grid;grid-template-columns:minmax(96px,140px) minmax(0,1fr) auto;align-items:center;gap:12px;width:100%;padding:10px 0;border-bottom:1px solid var(--graphite);background:none;border-left:0;border-right:0;border-top:0;text-align:left;cursor:pointer}
.cat-row:last-child{border-bottom:0}
.cat-label{display:flex;align-items:center;gap:6px;color:var(--mist);font-size:13px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cat-row i{height:8px;border-radius:4px;background:var(--lime);opacity:.85;min-width:2px}
.cat-row b{font-size:14px;font-weight:600;color:var(--paper);font-variant-numeric:tabular-nums;white-space:nowrap}
/* Книги: строки-карточки с живыми цифрами. */
.total-budget{display:flex;align-items:flex-end;gap:10px;flex-wrap:wrap;margin:14px 0 4px}
.total-budget label{display:grid;gap:4px;color:var(--mist);font-size:13px;font-weight:500;flex:1;min-width:180px}
.total-budget input{max-width:220px}
.budget-split{display:grid;gap:0;margin:10px 0 4px;border:1px solid var(--graphite);border-radius:var(--r-input);overflow:hidden}
.budget-split-row{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 14px;border-bottom:1px solid var(--graphite);background:var(--carbon)}
.budget-split-row:last-child{border-bottom:0}
.budget-split-row span{color:var(--mist);font-size:13px}
.budget-split-row b{font-size:15px;font-weight:600;color:var(--paper);font-variant-numeric:tabular-nums}
.budget-split-row b.neg{color:var(--coral)}
.period-picker{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin:12px 0 4px}
.period-picker label{display:flex;align-items:center;gap:8px;color:var(--mist);font-size:13px;font-weight:500}
.period-picker select{min-height:40px}
.period-current{color:var(--fog);font-size:13px;font-variant-numeric:tabular-nums}
.book-cards{display:grid;gap:var(--gap);margin-bottom:24px}
.book-card{display:flex;align-items:center;gap:12px;padding:14px 16px;min-height:56px;background:var(--carbon);border:1px solid var(--graphite);border-radius:var(--r-input);text-align:left;cursor:pointer;transition:transform 140ms var(--ease-out)}
.book-card:active{transform:scale(.985)}
.book-ico{display:grid;place-items:center;width:36px;height:36px;border-radius:10px;background:var(--obsidian);color:var(--mist);font-size:16px;flex:none}
.book-body{min-width:0;flex:1}
.book-body b{display:block;color:var(--paper);font-weight:600;font-size:15px;letter-spacing:-.01em}
.book-body small{display:block;color:var(--fog);font-size:12px;margin-top:1px}
.book-figs{display:flex;flex-direction:column;align-items:flex-end;gap:2px;margin-right:2px}
.book-figs b{font-size:12px;font-weight:600;color:var(--mist);font-variant-numeric:tabular-nums;white-space:nowrap}
.book-figs b.in{color:var(--pulse);font-weight:500}
.book-figs b.muted{color:var(--fog);font-weight:400}
.book-go{color:var(--fog);font-weight:400}
/* Дуо: расходы и прибыль — две главные цифры экрана рядом (Monitor surface). */
.hero-duo{display:grid;grid-template-columns:1fr 1fr;gap:20px;margin-top:14px}
.hero-cell small{display:block;margin:0 0 6px;color:var(--fog);font-size:12px;font-weight:500;letter-spacing:.06em;text-transform:uppercase}
.hero-cell strong{display:block;color:var(--paper);font-size:30px;font-weight:600;line-height:1;letter-spacing:-.03em;font-variant-numeric:tabular-nums}
.hero-cell.in strong{color:var(--pulse)}
@media (max-width:360px){.hero-cell strong{font-size:25px}}
.hero-kpi>strong{display:block;margin:14px 0 18px;color:var(--paper);font-size:44px;font-weight:600;line-height:1;letter-spacing:-.04em;font-variant-numeric:tabular-nums}
.hero-currency{color:var(--fog);font-size:22px;font-weight:500;letter-spacing:-.02em;margin-left:2px}
.hero-meta{display:flex;flex-wrap:wrap;gap:8px;color:var(--fog);font-size:13px}
.hero-meta span+span{padding-left:12px;border-left:1px solid var(--graphite)}
.hero-flag{color:var(--coral)}
.context-bar{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 0 20px;border-bottom:1px solid var(--graphite);margin-bottom:24px}
.context-now{color:var(--paper);font-size:20px;font-weight:400;letter-spacing:-.012em}
.context-figures{display:flex;align-items:center;gap:12px;color:var(--fog);font-size:12px;font-variant-numeric:tabular-nums}
.context-figures b{font-weight:400}.context-figures .in{color:var(--pulse)}.context-flag{color:var(--coral)!important}
.tx-day{margin-bottom:24px}
.tx-day-head{display:flex;justify-content:space-between;align-items:center;margin:0 0 4px;color:var(--fog);font-size:12px;font-weight:400}
.tx-day-head b{color:var(--ash);font-weight:400;font-variant-numeric:tabular-nums}
.tx-day .transaction-list{border-top:1px solid var(--graphite)}

/* --------------------------- bottom navigation -------------------------- */
.tabbar{display:flex;position:fixed;z-index:40;left:0;right:0;bottom:0;height:calc(64px + var(--safe-bottom));padding:6px 8px var(--safe-bottom);background:color-mix(in srgb,var(--void) 88%,transparent);-webkit-backdrop-filter:saturate(180%) blur(16px);backdrop-filter:saturate(180%) blur(16px);border-top:1px solid var(--graphite)}
.tabbar button{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;min-height:48px;padding:4px;border:0;border-radius:var(--r-input);background:transparent;color:var(--ash);font-size:10px;transition:transform 140ms var(--ease-out)}
@media (hover: hover) and (pointer: fine){
  .tabbar button:hover{background:var(--obsidian);color:var(--mist)}}
.tabbar button.active{color:var(--lime);background:transparent}
.tabbar button.active .nav-glyph{transform:translateY(-1px)}
/* Sheet content settles slightly after the sheet — follow-through adds weight. */
.finance-dialog .dialog-title,.finance-dialog .dialog-actions{
  animation:content-in 360ms var(--ease-out) both;
}
@keyframes content-in{from{opacity:0}to{opacity:1}}
.nav-glyph{font-size:21px;line-height:20px;font-weight:400}.nav-label{font-size:10px;line-height:14px}
.fab{display:grid;place-items:center;position:fixed;z-index:45;right:18px;bottom:calc(76px + var(--safe-bottom));width:52px;height:52px;min-height:52px;padding:0;border-radius:50%;background:var(--bone);color:#fafafa;border:0;font-size:28px;font-weight:300;line-height:1;box-shadow:var(--sh-pop)}
@media (hover: hover) and (pointer: fine){
  .fab:hover{background:var(--paper);color:#fff;border:0}}
.sheet-nav{max-height:80vh;padding-top:0}.sheet-list{display:grid;gap:8px}.sheet-list button{display:flex;justify-content:space-between;align-items:center;width:100%;text-align:left;min-height:44px}.sheet-list strong{color:var(--fog);font-weight:400}

main{padding-bottom:calc(96px + 64px + var(--safe-bottom))}
@media(max-width:720px){
  .kpi-grid{grid-template-columns:1fr}
  .dashboard-grid,.business-columns,.import-grid,.allocation-line,.controls,
  .account-grid,.account-grid-inner{grid-template-columns:1fr}
  .dashboard-grid>.wide{grid-column:auto}
  .dashboard-grid>article{padding:0;background:transparent;border:0;border-radius:0}
  .summary-strip{grid-template-columns:1fr}
  .stats{grid-template-columns:1fr 1fr}
  .section-head{margin-top:48px}
  .section-tabs,.settings-nav{margin:0 -16px;padding:0 16px var(--gap-md)}
  .kpi strong{font-size:26px}
  @media (max-width:360px){.hero-kpi>strong{font-size:38px}.hero-currency{font-size:19px}}
  .account-tile,.budget-row,.settings-section,.task,.experiment-card,
  .upload-card,.suggestions,.preview-box,.review-row,.forecast-week{padding:16px}
  .panel-section{padding:0;margin-bottom:48px}
}
@media(max-width:390px){
  .account-grid-inner,.account-grid,.form-grid,.category-grid,.statement-extra,
  .date-range,.week-totals{grid-template-columns:1fr}
  .transaction-actions{flex-direction:column}
  .stats{grid-template-columns:1fr 1fr}
  h1{font-size:26px;letter-spacing:-.02em}
  .wizard-dialog h2{font-size:22px;letter-spacing:-.02em}
}
/* Reduced motion: gentler, not zero (Kowalski). Keep opacity/color transitions
   that aid comprehension; drop the movement. Dialogs stay visible (no teleport
   mid-transition): their transform entry is dropped, opacity remains. */
@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms!important;transition-duration:.01ms!important}
  /* Keep opacity/color fades, drop the movement (gentler, not zero): */
  .finance-dialog[open] .sheet-body{animation:sheet-fade 200ms ease}
  @keyframes sheet-fade{from{opacity:0}}
  .home-section-card{transform:none}
  button:active{transform:none}
  .toast.show{transform:none}
  .toast{transform:none;transition:opacity 150ms ease}
}

/* ---------------------------- feedback layer ----------------------------- */
/* Toast: transitions, not keyframes — toasts can fire twice in a second and
   transitions retarget from the current state (Kowalski, interruptibility). */
.toast{
  position:fixed;z-index:120;left:16px;right:16px;bottom:calc(148px + var(--safe-bottom));
  margin:0 auto;max-width:520px;padding:14px 16px;border-radius:var(--r-card);
  background:var(--paper);color:#fafafa;font-size:14px;line-height:1.4;text-align:center;
  box-shadow:var(--sh-pop);
  opacity:0;transform:translateY(12px);
  transition:opacity 200ms var(--ease-out),transform 240ms var(--ease),
             visibility .2s allow-discrete,display .2s allow-discrete;
  visibility:hidden;
}
.toast.show{opacity:1;transform:none}
/* pending unattributed spend + untracked cards */
.pending-cta{
  width:100%;margin:0 0 16px;min-height:48px;padding:12px 16px;text-align:left;
  border-radius:var(--r-input);border:1px solid var(--lime);background:rgba(0,7,203,.05);
  color:var(--lime);font-size:14px;font-weight:400;
}
@media (hover: hover) and (pointer: fine){
  .pending-cta:hover{background:rgba(0,7,203,.09)}}
.untracked-note{
  margin:0 0 16px;padding:14px 16px;border-radius:var(--r-input);
  border:1px solid var(--coral);background:rgba(179,38,30,.05);
}
.untracked-note b{display:block;color:var(--coral);font-size:14px}
.untracked-note p{margin:4px 0 8px;color:var(--mist);font-size:12.5px}
.untracked-note ul{margin:0;padding-left:20px;color:var(--paper);font-size:13px}
.untracked-note li{margin:4px 0}
.link-card{
  width:100%;min-height:44px;padding:8px 12px;text-align:left;font-size:14px;
  border-radius:var(--r-input);border:1px solid transparent;background:transparent;color:var(--paper);
}
@media (hover: hover) and (pointer: fine){
  .link-card:hover{border-color:var(--coral);background:rgba(179,38,30,.07);color:var(--coral)}}
/* One card per row: name, source, amount, and a button that says what it does.
   A bare list of clickable text did not read as actionable. */
/* Multi-currency budget: a row can exist without a limit, and a category paid in
   several currencies gets one MDL total so the numbers are comparable. */
.budget-total{
  margin:8px 0 0;padding-top:8px;border-top:1px solid var(--graphite);
  font-size:12.5px;color:var(--mist);display:flex;flex-wrap:wrap;gap:4px 8px;align-items:baseline;
}
.budget-total b{color:var(--paper);font-size:13.5px}
.budget-total small{color:var(--ash);font-size:12px}
.budget-row strong.none{color:var(--ash)}
.progress i.none{width:0;background:var(--slate)}
/* First-run tour. A centred card rather than tooltips pinned to elements: anchored
   tooltips need their target on screen, which on a phone means scrolling the user
   around mid-sentence, and any layout change breaks the anchor silently. */
/* The card sits at the BOTTOM, not centred: each step now navigates to the section it
   describes automatically, so the section has to stay visible behind the explanation.
   A centred card would cover exactly what the user is being told to look at. */
.tour{
  max-width:min(96vw,460px);
  position:fixed;left:50%;transform:translateX(-50%);
  bottom:max(12px,env(safe-area-inset-bottom));top:auto;margin:0;
  max-height:62vh;overflow-y:auto;z-index:60;
}
/* A dimmer over the top of the screen only, so the section reads as "behind" the card
   without hiding it. Non-interactive: tapping around must not dismiss progress. */
.tour-scrim{position:fixed;inset:0;background:rgba(0,0,0,.3);z-index:59;pointer-events:none}
.tour-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}
.tour-head .eyebrow{margin:0}
.tour h2{margin:6px 0 8px;font-size:20px;line-height:1.25}
.tour-body{margin:0;color:var(--mist);font-size:15px;line-height:1.5;
  font-family:inherit}
.tour-dots{display:flex;gap:6px;margin:16px 0 4px}
.tour-dots i{width:6px;height:6px;border-radius:50%;background:rgba(17,17,17,.20)}
.tour-dots i.on{background:var(--lime);width:18px;border-radius:2px}
/* Buttons wrap instead of shrinking: three actions must stay tappable on a narrow
   phone rather than becoming 20px slivers. */
.tour-actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:14px}
.tour-actions button{flex:1 1 auto;min-height:44px;min-width:96px}
.tour-actions button.primary{flex:2 1 140px}
/* "Обучение" sits with the sections but reads as help, not another screen. */
.sheet-help span{color:var(--lime)}

/* Shared budget: one limit, several people. The per-person split is shown because a
   shared pot you cannot attribute causes arguments rather than clarity. */
.shared-budget .family-main{gap:6px}
.shared-bar{display:block;height:6px;border-radius:var(--r-pill);background:var(--slate);overflow:hidden;margin-top:4px}
.shared-bar i{display:block;height:100%;background:var(--lime);transition:width .25s var(--ease-out)}
.shared-bar i.over{background:var(--coral)}
.shared-split{color:var(--mist);font-size:12px}
.family-sums small.over{color:var(--coral)}
.shared-actions{display:flex;flex-wrap:wrap;gap:6px;justify-content:flex-end;align-items:center;
  flex:0 0 auto;justify-self:end}
.shared-actions button{padding:6px 12px;min-height:44px}
/* Три элемента в строке общего бюджета (текст, суммы, действия) не влезают в двухколоночную
   сетку .family-row: третий переносился во второй ряд и оказывался под текстом. */
.family-row.shared-budget{grid-template-columns:minmax(0,1fr) auto auto}
@media(max-width:520px){
  .family-row.shared-budget{grid-template-columns:minmax(0,1fr) auto}
  .family-row.shared-budget .shared-actions{grid-column:1 / -1;justify-content:flex-end}
}
/* People as checkboxes, never hand-typed ids. */
.people-pick{border:1px solid var(--graphite);border-radius:var(--r-input);padding:12px;margin:0}
.people-pick legend{padding:0 6px;color:var(--mist);font-size:12px}
.person-check{display:flex;align-items:center;gap:10px;min-height:44px;cursor:pointer}
.person-check input{width:20px;height:20px;accent-color:var(--lime)}

/* People as tappable cards: the family screen is a list of PEOPLE, and each one opens
   their own page. Cards are buttons (not divs with handlers) so keyboard and screen
   readers get real semantics, and the whole card is the target — a 44px+ tap area. */
.section-lead{margin:0 0 var(--gap-md);color:var(--mist);font-size:16px;line-height:1.5;
  font-family:inherit}
.people-grid{display:grid;gap:10px}
.person-card{
  display:grid;grid-template-columns:44px 1fr 16px;align-items:center;gap:12px;
  width:100%;padding:14px;text-align:left;cursor:pointer;
  background:var(--carbon);border:1px solid var(--graphite);border-radius:var(--r-input);
  color:inherit;font:inherit;min-height:64px;box-shadow:var(--sh-subtle);
}
@media (hover: hover) and (pointer: fine){
  .person-card:hover{border-color:var(--smoke)}}
.person-card:active{transform:scale(.995)}
.person-card:focus-visible{outline:2px solid var(--lime);outline-offset:2px}
.person-avatar{
  display:grid;place-items:center;width:44px;height:44px;border-radius:50%;
  background:var(--slate);color:var(--paper);font-weight:600;font-size:16px;
}
.person-body{display:grid;gap:2px;min-width:0}
.person-body b{font-size:16px}
.person-body small{color:var(--mist);font-size:12px}
.person-spent{display:flex;flex-wrap:wrap;gap:8px;margin-top:4px}
.person-spent em{font-style:normal;font-weight:400;font-size:14px}
.person-spent em.muted{font-weight:400;color:var(--mist)}
/* CSS chevron: this codebase has no icon() helper in app.js, the sprite is HTML-side. */
.person-go{
  width:8px;height:8px;justify-self:end;border-right:2px solid var(--mist);
  border-bottom:2px solid var(--mist);transform:rotate(-45deg);
}
.person-actions{margin:0 0 var(--gap-md)}
.person-actions button{width:100%}

/* The link the owner hands over, since a bot cannot message by username. */
.invite-link{
  display:block;margin-top:6px;padding:6px 8px;border-radius:var(--r-input);
  background:var(--obsidian);border:1px solid var(--graphite);
  font-size:11px;color:var(--mist);word-break:break-all;
}
/* The only screen a pending invitee sees. */
.invite-pending{margin-top:var(--gap-md)}
.invite-pending button.primary{width:100%;margin-top:var(--gap-md)}
/* Disclosure that part of an account's movement was converted at the NBM rate,
   which is not the bank's own rate. */
.fx-note{margin-top:6px;font-size:11px;line-height:1.4;color:var(--fog)}
/* Empty surfaces must say what they are for, otherwise a feature with no data
   reads as a missing feature. */
.empty-hint{
  margin:var(--gap) 0;padding:12px;border-radius:var(--r-input);
  background:var(--obsidian);border:1px dashed var(--graphite);
  color:var(--fog);font-size:13px;line-height:1.45;
}
.business-intro{margin-top:var(--gap-md)}
/* Entry cards for sections that a phone-width tab strip hides. */
/* Home load: cards cascade in (30-80ms stagger). Home opens a few times a day,
   this is the sanctioned 'prevent jarring change' + light delight tier. */
@starting-style{
  .home-section-card{opacity:0;transform:translateY(6px)}
}
.home-section-card{
  transition:opacity 240ms var(--ease-out),transform 300ms var(--ease),border-color var(--quick) ease;
}
.home-section-card:nth-child(2){transition-delay:40ms}
.home-section-card:nth-child(3){transition-delay:80ms}
.home-sections{display:grid;gap:8px;margin-top:var(--gap-md)}
.home-section-card{
  display:grid;grid-template-columns:minmax(0,1fr) auto;gap:12px;align-items:center;
  width:100%;min-height:56px;padding:12px;text-align:left;
  border-radius:var(--r-input);background:var(--carbon);border:1px solid var(--graphite);color:var(--paper);
}
.home-section-card span{min-width:0;display:flex;flex-direction:column;gap:2px}
.home-section-card b{font-size:14px;font-weight:400}
.home-section-card small{font-size:12px;color:var(--fog)}
.home-section-card strong{color:var(--mist);font-size:15px}
.home-section-card:active{transform:scale(.985)}
@media (hover: hover) and (pointer: fine){
  .home-section-card:hover{border-color:var(--smoke)}}
/* Month close: one row per completeness check. */
.close-list{display:grid;gap:8px;margin-top:var(--gap-md)}
.close-row{
  display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 12px;align-items:center;
  padding:12px;border-radius:var(--r-input);background:var(--carbon);border:1px solid var(--graphite);
}
.close-row span{min-width:0;display:flex;flex-direction:column;gap:2px}
.close-row b{font-size:14px;color:var(--paper)}
.close-row small{font-size:12px;color:var(--fog)}
.close-row strong{font-size:14px;white-space:nowrap;font-variant-numeric:tabular-nums}
.close-row.ok strong{color:var(--lime)}
.close-row.blocked strong{color:var(--coral)}
.close-row button{grid-column:1/-1;min-height:44px;font-size:13px}
.hero-kpi.attention{border-color:var(--coral)}
/* Family space: per-member figures, per currency, never blended. */
.family-totals{display:grid;gap:8px;margin-bottom:var(--gap-md)}
.family-list{display:grid;gap:8px}
.family-row{
  display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 12px;align-items:center;
  padding:12px;border-radius:var(--r-input);background:var(--carbon);border:1px solid var(--graphite);
}
.family-row.pending{border-style:dashed}
.family-main{min-width:0;display:flex;flex-direction:column;gap:2px}
.family-main b{font-size:14px;color:var(--paper)}
.family-main small{font-size:12px;color:var(--fog)}
.family-sums{display:flex;flex-direction:column;gap:2px;text-align:right}
.family-sums em{font-style:normal;font-size:14px;color:var(--mist);font-variant-numeric:tabular-nums;white-space:nowrap}
.family-sums em.muted{color:var(--ash);font-size:12px}
.family-row button{grid-column:1/-1;min-height:44px;font-size:13px}
.untracked-list{display:grid;gap:8px}
.untracked-row{
  display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 12px;align-items:center;
  padding:12px;border-radius:var(--r-input);background:var(--carbon);border:1px solid var(--graphite);
}
.untracked-main{min-width:0;display:flex;flex-direction:column;gap:2px}
.untracked-main b{font-size:14px;color:var(--paper)}
.untracked-main small{font-size:12px;color:var(--fog)}
.untracked-sum{font-size:14px;color:var(--coral);white-space:nowrap}
.untracked-link{grid-column:1/-1;min-height:44px;font-size:13px}
/* ---- own category iconography (replaces iPhone emoji) ---- */
.icon-sprite{position:absolute;width:0;height:0;overflow:hidden}
.cat-icon{width:22px;height:22px;flex:0 0 auto;color:var(--mist)}
.cat-icon-sm{width:18px;height:18px}
.category-choice .cat-icon{color:var(--lime)}
.category-choice.selected .cat-icon{color:#fff}
.tx-mark .cat-icon{width:19px;height:19px;color:inherit}
.budget-top span{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.budget-top span b{flex:1 1 auto}
.category-new{border-style:dashed;color:var(--lime)}
/* Category picker sheet + the button that opens it. A native <select> could
   only show text, which is why emoji survived on these screens. */
.cat-sheet{width:min(100%,560px);max-height:82vh;overflow:auto}
.cat-sheet .category-grid{margin-top:var(--gap)}
.cat-pick{
  min-height:48px;padding:12px;text-align:left;font-size:14px;
  display:flex;align-items:center;gap:9px;
  border-radius:var(--r-input);border:1px solid var(--graphite);background:var(--carbon);color:var(--paper);
}
.cat-pick>span{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
@media (hover: hover) and (pointer: fine){
  .cat-pick:hover{border-color:var(--lime);color:var(--lime)}}
.cat-pick .cat-icon{color:var(--lime)}
.review-row .cat-pick{max-width:220px}
@media (hover: hover) and (pointer: fine){
  .cat-edit:hover{background:var(--slate);color:var(--lime)}}
.category-choice.selected .cat-edit{color:rgba(17,17,17,.55)}
.toast-info{background:var(--paper)}
.toast-error{background:var(--coral)}
/* toast-in keyframe removed: toasts use transitions (rapidly-triggered UI) */
/* Подтверждение всегда выше того, что подтверждает: при одинаковом z-index его затемнение
   оказывалось ПОД открытой формой, и окно выглядело «на фоне другим». */
.confirm-dialog{width:min(100%,420px);border-radius:var(--r-card);inset:auto 0 auto 0;top:50%;transform:translateY(-50%);border-bottom:1px solid var(--graphite);z-index:130}
.confirm-dialog .dialog-actions{display:flex;gap:var(--gap-md);position:static;margin-top:24px}
.danger-solid{background:var(--coral);color:#fff;border-color:transparent;font-size:14px;font-weight:400;padding:12px 16px}
@media (hover: hover) and (pointer: fine){
  .danger-solid:hover{background:#931d17;color:#fff;border-color:transparent}}

/* Charts. Inline SVG, styled here so both themes work: the dark theme's bars must not be
   invisible on the light one, which is what happens with hardcoded fills. */
/* Нулевой верхний отступ прижимал «Куда ушли деньги» к предыдущему контейнеру. */
.chart{margin:20px 0;padding:var(--pad-card);background:var(--carbon);
  border:1px solid var(--graphite);border-radius:var(--r-card);box-shadow:var(--sh-subtle)}
.chart svg{width:100%;height:auto;display:block;margin:10px 0 6px;overflow:visible}
.chart figcaption{font-size:13px;color:var(--fog);letter-spacing:.04em;text-transform:uppercase}
.chart figcaption.chart-total{text-transform:none;letter-spacing:0;color:var(--mist);margin-top:4px}
.c-label{font-size:11px;fill:var(--mist)}
.c-value{font-size:11px;fill:var(--paper);font-weight:400}
.c-bar{fill:var(--lime)}
.t-area{fill:var(--lime);opacity:.10}
.t-line{fill:none;stroke:var(--lime);stroke-width:2;vector-effect:non-scaling-stroke}
.t-line.income{stroke:var(--pulse);opacity:.85}
.compare-note{margin:0 0 20px;padding:var(--pad-card);list-style:none;
  background:var(--carbon);border:1px solid var(--graphite);border-radius:var(--r-card);
  color:var(--mist);font-size:14px}
.compare-note li+li{margin-top:6px}
.intro-note{margin:0 0 var(--gap-md);color:var(--mist);font-size:15px;line-height:1.55}

/* Period chips: five taps-worth of choice, visible at once instead of hidden in a select. */
.report-controls{display:flex;flex-wrap:wrap;gap:var(--gap);margin:0 0 16px;align-items:center}
.chip{padding:7px 14px;border-radius:var(--r-pill);border:1px solid var(--graphite);
  background:var(--carbon);color:var(--mist);font-size:14px;cursor:pointer;box-shadow:var(--sh-subtle)}
.chip.on{background:var(--paper);border-color:var(--paper);color:#fafafa;font-weight:500}
/* Пунктирная рамка во всю ширину читалась как серая полоса, а не как «показать ещё». Тот же
   вид, что у «Ещё» в Операциях: подчёркнутая ссылка, выровненная по содержимому. */
.ghost-btn{
  display:inline-flex;align-items:center;gap:var(--gap);
  width:auto;margin:var(--gap-md) auto 0;padding:12px 16px;
  border:1px solid var(--graphite);border-radius:var(--r-pill);
  background:var(--carbon);color:var(--mist);
  font-size:13px;text-decoration:none;min-height:44px;cursor:pointer;
}
.ghost-btn::after{content:'';width:6px;height:6px;border-right:2px solid currentColor;border-bottom:2px solid currentColor;transform:rotate(45deg);margin-top:-3px}
@media (hover: hover) and (pointer: fine){
  .ghost-btn:hover{border-color:var(--lime);color:var(--lime);background:var(--obsidian)}}
/* Trend points. The hit areas are invisible but must stay clickable, so opacity — not
   display:none, which removes them from hit testing entirely. */
.t-hit{fill:transparent;stroke:none;cursor:pointer}
.t-hit:focus{outline:none}
.t-hit.on,.t-hit:focus-visible{fill:var(--lime);fill-opacity:.35}
.t-peak{fill:var(--lime);stroke:var(--carbon);stroke-width:1.5}

/* ---- "Ещё" sheet: pinned header, tap-outside-to-close ------------------- */
/* The whole top strip (eyebrow, title, ×) stays put while the list scrolls. Sticky needs an
   opaque background or the list shows through it, and a negative-margin/padding pair so the
   sticky block spans the dialog's own padding instead of leaving a transparent gutter. */
/* Диалог сам является контейнером прокрутки (overflow:auto), поэтому `top:0` прижимает полосу к
   краю СОДЕРЖИМОГО — то есть НИЖЕ собственного верхнего отступа диалога. Эти 24px оставались над
   закреплённым заголовком, и список прокручивался сквозь них: «над заголовком прокручивается
   страница». Верхнего отступа у контейнера прокрутки быть не должно — его задаёт сама полоса. */
.sheet-nav .dialog-title {
  position:sticky; top:0; z-index:3;
  /* Отрицательный отступ по бокам — чтобы полоса перекрыла горизонтальные поля диалога и список
     не было видно слева и справа от неё. Сверху отступа нет: полоса стоит в самом верху. */
  margin:0 calc(var(--pad-card) * -1) var(--gap-md);
  padding:var(--pad-card) var(--pad-card) var(--gap-md);
  background:var(--carbon);
  border-bottom:1px solid var(--graphite);
  /* Скруглённый верх диалога: без этого углы полосы выступают за его края. */
  border-radius:var(--r-card) var(--r-card) 0 0;
}
/* Backdrop. NOT pointer-events:none (unlike .tour-scrim, which is decorative) — this one
   exists precisely to catch the tap. */
/* «разбивка трат по дням: компактнее». One line per day: label, bar, figure — and a real button,
   because tapping it opens that day's operations. The row was a <div> with the date on its own
   line, which made a week of spending taller than a phone screen. */
.chart-series.compact .day-row {
  display:grid; grid-template-columns:minmax(84px,auto) minmax(0,1fr) auto; align-items:center;
  gap:var(--gap-md); width:100%; min-height:0;
  margin:0; color:var(--mist); font:inherit; text-align:left;
}
.chart-series.compact button.day-row { cursor:pointer; }
.chart-series.compact .day-row span { color:var(--fog); font-size:.82rem; white-space:nowrap; }
.chart-series.compact .day-row b { font-variant-numeric:tabular-nums; white-space:nowrap; }
/* Полоска дня: --lime это СИНИЙ (#0007cb), и при малой сумме полностью скруглённый отрезок в
   6px вырождался в синюю точку — на вид «что-то не загрузилось». Полоска теперь того же цвета,
   что остальной график, лежит на видимой дорожке и не скругляется в точку. */
.chart-series.compact .day-row i { height:6px; border-radius:2px; background:var(--lime);
  min-width:2px; }
.chart-series.compact .day-row { position:relative; }
.chart-series.compact .day-row::before { content:''; grid-column:2; grid-row:1;
  height:6px; border-radius:2px; background:var(--graphite); width:100%; align-self:center; }
.chart-series.compact .day-row i { grid-column:2; grid-row:1; align-self:center; z-index:1; }

.sheet-scrim, .dialog-scrim {
  position:fixed; inset:0; z-index:99;
  background:rgba(0,0,0,.4);
}
/* Затемнение подтверждения перекрывает и обычные диалоги (z-index:100), иначе форма под ним
   остаётся яркой и на вид активной. Класс ставится в JS: `~` не смотрит назад (scrim добавляется
   ПЕРЕД диалогом), а `:has()` нельзя требовать от старого WebView. */
.dialog-scrim.above-dialogs { z-index:129; }

/* ---- Бизнес → Проекты: одна сетка на все четыре колонки ------------------ */
/* Проекты, Клиенты, Операции без проекта и статистика используют один класс строки, но справа у
   них разное: три кнопки, одна кнопка и сумма. Из-за этого каждая колонка задавала собственный
   ритм и строки соседних колонок не совпадали по высоте. Общая минимальная высота = высота
   кнопки, чтобы строка с кнопками и строка с суммой были одного роста. */
.business-columns .project-row{min-height:44px;align-items:center}
/* Заголовок колонки не добавляет верхний отступ ВНУТРИ своей группы: иначе первая строка одной
   колонки начинается ниже, чем у соседней. Но :first-child верен и для второй группы колонок —
   из-за этого «Операции без проекта» теряли отбивку от группы выше и прижимались к ней. Отступ
   между группами задаёт сама группа. */
.business-columns>div>h3:first-child{margin-top:0}
.intro-note+.business-columns{margin-top:0}
.business-columns+.business-columns{margin-top:24px}

/* ---- project contribution table: stop it sliding off a phone ------------ */
/* A flex row whose text half cannot shrink below its content width pushes the amount past the
   right edge. min-width:0 is what allows it to give way; the wrap rule handles a single long
   client name with no spaces. */
.project-row span, .settings-row span, .goal-row span, .debt-row span, .occurrence-row span {
  min-width:0; overflow-wrap:anywhere;
}
/* Текстовая половина строки — это <b> над <small>, а не строка из двух инлайнов. Без колонки
   заголовок и пояснение текли в одну строку и переносились рваным столбиком слева. Так же уже
   устроены .family-main, .close-row span и .untracked-main. */
.project-row>span:not(.row-actions), .settings-row>span:not(.row-actions),
.goal-row>span:not(.row-actions), .debt-row>span:not(.row-actions),
.occurrence-row>span:not(.row-actions), .commitment>span:not(.row-actions) {
  display:flex; flex-direction:column; gap:2px; align-items:flex-start; flex:1 1 auto;
}
/* space-between распределяет КАЖДОГО прямого ребёнка, поэтому строка из четырёх элементов
   (текст, сумма, «Подтвердить», «Пропустить») растаскивала кнопки по середине. Текст забирает
   свободное место, остальное держится справа и не сжимается. */
.settings-row>strong, .project-row>strong, .goal-row>strong, .debt-row>strong,
.occurrence-row>strong, .commitment>strong,
.settings-row>button, .debt-row>button, .occurrence-row>button {
  flex:0 0 auto; margin-left:0;
}


/* Кнопка-ссылка внутри пояснения: «показать все» рядом с фильтром по одному проекту.
   Без стилей она читалась как обычная кнопка и спорила по весу с самим пояснением. */
.link-btn{
  background:none; border:0; padding:0; margin:0; width:auto; min-height:0;
  color:var(--lime); font:inherit; text-decoration:underline; cursor:pointer;
}
@media (hover: hover) and (pointer: fine){
  .link-btn:hover{opacity:.8}}
/* Выбранный проект видно в списке, пока его статистика открыта. */
/* --mist это цвет ТЕКСТА (#414141): в качестве фона он давал тёмно-серый прямоугольник.
   Выбранный проект подсвечивается светлым голубым, как активные чипы. */
.project-row.on{border-color:var(--lime); background:#eff6ff}
.project-row.on button{color:var(--paper)}

/* Три поля в ряд (С / По / Обновить) не влезают в 360px — на узком экране в столбик. */
@media (max-width:520px){
  .date-range { grid-template-columns:1fr; }
  .date-range button { width:100%; }
}

/* ---- пара действий в строке: «Изменить» + «Удалить» ---------------------- */
/* `.settings-row` — flex со space-between, но правая часть из ДВУХ кнопок не имела ни wrap, ни
   запрета сжатия, поэтому пара выдавливала подпись или вылезала за край. Тот же класс дефекта,
   что уже исправлен для `.row-actions` на экране проектов. */
.settings-actions{display:flex;flex-wrap:wrap;justify-content:flex-end;flex:0 0 auto;
  gap:var(--gap);align-items:center}

/* ---- category picker: edit control first and comfortably large ---------- */
.cat-pick-row { display:flex; align-items:center; gap:var(--gap); }
.cat-edit {
  min-width:44px; min-height:44px;
  font-size:16px; line-height:1;
  flex:0 0 auto;
  display:inline-flex; align-items:center; justify-content:center;
  border-radius:var(--r-input); font-style:normal; color:var(--ash); cursor:pointer;
}
.cat-pick-label { min-width:0; overflow-wrap:anywhere; }

/* ---- account tiles are actionable wherever they appear ------------------ */
/* The tile stays an <article> — a <button> may not contain the "Обновить баланс" <button>,
   browsers drop the inner one. An absolutely positioned overlay button provides the tap target
   and sits BELOW the inner controls so they keep working. */
.account-tile { position:relative; }
.account-tile .tile-open {
  position:absolute; inset:0; z-index:0;
  width:100%; padding:0; margin:0; border:0; background:none;
  cursor:pointer; font:inherit; color:inherit;
}
.account-tile > *:not(.tile-open) { position:relative; z-index:1; pointer-events:none; }
/* Every interactive control on the tile, not a hand-listed one: "Удалить счёт" was added
   without being named here, so its taps fell through to the .tile-open overlay and opened
   "Обновить баланс" instead. */
.account-tile [data-balance-account],
.account-tile [data-account-delete],
.account-tile .account-tile-actions,
.account-tile .account-tile-actions button { pointer-events:auto; }
.account-tile .tile-open:focus-visible { outline:2px solid var(--lime); outline-offset:2px; }

/* ---- single-point period: a sentence instead of a flat line ------------- */
.chart-single .data-note { margin:var(--gap) 0; }

/* ---- ask-in-Russian analyst ------------------------------------------------ */
.ask-examples { display:flex; flex-wrap:wrap; gap:var(--gap); margin:var(--gap) 0; }
.ask-answer { margin:var(--gap-md) 0 0; white-space:pre-wrap; line-height:1.5; }
#analystQuestion { width:100%; }

/* ---- every row is actionable: budget rows and category rows -------------- */
/* A row that opens something must look like it does. */
.budget-row[data-budget-key]{cursor:pointer}
.budget-row[data-budget-key]:focus-visible{outline:2px solid var(--lime);outline-offset:2px}
/* Category rows in the statistics chart. .c-hit is a transparent full-width tap target: a 14px
   label and an 18px bar are both too small to hit with a finger. */
.c-row{cursor:pointer}
.c-hit{fill:transparent}
@media (hover: hover) and (pointer: fine){
  .c-row:hover .c-bar,.c-row:focus-visible .c-bar{opacity:.8}}
.c-row:focus-visible{outline:2px solid var(--lime);outline-offset:-2px}

/* ---- the active category filter on the operations screen ----------------- */
/* A filtered list must say so and offer a way out. */
.active-filter{
  display:flex;align-items:center;justify-content:space-between;gap:var(--gap);
  margin:var(--gap-md) 0;padding:var(--gap-md);
  background:var(--carbon);border:1px solid var(--graphite);border-radius:var(--r-card);
}
.active-filter span{display:flex;align-items:center;gap:8px;min-width:0;overflow-wrap:anywhere}
.active-filter button{flex:0 0 auto;min-height:44px}

/* ---- deleting an account: soft delete with a way back ---------------------- */
.account-tile-actions{display:flex;flex-direction:column;gap:6px;margin-top:8px;position:relative;z-index:1;pointer-events:auto}
.account-tile-actions button{min-height:44px;width:100%;pointer-events:auto}
.deleted-accounts{margin-top:16px;border-top:1px solid var(--graphite);padding-top:12px}
.deleted-accounts summary{min-height:44px;display:flex;align-items:center;gap:8px;cursor:pointer}
.deleted-accounts summary b{margin-left:auto;color:var(--fog)}
.deleted-accounts .settings-row button{min-height:44px}

/* ---- spending with no limit behind it -------------------------------------- */
.outside-budget{margin-top:20px;border-top:1px solid var(--graphite);padding-top:16px}
.outside-budget .section-head{align-items:baseline}
.outside-budget h3{margin:0;font-size:15px;font-weight:400}
.outside-budget>.section-head>strong{font-size:20px;font-variant-numeric:tabular-nums;white-space:nowrap}
.outside-list{display:flex;flex-direction:column;gap:8px;margin-top:12px}
.outside-row{display:flex;align-items:center;justify-content:space-between;gap:12px;
  min-height:44px;padding:8px 12px;background:var(--carbon);border:1px solid var(--graphite);
  border-radius:var(--r-input);cursor:pointer;width:100%;text-align:left}
.outside-row>span{display:flex;align-items:center;gap:8px;min-width:0;flex-wrap:wrap}
.outside-row b{font-weight:400}
.outside-row small{color:var(--fog);font-size:12px;flex-basis:100%}
.outside-row>strong{font-variant-numeric:tabular-nums;white-space:nowrap}
.outside-row:focus-visible{outline:2px solid var(--lime);outline-offset:2px}

/* ---- goals: fund and close --------------------------------------------------- */
/* Колонка наследовала align-items:center из группы строк настроек, а в колонке это
   ГОРИЗОНТАЛЬНАЯ ось — прогресс-бар сжимался до ширины содержимого и не мог показать
   прогресс (его <i> задан в процентах). stretch возвращает детям полную ширину. */
.goal-row{display:flex;flex-direction:column;align-items:stretch;gap:var(--gap-md)}
.goal-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}
.goal-head>span{display:flex;flex-direction:column;gap:2px;min-width:0}
.goal-head>strong{font-variant-numeric:tabular-nums;white-space:nowrap}
.goal-actions{display:flex;gap:8px;flex-wrap:wrap;align-items:center}
.goal-actions button{min-height:44px;flex:1 1 auto}
.goal-actions .data-note{margin:0;flex:1 1 100%}

/* ---- recurring: what repeats, and from which account ------------------------ */
.plan-list{display:flex;flex-direction:column;gap:8px;margin-top:8px}
.plan-row{display:flex;align-items:center;justify-content:space-between;gap:12px;
  min-height:44px;padding:10px 12px;background:var(--carbon);border:1px solid var(--graphite);
  border-radius:var(--r-input)}
.plan-row>span{display:flex;flex-direction:column;gap:2px;min-width:0}
.plan-row small{color:var(--fog);font-size:12px}
.plan-row>strong{font-variant-numeric:tabular-nums;white-space:nowrap}

/* Two fields side by side in a dialog, so amount+currency read as one thing. */

.form-grid>label{margin:0}
/* Диалог задаёт единый шаг между полями сам. Раньше поля внутри .form-grid получали отступ от
   сетки, а одиночные — от глобального label{margin}, поэтому шаг «гулял» от поля к полю.
   Разметка — form > dialog > label, поэтому селектор целится в детей ДИАЛОГА. */
.finance-dialog>label,.finance-dialog>.form-grid,.finance-dialog>fieldset{
  margin-top:0; margin-right:0; margin-left:0; margin-bottom:var(--gap-md);
}
/* datetime-local и date несут собственную минимальную ширину и вылезали из колонки 1fr. */
.form-grid>label{min-width:0}
.form-grid input,.form-grid select{min-width:0;max-width:100%}
input[type=datetime-local]{appearance:none;-webkit-appearance:none;min-width:0;max-width:100%}

/* ---- подробности дня: диалог поверх графика ------------------------------- */
/* Открывается по тапу на столбик в «По дням»: список операций этого дня с итогом по валютам,
   вместо ухода на экран Операций с фильтром. */
.day-dialog{width:min(100%,560px);max-height:82vh;overflow:auto}
.day-dialog .day-body{margin-bottom:var(--gap-md)}
.day-dialog .transaction-list{margin-top:var(--gap)}

/* ---- окно усреднения для предложенных лимитов ----------------------------- */
/* «на основе каких месяцев считать»: выбор рядом с кнопкой расчёта, а не в объяснении после. */
.suggest-window{align-items:center}
/* Подпись и чипы в одной flex-строке не влезали в 360px: «Неделя» и «Год» уезжали вниз и
   строка выглядела сломанной. Подпись — своя строка, чипы — своя. */
.suggest-window-label{display:block;color:var(--fog);font-size:12px;margin:0 0 var(--gap)}
.basis-kinds{margin:0 0 var(--gap-md)}

/* ---- закрытие месяца: выбор месяца ---------------------------------------- */
.close-month-pick{display:grid;gap:4px;margin:0 0 var(--gap-md);max-width:260px}
.close-month-pick select{margin-top:0}

/* ---- m04: заголовки внутри блока статистики ------------------------------- */
/* Глобальный h3{margin:0}, а правило .panel-section h3 не действует: блок статистики — это
   <article class="wide">, не .panel-section. Поэтому «По дням» прижималось к графику выше. */
#overviewStats h3,.stats-heading{margin-top:28px;margin-right:0;margin-bottom:12px;margin-left:0}
#overviewStats .compare-note{margin-top:20px;margin-bottom:24px}
#overviewStats .kpi-grid+.chart,#overviewStats .chart+.stats-heading{margin-top:28px}
.day-hint{margin:0 0 var(--gap)}

/* ---- m03: день должен выглядеть нажимаемым -------------------------------- */
/* Строка дня — такая же карточка, как .budget-row и .account-tile: тот же фон, та же рамка,
   тот же радиус и тот же hover. До этого она была голой кнопкой на фоне страницы. */
.chart-series.compact .day-row{
  grid-template-columns:minmax(84px,auto) minmax(0,1fr) auto;
  padding:var(--pad-card);
  background:var(--carbon);border:1px solid var(--graphite);border-radius:var(--r-card);
}
.chart-series.compact button.day-row{grid-template-columns:minmax(84px,auto) minmax(0,1fr) auto 10px}
@media (hover: hover) and (pointer: fine){
  .chart-series.compact button.day-row:hover{border-color:var(--smoke);background:var(--carbon)}}
/* Focus must be visible on every interactive element: a 1px border tint is not an
   indicator at 3:1. Keep the outline, theme it. */
.chart-series.compact button.day-row:focus-visible{outline:2px solid var(--iris);outline-offset:2px;border-color:var(--mist)}
/* Шеврон: та же CSS-стрелка, что у карточек людей. */
.day-go{
  width:6px;height:6px;justify-self:end;align-self:center;
  border-right:2px solid var(--ash);border-bottom:2px solid var(--ash);
  transform:rotate(-45deg);
}
@media (hover: hover) and (pointer: fine){
  .chart-series.compact button.day-row:hover .day-go{border-color:var(--lime)}}
/* Дорожка и полоска не должны попадать в колонку шеврона. */
.chart-series.compact button.day-row::before{grid-column:2}

/* ---- m07: «Ещё …» ---------------------------------------------------------- */
/* The single .ghost-btn rule above is the compact expansion control. */
.more-row{display:flex;justify-content:center}

/* ---- m01: конкретный период для расчёта лимитов --------------------------- */
.suggest-basis{margin:0 0 var(--gap-md)}
.basis-pick{display:flex;flex-direction:column;gap:4px;margin:var(--gap) 0 0;max-width:280px}
.basis-pick select{margin-top:0}

/* ---- m06: распределение траты по проектам --------------------------------- */
/* Строка: проект, доля в процентах, сумма и «убрать». На узком экране — в столбик. */
.allocation-line>label{margin:0;min-width:0;display:flex;flex-direction:column;gap:4px}
.allocation-line input,.allocation-line select{margin-top:0;min-width:0}
.allocation-line .alloc-drop{min-height:44px;padding:0;font-size:18px;line-height:1}
.alloc-quick{display:flex;flex-wrap:wrap;gap:var(--gap);margin:0 0 var(--gap-md)}
.alloc-quick button{min-height:44px}
.alloc-over{color:var(--coral)}
@media(max-width:520px){
  .allocation-line{grid-template-columns:minmax(0,1fr) 44px;grid-template-areas:'project drop' 'percent percent' 'amount amount'}
  .allocation-line>label:nth-of-type(1){grid-area:project}
  .allocation-line>label:nth-of-type(2){grid-area:percent}
  .allocation-line>label:nth-of-type(3){grid-area:amount}
  .allocation-line .alloc-drop{grid-area:drop;align-self:end}
}

/* m08 кредиты */
.credit-row{display:grid;grid-template-columns:minmax(0,1fr) auto auto;gap:var(--gap);align-items:center;padding:var(--gap-md) 0;border-bottom:1px solid var(--graphite)}
.credit-row:last-child{border-bottom:0}.credit-row>span{display:flex;flex-direction:column;gap:3px;min-width:0}.credit-row>span small{color:var(--ash)}.credit-row>strong{text-align:right;font-size:14px;line-height:1.35}.credit-row .row-actions{display:flex;gap:var(--gap);flex-wrap:wrap;justify-content:flex-end}
@media(max-width:520px){.credit-row{grid-template-columns:minmax(0,1fr) auto}.credit-row .row-actions{grid-column:1/-1;justify-content:stretch}.credit-row .row-actions button{flex:1}}

/* Brief highlight on the tile the person tapped from the overview, so arriving on a long
   list of accounts does not feel like nothing happened. */
.account-tile.just-opened{outline:2px solid var(--lime);outline-offset:2px}
