/* AD.Ui — the family's base (../.claude/DESIGN-SYSTEM.md): what every Atlas Digital product draws the same way. The
   reset, the one focus ring, the one reduced-motion rule, the layout primitives, the signature stroke and the
   accessibility helpers. Every value comes from the app's tokens (TokenContract): its colours, its type, its scale and
   its root size stay its own. A primitive declares its knob with its default; a component re-declares the knob.
   Link it before the app's own sheets. */

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

/* Native defaults: no font inflation on rotation, no grey flash under the finger. The app's root size is its own. */
html {
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
    -webkit-tap-highlight-color: transparent;
}

/* No double-tap-to-zoom wait on anything tappable: the tap fires at once, like a native control. */
a, button, input, select, textarea, label, summary, [role="button"] {
    touch-action: manipulation;
}

/* A control takes the page's type, not the browser's 13 px system font. */
button, input, select, textarea {
    font: inherit;
}

/* The selection carries the brand, translucent so the selected text stays readable in both themes. */
::selection {
    background: color-mix(in oklab, var(--clr-accent) var(--selection-mix, 30%), transparent);
}

/* The type pair stops at 600 (DESIGN-SYSTEM §1): a bold of 700 is one the browser fakes. */
b, strong {
    font-weight: 600;
}

/* Every figure in tabular figures. */
time, .num {
    font-variant-numeric: tabular-nums;
}

/* ── The type helpers (DESIGN-SYSTEM §1): a heading's lines balanced, a paragraph's last line never one word, a long
   word (an IBAN, an address) broken rather than pushing the page sideways. ── */
h1, h2, h3 {
    text-wrap: balance;
}

p, li {
    text-wrap: pretty;
    overflow-wrap: break-word;
}

/* An amount or a count a page is about: the serif, in tabular figures, at 600; its size is its context's. */
.figure {
    font-family: var(--font-serif);
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}

/* The sentence under a page's title. */
.lead {
    color: var(--clr-text-secondary);
    font-size: var(--lead-size, var(--fs-md));
}

/* What is said in passing: the muted ink, whatever a component paints (a muted table cell stays muted), hence
   !important. Its size stays its element's: an app that wants it smaller says so in its own rule. */
.muted {
    color: var(--clr-text-muted) !important;
}

/* ── The one focus ring: the app's focus colour, outside the element, never removed without a replacement. ───── */
a:focus-visible,
button:focus-visible,
summary:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[role="button"]:focus-visible,
[tabindex]:not([tabindex="-1"]):focus-visible {
    outline: var(--ring-width) solid var(--clr-focus);
    outline-offset: 2px;
}

/* What the code focuses for a screen reader (a page's title on arrival, a skip target, a deep-linked card) is no
   control: the ring is for what a person can operate. */
[tabindex="-1"]:focus {
    outline: none;
}

/* ── The one reduced-motion rule. ────────────────────────────────────────────────────────────────────────────── */
/* The delays go too: a staggered entrance kept its delays, and its items stayed invisible, then appeared in a row
   (Parallax, 2026-10-26). */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.001ms !important;
        animation-delay: 0s !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.001ms !important;
        transition-delay: 0s !important;
        scroll-behavior: auto !important;
    }
}

/* ── Layout primitives: responsive without a breakpoint. ─────────────────────────────────────────────────────── */
.stack {
    display: flex;
    flex-direction: column;
    gap: var(--stack-gap, var(--space-16));
}

/* A row that wraps: what does not fit moves to the next line, never past the edge. */
.cluster {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--cluster-gap, var(--space-8));
}

/* As many columns as fit, each at least --grid-min, never wider than the row. */
.grid-auto {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--grid-min, 18rem)), 1fr));
    gap: var(--grid-gap, var(--space-16));
}

/* ── The signature stroke: a short bar slanted like a rising arrow, drawn before the label that carries the class
   (a section's label, a page title, an eyebrow). The accent on the ground, the highlight on the frame (the app
   re-declares --clr-signature there). An app draws it in its own proportions with --signature-width,
   --signature-height and --signature-gap. ────────────────────────────────────────────────────────────────────── */
