/* Shared kernel component styles -- tokens only, no hard-coded colors, per BLAZOR-REBUILD-SPEC.md's
   DESIGN SYSTEM -- BINDING section (0-DESIGN, 2026-08-04). Built to
   docs/for-claude-code/design/COMPONENT-SPECS.md; that document wins wherever this file and it
   disagree. --st- names throughout (not the deprecated aliases in tokens.css), since this file is
   the migration target, not a consumer that still needs the bridge. */

/* #blazor-error-ui ships hidden by default in every Blazor WASM template's own stylesheet; only
   the framework's boot JS ever removes the hidden state, on a genuine unhandled exception. This
   rule has to exist SOMEWHERE in the app's CSS or the div (a plain block-level <div> with no other
   styling) is visible on every single page load regardless of whether anything actually failed. */
#blazor-error-ui {
    display: none;
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    background: var(--st-danger-subtle);
    color: var(--st-danger-on-subtle);
    padding: var(--st-space-3) var(--st-space-4);
    box-shadow: var(--st-shadow-md);
    z-index: 1000;
}

#blazor-error-ui .dismiss {
    cursor: pointer;
    float: right;
}

/* 0-LOADER (2026-08-05): the boot/reload splash's own styling moved to an inline <style> block in
   index.html's <head> -- a real Playwright run against beta proved it must never depend on an
   external stylesheet's network round-trip (see index.html's own comment on that block for the
   full story: the "render-blocking <link> means always-applied-before-paint" assumption didn't
   hold in practice). Nothing here anymore; kept as a marker so a future reader searching for
   ".loading-progress" finds this note instead of wondering where it went. */

/* ── Shell: top bar + left nav (COMPONENT-SPECS.md §8-9) ──────────────────────────────────────
   The chrome is dark in BOTH themes -- the product's signature, not a dark-mode artefact -- so it
   uses --st-surface-chrome / --st-text-on-chrome and the *-on-chrome state tokens throughout,
   never the theme-flipping --st-surface/--st-border. .st-on-chrome (on .shell-topbar and
   .shell-nav) swaps the global :focus-visible ring for the on-chrome variant (tokens.css). */

.shell {
    display: flex;
    flex-direction: column;
    height: 100vh;
}

.shell-topbar {
    display: flex;
    align-items: center;
    height: var(--st-topbar-h);
    flex: 0 0 var(--st-topbar-h);
    padding: 0 var(--st-space-4);
    background: var(--st-surface-chrome);
    color: var(--st-text-on-chrome);
}

.shell-brand {
    flex: 0 0 calc(var(--st-nav-w) - var(--st-space-4));
    display: flex;
    align-items: center;
    gap: var(--st-space-2);
    font-family: var(--st-font-ui);
    font-weight: 600;
    font-size: var(--st-text-base);
    color: var(--st-text-on-chrome);
    text-decoration: none;
}

.shell-brand-mark {
    flex: 0 0 20px;
    width: 20px;
    height: 20px;
    background-color: currentColor;
    -webkit-mask: var(--menu-icon-url) no-repeat center / contain;
    mask: var(--menu-icon-url) no-repeat center / contain;
}

/* Task 28 (2026-08-04): hamburger that opens the phone nav drawer -- hidden on desktop, where the
   nav is always visible as the 210px sidebar and there's nothing to open. */
.shell-hamburger {
    display: none;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    margin-right: var(--st-space-2);
    border: none;
    border-radius: var(--st-radius-sm);
    background: transparent;
    color: var(--st-text-on-chrome);
    font-size: var(--st-text-lg);
    cursor: pointer;
}

.shell-hamburger:hover {
    background: var(--st-surface-hover-on-chrome);
}

/* COMPONENT-SPECS.md §8 "Page name" -- documented in the topbar anatomy since before ScreenHeader
   existed, never actually populated until 0-REVIEW-9 #4 (2026-08-06). min-width: 0 + overflow: hidden
   on the row (flex: 1 1 auto, the only flexible item between the fixed-width brand and the
   margin-left: auto actions) is what lets the description truncate with ellipsis instead of pushing
   the account name/icons off the edge of the topbar. */
.shell-page-name {
    display: flex;
    align-items: baseline;
    gap: var(--st-space-2);
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    margin-left: 30px;
}

.shell-page-name-title {
    font-family: var(--st-font-ui);
    font-size: var(--st-text-lg);
    font-weight: 400;
    color: var(--st-text-on-chrome);
    white-space: nowrap;
    flex: 0 0 auto;
}

.shell-page-name-sep {
    color: var(--st-text-on-chrome-muted);
    flex: 0 0 auto;
}

.shell-page-name-description {
    font-size: var(--st-text-sm);
    color: var(--st-text-on-chrome-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}

.shell-topbar-actions {
    display: flex;
    align-items: center;
    gap: var(--st-space-1);
    margin-left: auto;
}

.shell-signout-btn {
    font-family: var(--st-font-ui);
    font-size: var(--st-text-xs);
    height: var(--st-control-h-sm);
    border: 1px solid var(--st-border-on-chrome);
    border-radius: var(--st-radius-sm);
    background: transparent;
    color: var(--st-text-on-chrome);
    padding: 0 var(--st-space-2);
    cursor: pointer;
}

.shell-signout-btn:hover {
    background: var(--st-surface-hover-on-chrome);
}

/* 0-REVIEW-9 #7 (2026-08-06), COMPONENT-SPECS.md §8 "Icon button": 35px circle, transparent, glyph
   in --st-text-on-chrome. Generic enough to reuse for any future topbar icon (search/notifications),
   not just the theme control. */
.shell-icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 35px;
    height: 35px;
    border: none;
    border-radius: var(--st-radius-pill);
    background: transparent;
    color: var(--st-text-on-chrome);
    font-size: var(--st-text-base);
    line-height: 1;
    cursor: pointer;
}

.shell-icon-btn:hover {
    background: var(--st-surface-hover-on-chrome);
}

.shell-icon-btn[aria-expanded="true"] {
    background: var(--st-surface-active-on-chrome);
}

.shell-theme-menu-wrap {
    position: relative;
}

/* Same "real, focusable, labeled backdrop button" reasoning as .shell-nav-scrim (COMPONENT-SPECS.md
   §7's scrim precedent) -- click-outside-to-close without a JS interop listener. */
.shell-menu-scrim {
    position: fixed;
    inset: 0;
    z-index: 999;
    border: none;
    padding: 0;
    background: transparent;
    cursor: default;
}

.shell-theme-menu {
    position: absolute;
    top: calc(100% + var(--st-space-1));
    right: 0;
    z-index: 1000;
    display: flex;
    flex-direction: column;
    min-width: 140px;
    padding: var(--st-space-1);
    background: var(--st-surface-overlay);
    border: 1px solid var(--st-border);
    border-radius: var(--st-radius-md);
    box-shadow: var(--st-shadow-md);
}

