/*
 * agentic-cbp — admin UI design system.
 *
 * One file, no preprocessor, dependency-free. Organized as:
 *   1. Design tokens         — colors, spacing, type, radii, shadows.
 *   2. Base / reset          — HTML element defaults.
 *   3. Layout                — topbar, main, page header.
 *   4. Components            — card, table, badge, button, form, etc.
 *   5. Utilities             — small helpers (text-muted, mt-*, …).
 *
 * Adding a new visual element should mean adding a class here and
 * referencing it in the template — no inline `style=""` in templates.
 */

/* =====================================================================
 * 1. Design tokens
 * ===================================================================== */

:root {
    /* Color — neutral surface */
    --color-bg: #f6f8fa;
    --color-surface: #ffffff;
    --color-surface-alt: #fafbfc;
    --color-border: #e2e6ec;
    --color-border-strong: #cdd3dc;

    /* Color — text */
    --color-text: #0f172a;
    --color-text-muted: #64748b;
    --color-text-subtle: #94a3b8;

    /* Color — brand + status */
    --color-primary: #4f46e5;
    --color-primary-hover: #4338ca;
    --color-primary-bg: #eef2ff;

    --color-success: #16a34a;
    --color-success-bg: #f0fdf4;

    --color-warning: #b45309;
    --color-warning-bg: #fffbeb;

    --color-danger: #b91c1c;
    --color-danger-bg: #fef2f2;
    --color-danger-border: #fecaca;

    --color-info: #1e40af;
    --color-info-bg: #eff6ff;

    /* Role-specific accents */
    --color-role-root-bg: #1e1b4b;
    --color-role-root-fg: #ffffff;
    --color-role-admin-bg: #e2e8f0;
    --color-role-admin-fg: #475569;

    /* Spacing scale */
    --space-1: 0.25rem;   /*  4px */
    --space-2: 0.5rem;    /*  8px */
    --space-3: 0.75rem;   /* 12px */
    --space-4: 1rem;      /* 16px */
    --space-5: 1.5rem;    /* 24px */
    --space-6: 2rem;      /* 32px */
    --space-8: 3rem;      /* 48px */

    /* Type scale */
    --text-xs: 11px;
    --text-sm: 12px;
    --text-base: 13.5px;
    --text-md: 15px;
    --text-lg: 18px;
    --text-xl: 22px;
    --text-2xl: 28px;

    /* Radii */
    --radius-sm: 4px;
    --radius-md: 6px;
    --radius-lg: 10px;
    --radius-pill: 9999px;

    /* Shadows — subtle, professional */
    --shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.04);
    --shadow:    0 1px 3px rgba(15, 23, 42, 0.06), 0 1px 2px rgba(15, 23, 42, 0.04);
    --shadow-lg: 0 10px 25px rgba(15, 23, 42, 0.08), 0 2px 6px rgba(15, 23, 42, 0.04);

    /* Misc */
    --transition: 120ms ease-out;
    --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
    --font-mono: ui-monospace, "SFMono-Regular", Menlo, monospace;
}

/* =====================================================================
 * 2. Base / reset
 * ===================================================================== */

*, *::before, *::after { box-sizing: border-box; }

html, body {
    margin: 0;
    padding: 0;
}

body {
    font-family: var(--font-sans);
    font-size: var(--text-base);
    line-height: 1.55;
    color: var(--color-text);
    background: var(--color-bg);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

a {
    color: var(--color-primary);
    text-decoration: none;
    transition: color var(--transition);
}
a:hover { color: var(--color-primary-hover); text-decoration: underline; }

h1, h2, h3, h4 {
    margin: 0;
    font-weight: 600;
    color: var(--color-text);
    letter-spacing: -0.01em;
}
h1 { font-size: var(--text-2xl); line-height: 1.25; }
h2 { font-size: var(--text-lg);  line-height: 1.35; margin-top: var(--space-6); margin-bottom: var(--space-3); }
h3 { font-size: var(--text-md);  line-height: 1.4;  margin-top: var(--space-5); margin-bottom: var(--space-2); }

code, .mono {
    font-family: var(--font-mono);
    font-size: 0.92em;
}
code {
    background: var(--color-surface-alt);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    padding: 1px 5px;
}

p { margin: 0 0 var(--space-4); }

hr {
    border: 0;
    border-top: 1px solid var(--color-border);
    margin: var(--space-5) 0;
}

/* =====================================================================
 * 3. Layout
 * ===================================================================== */

.topbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--space-3) var(--space-5);
    background: var(--color-surface);
    border-bottom: 1px solid var(--color-border);
    box-shadow: var(--shadow-sm);
    position: sticky;
    top: 0;
    z-index: 10;
}

.topbar__left {
    display: flex;
    align-items: center;
    gap: var(--space-5);
}

.topbar__logo {
    display: inline-flex;
    align-items: center;
}

.topbar__logo-img {
    height: 28px;
    width: auto;
    display: block;
}

.topbar__nav {
    display: flex;
    gap: var(--space-1);
}

.topbar__nav a {
    color: var(--color-text-muted);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-md);
    font-weight: 500;
    transition: background var(--transition), color var(--transition);
}
.topbar__nav a:hover {
    color: var(--color-text);
    background: var(--color-surface-alt);
    text-decoration: none;
}

.topbar__right {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    color: var(--color-text-muted);
    font-size: var(--text-sm);
}

.topbar__user {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

.topbar__email {
    color: var(--color-text);
}

.main {
    max-width: 1416px;
    margin: var(--space-6) auto;
    padding: 0 var(--space-5);
}

/* Pages whose content (typically a wide data table) reads better at
 * the full viewport width — the global `.main` max-width is sized for
 * forms / dashboards and cramps the invoice-draft table once every
 * column is in play. Modifier opt-in to keep narrower pages honest. */
.main--full {
    max-width: none;
}

.page-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: var(--space-4);
    margin-bottom: var(--space-5);
}

/* Pages where we want the title to sit closer to the content
 * (currently just /cashflow). The page-header gap is collapsed and
 * the H1's intrinsic line-height padding is negated, leaving the
 * cashflow card riding right under the title.
 *
 * The right-side toggle is floated absolutely so its `margin-top`
 * (which deliberately drops it 30 px below the title) doesn't
 * inflate the header's height. */
.main--tight                  { margin-top: var(--space-4); }
.main--tight .page-header     { margin-bottom: 15px; position: relative; }
.main--tight .page-header h1  { line-height: 1; }
.main--tight .toggle-switch {
    position: absolute;
    top: 0;
    right: 0;
}

.page-header__meta {
    color: var(--color-text-muted);
    font-size: var(--text-sm);
    margin-top: var(--space-1);
}

/* =====================================================================
 * 4. Components
 * ===================================================================== */

/* ---- Card ----------------------------------------------------------- */
.card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    padding: var(--space-5);
    box-shadow: var(--shadow-sm);
}
.card--tight { padding: var(--space-4); }
/* Tight card variant used by the invoice-draft sections — keeps
 * the white wrapper + border + shadow but trims everything around
 * the table to a uniform 8 px gutter. The default global rules
 * (`.mt-3` on the table-wrap, `.table-wrap { margin-bottom }`)
 * would otherwise stack on the card padding and produce
 * asymmetric whitespace, so we zero them inside this scope. */
.card--bare {
    padding: var(--space-3);
}
.card--bare .table-wrap {
    margin-bottom: 0;
    /* `.table-wrap` clips its descendants by default so the rounded
       outer corners stay tidy; that also clips the Konto autocomplete
       popover when it extends below the row. Allow visible overflow
       inside the section card so the dropdown can escape. */
    overflow: visible;
}
.card--bare .table-wrap.mt-3 {
    /* Match the card padding so the heading-to-table distance
       equals the surrounding gutter. */
    margin-top: var(--space-3);
}
.card + .card { margin-top: var(--space-4); }
.card--info    { background: var(--color-info-bg);    border-color: #c7d2fe; color: var(--color-info); }
.card--warning { background: var(--color-warning-bg); border-color: #fde68a; color: var(--color-warning); }
.card--danger  { background: var(--color-danger-bg);  border-color: var(--color-danger-border); color: var(--color-danger); }

/* ---- Stat cards (dashboard summary) -------------------------------- */
.stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: var(--space-3);
    margin-bottom: var(--space-5);
}

.stat {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    padding: var(--space-4) var(--space-5);
    box-shadow: var(--shadow-sm);
}
.stat__label {
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--color-text-muted);
    font-weight: 600;
}
.stat__value {
    font-size: var(--text-2xl);
    font-weight: 600;
    margin-top: var(--space-2);
    line-height: 1;
    font-variant-numeric: tabular-nums;
}
.stat__sub {
    font-size: var(--text-sm);
    font-weight: 500;
    margin-left: var(--space-2);
}
.stat--warning {
    background: var(--color-warning-bg);
    border-color: #fde68a;
}

/* ---- Table --------------------------------------------------------- */
.table-wrap {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    overflow: hidden;
    box-shadow: var(--shadow-sm);
    margin-bottom: var(--space-5);
}

.table {
    width: 100%;
    border-collapse: collapse;
    font-variant-numeric: tabular-nums;
}

.table th, .table td {
    padding: var(--space-3) var(--space-4);
    text-align: left;
    border-bottom: 1px solid var(--color-border);
}

.table thead th {
    background: var(--color-surface-alt);
    font-weight: 600;
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--color-text-muted);
}

.table tbody tr {
    transition: background var(--transition);
}
.table tbody tr:hover {
    background: var(--color-primary-bg);
}
.table tbody tr:last-child td {
    border-bottom: 0;
}

.table .row-muted {
    color: var(--color-text-muted);
}
.table .row-muted a {
    color: var(--color-text-muted);
}