.signature::before {
    content: "";
    display: inline-block;
    flex: none;
    inline-size: var(--signature-width, 0.875rem);
    block-size: var(--signature-height, var(--ring-width));
    margin-inline-end: var(--signature-gap, var(--space-8));
    vertical-align: middle;
    border-radius: var(--radius-xs);
    background: var(--clr-signature);
    transform: skewX(-30deg);
}

/* ── The chip: a choice among a few options, a lone toggle or an action, one look for all (Chips draws the groups).
   Pressed is aria-pressed, never a class: the state assistive technology reads is the state drawn. An app sizes its
   chips with --chip-height, --chip-font-size and --chip-color. ──────────────────────────────────────────────── */
.chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-8);
}

.chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-6);
    min-height: var(--chip-height, var(--target));
    padding: 0 var(--space-16);
    border: 1px solid var(--clr-field-border);
    border-radius: var(--radius-pill);
    background: var(--clr-surface);
    color: var(--chip-color, var(--clr-text));
    font-size: var(--chip-font-size, inherit);
    font-weight: 500;
    white-space: nowrap;
    cursor: pointer;
    transition: border-color var(--dur-2) var(--ease-out), background-color var(--dur-2) var(--ease-out);
}

.chip:active {
    transform: scale(0.98);
}

.chip[aria-pressed="true"] {
    border-color: var(--clr-accent-fill);
    background: var(--clr-accent-fill);
    color: var(--clr-text-on-accent);
}

.chip-count {
    font-size: var(--fs-sm);
}

@media (hover: hover) {
    .chip:hover {
        border-color: var(--clr-text-muted);
    }

    .chip[aria-pressed="true"]:hover {
        border-color: var(--clr-accent-fill-hover);
        background: var(--clr-accent-fill-hover);
    }
}

/* ── The notice (DESIGN-SYSTEM §6): a full-width alert, never tinted. A 4 px rule on its left edge says the role
   (neutral by default), a pill the word, the message stays neutral, the action sits at the far end; it wraps on its
   message's minimum, without a breakpoint. Notice draws it, a hand-written <p class="notice"> looks the same. An app
   sets its ground with --notice-background. ─────────────────────────────────────────────────────────────────── */
.notice {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-8) var(--space-12);
    padding: var(--space-12) var(--space-16);
    border-left: 4px solid var(--clr-border-strong);
    background: var(--notice-background, var(--clr-surface));
    color: var(--clr-text-secondary);
}

.notice-success {
    border-left-color: var(--clr-success);
}

.notice-warning {
    border-left-color: var(--clr-warning);
}

.notice-danger {
    border-left-color: var(--clr-danger);
}

.notice-info {
    border-left-color: var(--clr-info);
}

.notice-body {
    flex: 1;
    min-width: min(16rem, 100%);
}

.notice-action {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-8);
    margin-inline-start: auto;
}

/* Blazor's own error bar, in the notice's anatomy: the surface, the danger's rule (on top, as it stands at the foot),
   above every layer but the skip link, clear of the home indicator. Blazor shows it when the runtime fails. */
#blazor-error-ui {
    display: none;
    position: fixed;
    inset: auto 0 0;
    z-index: var(--layer-fatal);
    padding: var(--space-12) var(--space-16) calc(var(--space-12) + env(safe-area-inset-bottom));
    border-top: 4px solid var(--clr-danger);
    background: var(--clr-surface);
    color: var(--clr-text);
    box-shadow: var(--shadow-lift);
}

#blazor-error-ui .reload {
    margin-inline-start: var(--space-12);
    color: var(--clr-accent);
}

#blazor-error-ui .dismiss {
    position: absolute;
    top: var(--space-12);
    right: var(--space-16);
    cursor: pointer;
}

