/* App styles: bk-* building blocks for the redesigned UI, base look comes from MudBlazor */

/* ===== Design tokens ======================================================
   The one place corner radii, depth, spacing, timing and status colors are
   defined. Every stylesheet (this one and every scoped *.razor.css) uses
   these instead of raw values - CssTokenGuardTests fails the build on a raw
   border-radius or transition timing anywhere else. Custom properties are
   inherited, so the tokens reach scoped component CSS and MudBlazor's own
   elements alike.

   Corners: xs for hairline bars and tiny marks, sm for chips and small
   controls, md for buttons, fields and rows, lg for cards and panels, xl for
   hero surfaces (the login card), pill for fully rounded ends, round for
   circles.

   Spacing: a 4px scale. Timing: fast for hover/press feedback, base for
   state changes, slow for larger surfaces; ease-out for things arriving,
   spring for a small overshoot on toggles and landings. */
:root {
    --bk-radius-xs: 4px;
    --bk-radius-sm: 8px;
    --bk-radius-md: 12px;
    --bk-radius-lg: 18px;
    --bk-radius-xl: 28px;
    --bk-radius-pill: 999px;
    --bk-radius-round: 50%;

    --bk-space-1: 4px;
    --bk-space-2: 8px;
    --bk-space-3: 12px;
    --bk-space-4: 16px;
    --bk-space-6: 24px;
    --bk-space-8: 32px;

    --bk-dur-fast: 120ms;
    --bk-dur-base: 200ms;
    --bk-dur-slow: 320ms;
    --bk-ease-out: cubic-bezier(0.22, 1, 0.36, 1);
    --bk-ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
    /* Gap between two items of a staggered entrance (see Motion below). */
    --bk-stagger: 35ms;

    /* Status hues - the pipeline's color language (StatusDisplay): one hue
       per state, from which every pill, tile and callout mixes its fill and
       text against the active theme's own tone (see .bk-pill below). */
    --bk-hue-draft: #888780;
    --bk-hue-diagnosis: #B23B85;
    --bk-hue-architecture: #6B62D6;
    --bk-hue-approval: #BA7517;
    --bk-hue-development: #378ADD;
    --bk-hue-qa: #1D9E75;
    --bk-hue-merge: #D4537E;
    --bk-hue-done: #639922;
    --bk-hue-paused: #E24B4A;
    --bk-hue-archived: #7B74C9;
}

/* Depth: the neumorphic shadow levels, drawn from two shadow colors mixed
   out of the active tone (--bk-soft-hi up-left, --bk-soft-lo down-right).
   Declared on every element that defines those two colors - the app's body
   (via :has, since menus and dialogs render outside MudLayout) and the
   sign-in page's shell (LoginLayout.razor.css) - because a custom property
   resolves the var() references inside it where it is declared.
   inset-sm/inset: pressed in (small marks / fields, wells, held controls);
   raised-sm: controls; raised: cards and panels; lift-sm/lift: a pill or a
   card under the pointer; floating: menus, dialogs, sticky bars; ring: keyboard focus;
   pressed-accent: a filled accent control held down. */
body:has(.bk-soft),
.bk-login-shell {
    --bk-depth-inset-sm: inset 1px 1px 3px var(--bk-soft-lo), inset -1px -1px 3px var(--bk-soft-hi);
    --bk-depth-inset: inset 3px 3px 7px var(--bk-soft-lo), inset -3px -3px 7px var(--bk-soft-hi);
    --bk-depth-raised-sm: 3px 3px 8px var(--bk-soft-lo), -3px -3px 8px var(--bk-soft-hi);
    --bk-depth-raised: 7px 7px 16px var(--bk-soft-lo), -7px -7px 16px var(--bk-soft-hi);
    --bk-depth-lift-sm: 5px 5px 12px var(--bk-soft-lo), -5px -5px 12px var(--bk-soft-hi);
    --bk-depth-lift: 10px 10px 22px var(--bk-soft-lo), -10px -10px 22px var(--bk-soft-hi);
    --bk-depth-floating: 14px 14px 32px var(--bk-soft-lo), -14px -14px 32px var(--bk-soft-hi);
    --bk-depth-pressed-accent: inset 3px 3px 7px rgba(0, 0, 0, 0.28), inset -2px -2px 6px rgba(255, 255, 255, 0.18);
    --bk-ring: 0 0 0 2px rgba(var(--mud-palette-primary-rgb), 0.4);
}

h1:focus {
    outline: none;
}

/* Responsive shell (MainLayout). The sidebar is a persistent drawer at Sm and up
   and collapses into an overlay drawer plus the mobile app bar below Sm.
   MudBlazor's Sm breakpoint is 600px, so mobile styles use max-width 599.98px. */

/* Content padding: no app bar space on wider screens (there is no app bar there),
   restore it below Sm where the mobile app bar (Dense, 48px toolbar) is shown. */
.bk-main-content {
    padding-top: 0 !important;
}

/* Height of the phone bottom bar (feature "Phone navigation at the bottom
   edge"), one shared value so every scoped stylesheet that has to size its
   own content around the bar (ChatPanel.razor.css, ChatSessionsSidebar.razor.css)
   agrees with the bar's own rule further below instead of guessing at it a
   second time. A plain custom property, not a *.razor.css-local one: it has
   to reach across component boundaries, which Blazor CSS isolation's scope
   attribute would otherwise block. */
:root {
    --bk-phone-bar-h: 60px;
}

/* Hidden on every viewport by default; the phone media query below is the
   only place that ever shows these. Kept outside that query so a component
   using one of these classes never renders visible, unstyled markup for the
   brief moment before app.css's cascade is resolved, and so tablet/desktop
   need no rule of their own to hide what the mobile shell only ever shows
   below 600px. */
.bk-phone-nav,
.bk-phone-scrim,
.bk-phone-sheet {
    display: none;
}

.bk-page {
    padding: 24px;
    max-width: min(96vw, 1600px);
}

/* A chat page is a WORKING SURFACE, not a reading column, so the cap above
   does not apply to it: a conversation next to a draft tree or an artifact
   uses whatever width the screen has. The cap plus the missing auto margins
   left everything past it as one dead strip on the right - 1240px of it on a
   3440px ultrawide, wider than a laptop screen. What stays bounded is the
   CONTENT of each column (.bk-thread and the composer below, the artifact
   body in ChatArtifactPanel), which is what actually has a readable maximum;
   the columns themselves fill.
   :has() rather than a class on .bk-page: the page container is MainLayout's,
   and no page can reach it - while every chat kind renders .bk-chat-page
   through ChatPanel. */
.bk-page:has(.bk-chat-page) {
    max-width: none;
    /* No vertical padding either: the chat page sizes itself to the window
       (ChatPanel.razor.css / ChatSessionsSidebar.razor.css, whose height
       formulas account for exactly what is left), so the old 24px above and
       below only shortened the one element that was already full height - it
       bought nothing and cost two rows of transcript. What is left is a small
       inset at the top, so the breadcrumb does not sit flush against the
       browser's own chrome. The horizontal padding stays: that one separates
       content from the window edge. */
    padding-block: 12px 0;
}

/* Ultra-wide monitors get more room than the default cap so the page does not
   leave large empty margins on either side. */
@media (min-width: 1920px) {
    .bk-page {
        max-width: min(96vw, 2200px);
    }
}

/* Clickable brand logo plus name (drawer header and mobile app bar). The product
   is branded Buildbit on screen while the code keeps the BuildKit name. Links to
   the overview; inherits text color and drops the underline so it reads as branding.
   The padding is cancelled by an equal negative margin so the logo keeps its place
   while the clickable area highlights on hover like the MudNavLink nav items. */
.bk-brand {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
    text-decoration: none;
    color: inherit;
    padding: 4px 6px;
    margin: -4px -6px;
    border-radius: var(--bk-radius-sm);
    transition: background-color var(--bk-dur-fast) var(--bk-ease-out);
}

.bk-brand:hover {
    text-decoration: none;
    background: var(--mud-palette-action-default-hover);
}

/* Brand name next to the logo, set in the same Space Grotesk as the login page's
   wordmark (loaded in App.razor). Wins over MudText's typography class because
   app.css loads after MudBlazor.min.css. */
.bk-brand-name {
    font-family: "Space Grotesk", "Inter", system-ui, sans-serif;
    font-weight: 600;
    letter-spacing: -0.01em;
}

/* Neumorphism ("soft UI"), the design every color theme shares (ThemePresets;
   MainLayout puts bk-soft on MudLayout in both modes). The palette gives page,
   surfaces, drawer and app bar one tone; every shape here comes from two
   shadows instead - a light one up-left and a dark one down-right. Raised for
   panels and controls, pressed in (inset) for inputs, the current page's
   navigation entry and any control while it is held down.

   Both shadow colors are mixed from the active palette's own background, so
   one set of rules fits every theme in light and dark mode and follows a live
   switch with no per-theme CSS. They sit on body (via :has) rather than on
   MudLayout, because menus, dialogs and snackbars render outside MudLayout
   and are shaped with the same shadows; the depth levels built from them are
   the --bk-depth-* tokens at the top of this file. */