.shell-theme-menu-item {
    display: flex;
    align-items: center;
    gap: var(--st-space-2);
    height: var(--st-tap-min);
    padding: 0 var(--st-space-2);
    border: none;
    border-radius: var(--st-radius-sm);
    background: transparent;
    color: var(--st-text-primary);
    font-family: var(--st-font-ui);
    font-size: var(--st-text-sm);
    text-align: left;
    cursor: pointer;
}

.shell-theme-menu-item:hover {
    background: var(--st-surface-hover);
}

.shell-theme-menu-item-active {
    color: var(--st-accent-fill);
    font-weight: 600;
}

.shell-user-email {
    font-size: var(--st-text-xs);
    color: var(--st-text-on-chrome-muted);
    max-width: 16rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* NIGHT-QUEUE full-unpark (2026-08-06): persistent "you're in the read-only demo" indicator --
   a filled pill (not plain text like .shell-user-email) so it reads as a status, not just a label. */
.shell-demo-badge {
    font-size: var(--st-text-xs);
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--st-text-on-fill);
    background: var(--st-accent-fill);
    border-radius: var(--st-radius-pill);
    padding: 2px var(--st-space-3);
    white-space: nowrap;
}

/* 0-UNPARK (2026-08-06): Terms/Support -- unconditional topbar utilities, same pattern as the old
   app's TopBar.razor (see AppShell.razor's own comment at the call site for why these aren't in the
   sec_menu-driven sidebar). Plain text links, not the bordered .shell-signout-btn treatment -- two
   more bordered boxes next to the theme select/sign-out would visually compete with the actual
   session-critical controls for a pair of low-frequency utility links. */
.shell-topbar-link {
    font-size: var(--st-text-xs);
    color: var(--st-text-on-chrome-muted);
    text-decoration: none;
    white-space: nowrap;
}

.shell-topbar-link:hover {
    color: var(--st-text-on-chrome);
    text-decoration: underline;
}

.shell-body {
    flex: 1;
    display: flex;
    min-height: 0;
}

.shell-nav {
    width: var(--st-nav-w);
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    /* Bug ledger #23 (2026-08-11): Mazhar's own words, live on beta -- "I asked for gap between menu
       rows. Now it is too much... just increase a little than what was before. It was not too bad."
       "Before" was 0 (rows flush, matching old app's own .Nov-Body -- checked directly, no gap/margin
       between .Nav-Body-Item rows there at all). The full --st-space-1 (4px) step overshot what he
       pictured; there's no smaller token on the scale to fall back to, so this is a deliberate,
       narrow exception below it -- same class of exception this file's own literal-colour rules take
       elsewhere, not a token violation by accident. */
    gap: 2px;
    padding: var(--st-space-3);
    background: var(--st-surface-chrome);
    border-right: 1px solid var(--st-border-on-chrome);
    overflow-y: auto;
    /* Bug ledger #19 (2026-08-10, Mazhar, REOPENED "reported before"): the rail's own collapse/
       expand (the «/» toggle snapping between --st-nav-w and --st-nav-w-collapsed) had no
       transition at all -- width changed in a single frame. This is a DIFFERENT animation from the
       accordion group's own open/close (already 0.3s eased, PEN-2 item 8) -- that one was fine; this
       one was never built. Matches old app's own NavMenu.css exactly (`transition: width 0.3s
       ease` on both its expanded and collapsed rail rules). */
    transition: width 0.3s ease;
}

/* Close button + scrim only ever render/matter on the phone drawer (COMPONENT-SPECS.md §9 Phone) --
   hidden here, made visible inside the 768px breakpoint below. */
.shell-nav-close {
    display: none;
}

.shell-nav-scrim {
    display: none;
}

/* 0-MENU hardening (2026-08-05): MenuIcon.razor's own masking technique, ported verbatim from the
   old app's MenuIcon.razor/shared.css (same sec_menu.IconName key, same static SVG-per-key file) --
   background-color: currentColor means the icon always matches whatever color: the wrapping
   .shell-nav-item/.shell-nav-group/.shell-nav-subitem already sets, no per-theme icon variant
   needed. */
.menu-icon-masked {
    background-color: currentColor;
    -webkit-mask: var(--menu-icon-url) no-repeat center / contain;
    mask: var(--menu-icon-url) no-repeat center / contain;
}

.shell-nav-icon {
    flex: 0 0 1.3rem;
    width: 1.3rem;
    height: 1.3rem;
}

/* 0-REVIEW-9 #6 (2026-08-06): a long name ("Interest Income") wrapped to two lines inside the fixed
   210px rail -- no nav item/sub-item label had nowrap+ellipsis, only the fixed row height clipped
   the wrapped second line. flex: 1 1 auto + min-width: 0 lets the label actually shrink inside its
   flex row (the browser default min-width: auto on a flex child blocks ellipsis otherwise); the
   native title= tooltip every item already carries (AppShell.razor, since 0-MENU hardening) covers
   "genuinely long names" once truncated. */
.shell-nav-trigger-label,
.shell-nav-label {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    text-align: left;
}

/* COMPONENT-SPECS.md §9 "Header"/"Divider" -- the collapse toggle's own row, above everything else
   in the rail. flex: 0 0 48px matches the top bar's own height so the toggle visually aligns with it. */
.shell-nav-header {
    flex: 0 0 48px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--st-space-2);
    padding: 0 0 0 var(--st-space-3);
}

/* 0-MENU-2 #1 (2026-08-06): signed-in account name, prod's own placement (beside the collapse
   arrow). Same shrink-and-ellipsis rule as every other nav label (see .shell-nav-trigger-label's
   own comment) -- a long store name must not push the toggle button out of the rail. Hidden on the
   collapsed rail alongside every other label, .shell-nav-collapsed's existing rule below. */
.shell-nav-account-name {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-family: var(--st-font-ui);
    font-size: var(--st-text-xs);
    font-weight: 600;
    color: var(--st-text-on-chrome);
}

.shell-nav-collapse-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--st-tap-min);
    height: var(--st-tap-min);
    border: none;
    border-radius: var(--st-radius-sm);
    background: transparent;
    color: var(--st-text-on-chrome-muted);
    font-size: var(--st-text-base);
    cursor: pointer;
}

.shell-nav-collapse-toggle:hover {
    background: var(--st-surface-hover-on-chrome);
    color: var(--st-text-on-chrome);
}

.shell-nav-divider {
    height: 1px;
    width: calc(100% - 16px);
    margin: 0 8px 8px;
    background: var(--st-border-on-chrome);
    opacity: 0.5;
}

.shell-nav-item {
    display: flex;
    align-items: center;
    gap: var(--st-space-2);
    height: var(--st-nav-item-h);
    padding: 0 var(--st-space-3);
    border-radius: var(--st-radius-sm);
    color: var(--st-text-on-chrome);
    text-decoration: none;
    font-family: var(--st-font-ui);
    font-size: var(--st-text-xs);
    font-weight: 500;
    transition: background var(--st-duration-fast) var(--st-ease);
}