/* ── The disclosure: a details whose summary is a control — a whole target, the accent's word, no marker. ── */
.disclosure > summary {
    display: flex;
    align-items: center;
    gap: var(--space-8);
    min-height: var(--target);
    list-style: none;
    color: var(--disclosure-color, var(--clr-accent));
    font-weight: 500;
    cursor: pointer;
}

.disclosure > summary::-webkit-details-marker {
    display: none;
}

.disclosure[open] > summary {
    margin-bottom: var(--disclosure-gap, var(--space-12));
}

/* ── The field: its label, its hint, the control, its refusal, in one column (Field draws it). An app sizes the label
   and the hint with --field-label-size, --field-label-color and --field-hint-size. ───────────────────────────── */
.field {
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
}

.field-label {
    color: var(--field-label-color, inherit);
    font-size: var(--field-label-size, inherit);
    font-weight: 500;
}

.field-hint {
    color: var(--clr-text-muted);
    font-size: var(--field-hint-size, var(--fs-sm));
}

/* ── The tab bar: the phone's bottom navigation (TabBar draws it), hidden until its width — under 900 px
   (.tabbar-tablet) or under 640 px (.tabbar-phone). While it shows, --tabbar-height is the room it takes, 0 otherwise:
   a page pads its foot with it, the toasts sit above it, and a page without the bar (a sign-in) keeps 0. An app sets its
   look with --tabbar-size, --tabbar-background, --tabbar-color, --tabbar-active-color, --tabbar-layer,
   --tabbar-icon-size, --tabbar-icon-stroke and --tabbar-label-size. ──────────────────────────────────────────── */
:root {
    --tabbar-height: 0px;
    /* The room under a page's last line, a toast or a sticky bar: the tab bar and the phone's home indicator. */
    --tabbar-room: calc(var(--tabbar-height) + env(safe-area-inset-bottom));
}

.tabbar {
    position: fixed;
    inset: auto 0 0;
    z-index: var(--tabbar-layer, var(--layer-tabbar));
    display: none;
    grid-auto-columns: minmax(0, 1fr);
    grid-auto-flow: column;
    height: var(--tabbar-room);
    padding: 0 env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
    background: var(--tabbar-background, var(--clr-surface));
}

@media (max-width: 900px) {
    :root:has(.tabbar-tablet) {
        --tabbar-height: var(--tabbar-size, 3.5rem);
    }

    .tabbar-tablet {
        display: grid;
    }
}

@media (max-width: 640px) {
    :root:has(.tabbar-phone) {
        --tabbar-height: var(--tabbar-size, 3.5rem);
    }

    .tabbar-phone {
        display: grid;
    }
}

/* A place, or the app's last cell (a button): an icon over its word, a 44 px target, the press answered by the icon. */
.tabbar-item {
    --icon-size: var(--tabbar-icon-size, 1.25rem);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-width: 0;
    min-height: var(--target);
    padding: 0;
    border: none;
    background: none;
    color: var(--tabbar-color, var(--clr-text-muted));
    font: inherit;
    text-decoration: none;
    cursor: pointer;
    -webkit-user-select: none;
    user-select: none;
    transition: color var(--dur-2) var(--ease-out);
}

.tabbar-item .icon {
    stroke-width: var(--tabbar-icon-stroke, 2);
    transition: transform var(--dur-2) var(--ease-out);
}

.tabbar-item:active .icon {
    transform: scale(0.9);
}

/* The current place, and the last cell while what it opens is open. */
.tabbar-item[aria-current="page"],
.tabbar-item[aria-expanded="true"] {
    color: var(--tabbar-active-color, var(--clr-accent));
}