body:has(.bk-soft) {
    --bk-soft-hi: color-mix(in srgb, var(--mud-palette-background) 25%, #FFFFFF);
    --bk-soft-lo: color-mix(in srgb, var(--mud-palette-background) 80%, #000000);
}

/* Dark tones need a much deeper shade, and only a faint highlight - a bright
   one would read as a grey smear rather than as light. */
body:has(.bk-soft.bk-dark) {
    --bk-soft-hi: color-mix(in srgb, var(--mud-palette-background) 90%, #FFFFFF);
    --bk-soft-lo: color-mix(in srgb, var(--mud-palette-background) 45%, #000000);
}

/* Panels: the app's cards are outlined MudPapers - the outline gives way to
   the raised shape. */
.bk-soft .mud-paper-outlined,
body:has(.bk-soft) .mud-popover,
body:has(.bk-soft) .mud-dialog {
    border-color: transparent;
    border-radius: var(--bk-radius-lg);
    box-shadow: var(--bk-depth-raised);
}

/* Menus and dialogs float above the page rather than rising out of it: a
   deeper, softer shadow (and, with motion, they grow into it - see Motion). */
body:has(.bk-soft) .mud-popover:not(.mud-tooltip),
body:has(.bk-soft) .mud-dialog {
    box-shadow: var(--bk-depth-floating);
}

body:has(.bk-soft) .mud-popover {
    border-radius: var(--bk-radius-md);
}

/* Scrim behind a dialog or the Protokoll drawer: the page's own tone,
   tinted and softly blurred, so the page stays recognizable behind it
   instead of MudBlazor's flat grey backdrop (.mud-overlay-dark) - and, since
   it reads var(--mud-palette-background) rather than a fixed color, it
   follows a live theme or light/dark switch on an already-open dialog with
   no extra rule per preset. Scoped to exactly two overlays, both structural
   siblings of the surface they belong to rather than reachable by a shared
   class of their own (MudDialogContainer.razor renders <MudOverlay
   Class="mud-overlay-dialog .."/> next to .mud-dialog inside one
   .mud-dialog-container per open dialog; MudDrawer.razor renders its own
   <MudOverlay Class="mud-drawer-overlay mud-overlay-drawer .."/> as the next
   sibling of its <aside>) - never MudBlazor's other overlays, which the
   architecture proposal's own caveat says must stay exactly as they are: a
   select list's popover backdrop (.mud-overlay-popover) and the responsive
   overlay the main navigation drawer (.bk-drawer-nav) shows on a narrow
   viewport both keep their plain scrim, so opening either does not blur the
   whole page. */
body:has(.bk-soft) .mud-dialog-container .mud-overlay-scrim,
body:has(.bk-soft) .bk-protokoll-drawer + .mud-overlay-drawer .mud-overlay-scrim {
    background: color-mix(in srgb, var(--mud-palette-background) 55%, transparent);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
}

/* Drawer and app bar sit on the page tone; a soft shadow instead of the
   dividing line (which is an inline style, hence !important). */
.bk-soft .bk-drawer-nav {
    border-right-color: transparent !important;
    box-shadow: 6px 0 18px -10px var(--bk-soft-lo);
}

/* Sidebar looks like the agreed design (feature "Desktop sidebar looks like
   the agreed design"): MudDrawer's own default is "overflow-y: auto" on this
   root element, which would scroll the WHOLE drawer (links, lists, footer)
   as one block once it overflows. Turned off so only MainLayout.razor.css's
   own bk-drawer-scroll area - the flex child between the fixed links and the
   pinned footer - ever grows a scrollbar. Unconditional, not gated on
   .bk-soft: every palette shares this structural layout. */
.bk-drawer-nav {
    overflow-y: hidden;
}

.bk-soft .bk-mobile-appbar {
    border-bottom-color: transparent !important;
    box-shadow: 0 6px 18px -10px var(--bk-soft-lo);
}

/* Buttons: raised. The primary one is the glossy accent of the reference
   design - a faint top-left sheen and a shadow in its own color instead of
   the dark one. */
.bk-soft .mud-button-filled,
.bk-soft .mud-button-outlined,
body:has(.bk-soft) .mud-dialog .mud-button-filled,
body:has(.bk-soft) .mud-dialog .mud-button-outlined {
    border-color: transparent;
    border-radius: var(--bk-radius-md);
    box-shadow: var(--bk-depth-raised-sm);
}

.bk-soft .mud-button-filled-primary,
body:has(.bk-soft) .mud-dialog .mud-button-filled-primary {
    background-image: linear-gradient(145deg, rgba(255, 255, 255, 0.22), rgba(255, 255, 255, 0) 55%);
    box-shadow:
        0 8px 18px -6px rgba(var(--mud-palette-primary-rgb), 0.6),
        -4px -4px 10px var(--bk-soft-hi);
}

/* Icon buttons in the drawer and app bar are the round raised knobs of the
   reference design; elsewhere (dense rows, tables) they only lift on hover,
   so a list of rows does not turn into a field of bumps. A dialog's own
   close button is the same isolated case as the drawer/app bar ones - one
   button per dialog, not a dense row - and the reference design draws it
   raised at rest too. */
.bk-soft .bk-drawer-nav .mud-icon-button,
.bk-soft .bk-mobile-appbar .mud-icon-button,
body:has(.bk-soft) .mud-dialog .mud-button-close {
    box-shadow: var(--bk-depth-raised-sm);
}

/* Not the icon buttons MudBlazor builds switches, checkboxes and radios
   from - those get their own shapes further down. */
.bk-soft .mud-icon-button:not(.mud-switch-base, .mud-checkbox *, .mud-radio *):hover {
    box-shadow: var(--bk-depth-raised-sm);
}

.bk-soft .mud-button-root,
body:has(.bk-soft) .mud-dialog .mud-button-root,
.bk-soft .mud-nav-link,
.bk-soft .bk-side-item {
    transition: box-shadow var(--bk-dur-fast) var(--bk-ease-out), transform var(--bk-dur-fast) var(--bk-ease-out);
}

/* Pressed: the current page's navigation entry stays pressed in (the icon
   row's active knob, a MudNavLink, anything marked aria-current), and every
   control sinks in physically while it is held down - its raised shadow
   turns inset and it drops a pixel, like a real key. */
.bk-soft .bk-nav-icon-row .bk-nav-icon-btn-active,
.bk-soft .mud-nav-link.active,
.bk-soft [aria-current="page"]:is(a, button) {
    background: transparent;
    color: var(--mud-palette-primary);
    box-shadow: var(--bk-depth-inset);
}

.bk-soft .mud-button-root:not(.mud-switch-base, .mud-checkbox *, .mud-radio *):active:not(:disabled),
body:has(.bk-soft) .mud-dialog .mud-button-root:not(.mud-switch-base, .mud-checkbox *, .mud-radio *):active:not(:disabled),
.bk-soft .mud-nav-link:active,
.bk-soft .bk-side-item:active {
    box-shadow: var(--bk-depth-inset);
    transform: translateY(1px) scale(0.97);
}

/* A filled button keeps its own color while pressed; the inset shadow is
   drawn in darker shades of that color rather than of the page tone. */
.bk-soft .mud-button-filled:active:not(:disabled),
body:has(.bk-soft) .mud-dialog .mud-button-filled:active:not(:disabled) {
    box-shadow: var(--bk-depth-pressed-accent);
}

/* Inputs: pressed into the surface, no outline. */
.bk-soft .mud-input.mud-input-outlined,
body:has(.bk-soft) .mud-dialog .mud-input.mud-input-outlined {
    border-radius: var(--bk-radius-md);
    box-shadow: var(--bk-depth-inset);
}

.bk-soft .mud-input-outlined-border,
body:has(.bk-soft) .mud-dialog .mud-input-outlined-border {
    border-color: transparent !important;
}

.bk-soft .mud-input.mud-input-outlined:focus-within,
body:has(.bk-soft) .mud-dialog .mud-input.mud-input-outlined:focus-within {
    box-shadow: var(--bk-depth-inset), var(--bk-ring);
}

/* MudBlazor floats an outlined field's label up onto the outline once the
   field has focus or a value (.mud-shrink). With the outline gone, that put
   the label outside the pressed-in shape, so it floats to the top INSIDE the
   field instead, and the text moves down to make room for it - both field
   sizes keep their height (normal 56px, dense 40px). Specificity outranks
   MudBlazor's own shrink rule by the leading .bk-soft. */
.bk-soft .mud-input-outlined-with-label > .mud-input-root-outlined,
body:has(.bk-soft) .mud-dialog .mud-input-outlined-with-label > .mud-input-root-outlined {
    padding-top: 26px;
    padding-bottom: 11px;
}

.bk-soft .mud-input-outlined-with-label > .mud-input-root-outlined.mud-input-root-margin-dense,
body:has(.bk-soft) .mud-dialog .mud-input-outlined-with-label > .mud-input-root-outlined.mud-input-root-margin-dense {
    padding-top: 17px;
    padding-bottom: 4px;
}

.bk-soft .mud-shrink ~ label.mud-input-label.mud-input-label-inputcontrol.mud-input-label-outlined,
.bk-soft .mud-input:focus-within ~ label.mud-input-label.mud-input-label-inputcontrol.mud-input-label-outlined,
body:has(.bk-soft) .mud-dialog .mud-shrink ~ label.mud-input-label.mud-input-label-inputcontrol.mud-input-label-outlined,
body:has(.bk-soft) .mud-dialog .mud-input:focus-within ~ label.mud-input-label.mud-input-label-inputcontrol.mud-input-label-outlined {
    transform: translate(14px, 7px) scale(0.75);
}

.bk-soft .mud-shrink ~ label.mud-input-label.mud-input-label-inputcontrol.mud-input-label-outlined.mud-input-label-margin-dense,
.bk-soft .mud-input:focus-within ~ label.mud-input-label.mud-input-label-inputcontrol.mud-input-label-outlined.mud-input-label-margin-dense,
body:has(.bk-soft) .mud-dialog .mud-shrink ~ label.mud-input-label.mud-input-label-inputcontrol.mud-input-label-outlined.mud-input-label-margin-dense,
body:has(.bk-soft) .mud-dialog .mud-input:focus-within ~ label.mud-input-label.mud-input-label-inputcontrol.mud-input-label-outlined.mud-input-label-margin-dense {
    transform: translate(14px, 3px) scale(0.7);
}

/* A field with a start icon (search boxes): MudBlazor keeps its label
   floated at all times and never moves it sideways, so inside the field it
   would sit on top of the icon - it floats above the text instead, which
   starts after the 24px icon and its margins (46px). */
.bk-soft .mud-input-adorned-start.mud-shrink ~ label.mud-input-label.mud-input-label-inputcontrol.mud-input-label-outlined,
.bk-soft .mud-input-adorned-start:focus-within ~ label.mud-input-label.mud-input-label-inputcontrol.mud-input-label-outlined,
body:has(.bk-soft) .mud-dialog .mud-input-adorned-start.mud-shrink ~ label.mud-input-label.mud-input-label-inputcontrol.mud-input-label-outlined,
body:has(.bk-soft) .mud-dialog .mud-input-adorned-start:focus-within ~ label.mud-input-label.mud-input-label-inputcontrol.mud-input-label-outlined {
    transform: translate(46px, 7px) scale(0.75);
}

.bk-soft .mud-input-adorned-start.mud-shrink ~ label.mud-input-label.mud-input-label-inputcontrol.mud-input-label-outlined.mud-input-label-margin-dense,
.bk-soft .mud-input-adorned-start:focus-within ~ label.mud-input-label.mud-input-label-inputcontrol.mud-input-label-outlined.mud-input-label-margin-dense,
body:has(.bk-soft) .mud-dialog .mud-input-adorned-start.mud-shrink ~ label.mud-input-label.mud-input-label-inputcontrol.mud-input-label-outlined.mud-input-label-margin-dense,
body:has(.bk-soft) .mud-dialog .mud-input-adorned-start:focus-within ~ label.mud-input-label.mud-input-label-inputcontrol.mud-input-label-outlined.mud-input-label-margin-dense {
    transform: translate(46px, 3px) scale(0.7);
}

/* Clickable cards (settings hub tiles and the like): instead of the primary
   outline on hover, the card lifts further, and it sinks in while pressed. */
.bk-soft .bk-clickable {
    transition: box-shadow var(--bk-dur-fast) var(--bk-ease-out), transform var(--bk-dur-fast) var(--bk-ease-out);
}

.bk-soft .bk-clickable:hover {
    border-color: transparent !important;
    box-shadow: var(--bk-depth-lift);
}

.bk-soft .bk-clickable:active {
    box-shadow: var(--bk-depth-inset);
    transform: translateY(1px) scale(0.99);
}

/* The danger card keeps its warning without an outline: a faint inner ring
   in the error color on top of the raised shape. */
.bk-soft .bk-danger-card,
.bk-soft .bk-danger-card:hover {
    border-color: transparent !important;
    box-shadow: var(--bk-depth-raised), inset 0 0 0 1.5px color-mix(in srgb, var(--mud-palette-error) 45%, transparent);
}

/* Settings icon badges: a small raised knob with the icon in the accent
   color (the base rule's tint follows the palette as well, see
   .bk-settings-icon). */
.bk-soft .bk-settings-icon {
    background: var(--mud-palette-surface);
    border-radius: var(--bk-radius-md);
    box-shadow: var(--bk-depth-raised-sm);
}

/* Feature page: the phase sections on the spine are raised cards, the
   current one ringed in the accent; phases still to come are pressed-in
   wells rather than dashed outlines. */
.bk-soft .bk-sec {
    border-color: transparent;
    border-radius: var(--bk-radius-lg);
    box-shadow: var(--bk-depth-raised);
}

.bk-soft .bk-sec-cur {
    box-shadow: var(--bk-depth-raised), var(--bk-ring);
}

.bk-soft .bk-sec-ghost {
    background: transparent;
    box-shadow: var(--bk-depth-inset);
}

/* The spine itself: a groove pressed into the page, filled with the accent
   where the phases are done; open nodes are pressed-in, done and current
   ones raised. */
.bk-soft .bk-sp-rail::before {
    left: 9px;
    width: 4px;
    border-radius: var(--bk-radius-xs);
    /* The accent fill is its own background layer, sized from nothing (open)
       to the full groove (done) - which is what lets the fill run down the
       groove when a phase finishes live (see Motion). */
    background: linear-gradient(var(--mud-palette-primary), var(--mud-palette-primary)) top / 100% 0 no-repeat,
        var(--mud-palette-background);
    box-shadow: var(--bk-depth-inset-sm);
}

/* The full shorthand again (not just the size): the plain done rule further
   down would otherwise win over the open one above and swap the layers for a
   flat color, which cannot run. */
.bk-soft .bk-sp-done .bk-sp-rail::before {
    background: linear-gradient(var(--mud-palette-primary), var(--mud-palette-primary)) top / 100% 100% no-repeat,
        var(--mud-palette-background);
}

.bk-soft .bk-sp-dot-open {
    border-color: transparent;
    box-shadow: var(--bk-depth-inset);
}

.bk-soft .bk-sp-dot-done,
.bk-soft .bk-sp-dot-cur,
.bk-soft .bk-sp-dot-fail {
    box-shadow: var(--bk-depth-raised-sm);
}

/* Command bar telemetry pill and the Protokoll drawer's meta chips. */
.bk-soft .bk-protokoll-trigger,
body:has(.bk-soft) .bk-drawer-chip {
    border-color: transparent;
    box-shadow: var(--bk-depth-raised-sm);
    transition: box-shadow var(--bk-dur-fast) var(--bk-ease-out), transform var(--bk-dur-fast) var(--bk-ease-out);
}

.bk-soft .bk-protokoll-trigger:hover {
    border-color: transparent;
    background: var(--mud-palette-surface);
    box-shadow: var(--bk-depth-lift-sm);
}

.bk-soft .bk-protokoll-trigger:active {
    box-shadow: var(--bk-depth-inset);
    transform: translateY(1px) scale(0.97);
}

/* Paused/archived callouts: the semantic color only as a faint tint of the
   page tone, on a raised pad - not a flat colored block. */
.bk-soft .bk-callout-danger,
.bk-soft .bk-callout-info {
    border-radius: var(--bk-radius-md);
    box-shadow: var(--bk-depth-raised-sm);
}

.bk-soft .mud-alert {
    border-radius: var(--bk-radius-md);
}

.bk-soft .mud-nav-link {
    border-radius: var(--bk-radius-md);
}

/* The drawer's project selector: a raised control, pressed while held. */
.bk-soft .bk-proj-select {
    border-color: transparent;
    border-radius: var(--bk-radius-md);
    box-shadow: var(--bk-depth-raised-sm);
    transition: box-shadow var(--bk-dur-fast) var(--bk-ease-out), transform var(--bk-dur-fast) var(--bk-ease-out);
}

.bk-soft .bk-proj-select:active {
    box-shadow: var(--bk-depth-inset);
    transform: translateY(1px) scale(0.98);
}

/* Chips are labels, not controls: a small pill pressed into the surface.
   Only a clickable chip is raised like a button. */
.bk-soft .mud-chip.mud-chip-filled.mud-chip-color-default {
    background: transparent;
    box-shadow: var(--bk-depth-inset-sm);
}

.bk-soft .mud-chip.mud-clickable {
    box-shadow: var(--bk-depth-raised-sm);
}

/* A disabled button is flat: no raised shape, no glossy sheen, no colored
   shadow - it cannot be pressed, so it does not pretend to stand out. */
.bk-soft .mud-button-root:disabled,
body:has(.bk-soft) .mud-dialog .mud-button-root:disabled {
    background-image: none;
    box-shadow: none !important;
}

/* Switches: the track is a groove pressed into the surface - widened by 3px
   on every side, so the 20px thumb sits exactly inside it at both ends of
   its 20px travel - lit in the accent when on; the thumb is a raised knob.
   MudBlazor's hover disc behind the thumb goes; keyboard focus rings the
   knob instead. */
.bk-soft .mud-switch-span .mud-switch-track,
body:has(.bk-soft) .mud-dialog .mud-switch-span .mud-switch-track {
    margin: -3px;
    width: calc(100% + 6px);
    height: calc(100% + 6px);
    border-radius: var(--bk-radius-pill);
    opacity: 1;
    background: var(--mud-palette-background);
    box-shadow: var(--bk-depth-inset-sm);
}

.bk-soft .mud-switch-base.mud-checked + .mud-switch-track,
body:has(.bk-soft) .mud-dialog .mud-switch-base.mud-checked + .mud-switch-track {
    opacity: 1;
    background: color-mix(in srgb, var(--mud-palette-primary) 75%, var(--mud-palette-background));
    box-shadow:
        inset 2px 2px 4px color-mix(in srgb, var(--mud-palette-primary) 55%, #000000),
        inset -2px -2px 4px color-mix(in srgb, var(--mud-palette-primary) 70%, #FFFFFF);
}

.bk-soft .mud-switch-base.mud-switch-disabled + .mud-switch-track {
    opacity: 0.5 !important;
}

.bk-soft .mud-switch-base,
.bk-soft .mud-switch-base:hover {
    background-color: transparent !important;
}

.bk-soft .mud-switch-button [class*="mud-switch-thumb-"],
body:has(.bk-soft) .mud-dialog .mud-switch-button [class*="mud-switch-thumb-"] {
    background: color-mix(in srgb, var(--mud-palette-surface) 85%, #FFFFFF);
    box-shadow: 2px 2px 4px var(--bk-soft-lo), -1px -1px 3px var(--bk-soft-hi);
}

.bk-soft .mud-switch-base:focus-visible [class*="mud-switch-thumb-"],
.bk-soft .mud-switch-input:focus-visible + [class*="mud-switch-thumb-"] {
    box-shadow: 2px 2px 4px var(--bk-soft-lo), 0 0 0 3px rgba(var(--mud-palette-primary-rgb), 0.4);
}

/* Selects are controls, not text: raised like a button (the text fields
   around them stay pressed in), pressed in while focused or open. */
.bk-soft .mud-select .mud-input.mud-input-outlined,
body:has(.bk-soft) .mud-dialog .mud-select .mud-input.mud-input-outlined {
    cursor: pointer;
    box-shadow: var(--bk-depth-raised-sm);
}

.bk-soft .mud-select .mud-input.mud-input-outlined:focus-within,
body:has(.bk-soft) .mud-dialog .mud-select .mud-input.mud-input-outlined:focus-within {
    box-shadow: var(--bk-depth-inset), var(--bk-ring);
}

/* The opened list (selects and menus alike): rounded entries, the chosen
   one pressed in and in the accent. */
body:has(.bk-soft) .mud-popover .mud-list {
    padding: 6px;
}

body:has(.bk-soft) .mud-popover .mud-list-item {
    border-radius: var(--bk-radius-md);
}

body:has(.bk-soft) .mud-popover .mud-list-item.mud-selected-item {
    background: transparent;
    color: var(--mud-palette-primary);
    box-shadow: var(--bk-depth-inset);
}

body:has(.bk-soft) .mud-popover .mud-list-item:not(.mud-selected-item):hover {
    box-shadow: var(--bk-depth-raised-sm);
}

/* Chat session rows (ChatSessionRow). Their root is a MudPaper, which never
   carries the component's CSS-isolation attribute - so these states live
   here rather than in ChatSessionRow.razor.css, where they could not reach
   it. The open session is marked in the accent; a free chat keeps its amber
   tint. */
.bk-chat-session-row-current {
    background: color-mix(in srgb, var(--mud-palette-primary) 8%, var(--mud-palette-surface));
    border-color: var(--mud-palette-primary) !important;
}

.bk-free-chat-row {
    background: #FAEEDA;
    border-color: #E0C892;
}

.bk-dark .bk-free-chat-row {
    background: #4A2C05;
    border-color: #633806;
}

/* In the neumorphic design the rows are compact raised pads (a list beside
   a chat, not page-level cards), and the open session is the pressed-in
   one; a free chat's amber shrinks to a faint tint of the tone. */
.bk-soft .bk-chat-sessions-scroll .mud-paper-outlined {
    border-radius: var(--bk-radius-md);
    box-shadow: var(--bk-depth-raised-sm);
}

.bk-soft .bk-chat-sessions-scroll .bk-chat-session-row-current {
    background: var(--mud-palette-surface);
    border-color: transparent !important;
    box-shadow: var(--bk-depth-inset), inset 3px 0 0 0 var(--mud-palette-primary);
}

.bk-soft .bk-free-chat-row {
    background: color-mix(in srgb, var(--mud-palette-surface) 88%, #E0A43A);
    border-color: transparent;
}

/* Sci-fi glow accent of the glow presets (ThemePresets.Glow, dark mode only -
   MainLayout adds bk-glow next to bk-soft): neon halos on top of the soft
   shapes. Every rule reads the active palette's own
   --mud-palette-primary-rgb, so one set serves every glowing preset. */
.bk-glow .bk-brand-name {
    text-shadow: 0 0 12px rgba(var(--mud-palette-primary-rgb), 0.55);
}

.bk-glow .mud-button-filled-primary {
    box-shadow:
        0 0 0 1px rgba(var(--mud-palette-primary-rgb), 0.55),
        0 0 20px -2px rgba(var(--mud-palette-primary-rgb), 0.6),
        -4px -4px 10px var(--bk-soft-hi);
}

.bk-glow .mud-button-filled-primary:hover {
    box-shadow:
        0 0 0 1px rgba(var(--mud-palette-primary-rgb), 0.8),
        0 0 28px 0 rgba(var(--mud-palette-primary-rgb), 0.7),
        -4px -4px 10px var(--bk-soft-hi);
}

/* The pressed-in current page glows from inside. */
.bk-glow .bk-nav-icon-row .bk-nav-icon-btn-active,
.bk-glow .mud-nav-link.active,
.bk-glow [aria-current="page"]:is(a, button) {
    text-shadow: 0 0 10px rgba(var(--mud-palette-primary-rgb), 0.6);
    box-shadow: var(--bk-depth-inset), inset 0 0 12px rgba(var(--mud-palette-primary-rgb), 0.25);
}

.bk-glow .mud-input.mud-input-outlined:focus-within {
    box-shadow: var(--bk-depth-inset), 0 0 0 1px rgba(var(--mud-palette-primary-rgb), 0.6), 0 0 14px -2px rgba(var(--mud-palette-primary-rgb), 0.55);
}

.bk-glow .mud-progress-linear-bar {
    box-shadow: 0 0 10px rgba(var(--mud-palette-primary-rgb), 0.8);
}

.bk-glow ::selection {
    background: rgba(var(--mud-palette-primary-rgb), 0.35);
}

/* ===== Motion ==============================================================
   Calm and physical, never showy: things arrive rather than appear, on the
   duration and easing tokens above. The entrances - the page fading in on a
   navigation, cards and rows arriving one after another, a new sidebar entry
   sliding in - are wwwroot/js/motion-interop.js: a CSS animation would also
   replay when Blazor swaps the prerendered page for the interactive one.
   What stays here needs no such care, and is gated on prefers-reduced-motion:
   no-preference like the script - with reduced motion (the OS setting, and
   every browser test, see BrowserLaunch) nothing moves at all. Keyframes use
   the standalone translate/scale properties rather than transform, so they
   compose with a transform an element already carries (MudBlazor positions
   some of its own elements that way) instead of replacing it for the
   duration of the animation. */

/* Menus and dialogs grow out of the surface, their shadow deepening from a
   control's to a floating layer's as they come up. */
@keyframes bk-float-in {
    from { opacity: 0; scale: 0.96; box-shadow: var(--bk-depth-raised-sm); }
    to { opacity: 1; scale: 1; box-shadow: var(--bk-depth-floating); }
}

@media (prefers-reduced-motion: no-preference) {
    /* Floating layers. The static shape (app.css .bk-soft) is the end state;
       fill-mode both holds the floating shadow once the animation is done. */
    body:has(.bk-soft) .mud-dialog,
    body:has(.bk-soft) .mud-popover.mud-popover-open:not(.mud-tooltip) {
        animation: bk-float-in var(--bk-dur-base) var(--bk-ease-out) both;
    }

    /* A switched toggle overshoots a hair and settles (MudBlazor's own
       list, with the thumb's travel on the spring). */
    .bk-soft .mud-switch-base {
        transition: transform var(--bk-dur-base) var(--bk-ease-spring),
            background-color var(--bk-dur-base) var(--bk-ease-out),
            box-shadow var(--bk-dur-base) var(--bk-ease-out);
    }

    /* A phase finishing on the feature page: the groove fills with the
       accent down to the next node (the fill is a background sized from 0
       to full height, see the spine rules above). */
    .bk-soft .bk-sp-rail::before {
        transition: background-size var(--bk-dur-slow) var(--bk-ease-out);
    }
}

/* The mobile app bar only exists to toggle the overlay drawer, so hide it once the
   drawer becomes the persistent sidebar (MainLayout's MudDrawer Breakpoint.Lg -
   1280px, widened from 600px so a chat page gets the tablet width back too,
   see MainLayout.razor's own comment). */
@media (min-width: 1280px) {
    .bk-mobile-appbar {
        display: none !important;
    }
}

@media (max-width: 1279.98px) {
    /* !important is required here: the unconditional rule above sets
       padding-top: 0 !important (to drop MudBlazor's app-bar offset on wider
       screens, where the app bar is hidden). Without !important on this mobile
       override, that global !important wins and the fixed 48px mobile app bar
       overlaps the top of the page - hiding the first row of content (e.g. the
       chat page's title). The fixed-height chat page (100dvh - 72px = 48 app
       bar + 12 + 12 page padding) depends on this offset being applied. */
    .bk-main-content {
        padding-top: 48px !important;
    }
}

@media (max-width: 599.98px) {
    .bk-page {
        padding: 12px;
        /* The base rule caps the page at min(96vw, 1600px); on a phone that
           96vw leaves a ~4vw empty strip on the (left-aligned) right edge.
           Fill the full width on mobile - the 12px padding is the only inset. */
        max-width: none;
        /* Room for the fixed bottom bar below, so the last card of a long
           page (the requirement tree, the timeline) does not end up hidden
           behind it - see the criterion that the bar stays usable while the
           page scrolls. Chat pages size themselves to the exact remaining
           viewport height instead (ChatPanel.razor.css) and never rely on
           this padding, so :has(.bk-chat-page) is left alone here. */
        padding-bottom: calc(var(--bk-phone-bar-h) + 12px);
    }

    /* The "For you" page (ForYou.razor) wants NO gap at all between its last
       entry and the bar (feature "For you on the phone fills up from the
       bottom bar") - the extra 12px breathing room the base rule above
       reserves for every other phone page would otherwise leave exactly that
       gap. Same :has() escape hatch as the chat page's own override below;
       ForYou.razor.css's own min-height formula on .bk-foryou-page is tuned
       against this exact padding-bottom value. */
    .bk-page:has(.bk-foryou-page) {
        padding-bottom: var(--bk-phone-bar-h);
    }

    /* Below 600px the phone shell (bottom bar plus its "More" sheet, further
       down) replaces the mobile app bar and the drawer behind it outright -
       the criterion that no navigation menu remains at the top edge on a
       phone. Tablet (600px up to the drawer's own Lg breakpoint) keeps both
       exactly as they already are; only this one breakpoint removes them. */
    .bk-mobile-appbar,
    .bk-drawer-nav {
        display: none !important;
    }

    /* No app-bar chrome above the page any more on a phone, so the tablet
       rule further up (padding-top: 48px) must not apply here - the phone's
       own chrome sits at the BOTTOM of the page instead (the padding-bottom
       above, and the bar itself, both further down). */
    .bk-main-content {
        padding-top: 0 !important;
    }

    /* The bar: "For you", Implementation, the raised Chat key, Requirements
       and More, every one of them reachable without opening a menu first
       (see MainLayout.razor for the markup and the active-tab/badge logic).
       Fixed to the bottom edge so it stays visible and usable while the page
       underneath scrolls - the page's own bottom padding above is what keeps
       its last content from ending up underneath it. z-index above the
       scrim/sheet below so the bar stays reachable even while the sheet is
       open (tapping another tab must still work without closing the sheet
       first) - the sheet's own bottom padding leaves exactly this much room
       clear so nothing of its content ends up hidden behind the bar either. */
    .bk-phone-nav {
        display: flex;
        align-items: stretch;
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 1250;
        height: var(--bk-phone-bar-h);
        padding-bottom: env(safe-area-inset-bottom, 0px);
        background: var(--mud-palette-surface);
        border-top: 1px solid var(--mud-palette-lines-default);
    }

    /* The four plain tabs (anchors) and the More toggle (a button, same
       class) share this look; min-width: 0 plus the label's own ellipsis
       below is what keeps a translated label from forcing the bar wider
       than a 360px viewport (the criterion that a 360px phone never scrolls
       sideways). */
    .bk-phone-tab {
        flex: 1 1 0;
        min-width: 0;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 2px;
        border: 0;
        background: none;
        padding: 4px 2px;
        font: inherit;
        color: var(--mud-palette-text-secondary);
        text-decoration: none;
        position: relative;
        cursor: pointer;
    }

    .bk-phone-tab-active {
        color: var(--mud-palette-primary);
    }

    /* Small top-edge indicator on the active tab - the same "a bar under the
       active item" idea MudTabs already uses elsewhere, reproduced by hand
       since these are plain anchors/buttons, not MudTabs. */
    .bk-phone-tab-active::before {
        content: "";
        position: absolute;
        top: 0;
        left: 50%;
        transform: translateX(-50%);
        width: 24px;
        height: 2px;
        border-radius: var(--bk-radius-xs);
        background: var(--mud-palette-primary);
    }

    .bk-phone-tab-label {
        font-size: 0.625rem;
        line-height: 1.2;
        max-width: 100%;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    /* Waiting/open-finding count on "For you"/"More" - same idea as
       .bk-tab-count, positioned as a corner badge on the icon instead of
       trailing inline text, since there is no room for that in a 10px label
       under a 20px icon. */
    .bk-phone-badge {
        position: absolute;
        top: 0;
        left: 50%;
        margin-left: 6px;
        min-width: 15px;
        height: 15px;
        padding: 0 4px;
        border-radius: var(--bk-radius-sm);
        background: var(--mud-palette-warning);
        color: var(--mud-palette-warning-text);
        font-size: 0.625rem;
        font-weight: 700;
        line-height: 15px;
        text-align: center;
    }

    /* The raised chat key: its own fixed-width slot (not flex: 1 1 0 like
       the other tabs) so the circle can overflow the bar's top edge without
       skewing the four text tabs' widths around it. */
    .bk-phone-fab-wrap {
        flex: 0 0 72px;
        min-width: 0;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 2px;
    }

    .bk-phone-fab {
        width: 48px;
        height: 48px;
        margin-top: -20px;
        border-radius: var(--bk-radius-round);
        display: inline-flex;
        align-items: center;
        justify-content: center;
        background: var(--mud-palette-primary);
        color: var(--mud-palette-primary-text);
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
        text-decoration: none;
    }

    .bk-phone-fab-wrap .bk-phone-tab-label {
        color: var(--mud-palette-text-secondary);
    }

    /* Backdrop behind the "More" sheet: closes it on tap, same as the grip
       and every row that navigates away. */
    .bk-phone-scrim {
        display: block;
        position: fixed;
        inset: 0;
        z-index: 1240;
        background: rgba(0, 0, 0, 0.35);
        opacity: 0;
        pointer-events: none;
        transition: opacity var(--bk-dur-base) var(--bk-ease-out);
    }

    .bk-phone-scrim-open {
        opacity: 1;
        pointer-events: auto;
    }

    /* The sheet itself: everything used rarely (project switch, audit,
       project settings, the three global pages, language, appearance, sign
       out - see MainLayout.razor). Always rendered (never @if), only ever
       shown via the -open class, so it can slide in with a transition
       instead of popping into existence; the bottom padding leaves exactly
       the height of the bar above visible underneath its own last row, per
       the bar's own comment above. z-index below the bar but above the
       scrim, both below MudBlazor's Dialog (1400) so a dialog opened from a
       sheet row's destination still stacks correctly above everything here. */
    .bk-phone-sheet {
        display: block;
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 1245;
        /* Widened from 80dvh: the added version line at the foot (below)
           needs to stay readable with no scrolling even on a short phone,
           per the architecture proposal's own caveat on this row count. */
        max-height: 84dvh;
        overflow-y: auto;
        background: var(--mud-palette-surface);
        border-top: 1px solid var(--mud-palette-lines-default);
        border-radius: var(--bk-radius-lg) var(--bk-radius-lg) 0 0;
        box-shadow: 0 -6px 18px rgba(0, 0, 0, 0.18);
        padding: 6px 8px calc(var(--bk-phone-bar-h) + env(safe-area-inset-bottom, 0px) + 8px);
        transform: translateY(100%);
        transition: transform var(--bk-dur-base) var(--bk-ease-out);
    }

    .bk-phone-sheet-open {
        transform: translateY(0);
    }

    .bk-phone-sheet-grip {
        display: block;
        width: 36px;
        height: 4px;
        margin: 6px auto 8px;
        border: 0;
        padding: 0;
        border-radius: var(--bk-radius-xs);
        background: var(--mud-palette-lines-default);
        cursor: pointer;
    }

    .bk-phone-sheet-head {
        padding: 6px 10px 2px;
        font-size: 0.625rem;
        font-weight: 600;
        letter-spacing: 0.06em;
        text-transform: uppercase;
        color: var(--mud-palette-text-secondary);
    }

    .bk-phone-sheet-row {
        display: flex;
        align-items: center;
        gap: 12px;
        width: 100%;
        /* Slightly tighter than the sheet's very first version (11px) - the
           foot's own version line below has to fit within the sheet's 80dvh
           cap even on a short phone, without pushing any row density lower
           than this still-comfortable tap target height (see the
           architecture proposal's own caveat on this row count). */
        padding: 9px 10px;
        border: 0;
        border-radius: var(--bk-radius-sm);
        background: none;
        color: var(--mud-palette-text-primary);
        font: inherit;
        font-size: 0.8125rem;
        text-align: left;
        cursor: pointer;
    }

    .bk-phone-sheet-row:hover {
        background: var(--mud-palette-action-default-hover);
    }

    .bk-phone-sheet-label {
        flex: 1 1 auto;
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    /* Trailing text on a row (the current project name, the current
       language/appearance) - capped so a very long project name cannot push
       the row wider than the sheet (the 360px legibility criterion). */
    .bk-phone-sheet-sub {
        flex: 0 0 auto;
        max-width: 40%;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        font-size: 0.75rem;
        color: var(--mud-palette-text-secondary);
    }

    /* Open-finding count on the Audit row - same treatment as .bk-tab-count,
       sized for the sheet's own row height instead of inline text. */
    .bk-phone-sheet-count {
        flex: 0 0 auto;
        min-width: 18px;
        height: 18px;
        padding: 0 5px;
        border-radius: var(--bk-radius-sm);
        background: var(--mud-palette-warning);
        color: var(--mud-palette-warning-text);
        font-size: 0.6875rem;
        font-weight: 700;
        line-height: 18px;
        text-align: center;
    }

    /* Running app version, at the foot of the sheet (same value the sidebar
       shows on wider screens, see MainLayout.razor's shared Loc["VersionLabel"]
       call) - readable the moment the sheet opens, with no scrolling needed. */
    .bk-phone-sheet-version {
        margin-top: 2px;
        padding: 6px 10px 2px;
        border-top: 1px solid var(--mud-palette-lines-default);
        font-size: 0.6875rem;
        color: var(--mud-palette-text-secondary);
        text-align: center;
    }

    /* Keyboard-aware phone shell (feature "Phone navigation at the bottom
       edge, hidden while typing"): js/keyboard-interop.js marks the document
       root with this class the moment window.visualViewport shrinks enough
       to mean an on-screen keyboard opened, and clears it the instant the
       viewport grows back - purely a CSS reaction from here, so it needs
       neither a Blazor round trip nor anything for the user to scroll or
       tap. Bar, scrim and sheet all disappear outright (not merely pushed
       up), so none of them can still cover the field or its confirm control
       while the keyboard is open. */
    .bk-keyboard-open .bk-phone-nav,
    .bk-keyboard-open .bk-phone-scrim,
    .bk-keyboard-open .bk-phone-sheet {
        display: none !important;
    }

    /* No bar to reserve room for while it is hidden. Excludes a chat page
       (which never relies on this padding to begin with - see its own
       :has(.bk-chat-page) rule above and ChatPanel.razor.css's fixed height
       formula) so this cannot add back the 12px bottom inset that rule
       deliberately drops. */
    .bk-keyboard-open .bk-page:not(:has(.bk-chat-page)) {
        padding-bottom: 12px;
    }
}

.validation-message {
    color: #e50000;
}

/* Status pill: small rounded label showing the exact feature status.
   Color pairs use the light fill and dark text of the same color ramp. */
.bk-pill {
    display: inline-block;
    font-size: 11px;
    font-weight: 500;
    line-height: 1.6;
    padding: 1px 9px;
    border-radius: var(--bk-radius-md);
    white-space: nowrap;
}

/* Each status names its hue (the --bk-hue-* tokens); fill and text are
   mixed from that hue and the active theme's own tone, so a pill belongs to
   every color theme instead of carrying fixed light/dark hex pairs. Light
   mode: a faint tint with deep text of the same hue; dark mode: a stronger
   tint with light text. Both keep at least 5:1 on every preset's tone
   (checked across all of ThemePresets' light and dark tones). */
.bk-pill {
    --bk-pill-hue: var(--bk-hue-draft);
    background: color-mix(in srgb, var(--bk-pill-hue) 18%, var(--mud-palette-background));
    color: color-mix(in srgb, var(--bk-pill-hue) 58%, #000000);
}

.bk-dark .bk-pill {
    background: color-mix(in srgb, var(--bk-pill-hue) 30%, var(--mud-palette-background));
    color: color-mix(in srgb, var(--bk-pill-hue) 52%, #FFFFFF);
}

.bk-pill-draft { --bk-pill-hue: var(--bk-hue-draft); }
.bk-pill-diagnosis { --bk-pill-hue: var(--bk-hue-diagnosis); }
.bk-pill-architecture { --bk-pill-hue: var(--bk-hue-architecture); }
.bk-pill-approval { --bk-pill-hue: var(--bk-hue-approval); }
.bk-pill-development { --bk-pill-hue: var(--bk-hue-development); }
.bk-pill-qa { --bk-pill-hue: var(--bk-hue-qa); }
.bk-pill-merge { --bk-pill-hue: var(--bk-hue-merge); }
.bk-pill-done { --bk-pill-hue: var(--bk-hue-done); }
.bk-pill-paused { --bk-pill-hue: var(--bk-hue-paused); }
.bk-pill-archived { --bk-pill-hue: var(--bk-hue-archived); }

/* Bug kind badge on the kanban card and the feature banner, same magenta
   accent as the Diagnosis status pill (bk-pill-diagnosis) to visually tie
   "this is a bug" to "it goes through Diagnose first". */
.bk-pill-bug { --bk-pill-hue: var(--bk-hue-diagnosis); }

/* Context chip of the shared page header (ProjectPageHeader's Context slot):
   quiet, bordered marker for the per-page context - repo and branch on the
   board, counters on the goals page, the last audit run on the audit page.
   Shared by several pages, so it lives here and not in the header's scoped
   stylesheet. */
.bk-head-chip {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    max-width: 340px;
    padding: 2px 10px;
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: var(--bk-radius-md);
    font-size: 0.75rem;
    line-height: 1.6;
    color: var(--mud-palette-text-secondary);
    text-decoration: none;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

a.bk-head-chip:hover {
    background: var(--mud-palette-action-default-hover);
    text-decoration: none;
}

.bk-head-chip .mud-icon-root {
    font-size: 0.875rem;
    flex-shrink: 0;
}

.bk-head-chip-sep {
    color: var(--mud-palette-text-disabled);
}

/* Neumorphic design: the context chip is a label pressed into the header,
   not an outlined box; as a link it rises a little when pointed at. */
.bk-soft .bk-head-chip {
    border-color: transparent;
    border-radius: var(--bk-radius-pill);
    padding: 3px 12px;
    box-shadow: var(--bk-depth-inset-sm);
}

.bk-soft a.bk-head-chip:hover {
    background: transparent;
    box-shadow: var(--bk-depth-raised-sm);
}

/* Requirement key (REQ-014) in front of the requirement title in the page
   header, set in monospace so it reads as an identifier, not as part of the
   title. */
.bk-req-key {
    font-family: monospace;
    font-size: 0.75rem;
    color: var(--mud-palette-text-secondary);
    white-space: nowrap;
}

/* Breakdown review (TaskDetail): item card on the proposal gate, violet
   accent ties every item back to the proposal it belongs to. */
.bk-item-card {
    padding: 12px 14px;
    border-left: 3px solid #8A63D2;
}

/* Goal/requirement assignment box: a bordered block directly under the item
   title showing Ziel and Anforderung as separate, iconized badges instead of
   inline text/a lone pill, so the reviewer sees the (possibly not-yet-existing)
   assignment for every item without opening the goal/requirement dialog. */
.bk-assign-box {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 10px;
    margin: 10px 0;
    padding: 8px 10px;
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: var(--bk-radius-sm);
    background: var(--mud-palette-background-gray);
}

.bk-assign-group {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
}

.bk-assign-chip {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-size: 0.75rem;
    font-weight: 500;
    border-radius: var(--bk-radius-pill);
    padding: 3px 10px;
    line-height: 1.3;
}

.bk-assign-chip .mud-icon-root {
    font-size: 1rem;
    flex-shrink: 0;
}

.bk-assign-chip-label {
    overflow-wrap: anywhere;
}

.bk-assign-parent {
    background: #EEEDFE;
    color: #3C3489;
}

.bk-dark .bk-assign-parent {
    background: #3C3489;
    color: #CECBF6;
}

.bk-assign-requirement {
    background: #E6F1FB;
    color: #0C447C;
}

.bk-dark .bk-assign-requirement {
    background: #0C447C;
    color: #B5D4F4;
}

/* "neu anlegen" case for goal or requirement: same size/prominence as an
   assigned badge, but a dashed outline instead of a filled color so it reads
   as "will be created", not "already exists". */
.bk-assign-new {
    background: transparent;
    border: 1px dashed var(--mud-palette-text-secondary);
    color: var(--mud-palette-text-secondary);
}

/* Goal not decided yet at all (no task goal, no proposed match, no proposed
   new title) - rare edge case, kept visually calmer than bk-assign-new since
   nothing has been proposed for the reviewer to confirm. */
.bk-assign-pending {
    background: transparent;
    border: 1px dashed var(--mud-palette-lines-default);
    color: var(--mud-palette-text-secondary);
    font-style: italic;
}

/* KPI tiles on the dashboard, tinted variants with matching label/value colors */
.bk-tile-neutral {
    background: var(--mud-palette-background-gray);
}

/* Tinted tiles mix from a status hue like the pills: the label in the
   pill's text tone, the value a step deeper (lighter in dark mode). */
.bk-tile-amber { --bk-tile-hue: var(--bk-hue-approval); }
.bk-tile-red { --bk-tile-hue: var(--bk-hue-paused); }
.bk-tile-green { --bk-tile-hue: var(--bk-hue-done); }

.bk-tile-amber,
.bk-tile-red,
.bk-tile-green {
    background: color-mix(in srgb, var(--bk-tile-hue) 18%, var(--mud-palette-background));
}

:is(.bk-tile-amber, .bk-tile-red, .bk-tile-green) .bk-tile-label {
    color: color-mix(in srgb, var(--bk-tile-hue) 58%, #000000);
}

:is(.bk-tile-amber, .bk-tile-red, .bk-tile-green) .bk-tile-value {
    color: color-mix(in srgb, var(--bk-tile-hue) 42%, #000000);
}

.bk-dark :is(.bk-tile-amber, .bk-tile-red, .bk-tile-green) {
    background: color-mix(in srgb, var(--bk-tile-hue) 30%, var(--mud-palette-background));
}

.bk-dark :is(.bk-tile-amber, .bk-tile-red, .bk-tile-green) .bk-tile-label {
    color: color-mix(in srgb, var(--bk-tile-hue) 52%, #FFFFFF);
}

.bk-dark :is(.bk-tile-amber, .bk-tile-red, .bk-tile-green) .bk-tile-value {
    color: color-mix(in srgb, var(--bk-tile-hue) 32%, #FFFFFF);
}

/* Danger colored inline text, e.g. the paused warning on project cards */
.bk-text-paused {
    color: #A32D2D;
}

.bk-dark .bk-text-paused {
    color: #F09595;
}

/* Neutral colored inline text, e.g. the archived reason on cards and the collapsed board section */
.bk-text-archived {
    color: #453F79;
}

.bk-dark .bk-text-archived {
    color: #D9D6F2;
}

/* Pulsing dot indicating a running agent or an active state */
@keyframes bk-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.35; }
}

.bk-pulse-dot {
    display: inline-block;
    width: 7px;
    height: 7px;
    border-radius: var(--bk-radius-round);
    animation: bk-pulse 1.4s ease-in-out infinite;
    flex-shrink: 0;
}

/* Small square color marker for projects (sidebar, cards) */
.bk-proj-dot {
    display: inline-block;
    width: 9px;
    height: 9px;
    border-radius: var(--bk-radius-xs);
    flex-shrink: 0;
}

/* Sidebar: current project selector (MudMenu activator in MainLayout).
   MudMenu is inline-level by default, which would make the activator narrower
   than the full-width search box below it and mis-size the FullWidth popover.
   Force the menu and its activator to a full-width block so both line up. */
.bk-proj-menu {
    display: block;
    width: 100%;
}

.bk-proj-menu .mud-menu-activator {
    display: block;
    width: 100%;
}

.bk-proj-select {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 7px 12px;
    border: 1px solid var(--mud-palette-lines-inputs);
    border-radius: var(--bk-radius-sm);
    background: var(--mud-palette-surface);
    cursor: pointer;
    transition: border-color var(--bk-dur-fast) var(--bk-ease-out);
}

.bk-proj-select:hover {
    border-color: var(--mud-palette-primary);
}

.bk-proj-select-text {
    min-width: 0;
    flex: 1;
}

.bk-proj-select-label {
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    line-height: 1.2;
    color: var(--mud-palette-text-secondary);
}

.bk-proj-select-name {
    font-size: 0.8125rem;
    font-weight: 500;
    line-height: 1.3;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Sidebar project switcher popover (MudMenu PopoverClass/ListClass, see the
   MudMenu markup in MainLayout.razor): a soft floating-card treatment
   (rounded corners, border, layered shadow) instead of MudBlazor's flat
   default, echoing the shadow bk-cmdbar already uses for its own floating
   surface. app.css loads after MudBlazor.min.css, so these plain classes
   already win the cascade without needing !important.

   width is a plain, static match for the activator's own content width
   (MudDrawer Width="230px" in MainLayout.razor, minus the "px-3" 12px side
   padding) instead of using MudMenu's RelativeWidth="DropdownWidth.Adaptive":
   that JS-driven positioning is flaky with a custom ActivatorContent in this
   MudBlazor version (intermittently stuck off-screen at 0 opacity). A fixed
   CSS width keeps MudMenu on its normal positioning codepath - overflowing
   content is handled by the ellipsis on bk-proj-item-name (plus its title
   attribute), not by growing this box.

   left is forced too, and needs !important to beat the plain (non-important)
   inline "left" MudBlazor's own position script sets: that script still
   consistently lands this popover a fixed 12px right of the activator's true
   left edge (verified via getBoundingClientRect, not just eyeballed - at this
   width the gap reads as "not quite flush" rather than obviously wrong)
   however this popover is opened, width-related JS aside or not. The drawer
   is pinned to the viewport's left edge (mud-drawer-fixed) and never
   scrolls, so document-coordinate 12px - the same "px-3" padding used for
   width above - is always the activator's true left edge here, not a
   value that could drift with scroll position. */
.bk-proj-popover {
    width: 206px;
    left: 12px !important;
    border-radius: var(--bk-radius-sm);
    border: 1px solid var(--mud-palette-lines-inputs);
    box-shadow: 0 2px 4px rgba(38, 36, 58, 0.08), 0 10px 24px rgba(38, 36, 58, 0.14);
}

.bk-dark .bk-proj-popover {
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.45), 0 10px 24px rgba(0, 0, 0, 0.5);
}

.bk-proj-list {
    padding: 6px;
}

.bk-proj-item {
    border-radius: var(--bk-radius-sm);
    padding: 8px 10px;
    margin: 1px 0;
}

.bk-proj-item:hover,
.bk-proj-item:focus-visible {
    background: color-mix(in srgb, var(--mud-palette-primary) 10%, transparent);
}

/* The currently selected project: a faint permanent tint plus the check mark
   (see bk-proj-item-check) so the active project reads at a glance, not only
   on hover. */
.bk-proj-item-active {
    font-weight: 600;
    background: color-mix(in srgb, var(--mud-palette-primary) 6%, transparent);
}

.bk-proj-item-row {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
}

.bk-proj-item-name {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.bk-proj-item-check {
    flex-shrink: 0;
    color: var(--mud-palette-primary);
}

.bk-proj-divider {
    margin: 4px 0;
}

/* "Alle Projekte": a tinted icon tile (same treatment as the command bar's
   bk-kindchip) sets the "leave this project" action apart from the colored
   project dots above it. */
.bk-proj-item-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    border-radius: var(--bk-radius-sm);
    flex-shrink: 0;
    background: var(--mud-palette-background-gray);
    color: var(--mud-palette-text-secondary);
}

/* Sidebar entries (SidebarEntry.razor.css): the "Wartet auf dich" /
   "Agenten aktiv" rows moved their bk-side-* rules into their own component
   stylesheet, since SidebarEntry is their only consumer (see project
   convention on component-local CSS). */

/* Segmented mini bar showing the status distribution of a project */
.bk-statusbar {
    display: flex;
    height: 6px;
    border-radius: var(--bk-radius-xs);
    overflow: hidden;
}

/* Clickable cards (board cards, project cards) */
.bk-clickable {
    cursor: pointer;
    transition: border-color var(--bk-dur-fast) var(--bk-ease-out), background-color var(--bk-dur-fast) var(--bk-ease-out);
}

.bk-clickable:hover {
    border-color: var(--mud-palette-primary) !important;
}

/* Feature detail dossier (FeatureDetail): sticky command bar above a phase
   stream. Each pipeline phase is a collapsible section docked to a continuous
   spine; the current phase is expanded and highlighted, past phases collapse
   to summary rows, future phases render as dashed ghost placeholders. */

/* Command bar: title, KPI chips and the primary action. Sticky so the
   primary action stays reachable while scrolling; on phones it clears the
   fixed 48px mobile app bar (see .bk-mobile-appbar above). The translucent
   blurred background and the layered shadow set it apart from the flat
   outlined section cards it floats over. */
.bk-cmdbar {
    position: sticky;
    top: 8px;
    z-index: 20;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
    padding: 10px 16px;
    margin-bottom: 16px;
    background: color-mix(in srgb, var(--mud-palette-surface) 86%, transparent);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    border-color: var(--mud-palette-lines-inputs);
    box-shadow: 0 1px 2px rgba(38, 36, 58, 0.08), 0 6px 18px rgba(38, 36, 58, 0.12);
}

.bk-dark .bk-cmdbar {
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 6px 18px rgba(0, 0, 0, 0.45);
}

/* Phone-only extra row (phase, waiting state, the Freigeben/Aenderungen
   anfordern decision) - see the phone media query below for its content. */
.bk-cmdbar-phone {
    display: none;
}

.bk-cmdbar-title {
    font-size: 1.0625rem;
    font-weight: 600;
    line-height: 1.3;
}

.bk-kindchip {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border-radius: var(--bk-radius-sm);
    flex-shrink: 0;
    background: color-mix(in srgb, var(--mud-palette-primary) 14%, transparent);
    color: var(--mud-palette-primary);
}

/* KPI chips in the command bar (cost, QA escalation, branch, live agent) */
.bk-kpi {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 0.71875rem;
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: var(--bk-radius-pill);
    padding: 1px 10px;
    color: var(--mud-palette-text-secondary);
    white-space: nowrap;
}

.bk-kpi b {
    color: var(--mud-palette-text-primary);
    font-weight: 600;
}

.bk-kpi code {
    background: none;
    padding: 0;
    font-size: 0.6875rem;
}

.bk-kpi-live {
    border-color: #EF9F27;
    background: #FCF3E3;
    color: #7A5310;
}

.bk-dark .bk-kpi-live {
    background: #4A3A14;
    color: #F2CE8C;
}

/* Blocked-tool-calls chip: a warning-tinted KPI shown only when the feature
   has denied agent tool calls (see FeatureDetail, AgentRunsTab). */
.bk-kpi-blocked {
    border-color: #EF9F27;
    background: #FCF3E3;
    color: #7A5310;
    font-weight: 600;
}

.bk-dark .bk-kpi-blocked {
    background: #4A3A14;
    color: #F2CE8C;
}

/* Protokoll trigger: a single pill in the command bar that summarises the run
   telemetry (cost, turns, blocked calls, QA escalation) and opens the Protokoll
   drawer. Replaces the former row of separate KPI chips. */
.bk-protokoll-trigger {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 0.71875rem;
    color: var(--mud-palette-text-secondary);
    background: var(--mud-palette-surface);
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: var(--bk-radius-pill);
    padding: 3px 6px 3px 12px;
    white-space: nowrap;
    cursor: pointer;
    transition: border-color var(--bk-dur-fast) var(--bk-ease-out), background-color var(--bk-dur-fast) var(--bk-ease-out);
}

.bk-protokoll-trigger:hover {
    border-color: var(--mud-palette-text-secondary);
    background: color-mix(in srgb, var(--mud-palette-text-primary) 6%, transparent);
}

.bk-protokoll-trigger:focus-visible {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: 1px;
}

.bk-pt-metric b {
    color: var(--mud-palette-text-primary);
    font-weight: 600;
}

/* Thin vertical divider between the trigger's summary groups */
.bk-pt-sep {
    width: 1px;
    height: 14px;
    background: var(--mud-palette-lines-default);
    flex-shrink: 0;
}

.bk-pt-blocked {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    font-weight: 600;
    color: #B26A00;
}

.bk-dark .bk-pt-blocked {
    color: #F2CE8C;
}

/* Chevron hinting the pill opens a panel; nudged to the far right */
.bk-pt-chev {
    color: var(--mud-palette-text-secondary);
    margin-left: 2px;
}

/* Protokoll drawer: right-anchored slide-over holding the unabridged record.
   The width is set inline by MudDrawer; cap it so it never overflows a phone. */
.bk-protokoll-drawer {
    max-width: 92vw;
}

/* Neumorphic edge: a soft shadow on the leading (left) side instead of the
   dividing line MudDrawer's own border would draw, and a rounded inner
   corner to match - the other three sides stay flush with the viewport
   (Fixed drawer), same as the reference design's own drawer state. Scoped to
   .bk-protokoll-drawer rather than to .mud-drawer in general so the app's
   persistent navigation drawer (.bk-drawer-nav, out of scope for this
   change) keeps its own existing look. */
body:has(.bk-soft) .bk-protokoll-drawer {
    border-color: transparent;
    border-radius: var(--bk-radius-lg) 0 0 var(--bk-radius-lg);
    box-shadow: -14px 0 32px -8px var(--bk-soft-lo);
}

.bk-drawer-head {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 12px 16px;
    border-bottom: 1px solid var(--mud-palette-lines-default);
    flex-shrink: 0;
}

/* The Protokoll drawer's own header reads seamless, like every other
   floating layer's - only .bk-drawer-head's OTHER user, the persistent
   navigation drawer's header, keeps the divider above. */
.bk-protokoll-drawer .bk-drawer-head {
    border-bottom: none;
}

.bk-drawer-title {
    font-size: 1rem;
    font-weight: 600;
}

.bk-drawer-body {
    padding: 16px;
    overflow-y: auto;
}

.bk-drawer-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 16px;
}

/* Meta chips inside the Protokoll drawer. A dedicated class rather than the
   command-bar .bk-kpi so the mobile "hide KPI chips" rule does not hide them. */
.bk-drawer-chip {
    display: inline-flex;
    align-items: center;
    font-size: 0.71875rem;
    color: var(--mud-palette-text-secondary);
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: var(--bk-radius-pill);
    padding: 1px 10px;
    white-space: nowrap;
}

.bk-drawer-chip code {
    background: none;
    padding: 0;
    font-size: 0.6875rem;
}

/* Agent-run list: a stacked-card alternative to the former wide table, sized
   for the narrow Protokoll drawer. Each card carries the run identity on top
   and its metrics plus actions below; both lines reflow rather than overflow. */
.bk-run-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.bk-run-row {
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: var(--bk-radius-sm);
    padding: 8px 12px;
}

.bk-run-line {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
}

.bk-run-line-meta {
    margin-top: 6px;
}

.bk-run-role {
    font-weight: 500;
    font-size: 0.8125rem;
    flex-shrink: 0;
}

/* Result label yields first: it truncates so the status pill keeps its place */
.bk-run-result {
    color: var(--mud-palette-text-secondary);
    font-size: 0.75rem;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The result line as the row's own link into the run (AgentRunRow.TitleHref):
   it keeps the muted result styling and only picks up an underline on hover,
   so a list of rows does not read as a wall of links. */
.bk-run-result-link {
    text-decoration: none;
}

.bk-run-result-link:hover,
.bk-run-result-link:focus-visible {
    color: var(--mud-palette-text-primary);
    text-decoration: underline;
}

.bk-run-metrics {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px 10px;
    color: var(--mud-palette-text-secondary);
    font-size: 0.75rem;
    min-width: 0;
}

.bk-run-blocked {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    font-weight: 600;
    color: #B26A00;
}

.bk-dark .bk-run-blocked {
    color: #F2CE8C;
}

.bk-run-actions {
    display: flex;
    gap: 6px;
    flex-shrink: 0;
}

/* Blocked-tool-calls panel in the Protokoll section: a warning-accented card
   listing the feature's denied agent tool calls grouped by command. */
.bk-blocked-panel {
    border: 1px solid rgba(239, 159, 39, 0.45);
    border-left: 3px solid #EF9F27;
    border-radius: var(--bk-radius-xs);
    padding: 12px 14px;
    margin-bottom: 16px;
    background: rgba(239, 159, 39, 0.06);
}

.bk-blocked-suggestion {
    font-family: monospace;
    font-size: 0.6875rem;
    padding: 1px 7px;
    border-radius: var(--bk-radius-xs);
    background: rgba(29, 158, 117, 0.14);
    color: #0F6E56;
}

.bk-dark .bk-blocked-suggestion {
    background: rgba(29, 158, 117, 0.22);
    color: #5DCAA5;
}

.bk-blocked-intentional {
    font-size: 0.6875rem;
    padding: 1px 7px;
    border-radius: var(--bk-radius-xs);
    background: rgba(226, 75, 74, 0.14);
    color: #A32D2D;
}

.bk-dark .bk-blocked-intentional {
    color: #F09595;
}

/* Spine: a continuous vertical line connecting the phase dots; segments up
   to the current phase render in the primary color. */
.bk-spine {
    display: grid;
}

.bk-sp-row {
    display: grid;
    grid-template-columns: 30px 1fr;
}

.bk-sp-rail {
    position: relative;
}

.bk-sp-rail::before {
    content: "";
    position: absolute;
    left: 10px;
    top: 0;
    bottom: 0;
    width: 2px;
    background: var(--mud-palette-lines-default);
}

.bk-sp-first .bk-sp-rail::before {
    top: 18px;
}

.bk-sp-last .bk-sp-rail::before {
    bottom: auto;
    height: 18px;
}

.bk-sp-done .bk-sp-rail::before {
    background: var(--mud-palette-primary);
}

.bk-sp-dot {
    position: absolute;
    left: 1px;
    top: 12px;
    z-index: 2;
    width: 20px;
    height: 20px;
    border-radius: var(--bk-radius-round);
    display: flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
}

.bk-sp-dot-done {
    background: var(--mud-palette-primary);
    color: #fff;
}

.bk-sp-dot-cur {
    background: var(--mud-palette-surface);
    border: 2px solid var(--mud-palette-primary);
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--mud-palette-primary) 18%, transparent);
}

.bk-sp-dot-open {
    background: var(--mud-palette-background);
    border: 1px solid var(--mud-palette-lines-default);
}

.bk-sp-dot-fail {
    background: #E24B4A;
    color: #fff;
}

/* Smaller end cap dot for the final "Fertig" node */
.bk-sp-dot-end {
    width: 14px;
    height: 14px;
    left: 4px;
    top: 4px;
}

.bk-sp-body {
    padding: 0 0 10px 10px;
    min-width: 0;
}

/* Phase sections on the spine */
.bk-sec {
    background: var(--mud-palette-surface);
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: var(--bk-radius-sm);
}

.bk-sec-cur {
    border-color: var(--mud-palette-primary);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--mud-palette-primary) 12%, transparent);
}

.bk-sec-ghost {
    border-style: dashed;
    background: transparent;
}

.bk-sec-head {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    padding: 10px 14px;
    min-height: 42px;
}

.bk-clickable-head {
    cursor: pointer;
}

.bk-sec-title {
    font-weight: 600;
    font-size: 0.875rem;
}

/* Compact phase-state dot inside the section header, phone-only (the rail's
   own dot serves this purpose at Sm and up, see .bk-sp-dot below - both read
   PhaseSpineState.DotState so they always agree). Hidden here, shown in the
   phone media query. */
.bk-sec-dot {
    display: none;
    align-items: center;
    justify-content: center;
    width: 10px;
    height: 10px;
    border-radius: var(--bk-radius-round);
    flex-shrink: 0;
    box-sizing: border-box;
}

.bk-sec-dot-done { background: var(--mud-palette-primary); }
.bk-sec-dot-cur { background: var(--mud-palette-surface); border: 2px solid var(--mud-palette-primary); }
.bk-sec-dot-open { background: var(--mud-palette-background); border: 1px solid var(--mud-palette-lines-default); }
.bk-sec-dot-fail { background: #E24B4A; }

.bk-sec-meta {
    font-size: 0.71875rem;
    color: var(--mud-palette-text-secondary);
}

/* One-line summary of a collapsed section, truncated by the flex layout */
.bk-sec-preview {
    flex: 1 1 140px;
    min-width: 120px;
    font-size: 0.75rem;
    color: var(--mud-palette-text-secondary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.bk-sec-chev {
    margin-left: auto;
    flex-shrink: 0;
}

.bk-sec-body {
    border-top: 1px solid var(--mud-palette-lines-default);
    padding: 16px;
}

/* End cap row ("Fertig") without a card */
.bk-sec-endcap {
    padding: 2px 0 0 4px;
    font-size: 0.75rem;
    color: var(--mud-palette-text-secondary);
}

.bk-sec-endcap-done {
    color: #27500A;
    font-weight: 600;
}

.bk-dark .bk-sec-endcap-done {
    color: #C0DD97;
}

/* QA check results as a compact dot strip in the section header */
.bk-checkdots {
    display: inline-flex;
    gap: 3px;
}

.bk-checkdots span {
    width: 8px;
    height: 8px;
    border-radius: var(--bk-radius-round);
}

@media (max-width: 599.98px) {
    /* The KPI chips are secondary information; on phones they yield to the
       title and the primary action. The live agent chip stays visible. */
    .bk-kpi {
        display: none;
    }

    .bk-kpi-live {
        display: inline-flex;
    }

    /* Compact the Protokoll trigger on phones: keep the icon, the blocked
       badge and the chevron, drop the wordier cost summary. */
    .bk-pt-metric,
    .bk-pt-sep {
        display: none;
    }

    /* No mobile app bar to clear any more on a phone (feature "Phone
       navigation at the bottom edge" removes it for the bottom bar/sheet
       instead), so this sticks at the same top: 8px the base rule already
       uses - no override needed here. */
    .bk-cmdbar {
        padding: 8px 10px;
        gap: 6px;
    }

    .bk-cmdbar-title {
        font-size: 0.9375rem;
        min-width: 0;
        overflow-wrap: anywhere;
    }

    /* Phone-only status/decision row, see FeatureCommandBar: phase, waiting
       state and (for the pending-proposal case) the Freigeben/Aenderungen
       anfordern buttons, stacked below the title row. width: 100% forces it
       onto its own line inside the bar's flex-wrap layout regardless of how
       much room the row above used. */
    .bk-cmdbar-phone {
        display: flex;
        align-items: center;
        flex-wrap: wrap;
        gap: 8px;
        width: 100%;
        padding-top: 6px;
        margin-top: 2px;
        border-top: 1px solid var(--mud-palette-lines-default);
    }

    .bk-cmdbar-phone-phase {
        font-weight: 600;
        font-size: 0.8125rem;
        flex-shrink: 0;
    }

    /* Phase stream: the rail column (connecting line and dot) gives way to a
       flat list of edge-to-edge cards; the phase dot moves into the section
       header instead (bk-sec-dot). On a 360px phone the rail column and the
       outer card border together used to cost about 40% of the width. */
    .bk-sp-row {
        grid-template-columns: 1fr;
    }

    .bk-sp-rail {
        display: none;
    }

    .bk-sp-body {
        padding: 0 0 10px;
    }

    /* Sections bleed to the screen edge (cancels .bk-page's 12px phone inset,
       see the .bk-page phone rule above - bk-spine sits directly inside
       .bk-page with no padding of its own in between) and drop their frame,
       keeping only a horizontal divider between stacked sections. */
    .bk-sec {
        margin: 0 -12px;
        border-left: 0;
        border-right: 0;
        border-radius: 0;
    }

    /* The outer glow ring would bleed past the viewport edge now that the
       card itself reaches it; an inset ring keeps the current-phase
       highlight without widening the box. */
    .bk-sec-cur {
        box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--mud-palette-primary) 12%, transparent);
    }

    .bk-sec-head {
        padding: 10px 12px;
        gap: 6px;
    }

    .bk-sec-head > * {
        min-width: 0;
    }

    .bk-sec-title {
        overflow-wrap: anywhere;
    }

    /* The one-line collapsed-body preview gives way to letting title, status
       pill and meta text wrap onto a second line instead - a mid-sentence
       truncation is less useful on a phone than the status pill next to it,
       see the architecture decision on the phase headers. */
    .bk-sec-preview {
        display: none;
    }

    .bk-sec-dot {
        display: inline-flex;
    }

    .bk-sec-body {
        padding: 10px 12px;
    }

    /* Inner MudPaper cards inside a phase body (ArchitectureTab, QaTab,
       ChangesTab, DiagnosisTab): the phase section already carries its own
       edge-to-edge frame above, so a second nested border/radius/padding
       here only ate into the readable width. The phase body's own padding
       becomes the single horizontal gutter; a bottom rule keeps stacked
       cards visually separated instead of the border that used to. */
    .bk-phase-card {
        border-left: 0;
        border-right: 0;
        border-top: 0;
        border-bottom: 1px solid var(--mud-palette-lines-default);
        border-radius: 0;
        padding-left: 0;
        padding-right: 0;
    }
}

/* Small count badge, e.g. in the sidebar sections of MainLayout */
.bk-tab-count {
    display: inline-block;
    font-size: 10px;
    font-weight: 500;
    line-height: 1.5;
    border-radius: var(--bk-radius-sm);
    padding: 0 6px;
    margin-left: 6px;
    background: var(--mud-palette-background-gray);
    color: var(--mud-palette-text-secondary);
    vertical-align: middle;
}

/* Settings pages: tinted icon square in section card headers */
.bk-settings-icon {
    width: 36px;
    height: 36px;
    border-radius: var(--bk-radius-md);
    display: flex;
    align-items: center;
    justify-content: center;
    /* Tinted with the active theme's accent (ThemePresets), not a fixed purple */
    background: rgba(var(--mud-palette-primary-rgb), 0.12);
    color: var(--mud-palette-primary);
    flex-shrink: 0;
}

.bk-dark .bk-settings-icon {
    background: rgba(var(--mud-palette-primary-rgb), 0.2);
    color: var(--mud-palette-primary);
}

/* Danger variant for the settings icon and the Gefahrenzone hub card */
.bk-settings-icon-danger {
    background: #FCEBEB;
    color: #A32D2D;
}

.bk-dark .bk-settings-icon-danger {
    background: #501313;
    color: #F09595;
}

.bk-danger-card {
    border-color: var(--mud-palette-error) !important;
}

/* Rendered markdown content inside a MarkdownField. Capped narrower than
   .bk-page so flowing text stays readable on wide/ultra-wide monitors. */
.bk-markdown {
    font-size: 0.875rem;
    line-height: 1.55;
    overflow-wrap: break-word;
    max-width: 860px;
}

.bk-markdown > :first-child {
    margin-top: 0;
}

.bk-markdown > :last-child {
    margin-bottom: 0;
}

.bk-markdown h1,
.bk-markdown h2,
.bk-markdown h3,
.bk-markdown h4,
.bk-markdown h5,
.bk-markdown h6 {
    margin: 14px 0 6px;
    line-height: 1.3;
    font-weight: 600;
}

.bk-markdown h1 { font-size: 1.25rem; }
.bk-markdown h2 { font-size: 1.125rem; }
.bk-markdown h3 { font-size: 1rem; }
.bk-markdown h4,
.bk-markdown h5,
.bk-markdown h6 { font-size: 0.875rem; }

.bk-markdown p {
    margin: 0 0 8px;
}

.bk-markdown ul,
.bk-markdown ol {
    margin: 0 0 8px;
    padding-left: 22px;
}

.bk-markdown li + li {
    margin-top: 2px;
}

.bk-markdown code {
    background: var(--mud-palette-background-gray);
    border-radius: var(--bk-radius-xs);
    padding: 1px 5px;
    font-size: 0.8125rem;
}

.bk-markdown pre {
    background: var(--mud-palette-background-gray);
    border-radius: var(--bk-radius-sm);
    padding: 10px 12px;
    margin: 0 0 8px;
    overflow-x: auto;
}

.bk-markdown pre code {
    background: none;
    padding: 0;
}

.bk-markdown blockquote {
    border-left: 3px solid var(--mud-palette-lines-default);
    margin: 0 0 8px;
    padding: 2px 12px;
    color: var(--mud-palette-text-secondary);
}

/* A table wider than its column (a wide fact table in a generated report)
   scrolls sideways on its own instead of pushing the whole page out - the
   standard "make a table element its own scroll container" trick, harmless
   when the table already fits (no scrollbar appears). */
.bk-markdown table {
    display: block;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    border-collapse: collapse;
    margin: 0 0 8px;
}

.bk-markdown th,
.bk-markdown td {
    border: 1px solid var(--mud-palette-lines-default);
    padding: 4px 10px;
    text-align: left;
}

.bk-markdown a {
    color: var(--mud-palette-primary);
}

.bk-markdown hr {
    border: 0;
    border-top: 1px solid var(--mud-palette-lines-default);
    margin: 10px 0;
}

/* Dialog header: tinted icon square plus a title and a supporting line.
   Shared by the create and edit feature dialogs so both read the same. */
.bk-dialog-head {
    display: flex;
    align-items: flex-start;
    gap: 12px;
}

/* A well, not a flat tinted square: the fixed purple this used to hardcode
   read wrong on every theme but the classic default (a dialog opened under
   e.g. the green or red preset kept the same purple icon regardless), which
   is exactly the kind of per-preset gap the acceptance criterion "gilt in
   jedem Farbthema" rules out. The primary color rides on the icon alone, the
   same idiom the status pills and nav icons already use elsewhere. */
.bk-dialog-head-icon {
    width: 40px;
    height: 40px;
    border-radius: var(--bk-radius-md);
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    color: var(--mud-palette-primary);
}

body:has(.bk-soft) .bk-dialog-head-icon {
    background: var(--mud-palette-background);
    box-shadow: var(--bk-depth-inset-sm);
}

.bk-dialog-sub {
    margin-top: 2px;
    color: var(--mud-palette-text-secondary);
    font-size: 0.8125rem;
    line-height: 1.4;
    font-weight: 400;
}

/* Dialog shape, shadow and header now come entirely from the shared bk-soft
   rules above (body:has(.bk-soft) .mud-dialog and the scrim rule next to
   it): a seamless header with no tint and no divider line, floating on the
   same shadow tokens as every other panel - MudDialogProvider renders as a
   sibling of <MudLayout>, so a class-selector ancestor rule like .bk-soft
   could never reach a dialog, which is why those rules key off
   body:has(.bk-soft) instead. */

/* Fullscreen viewers (live transcripts, diagram/mockup zoom) fill the whole
   viewport, so the floating-panel chrome above would look wrong there - strip
   it back to flush edges. !important is needed here (not just belt-and-
   braces): body:has(.bk-soft) .mud-dialog above carries one more simple
   selector than .mud-dialog.mud-dialog-fullscreen, so without it that rule's
   own radius/shadow would win on specificity and a "fullscreen" dialog would
   still show rounded, floating chrome instead of filling the viewport edge
   to edge. */
.mud-dialog.mud-dialog-fullscreen,
.mud-dialog.mud-dialog-fullscreen .mud-dialog-title {
    border: none !important;
    box-shadow: none !important;
    border-radius: 0 !important;
}

/* A dialog that fills the phone screen below MudBlazor's Sm breakpoint but
   keeps its centered, capped width on wider viewports - MudBlazor's own
   FullScreen option is all-or-nothing. Applied through MudDialog's Class; the
   dialog element renders under MudDialogProvider, outside every page's scoped
   CSS, so this cannot live in a *.razor.css. Mirrors MudBlazor's
   .mud-dialog-fullscreen geometry plus the flush-edge chrome above. Used by
   the task page's transcript dialog, whose transcript would otherwise lose a
   fifth of a 360px screen to the default dialog margins. */
@media (max-width: 599.98px) {
    .mud-dialog.bk-dialog-phone-full {
        width: 100%;
        max-width: 100%;
        height: 100%;
        max-height: none;
        margin: 0;
        border: none !important;
        border-radius: 0 !important;
        box-shadow: none !important;
        overflow-y: hidden;
    }

    .mud-dialog.bk-dialog-phone-full .mud-dialog-title {
        border-radius: 0 !important;
    }
}

/* Segmented Feature/Bug switch in the new-feature dialog. The selected segment
   carries the accent: primary for a feature, error (bk-seg-on-bug) for a bug. */
.bk-seg-group {
    display: flex;
    gap: 6px;
    background: var(--mud-palette-background-gray);
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: var(--bk-radius-md);
    padding: 4px;
}

.bk-seg {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    height: 38px;
    border-radius: var(--bk-radius-sm);
    font-size: 0.875rem;
    font-weight: 500;
    font-family: inherit;
    color: var(--mud-palette-text-secondary);
    background: transparent;
    border: 1px solid transparent;
    cursor: pointer;
    transition: background var(--bk-dur-fast), color var(--bk-dur-fast), border-color var(--bk-dur-fast);
}

.bk-seg:hover {
    color: var(--mud-palette-text-primary);
}

.bk-seg-on,
.bk-seg-on:hover {
    background: var(--mud-palette-primary);
    color: var(--mud-palette-primary-text);
    border-color: var(--mud-palette-primary);
}

.bk-seg-on-bug,
.bk-seg-on-bug:hover {
    background: var(--mud-palette-error);
    color: var(--mud-palette-error-text);
    border-color: var(--mud-palette-error);
}

/* Reference-image drop zone. The native file input is stretched transparently
   over the whole zone (bk-drop-input) so a click opens the picker and a file
   drop is captured natively (no browser open-in-new-tab). The dashed area is
   the MudFileUpload CustomContent underneath. */
.bk-drop-root {
    position: relative;
    display: block;
    width: 100%;
}

.bk-drop-input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    cursor: pointer;
    z-index: 1;
}

/* Denser dialog body: trim MudDialog's default content padding so the whole
   new-feature form fits without scrolling. */
.bk-dialog-dense {
    padding-top: 8px;
    padding-bottom: 8px;
}

.bk-drop {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    text-align: center;
    padding: 10px 16px;
    min-height: 82px;
    border: 1.5px dashed var(--mud-palette-lines-default);
    border-radius: var(--bk-radius-md);
    background: var(--mud-palette-background-gray);
    cursor: pointer;
    transition: border-color var(--bk-dur-fast), background var(--bk-dur-fast);
}

.bk-drop:hover,
.bk-drop-root:hover .bk-drop {
    border-color: var(--mud-palette-primary);
}

.bk-drop-root:hover .bk-drop-bug {
    border-color: var(--mud-palette-error);
}

.bk-drop-active {
    border-color: var(--mud-palette-primary);
    background: color-mix(in srgb, var(--mud-palette-primary) 8%, transparent);
}

.bk-drop-bug:hover,
.bk-drop-bug.bk-drop-active {
    border-color: var(--mud-palette-error);
}

.bk-drop-icon {
    width: 30px;
    height: 30px;
    border-radius: var(--bk-radius-sm);
    display: flex;
    align-items: center;
    justify-content: center;
    background: #EEEDFE;
    color: #3C3489;
}

.bk-dark .bk-drop-icon {
    background: #3C3489;
    color: #CECBF6;
}

.bk-drop-bug .bk-drop-icon {
    background: #FCEBEB;
    color: #A32D2D;
}

.bk-dark .bk-drop-bug .bk-drop-icon {
    background: #501313;
    color: #F7C1C1;
}

.bk-drop-text {
    font-size: 0.8125rem;
    color: var(--mud-palette-text-primary);
}

.bk-drop-link {
    color: var(--mud-palette-primary);
    font-weight: 500;
}

.bk-drop-bug .bk-drop-link {
    color: var(--mud-palette-error);
}

.bk-drop kbd {
    border: 0.5px solid var(--mud-palette-lines-default);
    border-radius: var(--bk-radius-sm);
    padding: 1px 5px;
    font-size: 0.6875rem;
    font-family: monospace;
    background: var(--mud-palette-surface);
}

/* Secondary hint line under the primary action (formats, size, paste shortcut) */
.bk-drop-meta {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: 6px;
    font-size: 0.75rem;
    color: var(--mud-palette-text-secondary);
}

.bk-drop-sep {
    opacity: 0.5;
}

/* Drag-over state: the whole zone becomes a single accent "drop here" target */
.bk-drop-dl {
    font-size: 26px;
    color: var(--mud-palette-primary);
}

.bk-drop-drag-text {
    font-weight: 500;
    color: var(--mud-palette-primary);
}

.bk-drop-bug .bk-drop-dl,
.bk-drop-bug .bk-drop-drag-text {
    color: var(--mud-palette-error);
}

/* Limit-reached state: passive, muted, laid out as a single row */
.bk-drop-max,
.bk-drop-max:hover {
    flex-direction: row;
    gap: 8px;
    min-height: 0;
    padding: 12px 16px;
    color: var(--mud-palette-text-disabled);
    background: var(--mud-palette-background-gray);
    border-color: var(--mud-palette-lines-default);
    font-size: 0.8125rem;
    cursor: default;
}

/* Pending-attachment thumbnail grid (both pasted and picker-selected images) */
.bk-thumb-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
    gap: 10px;
}

.bk-thumb-card {
    min-width: 0;
}

.bk-thumb {
    position: relative;
    aspect-ratio: 1;
    border-radius: var(--bk-radius-md);
    overflow: hidden;
    border: 1px solid var(--mud-palette-lines-default);
}

.bk-thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.bk-thumb-x {
    position: absolute;
    top: 5px;
    right: 5px;
    width: 22px;
    height: 22px;
    padding: 0;
    border: none;
    border-radius: var(--bk-radius-round);
    background: rgba(20, 20, 22, 0.62);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    opacity: 0;
    transition: opacity var(--bk-dur-fast);
}

.bk-thumb:hover .bk-thumb-x,
.bk-thumb-x:focus-visible {
    opacity: 1;
}

.bk-thumb-name {
    font-size: 0.6875rem;
    color: var(--mud-palette-text-secondary);
    margin-top: 4px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.bk-thumb-size {
    font-size: 0.6875rem;
    color: var(--mud-palette-text-disabled);
}

/* AttachmentFileList: a non-image attachment's file row (name, Dateiart,
   size, remove-before-send or download-after-upload), reused by the task
   intake dialog, the task page, the chat composer/transcript and the
   requirement page - the sibling of the thumbnail grid above for anything
   that is not a picture. */
.bk-file-list {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.bk-file-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 10px;
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: var(--bk-radius-sm);
    min-width: 0;
}

.bk-file-row-icon {
    flex-shrink: 0;
    color: var(--mud-palette-text-secondary);
}

.bk-file-row-info {
    flex: 1;
    min-width: 0;
}

.bk-file-row-name {
    font-size: 0.8125rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.bk-file-row-meta {
    font-size: 0.6875rem;
    color: var(--mud-palette-text-secondary);
}

/* Preview of an editable MarkdownField, framed like the outlined textarea */
.bk-markdown-frame {
    border: 1px solid var(--mud-palette-lines-inputs);
    border-radius: var(--bk-radius-sm);
    padding: 10px 14px;
}

/* Callout row for the cross-phase states (paused, archived) under the
   command bar; danger and info follow the same tint/text pairing as the
   status pills. */
.bk-callout-danger,
.bk-callout-info {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px;
    border-radius: var(--bk-radius-sm);
    padding: 8px 14px;
}

/* Lets the text block shrink/wrap on a narrow viewport instead of forcing
   the callout (and the page) to scroll horizontally; icon and button keep
   their intrinsic size. */
.bk-callout-danger > *,
.bk-callout-info > * {
    min-width: 0;
}

.bk-callout-danger { --bk-callout-hue: var(--bk-hue-paused); }
.bk-callout-info { --bk-callout-hue: var(--bk-hue-development); }

.bk-callout-danger,
.bk-callout-info {
    background: color-mix(in srgb, var(--bk-callout-hue) 14%, var(--mud-palette-background));
    color: color-mix(in srgb, var(--bk-callout-hue) 58%, #000000);
}

.bk-dark .bk-callout-danger,
.bk-dark .bk-callout-info {
    background: color-mix(in srgb, var(--bk-callout-hue) 26%, var(--mud-palette-background));
    color: color-mix(in srgb, var(--bk-callout-hue) 52%, #FFFFFF);
}

/* Tiny progress bar for escalation counters (revisions, QA fails) */
.bk-limitbar {
    display: inline-block;
    width: 44px;
    height: 4px;
    border-radius: var(--bk-radius-xs);
    background: var(--mud-palette-background-gray);
    overflow: hidden;
    vertical-align: middle;
}

.bk-limitbar > span {
    display: block;
    height: 100%;
    border-radius: var(--bk-radius-xs);
}

#blazor-error-ui {
    background: #b32121;
    color: white;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 2000;
}

    #blazor-error-ui .dismiss {
        cursor: pointer;
        position: absolute;
        right: 0.75rem;
        top: 0.5rem;
    }

    #blazor-error-ui .reload {
        color: white;
    }

.blazor-error-boundary {
    background: #b32121;
    padding: 1rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "Ein Fehler ist aufgetreten."
    }

/* Reconnect notice (markup in MainLayout, feature "Clear reconnecting screen
   when the connection drops"): a small bar at the top edge instead of a
   full-screen overlay, so the page stays readable and usable while the
   circuit reconnects. Blazor's client toggles exactly one
   components-reconnect-{show,hide,failed,rejected} class on the element;
   every visible state looks the same, since retrying never stops
   (blazor-start.js, pwa-interop.js). The element sits outside MudLayout, so
   it reads only the --mud-palette-* variables MudThemeProvider emits on
   :root. MudBlazor's own stylesheet forces a page-colored background and a
   top z-index on this id with !important; the id plus class here outranks
   the first, the second is welcome. It never takes a pointer event: a tap
   meant for the page underneath always reaches it. */
#components-reconnect-modal.bk-reconnect {
    display: none;
    position: fixed;
    top: var(--bk-space-3);
    left: 50%;
    transform: translateX(-50%);
    width: max-content;
    max-width: calc(100vw - 2 * var(--bk-space-3));
    box-sizing: border-box;
    align-items: center;
    gap: var(--bk-space-2);
    padding: var(--bk-space-2) var(--bk-space-4);
    border: 1px solid var(--mud-palette-warning);
    border-radius: var(--bk-radius-md);
    background-color: color-mix(in srgb, var(--mud-palette-warning) 22%, var(--mud-palette-surface)) !important;
    color: var(--mud-palette-text-primary);
    font-family: var(--mud-typography-default-family);
    font-size: 0.8125rem;
    line-height: 1.3;
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.14);
    pointer-events: none;
}

#components-reconnect-modal.components-reconnect-show,
#components-reconnect-modal.components-reconnect-failed,
#components-reconnect-modal.components-reconnect-rejected,
#components-reconnect-modal.components-reconnect-paused {
    display: flex;
}

/* A small dot in the warning hue that breathes while the notice is up. */
.bk-reconnect-pulse {
    flex: none;
    width: 8px;
    height: 8px;
    border-radius: var(--bk-radius-round);
    background: var(--mud-palette-warning);
}

@media (prefers-reduced-motion: no-preference) {
    .bk-reconnect-pulse {
        animation: bk-reconnect-pulse 1.4s ease-in-out infinite;
    }
}

@keyframes bk-reconnect-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.35; }
}

/* The notice's text: may shrink and wrap on a narrow phone so the notice
   never grows past its max-width, and stays in the bar's own weight. */
.bk-reconnect-text {
    min-width: 0;
    font-weight: 500;
    overflow-wrap: anywhere;
}

/* Row lists on the requirement detail page (criteria, linked features, child
   requirements) and the agent run detail page's own criterion-check results:
   a shared separator that only shows between rows (not floating above the
   first one), plus a background hover highlight for the rows that navigate
   elsewhere on click. Lives here rather than in RequirementDetail.razor.css
   because more than one component renders these rows now. */
.bk-req-row:not(:first-child) {
    border-top: 1px solid var(--mud-palette-lines-default);
}

.bk-req-row-clickable {
    cursor: pointer;
    border-radius: var(--bk-radius-sm);
    transition: background-color var(--bk-dur-fast) var(--bk-ease-out);
}

.bk-req-row-clickable:hover {
    background: var(--mud-palette-action-default-hover);
}

.bk-diag-dot {
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: var(--bk-radius-round);
    flex-shrink: 0;
}

/* Diff viewer (Aenderungen tab): unified and side-by-side git diff */

.bk-diff-file {
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: var(--bk-radius-sm);
    margin-bottom: 12px;
    overflow: hidden;
}

.bk-diff-file-header {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 12px;
    cursor: pointer;
    background: var(--mud-palette-background-gray);
    user-select: none;
}

.bk-diff-file-path {
    font-family: monospace;
    font-size: 0.8rem;
    overflow-wrap: anywhere;
    flex: 1;
    min-width: 0;
}

.bk-diff-status {
    font-size: 0.65rem;
    font-weight: 600;
    text-transform: uppercase;
    padding: 1px 7px;
    border-radius: var(--bk-radius-md);
    flex-shrink: 0;
}

.bk-diff-status-add { background: rgba(70, 160, 90, 0.18); color: #2E7D32; }
.bk-diff-status-mod { background: rgba(133, 183, 235, 0.25); color: #1565C0; }
.bk-diff-status-del { background: rgba(226, 75, 74, 0.18); color: #C62828; }
.bk-diff-status-ren { background: rgba(127, 119, 221, 0.2); color: #5E35B1; }

.bk-diff-count-add { color: #2E7D32; font-family: monospace; font-size: 0.75rem; flex-shrink: 0; }
.bk-diff-count-del { color: #C62828; font-family: monospace; font-size: 0.75rem; flex-shrink: 0; }

/* The table itself can grow wider than the viewport (long lines, split mode's
   four columns), so its scroll container - not the page - takes the overflow. */
.bk-diff-scroll {
    overflow-x: auto;
    /* Stops the horizontal scroll gesture from chaining into the page once
       the diff's own scroll edge is reached - on a phone, this is the only
       thing standing between "swipe through a wide diff" and "swipe the
       whole page sideways". */
    overscroll-behavior-x: contain;
}

.bk-diff-table {
    width: 100%;
    border-collapse: collapse;
    font-family: monospace;
    font-size: 12px;
    line-height: 1.5;
}

.bk-diff-table-split {
    table-layout: fixed;
}

.bk-diff-lineno {
    width: 1%;
    min-width: 40px;
    text-align: right;
    padding: 0 8px;
    color: var(--mud-palette-text-secondary);
    user-select: none;
    vertical-align: top;
    border-right: 1px solid var(--mud-palette-lines-default);
}

.bk-diff-table-split .bk-diff-lineno {
    width: 40px;
}

.bk-diff-code {
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    padding: 0 8px;
    vertical-align: top;
}

.bk-diff-ins { background: rgba(70, 160, 90, 0.12); }
.bk-diff-del { background: rgba(226, 75, 74, 0.10); }
.bk-diff-ins-word { background: rgba(70, 160, 90, 0.35); border-radius: var(--bk-radius-xs); }
.bk-diff-del-word { background: rgba(226, 75, 74, 0.30); border-radius: var(--bk-radius-xs); }
.bk-diff-imaginary { background: var(--mud-palette-background-gray); }

.bk-diff-fold {
    text-align: center;
    padding: 3px 8px;
    cursor: pointer;
    color: var(--mud-palette-primary);
    background: var(--mud-palette-background-gray);
    font-size: 0.75rem;
    user-select: none;
}

.bk-diff-fold:hover {
    background: var(--mud-palette-primary-hover);
}

.bk-diff-fold .mud-icon-root {
    font-size: 14px;
    vertical-align: text-bottom;
}

/* Diff annotations: questions and change requests on diff lines */

.bk-diff-annotate-cell {
    width: 22px;
    padding: 0;
    text-align: center;
    vertical-align: top;
}

.bk-diff-annotate {
    visibility: hidden;
    border: none;
    border-radius: var(--bk-radius-xs);
    width: 16px;
    height: 16px;
    line-height: 14px;
    font-size: 12px;
    cursor: pointer;
    background: var(--mud-palette-primary);
    color: var(--mud-palette-primary-text);
    padding: 0;
}

.bk-diff-row:hover .bk-diff-annotate {
    visibility: visible;
}

.bk-anno-cell {
    padding: 4px 8px 4px 30px;
}

.bk-anno {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    padding: 8px 10px;
    border-radius: var(--bk-radius-sm);
    border: 1px solid var(--mud-palette-lines-default);
    font-family: var(--mud-typography-default-family);
    font-size: 0.8rem;
}

.bk-anno-question {
    background: rgba(133, 183, 235, 0.10);
    border-color: rgba(133, 183, 235, 0.45);
}

.bk-anno-change {
    background: rgba(240, 153, 123, 0.10);
    border-color: rgba(240, 153, 123, 0.5);
}

.bk-anno-draft {
    background: var(--mud-palette-background-gray);
}

.bk-anno-type {
    font-weight: 600;
    font-size: 0.7rem;
    text-transform: uppercase;
}

.bk-anno-text {
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

.bk-anno-answer {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin-top: 6px;
    padding: 6px 8px;
    border-radius: var(--bk-radius-xs);
    background: var(--mud-palette-background-gray);
    font-size: 0.8rem;
}

.bk-anno-pending {
    color: var(--mud-palette-text-secondary);
    align-items: center;
}

.bk-anno-answer-user {
    background: transparent;
    border: 1px dashed var(--mud-palette-lines-default);
    border-radius: var(--bk-radius-xs);
}

/* Architecture tab: Mermaid diagram container. Sizing/colors of the diagram
   itself come from mermaid.js (classDef existing/changed, see AgentPrompts).
   That palette is fixed light-mode (light node fills, near-black edges and
   labels), so the diagram is rendered on a constant light canvas - otherwise
   the edges and text would be black-on-dark and barely visible in dark mode. */
.bk-mermaid {
    display: flex;
    justify-content: center;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    padding: 12px;
    background: #ffffff;
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: var(--bk-radius-xs);
}

.bk-mermaid svg {
    max-width: 100%;
}

/* Legend for the fixed existing/changed classDef colors (MermaidDiagram
   ShowLegend). Swatch colors mirror the classDef fills/strokes exactly. */
.bk-mermaid-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
    margin-top: 8px;
    font-size: 0.75rem;
    color: var(--mud-palette-text-secondary);
}

.bk-mermaid-legend .item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.bk-mermaid-legend .swatch {
    width: 14px;
    height: 14px;
    border-radius: var(--bk-radius-xs);
    border: 1px solid;
}

.bk-mermaid-legend .existing {
    background: #eef2f7;
    border-color: #94a3b8;
}

.bk-mermaid-legend .removed {
    background: #fee2e2;
    border-color: #dc2626;
    border-style: dashed;
}

.bk-mermaid-legend .changed {
    background: #fef3c7;
    border-color: #d97706;
}

/* Architecture tab (Concept A): responsive card grid for decisions, risks
   and components. Cards wrap to fewer columns as the width shrinks. */
.bk-arch-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 12px;
    align-items: stretch;
}

/* Architecture tab: 'Betroffene Komponenten' card as a flex column so the
   title stays fixed at the top and the tree below fills the remaining card
   height (the card itself is stretched to the row height of its neighbours
   by align-items: stretch on bk-arch-grid). */
.bk-arch-card-fill {
    display: flex;
    flex-direction: column;
    height: 100%;
}

/* Architecture tab: 'Betroffene Komponenten' card as a collapsible tree
   (ComponentsTreeView, MudTreeView based) instead of a flat list. Compacts
   MudBlazor's default treeview density to the 12px code-font look of the
   rest of the card. flex: 1 1 auto lets it grow into the card's full height
   instead of leaving empty space below; min-height: 0 is required so this
   flex child can actually shrink below its content size instead of blowing
   up the card (default flex-basis behaviour). max-height keeps a generous,
   viewport-relative cap so a proposal with very many affected files still
   scrolls internally instead of dominating the page. */
.bk-components-tree {
    flex: 1 1 auto;
    min-height: 0;
    max-height: 60vh;
    overflow-y: auto;
}

.bk-components-tree .mud-treeview-item-content {
    min-height: 28px;
}

.bk-components-tree-leaf {
    font-size: 12px;
}

/* Inline annotation highlights (annotations-interop.js): a <mark> wrapping a
   text-span anchor, or - widened to any element, not just <mark> - the whole
   block a span-less anchor marks instead (an illustration's press-and-hold,
   or any whole-block strand written before that gesture existed). Color
   inherits so text stays readable; the underline/box-shadow encodes the
   type either way. */
.bk-annot-mark {
    background: transparent;
    color: inherit;
    border-radius: var(--bk-radius-xs);
    padding: 0 1px;
    cursor: pointer;
}

.bk-annot-mark.q {
    background: rgba(29, 158, 117, 0.16);
    box-shadow: inset 0 -2px 0 #1D9E75;
}

.bk-annot-mark.cr {
    background: rgba(216, 90, 48, 0.16);
    box-shadow: inset 0 -2px 0 #D85A30;
}

.bk-annot-mark.done {
    opacity: 0.55;
}

/* The marked spot of the currently open annotation overlay (ProposalAnnotationOverlay):
   an extra ring on top of the q/cr colour so it stays visibly singled out
   while the overlay itself floats or gets dragged elsewhere on screen. Set
   by ArchitectureTab.HighlightData's active flag, applied by
   annotations-interop.js applyHighlights/wrapRange - not tied to
   openOverlay's own call, so it follows every part-open/close the same way
   the highlight marks themselves already do. */
.bk-annot-mark.active {
    box-shadow: 0 0 0 2px var(--mud-palette-primary);
    border-radius: var(--bk-radius-xs);
}

/* Brief highlight pulse when scrolling to an annotation's spot (bkAnnot.
   scrollToMark/scrollToElement) - a highlight mark, a whole markable block or
   a ChatSideThreadRail card, so "click a mark -> jump to its rail card" and
   "click a rail card -> jump to its spot" both give a visible confirmation
   that the SAME element as before is now in view. */
@keyframes bk-annot-flash {
    0%, 100% { box-shadow: none; }
    25%, 75% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--mud-palette-primary) 45%, transparent); }
}

.bk-annot-flash {
    animation: bk-annot-flash 1.6s ease-in-out;
    border-radius: var(--bk-radius-xs);
}

/* Positioning host of the architecture section's annotatable content;
   ArchitectureTab passes its id to annotations-interop.js as the container
   for selection/highlight lookups. The annotation overlay itself
   (.bk-annot-overlay, ProposalAnnotationOverlay.razor.css) is viewport-fixed
   and does not need this to be a positioning context. */
.bk-annot-host {
    position: relative;
}

/* Decision bar at the top of the architecture section */
.bk-decbar-blocked {
    border-left: 3px solid #EF9F27;
}

.bk-decbar-ready {
    border-left: 3px solid #1D9E75;
}

/* Floating "+ Anmerkung" button shown next to a text selection */
.bk-annot-addbtn {
    position: absolute;
    z-index: 1300;
    font-size: 12px;
    font-weight: 500;
    padding: 4px 10px;
    border: none;
    border-radius: var(--bk-radius-sm);
    background: var(--mud-palette-primary);
    color: var(--mud-palette-primary-text);
    cursor: pointer;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28);
}

/* Larger tap target on touch devices, where the button is triggered from
   native text-selection handles instead of a precise mouse click. */
@media (pointer: coarse) {
    .bk-annot-addbtn {
        font-size: 14px;
        padding: 8px 14px;
    }
}

/* An annotatable text block: subtle affordance that text can be selected */
.bk-annotatable {
    cursor: text;
}

/* Architecture tab: sandboxed SVG mockup/visualization (SandboxedSvg). The
   iframe is sized by its inline aspect-ratio (from the SVG viewBox); this only
   frames it consistently with the rest of the page and caps the height for
   pathological (very tall) viewBoxes. */
.bk-agent-svg {
    display: block;
    width: 100%;
    margin: 0 auto;
    border: 0;
    border-radius: var(--bk-radius-xs);
    background: var(--mud-palette-surface);
}

/* Screenshot an agent captured during a run (RunVisualization of kind Image),
   served by /run-visualizations/{id}. Capped in height so a full-page capture
   does not push the rest of the artifact off the screen; clicking it opens the
   full-screen dialog of VisualizationGallery, which also links the original. */
.bk-run-visual-image {
    display: block;
    max-width: 100%;
    max-height: 560px;
    margin: 0 auto;
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: var(--bk-radius-xs);
    background: var(--mud-palette-surface);
    cursor: zoom-in;
}

/* Same image inside that dialog: the cap is raised to the dialog's own room and
   the zoom cursor dropped, since there is nothing further to zoom into. */
.bk-run-visual-image-full {
    max-height: calc(100dvh - 160px);
    cursor: default;
}

/* Click-to-zoom hint shown on hover over the rendered Mermaid diagram
   (MermaidDiagram) or UI mockup (SandboxedSvg), opening the same content
   full-screen in a MudDialog. */
.bk-diagram-clickable {
    position: relative;
    cursor: zoom-in;
}

.bk-fullscreen-hint {
    position: absolute;
    top: 8px;
    right: 8px;
    display: flex;
    align-items: center;
    padding: 2px;
    border-radius: var(--bk-radius-xs);
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    opacity: 0;
    transition: opacity var(--bk-dur-fast) var(--bk-ease-out);
    pointer-events: none;
}

.bk-diagram-clickable:hover .bk-fullscreen-hint,
.bk-svg-clickable:hover .bk-fullscreen-hint {
    opacity: 1;
}

/* Wraps the sandboxed SVG iframe with a transparent click layer: the iframe
   is a separate document, so a click handler on this wrapper alone would
   never fire for clicks landing on the iframe's surface. */
.bk-svg-clickable {
    position: relative;
    margin: 0 auto;
    cursor: zoom-in;
}

.bk-svg-clickable .bk-fullscreen-overlay {
    position: absolute;
    inset: 0;
}

/* A run's stored input/result, reindented as JSON: shared by
   StructuredTextView.razor (Eingabe/Ergebnis on the detail page, the
   Zusammenfassung dialog) and TranscriptView.razor's own Result entry - both
   need the identical block, and a rule scoped to either component's own
   *.razor.css would not reach the other's markup. */
.bk-structured-text-pre {
    margin: 0;
    padding: 12px;
    background: var(--mud-palette-background-gray);
    border-radius: var(--bk-radius-sm);
    font-family: monospace;
    font-size: 0.8125rem;
    line-height: 1.5;
    white-space: pre-wrap;
    word-break: break-word;
    overflow-wrap: anywhere;
}

/* Segmented progress bar on the goal cards (verified/implemented/in progress) */
.bk-segbar {
    display: flex;
    height: 7px;
    border-radius: var(--bk-radius-xs);
    overflow: hidden;
    background: var(--mud-palette-background-gray);
}
.bk-segbar span {
    display: block;
    height: 100%;
}

/* Requirement detail two-column layout (Soll | technische Spur) */
.bk-req-cols {
    display: grid;
    grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr);
    gap: 12px;
    align-items: start;
}
@media (max-width: 959.98px) {
    .bk-req-cols { grid-template-columns: minmax(0, 1fr); }
}

/* ----- Chat: the welcome / empty state of a brand new chat -----
   A centered hero shown before the first message is sent: a tinted icon badge,
   a short explainer, and a few one-click starter prompts that prefill the
   composer. Every chat kind hands its own copy to ChatPanel as the Welcome
   fragment (see RequirementDraft.razor and ProjectSetupChat.razor), so the
   building blocks live here rather than in one page's scoped stylesheet; the
   hero sits inside the panel's flex-column thread and centers via margin:auto. */
.bk-chat-welcome {
    margin: auto 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 10px;
    padding: 24px 8px 32px;
}

.bk-welcome-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 60px;
    height: 60px;
    margin-bottom: 4px;
    border-radius: var(--bk-radius-round);
    background: color-mix(in srgb, var(--mud-palette-primary) 14%, transparent);
    color: var(--mud-palette-primary);
}

.bk-welcome-lead {
    max-width: 540px;
    color: var(--mud-palette-text-secondary);
}

.bk-suggestions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 8px;
    margin-top: 8px;
}

/* One starter prompt: a pill button, same flat look as the rest of the chat. */
.bk-suggestion {
    border: 1px solid var(--mud-palette-lines-default);
    background: var(--mud-palette-surface);
    color: var(--mud-palette-text-primary);
    border-radius: var(--bk-radius-pill);
    padding: 7px 14px;
    font-family: inherit;
    font-size: 0.8125rem;
    line-height: 1.3;
    cursor: pointer;
    transition: border-color var(--bk-dur-fast) var(--bk-ease-out), background-color var(--bk-dur-fast) var(--bk-ease-out);
}

.bk-suggestion:hover {
    border-color: var(--mud-palette-primary);
    background: color-mix(in srgb, var(--mud-palette-primary) 8%, transparent);
}

/* Neumorphic design (.bk-soft): starter prompts are raised pills that lift
   on hover and sink in while pressed. */
.bk-soft .bk-suggestion,
.bk-soft .bk-suggestion:hover {
    border-color: transparent;
    background: var(--mud-palette-surface);
    box-shadow: var(--bk-depth-raised-sm);
    transition: box-shadow var(--bk-dur-fast) var(--bk-ease-out), transform var(--bk-dur-fast) var(--bk-ease-out), color var(--bk-dur-fast) var(--bk-ease-out);
}

.bk-soft .bk-suggestion:hover {
    color: var(--mud-palette-primary);
    box-shadow: var(--bk-depth-lift-sm);
}

.bk-soft .bk-suggestion:active {
    box-shadow: var(--bk-depth-inset);
    transform: translateY(1px) scale(0.98);
}

/* Mobile: the same hero, tighter, with the starter prompts as full-width rows */
@media (max-width: 599.98px) {
    .bk-chat-welcome {
        padding: 8px 4px 20px;
        gap: 8px;
    }

    .bk-welcome-icon {
        width: 52px;
        height: 52px;
    }

    /* Starter prompts as full-width, comfortably tappable rows rather than
       pills that wrap awkwardly on a phone. */
    .bk-suggestions {
        width: 100%;
        flex-direction: column;
        align-items: stretch;
        gap: 8px;
    }

    .bk-suggestion {
        padding: 12px 14px;
        text-align: center;
    }
}


/* ----- Chat: the review gate of a drafted proposal -----
   The cards the requirement chat's review step is built from: one accented
   card per drafted entry, its kind badge, its goal chip, its checklist of
   acceptance criteria and the applied summary's result links. Shared building
   blocks rather than the host page's own scoped stylesheet, since the shared
   RequirementDraftCard component and the host page's own goal-operation cards
   and review head all draw from them. */
/* Proposal review ("Entwurf vN"): each drafted requirement as an accented
   card, criteria as a checklist, the goal assignment as a chip that tells an
   existing goal apart from a newly proposed one. */
.bk-draft-head {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 4px 0 12px;
}

.bk-draft-version {
    display: inline-flex;
    align-items: center;
    font-size: 11px;
    font-weight: 600;
    padding: 2px 9px;
    border-radius: var(--bk-radius-pill);
    background: color-mix(in srgb, var(--mud-palette-primary) 13%, transparent);
    color: var(--mud-palette-primary);
}

.bk-draft-card {
    position: relative;
    padding: 16px 18px 16px 20px;
    margin-bottom: 12px;
    border: 1px solid var(--mud-palette-lines-default);
    border-left: 3px solid var(--mud-palette-primary);
    border-radius: var(--bk-radius-md);
    background: var(--mud-palette-surface);
}

/* RequirementDraftCard (Author items): the whole head is the fold's click
   target, so a proposal touching many requirements stays scannable by title/
   goal/criteria-count alone with the full description and criteria list
   collapsed by default. A slight negative margin lets the hover highlight
   bleed to the card's own padding edge instead of looking inset. The
   role="button" is only present while the card is actually foldable
   (RequirementDraftCard omits it for AlwaysExpanded, the detail-pane mode
   RequirementDraftBoard renders through), so the pointer cursor/hover only
   ever invites a click that does something. */
.bk-draft-card-head {
    margin: -4px -6px 0;
    padding: 4px 6px 0;
    border-radius: var(--bk-radius-sm);
    transition: background-color var(--bk-dur-fast) var(--bk-ease-out);
}

.bk-draft-card-head[role="button"] {
    cursor: pointer;
}

.bk-draft-card-head[role="button"]:hover,
.bk-draft-card-head[role="button"]:focus-visible {
    background: color-mix(in srgb, var(--mud-palette-primary) 6%, transparent);
}

.bk-draft-card-head[role="button"]:focus-visible {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: 2px;
}

/* The detail pane's own "seine Stelle im Baum" line (RequirementDraftCard.
   PathSegments/AlwaysExpanded) - a breadcrumb of ancestor titles, or "Oberste
   Ebene" for a top-level item, right above the item-kind badge. */
.bk-draft-path {
    font-size: 12px;
    color: var(--mud-palette-text-secondary);
    margin-bottom: 4px;
}

.bk-draft-path-top {
    font-style: italic;
}

.bk-draft-card-body {
    margin-top: 12px;
    padding-top: 12px;
    border-top: 1px solid var(--mud-palette-lines-default);
}

/* Item-kind badge ("Neue Anforderung" vs "Erweiterung von R<n>"): sits above
   the title so the review's most consequential fact - does this touch an
   existing requirement or not - is the first thing read, before the goal
   chip. Extend uses the warning tint (not the goal chip's info/success ones)
   since it is the riskier of the two outcomes (concept risk: a wrongly
   recognized extension writes into the wrong existing requirement). */
.bk-draft-kind {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    padding: 3px 10px;
    border-radius: var(--bk-radius-pill);
    margin-bottom: 8px;
}

.bk-draft-kind-new {
    background: color-mix(in srgb, var(--mud-palette-primary) 12%, transparent);
    color: var(--mud-palette-primary);
}

/* Art selector (fachlich/technisch/Randbedingung) next to the kind badge on a
   fresh drafted item's card head - kept narrow so it reads as one more badge
   in the row instead of a full-width form field taking over the header. */
.bk-draft-kind-select {
    display: inline-flex;
    width: 132px;
    margin-bottom: 8px;
    vertical-align: middle;
}

.bk-draft-kind-extend {
    background: color-mix(in srgb, var(--mud-palette-warning) 18%, transparent);
    color: var(--mud-palette-warning-darken, var(--mud-palette-warning));
}

/* Archive/Unarchive/Delete review card badges: archive and delete both change
   or end a requirement's lifecycle, so they share the error/danger tint
   (delete a shade stronger via the same palette token everywhere else uses
   for a destructive action); unarchive is the reversing, welcome-back action
   and gets the success tint. */
.bk-draft-kind-archive,
.bk-draft-kind-delete {
    background: color-mix(in srgb, var(--mud-palette-error) 14%, transparent);
    color: var(--mud-palette-error);
}

.bk-draft-kind-unarchive {
    background: color-mix(in srgb, var(--mud-palette-success) 16%, transparent);
    color: var(--mud-palette-success);
}

/* Goal-move hint ("Ziel wird verschoben: A -> B"): deliberately its own
   bordered, warning-colored block rather than folded into the goal chip above
   - the concept calls for this to be unmissable, since a wrong match here
   moves an existing requirement's whole goal assignment as a side effect. */
.bk-draft-move-hint {
    display: flex;
    align-items: center;
    gap: 6px;
    margin: 8px 0 10px;
    padding: 6px 10px;
    border: 1px solid color-mix(in srgb, var(--mud-palette-warning) 45%, transparent);
    border-radius: var(--bk-radius-sm);
    background: color-mix(in srgb, var(--mud-palette-warning) 10%, transparent);
    color: var(--mud-palette-warning-darken, var(--mud-palette-warning));
    font-size: 0.8125rem;
    font-weight: 500;
}

.bk-draft-parent {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-size: 12px;
    font-weight: 500;
    padding: 3px 10px;
    border-radius: var(--bk-radius-pill);
    margin: 6px 0 10px;
}

.bk-draft-parent-existing {
    background: color-mix(in srgb, var(--mud-palette-info) 14%, transparent);
    color: var(--mud-palette-info);
}

.bk-draft-parent-new {
    background: color-mix(in srgb, var(--mud-palette-success) 16%, transparent);
    color: var(--mud-palette-success);
}

/* No parent at all: the requirement lands at the top level of the tree. A
   deliberate placement like the other two, but neither an existing nor a new
   parent, so it stays neutral instead of borrowing one of their tints. */
.bk-draft-parent-none {
    background: var(--mud-palette-background-gray);
    color: var(--mud-palette-text-secondary);
}

/* The head's generated summary: the rendered markdown in full, never clamped
   - a half-shown summary hides the very thing the reviewer decides on, and
   the agents cap it at two sentences at the source instead (see
   AgentPrompts.RequirementSummaryGuidelines). Slightly muted and one step
   below body size so it stays subordinate to the title above it. */
.bk-draft-desc {
    margin: 4px 0 2px;
    color: var(--mud-palette-text-secondary);
}

.bk-draft-desc .bk-markdown {
    font-size: 0.8125rem;
    line-height: 1.45;
}

/* Collapsed head's criteria short info ("7 Kriterien - 2 neu, 1 archiviert")
   plus the fold's chevron, right-aligned in the same row so the affordance
   to expand sits right next to the fact that motivates expanding. */
.bk-draft-crit-summary {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 8px;
}

.bk-draft-crit-pill {
    display: inline-flex;
    align-items: center;
    font-size: 12px;
    font-weight: 500;
    padding: 3px 10px;
    border-radius: var(--bk-radius-pill);
    background: color-mix(in srgb, var(--mud-palette-text-secondary) 12%, transparent);
    color: var(--mud-palette-text-secondary);
}

.bk-draft-chevron {
    margin-left: auto;
    color: var(--mud-palette-text-secondary);
}

.bk-draft-crit-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--mud-palette-text-secondary);
    margin: 4px 0 6px;
}

/* Zerlegungs-Gate only: accept/reject the criteria change shown below,
   reversibly (BreakdownItem.AcceptCriteriaChange) - see RequirementDraftCard's
   AcceptCriteriaChange parameter. */
.bk-draft-crit-toggle {
    display: flex;
    align-items: center;
    gap: 4px;
    margin-bottom: 6px;
    font-size: 0.875rem;
    color: var(--mud-palette-text-secondary);
}

.bk-draft-crit {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin: 6px 0;
    font-size: 0.9375rem;
    line-height: 1.5;
}

.bk-draft-crit .mud-icon-root {
    flex-shrink: 0;
    margin-top: 2px;
    color: var(--mud-palette-success);
}

/* Extension review: per-criterion unchanged/archived/new status. The default
   icon color above already reads as "unchanged" (success green); archived and
   new criteria get their own icon color plus a small trailing status tag so
   the three states stay distinguishable without relying on color alone. */
.bk-draft-crit-archived .mud-icon-root {
    color: var(--mud-palette-text-secondary);
}

.bk-draft-crit-archived span:first-of-type {
    color: var(--mud-palette-text-secondary);
    text-decoration: line-through;
}

.bk-draft-crit-new .mud-icon-root {
    color: var(--mud-palette-info);
}

.bk-draft-crit-tag {
    flex-shrink: 0;
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--mud-palette-text-secondary);
    margin-top: 3px;
}

/* Rejected criterion (feature "Einzelne Kriterien im Entwurf direkt
   ablehnen"): stays visible, struck through and muted rather than removed
   outright, so the reviewer can always see what they dismissed and undo it
   before accepting - the same "still readable, marked" idea .bk-draft-crit-
   archived above already uses for an extension's own archived criterion. */
.bk-draft-crit-rejected .mud-icon-root {
    color: var(--mud-palette-text-secondary);
}

.bk-draft-crit-text-rejected {
    color: var(--mud-palette-text-secondary);
    text-decoration: line-through;
}

/* The reject/undo toggle itself: a small neutral button at the row's own
   end (margin-left: auto inside .bk-draft-crit's flex row), never a
   saturated fill - text plus the Close/Undo icon are what tell "not yet
   rejected" from "rejected" apart, not color. */
.bk-draft-crit-reject-btn {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    margin-left: auto;
    flex-shrink: 0;
    font-size: 11px;
    font-weight: 500;
    padding: 2px 8px;
    border-radius: var(--bk-radius-pill);
    border: 1px solid var(--mud-palette-lines-default);
    background: none;
    color: var(--mud-palette-text-secondary);
    cursor: pointer;
}

.bk-draft-crit-reject-btn:hover {
    color: var(--mud-palette-text-primary);
    border-color: var(--mud-palette-text-primary);
}

.bk-draft-rationale {
    margin-top: 12px;
    padding-top: 10px;
    border-top: 1px dashed var(--mud-palette-lines-default);
    font-size: 0.8125rem;
    color: var(--mud-palette-text-secondary);
}

/* Illustrations a chat drafted item carries into the accept (RequirementDraftCard):
   a small count label above the same VisualizationGallery a chat message's own
   pictures use, so the review gate discloses that and how many images will be
   attached before anything is saved. */
.bk-draft-illustrations {
    margin-top: 12px;
    padding-top: 10px;
    border-top: 1px dashed var(--mud-palette-lines-default);
}

.bk-draft-illustrations-label {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 0.8125rem;
    color: var(--mud-palette-text-secondary);
    margin-bottom: 8px;
}

.bk-draft-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 4px;
}

