/*
 * shared/ui — components. Every rule references tokens from `tokens.css` and
 * nothing else (directly, or through `color-mix()` for shades) — no raw hex
 * values, no mode queries. Breakpoints are 720px and 480px, and no others.
 * Layered as ui.system (order declared in tokens.css); not indented under it.
 */

@layer ui.system {

/* --- Base --- */

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
    margin: 0;
    font-family: var(--font-body);
    font-size: var(--font-size-base);
    line-height: 1.5;
    background: var(--bg);
    color: var(--text);
    -webkit-font-smoothing: antialiased;
}

/* The display face is a role: headings and lead figures. */
h1, h2, h3, .kpi-value, .tile-value { font-family: var(--font-display); }

h1 { font-size: 1.45rem; font-weight: 650; letter-spacing: -0.01em; margin: 0; }
h2 { font-size: 1.05rem; font-weight: 650; letter-spacing: -0.005em; }

a { color: var(--primary); }

:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 2px;
    border-radius: var(--r-s);
}

::selection { background: color-mix(in srgb, var(--primary) 25%, transparent); }

svg.icon {
    width: 1em;
    height: 1em;
    flex: none;
    vertical-align: -0.125em;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
    fill: none;
}

/* The icon set under `icons/`, rendered inline by `ux_icon('ui:…')`. Filled,
   not stroked, so it cannot share `svg.icon`. Sized and aligned like the
   Bootstrap Icons font glyph it replaces. */
.ui-icon {
    display: inline-block;
    width: 1em;
    height: 1em;
    flex: none;
    vertical-align: -0.125em;
    fill: currentColor;
}

[hidden] { display: none !important; }

/* --- Shell: rail, top bar, tab bar (`ui:Shell`) --- */

/* One `.rail` serves both tiers: from 720px up it is the side rail, below it
   the top bar, so there is one markup shape and one view-transition name.
   The user cluster (`.rail-foot`) is a popover on phones and a plain block in
   the rail; see README.md "Shell". */

.brand {
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
    min-width: 0;
    color: var(--text);
    text-decoration: none;
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 0.98rem;
    letter-spacing: -0.01em;
}
.brand-mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 1.7rem;
    height: 1.7rem;
    border-radius: var(--r-s);
    background: var(--primary);
    color: var(--on-primary);
    font-size: 0.95rem;
}

.rail {
    z-index: 100;
    background: var(--card);
}

/* Opens the command palette. Label and shortcut hint drop below 720px. */
.palette-trigger {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0.45rem 0.6rem;
    border: 1px solid var(--border);
    border-radius: var(--r-s);
    background: var(--bg);
    color: var(--muted);
    font: inherit;
    font-size: 0.88rem;
    text-align: left;
    cursor: pointer;
}
.palette-trigger:hover { color: var(--text-2); border-color: var(--border-strong); }
.palette-trigger kbd {
    margin-left: auto;
    padding: 0 0.35rem;
    border: 1px solid var(--border);
    border-radius: calc(var(--r-s) * 0.7);
    background: var(--card);
    font-family: inherit;
    font-size: 0.72rem;
    white-space: nowrap;
}

.rail-group + .rail-group { margin-top: 0.9rem; }
.rail-group-label {
    margin: 0 0 0.3rem;
    padding: 0 0.6rem;
    color: var(--muted);
    font-size: 0.72rem;
    font-weight: 650;
    letter-spacing: 0.05em;
    text-transform: uppercase;
}
.rail-list { display: grid; gap: 0.1rem; margin: 0; padding: 0; list-style: none; }