/* ---- Badges -------------------------------------------------------- */
.badge {
    display: inline-block;
    padding: 2px var(--space-2);
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    border-radius: var(--radius-pill);
    line-height: 1.6;
    white-space: nowrap;
}
.badge--active   { background: var(--color-success-bg); color: var(--color-success); }
.badge--inactive { background: var(--color-surface-alt); color: var(--color-text-muted); border: 1px solid var(--color-border); }
.badge--root     { background: var(--color-info-bg); color: var(--color-info); border-radius: var(--radius-sm); }
.badge--admin    { background: var(--color-info-bg); color: var(--color-info); border-radius: var(--radius-sm); }
.badge--warning  { background: var(--color-warning-bg);    color: var(--color-warning); }
.badge--info     { background: var(--color-info-bg);       color: var(--color-info); }

/* Invoice-draft row-type pills use the shared badge palette, but with
   a slightly flatter silhouette than global status badges. */
.invoice-draft-kind {
    display: inline-flex;
    align-items: center;
    gap: 0;
    padding: 2px 5px 2px 3px;
    border-radius: 6px;
    line-height: 1;
}
/* `display: block` removes the baseline gap inline SVGs otherwise sit
 * on, and `aspect-ratio: 1 / 1` guarantees the icon stays square even
 * if a parent rule clamps one dimension — without these, the icon
 * can end up rendered at e.g. 16×13 inside the pill and look squashed. */
.invoice-draft-kind__icon {
    width: 20px;
    height: 20px;
    aspect-ratio: 1 / 1;
    flex: 0 0 auto;
    display: block;
}
.invoice-draft-kind__label {
    margin-left: 3px;
}
.invoice-draft-kind--charge {
    background: transparent;
    color: #111111;
    border: 1px solid currentColor;
}
/* Subscription palette: pink. Tailwind's `pink-100`/`pink-50`/
 * `pink-700` give us a soft tint for the row plus a darker text
 * colour that keeps the badge readable. Border picks up
 * `currentColor` (= the pink-700 text) so the pill has a defined
 * edge matching the charge variant's outlined look. */
.invoice-draft-kind--subscription {
    background: #fce7f3;          /* pink-100 */
    color: #be185d;               /* pink-700 */
    border: 1px solid currentColor;
}
.invoice-draft-kind--usage {
    background: #f3e8ff;          /* purple-100 */
    color: #7e22ce;               /* purple-700 */
    border: 1px solid currentColor;
}

/* Tighten the cell that holds the type pill — the badge already
 * has its own padding/border-radius, so the default `.table td`
 * spacing leaves too much air around it. `:has()` keeps the rule
 * structural so we don't need a class on every charge cell. */
.table td:has(.invoice-draft-kind) {
    padding-left:  var(--space-2);
    padding-right: var(--space-2);
    white-space: nowrap;
}

/* Pin column widths on the invoice-draft table so cells don't reflow
 * when one swaps to an `<input>` in edit mode. `table-layout: fixed`
 * is what makes the browser honour these widths exactly instead of
 * sizing each column to its content. The description column gets
 * the leftover space (no explicit width). */
.invoice-draft-table { table-layout: fixed; }
/* Keep every column on a single line. Combined with `table-layout:
   fixed` and the colgroup widths above, long content (typically a
   description like "Northwind Forecaster · DEMO · tokens.completion")
   gets clipped at the column boundary with an ellipsis instead of
   wrapping the row to two lines and breaking the visual rhythm.
   Exclude the first and last columns explicitly: the Type column
   contains a flex badge whose inline-flex layout text-overflow
   would mistake for overflowing text and render `…` after the
   icon, and the Action column contains a single icon button that
   should never get clipped. */
.invoice-draft-table th:not(:first-child):not(:last-child),
.invoice-draft-table td:not(:first-child):not(:last-child) {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* Type column fits the longest badge label ("PRENUMERATION") plus
 * the kind icon without ellipsis-truncating; Qty needs to seat a
 * 10-digit number ("1 234 567 890") inside the mono dashed pill,
 * so we size for the literal worst case in both columns. */
.invoice-draft-table__col-type        { width: 170px; }
.invoice-draft-table__col-description { width: auto;  }
.invoice-draft-table__col-qty         { width: 130px; }
.invoice-draft-table__col-action      { width:  56px; }

/* Subscription and usage rows render Beskrivning / Antal / Enhet /
 * Pris as plain text (no dashed pill), so their values sit
 * visually 8 px left of where the charge rows' values sit (those
 * carry an inline-block pill with its own left padding). Add a
 * touch of left padding on those four cells in just those two row
 * kinds — and on the matching column headers — so values line up
 * vertically across all line kinds, with the headers tracking the
 * column they label.
 *
 * Cell positions in the colgroup above: 2 = Beskrivning, 3 = Antal,
 * 4 = Enhet, 5 = Pris. */
.invoice-draft-row[data-line-kind="USAGE"] td:nth-child(2),
.invoice-draft-row[data-line-kind="USAGE"] td:nth-child(3),
.invoice-draft-row[data-line-kind="USAGE"] td:nth-child(4),
.invoice-draft-row[data-line-kind="USAGE"] td:nth-child(5),
.invoice-draft-row[data-line-kind="SUBSCRIPTION"] td:nth-child(2),
.invoice-draft-row[data-line-kind="SUBSCRIPTION"] td:nth-child(3),
.invoice-draft-row[data-line-kind="SUBSCRIPTION"] td:nth-child(4),
.invoice-draft-row[data-line-kind="SUBSCRIPTION"] td:nth-child(5),
.invoice-draft-table thead th:nth-child(2),
.invoice-draft-table thead th:nth-child(3),
.invoice-draft-table thead th:nth-child(4),
.invoice-draft-table thead th:nth-child(5) {
    padding-left: calc(var(--space-4) + 8px);
}
/* Darker thead than the global `.table thead th` so the column
 * labels read clearly above the busy invoice-draft rows (mixed
 * usage / subscription / charge tints make the default near-white
 * `--color-surface-alt` blend into the body). Scoped to the
 * invoice-draft table so other lists keep their lighter header. */
.invoice-draft-table thead th {
    background: var(--color-border);
    color: var(--color-text);
}
.invoice-draft-table__col-account     { width: 100px; }
.invoice-draft-table__col-vat         { width:  80px; }
/* Unit column shows short tags ("st", "h", "mil", "tokens"). */
.invoice-draft-table__col-unit        { width: 117px; }
.invoice-draft-table__col-price       { width: 130px; }
.invoice-draft-table__col-total       { width: 170px; }

/* Two-column strip above each section's table: left cell holds
 * the heading (customer name + total badge + line count), right
 * cell holds the include-in-batch affordance (checkbox or "Send
 * to Fortnox" button). Plain grid layout means the right column's
 * height — including any vertical offset on the checkbox — never
 * affects where the heading lands in the left column. */
.invoice-draft-section__topbar {
    /* Tabs-on-table layout: customer-name tab pinned top-left,
     * action tab top-right, with the empty stretch between them
     * letting the table-wrap's top edge peek through. The
     * `margin-bottom: -1px` + `z-index: 1` make each tab's
     * borderless bottom edge sit cleanly on top of the table-wrap's
     * top border, hiding the seam. `align-items: flex-end` keeps
     * both tabs bottom-aligned even when their internal heights
     * differ (e.g. checkbox row taller than just-a-button row). */
    display: flex;
    justify-content: space-between;
    align-items: flex-end;
    gap: var(--space-3);
    margin: 0 0 -1px 0;
    padding: 0;
    position: relative;
    z-index: 1;
}
.invoice-draft-section__topbar > * {
    margin: 0;
}
/* The populated section variant (carries data-customer-id) drops
 * the outer card chrome — only the two tabs and the table-wrap
 * carry visual weight. The empty-state card on the per-customer
 * detail page (no data-customer-id) keeps the regular .card--bare
 * styling so a "nothing to invoice" panel still reads as a card. */
.card--bare[data-customer-id] {
    padding: 0;
    background: transparent;
    border: none;
    box-shadow: none;
}
/* Stack two adjacent invoice-draft sections with a 20 px gap —
 * specifically tuned (not on the `--space` scale) so the section
 * shadow has room to breathe without bleeding into the next one.
 * Overrides the global `.card + .card { margin-top: --space-4 }` */
.card--bare[data-customer-id] + .card--bare[data-customer-id] {
    margin-top: 40px;
}
.card--bare[data-customer-id] .table-wrap.mt-3 {
    /* Tabs sit flush against the table top — kill the gap that the
     * default heading-to-table rule would otherwise re-introduce. */
    margin-top: 0;
}
.card--bare[data-customer-id] .invoice-draft-section__heading,
.card--bare[data-customer-id] .invoice-draft-section__actions {
    /* White tabs — same surface as the table body — so the only
     * visual weight comes from the borders and the thead's darker
     * fill below. Reads as plain "tabs sticking up out of the
     * table". */
    background: var(--color-surface);
    border: 1px solid var(--color-text-subtle);
    /* Rounded only on the top corners. `--radius-md` (6 px) is
     * small enough to read as "tab" rather than "floating pill". */
    border-radius: var(--radius-md) var(--radius-md) 0 0;
    padding: var(--space-2) var(--space-3);
    /* Directional shadow on top + sides only — `box-shadow` blurs
     * in every direction by default, and the downward bleed was
     * spilling onto the thead and reading as a dirty seam. The
     * three offsets below cast a soft halo above and to either
     * side of the tab; the bottom edge sits cleanly against the
     * table without projecting anything onto it. */
    box-shadow:
        0 -3px 8px rgba(15, 23, 42, 0.08),
        -2px 0 6px rgba(15, 23, 42, 0.05),
        2px 0 6px rgba(15, 23, 42, 0.05);
}
/* Darker borders inside the invoice-draft section: outer table
 * frame, inter-tab seam, and the line between thead and tbody.
 * Scoped to the populated section so other lists keep their
 * lighter `--color-border` separators. */
.card--bare[data-customer-id] .table-wrap {
    border-color: var(--color-text-subtle);
    /* Bump the table well above the global `--shadow-sm` default
     * so the whole section reads as a clearly raised element. A
     * custom shadow rather than `--shadow-lg` because the lg token
     * blurs upward too — and the tabs above the table would
     * project their own; here we want one big drop shadow under
     * the section. */
    box-shadow: 0 5px 16px rgba(15, 23, 42, 0.12), 0 1px 4px rgba(15, 23, 42, 0.07);
    /* Square top corners so the tabs sit flush against a flat
     * edge — the tab's own rounded top corners + the wrap's
     * rounded bottom corners give the section the "little radius"
     * the operator asked for without the awkward radius collision
     * where a tab tries to overlap a curved table corner. Bottom
     * corners explicitly set to `--radius-md` (6 px, same as the
     * tab tops) so the curve is tight enough that the inner table
     * cells — which run with `overflow: visible` to let the Konto
     * autocomplete dropdown escape the wrap — don't visibly clash
     * with the rounded corner at the bottom-right. */
    border-top-left-radius: 0;
    border-top-right-radius: 0;
    /* Bottom corners get the same `--radius-md` as the tab tops so
     * the section reads as a unified rounded element. Note: with
     * `overflow: visible` (kept for the Konto autocomplete) the
     * inner cell `border-bottom`s run straight to the table's
     * outer edge, which can show a tiny seam where the curve meets
     * the straight border. The fix below — clipping the table
     * itself with a matching `border-radius` — keeps the cells
     * inside the curve. */
    border-bottom-left-radius: var(--radius-md);
    border-bottom-right-radius: var(--radius-md);
}
.card--bare[data-customer-id] .invoice-draft-table {
    /* Round the table itself so the `border-collapse: collapse`
     * cell borders follow the wrap's curve at the bottom corners
     * instead of poking past it. `overflow: hidden` here clips the
     * last row's cell borders to the rounded shape but does NOT
     * affect the Konto autocomplete dropdown — that escapes via
     * the wrap's `overflow: visible` set above. */
    border-bottom-left-radius: var(--radius-md);
    border-bottom-right-radius: var(--radius-md);
    overflow: hidden;
}
.card--bare[data-customer-id] .invoice-draft-table th,
.card--bare[data-customer-id] .invoice-draft-table td {
    border-bottom-color: var(--color-text-subtle);
}
/* Strip the 1 px cell border-bottom on the very last visible row
 * of the table (the tfoot "Lägg till" add-charge row). The global
 * `tbody tr:last-child td { border-bottom: 0 }` only covers tbody;
 * here the tfoot is what actually closes the table, and its own
 * border-bottom was stacking on top of the wrap's 1 px border —
 * reading as a 2 px-thick bottom edge. */
.card--bare[data-customer-id] .invoice-draft-table tfoot tr:last-child td {
    border-bottom: 0;
}
/* Visible separator above the tfoot add-charge row. The last
 * tbody row drops its own border-bottom (global rule above), so
 * without an explicit top border on the tfoot row the add-charge
 * line floated against the previous row with no divider. */
.card--bare[data-customer-id] .invoice-draft-table tfoot tr td {
    border-top: 1px solid var(--color-text-subtle);
}
.invoice-draft-section__topbar h2,
.invoice-draft-section__topbar h2 a {
    /* `<h2>` and its `<a>` child both inherit large vertical
       margins from the global typography rules (`h2 { margin-top:
       var(--space-6) }` etc.); the template applies `mt-0 mb-0`
       utility classes but they don't always win the cascade
       depending on stylesheet ordering. Force-zero here so the
       heading never adds vertical space inside the section card. */
    margin: 0;
    padding: 0;
    line-height: 1.2;
}
.invoice-draft-section__actions {
    display: flex;
    align-items: flex-start;
    justify-content: flex-end;
}

/* Header line of each section card: customer name + total badge +
 * line-count subtitle, lined up baseline-to-baseline. */
.invoice-draft-section__heading {
    display: flex;
    /* Center the price-tag badge vertically against the customer
     * name so the two read as a single horizontal "title strip"
     * inside the tab. Baseline alignment used to do the job when
     * the line count subtitle anchored the row, but with the
     * subtitle removed the badge's larger box made baseline look
     * mis-aligned. */
    align-items: center;
    gap: var(--space-3);
    flex-wrap: wrap;
}

/* Section selector — replaces the per-section "Send to Fortnox" button
 * on the global drafts page with a tick-box, so a single global
 * "Create N invoices" action acts on every checked section at once.
 * Big checkbox + muted label so the affordance is obvious without
 * shouting from the page. */
.invoice-draft-section__select {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    cursor: pointer;
    user-select: none;
}
.invoice-draft-section__select input[type="checkbox"] {
    width: 18px;
    height: 18px;
    accent-color: var(--color-success);
    cursor: pointer;
}
.invoice-draft-section__select-label {
    color: var(--color-text-muted);
    font-size: var(--text-sm);
}

/* The two "split usage rows by …" toggles inside the customer-
 * name tab. Same general shape as the batch-include checkbox
 * above but smaller and quieter — they're view preferences, not
 * billing decisions, so the affordances tuck next to the total
 * badge without competing with it. The labels use `nowrap` so
 * they don't break to a second line when the tab is narrow. */
.invoice-draft-section__group-toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    cursor: pointer;
    user-select: none;
    margin-left: var(--space-2);
}
.invoice-draft-section__group-toggle input[type="checkbox"] {
    width: 14px;
    height: 14px;
    cursor: pointer;
}
.invoice-draft-section__group-toggle-label {
    color: var(--color-text-muted);
    font-size: var(--text-sm);
    white-space: nowrap;
}