/* Applied view: the results an accept produced, one clickable link each
   (RequirementLink). Primary-colored, unlike the link's default inherited
   color, since this list IS the set of primary follow-up actions after
   accepting. */
.bk-applied-results {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-top: 8px;
}

.bk-applied-results .bk-req-link {
    color: var(--mud-palette-primary);
    font-size: 0.875rem;
}

/* ----- Chat: full-width column, per-block reading measure -----
   The thread, the working-status area and the composer all fill the
   conversation column's own width (feature "Chat nutzt den Platz auf jedem
   Bildschirm effizient": no draft open, the transcript uses the full content
   area instead of standing in a narrow centered column beside empty space).
   Readability on a very wide screen comes from capping each TEXT block's own
   measure at roughly 100 characters (ChatTranscript.razor.css's
   .bk-turn-agent .bk-turn-body, the user bubble's existing 660px cap, and
   RequirementDraftBoard.razor.css's .bk-draft-card) instead of the whole
   column - see each rule's own comment.

   Two of this group's three rules have moved to the component that renders
   them: .bk-chat-status-inner to ChatLiveTurn.razor.css, .bk-composer-inner
   to ChatComposerSurface.razor.css (which became the single owner of the
   whole input surface). .bk-thread/.bk-inline-review belong the same way in
   ChatSideThreadLayer's own scope - it renders both - but that sheet already
   sits at ChatCoreConcernGuard's 300-line budget, so moving them needs the
   file split by concern first. Until then they stay here. */