/* Rail links and the rows of the user cluster share one shape. */
.rail-link,
.rail-foot > a,
.rail-foot > button,
.rail-foot > form > button,
.rail-foot .menu > summary {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    width: 100%;
    padding: 0.42rem 0.6rem;
    border: none;
    border-radius: var(--r-s);
    background: none;
    color: var(--text-2);
    text-align: left;
    text-decoration: none;
    font: inherit;
    font-size: 0.9rem;
    font-weight: 500;
    cursor: pointer;
}
.rail-link:hover,
.rail-foot > a:hover,
.rail-foot > button:hover,
.rail-foot > form > button:hover,
.rail-foot .menu > summary:hover { background: color-mix(in srgb, var(--text) 6%, transparent); color: var(--text); }
.rail-link.is-active {
    background: var(--primary-soft);
    color: var(--primary-ink);
    font-weight: 650;
}
.rail-link-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rail-count {
    margin-left: auto;
    min-width: 1.5rem;
    padding: 0 0.4rem;
    border-radius: 999px;
    background: var(--primary);
    color: var(--on-primary);
    font-size: 0.72rem;
    font-weight: 700;
    line-height: 1.35rem;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

.rail-foot {
    flex-direction: column;
    gap: 0.1rem;
    color: var(--muted);
    font-size: 0.88rem;
}
.rail-foot .user {
    padding: 0.3rem 0.6rem;
    overflow: hidden;
    color: var(--text);
    font-weight: 600;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.rail-foot > form { margin: 0; }
/* A menu in the user cluster (a language switch) opens as an accordion: an
   absolutely positioned panel would leave the rail at its foot. */
.rail-foot .menu-list {
    position: static;
    min-width: 0;
    max-width: none;
    margin: 0.1rem 0 0.3rem 0.75rem;
    padding: 0 0 0 0.6rem;
    border: none;
    border-left: 1px solid var(--border);
    border-radius: 0;
    background: none;
    box-shadow: none;
}

.rail-tools { display: flex; align-items: center; gap: 0.4rem; }

/* The theme toggle carries both icons and shows the one for the scheme it
   switches to. A scheme deciding display rather than colour cannot be a
   light-dark() pair, so these are the only mode queries check-tokens allows. */
:root[data-theme="light"] .theme-icon-sun,
:root[data-theme="dark"] .theme-icon-moon { display: none; }
@media (prefers-color-scheme: light) {
    :root:not([data-theme]) .theme-icon-sun { display: none; }
}
@media (prefers-color-scheme: dark) {
    :root:not([data-theme]) .theme-icon-moon { display: none; }
}

.tabbar { display: none; }

@media (min-width: 721px) {
    .shell {
        display: grid;
        grid-template-columns: var(--rail-w) minmax(0, 1fr);
        grid-template-rows: 1fr auto;
        min-height: 100dvh;
    }
    .shell > main { grid-column: 2; grid-row: 1; }
    .shell > .app-footer { grid-column: 2; grid-row: 2; }

    .rail {
        grid-column: 1;
        grid-row: 1 / span 2;
        position: sticky;
        top: 0;
        height: 100dvh;
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto;
        grid-template-rows: auto auto minmax(0, 1fr) auto;
        grid-template-areas:
            "brand brand"
            "trigger trigger"
            "nav nav"
            "foot tools";
        row-gap: 0.75rem;
        padding: 1rem 0.75rem 0.75rem max(0.75rem, env(safe-area-inset-left));
        border-right: 1px solid var(--border);
    }
    .rail > .brand { grid-area: brand; padding: 0.15rem 0.35rem 0.35rem; }
    .rail .palette-trigger { grid-area: trigger; }
    .rail-nav { grid-area: nav; margin: 0 -0.75rem; padding: 0 0.75rem; overflow-y: auto; overscroll-behavior: contain; }

    /* Author styles beat the UA's popover rules, so the closed popover shows
       as a plain block here. */
    .rail-foot[popover] {
        grid-area: foot;
        display: flex;
        position: static;
        inset: auto;
        width: auto;
        height: auto;
        margin: 0;
        padding: 0.6rem 0 0;
        overflow: visible;
        border: none;
        border-top: 1px solid var(--border);
        background: none;
        color: var(--muted);
    }
    .rail-tools {
        grid-area: tools;
        align-self: stretch;
        align-items: flex-end;
        padding: 0.6rem 0 0.2rem 0.4rem;
        border-top: 1px solid var(--border);
    }
    .rail-tools .rail-user-btn { display: none; }
}

@media (max-width: 720px) {
    .rail {
        position: sticky;
        top: 0;
        display: flex;
        align-items: center;
        gap: 0.5rem;
        height: calc(var(--topbar-h) + env(safe-area-inset-top));
        padding: env(safe-area-inset-top) max(1rem, env(safe-area-inset-right)) 0 max(1rem, env(safe-area-inset-left));
        border-bottom: 1px solid var(--border);
    }
    .rail > .brand { margin-right: auto; }
    .rail-nav { display: none; }

    .rail .palette-trigger {
        justify-content: center;
        width: var(--tap-min);
        height: var(--tap-min);
        padding: 0;
        border-radius: 999px;
        background: none;
        color: var(--text-2);
    }
    .palette-trigger kbd { display: none; }
    /* Out of sight, but still the icon button's accessible name. */
    .palette-trigger-label {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
    }

    .rail-foot:popover-open {
        display: flex;
        position: fixed;
        inset: auto;
        top: calc(var(--topbar-h) + env(safe-area-inset-top) + 0.25rem);
        right: max(0.5rem, env(safe-area-inset-right));
        width: min(18rem, calc(100vw - 1rem));
        max-height: calc(100dvh - var(--topbar-h) - var(--tabbar-h) - 1rem);
        margin: 0;
        padding: 0.4rem;
        overflow-y: auto;
        border: 1px solid var(--border);
        border-radius: var(--r-m);
        background: var(--card);
        box-shadow: var(--shadow-2);
    }
    .rail-foot > a,
    .rail-foot > button,
    .rail-foot > form > button,
    .rail-foot .menu > summary,
    .rail-foot .menu-list a,
    .rail-foot .menu-list button { min-height: var(--tap-min); }

    .tabbar {
        position: fixed;
        right: 0;
        bottom: 0;
        left: 0;
        z-index: 100;
        display: grid;
        grid-auto-columns: minmax(0, 1fr);
        grid-auto-flow: column;
        height: calc(var(--tabbar-h) + env(safe-area-inset-bottom));
        padding: 0 env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
        border-top: 1px solid var(--border);
        background: var(--card);
    }
    .tab {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 0.15rem;
        min-width: 0;
        padding: 0 0.15rem;
        border: none;
        background: none;
        color: var(--text-2);
        text-decoration: none;
        font: inherit;
        font-size: 0.7rem;
        font-weight: 500;
        cursor: pointer;
    }
    .tab-icon {
        display: inline-flex;
        justify-content: center;
        padding: 0.2rem 1rem;
        border-radius: 999px;
        font-size: 1.15rem;
    }
    .tab-label { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .tab.is-active { color: var(--primary-ink); font-weight: 650; }
    .tab.is-active .tab-icon { background: var(--primary-soft); }

    /* The last row of the page scrolls clear of the fixed bar. */
    .shell.has-tabbar { padding-bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom)); }
    .shell.has-tabbar .floating-nav { bottom: calc(var(--tabbar-h) + max(1rem, env(safe-area-inset-bottom))); }
}

/* --- Command palette (`ui:Shell`, driven by chrome.js) --- */

.palette {
    width: min(36rem, calc(100vw - 2rem));
    max-height: min(32rem, calc(100dvh - 4rem));
    margin: 12dvh auto auto;
    padding: 0;
    overflow: hidden;
    border: 1px solid var(--border);
    border-radius: var(--r-l);
    background: var(--card);
    color: var(--text);
    box-shadow: var(--shadow-2);
}
.palette[open] { display: flex; flex-direction: column; }
.palette::backdrop { background: var(--scrim); }
.palette-form { display: flex; flex-direction: column; flex: 1; min-height: 0; margin: 0; }

.palette-input {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.4rem 0.5rem 0.4rem 1rem;
    border-bottom: 1px solid var(--border);
    color: var(--muted);
}
.palette-input input {
    flex: 1;
    min-width: 0;
    padding: 0.5rem 0;
    border: none;
    outline: none;
    background: none;
    color: var(--text);
    font: inherit;
    font-size: 1rem;
}
.palette-close { border-color: transparent; }

.palette-list { flex: 1; min-height: 0; padding: 0.4rem; overflow-y: auto; overscroll-behavior: contain; }
.palette-group + .palette-group { margin-top: 0.3rem; }
.palette-group-label {
    padding: 0.5rem 0.6rem 0.25rem;
    color: var(--muted);
    font-size: 0.72rem;
    font-weight: 650;
    letter-spacing: 0.05em;
    text-transform: uppercase;
}
.palette-option {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    width: 100%;
    padding: 0.5rem 0.6rem;
    border: none;
    border-radius: var(--r-s);
    background: none;
    color: var(--text);
    text-align: left;
    text-decoration: none;
    font: inherit;
    font-size: 0.92rem;
    cursor: pointer;
}
.palette-option:hover { background: var(--card-2); }
.palette-option[aria-selected="true"] { background: var(--primary-soft); color: var(--primary-ink); }
.palette-option > .ui-icon { color: var(--muted); }
.palette-option[aria-selected="true"] > .ui-icon { color: inherit; }
.palette-option small { margin-left: auto; color: var(--muted); font-size: 0.78rem; white-space: nowrap; }
.palette-search { margin-top: 0.3rem; }
.palette-keys { display: inline-flex; gap: 0.2rem; margin-left: auto; }
.palette-keys kbd {
    min-width: 1.25rem;
    padding: 0 0.3rem;
    border: 1px solid var(--border);
    border-radius: calc(var(--r-s) * 0.7);
    background: var(--card);
    color: var(--muted);
    font-family: inherit;
    font-size: 0.72rem;
    text-align: center;
}
/* A result row from `resultsUrls` (fleet-api's palette routes): the title, then its subtitle muted. */
.palette-hit-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.palette-hit-sub { margin-left: 0.35rem; color: var(--muted); font-size: 0.82rem; }
.palette-option[aria-selected="true"] .palette-hit-sub { color: inherit; opacity: 0.75; }
/* Answers to an older query, until the newer one's arrive. */
.palette-results[aria-busy="true"] { opacity: 0.55; }
.palette-results .palette-group { margin-top: 0.3rem; }

/* Each app in its own hue, at this app's accent lightness and chroma; the
   feed's hue is set on the option by chrome.js. */
.palette-app { --app-hue: var(--hue); }
.palette-app-mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 1.3rem;
    height: 1.3rem;
    border-radius: calc(var(--r-s) * 0.8);
    background: oklch(from var(--primary) l c var(--app-hue));
    color: var(--on-primary);
    font-size: 0.72rem;
    font-weight: 700;
}