.tabbar-label {
    max-width: 100%;
    overflow: hidden;
    font-size: var(--tabbar-label-size, var(--fs-xs));
    font-weight: 500;
    line-height: 1.2;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ── The layers: one scale for every z-index, so a popover never hides under a bar nor a toast under a dialog. The
   package reads them (its toasts, its skip link, its tab bar); an app stacks its own bars, sheets and dialogs on them
   and re-declares a step only to differ. ──────────────────────────────────────────────────────────────────────── */
:root {
    --layer-raised: 1;
    --layer-bar: 100;
    --layer-tabbar: 110;
    --layer-popover: 200;
    --layer-scrim: 900;
    --layer-sheet: 950;
    --layer-dialog: 1000;
    --layer-toast: 1500;
    --layer-boot: 2000;
    --layer-fatal: 10000;
    --layer-tour: 20000;
    --layer-skip: 30000;
}

/* ── The button (DESIGN-SYSTEM §6–§7): a pill as tall as a touch target, one primary per view. The bare .btn is the
   outlined, secondary action; .btn-primary the filled one; .btn-quiet a borderless action inside a card; .btn-danger
   the outlined loss (never a red slab). A button that cannot act now is aria-disabled: dimmed, still focusable, so a
   tap can say why (ENGINEERING §5); aria-busy while it works. The press answers with :active everywhere, the lift
   only where a pointer hovers. An app tunes it with --btn-radius, --btn-padding-y, --btn-padding-x,
   --btn-font-size, --btn-press, --btn-primary-image, --btn-primary-shadow, --btn-primary-shadow-hover,
   --btn-quiet-color, --btn-quiet-padding-x, --btn-danger-border, --btn-sm-padding-x and --btn-disabled-opacity. ── */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-8);
    min-height: var(--target);
    padding: var(--btn-padding-y, 0) var(--btn-padding-x, var(--space-20));
    border: 1px solid var(--clr-field-border);
    border-radius: var(--btn-radius, var(--radius-pill));
    background: transparent;
    color: var(--clr-text);
    font-size: var(--btn-font-size, inherit);
    font-weight: 500;
    text-decoration: none;
    cursor: pointer;
    transition: background-color var(--dur-2) var(--ease-out), border-color var(--dur-2) var(--ease-out),
        box-shadow var(--dur-2) var(--ease-out), transform var(--dur-1) var(--ease-spring);
}

.btn:active:not(:disabled):not([aria-disabled="true"]) {
    transform: scale(var(--btn-press, 0.96));
}

.btn-primary {
    border-color: var(--clr-accent-fill);
    background-color: var(--clr-accent-fill);
    background-image: var(--btn-primary-image, none);
    color: var(--clr-text-on-accent);
    box-shadow: var(--btn-primary-shadow, none);
}

.btn-secondary {
    background-color: var(--clr-surface);
}

.btn-quiet {
    border-color: transparent;
    padding-inline: var(--btn-quiet-padding-x, var(--space-12));
    color: var(--btn-quiet-color, var(--clr-accent));
}

.btn-danger {
    border-color: var(--btn-danger-border, var(--clr-danger));
    background-color: var(--clr-surface);
    color: var(--clr-danger);
}

.btn-sm {
    padding-inline: var(--btn-sm-padding-x, var(--space-12));
    font-size: var(--fs-sm);
}

/* A toggle drawn as a button: pressed is aria-pressed, the accent's word and edge. */
.btn[aria-pressed="true"] {
    border-color: var(--clr-accent);
    color: var(--clr-accent);
}

.btn:disabled,
.btn[aria-disabled="true"] {
    opacity: var(--btn-disabled-opacity, 0.55);
    box-shadow: none;
    cursor: not-allowed;
}

.btn[aria-busy="true"] {
    cursor: progress;
}

/* The spinner of a busy button, in its text's colour. */
.btn-spinner {
    flex: none;
    inline-size: 1em;
    block-size: 1em;
    border: 2px solid color-mix(in oklab, currentColor 35%, transparent);
    border-top-color: currentColor;
    border-radius: var(--radius-pill);
    animation: btn-spin 0.7s linear infinite;
}

@keyframes btn-spin {
    to {
        transform: rotate(1turn);
    }
}

