/* Backstage dark chrome. The palette started from the Telerik AI Usage
   Monitoring template (navy/ink + amber) and now derives from the public
   homepage's hero decision board instead: same charcoal ground, same
   teal/amber/coral semantics, so the front door and the backstage read as one
   product. Component skins stay on kendo-theme-default; these variables
   restyle both the page chrome and the Kendo components around them.

   Two rules hold the system together:
   1. Surfaces step by lightness, and every neighbouring step stays visible
      (≥1.14:1, or ≥1.08:1 plus a solid divider). Near-black compresses
      contrast ratios, so a border does the work a shadow would do in light UI.
   2. Neutral text carries no hue of its own; color appears only where it
      means something. Interaction is teal, attention is amber, risk is coral,
      health is green, and plain information is blue. */

:root {
    /* ---- Surfaces: sidebar (deepest) → canvas → card → raised ---- */
    --backstage-sidebar: #0b1114;
    --backstage-bg: #121a1e;
    --backstage-surface: #1b262b;
    --backstage-surface-raised: #253338;

    /* Three borders, three jobs — a line's weight should match how much work it
       is doing. The hairline separates rows inside dense data, where there are
       hundreds of them and any strength adds up to noise. The divider marks the
       few real region boundaries. The control grade is what makes an input or a
       checkbox findable, so it must clear 3:1 against the surface behind it
       (WCAG 1.4.11) — using the divider on a control is what made the login
       fields look like empty space. */
    --backstage-hairline: #2a3840;
    --backstage-border: #3e4f56;
    --backstage-border-control: #71858b;

    /* ---- Text: three layers, no fourth ---- */
    --backstage-text: #edf2ef;
    --backstage-text-2: #c2cdc9;
    --backstage-text-meta: #a6b5b0;
    --backstage-text-quiet: #8b9994;
    /* Alias kept because the name is referenced widely; it resolves to the
       secondary layer so existing rules land on the new scale. */
    --backstage-text-subtle: var(--backstage-text-2);

    /* ---- Semantic color ---- */
    --backstage-teal: #4fd1c5;
    --backstage-teal-hover: #6fdad0;
    --backstage-teal-active: #3bb8ad;
    --backstage-amber: #f2b544;
    --backstage-amber-strong: #f7c766;
    --backstage-coral: #ff8a7a;
    --backstage-green: #3ecf8e;
    --backstage-info: #7fb3ef;
    /* Older names, kept as aliases so amber-tinted rules keep working while
       they migrate to their real role (attention, not "the accent color"). */
    --backstage-accent: var(--backstage-amber);
    --backstage-accent-strong: var(--backstage-amber-strong);
    --backstage-danger: var(--backstage-coral);

    /* Every fill sits on a light hue, so foreground on solid is always ink. */
    --backstage-on-solid: #0b1114;

    /* Washes and tinted borders are declared once per hue. Picking an ad-hoc
       alpha per rule is how the old palette ended up with thirteen different
       shades of the same amber. */
    --backstage-teal-wash: color-mix(in srgb, var(--backstage-teal) 12%, transparent);
    --backstage-teal-border: color-mix(in srgb, var(--backstage-teal) 45%, transparent);
    --backstage-amber-wash: color-mix(in srgb, var(--backstage-amber) 12%, transparent);
    --backstage-amber-border: color-mix(in srgb, var(--backstage-amber) 45%, transparent);
    --backstage-coral-wash: color-mix(in srgb, var(--backstage-coral) 12%, transparent);
    --backstage-coral-border: color-mix(in srgb, var(--backstage-coral) 45%, transparent);
    --backstage-green-wash: color-mix(in srgb, var(--backstage-green) 12%, transparent);
    --backstage-green-border: color-mix(in srgb, var(--backstage-green) 45%, transparent);
    --backstage-info-wash: color-mix(in srgb, var(--backstage-info) 12%, transparent);
    --backstage-info-border: color-mix(in srgb, var(--backstage-info) 45%, transparent);

    /* ---- Kendo swatch remap ----
       The vendored default-main-dark.css resolves its state colors from fixed
       OKLCH literals, not from a base hue, so every family has to be named
       here — overriding `primary` alone leaves hover, subtle and the on-*
       foregrounds on the stock palette. */
    --kendo-color-app-surface: var(--backstage-bg);
    --kendo-color-surface: var(--backstage-surface);
    --kendo-color-surface-alt: var(--backstage-sidebar);
    --kendo-color-on-app-surface: var(--backstage-text);
    --kendo-color-border: var(--backstage-border);
    --kendo-color-border-alt: var(--backstage-border-control);
    --kendo-color-base: var(--backstage-surface);
    --kendo-color-base-hover: var(--backstage-surface-raised);
    --kendo-color-base-active: var(--backstage-surface-raised);
    --kendo-color-base-emphasis: var(--backstage-border-control);
    --kendo-color-base-subtle: var(--backstage-surface-raised);
    --kendo-color-base-subtle-hover: #2c3b41;
    --kendo-color-base-subtle-active: #33444a;
    --kendo-color-base-on-subtle: var(--backstage-text);
    --kendo-color-base-on-surface: var(--backstage-text);
    --kendo-color-on-base: var(--backstage-text);

    /* Interaction is teal: buttons, selection, focus, "you are here". */
    --kendo-color-primary: var(--backstage-teal);
    --kendo-color-primary-hover: var(--backstage-teal-hover);
    --kendo-color-primary-active: var(--backstage-teal-active);
    --kendo-color-primary-emphasis: var(--backstage-teal-hover);
    --kendo-color-on-primary: var(--backstage-on-solid);
    --kendo-color-primary-subtle: var(--backstage-teal-wash);
    --kendo-color-primary-subtle-hover: color-mix(in srgb, var(--backstage-teal) 18%, transparent);
    --kendo-color-primary-subtle-active: color-mix(in srgb, var(--backstage-teal) 24%, transparent);
    --kendo-color-primary-on-subtle: var(--backstage-teal);
    --kendo-color-primary-on-surface: var(--backstage-teal);

    /* Attention and deadlines — never the page's main action. */
    --kendo-color-warning: var(--backstage-amber);
    --kendo-color-warning-hover: var(--backstage-amber-strong);
    --kendo-color-warning-active: #d99e2f;
    --kendo-color-warning-emphasis: var(--backstage-amber-strong);
    --kendo-color-on-warning: var(--backstage-on-solid);
    --kendo-color-warning-subtle: var(--backstage-amber-wash);
    --kendo-color-warning-subtle-hover: color-mix(in srgb, var(--backstage-amber) 18%, transparent);
    --kendo-color-warning-subtle-active: color-mix(in srgb, var(--backstage-amber) 24%, transparent);
    --kendo-color-warning-on-subtle: var(--backstage-amber-strong);
    --kendo-color-warning-on-surface: var(--backstage-amber);

    --kendo-color-error: var(--backstage-coral);
    --kendo-color-error-hover: #ff9f92;
    --kendo-color-error-active: #e5705f;
    --kendo-color-error-emphasis: #ff9f92;
    --kendo-color-on-error: var(--backstage-on-solid);
    --kendo-color-error-subtle: var(--backstage-coral-wash);
    --kendo-color-error-subtle-hover: color-mix(in srgb, var(--backstage-coral) 18%, transparent);
    --kendo-color-error-subtle-active: color-mix(in srgb, var(--backstage-coral) 24%, transparent);
    --kendo-color-error-on-subtle: var(--backstage-coral);
    --kendo-color-error-on-surface: var(--backstage-coral);

    --kendo-color-success: var(--backstage-green);
    --kendo-color-success-hover: #5cdaa1;
    --kendo-color-success-active: #33b87c;
    --kendo-color-success-emphasis: #5cdaa1;
    --kendo-color-on-success: var(--backstage-on-solid);
    --kendo-color-success-subtle: var(--backstage-green-wash);
    --kendo-color-success-subtle-hover: color-mix(in srgb, var(--backstage-green) 18%, transparent);
    --kendo-color-success-subtle-active: color-mix(in srgb, var(--backstage-green) 24%, transparent);
    --kendo-color-success-on-subtle: var(--backstage-green);
    --kendo-color-success-on-surface: var(--backstage-green);

    /* Plain information — freshness notes, timezone, system asides. Separate
       from primary on purpose: reading a notice is not an invitation to act. */
    --kendo-color-info: var(--backstage-info);
    --kendo-color-info-hover: #9bc4f3;
    --kendo-color-info-active: #6a9fdc;
    --kendo-color-info-emphasis: #9bc4f3;
    --kendo-color-on-info: var(--backstage-on-solid);
    --kendo-color-info-subtle: var(--backstage-info-wash);
    --kendo-color-info-subtle-hover: color-mix(in srgb, var(--backstage-info) 18%, transparent);
    --kendo-color-info-subtle-active: color-mix(in srgb, var(--backstage-info) 24%, transparent);
    --kendo-color-info-on-subtle: var(--backstage-info);
    --kendo-color-info-on-surface: var(--backstage-info);

    /* The theme ships `inherit`, so this is the single place the app's font is
       decided. Roboto matches the Telerik dashboard demos; the stack degrades
       to system sans if the webfont is unavailable. */
    --kendo-font-family: Roboto, system-ui, -apple-system, "Segoe UI",
        "Helvetica Neue", "Noto Sans TC", "Microsoft JhengHei", sans-serif;
}