/* Centred inside whatever width the conversation column gets, and bounded to
   a width a conversation is still readable at. The column itself fills (see
   .bk-page:has(.bk-chat-page) above) - capping the COLUMN would bring the
   dead strip back one level down, while capping nothing would run agent
   answers out to 200 characters a line on an ultrawide. The composer carries
   the same cap (.bk-composer-inner) so the field stays flush with the
   transcript above it. */
.bk-thread,
.bk-inline-review {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    width: 100%;
    max-width: 1040px;
    margin-inline: auto;
}

/* .bk-inline-review (ChatSideThreadLayer's inline Review placement, see
   ChatReviewPlacement) sits below .bk-thread as a second flow child of the
   same .bk-chat-scroll, not stretched to fill it - flex: 0 0 auto keeps its
   own height to whatever its content (the review card plus its own clamp)
   needs, instead of the 1 1 auto every other user of this shared rule wants. */
.bk-inline-review {
    flex: 0 0 auto;
    margin-top: 12px;
}

/* ----- Chat: compact phone header's Discard label -----
   Shared by every chat kind's own Header fragment (RequirementDraft.razor,
   InventoryChat.razor) - both put the same <span class="bk-discard-label">
   text next to Discard's icon. Global (not scoped) on purpose: the class is
   authored in each HOST PAGE's own markup (a RenderFragment parameter of
   ProjectPageHeader compiles into the caller, not into ChatPanel), so a rule
   scoped to ChatPanel.razor.css's ::deep would style it at runtime but leave
   it unreachable from either page's own scope (own .razor.css or app.css) -
   exactly what ScopedCssOwnershipGuard checks for. Hidden on a phone
   (ChatPanel.razor.css's own compact single-row header) so Discard drops to
   an icon-only button next to the chat-list toggle instead of pushing that
   row's own width past what the two of them can share. */