@media (hover: hover) {
    .btn:hover:not(:disabled):not([aria-disabled="true"]) {
        border-color: var(--clr-text-muted);
    }

    .btn-primary:hover:not(:disabled):not([aria-disabled="true"]) {
        border-color: var(--clr-accent-fill-hover);
        background-color: var(--clr-accent-fill-hover);
        box-shadow: var(--btn-primary-shadow-hover, none);
        transform: translateY(-1px);
    }

    .btn-quiet:hover:not(:disabled):not([aria-disabled="true"]) {
        border-color: transparent;
        background-color: var(--clr-band);
    }

    .btn-danger:hover:not(:disabled):not([aria-disabled="true"]) {
        border-color: var(--clr-danger);
    }
}

/* ── The field's control (the default FieldClass): as tall as a target, 16 px under 900 px so iOS does not zoom,
   the one focus ring, a refusal in the danger's edge. A check is a 44 px row, its box in the accent. An app tunes it
   with --input-padding, --input-font-size and --input-radius. ──────────────────────────────────────────────── */
.input {
    width: 100%;
    min-height: var(--target);
    padding: var(--input-padding, var(--space-8) var(--space-12));
    border: 1px solid var(--clr-field-border);
    border-radius: var(--input-radius, var(--radius-md));
    background: var(--clr-surface);
    color: var(--clr-text);
    font-size: var(--input-font-size, var(--fs-base));
}

.input::placeholder {
    color: var(--clr-text-muted);
}

textarea.input {
    min-height: 8rem;
    resize: vertical;
    line-height: 1.5;
}

.input[aria-invalid="true"] {
    border-color: var(--clr-danger);
}

@media (max-width: 900px) {
    .input {
        font-size: max(16px, var(--input-font-size, var(--fs-base)));
    }
}

input[type="checkbox"], input[type="radio"], input[type="range"] {
    accent-color: var(--clr-accent-fill);
}

.check {
    display: flex;
    align-items: flex-start;
    gap: var(--space-12);
    min-height: var(--target);
    padding-top: var(--space-8);
}

.check input {
    flex: none;
    width: 1.25rem;
    height: 1.25rem;
    margin-top: var(--space-2);
}

/* ── The card (DESIGN-SYSTEM §6): ONE surface, never tinted. A card that carries a state takes a 3 px rule on its
   top edge (.card-state, neutral; -success, -warning, -danger, -info). A card the pointer opens (.card-link) is opened
   by the link of its title stretched over it (.stretched), its other controls kept above; only such a card lifts.
   Flat on a phone (ENGINEERING §6). An app tunes it with --card-padding, --card-padding-phone, --card-shadow and
   --card-image. ─────────────────────────────────────────────────────────────────────────────────────────────── */
.card {
    padding: var(--card-padding, var(--space-20));
    border: 1px solid var(--clr-border);
    border-radius: var(--radius-lg);
    background-color: var(--clr-surface);
    background-image: var(--card-image, none);
    box-shadow: var(--card-shadow, var(--shadow-rest));
}

.card-state {
    border-top: 3px solid var(--clr-border-strong);
}

.card-state-success {
    border-top: 3px solid var(--clr-success);
}

.card-state-warning {
    border-top: 3px solid var(--clr-warning);
}

.card-state-danger {
    border-top: 3px solid var(--clr-danger);
}

.card-state-info {
    border-top: 3px solid var(--clr-info);
}

.card-link {
    position: relative;
    transition: transform var(--dur-2) var(--ease-out), box-shadow var(--dur-2) var(--ease-out),
        border-color var(--dur-2) var(--ease-out), background-color var(--dur-2) var(--ease-out);
}

.card-link .stretched::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: var(--radius-lg);
}

.card-link:active {
    transform: translateY(-1px) scale(0.99);
}

.card-link a:not(.stretched),
.card-link button {
    position: relative;
    z-index: var(--layer-raised);
}

@media (hover: hover) {
    .card-link:hover {
        transform: translateY(-3px);
        border-color: var(--clr-border-strong);
        background-color: var(--clr-surface-hover);
        box-shadow: var(--shadow-lift);
    }
}