.shell-nav-item:hover {
    background: var(--st-surface-hover-on-chrome);
}

.shell-nav-item.active {
    background: var(--st-surface-active-on-chrome);
    /* LOGIN-SPEC §9.2: this is a GRAPHIC (the active-page indicator), not a focus ring that
       happens to be active -- --st-accent-on-chrome is the honest name for it now. */
    box-shadow: inset 3px 0 0 var(--st-accent-on-chrome);
}

.shell-nav-item-disabled {
    display: flex;
    align-items: center;
    gap: var(--st-space-2);
    height: var(--st-nav-item-h);
    padding: 0 var(--st-space-3);
    color: var(--st-text-on-chrome-muted);
    opacity: var(--st-disabled-opacity);
    cursor: default;
    font-family: var(--st-font-ui);
    font-size: var(--st-text-xs);
}

.shell-nav-item-disabled small {
    font-size: var(--st-text-xs);
}

/* Item 1 (2026-08-07 pen, 0-429-REFRESH): the "never a dead end" banner -- AppSession retries a 429
   automatically before this ever shows; this is specifically the still-failed case, so it needs to
   read as recoverable (a real button), not as one more wall of red text on top of an already-empty
   nav. */
.shell-nav-session-error {
    display: flex;
    flex-direction: column;
    gap: var(--st-space-2);
    padding: var(--st-space-3);
    margin-bottom: var(--st-space-2);
    border-radius: var(--st-radius-sm);
    background: var(--st-surface-hover-on-chrome);
    color: var(--st-text-on-chrome);
    font-size: var(--st-text-xs);
}

.shell-nav-session-error p {
    margin: 0;
}

.shell-nav-session-error .kernel-btn {
    align-self: flex-start;
}

/* Group + sub-item (COMPONENT-SPECS.md §9) -- 0-MENU (2026-08-05): the spec already documented
   these tokens/metrics, this is the first screen that actually needed them (the nav was a flat list
   until this pass). */
.shell-nav-group {
    width: 100%;
    justify-content: space-between;
    background: transparent;
    border: none;
    cursor: pointer;
}

.shell-nav-chevron {
    display: inline-flex;
    flex-shrink: 0;
    color: var(--st-text-on-chrome-muted);
    transition: transform var(--st-duration-fast) var(--st-ease);
}

.shell-nav-chevron-open {
    transform: rotate(90deg);
}

/* Positioning anchor for the collapsed rail's flyout (COMPONENT-SPECS.md §9 "Collapsed behaviour")
   -- .shell-nav-children becomes position: absolute against THIS element when collapsed, so the
   flyout opens flush against its own trigger, not pinned to the top of the whole rail. */
.shell-nav-group-wrap {
    position: relative;
}

/* PEN-2 item 8 (2026-08-08): old app's own submenu transition is `max-height 0.3s ease-in-out,
   opacity 0.3s ease-in-out` (SellerTally.WebUI.Blazor/wwwroot/Styles/Shared/Layout/NavMenu.css) --
   matched here exactly. 500px is a generous fixed ceiling (not a JS-measured scrollHeight, which
   old app's own CSS doesn't appear to use either) -- comfortably larger than any real group's child
   list, so the transition always runs its full visual duration against actual content height rather
   than snapping to it. Requires the element to always be in the DOM (AppShell.razor no longer wraps
   this in @if) -- a CSS transition can't animate something that doesn't exist yet. */
.shell-nav-children {
    display: flex;
    flex-direction: column;
    /* Bug ledger #23: same rhythm as .shell-nav's own row gap above -- kept in sync deliberately so
       top-level rows and their sub-items read as one consistent list, not two different densities. */
    gap: 2px;
    max-height: 0;
    opacity: 0;
    overflow: hidden;
    transition: max-height 0.3s ease-in-out, opacity 0.3s ease-in-out;
}

.shell-nav-children.shell-nav-children-open {
    max-height: 500px;
    opacity: 1;
}

.shell-nav-subitem {
    display: flex;
    align-items: center;
    gap: var(--st-space-2);
    height: var(--st-nav-subitem-h);
    padding: 0 var(--st-space-3) 0 40px;
    border-radius: var(--st-radius-sm);
    color: var(--st-text-on-chrome);
    text-decoration: none;
    font-family: var(--st-font-ui);
    font-size: var(--st-text-xs);
    font-weight: 500;
    transition: background var(--st-duration-fast) var(--st-ease);
}

.shell-nav-subitem:hover {
    background: var(--st-surface-hover-on-chrome);
}

.shell-nav-subitem.active {
    background: var(--st-surface-active-on-chrome);
    box-shadow: inset 3px 0 0 var(--st-accent-on-chrome);
}

.shell-nav-subitem.shell-nav-item-disabled {
    height: var(--st-nav-subitem-h);
    padding: 0 var(--st-space-3) 0 40px;
}

/* COMPONENT-SPECS.md §9 "Collapsed behaviour" (0-MENU hardening, 2026-08-05): labels hidden (never
   shrunk), icons stay full size and centred, the active item keeps its 3px marker (untouched --
   .shell-nav-item.active above already applies regardless of width), every trigger gets a native
   title= tooltip (already on every item, not just collapsed, see AppShell.razor), and a group opens
   as a flyout instead of expanding inline. .shell-nav-trigger-label is deliberately a DIFFERENT class
   from the sub-items' own .shell-nav-label -- the flyout's contents must keep their labels even while
   the rail itself is collapsed, so the hide-on-collapse rule below must not reach inside
   .shell-nav-children at all. */
.shell-nav-collapsed {
    width: var(--st-nav-w-collapsed);
}

.shell-nav-collapsed .shell-nav-trigger-label {
    display: none;
}

/* W157 (Mazhar, 2026-08-12). The account name is the ONLY thing on screen that says whose books
   you are in, and one login attaches to many accounts -- an accountant misreading it is how a
   transaction lands in the wrong company's ledger. So it must NOT disappear when the rail
   collapses, which is what the old `display: none` here did. It now truncates instead, with the
   full value on hover via the element's own `title`. The header stacks on the collapsed rail
   because 70px cannot hold a name and the toggle side by side. */
.shell-nav-collapsed .shell-nav-header {
    flex: 0 0 auto;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--st-space-1);
    padding: var(--st-space-2) var(--st-space-1);
}

.shell-nav-collapsed .shell-nav-account-name {
    flex: 0 0 auto;
    max-width: 100%;
    font-size: 10px;
    line-height: 1.2;
    text-align: center;
}

.shell-nav-collapsed .shell-nav-chevron {
    display: none;
}

.shell-nav-collapsed > .shell-nav-item,
.shell-nav-collapsed > .shell-nav-item-disabled,
.shell-nav-collapsed .shell-nav-group-wrap > .shell-nav-item {
    justify-content: center;
    padding: 0;
    gap: 0;
}

