/* ============================================================
 * Budget Gorion — app.css  (mobile-first)
 *
 * Organisation:
 *   1. Design tokens
 *   2. Base reset + typography
 *   3. Layout shells (topbar, wrap, footer)
 *   4. Cards, grids, page head
 *   5. KPIs
 *   6. Quick-add (the hero UI)
 *   7. Transactions list (cards, no tables)
 *   8. Tables (for /transactions and /categories)
 *   9. Buttons & chips
 *  10. Forms & filters
 *  11. Budget progress
 *  12. Flash + toast
 *  13. Auth card
 *  14. Charts
 *  15. Desktop overrides (media queries)
 * ============================================================ */

/* 1. Design tokens ------------------------------------------------
 *
 * Refreshed 2026-04-11 for an "enterprise finance tool" feel — think
 * Mercury / Stripe / Linear, not Revolut. Key changes:
 *   - Tighter border radii (10px primary, 6px small) — fewer pillows
 *   - Subtle 1px borders on every surface, weaker shadows
 *   - Slate-grey neutrals throughout (bg slightly warmer #f8fafc)
 *   - Slightly deeper good/warn/bad — less flat-design, more business
 *   - Inter as the primary font with system fallback
 *   - Three-tier shadow scale (sm/md/lg) instead of one
 *   - Explicit `--bg-inset` token for input fields
 */
:root {
    /* Opt out of browser-applied forced-dark filters (Samsung Internet,
     * Android WebView). Synced with the active [data-theme] below so the
     * browser's UA form controls / scrollbars match our palette. */
    color-scheme: light;

    /* Surfaces */
    --bg:         #f8fafc;
    --bg-card:    #ffffff;
    --bg-subtle:  #f1f5f9;
    --bg-inset:   #f8fafc;

    /* Ink */
    --ink:        #0f172a;
    --ink-soft:   #334155;
    --ink-dim:    #475569;
    --muted:      #64748b;

    /* Lines */
    --line:       #e2e8f0;
    --line-2:     #cbd5e1;
    --line-3:     #94a3b8;

    /* Brand + semantic */
    --primary:    #2563eb;
    --primary-d:  #1d4ed8;
    --primary-50: #eff6ff;
    --good:       #059669;
    --good-50:    #ecfdf5;
    --warn:       #d97706;
    --warn-50:    #fffbeb;
    --bad:        #dc2626;
    --bad-50:     #fef2f2;
    --accent:     #7c3aed;
    --dark:       #0f172a;

    /* Geometry */
    --radius-sm:  6px;
    --radius:     10px;
    --radius-l:   14px;
    --radius-xl:  18px;

    /* Layered shadow scale (lighter than before) */
    --shadow-sm:  0 1px 2px rgba(15,23,42,.05);
    --shadow:     0 1px 3px rgba(15,23,42,.07), 0 1px 2px rgba(15,23,42,.04);
    --shadow-md:  0 4px 6px -1px rgba(15,23,42,.07), 0 2px 4px -2px rgba(15,23,42,.05);
    --shadow-lg:  0 10px 20px -8px rgba(15,23,42,.15), 0 4px 8px -4px rgba(15,23,42,.08);
    --ring:       0 0 0 3px rgba(37,99,235,.18);

    /* Translucent topbar — overridden in dark mode */
    --topbar-bg:  rgba(255, 255, 255, .85);

    /* Type — system-ui resolves to SF Pro on Apple, Segoe UI on Windows,
     * Roboto on Android. All three are enterprise-grade sans-serifs and
     * load instantly with zero CSP / FOIT concerns. */
    --font-sans:  -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", system-ui, Roboto, "Helvetica Neue", Arial, sans-serif;
    --font-mono:  ui-monospace, "SF Mono", Menlo, Consolas, monospace;

    /* Safe area */
    --safe-top:    env(safe-area-inset-top, 0px);
    --safe-bottom: env(safe-area-inset-bottom, 0px);
    --safe-left:   env(safe-area-inset-left, 0px);
    --safe-right:  env(safe-area-inset-right, 0px);
}

/* Feature 8: dark mode tokens. `html[data-theme="dark"]` is set
 * explicitly from the /settings toggle; `data-theme="auto"` respects
 * the OS preference via prefers-color-scheme. */
:root[data-theme="auto"] { color-scheme: light dark; }
:root[data-theme="dark"] { color-scheme: dark; }
:root[data-theme="dark"] {
    --bg:         #0b1220;
    --bg-card:    #111a2e;
    --bg-subtle:  #1a253f;
    --bg-inset:   #0e1628;
    --ink:        #e2e8f0;
    --ink-soft:   #cbd5e1;
    --ink-dim:    #cbd5e1;
    --muted:      #94a3b8;
    --line:       #1f2c4a;
    --line-2:     #2a3a60;
    --line-3:     #3e4f76;
    --primary:    #60a5fa;
    --primary-d:  #3b82f6;
    --primary-50: rgba(96, 165, 250, .12);
    --good:       #10b981;
    --good-50:    rgba(16, 185, 129, .12);
    --warn:       #f59e0b;
    --warn-50:    rgba(245, 158, 11, .12);
    --bad:        #f87171;
    --bad-50:     rgba(248, 113, 113, .12);
    --accent:     #a78bfa;
    --dark:       #020617;
    --topbar-bg:  rgba(11, 18, 32, .88);
    --shadow-sm:  0 1px 2px rgba(0,0,0,.4);
    --shadow:     0 1px 3px rgba(0,0,0,.5), 0 1px 2px rgba(0,0,0,.35);
    --shadow-md:  0 4px 6px -1px rgba(0,0,0,.5), 0 2px 4px -2px rgba(0,0,0,.35);
    --shadow-lg:  0 15px 30px -10px rgba(0,0,0,.65), 0 6px 12px -4px rgba(0,0,0,.4);
    --ring:       0 0 0 3px rgba(96, 165, 250, .25);
}
@media (prefers-color-scheme: dark) {
    :root[data-theme="auto"] {
        --bg:         #0b1220;
        --bg-card:    #111a2e;
        --bg-subtle:  #1a253f;
        --bg-inset:   #0e1628;
        --ink:        #e2e8f0;
        --ink-soft:   #cbd5e1;
        --ink-dim:    #cbd5e1;
        --muted:      #94a3b8;
        --line:       #1f2c4a;
        --line-2:     #2a3a60;
        --line-3:     #3e4f76;
        --primary:    #60a5fa;
        --primary-d:  #3b82f6;
        --primary-50: rgba(96, 165, 250, .12);
        --good:       #10b981;
        --good-50:    rgba(16, 185, 129, .12);
        --warn:       #f59e0b;
        --warn-50:    rgba(245, 158, 11, .12);
        --bad:        #f87171;
        --bad-50:     rgba(248, 113, 113, .12);
        --accent:     #a78bfa;
        --dark:       #020617;
        --topbar-bg:  rgba(11, 18, 32, .88);
        --shadow-sm:  0 1px 2px rgba(0,0,0,.4);
        --shadow:     0 1px 3px rgba(0,0,0,.5), 0 1px 2px rgba(0,0,0,.35);
        --shadow-md:  0 4px 6px -1px rgba(0,0,0,.5), 0 2px 4px -2px rgba(0,0,0,.35);
        --shadow-lg:  0 15px 30px -10px rgba(0,0,0,.65), 0 6px 12px -4px rgba(0,0,0,.4);
        --ring:       0 0 0 3px rgba(96, 165, 250, .25);
    }
}

/* 2. Base reset + typography ------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }

/* Screen-reader-only utility. Content is accessible to ATs but
   invisible on screen. Used for permanent dialog labels etc. */
.visually-hidden {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}
html {
    -webkit-text-size-adjust: 100%;
    text-rendering: optimizeLegibility;
    /* Reserve space for the vertical scrollbar even when `body { overflow:hidden }`
     * is applied (used to lock scrolling while the quick-add sheet is open).
     * Without this, the page visibly "jumps" sideways as the scrollbar gutter
     * disappears — a flash of the whole background. */
    scrollbar-gutter: stable;
}
body {
    font-family: var(--font-sans);
    background: var(--bg);
    color: var(--ink);
    line-height: 1.5;
    font-size: 14.5px;
    letter-spacing: -.005em;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    min-height: 100dvh;
    overflow-x: hidden;
}
a { color: var(--primary); text-decoration: none; transition: color .12s ease; }
a:hover { color: var(--primary-d); text-decoration: underline; text-underline-offset: 2px; }
.link { color: var(--primary); font-weight: 500; font-size: .82rem; }
/* Touch-friendly tap area for inline .link elements (e.g. "ρυθμίσεις →"
 * inside card headers). Desktop keeps the tight inline look; on coarse
 * pointers we pad to ≥ 32 px vertical (BUG-019). */
@media (pointer: coarse) {
    .link {
        display: inline-block;
        padding: .3rem .45rem;
        margin: -.3rem -.45rem;
        min-height: 32px;
        line-height: 1.4;
    }
}

h1, h2, h3 { margin: 0 0 .3rem 0; line-height: 1.25; letter-spacing: -.018em; color: var(--ink); }
h1 { font-size: 1.4rem;  font-weight: 700; }
h2 { font-size: 1rem;    font-weight: 600; }
h3 { font-size: .9rem;   font-weight: 600; }

small { color: var(--muted); font-size: .82rem; }
.muted { color: var(--muted); }
/* BUG-093: theme-aware warning/error text. Replace inline
 * `style="color:#f59e0b/#dc2626"` so dark mode picks the right token. */
/* BUG-094: explicit darker shades για WCAG AA σε light theme — τα global
 * --warn (#d97706, 2.97:1) / --bad (#dc2626, 4.0:1) δεν περνούν AA σε small
 * text πάνω σε λευκό. Dark theme οι defaults ήδη contrast-pass. */
.muted-warn  { color: #b45309; }
.muted-error { color: #b91c1c; }
:root[data-theme="dark"] .muted-warn,
:root[data-theme="dark"] .muted-error {
    color: var(--warn);
}
:root[data-theme="dark"] .muted-error { color: var(--bad); }

/* BUG-094: skip-link (WCAG 2.4.1) — hidden by default, jumps to top-left
 * όταν παίρνει focus με Tab. */
.skip-link {
    position: absolute;
    top: -100px;
    left: 0;
    z-index: 9999;
    padding: .75rem 1rem;
    background: var(--primary);
    color: #fff;
    font-weight: 600;
    border-bottom-right-radius: var(--radius);
    transition: top .12s ease;
}
.skip-link:focus,
.skip-link:focus-visible {
    top: 0;
    outline: 2px solid var(--ink);
    outline-offset: 2px;
}
.num { text-align: right; font-variant-numeric: tabular-nums; }
.pos { color: var(--good); font-weight: 600; }
.neg { color: var(--ink);  font-weight: 600; }

/* Prevent iOS zoom on input focus: font-size >= 16px */
input, select, textarea, button { font-size: 16px; font-family: inherit; }

/* Re-skin the native × clear button on every <input type="search">.
 * The default webkit glyph is heavy and sits flush against the pill's
 * rounded border, visually merging with the focus ring. Force size +
 * a generous right margin so it breathes away from the border. Use
 * !important so the UA defaults (which set a specific size via
 * `-webkit-appearance`) can't sneak back in. iOS-safe — BUG-028
 * forbids `appearance:none` on the input ITSELF, not on this pseudo.
 * Firefox has no native × here so no -moz counterpart needed. */
input[type="search"]::-webkit-search-cancel-button {
    -webkit-appearance: none !important;
    appearance: none !important;
    display: inline-block !important;
    width: 14px !important;
    height: 14px !important;
    margin: 0 .55rem 0 .3rem !important;
    padding: 0 !important;
    border: 0 !important;
    background: center/contain no-repeat
        url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2.5' stroke-linecap='round'><path d='M18 6 6 18M6 6l12 12'/></svg>") !important;
    cursor: pointer;
    opacity: .6;
    transition: opacity .15s;
}
input[type="search"]::-webkit-search-cancel-button:hover { opacity: 1; }
/* Suppress the legacy webkit search decoration / results pseudos —
 * some browsers reserve horizontal space for a magnifier glyph even
 * when we don't want it, which also crowds the left side. */
input[type="search"]::-webkit-search-decoration,
input[type="search"]::-webkit-search-results-button,
input[type="search"]::-webkit-search-results-decoration {
    -webkit-appearance: none !important;
    appearance: none !important;
    display: none !important;
}

/* 3. Layout shells ----------------------------------------------- */
.wrap {
    width: 100%;
    max-width: 1200px;
    margin: 0 auto;
    padding: 1rem;
    padding-left:  max(1rem, var(--safe-left));
    padding-right: max(1rem, var(--safe-right));
}

.topbar {
    /* Translucent "glass" surface. The token --topbar-bg is a
     * semi-transparent white in light mode and a semi-transparent near-
     * black in dark mode (see the :root[data-theme=…] overrides). */
    background: var(--topbar-bg, rgba(255, 255, 255, .85));
    color: var(--ink);
    position: sticky; top: 0; z-index: 50;
    border-bottom: 1px solid var(--line);
    padding-top: var(--safe-top);
    backdrop-filter: saturate(180%) blur(8px);
    -webkit-backdrop-filter: saturate(180%) blur(8px);
}
.topbar-inner {
    display: flex;
    align-items: center;
    gap: .8rem;
    padding: .65rem 1rem;
    padding-left:  max(1rem, var(--safe-left));
    padding-right: max(1rem, var(--safe-right));
    max-width: 1200px;
    margin: 0 auto;
    flex-wrap: wrap;
}
.brand {
    display: flex;
    align-items: center;
    gap: .6rem;
    color: var(--ink);
    font-weight: 700;
    font-size: 1rem;
    letter-spacing: -.015em;
}
.brand:hover { text-decoration: none; color: var(--ink); }
.brand-mark {
    display: inline-flex; align-items: center; justify-content: center;
    width: 30px; height: 30px;
    border-radius: var(--radius-sm);
    background: linear-gradient(135deg, var(--primary) 0%, var(--accent) 100%);
    color: #fff;
    font-weight: 800;
    font-size: .95rem;
    letter-spacing: -.02em;
    box-shadow: var(--shadow-sm), inset 0 1px 0 rgba(255,255,255,.2);
}
/* Brand mark bumps to 36×36 on touch so the whole header row clears the
 * 40 px touch-target guideline (BUG-019). Desktop keeps the 30 px badge. */
@media (pointer: coarse) {
    .brand-mark { width: 36px; height: 36px; font-size: 1rem; }
}
.brand-mark.big {
    width: 64px;
    height: 64px;
    font-size: 2rem;
    border-radius: var(--radius-l);
    margin: 0 auto .8rem;
    box-shadow: var(--shadow-md), inset 0 1px 0 rgba(255,255,255,.2);
}

.nav {
    order: 3;
    width: 100%;
    display: flex;
    gap: .15rem;
    /* Single-row, no scroll — flex items sized to fit the viewport.
       Avoids overflow-x:auto which causes iOS Safari tap delays. */
    flex-wrap: nowrap;
    justify-content: stretch;
    padding: .35rem 0 .15rem;
    margin: 0;
    margin-top: .55rem;
}
.nav a {
    flex: 1 1 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--ink-dim);
    padding: .5rem .35rem;
    border-radius: var(--radius-sm);
    font-weight: 500;
    font-size: .78rem;
    letter-spacing: -.01em;
    white-space: nowrap;
    transition: background .12s ease, color .12s ease;
    min-height: 44px;
    text-decoration: none;
    -webkit-tap-highlight-color: transparent;
    -webkit-user-select: none;
    user-select: none;
    touch-action: manipulation;
}
.nav a:hover {
    background: var(--bg-subtle);
    color: var(--ink);
    text-decoration: none;
}
.nav a.active {
    background: var(--primary-50);
    color: var(--primary-d);
    font-weight: 600;
}

/* ============================================================
 * Avatars (img if uploaded, colored initials otherwise)
 * ============================================================ */
.avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    border-radius: 50%;
    overflow: hidden;
    color: #fff;
    font-weight: 600;
    line-height: 1;
    text-transform: uppercase;
    user-select: none;
    background: #94a3b8;
    /* Inner highlight + outer per-user ring (CSS var set inline by
       user_avatar_html() from users.color; transparent if absent). */
    box-shadow: 0 0 0 1px rgba(255,255,255,.12) inset,
                0 0 0 1px var(--ring-color, transparent);
}
.avatar img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}.avatar-sm { width: 32px; height: 32px; font-size: .85rem; }
.avatar-md { width: 44px; height: 44px; font-size: 1.05rem; }.avatar-xl { width: 88px; height: 88px; font-size: 2rem; }


/* ============================================================
 * Topbar user menu — button + avatar + dropdown panel
 * ============================================================ */
.user-menu {
    position: relative;
    margin-left: auto;
}
.user-menu-trigger {
    display: inline-flex;
    align-items: center;
    gap: .55rem;
    padding: .3rem .55rem .3rem .35rem;
    background: var(--bg-card);
    border: 1px solid var(--line);
    color: var(--ink);
    border-radius: var(--radius);
    cursor: pointer;
    font: inherit;
    min-height: 40px;
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
    transition: background .12s ease, border-color .12s ease, box-shadow .12s ease;
    box-shadow: var(--shadow-sm);
}
.user-menu-trigger:hover,
.user-menu-trigger[aria-expanded="true"] {
    background: var(--bg-subtle);
    border-color: var(--line-2);
}
.user-menu-trigger .who {
    display: inline-block;
    font-size: .85rem;
    font-weight: 500;
    color: var(--ink-soft);
    max-width: 14ch;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.user-menu-trigger .chev {
    font-size: .7rem;
    color: var(--muted);
    transition: transform .15s ease;
}
.user-menu-trigger[aria-expanded="true"] .chev {
    transform: rotate(180deg);
}

.user-menu-panel {
    position: absolute;
    top: calc(100% + .4rem);
    right: 0;
    min-width: 220px;
    background: var(--bg-card);
    color: var(--ink);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow-lg);
    padding: .4rem;
    z-index: 90;
    animation: menuDrop .14s ease-out;
}
@keyframes menuDrop {
    from { opacity: 0; transform: translateY(-6px); }
    to   { opacity: 1; transform: none; }
}
/* Logout is a <form><button> (POST+CSRF) but must look identical to the
 * sibling <a> items in the panel — shared styles in one selector list. */