@media (max-width: 599.98px) {
    .bk-chat-head .bk-discard-label {
        display: none;
    }
}

/* ----- Chat composer surface: the shared control-row primitive -----
   The surface itself (.bk-composer, .bk-composer-inner, .bk-composer-input,
   .bk-composer-hint, .bk-composer-row) moved to ChatComposerSurface.razor.css
   once that component became its single owner. What stays here is the part
   that cannot follow: ChatComposer and ChatQuestionBox each stamp these
   classes onto buttons THEY render, so by CSS isolation the rules have to
   resolve from each caller's own scope or from this file - see
   ScopedCssOwnershipGuard. --bk-control-h, which sizes them, is declared on
   .bk-composer-row in that scoped sheet and still cascades in at runtime. */

/* Pushes whichever element carries it (the send/answer action) to the row's
   far end, with every action before it (attach, the run-settings chip, skip)
   grouped at the start - one flex row, no separate wrapper needed just to
   split the two groups apart. The order also keeps it behind the row's
   Enter-to-send hint (order 1, see ChatComposerSurface.razor.css), which each
   caller renders no control for and which sits between the two groups. */
.bk-composer-row-end {
    order: 2;
    margin-left: auto;
}


/* The composer's action button (Senden/Unterbrechen/Antworten/Anhaengen): a
   plain <button>, styled directly rather than via MudButton, so its size
   matches --bk-control-h exactly - MudButton's own CSS (min-height,
   size-tuned padding) kept overriding a single class override on it.
   Icon-only, so it is a square the same size as the control row's own
   height. */