.palette-empty { margin: 0; padding: 1.25rem 0.6rem; color: var(--muted); font-size: 0.9rem; text-align: center; }

@media (max-width: 720px) {
    .palette {
        width: calc(100vw - 1rem);
        max-height: calc(100dvh - 1rem);
        margin-top: max(0.5rem, env(safe-area-inset-top));
    }
    .palette-option { min-height: var(--tap-min); }
    .palette-keys { display: none; }
}

/* --- Menus --- */

/* Dropdown built on <details>/<summary>, so it opens with no JavaScript. */
.menu { position: relative; }
.menu > summary {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.3rem 0.65rem;
    border: 1px solid var(--border);
    border-radius: 999px;
    color: var(--text-2);
    font-size: 0.85rem;
    font-weight: 500;
    cursor: pointer;
    list-style: none;
    user-select: none;
}
.menu > summary::-webkit-details-marker { display: none; }
.menu > summary::marker { content: ''; }
.menu > summary:hover { color: var(--text); border-color: var(--border-strong); }
.menu > summary .user { max-width: 10rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.menu-list {
    position: absolute;
    right: 0;
    top: calc(100% + 0.4rem);
    z-index: 200;
    min-width: 11rem;
    /* Clamp bounds a panel escaping the screen on the anchored side; use
       `.menu-left` when the trigger sits near the left edge instead. */
    max-width: min(18rem, calc(100vw - 1.5rem));
    padding: 0.3rem;
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: var(--r-m);
    box-shadow: var(--shadow-2);
}
/* Anchor to the trigger's left edge instead — for triggers on the left half. */
.menu-left .menu-list { right: auto; left: 0; }
/* Buttons as well as links: a logout entry has to be a POST form. */
.menu-list a,
.menu-list button {
    display: block;
    width: 100%;
    padding: 0.4rem 0.6rem;
    border: none;
    border-radius: var(--r-s);
    background: none;
    color: var(--text-2);
    text-align: left;
    text-decoration: none;
    font: inherit;
    font-size: 0.85rem;
    white-space: nowrap;
    cursor: pointer;
}
.menu-list a:hover,
.menu-list button:hover { background: var(--card-2); color: var(--text); }
.menu-list a.is-active { background: var(--primary-soft); color: var(--primary-ink); font-weight: 600; }

.menu-sep { height: 1px; margin: 0.3rem 0; background: var(--border); }

/* --- Page layout --- */

.container {
    /* A size container has no intrinsic width, so it must not shrink to fit
       (the auto margins would do that in the shell's grid). */
    container: page / inline-size;
    width: 100%;
    max-width: 1180px;
    margin: 1.75rem auto;
    padding: 0 1.25rem;
}

.page-head {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-bottom: 1.25rem;
}

/* Title cluster inside `.page-head`: optional icon, heading, optional subtitle. Its end margin, not
   space-between on the head, keeps it beside a `lead` while still pushing `.actions` right. */
.page-titles { display: flex; align-items: center; gap: 0.9rem; min-width: 0; margin-inline-end: auto; }
.page-titles .subtitle { margin: 0.15rem 0 0; font-size: 0.85rem; color: var(--muted); }
.page-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 2.75rem;
    height: 2.75rem;
    border-radius: var(--r-m);
    background: var(--primary-soft);
    color: var(--primary-ink);
    font-size: 1.25rem;
}

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

.app-footer {
    margin-top: 2.5rem;
    padding: 1.1rem 1.25rem;
    text-align: center;
    background: var(--card);
    border-top: 1px solid var(--border);
    color: var(--muted);
    font-size: 0.85rem;
}
.app-footer .brand-dot { margin-right: 0.25rem; }

/* --- Layout primitives --- */

/* Named structures, not a utility scale — no `.p-4` here. Density is a token
   (`--gap-*`, `--col-min`), tuned in an app's theme rather than in `app.css`. */

/* Vertical rhythm between the sections of a long page. */
.stack { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--gap-section); }
.stack-tight { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--gap-tight); }

/* Two columns that collapse to one when there is no room for both. */
.cols-2 {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(var(--col-min), 100%), 1fr));
    gap: var(--gap-grid);
}

/* …and the same with one dominant column. Unlike `.cols-2` these cannot use
   auto-fit — the ratio is the point — so they reflow at an explicit width of
   the page column, which the rail makes narrower than the viewport. */