@media (max-width: 640px) {
    .card {
        padding: var(--card-padding-phone, var(--space-16));
        background-image: none;
        box-shadow: none;
    }
}

/* ── The frame (DESIGN-SYSTEM §2): the app's bars and rails, in its frame colour. On it, the focus ring and the
   signature stroke take the highlight; an app whose frame holds more re-maps more of its tokens under this class. ── */
.on-frame {
    --clr-focus: var(--clr-highlight);
    --clr-signature: var(--clr-highlight);
    background-color: var(--clr-frame);
    color: var(--clr-frame-text);
}

/* The top bar: sticky, clear of the notch, its row as wide as the page. */
.bar {
    position: sticky;
    top: 0;
    z-index: var(--layer-bar);
    padding-top: env(safe-area-inset-top);
    box-shadow: var(--bar-shadow, none);
}

.bar-inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-16);
    max-width: var(--page-max, 72rem);
    min-height: var(--bar-height, 3.5rem);
    margin-inline: auto;
    padding-inline: var(--space-24);
}

.bar-nav {
    display: flex;
    gap: var(--space-4);
}

/* A place of the bar. The current one is a pill on the frame's raised step, its word in the highlight — never the
   accent painted on the frame. */
.bar-link {
    display: inline-flex;
    align-items: center;
    min-height: var(--target);
    padding: 0 var(--space-16);
    border-radius: var(--radius-pill);
    color: var(--clr-frame-text-muted);
    font-weight: 500;
    text-decoration: none;
    transition: color var(--dur-2) var(--ease-out), background-color var(--dur-2) var(--ease-out);
}

.bar-link[aria-current="page"] {
    background: var(--clr-frame-raised);
    color: var(--clr-highlight);
    animation: var(--bar-link-current-animation, none);
}

@media (hover: hover) {
    .bar-link:hover {
        background: var(--clr-frame-raised);
        color: var(--clr-frame-text);
    }

    .bar-link[aria-current="page"]:hover {
        color: var(--clr-highlight);
    }
}

/* Under 900 px the tab bar carries the places. */
@media (max-width: 900px) {
    .bar-inner {
        padding-inline: var(--space-16);
    }

    .bar-nav {
        display: none;
    }
}

/* ── The page: its column, centred, and a reading measure for a page of prose. ── */
.page {
    width: 100%;
    max-width: var(--page-max, 72rem);
    margin-inline: auto;
    padding: var(--page-padding, var(--space-32) var(--space-24) calc(var(--space-32) * 2));
}

.reading {
    max-width: var(--reading-max);
}

@media (max-width: 640px) {
    .page {
        padding: var(--page-padding-phone, var(--space-20) var(--space-16) calc(var(--space-24) * 2));
    }
}

/* ── A page's head (PageHeader): its one h1 in the serif, its subtitle, its actions — beside them on a desk, under
   them below 900 px, wrapping rather than pushing the page sideways. ── */
.page-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-16);
    margin-bottom: var(--page-header-gap, var(--space-24));
}

.page-title {
    color: var(--page-title-color, var(--clr-text));
    font-family: var(--font-serif);
    font-size: var(--page-title-size, var(--fs-2xl));
    font-weight: 600;
    line-height: 1.2;
    letter-spacing: -0.015em;
}

.page-subtitle {
    margin-top: var(--space-2);
    color: var(--clr-text-muted);
    font-size: var(--fs-md);
}

.page-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-8);
}

@media (max-width: 900px) {
    .page-header {
        flex-direction: column;
        align-items: stretch;
        gap: var(--space-12);
    }
}

@media (max-width: 640px) {
    .page-header {
        margin-bottom: var(--page-header-gap-phone, var(--space-16));
    }

    .page-title {
        font-size: var(--page-title-size-phone, var(--fs-xl));
    }
}

/* ── The figure tile (KpiCard): a card whose figure is in the serif, in tabular figures. ── */
.kpi-card {
    gap: var(--space-8);
}