/* The total-amount price tag next to the customer name. Saturated
 * green reads as "money about to land" — pairs well with the
 * "Send to Fortnox" affordance to its right. Square corners
 * (small radius) instead of the global pill default keep it
 * looking like a price tag rather than a soft status label. */
.invoice-draft-total-badge {
    background: var(--color-success-bg);
    color: var(--color-success);
    border: 1px solid var(--color-success);
    font-size: var(--text-sm);
    text-transform: none;          /* override the global .badge upper-case */
    letter-spacing: 0;
    padding: 3px 8px;
    border-radius: var(--radius-sm);  /* 4px, replacing the inherited pill radius */
}


/* Inline "add free text" row in the invoice-draft tfoot. Lighter
 * background distinguishes it from data rows; the `not-allowed`
 * cursor that applies to the rest of the draft tbody is overridden
 * here because every cell on this row IS interactive. */
.invoice-draft-add-row td {
    background: var(--color-surface-alt);
    cursor: auto;
}
.invoice-draft-add-row .input {
    width: 100%;
    box-sizing: border-box;
    text-align: inherit;          /* center cells get centered inputs */
    /* Tighter than the global `.input` padding so the add-row sits
       at the same vertical rhythm as the body rows above — the
       global `var(--space-2) var(--space-3)` is comfortable for
       full-page forms but oversized inside a packed table. */
    padding: var(--space-1) var(--space-2);
}
/* The qty / unit / amount inputs in the add-row carry `class="mono"`
   in the template, but the global `.input` rule's `font: inherit`
   (defined later in the cascade) clobbers that. Restore the mono
   font explicitly so these three numeric inputs match the body
   rows' Antal / Enhet / Pris column. The description input is
   intentionally left in the body font. */
.invoice-draft-add-row .input.mono {
    font-family: var(--font-mono);
    font-size: 0.92em;
}
/* Tighten button to fit comfortably inside the totals column without
 * stretching to its full 130px-ish width. */
.invoice-draft-add-row .btn {
    padding: var(--space-1) var(--space-3);
    font-size: var(--text-sm);
}
/* Inline error message rendered above the table on AJAX add-charge
 * failure (validation or network). Hidden by default — the JS toggles
 * the `hidden` attribute on/off. */
.invoice-draft-add__error {
    margin: var(--space-2) 0;
    padding: var(--space-2) var(--space-3);
    background: var(--color-danger-bg);
    color: var(--color-danger);
    border: 1px solid var(--color-danger-border);
    border-radius: var(--radius-sm);
    font-size: var(--text-sm);
}
/* Per-input error ring while the operator corrects the failed field. */
.input--error {
    border-color: var(--color-danger);
    box-shadow: 0 0 0 1px var(--color-danger-border);
}

/* Tint each draft row to match its badge's accent — very light so the
 * row stays readable but the row-type is recognizable at a glance.
 * Charge rows stay neutral (the outlined badge already gives them a
 * distinct visual signal). Hover intensifies each row's own tint
 * (one Tailwind step darker) so the hover still tracks the row's
 * type rather than flattening to a single primary-bg color. */