.cols-wide-narrow { display: grid; grid-template-columns: minmax(0, var(--col-wide)) minmax(0, 1fr); gap: var(--gap-grid); }
.cols-narrow-wide { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, var(--col-wide)); gap: var(--gap-grid); }
@container page (max-width: 860px) {
    .cols-wide-narrow,
    .cols-narrow-wide { grid-template-columns: minmax(0, 1fr); }
}

/* Inline row of items that wraps: filter bars, tag lists, action clusters. */
.row-wrap { display: flex; flex-wrap: wrap; align-items: center; gap: var(--gap-inline); }
.row-between {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--gap-tight);
}

/* A single-column page that should not run the full container width — the
   one-thing-at-a-time workflows. */
.narrow-col { max-width: 44rem; margin: 0 auto; }
.narrow-col-wide { max-width: 58rem; }

/* --- Buttons --- */

.btn {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.45rem 0.85rem;
    border-radius: var(--r-s);
    border: 1px solid var(--border);
    background: var(--card);
    color: var(--text);
    text-decoration: none;
    font: inherit;
    font-size: 0.88rem;
    font-weight: 500;
    cursor: pointer;
    box-shadow: var(--shadow-1);
    transition: border-color 0.12s ease, background 0.12s ease;
}
.btn:hover { border-color: var(--border-strong); background: var(--card-2); }

.btn-primary {
    background: var(--primary);
    border-color: var(--primary);
    color: var(--on-primary);
}
.btn-primary:hover { background: var(--primary-hover); border-color: var(--primary-hover); }

/* Square icon-only button — row actions, dismiss controls. */
.icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.9rem;
    height: 1.9rem;
    padding: 0;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: none;
    color: var(--text-2);
    font: inherit;
    cursor: pointer;
}
.icon-btn:hover { color: var(--text); border-color: var(--border-strong); }
.icon-btn.danger:hover { color: var(--neg); border-color: var(--neg); }

/* Mutually exclusive views of one list. `.segmented` is a joined strip that
   cannot wrap; `.segmented-pills` is separate pills that do. Each works on its
   own; the older `.segmented .segmented-pills` pairing renders the same. */
.segmented {
    display: inline-flex;
    /* More options than a phone has room for scroll inside the control, not the page. */
    max-width: 100%;
    border: 1px solid var(--border);
    border-radius: var(--r-s);
    overflow-x: auto;
    background: var(--card);
    box-shadow: var(--shadow-1);
}
.segmented a,
.segmented-pills a {
    padding: 0.45rem 0.8rem;
    color: var(--text-2);
    text-decoration: none;
    font-size: 0.85rem;
    font-weight: 500;
    white-space: nowrap;
}
.segmented a + a { border-left: 1px solid var(--border); }
.segmented a:hover { background: var(--card-2); color: var(--text); }
.segmented a.is-active,
.segmented-pills a.is-active { background: var(--primary); color: var(--on-primary); font-weight: 600; }

.segmented-pills {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    border: none;
    border-radius: 0;
    background: none;
    box-shadow: none;
}
.segmented-pills a {
    padding: 0.3rem 0.7rem;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--card);
    font-size: 0.83rem;
    font-variant-numeric: tabular-nums;
}
.segmented-pills a:hover { background: var(--card); border-color: var(--border-strong); color: var(--text); }
.segmented-pills a.is-active { background: var(--primary); border-color: var(--primary); }

/* --- Cards & key figures --- */

.cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 1rem;
    margin: 1.25rem 0;
}

.card {
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: var(--r-l);
    padding: var(--card-pad-y) var(--card-pad-x);
    box-shadow: var(--shadow-1);
}

.card h2 { margin: 0 0 0.75rem; }

/* Full-bleed footer bonded to the bottom of a `.card`: it cancels the card's
   own padding to reach the edges, which is why that padding is a token. */
.card-foot {
    margin: 1rem calc(-1 * var(--card-pad-x)) calc(-1 * var(--card-pad-y));
    padding: 0.75rem var(--card-pad-x);
    border-top: 1px solid var(--border);
    background: var(--card-2);
    border-radius: 0 0 var(--r-l) var(--r-l);
    font-size: 0.85rem;
}

/* KPI head: one big number per card, details underneath. */
.kpi { margin: 0 0 0.9rem; }
.kpi-value {
    font-size: 1.9rem;
    font-weight: 700;
    letter-spacing: -0.02em;
    line-height: 1.15;
    white-space: nowrap;
}
.kpi-label {
    display: block;
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--muted);
    margin-top: 0.1rem;
}
.card .kpi + dl { border-top: 1px solid var(--border); padding-top: 0.75rem; }

/* Stat tiles: denser than `.cards` + `.kpi`. Default centres the figure;
   `.tile-lead` turns it into an icon row with a larger figure. */
.tiles {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(var(--tile-min), 100%), 1fr));
    gap: var(--gap-tight);
}

.tile {
    padding: 0.85rem 0.9rem;
    text-align: center;
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: var(--r-m);
    box-shadow: var(--shadow-1);
}

.tile-lead {
    display: flex;
    align-items: center;
    gap: 0.85rem;
    padding: 1rem 1.15rem;
    text-align: left;
}

.tile-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 2.25rem;
    height: 2.25rem;
    border-radius: var(--r-s);
    background: var(--primary-soft);
    color: var(--primary-ink);
    font-size: 1.05rem;
}

.tile-value {
    font-family: var(--font-display);
    font-size: 1.4rem;
    font-weight: 700;
    line-height: 1.2;
    letter-spacing: -0.02em;
    font-variant-numeric: tabular-nums;
    color: var(--text);
}
.tile-lead .tile-value { font-size: 1.8rem; line-height: 1.15; letter-spacing: normal; }
.tile-value.is-date { font-size: 0.95rem; }

.tile-unit { font-size: 0.8rem; font-weight: 400; color: var(--muted); }

/* No `overflow-wrap: break-word`: it splits uppercase labels at an arbitrary
   letter. Long compounds ("Veranstaltungsorte") hyphenate by the page's `lang`. */
.tile-label {
    display: block;
    margin-top: 0.15rem;
    color: var(--muted);
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    hyphens: auto;
}

dl { display: grid; grid-template-columns: 1fr auto; gap: 0.35rem 1rem; margin: 0; font-size: 0.9rem; }
dt { color: var(--muted); }
dd { margin: 0; text-align: right; font-variant-numeric: tabular-nums; color: var(--text-2); }
.strong, dd.strong { font-weight: 700; color: var(--text); }
.pos { color: var(--pos); }
.neg { color: var(--neg); }

