/*
 * Multi-theme stylesheet. Every component below is styled in terms of --kb-*
 * CSS variables; each theme just supplies a different set of those variables,
 * selected by the data-theme attribute header.jsp puts on <html> (see
 * ThemeServlet / model/Theme.java). Bootstrap's own components are kept in
 * step by also overriding the relevant --bs-* variables per theme.
 *
 * To add a theme: add a :root[data-theme="id"] block here, plus an enum
 * constant in model/Theme.java. No per-component edits needed.
 */

/* ============================ THEME PALETTES ============================ */

/* --- ダーク・ラグジュアリー: 漆黒 × ゴールド (dark) --- */
[data-theme="dark-luxury"] {
    --kb-body-bg: #0e1116;
    --kb-body-image:
        radial-gradient(1100px 500px at 85% -10%, rgba(212, 176, 106, 0.07), transparent 60%),
        radial-gradient(900px 500px at -10% 100%, rgba(78, 104, 148, 0.08), transparent 55%);
    --kb-surface: #161b23;
    --kb-surface-2: #1d232d;
    --kb-border: rgba(212, 176, 106, 0.16);
    --kb-border-strong: rgba(212, 176, 106, 0.25);
    --kb-accent: #d4b06a;
    --kb-accent-2: #e0c184;
    --kb-accent-soft: rgba(212, 176, 106, 0.12);
    --kb-btn-bg: linear-gradient(135deg, #e0c184, #c49b52);
    --kb-btn-bg-hover: linear-gradient(135deg, #ecd09a, #d4ac62);
    --kb-btn-shadow: 0 2px 10px rgba(212, 176, 106, 0.25);
    --kb-on-accent: #14100a;
    --kb-ink: #e8e6e1;
    --kb-muted: #9a958a;
    --kb-income: #5ec9a0;
    --kb-expense: #e0806e;
    --kb-warn: #d4b06a;
    --kb-input-bg: #10151c;
    --kb-nav-bg: rgba(14, 17, 22, 0.85);
    --kb-track: rgba(255, 255, 255, 0.07);
    --kb-heading-font: "Shippori Mincho", "Hiragino Mincho ProN", serif;
    --kb-body-font: "Zen Kaku Gothic New", "Hiragino Kaku Gothic ProN", sans-serif;
    --kb-radius: 14px;
    --kb-radius-sm: 10px;
    --kb-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);

    --bs-body-bg: #0e1116;
    --bs-body-color: #e8e6e1;
    --bs-secondary-color: #9a958a;
    --bs-tertiary-color: #6e6a61;
    --bs-border-color: rgba(212, 176, 106, 0.16);
    --bs-border-color-translucent: rgba(212, 176, 106, 0.16);
    --bs-link-color: #d4b06a;
    --bs-link-hover-color: #e0c184;
    --bs-link-color-rgb: 212, 176, 106;
    --bs-link-hover-color-rgb: 224, 193, 132;
    --bs-emphasis-color: #f5f3ee;
    --bs-secondary-bg: #1d232d;
    --bs-tertiary-bg: #161b23;
}

/* --- モダン・ミニマル: 白基調 × 深いグリーン (light) --- */
[data-theme="modern-minimal"] {
    --kb-body-bg: #f6f8f7;
    --kb-body-image: none;
    --kb-surface: #ffffff;
    --kb-surface-2: #eef2f0;
    --kb-border: #e2e8e5;
    --kb-border-strong: #cfdad5;
    --kb-accent: #2f6f57;
    --kb-accent-2: #3a8368;
    --kb-accent-soft: rgba(47, 111, 87, 0.10);
    --kb-btn-bg: #2f6f57;
    --kb-btn-bg-hover: #3a8368;
    --kb-btn-shadow: 0 1px 2px rgba(31, 54, 44, 0.12);
    --kb-on-accent: #ffffff;
    --kb-ink: #1d2b25;
    --kb-muted: #69756f;
    --kb-income: #2f6f57;
    --kb-expense: #c1524a;
    --kb-warn: #c98a2e;
    --kb-input-bg: #ffffff;
    --kb-nav-bg: rgba(255, 255, 255, 0.85);
    --kb-track: #e4eae7;
    --kb-heading-font: "Zen Kaku Gothic New", "Hiragino Kaku Gothic ProN", sans-serif;
    --kb-body-font: "Zen Kaku Gothic New", "Hiragino Kaku Gothic ProN", sans-serif;
    --kb-radius: 12px;
    --kb-radius-sm: 9px;
    --kb-shadow: 0 1px 3px rgba(20, 40, 33, 0.08), 0 6px 20px rgba(20, 40, 33, 0.05);

    --bs-body-bg: #f6f8f7;
    --bs-body-color: #1d2b25;
    --bs-secondary-color: #69756f;
    --bs-tertiary-color: #98a29d;
    --bs-border-color: #e2e8e5;
    --bs-border-color-translucent: #e2e8e5;
    --bs-link-color: #2f6f57;
    --bs-link-hover-color: #3a8368;
    --bs-link-color-rgb: 47, 111, 87;
    --bs-link-hover-color-rgb: 58, 131, 104;
    --bs-emphasis-color: #0f1a15;
    --bs-secondary-bg: #eef2f0;
    --bs-tertiary-bg: #ffffff;
}

/* --- ウォーム・フレンドリー: クリーム × テラコッタ (light) --- */
[data-theme="warm-friendly"] {
    --kb-body-bg: #fbf5ec;
    --kb-body-image: radial-gradient(900px 500px at 100% -10%, rgba(217, 122, 84, 0.08), transparent 60%);
    --kb-surface: #fffdf8;
    --kb-surface-2: #f5ebdd;
    --kb-border: #ecdcc7;
    --kb-border-strong: #e0c9ab;
    --kb-accent: #d0704a;
    --kb-accent-2: #e58e69;
    --kb-accent-soft: rgba(217, 122, 84, 0.12);
    --kb-btn-bg: linear-gradient(135deg, #e58e69, #d0704a);
    --kb-btn-bg-hover: linear-gradient(135deg, #eb9b78, #d97a54);
    --kb-btn-shadow: 0 3px 10px rgba(217, 122, 84, 0.22);
    --kb-on-accent: #ffffff;
    --kb-ink: #4a3c30;
    --kb-muted: #9a897a;
    --kb-income: #6f9a5f;
    --kb-expense: #cf6b5b;
    --kb-warn: #e0a53f;
    --kb-input-bg: #fffdf8;
    --kb-nav-bg: rgba(251, 245, 236, 0.88);
    --kb-track: #ece0cf;
    --kb-heading-font: "Zen Maru Gothic", "Hiragino Maru Gothic ProN", sans-serif;
    --kb-body-font: "Zen Kaku Gothic New", "Hiragino Kaku Gothic ProN", sans-serif;
    --kb-radius: 18px;
    --kb-radius-sm: 12px;
    --kb-shadow: 0 2px 4px rgba(74, 60, 48, 0.06), 0 10px 26px rgba(74, 60, 48, 0.06);

    --bs-body-bg: #fbf5ec;
    --bs-body-color: #4a3c30;
    --bs-secondary-color: #9a897a;
    --bs-tertiary-color: #b6a897;
    --bs-border-color: #ecdcc7;
    --bs-border-color-translucent: #ecdcc7;
    --bs-link-color: #d0704a;
    --bs-link-hover-color: #e58e69;
    --bs-link-color-rgb: 208, 112, 74;
    --bs-link-hover-color-rgb: 229, 142, 105;
    --bs-emphasis-color: #3a2e24;
    --bs-secondary-bg: #f5ebdd;
    --bs-tertiary-bg: #fffdf8;
}

/* --- フレッシュ・ポップ: 白基調 × 青紫グラデ (light) --- */
[data-theme="fresh-pop"] {
    --kb-body-bg: #f5f6fd;
    --kb-body-image:
        radial-gradient(800px 450px at 95% -12%, rgba(109, 94, 240, 0.10), transparent 60%),
        radial-gradient(700px 400px at -8% 108%, rgba(240, 86, 122, 0.08), transparent 55%);
    --kb-surface: #ffffff;
    --kb-surface-2: #eef0fb;
    --kb-border: #e6e8f6;
    --kb-border-strong: #d3d7f0;
    --kb-accent: #6d5ef0;
    --kb-accent-2: #9b6df0;
    --kb-accent-soft: rgba(109, 94, 240, 0.10);
    --kb-btn-bg: linear-gradient(135deg, #6d5ef0, #a56cf0);
    --kb-btn-bg-hover: linear-gradient(135deg, #7d70f2, #b078f2);
    --kb-btn-shadow: 0 4px 14px rgba(109, 94, 240, 0.28);
    --kb-on-accent: #ffffff;
    --kb-ink: #20233a;
    --kb-muted: #6f7391;
    --kb-income: #12a982;
    --kb-expense: #ef4f74;
    --kb-warn: #e79b2b;
    --kb-input-bg: #ffffff;
    --kb-nav-bg: rgba(245, 246, 253, 0.88);
    --kb-track: #e7e9f7;
    --kb-heading-font: "Zen Kaku Gothic New", "Hiragino Kaku Gothic ProN", sans-serif;
    --kb-body-font: "Zen Kaku Gothic New", "Hiragino Kaku Gothic ProN", sans-serif;
    --kb-radius: 16px;
    --kb-radius-sm: 11px;
    --kb-shadow: 0 2px 6px rgba(32, 35, 58, 0.06), 0 12px 30px rgba(109, 94, 240, 0.10);

    --bs-body-bg: #f5f6fd;
    --bs-body-color: #20233a;
    --bs-secondary-color: #6f7391;
    --bs-tertiary-color: #9498b4;
    --bs-border-color: #e6e8f6;
    --bs-border-color-translucent: #e6e8f6;
    --bs-link-color: #6d5ef0;
    --bs-link-hover-color: #9b6df0;
    --bs-link-color-rgb: 109, 94, 240;
    --bs-link-hover-color-rgb: 155, 109, 240;
    --bs-emphasis-color: #12142a;
    --bs-secondary-bg: #eef0fb;
    --bs-tertiary-bg: #ffffff;
}

/* ============================ COMPONENTS ============================ */

body {
    font-family: var(--kb-body-font);
    color: var(--kb-ink);
    background-color: var(--kb-body-bg);
    background-image: var(--kb-body-image);
    background-attachment: fixed;
    background-repeat: no-repeat;
    letter-spacing: 0.01em;
}

h1, h2, h3, h4, h5, h6, .navbar-brand, .offcanvas-title {
    font-family: var(--kb-heading-font);
    letter-spacing: 0.02em;
}

/* ---------- navbar ---------- */

.app-nav {
    background: var(--kb-nav-bg);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    border-bottom: 1px solid var(--kb-border);
}

.app-nav .navbar-brand {
    color: var(--kb-accent);
    font-size: 1.35rem;
    font-weight: 600;
}

.app-nav .navbar-brand:hover {
    color: var(--kb-accent-2);
}

/* ---------- cards ---------- */

.card {
    background-color: var(--kb-surface);
    border: 1px solid var(--kb-border);
    border-radius: var(--kb-radius);
    box-shadow: var(--kb-shadow);
}

.card h5 {
    color: var(--kb-accent);
    font-size: 1.05rem;
    margin-bottom: 0.9rem;
}

.amount-income {
    color: var(--kb-income);
}

.amount-expense {
    color: var(--kb-expense);
}

.fs-4 {
    font-variant-numeric: tabular-nums;
    font-weight: 500;
}

/* ---------- buttons ---------- */

.btn {
    border-radius: var(--kb-radius-sm);
    letter-spacing: 0.02em;
}

.btn-primary {
    background: var(--kb-btn-bg);
    border: 1px solid transparent;
    color: var(--kb-on-accent);
    font-weight: 600;
    box-shadow: var(--kb-btn-shadow);
}

.btn-primary:hover,
.btn-primary:focus-visible,
.btn-primary:active,
.btn-primary:first-child:active,
.btn-check:checked + .btn-primary {
    background: var(--kb-btn-bg-hover);
    border-color: transparent;
    color: var(--kb-on-accent);
}

.btn-outline-primary {
    color: var(--kb-accent);
    border-color: var(--kb-accent);
}

.btn-outline-primary:hover,
.btn-outline-primary:focus-visible,
.btn-outline-primary:active,
.btn-outline-primary:first-child:active {
    background: var(--kb-accent-soft);
    border-color: var(--kb-accent);
    color: var(--kb-accent-2);
}

.btn-outline-secondary {
    color: var(--kb-muted);
    border-color: var(--kb-border-strong);
}

.btn-outline-secondary:hover,
.btn-outline-secondary:focus-visible,
.btn-outline-secondary:active,
.btn-outline-secondary:first-child:active {
    background: var(--kb-surface-2);
    border-color: var(--kb-muted);
    color: var(--kb-ink);
}

.btn-outline-danger {
    color: var(--kb-expense);
    border-color: var(--kb-expense);
}

.btn-outline-danger:hover,
.btn-outline-danger:focus-visible,
.btn-outline-danger:active,
.btn-outline-danger:first-child:active {
    background: var(--kb-expense);
    border-color: var(--kb-expense);
    color: #fff;
}

.btn-outline-success {
    color: var(--kb-income);
    border-color: var(--kb-income);
}

.btn-outline-success:hover,
.btn-outline-success:focus-visible,
.btn-outline-success:active,
.btn-outline-success:first-child:active {
    background: var(--kb-income);
    border-color: var(--kb-income);
    color: #fff;
}

/* ---------- badges ---------- */

.badge {
    font-weight: 500;
    letter-spacing: 0.03em;
    border-radius: 6px;
    padding: 0.35em 0.6em;
}

.badge.bg-success {
    background-color: color-mix(in srgb, var(--kb-income) 18%, transparent) !important;
    color: var(--kb-income);
    border: 1px solid color-mix(in srgb, var(--kb-income) 45%, transparent);
}

.badge.bg-danger {
    background-color: color-mix(in srgb, var(--kb-expense) 18%, transparent) !important;
    color: var(--kb-expense);
    border: 1px solid color-mix(in srgb, var(--kb-expense) 45%, transparent);
}

.badge.bg-info {
    background-color: var(--kb-accent-soft) !important;
    color: var(--kb-accent) !important;
    border: 1px solid color-mix(in srgb, var(--kb-accent) 45%, transparent);
}

.badge.bg-primary {
    background-color: var(--kb-accent-soft) !important;
    color: var(--kb-accent) !important;
    border: 1px solid color-mix(in srgb, var(--kb-accent) 45%, transparent);
}

.badge.bg-secondary {
    background-color: color-mix(in srgb, var(--kb-muted) 16%, transparent) !important;
    color: var(--kb-muted);
    border: 1px solid color-mix(in srgb, var(--kb-muted) 35%, transparent);
}

/* Category badges (transactions.jsp / shared.jsp): the user-picked category
   color is set as --cat-color inline and blended toward the current theme's
   ink color here, rather than rendered raw - a raw color can land almost on
   top of a theme's own background/ink (e.g. a color chosen while on a dark
   theme can go nearly invisible after switching to a light one). Blending
   keeps the category's color identity recognizable while staying legible on
   any theme, present or future, with no per-theme special-casing needed. */
.category-badge {
    --cat-color: var(--kb-muted);
    background-color: transparent;
    font-weight: normal;
    color: color-mix(in srgb, var(--cat-color) 65%, var(--kb-ink) 35%);
    border: 1px solid color-mix(in srgb, var(--cat-color) 65%, var(--kb-ink) 35%);
}

/* ---------- tables ---------- */

.table {
    --bs-table-bg: transparent;
    --bs-table-color: var(--kb-ink);
    --bs-table-hover-bg: var(--kb-accent-soft);
    --bs-table-hover-color: var(--kb-ink);
    font-variant-numeric: tabular-nums;
}

.table thead th {
    color: var(--kb-muted);
    font-weight: 500;
    font-size: 0.85rem;
    letter-spacing: 0.06em;
    border-bottom: 1px solid var(--kb-border-strong);
}

.table td {
    border-color: var(--kb-border);
}

/* .ms-label spans exist purely to re-label a cell once the mobile-stacked
   <thead> is hidden below - on desktop the real <th> already labels the
   column, so the span must stay hidden there too, or its text leaks in as an
   unstyled inline prefix ("日付7/19") ahead of the cell's real content.
   The 767.98px media query below sets display:block to reveal it again. */
.ms-label,
.ms-inline-label,
.ms-bar-only {
    display: none;
}

/*
 * Mobile-stacked tables: below 768px, turns each <tr> into a 2-line "card" via
 * CSS Grid instead of a horizontally-scrolling row. Each variant
 * (.ms-grid-tx / .ms-grid-shared / .ms-grid-paypay, added alongside
 * .table-mobile-stack on the <table>) defines its own column template;
 * semantic classes on each <td> (.ms-date, .ms-amount, etc.) place it within
 * that grid via grid-column/grid-row, so row2 items stay aligned under their
 * related row1 column (e.g. action buttons under the amount) regardless of
 * how much content is in any other cell - unlike flex-wrap, which packs
 * items in a way that shifts position row to row as badge counts differ.
 */
@media (max-width: 767.98px) {
    .table-mobile-stack thead {
        display: none;
    }

    .table-mobile-stack tbody tr {
        display: grid;
        align-items: center;
        column-gap: 0.6rem;
        row-gap: 0.3rem;
        padding: 0.65rem 0;
        border-bottom: 1px solid var(--kb-border);
    }

    .table-mobile-stack tbody tr:last-child {
        border-bottom: none;
    }

    .table-mobile-stack tbody td {
        display: block;
        border: none;
        padding: 0;
        min-width: 0;
    }

    .table-mobile-stack .ms-label {
        display: block;
        font-size: 0.65rem;
        color: var(--kb-muted);
        letter-spacing: 0.04em;
        margin-bottom: 0.1rem;
    }

    /* Inline variant of .ms-label for cells whose row2 content isn't
       self-explanatory without headers - e.g. a bare checkbox needs an
       adjacent "共有" to mean anything once the <thead> is hidden. */
    .table-mobile-stack .ms-inline-label {
        display: inline;
        font-size: 0.75rem;
        color: var(--kb-muted);
        margin-right: 0.3rem;
    }

    .table-mobile-stack .ms-row2 {
        padding-top: 0.3rem;
        margin-top: 0.15rem;
        border-top: 1px dashed color-mix(in srgb, var(--kb-border) 70%, transparent);
    }

    .table-mobile-stack .category-badge {
        display: inline-block;
        max-width: 100%;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        vertical-align: bottom;
    }

    .table-mobile-stack .ms-type {
        display: flex;
        flex-wrap: wrap;
        gap: 0.25rem;
        align-items: center;
    }

    /* Without this, "種別" is a flex sibling of the badges and competes with
       them for the row's width, forcing badges to wrap one-per-line even
       when 2 would fit side by side - flex-basis:100% makes the label take
       its own full-width line so badges wrap only among themselves below it. */
    .table-mobile-stack .ms-type .ms-label {
        flex-basis: 100%;
        margin-bottom: 0;
    }

    /* Fixed-width ellipsis instead of wrapping/overflowing - full text is
       still in the DOM (just visually trimmed), so nothing is actually lost.
       Plain text-overflow:ellipsis rather than a mask-image fade: a
       percentage-based fade dims the last ~20% of the box regardless of
       whether the text actually overflows, which looks wrong on short memos
       that fit completely - ellipsis only ever shows/kicks in when content
       is truly clipped. */
    .table-mobile-stack .memo-fade {
        display: inline-block;
        max-width: 17em;
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
        color: var(--kb-muted);
        font-size: 0.85rem;
        vertical-align: bottom;
    }

    .table-mobile-stack .ms-actions {
        display: flex;
        justify-content: flex-end;
        gap: 0.4rem;
    }

    /* Bootstrap's .form-select is width:100% by default - cap it so the
       PayPay import preview's category picker doesn't stretch unpredictably. */
    .table-mobile-stack td select {
        width: auto;
        max-width: 42vw;
    }

    /* transactions.jsp: date | type | category | amount // memo | actions.
       .ms-type is a FIXED width (not auto) - each <tr> is its own separate
       grid container, so an auto column sizes only off that row's own badge
       count; without a fixed width, category/amount would shift left/right
       row to row depending on how many badges happen to be on it. Wide
       enough for all 3 possible badges (支出/収入 + 共有 + PayPay) to sit on
       one line, so the category column after it starts at a consistent
       position regardless of how many badges a given row has.
       minmax(0, ...) on category instead of a bare 1fr: a plain 1fr track
       refuses to shrink below its content's min-content width (e.g. a long
       category name), so the whole row silently overflows the viewport no
       matter how narrow the track "should" be - the 0 minimum is what
       actually lets text-overflow:ellipsis kick in instead of pushing later
       columns off the edge. */
    .ms-grid-tx tbody tr {
        grid-template-columns: 2.6rem minmax(0, 10rem) minmax(0, 1fr) auto;
        column-gap: 0.4rem;
    }
    .ms-grid-tx .ms-date     { grid-column: 1; grid-row: 1; }
    .ms-grid-tx .ms-type     { grid-column: 2; grid-row: 1; }
    .ms-grid-tx .ms-category { grid-column: 3; grid-row: 1; }
    .ms-grid-tx .ms-amount   { grid-column: 4; grid-row: 1; text-align: right; }
    .ms-grid-tx .ms-memo     { grid-column: 1 / 4; grid-row: 2; }
    .ms-grid-tx .ms-actions  { grid-column: 4; grid-row: 2; }

    /* shared.jsp: date | member | type | category | amount // memo - same
       fixed-width reasoning as .ms-grid-tx above for .ms-type/.ms-member. */
    .ms-grid-shared tbody tr {
        grid-template-columns: 2.6rem minmax(0, 3.8rem) minmax(0, 6.8rem) minmax(0, 1fr) auto;
        column-gap: 0.4rem;
    }
    .ms-grid-shared .ms-date     { grid-column: 1; grid-row: 1; }
    .ms-grid-shared .ms-member   { grid-column: 2; grid-row: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .ms-grid-shared .ms-type     { grid-column: 3; grid-row: 1; }
    .ms-grid-shared .ms-category { grid-column: 4; grid-row: 1; }
    .ms-grid-shared .ms-amount   { grid-column: 5; grid-row: 1; text-align: right; }
    .ms-grid-shared .ms-memo     { grid-column: 1 / -1; grid-row: 2; }
    .ms-grid-shared .memo-fade   { max-width: 20em; }

    /* paypay-import-preview.jsp: check | date | counterparty | amount // category-select | shared-check */
    .ms-grid-paypay tbody tr {
        grid-template-columns: 1.4rem 2.6rem minmax(0, 1fr) auto;
    }
    .ms-grid-paypay .ms-check          { grid-column: 1; grid-row: 1; }
    .ms-grid-paypay .ms-date           { grid-column: 2; grid-row: 1; }
    .ms-grid-paypay .ms-counterparty   { grid-column: 3; grid-row: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .ms-grid-paypay .ms-amount         { grid-column: 4; grid-row: 1; text-align: right; }
    .ms-grid-paypay .ms-category-select { grid-column: 2 / 4; grid-row: 2; }
    .ms-grid-paypay .ms-shared-check   { grid-column: 4; grid-row: 2; justify-self: end; }

    /* admin.jsp: unlike the grids above (tight single-line rows built for
       high-traffic screens), this one is a plain single-column stack - each
       <td> is a full-width block in source order, letting text-heavy cells
       (email, status + counts) and variable-width ones (action button
       forms) wrap freely without fighting for a fixed slot. Admin's user
       list is low-traffic, so simplicity/robustness wins over a tight
       layout here. */
    .ms-grid-admin tbody tr {
        grid-template-columns: 1fr;
        row-gap: 0.45rem;
    }
    .ms-grid-admin .ms-username {
        font-size: 1rem;
    }
    .ms-grid-admin .ms-actions {
        display: flex;
        flex-wrap: wrap;
        gap: 0.4rem;
    }

    /* budgets.jsp: category+badge // progress bar (spent vs. limit) // prev/actions.
       .ms-value/.ms-limit/.ms-spent are the desktop-only numeric columns -
       their info is redundant with the mobile-only bar row below (.ms-bar-only),
       so they're hidden rather than repositioned. */
    .ms-grid-budgets tbody tr {
        grid-template-columns: minmax(0, 1fr) auto;
        row-gap: 0.35rem;
    }
    .ms-grid-budgets .ms-category {
        grid-column: 1;
        grid-row: 1;
        font-size: 0.95rem;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    .ms-grid-budgets .ms-alloc-type { grid-column: 2; grid-row: 1; text-align: right; }
    .ms-grid-budgets .ms-value,
    .ms-grid-budgets .ms-limit,
    .ms-grid-budgets .ms-spent      { display: none; }
    .ms-grid-budgets .ms-bar-only   { display: block; grid-column: 1 / -1; grid-row: 2; }
    .ms-grid-budgets .ms-prev       { grid-column: 1; grid-row: 3; text-align: left; }
    .ms-grid-budgets .ms-actions    { grid-column: 2; grid-row: 3; }

    .ms-bar-row {
        display: flex;
        justify-content: space-between;
        align-items: baseline;
        font-size: 0.8rem;
        font-variant-numeric: tabular-nums;
        margin-bottom: 0.3rem;
    }
    .ms-bar-row .ms-bar-limit {
        color: var(--kb-muted);
        margin-left: 0.25rem;
    }
    .ms-bar-track {
        height: 6px;
        border-radius: 4px;
        background: var(--kb-track);
        overflow: hidden;
    }
    .ms-bar-fill {
        height: 100%;
        border-radius: 4px;
        background: var(--kb-accent);
    }
    .ms-bar-fill.over {
        background: var(--kb-expense);
    }
}

/* ---------- calendar (transactions.jsp) ---------- */

.cal-header {
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 0.75rem;
}

.cal-header .month-label {
    font-family: var(--kb-heading-font);
    font-size: 1.05rem;
    color: var(--kb-ink);
}

.cal-weekdays {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    font-size: 0.68rem;
    color: var(--kb-muted);
    text-align: center;
    margin-bottom: 0.3rem;
}

.cal-weekdays span:first-child { color: var(--kb-expense); }
.cal-weekdays span:last-child { color: var(--kb-income); }

.cal-grid {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 3px;
}

.cal-cell {
    aspect-ratio: 1 / 0.92;
    border-radius: 8px;
    border: 1px solid transparent;
    background: var(--kb-surface-2);
    padding: 0.2rem 0.1rem;
    display: flex;
    flex-direction: column;
    align-items: center;
    cursor: pointer;
    min-width: 0;
}

.cal-cell:hover {
    background: color-mix(in srgb, var(--kb-surface-2) 70%, var(--kb-accent-soft));
}

.cal-cell.empty {
    background: transparent;
    cursor: default;
    visibility: hidden;
}

.cal-cell .day-num {
    font-size: 0.7rem;
    color: var(--kb-muted);
    font-variant-numeric: tabular-nums;
}

.cal-cell.today .day-num {
    color: var(--kb-on-accent);
    background: var(--kb-accent);
    border-radius: 999px;
    width: 1.1rem;
    height: 1.1rem;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
}

.cal-cell.selected {
    border-color: var(--kb-accent);
    background: var(--kb-accent-soft);
}

.cal-cell .amounts {
    margin-top: 0.1rem;
    font-size: 0.56rem;
    line-height: 1.3;
    font-variant-numeric: tabular-nums;
    text-align: center;
}

.cal-cell .amt-income { color: var(--kb-income); display: block; }
.cal-cell .amt-expense { color: var(--kb-expense); display: block; }

.day-list-header {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    margin-bottom: 0.6rem;
}

.day-list-header .date-label {
    font-family: var(--kb-heading-font);
    font-size: 1.05rem;
}

.day-list-header .date-totals {
    font-size: 0.85rem;
    font-variant-numeric: tabular-nums;
}

.day-list-header .date-totals .amt-income { color: var(--kb-income); }
.day-list-header .date-totals .amt-expense { color: var(--kb-expense); margin-left: 0.6rem; }

.tx-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.65rem 0;
    border-bottom: 1px solid var(--kb-border);
    gap: 0.75rem;
}

.tx-row:last-child { border-bottom: none; }

.tx-left { display: flex; flex-direction: column; gap: 0.2rem; min-width: 0; }
.tx-memo {
    font-size: 0.78rem;
    color: var(--kb-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 14rem;
}

.tx-right { display: flex; flex-direction: column; align-items: flex-end; gap: 0.35rem; flex: none; }
.tx-amount { font-size: 0.95rem; font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
.tx-amount.income { color: var(--kb-income); }
.tx-amount.expense { color: var(--kb-expense); }
.tx-actions { display: flex; gap: 0.4rem; }

.empty-state { text-align: center; color: var(--kb-muted); font-size: 0.85rem; padding: 1.5rem 0; }

@media (max-width: 575.98px) {
    .tx-memo { max-width: 9rem; }
}

/* ---------- shared calendar (shared.jsp) - extends the calendar rules above with per-member coloring ---------- */

.cal-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    justify-content: center;
    font-size: 0.78rem;
    color: var(--kb-muted);
    margin-bottom: 0.6rem;
}

.legend-who, .subtotal-who, .tx-who {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
}

.legend-who .dot, .subtotal-who .dot {
    width: 0.6rem;
    height: 0.6rem;
    border-radius: 50%;
    flex: none;
}

/* One line per member with a net amount that day (colored dot + compact
   figure) - kept to at most 2 lines; a 3rd+ member the same day switches to
   .member-dots instead, since amounts stop being legible at cell size past
   that (see renderCalendar in shared.jsp). */
.cal-cell .member-line {
    margin-top: 0.1rem;
    font-size: 0.54rem;
    line-height: 1.3;
    font-variant-numeric: tabular-nums;
    display: flex;
    align-items: center;
    gap: 0.18rem;
    width: 100%;
    justify-content: center;
}

.cal-cell .member-line .dot {
    width: 0.32rem;
    height: 0.32rem;
    border-radius: 50%;
    flex: none;
}

.cal-cell .member-dots {
    margin-top: 0.15rem;
    display: flex;
    gap: 0.15rem;
    justify-content: center;
}

.cal-cell .member-dots .dot {
    width: 0.4rem;
    height: 0.4rem;
    border-radius: 50%;
}

.day-subtotals {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    font-size: 0.8rem;
    margin-bottom: 0.7rem;
    padding-bottom: 0.6rem;
    border-bottom: 1px dashed var(--kb-border);
}

.tx-who {
    font-size: 0.72rem;
    padding: 0.1rem 0.55rem;
    border-radius: 999px;
    width: fit-content;
}

/* ---------- forms ---------- */

.form-control, .form-select {
    background-color: var(--kb-input-bg);
    border: 1px solid var(--kb-border-strong);
    border-radius: var(--kb-radius-sm);
    color: var(--kb-ink);
}

.form-control:focus, .form-select:focus {
    background-color: var(--kb-input-bg);
    border-color: var(--kb-accent);
    box-shadow: 0 0 0 0.2rem var(--kb-accent-soft);
    color: var(--kb-ink);
}

.form-control::placeholder {
    color: var(--kb-muted);
    opacity: 0.7;
}

.form-check-input:checked {
    background-color: var(--kb-accent);
    border-color: var(--kb-accent);
}

.form-check-input:focus {
    border-color: var(--kb-accent);
    box-shadow: 0 0 0 0.2rem var(--kb-accent-soft);
}

.form-label {
    color: var(--kb-muted);
    font-size: 0.9rem;
}

.form-text {
    color: var(--kb-muted);
}

/* ---------- progress bars ---------- */

.progress {
    background-color: var(--kb-track);
    border-radius: 99px;
}

.progress-bar {
    border-radius: 99px;
}

.progress-bar.bg-success {
    background: linear-gradient(90deg, color-mix(in srgb, var(--kb-income) 80%, #000 8%), var(--kb-income)) !important;
}

.progress-bar.bg-warning {
    background: linear-gradient(90deg, color-mix(in srgb, var(--kb-warn) 80%, #000 8%), var(--kb-warn)) !important;
}

.progress-bar.bg-danger {
    background: linear-gradient(90deg, color-mix(in srgb, var(--kb-expense) 80%, #000 8%), var(--kb-expense)) !important;
}

/* ---------- alerts ---------- */

.alert {
    border-radius: var(--kb-radius-sm);
}

.alert-danger {
    background-color: color-mix(in srgb, var(--kb-expense) 12%, transparent);
    border-color: color-mix(in srgb, var(--kb-expense) 40%, transparent);
    color: color-mix(in srgb, var(--kb-expense) 78%, var(--kb-ink));
}

.alert-success {
    background-color: color-mix(in srgb, var(--kb-income) 12%, transparent);
    border-color: color-mix(in srgb, var(--kb-income) 40%, transparent);
    color: color-mix(in srgb, var(--kb-income) 78%, var(--kb-ink));
}

.alert-warning {
    background-color: color-mix(in srgb, var(--kb-warn) 12%, transparent);
    border-color: color-mix(in srgb, var(--kb-warn) 40%, transparent);
    color: color-mix(in srgb, var(--kb-warn) 78%, var(--kb-ink));
}

.alert-info {
    background-color: var(--kb-accent-soft);
    border-color: color-mix(in srgb, var(--kb-accent) 40%, transparent);
    color: color-mix(in srgb, var(--kb-accent) 82%, var(--kb-ink));
}

/* ---------- list groups / dropdowns / offcanvas ---------- */

.list-group-item {
    background-color: transparent;
    border-color: var(--kb-border);
    color: var(--kb-ink);
}

.dropdown-menu {
    background-color: var(--kb-surface);
    border: 1px solid var(--kb-border);
    border-radius: var(--kb-radius-sm);
    box-shadow: var(--kb-shadow);
    --bs-dropdown-link-color: var(--kb-ink);
    --bs-dropdown-link-hover-bg: var(--kb-accent-soft);
    --bs-dropdown-link-hover-color: var(--kb-accent-2);
}

.offcanvas {
    background-color: var(--kb-surface);
    color: var(--kb-ink);
    border-right: 1px solid var(--kb-border);
}

.offcanvas .nav-link {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    color: var(--kb-muted);
    border-radius: 8px;
    padding: 0.55rem 0.75rem;
}

.offcanvas .nav-link i {
    width: 1.1rem;
    text-align: center;
    opacity: 0.85;
}

.offcanvas .nav-link:hover, .offcanvas .nav-link:focus {
    color: var(--kb-accent);
    background-color: var(--kb-accent-soft);
}

/* Groups the side menu into 記録/データ連携/設定 sections (see header.jsp) -
   purely a visual label, doesn't affect navigation. */
.side-menu-group-label {
    font-size: 0.68rem;
    letter-spacing: 0.06em;
    color: var(--kb-muted);
    padding: 0.9rem 0.75rem 0.3rem;
}

.side-menu-group-label:first-child {
    padding-top: 0.2rem;
}

/* ---------- category drag-to-reorder ---------- */

.category-drag-handle {
    cursor: grab;
    color: var(--kb-muted);
    font-size: 1.1rem;
    touch-action: none;
}

.category-drag-handle:active {
    cursor: grabbing;
}

/* SortableJS applies this to the element being dragged. */
.sortable-ghost {
    opacity: 0.4;
    background: var(--kb-accent-soft);
}

/* ---------- QR codes (scannable needs light-on-dark contrast) ---------- */

.qr-code-box {
    display: inline-block;
    background: #fff;
    padding: 10px;
    border-radius: 12px;
    line-height: 0;
}

/* ---------- side menu tab ---------- */

.side-menu-tab {
    background: transparent;
    border: none;
    color: var(--kb-accent);
    font-size: 1.5rem;
    line-height: 1;
    padding: 0.25rem 0.5rem;
}

.side-menu-tab:hover,
.side-menu-tab:focus {
    color: var(--kb-accent-2);
}

#sideMenu.offcanvas-top {
    --bs-offcanvas-height: 50vh;
    border-right: none;
    border-bottom: 1px solid var(--kb-border);
}

@media (min-width: 768px) {
    .side-menu-tab {
        position: fixed;
        left: 12px;
        top: 72px;
        z-index: 1030;
        width: 42px;
        height: 42px;
        display: flex;
        align-items: center;
        justify-content: center;
        background-color: var(--kb-surface);
        color: var(--kb-accent);
        border: 1px solid var(--kb-border);
        border-radius: 10px;
        padding: 0;
        box-shadow: var(--kb-shadow);
        font-size: 1.25rem;
    }

    .side-menu-tab:hover,
    .side-menu-tab:focus {
        background-color: var(--kb-surface-2);
        color: var(--kb-accent-2);
    }

    .page-content {
        padding-left: 4.5rem;
    }
}

/* ============================ THEME PICKER ============================ */

.theme-option {
    display: block;
    width: 100%;
    padding: 0;
    text-align: left;
    background: var(--kb-surface);
    border: 1px solid var(--kb-border);
    border-radius: var(--kb-radius);
    box-shadow: var(--kb-shadow);
    overflow: hidden;
    cursor: pointer;
    transition: transform 0.12s ease, box-shadow 0.12s ease, border-color 0.12s ease;
}

.theme-option:hover {
    transform: translateY(-3px);
    border-color: var(--kb-border-strong);
}

.theme-option.is-current {
    border-color: var(--kb-accent);
    box-shadow: 0 0 0 2px var(--kb-accent), var(--kb-shadow);
}

.theme-option-meta {
    padding: 0.85rem 1rem 1rem;
}

.theme-option-name {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-family: var(--kb-heading-font);
    font-weight: 600;
    color: var(--kb-ink);
}

.theme-option-name .badge {
    background: var(--kb-accent-soft);
    color: var(--kb-accent);
    border: 1px solid color-mix(in srgb, var(--kb-accent) 45%, transparent);
    font-size: 0.7rem;
}

.theme-option-desc {
    margin-top: 0.25rem;
    font-size: 0.85rem;
    color: var(--kb-muted);
}

/*
 * Live preview. The data-theme on this element makes every --kb-* var inside
 * resolve to the previewed theme, so each card renders in its own colours
 * regardless of the page's active theme.
 */
.theme-preview {
    padding: 0.9rem;
    background-color: var(--kb-body-bg);
    background-image: var(--kb-body-image);
    border-bottom: 1px solid var(--kb-border);
}

.tp-nav {
    margin-bottom: 0.7rem;
}

.tp-brand {
    font-family: var(--kb-heading-font);
    font-weight: 600;
    font-size: 0.95rem;
    color: var(--kb-accent);
}

.tp-card {
    background: var(--kb-surface);
    border: 1px solid var(--kb-border);
    border-radius: var(--kb-radius);
    box-shadow: var(--kb-shadow);
    padding: 0.75rem 0.85rem;
}

.tp-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    font-size: 0.85rem;
    font-variant-numeric: tabular-nums;
    margin-bottom: 0.35rem;
}

.tp-label {
    color: var(--kb-muted);
}

.tp-income {
    color: var(--kb-income);
    font-weight: 600;
}

.tp-expense {
    color: var(--kb-expense);
    font-weight: 600;
}

.tp-track {
    height: 7px;
    border-radius: 99px;
    background: var(--kb-track);
    overflow: hidden;
    margin: 0.55rem 0 0.75rem;
}

.tp-fill {
    display: block;
    height: 100%;
    border-radius: 99px;
    background: linear-gradient(90deg, color-mix(in srgb, var(--kb-income) 80%, #000 8%), var(--kb-income));
}

.tp-btn {
    display: inline-block;
    padding: 0.3rem 0.85rem;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--kb-on-accent);
    background: var(--kb-btn-bg);
    border-radius: var(--kb-radius-sm);
    box-shadow: var(--kb-btn-shadow);
}

/* Transient confirmation after an in-place theme change (see settings-theme.jsp). */
.theme-toast {
    position: fixed;
    left: 50%;
    bottom: 24px;
    z-index: 1080;
    padding: 0.65rem 1.25rem;
    background: var(--kb-accent);
    color: var(--kb-on-accent);
    font-weight: 600;
    border-radius: 99px;
    box-shadow: var(--kb-btn-shadow);
    opacity: 0;
    pointer-events: none;
    transform: translateX(-50%) translateY(8px);
    transition: opacity 0.2s ease, transform 0.2s ease;
}

.theme-toast.is-visible {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
}