.bk-composer-send {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--bk-control-h);
    height: var(--bk-control-h);
    padding: 0;
    /* Softer than the 4px --mud-default-borderradius the rest of the app uses:
       these are the only controls sitting INSIDE the 16px composer card, and a
       near-square corner inside that radius read as a leftover box. */
    border-radius: var(--bk-radius-sm);
    cursor: pointer;
    transition: background-color var(--bk-dur-fast) var(--bk-ease-out), border-color var(--bk-dur-fast) var(--bk-ease-out), color var(--bk-dur-fast) var(--bk-ease-out);
}

/* A faded purple block still read as "primary, just dimmed" and drew the eye
   to the one control that cannot be used; a neutral one steps out of the way
   until there is actually something to send. */
.bk-composer-send:disabled {
    cursor: default;
}

.bk-composer-send-primary:disabled {
    border-color: transparent;
    background: var(--mud-palette-action-disabled-background);
    color: var(--mud-palette-action-disabled);
}

.bk-composer-send-primary {
    border: 1px solid var(--mud-palette-primary);
    background: var(--mud-palette-primary);
    color: var(--mud-palette-primary-text);
}

.bk-composer-send-primary:hover:not(:disabled) {
    background: var(--mud-palette-primary-darken, var(--mud-palette-primary));
}