html,
body {
    font-family: var(--kendo-font-family);
}

h1:focus {
    outline: none;
}

.backstage-body {
    margin: 0;
    min-height: 100vh;
    background:
        radial-gradient(1200px 500px at 80% -10%, #1d3a3f66 0%, transparent 60%),
        var(--backstage-bg);
    color: var(--backstage-text);
}

/* ---- Authenticated frame ---- */

.backstage-frame {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

.backstage-topbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.6rem 1.25rem;
    background: var(--backstage-surface);
    border-bottom: 1px solid var(--backstage-border);
    /* Brand plus four right-hand affordances exceed a phone's width; without
       wrapping here every page gets a horizontal scrollbar on mobile. */
    flex-wrap: wrap;
}

.backstage-brand {
    display: flex;
    align-items: center;
    gap: 0.6rem;
}

.backstage-brand-dot {
    width: 0.65rem;
    height: 0.65rem;
    border-radius: 50%;
    background: var(--backstage-amber);
    box-shadow: 0 0 8px 1px color-mix(in srgb, var(--backstage-amber) 50%, transparent);
}

.backstage-brand-name {
    font-weight: 600;
    letter-spacing: 0.02em;
}

.backstage-topbar-right {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
    justify-content: flex-end;
}

.topbar-menu-btn.k-button {
    color: var(--backstage-text-subtle);
}

/* The governance half of the header's pitch: live data, write path shut.
   Reads as a status, not a control — no hover state on purpose. */
.topbar-mode {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    padding: 0.3rem 0.75rem;
    border: 1px solid var(--backstage-border);
    border-radius: 999px;
    color: var(--backstage-text-subtle);
    font-size: 0.82rem;
    white-space: nowrap;
    cursor: default;
}

.topbar-mode::before {
    content: "";
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 50%;
    background: var(--backstage-green);
    box-shadow: 0 0 6px 1px var(--backstage-green-border);
}

/* The engineering half of the pitch. A labeled pill instead of a bare icon:
   the hiring manager should read "repo + agent skills" without hovering. */
.topbar-repo {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.3rem 0.75rem;
    border: 1px solid var(--backstage-border);
    border-radius: 999px;
    color: var(--backstage-text-subtle);
    font-size: 0.82rem;
    white-space: nowrap;
    text-decoration: none;
}

.topbar-repo:hover {
    border-color: var(--backstage-teal);
    color: var(--backstage-teal);
}

.topbar-repo svg {
    width: 16px;
    height: 16px;
    flex: none;
}

.topbar-account.k-button {
    color: var(--backstage-text-subtle);
}

/* The account popover: identity on top, then the owner-only actions. */
.topbar-account-popover .account-menu {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    min-width: 12rem;
    padding: 0.25rem;
}

.account-name {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.35rem 0.5rem 0.55rem;
    border-bottom: 1px solid var(--backstage-border);
    color: var(--backstage-text-subtle);
    font-size: 0.9rem;
}

.account-lang {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.35rem 0.5rem;
    border-radius: 4px;
    color: var(--backstage-text-subtle);
    font-size: 0.9rem;
    text-decoration: none;
}

.account-lang:hover {
    color: var(--backstage-teal);
}

.account-menu .logout-form .k-button {
    width: 100%;
    justify-content: flex-start;
    color: var(--backstage-text-subtle);
}

/* ---- Drawer navigation ---- */

.backstage-drawer {
    flex: 1;
    min-height: 0;
}

/* The rail sits deepest in the stack so the work area reads as the lit
   surface. The lightness step alone is small at this end of the scale, so the
   solid right border is what actually draws the boundary. */
.backstage-drawer .k-drawer {
    background: var(--backstage-sidebar);
    border-color: var(--backstage-border);
    color: var(--backstage-text);
}

.backstage-drawer .k-drawer-content {
    background: transparent;
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.backstage-nav {
    display: flex;
    flex-direction: column;
    padding: 0.6rem 0;
}

/* Strategy rail: the drawer's expanded state leads with today's to-do pulse
   (date, attention buckets, funnel); navigation follows below. */
.rail {
    padding: 0.85rem 0.9rem 0.6rem;
    border-bottom: 1px solid var(--backstage-border);
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.rail-date {
    color: var(--backstage-text-2);
    font-size: 0.85rem;
}

.rail-head {
    color: var(--backstage-text-meta);
    font-size: 0.75rem;
    font-weight: 500;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    margin-top: 0.45rem;
}

/* A bucket is a filter you can click, so its selected state is teal like every
   other "you are here" in the shell. The counts inside keep their own meaning
   colors — those say how urgent, not where you are. */
.rail-bucket {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 0.5rem;
    width: 100%;
    font: inherit;
    font-size: 0.85rem;
    color: var(--backstage-text-2);
    background: none;
    border: 1px solid var(--backstage-border);
    border-radius: 0.5rem;
    padding: 0.35rem 0.6rem;
    cursor: pointer;
    text-align: left;
}

.rail-bucket:hover {
    border-color: var(--backstage-teal-border);
}

.rail-bucket.is-active {
    border-color: var(--backstage-teal);
    background: var(--backstage-teal-wash);
}

.rail-bucket strong {
    font-variant-numeric: tabular-nums;
}

.rail-bucket--reply strong {
    color: var(--backstage-coral);
}

.rail-bucket--offer strong {
    color: var(--backstage-green);
}

/* Nothing in the queue is not an outcome. Zero owed replies is the good day,
   and zero offers to review is simply an empty list — neither should borrow
   the color that means risk or good news. */
.rail-bucket.is-empty strong {
    color: var(--backstage-text-quiet);
}

.rail-funnel {
    color: var(--backstage-text-meta);
    font-size: 0.8rem;
    font-weight: 500;
    line-height: 1.5;
}

.rail-quiet {
    color: var(--backstage-text-quiet);
    font-size: 0.8rem;
    font-weight: 500;
}

.nav-section {
    padding: 0.5rem 1rem 0.3rem;
    color: var(--backstage-text-meta);
    font-size: 0.75rem;
    font-weight: 500;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.nav-item {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0.5rem 1rem;
    color: var(--backstage-text-2);
    text-decoration: none;
    border-left: 3px solid transparent;
    white-space: nowrap;
}

.nav-item:hover {
    background: var(--backstage-surface);
    color: var(--backstage-text);
}

/* Where you are, not what needs attention: the current page is teal, and the
   amber stays free to mean "this one is overdue". */
.nav-item.is-active {
    background: var(--backstage-teal-wash);
    border-left-color: var(--backstage-teal);
    color: var(--backstage-teal);
    font-weight: 600;
}

.nav-item.is-planned {
    color: var(--backstage-text-quiet);
}

.nav-item-label {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
}

.nav-badge {
    font-size: 0.75rem;
    font-weight: 500;
    color: var(--backstage-text-meta);
    border: 1px solid var(--backstage-border);
    border-radius: 0.6rem;
    padding: 0.05rem 0.5rem;
}

/* Live pipeline warning. The icon carries the meaning so the badge does not
   depend on the amber alone, and a bare count cannot be misread as "3 items". */
.nav-badge--alert {
    display: inline-flex;
    align-items: center;
    gap: 0.2rem;
    color: var(--backstage-amber);
    border-color: var(--backstage-amber-border);
    background: var(--backstage-amber-wash);
    font-variant-numeric: tabular-nums;
}

/* MiniMode hides every label and badge, so the warning becomes a dot on the
   icon — otherwise a collapsed drawer would quietly hide the one thing this
   badge exists to surface. */
.backstage-nav.is-mini .nav-item.is-alert {
    position: relative;
}

.backstage-nav.is-mini .nav-item.is-alert::after {
    content: "";
    position: absolute;
    top: 0.4rem;
    left: 1.6rem;
    width: 0.45rem;
    height: 0.45rem;
    border-radius: 50%;
    background: var(--backstage-amber);
    box-shadow: 0 0 0 2px var(--backstage-sidebar);
}

.backstage-main {
    flex: 1;
    padding: 1.5rem;
    min-width: 0;
}

/* Timezone switch notice — appears once, on the load after the laptop
   changed country, and explains why the dates moved. */
.tz-notice {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    flex-wrap: wrap;
    margin-bottom: 1rem;
    padding: 0.55rem 0.9rem;
    border: 1px solid var(--backstage-info-border);
    border-radius: 6px;
    background: var(--backstage-info-wash);
    color: var(--backstage-info);
    font-size: 0.9rem;
}

.tz-notice-action {
    background: none;
    border: 1px solid currentColor;
    border-radius: 4px;
    color: inherit;
    cursor: pointer;
    font: inherit;
    padding: 0.1rem 0.55rem;
}

.tz-notice-dismiss {
    background: none;
    border: none;
    color: inherit;
    cursor: pointer;
    font: inherit;
    margin-left: auto;
}

/* The rail date names its own calendar, so「今天」is never a guess. */
.rail-zone {
    color: var(--backstage-text-subtle);
    font-size: 0.75rem;
    font-weight: 500;
    margin-left: 0.35rem;
}

/* ---- Workbench page chrome ---- */

.wb-page {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.wb-header {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
}

.wb-title {
    color: var(--backstage-accent);
    font-size: 1.6rem;
    font-weight: 600;
    margin: 0 0 0.15rem;
}

.wb-subtitle {
    color: var(--backstage-text-subtle);
    margin: 0;
    font-size: 0.95rem;
}

.wb-header-side {
    display: flex;
    align-items: center;
    gap: 0.9rem;
    /* Two labelled buttons do not fit beside the heading on a phone; without
       this the whole page gains a horizontal scrollbar. */
    flex-wrap: wrap;
}

.wb-asof {
    color: var(--backstage-text-subtle);
    font-size: 0.85rem;
}

.wb-filterbar {
    display: flex;
    align-items: center;
    gap: 0.75rem;
}

/* An active filter is a state the owner put the page into, so it reads teal
   like the rest of "you are here". */
.wb-filter-chip {
    display: inline-block;
    padding: 0.2rem 0.7rem;
    border-radius: 0.7rem;
    border: 1px solid var(--backstage-teal-border);
    background: var(--backstage-teal-wash);
    color: var(--backstage-teal);
    font-size: 0.85rem;
}

.wb-filter-clear {
    color: var(--backstage-text-subtle);
    font-size: 0.85rem;
}

/* Grid + detail panel split. The panel column appears only when a row is
   selected (route-carried). The base rules describe the stacked (narrow)
   layout; the two-column split and the fixed-shell height chain live in the
   desktop media blocks below, so narrow viewports never inherit them. */
.wb-split {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 1rem;
}

.wb-split-panel {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

/* Negated rather than bounded, so this is the exact complement of the desktop
   query below. The media width is fractional whenever the display scales
   (measured 1100.622px at dpr 1.65), and any numeric pair leaves a band that
   neither query matches — 1100.98/1101 still misses 1100.984375, which is
   representable at the 1/64px precision browsers resolve to. In that band the
   panel keeps source order and drops below the grid instead of stacking above
   it. A negation cannot leave a band. */
@media not all and (min-width: 1101px) {
    /* Stacked: the answer strip goes above the grid, first thing on screen. */
    .wb-split-panel {
        order: -1;
    }
}

@media (min-width: 1101px) {
    .wb-split--open {
        grid-template-columns: minmax(0, 1fr) minmax(360px, 420px);
    }

    /* Wide panel mode: two named widths instead of a drag handle (decision
       record: docs/backstage-telerik-polish-plan-20260804.md). */
    .wb-split--open.wb-split--wide {
        grid-template-columns: minmax(0, 1fr) minmax(420px, 560px);
    }
}

/* ---- Fixed app shell (desktop) ----
   Three independent scroll regions: drawer rail, grid data area, detail
   panel. The shell pins topbar / page title / grid header / pager so the
   attention rail and column context never scroll away mid-triage. Height
   gate: on very short viewports a pinned shell would crush the grid, so
   those fall back to normal document scrolling. */
@media (min-width: 1101px) and (min-height: 480px) {
    .backstage-frame {
        height: 100vh;
        min-height: 0;
        overflow: hidden;
    }

    /* Telerik pins the container to align-items: flex-start, which lets
       .k-drawer-content grow past the fixed shell instead of stretching to
       it — the whole height chain below depends on this override. */
    .backstage-drawer.k-drawer-container {
        align-items: stretch;
    }

    /* Left region: the drawer column scrolls its own rail + nav. */
    .backstage-drawer .k-drawer {
        overflow-y: auto;
    }

    .backstage-drawer .k-drawer-content {
        min-height: 0;
    }

    /* Non-workbench pages (Sources, planned stubs) keep scrolling here;
       the workbench page sizes itself to fit, so no scrollbar appears. */
    .backstage-main {
        display: flex;
        flex-direction: column;
        min-height: 0;
        overflow-y: auto;
    }

    /* Only a page that scrolls its own insides may fill the shell. Applied to
       every .wb-page, this quietly ate content: a page whose children are
       plain blocks gets them flex-shrunk instead, and anything with its own
       `overflow: hidden` (the Sources list has it, for rounded corners) then
       clips the overflow with no scrollbar anywhere to reveal it. Pages
       without this class stay content-sized and let .backstage-main scroll. */
    .wb-page--fills {
        flex: 1;
        min-height: 0;
    }

    .wb-split {
        flex: 1;
        min-height: 0;
        align-items: stretch;
        /* The implicit row is auto-sized to content by default, which would
           blow the row past the fixed shell; pin it to the container. */
        grid-template-rows: minmax(0, 1fr);
    }

    .wb-split-main {
        min-height: 0;
        display: flex;
        flex-direction: column;
    }

    /* Middle region: the grid gets a definite height so Height="100%"
       resolves and the data area scrolls under a fixed header + pager. */
    .wb-split-main .aag-root,
    .wb-split-main .aag-wrap {
        flex: 1;
        min-height: 0;
        display: flex;
        flex-direction: column;
    }

    /* Right region: the panel caps at the column height and .adp-root
       scrolls internally — same behavior the old sticky rule produced,
       minus the viewport math that ignored the topbar. */
    .wb-split-panel {
        max-height: 100%;
    }

    .wb-split-panel > * {
        max-height: 100%;
        min-height: 0;
    }
}

.wb-error {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    color: var(--backstage-coral);
    background: var(--backstage-coral-wash);
    border: 1px solid var(--backstage-coral-border);
    border-radius: 0.5rem;
    padding: 0.6rem 0.9rem;
}

/* ---- Sources page: flat pipeline list ---- */

.src-summary {
    color: var(--backstage-text-subtle);
    margin: 0;
    font-size: 0.95rem;
}

.src-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    background: var(--backstage-surface);
    border: 1px solid var(--backstage-border);
    border-radius: 0.6rem;
    overflow: hidden;
}

.src-row {
    display: flex;
    align-items: flex-start;
    gap: 0.8rem;
    padding: 0.85rem 1rem;
}

.src-row + .src-row {
    border-top: 1px solid var(--backstage-hairline);
}

/* Status is carried by icon shape + sentence, never by color alone; the tint
   only reinforces it. */
.src-icon {
    margin-top: 0.1rem;
}

.src-row--healthy .src-icon {
    color: var(--backstage-green);
}

.src-row--warning .src-icon {
    color: var(--backstage-amber);
}

.src-row--idle .src-icon {
    color: var(--backstage-text-subtle);
}

.src-row--unavailable .src-icon {
    color: var(--backstage-danger);
}

.src-body {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.src-name {
    font-weight: 600;
}

.src-sentence {
    color: var(--backstage-text-2);
    font-size: 0.92rem;
    line-height: 1.5;
}

/* The action-first summary: the attention line reads at body strength, the
   quiet remainder drops to a sub-line so the eye lands on the problem. */
.src-summary--attention {
    color: var(--backstage-text);
    font-size: 1rem;
}

.src-summary-rest {
    color: var(--backstage-text-subtle);
    font-size: 0.85rem;
    margin-top: -0.6rem;
}

/* Cadence chip: a property of the pipeline, not of today's data — muted so it
   reads as context, never as a status. */
.src-cadence {
    margin-left: 0.5rem;
    font-size: 0.75rem;
    font-weight: 400;
    color: var(--backstage-text-subtle);
    border: 1px solid var(--backstage-border);
    border-radius: 0.6rem;
    padding: 0 0.45rem;
    white-space: nowrap;
}

/* Closed-loop guidance: impact above the facts, next step and recovery below
   them. Same muted tone — judgment support, not more alarm. */
.src-impact,
.src-next-step,
.src-recovery {
    color: var(--backstage-text-subtle);
    font-size: 0.85rem;
    line-height: 1.5;
}

.src-guidance-label {
    font-weight: 600;
    color: var(--backstage-info);
    margin-right: 0.35rem;
}

/* An idle row's "next step" is conditional — run it when you want fresh data,
   not because something is owed. It must not compete for the eye with the
   rows the summary actually counted as needing action. */
.src-row--idle .src-guidance-label {
    color: var(--backstage-text-subtle);
    font-weight: 500;
}

/* Rules dialog styles live here (global), not in a scoped file: the Telerik
   popup renders at the root, outside the page's scope attribute. */
.src-rules-dialog .src-rules-intro {
    color: var(--backstage-text-subtle);
    font-size: 0.9rem;
    line-height: 1.6;
    margin: 0 0 0.9rem;
}

.src-rules-dialog .src-rules-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.88rem;
}

.src-rules-dialog .src-rules-table th {
    text-align: left;
    color: var(--backstage-text-subtle);
    font-weight: 600;
    padding: 0.35rem 0.6rem;
    border-bottom: 1px solid var(--backstage-border);
}

.src-rules-dialog .src-rules-table td {
    padding: 0.4rem 0.6rem;
    border-bottom: 1px solid var(--backstage-border);
    vertical-align: top;
    line-height: 1.5;
}

.src-rules-dialog .src-rules-table td:first-child {
    white-space: nowrap;
    color: var(--backstage-text-subtle);
}

.src-rules-dialog .src-rules-table td:last-child {
    white-space: nowrap;
}

.src-sublist {
    list-style: none;
    margin: 0.1rem 0 0;
    padding: 0 0 0 0.9rem;
    border-left: 2px solid var(--backstage-border);
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.src-sublist li {
    display: flex;
    align-items: baseline;
    gap: 0.6rem;
    flex-wrap: wrap;
    font-size: 0.85rem;
    color: var(--backstage-text-subtle);
}

.src-gap-name {
    color: var(--backstage-text-2);
}

/* A linked gap goes straight to its case; underline so the affordance never
   rides on hue alone. */
a.src-gap-name {
    text-decoration: underline;
    text-underline-offset: 2px;
}

a.src-gap-name:hover {
    color: var(--backstage-teal);
}

.src-gap-surface {
    font-size: 0.78rem;
    font-weight: 500;
    border: 1px solid var(--backstage-border);
    border-radius: 0.6rem;
    padding: 0 0.45rem;
}

.src-time {
    margin-left: auto;
    color: var(--backstage-text-subtle);
    font-size: 0.85rem;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* ---- Shell placeholder (phase 1 only) ---- */

.shell-placeholder {
    max-width: 40rem;
    margin: 14vh auto 0;
    text-align: center;
}

.shell-heading {
    color: var(--backstage-text);
    font-size: 2rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    margin-bottom: 0.75rem;
}

.shell-note {
    color: var(--backstage-text-subtle);
    font-size: 1.05rem;
}

/* ---- Data tables (every page) ----

   The theme rules a vertical line down the start edge of every cell, which cuts
   each case into as many fragments as there are columns. A queue is read along
   the row, so the row is what earns a line: verticals go, and a hairline under
   each row does the separating. The header keeps its full-strength underline —
   that boundary is real — and the locked column keeps a visible right edge, or
   nothing would say where the frozen part ends. */

/* The table is the page's content, not a panel floating on top of it, so it
   does not get an outline. A divider-grade frame around it read as a boxed
   widget; the header underline and the row hairlines already say where the
   data starts and stops. */
.backstage-body .k-grid,
.backstage-body .k-grid .k-grid-pager {
    border-color: var(--backstage-hairline);
}

.backstage-body .k-grid .k-table-th,
.backstage-body .k-grid .k-table-td {
    border-inline-start-color: transparent;
}

.backstage-body .k-grid .k-table-td {
    border-block-end: 1px solid var(--backstage-hairline);
}

.backstage-body .k-grid .k-table-th {
    border-block-end-color: var(--backstage-border);
}

.backstage-body .k-grid .k-grid-content-sticky,
.backstage-body .k-grid .k-grid-header-sticky {
    border-inline-end-color: var(--backstage-border);
}

/* ---- Form controls (every page) ----

   An input has to look like a place you can type before it is focused. The
   theme leaves it on the same surface as the card behind it, which at this end
   of the lightness scale is invisible, so inputs step up one surface and take
   the control-grade border. */

.backstage-body .k-input,
.backstage-body .k-picker {
    background-color: var(--backstage-surface-raised);
    border-color: var(--backstage-border-control);
}

.backstage-body .k-input:hover,
.backstage-body .k-picker:hover {
    border-color: var(--backstage-text-meta);
}

.backstage-body .k-checkbox:not(:checked),
.backstage-body .k-radio:not(:checked) {
    background-color: var(--backstage-surface-raised);
    border-color: var(--backstage-border-control);
}

/* ---- Login ---- */

.login-viewport {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1.5rem;
}

.login-card {
    width: 100%;
    max-width: 24rem;
    background: var(--backstage-surface);
    border: 1px solid var(--backstage-border);
    border-radius: 0.75rem;
    padding: 2rem 1.75rem;
    box-shadow: 0 18px 45px -18px rgb(0 0 0 / 56%);
}

/* The page title is a title, not a call to action: plain text, so amber is
   free to mean "attention" and teal to mean "press this". */
.login-heading {
    color: var(--backstage-text);
    font-size: 1.35rem;
    font-weight: 600;
    margin: 0 0 0.35rem;
    text-align: center;
}

.login-subtitle {
    color: var(--backstage-text-subtle);
    font-size: 0.85rem;
    text-align: center;
    margin: 0 0 1.25rem;
}

.login-error {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    color: var(--backstage-coral);
    background: var(--backstage-coral-wash);
    border: 1px solid var(--backstage-coral-border);
    border-radius: 0.5rem;
    padding: 0.5rem 0.75rem;
    margin-bottom: 1rem;
    font-size: 0.9rem;
}

.login-form {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.login-field {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
}

.login-label {
    color: var(--backstage-text-subtle);
    font-size: 0.85rem;
}

.login-input {
    width: 100%;
}

/* Chromium paints the autofilled inner <input> pale blue while the k-input
   wrapper and the suffix button keep the dark theme — that split is what makes
   the password field look broken. Background cannot be overridden directly on
   :autofill, so the inset box-shadow trick repaints the input in the wrapper's
   own surface color; text/caret colors follow the theme. */
.login-card input:-webkit-autofill,
.login-card input:-webkit-autofill:hover,
.login-card input:-webkit-autofill:focus {
    -webkit-text-fill-color: var(--backstage-text);
    caret-color: var(--backstage-text);
    box-shadow: inset 0 0 0 1000px var(--backstage-surface-raised);
    transition: background-color 999999s;
}

.login-card input:autofill {
    -webkit-text-fill-color: var(--backstage-text);
    caret-color: var(--backstage-text);
    box-shadow: inset 0 0 0 1000px var(--backstage-surface-raised);
}

/* The reveal toggle reads as part of the field, not a sibling control: fixed
   square footprint, no own rounding, quiet color until hover/selected. */
.login-card .login-reveal {
    width: 2.5rem;
    align-self: stretch;
    border-radius: 0;
    color: var(--backstage-text-subtle);
}

.login-card .login-reveal:hover,
.login-card .login-reveal.k-selected {
    color: var(--backstage-text);
}

.login-remember {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    color: var(--backstage-text-subtle);
    font-size: 0.9rem;
    min-height: 2.75rem;
    cursor: pointer;
}

/* Bigger than the theme default so the box is a comfortable target; the fill
   and border come from the shared control rules above, and the checked state
   inherits the teal primary tokens. */
.login-remember .k-checkbox {
    width: 1.25rem;
    height: 1.25rem;
}

.login-remember:hover .k-checkbox:not(:checked) {
    border-color: var(--backstage-text-meta);
}

.login-submit {
    width: 100%;
}

.login-langs {
    display: flex;
    justify-content: center;
    gap: 0.5rem;
    margin-top: 1.25rem;
    font-size: 0.85rem;
}

.login-lang {
    color: var(--backstage-text-subtle);
    text-decoration: none;
}

.login-lang.is-active {
    color: var(--backstage-teal);
    font-weight: 600;
}

.login-lang-sep {
    color: var(--backstage-border);
}

@media (max-width: 480px) {
    .login-card {
        padding: 1.5rem 1.25rem;
    }
}

/* ---- Blazor error bar (template default, dark-adjusted) ---- */

#blazor-error-ui {
    color-scheme: light only;
    background: lightyellow;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    box-sizing: border-box;
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
    color: #111;
}

#blazor-error-ui .dismiss {
    cursor: pointer;
    position: absolute;
    right: 0.75rem;
    top: 0.5rem;
}

/* Slim dark scrollbars: the browser default renders as a wide light bar that
   glows against the dark canvas. Firefox takes the two properties; WebKit
   browsers take the pseudo-elements. */
* {
    scrollbar-width: thin;
    scrollbar-color: var(--backstage-border) var(--backstage-bg);
}

::-webkit-scrollbar {
    width: 8px;
    height: 8px;
}

::-webkit-scrollbar-track {
    background: var(--backstage-bg);
}

::-webkit-scrollbar-thumb {
    background: var(--backstage-border);
    border-radius: 4px;
}

::-webkit-scrollbar-thumb:hover {
    background: var(--backstage-border-control);
}