.table tbody tr:has(.invoice-draft-kind--usage)               { background: #faf5ff; } /* purple-50  */
.table tbody tr:has(.invoice-draft-kind--usage):hover         { background: #f3e8ff; } /* purple-100 */
.table tbody tr:has(.invoice-draft-kind--subscription)        { background: #fdf2f8; } /* pink-50   */
.table tbody tr:has(.invoice-draft-kind--subscription):hover  { background: #fce7f3; } /* pink-100  */
.table tbody tr:has(.invoice-draft-kind--charge):hover        { background: var(--color-surface-alt); }

/* `not-allowed` cursor on every cell in an invoice-draft row that
 * is NOT marked as inline-editable. That covers all cells on usage
 * and subscription rows (none of which are editable from this view —
 * usage flows from real events, subscriptions are computed) plus
 * the non-editable cells on charge rows (badge / qty / unit). The
 * editable cells (`.invoice-draft-edit`) keep `cursor: text` from
 * their own rule below, so the disabled affordance doesn't bleed
 * onto the description or total cell of a charge row. */
.table tbody tr:has(.invoice-draft-kind) td:not(.invoice-draft-edit) {
    cursor: not-allowed;
}

/* Inline-editable cells on the invoice-draft view. At rest the cell
 * reads as text; hovering reveals a pointer cursor and a dashed
 * outline that mirrors the Konto button / Moms input affordance,
 * so every edit-on-click cell on the page tells the same visual
 * story. The transient `--saved` / `--error` classes below still
 * flash briefly after a save attempt. */
.invoice-draft-edit {
    cursor: pointer;
    transition: background 200ms ease-out;
}
/* The dashed affordance lives on a value-sized pill *inside* the
   cell, not on the whole TD — same shape and treatment as the
   Konto button / Moms input above the body rows, so the row reads
   as a single column-strip of editable affordances. */
/* Descendant selector (not direct-child) so the pill renders
 * regardless of intermediate wrappers. The Antal/Pris/Enhet/Total
 * cells host the faux pill as a direct child of the TD; the
 * description cell wraps it in `.invoice-draft-row__description-anchor`
 * (needed because absolute-positioning the excluded warn pill off
 * a TD is unreliable). The earlier `>` form silently skipped
 * description, leaving its value unstyled. */
.invoice-draft-edit .invoice-draft-edit__faux {
    display: inline-block;
    padding: 2px var(--space-2);
    border: 1px dashed var(--color-border);
    border-radius: var(--radius-sm);
    color: inherit;
    /* Hug the value horizontally; long descriptions get capped at
       the cell width with an ellipsis inside the pill, matching
       the cell-level nowrap rule above. */
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    vertical-align: middle;
    transition: background var(--transition), border-color var(--transition);
}
.invoice-draft-edit:hover .invoice-draft-edit__faux {
    background: var(--color-surface-alt);
    border-color: var(--color-text-muted);
}
.invoice-draft-edit__input {
    width: 100%;
    padding: 2px 4px;
    border: 1px solid var(--color-primary);
    border-radius: 3px;
    font: inherit;
    background: var(--color-surface);
    color: inherit;
    text-align: inherit;
    box-sizing: border-box;
}
.invoice-draft-edit--saved { background: var(--color-success-bg); }
.invoice-draft-edit--error { background: var(--color-danger-bg);  }


/* ---- Buttons ------------------------------------------------------- */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-4);
    font: inherit;
    font-weight: 500;
    line-height: 1.2;
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    cursor: pointer;
    transition: background var(--transition), border-color var(--transition), color var(--transition), box-shadow var(--transition);
    white-space: nowrap;
    text-decoration: none;
}
.btn:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}

.btn-primary {
    background: var(--color-primary);
    color: #fff;
}
.btn-primary:hover { background: var(--color-primary-hover); color: #fff; text-decoration: none; }

.btn-secondary {
    background: var(--color-surface);
    color: var(--color-text);
    border-color: var(--color-border-strong);
}
.btn-secondary:hover { background: var(--color-surface-alt); color: var(--color-text); text-decoration: none; }

.btn-danger {
    background: var(--color-surface);
    color: var(--color-danger);
    border-color: var(--color-danger-border);
}
.btn-danger:hover {
    background: var(--color-danger);
    color: #fff;
    border-color: var(--color-danger);
    text-decoration: none;
}

.btn-ghost {
    background: transparent;
    color: var(--color-text-muted);
    padding: var(--space-1) var(--space-2);
    font-size: var(--text-sm);
}
.btn-ghost:hover { color: var(--color-text); background: var(--color-surface-alt); text-decoration: none; }

.btn-sm { padding: var(--space-1) var(--space-3); font-size: var(--text-sm); }

.btn-link {
    background: transparent;
    border: 0;
    color: var(--color-text-muted);
    padding: 0;
    font: inherit;
    cursor: pointer;
    text-decoration: underline;
}
.btn-link:hover { color: var(--color-text); }

/* Per-row delete column on the invoice-draft table. The trash button
   sits in its own narrow cell to the right of the total. Hidden by
   default and revealed on row hover so the table reads cleanly when
   the operator isn't editing. */
.invoice-draft-row__action-cell {
    vertical-align: middle;
}
.invoice-draft-row__delete-btn {
    background: transparent;
    border: 1px solid transparent;
    color: var(--color-text-muted);
    width: 32px;
    height: 32px;
    border-radius: var(--radius-sm);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    transition: background var(--transition), color var(--transition), border-color var(--transition);
}
.invoice-draft-row__delete-btn svg { width: 18px; height: 18px; }
.invoice-draft-row__delete-btn:hover {
    color: var(--color-danger);
    background: var(--color-danger-bg, #fee2e2);
    border-color: #fecaca;
}
tr.invoice-draft-row--excluded .invoice-draft-row__delete-btn:hover {
    color: var(--color-text);
    background: var(--color-surface-alt);
    border-color: var(--color-border);
}

/* Strike-through styling for excluded rows. Applies a single line
   through every cell (including monospaced money columns) so the
   row reads "this is gone from the invoice" at a glance. The total
   cell stays struck-through too — the operator can still see what
   was excluded, even though it doesn't count toward the green pill. */
tr.invoice-draft-row--excluded td {
    color: var(--color-text-muted);
    text-decoration: line-through;
    text-decoration-thickness: 1px;
}
/* Form elements (the Konto button + Moms input) carry their own
   `color` and don't inherit `text-decoration` through their inner
   layout — they need explicit overrides on the excluded variant
   so the strike + muted treatment is consistent across the row.
   Same for the inline-edit faux pill on charge rows. The `.badge`
   and `.invoice-draft-kind` get the strike too — earlier code
   stripped it to "keep the badge readable", but the user wants
   the row to look uniformly disabled, badge included. */
tr.invoice-draft-row--excluded .invoice-draft-row__account-input,
tr.invoice-draft-row--excluded .invoice-draft-row__vat-input,
tr.invoice-draft-row--excluded .invoice-draft-edit__faux,
tr.invoice-draft-row--excluded .invoice-draft-row__description-text {
    color: var(--color-text-muted);
    text-decoration: line-through;
    text-decoration-thickness: 1px;
    border-color: var(--color-border);
}
/* The kind badge is `display: inline-flex`, which establishes its
   own text-decoration context — the cell-level strike doesn't
   propagate. Apply it explicitly on the label, and mute the badge
   tint so the row reads as uniformly disabled. */
tr.invoice-draft-row--excluded .invoice-draft-kind__label {
    text-decoration: line-through;
    text-decoration-thickness: 1px;
}
tr.invoice-draft-row--excluded .invoice-draft-kind {
    color: var(--color-text-muted);
}
/* Reveal the warn pill — gated on the row class so the toggle
   flow shows / hides it in sync with the eye-icon state.
   `inline-flex` (not `inline-block`) keeps the pill content-sized
   inside the description-anchor's flex layout, so it shrink-wraps
   tightly around its label rather than stretching with the
   surrounding flex sizing. */
tr.invoice-draft-row--excluded .invoice-draft-row__excluded-warn {
    display: inline-flex;
    align-items: center;
    width: fit-content;
    text-decoration: none !important;
}
/* Stop the cell-level strike from propagating through the
   description cell entirely — Chrome still paints the line across
   atomic inline-level descendants (the warn pill) despite spec
   guidance. The description text gets its strike from a direct
   rule on `.invoice-draft-row__description-text`, so removing the
   cell-level decoration here doesn't lose anything. */
tr.invoice-draft-row--excluded .invoice-draft-row__description-cell {
    text-decoration: none !important;
}

/* Warning pill on excluded rows. Sits at the right of the
   description cell, position-absolute so a long description
   visually runs underneath it (the cell already has
   text-overflow: ellipsis from the table-wide rule). The pill
   itself opts out of the row-wide muted/strike treatment because
   it's the explanation of WHY the row is muted — has to read
   sharp and clear to do its job. */
.invoice-draft-row__description-anchor {
    /* Flex layout: description on the left (truncates with
       ellipsis), warn pill on the right (always visible). The
       earlier attempt at absolute positioning vanished into the
       cell's overflow-hidden clipping; this approach guarantees
       the pill stays in the layout flow and visible. */
    display: flex;
    align-items: center;
    gap: var(--space-3);
    width: 100%;
    min-width: 0;
}
.invoice-draft-row__description-text {
    /* The description content takes whatever room is left after
       the pill, with its own ellipsis-clipping inside. Targeted
       by class (not `:first-child`) so the rule matches even when
       the pill is the only element sibling. */
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.invoice-draft-row__excluded-warn {
    /* Always rendered server-side on toggleable rows; visibility
       is gated on the row's `--excluded` class so the client-side
       eye-icon toggle (which just flips that class) keeps the
       pill in sync without DOM mutation. */
    display: none;
    flex: 0 0 auto;
    /* `--color-warning-bg` is `#fffbeb` — the same shade as the
       SUBSCRIPTION row tint, so the pill would vanish into the row.
       Use a more saturated yellow with a darker border so it
       reads on white, amber-50 (subscription), and purple-50
       (usage) backgrounds alike. */
    background: #fef08a;
    color: var(--color-warning);
    border: 1px solid #fbbf24;
    border-radius: var(--radius-sm);
    padding: 2px var(--space-2);
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    white-space: nowrap;
    /* The row's strike-through treatment shouldn't bleed onto the
       warning pill; explicit override keeps it readable. */
    text-decoration: none;
}
/* Inner text wrapper — establishes its own decoration context
   so any strikethrough drawn by the row / cell / description-text
   can't visually carry through to the warning label. Belt-and-
   braces: the outer pill already has `text-decoration: none
   !important` and is `inline-flex` (atomic-inline, parent line
   shouldn't draw through), but some renderers still bleed the
   line across — wrapping the actual text in its own block-level
   inline-block guarantees a clean re-render. */
.invoice-draft-row__excluded-warn__text {
    display: inline-block;
    text-decoration: none !important;
    text-decoration-line: none !important;
    /* Last-ditch defense: even if some strike line is being drawn
       through this element from a source we can't suppress, render
       it transparent so it's visually invisible. */
    text-decoration-color: transparent !important;
}
/* Action column carries the toggle button — it has to stay legible
   so the operator can click "show again", so it's the only piece
   of an excluded row that opts out of the strike treatment. */
tr.invoice-draft-row--excluded .invoice-draft-row__action-cell {
    text-decoration: none;
}

/* Footer of the shared confirm modal: right-aligned action buttons
   matching the events-modal footer styling. */
.modal__footer {
    padding: var(--space-4) var(--space-5);
    border-top: 1px solid var(--color-border);
}
.flex-end {
    display: flex;
    justify-content: flex-end;
    align-items: center;
}

/* Account picker — invoice-draft "Konto" column. The button cell
   shows the current account number (or em-dash); clicking it opens
   the autocomplete popover. Strikethrough rules from the excluded-row
   CSS apply automatically because the cell is a regular <td>. */
.invoice-draft-row__account-cell,
.invoice-draft-row__vat-cell,
.invoice-draft-table thead th:nth-child(7),
.invoice-draft-table thead th:nth-child(8) {
    /* The `.text-center` utility on these cells in the template has
       lower specificity than the global `.table td { text-align:
       left }` rule and gets clobbered. Keep the centring at a
       higher specificity here so the Konto / Moms columns —
       header and body — stay centred. */
    text-align: center;
}
/* The Konto cell hosts an autocomplete popover anchored under the
   account button. The table-wide `overflow: hidden` rule would
   otherwise clip the popover at the cell boundary; exempt this
   cell so the dropdown can extend below the row. The cell's
   content (a short number or em-dash) is too narrow to overflow
   on its own, so dropping ellipsis here costs nothing. */
.invoice-draft-row__account-cell {
    overflow: visible;
    text-overflow: clip;
}
.invoice-draft-row__account-cell {
    vertical-align: middle;
}
/* In-cell account input — same dashed-pill aesthetic as the Moms
   input. Doubles as the picker's search field: typing surfaces a
   floating dropdown of cached account suggestions, blur / Enter
   commits the typed value (free-text fallback for accounts not
   yet in the local kontoplan). */
.invoice-draft-row__account-input {
    width: 100%;
    box-sizing: border-box;
    padding: 2px var(--space-2);
    border: 1px dashed var(--color-border);
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--color-text);
    text-align: center;
    font-family: var(--font-mono);
    font-size: 0.92em;
    line-height: inherit;
    transition: border-color var(--transition), background var(--transition);
}
.invoice-draft-row__account-input::placeholder {
    color: var(--color-text-muted);
}
.invoice-draft-row__account-input:hover {
    background: var(--color-surface-alt);
    border-color: var(--color-text-muted);
}
.invoice-draft-row__account-input:focus {
    outline: none;
    border-style: solid;
    border-color: var(--color-text-muted);
    background: var(--color-surface);
}

/* Inline Moms input. Always-visible text field; minimal chrome so
   the column reads as text until focused. Matches the picker's
   hover treatment for visual continuity across the two
   Konto/Moms cells. */
.invoice-draft-row__vat-cell {
    vertical-align: middle;
}
.invoice-draft-row__vat-input {
    width: 100%;
    box-sizing: border-box;
    padding: 2px var(--space-2);
    border: 1px dashed var(--color-border);
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--color-text);
    text-align: center;
    /* Mono, matching the Konto / Antal / Pris columns so the row
       reads as a single numeric column-strip. */
    font-family: var(--font-mono);
    font-size: 0.92em;
    line-height: inherit;
    transition: border-color var(--transition), background var(--transition);
}
.invoice-draft-row__vat-input::placeholder {
    color: var(--color-text-muted);
}
.invoice-draft-row__vat-input:hover {
    background: var(--color-surface-alt);
    border-color: var(--color-text-muted);
}
.invoice-draft-row__vat-input:focus {
    outline: none;
    border-style: solid;
    border-color: var(--color-text-muted);
    background: var(--color-surface);
}

.invoice-draft-account-popover {
    position: absolute;
    top: calc(100% + 4px);
    right: 0;
    z-index: 60;
    width: 280px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-md, 0 4px 16px rgba(15, 23, 42, 0.12));
    padding: var(--space-2);
    text-align: left;
    text-decoration: none;
}
/* Floating variant — appended to <body> and positioned via
   `position: fixed` so it can never be clipped by an ancestor's
   overflow. JS sets `top`/`right` from the trigger button's
   bounding rect on open and on scroll / resize. */
.invoice-draft-account-popover--floating {
    position: fixed;
    top: 0;
    right: 0;
}
.invoice-draft-account-popover__list {
    list-style: none;
    margin: 0;
    padding: 0;
    max-height: 240px;
    overflow-y: auto;
}
.invoice-draft-account-popover__item {
    display: flex;
    gap: var(--space-2);
    padding: var(--space-1) var(--space-2);
    border-radius: var(--radius-sm);
    cursor: pointer;
}
.invoice-draft-account-popover__item:hover {
    background: var(--color-surface-alt);
}
.invoice-draft-account-popover__num {
    font-weight: 600;
    flex: 0 0 auto;
}
.invoice-draft-account-popover__desc {
    color: var(--color-text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.invoice-draft-account-popover__empty {
    padding: var(--space-2);
    color: var(--color-text-muted);
    font-style: italic;
}

/* Screen-reader-only utility for headings that are visually
   represented by an icon-only column (e.g. the action column on
   the invoice-draft table). */
.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;
}

/* ---- Forms --------------------------------------------------------- */
.form { display: block; }
.form--narrow { max-width: 560px; }
.form--wide   { max-width: 760px; }

.form-actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin-top: var(--space-4);
}

.form-grid-2 {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-3);
}
.form-grid-postal {
    display: grid;
    grid-template-columns: 1fr 2fr 100px;
    gap: var(--space-3);
}