/* order: "Unterbrechen" renders after the send action and stays there, past
   both the row's Enter-to-send hint (order 1) and the row-end action (2). */
.bk-composer-send-danger {
    order: 3;
    border: 1px solid var(--mud-palette-error);
    background: transparent;
    color: var(--mud-palette-error);
}

.bk-composer-send-danger:hover:not(:disabled) {
    background: color-mix(in srgb, var(--mud-palette-error) 10%, transparent);
}

/* Neumorphic design (.bk-soft): the send button is the glossy accent knob
   of the design, the interrupt button a raised knob in the error color; both
   sink in while pressed, and a disabled one lies flat. */
.bk-soft .bk-composer-send {
    border-color: transparent;
    transition: box-shadow var(--bk-dur-fast) var(--bk-ease-out), transform var(--bk-dur-fast) var(--bk-ease-out);
}

.bk-soft .bk-composer-send-primary:not(:disabled) {
    background-image: linear-gradient(145deg, rgba(255, 255, 255, 0.22), rgba(255, 255, 255, 0) 55%);
    box-shadow: 0 6px 14px -5px rgba(var(--mud-palette-primary-rgb), 0.6), -3px -3px 8px var(--bk-soft-hi);
}

.bk-soft .bk-composer-send-danger:not(:disabled) {
    background: var(--mud-palette-surface);
    box-shadow: var(--bk-depth-raised-sm);
}

