/* Base text size for the whole app (every layout loads this file). BassCSS's
   typography/spacing classes and daisyUI's own component CSS are all rem-based, so
   bumping the root size scales every .h1-.h6, body copy, and component consistently
   from a single place instead of overriding sizes piecemeal. */
html {
    font-size: 125%; /* 20px at the browser default of 16px */
}

/* App shell layout: collapsible sidebar (icons-only when collapsed) + top bar. */
.app-shell {
    display: flex;
    height: 100vh;      /* lock to the viewport */
    overflow: hidden;  /* page never scrolls; content area scrolls instead */
}

/* ---------- Sidebar ---------- */
.sidebar {
    width: 16rem;                    /* 256px expanded */
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    background: var(--color-base-100, #ffffff);
    border-right: 1px solid var(--color-base-300, #d1d5db);
    transition: width 180ms ease;
    overflow: hidden;
}

.app-shell.sidebar-collapsed .sidebar {
    width: 4.5rem;                   /* 72px icons-only */
}

.sidebar-brand {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 1rem 1rem;
    height: 3.5rem;
    white-space: nowrap;
}

.sidebar-nav {
    flex: 1;
    overflow-y: auto;
    padding: 0.5rem;
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}

.sidebar-footer {
    border-top: 1px solid var(--color-base-300, #d1d5db);
    padding: 0.5rem;
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}

.sidebar-link {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.5rem 0.75rem;
    border-radius: 0.5rem;
    color: var(--color-base-content, #1f2937);
    text-decoration: none;
    white-space: nowrap;
    position: relative;
}

.sidebar-link:hover {
    background: var(--color-base-200, #f3f4f6);
    text-decoration: none;
}

.sidebar-link.active {
    background: var(--color-primary, #2563eb);
    color: var(--color-primary-content, #ffffff);
}

.sidebar-link .ti {
    font-size: 1.25rem;
    line-height: 1;
}

/* When collapsed: center icons, hide labels and brand text. */
.app-shell.sidebar-collapsed .sidebar-link,
.app-shell.sidebar-collapsed .sidebar-footer,
.app-shell.sidebar-collapsed .sidebar-brand {
    justify-content: center;
}

.app-shell.sidebar-collapsed .nav-label,
.app-shell.sidebar-collapsed .sidebar-brand-text {
    display: none;
}

.app-shell.sidebar-collapsed .sidebar-link {
    justify-content: center;
    padding: 0.5rem 0;
}

/* Tooltip on hover when collapsed (shows the hidden label). */
.app-shell.sidebar-collapsed .sidebar-link[data-title]:hover::after {
    content: attr(data-title);
    position: absolute;
    left: 100%;
    top: 50%;
    transform: translateY(-50%);
    margin-left: 0.5rem;
    padding: 0.25rem 0.5rem;
    border-radius: 0.375rem;
    background: var(--color-base-content, #1f2937);
    color: var(--color-base-100, #ffffff);
    font-size: 0.75rem;
    white-space: nowrap;
    z-index: 30;
}

.sidebar-collapse-btn {
    margin: 0.25rem 0.5rem 0.75rem;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* ---------- Main area ---------- */
.app-main {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-width: 0;
    min-height: 0;   /* allow inner content to scroll */
}

.app-topbar {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.75rem 1.25rem;
    border-bottom: 1px solid var(--color-base-300, #d1d5db);
    background: var(--color-base-100, #ffffff);
    position: sticky;
    top: 0;
    z-index: 20;
}

.app-content {
    padding: 1.5rem;
    flex: 1;
    overflow-y: auto;  /* content scrolls here, independent of the sidebar */
    min-height: 0;
}


/* The committed daisyUI app.css did not emit the filled .btn-error variant.
   Define it here (app-specific CSS, no Tailwind) so error buttons render red. */
.btn-error{--btn-color:var(--color-error);--btn-fg:var(--color-error-content);--btn-soft-bg:initial}


/* Make the sidebar toggle icon match the sidebar links' 1.25rem icon size. */
.sidebar-brand-toggle .ti{font-size:1.25rem;line-height:1}

/* Top bar controls: theme switcher icon + logout icon match the sidebar's 1.25rem
   icon size; logout text is bumped to 1rem so it's not tiny. */
.app-topbar .btn .ti,
.app-topbar #theme-icon { font-size: 1.25rem; line-height: 1; }
.app-topbar .btn { font-size: 1rem; }

/* Accounts: the active-marker column is just wide enough for the check. */
.accounts-active-col{width:1px;white-space:nowrap;text-align:center}
.accounts-active-col .active-check{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;border-radius:9999px;background:var(--su, #16a34a);color:#fff;font-size:1.4rem}
.active-check .ti{line-height:1}

/* Accounts: Actions column just wide enough for the icon buttons. */
.accounts-actions-col{width:1px;white-space:nowrap}

/* Generic narrow Actions column for icon-button rows. */
.actions-col{width:1px;white-space:nowrap}

/* Shrink a table column to exactly its content and stop it wrapping. Same width:1px
   trick as .actions-col (browsers treat it as "as narrow as the content allows"), but
   named for the general case: on the Reconcile tables the Date, Amount and Actions
   columns are all fixed-shape, so pinning them lets the free-text Transaction column
   absorb the remaining width instead of squeezing a date onto two lines or wrapping a
   three-button action group. */
.bf-shrink-col{width:1px;white-space:nowrap}

/* Two-column form row cells (income/expense dialogs): equal 50/50 columns that
   ignore the widget's intrinsic width so wide inputs never force a wrap. */
.bf-field-2col{flex:1 1 0%;min-width:0}

/* Amount/Balance columns: just wide enough for a 7-figure amount, no more. */
.money-col{white-space:nowrap;width:7.25em;max-width:7.25em}


/* Calendar: 7-column CSS-grid month (no JS). Won't fit flex; needs a real grid. */
.bf-cal-head,
.bf-cal-grid { display:grid; grid-template-columns:repeat(7,minmax(0,1fr)); gap:4px; }
.bf-cal-head { margin-bottom:4px; }
.bf-cal-day { min-height:5rem; }


/* Date picker (shared, server-rendered, htmx month nav — no calendar JS). */
.df-head{display:flex;align-items:center;justify-content:space-between;gap:.5rem;margin-bottom:.4rem}
.df-title{font-weight:600}
.df-nav{cursor:pointer;border:none;background:transparent;font-size:1.1rem;line-height:1;color:inherit}
.df-cal{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:2px;justify-items:center;margin-top:2px}
.df-dow{font-size:.7rem;font-weight:600;padding:.15rem 0;text-align:center;color:inherit}


/* Calendar: stretch the month grid to the full window height (below the top bar).
   The Calendar page sets ViewData["ShellClass"]="calendar-fill". */
.app-shell.calendar-fill .app-content{display:flex;flex-direction:column}
.app-shell.calendar-fill .bf-cal-page{flex:1;min-height:0;display:flex;flex-direction:column}
.app-shell.calendar-fill .cal-card{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden}
.app-shell.calendar-fill .bf-cal-grid{flex:1 1 auto;min-height:0;grid-auto-rows:1fr}


/* Calendar: full-width item bars inside each day cell. Red for expenses, green for
   income; a negative balance forces red regardless of type. White text. */
.cal-item{display:block;width:100%;box-sizing:border-box;margin:2px 0 2px 0;padding:1px 6px;border:none;border-radius:4px;cursor:pointer;text-align:left;color:#fff;font-size:.72rem;line-height:1.5;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-weight:500}
.cal-item:hover{opacity:.9}
.cal-item-expense{background:var(--color-error,#dc2626)}
.cal-item-income{background:var(--color-success,#16a34a)}
.cal-item-negative{background:var(--color-error,#b91c1c);opacity:1}
.cal-item-negative:hover{opacity:1;filter:brightness(.9)}
.cal-item-bal{font-weight:700}

/* The committed daisyUI app.css did not emit the filled .btn-success variant either. */
.btn-success{--btn-color:var(--color-success);--btn-fg:var(--color-success-content);--btn-soft-bg:initial}


/* Calendar drag-and-drop affordances. */
.cal-item[draggable=true]{cursor:grab}
.cal-item.dragging{opacity:.5}
.bf-cal-day.bf-drop{box-shadow:inset 0 0 0 2px var(--color-primary,#2563eb);border-radius:.5rem}

/* ---------- Edit (occurrence) dialog: one field per line with a label -------- */
.bf-dlg-field{display:block;margin-bottom:1rem}
.bf-dlg-field:last-of-type{margin-bottom:.25rem}
.bf-dlg-label{display:block;font-weight:600;font-size:.875rem;margin-bottom:.4rem}
.bf-dlg-input{width:100%;box-sizing:border-box;padding:.55rem .75rem;border:1px solid var(--color-base-300,#d1d5db);border-radius:.5rem;background:var(--color-base-100,#fff);color:inherit;font:inherit;line-height:1.5}
.bf-dlg-input:focus{outline:2px solid var(--color-primary,#2563eb);outline-offset:1px;border-color:transparent}
.bf-dlg-textarea{min-height:4.5rem;resize:vertical}
.bf-dlg-datebtn{width:100%;justify-content:space-between}

/* ---------- Date picker: highlight the chosen day, mark today, orange for today --- */
.df-day{position:relative}
.df-today{color:var(--color-warning,#f59e0b);font-weight:700}
.df-selected{background:var(--color-primary,#2563eb);color:#fff;font-weight:600}

/* ---------- Forecast Date column: today orange, past-due red ---------- */
.bf-date-today{color:var(--color-warning,#f59e0b);font-weight:600}
.bf-date-past{color:var(--color-error,#dc2626)}

/* Forecast Date column: stack date above day-of-week (the .btn is flex-row by default). */
.date-resched{flex-direction:column;align-items:center;justify-content:center;height:auto;line-height:1.05;gap:1px}

/* Theme selector dropdown (daisyUI homepage style swatch list). */
.theme-menu{width:18rem;max-height:22rem;overflow-y:auto;background-color:var(--color-base-100);border-radius:var(--radius-box,.5rem);box-shadow:0 10px 20px -8px rgb(0 0 0/.3);padding:.5rem}
.theme-menu::-webkit-scrollbar{width:8px}
.theme-menu::-webkit-scrollbar-thumb{background-color:var(--color-base-300);border-radius:4px}
.theme-option{display:flex;align-items:center;gap:.6rem;width:100%;text-align:left;padding:.45rem .6rem;border-radius:var(--radius-field,.25rem);cursor:pointer;background:none;border:none;font-size:.875rem;color:var(--color-base-content)}
.theme-option:hover{background-color:var(--color-base-200)}
.theme-option.active{outline:2px solid var(--color-primary);outline-offset:-1px}
.theme-swatch{display:inline-flex;gap:2px;flex:none}
.theme-swatch i{width:12px;height:12px;border-radius:50%}

/* Budget progress bar */
.bf-progress{width:8rem;height:.6rem;background-color:var(--color-base-300);border-radius:999px;overflow:hidden;flex:none}
.bf-progress-fill{height:100%;background-color:var(--color-primary);border-radius:999px;transition:width .2s ease-out}
.bf-progress-fill.bf-progress-over{background-color:var(--color-error)}

/* Color-button variants that the compiled app.css subset omits (mirrors daisyUI). */
.btn-secondary{--btn-color:var(--color-secondary);--btn-fg:var(--color-secondary-content);--btn-soft-bg:initial}
.btn-error{--btn-color:var(--color-error);--btn-fg:var(--color-error-content);--btn-soft-bg:initial}

/* Dashboard layout */
.bf-stat-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));gap:12px}
.bf-two-col{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(320px,100%),1fr));gap:16px}

/* Reports polish */
.bf-pill{display:inline-flex;align-items:center;gap:.2rem;padding:.15rem .55rem;border-radius:999px;font-size:.72rem;font-weight:600;line-height:1.3;white-space:nowrap}
.bf-pill-success{background-color:color-mix(in srgb,var(--color-success) 20%,transparent);color:var(--color-success)}
.bf-pill-warning{background-color:color-mix(in srgb,var(--color-warning) 20%,transparent);color:var(--color-warning)}

.bf-cat-block{border-top:1px solid var(--color-base-300)}
.bf-cat-block:first-child{border-top:none}

table.bf-rpt-inc,table.bf-rpt-exp{table-layout:fixed;width:100%}
table.bf-rpt-inc th:first-child,table.bf-rpt-inc td:first-child,
table.bf-rpt-exp th:first-child,table.bf-rpt-exp td:first-child{width:9.5rem;white-space:nowrap}
table.bf-rpt-inc th:nth-child(2),table.bf-rpt-inc td:nth-child(2),
table.bf-rpt-exp th:nth-child(2),table.bf-rpt-exp td:nth-child(2){width:40%}
table.bf-rpt-inc th:nth-child(3),table.bf-rpt-inc td:nth-child(3),
table.bf-rpt-exp th:nth-child(3),table.bf-rpt-exp td:nth-child(3){width:11rem}
table.bf-rpt-inc th:nth-child(4),table.bf-rpt-inc td:nth-child(4),
table.bf-rpt-exp th:nth-child(4),table.bf-rpt-exp td:nth-child(4){width:7rem}

/* ---- Tailwind-free replacements (no Tailwind utility framework) ---- */
.bf-num{font-variant-numeric:tabular-nums}
.bf-semibold{font-weight:600}
.bf-medium{font-weight:500}
.bf-extrabold{font-weight:800}
.bf-normal{font-weight:400}
.bf-text-10{font-size:10px}
.bf-text-xs{font-size:.75rem}
.bf-text-sm{font-size:.875rem}
.bf-text-base{font-size:1rem}
.bf-text-lg{font-size:1.125rem}
.bf-nowrap{white-space:nowrap}
.bf-overflow-x{overflow-x:auto}
.bf-rounded-full{border-radius:9999px}
.bf-rounded-box{border-radius:var(--radius-box)}
.bf-rounded-lg{border-radius:.5rem}
.bf-w-full{width:100%}
.bf-w-28{width:7rem}
.bf-w-32{width:8rem}
.bf-w-64{width:16rem}
.bf-h-4{height:1rem}
.bf-h-28{height:7rem}
.bf-h-32{height:8rem}
.bf-shrink-0{flex-shrink:0}
.bf-flex-1{flex:1 1 0%}
.bf-max-w-md{max-width:28rem}
.bf-max-w-xl{max-width:36rem}
.bf-max-w-3xl{max-width:48rem}
.bf-opacity-40{opacity:.4}
.bf-opacity-70{opacity:.7}
.bf-opacity-80{opacity:.8}
.bf-min-h-screen{min-height:100vh}
.bf-z-30{z-index:30}
.bf-z-50{z-index:50}
.bf-cursor-pointer{cursor:pointer}

.bf-link-plain{text-decoration:none}
.bf-link-plain:hover,.bf-link-plain:focus{text-decoration:none}
.bf-ring-primary{box-shadow:0 0 0 2px var(--color-primary)}
.bf-shadow-lg{box-shadow:0 10px 15px -3px rgb(0 0 0/.1),0 4px 6px -4px rgb(0 0 0/.1)}
.bf-leading-tight{line-height:1.25}

/* ---- Slash-opacity equivalents (Tailwind-free; use daisyUI theme vars) ---- */
.bf-muted-50{color:color-mix(in oklab,var(--color-base-content) 50%,transparent)}
.bf-muted-60{color:color-mix(in oklab,var(--color-base-content) 60%,transparent)}
.bf-muted-70{color:color-mix(in oklab,var(--color-base-content) 70%,transparent)}
.bf-muted-80{color:color-mix(in oklab,var(--color-base-content) 80%,transparent)}
.bf-bg-base200-40{background-color:color-mix(in oklab,var(--color-base-200) 40%,transparent)}
.bf-bg-error-10{background-color:color-mix(in oklab,var(--color-error) 10%,transparent)}

/* File input (statement-import dialog, Reconcile page). daisyUI's own .input class flattens
   a native <input type="file"> into a run-together "Choose FileNo file chosen" string with no
   visible separation between the button and the filename/status text -- confirmed by an actual
   screenshot; layout metrics (scrollWidth/clientWidth, overflow:clip) showed no clipping and
   completely missed the problem, because it isn't a sizing bug, it's a native-control styling
   gap daisyUI's .input never accounts for. This repo has no build step to regenerate app.css
   with a real file-input variant (see AGENTS.md: "If a completely new daisyUI component is
   ever needed, hand-write its small CSS snippet into a project CSS file rather than introducing
   a build step"), so this styles the native ::file-selector-button pseudo-element directly,
   echoing app.css's own .btn/.btn-outline values (1px solid border in --color-base-content,
   var(--radius-field) corners, 600 weight, transparent background) so the button half reads as
   a real button next to the filename text instead of bolted on.
   Deliberately placed here, not in wwwroot/css/components.css: components.css is a legacy,
   pre-daisyUI component system (its own .btn/.card/etc. carry entirely different values) that
   predates app.css's adoption and is not linked from _AppLayout.cshtml or any layout currently
   in use -- adding a link for it would collide with every daisyUI .btn/.card on every
   authenticated page. app-shell.css is what _AppLayout.cshtml actually loads, and is already
   this app's home for hand-written bf-* utility classes (see above).
   No `width` here, deliberately: an earlier version copied daisyUI's own .input width formula
   (`clamp(3rem,20rem,100%)`), which computed to a fixed ~20rem regardless of container width --
   that formula was written for a component that owns its own sizing, not one the caller sizes
   externally. On this element the markup sizes width via BassCSS's col-12 (width:100%), and
   because both rules are single-class selectors of equal specificity, whichever one happened to
   load later in the stylesheet order (this file, after basscss.min.css) silently won, so the
   field measured 400px against a 580px modal-action row it was supposed to line up with. This
   class now declares no width at all, so col-12 (or whatever width/layout class the caller
   applies) is never contested. display is `flex`, not `inline-flex`, for the same reason:
   `inline-flex` is a shrink-to-fit atomic inline-level box, and a plain block flex container is
   what naturally stretches to fill its parent's width by default (col-12 still applies
   explicitly and would win regardless, but this avoids relying on that alone). Changing
   inline-flex -> flex only changes the box's outer participation in layout, not the inner flex
   algorithm (align-items:center, row direction) that arranges the ::file-selector-button next
   to the filename text -- that arrangement is unaffected. */
.bf-file-input{display:flex;align-items:center;height:calc(var(--size-field,.25rem) * 10);padding-inline:.75rem;font-size:.875rem;color:var(--color-base-content);background-color:var(--color-base-100);border:1px solid color-mix(in oklab,var(--color-base-content) 20%,transparent);border-radius:var(--radius-field)}
.bf-file-input::file-selector-button{margin-inline-end:.75rem;padding:.375rem .75rem;font-size:.75rem;font-weight:600;color:var(--color-base-content);background-color:transparent;border:1px solid var(--color-base-content);border-radius:var(--radius-field);cursor:pointer}
.bf-file-input::file-selector-button:hover{background-color:var(--color-base-200)}
.bf-file-input:focus-visible{outline:2px solid var(--color-base-content);outline-offset:2px}

/* Mobile: stack the top-bar groups vertically and space them with explicit
   margins instead of relying on flex `wrap` + `justify-content: space-between`,
   whose iOS Safari handling of wrapped lines is unreliable and can produce
   large gaps (and no spacing between controls that wrap inside a toolbar).
   Works identically in Safari, Chrome, and Firefox. */
@media (max-width: 767px) {
  .bf-topbar {
    flex-direction: column;
    align-items: stretch;
    justify-content: flex-start;
    gap: 0;
  }
  /* Space between stacked top-bar groups (account row, nav row, action rows). */
  .bf-topbar > *:not(:first-child) { margin-top: 0.75rem; }
  /* Space between controls that wrap to a new line inside a toolbar. */
  .bf-toolbar { row-gap: 0.5rem; }
}

/* Spins an icon inside a button while htmx has an in-flight request on it
   (htmx toggles the .htmx-request class on the requesting element for the
   duration of the request — this is htmx's own built-in indicator class,
   no extra markup or attributes needed). */
.htmx-request .bf-spin-on-request {
  animation: bf-spin 0.8s linear infinite;
}
/* Swaps a button's resting icon for a spinner while htmx has an in-flight request on
   it. Preferred over spinning the resting icon when that icon is not itself a motion
   glyph — a rotating link or check mark reads as a rendering glitch rather than as
   progress. Pair .bf-idle-icon / .bf-busy-icon on two <i> elements inside the button. */
.bf-busy-icon { display: none; }
.htmx-request .bf-idle-icon { display: none; }
.htmx-request .bf-busy-icon { display: inline-block; }

/* Blocks a second click while the first request is still running. Deliberately NOT
   `disabled`: this htmx build has no hx-disabled-elt (verified — the attribute appears
   nowhere in htmx.min.js), so nothing would re-enable the button if the swap failed,
   and toggling `disabled` on the element htmx is mid-request on risks interfering with
   that request. pointer-events costs nothing and cannot strand the control. */
.htmx-request.bf-lock-on-request { pointer-events: none; opacity: 0.65; }

/* Same spin, but unconditional — for a plain (non-htmx) form's own JS to apply
   directly on submit, e.g. while waiting on a slow server-side call. */
.bf-spinning {
  animation: bf-spin 0.8s linear infinite;
}
@keyframes bf-spin {
  to { transform: rotate(360deg); }
}
/* A <form> used only to scope a field for AJAX/antiforgery purposes, with no
   styling of its own, inside a flex row (e.g. the account selector next to
   the balance button). Safari doesn't shrink a <form> flex item to its
   content width the way Chrome/Firefox do, leaving a large gap after it;
   `display: contents` drops the form's own box so its children become flex
   items of the surrounding row directly, in every browser. */
.bf-form-contents {
  display: contents;
}

/* Table text at 125% of daisyUI's defaults (on top of the 125% root size above) — users
   found data tables still too small. Scales text only: buttons, selects and checkboxes in
   rows keep their size so the tables don't widen. Every value is its default x 1.25. */
.table,
.table :where(thead,tfoot) { font-size: 1.09375rem; }          /* .875rem */
.table-sm :not(thead,tfoot) tr { font-size: .9375rem; }        /* .75rem */
.table .bf-text-xs { font-size: .9375rem; }                    /* .75rem */
.table .bf-text-sm { font-size: 1.09375rem; }                  /* .875rem */
.table .bf-pill { font-size: .9rem; }                          /* .72rem */
.table .badge { --size: calc(var(--size-selector,.25rem) * 7.5); font-size: 1.09375rem; } /* x6, .875rem */
.table .badge-sm { --size: calc(var(--size-selector,.25rem) * 6.25); font-size: .9375rem; } /* x5, .75rem */
/* The forecast's date reads as text but is a btn-xs (reschedule on click). */
.table .date-resched { --fontsize: .859375rem; }               /* .6875rem */

/* Landing pricing cards: one column on phones, three equal-height columns from BassCSS's
   md breakpoint up, with each card's button pinned to the bottom so they line up however
   long the feature lists are. */
.bf-pricing { display: grid; gap: 1rem; }
@media (min-width: 52em) { .bf-pricing { grid-template-columns: repeat(3, 1fr); } }
.bf-pricing .card-body > .btn:last-child { margin-top: auto; }
/* A note above a card's button (e.g. "Subscription only") drops to the bottom with it. */
.bf-pricing .card-body > .bf-price-note { margin-top: auto; }
.bf-pricing .card-body > .bf-price-note + .btn { margin-top: 0; }

/* ================= Phones and portrait tablets =================
   The sidebar becomes an off-canvas drawer toggled by the hidden #bf-nav-open checkbox
   (top-bar menu button, the drawer's X and the backdrop are all <label>s for it), the top
   bar is compacted, and nothing may scroll sideways. */
.bf-nav-open{position:absolute;opacity:0;pointer-events:none;width:0;height:0}
.bf-topbar-menu,.bf-nav-close,.bf-nav-backdrop{display:none}
.bf-show-mobile{display:none}

/* Phones only: the desktop 125% root size is too big for a ~390px screen. */
@media (max-width: 767px) { html{font-size:100%} }

/* Phones and portrait tablets (up to 1023px): below this the desktop sidebar leaves too
   little width for the tables, so the drawer, stacked tables and helpers apply here. */
@media (max-width: 1023px) {
  html,body{overflow-x:hidden}
  .app-content{padding:.75rem;overflow-x:hidden}

  .app-shell .sidebar,
  .app-shell.sidebar-collapsed .sidebar{position:fixed;top:0;bottom:0;left:0;width:min(18rem,85vw);z-index:60;transform:translateX(-105%);visibility:hidden;transition:transform 200ms ease,visibility 0s linear 200ms;box-shadow:0 10px 30px rgb(0 0 0/.25)}
  /* visibility too, so the closed drawer's links can't be tabbed to. */
  .bf-nav-open:checked ~ .app-shell .sidebar{transform:none;visibility:visible;transition:transform 200ms ease}
  .bf-nav-open:checked ~ .app-shell .bf-nav-backdrop{display:block;position:fixed;inset:0;z-index:55;background:rgb(0 0 0/.4)}
  /* The desktop "collapsed" state never applies to the drawer. */
  .app-shell.sidebar-collapsed .nav-label,
  .app-shell.sidebar-collapsed .sidebar-brand-text{display:inline}
  .app-shell.sidebar-collapsed .sidebar-link{justify-content:flex-start;padding:.5rem .75rem}
  .app-shell.sidebar-collapsed .sidebar-brand{justify-content:flex-start}
  .sidebar-brand-toggle{display:none}
  .bf-nav-close,.bf-topbar-menu{display:inline-flex}

  .app-topbar{padding:.5rem .5rem;gap:.25rem}
  .bf-topbar-title{flex:1 1 auto;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
  .app-topbar .ml-auto{margin-left:0}
  .app-topbar .mr2{margin-right:0}
  .bf-hide-mobile{display:none}
  .bf-show-mobile{display:inline-flex}
}

/* Stacked tables on phones and portrait tablets (<1024px): add .bf-stack to a <table> and each row becomes a card. The
   header row is hidden; each <td> shows its data-label beside its value; a
   .bf-stack-title cell becomes the card's bold first line (no label) and a
   .bf-stack-actions cell its right-aligned last line. Desktop layout is untouched. */
@media (max-width: 1023px) {
  table.bf-stack, table.bf-stack tbody, table.bf-stack tfoot{display:block;width:100%}
  table.bf-stack thead{display:none}
  table.bf-stack tr{display:flex;flex-direction:column;padding:.6rem .25rem;border-bottom:1px solid var(--color-base-300)}
  table.bf-stack.bf-stack td{width:auto!important;max-width:none!important} /* beats fixed column widths (e.g. Reports) */
  table.bf-stack td{display:flex;align-items:center;justify-content:space-between;gap:.75rem;width:auto;max-width:none;min-width:0;padding:.2rem .5rem;border:none;text-align:right;white-space:normal}
  table.bf-stack td[data-label]::before{content:attr(data-label);flex:none;text-align:left;font-weight:600;font-size:.85em;color:color-mix(in oklab,var(--color-base-content) 60%,transparent)}
  table.bf-stack td.bf-stack-title{order:-1;display:block;text-align:left;font-weight:600}
  table.bf-stack td.bf-stack-actions{justify-content:flex-end;flex-wrap:wrap;padding-top:.4rem}
  table.bf-stack td.bf-stack-hide{display:none}
  /* A small control (e.g. a row checkbox) pinned to the card's top-right, beside the title. */
  table.bf-stack tr{position:relative}
  table.bf-stack td.bf-stack-corner{position:absolute;top:.55rem;right:.25rem;padding:.2rem .5rem}
  table.bf-stack tr:has(> td.bf-stack-corner) td.bf-stack-title{padding-right:2.75rem}
  .card-body{padding:1rem}
  /* Long badges (e.g. category names) wrap inside a stacked card instead of overflowing
     daisyUI's fixed badge height. */
  /* Chart page's balance line: the 900-unit-wide SVG shrinks ~3x on a phone, so scale its
     labels and dots back up (in SVG units) so they stay readable and tappable. */
  .bf-line-chart{overflow:visible}
  .bf-line-chart text{font-size:28px}
  .bf-line-chart circle{r:12px;stroke-width:4px}
  /* Reports' yearly matrix: the 12 months three to a row (name above amount), with the
     item's title and Total spanning the full width, instead of 14 lines per item. */
  table.year-matrix.bf-stack tr{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));column-gap:.5rem}
  table.year-matrix.bf-stack td.bf-stack-title,
  table.year-matrix.bf-stack td[data-label="Total"]{grid-column:1/-1}
  table.year-matrix.bf-stack td[data-label]:not([data-label="Total"]){flex-direction:column;align-items:flex-start;gap:0;text-align:left}
  table.bf-stack .badge{height:auto;min-height:var(--size);white-space:normal;text-align:left;line-height:1.25;padding-block:.15rem}
  .bf-break{overflow-wrap:anywhere}
  table.bf-stack td.bf-stack-actions > .flex{flex-wrap:wrap;justify-content:flex-end;gap:.25rem}
  /* .bf-stack-fill action cells: the buttons share the card's last line equally, each
     showing its .bf-show-mobile text label beside its icon. A button may sit alone or
     inside its own <form>; either way the direct child is what takes the share. A grid,
     not flex: flex adds a bare button's border on top of a zero basis, so it would come
     out 2px wider than one wrapped in a borderless <form>. */
  table.bf-stack td.bf-stack-actions.bf-stack-fill{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(0,1fr);gap:.5rem}
  /* Four or more don't fit a phone's width with their labels, so they go two by two. */
  table.bf-stack td.bf-stack-actions.bf-stack-fill:has(> :nth-child(4)){grid-auto-flow:row;grid-template-columns:repeat(2,minmax(0,1fr))}
  table.bf-stack td.bf-stack-fill > *{min-width:0;margin:0!important}
  table.bf-stack td.bf-stack-fill > form{display:flex}
  table.bf-stack td.bf-stack-fill .btn{width:100%;margin:0!important}
  /* One button size on every page's cards: the desktop tables mix btn-sm/btn-xs rows,
     which would leave some phones' action rows with small, hard-to-tap buttons. */
  table.bf-stack td.bf-stack-fill .btn{--size:calc(var(--size-field,.25rem) * 10);--fontsize:.875rem}
  /* Toolbars space their controls with a gap rather than per-button side margins, so a
     control that wraps to a new line starts flush with the ones above it. */
  .bf-toolbar{column-gap:.5rem}
  .bf-toolbar > *{margin-left:0!important;margin-right:0!important}
  /* Generic phone helpers for flex rows: stack vertically, or allow wrapping. */
  .bf-col-mobile{flex-direction:column;align-items:stretch;gap:.5rem}
  .bf-col-mobile > *{margin-left:0!important}
  .bf-wrap-mobile{flex-wrap:wrap;row-gap:.5rem}
  /* daisyUI stats: one per row (its own .stats-vertical, but only on phones). */
  .stats{grid-auto-flow:row;overflow-x:visible}
  .stats .stat:not(:last-child){border-inline-end:none;border-block-end:var(--border) dashed color-mix(in oklab,currentColor 10%,#0000)}
}

/* Income & Expenses: a header row per repeat type (Daily/Weekly/Monthly/Yearly), and row
   shading that restarts under each header (.bf-row-alt, set by the page), since daisyUI's
   :nth-child zebra would count the header rows and drift. */
table tbody tr.bf-row-alt{background-color:var(--color-base-200)}
tr.bf-group-row td{background:var(--color-base-300);font-weight:600;font-size:.85em;letter-spacing:.02em;text-transform:uppercase;color:color-mix(in oklab,var(--color-base-content) 70%,transparent)}
@media (max-width: 1023px){table.bf-stack tr.bf-group-row{padding:0;border-bottom:none}table.bf-stack tr.bf-group-row td{justify-content:flex-start;text-align:left;padding:.5rem .75rem;border-radius:.375rem}}