/* Bug #17 (2026-08-10): `position: fixed`, not `absolute` -- .shell-nav has `overflow-y: auto` for
   its own scrollbar, which clips ANY absolutely-positioned descendant that extends past the 70px
   collapsed rail's own right edge (confirmed live: the flyout opened, class and opacity both
   correct, but was never actually visible on screen). `fixed` escapes ALL ancestor overflow
   clipping by definition, so top/left are no longer expressible as a CSS offset from the trigger --
   AppShell.razor's OnGroupMouseEnter measures the real trigger position via nav-flyout.js and binds
   it as an inline style instead. Max-height-based clipping isn't meaningful here (there's no layout
   height to reclaim by collapsing it), so this overrides the base rule's height/overflow approach
   with a plain fade + slight rise instead, closed by default via opacity/visibility (not max-height)
   so this flyout can also be revealed on HOVER (PEN-2 item 1, AppShell.razor's OnGroupMouseEnter)
   without a click first. */
.shell-nav-collapsed .shell-nav-children {
    position: fixed;
    min-width: 200px;
    background: var(--st-surface-overlay);
    box-shadow: var(--st-shadow-md);
    border-radius: var(--st-radius-md);
    padding: var(--st-space-2);
    z-index: 20;
    max-height: none;
    overflow: visible;
    visibility: hidden;
    opacity: 0;
    transform: translateY(-4px);
    transition: opacity 0.2s ease, transform 0.2s ease, visibility 0s linear 0.2s;
}

.shell-nav-collapsed .shell-nav-children.shell-nav-children-open {
    visibility: visible;
    opacity: 1;
    transform: translateY(0);
    transition: opacity 0.2s ease, transform 0.2s ease, visibility 0s linear 0s;
}

/* Bug #17 follow-up, found while verifying the position:fixed fix live: `.shell-nav-subitem`'s
   *-on-chrome tokens (color, hover/active background) are tuned for sitting on the DARK rail
   background (the inline-accordion case) -- but the collapsed flyout is `--st-surface-overlay`, a
   LIGHT popover card, not chrome. Left unpaired, this rendered literally white text on a white
   background: invisible, not just low-contrast. Re-paired to the same *-primary/*-hover tokens
   every other light-surface popover in this file uses. Left padding also drops the 40px inline-
   accordion indent -- that aligns a sub-item under its own parent icon in the rail; a standalone
   floating card has no parent icon to align under, so it read as a large empty gap instead. */
.shell-nav-collapsed .shell-nav-children .shell-nav-subitem {
    color: var(--st-text-primary);
    padding-left: var(--st-space-3);
}

.shell-nav-collapsed .shell-nav-children .shell-nav-subitem:hover {
    background: var(--st-surface-hover);
}

.shell-nav-collapsed .shell-nav-children .shell-nav-subitem.active {
    background: var(--st-surface-active);
    box-shadow: inset 3px 0 0 var(--st-accent);
}

.shell-main {
    flex: 1;
    min-width: 0;
    overflow: auto;
    padding: var(--st-space-4);
    background: var(--st-surface-app);
}

/* 0-LAYOUT (2026-08-04), rule 4: "Content has a max width -- nothing stretches just because the
   monitor is wide. Full-width is EARNED: data grids and report tables only." Two variants because
   data screens and form screens earn different widths -- a screen applies exactly one to its own
   root element. Neither is used by Dashboard (waits for DASHBOARD-SPEC.md, item 0x) or Login (its
   own spec already encodes correct desktop layout via .lg-shell). */
.st-page-wide {
    max-width: 1400px;
    margin: 0 auto;
}

.st-page-narrow {
    max-width: 800px;
    margin: 0 auto;
}

@media (max-width: 768px) {
    /* Task 28 (2026-08-04): CI's mobile-viewport check measured 89-98px of real horizontal overflow on
       every authenticated screen -- traced to .shell-topbar, not the nav or DataGrid (neither was
       independently overflowing at 375px in direct testing). .shell-brand's flex-basis pins it to the
       desktop NAV's width so the wordmark aligns above the sidebar -- on mobile the nav isn't a sidebar
       anymore, so that fixed 194px is pure waste and the single largest contributor. */
    .shell-brand {
        flex: 0 1 auto;
    }

    /* 0-REVIEW-9 #4 (2026-08-06): "keep phone behavior sane -- description may drop on narrow
       widths." Title stays (it's short and the whole reason the ribbon exists); the description is
       the long, droppable part -- topbar space is already tight against the account name/icons at
       this width. */
    .shell-page-name-sep,
    .shell-page-name-description {
        display: none;
    }

    .shell-page-name {
        margin-left: var(--st-space-2);
    }

    .shell-user-email {
        display: none;
    }

    .shell-hamburger {
        display: inline-flex;
    }

    /* COMPONENT-SPECS.md §9 Phone: "the nav becomes an overlay drawer... position: fixed, width:
       --st-nav-w, height: 100vh, z-index: 1000, transform: translateX(-100%) -> translateX(0)".
       Replaces the earlier always-visible wrapped-row stopgap (shipped same night to kill the
       overflow bug fast) with the real spec behavior -- hidden by default, opened via the
       hamburger, not permanently eating vertical space above every screen's content. */
    .shell-nav {
        position: fixed;
        inset: 0 auto 0 0;
        width: var(--st-nav-w);
        z-index: 1000;
        flex-direction: column;
        flex-wrap: nowrap;
        transform: translateX(-100%);
        transition: transform var(--st-duration-slow) var(--st-ease);
        border-right: 1px solid var(--st-border-on-chrome);
        border-bottom: none;
        box-shadow: var(--st-shadow-md);
    }

    .shell-nav.shell-nav-open {
        transform: translateX(0);
    }

    .shell-nav-close {
        display: flex;
        align-items: center;
        justify-content: center;
        align-self: flex-end;
        width: var(--st-tap-min);
        height: var(--st-tap-min);
        margin-bottom: var(--st-space-2);
        border: none;
        border-radius: var(--st-radius-sm);
        background: transparent;
        color: var(--st-text-on-chrome);
        font-size: var(--st-text-base);
        cursor: pointer;
    }

    .shell-nav-close:hover {
        background: var(--st-surface-hover-on-chrome);
    }

    /* Backdrop: a real <button>, not a bare div, so it's natively focusable/clickable/labeled --
       matches .st-modal-scrim's own reasoning (COMPONENT-SPECS.md §7) applied to the drawer. */
    .shell-nav-scrim {
        display: block;
        position: fixed;
        inset: 0;
        z-index: 999;
        border: none;
        padding: 0;
        background: var(--st-scrim);
        cursor: pointer;
    }

    .shell-nav-item,
    .shell-nav-item-disabled,
    .shell-nav-subitem {
        height: var(--st-tap-min);
    }

    /* COMPONENT-SPECS.md §9: "The drawer never coexists with the collapsed 70px state -- that
       variant is desktop-only." The toggle only makes sense on desktop; hidden here. Belt-and-
       suspenders against the state itself: isNavCollapsed lives in Blazor memory, not tied to
       viewport width, so a user who collapses on desktop and then resizes/rotates down to phone
       width (no page reload, same live SPA session) must not end up with a squashed 70px drawer --
       every collapsed-only visual effect is neutralised here regardless of the class still being
       present in the DOM. */
    .shell-nav-header {
        display: none;
    }

    .shell-nav.shell-nav-collapsed {
        width: var(--st-nav-w);
    }

    .shell-nav-collapsed .shell-nav-trigger-label {
        display: block;
    }

    .shell-nav-collapsed .shell-nav-chevron {
        display: inline-flex;
    }

    .shell-nav-collapsed > .shell-nav-item,
    .shell-nav-collapsed > .shell-nav-item-disabled,
    .shell-nav-collapsed .shell-nav-group-wrap > .shell-nav-item {
        justify-content: flex-start;
        padding: 0 var(--st-space-3);
        gap: var(--st-space-2);
    }

    /* PEN-2 item 8 (2026-08-08): on phone, "collapsed" renders inline (position:static above), not as
       the desktop flyout -- so it needs the desktop flyout rule's opacity/visibility/transform
       CLOSED-state properties overridden back to the base rule's max-height approach, otherwise
       visibility:hidden (unlike display:none) still reserves full layout height while "closed,"
       permanently pushing every nav item below it down regardless of expand state. */
    .shell-nav-collapsed .shell-nav-children {
        position: static;
        background: transparent;
        box-shadow: none;
        padding: 0;
        max-height: 0;
        overflow: hidden;
        visibility: visible;
        transform: none;
        transition: max-height 0.3s ease-in-out, opacity 0.3s ease-in-out;
    }

    .shell-nav-collapsed .shell-nav-children.shell-nav-children-open {
        max-height: 500px;
        opacity: 1;
    }
}