.field { display: block; margin-top: var(--space-3); }
.field:first-child { margin-top: 0; }

.field__label {
    display: block;
    color: var(--color-text-muted);
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    margin-bottom: var(--space-1);
}

.field__hint {
    display: block;
    color: var(--color-text-muted);
    font-size: var(--text-xs);
    margin-top: var(--space-1);
}

.field__error {
    display: block;
    color: var(--color-danger);
    font-size: var(--text-xs);
    margin-top: var(--space-1);
}

.input, .select, .textarea {
    width: 100%;
    padding: var(--space-2) var(--space-3);
    font: inherit;
    color: var(--color-text);
    background: var(--color-surface);
    border: 1px solid var(--color-text-subtle);
    border-radius: var(--radius-md);
    transition: border-color var(--transition), box-shadow var(--transition);
}
.input:focus, .select:focus, .textarea:focus {
    outline: 0;
    border-color: var(--color-primary);
    box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.18);
}
.textarea { resize: vertical; min-height: 72px; }

/* Inline-edit input that masquerades as a heading until focused. */
.input-inline-h1 {
    flex: 1;
    padding: var(--space-1) var(--space-2);
    font: inherit;
    font-size: var(--text-xl);
    font-weight: 600;
    color: var(--color-text);
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    transition: background var(--transition), border-color var(--transition);
}
.input-inline-h1:hover  { background: var(--color-surface); }
.input-inline-h1:focus  { background: var(--color-surface); border-color: var(--color-text-subtle); outline: 0; box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.18); }

/* ---- Empty state --------------------------------------------------- */
.empty {
    color: var(--color-text-muted);
    background: var(--color-surface);
    border: 1px dashed var(--color-text-subtle);
    border-radius: var(--radius-lg);
    padding: var(--space-6);
    text-align: center;
}

/* ---- Login page --------------------------------------------------- */
.login-shell {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    padding: var(--space-5);
    background: var(--color-bg);
}

.login-card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    padding: var(--space-6);
    width: 100%;
    max-width: 380px;
}