.table-title { margin: 1.75rem 0 0.5rem; }

/* --- Lead figure & attention (`ui:StatLead`, `ui:Attention`) --- */

/* One number large, its trend and sparkline under it, the other figures as a
   compact list beneath a hairline. */
.stat-lead { display: grid; gap: 0.6rem; align-content: start; }
/* Out-specifies `.card h2`, whose bottom margin would stack on the grid gap. */
.stat-lead > .stat-lead-label {
    margin: 0;
    font-family: var(--font-body);
    font-size: 0.75rem;
    font-weight: 650;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--text-2);
}
.stat-lead-figure { display: flex; align-items: baseline; gap: 0.5rem; margin: 0; color: var(--text-2); }
.stat-lead-value {
    font-family: var(--font-display);
    font-size: 3.2rem;
    font-weight: 800;
    line-height: 0.95;
    letter-spacing: -0.04em;
    font-variant-numeric: tabular-nums;
    color: var(--text);
    text-decoration: none;
}
a.stat-lead-value:hover { color: var(--primary); }
.stat-lead-trend { margin: 0; font-size: 0.85rem; color: var(--muted); }
.stat-lead-trend.is-up { color: var(--pos); font-weight: 600; }

.spark { display: block; width: 100%; height: 3.5rem; margin-top: 0.25rem; }
.spark-area { fill: color-mix(in oklab, var(--primary) 16%, transparent); }
.spark-line { fill: none; stroke: var(--primary); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.spark-axis { display: flex; justify-content: space-between; font-size: 0.72rem; color: var(--muted); }

.stat-list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0.75rem 1rem;
    margin-top: 0.4rem;
    padding-top: 0.9rem;
    border-top: 1px dashed var(--border);
}
.stat-list div { display: flex; flex-direction: column-reverse; }
.stat-list dt { font-size: 0.8rem; }
.stat-list dd { text-align: left; font-size: 1.1rem; font-weight: 700; color: var(--text); }
.stat-list dd a { color: inherit; text-decoration: none; }
.stat-list dd a:hover { color: var(--primary); }

/* What is waiting on the reader, as chips rather than a banner: each one is a
   count and the way to the page that clears it. */
.attention-chips { display: flex; flex-wrap: wrap; align-items: center; gap: var(--gap-inline); }
.attention-label {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    margin-right: 0.25rem;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--text-2);
}
.attention-label::before {
    content: '';
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 50%;
    background: var(--warn);
}
.attention-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.3rem 0.35rem 0.3rem 0.8rem;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--card);
    color: var(--text);
    font-size: 0.85rem;
    font-weight: 500;
    text-decoration: none;
}
.attention-chip:hover { border-color: var(--primary); }
.attention-chip b {
    min-width: 1.5rem;
    padding: 0.05rem 0.45rem;
    border-radius: 999px;
    background: color-mix(in oklab, var(--warn) 22%, transparent);
    font-size: 0.78rem;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

/* The same artist or album in the fleet's other apps (ui:FleetLinks). */
.fleet-links { display: flex; flex-wrap: wrap; align-items: center; gap: var(--gap-inline); }
.fleet-links-label { margin-right: 0.25rem; font-size: 0.85rem; font-weight: 600; color: var(--text-2); }
.fleet-link {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.3rem 0.8rem;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--card);
    color: var(--text);
    font-size: 0.85rem;
    font-weight: 500;
    text-decoration: none;
}
.fleet-link:hover { border-color: var(--primary); }
.fleet-link > .ui-icon { color: var(--muted); }

/* --- Tables --- */

.grid {
    width: 100%;
    border-collapse: collapse;
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: var(--r-m);
    overflow: hidden;
    font-size: 0.88rem;
    box-shadow: var(--shadow-1);
}

.grid th, .grid td { padding: 0.55rem 0.75rem; text-align: left; border-bottom: 1px solid var(--border); }
.grid tbody tr:last-child td { border-bottom: none; }
.grid th {
    background: var(--card-2);
    font-weight: 600;
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--muted);
}
.grid td { font-variant-numeric: tabular-nums; }

/* Denser rows, for tables that are read as a block rather than scanned. */
.grid-sm th, .grid-sm td { padding: 0.35rem 0.6rem; }

/* Required, not decorative: a `.grid` outside this does not shrink to fit and
   pushes the whole page into a sideways scroll instead. */
.table-wrap {
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: thin;
    scrollbar-color: var(--border-strong) transparent;
}
.table-wrap-tall { max-height: 26rem; overflow: auto; }

/* --- Labels --- */

/* Short colour-coded label. The app sets the accent per variant via
   --badge-accent (e.g. `.badge-U { --badge-accent: var(--type-U); }`). */
.badge {
    display: inline-block;
    min-width: 1.5rem;
    text-align: center;
    padding: 0.1rem 0.35rem;
    border-radius: 4px;
    font-weight: 700;
    font-size: 0.78rem;
    --badge-accent: var(--muted);
    color: color-mix(in srgb, var(--badge-accent) 75%, var(--text));
    background: color-mix(in srgb, var(--badge-accent) 16%, var(--card));
}

/* --- List head & collapsible groups --- */

.list-head {
    display: flex;
    align-items: baseline;
    gap: 0.6rem;
    margin: 1.75rem 0 0.6rem;
}
.list-head .table-title { margin: 0; }
.list-count {
    font-size: 0.82rem;
    /* --muted misses 4.5:1 on the tint. */
    color: var(--text-2);
    background: color-mix(in srgb, var(--text) 7%, transparent);
    padding: 0.1rem 0.55rem;
    border-radius: 999px;
}

/* Rows that are each a single link (`ui:ListRow`): lead (cover, date, rank),
   title over one line of meta, and badges or a score at the end. */