/* Error boundary */

.error-boundary-fallback {
    max-width: 28rem;
    margin: var(--st-space-6) auto;
    padding: var(--st-space-5);
    text-align: center;
    background: var(--st-surface);
    border: 1px solid var(--st-border);
    border-radius: var(--st-radius-md);
    box-shadow: var(--st-shadow-md);
}

.error-boundary-title {
    font-family: var(--st-font-ui);
    font-weight: 600;
    font-size: var(--st-text-lg);
    margin: 0 0 var(--st-space-2);
    color: var(--st-text-primary);
}

.error-boundary-detail {
    color: var(--st-text-secondary);
    margin: 0 0 var(--st-space-4);
}

.error-boundary-reload {
    height: var(--st-control-h);
    padding: 0 var(--st-space-4);
    background: var(--st-accent-fill);
    color: var(--st-text-on-fill);
    border: 1px solid var(--st-accent-fill);
    border-radius: var(--st-radius-sm);
    font-family: var(--st-font-ui);
    font-size: var(--st-text-xs);
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    cursor: pointer;
}

/* ── Loading spinner (PEN-2 item 10, 2026-08-08) ───────────────────────────────────────────────
   Was N shimmering placeholder rows (COMPONENT-SPECS.md §5's original skeleton spec) -- Mazhar
   wants a real wait indicator instead, app-wide. One shared component (LoadingSkeleton.razor,
   ~20 screens already use it), so replacing what it renders here fixes every screen at once,
   nothing per-screen to touch. */

.loading-spinner {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--st-space-3);
    padding: var(--st-space-6) 0;
}

.loading-spinner-ring {
    width: 32px;
    height: 32px;
    border: 3px solid var(--st-border-subtle);
    border-top-color: var(--st-accent);
    border-radius: 50%;
    animation: loading-spinner-spin 0.8s linear infinite;
}

.loading-spinner-text {
    color: var(--st-text-muted);
    font-size: var(--st-text-sm);
}

@keyframes loading-spinner-spin {
    to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
    .loading-spinner-ring {
        animation: none;
    }
}

/* ── Empty state (COMPONENT-SPECS.md §4) ───────────────────────────────────────────────────── */

.empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--st-space-3);
    padding: 40px var(--st-space-5);
    text-align: center;
    background: var(--st-surface);
    border: 1px solid var(--st-border);
    border-radius: var(--st-radius-md);
}

.empty-state-title {
    font-family: var(--st-font-ui);
    font-size: 15px;
    font-weight: 600;
    color: var(--st-text-primary);
    margin: 0;
}

.empty-state-detail {
    font-size: var(--st-text-sm);
    color: var(--st-text-muted);
    max-width: 34ch;
    margin: 0;
}

/* Auth screens (Login etc.) -- pre-boot chrome doesn't apply here (no shell yet), plain surface. */

.auth-card {
    max-width: 24rem;
    margin: var(--st-space-6) auto;
    padding: var(--st-space-5);
    background: var(--st-surface);
    border: 1px solid var(--st-border);
    border-radius: var(--st-radius-md);
    box-shadow: var(--st-shadow-md);
}

.auth-title {
    font-family: var(--st-font-ui);
    font-size: var(--st-text-xl);
    font-weight: 700;
    margin: 0 0 var(--st-space-4);
    color: var(--st-text-primary);
}

.auth-field {
    display: flex;
    flex-direction: column;
    gap: var(--st-space-1);
    margin-bottom: var(--st-space-3);
}

.auth-field label {
    font-family: var(--st-font-ui);
    font-size: var(--st-text-xs);
    font-weight: 500;
    color: var(--st-text-secondary);
}

.auth-field input {
    height: var(--st-control-h);
    font-family: var(--st-font-text);
    font-size: var(--st-text-sm);
    padding: 0 10px;
    border: 1px solid var(--st-border-input);
    border-radius: var(--st-radius-sm);
    background: var(--st-surface);
    color: var(--st-text-primary);
}

.auth-field input:hover {
    border-color: var(--st-text-secondary);
}

.auth-field input:focus-visible {
    outline: 2px solid var(--st-focus-ring);
    outline-offset: 1px;
    border-color: var(--st-accent);
}

.auth-submit {
    width: 100%;
    height: var(--st-control-h);
    background: var(--st-accent-fill);
    color: var(--st-text-on-fill);
    border: 1px solid var(--st-accent-fill);
    border-radius: var(--st-radius-sm);
    font-family: var(--st-font-ui);
    font-size: var(--st-text-xs);
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    cursor: pointer;
    transition: background var(--st-duration-fast) var(--st-ease);
}

.auth-submit:hover:not(:disabled) {
    background: var(--st-accent-fill-hover);
}

.auth-submit:disabled {
    opacity: var(--st-disabled-opacity);
    cursor: not-allowed;
}

.auth-error {
    color: var(--st-danger-on-subtle);
    background: var(--st-danger-subtle);
    box-shadow: inset 3px 0 0 var(--st-danger);
    padding: var(--st-space-3);
    border-radius: var(--st-radius-sm);
    font-size: var(--st-text-sm);
    margin-bottom: var(--st-space-3);
}