/* ---- Notice / flash ------------------------------------------------ */
.notice {
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-md);
    margin-bottom: var(--space-4);
    font-size: var(--text-base);
}
.notice--info    { background: var(--color-info-bg);    color: var(--color-info);    border: 1px solid #c7d2fe; }
.notice--success { background: var(--color-success-bg); color: var(--color-success); border: 1px solid #bbf7d0; }
.notice--warning { background: var(--color-warning-bg); color: var(--color-warning); border: 1px solid #fde68a; }
.notice--danger  { background: var(--color-danger-bg);  color: var(--color-danger);  border: 1px solid var(--color-danger-border); }

/* Reveal-once API key callout */
.api-key-reveal {
    background: var(--color-warning-bg);
    border: 1px solid #fde68a;
    border-radius: var(--radius-md);
    padding: var(--space-4);
    margin-bottom: var(--space-4);
}
.api-key-reveal__label {
    color: var(--color-warning);
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.api-key-reveal__value {
    display: block;
    margin-top: var(--space-2);
    padding: var(--space-3);
    background: var(--color-surface);
    border: 1px solid #fde68a;
    border-radius: var(--radius-sm);
    word-break: break-all;
    color: var(--color-text);
}

/* Danger zone (for deactivate buttons etc.) */
.danger-zone {
    border: 1px solid var(--color-danger-border);
    border-radius: var(--radius-lg);
    padding: var(--space-4);
    background: var(--color-surface);
    margin-top: var(--space-5);
}
.danger-zone__heading {
    color: var(--color-danger);
    margin-top: 0;
    margin-bottom: var(--space-3);
}

/* Inline form (single row of input + submit) */
.inline-form {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}
.inline-form .input,
.inline-form .select { flex: 1; }

/* ---- Cashflow hero ------------------------------------------------- */

.cashflow {
    background: linear-gradient(180deg, #ffffff 0%, var(--color-surface-alt) 100%);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
    padding: var(--space-5) var(--space-6);
    margin-bottom: var(--space-6);
}


/* Row containing the smoothed revenue counter (large) and lifetime
 * counter (medium). Sits between the net hero and the MTD bars. */
.cashflow__counter-row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-6);
    align-items: flex-end;
    justify-content: space-between;
    margin: 0 0 var(--space-6);
    padding: 0 0 var(--space-6);
    border-bottom: 1px solid var(--color-border);
}

.cashflow__counter-cell {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.cashflow__counter-label {
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
    color: var(--color-text-muted);
}

.cashflow__bars {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.cashflow__row {
    display: grid;
    /* Label column width was tuned for "REVENUE MTD" / "COST MTD" but
     * the Swedish "KOSTNADER DENNA VECKA" / "INTÄKTER DENNA VECKA"
     * (uppercased, with the legend dot in front) needs more room or
     * it wraps to a second line. */
    grid-template-columns: 220px 1fr 140px;
    align-items: center;
    gap: var(--space-3);
}

.cashflow__row-label {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--color-text-muted);
    font-size: var(--text-sm);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.cashflow__legend {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    display: inline-block;
}
.cashflow__legend--revenue { background: var(--color-success); }
.cashflow__legend--cost    { background: var(--color-danger); }

.cashflow__bar {
    position: relative;
    height: 18px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-pill);
    overflow: hidden;
}

.cashflow__bar-fill {
    height: 100%;
    border-radius: var(--radius-pill);
    transition: width 600ms cubic-bezier(0.2, 0.9, 0.3, 1.05);
}

.cashflow__bar-fill--revenue {
    background: linear-gradient(90deg, #34d399 0%, var(--color-success) 100%);
}
.cashflow__bar-fill--cost {
    background: linear-gradient(90deg, #f87171 0%, var(--color-danger) 100%);
}

.cashflow__row-amount {
    font-size: var(--text-md);
    font-weight: 600;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* Bottom row of the cashflow card: rates on the left, net amount on
 * the right. The hairline above visually separates this summary row
 * from the bars. */
.cashflow__net-line {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--space-3);
    margin-top: var(--space-3);
    padding-top: var(--space-3);
    border-top: 1px solid var(--color-border);
    transition: color var(--transition);
}
.cashflow__net-line-amount {
    font-size: var(--text-md);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}
.cashflow__net-line[data-state="positive"] .cashflow__net-line-amount { color: var(--color-success); }
.cashflow__net-line[data-state="negative"] .cashflow__net-line-amount { color: var(--color-danger); }
.cashflow__net-line[data-state="neutral"]  .cashflow__net-line-amount { color: var(--color-text-muted); }

.cashflow__rates {
    display: flex;
    gap: var(--space-3);
    align-items: center;
    flex-wrap: wrap;
}
.cashflow__rates strong { font-weight: 600; }
.cashflow__sep          { opacity: 0.5; }

/* ---- Flip counter (split-flap clock) ------------------------------- */

/*
 * Split-flap-styled numeric counter. Used for the smoothed revenue
 * ticker and the lifetime counter on /cashflow.
 *
 * Each digit is a 3D-rotating card with a front and back face. When
 * the digit changes, the card rotates 180° around the X axis and the
 * face that comes forward shows the new digit. Successive flips
 * always rotate forward (+180° per flip).
 *
 * The horizontal mid-line on each card is a CSS pseudo-element
 * suggesting the split-flap aesthetic without actually animating two
 * halves separately — keeps the DOM tiny while looking mechanical.
 */
.flip-counter {
    display: inline-flex;
    align-items: stretch;
    gap: 4px;
    font-family: var(--font-mono);
    font-weight: 700;
    line-height: 1;
}

.flip-counter--lg { font-size: 56px; }
.flip-counter--md { font-size: 28px; }
.flip-counter--sm { font-size: 18px; }

/* Non-digit cells (separators like ' ' or '.'): plain text, no flip.
 * The cell renders a literal nbsp glyph, so a `min-width` smaller
 * than the glyph's natural width has no effect. Use an explicit
 * `width` (and `overflow: hidden`) to actually shrink the slot below
 * the glyph's natural width, giving a tight thousands break. The
 * prefix/suffix and decimal-separator variants below override `width`
 * back to auto so their text isn't clipped. */
.flip-sep {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--color-text-muted);
    padding: 0;
    width: 0.14em;
    overflow: hidden;
    user-select: none;
    /* Match `.flip-digit` so a thousands-separator cell slides in/out
     * in lockstep with the digits added/removed around it. */
    transition:
        opacity      250ms ease-out,
        width        280ms ease-out,
        margin-left  280ms ease-out,
        margin-right 280ms ease-out;
}
/* Prefix/suffix/decimal-separator cells render a visible glyph, so
 * they need to opt out of the tight `width: 0.08em` clamp on the
 * thousands-separator base rule above. */
.flip-sep--prefix,
.flip-sep--suffix,
.flip-sep--decimal {
    width: auto;
    overflow: visible;
}
.flip-sep--prefix,
.flip-sep--suffix {
    font-size: 0.6em;
    color: var(--color-text-muted);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-weight: 600;
}
/* Prefix stays vertically centered; suffix sits on the baseline of
 * the digit row (bottom-aligned) so "kr" reads as a unit tag tucked
 * under the figure rather than a balanced sibling. */
.flip-sep--prefix { align-self: center;   margin-right: 6px; }
.flip-sep--suffix { align-self: flex-end; margin-left:  8px; padding-bottom: 0; }

/* Each digit cell. */
.flip-digit {
    position: relative;
    display: inline-block;
    width: 0.7em;
    height: 1em;
    perspective: 600px;
    /* Width/margin transitions drive the slide-in / slide-out when
     * the integer part grows or shrinks (e.g. the cashflow page's
     * Day → YTD switch). Opacity covers both the per-position fade
     * (`flip-digit--hidden`) and the slide animation. */
    transition:
        opacity      250ms ease-out,
        width        280ms ease-out,
        margin-left  280ms ease-out,
        margin-right 280ms ease-out;
}

/*
 * Per-cell fade for decimal digits whose rotation rate is too high
 * to read. Set by `FlipCounter.setHiddenSuffix(count)` — typically
 * driven by per-position hysteresis on the cashflow page so that
 * öre fades while tens-of-öre stays visible at moderate rates.
 *
 * Hidden cells collapse to width 0 (and absorb the parent's
 * `gap: 4px` via a negative margin) so the suffix — typically "kr"
 * on the cashflow counter — slides left and sits flush against the
 * rightmost visible digit instead of leaving an empty hole.
 */
.flip-digit--hidden,
.flip-sep--hidden {
    width: 0;
    opacity: 0;
    overflow: hidden;
    pointer-events: none;
}
.flip-digit--hidden:not(:first-child),
.flip-sep--hidden:not(:first-child) {
    margin-left: -4px;
}

/*
 * Slide-in / slide-out collapsed state. Applied for one frame on
 * insert (then removed so the cell expands to natural width) and
 * permanently on remove (the JS removes the node once the width
 * transition finishes).
 *
 * The negative `margin-left` absorbs the parent's `gap: 4px` while
 * the cell is collapsed, so disappearing/appearing cells don't leave
 * a 4-pixel ghost gap. `:not(:first-child)` skips the leftmost cell,
 * which has no preceding gap to absorb.
 */
.flip-digit.flip-cell--entering,
.flip-digit.flip-cell--leaving,
.flip-sep.flip-cell--entering,
.flip-sep.flip-cell--leaving {
    width: 0;
    opacity: 0;
    overflow: hidden;
    pointer-events: none;
}
.flip-digit.flip-cell--entering:not(:first-child),
.flip-digit.flip-cell--leaving:not(:first-child),
.flip-sep.flip-cell--entering:not(:first-child),
.flip-sep.flip-cell--leaving:not(:first-child) {
    margin-left: -4px;
}

/*
 * The fastest-spinning decimal digit (rightmost cell) is rendered
 * 30% smaller and bottom-aligned, making it read like a subscript
 * — present and ticking, but visually de-emphasised so the eye
 * isn't drawn to the noise.
 */
.flip-digit--decimal-last {
    font-size: 0.7em;
    align-self: flex-end;
    margin-left: 1px;
}
.flip-sep--decimal {
    /* Inherit the same width/margin/opacity transitions as the base
     * `.flip-sep` rule so the decimal separator collapses smoothly
     * alongside its hidden decimal digits (rather than snapping
     * width while the digits ease out around it). */
    transition:
        opacity      250ms ease-out,
        width        280ms ease-out,
        margin-left  280ms ease-out,
        margin-right 280ms ease-out;
}

/* The 3D-rotating card sitting inside the digit cell. */
.flip-digit__card {
    position: absolute;
    inset: 0;
    transform-style: preserve-3d;
    transition: transform 280ms cubic-bezier(0.55, 0.05, 0.45, 0.95);
}

/* Front and back faces of the card. Neutral-gray palette (no hue)
 * matches a real split-flap leaf photographed under cool office
 * light — pure white digit on a near-black background, with a faint
 * inset highlight reading as the leaf's beveled edge. */
.flip-digit__face {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 2px;
    background: linear-gradient(180deg, #2a2a2a 0%, #1a1a1a 50%, #1d1d1d 50.1%, #0d0d0d 100%);
    color: #ffffff;
    /* 1 px light-gray hairline outlines each leaf so the row reads
     * as a string of distinct cards rather than one continuous black
     * panel. Sits on top of the existing inner highlight + drop
     * shadows. */
    border: 1px solid rgba(220, 220, 220, 0.25);
    box-shadow:
        inset 0 0 0 1px rgba(255, 255, 255, 0.10),
        0 1px 2px rgba(0, 0, 0, 0.4),
        0 4px 10px rgba(0, 0, 0, 0.20);
    backface-visibility: hidden;
    -webkit-backface-visibility: hidden;
    overflow: hidden;
}

/* Horizontal mid-seam — suggests the split-flap "leaf" boundary. */
.flip-digit__face::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 0;
    right: 0;
    height: 1px;
    background: rgba(0, 0, 0, 0.55);
    box-shadow: 0 1px 0 rgba(255, 255, 255, 0.04);
}

.flip-digit__face--back {
    transform: rotateX(180deg);
}

/* Subtle outer "frame" so successive cards look like a row of leaves. */
.flip-digit::before {
    content: '';
    position: absolute;
    inset: -2px;
    border-radius: 8px;
    background: rgba(0, 0, 0, 0.18);
    z-index: -1;
}

/* ---- Toggle switch (sliding two-state) ----------------------------- */

/*
 * Two-position pill switch with a sliding thumb. Uses CSS grid so
 * the two options always have equal width; the thumb is positioned
 * absolutely and animated via translateX(0|100%) keyed off the
 * parent's `data-active` attribute.
 */
.toggle-switch {
    position: relative;
    display: grid;
    grid-template-columns: 1fr 1fr;
    background: var(--color-surface-alt);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    padding: 3px;
    isolation: isolate;
}
.toggle-switch--four { grid-template-columns: repeat(4, 1fr); }

.toggle-switch__option {
    position: relative;
    z-index: 2;
    padding: 5px 14px;
    background: transparent;
    border: 0;
    font: inherit;
    font-size: var(--text-xs);
    font-weight: 700;
    color: var(--color-text-muted);
    cursor: pointer;
    text-align: center;
    user-select: none;
    transition: color 200ms ease-out;
    border-radius: var(--radius-sm);
    min-width: 56px;
}
.toggle-switch__option:hover { color: var(--color-text); }
.toggle-switch__option.is-active {
    color: var(--color-surface);
    cursor: default;
}

.toggle-switch__thumb {
    position: absolute;
    z-index: 1;
    top: 3px;
    bottom: 3px;
    left: 3px;
    width: calc(50% - 3px);
    background: var(--color-text);
    border-radius: var(--radius-sm);
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.18), 0 0 0 0.5px rgba(0, 0, 0, 0.08);
    transition: transform 240ms cubic-bezier(0.4, 0, 0.2, 1);
}
.toggle-switch[data-active="always"] .toggle-switch__thumb {
    transform: translateX(100%);
}

/* Four-position variant (Day / Week / MTD / YTD on the cashflow page).
 * Each segment is a quarter of the inner track; the thumb slides in
 * 100% increments because its width matches one segment exactly. */
.toggle-switch--four .toggle-switch__thumb       { width: calc(25% - 1.5px); }
.toggle-switch--four[data-active="day"]   .toggle-switch__thumb { transform: translateX(0); }
.toggle-switch--four[data-active="week"]  .toggle-switch__thumb { transform: translateX(100%); }
.toggle-switch--four[data-active="month"] .toggle-switch__thumb { transform: translateX(200%); }
.toggle-switch--four[data-active="year"]  .toggle-switch__thumb { transform: translateX(300%); }

/* Five-position variant (Yesterday / Today / Week / Month / Year). */
.toggle-switch--five { grid-template-columns: repeat(5, 1fr); }
.toggle-switch--five .toggle-switch__thumb            { width: calc(20% - 1.2px); }
.toggle-switch--five[data-active="yesterday"] .toggle-switch__thumb { transform: translateX(0); }
.toggle-switch--five[data-active="day"]       .toggle-switch__thumb { transform: translateX(100%); }
.toggle-switch--five[data-active="week"]      .toggle-switch__thumb { transform: translateX(200%); }
.toggle-switch--five[data-active="month"]     .toggle-switch__thumb { transform: translateX(300%); }
.toggle-switch--five[data-active="year"]      .toggle-switch__thumb { transform: translateX(400%); }

/* ---- Cashflow smoothed-counter "sign" frame ------------------------
 *
 * Visual reference: the West Elm / Cifra-style flip clock — a thick
 * brushed-aluminum bezel housing a pure-black panel of split-flap
 * cards. The frame is built without extra markup by painting two
 * background layers on `#cashflow-smoothed-counter`:
 *
 *   1. a black panel clipped to `padding-box`,
 *   2. a brushed-silver gradient clipped to `border-box`.
 *
 * The transparent `border` defines how thick the bezel is; `padding`
 * controls the breathing room between the bezel and the digit cards.
 * Inner box-shadow lays a hairline at the metal/panel boundary so
 * the bezel reads as having a small ledge into the black field.
 */
#cashflow-smoothed-counter {
    background:
        /* inner panel — pure black */
        linear-gradient(#000, #000) padding-box,
        /* brushed-aluminum bezel: light at top, mid in the middle,
         * slightly darker at the bottom so the metal looks lit from
         * above. Stays neutral (no hue) to read as real aluminum. */
        linear-gradient(180deg, #d2d5d8 0%, #b6b9bd 45%, #9fa2a6 100%) border-box;
    border: 14px solid transparent;
    border-radius: 3px;
    padding: 14px 22px 12px;
    box-shadow:
        /* outer drop — the sign hangs off the page */
        0 22px 44px rgba(0, 0, 0, 0.40),
        0 4px 10px  rgba(0, 0, 0, 0.25),
        /* dark hairline ledge at the inside edge of the bezel,
         * suggesting a small recess into the black panel */
        inset 0 0 0 1px rgba(0, 0, 0, 0.55),
        inset 0 1px 0   rgba(0, 0, 0, 0.35);
}

/* ---- Cashflow demo trigger -----------------------------------------
 *
 * The leftmost digit of the smoothed counter is a hidden click target
 * that spawns a demo toast (see `cashflow.js`). The pointer cursor is
 * the only affordance — sibling-combinator trick disables it on every
 * subsequent `.flip-digit`, leaving only the first one interactive.
 */
#cashflow-smoothed-counter .flip-digit            { cursor: pointer; }
#cashflow-smoothed-counter .flip-digit ~ .flip-digit { cursor: default; }

/* ---- Cashflow toasts (stacked bottom-right) -------------------------
 *
 * Visual brief: lean fully into champagne. Warm espresso-black base
 * with a champagne-tinted radial wash, gradient gold border courtesy
 * of a layered background-clip trick, and a one-shot diagonal shimmer
 * that sweeps across on entry — like light catching the rim of a
 * flute. Resting state stays restrained so the dashboard isn't loud.
 *
 * Champagne palette used throughout:
 *   #f6e1b1  pale highlight
 *   #e8c987  primary champagne
 *   #d4af6e  amber champagne
 *   #8a6a3a  burnished shadow
 */

.cashflow-toasts {
    position: fixed;
    right: var(--space-5);
    bottom: var(--space-5);
    z-index: 90;
    display: flex;
    flex-direction: column-reverse;
    gap: var(--space-3);
    pointer-events: none;
}

.cashflow-toast {
    /* Layered background:
     *   1. radial champagne wash from top-right (the "light source")
     *   2. soft top sheen
     *   3. base espresso-black tinted slightly warm
     *
     * The gradient border is achieved with a second background layer
     * via `background-origin/clip` — a single gradient acts as both
     * border and inner fill simultaneously. */
    position: relative;
    background:
        /* inner content fill */
        linear-gradient(#15110a, #15110a) padding-box,
        /* gradient border */
        linear-gradient(
            145deg,
            rgba(246, 225, 177, 0.55)  0%,
            rgba(212, 175, 110, 0.30) 35%,
            rgba(138, 106, 58,  0.18) 70%,
            rgba(246, 225, 177, 0.45) 100%
        ) border-box;
    border: 1px solid transparent;
    border-radius: var(--radius-lg);
    box-shadow:
        0 1px 0 rgba(246, 225, 177, 0.10) inset,    /* top inner highlight */
        0 0 0 1px rgba(0, 0, 0, 0.2),                /* outer hairline */
        0 24px 48px rgba(20, 14, 5, 0.55),           /* deep ambient drop */
        0 2px 8px rgba(20, 14, 5, 0.40);
    padding: var(--space-4) var(--space-4) var(--space-3);
    min-width: 280px;
    max-width: 400px;
    overflow: hidden;
    color: #f0e6d2;
    /* Note: do NOT use `animation-fill-mode: forwards/both` here. With
     * the animation filling forward, its 100% keyframe pins `transform`
     * and `box-shadow` onto the element — and animation values beat the
     * cascade, so the `--leaving` class can't change those properties
     * and no exit transition ever fires. The 100% keyframe values match
     * the resting state declared on `.cashflow-toast`, so falling back
     * to the cascade after the animation looks identical, and lets the
     * `transition` below take over on exit. */
    animation: cashflow-toast-in 420ms cubic-bezier(0.16, 0.84, 0.3, 1);
    transition:
        opacity   500ms cubic-bezier(0.4, 0, 0.2, 1),
        transform 500ms cubic-bezier(0.4, 0, 0.2, 1);
}

/* Champagne radial wash — sits behind the content but above the fill.
 * Pseudo-element so we can layer it without losing the gradient border. */
.cashflow-toast::before {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    background:
        radial-gradient(
            120% 80% at 100% 0%,
            rgba(232, 201, 135, 0.18) 0%,
            rgba(232, 201, 135, 0.06) 35%,
            rgba(232, 201, 135, 0)    70%
        );
}

/* One-shot diagonal shimmer that sweeps across on entry, then is
 * gone. Width is much larger than the toast so the highlight feels
 * like a thin beam rather than a block, and `transform` is animated
 * (compositor-friendly) rather than `background-position`. */
.cashflow-toast::after {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: -60%;
    width: 60%;
    pointer-events: none;
    background: linear-gradient(
        110deg,
        rgba(246, 225, 177, 0)    0%,
        rgba(246, 225, 177, 0)   35%,
        rgba(246, 225, 177, 0.18) 50%,
        rgba(246, 225, 177, 0)   65%,
        rgba(246, 225, 177, 0)  100%
    );
    transform: translateX(0);
    animation: cashflow-toast-shimmer 1100ms ease-out 120ms 1 both;
}

/* Negative toasts: swap the champagne wash for a muted rose so a
 * refund/credit reads as different in tone but stays luxe. */
.cashflow-toast--negative {
    background:
        linear-gradient(#1a1010, #1a1010) padding-box,
        linear-gradient(
            145deg,
            rgba(230, 163, 163, 0.45)  0%,
            rgba(180, 100, 100, 0.25) 35%,
            rgba(110,  60,  60, 0.18) 70%,
            rgba(230, 163, 163, 0.40) 100%
        ) border-box;
}
.cashflow-toast--negative::before {
    background:
        radial-gradient(
            120% 80% at 100% 0%,
            rgba(230, 163, 163, 0.16) 0%,
            rgba(230, 163, 163, 0.05) 35%,
            rgba(230, 163, 163, 0)    70%
        );
}

.cashflow-toast--leaving {
    opacity: 0;
    transform: translateY(8px) scale(0.985);
}

/* Entry: slide in from the right with a champagne aura that decays
 * back to a flat resting shadow. Pairs with the diagonal shimmer
 * sweep on `::after`. */
@keyframes cashflow-toast-in {
    0% {
        opacity: 0;
        transform: translateX(28px);
        box-shadow:
            0 1px 0 rgba(246, 225, 177, 0.10) inset,
            0 0 0 1px rgba(0, 0, 0, 0.2),
            0 0 0 1px rgba(232, 201, 135, 0.0),
            0 24px 48px rgba(20, 14, 5, 0.55);
    }
    40% {
        opacity: 1;
        transform: translateX(0);
        box-shadow:
            0 1px 0 rgba(246, 225, 177, 0.18) inset,
            0 0 0 1px rgba(232, 201, 135, 0.45),
            0 0 32px rgba(232, 201, 135, 0.22),
            0 24px 48px rgba(20, 14, 5, 0.55);
    }
    100% {
        opacity: 1;
        transform: translateX(0);
        box-shadow:
            0 1px 0 rgba(246, 225, 177, 0.10) inset,
            0 0 0 1px rgba(0, 0, 0, 0.2),
            0 24px 48px rgba(20, 14, 5, 0.55),
            0 2px 8px rgba(20, 14, 5, 0.40);
    }
}

@keyframes cashflow-toast-shimmer {
    0%   { transform: translateX(0); }
    100% { transform: translateX(800%); }
}

/* Amount line layout: sign + digits (colored) followed by unit "kr"
 * in a quieter champagne tone, e.g. "+22 kr". Each child owns its
 * own gradient/background-clip so the parent is purely a flex row. */
.cashflow-toast__amount {
    position: relative;        /* sit above ::before/::after layers */
    display: flex;
    align-items: baseline;
    gap: 0.35em;
    font-weight: 700;
    font-size: var(--text-xl);
    letter-spacing: -0.015em;
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1, "lnum" 1;
}

/* Sign hugs the digits — no gap between them. The digits span keeps
 * its own gap from the unit via the parent's `gap`. */
.cashflow-toast__sign {
    margin-right: -0.15em;     /* pull "+" tight against the first digit */
}

/* Sign + digits + "kr" unit: shared metallic green gradient with a
 * soft inner glow. The unit ("kr") is sized down so the number reads
 * first, but it keeps the same color treatment for visual cohesion. */
.cashflow-toast__sign,
.cashflow-toast__digits,
.cashflow-toast__unit {
    background: linear-gradient(180deg, #d8f3c4 0%, #9bd17b 45%, #5fa84a 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    filter: drop-shadow(0 1px 0 rgba(8, 20, 6, 0.6))
            drop-shadow(0 0 10px rgba(155, 209, 123, 0.22));
}

/* "kr" tag: smaller, lower-case, slightly faded so the number stays
 * the focal point. Color/gradient inherited from the rule above. */
.cashflow-toast__unit {
    font-size: 0.62em;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: lowercase;
    opacity: 0.85;
}

/* Negative variant: swap the green for a muted rose across all
 * three spans (sign, digits, unit) so refunds read consistently. */
.cashflow-toast--negative .cashflow-toast__sign,
.cashflow-toast--negative .cashflow-toast__digits,
.cashflow-toast--negative .cashflow-toast__unit {
    background: linear-gradient(180deg, #f5cccc 0%, #e6a3a3 45%, #c98080 100%);
    -webkit-background-clip: text;
    background-clip: text;
    filter: drop-shadow(0 1px 0 rgba(20, 14, 5, 0.6))
            drop-shadow(0 0 10px rgba(230, 163, 163, 0.16));
}

.cashflow-toast__meta {
    position: relative;
    margin-top: 6px;
    font-size: 10.5px;
    text-transform: uppercase;
    letter-spacing: 0.18em;
    color: rgba(232, 201, 135, 0.55);  /* faded champagne */
}

/* Used by cashflow.js to display already-formatted amounts that need
 * default text color (override the muted ancestor in `.cashflow__rates`). */
.text-default { color: var(--color-text); }

/* ---- Modal --------------------------------------------------------- */

/*
 * The modal is always present in `layout.twig` but hidden by default.
 * `[hidden]` does the heavy lifting; we add visibility/opacity
 * transitions for polish and trap pointer events on the overlay.
 */
.modal {
    position: fixed;
    inset: 0;
    z-index: 100;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-5);
    background: rgba(15, 23, 42, 0.5);
    backdrop-filter: blur(2px);
    animation: modal-fade var(--transition);
}
.modal[hidden] { display: none; }

@keyframes modal-fade {
    from { opacity: 0; }
    to   { opacity: 1; }
}

.modal__dialog {
    background: var(--color-surface);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    width: 100%;
    max-width: 640px;
    max-height: calc(100vh - 2 * var(--space-5));
    display: flex;
    flex-direction: column;
    animation: modal-pop 160ms cubic-bezier(0.2, 0.9, 0.3, 1.1);
}

@keyframes modal-pop {
    from { transform: translateY(8px) scale(0.98); opacity: 0; }
    to   { transform: translateY(0)   scale(1);    opacity: 1; }
}

.modal__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--space-4) var(--space-5);
    border-bottom: 1px solid var(--color-border);
}
.modal__title {
    font-size: var(--text-md);
    font-weight: 600;
    margin: 0;
}
.modal__close {
    background: transparent;
    border: 0;
    color: var(--color-text-muted);
    font-size: 22px;
    line-height: 1;
    cursor: pointer;
    padding: 0 var(--space-1);
    border-radius: var(--radius-sm);
    transition: color var(--transition), background var(--transition);
}
.modal__close:hover {
    color: var(--color-text);
    background: var(--color-surface-alt);
}

.modal__body {
    padding: var(--space-5);
    overflow-y: auto;
    flex: 1;
}

.modal__loading {
    text-align: center;
    color: var(--color-text-muted);
    padding: var(--space-6);
}

/* ---- Event-detail layout (inside modal) ---------------------------- */
.event-detail__row {
    display: grid;
    grid-template-columns: 130px 1fr;
    gap: var(--space-3);
    padding: var(--space-2) 0;
    border-bottom: 1px solid var(--color-border);
    font-size: var(--text-base);
}
.event-detail__row:first-child { padding-top: 0; }
.event-detail__row:last-of-type { border-bottom: 0; }

.event-detail__label {
    color: var(--color-text-muted);
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-weight: 600;
    align-self: center;
}

.json-block {
    background: var(--color-surface-alt);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    padding: var(--space-3) var(--space-4);
    margin: var(--space-2) 0 0;
    overflow-x: auto;
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    line-height: 1.5;
    color: var(--color-text);
    white-space: pre;
}

/* Clickable event rows in tables */
.table tbody tr.event-row {
    cursor: pointer;
}

/* =====================================================================
 * 5. Utilities
 * ===================================================================== */

.text-muted    { color: var(--color-text-muted); }
.text-subtle   { color: var(--color-text-subtle); }
.text-danger   { color: var(--color-danger); }
.text-success  { color: var(--color-success); }
.text-sm       { font-size: var(--text-sm); }
.text-xs       { font-size: var(--text-xs); }
.text-right    { text-align: right; }
.text-center   { text-align: center; }

.flex          { display: flex; }
.flex-between  { display: flex; align-items: center; justify-content: space-between; }
.flex-gap-2    { gap: var(--space-2); }
.flex-gap-3    { gap: var(--space-3); }

.mt-2 { margin-top: var(--space-2); }
.mt-3 { margin-top: var(--space-3); }
.mt-4 { margin-top: var(--space-4); }
.mt-5 { margin-top: var(--space-5); }
.mt-6 { margin-top: var(--space-6); }
.mb-0 { margin-bottom: 0; }
.mb-3 { margin-bottom: var(--space-3); }
.mb-4 { margin-bottom: var(--space-4); }

/* ---- Fortnox sync overlay ----------------------------------------
 *
 * Modal-ish full-screen overlay shown over the customer-edit page
 * while the auto-sync AJAX call is in flight. The page is
 * server-rendered with the cached data underneath; the overlay
 * just blocks interaction (and reads "synkar med Fortnox") until
 * the sync resolves. On failure the spinner is replaced with the
 * error message and a Stäng button.
 *
 * Visible-for-at-least-500ms is enforced in JS, not CSS — we
 * always render the overlay; the JS controls when to dismiss it.
 */
.sync-overlay {
    position: fixed;
    inset: 0;
    z-index: 1000;
    background: rgba(15, 15, 15, 0.55);
    backdrop-filter: blur(2px);
    display: flex;
    align-items: center;
    justify-content: center;
    /* Don't fade in — operator's eye is already on the link they
     * clicked; a subtle entrance would just feel laggy. */
}
.sync-overlay__card {
    background: var(--color-surface);
    color: var(--color-text);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    padding: var(--space-6) var(--space-7);
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.25);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-3);
    min-width: 280px;
    max-width: 480px;
}
.sync-overlay__spinner {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    border: 3px solid var(--color-border);
    border-top-color: var(--color-accent, #2563eb);
    animation: sync-overlay-spin 800ms linear infinite;
}
.sync-overlay__message {
    font-size: var(--text-sm);
    text-align: center;
    color: var(--color-text-muted);
}
/* Error variant: hide the spinner, intensify the message colour,
 * surface the dismiss button (which JS un-hides). */
.sync-overlay--error .sync-overlay__spinner { display: none; }
.sync-overlay--error .sync-overlay__message {
    color: var(--color-danger);
    font-weight: 600;
}
@keyframes sync-overlay-spin {
    from { transform: rotate(0deg); }
    to   { transform: rotate(360deg); }
}