.kpi-value {
    color: var(--clr-text);
    font-family: var(--font-serif);
    font-size: var(--kpi-size, var(--fs-2xl));
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    line-height: 1;
    letter-spacing: -0.015em;
}

.kpi-trend {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-4);
    color: var(--clr-text-muted);
    font-size: var(--fs-xs);
}

@media (max-width: 640px) {
    .kpi-value {
        font-size: var(--kpi-size-phone, var(--fs-xl));
    }
}

/* ── The gauge (ProgressBar): a track in the fill, the share in the accent's flat colour by default — progress
   toward a goal steers, it judges nothing; its tone when the share means a state. ── */
.progress {
    position: relative;
    display: block;
    block-size: var(--progress-height, 0.5rem);
    overflow: hidden;
    border-radius: var(--radius-xs);
    background: var(--clr-fill);
}

.progress-thin {
    block-size: var(--progress-thin-height, 0.25rem);
}

.progress-track {
    display: block;
    inline-size: 100%;
    block-size: 100%;
}

.progress-bar {
    fill: var(--progress-color, var(--clr-accent-fill));
    rx: var(--radius-xs);
    transition: width var(--dur-3) var(--ease-out);
}

.progress-success {
    --progress-color: var(--clr-success);
}

.progress-warning {
    --progress-color: var(--clr-warning);
}

.progress-danger {
    --progress-color: var(--clr-danger);
}

.progress-muted {
    --progress-color: var(--clr-text-muted);
}

/* A long action's steps under its button (ProgressButton.OnRunSteps): the bar, then "n / total". */
.progress-steps {
    display: flex;
    align-items: center;
    gap: var(--space-8);
    margin-block-start: var(--space-8);
    font-size: var(--fs-xs);
}

.progress-steps > .progress {
    flex: 1;
}

/* While the duration is unknown: a share that sweeps across the track. */
.progress-sweep {
    position: absolute;
    inset-block: 0;
    inline-size: 40%;
    border-radius: inherit;
    background: var(--progress-color, var(--clr-accent-fill));
    animation: progress-sweep var(--dur-pulse) var(--ease-out) infinite;
}

@keyframes progress-sweep {
    from {
        transform: translateX(-100%);
    }

    to {
        transform: translateX(250%);
    }
}

/* Without motion, a still half-tone across the track: the sweep would otherwise end off the track, and nothing would
   say the work goes on. */
@media (prefers-reduced-motion: reduce) {
    .progress-sweep {
        inline-size: 100%;
        opacity: 0.5;
        animation: none;
    }
}

/* ── The inline panel: a step inside a card (InlineConfirm), on the page's ground. ── */
.inline-panel {
    --stack-gap: var(--space-12);
    padding: var(--space-16);
    border: 1px solid var(--clr-border);
    border-radius: var(--radius-md);
    background: var(--clr-bg);
}

/* ── Accessibility helpers ── */
/* Pinned to its containing block's left edge: left where it was born, in the last column of a table that scrolls
   sideways, it escaped the scroller and widened the page (LumoHub, /expenses at 768 px). */
.visually-hidden {
    position: absolute;
    left: 0;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* The skip link opens the tab order and shows only when it has the focus, above every layer an app stacks (its
   bars, its dialogs, its toasts), with an edge that sets it apart from a white page. */
.skip-link {
    position: absolute;
    top: var(--space-8);
    left: var(--space-8);
    z-index: var(--layer-skip);
    padding: var(--space-8) var(--space-16);
    border: 1px solid var(--clr-border-strong);
    border-radius: var(--radius-pill);
    background: var(--clr-surface);
    transform: translateY(-200%);
}

.skip-link:focus-visible {
    transform: none;
}

/* ── The modal dialog (Dialog, since 0.18.0): the page under an open dialog does not scroll. ─────────────────────── */
body:has(.dialog-scrim) {
    overflow: hidden;
}