/* Charts (Kernel/Components/Charts/*.razor) -- shared here rather than per-component CSS isolation
   because BarChart/LineChart/DonutChart intentionally reuse the same class names for the same
   visual language (one axis style, one label style, everywhere a chart appears). Chart colours are
   categorical only (--st-chart-*) and never carry meaning -- COMPONENT-SPECS.md's own rule. */

.chart-svg {
    width: 100%;
    height: auto;
    max-height: 220px;
}

.chart-axis-line {
    stroke: var(--st-border-strong);
    stroke-width: 1;
}

.chart-axis-line-zero {
    stroke: var(--st-text-muted);
    stroke-dasharray: 3 3;
}

.chart-axis-label {
    fill: var(--st-text-secondary);
    font-size: 9px;
    font-family: var(--st-font-ui);
}

.chart-bar {
    fill: var(--st-accent);
}

.chart-bar-negative {
    fill: var(--st-danger);
}

.chart-line {
    fill: none;
    stroke: var(--st-accent);
    stroke-width: 2;
    stroke-linejoin: round;
    stroke-linecap: round;
}

.chart-area-fill {
    fill: var(--st-accent);
    opacity: 0.18;
}

.chart-point {
    fill: var(--st-surface);
    stroke: var(--st-accent);
    stroke-width: 1.5;
}

.donut-chart-wrap {
    display: flex;
    align-items: center;
    gap: var(--st-space-4);
    flex-wrap: wrap;
}

.chart-donut-svg {
    width: 120px;
    height: 120px;
    max-height: none;
    flex-shrink: 0;
}

.chart-donut-slice {
    stroke: var(--st-surface);
    stroke-width: 1;
}

.chart-legend {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--st-space-1);
    flex: 1;
    min-width: 140px;
}

.chart-legend-item {
    display: flex;
    align-items: center;
    gap: var(--st-space-2);
    font-size: var(--st-text-xs);
    color: var(--st-text-secondary);
}

.chart-legend-swatch {
    width: 10px;
    height: 10px;
    border-radius: var(--st-radius-sm);
    flex-shrink: 0;
}

.chart-legend-label {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.chart-legend-value {
    color: var(--st-text-primary);
    font-family: var(--st-font-num);
    font-variant-numeric: tabular-nums;
}

/* ── Form field (COMPONENT-SPECS.md §2) ────────────────────────────────────────────────────── */

.st-fields {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(272px, 1fr));
    gap: var(--st-space-4) var(--st-space-5);
}

/* 0-VISUAL-PARITY finding 2 (2026-08-05), COMPONENT-SPECS.md §1a "Filter bar": one shared
   implementation instead of TransactionSearch/SubAccountSearch each carrying their own near-
   identical copy. A horizontal row of .kernel-field controls (each already capped to its own
   content-appropriate width -- date/number inputs narrower still, see .kernel-input[type] below)
   that wraps to additional rows before it ever stacks to a single narrow column. */
.kernel-filter-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: end;
    gap: var(--st-space-4);
}

@media (max-width: 767px) {
    .kernel-filter-bar {
        flex-direction: column;
        align-items: stretch;
    }
}

.kernel-field {
    display: flex;
    flex-direction: column;
    gap: var(--st-space-1);
    /* 0-LAYOUT (2026-08-04), rule 2: "Inputs size to content, never to viewport." .kernel-field is a
       flex item everywhere it's used, and flexbox's default align-items: stretch was silently
       stretching every field to its ANCESTOR's full width -- with no width cap anywhere in the
       chain, that ancestor was ultimately the viewport. max-width here (not just on .kernel-input)
       means the label/help text block stays the same width as its control, not just the control. */
    max-width: 400px;
}

.kernel-field > span:first-child {
    font-family: var(--st-font-ui);
    font-size: var(--st-text-xs);
    font-weight: 500;
    color: var(--st-text-secondary);
}

.kernel-field-hint {
    font-family: var(--st-font-ui);
    font-size: var(--st-text-xs);
    color: var(--st-text-muted);
}

.kernel-field-hint a {
    color: var(--st-text-muted);
    text-decoration: underline;
}

.kernel-field-hint a:hover {
    color: var(--st-text-secondary);
}

.kernel-input {
    width: 100%;
    height: var(--st-control-h);
    border: 1px solid var(--st-border-input);
    border-radius: var(--st-radius-sm);
    padding: 0 10px;
    background: var(--st-surface);
    color: var(--st-text-primary);
    font-family: var(--st-font-text);
    font-size: var(--st-text-sm);
    transition: border-color var(--st-duration-fast) var(--st-ease);
}

.kernel-input::placeholder {
    color: var(--st-text-muted);
}

.kernel-input:hover {
    border-color: var(--st-text-secondary);
}

.kernel-input:focus-visible {
    outline: 2px solid var(--st-focus-ring);
    outline-offset: 1px;
    border-color: var(--st-accent);
}

.kernel-input:disabled {
    background: var(--st-surface-alt);
    opacity: var(--st-disabled-opacity);
    cursor: not-allowed;
}

.kernel-input[aria-invalid="true"] {
    border-color: var(--st-danger);
}

textarea.kernel-input {
    height: auto;
    padding: var(--st-space-2) 10px;
}

/* 0-LAYOUT rule 2's own examples: "date fields date-wide, amount fields amount-wide" -- narrower
   than the 400px generic-text cap, sized to what the content actually needs. A .kernel-field
   wrapping one of these overrides its own max-width to match, so the label doesn't sit orphaned
   above a much narrower control. */
.kernel-input[type="date"],
.kernel-field:has(> .kernel-input[type="date"]) {
    max-width: 160px;
}

.kernel-input[type="number"],
.kernel-field:has(> .kernel-input[type="number"]) {
    max-width: 140px;
}

/* COMPONENT-SPECS.md §2 Form field: "--st-font-num right-aligned for amounts" -- every numeric
   control, not just read-only totals. Found unapplied everywhere during the 0-CONSISTENCY sweep
   (2026-08-05): TrnEdit's qty/unit-price/amount cells and Form1099K's monthly inputs were all
   left-aligned in the body text font. */
.kernel-input[type="number"] {
    text-align: right;
    font-family: var(--st-font-num);
    font-variant-numeric: tabular-nums;
}

/* Bug ledger #14 (2026-08-10, Mazhar): "a plain text box is easier to type into" -- the native
   up/down spinner steals horizontal space from a right-aligned amount and invites accidental
   scroll-wheel value changes; removed app-wide (every numeric input, not just .kernel-input --
   the checklist editor's own type="number" cells don't carry that class). Firefox needs the
   standard property, WebKit/Blink (Chrome/Edge/Safari) only respect the pseudo-elements. */
input[type="number"] {
    -moz-appearance: textfield;
}

input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