.list-rows { list-style: none; padding: 0; margin: 0; }
.list-rows > li + li { border-top: 1px solid var(--border); }
.list-row {
    display: flex;
    align-items: center;
    gap: 0.85rem;
    padding: 0.6rem;
    margin: 0 -0.6rem;
    border-radius: var(--r-s);
    color: inherit;
    text-decoration: none;
}
.list-row:hover { background: var(--card-2); }
.list-row-lead { flex: none; display: flex; }
.list-row-body { display: flex; flex-direction: column; min-width: 0; }
.list-row-title,
.list-row-meta { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.list-row-title { font-weight: 600; }
.list-row:hover .list-row-title { color: var(--primary); }
.list-row-meta { font-size: 0.85rem; color: var(--muted); }
.list-row-end { display: flex; align-items: center; gap: 0.5rem; margin-left: auto; flex: none; }

/* Collapsible section (<details>/<summary>) with a heading and a meta column. */
.group { margin-bottom: 0.6rem; }
.group-summary {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0.65rem 0.95rem;
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: var(--r-m);
    cursor: pointer;
    list-style: none;
    user-select: none;
    box-shadow: var(--shadow-1);
}
.group-summary::-webkit-details-marker { display: none; }
.group-summary::marker { content: ''; }
.group-summary:hover { background: var(--card-2); }
.group[open] > .group-summary {
    border-radius: var(--r-m) var(--r-m) 0 0;
    border-bottom-color: transparent;
}
.group-chevron {
    color: var(--muted);
    font-size: 0.8rem;
    transition: transform 0.15s ease;
}
.group[open] > .group-summary .group-chevron { transform: rotate(90deg); }
.group-name { font-weight: 700; font-size: 1rem; }
.group-count { color: var(--muted); font-size: 0.83rem; }
.group-meta {
    margin-left: auto;
    display: flex;
    align-items: baseline;
    gap: 0.4rem;
    font-size: 0.83rem;
    color: var(--muted);
}

@media (max-width: 720px) {
    .group-summary { flex-wrap: wrap; gap: 0.4rem 0.55rem; }
    .group-meta-lbl { display: none; }
}

/* --- Forms --- */

.form-card { max-width: 520px; }
.form-card label { display: block; font-weight: 600; font-size: 0.9rem; margin: 0.85rem 0 0.3rem; }
.form-card > form > div:first-child label,
.form-card form > label:first-child { margin-top: 0; }
.form-card input:where(:not([type=checkbox], [type=radio])), .form-card select, .form-card textarea {
    width: 100%;
    padding: 0.5rem 0.65rem;
    border: 1px solid var(--border-strong);
    border-radius: var(--r-s);
    font: inherit;
    background: var(--card);
    color: var(--text);
    transition: border-color 0.12s ease, box-shadow 0.12s ease;
}
.form-card input:where(:not([type=checkbox], [type=radio])):focus, .form-card select:focus, .form-card textarea:focus {
    outline: none;
    border-color: var(--primary);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 20%, transparent);
}
.form-card input:where(:not([type=checkbox], [type=radio])):disabled, .form-card select:disabled {
    background: var(--card-2);
    color: var(--muted);
    cursor: not-allowed;
}
.form-card input:where([type=checkbox], [type=radio]) {
    min-width: 1.5rem;
    min-height: 1.5rem;
    accent-color: var(--primary);
}
.form-card .help-text {
    display: block;
    margin: 0.3rem 0 0;
    font-size: 0.8rem;
    font-weight: 400;
    color: var(--muted);
}
.form-card ul {
    margin: 0.3rem 0 0;
    padding: 0;
    list-style: none;
    font-size: 0.82rem;
    color: var(--neg);
}
.form-actions { margin-top: 1.25rem; }

/* --- Login page --- */

.login-card { max-width: 380px; margin: 4rem auto; }
.login-brand {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.6rem;
    margin-bottom: 1.25rem;
    text-align: center;
}
.login-brand .brand-mark { width: 2.6rem; height: 2.6rem; font-size: 1.4rem; border-radius: var(--r-m); }
.login-card h1 { font-size: 1.2rem; }
.login-card .checkbox {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    font-weight: 400;
    font-size: 0.9rem;
    margin-top: 0.9rem;
}
.login-card .checkbox input { width: auto; accent-color: var(--primary); }
.login-card .btn { width: 100%; justify-content: center; }

/* --- Notices & messages --- */

.flash {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    padding: 0.7rem 1rem;
    border-radius: var(--r-m);
    border: 1px solid transparent;
    margin-bottom: 1rem;
    font-size: 0.9rem;
}
/* The semantic colours alone miss 4.5:1 on their own 10% tint in the light scheme. */
.flash-success {
    background: color-mix(in srgb, var(--pos) 10%, var(--card));
    border-color: color-mix(in srgb, var(--pos) 35%, transparent);
    color: color-mix(in srgb, var(--pos) 80%, var(--text));
}
.flash-danger {
    background: color-mix(in srgb, var(--neg) 10%, var(--card));
    border-color: color-mix(in srgb, var(--neg) 35%, transparent);
    color: color-mix(in srgb, var(--neg) 80%, var(--text));
}
.flash-warn {
    background: color-mix(in srgb, var(--warn) 10%, var(--card));
    border-color: color-mix(in srgb, var(--warn) 35%, transparent);
    color: color-mix(in srgb, var(--warn) 80%, var(--text));
}
/* Informational, so it carries the accent rather than a semantic colour. */
.flash-info {
    background: var(--primary-soft);
    border-color: color-mix(in srgb, var(--primary) 35%, transparent);
    color: var(--primary-ink);
}