.user-menu-panel a,
.user-menu-panel .menu-logout-btn,
.user-menu-panel .menu-privacy-toggle {
    display: flex;
    align-items: center;
    gap: .6rem;
    padding: .7rem .8rem;
    color: inherit;
    border-radius: 10px;
    font-size: .95rem;
    min-height: 44px;
}
.user-menu-panel a { text-decoration: none; }
.user-menu-panel .menu-logout-btn,
.user-menu-panel .menu-privacy-toggle {
    width: 100%;
    background: none;
    border: 0;
    font-family: inherit;
    text-align: left;
    cursor: pointer;
}
.user-menu-panel a:hover,
.user-menu-panel a:focus-visible,
.user-menu-panel .menu-logout-btn:hover,
.user-menu-panel .menu-logout-btn:focus-visible,
.user-menu-panel .menu-privacy-toggle:hover,
.user-menu-panel .menu-privacy-toggle:focus-visible {
    background: var(--bg-subtle, #f1f5f9);
    outline: none;
}
.user-menu-panel a:focus-visible,
.user-menu-panel .menu-logout-btn:focus-visible,
.user-menu-panel .menu-privacy-toggle:focus-visible {
    box-shadow: inset 0 0 0 2px var(--primary, #2563eb);
}
.user-menu-panel .mi {
    display: inline-flex;
    width: 1.35rem;
    justify-content: center;
    font-size: 1.05rem;
}

/* ============================================================
 * Settings page
 * ============================================================ */
.settings-avatar {
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
}
.settings-avatar-preview {
    display: flex;
    align-items: center;
    gap: 1rem;
}
.settings-avatar-preview strong {
    display: block;
    font-size: 1.05rem;
}
.settings-avatar-preview small {
    display: block;
    font-size: .85rem;
}
/* Drop-zone style file picker (used in /import). The native input is
 * visually hidden but stays focusable; the label is the click target.
 * .is-dragover toggled by JS for visual drag feedback. .has-file when
 * a file is chosen (changes icon/title via JS too). */
.dropzone {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: .35rem;
    padding: 2rem 1rem;
    border: 2px dashed var(--line);
    border-radius: var(--radius-l, 14px);
    background: var(--bg-subtle, rgba(100,116,139,.06));
    cursor: pointer;
    transition: border-color .15s, background .15s;
}
.dropzone:hover,
.dropzone:focus-within { border-color: var(--primary); }
.dropzone.is-dragover {
    border-color: var(--primary);
    background: rgba(37,99,235,.08);
}
.dropzone.has-file {
    border-style: solid;
    border-color: var(--good, #16a34a);
    background: rgba(22,163,74,.08);
}
.dropzone-input,
.dropzone input[type="file"] {
    position: absolute;
    width: 1px; height: 1px;
    opacity: 0;
    pointer-events: none;
}
.dropzone-icon { font-size: 2.4rem; line-height: 1; }
.dropzone-title {
    font-weight: 600;
    font-size: 1rem;
    color: var(--ink);
    text-align: center;
    word-break: break-all;
}
.dropzone-hint {
    font-size: .82rem;
    color: var(--muted);
    text-align: center;
}

.form-actions {
    display: flex;
    align-items: center;
    gap: .75rem;
    flex-wrap: wrap;
}

/* Password change — live validation rules. Each <li> is neutral grey
 * by default (•), flips green with ✓ when its check passes, red with
 * ✗ once the field has been touched and still fails. Submit button
 * stays `disabled` via JS until every rule is green. */
.pw-rules {
    list-style: none;
    padding: 0;
    margin: .25rem 0 0;
    display: flex;
    flex-direction: column;
    gap: .25rem;
    font-size: .88rem;
    color: var(--muted);
}
.pw-rules li {
    position: relative;
    padding-left: 1.25rem;
}
.pw-rules li::before {
    content: '•';
    position: absolute;
    left: 0;
    font-weight: 700;
}
.pw-rules li.ok  { color: var(--good); }
.pw-rules li.ok::before  { content: '✓'; }
.pw-rules li.bad { color: var(--muted); }
.pw-rules li.bad::before { content: '•'; }

[data-password-form] .form-actions { justify-content: flex-end; }
[data-pw-submit]:disabled { opacity: .55; cursor: not-allowed; }
@media (min-width: 720px) {
    .settings-avatar {
        flex-direction: row;
        align-items: center;
        justify-content: space-between;
    }
    .settings-avatar-form {
        border-top: 0;
        padding-top: 0;
        border-left: 1px dashed var(--line-2);
        padding-left: 1.5rem;
        flex: 1;
        max-width: 420px;
    }
}
.avatar-buttons {
    display: flex;
    gap: .6rem;
    align-items: center;
    flex-wrap: wrap;
}

/* ---- Avatar editor (full-screen, WhatsApp-style) ---- */
.avatar-editor[hidden] { display: none; }
.avatar-editor {
    position: fixed;
    inset: 0;
    z-index: 200;
    background: #000;
    display: flex;
    flex-direction: column;
    user-select: none;
    -webkit-user-select: none;
}
.avatar-editor-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: .6rem .8rem;
    padding-top: calc(.6rem + var(--safe-top, 0px));
    background: rgba(0,0,0,.85);
    z-index: 1;
}
.avatar-editor-title {
    color: rgba(255,255,255,.6);
    font-size: .85rem;
    font-weight: 500;
}
.avatar-editor-cancel,
.avatar-editor-done {
    background: none;
    border: none;
    color: #fff;
    font-size: .9rem;
    font-weight: 600;
    font-family: inherit;
    cursor: pointer;
    padding: .5rem .7rem;
    border-radius: var(--radius-sm);
    min-height: 44px;
    -webkit-tap-highlight-color: transparent;
}
.avatar-editor-cancel { color: rgba(255,255,255,.7); }
.avatar-editor-done { color: #60a5fa; }
.avatar-editor-done:disabled { opacity: .4; }
.avatar-editor-canvas-wrap {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    touch-action: none;
}
.avatar-editor-canvas-wrap canvas {
    display: block;
}
.avatar-editor-toolbar {
    display: flex;
    align-items: center;
    gap: .7rem;
    padding: .7rem 1.2rem;
    padding-bottom: calc(.7rem + var(--safe-bottom, 0px));
    background: rgba(0,0,0,.85);
}
.avatar-editor-toolbar .btn { color: #fff; border-color: rgba(255,255,255,.2); font-size: 1.1rem; }
.avatar-editor-zoom {
    flex: 1;
    accent-color: #60a5fa;
    height: 4px;
}
.avatar-editor-zoom-label {
    color: rgba(255,255,255,.5);
    font-size: .78rem;
    min-width: 3.5ch;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* iOS-style toggle switch for /settings */
.settings-toggle {
    display: flex;
    align-items: center;
    gap: .75rem;
    margin-top: .5rem;
}
.switch {
    display: inline-flex;
    align-items: center;
    gap: .75rem;
    cursor: pointer;
    user-select: none;
    min-height: 44px;
}
.switch input {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}
.switch-track {
    position: relative;
    width: 54px;
    height: 30px;
    background: #cbd5e1;
    border-radius: 999px;
    transition: background .18s ease;
    flex-shrink: 0;
}
.switch-track::after {
    content: '';
    position: absolute;
    top: 3px;
    left: 3px;
    width: 24px;
    height: 24px;
    background: #fff;
    border-radius: 50%;
    box-shadow: 0 2px 4px rgba(15,23,42,.25);
    transition: transform .18s ease;
}
.switch input:checked + .switch-track {
    background: #22c55e;
}
.switch input:checked + .switch-track::after {
    transform: translateX(24px);
}
.switch input:focus-visible + .switch-track {
    box-shadow: 0 0 0 3px rgba(34,197,94,.35);
}
.switch-label {
    font-weight: 600;
    font-size: .95rem;
}

/* 4. Cards / grids / page head ----------------------------------- */
.card {
    background: var(--bg-card);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 1.15rem 1.25rem;
    box-shadow: var(--shadow-sm);
    margin-bottom: .8rem;
}
.card.narrow { max-width: 680px; margin-left: auto; margin-right: auto; }
.card-title {
    font-size: .92rem;
    font-weight: 600;
    color: var(--ink);
    letter-spacing: -.01em;
    margin: 0 0 .9rem;
    display: flex;
    align-items: center;
    gap: .55rem;
    flex-wrap: wrap;
}
/* Secondary CTAs inside a .card-title (e.g. "Αντιγραφή από Σεπτέμβριο"
 * on /budgets — 23 characters at the widest month) must never wrap
 * mid-phrase. flex-wrap on the parent already lets the button drop to
 * its own line when width is tight; nowrap here keeps the label
 * atomic. BUG-022 (2026-04-16). */
.card-title form button,
.card-title .btn { white-space: nowrap; }

.page-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
    margin: 1rem 0 1.2rem;
    flex-wrap: wrap;
}
.page-head > div,
.page-head > form { min-width: 0; }
.page-head h1 {
    margin: 0;
    min-width: 0;
    overflow-wrap: anywhere;
    font-size: 1.5rem;
    font-weight: 700;
    letter-spacing: -.022em;
}
.page-head p {
    margin: .15rem 0 0;
    color: var(--muted);
    font-size: .88rem;
}
/* Centered variant — used on single-card pages where both the heading
 * and the (.card.narrow) body sit centered in the viewport. */
.page-head-centered { justify-content: center; text-align: center; }
.grid { display: grid; gap: .8rem; }
.grid-2 { grid-template-columns: 1fr; }
.span-2 { grid-column: 1 / -1; }

/* Three-up row for clusters of small equal-size cards (e.g. the
 * pie/doughnut trio on /stats). Stacks on mobile, 3 columns ≥720 px.
 * Use together with `.span-2` so the row spans both tracks of the
 * parent `.grid.grid-2`. */
.charts-row-3 { display: grid; gap: .8rem; grid-template-columns: 1fr; }
@media (min-width: 720px) {
    .charts-row-3 { grid-template-columns: repeat(3, 1fr); }
}

/* 5. KPIs -------------------------------------------------------- */
.kpis {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: .6rem;
    margin-bottom: 1rem;
}
.kpi {
    background: var(--bg-card);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: .85rem 1rem;
    box-shadow: var(--shadow-sm);
    position: relative;
    overflow: hidden;
}
.kpi::before {
    /* Slim accent rail on the top edge — replaces the heavy left-border */
    content: '';
    position: absolute;
    top: 0; left: 0; right: 0;
    height: 2px;
    background: var(--line-2);
}
.kpi-label {
    font-size: .68rem;
    text-transform: uppercase;
    letter-spacing: .07em;
    color: var(--muted);
    font-weight: 600;
    margin-bottom: .35rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.kpi-value {
    font-size: 1.35rem;
    font-weight: 700;
    color: var(--ink);
    letter-spacing: -.022em;
    font-variant-numeric: tabular-nums;
    display: flex;
    align-items: center;
    gap: .4rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    line-height: 1.1;
}.kpi-income::before  { background: var(--good); }
.kpi-expense::before { background: var(--bad); }
.kpi-good::before    { background: var(--primary); }
.kpi-bad::before     { background: var(--warn); }
/* Brief post-save highlight on KPI cards. The peak color is the
 * warn-50 token so it reads "softly positive" on both light and
 * dark surfaces — was hardcoded #fef3c7 which washed out on dark
 * backgrounds. */
.kpi.flashing { animation: kpiFlash .6s ease; }
@keyframes kpiFlash {
    0%, 100% { background: var(--bg-card); }
    40%      { background: var(--warn-50); }
}

/* 6. Tiles + bottom sheet (the hero UI) -------------------------- */

/* --- Tiles section --- */
.tiles-section {
    background: var(--bg-card);
    border-radius: var(--radius-l);
    padding: 1rem 1rem 1.1rem;
    box-shadow: var(--shadow);
    margin-bottom: 1.1rem;
    border: 1px solid var(--line);
}
.tiles-section .btn-new-tx {
    margin-top: 1rem;
    display: flex;
    width: 100%;
}
/* Desktop: the "+ Νέα καταχώρηση" CTA lives inside .tiles-head on the
 * same row as the title (right-aligned). Mobile keeps the bottom full-
 * width button. */
.btn.btn-new-tx-top { display: none; }
@media (min-width: 720px) {
    .btn.btn-new-tx-top {
        display: inline-flex;
        margin-left: auto;
    }
    .tiles-section > .btn-new-tx { display: none; }
}

/* /transactions (picker-only) button group */
.new-tx-buttons {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .6rem;
    flex-wrap: wrap;
}
.tiles-head {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin-bottom: .9rem;
    flex-wrap: wrap;
}
.tiles-head h2 { margin-right: auto; }
.tiles-head h2 {
    margin: 0;
    font-size: 1.05rem;
    display: flex;
    align-items: center;
    gap: .5rem;
}
.tiles-edit {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    font-size: .95rem;
    background: #f1f5f9;
    border-radius: 8px;
    border: 1px solid var(--line);
    text-decoration: none;
    transition: background .15s;
}
.tiles-edit:hover { background: #e2e8f0; text-decoration: none; }.tiles-kind button.active {
    background: #fff;
    color: var(--ink);
    box-shadow: 0 1px 3px rgba(0,0,0,.1);
}
.tiles-kind button[data-tile-kind="expense"].active { color: var(--bad); }
.tiles-kind button[data-tile-kind="income"].active  { color: var(--good); }

.tiles {
    display: grid;
    /* `minmax(0, 1fr)` αντί για `1fr` → εξαναγκάζει ομοιόμορφες columns
     * ακόμα κι όταν το intrinsic content (nowrap label με μεγάλο text)
     * θα ζητούσε μεγαλύτερο track width. Χωρίς αυτό, ένα tile με long
     * label σπρώχνει την track και τα 3 tiles ξεφεύγουν εκτός container
     * σε narrow viewports (BUG-042). */
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: .7rem;
}
/* Collapse tiles to 3 rows on mobile; tap "Δες περισσότερες" to expand.
   We clip the grid with max-height + overflow:hidden (rather than
   display:none on the 10th+ child). Hiding individual children caused
   the remaining rows to redistribute, which visibly shrank the icons. */
@media (max-width: 479px) {
    .tiles-section:not(.tiles-expanded) .tiles {
        /* +6px buffer: tiles can grow a few px past min-height in the wild
         * (padding rounding, text-wrap on long names), and a hard clip at
         * exactly 3 × min-height shaved the bottom edge of the 3rd row. */
        max-height: calc(108px * 3 + .7rem * 2 + 8px);
        overflow: hidden;
    }
    /* Edit mode: un-clip so the red ✕ remove badges (which sit at
     * top:-8px; right:-8px; outside each tile) aren't shaved by the
     * grid's overflow:hidden. */
    .tiles-section.edit-mode .tiles {
        max-height: none;
        overflow: visible;
    }
}
.tiles-more-btn {
    display: none;
    width: 100%;
    margin-top: 1.3rem;
    padding: .55rem .8rem;
    background: transparent;
    border: 1px solid var(--line);
    border-radius: 12px;
    color: var(--ink-soft);
    font-family: inherit;
    font-size: .9rem;
    font-weight: 600;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    transition: background .15s, border-color .15s;
}
.tiles-more-btn:hover { background: var(--bg-subtle); border-color: var(--line-2); }
@media (max-width: 479px) {
    .tiles-more-btn { display: block; }
}
.tile {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-start;
    gap: .5rem;
    padding: 1rem .3rem .85rem;
    background: #fff;
    border: 1px solid var(--line);
    border-radius: 16px;
    cursor: pointer;
    font-family: inherit;
    font-size: .78rem;
    font-weight: 600;
    color: var(--ink);
    transition: transform .08s, box-shadow .15s, border-color .15s;
    -webkit-tap-highlight-color: transparent;
    /* Defensive: η πραγματική λύση για grid blowout είναι το
     * `minmax(0, 1fr)` στο `.tiles` template· κρατάμε αυτό για να
     * προστατεύσει σε flex/non-grid contexts όπου το tile θα
     * τοποθετηθεί. (BUG-042) */
    min-width: 0;
    min-height: 108px;
    text-align: center;
    line-height: 1.2;
    touch-action: manipulation;
}
.tile:active {
    transform: scale(.95);
    box-shadow: 0 2px 8px rgba(15,23,42,.08);
}
.tile:focus-visible {
    outline: none;
    border-color: var(--primary);
    box-shadow: var(--ring);
}
.tile[hidden] { display: none !important; }

.tile-icon {
    width: 56px;
    height: 56px;
    border-radius: 16px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 1.9rem;
    color: #fff;
    flex-shrink: 0;
    box-shadow: 0 4px 12px rgba(15,23,42,.12);
}
.tile-name {
    width: 100%;
    padding: 0 .2rem;
    /* Aggressive fluid font: 0.6rem σε 360 px viewport (Samsung S23),
     * scales linearly to 0.78rem σε 480 px+. Tuned ώστε το «Βενζίνη /
     * Diesel» (15 chars) να χωρά σε 1 line στις περισσότερες mobile
     * συσκευές. Πιο μακριά labels (>18 chars σε narrow viewport) πέφτουν
     * πίσω σε ellipsis. Αν προσθέσεις πολλές κατηγορίες με μακρύ όνομα,
     * επανεκτιμάμε αν χρειάζεται 2-line wrap. */
    font-size: clamp(.6rem, calc(.06rem + 2.4vw), .78rem);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Brief flash animation after a successful entry */
.tile.tile-flash { animation: tileFlash .55s ease; }
@keyframes tileFlash {
    0%   { transform: scale(1);    box-shadow: 0 0 0 0 rgba(16,185,129,.6); }
    30%  { transform: scale(1.06); box-shadow: 0 0 0 10px rgba(16,185,129,0); }
    100% { transform: scale(1);    box-shadow: 0 0 0 0 rgba(16,185,129,0); }
}

/* ---- Tile edit mode ---- */

/* Wrapper: position context for the remove badge, stretches tile to fill grid */
.tile-wrap { position: relative; }
.tile-wrap .tile { width: 100%; height: 100%; }

/* Edit toggle button */
/* Base styling comes from `.icon-btn`; only the active (edit-mode ON)
 * state needs a filled-blue override to signal the toggle is engaged. */
.tiles-edit-btn.active {
    background: var(--primary);
    color: #fff;
}
.tiles-edit-btn.active:hover { background: var(--primary-d); color: #fff; }

/* Remove badge — outside the tile button, inside .tile-wrap */
.tile-remove-btn {
    display: none;
    position: absolute;
    top: -8px; right: -8px;
    width: 24px; height: 24px;
    border-radius: 50%;
    background: var(--bad);
    color: #fff;
    font-size: .75rem; font-weight: 700;
    align-items: center; justify-content: center;
    line-height: 1;
    z-index: 3;
    cursor: pointer;
    box-shadow: 0 2px 6px rgba(0,0,0,.3);
    border: 2px solid var(--bg-card);
    padding: 0;
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
}
.tile-remove-btn:active { transform: scale(.85); }

/* Edit toolbar (add picker) */
.tiles-edit-toolbar[hidden] { display: none; }
.tiles-edit-toolbar {
    margin-top: .8rem;
    display: flex;
    justify-content: center;
    gap: .5rem;
}
.tiles-add-select {
    font-size: .88rem;
    padding: .5rem .9rem;
    border-radius: var(--radius);
    border: 2px dashed var(--line-2);
    background: var(--bg-subtle);
    color: var(--ink-soft);
    cursor: pointer;
    min-width: 240px;
    min-height: 44px;
    font-family: inherit;
    transition: border-color .15s;
}
.tiles-add-select:focus-visible {
    border-color: var(--primary);
    outline: none;
    box-shadow: var(--ring);
}

/* ---- Edit mode ON ---- */
.tiles-section.edit-mode .tile-remove-btn { display: inline-flex; }
.tiles-section.edit-mode .btn-new-tx { display: none; }
.tiles-section.edit-mode .tiles-more-btn { display: none; }
.tiles-section.edit-mode .tile {
    cursor: grab;
    user-select: none;
    -webkit-user-select: none;
}
.tiles-section.edit-mode .tile:active { cursor: grabbing; }

/* Wiggle in edit mode */
.tiles-section.edit-mode .tile-wrap {
    animation: tileWiggle .25s ease-in-out infinite alternate;
}
.tiles-section.edit-mode .tile-wrap:nth-child(even) { animation-delay: .12s; }
.tiles-section.edit-mode .tile-wrap:nth-child(3n)   { animation-delay: .06s; }
.tiles-section.edit-mode .tile-wrap.dragging { animation: none; }
@keyframes tileWiggle {
    0%   { transform: rotate(-.8deg); }
    100% { transform: rotate(.8deg);  }
}

/* Drag placeholder + dragging state */
.tiles .drag-placeholder {
    border: 2px dashed var(--line-2);
    border-radius: 16px;
    background: var(--bg-subtle);
    opacity: .4;
    min-height: 108px;
}
.tiles .tile-wrap.dragging { opacity: .7; }
.tiles .tile-wrap.dragging .tile { box-shadow: var(--shadow-lg); transform: scale(1.05); }

/* ============================================================
 * Quick-add form (legacy no-JS fallback — the live UI uses the
 * bottom sheet + numpad; these rules cover the .quick-form markup
 * still emitted by server-rendered forms like /transactions with
 * ?new=1 or the in-page edit fallback, if ever reintroduced)
 * ------------------------------------------------------------
 * Mobile (default): everything stacks in a single column — each
 * label takes full width, touch targets are comfy. At ≥560px we
 * switch to a two-column grid for denser desktop/tablet use, with
 * the note field and action buttons spanning both columns.
 * ============================================================ */
.quick-form { display: flex; flex-direction: column; gap: .9rem; }@media (min-width: 560px) {
    .quick-grid {
        grid-template-columns: 1fr 1fr;
    }
    .quick-grid > .qa-note,
    .quick-grid > .qa-btn,
    .quick-grid > .form-actions {
        grid-column: 1 / -1;
    }
}.quick-grid > .qa-btn { width: 100%; }
/* ============================================================
 * .overlay — shared base for every modal/dialog/bottom-sheet.
 * Covers: .sheet (numpad), .child-picker (υποκατηγορίες),
 * .category-picker (+ Νέα Καταχώρηση), and any future overlay.
 *
 * Component-specific classes only override sizing / alignment
 * / z-index. Everything below (positioning, pointer-events,
 * opacity fade, [hidden] handling, backdrop color, page scroll
 * lock) is shared — so new overlays get it by adding `.overlay`
 * to the root element plus a `.overlay-backdrop` backdrop child
 * (or by keeping a component-specific *-backdrop that inherits
 * its styling here).
 *
 * Backdrop color unified at rgba(15,23,42,.55) (2026-04-20).
 * Page scroll lock is DECLARATIVE via :has() — no JS needed;
 * as long as an overlay with `aria-hidden="false"` is in the
 * DOM, body overflow is hidden.
 * ========================================================= */
.overlay {
    position: fixed;
    inset: 0;
    z-index: 100;
    display: flex;
    flex-direction: column;
    pointer-events: none;
    opacity: 0;
    transition: opacity .2s ease;
}
.overlay[aria-hidden="false"] { pointer-events: auto; opacity: 1; }
.overlay[hidden] { display: none; }
/* Unified backdrop: same color for every overlay, catches touches so
 * the locked page can't be panned from the transparent area around the
 * overlay body. `touch-action: none` stays on the backdrop (a sibling
 * of the scroll container) — NOT on html/body, which would break
 * nested `pan-y` via the touch-action intersection rule. */
.overlay-backdrop,
.sheet-backdrop,
.child-picker-backdrop,
.category-picker-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(0,0,0,.65);
    touch-action: none;
}

/* Declarative page scroll-lock while any overlay is open.
 *
 * iOS Safari is stubborn:
 *   • `overflow: hidden` alone isn't enough (momentum leaks).
 *   • When an input inside the overlay gets focus, iOS auto-scrolls
 *     the page to bring it into view — bypassing overflow:hidden
 *     unless the body is actually taken out of flow.
 *
 * Robust fix: `position: fixed` on body. Pins the document to the
 * viewport so iOS has nothing to scroll, even when the keyboard opens.
 * Nested scroll inside the overlay still works (ancestors keep
 * `touch-action: auto`). Scroll position is not preserved — acceptable
 * since overlays are opened from the top of the page in practice. */
body:has(.overlay[aria-hidden="false"]) {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    overflow: hidden;
    overscroll-behavior: none;
}

/* --- Bottom sheet (layout override: body pinned to bottom on mobile) ---
 * .overlay sets `flex-direction: column`, so:
 *   justify-content = main axis (vertical) → flex-end pins body to bottom
 *   align-items     = cross axis (horizontal) → center for h-centering
 * Desktop override below switches vertical alignment to center. */
.sheet {
    justify-content: flex-end;
    align-items: center;
    transition: opacity .25s ease;
}
.sheet-body {
    position: relative;
    width: 100%;
    max-width: 500px;
    background: #fff;
    border-top-left-radius: 28px;
    border-top-right-radius: 28px;
    padding: .6rem 1.15rem calc(1.25rem + var(--safe-bottom));
    padding-left:  max(1.15rem, var(--safe-left));
    padding-right: max(1.15rem, var(--safe-right));
    box-shadow: 0 -20px 60px rgba(0,0,0,.3);
    max-height: 95dvh;
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
}

.sheet-handle {
    width: 40px;
    height: 5px;
    background: #cbd5e1;
    border-radius: 999px;
    margin: .25rem auto .7rem;
    cursor: pointer;
}

.sheet-head {
    display: flex;
    align-items: center;
    gap: .7rem;
    margin-bottom: .2rem;
}
.sheet-close {
    /* Flex-center + explicit padding:0 is required to center the ×
     * glyph: most fonts render U+00D7 with asymmetric side-bearings,
     * so relying on text-align + line-height alone leaves it visibly
     * off-center inside a round button. Flex centers by bounding box,
     * not glyph metrics. */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-left: auto;
    padding: 0;
    background: #f1f5f9;
    border: none;
    width: 38px;
    height: 38px;
    border-radius: 50%;
    font-size: 1.5rem;
    line-height: 1;
    color: var(--muted);
    cursor: pointer;
    flex-shrink: 0;
    transition: background .15s, color .15s;
    touch-action: manipulation;
}
.sheet-close:hover, .sheet-close:active {
    background: #e2e8f0;
    color: var(--ink);
}

.sheet-amount {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .9rem;
    padding: 1.2rem .4rem 1.3rem;
    font-variant-numeric: tabular-nums;
    min-width: 0;
}
.sheet-amount-value {
    /* Scales down on narrow phones so "1.234.567,89" fits at 320px wide,
       but stays 3rem on anything ≥ 400px. */
    font-size: clamp(2rem, 9.5vw, 3rem);
    font-weight: 800;
    line-height: 1;
    color: var(--ink);
    letter-spacing: -.02em;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}
.sheet-amount-value.zero { color: #cbd5e1; }
.sheet-amount-currency {
    font-size: clamp(1.1rem, 5vw, 1.6rem);
    color: var(--muted);
    font-weight: 600;
    flex-shrink: 0;
    margin-left: -.4rem;  /* tighten the gap between value and "€" */
}

/* Round ± buttons flanking the big amount display. Neutral colors —
 * the amount itself is the focal point, the buttons are just side
 * controls. Tap = soft fill, no red/green tinting. */
.amount-step {
    flex-shrink: 0;
    width: 44px;
    height: 44px;
    border: 1px solid var(--line-2);
    background: var(--bg-card);
    color: var(--ink-soft);
    border-radius: 999px;
    font-size: 1.4rem;
    font-weight: 700;
    line-height: 1;
    cursor: pointer;
    font-family: inherit;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
    transition: background .14s ease, border-color .14s ease,
                color .14s ease, transform .08s ease;
}
.amount-step:hover {
    background: var(--bg-subtle);
    border-color: var(--line-2);
}
.amount-step:active {
    background: var(--ink-soft);
    border-color: var(--ink-soft);
    color: #fff;
    transform: scale(.92);
}

/* Date + payment pills, always visible above the numpad */
.sheet-opts {
    display: flex;
    gap: .5rem;
    margin-bottom: .7rem;
}
.sheet-opt {
    flex: 1;
    display: flex;
    align-items: center;
    gap: .4rem;
    padding: .55rem .8rem;
    background: #f1f5f9;
    border: 1px solid var(--line);
    border-radius: 12px;
    font-size: .92rem;
    font-weight: 600;
    color: var(--ink);
    cursor: pointer;
    position: relative;
    min-height: 44px;
    overflow: hidden;
    transition: background .12s, border-color .12s;
}
.sheet-opt:active {
    background: #e2e8f0;
}
.sheet-opt-icon {
    font-size: 1.05rem;
    flex-shrink: 0;
}
.sheet-opt-label {
    flex: 1;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    pointer-events: none;
}
/* The actual <input>/<select> is overlaid transparently so tapping opens the
 * native iOS picker, while the pill layout stays stable. */
.sheet-opt-input {
    position: absolute;
    inset: 0;
    opacity: 0;
    width: 100%;
    height: 100%;
    border: none;
    background: transparent;
    font-size: 16px;
    font-family: inherit;
    cursor: pointer;
    -webkit-appearance: none;
    appearance: none;
    padding: 0;
}
.sheet-opt-input:focus-visible { outline: none; box-shadow: var(--ring); }
/* Visible focus ring for keyboard users — paint on the wrapper so it covers
 * the whole pill (BUG-?? a11y: prior outline:none had no replacement). */
.sheet-opt:focus-within {
    border-color: var(--primary, #2563eb);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--primary, #2563eb) 30%, transparent);
}
.sheet-opt-input::-webkit-date-and-time-value { text-align: left; }

/* Note: NO appearance:none — BUG-028 (iOS hardware Backspace + IME break
 * silently on <input type="text"> με stripped native chrome). The
 * <textarea>-like quick-add note is also `type="text"` in practice. */
.sheet-note {
    width: 100%;
    padding: .7rem 1rem;
    border: 1px solid var(--line);
    border-radius: 12px;
    background: #f8fafc;
    font-size: 16px;
    margin-bottom: 1rem;
    color: var(--ink);
    font-family: inherit;
}
.sheet-note:focus-visible {
    outline: none;
    border-color: var(--primary);
    background: #fff;
    box-shadow: 0 0 0 3px rgba(37,99,235,.15);
}

/* Quantity stepper inside .sheet-head, sitting between the product
 * name (or picker) and the close button. Min = 1, max = 999. */
.sheet-qty {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    padding: 3px;
    background: var(--bg-subtle);
    border: 1px solid var(--line);
    border-radius: 999px;
    flex-shrink: 0;
}
.sheet-qty-btn {
    width: 44px;
    height: 44px;
    border: none;
    background: transparent;
    color: var(--primary);
    font-size: 1.15rem;
    font-weight: 700;
    line-height: 1;
    cursor: pointer;
    border-radius: 999px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-family: inherit;
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
    transition: background .14s ease, color .14s ease, transform .08s ease;
}
.sheet-qty-btn:hover { background: rgba(37, 99, 235, .12); }
.sheet-qty-btn:active {
    background: var(--primary);
    color: #fff;
    transform: scale(.92);
}
.sheet-qty-value {
    min-width: 22px;
    text-align: center;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: var(--ink);
    font-size: .95rem;
    pointer-events: none;
    user-select: none;
}

/* Calculator-style numpad */
.numpad {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: .55rem;
    margin-bottom: .9rem;
}
.numpad button {
    height: 56px;
    border: none;
    background: #f1f5f9;
    border-radius: 14px;
    font-size: 1.55rem;
    font-weight: 500;
    color: var(--ink);
    cursor: pointer;
    transition: background .08s, transform .06s;
    -webkit-tap-highlight-color: transparent;
    font-family: inherit;
    touch-action: manipulation;
    user-select: none;
    -webkit-user-select: none;
    font-variant-numeric: tabular-nums;
}
.numpad button:active {
    background: #cbd5e1;
    transform: scale(.94);
}
.numpad button.comma {
    background: #e0e7ff;
    color: var(--primary);
    font-weight: 800;
}
.numpad button.comma:active { background: #c7d2fe; }
.numpad button.del {
    background: #fee2e2;
    color: var(--bad);
    font-size: 1.3rem;
}
.numpad button.del:active { background: #fecaca; }

.sheet-submit {
    margin-top: 0;
}
/* Footer row: [Αποθήκευση ως νέα] [Αποθήκευση]
 * In create mode only the primary button shows (flex:1, full width).
 * In edit mode the ghost "save as new" appears on the left — smaller
 * and muted so the primary keeps the visual weight. */
.sheet-submit-row {
    display: flex;
    gap: .5rem;
    margin-top: .4rem;
}
.sheet-submit-row .sheet-submit,
.sheet-submit-row .sheet-submit-clone {
    flex: 1 1 0;
    min-width: 0;
}
.sheet-submit-row .sheet-submit-clone {
    background: var(--bg-subtle);
    color: var(--ink-dim);
    border: 1px solid var(--line);
}
.sheet-submit-row .sheet-submit-clone:hover {
    background: var(--bg-card);
    color: var(--ink);
    border-color: var(--line-2);
}
.sheet-submit-row .sheet-submit-clone[hidden] { display: none; }
/* Narrow phones: stack the two buttons so neither is squeezed too tight. */
@media (max-width: 420px) {
    .sheet-submit-row {
        flex-direction: column-reverse; /* primary ON TOP — thumb-reachable */
        gap: .4rem;
    }
    .sheet-submit-row .sheet-submit-clone { width: 100%; }
}

/* 7. Transactions list (cards) ----------------------------------- */
.tx-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: .5rem;
}
.tx-item {
    position: relative;
    overflow: hidden;
    border-bottom: 1px solid var(--line);
    border-radius: 12px;
}
.tx-item:last-child { border-bottom: none; }
.tx-item .cat-icon {
    width: 40px;
    height: 40px;
    font-size: 1.2rem;
    flex-shrink: 0;
}

/* The sliding row surface. Everything user-visible lives here; when
 * the user swipes horizontally on mobile, the surface translates over
 * two absolute-positioned badges (edit / delete) sitting behind it. */
.tx-swipe-surface {
    position: relative;
    z-index: 1;
    display: grid;
    /* 5-column grid at rest — the amount (col 5) right-aligns to the
     * surface's right padding (= exact same x as `.day-header`'s right
     * padding, so day-totals and row amounts land on the same vertical
     * line). `.tx-actions` is positioned absolutely over the amount on
     * hover — see the `hover: hover` block below. */
    grid-template-columns: auto auto auto minmax(0, 1fr) auto;
    grid-template-rows: auto auto;
    column-gap: .55rem;
    row-gap: .15rem;
    align-items: center;
    padding: .75rem .65rem;
    background: var(--bg-card);
    transition: background-color .12s ease, box-shadow .12s ease;
}
html:not(.has-touch) .tx-item:not(.tx-item-readonly):hover .tx-swipe-surface {
    background: color-mix(in srgb, var(--bg-card) 88%, var(--accent) 12%);
}
/* Grid placement: avatar · dot · catIcon · body · amount · actions */
.tx-swipe-surface .tx-author  { grid-column: 1; grid-row: 1 / 3; align-self: center; }
.tx-swipe-surface .tx-divider { grid-column: 2; grid-row: 1 / 3; align-self: center; }
.tx-swipe-surface .cat-icon   { grid-column: 3; grid-row: 1 / 3; align-self: center; }
.tx-swipe-surface .tx-body    { grid-column: 4; grid-row: 1 / 3; min-width: 0; }
.tx-swipe-surface .tx-amount  {
    grid-column: 5; grid-row: 1 / 3;
    justify-self: end; align-self: center;
    transition: margin-right .14s ease;
}
/* Absolutely positioned: does NOT take grid space, so amounts keep
 * aligning to the surface's right padding at rest. On hover the amount
 * slides left via a margin-right override (see `hover: hover` block). */
.tx-swipe-surface .tx-actions {
    position: absolute;
    right: .5rem;
    top: 50%;
    transform: translateY(-50%);
    opacity: 0;
    pointer-events: none;
    transition: opacity .12s ease;
}


/* Divider between avatar and cat-icon: styled as a centered dot */
.tx-divider {
    display: inline-flex;
    width: auto; height: auto;
    align-self: center;
    background: none;
    margin: 0;
    font-size: .55rem;
    color: var(--muted);
    flex-shrink: 0;
}
.tx-divider::before { content: '·'; }
/* Default: hide the dot when the name column is visible (desktop /
 * landscape touch). It's only useful as a visual separator when the
 * name is hidden on narrow portrait touch screens, so the mobile
 * rule below re-enables it. */
.tx-swipe-surface .tx-divider { display: none; }

/* tx-body: title on line 1, time + payment + note inline on line 2 */
.tx-body .tx-sub { display: inline; }
.tx-body .tx-pm  { display: inline; }
.tx-body .tx-note-line { display: inline; }
.tx-body .tx-pm::before,
.tx-body .tx-note-line::before { content: ' · '; color: var(--muted); }
/* Wrapper for the second line — ensures truncation works on inline children */
.tx-meta-line {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    font-size: .78rem;
    color: var(--muted);
    min-width: 0;
}
/* Payment-method chip on the meta line. The label is hidden on portrait
   touch viewports (where vertical real-estate is tight); the emoji stays
   visible. Author-meta also hides the label there, so this becomes the
   only payment indicator on small screens — answers the "γιατί δε φαίνεται
   ο τρόπος πληρωμής στο κινητό" feedback. */
.tx-pm-label { font-size: .78rem; }
@media (pointer: coarse) and (orientation: portrait) {
    .tx-pm-label { display: none; }
}

/* Avatar 36px everywhere */
.tx-swipe-surface .tx-author .avatar { width: 36px; height: 36px; font-size: .8rem; }
.tx-swipe-surface .tx-author .avatar img { width: 36px; height: 36px; }

/* Swipe-reveal backgrounds sitting behind .tx-swipe-surface.
 * Full-width absolute layers with the label + icon pinned to the
 * far edge, so swiping right uncovers "✎ Επεξεργασία" on the left
 * and swiping left uncovers "Διαγραφή 🗑" on the right. Colors are
 * the iOS system red/blue — more vivid than the muted dashboard
 * palette to match the urgency of a direct-action gesture.
 *
 * Both backgrounds are stacked full-width, so we opacity-toggle the
 * correct one based on the current swipe direction (via
 * `.tx-swiping-left` / `.tx-swiping-right` classes on the parent
 * .tx-item, managed in app.js). Without this, the later sibling
 * would always paint over the earlier one and a right-swipe would
 * reveal the wrong color. Hidden by default on desktop. */
.tx-swipe-bg {
    position: absolute;
    inset: 0;
    display: none;
    align-items: center;
    gap: .6rem;
    padding: 0 1.5rem;
    color: #fff;
    font-size: .82rem;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    opacity: 0;
    transition: opacity .18s ease;
    z-index: 0;
    pointer-events: none;
    user-select: none;
    border-radius: 12px;
}
.tx-swipe-bg-edit {
    justify-content: flex-start;
    background: linear-gradient(135deg, #0071e3, #34aadc);
}
.tx-swipe-bg-delete {
    justify-content: flex-end;
    background: linear-gradient(135deg, #e8453c, #ff6961);
}
.tx-item.tx-swiping-right .tx-swipe-bg-edit   { opacity: 1; }
.tx-item.tx-swiping-left  .tx-swipe-bg-delete { opacity: 1; }

/* Author block on the left of every .tx-item: avatar + two-line
 * meta (display name / payment method), followed by a vertical
 * divider that separates the "who / how" block from the rest of
 * the row ("what / when / how much"). */
.tx-author {
    display: flex;
    align-items: center;
    gap: .55rem;
    flex-shrink: 0;
    min-width: 0;
}
.tx-author-meta {
    display: flex;
    flex-direction: column;
    line-height: 1.2;
    /* Fixed width so the category column lines up across rows regardless
     * of display-name length ("Σύζυγος" vs "Gorion"). Longer names get
     * ellipsis'd via the strong/small rules below. */
    width: 96px;
    min-width: 96px;
    max-width: 96px;
}
.tx-author-meta strong {
    font-size: .85rem;
    color: var(--ink);
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.tx-author-meta small {
    font-size: .72rem;
    color: var(--muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* On touch devices in portrait: hide author name + payment — avatar is enough.
   Landscape (wider) shows them again. */
@media (pointer: coarse) and (orientation: portrait) {
    .tx-author-meta { display: none !important; }
    .tx-author { gap: 0; }
    /* Name column is hidden here, so the dot acts as the only visual
     * separator between avatar and cat-icon. Re-enable it. */
    .tx-swipe-surface .tx-divider { display: inline-flex; }
}
/* base .tx-divider styles now in the grid placement block above */
.tx-body {
    flex: 1;
    min-width: 0;
}
.tx-title {
    font-weight: 600;
    font-size: 1rem;
    line-height: 1.25;
    color: var(--ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.tx-note { color: var(--muted); font-weight: 400; }
.tx-note-line {
    font-size: .78rem;
    color: var(--muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.tx-sub {
    font-size: .78rem;
    color: var(--muted);
    margin-top: .1rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.tx-amount {
    font-weight: 700;
    font-size: .95rem;
    font-variant-numeric: tabular-nums;
    flex-shrink: 0;
    white-space: nowrap;
}
.tx-amount.pos { color: var(--good); }
.tx-amount.neg { color: var(--ink); }

/* 8. Tables (for /transactions, /categories, /budgets) ------------ */
.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.table { width: 100%; border-collapse: collapse; font-size: .92rem; min-width: 520px; }
.table th, .table td { padding: .6rem .7rem; border-bottom: 1px solid var(--line); text-align: left; vertical-align: middle; }
.table th { font-size: .75rem; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); background: #f8fafc; }
.table tr:last-child td { border-bottom: none; }
.table tr:hover td { background: #f8fafc; }
.table tr.archived td { opacity: .55; }
/* 9. Buttons & chips ---------------------------------------------- */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .45rem;
    min-height: 40px;
    padding: .55rem 1rem;
    border-radius: var(--radius-sm);
    border: 1px solid var(--line);
    background: var(--bg-card);
    color: var(--ink);
    font-weight: 500;
    font-size: .88rem;
    letter-spacing: -.005em;
    cursor: pointer;
    box-shadow: var(--shadow-sm);
    transition: background .12s ease, border-color .12s ease,
                box-shadow .12s ease, transform .06s ease;
    -webkit-tap-highlight-color: transparent;
}
.btn:hover {
    background: var(--bg-subtle);
    border-color: var(--line-2);
    text-decoration: none;
}
.btn:active { transform: translateY(1px); box-shadow: none; }
.btn:focus-visible { outline: none; box-shadow: var(--shadow-sm), var(--ring); }

.btn-primary {
    background: var(--primary);
    border-color: var(--primary);
    color: #fff;
    font-weight: 600;
    box-shadow: var(--shadow-sm), inset 0 1px 0 rgba(255,255,255,.18);
}
.btn-primary:hover {
    background: var(--primary-d);
    border-color: var(--primary-d);
}
.btn-primary:disabled { opacity: .55; cursor: not-allowed; }

.btn-ghost {
    background: transparent;
    border-color: var(--line-2);
    color: var(--ink-soft);
    box-shadow: none;
}
.btn-ghost:hover {
    background: var(--bg-subtle);
    color: var(--ink);
}

.btn-sm    { padding: .35rem .7rem; font-size: .8rem; border-radius: var(--radius-sm); min-height: 32px; }
/* BUG-019 — touch laptops sit on desktop widths but still need ≥40 px targets. */
@media (pointer: coarse) {
    .btn-sm { min-height: 40px; padding: .5rem .85rem; }
}
.btn-block { width: 100%; }
.btn-big   { min-height: 48px; font-size: .95rem; padding: .8rem 1.2rem; border-radius: var(--radius); }.btn-link.danger { color: var(--bad); }
.btn-link.danger:hover { color: #b91c1c; }

/* Category & user chips for lists/tables */
.cat-chip {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: .15rem .6rem .15rem .15rem;
    border-radius: 999px;
    background: #f1f5f9;
    font-size: .85rem;
    font-weight: 500;
    color: var(--ink);
}
.cat-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    color: #fff;
    font-size: .9rem;
    background: #94a3b8;
    flex-shrink: 0;
}
/* 10. Forms & filters -------------------------------------------- */
.form label { display: flex; flex-direction: column; gap: .3rem; font-size: .88rem; color: var(--ink-soft); }
.form label > span { font-weight: 500; }
.form input[type=text],
.form input[type=password],
.form input[type=search],
.form input[type=email],
.form input[type=tel],
.form input[type=url],
.form input[type=date],
.form input[type=time],
.form input[type=datetime-local],
.form input[type=month],
.form input[type=week],
.form input[type=number],
.form select,
.form textarea {
    width: 100%;
    padding: .6rem .75rem;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--bg-inset);
    color: var(--ink);
    font-size: 16px;                /* prevent iOS zoom on focus */
    font-weight: 500;
    transition: border-color .12s ease, box-shadow .12s ease, background .12s ease;
    min-height: 40px;
    font-family: inherit;
}
/* appearance:none ONLY for select + type=search — BUG-028: type=text/etc on iOS
   silently break hardware Backspace + IME if appearance is reset. */
.form select,
.form input[type=search] {
    -webkit-appearance: none;
    appearance: none;
}
.form input:hover,
.form select:hover,
.form textarea:hover {
    border-color: var(--line-2);
}
.form input[type=color] {
    width: 54px;
    height: 44px;
    padding: 2px;
    border: 1px solid var(--line-2);
    border-radius: 10px;
    background: #fff;
    cursor: pointer;
    -webkit-appearance: none;
}
.form input:focus-visible, .form select:focus-visible, .form textarea:focus-visible {
    outline: none;
    background: var(--bg-card);
    border-color: var(--primary);
    box-shadow: var(--ring);
}
.form > label { margin-bottom: .9rem; }
.form-inline { display: flex; gap: .7rem; flex-wrap: wrap; align-items: end; }
.form-inline label { flex: 1 1 160px; min-width: 130px; }
.form-inline .grow { flex: 1 1 100%; }
/* ============================================================
 * /transactions filters — minimal bar + collapsible panel.
 * Primary row (month + search + ⋯) is always visible; the rest
 * lives in #tx-filters-panel, toggled via [data-filters-toggle].
 * ============================================================ */
.tx-filters-card { padding: .7rem .85rem; }
.tx-filters {
    display: flex;
    flex-direction: column;
    gap: .55rem;
    margin: 0;
}
.tx-filters-primary {
    display: flex;
    align-items: center;
    gap: .45rem;
    flex-wrap: nowrap;
}
.tx-filters-primary .filter-month-wrap { flex: 0 0 auto; }

.filter-search {
    flex: 1 1 140px;
    min-width: 0;
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: 0 .7rem;
    background: var(--bg-subtle, #f1f5f9);
    border: 1px solid var(--line);
    border-radius: 999px;
    transition: border-color .12s, box-shadow .12s;
    margin: 0;
}
.filter-search svg { color: var(--muted); flex-shrink: 0; }
.filter-search input[type="search"] {
    border: none;
    outline: none;
    background: transparent;
    padding: .5rem 0;
    font-size: 16px;
    color: var(--ink);
    font-family: inherit;
    flex: 1;
    min-width: 0;
}
.filter-search input::placeholder { color: var(--muted); }
.filter-search:focus-within {
    border-color: var(--primary);
    box-shadow: var(--ring);
}

.tx-filters-more {
    flex: 0 0 auto;
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: var(--bg-card);
    color: var(--ink-soft);
    cursor: pointer;
    padding: 0;
    -webkit-tap-highlight-color: transparent;
    transition: background .12s, border-color .12s;
}
.tx-filters-more:hover { background: var(--bg-subtle); border-color: var(--line-2); }
.tx-filters-more[aria-expanded="true"] {
    background: var(--primary-50);
    border-color: var(--primary);
    color: var(--primary-d);
}
.tx-filters-badge {
    position: absolute;
    top: -4px;
    right: -4px;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    border-radius: 999px;
    background: var(--primary);
    color: #fff;
    font-size: .68rem;
    font-weight: 700;
    line-height: 18px;
    text-align: center;
    pointer-events: none;
}

.tx-filters-panel {
    border-top: 1px solid var(--line);
    padding-top: .6rem;
}
.tx-filter-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: .5rem .7rem;
}
/* BUG-024 (2026-04-16): flex-column labels default to min-width:auto,
 * which is content-driven — an un-constrained <input>/<select> inside
 * computes its own intrinsic width (~194 px for the native select with
 * "— Επιλογή —" placeholder) and pushes the grid column past its `1fr`
 * fraction, overflowing the parent by ~27 px on narrow viewports.
 * Pinning `min-width:0` on the label lets it shrink to the grid cell,
 * and `width:100%` on the input forces them to follow. */
.tx-filter-grid label {
    display: flex;
    flex-direction: column;
    gap: .2rem;
    font-size: .75rem;
    color: var(--muted);
    margin: 0;
    min-width: 0;
}
.tx-filter-grid input,
.tx-filter-grid select {
    width: 100%;
    padding: .45rem .55rem;
    font-size: 16px;                /* iOS no-zoom — BUG-027 */
    min-height: 40px;
    border: 1px solid var(--line);
    border-radius: 9px;
    background: var(--bg-card);
    color: var(--ink);
    font-family: inherit;
}
.tx-filter-grid select {
    -webkit-appearance: none;       /* native chrome — BUG-028 keeps text inputs clean */
    appearance: none;
}
.tx-filter-grid .filter-cat-btn {
    display: flex;
    align-items: center;
    gap: .4rem;
    width: 100%;
    padding: .35rem .5rem;
    font-size: 15px;
    min-height: 40px; /* BUG-019 — ≥40 px touch target */
    border: 1px solid var(--line);
    border-radius: 9px;
    background: var(--bg-card);
    color: var(--ink);
    font-family: inherit;
    cursor: pointer;
    text-align: left;
}
.tx-filter-grid .filter-cat-btn:hover { border-color: var(--line-2); }
.filter-cat-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border-radius: 6px;
    font-size: 14px;
    flex: 0 0 auto;
    color: #fff;
}
.filter-cat-icon--all { background: #94a3b8; }
.filter-cat-name {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.filter-cat-chevron {
    flex: 0 0 auto;
    color: var(--muted);
    font-size: 11px;
}
.tx-filters-clear {
    margin-top: .55rem;
    display: inline-block;
}
@media (max-width: 479px) {
    .tx-filters-primary { gap: .35rem; }
    .tx-filter-grid { grid-template-columns: repeat(2, 1fr); gap: .4rem .5rem; }
}

/* 11. Budget progress -------------------------------------------- */
.budget-list { display: flex; flex-direction: column; gap: .9rem; }
.budget-label { display: flex; align-items: center; gap: .5rem; font-size: .88rem; margin-bottom: .35rem; }
.budget-name { font-weight: 600; }
.budget-num { margin-left: auto; font-variant-numeric: tabular-nums; color: var(--muted); font-size: .8rem; }
.progress { width: 100%; height: 10px; background: #e2e8f0; border-radius: 999px; overflow: hidden; }
.progress-bar { height: 100%; border-radius: 999px; transition: width .3s; background: linear-gradient(90deg, #60a5fa, var(--primary)); }
.progress-bar.ok   { background: linear-gradient(90deg, #6ee7b7, var(--good)); }
.progress-bar.warn { background: linear-gradient(90deg, #fcd34d, var(--warn)); }
.progress-bar.bad  { background: linear-gradient(90deg, #fca5a5, var(--bad));  }

.budget-edit { display: grid; gap: .6rem; grid-template-columns: 1fr; }
.budget-edit-row {
    padding: .7rem .85rem;
    background: #fff;
    border: 1px solid var(--line);
    border-radius: 14px;
    transition: border-color .15s, background .15s;
}
/* Με όριο: διακριτικό μπλε περίγραμμα, όχι γέμισμα. */
.budget-edit-row.has-limit { border-color: var(--primary); }
.budget-edit-row .budget-head {
    display: flex;
    align-items: center;
    gap: .55rem;
    flex-wrap: wrap;
}
.budget-edit-row .budget-head .cat-icon {
    width: 38px; height: 38px;
    font-size: 1.2rem;
    border-radius: 11px;
    flex-shrink: 0;
}
.budget-head-info {
    flex: 1 1 140px;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: .1rem;
    overflow: hidden;
}
.budget-head-info strong {
    font-size: .98rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.budget-head-info small {
    font-size: .74rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.budget-limit-form {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    margin: 0;
    flex-shrink: 0;
}
.budget-limit-form input[type="text"] {
    width: 110px;
    min-width: 0;
    padding: .4rem .55rem;
    font-size: 16px;                /* iOS no-zoom — BUG-027 */
    min-height: 40px;
    border-radius: 9px;
    text-align: right;
}
.budget-delete-form,
.budget-pin-form { display: inline-flex; margin: 0; flex-shrink: 0; }
.budget-pin { color: var(--muted); }
.budget-pin.is-pinned { color: #f59e0b; }
.budget-pin.is-pinned:hover { color: #d97706; }
.budget-limit-readonly {
    font-weight: 600;
    font-size: 1rem;
    color: var(--ink);
    flex-shrink: 0;
    padding: .4rem .55rem;
}
.budget-edit-row .progress { margin-top: .6rem; }
.copy-prev-form { margin: 0 0 0 auto; display: inline-flex; }.bank-acc-list { display: flex; flex-direction: column; gap: .5rem; }
.bank-acc-row {
    display: flex; align-items: center; gap: .75rem;
    padding: .75rem; border: 1px solid var(--line); border-radius: 10px;
    background: var(--bg-card);
}
.bank-acc-row.is-archived { opacity: .55; }
.bank-acc-row.is-editing {
    border-color: var(--primary, #2563eb);
    box-shadow: 0 0 0 2px rgba(37, 99, 235, .2);
}
.bank-acc-icon {
    width: 40px; height: 40px; border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    font-size: 1.25rem; flex-shrink: 0;
}
.bank-acc-info { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: .15rem; }
.bank-acc-info strong { font-size: 1rem; }
.bank-acc-actions { flex-shrink: 0; display: flex; gap: .5rem; }

/* Banks management — parser_config form */
.banks-form { display: flex; flex-direction: column; gap: 1rem; }
.banks-fieldset {
    border: 1px solid var(--line);
    border-radius: 10px;
    padding: .75rem 1rem 1rem;
}
.banks-fieldset legend {
    font-weight: 600;
    font-size: .9rem;
    padding: 0 .35rem;
    color: var(--ink);
}
.banks-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
    gap: .65rem .85rem;
    margin-top: .35rem;
}
.banks-grid label { display: flex; flex-direction: column; gap: .2rem; min-width: 0; }
.banks-grid label > span { font-size: .8rem; color: var(--muted); font-weight: 500; }
.banks-grid input,
.banks-grid select { width: 100%; }
.banks-label { grid-column: span 2; }
.banks-wide { grid-column: 1 / -1; }
.banks-grid code { font-size: .8rem; padding: .05rem .25rem; background: rgba(148,163,184,.15); border-radius: 3px; }

/* Step-2 confirmation preview rows on /bank-imports/new */
.bank-import-confirm { display: flex; flex-direction: column; gap: .65rem; }
.bank-import-preview {
    display: grid;
    grid-template-columns: 32px 1fr 200px auto;
    gap: .5rem .85rem;
    align-items: center;
    padding: .65rem .85rem;
    border: 1px solid var(--line);
    border-radius: 10px;
    background: var(--bg-card);
}
.bank-import-preview.is-undetected { border-color: rgba(220, 38, 38, .45); background: rgba(220, 38, 38, .04); }
.bank-import-preview.is-duplicate  { opacity: .65; }
.bank-import-preview-icon { font-size: 1.25rem; text-align: center; }
.bank-import-preview-info { min-width: 0; display: flex; flex-direction: column; gap: .15rem; }
.bank-import-preview-info strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bank-import-preview select { width: 100%; }
.bank-import-preview-skip {
    display: inline-flex; gap: .25rem; align-items: center;
    font-size: .8rem; color: var(--muted); white-space: nowrap;
}
@media (max-width: 700px) {
    .bank-import-preview {
        grid-template-columns: 32px 1fr;
        grid-template-areas: "icon info" "select select" "skip skip";
        gap: .35rem .65rem;
    }
    .bank-import-preview-icon { grid-area: icon; }
    .bank-import-preview-info { grid-area: info; }
    .bank-import-preview select { grid-area: select; }
    .bank-import-preview-skip  { grid-area: skip; }
}
/* Bank import history + single-import movements list */
.bank-imp-list { display: flex; flex-direction: column; gap: .5rem; }
.bank-imp-row {
    display: flex; align-items: center; gap: .75rem;
    padding: .75rem; border: 1px solid var(--line); border-radius: 10px;
    background: var(--bg-card); text-decoration: none; color: inherit;
    transition: background .15s;
}
.bank-imp-row:hover { background: rgba(148, 163, 184, .08); }
.bank-imp-info { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: .15rem; }.bank-mov-actions .form-inline { margin: 0; }.btn.btn-success {
    background: #16a34a; color: #fff; border-color: #15803d;
}
.btn.btn-success:hover { background: #15803d; }.merchant-add-form {
    display: grid;
    grid-template-columns: minmax(180px, 1fr) minmax(220px, 2fr) auto;
    gap: .75rem; align-items: end;
}
.merchant-add-form label {
    display: flex; flex-direction: column; gap: .25rem;
    font-size: .85rem; color: var(--muted);
}
.merchant-add-form label input,
.merchant-add-form label select,
.merchant-add-inline select {
    width: 100%;
    padding: .5rem .6rem;
    border: 1px solid var(--line);
    border-radius: 8px;
    background: var(--bg-card);
    color: var(--ink);
    font-size: 1rem;
}
.merchant-add-form input { text-transform: uppercase; }
@media (max-width: 700px) {
    .merchant-add-form { grid-template-columns: 1fr; }
}

.merchant-list {
    list-style: none; margin: 0; padding: 0;
    display: flex; flex-direction: column; gap: .75rem;
}
/* Right-aligned form action row (2026-05-13). Used for the «Συνέχεια»
 * step in /bank-imports/new so the CTA sits flush-right like a wizard. */
.form-actions.form-actions-right { justify-content: flex-end; }

/* Collapsible create-card on /banks (2026-05-13). Wraps the «➕ Νέα
 * τράπεζα» form in `<details>` so it stays out of the way until
 * needed. Auto-expanded server-side in edit mode (`<details open>`).
 * Summary mimics `.card-title` so the closed state visually matches
 * the rest of the cards on the page. */
.banks-create-card { padding: 0; }
.banks-create-card[open] > .banks-create-summary { border-bottom: 1px solid var(--line); }
.banks-create-summary {
    cursor: pointer;
    padding: .9rem 1.1rem;
    font-size: 1.05rem;
    font-weight: 600;
    list-style: none;
    display: flex;
    align-items: center;
    gap: .6rem;
    user-select: none;
}
.banks-create-summary::-webkit-details-marker { display: none; }
.banks-create-summary::before {
    content: '▸';
    color: var(--accent);
    font-size: .85em;
    transition: transform .15s ease;
    display: inline-block;
}
.banks-create-card[open] > .banks-create-summary::before { transform: rotate(90deg); }
.banks-create-summary:hover { background: color-mix(in srgb, var(--accent) 8%, transparent); }
.banks-create-cancel { margin-left: auto; }
.banks-create-card > .form { padding: 0 1.1rem 1.1rem; }
/* The merchant card reuses .banks-create-card but its form lives at the
 * .merchant-add-form selector, so wire the inner padding there too. */
.merchant-create-card > .merchant-add-form { padding: 0 1.1rem 1.1rem; }
/* Italic placeholder for the merchant pattern input — keeps the field
 * looking unfilled even though the placeholder reads as a proper label
 * («Όνομα») instead of a dummy example. */
.merchant-add-pattern input::placeholder { font-style: italic; opacity: .65; }

/* ── Bank admin sub-navigation (2026-05-13) ────────────────────────
 * GitHub-style tab strip that sits above the page header on every page
 * in the bank-imports family (`/bank-imports`, `/bank-imports/new`,
 * `/banks`, `/merchants`). Replaces the per-page inline link lists with
 * a uniform navigation contract. */
.bank-subnav {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: .35rem;
    margin: 0 0 1.1rem;
    padding: .4rem;
    background: color-mix(in srgb, var(--bg-card) 70%, transparent);
    border: 1px solid var(--line);
    border-radius: 12px;
}
.bank-subnav-item {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: .5rem .85rem;
    border-radius: 8px;
    border: 1px solid transparent;
    text-decoration: none;
    color: inherit;
    font-weight: 500;
    font-size: .92rem;
    line-height: 1.2;
    transition: background .12s, border-color .12s, color .12s;
}
.bank-subnav-item:hover {
    background: color-mix(in srgb, var(--accent) 12%, var(--bg-card));
    text-decoration: none;
}
.bank-subnav-item:focus-visible {
    outline: none;
    box-shadow: var(--ring);
}
.bank-subnav-item.is-current {
    background: color-mix(in srgb, var(--accent) 18%, var(--bg-card));
    border-color: color-mix(in srgb, var(--accent) 42%, var(--line));
    color: var(--accent);
    font-weight: 600;
}
.bank-subnav-icon { font-size: 1.05em; line-height: 1; }
@media (max-width: 640px) {
    .bank-subnav-item { padding: .45rem .65rem; font-size: .88rem; }
    .bank-subnav-label { display: none; }
    .bank-subnav-item.is-current .bank-subnav-label { display: inline; }
}

/* ── /bank-imports?batch=… header (redesign 2026-05-13) ────────────
 * Reuses the KPI-tile language from /merchants for visual consistency
 * with a few status-specific colour ramps. Breadcrumb → H1 → subtitle
 * → stats tiles → files chip row → actions card. */
.bank-batch-header { margin-bottom: 1rem; }
/* Premium batch H1 (2026-05-13 refresh, single-line variant): icon tile
 * + «Εισαγωγή» main title + smaller secondary date + copyable ID pill
 * pushed to the far right (`margin-left: auto`). Items baseline-aligned
 * so the smaller date sits flush with the title's text baseline. Wraps
 * gracefully on narrow viewports. */
.bank-batch-h1 {
    margin: .15rem 0 .85rem;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .65rem;
    font-size: 1.6rem;
    line-height: 1.15;
}
.bank-batch-h1-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    border-radius: 12px;
    background: color-mix(in srgb, var(--accent, #2563eb) 18%, var(--bg-card));
    border: 1px solid color-mix(in srgb, var(--accent, #2563eb) 35%, var(--line));
    font-size: 1.4rem;
    flex: 0 0 auto;
}
/* Both the «Εισαγωγή» label and the date share the same understated
 * treatment now — the icon tile carries the visual weight. */
.bank-batch-h1-title,
.bank-batch-h1-date {
    font-size: .65em;
    font-weight: 500;
    color: var(--muted);
    align-self: center;
}
.bank-batch-id-pill {
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: .35rem .7rem;
    font-size: .8rem;
    font-weight: 600;
    color: var(--muted);
    background: var(--bg-card);
    border: 1px solid var(--line);
    border-radius: 999px;
    cursor: pointer;
    font-family: inherit;
    transition: background .12s, border-color .12s, color .12s;
}
.bank-batch-id-pill:hover {
    background: color-mix(in srgb, var(--accent, #2563eb) 12%, var(--bg-card));
    border-color: color-mix(in srgb, var(--accent, #2563eb) 35%, var(--line));
    color: var(--ink);
}
.bank-batch-id-pill:focus-visible { outline: none; box-shadow: var(--ring); }
.bank-batch-id-pill.is-copied {
    background: color-mix(in srgb, #16a34a 18%, var(--bg-card));
    border-color: #16a34a;
    color: #16a34a;
}
.bank-batch-id-label {
    font-size: .72em;
    letter-spacing: .1em;
    text-transform: uppercase;
    opacity: .7;
}
.bank-batch-id-pill code {
    font-size: .95em;
    font-family: ui-monospace, 'SFMono-Regular', Menlo, Consolas, monospace;
    color: inherit;
}

.bank-batch-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: .65rem;
    margin: 0 0 1rem;
}
.bank-batch-stat {
    background: var(--bg-card);
    border: 1px solid var(--line);
    border-radius: 12px;
    padding: .75rem .9rem;
    display: flex;
    flex-direction: column;
    gap: .15rem;
    border-left: 3px solid var(--line);
}
.bank-batch-stat-value {
    font-size: 1.5rem;
    font-weight: 700;
    line-height: 1.1;
    font-variant-numeric: tabular-nums;
}
.bank-batch-stat-label {
    color: var(--muted);
    font-size: .85rem;
    font-weight: 500;
}
/* Colour-coded left border + value tint per status, matches the bezier
 * line palette in `recv-line` so the visual story is consistent. */
.bank-batch-stat.is-pct.is-complete { border-left-color: #16a34a; }
.bank-batch-stat.is-pct.is-complete .bank-batch-stat-value { color: #16a34a; }
.bank-batch-stat.is-pct.is-mid      { border-left-color: #f59e0b; }
.bank-batch-stat.is-pct.is-mid      .bank-batch-stat-value { color: #f59e0b; }
.bank-batch-stat.is-pct.is-low      { border-left-color: var(--muted); }
.bank-batch-stat.is-auto      { border-left-color: #16a34a; }
.bank-batch-stat.is-matched   { border-left-color: #2563eb; }
.bank-batch-stat.is-suggested { border-left-color: #f59e0b; }
.bank-batch-stat.is-pending   { border-left-color: var(--muted); }
.bank-batch-stat.is-new       { border-left-color: #a855f7; }
.bank-batch-stat.is-ignored   { border-left-color: var(--muted); opacity: .75; }

.bank-batch-files {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .5rem;
    margin: 0;
    padding: .5rem .75rem;
    background: color-mix(in srgb, var(--bg-card) 70%, transparent);
    border: 1px solid var(--line);
    border-radius: 10px;
}
.bank-batch-files-label { font-size: .85rem; flex: 0 0 auto; }
.bank-batch-file-chip {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .2rem .55rem;
    background: var(--bg-card);
    border: 1px solid var(--line);
    border-radius: 999px;
    font-size: .85rem;
    white-space: nowrap;
}
.bank-batch-file-chip strong { font-weight: 600; }

.bank-batch-actions { padding: .85rem 1rem; margin-bottom: 1rem; }
.bank-batch-actions-row {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    align-items: center;
}
.bank-batch-delete { margin-left: auto; }
/* ── /merchants stats + toolbar (redesign 2026-05-13) ──────────────
 * KPI tile row above the «+ Νέο mapping» card and a search/filter/sort
 * toolbar above the patterns list. Tiles use auto-fit so 4 fit on desktop,
 * 2 on tablet, 1 on phone without media queries. */
.merchants-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: .75rem;
    margin: 1rem 0 1.25rem;
}
.merchants-stat {
    background: var(--bg-card);
    border: 1px solid var(--line);
    border-radius: 12px;
    padding: .85rem 1rem;
    display: flex;
    flex-direction: column;
    gap: .15rem;
}
.merchants-stat-value {
    font-size: 1.5rem;
    font-weight: 700;
    line-height: 1.15;
    display: flex;
    align-items: center;
    gap: .4rem;
    overflow: hidden;
    text-overflow: ellipsis;
}
.merchants-stat-cat-icon { font-size: 1.1em; }
.merchants-stat-label {
    color: var(--muted);
    font-size: .85rem;
    font-weight: 500;
}
.merchants-toolbar-card { padding: 1rem; }
.merchants-toolbar {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem;
    align-items: center;
}
.merchants-search { flex: 1 1 240px; min-width: 200px; }
.merchants-search input {
    width: 100%;
    padding: .55rem .8rem;
    background: var(--bg-card);
    border: 1px solid var(--line);
    border-radius: 8px;
    font-size: 16px;  /* iOS no-zoom rule */
    color: inherit;
}
.merchants-search input:focus-visible {
    outline: none;
    border-color: var(--primary, #2563eb);
    box-shadow: var(--ring);
}
.merchants-filter select,
.merchants-sort select {
    padding: .55rem .65rem;
    background: var(--bg-card);
    border: 1px solid var(--line);
    border-radius: 8px;
    font-size: 16px;
    color: inherit;
    min-width: 160px;
}
.merchants-result-line { margin: .8rem 0 0; font-size: .9rem; }

.merchant-list {
    display: flex;
    flex-direction: column;
    gap: .6rem;
}
.merchant-row.is-filtered-out { display: none; }
.merchants-empty-state { padding: 1rem 0; }

.merchant-pattern-stats { white-space: nowrap; }

/* Category-picker trigger buttons on /merchants (2026-05-13): replace
 * the per-row + top-form native `<select>` with a button that opens
 * the same full-screen category picker overlay as «+ Νέα καταχώρηση».
 * Icon · name · chevron layout, gets a tinted background once a
 * category has been picked (`.is-picked`). The small variant is for
 * the per-row inline «+ προσθήκη κατηγορίας» button. */
.merchant-cat-trigger {
    display: inline-flex;
    align-items: center;
    gap: .55rem;
    padding: .55rem .8rem;
    background: var(--bg-card);
    border: 1px solid var(--line);
    border-radius: 10px;
    cursor: pointer;
    font: inherit;
    font-size: 16px;       /* iOS no-zoom rule */
    color: inherit;
    text-align: left;
    min-width: 220px;
    transition: border-color .12s, background .12s;
}
.merchant-cat-trigger:hover {
    border-color: color-mix(in srgb, var(--accent, #2563eb) 35%, var(--line));
}
.merchant-cat-trigger:focus-visible { outline: none; box-shadow: var(--ring); }
.merchant-cat-trigger.is-picked {
    border-color: color-mix(in srgb, var(--accent, #2563eb) 42%, var(--line));
    background: color-mix(in srgb, var(--accent, #2563eb) 10%, var(--bg-card));
}
.merchant-cat-trigger-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background: var(--bg-subtle, var(--line));
    font-size: 1rem;
    flex: 0 0 auto;
}
.merchant-cat-trigger-name { flex: 1 1 auto; min-width: 0; }
.merchant-cat-trigger-chevron { color: var(--muted); font-size: .85em; flex: 0 0 auto; }
.merchant-cat-trigger-sm {
    padding: .35rem .65rem;
    min-width: 0;
    font-size: .9rem;
}
.merchant-cat-trigger-sm .merchant-cat-trigger-icon {
    width: 20px; height: 20px; font-size: .85rem;
}

/* Merchant row visual upgrade (2026-05-13). Initials avatar + bigger
 * pattern label + clean chip row + meta line «N mappings · M confirms».
 * Reusable across other «entity with derived stats» rows. */
.merchant-row {
    border: 1px solid var(--line); border-radius: 14px;
    padding: 1rem 1.1rem; background: var(--bg-card);
    display: flex; flex-direction: column; gap: .6rem;
    transition: border-color .12s, transform .12s, box-shadow .12s;
}
.merchant-row:hover {
    border-color: color-mix(in srgb, var(--accent, #2563eb) 28%, var(--line));
    box-shadow: 0 2px 6px rgba(0,0,0,.05);
}
.merchant-head {
    display: flex; align-items: center; gap: .6rem;
}
.merchant-pattern {
    font-family: 'SFMono-Regular', Menlo, Consolas, monospace;
    font-size: 1.05rem; font-weight: 700;
    background: color-mix(in srgb, var(--accent, #2563eb) 10%, var(--bg-card));
    border: 1px solid color-mix(in srgb, var(--accent, #2563eb) 30%, var(--line));
    color: var(--accent, #2563eb);
    padding: .2rem .65rem;
    border-radius: 8px;
    letter-spacing: .02em;
}
.merchant-delete-all { margin-left: auto; }

.merchant-chips {
    display: flex; flex-wrap: wrap; gap: .4rem;
}
.merchant-chip {
    display: inline-flex; align-items: center; gap: .35rem;
    padding: .25rem .35rem .25rem .55rem;
    border: 1px solid color-mix(in srgb, var(--chip-color, #94a3b8) 50%, transparent);
    background: color-mix(in srgb, var(--chip-color, #94a3b8) 14%, transparent);
    border-radius: 999px;
    font-size: .85rem;
}
.merchant-chip-icon {
    display: inline-flex; align-items: center; justify-content: center;
    width: 22px; height: 22px; border-radius: 50%;
    background: var(--chip-color, #94a3b8); font-size: .9rem;
}
.merchant-chip-name small { font-size: .72rem; margin-left: .15rem; }
.merchant-chip-count { font-variant-numeric: tabular-nums; }
.merchant-chip-remove {
    border: none; background: transparent;
    color: var(--muted); cursor: pointer;
    width: 20px; height: 20px; border-radius: 50%;
    font-size: .85rem; line-height: 1;
    display: inline-flex; align-items: center; justify-content: center;
    padding: 0;
}
.merchant-chip-remove:hover { background: rgba(239, 68, 68, .15); color: #dc2626; }

.merchant-add-inline {
    display: flex; gap: .5rem; align-items: center;
    padding-top: .25rem; border-top: 1px dashed var(--line);
    margin-top: .25rem;
}
.merchant-add-inline select { max-width: 360px; flex: 1 1 auto; }
@media (max-width: 600px) {
    .bank-mov-pair-form select { max-width: 100%; flex: 1 1 100%; }
}

/* ─── Reconcile workspace (Phase 2 redesign, 2026-05-06) ───
   Two-pane "Figma-like" canvas: bank movements left, user transactions
   right. Connection lines drawn via SVG overlay (bank-reconcile.js).
   3D feel via multi-layer shadows + hover lift. Desktop-first; mobile
   falls back to the legacy linear list (data-recv-mode="list"). */
.recv-grid {
    position: relative;
    display: grid;
    /* `minmax(0, 1fr)` instead of plain `1fr` keeps the two columns
     * mathematically equal regardless of their content's min-content
     * width (cards in one column, no cards in the other when fully
     * collapsed). With plain `1fr`, the implicit min size is min-content,
     * so removing all cards from a column shrinks it and the centered
     * pane titles visibly shift sideways (2026-05-13 user feedback:
     * «όταν τα κλείνω όλα μετακινούνται λίγο δεξιά»). */
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    column-gap: 160px;
    /* Tightened from 1.5rem so all-days-collapsed view doesn't accumulate
     * huge gaps between consecutive divider pills (2026-05-12 user
     * feedback: σελίδα γέμιζε κενό όταν όλες οι μέρες collapsed). */
    row-gap: 1rem;
    margin-top: 1rem;
    padding: 1rem 0;
}
.recv-pane { display: flex; flex-direction: column; gap: 1rem; min-width: 0; }
.recv-pane-title {
    font-size: .8rem; font-weight: 600; text-transform: uppercase;
    color: var(--muted); letter-spacing: .04em;
    /* Centered over the column. Used to be left-aligned (`padding-left: 1rem`)
     * but the centered look reads better as a section header that sits above
     * a stack of cards (2026-05-13 user feedback). Titles stay pinned to
     * grid-row: 1 in `display: contents` mode so they never shift when
     * cards below collapse/expand. */
    text-align: center;
    padding: 0 1rem;
}

/* The SVG sits behind the cards and draws connection bezier lines.
   pointer-events: none so card clicks pass through. */
.recv-svg {
    position: absolute; inset: 0; width: 100%; height: 100%;
    pointer-events: none; z-index: 1;
}
.recv-line { fill: none; stroke-width: 2.5px; transition: stroke-width .15s, opacity .15s; }
.recv-line.is-matched      { stroke: #2563eb; }
.recv-line.is-matched-auto { stroke: #16a34a; }
.recv-line.is-suggested    { stroke: #f59e0b; stroke-dasharray: 6 4; }
.recv-line.is-new-tx       { stroke: #a855f7; }

/* The card itself — floating, slightly 3D, hover-lifts */
.recv-card {
    position: relative;
    background: linear-gradient(150deg, var(--bg-card) 0%, color-mix(in srgb, var(--bg-card) 90%, var(--ink) 4%) 100%);
    border: 1px solid var(--line);
    border-radius: 14px;
    padding: .85rem 1rem .85rem 1.1rem;
    box-shadow:
        0 1px 2px rgba(0,0,0,.06),
        0 4px 10px rgba(0,0,0,.06),
        0 12px 24px rgba(0,0,0,.04);
    transition: transform .18s ease, box-shadow .18s ease, border-color .15s;
    z-index: 2;
    cursor: default;
}
.recv-card:hover {
    transform: translateY(-3px);
    box-shadow:
        0 2px 4px rgba(0,0,0,.08),
        0 8px 18px rgba(0,0,0,.10),
        0 20px 40px rgba(0,0,0,.06);
}
/* Left accent stripe per card type */
.recv-card::before {
    content: ""; position: absolute; left: 0; top: 12px; bottom: 12px;
    width: 4px; border-radius: 0 4px 4px 0;
    background: var(--muted); opacity: .35;
    transition: opacity .15s, background .15s;
}
.recv-bank::before { background: #94a3b8; }
.recv-tx::before   { background: #94a3b8; }

/* State-specific styling */
.recv-card.is-matched      { border-color: rgba(37, 99, 235, .5); }
.recv-card.is-matched::before { background: #2563eb; opacity: 1; }
.recv-card.is-matched-auto { border-color: rgba(34, 197, 94, .5); }
.recv-card.is-matched-auto::before { background: #16a34a; opacity: 1; }
.recv-card.is-suggested    { border-color: rgba(234, 179, 8, .5); }
.recv-card.is-suggested::before { background: #f59e0b; opacity: 1; }
.recv-card.is-new-tx       { border-color: rgba(168, 85, 247, .55); }
.recv-card.is-new-tx::before { background: #a855f7; opacity: 1; }
.recv-card.is-ignored      { opacity: .35; filter: saturate(.4); }
.recv-card.is-ignored::before { opacity: 0; }

/* Click-to-pair: the card the user clicked first lifts + glows hard,
   candidate tx cards on the right pulse with a strong blue ring. When
   the grid contains a selecting card, every other tx card dims so the
   candidates "pop" — answers user feedback «να είναι πιο εμφανή». */
.recv-card.is-selecting {
    border-color: #2563eb;
    border-width: 2px;
    background: linear-gradient(150deg,
        color-mix(in srgb, var(--bg-card) 88%, #3b82f6 12%) 0%,
        var(--bg-card) 100%);
    box-shadow:
        0 0 0 4px rgba(59, 130, 246, .35),
        0 0 32px 4px rgba(59, 130, 246, .35),
        0 14px 32px rgba(59, 130, 246, .28);
    transform: translateY(-4px) scale(1.02);
    z-index: 4;
}
.recv-card.is-candidate {
    cursor: pointer;
    border-color: #3b82f6;
    border-width: 2px;
    animation: recv-pulse 1.2s ease-in-out infinite;
    z-index: 3;
}
@keyframes recv-pulse {
    0%, 100% {
        box-shadow:
            0 0 0 0 rgba(59, 130, 246, .55),
            0 4px 14px rgba(59, 130, 246, .25);
        transform: translateY(0) scale(1);
    }
    50% {
        box-shadow:
            0 0 0 12px rgba(59, 130, 246, 0),
            0 4px 14px rgba(59, 130, 246, .25);
        transform: translateY(-2px) scale(1.015);
    }
}
.recv-card.is-pair-target { cursor: pointer; }
.recv-card.is-pair-target:hover {
    border-color: #1d4ed8;
    box-shadow:
        0 0 0 4px rgba(59, 130, 246, .5),
        0 12px 28px rgba(59, 130, 246, .35);
    transform: translateY(-3px) scale(1.02);
    animation: none;
}
/* When a bank card is in selecting mode, force-pair scope is its
 * `is-pair-eligible` set (= same-day, not-locked-elsewhere). The ±5%
 * candidates pulse as strong hints; eligible-but-not-candidate cards
 * are clickable with a soft hover; non-eligible ones get dimmed and
 * become unclickable so the user can't pick the wrong day or a tx
 * already locked to another bank movement. (2026-05-08 — replaces the
 * earlier «every tx is clickable» rule per user feedback «μόνο της
 * ίδιας ημέρας που δεν είναι ματσαρισμένη».) Eligible cards forcibly
 * restore opacity + saturation so the baseline `.is-ignored` dim
 * (applied to any tx that isn't a candidate of any movement) doesn't
 * make them look unclickable during selecting mode. */
.recv-grid:has(.recv-card.is-selecting) [data-recv-card="tx"].is-pair-eligible {
    opacity: 1 !important;
    filter: none !important;
}
.recv-grid:has(.recv-card.is-selecting) [data-recv-card="tx"].is-pair-eligible:not(.is-candidate) {
    cursor: pointer;
    border-color: rgba(59, 130, 246, .35);
}
.recv-grid:has(.recv-card.is-selecting) [data-recv-card="tx"].is-pair-eligible:not(.is-candidate):hover {
    border-color: rgba(59, 130, 246, .65);
    box-shadow:
        0 0 0 2px rgba(59, 130, 246, .28),
        0 6px 18px rgba(59, 130, 246, .2);
    transform: translateY(-2px);
}
.recv-grid:has(.recv-card.is-selecting) [data-recv-card="tx"]:not(.is-pair-eligible) {
    opacity: .25;
    filter: saturate(.4);
    pointer-events: none;
}

/* Dim everything else when a bank card is in selecting mode, so the
   pulsing candidates dominate the canvas. Uses :has() (Chrome 105+,
   Safari 15.4+, Firefox 121+ — fine for the admin workflow). */
.recv-grid:has(.recv-card.is-selecting) .recv-card:not(.is-selecting):not(.is-candidate):not(.is-pair-target) {
    opacity: .25;
    filter: saturate(.4);
    transition: opacity .18s ease, filter .18s ease;
}

/* Body: status icon + date + amount + desc */
.recv-card-head {
    display: flex; align-items: center; gap: .55rem;
    margin-bottom: .4rem;
}
.recv-card-status { font-size: 1.05rem; }
.recv-card-date {
    font-variant-numeric: tabular-nums; font-size: .82rem;
    color: var(--muted);
}
.recv-card-amount {
    margin-left: auto; font-weight: 700; font-size: 1.15rem;
    font-variant-numeric: tabular-nums; color: var(--ink);
    letter-spacing: -0.01em;
}
.recv-card-desc {
    font-size: .92rem; line-height: 1.35; color: var(--ink);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.recv-card-desc strong {
    font-weight: 600;
    color: var(--ink);
}
.recv-card-meta {
    display: flex; flex-wrap: wrap; gap: .35rem; margin-top: .45rem;
    font-size: .75rem; color: var(--muted);
}
.recv-card-meta .recv-tag {
    background: rgba(148, 163, 184, .14);
    padding: .15rem .55rem; border-radius: 999px;
    display: inline-flex; align-items: center; gap: .2rem;
    border: 1px solid transparent;
}
/* Coloured chip variants for tx cards: payment method (neutral), linked
   (matched/auto = blue), candidate (within ±48h, score-eligible = amber). */
.recv-card-meta .recv-tag-pm {
    background: color-mix(in srgb, var(--ink) 6%, transparent);
    color: var(--ink);
}
.recv-card-meta .recv-tag-linked {
    background: rgba(37, 99, 235, .14);
    color: #1d4ed8;
    border-color: rgba(37, 99, 235, .35);
    font-weight: 600;
}
.recv-card-meta .recv-tag-candidate {
    background: rgba(245, 158, 11, .15);
    color: #b45309;
    border-color: rgba(245, 158, 11, .4);
    font-weight: 600;
}
:root[data-theme="dark"] .recv-card-meta .recv-tag-linked    { color: #93c5fd; }
:root[data-theme="dark"] .recv-card-meta .recv-tag-candidate { color: #fcd34d; }
.recv-card-actions {
    display: flex; flex-wrap: wrap; gap: .3rem;
    margin-top: .55rem;
}
.recv-card-actions .form-inline { margin: 0; }
.recv-card-actions .btn { font-size: .75rem; padding: .25rem .55rem; }

/* Big colour-tiered match-score badge, anchored bottom-right of every
 * bank card that has a score. Tiers map to matcher confidence bands:
 *   ≥ 90 excellent (green)  · ≥ 75 good (blue)  · ≥ 50 fair (amber)  · <50 low (muted)
 * `currentColor` drives both border and tinted background so the four
 * tiers share the same shape rules and only the colour token changes.
 * Card needs to provide `.recv-card-actions` enough right padding so
 * the badge doesn't overlap action buttons — `.recv-card` already has
 * `position: relative` from earlier rules. */
.recv-score-badge {
    position: absolute;
    right: .75rem;
    bottom: .65rem;
    padding: .3rem .7rem;
    font-size: 1.05rem;
    font-weight: 800;
    line-height: 1;
    letter-spacing: .02em;
    font-variant-numeric: tabular-nums;
    border-radius: 999px;
    border: 1.5px solid currentColor;
    background: color-mix(in srgb, currentColor 14%, var(--bg-card));
    box-shadow: 0 2px 6px color-mix(in srgb, currentColor 25%, transparent);
    z-index: 3;  /* above bezier lines (z-index 1) */
    /* `pointer-events: auto` (default) so the native `title` tooltip
     * with the score breakdown fires on hover. Previously `none` to
     * pass clicks through to the card underneath, but the card's click
     * handler in bank-reconcile.js already skips button/input targets
     * via `closest('button, a, select, input')` — a non-interactive
     * `<span>` like this badge doesn't trigger that bail, but neither
     * does it have any click side-effect, so clicks on the pill simply
     * bubble to the card as before. Net: hover works, click unaffected. */
    pointer-events: auto;
}
.recv-score-badge.is-excellent { color: #16a34a; }   /* green: ≥90 */
.recv-score-badge.is-good      { color: #2563eb; }   /* blue:  75–89 */
.recv-score-badge.is-fair      { color: #f59e0b; }   /* amber: 50–74 */
.recv-score-badge.is-low       { color: var(--muted); }
/* Fallback status pills για rows χωρίς score — solid-fill + small, οπτικά
 * διακριτά από τα tier-coloured score pills (που είναι outlined). */
.recv-score-badge.is-manual,
.recv-score-badge.is-new,
.recv-score-badge.is-ignored {
    color: #fff;
    font-size: .88rem;
    padding: .28rem .7rem;
    border: 0;
    letter-spacing: .03em;
    text-transform: none;
}
.recv-score-badge.is-manual  { background: #2563eb; box-shadow: 0 2px 5px rgba(37, 99, 235, .35); }   /* matches 🔵 */
.recv-score-badge.is-new     { background: #a855f7; box-shadow: 0 2px 5px rgba(168, 85, 247, .35); }  /* matches 🟣 */
.recv-score-badge.is-ignored { background: #64748b; box-shadow: 0 2px 5px rgba(100, 116, 139, .35); } /* matches ⚫ */
.recv-card-actions { padding-right: 4rem; }   /* room for the badge */

/* Pending-change preview on tx cards: shows in red when confirming the
 * pair would overwrite the user's tx amount/time via
 * `bank_sync_tx_from_movement()`. The badge sits below `.recv-card-meta`
 * so it doesn't disturb the chip line. Red border + tinted background
 * make it the loudest thing on the card — the user shouldn't click
 * Επιβεβαίωση without noticing. (2026-05-08 user feedback: «να μου το
 * λέει κάπως, να το βάζει κόκκινο».) */
.recv-pending-change {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin-top: .55rem;
    padding: .4rem .65rem;
    border: 1.5px solid #ef4444;
    background: color-mix(in srgb, #ef4444 12%, var(--bg-card));
    border-radius: 10px;
    color: #b91c1c;
    font-size: .85rem;
    font-weight: 600;
    line-height: 1.3;
}
:root[data-theme="dark"] .recv-pending-change { color: #fca5a5; }
.recv-pending-icon  { font-size: 1.05rem; flex: 0 0 auto; }
.recv-pending-text  { flex: 1 1 auto; }
.recv-pending-text strong {
    font-weight: 800;
    font-variant-numeric: tabular-nums;
}

/* Day-divider rows: grid items spanning both columns at row positions
 * tracked server-side ($bankDividers / $txDividers). Renders a horizontal
 * rule with a centered Greek date label («Σαβ 18 Απρ»), with extra
 * vertical breathing room so day groups visibly chunk on the canvas.
 * On mobile the panes flex-stack so dividers naturally appear between
 * cards in their pane (no special handling needed). */
.recv-day-divider {
    grid-column: 1 / -1;
    display: flex;
    align-items: center;
    gap: 1rem;
    margin: 2rem 0 .75rem;
    padding: 0 .25rem;
    color: var(--ink);
    font-size: 1.05rem;
    font-weight: 700;
    letter-spacing: .01em;
}
.recv-day-divider::before,
.recv-day-divider::after {
    content: "";
    flex: 1 1 auto;
    height: 2px;
    background: linear-gradient(90deg,
        transparent,
        color-mix(in srgb, var(--accent) 35%, var(--line)) 18%,
        color-mix(in srgb, var(--accent) 35%, var(--line)) 82%,
        transparent);
}
.recv-day-divider span {
    flex: 0 0 auto;
    white-space: nowrap;
    padding: .35rem 1rem;
    border-radius: 999px;
    background: color-mix(in srgb, var(--accent) 14%, var(--bg-card));
    border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--line));
    display: inline-flex;
    align-items: center;
    gap: .55em;
}
/* Day-divider as collapse toggle. NO transitions on pill background/
 * border (they ran on every divider on every batch-collapse and added
 * perceptible lag on 30+ dividers — user feedback 2026-05-12). The
 * chevron rotation is the only animated piece because it's the
 * essential affordance for the toggle state. */
.recv-day-divider {
    cursor: pointer;
    user-select: none;
    /* Minimal top breathing room only — row-gap supplies the spacing
     * between this divider and the row above. Bottom margin removed so
     * consecutive collapsed dividers stack tightly (only `row-gap: 1rem`
     * separates them, ~16px instead of the old ~52px). */
    margin: .5rem 0 0;
}
.recv-day-divider span::before {
    content: "▾";
    display: inline-block;
    font-size: .9em;
    line-height: 1;
    color: var(--accent);
    font-weight: 800;
    /* No transition — user-perceived collapse delay vanishes when the
     * chevron flips instantly (2026-05-12 user feedback «Έχει αρκετό delay»). */
}
.recv-day-divider.is-collapsed span::before { transform: rotate(-90deg); }
.recv-day-divider.is-collapsed span {
    background: color-mix(in srgb, var(--muted) 18%, var(--bg-card));
    border-color: color-mix(in srgb, var(--muted) 40%, var(--line));
    color: var(--muted);
}
.recv-day-divider.is-collapsed::before,
.recv-day-divider.is-collapsed::after {
    opacity: .35;
}
/* Per-day completion stats («5/8 · 63%»). Subtler than the date label —
 * monospace digits for a clean alignment regardless of value width. Tint
 * shifts with completion: complete=green, mid=amber-ish, low=muted. */
.recv-day-stats {
    margin-left: .55em;
    font-size: .82em;
    font-weight: 600;
    letter-spacing: 0;
    font-variant-numeric: tabular-nums;
    opacity: .75;
    color: var(--muted);
}
.recv-day-divider.is-complete .recv-day-stats { color: #16a34a; opacity: .9; }
.recv-day-divider.is-mid      .recv-day-stats { color: #f59e0b; opacity: .9; }
.recv-day-divider.is-low      .recv-day-stats { color: var(--muted); }
/* Per-day status chips on the divider — 🟢 N 🔵 N 🟡 N ⚪ N 🟣 N ⚫ N.
 * Only render server-side when count > 0 (PHP loop), so they're a tight
 * inline breakdown of "what's in this day" right after the completion %.
 * Stays visible when the day is collapsed (great for at-a-glance scanning). */
.recv-day-status {
    margin-left: .5em;
    font-size: .82em;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    color: inherit;
}
.recv-day-divider.is-collapsed .recv-day-status {
    color: var(--ink, inherit);
}
.recv-day-divider:hover span {
    background: color-mix(in srgb, var(--accent) 24%, var(--bg-card));
}
/* Ring στο parent ώστε να επιβιώνει αν το inner span re-render-εται (BUG-092). */
.recv-day-divider:focus-visible { outline: none; box-shadow: var(--ring); border-radius: 8px; }
/* Hidden cards: `display:none` collapses the grid row entirely (no
 * residual `row-gap` because the JS renumbers visible items into a
 * contiguous range — see `applyCollapsed` in bank-reconcile.js). */
.recv-card.is-day-hidden { display: none; }
/* Desktop: each divider is emitted in BOTH panes (bank + tx) so mobile
 * stacked layout shows separators in each pane independently. On desktop
 * panes are `display: contents`, so two divider elements end up at the
 * same grid cell and visually overlap. Hide the tx-pane copy to avoid
 * the double paint — bank-pane copy already spans column 1 / -1. */
@media (min-width: 901px) {
    .recv-pane-tx .recv-day-divider { display: none; }
}

/* Tx-side card: small category icon as left thumbnail */
.recv-tx-cat {
    display: inline-flex; align-items: center; justify-content: center;
    width: 24px; height: 24px; border-radius: 50%;
    font-size: .9rem; flex-shrink: 0;
}.recv-controls .recv-help {
    color: var(--muted); font-size: .85rem; margin-left: auto;
}
.recv-controls .recv-help.is-active {
    color: #3b82f6; font-weight: 600;
}

/* Desktop: panes are display:contents so cards become direct grid items
   placed by `--row` (set inline server-side). Matched bank/tx pairs share
   a row → tx ends up directly opposite its bank movement. Unmatched txs
   get rows below the bank list, in their own occurred_at ASC order. */
@media (min-width: 901px) {
    .recv-grid > .recv-pane { display: contents; }
    .recv-pane-title-bank { grid-column: 1; grid-row: 1; }
    .recv-pane-title-tx   { grid-column: 2; grid-row: 1; }
    .recv-card.recv-bank { grid-column: 1; grid-row: var(--row); }
    .recv-card.recv-tx   { grid-column: 2; grid-row: var(--row); }
}

/* Mobile fallback — legacy list view (existing styles already in place) */
@media (max-width: 900px) {
    .recv-grid { grid-template-columns: 1fr; column-gap: 0; }
    .recv-svg { display: none; }
    .recv-pane.recv-pane-tx::before {
        content: "Δικές σου συναλλαγές";
        font-size: .8rem; font-weight: 600; text-transform: uppercase;
        color: var(--muted); letter-spacing: .04em;
        text-align: center;
        margin-top: 1.5rem;
    }
}
:root[data-theme="dark"] .recv-card {
    background: linear-gradient(150deg, color-mix(in srgb, var(--bg-card) 92%, white 4%) 0%, var(--bg-card) 100%);
    box-shadow:
        0 1px 2px rgba(0,0,0,.4),
        0 4px 10px rgba(0,0,0,.35),
        0 12px 24px rgba(0,0,0,.25);
}
:root[data-theme="dark"] .recv-card:hover {
    box-shadow:
        0 2px 4px rgba(0,0,0,.5),
        0 8px 18px rgba(0,0,0,.45),
        0 20px 40px rgba(0,0,0,.35);
}.bank-mov-summary .chip {
    display: inline-flex; align-items: center; gap: .25rem;
    padding: .15rem .55rem; border-radius: 999px;
    font-size: .8rem; background: var(--bg-card); border: 1px solid var(--line);
}@media (max-width: 600px) {
    .bank-mov-row {
        grid-template-columns: 24px 1fr 90px;
        grid-template-areas: "s d a" "s desc desc";
        gap: .15rem .5rem;
    }
    .bank-mov-status { grid-area: s; }
    .bank-mov-date { grid-area: d; }
    .bank-mov-amount { grid-area: a; }
    .bank-mov-desc { grid-area: desc; white-space: normal; }
}

/* 12. Flash + toast ---------------------------------------------- */
.flash {
    padding: .75rem 1rem;
    border-radius: 12px;
    margin: 0 0 1rem;
    font-weight: 500;
    font-size: .95rem;
}
.flash-success { background: #dcfce7; color: #065f46; border: 1px solid #bbf7d0; }
.flash-error   { background: #fee2e2; color: #991b1b; border: 1px solid #fecaca; }
.flash-undo    { display: inline-block; margin-left: .5rem; vertical-align: middle; }
.flash-undo button {
    font-weight: 700;
    text-decoration: underline;
    color: inherit;
    padding: .35rem .55rem;         /* actual hit area, not just text */
    margin: -.35rem 0;              /* don't inflate the flash box height */
    min-height: 36px;
}

#toast-host {
    position: fixed;
    left: 0;
    right: 0;
    bottom: calc(var(--safe-bottom) + 1rem);
    display: flex;
    justify-content: center;
    z-index: 1000;
    pointer-events: none;
    padding: 0 1rem;
}
.toast {
    background: var(--ink);
    color: #fff;
    padding: .85rem 1.1rem;
    border-radius: 14px;
    font-weight: 600;
    font-size: .95rem;
    box-shadow: 0 12px 32px rgba(15,23,42,.35);
    animation: toastIn .25s ease;
    pointer-events: auto;
    max-width: 92vw;
    display: flex;
    align-items: center;
    gap: .5rem;
}
.toast.success { background: var(--good); }
.toast.error   { background: var(--bad); }
@keyframes toastIn {
    from { transform: translateY(120%); opacity: 0; }
    to   { transform: translateY(0);    opacity: 1; }
}
@keyframes toastOut {
    from { transform: translateY(0);    opacity: 1; }
    to   { transform: translateY(120%); opacity: 0; }
}
.toast.leaving { animation: toastOut .2s ease forwards; }

/* Secondary action button inside a toast — used by the AJAX delete
 * undo flow (BUG-015). `.toast-action-form` is a real <form method="post">
 * with CSRF token so clicking "Αναίρεση" actually restores the tx. */
.toast-msg         { flex: 1; min-width: 0; }
.toast-action-form { display: inline-flex; margin-left: .25rem; }
.toast-action      {
    background: rgba(255,255,255,.2);
    color: inherit;
    border: 0;
    padding: .35rem .75rem;
    border-radius: 8px;
    font-weight: 700;
    font-size: .9rem;
    cursor: pointer;
    white-space: nowrap;
}
.toast-action:hover { background: rgba(255,255,255,.35); }
.toast-action:focus-visible {
    outline: none;
    background: rgba(255,255,255,.35);
    box-shadow: 0 0 0 2px rgba(255,255,255,.6);
}

/* 13. Auth card --------------------------------------------------- */
.auth-body {
    min-height: 100dvh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1.5rem;
    padding-top: calc(1.5rem + var(--safe-top));
    padding-bottom: calc(1.5rem + var(--safe-bottom));
    background: linear-gradient(135deg, #0f172a 0%, #1e3a8a 100%);
}
.auth-card {
    background: var(--bg-card, #fff);
    padding: 2rem 1.8rem;
    border-radius: 20px;
    box-shadow: var(--shadow-lg);
    width: 100%;
    max-width: 400px;
    border: 1px solid transparent;
}
.auth-head { text-align: center; margin-bottom: 1.4rem; }
.auth-head h1 { font-size: 1.45rem; margin-bottom: .2rem; color: var(--primary); }
.auth-head p  { color: var(--muted); font-size: .92rem; }
.auth-card .form label { margin-bottom: 1rem; }

/* 14. Charts ------------------------------------------------------ */
.chart-wrap {
    position: relative;
    height: 230px;
    /* Clip stale canvas inline-styles after mobile rotation. Chart.js
     * leaves `style="width: NNNpx"` from the previous orientation; if
     * the new container is narrower the canvas overflows and pushes
     * the parent `.card` outside `<main>` boundaries (BUG-043). */
    overflow: hidden;
}
.chart-wrap.tall { height: 260px; }
/* Defensive: any stale inline width on the canvas can't exceed the
 * wrapper. Pairs with the rotation handler in dashboard.js. */
.chart-wrap canvas { max-width: 100% !important; }
.chart-empty { padding: 2rem 0; text-align: center; }
.charts-grid .card { margin-bottom: 0; }

/* Forecast KPI card (/stats). Markup shares the `.kpis` grid with
 * dashboard + report — grid layout (count-aware: 4 KPIs = 4×1 ή 2×2,
 * 6 KPIs = 6×1 ή 3×2 ή 2×3) inherits από το generic `.kpis` rules.
 * Ο modifier κρατάει μόνο το centered text alignment ώστε τα labels
 * + amounts να κεντράρονται μέσα στο cell — desktop reads better σε
 * symmetric cluster. `.forecast-proj` adds dashed/italic treatment
 * για projection rows. */
.forecast-card { margin-bottom: 1rem; }
@media (max-width: 720px) {
    .forecast-kpis .kpi { padding: .7rem .65rem; }
}
.forecast-kpis .kpi {
    text-align: center;
    display: flex;
    flex-direction: column;
    align-items: center;
}
.kpi.forecast-proj .kpi-value {
    opacity: .75;
    font-style: italic;
}.summary-value.pos .money { color: var(--good); }
.summary-value.neg .money { color: var(--ink); }.kpi-delta.good { color: var(--good); }
.kpi-delta.bad  { color: var(--bad); }
.kpi-delta.neutral { color: var(--muted); font-weight: 500; }

.forecast-card .chart-wrap { margin-top: .8rem; }
.insights-card .insights {
    margin: .4rem 0 0;
    padding-left: 1.1rem;
}
.insights-card li { margin: .2rem 0; font-size: .95rem; }

.top-cats-card .budget-list { margin-top: .4rem; }

/* Group chart card: keep bars comfortable on mobile. */
#chartGroups { max-height: 340px; }
@media (min-width: 720px) {
    #chartGroups { max-height: 380px; }
}

/* 14b. Categories page — card list + pin toggle ------------------ */

/* New-category / new-group form — redesigned 2026-04-16 (BUG-025).
 * Replaces the earlier single-flex-row layout that became a cramped
 * "parent + checkbox + colour + Προσθήκη" strip on mobile (BUG-018)
 * and never felt cohesive on desktop either. The new design is a
 * stacked, semantic form:
 *   1. Mode picker (segmented, matches /settings theme picker pattern)
 *   2. Icon + name (identity row)
 *   3. Conditional field: parent dropdown (leaf) OR color swatch (group)
 *   4. Full-width primary submit CTA
 * Same vertical structure on desktop and mobile — the card is narrow
 * enough that the old single-row layout only ever saved a few pixels
 * and traded clarity for horizontal compression. Outer card padding is
 * set at the toggle rule (.cat-new[data-cat-new]) further down. */
.cat-new-form {
    display: flex;
    flex-direction: column;
    gap: .7rem;
    margin: 0;
}
/* Explicit override — custom `display: flex` above loses the
 * specificity tie to the UA `[hidden] { display: none }`, so without
 * this the `hidden` attribute becomes a no-op and the form never
 * collapses behind its toggle. Same gotcha as BUG-025. */
.cat-new-form[hidden] { display: none; }

/* Shared input/select styling for the form. font-size: 16px για iOS no-zoom
 * (BUG-027). appearance: none μόνο σε <select> — BUG-028 για input[type=text]. */
.cat-new-form input[type="text"],
.cat-new-form select {
    padding: .55rem .65rem;
    font-size: 16px;
    min-height: 40px;
    width: 100%;
    border: 1px solid var(--line);
    border-radius: 9px;
    background: var(--bg-card);
    color: var(--ink);
    font-family: inherit;
}
.cat-new-form select {
    -webkit-appearance: none;
    appearance: none;
}

/* Mode picker — iOS-style pill segmented control. The native radios
 * are hidden (opacity: 0, still keyboard-accessible) and the <label>
 * paints the state via :has(input:checked). */
.cat-new-mode {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 4px;
    padding: 4px;
    background: var(--bg-subtle, rgba(100,116,139,.12));
    border-radius: 12px;
    border: none;
    margin: 0;
}
.cat-new-mode-opt {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .4rem;
    padding: .55rem .5rem;
    font-size: .9rem;
    font-weight: 600;
    color: var(--muted);
    cursor: pointer;
    border-radius: 9px;
    user-select: none;
    transition: background .15s, color .15s, box-shadow .15s;
    min-height: 40px;
}
.cat-new-mode-opt input {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}
.cat-new-mode-opt:has(input:checked) {
    background: var(--bg-card);
    color: var(--ink);
    box-shadow: 0 1px 2px rgba(0,0,0,.12);
}
.cat-new-mode-ico { font-size: 1.05rem; line-height: 1; }

/* Single-row layout: Όνομα (flex-grow) + icon (44) + color (44) +
 * Ομάδα dropdown (flex-grow, Κατηγορία mode only). flex-wrap lets the
 * row break to two lines on very narrow viewports (<360 px) but at
 * 430 px mobile everything fits on one line. */
.cat-new-row {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
    align-items: stretch;
    min-width: 0;
}
.cat-new-row .cat-new-name {
    flex: 2 1 100px;
    min-width: 0;
}
.cat-new-row .cat-new-icon {
    flex: 0 0 44px;
    width: 44px;
    text-align: center;
    font-size: 1.2rem;
    padding-left: .2rem;
    padding-right: .2rem;
}
/* Icon picker button replaces the manual emoji text input — opens the
 * emoji-picker overlay on click. Sized to match the adjacent color
 * swatch (44x40) so the Όνομα + emoji + χρώμα triplet stays aligned. */
.cat-new-row .cat-new-icon-btn {
    flex: 0 0 44px;
    width: 44px;
    height: 40px;
    align-self: center;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 1.3rem;
    padding: 0;
    border: 1px solid var(--line);
    border-radius: .5rem;
    background: var(--bg-subtle);
    color: var(--ink);
    cursor: pointer;
    line-height: 1;
    transition: background .12s, border-color .12s;
}
.cat-new-row .cat-new-icon-btn:hover,
.cat-new-row .cat-new-icon-btn:focus-visible {
    background: var(--bg-inset);
    border-color: var(--primary);
}

/* ---------- Emoji picker overlay ----------
 * Reuses `.overlay` + `.overlay-backdrop` recipe. Grid of emoji cells
 * grouped by theme (Φαγητό, Σπίτι, Μεταφορές, …) with a search input
 * that filters on keyword substrings (GR+EN). Tall body, scrolls
 * inside `.emoji-picker-list`. */
.overlay.emoji-picker {
    display: flex;
    align-items: flex-end;
    justify-content: center;
}
.overlay.emoji-picker[hidden] { display: none; }
@media (min-width: 640px) {
    .overlay.emoji-picker { align-items: center; }
}
.emoji-picker-body {
    position: relative;
    z-index: 1;
    width: 100%;
    max-width: 560px;
    max-height: 85vh;
    display: flex;
    flex-direction: column;
    background: var(--bg-card);
    color: var(--ink);
    border-radius: 1rem 1rem 0 0;
    box-shadow: var(--shadow-lg);
    overflow: hidden;
}
@media (min-width: 640px) {
    .emoji-picker-body { border-radius: 1rem; max-height: 80vh; }
}
.emoji-picker-head {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .75rem 1rem;
    border-bottom: 1px solid var(--line);
}
.emoji-picker-title {
    font-size: 1rem;
    font-weight: 600;
    margin: 0;
    flex: 1 1 auto;
    color: var(--ink);
}
.emoji-picker-search {
    padding: .6rem .75rem;
    border-bottom: 1px solid var(--line);
}
.emoji-picker-search-input {
    width: 100%;
    padding: .55rem .75rem;
    font-size: 16px; /* BUG-027 iOS auto-zoom guard */
    border: 1px solid var(--line);
    border-radius: .5rem;
    background: var(--bg-subtle);
    color: var(--ink);
}
.emoji-picker-search-input::placeholder { color: var(--muted); }
.emoji-picker-list {
    overflow-y: auto;
    padding: .5rem .75rem 1rem;
    -webkit-overflow-scrolling: touch;
}
.emoji-picker-group { margin-top: .75rem; }
.emoji-picker-group[hidden] { display: none; }
.emoji-picker-group-title {
    font-size: .78rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--muted);
    margin: 0 0 .4rem;
}
.emoji-picker-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(40px, 1fr));
    gap: .25rem;
}
.emoji-picker-cell {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    aspect-ratio: 1 / 1;
    font-size: 1.5rem;
    padding: 0;
    background: transparent;
    border: 1px solid transparent;
    border-radius: .5rem;
    cursor: pointer;
    line-height: 1;
    transition: background .1s, transform .08s;
}
.emoji-picker-cell[hidden] { display: none; }
.emoji-picker-cell:hover,
.emoji-picker-cell:focus-visible {
    background: var(--bg-subtle);
    border-color: var(--primary);
    transform: scale(1.08);
}
@media (pointer: coarse) {
    .emoji-picker-cell { min-height: 44px; }
}

/* Color swatch wrapper — 44 px square to match icon. 🎨 glyph overlay
 * for affordance (the row reads as a "what does this look like" trio:
 * emoji + color + group). */
.cat-new-color-wrap {
    flex: 0 0 44px;
    width: 44px;
    height: 40px;
    min-height: 40px;
    max-height: 40px;
    align-self: center;
    display: flex;
    align-items: stretch;
    justify-content: stretch;
    box-sizing: border-box;
}
.cat-new-color {
    width: 100%;
    height: 40px;
    min-height: 40px;
    max-height: 40px;
    padding: 3px;
    border: 1px solid var(--line);
    border-radius: 9px;
    cursor: pointer;
    background: var(--bg-card);
    -webkit-appearance: none;
    appearance: none;
    box-sizing: border-box;
}
.cat-new-color::-webkit-color-swatch-wrapper { padding: 0; border-radius: 6px; }
.cat-new-color::-webkit-color-swatch        { border: 0; border-radius: 6px; }
.cat-new-color::-moz-color-swatch           { border: 0; border-radius: 6px; }
.cat-new-color:hover,
.cat-new-color:focus { outline: 2px dashed var(--primary); outline-offset: 1px; }

/* Parent dropdown — chevron, shown in its own row inside `.cat-new-field` */
.cat-new-parent {
    padding-right: 1.8rem;
    background: var(--bg-card) no-repeat right .55rem center / 14px url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
}

/* Conditional field wrapper — parent dropdown in Κατηγορία mode only.
 * Sits in its own standalone row BELOW `.cat-new-row` (per user
 * preference — dropdown in a separate line is easier to scan and
 * leaves the top row focused on identity: name + icon + color).
 * Explicit `[hidden]` rule required because the `display` override
 * wins the specificity tie against UA `[hidden]{display:none}`. */
.cat-new-field {
    display: block;
    min-width: 0;
}
.cat-new-field[hidden] { display: none; }

/* Submit CTA — full-width primary on mobile, inline on desktop */
.cat-new-submit {
    width: 100%;
    padding: .75rem 1rem;
    font-size: 1rem;
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .35rem;
}

/* Desktop (≥ 720 px): full-width submit becomes right-aligned auto-
 * width — makes the button a clear "action" accent instead of a
 * dominant full-bleed bar on a roomy viewport. Mobile keeps the
 * full-width CTA so the thumb has a big target. */
@media (min-width: 720px) {
    .cat-new-submit {
        width: auto;
        align-self: flex-end;
        min-width: 160px;
        padding: .65rem 1.4rem;
    }
}

/* Search pill inside the card title — right-aligned, rounded, icon prefix. */
.cat-search {
    margin-left: auto;
    flex: 0 1 260px;
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: 0 .7rem;
    background: var(--bg-subtle, #f1f5f9);
    border: 1px solid var(--line);
    border-radius: 999px;
    transition: border-color .12s, box-shadow .12s;
}
.cat-search svg { color: var(--muted); flex-shrink: 0; }
.cat-search input {
    border: none;
    outline: none;
    background: transparent;
    padding: .45rem 0;
    font-size: .9rem;
    color: var(--ink);
    font-family: inherit;
    flex: 1;
    min-width: 0;
    font-weight: 400;
}
.cat-search input::placeholder { color: var(--muted); }
.cat-search:focus-within {
    border-color: var(--primary);
    box-shadow: var(--ring);
}
.cat-empty {
    margin: .9rem 0 .3rem;
    text-align: center;
    font-size: .9rem;
}.cat-card {
    background: #fff;
    border: 1px solid var(--line);
    border-radius: 14px;
    padding: .7rem .85rem;
    transition: border-color .15s, background .15s;
}
/* Pinned: διακριτικό — μόνο χρυσό περίγραμμα, όχι κίτρινο γέμισμα. */
.cat-card.pinned { border-color: #f59e0b; }
.cat-card.archived { opacity: .6; background: #f8fafc; }.cat-card.editing .cat-card-head { margin-bottom: .65rem; }
.cat-card-head .cat-icon {
    width: 38px;
    height: 38px;
    font-size: 1.2rem;
    border-radius: 11px;
    flex-shrink: 0;
}
/* Quick-action icon buttons in the card head (edit, archive, …) */
.cat-head-actions {
    display: inline-flex;
    align-items: center;
    gap: .1rem;
    margin-left: auto;
    flex-shrink: 0;
}
.cat-quick-action { display: inline-flex; }
.icon-btn {
    border: none;
    background: transparent;
    color: var(--muted);
    width: 36px;
    height: 36px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 9px;
    cursor: pointer;
    padding: 0;
    transition: background .15s, color .15s;
    -webkit-tap-highlight-color: transparent;
}
.icon-btn:hover { background: rgba(148,163,184,.14); color: var(--ink-soft); }
.icon-btn:focus-visible { outline: none; box-shadow: var(--ring); background: rgba(148,163,184,.14); color: var(--ink-soft); }
.icon-btn:active { transform: scale(.92); }
.icon-btn.danger:hover { color: var(--bad); background: var(--bad-50); }
.icon-btn.primary { color: var(--primary); }
.icon-btn.primary:hover { color: var(--primary-d); background: var(--primary-50); }
.icon-btn svg { display: block; }
.icon-btn:disabled,
.icon-btn[disabled] {
    opacity: .35;
    cursor: not-allowed;
    pointer-events: auto; /* keep title tooltip visible on hover */
}
.icon-btn:disabled:hover,
.icon-btn[disabled]:hover { background: transparent; color: var(--muted); }
.cat-card.editing [data-cat-edit-toggle] {
    background: var(--primary-50);
    color: var(--primary);
}

/* Edit form: collapsed by default, ανοίγει μόνο όταν πατηθεί το ✎ */
.cat-edit { display: none; }
.cat-card.editing .cat-edit { display: block; }

.pin-form { display: inline; flex-shrink: 0; }
/* Base styling comes from `.icon-btn`; pin-btn only customises the
 * star size + the "pinned" amber tint. */
.pin-btn { font-size: 1.4rem; color: #cbd5e1; }
.pin-btn.on { color: #f59e0b; }
.pin-btn.on:hover { color: #d97706; background: rgba(245,158,11,.12); }

/* ============================================================
 * Drag-to-reorder (pinned categories grid)
 * ------------------------------------------------------------
 * The drag handle is the only element with `touch-action: none`,
 * so touching elsewhere on the card still scrolls the page
 * normally on mobile.
 * ============================================================ */
.drag-handle {
    border: none;
    background: transparent;
    color: #94a3b8;
    font-size: 1rem;
    line-height: 1;
    width: 32px;
    height: 38px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: grab;
    padding: 0;
    touch-action: none;
    -webkit-tap-highlight-color: transparent;
    user-select: none;
    letter-spacing: -2px;
    flex-shrink: 0;
}
.drag-handle:hover { color: #64748b; }
.drag-handle:active { cursor: grabbing; color: #334155; }

.cat-card.dragging {
    box-shadow: 0 20px 45px -10px rgba(15,23,42,.35),
                0 8px 20px -8px rgba(15,23,42,.25);
    transform: scale(1.02);
    opacity: .96;
    cursor: grabbing;
    transition: none;   /* kill any inherited transitions during the drag */
}
.drag-placeholder {
    border: 2px dashed #cbd5e1;
    border-radius: var(--radius, 14px);
    background: rgba(148,163,184,.08);
    box-sizing: border-box;
}
@media (max-width: 480px) {
    .cat-edit-grid {
        grid-template-columns: 60px 1fr;
    }
    .cat-edit-grid label:nth-child(3),
    .cat-edit-grid label:nth-child(4) {
        grid-column: span 1;
    }
    .cat-edit-grid label:nth-child(3) { grid-column: 1 / span 1; grid-row: 2; }
    .cat-edit-grid label:nth-child(4) { grid-column: 2 / span 1; grid-row: 2; }
}
/* Desktop: κατηγορίες σε μία γραμμή, όχι κουτάκια.
   Αντί για card-grid, μια απλή λίστα με divider. */
@media (min-width: 720px) {
    .cat-grid {
        grid-template-columns: 1fr;
        gap: 0;
        border-top: 1px solid var(--line);
    }
    .cat-card {
        background: transparent;
        border: none;
        border-bottom: 1px solid var(--line);
        border-radius: 0;
        padding: .45rem .4rem .45rem .55rem;
    }
    .cat-card:hover { background: rgba(148,163,184,.06); }
    .cat-card.pinned {
        background: transparent;
        border-color: var(--line);
        box-shadow: inset 3px 0 0 #f59e0b;
        padding-left: .85rem;
    }
    .cat-card.pinned:hover { background: rgba(245,158,11,.05); }
    .cat-card.archived { background: transparent; }
    .cat-card.editing {
        background: rgba(37, 99, 235, .035);
        box-shadow: inset 3px 0 0 var(--primary);
        padding-left: .85rem;
    }
    .cat-card.editing.pinned { box-shadow: inset 3px 0 0 #f59e0b, inset 0 -1px 0 var(--primary); }

    .cat-card-head { gap: .5rem; }
    .cat-card.editing .cat-card-head { margin-bottom: .5rem; }
    .cat-card-head .cat-icon {
        width: 34px;
        height: 34px;
        font-size: 1.05rem;
        border-radius: 9px;
    }
    .pin-btn { font-size: 1.25rem; }
    .icon-btn { width: 32px; height: 32px; }
    .drag-handle { height: 34px; }
}

/* 15. Desktop overrides ------------------------------------------ */
@media (min-width: 720px) {
    h1 { font-size: 1.55rem; }
    h2 { font-size: 1.15rem; }

    .topbar-inner { gap: 1rem; flex-wrap: wrap; padding: .7rem 1rem; }
    .nav {
        order: 0;
        width: auto;
        margin-left: auto;
        flex-wrap: nowrap;
        padding: 0;
        margin-right: 0;
    }
    .nav a { padding: .45rem .85rem; border-radius: 10px; font-size: .95rem; }

    .page-head { margin: 1rem 0 1.3rem; }
    .page-head h1 { margin-bottom: .15rem; }
    .month-picker input { max-width: 170px; }

    .kpis {
        /* Count-aware grid: ποτέ orphan rows (3+1 για 4 KPIs ή 4+2 για 6).
         * Default base mobile rule (2 cols) ισχύει για unmatched counts.
         * Mobile (<720px) πάντα 2 cols → 4 KPIs = 2×2, 6 KPIs = 2×3. */
        gap: 1rem;
    }
    /* 3 KPIs → 3×1 */
    .kpis:has(.kpi:nth-child(3)):not(:has(.kpi:nth-child(4))) {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
    /* 4 KPIs → 4×1 (full row· fallback 2×2 σε mobile via base rule) */
    .kpis:has(.kpi:nth-child(4)):not(:has(.kpi:nth-child(5))) {
        grid-template-columns: repeat(4, minmax(0, 1fr));
    }
    /* 6 KPIs → 3×2 σε desktop (medium) */
    .kpis:has(.kpi:nth-child(6)):not(:has(.kpi:nth-child(7))) {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
    .kpi { padding: 1rem 1.1rem; }
    .kpi-label { font-size: .76rem; }
    .kpi-value { font-size: 1.45rem; }
    .kpi-sub   { font-size: .78rem; }

    .card { padding: 1.1rem 1.25rem; margin-bottom: 1.1rem; }
    .grid-2 { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }

    .tiles-section { padding: 1.2rem 1.35rem 1.3rem; }
    .tiles { grid-template-columns: repeat(5, minmax(0, 1fr)); gap: .85rem; }
    .tile { min-height: 118px; }
    .tile-icon { width: 60px; height: 60px; font-size: 2rem; }
    .tile-name { font-size: .82rem; }

    /* Desktop: vertically center the sheet so the calendar / wheels
       don't get clipped or hide content below. (.overlay is flex-column,
       so vertical centering uses justify-content.) */
    .sheet { justify-content: center; }
    .sheet-body {
        max-width: 480px;
        border-radius: 28px;
        margin: 0;
        max-height: min(86vh, 760px);
    }

    /* No padding on .tx-item in wide viewports — `.tx-swipe-surface`
     * has its own padding (see line ~1677). Adding padding here
     * creates a gap that `.tx-swipe-bg` (`inset: 0` on .tx-item)
     * fills, leaking the red delete / blue edit background past the
     * visible row when swiped — ugly in landscape mobile. */
    .tx-title { font-size: 1rem; }
    .tx-sub { font-size: .82rem; }
    .tx-amount { font-size: 1rem; }

    .chart-wrap { height: 260px; }
    .chart-wrap.tall { height: 320px; }

    /* Desktop: το budget rows γίνεται flat λίστα (όχι κουτάκια),
       ίδια λογική με το /categories. */
    .budget-edit {
        grid-template-columns: 1fr;
        gap: 0;
        border-top: 1px solid var(--line);
    }
    .budget-edit-row {
        background: transparent;
        border: none;
        border-bottom: 1px solid var(--line);
        border-radius: 0;
        padding: .5rem .4rem .55rem .55rem;
    }
    .budget-edit-row:hover { background: rgba(148,163,184,.06); }
    .budget-edit-row.has-limit {
        background: transparent;
        border-color: var(--line);
        box-shadow: inset 3px 0 0 var(--primary);
        padding-left: .85rem;
    }
    .budget-edit-row.has-limit:hover { background: rgba(37, 99, 235, .04); }
    .budget-edit-row .budget-head .cat-icon {
        width: 34px; height: 34px;
        font-size: 1.05rem;
        border-radius: 9px;
    }
    .budget-edit-row .progress {
        margin-top: .45rem;
        margin-left: 46px;
        width: calc(100% - 46px);
    }
}

@media (min-width: 480px) {
    .tiles { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

@media (min-width: 1000px) {
    .tiles { grid-template-columns: repeat(6, minmax(0, 1fr)); }
}

/* Wide desktop: 6 KPIs χωράνε σε 1 row (medium desktop falls back to 3×2). */
@media (min-width: 1200px) {
    .kpis:has(.kpi:nth-child(6)):not(:has(.kpi:nth-child(7))) {
        grid-template-columns: repeat(6, minmax(0, 1fr));
    }
}

/* ============================================================
 *  New features: suggest-chip, goals, report,
 *  theme-choice, import
 * ============================================================ */

/* /transactions "+ Νέα κίνηση" opener card — sits where the tile grid
 * would on the dashboard, but is a single big button. */
.new-tx-wrap {
    padding: 1.1rem;
}

/* Breakdown line shown under the big total when a ×N multiplier is active.
 * e.g. "2 × 2,20 €" centered in a muted color. */
.sheet-breakdown {
    text-align: center;
    font-size: .95rem;
    color: var(--muted);
    margin: -.4rem 0 .7rem;
    font-variant-numeric: tabular-nums;
    letter-spacing: .01em;
}

/* Feature 16: category suggestion chip */
.suggest-chip {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    padding: .4rem .7rem;
    border: 1px dashed var(--primary);
    background: rgba(37,99,235,.08);
    color: var(--primary);
    border-radius: 999px;
    font-size: .85rem;
    cursor: pointer;
    font-family: inherit;
    margin: .4rem 0;
    align-self: flex-start;
}
.suggest-chip:hover { background: rgba(37,99,235,.15); }

/* Feature 8: theme picker on /settings */
.theme-choices {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: .5rem;
    max-width: 420px;
}
.theme-choice {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .4rem;
    padding: .6rem .8rem;
    border: 1px solid var(--line-2);
    border-radius: 12px;
    background: var(--bg-card);
    cursor: pointer;
    font-size: .95rem;
    min-height: 44px;
}
.theme-choice input { accent-color: var(--primary); }
.theme-choice:has(input:checked) {
    border-color: var(--primary);
    background: rgba(37,99,235,.1);
    font-weight: 600;
}

/* Feature 11: report page. Uses the shared `.kpis` grid from the
 * dashboard now — no bespoke `.report-kpis` anymore. `.report-head`
 * is rendered only during print (the screen view uses the shared
 * `.month-header`). */
.report-head { flex-wrap: wrap; gap: 1rem; }
/* Print-only / screen-only visibility helpers. Default is screen-only:
 * hide when the print stylesheet block below flips .no-print off. */
.print-only { display: none; }
@media print {
    .print-only { display: block; }
}
.insights {
    margin: 0;
    padding-left: 1.2rem;
    line-height: 1.75;
}
.insights li { font-size: .95rem; }

/* ============================================================
 * Custom date / datetime / month picker (date-picker.js)
 *
 * Two render modes:
 *   .dp-floating — absolutely positioned popover (desktop)
 *   .dp-modal    — centered modal, backed by .dp-backdrop (mobile)
 *
 * All colors come from theme tokens, so light/dark work automatically.
 * ============================================================ */
.dp-backdrop {
    position: fixed; inset: 0;
    background: rgba(15, 23, 42, .5);
    z-index: 1900;
    animation: dp-fade .12s ease-out;
}
.dp-popover {
    z-index: 2000;
    width: 320px;
    max-width: calc(100vw - 24px);
    background: var(--bg-card);
    color: var(--ink);
    border: 1px solid var(--line);
    border-radius: 16px;
    box-shadow: 0 18px 40px -12px rgba(15, 23, 42, .28),
                0 6px 18px -8px rgba(15, 23, 42, .18);
    padding: .7rem .8rem .8rem;
    font-family: inherit;
    -webkit-user-select: none;
    user-select: none;
    animation: dp-pop .12s ease-out;
}
.dp-popover.dp-floating { position: absolute; }
.dp-popover.dp-modal {
    position: fixed;
    top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    width: min(340px, calc(100vw - 32px));
}
@keyframes dp-fade { from { opacity: 0 } to { opacity: 1 } }
@keyframes dp-pop  { from { opacity: 0; transform: translateY(-4px) scale(.98) } to { opacity: 1; transform: translateY(0) scale(1) } }
.dp-popover.dp-modal { animation: dp-modal .15s ease-out; }
@keyframes dp-modal { from { opacity: 0; transform: translate(-50%, -48%) scale(.96) } to { opacity: 1; transform: translate(-50%, -50%) scale(1) } }

.dp-head {
    display: flex;
    align-items: center;
    gap: .4rem;
    margin-bottom: .5rem;
}
.dp-title {
    flex: 1;
    text-align: center;
    font-weight: 700;
    font-size: 1rem;
    letter-spacing: -.01em;
    color: var(--ink);
}
.dp-nav {
    border: none;
    background: transparent;
    color: var(--ink-soft);
    width: 34px;
    height: 34px;
    border-radius: 9px;
    font-size: 1.4rem;
    line-height: 1;
    cursor: pointer;
    transition: background .12s, color .12s;
}
.dp-nav:hover { background: rgba(148,163,184,.16); color: var(--ink); }
.dp-nav:active { transform: scale(.94); }

.dp-body { display: grid; gap: .35rem; }

.dp-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 2px;
}
.dp-dow {
    font-size: .7rem;
    font-weight: 600;
    text-transform: uppercase;
    color: var(--muted);
    text-align: center;
    padding: .35rem 0 .25rem;
    letter-spacing: .02em;
}
.dp-cell {
    min-height: 36px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: .88rem;
    font-family: inherit;
    background: transparent;
    color: var(--ink);
    border: 1px solid transparent;
    border-radius: 9px;
    cursor: pointer;
    padding: 0;
    transition: background .12s, color .12s, border-color .12s;
    font-variant-numeric: tabular-nums;
    -webkit-tap-highlight-color: transparent;
}
.dp-cell:hover:not(.dp-empty) { background: rgba(148,163,184,.14); }
.dp-cell.dp-empty { cursor: default; }
.dp-cell.dp-day.is-today {
    border-color: var(--line-2);
    font-weight: 600;
}
.dp-cell.is-selected {
    background: var(--primary);
    color: #fff;
    border-color: var(--primary);
    font-weight: 600;
}
.dp-cell.is-selected:hover { background: var(--primary-d, var(--primary)); }

.dp-months {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: .35rem;
}
.dp-months .dp-cell {
    min-height: 48px;
    font-size: .92rem;
}

.dp-time {
    margin-top: .55rem;
    padding-top: .55rem;
    border-top: 1px solid var(--line);
    color: var(--ink);
}
.dp-time-wheels {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .5rem;
}
.dp-time-sep {
    font-size: 1.4rem;
    font-weight: 700;
    color: var(--muted);
    line-height: 1;
}

/* iPhone-style scroll wheel.
 * Track height = ROW * VISIBLE (set inline by JS to keep numbers in sync).
 * The track translates on Y; only the centred row is fully opaque thanks
 * to the gradient mask, mirroring the iOS picker. */
.dp-wheel {
    position: relative;
    width: 64px;
    overflow: hidden;
    border: 1px solid var(--line);
    border-radius: 12px;
    background: var(--bg-subtle, transparent);
    cursor: grab;
    -webkit-user-select: none;
    user-select: none;
    touch-action: none;
}
.dp-wheel:active { cursor: grabbing; }
.dp-wheel:focus-visible {
    outline: none;
    border-color: var(--primary);
    box-shadow: var(--ring);
}
.dp-wheel-band {
    position: absolute;
    left: 4px; right: 4px;
    top: 50%;
    transform: translateY(-50%);
    height: 36px;                 /* must match ROW in JS */
    background: rgba(148,163,184,.15);
    border-radius: 8px;
    pointer-events: none;
    z-index: 1;
}
.dp-wheel-track {
    position: relative;
    z-index: 2;
    will-change: transform;
    /* No mask — the user wants every number readable, no fade-out. */
}
.dp-wheel-item {
    height: 36px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.05rem;
    font-variant-numeric: tabular-nums;
    color: var(--ink-soft);
    transition: color .15s, font-weight .15s;
    cursor: pointer;
}
.dp-wheel-item.is-selected {
    color: var(--ink);
    font-weight: 700;
}
:root[data-theme="dark"] .dp-wheel-band { background: rgba(255,255,255,.08); }

.dp-foot {
    display: flex;
    gap: .4rem;
    margin-top: .55rem;
    padding-top: .55rem;
    border-top: 1px solid var(--line);
}
.dp-btn {
    flex: 1;
    padding: .55rem .6rem;
    border: 1px solid var(--line);
    border-radius: 10px;
    background: var(--bg-card);
    color: var(--ink);
    font-size: .88rem;
    font-weight: 500;
    font-family: inherit;
    cursor: pointer;
    transition: background .12s, border-color .12s;
}
.dp-btn:hover { background: rgba(148,163,184,.12); }
.dp-btn.dp-btn-primary {
    background: var(--primary);
    color: #fff;
    border-color: var(--primary);
    font-weight: 600;
}
.dp-btn.dp-btn-primary:hover { background: var(--primary-d, var(--primary)); }

/* Native picker indicator hidden because our widget handles opening. */
input[type="date"]::-webkit-calendar-picker-indicator,
input[type="datetime-local"]::-webkit-calendar-picker-indicator,
input[type="month"]::-webkit-calendar-picker-indicator {
    opacity: 0;
    cursor: pointer;
}
/* Exception: the month-header picker in /stats + /budgets keeps its
   own invisible text style — nothing to change there. */
@media print {
    .topbar, .no-print { display: none !important; }
    body { background: #fff !important; }
    .card { box-shadow: none !important; border: 1px solid #ddd !important; }
}

/* Dashboard month header: left = "Απρίλιος 2026", right = "31/3 → 29/4".
 * Replaces the old page-head + datepicker on / — the dashboard is always
 * current-period and has no month navigation of its own. */
.month-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    margin: 1rem 0 1.1rem;
    flex-wrap: wrap;
}
.month-header-name,
.month-header-range {
    font-size: 1.2rem;
    font-weight: 700;
    letter-spacing: -.018em;
    line-height: 1.15;
}
.month-header-name  { color: var(--ink); }
.month-header-range {
    color: var(--muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* Month trigger used in /stats + /budgets headers — reads as the big
   bold month title (matches the dashboard's .month-header-name). A
   custom button, not a native <input type="month">, so iOS doesn't
   show the English locale value or pop its own wheel on tap. */
.month-header-picker { margin: 0; display: inline-flex; }
.month-header-trigger {
    font-size: 1.2rem;
    font-weight: 700;
    letter-spacing: -.018em;
    line-height: 1.15;
    color: var(--ink);
    background: transparent;
    border: none;
    /* padding + negative margin: enlarges the click/touch target on the
       right side (where the ▾ caret sits) without shifting layout. */
    padding: .4rem .7rem .4rem 0;
    margin: -.4rem 0 -.4rem 0;
    min-height: 0;
    box-shadow: none;
    font-family: inherit;
    cursor: pointer;
    text-align: left;
    -webkit-tap-highlight-color: transparent;
    border-radius: 6px;
}
.month-header-trigger:hover { background: rgba(148,163,184,.08); }
.month-header-trigger::after {
    content: "▾";
    font-size: .75em;
    color: var(--muted);
    margin-left: .35em;
    font-weight: 500;
    vertical-align: baseline;
}
.month-header-trigger:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 3px;
    border-radius: 3px;
}

/* Inline month trigger used in the /transactions filter row — looks
   like the rest of the form pills, with a tiny ▾ caret. */
.filter-month-wrap { display: inline-flex; }
.filter-month-trigger {
    padding: .55rem .7rem;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--bg-card);
    color: var(--ink);
    font-size: 16px;
    font-weight: 500;
    min-height: 40px;
    font-family: inherit;
    box-shadow: var(--shadow-sm);
    cursor: pointer;
    text-align: left;
    -webkit-tap-highlight-color: transparent;
    transition: border-color .12s, box-shadow .12s;
}
.filter-month-trigger::after {
    content: "▾";
    font-size: .75em;
    color: var(--muted);
    margin-left: .4em;
    font-weight: 500;
}
.filter-month-trigger:hover { border-color: var(--line-2); }
.filter-month-trigger:focus-visible {
    outline: none;
    border-color: var(--primary);
    box-shadow: var(--ring);
}

/* ============================================================
 * 16. New surfaces for 2026-04-11 v2:
 *   - Day groups on /transactions (separator + list per day)
 *   - tx-actions (✎/🗑) inline on every row
 *   - .kpi-savings card (replaces "Top κατηγορία")
 *   - /categories default_is_shared toggle button
 *   - Results head layout tightening
 * ============================================================ */

/* Day separators on /transactions */
.day-group { margin-bottom: 1.1rem; }
.day-group:last-child { margin-bottom: 0; }
.day-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .6rem;
    /* Horizontal padding matches `.tx-swipe-surface` (.5rem) so the
     * right-edge of the day total lands on the same x coordinate as
     * each row's amount below it. */
    padding: .55rem .5rem;
    margin: 0 0 .3rem 0;
    border-bottom: 1px solid var(--line);
    font-size: .82rem;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: .04em;
    font-weight: 600;
}
.day-header-label { flex: 1; min-width: 0; }
.day-header-totals {
    display: inline-flex;
    gap: .7rem;
    font-variant-numeric: tabular-nums;
    /* Match .tx-amount so the per-day total lines up visually with the
       individual transaction amounts above. */
    font-size: .95rem;
    font-weight: 700;
    text-transform: none;
    letter-spacing: normal;
}
.day-header-totals .pos { color: var(--good); }
.day-header-totals .neg { color: var(--bad); }

/* Inline tx actions (✎ / 🗑) — used on both the dashboard today list
 * and the day-grouped /transactions list. */
.tx-actions {
    display: flex;
    align-items: center;
    gap: .3rem;
    flex-shrink: 0;
    margin-left: .4rem;
}
.tx-inline-form { display: inline; margin: 0; padding: 0; }
/* Row edit/delete/restore buttons now reuse the canonical `.icon-btn`
 * class (see line ~2340). This used to be a separate `.tx-action` style
 * which had a different hover — consolidated 2026-04-15 so every icon
 * button (categories, budgets, tx rows, trash) shares the same look. */

/* Parent tile chevron (indicates "tap to see children" on the dashboard
 * tile grid) + child-picker overlay that slides up when a parent is
 * tapped. Children inherit the plain .tile look — they're just a smaller
 * grid inside the modal. */
.tile-parent { position: relative; }
.tile-chevron {
    position: absolute;
    top: 6px; right: 8px;
    font-size: .95rem;
    color: var(--muted);
    line-height: 1;
    font-weight: 700;
    opacity: .7;
}
/* Inherits positioning, pointer-events gate, [hidden] handling,
 * backdrop color, page scroll-lock, and opacity fade from .overlay. */
.child-picker { z-index: 60; justify-content: flex-end; }
.child-picker-body {
    position: relative;
    background: var(--bg-card);
    border-top-left-radius: var(--radius-l);
    border-top-right-radius: var(--radius-l);
    padding: .8rem 1rem calc(1rem + env(safe-area-inset-bottom, 0));
    box-shadow: 0 -12px 40px rgba(15,23,42,.25);
    max-height: 80vh;
    /* `scroll` + translateZ fixes iOS "tap first to activate scroll"
     * quirk inside a position:fixed body (see category-picker-list). */
    overflow-y: scroll;
    transform: translateZ(0);
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    touch-action: pan-y;
}
.child-picker-head {
    display: flex;
    align-items: center;
    gap: .6rem;
    margin-bottom: .9rem;
}
.child-picker-icon {
    width: 40px; height: 40px;
    font-size: 1.3rem;
    flex-shrink: 0;
}
.child-picker-title {
    flex: 1;
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--ink);
}
.child-picker-tiles {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: .7rem;
}
@media (min-width: 480px) { .child-picker-tiles { grid-template-columns: repeat(4, 1fr); } }
@media (min-width: 720px) {
    .child-picker-tiles { grid-template-columns: repeat(5, 1fr); }
    /* Desktop: centered modal, not full-width sliding sheet. Without
     * constraints, the body stretched to 100% since flex-column items
     * default to `align-items: stretch` (2026-04-17, user request).
     * Same sizing pattern as the category-picker desktop override. */
    .child-picker { justify-content: center; align-items: center; }
    /* Width tuned to match the page shell (.wrap max-width 1200px)
     * so the overlay reads as "the same page, just focused" rather
     * than a floating chip. `calc(100% - 2rem)` mirrors the 1rem
     * horizontal padding of .wrap on narrower viewports. */
    .child-picker-body {
        width: min(1200px, calc(100% - 2rem));
        max-width: 1200px;
        margin: auto;
        border-radius: var(--radius-l);
    }
}

/* ============================================================
 * Category picker — full-screen overlay with search + hierarchy
 * (BUG-026, 2026-04-16). Replaces the native <select dropdown>
 * on the quick-add sheet: categories are grouped under their
 * parent, a live search filters by name (including parent
 * group name), and a single tap selects + closes.
 *
 * DOM contract (all in _quick_add.php):
 *   .sheet-picker-btn[data-sheet-picker-btn]     → the trigger
 *   .category-picker[data-category-picker]       → the overlay root
 *   [data-category-picker-input]                 → search input
 *   .cp-group[data-cp-group]                     → one per parent
 *   .cp-item[data-cp-item][data-cat-id][data-cat-search]
 *                                                → one per leaf
 *   [data-category-picker-empty]                 → empty-search msg
 *
 * (2026-04-17) The former hidden <select data-sheet-picker> was
 * retired — the .cp-item buttons above are now the single source of
 * truth. submitSheet() reads state.currentCategory, which is populated
 * by openSheet / openSheetForEdit / overlay click.
 * ============================================================ */

/* Sheet-head trigger button: appears in picker mode only (JS toggles
 * hidden). Styled as a chip-sized button with icon + label + caret. */
.sheet-picker-btn {
    display: none;
    flex: 1 1 0;
    min-width: 0;
    align-items: center;
    gap: .5rem;
    padding: .45rem .65rem;
    border: 1px solid var(--line);
    border-radius: 10px;
    background: var(--bg-subtle, #f1f5f9);
    color: var(--ink);
    font-family: inherit;
    font-size: 1rem;
    font-weight: 600;
    cursor: pointer;
    min-height: 44px;
    -webkit-tap-highlight-color: transparent;
    transition: background .12s, border-color .12s;
}
.sheet-picker-btn[data-picker-visible="true"] { display: inline-flex; }
.sheet-picker-btn:hover { background: var(--bg-card); border-color: var(--primary); }
.sheet-picker-btn:focus-visible { background: var(--bg-card); border-color: var(--primary); outline: none; box-shadow: var(--ring); }
.sheet-picker-btn-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border-radius: 8px;
    background: var(--bg-card);
    font-size: 1.1rem;
    flex-shrink: 0;
}
.sheet-picker-btn-name {
    flex: 1 1 0;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    text-align: left;
}
.sheet-picker-btn-caret { opacity: .6; font-size: .85rem; }

/* Overlay shell — same sliding-bottom-sheet pattern as .child-picker
 * but full-height with a dedicated search + grouped scrollable body.
 * Higher z-index so it stacks above the .sheet it was opened from. */
/* Inherits positioning, pointer-events gate, [hidden] handling,
 * backdrop color, page scroll-lock, and opacity fade from .overlay.
 * Only override: z-index (above .sheet / .child-picker). */
.category-picker { z-index: 120; }
.category-picker-body {
    position: relative;
    margin-top: auto;
    display: flex;
    flex-direction: column;
    background: var(--bg-card);
    border-top-left-radius: var(--radius-l);
    border-top-right-radius: var(--radius-l);
    box-shadow: 0 -12px 40px rgba(15,23,42,.3);
    /* Fixed height so the body never resizes / re-centers while the
     * user types and results come and go (BUG-028). 85 dvh is also
     * the upper cap for huge lists. */
    height: 85dvh;
    flex-shrink: 0;
}

.category-picker-head {
    display: flex;
    align-items: center;
    gap: .6rem;
    padding: .8rem 1rem .5rem;
    flex-shrink: 0;
}
.category-picker-title {
    flex: 1;
    margin: 0;
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--ink);
}

/* Search pill — mirrors the .cat-search pattern used elsewhere in the
 * app (/categories top, filter pills). Rounded pill, inline icon + input
 * + clear button, focus-ring via var(--ring) so keyboard users see the
 * same highlight they do on any other input. */
.category-picker-search {
    position: relative;
    display: flex;
    align-items: center;
    gap: .4rem;
    margin: .2rem 1rem .6rem;
    padding: 0 .7rem;
    background: var(--bg-subtle, #f1f5f9);
    border: 1px solid var(--line);
    border-radius: 999px;
    flex-shrink: 0;
    transition: border-color .12s, box-shadow .12s;
}
.category-picker-search:focus-within {
    border-color: var(--primary);
    box-shadow: var(--ring);
}
.category-picker-search-icon { color: var(--muted); flex-shrink: 0; display: inline-flex; align-items: center; }
.category-picker-search-input {
    flex: 1 1 0;
    min-width: 0;
    border: 0;
    outline: none;
    background: transparent;
    color: var(--ink);
    font-family: inherit;
    font-weight: 400;
    /* 16 px is the magic number below which iOS Safari auto-zooms on
     * input focus (BUG-027). Keep it ≥ 16 px in every form control
     * the user might tap on mobile. */
    font-size: 16px;
    padding: .55rem 0;
    /* IMPORTANT: NO `appearance: none` / `-webkit-appearance: none`
     * here (BUG-028). On iOS Safari, stripping the native input chrome
     * from a text input can disable hardware key handling (Backspace
     * was a no-op). The `.cat-search` input on /categories doesn't
     * set appearance:none and works perfectly — keep it that way. */
}
.category-picker-search-input::placeholder { color: var(--muted); }
.category-picker-search-clear {
    flex-shrink: 0;
    width: 28px; height: 28px;
    border: 0;
    border-radius: 50%;
    background: rgba(100,116,139,.2);
    color: var(--ink);
    font-size: 14px;
    line-height: 1;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
}
/* Keep the 28x28 slot reserved even when the button is not needed
 * (empty input), so the search input's right edge sits at a stable
 * distance from the rounded pill border regardless of whether the ×
 * has appeared. With plain `display: none`, the flex row collapsed
 * and the input crept right up against the focus ring (2026-04-17).
 * `visibility: hidden` keeps the layout space but makes the element
 * non-interactive (excluded from tab order, ignored by screen
 * readers) — which matches the `hidden` attribute semantics. */
.category-picker-search-clear[hidden] {
    visibility: hidden;
    pointer-events: none;
}
.category-picker-search-clear:hover { background: rgba(100,116,139,.35); }

/* Scrollable list with grouped items */
.category-picker-list {
    flex: 1 1 auto;
    /* min-height: 0 is critical in a flex-column parent — without it,
     * the list won't shrink below its intrinsic content height (the full
     * group grid), so on mobile the list extends past the body and
     * overflow-y can't kick in (scroll dies silently). */
    min-height: 0;
    /* `scroll` (not `auto`) + layer promotion via `translateZ(0)` fixes
     * iOS Safari's "need a tap first to start scrolling" quirk when the
     * scroll container is inside a position:fixed ancestor (our overlay
     * body sits inside the scroll-locked, position:fixed document body). */
    overflow-y: scroll;
    transform: translateZ(0);
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    touch-action: pan-y;
    padding: 0 .5rem calc(1rem + env(safe-area-inset-bottom, 0));
}

/* Slim themed scrollbar for every scrollable region inside an overlay.
 * Default native scrollbars (Firefox on Linux in particular, and some
 * GNOME/Ubuntu themes) render a thick accented bar that visually collides
 * with rounded pills and overlay corners. Keep it narrow + subtle.
 *
 * Scope: any scroll container living inside an overlay body should be
 * added to these selector lists — keeps the look consistent so new
 * overlays don't ship with the default OS scrollbar by accident. */
html {
    scrollbar-width: thin;
    scrollbar-color: var(--line) transparent;
}
html::-webkit-scrollbar { width: 10px; height: 10px; }
html::-webkit-scrollbar-track { background: transparent; }
html::-webkit-scrollbar-thumb { background: var(--line); border-radius: 8px; }
html::-webkit-scrollbar-thumb:hover { background: var(--muted); }

.category-picker-list,
.child-picker-body,
.emoji-picker-list,
.sheet-body {
    scrollbar-width: thin;
    scrollbar-color: var(--line) transparent;
}
.category-picker-list::-webkit-scrollbar,
.child-picker-body::-webkit-scrollbar,
.emoji-picker-list::-webkit-scrollbar,
.sheet-body::-webkit-scrollbar { width: 8px; }
.category-picker-list::-webkit-scrollbar-track,
.child-picker-body::-webkit-scrollbar-track,
.emoji-picker-list::-webkit-scrollbar-track,
.sheet-body::-webkit-scrollbar-track { background: transparent; }
.category-picker-list::-webkit-scrollbar-thumb,
.child-picker-body::-webkit-scrollbar-thumb,
.emoji-picker-list::-webkit-scrollbar-thumb,
.sheet-body::-webkit-scrollbar-thumb {
    background: var(--line);
    border-radius: 8px;
}
.category-picker-list::-webkit-scrollbar-thumb:hover,
.child-picker-body::-webkit-scrollbar-thumb:hover,
.emoji-picker-list::-webkit-scrollbar-thumb:hover,
.sheet-body::-webkit-scrollbar-thumb:hover { background: var(--muted); }

.cp-group {
    margin: 0 0 .8rem;
    border: 1px solid var(--line);
    border-radius: var(--radius-m, 10px);
    background: var(--bg-card);
    padding: 0 .55rem .5rem;
    overflow: hidden;
}
.cp-group[hidden] { display: none; }
.cp-group-header {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .5rem .55rem;
    margin: 0 -.55rem .45rem;
    background: rgba(37,99,235,.10);
    border-bottom: 1px solid var(--line);
}
.cp-group-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px; height: 28px;
    border-radius: 8px;
    font-size: .95rem;
    flex-shrink: 0;
}
.cp-group-icon--loose { background: var(--bg-subtle); }
.cp-group-name {
    flex: 1 1 0;
    font-weight: 700;
    font-size: .88rem;
    color: var(--ink);
    letter-spacing: .01em;
}
.cp-group-count {
    font-size: .75rem;
    color: var(--muted);
    background: var(--bg-subtle);
    padding: 2px 8px;
    border-radius: 999px;
    min-width: 22px;
    text-align: center;
}

.cp-group-items {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
    gap: .35rem;
    padding: .45rem 0;
}
.cp-item {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .5rem .6rem;
    border: 1px solid transparent;
    border-radius: 10px;
    background: transparent;
    color: var(--ink);
    font-family: inherit;
    font-size: .95rem;
    text-align: left;
    cursor: pointer;
    min-height: 44px;
    transition: background .12s, border-color .12s, transform .06s;
    -webkit-tap-highlight-color: transparent;
}
.cp-item[hidden] { display: none; }
.cp-item:hover { background: var(--bg-subtle, rgba(100,116,139,.1)); border-color: var(--line); }
.cp-item:focus-visible { background: var(--bg-subtle, rgba(100,116,139,.1)); border-color: var(--line); outline: none; box-shadow: var(--ring); }
.cp-item:active { transform: scale(.98); }
.cp-item-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px; height: 32px;
    border-radius: 8px;
    font-size: 1rem;
    flex-shrink: 0;
}
.cp-item-name {
    flex: 1 1 0;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: 500;
}

.category-picker-empty {
    text-align: center;
    padding: 2.5rem 1rem;
    color: var(--muted);
    font-size: .95rem;
}
.category-picker-empty[hidden] { display: none; }

@media (min-width: 720px) {
    /* Desktop: wide centered panel. Without an explicit `width`, the
     * flex column item collapsed to its intrinsic content width
     * (~350px) even with `max-width` set, because max-width alone
     * only caps — doesn't grow. `width: min(1200px, 92vw)` makes the
     * panel roomy on wide monitors and still leaves a breathing
     * margin on narrower laptop screens (2026-04-17, user request). */
    .category-picker-body {
        max-height: 80vh;
        min-height: 0;
        width: min(840px, 65vw);
        max-width: 840px;
        margin: auto;
        border-radius: var(--radius-l);
    }
    .category-picker { justify-content: center; align-items: center; }
}
/* ============================================================
 * /categories page — hierarchical layout (2026-04-15)
 *
 * Sections stacked as cards:
 *   1. Search input
 *   2. Collapsible "+ Νέα κατηγορία ή ομάδα" form
 *   3. ⭐ Αγαπημένα (pinned, drag-to-reorder, always expanded)
 *   4. One card per group (collapsed by default, expands to show its
 *      own edit row + a drag-to-reorder child list + per-group "+ Νέα
 *      υποκατηγορία" inline form)
 *   5. 📦 Χωρίς ομάδα (loose leaves, always expanded)
 * ============================================================ */

.cat-search-card { padding: .6rem .8rem; }
.cat-search-card .cat-search { margin: 0; width: 100%; }

/* Top-level "+ Νέα κατηγορία ή ομάδα" toggle. Folded by default; the
 * form slides out when the button flips aria-expanded. */
.cat-new[data-cat-new] { padding: .5rem .8rem; }
.cat-new-toggle {
    display: flex;
    align-items: center;
    gap: .55rem;
    width: 100%;
    background: none;
    border: none;
    padding: .45rem .2rem;
    font: inherit;
    color: var(--ink);
    font-weight: 600;
    cursor: pointer;
    text-align: left;
    -webkit-tap-highlight-color: transparent;
}
.cat-new-toggle-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    background: var(--primary-50, var(--bg-subtle));
    color: var(--primary);
    border-radius: 8px;
    font-weight: 700;
    font-size: 1.1rem;
    flex-shrink: 0;
}
.cat-new[data-cat-new] .cat-new-form { margin-top: .5rem; }

/* Group card: header is a tappable toggle, body is everything below. */
.cat-group-card {
    padding: 0;
    overflow: hidden;
}
/* `.cat-group-head` is the row wrapping the toggle + action cluster
 * (ex-"Η ίδια η ομάδα" chip folded into the header). The toggle
 * button itself is `.cat-group-head-toggle`; actions live in a
 * sibling `.cat-chip-actions`. */
.cat-group-head {
    display: flex;
    align-items: stretch;
    gap: .3rem;
    padding: 0 .4rem 0 0;
    border-radius: var(--radius, 14px);
}
.cat-group-head-toggle {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: .7rem;
    background: none;
    border: none;
    padding: .85rem 1rem;
    font: inherit;
    color: var(--ink);
    cursor: pointer;
    text-align: left;
    -webkit-tap-highlight-color: transparent;
    border-radius: var(--radius, 14px);
    transition: background .12s;
}
.cat-group-head-toggle:hover { background: var(--bg-subtle); }
.cat-group-head .cat-chip-actions { align-self: center; }
.cat-group-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: 12px;
    font-size: 1.25rem;
    color: #fff;
    flex-shrink: 0;
    box-shadow: 0 2px 6px rgba(15,23,42,.08);
}
.cat-group-title {
    flex: 1;
    font-size: 1rem;
    font-weight: 700;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.cat-group-title small { font-weight: 500; margin-left: .3rem; }
.cat-group-caret {
    font-size: .9rem;
    color: var(--muted);
    flex-shrink: 0;
    transition: transform .15s;
}
.cat-group-card.is-open > .cat-group-head {
    border-bottom: 1px solid var(--line);
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
}

.cat-group-body {
    padding: .7rem 1rem .9rem;
    display: flex;
    flex-direction: column;
    gap: .5rem;
}
.cat-group-body[hidden] { display: none; }

/* Group's own edit form — toggled via ✎ in the header. Same stack
 * as `.cat-new-form` (flex column, full-width inputs, primary
 * submit). Hidden by default because the form has custom `display`
 * which makes the UA `[hidden]` a no-op (see BUG-025). */
.cat-group-edit-form { display: none; }
.cat-group-card.editing .cat-group-edit-form {
    display: flex;
    flex-direction: column;
    gap: .7rem;
    background: var(--bg-subtle);
    border-radius: 10px;
    padding: .6rem .7rem .7rem;
}.cat-group-children-label {
    font-size: .7rem;
    text-transform: uppercase;
    letter-spacing: .06em;
    font-weight: 600;
    color: var(--muted);
    margin-top: .3rem;
    padding-left: .15rem;
}
.cat-group-hint {
    font-size: .78rem;
    margin: .2rem 0 0;
}

/* Chip list = vertical stack of chip rows, minimal spacing. */
.cat-chip-list {
    display: flex;
    flex-direction: column;
    gap: .3rem;
}
.cat-chip-row {
    background: var(--bg-card);
    border: 1px solid var(--line);
    border-radius: 10px;
    transition: background .12s, border-color .12s;
}
.cat-chip-row[hidden] { display: none; }
.cat-chip-row.dragging {
    box-shadow: 0 10px 24px -6px rgba(15,23,42,.25);
    transform: scale(1.01);
    opacity: .96;
}
.cat-chip-head {
    display: flex;
    align-items: center;
    gap: .55rem;
    padding: .45rem .55rem;
}
/* Drag handle: compact on desktop, bumped to ≥ 40 px on touch devices so
 * the grab target isn't a 26×32 sliver (BUG-019). touch-action: none on
 * the base .drag-handle already prevents scroll-hijacking, long-press is
 * handled in drag.js. */
.cat-chip-head .drag-handle { width: 28px; height: 34px; font-size: .9rem; }
@media (pointer: coarse) {
    .cat-chip-head .drag-handle { width: 40px; height: 40px; font-size: 1rem; }
}
.cat-chip-head .cat-icon {
    width: 34px;
    height: 34px;
    font-size: 1rem;
    flex-shrink: 0;
    border-radius: 10px;
    box-shadow: none;
}
.cat-chip-body {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 1px;
}
.cat-chip-body strong {
    font-size: .95rem;
    color: var(--ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.cat-chip-body small { font-size: .72rem; }
.cat-chip-actions {
    display: flex;
    align-items: center;
    gap: .15rem;
    flex-shrink: 0;
}
.cat-chip-actions .pin-form { display: inline-flex; }
/* Edit form inside a chip: folded by default, revealed by .editing. */
.cat-chip-row .cat-edit {
    display: none;
    padding: 0 .55rem .65rem;
    margin-top: -.1rem;
}
/* Flex column (matches .cat-new-form) so the Όνομα/Εικονίδιο/Χρώμα
 * row, the optional Ομάδα dropdown, and the submit button stack with
 * consistent spacing — same look as the "+ Νέα κατηγορία" form. */
.cat-chip-row.editing .cat-edit { display: flex; flex-direction: column; gap: .7rem; }
.cat-chip-row.editing {
    background: var(--bg-subtle);
    border-color: var(--line-2);
}
.cat-chip-row .cat-edit-grid {
    display: grid;
    grid-template-columns: 60px 1fr 60px;
    gap: .5rem .55rem;
    margin-bottom: .45rem;
}
.cat-chip-row .cat-edit-grid label:nth-child(1) { grid-column: 1; }
.cat-chip-row .cat-edit-grid label:nth-child(2) { grid-column: 2 / -1; }
.cat-chip-row .cat-edit-grid label:nth-child(3) { grid-column: 1 / 3; }
.cat-chip-row .cat-edit-grid label:last-child { grid-column: 3; }
@media (max-width: 480px) {
    .cat-chip-row .cat-edit-grid {
        grid-template-columns: 60px 1fr;
    }
    .cat-chip-row .cat-edit-grid label:nth-child(3),
    .cat-chip-row .cat-edit-grid label:last-child {
        grid-column: 1 / -1;
    }
}

/* Per-group "+ Νέα υποκατηγορία στο X" add control. */
.cat-group-add {
    margin-top: .2rem;
    padding-top: .5rem;
    border-top: 1px dashed var(--line);
}
/* Child-add toggle uses the shared `.cat-new-toggle` pill, but
 * centered (top-level toggle stays left-aligned — it anchors the
 * whole page). */
.cat-new-toggle[data-cat-new-child-toggle] {
    justify-content: center;
    text-align: center;
}
/* Child-add form follows the main `.cat-new-form` recipe — vertical
 * stack with Όνομα/Εικονίδιο/Χρώμα row + submit — so the legacy
 * flex-row/wrap styling that squeezed inputs to content width is
 * gone. Keep only the top margin below the toggle. */
.cat-group-add-form { margin-top: .55rem; }
.cat-group-add-form[hidden] { display: none; }

.cat-empty { margin: .3rem 0 0; }

/* Archive section — dim chip rows so they read as "inactive" at a
 * glance. Kept selectable/clickable (the action buttons inside need
 * full opacity), so only the chip container dims. */
.cat-archived { opacity: .7; }
.cat-archived .cat-chip-actions { opacity: 1; }
.cat-archive-card .cat-group-icon { color: #fff; }

/* Savings KPI card — clickable, same height as sibling KPIs */
.kpi-savings {
    display: flex;
    flex-direction: column;
    justify-content: center;
    color: inherit;
    text-decoration: none;
    background: var(--bg-card);
}
/* Neutral slate-400 rail — readable in both light and dark themes. */
.kpi-savings::before { background: #94a3b8; }

/* ============================================================
 * Privacy mode — every money amount server- or JS-rendered goes
 * through `<span class="money">`. When `html.privacy` is set,
 * the span's text is hidden and a "•••" overlay takes its place.
 * Inline-block so the width collapses to the content box.
 * ============================================================ */
html.privacy .money {
    color: transparent !important;
    position: relative;
    display: inline-block;
    user-select: none;
    text-shadow: none !important;
}
html.privacy .money::after {
    content: "•••";
    position: absolute;
    inset: 0;
    color: var(--ink);
    letter-spacing: 2px;
    font-weight: inherit;
    font-size: inherit;
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
}
/* Privacy dots are always neutral — deliberately NOT pos/neg-tinted, so
 * the mask reads as "amount hidden" without leaking sign info. */
html.privacy .money::after { color: var(--ink); }

/* Amount containers wrap the .money span alongside a bare sign ("−"/"+")
 * and currency ("€") as sibling text nodes. In privacy mode those
 * decorators stay visible next to the dots ("− ••• €"), leaking both
 * sign and currency. `visibility: hidden` on the container hides the
 * text nodes; children that re-enable `visibility: visible` come back —
 * so the `.money` mask renders, but the sign/currency don't. */
html.privacy .tx-amount,
html.privacy .day-header-totals > .neg,
html.privacy .day-header-totals > .pos {
    visibility: hidden;
}
html.privacy .tx-amount > .money,
html.privacy .day-header-totals > .neg > .money,
html.privacy .day-header-totals > .pos > .money {
    visibility: visible;
}

/* Editable amount inputs on /budgets (income, savings, category limits)
 * — native `<input type="text">` so `.money` can't wrap them. Use the
 * text-security glyph trick to replace every character with a disc, so
 * the privacy mask stays consistent even while the field is focused.
 * Flip privacy off to edit. */
html.privacy .budget-limit-form input[type="text"] {
    -webkit-text-security: disc;
    text-security: disc;
}
/* Results head: title + export link on one row */
.results-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .6rem;
    margin-bottom: .4rem;
    /* Match the .tx-swipe-surface `.5rem` horizontal padding so both the
     * title on the left and the action buttons on the right line up
     * vertically with the row content below. */
    padding: 0 .5rem;
}
.results-head .card-title { margin: 0; }

/* Right-aligned action cluster inside a .card-title — wraps to own
 * line on narrow widths because .card-title already uses flex-wrap.
 * Used on /report to pin CSV + Print top-right of the first section. */
.card-title-actions {
    margin-left: auto;
    display: inline-flex;
    gap: .35rem;
    flex-wrap: wrap;
}
.results-links { display: flex; gap: .5rem; align-items: center; }
.trash-item { opacity: .75; }
.trash-actions { display: flex; gap: .3rem; }

.pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .6rem;
    padding: .8rem 0 .3rem;
    border-top: 1px solid var(--line);
    margin-top: .6rem;
}
/* Numbered pagination [‹][1][…][p][…][N][›]
 * Row of square pill buttons. Current page is filled primary; prev/next
 * arrows share the same .page-btn base. Ellipsis is a plain span with
 * no background so it reads as a separator. Wraps on narrow screens. */
.pagination-numbers {
    flex-wrap: wrap;
    gap: .3rem;
}
/* Inline variant — lives inside .results-links next to CSV, so it needs
 * no top border / padding and uses compact 30px buttons to fit the row. */
.pagination-inline {
    padding: 0;
    margin: 0;
    border-top: 0;
    gap: .25rem;
}
.pagination-inline .page-btn {
    min-width: 30px;
    height: 30px;
    padding: 0 .4rem;
    font-size: .85rem;
}
/* Centered variant — used when the pagination sits as the middle slot of
 * `.results-head` (title left, pagination center, action links right).
 * `margin: 0 auto` is cancelled out inside the flex row because flex
 * aligns via `justify-content`, so we pin this with `flex: 1 0 auto`
 * and center its own children. */
.pagination-center {
    flex: 1 1 auto;
    justify-content: center;
}
.page-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 36px;
    height: 36px;
    padding: 0 .55rem;
    border: 1px solid var(--line);
    border-radius: .5rem;
    background: var(--bg-card);
    color: var(--muted);
    font-size: .9rem;
    font-variant-numeric: tabular-nums;
    text-decoration: none;
    line-height: 1;
    transition: background .12s, border-color .12s, color .12s;
}
.page-btn:hover,
.page-btn:focus-visible {
    background: var(--bg-subtle);
    border-color: var(--primary);
    color: #fff;
    text-decoration: none;
}
.page-btn-current {
    background: var(--primary);
    border-color: var(--primary);
    color: #fff;
    font-weight: 600;
    cursor: default;
}
.page-btn-disabled {
    opacity: .4;
    cursor: not-allowed;
}
/* Per-page select — lives next to the CSV link in the results head. Slim
 * inline form, no label (select itself conveys meaning via options). */
.per-form { display: inline-block; margin: 0; }
.per-select select {
    height: 30px;
    padding: 0 .4rem;
    font-size: 16px; /* BUG-027 iOS auto-zoom */
    border: 1px solid var(--line);
    border-radius: .4rem;
    background: var(--bg-subtle);
    color: var(--ink);
    cursor: pointer;
}
/* ============================================================
 * 17. Dashboard + /budgets + /transactions layout tightening
 * ============================================================ */
/* Reduce vertical sparseness between kpi cards and next section */
.kpis + .card { margin-top: .2rem; }
.grid.grid-2 + .card { margin-top: .2rem; }
/* Shrink the kpi cards' min-height slightly so 0 € doesn't float */
.kpi { min-height: 78px; justify-content: center; }
.kpi-value { line-height: 1.15; }

/* ============================================================
 * 18. Mobile /transactions list (< 720px): cards, not table.
 *   The old `.table-wrap` horizontal scroll was unusable on 320 px
 *   iPhone SE — columns got cut off. New layout uses `.tx-list`
 *   cards that are already mobile-friendly on the dashboard.
 * ============================================================ */
/* Small-screen layout adjustments (width-based). */
@media (max-width: 719px) {
    .results-head { flex-wrap: wrap; }
    .day-header { font-size: .75rem; padding: .4rem .5rem; }
    /* Keep the day total in sync with .tx-amount on small screens too. */
    .day-header-totals { font-size: .88rem; gap: .5rem; }
    .tx-actions { gap: .25rem; }

    /* Author meta already hidden globally — no small-screen overrides needed */
}

/* Tx row interaction — buttons as default, swipe only on pure-touch.
 *
 * Problem history: trying to gate the buttons behind `@media (hover: …)`
/* Tx row interaction — JS-driven mode detection.
 *
 * CSS `@media (hover/pointer)` queries proved unreliable across browsers
 * (touch-laptops, automated sessions, some Chrome/Windows configurations
 * mis-report input capability). Instead we treat "has the user touched
 * the screen?" as the ground truth:
 *   • Default: assume mouse → reveal buttons on hover.
 *   • First `touchstart` → html.has-touch → swipe mode takes over.
 * One-way latch; hybrid devices pick whichever input the user actually
 * used. Implemented in app.js (search "touch-mode detection"). */

.tx-swipe-bg { display: none; }

/* Hover-reveal (default, mouse mode). Scoped via `:not(.has-touch)`
 * on <html> so JS can flip to swipe mode by adding the class. */
html:not(.has-touch) .tx-item:not(.tx-item-readonly):hover .tx-swipe-surface .tx-amount {
    margin-right: 76px;
}
html:not(.has-touch) .tx-item:not(.tx-item-readonly):hover .tx-swipe-surface .tx-actions {
    opacity: 1;
    pointer-events: auto;
}

/* Swipe mode: engaged once the user touches the screen. */
html.has-touch .tx-swipe-surface { touch-action: pan-y; }
html.has-touch .tx-swipe-bg { display: flex; }:root[data-theme="dark"] .sheet-body,
:root[data-theme="dark"] .sheet-opt,
:root[data-theme="dark"] .sheet-note,
:root[data-theme="dark"] .numpad button {
    background: var(--bg-card);
    color: var(--ink);
    border-color: var(--line);
}
/* Qty stepper container: dark surface. Buttons stay neutral via vars. */
:root[data-theme="dark"] .sheet-qty {
    background: rgba(255, 255, 255, .04);
    border-color: var(--line);
}
/* ± round buttons next to the amount: dark card surface, soft fill on tap. */
:root[data-theme="dark"] .amount-step {
    background: var(--bg-card);
    border-color: var(--line-2);
    color: var(--ink-soft);
}
:root[data-theme="dark"] .amount-step:hover { background: var(--bg-subtle); }
:root[data-theme="dark"] .amount-step:active {
    background: var(--ink-soft);
    color: var(--bg);
}
:root[data-theme="dark"] .sheet-note {
    background: var(--bg-subtle);
}
:root[data-theme="dark"] .table th {
    background: var(--bg-subtle);
    color: var(--muted);
}
:root[data-theme="dark"] .table tr:hover td {
    background: var(--bg-subtle);
}
:root[data-theme="dark"] .cat-card {
    background: var(--bg-card);
    border-color: var(--line);
}
:root[data-theme="dark"] .cat-card.pinned {
    background: var(--bg-card);
    border-color: rgba(251, 191, 36, .55);
}
:root[data-theme="dark"] .cat-card.archived {
    background: var(--bg-subtle);
}
@media (min-width: 720px) {
    :root[data-theme="dark"] .cat-card,
    :root[data-theme="dark"] .cat-card.pinned,
    :root[data-theme="dark"] .cat-card.archived {
        background: transparent;
        border-color: transparent;
        border-bottom-color: var(--line);
    }
    :root[data-theme="dark"] .cat-card:hover { background: rgba(255,255,255,.04); }
    :root[data-theme="dark"] .cat-card.pinned:hover { background: rgba(251, 191, 36, .06); }
}
:root[data-theme="dark"] .form input,
:root[data-theme="dark"] .form select,
:root[data-theme="dark"] .form textarea,
:root[data-theme="dark"] input[type="text"],
:root[data-theme="dark"] input[type="search"],
:root[data-theme="dark"] input[type="date"],
:root[data-theme="dark"] input[type="datetime-local"],
:root[data-theme="dark"] input[type="month"],
:root[data-theme="dark"] input[type="password"],
:root[data-theme="dark"] input[type="number"],
:root[data-theme="dark"] select,
:root[data-theme="dark"] textarea {
    background: var(--bg-subtle);
    color: var(--ink);
    border-color: var(--line-2);
}
:root[data-theme="dark"] .form input::placeholder,
:root[data-theme="dark"] input::placeholder,
:root[data-theme="dark"] textarea::placeholder {
    color: var(--muted);
    opacity: .7;
}
:root[data-theme="dark"] .btn-ghost {
    background: var(--bg-subtle);
    color: var(--ink);
    border-color: var(--line-2);
}
:root[data-theme="dark"] .tile {
    background: var(--bg-card);
    border-color: var(--line);
    color: var(--ink);
}
:root[data-theme="dark"] .tile-name { color: var(--ink); }
:root[data-theme="dark"] .budget-edit-row {
    background: var(--bg-card);
    border-color: var(--line);
}
:root[data-theme="dark"] .budget-edit-row.has-limit {
    background: var(--bg-card);
    border-color: var(--primary);
}
@media (min-width: 720px) {
    :root[data-theme="dark"] .budget-edit-row,
    :root[data-theme="dark"] .budget-edit-row.has-limit {
        background: transparent;
        border-color: transparent;
        border-bottom-color: var(--line);
    }
    :root[data-theme="dark"] .budget-edit-row:hover { background: rgba(255,255,255,.04); }
    :root[data-theme="dark"] .budget-edit-row.has-limit:hover { background: rgba(37, 99, 235, .08); }
}
:root[data-theme="dark"] .progress { background: var(--line-2); }
:root[data-theme="dark"] .form input[type=color],
:root[data-theme="dark"] input[type=color] {
    background: var(--bg-card);
    border-color: var(--line-2);
}
:root[data-theme="dark"] .numpad button { color: var(--ink); }
:root[data-theme="dark"] .numpad .comma { color: var(--primary); }
:root[data-theme="dark"] .numpad .del   { color: var(--bad); }
:root[data-theme="dark"] .auth-card {
    background: var(--bg-card);
    border-color: var(--line);
}
:root[data-theme="dark"] .flash-success {
    background: rgba(16, 185, 129, .12);
    border-color: rgba(16, 185, 129, .35);
    color: #d1fae5;
}
:root[data-theme="dark"] .flash-error {
    background: rgba(239, 68, 68, .12);
    border-color: rgba(239, 68, 68, .35);
    color: #fecaca;
}
:root[data-theme="dark"] .toast.success { background: #064e3b; color: #d1fae5; }
:root[data-theme="dark"] .toast.error   { background: #7f1d1d; color: #fecaca; }
:root[data-theme="dark"] .toast.warn    { background: #78350f; color: #fef3c7; }

/* Same overrides when data-theme="auto" and OS prefers dark */
@media (prefers-color-scheme: dark) {
    :root[data-theme="auto"] .kind-toggle { background: var(--bg-subtle); }
    :root[data-theme="auto"] .kind-toggle input:checked + span {
        background: var(--bg-card); color: var(--ink);
        box-shadow: 0 1px 3px rgba(0,0,0,.6);
    }
    :root[data-theme="auto"] .sheet-body,
    :root[data-theme="auto"] .sheet-opt,
    :root[data-theme="auto"] .sheet-note,
    :root[data-theme="auto"] .numpad button {
        background: var(--bg-card); color: var(--ink); border-color: var(--line);
    }
    :root[data-theme="auto"] .sheet-qty {
        background: rgba(255, 255, 255, .04); border-color: var(--line);
    }
    :root[data-theme="auto"] .amount-step {
        background: var(--bg-card); border-color: var(--line-2); color: var(--ink-soft);
    }
    :root[data-theme="auto"] .amount-step:active {
        background: var(--ink-soft); color: var(--bg);
    }
    :root[data-theme="auto"] .sheet-note { background: var(--bg-subtle); }
    :root[data-theme="auto"] .table th { background: var(--bg-subtle); color: var(--muted); }
    :root[data-theme="auto"] .table tr:hover td { background: var(--bg-subtle); }
    :root[data-theme="auto"] .cat-card { background: var(--bg-card); border-color: var(--line); }
    :root[data-theme="auto"] .cat-card.pinned {
        background: var(--bg-card); border-color: rgba(251, 191, 36, .55);
    }
    :root[data-theme="auto"] .form input,
    :root[data-theme="auto"] .form select,
    :root[data-theme="auto"] input[type="text"],
    :root[data-theme="auto"] input[type="search"],
    :root[data-theme="auto"] input[type="date"],
    :root[data-theme="auto"] input[type="datetime-local"],
    :root[data-theme="auto"] input[type="month"],
    :root[data-theme="auto"] input[type="password"],
    :root[data-theme="auto"] select,
    :root[data-theme="auto"] textarea {
        background: var(--bg-subtle); color: var(--ink); border-color: var(--line-2);
    }
    :root[data-theme="auto"] .btn-ghost {
        background: var(--bg-subtle); color: var(--ink); border-color: var(--line-2);
    }
    :root[data-theme="auto"] .tile { background: var(--bg-card); border-color: var(--line); color: var(--ink); }
    :root[data-theme="auto"] .budget-edit-row { background: var(--bg-card); border-color: var(--line); }
    :root[data-theme="auto"] .budget-edit-row.has-limit { background: var(--bg-card); border-color: var(--primary); }
    :root[data-theme="auto"] .progress { background: var(--line-2); }
    :root[data-theme="auto"] .form input[type=color],
    :root[data-theme="auto"] input[type=color] {
        background: var(--bg-card); border-color: var(--line-2);
    }
    :root[data-theme="auto"] .numpad button { color: var(--ink); }
    :root[data-theme="auto"] .numpad .comma { color: var(--primary); }
    :root[data-theme="auto"] .numpad .del   { color: var(--bad); }
    :root[data-theme="auto"] .auth-card {
        background: var(--bg-card);
        border: 1px solid var(--line);
    }
    :root[data-theme="auto"] .flash-success {
        background: rgba(16, 185, 129, .12);
        border-color: rgba(16, 185, 129, .35);
        color: #d1fae5;
    }
    :root[data-theme="auto"] .flash-error {
        background: rgba(239, 68, 68, .12);
        border-color: rgba(239, 68, 68, .35);
        color: #fecaca;
    }
    :root[data-theme="auto"] .toast.success { background: #064e3b; color: #d1fae5; }
    :root[data-theme="auto"] .toast.error   { background: #7f1d1d; color: #fecaca; }
    :root[data-theme="auto"] .toast.warn    { background: #78350f; color: #fef3c7; }
}

/* PJAX transitions */
main.wrap { transition: opacity .12s ease; }
main.wrap.pjax-out { opacity: 0; }
main.wrap.pjax-in  { opacity: 1; }

/* PJAX progress bar. Themed via --primary so dark mode gets a
 * paler blue that matches the rest of the palette, instead of the
 * old hardcoded indigo. Inline geometry is still set from pjax.js
 * (width/opacity transitions) so we only need to own the color. */
#pjax-bar {
    background: var(--primary);
}

/* ============================================================
 * Custom confirm modal (replaces window.confirm() globally).
 * Exposed via window.budgetConfirm() from toast.js.
 * Focus-trapped, Esc cancels, backdrop click cancels, styleable.
 * ============================================================ */
.confirm-backdrop {
    position: fixed;
    inset: 0;
    z-index: 200;
    background: rgba(15, 23, 42, .55);
    -webkit-backdrop-filter: blur(2px);
            backdrop-filter: blur(2px);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
    padding-top:    calc(1rem + var(--safe-top));
    padding-bottom: calc(1rem + var(--safe-bottom));
    opacity: 0;
    transition: opacity .14s ease;
}
.confirm-backdrop.open { opacity: 1; }
.confirm-modal {
    background: var(--bg-card);
    color: var(--ink);
    border: 1px solid var(--line);
    border-radius: var(--radius-l);
    box-shadow: var(--shadow-lg);
    padding: 1.25rem 1.25rem 1rem;
    width: 100%;
    max-width: 420px;
    transform: translateY(8px) scale(.98);
    transition: transform .18s cubic-bezier(.32,.72,0,1);
}
.confirm-backdrop.open .confirm-modal { transform: none; }
.confirm-msg {
    margin: 0 0 1rem;
    font-size: 1rem;
    line-height: 1.45;
    color: var(--ink);
}
.confirm-actions {
    display: flex;
    justify-content: flex-end;
    gap: .6rem;
    flex-wrap: wrap;
}
.confirm-actions .btn { min-width: 100px; }

/* Danger button used for destructive confirmations (delete, purge). */
.btn-danger {
    background: var(--bad);
    border-color: var(--bad);
    color: #fff;
    font-weight: 600;
    box-shadow: var(--shadow-sm), inset 0 1px 0 rgba(255,255,255,.18);
}
.btn-danger:hover {
    background: #b91c1c;
    border-color: #b91c1c;
}
:root[data-theme="dark"] .btn-danger:hover {
    background: #ef4444;
    border-color: #ef4444;
}

/* Motion-reduce: respect OS setting.
 * The universal .01ms override kills one-shot animations cleanly,
 * BUT for `animation-iteration-count: infinite` animations
 * (like .tile-wrap tileWiggle in edit mode) it causes a rapid
 * tremor at ~100k cycles/second. Restore the intended duration
 * on those specific animations so they play as designed — the
 * wiggle is an intentional affordance (iOS-style "edit mode"
 * cue), not decoration, and the user wants it preserved on
 * every device regardless of reduced-motion. */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .01ms !important;
        transition-duration: .01ms !important;
    }
    .tiles-section.edit-mode .tile-wrap {
        animation-duration: .25s !important;
    }
}