/* ── Shared amount formatting (COMPONENT-SPECS.md §0 type scale: "every amount" gets
   --st-font-num with tabular-nums so digits align down a column). 0-CONSISTENCY (2026-08-05):
   this rule existed in the spec since day one but no shared class carried it -- each screen that
   rendered a total either applied tabular-nums alone (CoaTree) or nothing at all (TrnEdit,
   TransactionSearch, Form1099K). One class now, applied everywhere a monetary amount renders. */
.st-amount {
    font-family: var(--st-font-num);
    font-variant-numeric: tabular-nums;
}

/* ── Buttons and the placement grammar (COMPONENT-SPECS.md §3) ────────────────────────────────
   Secondary is the unmarked default (.kernel-btn alone) -- matches the existing convention of every
   screen already reaching for it as the base class. */

.kernel-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: var(--st-control-h);
    padding: 0 var(--st-space-4);
    min-width: 132px;
    border: 1px solid var(--st-border-input);
    border-radius: var(--st-radius-sm);
    background: var(--st-surface);
    color: var(--st-text-primary);
    font-family: var(--st-font-ui);
    font-size: var(--st-text-xs);
    font-weight: 500;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    white-space: nowrap;
    cursor: pointer;
    transition: background var(--st-duration-fast) var(--st-ease), border-color var(--st-duration-fast) var(--st-ease);
}

[data-theme="dark"] .kernel-btn {
    background: transparent;
}

.kernel-btn:hover:not(:disabled) {
    background: var(--st-surface-hover);
    border-color: var(--st-text-secondary);
}

.kernel-btn:active:not(:disabled) {
    background: var(--st-surface-active);
}

.kernel-btn-primary {
    border-color: var(--st-accent-fill);
    background: var(--st-accent-fill);
    color: var(--st-text-on-fill);
}

.kernel-btn-primary:hover:not(:disabled) {
    background: var(--st-accent-fill-hover);
    border-color: var(--st-accent-fill-hover);
}

.kernel-btn-primary:active:not(:disabled) {
    background: var(--st-accent-fill-active);
    border-color: var(--st-accent-fill-active);
}

.kernel-btn-destructive {
    border-color: var(--st-danger-fill);
    background: var(--st-danger-fill);
    color: var(--st-text-on-fill);
}

.kernel-btn-destructive:hover:not(:disabled) {
    background: var(--st-danger-fill-hover);
    border-color: var(--st-danger-fill-hover);
}

.kernel-btn:disabled {
    opacity: var(--st-disabled-opacity);
    cursor: not-allowed;
    pointer-events: none;
}

.kernel-btn-sm {
    height: var(--st-control-h-sm);
    min-width: 0;
}

.kernel-btn-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--st-icon-btn);
    height: var(--st-icon-btn);
    min-width: 0;
    padding: 0;
    border: 1px solid var(--st-border);
    border-radius: var(--st-radius-sm);
    background: transparent;
    color: var(--st-text-secondary);
    cursor: pointer;
    transition: background var(--st-duration-fast) var(--st-ease);
}

.kernel-btn-icon:hover:not(:disabled) {
    background: var(--st-surface-hover);
}

/* Placement grammar -- primary last in DOM order (tab order = visual order, never `order:`),
   cancel immediately to its left, constructive secondary actions in a separate left-hand group. */
.st-actionbar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--st-space-4);
}

.st-actionbar__primary {
    display: flex;
    gap: var(--st-space-4);
    margin-left: auto;
}

@media (max-width: 640px) {
    .st-actionbar {
        position: sticky;
        bottom: 0;
        background: var(--st-surface);
        border-top: 1px solid var(--st-border);
        padding: var(--st-space-3);
    }

    .st-actionbar__primary {
        width: 100%;
    }

    .st-actionbar__primary .kernel-btn {
        flex: 1;
        min-width: 0;
        height: var(--st-tap-min);
    }

    .st-actionbar__primary .kernel-btn-primary {
        flex: 1.4;
    }
}

/* ── Banner / alert (COMPONENT-SPECS.md §6) ────────────────────────────────────────────────── */

.kernel-error {
    display: flex;
    align-items: center;
    gap: var(--st-space-3);
    padding: var(--st-space-3);
    border-radius: var(--st-radius-sm);
    background: var(--st-danger-subtle);
    box-shadow: inset 3px 0 0 var(--st-danger);
    color: var(--st-danger-on-subtle);
    font-size: var(--st-text-sm);
}

.kernel-banner {
    display: flex;
    align-items: center;
    gap: var(--st-space-3);
    padding: var(--st-space-3);
    border-radius: var(--st-radius-sm);
    font-size: var(--st-text-sm);
}

.kernel-banner-message {
    flex: 1;
}

.kernel-banner-info {
    background: var(--st-accent-subtle);
    box-shadow: inset 3px 0 0 var(--st-accent);
    color: var(--st-accent-on-subtle);
}

.kernel-banner-success {
    background: var(--st-success-subtle);
    box-shadow: inset 3px 0 0 var(--st-success);
    color: var(--st-success-on-subtle);
}

.kernel-banner-warning {
    background: var(--st-warning-subtle);
    box-shadow: inset 3px 0 0 var(--st-warning);
    color: var(--st-warning-on-subtle);
}

.kernel-banner-danger {
    background: var(--st-danger-subtle);
    box-shadow: inset 3px 0 0 var(--st-danger);
    color: var(--st-danger-on-subtle);
}

/* A labeled card grouping a few related fields -- introduced for AccountSettings, promoted here
   once Form1099K needed the identical pattern: Blazor's CSS isolation means a component-scoped
   class never applies outside its own .razor file, so a second screen needing the same look
   belongs in the shared kernel stylesheet, not copy-pasted into a second .razor.css. */
.kernel-card {
    display: flex;
    flex-direction: column;
    gap: var(--st-space-2);
    padding: var(--st-space-4);
    border: 1px solid var(--st-border);
    border-radius: var(--st-radius-md);
    background: var(--st-surface);
}

.kernel-card-title {
    margin: 0 0 var(--st-space-1);
    font-family: var(--st-font-ui);
    font-size: var(--st-text-base);
    font-weight: 600;
    color: var(--st-text-primary);
}

/* TreeView (Kernel/Components/TreeView.razor) -- generic expand/collapse tree. */

.tree-node-row {
    display: flex;
    align-items: center;
    width: 100%;
    padding: var(--st-space-2);
    border: none;
    background: none;
    color: var(--st-text-primary);
    font-family: var(--st-font-text);
    font-size: var(--st-text-sm);
    text-align: left;
    cursor: default;
    border-bottom: 1px solid var(--st-border);
}

.tree-node-row.tree-clickable {
    cursor: pointer;
}

.tree-node-row.tree-clickable:hover {
    background: var(--st-surface-hover);
}

.tree-node-label {
    display: flex;
    align-items: center;
    gap: var(--st-space-2);
    flex: 1;
    min-width: 0;
}

.tree-toggle,
.tree-toggle-spacer {
    width: 0.75rem;
    flex-shrink: 0;
}