.bk-soft .bk-composer-send:active:not(:disabled) {
    box-shadow: var(--bk-depth-inset);
    transform: translateY(1px) scale(0.96);
}

.bk-soft .bk-composer-send-primary:active:not(:disabled) {
    box-shadow: var(--bk-depth-pressed-accent);
}

/* Plain CSS spinner for an in-flight composer action ("Unterbreche...",
   "Senden..."). Shared by ChatComposer's own Senden/Unterbrechen buttons and
   ChatQuestionBox's Antworten button (two separate components, CSS
   isolation), so it lives here rather than in either one's scoped sheet -
   avoids pulling in MudProgressCircular's own sizing/markup inside a button
   styled by hand. */
.bk-spinner {
    width: 14px;
    height: 14px;
    border: 2px solid currentColor;
    border-right-color: transparent;
    border-radius: var(--bk-radius-round);
    animation: bk-spin 0.7s linear infinite;
}

@keyframes bk-spin {
    to { transform: rotate(360deg); }
}

/* ChatRunSettingsPicker's own chip/panel/backdrop styles
   (.bk-chat-run-settings and friends) live in its own scoped
   ChatRunSettingsPicker.razor.css now - it renders the panel itself as a
   plain positioned child of its own markup (no MudMenu portal anymore, see
   that component's own doc comment on why), so CSS isolation reaches it
   directly and this app.css exception no longer applies. */


/* The card that stands in for something the PANE can show - an interactive
   artifact, or the draft awaiting review. Shared rather than copied: a reader
   should not have to learn two ways of opening the same column, and two
   private copies of one shape drift the first time either is touched. Quiet
   on purpose: it sits under an agent answer, where the answer is what should
   be read first, and it must not look like a second artifact of its own.
   Each host keeps a hook class of its own (bk-artifact-teaser,
   bk-draft-teaser) for what only it needs. */
.bk-teaser {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 12px;
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: var(--bk-radius-sm);
    background: var(--mud-palette-background-gray);
}

.bk-teaser-icon {
    color: var(--mud-palette-primary);
    flex: 0 0 auto;
}

/* min-width: 0 lets a long title ellipsize instead of stretching the whole
   turn, the same reason ChatTranscript.razor.css sets it on .bk-turn-visuals */
.bk-teaser-text {
    flex: 1 1 auto;
    min-width: 0;
}

.bk-teaser-title {
    font-size: 0.875rem;
    font-weight: 500;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.bk-teaser-hint {
    font-size: 0.75rem;
    color: var(--mud-palette-text-secondary);
}