/* chrome.js's live region for the title of a page Turbo has rendered: out of sight, still read. */
.page-announcer {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

/* ui:Shell's flash messages, fixed in the corner rather than at the top of the page,
   which a morph that keeps the scroll position would leave off-screen. */
.toasts {
    position: fixed;
    top: 1rem;
    right: max(1rem, env(safe-area-inset-right));
    z-index: 150;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    width: min(26rem, calc(100vw - 2rem));
    pointer-events: none;
}
.toasts:empty { display: none; }
.toasts .flash {
    align-items: center;
    margin: 0;
    box-shadow: var(--shadow-2);
    pointer-events: auto;
    animation: toast-in 0.2s ease-out;
}
.toasts .flash > .ui-icon { flex: none; }
.toasts .flash > span { flex: 1; min-width: 0; }
.toasts .flash-close { flex: none; margin: -0.35rem -0.5rem -0.35rem 0; color: inherit; }
.toasts .flash-action { flex: none; margin: 0; }
.toasts .flash-action button {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.2rem 0.5rem;
    border: 1px solid currentColor;
    border-radius: var(--r-s);
    background: none;
    color: inherit;
    font: inherit;
    font-weight: 600;
    cursor: pointer;
}
.toasts .flash-action button:hover { background: color-mix(in srgb, currentColor 12%, transparent); }
/* Success fades on its own (chrome.js removes it once gone); one that carries an action,
   like an undo button, waits to be closed, and hovering or focusing holds any of them. */
.toasts .flash-success:not(:has(form, a)) {
    animation: toast-in 0.2s ease-out, toast-out 0.4s ease-in 5s forwards;
}
.toasts .flash:hover,
.toasts .flash:focus-within { animation-play-state: paused; }
@keyframes toast-in { from { opacity: 0; translate: 0 -0.5rem; } }
@keyframes toast-out { to { opacity: 0; visibility: hidden; } }

.legend { color: var(--muted); font-size: 0.85rem; margin-top: 1rem; max-width: 72ch; }
.legend code {
    /* --muted misses 4.5:1 on the tint. */
    color: var(--text-2);
    background: color-mix(in srgb, var(--text) 7%, transparent);
    padding: 0.05rem 0.3rem;
    border-radius: 4px;
}

.empty-note {
    background: var(--card);
    border: 1px dashed var(--border-strong);
    border-radius: var(--r-m);
    padding: 1.25rem;
    color: var(--muted);
    text-align: center;
    margin: 0;
}
/* `ui:EmptyState`'s optional way out, on its own line under the message. */
.empty-note-action { display: flex; width: fit-content; margin: 0.9rem auto 0; }

/* `ui:JobStatus`: one line for a job off the request, its bar only while it runs. */
.job-status { display: block; }
.job-status:where(:has(> .job-status-body)) { margin-bottom: 1rem; }
td > .job-status { margin-bottom: 0; }
.job-status-body {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.35rem 0.6rem;
    font-size: 0.9rem;
    color: var(--text-2);
}
.job-status-icon { flex: none; color: var(--muted); }
.job-status-running .job-status-icon { color: var(--primary); animation: job-spin 1.2s linear infinite; }
.job-status-done .job-status-icon { color: var(--pos); }
.job-status-failed .job-status-icon,
.job-status-failed .job-status-label { color: var(--neg); }
.job-status-label { font-weight: 600; }
.job-status-count { color: var(--muted); font-variant-numeric: tabular-nums; }
.job-status-message { flex-basis: 100%; color: var(--muted); }
.job-status-body progress {
    flex: 1 1 8rem;
    max-width: 16rem;
    height: 0.4rem;
    border: 0;
    border-radius: 999px;
    background: color-mix(in srgb, var(--text) 10%, transparent);
    accent-color: var(--primary);
    overflow: hidden;
    appearance: none;
}
.job-status-body progress::-webkit-progress-bar { background: transparent; }
.job-status-body progress::-webkit-progress-value { background: var(--primary); border-radius: inherit; }
.job-status-body progress::-moz-progress-bar { background: var(--primary); border-radius: inherit; }
/* Firefox draws an indeterminate bar as a still, empty track; a sliding segment says it is busy. */
.job-status-body progress:indeterminate {
    background: linear-gradient(90deg, transparent 0 35%, var(--primary) 35% 65%, transparent 65% 100%)
        0 0 / 300% 100% color-mix(in srgb, var(--text) 10%, transparent);
    animation: job-slide 1.4s linear infinite;
}
.job-status-body progress:indeterminate::-moz-progress-bar { background: transparent; }
.job-status-body progress:indeterminate::-webkit-progress-value { background: transparent; }
@keyframes job-spin { to { rotate: 1turn; } }
@keyframes job-slide { from { background-position: 100% 0; } to { background-position: 0 0; } }

/* `ui:LazyFrame`: bars while a slow part loads, one muted line once it gave up. */
.lazy-frame { display: block; }
.skeleton {
    display: block;
    height: 0.9em;
    margin: 0.4em 0;
    border-radius: var(--r-s);
    background: color-mix(in srgb, var(--text) 8%, transparent);
    animation: skeleton-pulse 1.6s ease-in-out infinite;
}
.skeleton:last-of-type:not(:first-of-type) { width: 60%; }
.lazy-frame-failed {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin: 0;
    font-size: 0.9rem;
    color: var(--muted);
}
@keyframes skeleton-pulse { 50% { opacity: 0.45; } }

/* --- Fleet app switcher --- */

/* Links to fleet-id's app launcher; usage in README.md. Filled, not stroked
   — do NOT add a stroke or it clashes with the `svg.icon` base rule. */
.fleet-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.9rem;
    height: 1.9rem;
    padding: 0;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: none;
    color: var(--text-2);
    text-decoration: none;
}
.fleet-btn:hover { color: var(--text); border-color: var(--border-strong); }

.fleet-grid {
    width: 0.85rem;
    height: 0.85rem;
    flex: none;
    fill: currentColor;
}

/* --- Floating navigation --- */

.floating-nav {
    position: fixed;
    right: 1.25rem;
    bottom: 1.25rem;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    z-index: 50;
}
.fab {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.35rem;
    padding: 0.55rem 1rem;
    border: none;
    border-radius: 999px;
    background: var(--primary);
    color: var(--on-primary);
    font: inherit;
    font-size: 0.88rem;
    font-weight: 600;
    text-decoration: none;
    cursor: pointer;
    box-shadow: var(--shadow-2);
}
.fab:hover { background: var(--primary-hover); }
.fab-top { background: var(--text-2); color: var(--card); }
.fab-top:hover { background: var(--text); }

/* --- Charts --- */

/* Vertical bar chart. The sign is encoded twice (colour + label) so it stays
   readable without colour perception. */
.chart {
    display: flex;
    align-items: flex-end;
    gap: 0.4rem;
    height: 220px;
    padding: 1rem 1.1rem 0;
    margin: 0.5rem 0 1.25rem;
    background: var(--card);
    border: 1px solid var(--border);
    border-radius: var(--r-m);
    overflow-x: auto;
    box-shadow: var(--shadow-1);
}
.chart-col {
    flex: 1 1 0;
    min-width: 38px;
    display: flex;
    flex-direction: column;
    align-items: center;
    height: 100%;
}
.chart-bar-wrap {
    flex: 1;
    width: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-end;
}
.chart-value {
    font-size: 0.7rem;
    font-variant-numeric: tabular-nums;
    color: var(--text-2);
    margin-bottom: 0.2rem;
}
.chart-bar {
    width: 60%;
    max-width: 28px;
    min-height: 2px;
    border-radius: 4px 4px 0 0;
    background: var(--primary);
    transition: height 0.2s ease;
}
.chart-bar.pos { background: var(--chart-pos); }
.chart-bar.neg { background: var(--chart-neg); }
.chart-label {
    margin-top: 0.35rem;
    font-size: 0.72rem;
    color: var(--muted);
}