.tree-toggle::before {
    content: "▸";
    color: var(--st-text-secondary);
    display: inline-block;
    transition: transform var(--st-duration-fast) var(--st-ease);
}

.tree-toggle-expanded::before {
    transform: rotate(90deg);
}

.tree-children {
    margin-left: var(--st-space-4);
}

/* Pager (Kernel/Components/Pager.razor) -- secondary buttons at control-h-sm, per grid footer §1. */

.kernel-pager {
    display: flex;
    align-items: center;
    gap: var(--st-space-2);
    font-size: var(--st-text-xs);
    color: var(--st-text-muted);
    font-variant-numeric: tabular-nums;
}

.kernel-pager button {
    height: var(--st-control-h-sm);
    border: 1px solid var(--st-border-input);
    background: var(--st-surface);
    color: var(--st-text-primary);
    border-radius: var(--st-radius-sm);
    padding: 0 var(--st-space-3);
    font-family: var(--st-font-ui);
    font-size: var(--st-text-xs);
    cursor: pointer;
}

.kernel-pager button:hover:not(:disabled) {
    background: var(--st-surface-hover);
}

.kernel-pager button:disabled {
    opacity: var(--st-disabled-opacity);
    cursor: not-allowed;
}

/* CheckListEditor (Kernel/Components/CheckListEditor.razor) -- shared by the recipe and vendor
   pickers on SubAccountEdit. */

.checklist-editor {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--st-text-sm);
}

.checklist-editor th {
    text-align: left;
    color: var(--st-text-secondary);
    font-family: var(--st-font-ui);
    font-weight: 600;
    padding: var(--st-space-2);
    border-bottom: 1px solid var(--st-border-strong);
}

.checklist-editor td {
    padding: var(--st-space-2);
    border-bottom: 1px solid var(--st-border);
    color: var(--st-text-primary);
    vertical-align: middle;
}

.checklist-editor-check-col {
    width: 2.5rem;
}

.checklist-editor input[type="number"],
.checklist-editor input[type="text"] {
    width: 100%;
    max-width: 10rem;
    height: var(--st-control-h-sm);
    border: 1px solid var(--st-border-input);
    border-radius: var(--st-radius-sm);
    padding: 0 var(--st-space-2);
    background: var(--st-surface);
    color: var(--st-text-primary);
    font-family: var(--st-font-text);
}

.checklist-editor input:disabled {
    opacity: var(--st-disabled-opacity);
    background: var(--st-surface-alt);
}

.checklist-editor-empty {
    color: var(--st-text-muted);
    font-size: var(--st-text-sm);
}

/* ── Modal (COMPONENT-SPECS.md §7, Kernel/Components/Modal.razor) ─────────────────────────────── */

.st-modal-scrim {
    position: fixed;
    inset: 0;
    background: var(--st-scrim);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
}

.st-modal {
    background: var(--st-surface-overlay);
    border: 1px solid var(--st-border);
    border-radius: var(--st-radius-md);
    box-shadow: var(--st-shadow-md);
    display: flex;
    flex-direction: column;
    max-height: 90vh;
    width: 100%;
}

.st-modal-sm { max-width: 440px; }
.st-modal-md { max-width: 720px; }
.st-modal-lg { max-width: 960px; }

.st-modal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--st-space-4) var(--st-space-5);
    border-bottom: 1px solid var(--st-border);
    flex: 0 0 auto;
}

.st-modal-title {
    margin: 0;
    font-family: var(--st-font-ui);
    font-size: var(--st-text-base);
    font-weight: 600;
    color: var(--st-text-primary);
}

.st-modal-body {
    padding: var(--st-space-5);
    font-size: var(--st-text-sm);
    color: var(--st-text-secondary);
    max-height: 70vh;
    overflow-y: auto;
    flex: 1 1 auto;
}

.st-modal-footer {
    display: flex;
    justify-content: flex-end;
    gap: var(--st-space-4);
    padding: var(--st-space-4) var(--st-space-5);
    background: var(--st-surface-alt);
    border-top: 1px solid var(--st-border);
    flex: 0 0 auto;
}

@media (max-width: 640px) {
    .st-modal {
        max-width: 100%;
        width: 100%;
        height: 100%;
        max-height: 100%;
        border-radius: 0;
    }

    .st-modal-footer {
        position: sticky;
        bottom: 0;
    }

    .st-modal-footer .kernel-btn {
        flex: 1;
        min-width: 0;
    }
}

/* NIGHT-QUEUE full-unpark ruling (2026-08-06): Billing.razor's plan/usage summary. */
.billing-summary {
    display: flex;
    flex-direction: column;
    gap: var(--st-space-2);
    border: 1px solid var(--st-border);
    border-radius: var(--st-radius-md);
    padding: var(--st-space-4);
}

.billing-summary__row {
    display: flex;
    justify-content: space-between;
    gap: var(--st-space-4);
    font-size: var(--st-text-sm);
}

.billing-summary__label {
    color: var(--st-text-secondary);
}

.billing-summary__value {
    color: var(--st-text-primary);
    font-weight: 600;
}

.billing-recent__title {
    margin: 0;
    font-size: var(--st-text-sm);
    font-weight: 600;
    color: var(--st-text-primary);
}

.billing-recent__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--st-space-1);
}

.billing-recent__list li {
    display: flex;
    justify-content: space-between;
    gap: var(--st-space-4);
    font-size: var(--st-text-sm);
    color: var(--st-text-secondary);
    padding: var(--st-space-1) 0;
    border-bottom: 1px solid var(--st-border);
}

/* NIGHT-QUEUE full-unpark ruling (2026-08-06): SuperAdmin trio (Analytics/SupportAccess/Helpdesk). */
.analytics-section-title {
    margin: var(--st-space-5) 0 0;
    font-size: var(--st-text-sm);
    font-weight: 600;
    color: var(--st-text-primary);
}

.supportaccess-account-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--st-space-2);
}

.supportaccess-account-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--st-space-4);
    width: 100%;
    border: 1px solid var(--st-border);
    border-radius: var(--st-radius-md);
    padding: var(--st-space-3);
    background: var(--st-surface);
    color: var(--st-text-primary);
    font-size: var(--st-text-sm);
    text-align: left;
    cursor: pointer;
}

.supportaccess-account-row:hover {
    border-color: var(--st-accent);
}

/* Persistent banner AppShell shows while browsing a borrowed customer session -- same visual
   family as .kernel-banner-warning (impersonation is a genuinely higher-stakes state than the
   Demo badge, worth a stronger color), but full-width under the topbar rather than inline with it,
   since it needs room for the account name + the End button without cramping the topbar itself. */
.shell-support-access-banner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--st-space-4);
    background: var(--st-warning-subtle);
    color: var(--st-warning-on-subtle);
    padding: var(--st-space-2) var(--st-space-5);
    font-size: var(--st-text-sm);
    border-bottom: 1px solid var(--st-warning);
}

.shell-support-access-banner .kernel-btn {
    flex-shrink: 0;
}