/* Horizontal bars inside a table column: one magnitude → one colour. */
.grid .bar-col { width: 40%; vertical-align: middle; }
.hbar-track {
    background: color-mix(in srgb, var(--text) 8%, transparent);
    border-radius: 999px;
    height: 0.7rem;
    overflow: hidden;
}
.hbar {
    height: 100%;
    min-width: 2px;
    background: var(--primary);
    border-radius: 999px;
}

/* --- Near me (`ui:NearMe`) --- */

.near-me-bar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--gap-inline);
    margin-bottom: 0.75rem;
}
.near-me-bar[hidden] { display: none; }
.near-me-bar .btn[aria-pressed="true"] {
    background: var(--primary);
    border-color: var(--primary);
    color: var(--on-primary);
}
.near-me-bar .btn[aria-busy="true"] { cursor: progress; }
.near-me-radius {
    padding: 0.4rem 0.6rem;
    border: 1px solid var(--border-strong);
    border-radius: var(--r-s);
    font: inherit;
    font-size: 0.88rem;
    background: var(--card);
    color: var(--text);
}
.near-me-status { font-size: 0.85rem; color: var(--text-2); }
.near-me-distance {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    font-size: 0.82rem;
    font-variant-numeric: tabular-nums;
    color: var(--text-2);
}
.near-me-distance[hidden] { display: none; }

/* The device's position on a Leaflet map. Leaflet paints paths with presentation attributes,
   which beat any layered rule, so only !important gets the token through. */
.near-me-you { stroke: var(--card) !important; fill: var(--primary) !important; fill-opacity: 1 !important; }
.near-me-accuracy {
    stroke: var(--primary) !important;
    fill: var(--primary) !important;
    fill-opacity: 0.12 !important;
    stroke-opacity: 0.5 !important;
}

/* --- Helpers --- */

/* The complete set — single-purpose, no raw values, no scale. A `.p-4` or
   `.text-sm` would make this a utility framework; a helper needing a number
   wants to be a component instead. */
.push-right { margin-left: auto; }
.nowrap { white-space: nowrap; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.grow { flex: 1; min-width: 0; }
.num { font-variant-numeric: tabular-nums; }
/* A value meant to be copied verbatim — share URLs, mostly. */
.mono { font-family: var(--font-mono); font-size: 0.85rem; }

/* --- Mobile tier --- */

/* Cross-cutting changes each component doesn't handle in its own rules. */

@media (max-width: 720px) {
    /* Every control reaches `--tap-min` (44px) in at least one axis here. */
    .btn,
    .segmented a,
    .segmented-pills a,
    .menu-list a,
    .menu-list button {
        min-height: var(--tap-min);
        display: inline-flex;
        align-items: center;
    }
    .btn, .segmented a, .segmented-pills a { justify-content: center; }
    .icon-btn { width: var(--tap-min); height: var(--tap-min); }
    .menu-list a, .menu-list button { padding-block: 0; }

    /* iOS zooms the page on focus below 16px and never zooms back out; the
       base scale is 15px, so this is a threshold, not a design choice. */
    input, select, textarea { font-size: 16px; }

    .container {
        margin: 1rem auto;
        padding: 0 max(1rem, env(safe-area-inset-right)) 0 max(1rem, env(safe-area-inset-left));
    }

    /* Actions read as a stack of full-width targets rather than a cramped row. */
    .actions > .btn { flex: 1 1 auto; }

    .toasts {
        top: calc(var(--topbar-h) + env(safe-area-inset-top) + 0.5rem);
        right: max(0.75rem, env(safe-area-inset-right));
        width: calc(100vw - 1.5rem);
    }

    .floating-nav {
        right: max(1rem, env(safe-area-inset-right));
        bottom: max(1rem, env(safe-area-inset-bottom));
    }
    .fab { min-height: var(--tap-min); }

    .chart { height: 170px; padding: 0.75rem 0.75rem 0; }

    .login-card { margin: 1.5rem auto; }
}

@media (max-width: 480px) {
    /* Re-prices the density tokens rather than restyling components. */
    :root {
        --gap-section: 1.1rem;
        --gap-grid: 0.9rem;
        --gap-tight: 0.6rem;
        --card-pad-x: 1rem;
        --card-pad-y: 0.9rem;
        --tile-min: 8.5rem;
    }

    h1 { font-size: 1.25rem; }

    .page-icon { width: 2.25rem; height: 2.25rem; font-size: 1.05rem; }
    .page-titles { gap: 0.65rem; }

    .kpi-value { font-size: 1.6rem; }
    .tile-lead .tile-value { font-size: 1.5rem; }

    /* Narrower cells fit more columns on screen before the wrapper has to
       scroll, which is cheaper than scrolling for the reader. */
    .grid th, .grid td { padding: 0.5rem 0.55rem; }
    .grid-sm th, .grid-sm td { padding: 0.3rem 0.45rem; }

    /* A term and its value stop sharing a line once the value needs the room. */
    dl { grid-template-columns: minmax(0, 1fr) auto; column-gap: 0.6rem; }

    .cards { grid-template-columns: minmax(0, 1fr); }

    .app-footer { margin-top: 1.75rem; padding: 0.9rem 1rem; }
}

/* --- View transitions --- */

/* Used once an app turns on Turbo's `<meta name="view-transition">`: the rail
   (the top bar below 720px), the tab bar and the page head hold still while
   the content cross-fades. Scoped to the transition because a named element
   is a stacking context, which would trap an open `.menu` in `.page-head`
   under the content that follows it. */
:root:active-view-transition .rail { view-transition-name: rail; }
:root:active-view-transition .tabbar { view-transition-name: tabbar; }
:root:active-view-transition .page-head { view-transition-name: page-head; }

/* A page transition is the only motion the system adds, and it is decoration
   over an already legible change. */
@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    ::view-transition-group(*),
    ::view-transition-old(*),
    ::view-transition-new(*) { animation: none; }
    .toasts .flash { animation: none; }
    .toasts .flash-success:not(:has(form, a)) { animation: toast-out 0s 5s forwards; }
    .job-status-running .job-status-icon,
    .job-status-body progress:indeterminate,
    .skeleton { animation: none; }
}

}
