/* /Features/Account/OidcLinksSection.razor.rz.scp.css */
/* The list of provider links: the row pattern of the device list on the same screen - a framed
   list on the card's surface, rows divided by a hairline, 48px so the buttons have
   room without the row growing around them. Copied rather than shared, because the
   page's scoped stylesheet cannot reach markup this component writes. */

.oidc-links-list[b-l4dbxt7jtd] {
    display: flex;
    flex-direction: column;
    margin: 0;
    padding: 0;
    list-style: none;
    background-color: var(--ffp-bg);
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius);
    overflow: hidden;
}

.oidc-links-row[b-l4dbxt7jtd] {
    display: flex;
    align-items: center;
    gap: var(--ffp-space-sm);
    min-height: 48px;
    padding: var(--ffp-space-sm) var(--ffp-space-md);
    border-bottom: 1px solid var(--ffp-border);
}

.oidc-links-row:last-child[b-l4dbxt7jtd] {
    border-bottom: 0;
}

.oidc-links-name[b-l4dbxt7jtd] {
    flex: 1;
    min-width: 0;
    overflow-wrap: anywhere;
}
/* /Features/Account/PasskeysSection.razor.rz.scp.css */
/* The passkey list: the row pattern of the device list on the same screen - a framed
   list on the card's surface, rows divided by a hairline, 48px so the buttons have
   room without the row growing around them. Copied rather than shared, because the
   page's scoped stylesheet cannot reach markup this component writes. */

.passkeys-list[b-j3orjfytcn] {
    display: flex;
    flex-direction: column;
    margin: 0;
    padding: 0;
    list-style: none;
    background-color: var(--ffp-bg);
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius);
    overflow: hidden;
}

.passkeys-row[b-j3orjfytcn] {
    display: flex;
    align-items: center;
    gap: var(--ffp-space-sm);
    min-height: 48px;
    padding: var(--ffp-space-sm) var(--ffp-space-md);
    border-bottom: 1px solid var(--ffp-border);
}

.passkeys-row:last-child[b-j3orjfytcn] {
    border-bottom: 0;
}

.passkeys-name[b-j3orjfytcn] {
    flex: 1;
    min-width: 0;
    overflow-wrap: anywhere;
}

/* The rename field takes the room the name had. ::deep because the class sits on a
   MudTextField. */
.passkeys-row[b-j3orjfytcn]  .passkeys-rename {
    flex: 1;
}
/* /Features/Admin/GovernancePanel.razor.rz.scp.css */
/* Styles itself, and reaches MudBlazor through ::deep. Both halves of the 2.5b lesson:
   a class defined next to Admin.razor carries that file's scope id and matches nothing
   here, and a class handed to <MudText> or <MudSimpleTable> never carries this
   component's scope attribute at all. */

.ffp-governance[b-tvwlpakla9] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-md);
    padding: var(--ffp-space-lg) var(--ffp-space-xl) var(--ffp-space-xl);
    max-width: 60rem;
}

.ffp-gov-card[b-tvwlpakla9] {
    background-color: var(--ffp-surface);
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius-container);
    padding: var(--ffp-space-md);
}

.ffp-governance[b-tvwlpakla9]  .ffp-gov-title {
    font-size: 1.125rem;
}

.ffp-governance[b-tvwlpakla9]  .ffp-gov-hint {
    color: var(--ffp-muted);
    margin: var(--ffp-space-xs) 0 var(--ffp-space-md);
}

.ffp-gov-form[b-tvwlpakla9] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-sm);
    margin-bottom: var(--ffp-space-md);
    max-width: 32rem;
}

.ffp-gov-buttons[b-tvwlpakla9] {
    display: flex;
    gap: var(--ffp-space-sm);
    flex-wrap: wrap;
}

/* The count that has to be read before the button is pressed, so it sits between the
   fields and the button rather than under them. */
.ffp-governance[b-tvwlpakla9]  .ffp-gov-preview,
.ffp-governance[b-tvwlpakla9]  .ffp-gov-note {
    margin: var(--ffp-space-xs) 0;
}

.ffp-governance[b-tvwlpakla9]  .ffp-gov-table {
    font-size: 0.875rem;
    margin-top: var(--ffp-space-md);
}

/* The details column is the only long one, and wrapping the whole table for it would
   make every other column unreadable. */
.ffp-gov-details[b-tvwlpakla9] {
    max-width: 24rem;
    overflow-wrap: anywhere;
}

.ffp-governance[b-tvwlpakla9]  .ffp-gov-empty,
.ffp-governance[b-tvwlpakla9]  .ffp-gov-result {
    color: var(--ffp-muted);
    margin-top: var(--ffp-space-sm);
}

.ffp-governance[b-tvwlpakla9]  .ffp-gov-more {
    margin-top: var(--ffp-space-sm);
}

@media (max-width: 900px) {
    .ffp-governance[b-tvwlpakla9] {
        padding-left: var(--ffp-space-md);
        padding-right: var(--ffp-space-md);
    }
}
/* /Features/Admin/GuestsInDefaultChannels.razor.rz.scp.css */
/* The list sits under the alert it explains, one guest and channel per line with the
   action at the end - the same rhythm as the member table below it. */
.guests-in-defaults[b-rb6ljgjpbc] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-sm);
}

.guests-in-defaults-list[b-rb6ljgjpbc] {
    list-style: none;
    margin: 0;
    padding: 0;
}

.guests-in-defaults-list li[b-rb6ljgjpbc] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--ffp-space-sm);
    padding: var(--ffp-space-xs) 0;
    border-bottom: 1px solid var(--ffp-border);
}

.guests-in-defaults-who[b-rb6ljgjpbc] {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* /Features/Admin/IntegrationsPanel.razor.rz.scp.css */
/* The integrations panel styles itself rather than borrowing from Admin.razor.css.

   Not a preference: scoped CSS carries the scope id of the file it was written in, so
   .admin-card defined next to Admin.razor reaches nothing here. That is the failure
   phase 2.5b spent a week finding, and it is silent - the markup looks right, the build
   is green, and the screen falls back on whatever MudBlazor does.

   The ::deep rules below are the second half of the same lesson. Blazor puts the scope
   attribute on the plain elements this file writes and on nothing else, so a class
   handed to <MudText> or <MudSimpleTable> is never matched by a bare selector. Each one
   reaches through .ffp-integrations, which is an element this component does own. */

.ffp-integrations[b-b3mw07lnl0] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-md);
    padding: var(--ffp-space-lg) var(--ffp-space-xl) var(--ffp-space-xl);
    max-width: 60rem;
}

.ffp-int-card[b-b3mw07lnl0] {
    background-color: var(--ffp-surface);
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius-container);
    padding: var(--ffp-space-md);
}

.ffp-integrations[b-b3mw07lnl0]  .ffp-int-title {
    font-size: 1.125rem;
}

/* The sentence under each heading carries the rule that section enforces, so it is
   readable body text rather than a muted caption. */
.ffp-integrations[b-b3mw07lnl0]  .ffp-int-hint {
    color: var(--ffp-muted);
    margin: var(--ffp-space-xs) 0 var(--ffp-space-md);
}

/* One column, because every one of these forms mixes a wide field with a narrow one and
   a row of them would leave the URL field two inches long. */
.ffp-int-form[b-b3mw07lnl0] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-sm);
    margin-bottom: var(--ffp-space-md);
}

/* The button is the only thing here that should not stretch to the form's width. */
.ffp-int-form[b-b3mw07lnl0]  .mud-button-root {
    align-self: flex-start;
}

/* A real fieldset, so a screen reader announces "Scopes" before each box rather than
   reading seven unrelated checkboxes. */
.ffp-int-scopes[b-b3mw07lnl0] {
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius);
    padding: var(--ffp-space-sm) var(--ffp-space-md);
    margin: 0;
}

.ffp-int-scopes legend[b-b3mw07lnl0] {
    padding: 0 var(--ffp-space-xs);
    color: var(--ffp-muted);
    font-size: 0.875rem;
}

.ffp-integrations[b-b3mw07lnl0]  .ffp-int-table {
    font-size: 0.875rem;
}

/* A URL and a scope list are the two things here that are long and not worth wrapping
   the whole table for. */
.ffp-int-url[b-b3mw07lnl0],
.ffp-int-scopelist[b-b3mw07lnl0] {
    max-width: 22rem;
    overflow-wrap: anywhere;
}

.ffp-int-actions[b-b3mw07lnl0] {
    text-align: right;
    white-space: nowrap;
}

/* Dimmed, not hidden. A revoked row is what the audit trail is made of, and somebody
   looking for "what happened to that token" needs to find it. */
.ffp-int-revoked[b-b3mw07lnl0] {
    color: var(--ffp-muted);
}

.ffp-integrations[b-b3mw07lnl0]  .ffp-int-empty {
    color: var(--ffp-muted);
}

.ffp-int-deliveries[b-b3mw07lnl0] {
    margin-top: var(--ffp-space-md);
    padding-top: var(--ffp-space-md);
    border-top: 1px solid var(--ffp-border);
}

/* The credential, for the one moment it exists. An amber edge rather than a filled amber
   panel: the handbook forbids amber as a text colour, and a border is not text. */
.ffp-secret[b-b3mw07lnl0] {
    border-left: 3px solid var(--ffp-warnung);
}

.ffp-secret-head[b-b3mw07lnl0] {
    display: flex;
    align-items: center;
    gap: var(--ffp-space-sm);
}

.ffp-integrations[b-b3mw07lnl0]  .ffp-secret-hint {
    color: var(--ffp-muted);
    margin: var(--ffp-space-xs) 0 var(--ffp-space-sm);
}

/* Monospace, because this is copied by hand often enough that an l and a 1 have to be
   distinguishable. */
.ffp-integrations[b-b3mw07lnl0]  .ffp-secret-value input {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

.ffp-integrations[b-b3mw07lnl0]  .ffp-int-warning {
    margin-bottom: var(--ffp-space-md);
}

@media (max-width: 900px) {
    .ffp-integrations[b-b3mw07lnl0] {
        padding-left: var(--ffp-space-md);
        padding-right: var(--ffp-space-md);
    }
}

/* Under the hook's name rather than in a column of its own: most hooks have none, and
   a column that is empty on nearly every row is noise (#315). */
.ffp-integrations[b-b3mw07lnl0]  .ffp-int-guarded {
    display: block;
    color: var(--ffp-muted);
}

/* Where a row's clicks go (#85), under its name like the second secret, and allowed to
   break anywhere: a URL does not wrap at spaces because it has none. */
.ffp-integrations[b-b3mw07lnl0]  .ffp-int-interactivity {
    display: block;
    color: var(--ffp-muted);
    overflow-wrap: anywhere;
}

/* The URL editor, as a row of its own under the row it changes. */
.ffp-int-editor td[b-b3mw07lnl0] {
    background-color: var(--ffp-bg);
}

.ffp-int-inline[b-b3mw07lnl0] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-sm);
    padding: var(--ffp-space-sm) 0;
    white-space: normal;
}

.ffp-int-inline-actions[b-b3mw07lnl0] {
    display: flex;
    justify-content: flex-end;
    gap: var(--ffp-space-sm);
}
/* /Features/Admin/OidcPanel.razor.rz.scp.css */
/* The sign-in provider tab (F1.5). The governance panel's card, repeated rather than
   shared: a class from another component's stylesheet carries that component's scope
   id and matches nothing here. */

.ffp-oidc[b-kuqqfo9tar] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-md);
    padding: var(--ffp-space-lg) var(--ffp-space-xl) var(--ffp-space-xl);
    max-width: 60rem;
}

.ffp-oidc-card[b-kuqqfo9tar] {
    background-color: var(--ffp-surface);
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius-container);
    padding: var(--ffp-space-md);
}

.ffp-oidc[b-kuqqfo9tar]  .ffp-oidc-title {
    font-size: 1.125rem;
}

.ffp-oidc[b-kuqqfo9tar]  .ffp-oidc-hint {
    color: var(--ffp-muted);
    margin: var(--ffp-space-xs) 0 var(--ffp-space-md);
}

.ffp-oidc-form[b-kuqqfo9tar] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-sm);
    margin-bottom: var(--ffp-space-md);
    max-width: 36rem;
}

.ffp-oidc-buttons[b-kuqqfo9tar] {
    display: flex;
    gap: var(--ffp-space-sm);
    flex-wrap: wrap;
}
/* /Features/Channels/ChannelDirectoryDialog.razor.rz.scp.css */
.ffp-directory-row[b-8zjludxzp9] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
}
/* /Features/Channels/ChannelMemberList.razor.rz.scp.css */
/* The people in a channel, as the context drawer of Channel.razor lists them. Moved
   here with the markup when the list grew its role menu; the rules are the ones the
   drawer had. */

/* The drawer's card, written out again rather than reached from the page: a scoped
   rule in Channel.razor.css does not apply to markup this component writes. Keep the
   two in step - Channel.razor.css, .ffp-context-card. */
.ffp-member-card[b-5byoy1cpx0] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-sm);
    padding: var(--ffp-space-sm) 0.75rem;
    background: var(--ffp-bg);
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius);
}

.ffp-person[b-5byoy1cpx0] {
    display: flex;
    align-items: center;
    gap: var(--ffp-space-sm);
    min-width: 0;
}

.ffp-person-names[b-5byoy1cpx0] {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-width: 0;
}

.ffp-person-name[b-5byoy1cpx0] {
    font-size: 0.75rem;
    font-weight: 700;
    color: var(--ffp-fg);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ffp-person-role[b-5byoy1cpx0] {
    font-size: 0.6875rem;
    color: var(--ffp-muted);
}

/* A deactivated admin keeps the word on their row but no longer counts as one; this
   says so, quietly, so a workspace admin can see why the channel has nobody to run it. */
.ffp-person-inactive[b-5byoy1cpx0] {
    font-size: 0.6875rem;
    font-style: italic;
    color: var(--ffp-muted);
}

/* The sentence above the list when a workspace admin may step in. Muted rather than a
   warning colour: the channel works, it only has nobody to rename or archive it. */
.ffp-member-orphaned[b-5byoy1cpx0] {
    margin: 0;
    font-size: 0.6875rem;
    color: var(--ffp-muted);
}

/* The role menu sits at the end of the line and stays out of the way until the row is
   pointed at or focused, as the sidebar's row menu does - eight always-visible buttons
   down the drawer would be louder than the names. */
.ffp-person[b-5byoy1cpx0]  .ffp-person-menu {
    flex: 0 0 auto;
    opacity: 0;
    transition: opacity 120ms ease;
}

.ffp-person:hover[b-5byoy1cpx0]  .ffp-person-menu,
.ffp-person:focus-within[b-5byoy1cpx0]  .ffp-person-menu {
    opacity: 1;
}

@media (hover: none) {
    .ffp-person[b-5byoy1cpx0]  .ffp-person-menu {
        opacity: 1;
    }
}

.ffp-member-more[b-5byoy1cpx0] {
    margin: 0;
    font-size: 0.6875rem;
    color: var(--ffp-muted);
}

/* "Add people" (#528) under the list, left-aligned with the names rather than stretched
   across the card like a primary action: the drawer is about the conversation. */
.ffp-member-card[b-5byoy1cpx0]  .ffp-member-add {
    align-self: flex-start;
    text-transform: none;
}
/* /Features/Channels/ChannelRow.razor.rz.scp.css */
.ffp-channel-row[b-ofl9wdl274] {
    display: flex;
    align-items: center;
}

.ffp-channel-row[b-ofl9wdl274]  .mud-nav-link {
    flex: 1;
    min-width: 0;
}

.ffp-channel-name[b-ofl9wdl274] {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Unread is weight, not colour: it has to survive a colour-blind reader and a
   high-contrast theme (N11). */
.ffp-channel-row[b-ofl9wdl274]  .ffp-unread .ffp-channel-name {
    font-weight: 700;
}

.ffp-channel-dot[b-ofl9wdl274] {
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 50%;
    background: currentColor;
    margin-left: auto;
    flex: none;
}

/* ::deep, because the class is handed to MudBadge and lands on markup MudBlazor
   renders: a bare .ffp-channel-badge here compiles to .ffp-channel-badge[b-…] and
   matched nothing, so the count sat next to the name instead of at the end of the row.
   Found by ScopedCssTests rather than by anybody looking at a sidebar. */
.ffp-channel-row[b-ofl9wdl274]  .ffp-channel-badge {
    margin-left: auto;
}

/* A muted channel still counts its mentions - it is muted, not ignored. */
.ffp-muted[b-ofl9wdl274]  .mud-nav-link {
    opacity: 0.6;
}

/* The row menu appears on hover and on keyboard focus, so it is reachable without a
   mouse (N11). */
.ffp-channel-row[b-ofl9wdl274]  .ffp-channel-menu {
    opacity: 0;
    transition: opacity 120ms ease-in-out;
}

/* Reached through the row for the same reason as the badge above. Until now the whole
   group was dead, which is why the menu was visible on every row at once - the state it
   has when the rule that hides it does not exist. */
.ffp-channel-row:hover[b-ofl9wdl274]  .ffp-channel-menu,
.ffp-channel-row:focus-within[b-ofl9wdl274]  .ffp-channel-menu {
    opacity: 1;
}
/* /Features/Messages/BlockActions.razor.rz.scp.css */
/*
    A bot's buttons and selects (#85).

    Every colour is a token. Primary and danger are filled with the fixed brand colours
    and written in white, because those two pairs hold in both schemes without a flip:
    Petrol on white is 8.9:1 and Fehler on white 6.1:1, and a filled button carries its
    own ground with it. The default button is the outline one, ink on the canvas, with a
    border in the secondary ink so that its edge survives the 3:1 a control's boundary
    needs (WCAG 1.4.11) - the divider grey is 1.3:1 and would leave a floating word.
*/

.block-actions[b-nqn206n8tw] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ffp-space-sm);
    margin-top: var(--ffp-space-sm);
}

.block-button[b-nqn206n8tw] {
    display: inline-flex;
    align-items: center;
    min-height: 32px;
    padding: 0 0.85rem;
    border: 1px solid var(--ffp-muted);
    border-radius: var(--ffp-radius);
    background: var(--ffp-bg);
    color: var(--ffp-fg);
    font: inherit;
    font-size: 0.875rem;
    font-weight: 600;
    line-height: 1.2;
    text-decoration: none;
    cursor: pointer;
}

.block-button:hover:not(:disabled)[b-nqn206n8tw] {
    background: var(--ffp-surface);
}

.block-button.is-primary[b-nqn206n8tw] {
    border-color: var(--ffp-petrol);
    background: var(--ffp-petrol);
    color: var(--ffp-weiss);
}

.block-button.is-primary:hover:not(:disabled)[b-nqn206n8tw] {
    background: var(--ffp-petrol-tief);
}

.block-button.is-danger[b-nqn206n8tw] {
    border-color: var(--ffp-fehler);
    background: var(--ffp-fehler);
    color: var(--ffp-weiss);
}

.block-button.is-danger:hover:not(:disabled)[b-nqn206n8tw] {
    background: color-mix(in srgb, var(--ffp-fehler) 85%, black);
}

/* Visible on every control here, in both schemes: the accent is the one token that is
   read against the canvas it sits on and flips with it. The offset keeps the ring off a
   filled button's own edge, where Petrol on Petrol would vanish. */
.block-button:focus-visible[b-nqn206n8tw],
.block-select select:focus-visible[b-nqn206n8tw],
.block-ephemeral-close:focus-visible[b-nqn206n8tw] {
    outline: 2px solid var(--ffp-accent);
    outline-offset: 2px;
}

.block-button:disabled[b-nqn206n8tw],
.block-select select:disabled[b-nqn206n8tw] {
    cursor: default;
    opacity: 0.6;
}

.block-actions[aria-busy="true"][b-nqn206n8tw] {
    cursor: progress;
}

.block-select[b-nqn206n8tw] {
    display: inline-flex;
    align-items: center;
    gap: var(--ffp-space-xs);
}

.block-select select[b-nqn206n8tw] {
    min-height: 32px;
    max-width: 16rem;
    padding: 0 var(--ffp-space-sm);
    border: 1px solid var(--ffp-muted);
    border-radius: var(--ffp-radius);
    background: var(--ffp-bg);
    color: var(--ffp-fg);
    font: inherit;
    font-size: 0.875rem;
}

/* The same look as a slash command's answer in the composer: an edge, a label that says
   who can see it, and a way to put it away. */
.block-ephemeral[b-nqn206n8tw] {
    margin-top: var(--ffp-space-sm);
    padding: var(--ffp-space-sm) 0.85rem;
    border-left: 3px solid var(--ffp-accent);
    border-radius: 0 var(--ffp-radius) var(--ffp-radius) 0;
    background: var(--ffp-surface);
}

.block-ephemeral.is-error[b-nqn206n8tw] {
    border-left-color: var(--mud-palette-error);
}

.block-ephemeral-head[b-nqn206n8tw] {
    display: flex;
    align-items: center;
    gap: var(--ffp-space-sm);
    margin-bottom: 0.25rem;
}

.block-ephemeral-label[b-nqn206n8tw] {
    color: var(--ffp-muted);
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.block-ephemeral-close[b-nqn206n8tw] {
    margin-left: auto;
    padding: 0 var(--ffp-space-xs);
    border: 0;
    border-radius: var(--ffp-radius);
    background: none;
    color: var(--ffp-muted);
    font-size: 1rem;
    line-height: 1;
    cursor: pointer;
}

.block-ephemeral-close:hover[b-nqn206n8tw] {
    background: var(--mud-palette-action-default-hover);
}

.block-ephemeral-text[b-nqn206n8tw] {
    font-size: 0.875rem;
    overflow-wrap: anywhere;
    white-space: pre-line;
}

.block-ephemeral.is-error .block-ephemeral-text[b-nqn206n8tw] {
    color: var(--mud-palette-error);
}
/* /Features/Messages/CreatePollDialog.razor.rz.scp.css */
.poll-form-options[b-wu7ert1xo8] {
    border: none;
    margin: var(--ffp-space-md) 0 var(--ffp-space-sm);
    padding: 0;
    min-width: min(28rem, 80vw);
}

.poll-form-options legend[b-wu7ert1xo8] {
    font-weight: 600;
    margin-bottom: var(--ffp-space-xs);
}

.poll-form-option[b-wu7ert1xo8] {
    display: flex;
    align-items: center;
    gap: var(--ffp-space-xs);
}

.time-custom[b-wu7ert1xo8] {
    display: flex;
    gap: 1rem;
    flex-wrap: wrap;
}
/* /Features/Messages/MessageCollectionView.razor.rz.scp.css */
/*
    The saved list and the mentions list: messages out of their channel.

    Not the timeline - no grouping, no separators - so it is drawn as a table of rows
    rather than as a conversation, with the hairline doing the separating.
*/

.collection[b-44d683qsyi] {
    display: flex;
    flex-direction: column;
    min-height: 0;
    flex: 1;
    background: var(--ffp-bg);
}

.collection-header[b-44d683qsyi] {
    padding: 0.75rem 1.5rem;
    border-bottom: 1px solid var(--ffp-border);
}

.collection-body[b-44d683qsyi] {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    padding: var(--ffp-space-sm) 0;
}

/* ::deep because the class is handed to MudPaper: a component takes the class but not
   the scope attribute, so the plain selector matched nothing and these rows were drawn
   as MudBlazor's default card, shadow and all. */
[b-44d683qsyi] .collection-item {
    padding: 0.6rem 1.5rem;
    border-bottom: 1px solid var(--ffp-border);
    background: transparent;
    border-radius: 0;
}

[b-44d683qsyi] .collection-item:hover {
    background: var(--ffp-surface);
}

.collection-meta[b-44d683qsyi] {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    align-items: baseline;
    font-size: 0.6875rem;
    color: var(--ffp-muted);
    margin-bottom: 0.25rem;
}

@media (max-width: 600px) {
    .collection-header[b-44d683qsyi],
    [b-44d683qsyi] .collection-item {
        padding-left: var(--ffp-space-md);
        padding-right: var(--ffp-space-md);
    }
}
/* /Features/Messages/MessageComposer.razor.rz.scp.css */
/*
    The box a message is written in.

    Several selectors below are prefixed with ::deep. That is not decoration: MudPaper,
    MudMenu and InputFile are components, and a Class= handed to a component reaches its
    element while the scope attribute does not, so a plain ".composer-mentions" rule
    compiles to ".composer-mentions[b-composer]" and matches nothing. The visible
    symptom was the raw "Datei auswählen / keine ausgewählt" control sitting in the
    toolbar in every browser, because the rule meant to hide it never applied. ::deep
    puts the attribute on the ancestor this component does own, which is the box itself.
*/

.composer[b-dxakswacde] {
    margin: 0 1.5rem var(--ffp-space-md);
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius-container);
    background: var(--ffp-bg);
    position: relative;
}

/*
    The focus state the handbook asks of an input: a crisp Petrol border and no outer
    glow. It is drawn with an inset shadow rather than a thicker border so the box does
    not grow by a pixel and shove the timeline up when the caret lands. One static
    shadow on one element is not what the R3 benchmark warns about - that is a painted
    effect per row, of which there are thousands.
*/
.composer:focus-within[b-dxakswacde] {
    border-color: var(--ffp-accent);
    box-shadow: inset 0 0 0 1px var(--ffp-accent);
}

/* A file dragged over the box says so, or the drop looks like it did nothing. */
.composer.dragging[b-dxakswacde] {
    border-color: var(--ffp-accent);
    background: var(--ffp-surface);
}

.composer-input[b-dxakswacde] {
    display: block;
    width: 100%;
    border: none;
    outline: none;
    resize: vertical;
    padding: 0.7rem 0.85rem;
    background: transparent;
    color: var(--ffp-fg);
    font: inherit;
    line-height: 1.5;
}

.composer-input[b-dxakswacde]::placeholder {
    color: var(--ffp-muted);
}

/*
    The bar under the box: formatting on the left, the send button on the right.

    Separated from the input by a hairline rather than by a shade, which is the way this
    system draws boundaries everywhere else ("Physical Borders over Shadows").
*/
.composer-toolbar[b-dxakswacde] {
    display: flex;
    align-items: center;
    gap: 0.15rem;
    padding: var(--ffp-space-xs) var(--ffp-space-sm);
    border-top: 1px solid var(--ffp-border);
    flex-wrap: wrap;
}

.tool[b-dxakswacde] {
    background: none;
    border: none;
    border-radius: var(--ffp-radius);
    min-width: 1.9rem;
    height: 1.9rem;
    cursor: pointer;
    color: var(--mud-palette-action-default);
    font-size: 0.85rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.tool:hover[b-dxakswacde] {
    background: var(--mud-palette-action-default-hover);
}

/* 40px on the desktop, which is the height the handbook gives a primary button, and the
   0.25rem radius the theme already sets - stated here only so the button cannot inherit
   a pill from a future MudBlazor default. */
[b-dxakswacde] .composer-send {
    min-height: 40px;
    margin-left: var(--ffp-space-sm);
    border-radius: var(--ffp-radius);
}

/* The input is inside the label, so the label is the button - but only if the browser
   is actually told not to draw the input. */
[b-dxakswacde] .composer-file-input {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

/* .visually-hidden moved to wwwroot/css/app.css when the third component needed it. A
   scoped copy per component would have been three copies of a rule whose whole value is
   that it is the same everywhere, and a scoped rule cannot be reached from a sibling
   component anyway. */

.composer-footer[b-dxakswacde] {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0 0.85rem 0.5rem;
}

.composer-hint[b-dxakswacde] {
    font-size: 0.6875rem;
    color: var(--ffp-muted);
}

.composer-error[b-dxakswacde] {
    font-size: 0.78rem;
    color: var(--mud-palette-error);
}

/*
    Files that are uploaded and waiting to be sent.

    Each one is the same object the timeline draws under a message: white, one hairline,
    4px. Somebody should recognise what they are about to send as the thing that will
    appear once they do.
*/
.composer-attachments[b-dxakswacde] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--ffp-space-xs);
    padding: var(--ffp-space-sm) var(--ffp-space-sm) 0;
}

.composer-attachment[b-dxakswacde] {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    max-width: 22rem;
    padding: 0.3rem 0.4rem 0.3rem 0.5rem;
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius);
    background: var(--ffp-bg);
}

/* ::deep, because the thumbnail is drawn by AuthedImage now and a child component's
   elements never carry this component's scope attribute. Without it the rule compiles,
   loads, and matches nothing - the trap phase 2.5b already cost an afternoon to. */
[b-dxakswacde] .attachment-thumb {
    width: 2rem;
    height: 2rem;
    object-fit: cover;
    border-radius: var(--ffp-radius);
    background: var(--ffp-border);
}

.attachment-name[b-dxakswacde] {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 0.8125rem;
    font-weight: 600;
}

.attachment-size[b-dxakswacde] {
    color: var(--ffp-muted);
    font-size: 0.6875rem;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.attachment-drop[b-dxakswacde] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.35rem;
    height: 1.35rem;
    padding: 0;
    border: none;
    border-radius: var(--ffp-radius);
    background: none;
    color: var(--ffp-muted);
    font-size: 1.1rem;
    line-height: 1;
    cursor: pointer;
}

.attachment-drop:hover[b-dxakswacde] {
    background: var(--mud-palette-action-default-hover);
    color: var(--mud-palette-error);
}

/* What the server will make of the markdown, shown above the box so it reads in the
   same direction as the text it belongs to. */
[b-dxakswacde] .composer-preview {
    border-bottom: 1px solid var(--ffp-border);
    padding: var(--ffp-space-sm) 0.85rem;
    background: var(--ffp-surface);
    border-radius: var(--ffp-radius-container) var(--ffp-radius-container) 0 0;
}

.preview-label[b-dxakswacde] {
    font-size: 0.6875rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-weight: 600;
    color: var(--ffp-muted);
    margin-bottom: 0.25rem;
}

/*
    The mention list sits above the box: the caret is at the bottom of the screen and a
    list below it would be off the edge. This floats over the page, so it is one of the
    three things the handbook allows the single architectural shadow for.
*/
[b-dxakswacde] .composer-mentions {
    position: absolute;
    bottom: 100%;
    left: 0;
    right: 0;
    margin-bottom: 0.25rem;
    max-height: 14rem;
    overflow-y: auto;
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius-container);
    box-shadow: var(--ffp-shadow-overlay);
    z-index: 10;
}

/* A <button> since the accessibility sweep. app.css gives every bare button the primary
   fill, so the four lines that undo it are not optional - without them the mention list
   is a stack of Petrol blocks. */
.mention-option[b-dxakswacde] {
    display: flex;
    align-items: baseline;
    /* See QuickSwitcher.razor.css: a full-width flex button centres its own contents
       unless it is told not to. */
    justify-content: flex-start;
    gap: 0.5rem;
    width: 100%;
    padding: 0.4rem 0.75rem;
    border: 0;
    border-radius: 0;
    background: none;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.mention-option:hover[b-dxakswacde] {
    background: var(--ffp-surface);
}

.mention-name[b-dxakswacde] {
    font-weight: 600;
}

.mention-note[b-dxakswacde] {
    font-size: 0.72rem;
    color: var(--ffp-muted);
}

.emoji-picker[b-dxakswacde] {
    padding: 0.5rem;
    width: 17rem;
}

.emoji-grid[b-dxakswacde] {
    display: grid;
    grid-template-columns: repeat(8, 1fr);
    gap: 0.1rem;
    max-height: 12rem;
    overflow-y: auto;
}

.emoji[b-dxakswacde] {
    background: none;
    border: none;
    border-radius: var(--ffp-radius);
    font-size: 1.15rem;
    cursor: pointer;
    padding: 0.15rem;
}

.emoji:hover[b-dxakswacde] {
    background: var(--mud-palette-action-default-hover);
}

/*
    On a phone the box keeps the screen's full width and the touch targets grow to the
    48px the handbook asks for in the field ("Dimensions"). A bricklayer with gloves on
    is the reader this rule exists for.
*/
@media (max-width: 600px) {
    .composer[b-dxakswacde] {
        margin: 0 var(--ffp-space-sm) var(--ffp-space-sm);
    }

    .tool[b-dxakswacde] {
        min-width: 2.5rem;
        height: 2.5rem;
        font-size: 1rem;
    }

    [b-dxakswacde] .composer-send {
        min-height: 48px;
    }
}

/*
    What a slash command said, only for the person who typed it (#360). Drawn like the
    preview - above the box, a hairline under it - with the accent bar on the left that
    marks it as not part of the conversation. An error keeps the same shape in the error
    colour, so "no such command" and "here is your answer" read as the same kind of line.
*/
.composer-ephemeral[b-dxakswacde] {
    border-bottom: 1px solid var(--ffp-border);
    border-left: 3px solid var(--ffp-accent);
    padding: var(--ffp-space-sm) 0.85rem;
    background: var(--ffp-surface);
    border-radius: var(--ffp-radius-container) var(--ffp-radius-container) 0 0;
}

.composer-ephemeral.is-error[b-dxakswacde] {
    border-left-color: var(--mud-palette-error);
}

.ephemeral-head[b-dxakswacde] {
    display: flex;
    align-items: center;
    gap: var(--ffp-space-sm);
    margin-bottom: 0.25rem;
}

.ephemeral-label[b-dxakswacde] {
    font-size: 0.6875rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    font-weight: 600;
    color: var(--ffp-muted);
}

.ephemeral-command[b-dxakswacde] {
    font-family: ui-monospace, Menlo, Consolas, monospace;
    font-size: 0.78rem;
    color: var(--ffp-muted);
}

.ephemeral-close[b-dxakswacde] {
    margin-left: auto;
    border: 0;
    background: none;
    cursor: pointer;
    color: var(--ffp-muted);
    font-size: 1rem;
    line-height: 1;
    padding: 0 var(--ffp-space-xs);
    border-radius: var(--ffp-radius);
}

.ephemeral-close:hover[b-dxakswacde] {
    background: var(--mud-palette-action-default-hover);
}

.ephemeral-text[b-dxakswacde] {
    white-space: pre-line;
    overflow-wrap: anywhere;
    font-size: 0.875rem;
}

.composer-ephemeral.is-error .ephemeral-text[b-dxakswacde] {
    color: var(--mud-palette-error);
}

/*
    "Send later" (#87): the clock beside the send button and the short list of when it
    opens, floating over the page above the toolbar like the mention list.
*/
.composer-later[b-dxakswacde] {
    position: relative;
}

.later-toggle svg[b-dxakswacde] {
    width: 1.1rem;
    height: 1.1rem;
    fill: currentColor;
}

.later-toggle:disabled[b-dxakswacde] {
    opacity: 0.4;
    cursor: default;
}

[b-dxakswacde] .composer-later-menu {
    position: absolute;
    bottom: 100%;
    right: 0;
    margin-bottom: 0.25rem;
    min-width: 12rem;
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius-container);
    box-shadow: var(--ffp-shadow-overlay);
    z-index: 10;
    padding: 0.25rem 0;
}

.later-title[b-dxakswacde] {
    padding: 0.25rem 0.75rem;
    font-size: 0.72rem;
    color: var(--ffp-muted);
}

/* Undoes app.css's primary fill for bare buttons, as .mention-option does. */
.later-option[b-dxakswacde] {
    display: block;
    width: 100%;
    padding: 0.4rem 0.75rem;
    border: 0;
    border-radius: 0;
    background: none;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.later-option:hover[b-dxakswacde] {
    background: var(--ffp-surface);
}
/* /Features/Messages/MessageList.razor.rz.scp.css */
/*
    Chat timeline. The flip lives here, not in the data - see MessageList.razor.

    Only the container and the things this component draws itself are styled here. The
    rows moved to MessageRow.razor.css: scoped CSS stamps the owning component's
    attribute onto the last element of a selector, so a ".message" rule written in this
    file compiled to ".message[b-list]" and matched nothing, the row carrying
    MessageRow's attribute instead. Everything the row needs now sits beside the markup
    it applies to.
*/

.message-list[b-i5s20ggtfl] {
    display: flex;
    flex-direction: column-reverse;
    overflow-y: auto;
    height: 100%;
    background: var(--ffp-bg);
    /* Keeps the viewport still while older messages are prepended above it. */
    overflow-anchor: none;
    contain: strict;
}

/* The bottom padding is the breathing space between the newest message and the
   composer; the top one keeps the first day separator off the channel header.

   Top-down, oldest first: only the container above is reversed, which is what keeps
   the scroll position counted from the newest end (#335, see MessageList.razor). */
.message-list-inner[b-i5s20ggtfl] {
    display: flex;
    flex-direction: column;
    padding: var(--ffp-space-sm) 0 var(--ffp-space-md);
}

/* Stand-ins for the rows that are not drawn, sized by the script's measurements. They
   must keep exactly that height, or the rows below them move. */
.message-list-spacer[b-i5s20ggtfl],
.message-list-entry[b-i5s20ggtfl] {
    flex-shrink: 0;
}

/*
    The day separator: a rule across the width with the date resting in it.

    The handbook forbids decorative rules under headings, and this is not one - it is a
    divider between two days of conversation, which is the job Linie is for. The label
    is set in the small caps the screens use for the same band ("HEUTE - 24. OKTOBER").
*/
.day-separator[b-i5s20ggtfl] {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin: var(--ffp-space-lg) 1.5rem var(--ffp-space-xs);
    color: var(--ffp-muted);
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

.day-separator[b-i5s20ggtfl]::before,
.day-separator[b-i5s20ggtfl]::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--ffp-border);
}

/*
    Where the reader had got to when they opened the channel.

    It had no rule at all before and rendered as a stray line of text in the middle of
    the timeline. It is drawn like the day separator so the two read as one family, and
    in amber because this is a marker the eye is meant to find - the one tactical accent
    the handbook allows per view, spent here rather than on decoration.

    Which amber is --ffp-unread's business, not this file's, and the answer is not the
    same in both schemes (app.css). It used to be Amber Tief in both, on the strength of
    a measurement taken on the dark canvas only; on white that is 3.43:1, and 11px bold
    is not large text, so the light scheme had been failing AA here since the day the
    rule was written. Planning/10 left the choice open pending sign-off of the derived
    dark palette - the measurement closed it instead, and in the other scheme than the
    one the open point was looking at.
*/
.unread-divider[b-i5s20ggtfl] {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin: var(--ffp-space-md) 1.5rem var(--ffp-space-xs);
    color: var(--ffp-unread);
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}

/* Centred like the day separator, because the two are read as one family: the only
   difference between them is which of the two things the rule is announcing. */
.unread-divider[b-i5s20ggtfl]::before,
.unread-divider[b-i5s20ggtfl]::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--ffp-unread);
}

/*
    The end of what is loaded while older history exists (#339). Rows in the shape of a
    message - the avatar tile and two lines - in the colour of the Linie, so they read as
    "something goes here" and not as content. Static on purpose: a shimmer would repaint
    on every frame for as long as somebody reads near the top.
*/
.loading-older[b-i5s20ggtfl] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-md);
    padding: var(--ffp-space-md) 1.5rem;
}

.loading-older-row[b-i5s20ggtfl] {
    display: grid;
    grid-template-columns: 2.25rem minmax(0, 1fr);
    gap: 0 0.75rem;
    align-items: start;
}

.loading-older-avatar[b-i5s20ggtfl] {
    width: 2.25rem;
    height: 2.25rem;
    border-radius: var(--ffp-radius);
    background: var(--ffp-border);
}

.loading-older-lines[b-i5s20ggtfl] {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding-top: 0.25rem;
}

.loading-older-line[b-i5s20ggtfl] {
    display: block;
    width: 60%;
    height: 0.75rem;
    border-radius: var(--ffp-radius);
    background: var(--ffp-border);
}

.loading-older-line.short[b-i5s20ggtfl] {
    width: 35%;
}

@media (max-width: 600px) {
    .day-separator[b-i5s20ggtfl],
    .unread-divider[b-i5s20ggtfl] {
        margin-left: var(--ffp-space-md);
        margin-right: var(--ffp-space-md);
    }

    .loading-older[b-i5s20ggtfl] {
        padding-left: var(--ffp-space-md);
        padding-right: var(--ffp-space-md);
    }
}
/* /Features/Messages/MessageRow.razor.rz.scp.css */
/*
    The look of one message.

    These rules used to live in MessageList.razor.css, next to the component that owns
    the scrolling container. That was wrong in a way no build catches: scoped CSS puts
    the owning component's attribute on the *last* element of a selector, so
    ".message" written in MessageList.razor.css compiled to ".message[b-list]" and never
    matched the row, which carries MessageRow's own attribute. The grid, the avatar
    colours and the author line were dead from the day they were written; the timeline
    rendered as a stack of unstyled divs and the build stayed green throughout. A rule
    now lives in the .razor.css of the component whose markup it points at.

    The palette comes from wwwroot/css/app.css, which mirrors Theme/FlurfunkProTheme.cs.
    No hex value is written here: the handbook's distribution (60-30-5) and its forbidden
    pairings are enforced at the token layer, and a component that picks its own colour
    steps outside both.
*/

/*
    The row: avatar, body, and the actions that appear on hover.

    Grouping is expressed entirely as margin, because the handbook gives the two cases
    different values - 4px between paragraphs of the same sender, 16px between blocks
    (DESIGN.md, "Rhythmic Grid"). Padding would have to be split between neighbours and
    would stop the follow-up rows from sitting as close as the design asks.

    The margin is on the top edge although the container is column-reverse: physical
    margins do not flip with flex-direction, so margin-top is still the gap to whatever
    is drawn above.
*/
.message[b-847vjevnuj] {
    /* The gutter, named because ReactionBar has to align its own floating button to it
       and cannot read this file. Custom properties inherit, so the child component reads
       whatever the media query below has set. */
    --ffp-row-gutter: 1.5rem;

    display: grid;
    /* Two columns, not three: the hover actions float over the row instead of reserving
       a track. A track for them cost about 124px of measure on every width - invisible
       beside a 948px column on the desktop, but a third of the readable width on a
       414px phone, where the bubble stopped two thirds of the way across the screen.
       minmax(0, 1fr) rather than 1fr so a long unbroken URL cannot widen the track. */
    grid-template-columns: 2.25rem minmax(0, 1fr);
    gap: 0 0.75rem;
    padding: 0 var(--ffp-row-gutter);
    margin-top: var(--ffp-space-md);
    /* The anchor for the two floating clusters: the row actions here, and ReactionBar's
       "add a reaction" button, which sits beside them. */
    position: relative;
    /* A fixed line-height keeps row heights predictable, which is what virtualisation
       depends on. Leave this to the browser and the estimate drifts. */
    line-height: 1.45;
}

/* A further paragraph from the same sender within a few minutes: no avatar, no name,
   and the small gap that says "still the same person talking". */
.message.follow-up[b-847vjevnuj] {
    margin-top: var(--ffp-space-xs);
}

.message.follow-up .message-body[b-847vjevnuj] {
    grid-column: 2;
}

/* Deliberately not positioned: ReactionBar's floating button measures itself against
   .message, so that it lines up with the row actions rather than with the bubble. */
.message-body[b-847vjevnuj] {
    min-width: 0;
}

/* Name, time and the edited mark, on one baseline. */
.message-header[b-847vjevnuj] {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-bottom: 0.15rem;
}

/* Tinte and bold, as the handbook specifies for the author heading. --ffp-fg rather
   than the constant, so the dark scheme substitutes its own reading ink. */
.author[b-847vjevnuj] {
    color: var(--ffp-fg);
    font-weight: 700;
}

/* "Anna hat /deploy verwendet" under the author of a command's answer (#375). Muted
   and small, like the time: context for the message, not part of what was said. */
.invocation[b-847vjevnuj] {
    color: var(--ffp-muted);
    font-size: 0.75rem;
    margin-bottom: 0.15rem;
}

/* Grau at 11px, the handbook's value for a timestamp on white. Tabular figures so the
   column of times does not wobble from row to row. */
.message-header time[b-847vjevnuj] {
    color: var(--ffp-muted);
    font-size: 0.6875rem;
    font-variant-numeric: tabular-nums;
}

/*
    The bubble.

    Incoming messages sit on the white canvas inside a 1px Linie hairline; the reader's
    own sit on Nebel. That is the handbook's rule, and it is also the one place where
    depth is allowed to come from a border instead of a shadow ("Elevation & Depth" -
    physical borders over shadows). No shadow and no filter here for a second reason as
    well: the list is virtualised, and a painted effect per row is what quadrupled the
    frame time in the R3 benchmark (Planning/16-Blazor-Performance-Spike.md).

    90ch is the handbook's cap on the measure. Without it a message on a 1400px canvas
    runs to 160 characters a line, which is roughly twice what anyone reads comfortably.
*/
.text[b-847vjevnuj] {
    max-width: 90ch;
    padding: 0.45rem 0.7rem;
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius);
    background: var(--ffp-bg);
    overflow-wrap: anywhere;
}

/*
    The reader's own message: a solid Nebel container.

    The border stays, in the container's own colour. Dropping it would make every row
    2px shorter than an incoming one for no visible reason, and virtualisation is
    happier when rows of the same length are the same height.

    Not mirrored, at any width. Decided 21.09.2026, closing the open point in
    Planning/10 ("Eigene Nachrichten nicht gespiegelt"), and the reason is not that it
    would be hard - the avatar is aria-hidden, so putting it in the second column would
    cost a screen reader nothing.

    It is that the screen which mirrors is the phone screen of the Flutter client
    (Planning/design/mobile_app_channel_baustelle_ost), and this is the browser. A
    browser window is resizable: tie the side a message sits on to a breakpoint and
    dragging a window narrower throws half the conversation across the screen and back
    again. A phone cannot do that, which is exactly why the idiom works there. The
    desktop reference screen does not mirror, the browser follows it at every width, and
    the phone layout is Flutter's to build in phase 3 from the screen that is of it.
*/
.message.mine .text[b-847vjevnuj] {
    border-color: var(--ffp-surface);
    background: var(--ffp-surface);
}

/*
    Written by a program, not by a person: a 3px vertical edge on the left of a Nebel
    container, which is what the handbook prescribes for programmatic content.

    The edge takes --ffp-accent rather than the Petrol constant. In the light scheme
    those are the same colour. In the dark scheme Petrol sits at 1.5:1 against the dark
    surface and the edge would simply not be there, whereas the accent follows the
    theme's derived dark primary and stays visible - and an indicator that disappears
    for half the users is not an indicator.
*/
.message.generated .text[b-847vjevnuj] {
    border-color: var(--ffp-surface);
    border-left: 3px solid var(--ffp-accent);
    background: var(--ffp-surface);
}

/* The headstone left behind by a deletion (ADR-028): the row keeps its place in the
   count, and says plainly that there is nothing left to read. */
.deleted-text[b-847vjevnuj] {
    color: var(--ffp-muted);
    font-style: italic;
}

.edited[b-847vjevnuj] {
    color: var(--ffp-muted);
    font-size: 0.6875rem;
}

/*
    Files sent with a message.

    Written from here with ::deep because the markup belongs to MessageAttachments,
    which has no stylesheet of its own; ::deep compiles to "[b-row] .attachment", so it
    reaches the child's elements through the row this component does own. The card is
    the same object as the bubble - white, 1px Linie, 4px - because the screens draw it
    that way: a second surface under the message rather than a decorated link.
*/
[b-847vjevnuj] .attachments {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--ffp-space-xs);
    margin-top: var(--ffp-space-sm);
}

/* Both attachments are buttons now, not links, because opening one is an action this
   app performs - fetch with the session's token, then hand the bytes to the browser -
   rather than an address the browser can follow on its own. So they need the reset a
   link never did: a <button> arrives with the user agent's own border, background,
   padding and font, none of which the design system asked for. */
[b-847vjevnuj] .attachment-open,
[b-847vjevnuj] .attachment-file {
    appearance: none;
    font: inherit;
    cursor: pointer;
    text-align: left;
}

[b-847vjevnuj] .attachment-open {
    display: block;
    padding: 0;
    border: 0;
    background: none;
}

/* Holds the thumbnail's place while the bytes are on their way, so a timeline does not
   jump under the reader's eyes when an image lands.

   Since #341 a picture whose shape the server recorded brings its own box as an inline
   style (ThumbnailBox), on this placeholder and on the picture alike; 12rem x 8rem is
   the box for one whose shape is unknown. box-sizing keeps the 1px Linie inside the box,
   or the picture and its placeholder would differ by the border. */
[b-847vjevnuj] .ffp-image-pending {
    display: block;
    box-sizing: border-box;
    width: 12rem;
    height: 8rem;
    max-width: 100%;
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius);
    background: var(--ffp-surface-muted, var(--ffp-bg));
}

[b-847vjevnuj] .attachment img {
    display: block;
    box-sizing: border-box;
    max-width: min(22rem, 100%);
    max-height: 14rem;
    object-fit: cover;
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius);
}

/* No recorded shape: the placeholder's box, with the picture cropped into it rather
   than the row growing to whatever it turns out to be. */
[b-847vjevnuj] .attachment img.attachment-unsized {
    width: 12rem;
    height: 8rem;
}

[b-847vjevnuj] .attachment-file {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4rem 0.7rem;
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius);
    background: var(--ffp-bg);
    color: var(--ffp-fg);
    font-weight: 600;
}

/* The metadata line the screens put under the file name - size, type - in the secondary
   tone, and not bold like the name above it. */
[b-847vjevnuj] .attachment-file .size {
    color: var(--ffp-muted);
    font-size: 0.75rem;
    font-weight: 400;
}

/* The link into a thread. A button rather than an anchor, so it takes the same
   typography the rest of the row does and none of the underline the handbook forbids. */
.thread-link[b-847vjevnuj] {
    margin-top: 0.25rem;
    padding: 0;
    background: none;
    border: 0;
    color: var(--ffp-accent);
    font: inherit;
    font-size: 0.8125rem;
    font-weight: 600;
    cursor: pointer;
}

.thread-link:hover[b-847vjevnuj] {
    text-decoration: underline;
}

/*
    Row actions: plain buttons, shown on hover and on keyboard focus so they are
    reachable without a mouse (N11).

    Floating over the row's top right rather than sitting in a column of their own. The
    right offset leaves room for ReactionBar's button, which lands beside them and is one
    2.1rem target wide; the two together read as one cluster. They are given the page's
    own surface and a hairline so that they stay legible over the corner of a bubble on a
    narrow screen - no shadow, which the timeline cannot afford per row and the handbook
    does not allow on an in-page element anyway.
*/
.message-actions[b-847vjevnuj] {
    display: flex;
    gap: 0.125rem;
    position: absolute;
    top: 0;
    right: calc(var(--ffp-row-gutter) + 2.1rem);
    padding: 1px;
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius);
    background: var(--ffp-bg);
    opacity: 0;
    transition: opacity 120ms ease-in-out;
}

.message:hover .message-actions[b-847vjevnuj],
.message:focus-within .message-actions[b-847vjevnuj] {
    opacity: 1;
}

/*
    The "add a reaction" button belongs to ReactionBar but is revealed by this row's
    hover, and a scoped selector can carry only one component's attribute. Written from
    here with ::deep, the attribute lands on .message - the element this component owns -
    and the rule reaches the child. Written in ReactionBar.razor.css it would have had to
    land on .message too, where it does not belong, and would have matched nothing.
*/
.message:hover[b-847vjevnuj]  .reaction-add,
.message:focus-within[b-847vjevnuj]  .reaction-add {
    opacity: 1;
}

.row-action[b-847vjevnuj] {
    background: none;
    border: none;
    border-radius: var(--ffp-radius);
    padding: 0.2rem;
    cursor: pointer;
    color: var(--mud-palette-action-default);
    line-height: 0;
}

.row-action:hover[b-847vjevnuj] {
    background: var(--mud-palette-action-default-hover);
}

/* Pinned and saved are states, not commands, so their buttons are drawn in the accent
   rather than the neutral icon tone. Only while the row is hovered or focused, like the
   rest of this group - a persistent mark would need a rule that inspects the row's
   children on every pass, and this list cannot afford one per row. */
.row-action.on[b-847vjevnuj] {
    color: var(--ffp-accent);
}

.row-action svg[b-847vjevnuj] {
    width: 1.05rem;
    height: 1.05rem;
    fill: currentColor;
}

.edit-actions[b-847vjevnuj] {
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
    margin-top: 0.4rem;
}

/* The message a jump landed on, marked long enough to find and not long enough to
   nag. Amber is the handbook's beacon colour and this is the one place in the timeline
   that uses it. */
.message.highlighted[b-847vjevnuj] {
    animation: ffp-highlight-b-847vjevnuj 2.4s ease-out;
}

@keyframes ffp-highlight-b-847vjevnuj {
    0%, 55% { background: color-mix(in srgb, var(--ffp-amber) 22%, transparent); }
    100% { background: transparent; }
}

/*
    Narrow screens. The mobile screen keeps the same objects and gives back the gutter:
    24px of side padding on a 414px phone leaves the text in a column too narrow to
    read.
*/
@media (max-width: 600px) {
    .message[b-847vjevnuj] {
        --ffp-row-gutter: var(--ffp-space-md);

        grid-template-columns: 2rem minmax(0, 1fr);
        gap: 0 0.5rem;
    }
}
/* /Features/Messages/PinnedMessagesDialog.razor.rz.scp.css */
/*
    The pins of a channel, in a dialog.

    A dialog is one of the three places the handbook allows the architectural shadow, and
    MudDialog already draws it; nothing here adds a second one. The rows only need the
    quoted message to look like a quoted message: Nebel, a hairline, 4px - the same
    object as a message bubble, because that is what it is.
*/

.pin[b-3411h5o88a] {
    display: flex;
    align-items: flex-start;
    gap: 1rem;
    padding: 0.6rem 0;
}

.pin-body[b-3411h5o88a] {
    flex: 1;
    min-width: 0;
    padding: 0.45rem 0.7rem;
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius);
    background: var(--ffp-surface);
    overflow-wrap: anywhere;
}
/* /Features/Messages/PollView.razor.rz.scp.css */
/*
    A poll under its question (#533).

    A card in the handbook's vocabulary: one hairline of Linie, the container radius, no
    shadow. The options are the reaction chip grown to full width - the same border, the
    same accent for one's own choice - so a poll reads as part of the message rather than
    as a widget dropped into it.
*/

.poll[b-wclf8w41p4] {
    margin-top: var(--ffp-space-xs);
    max-width: 36rem;
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius-container);
    background: var(--ffp-bg);
    padding: var(--ffp-space-sm) var(--ffp-space-md);
}

.poll-question[b-wclf8w41p4] {
    font-weight: 600;
    margin-bottom: var(--ffp-space-sm);
}

.poll-options[b-wclf8w41p4] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-xs);
}

.poll-option[b-wclf8w41p4] {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--ffp-space-sm);
    width: 100%;
    overflow: hidden;
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius);
    background: var(--ffp-surface);
    padding: 0.35rem 0.6rem;
    color: var(--ffp-fg);
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.poll-option:hover:not(:disabled)[b-wclf8w41p4] {
    border-color: var(--ffp-accent);
}

.poll-option:focus-visible[b-wclf8w41p4] {
    outline: 2px solid var(--ffp-accent);
    outline-offset: 2px;
}

.poll-option:disabled[b-wclf8w41p4] {
    cursor: default;
}

/* One's own choice is marked by the border and the mark, not only by a tint (N11). */
.poll-option.mine[b-wclf8w41p4] {
    border-color: var(--ffp-accent);
}

/* The bar sits behind the text; its tint is mixed from the accent so it follows the
   theme into the dark scheme. */
.poll-bar[b-wclf8w41p4] {
    position: absolute;
    inset: 0 auto 0 0;
    background: color-mix(in srgb, var(--ffp-accent) 14%, transparent);
    transition: width 0.2s ease-out;
}

.poll-option.mine .poll-bar[b-wclf8w41p4] {
    background: color-mix(in srgb, var(--ffp-accent) 26%, transparent);
}

.poll-mark[b-wclf8w41p4],
.poll-text[b-wclf8w41p4],
.poll-count[b-wclf8w41p4] {
    position: relative;
}

.poll-mark[b-wclf8w41p4] {
    color: var(--ffp-accent);
}

.poll-text[b-wclf8w41p4] {
    flex: 1;
    min-width: 0;
    overflow-wrap: anywhere;
}

.poll-count[b-wclf8w41p4] {
    color: var(--ffp-muted);
    font-size: 0.8rem;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

.poll-voters[b-wclf8w41p4] {
    color: var(--ffp-muted);
    font-size: 0.8rem;
    padding: 0 0.6rem 0.2rem;
}

.poll-footer[b-wclf8w41p4] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ffp-space-xs) var(--ffp-space-md);
    margin-top: var(--ffp-space-sm);
    color: var(--ffp-muted);
    font-size: 0.8rem;
}

.poll.closed .poll-state[b-wclf8w41p4] {
    color: var(--ffp-fg);
    font-weight: 600;
}

.poll-link[b-wclf8w41p4] {
    border: none;
    background: none;
    padding: 0;
    color: var(--ffp-accent);
    font: inherit;
    cursor: pointer;
    text-decoration: underline;
}

.poll-link:focus-visible[b-wclf8w41p4] {
    outline: 2px solid var(--ffp-accent);
    outline-offset: 2px;
}
/* /Features/Messages/ReactionBar.razor.rz.scp.css */
/*
    Reactions under a message.

    A reaction chip is a categorical tag in the handbook's vocabulary: Nebel fill, one
    hairline of Linie, Tinte text and the base 4px radius. It used to be a 999px pill,
    which the system rules out - circles and pills are reserved for notification badges
    and status pips, and everything else keeps the architectural corner ("Shapes").
*/

.reactions[b-i4h6fnvfsw] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ffp-space-xs);
    margin-top: var(--ffp-space-sm);
}

/*
    A bar that holds nothing but the "add a reaction" button costs no height.

    It is lifted out of the flow to the row's top right, where it lands immediately
    beside the row's other hover actions and reads as part of the same cluster. The
    positioned ancestor is .message, and --ffp-row-gutter is the row's own side padding,
    published by MessageRow.razor.css because this file cannot read it.

    In the flow this cost 34 px under every message - one third of a one-line row -
    reserving space for a button nobody can see until they hover. On a screenful of
    fifteen messages that is a message and a half of conversation given away.
*/
.reactions.add-only[b-i4h6fnvfsw] {
    position: absolute;
    top: 0;
    right: var(--ffp-row-gutter, 1.5rem);
    margin: 0;
}

.reaction[b-i4h6fnvfsw] {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius);
    background: var(--ffp-surface);
    padding: 0.1rem 0.45rem;
    font-size: 0.78rem;
    cursor: pointer;
    color: var(--ffp-fg);
}

.reaction:hover[b-i4h6fnvfsw] {
    border-color: var(--ffp-accent);
}

/* Your own reaction is marked by the border, not only by a tint: at this size a tint
   is invisible to anyone with a contrast problem (N11). The tint is mixed from the
   accent rather than taken from MudBlazor's hover colour, so it is the same blue-green
   as the border and follows the theme into the dark scheme. */
.reaction.mine[b-i4h6fnvfsw] {
    border-color: var(--ffp-accent);
    background: color-mix(in srgb, var(--ffp-accent) 12%, var(--ffp-bg));
}

.reaction .emoji[b-i4h6fnvfsw] {
    font-size: 0.95rem;
    line-height: 1;
}

.reaction .count[b-i4h6fnvfsw] {
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}

/*
    The "add a reaction" button.

    ::deep because the class is handed to MudMenu, a component: the class reaches its
    element, the scope attribute does not, so the plain selector matched nothing and the
    button was drawn on every row all the time. The rule that reveals it lives in
    MessageRow.razor.css, because the hover it reacts to belongs to the row and a
    selector can only carry one component's scope.
*/
[b-i4h6fnvfsw] .reaction-add {
    opacity: 0;
    transition: opacity 120ms ease-in-out;
}

.emoji-picker[b-i4h6fnvfsw] {
    padding: 0.4rem;
    width: 15rem;
}

.emoji-grid[b-i4h6fnvfsw] {
    display: grid;
    grid-template-columns: repeat(8, 1fr);
    gap: 0.1rem;
}

.emoji-grid .emoji[b-i4h6fnvfsw] {
    background: none;
    border: none;
    border-radius: var(--ffp-radius);
    font-size: 1.1rem;
    cursor: pointer;
    padding: 0.15rem;
}

.emoji-grid .emoji:hover[b-i4h6fnvfsw] {
    background: var(--mud-palette-action-default-hover);
}

/* On a touch screen there is no hover to reveal anything, so the button is simply
   there - and big enough to hit. */
@media (hover: none) {
    [b-i4h6fnvfsw] .reaction-add {
        opacity: 1;
    }
}
/* /Features/Messages/ThreadPanel.razor.rz.scp.css */
/*
    The thread, beside the channel.

    Nebel against the white canvas, which is the handbook's own example of a nested
    surface ("thread sidebars"), and a Linie edge instead of a shadow. The panel is a
    layer above the timeline, not a floating card.
*/
.thread-panel[b-01j41c2r4f] {
    width: 26rem;
    max-width: 100%;
    border-left: 1px solid var(--ffp-border);
    display: flex;
    flex-direction: column;
    min-height: 0;
    background: var(--ffp-surface);
}

.thread-header[b-01j41c2r4f] {
    display: flex;
    align-items: center;
    padding: 0.5rem 0.5rem 0.5rem 1.5rem;
    border-bottom: 1px solid var(--ffp-border);
}

.thread-body[b-01j41c2r4f] {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

/* The timeline inside a thread stands on the panel's Nebel, not on the white canvas, so
   the bubbles keep their contrast the other way round: an incoming message is the white
   one here. ::deep because .message-list belongs to MessageList. */
.thread-body[b-01j41c2r4f]  .message-list {
    background: transparent;
}

/* On a narrow screen the thread takes the whole width: two columns of chat at 500 px
   are two unreadable columns (Planning/06-Clients.md). */
@media (max-width: 900px) {
    .thread-panel[b-01j41c2r4f] {
        width: 100%;
        border-left: none;
    }
}
/* /Features/Messages/TimeChoiceDialog.razor.rz.scp.css */
.time-presets[b-rezuds6mji] {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    min-width: 16rem;
}

.time-custom[b-rezuds6mji] {
    display: flex;
    gap: 1rem;
    flex-wrap: wrap;
}
/* /Features/Messages/TypingIndicator.razor.rz.scp.css */
/*
    The "somebody is typing" line, between the timeline and the composer.

    It keeps its height whether or not there is anything to say, so the message list does
    not jump every time a colleague starts a sentence. The gutter matches the rows above
    it, so the line starts where the avatars do rather than floating in the margin.

    A pip marks it the way the reference screens do, in Amber: a status indicator is one
    of the few places the handbook lets the accent be used, and a circle is one of the
    few shapes it lets be round. Drawn with an ::before rather than a character so it
    disappears with the text instead of leaving a lone dot behind.
*/

.typing[b-r8qabfljrt] {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    min-height: 1.25rem;
    padding: 0 1.5rem;
    font-size: 0.6875rem;
    color: var(--ffp-muted);
}

.typing.on[b-r8qabfljrt]::before {
    content: "";
    width: 0.4rem;
    height: 0.4rem;
    border-radius: 50%;
    background: var(--ffp-amber);
    flex: none;
}

@media (max-width: 600px) {
    .typing[b-r8qabfljrt] {
        padding: 0 var(--ffp-space-md);
    }
}
/* /Features/People/UserAvatar.razor.rz.scp.css */
/* The tile. Initials and picture share one cell of a grid, so the picture covers the
   letters exactly and without any positioning. */
.ffp-avatar[b-j9arjwh71l] {
    flex: none;
    display: inline-grid;
    place-items: center;
    overflow: hidden;
    border-radius: var(--ffp-radius);
    background: var(--ffp-petrol);
    color: var(--ffp-weiss);
    font-weight: 700;
    letter-spacing: 0.02em;
    line-height: 1;
    user-select: none;
    vertical-align: middle;
}

.ffp-avatar-initials[b-j9arjwh71l] {
    grid-area: 1 / 1;
}

.ffp-avatar-small[b-j9arjwh71l] {
    width: 1.75rem;
    height: 1.75rem;
    font-size: 0.6875rem;
}

.ffp-avatar-medium[b-j9arjwh71l] {
    width: 2.25rem;
    height: 2.25rem;
    font-size: 0.75rem;
}

.ffp-avatar-large[b-j9arjwh71l] {
    width: 3.5rem;
    height: 3.5rem;
    font-size: 1.25rem;
}

/* ::deep, because AuthedImage draws the img and a child component's elements never
   carry this component's scope attribute.

   The tile's own colour goes behind the picture: a logo with a transparent background
   would otherwise show the initials through it. */
.ffp-avatar[b-j9arjwh71l]  img {
    grid-area: 1 / 1;
    width: 100%;
    height: 100%;
    object-fit: cover;
    background: inherit;
}

/* AuthedImage holds a place while the bytes are on their way. Here the initials are
   that place already - and MessageRow sizes the placeholder for an attachment, which
   inside a tile would be a 12rem box. */
.ffp-avatar[b-j9arjwh71l]  .ffp-image-pending {
    display: none;
}

/*
    Eight tiles, one per person colour.

    A ramp mixed from the three Petrols the palette already has, so changing Petrol moves
    every tile with it. It runs from Petrol Schrift to Petrol Hell and no further: Petrol
    Hell is the lightest fill that still carries white type (5.0:1), so every tile clears
    AA by construction.
*/
.ffp-avatar.colour-0[b-j9arjwh71l] { background: var(--ffp-petrol-schrift); }
.ffp-avatar.colour-1[b-j9arjwh71l] { background: var(--ffp-petrol-tief); }
.ffp-avatar.colour-2[b-j9arjwh71l] { background: color-mix(in srgb, var(--ffp-petrol-tief) 55%, var(--ffp-petrol)); }
.ffp-avatar.colour-3[b-j9arjwh71l] { background: var(--ffp-petrol); }
.ffp-avatar.colour-4[b-j9arjwh71l] { background: color-mix(in srgb, var(--ffp-petrol) 70%, var(--ffp-petrol-hell)); }
.ffp-avatar.colour-5[b-j9arjwh71l] { background: color-mix(in srgb, var(--ffp-petrol) 40%, var(--ffp-petrol-hell)); }
.ffp-avatar.colour-6[b-j9arjwh71l] { background: var(--ffp-petrol-hell); }
.ffp-avatar.colour-7[b-j9arjwh71l] { background: color-mix(in srgb, var(--ffp-petrol-schrift) 60%, var(--ffp-petrol-hell)); }

/* The timeline's column narrows on a phone, and the tile with it. */
@media (max-width: 600px) {
    .ffp-avatar-medium[b-j9arjwh71l] {
        width: 2rem;
        height: 2rem;
    }
}
/* /Features/Search/FileExcerpt.razor.rz.scp.css */
/* Two lines of a document under its name. Smaller than a message body and in ink,
   because it is the reason the row is there; the label in front is the muted
   metadata voice of the line above it. */

.excerpt[b-av9if4xjlb] {
    margin: var(--ffp-space-xs) 0 0;
    font-size: 0.875rem;
    line-height: 1.4;
    color: var(--ffp-fg);
    overflow-wrap: anywhere;
}

.excerpt-label[b-av9if4xjlb] {
    margin-right: var(--ffp-space-xs);
    font-size: 0.75rem;
    color: var(--ffp-muted);
}

/* Amber with Tinte on it: the handbook's signal colour and the one ink that holds on
   it in both schemes (see the banner in app.css). */
mark[b-av9if4xjlb] {
    padding: 0 2px;
    border-radius: 2px;
    background-color: var(--ffp-amber);
    color: var(--ffp-tinte);
}
/* /Features/Search/QuickSwitcher.razor.rz.scp.css */
/* The Ctrl/Cmd+K box. It genuinely floats over the page, so it is the one place where
   the system's single shadow applies - and MudBlazor already draws it from the theme.
   What the rows need is the 4px geometry and the 4/8 rhythm.

   The dialog sits on Nebel (the theme's surface). The muted ink clears 4.5:1 there and
   nowhere darker - 4.55:1 is all it has - so on a tinted row the note switches to the
   full text colour rather than falling under the line (Theme/FlurfunkProTheme.cs,
   GrauTief). */

.quick[b-cinva612w6] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-xs);
}

/* The box: an outlined field drawn by hand, because the input is a plain one (see the
   component for why). Same border, radius and focus ring as the MudTextField it
   replaces. */
.quick-box[b-cinva612w6] {
    display: flex;
    align-items: center;
    gap: var(--ffp-space-sm);
    min-height: 40px;
    padding: 0 var(--ffp-space-sm);
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius);
    background: var(--ffp-bg);
}

.quick-box:focus-within[b-cinva612w6] {
    border-color: var(--ffp-accent);
    box-shadow: 0 0 0 0.5px var(--ffp-accent);
}

.quick[b-cinva612w6]  .quick-box-icon {
    color: var(--ffp-muted);
}

.quick-input[b-cinva612w6] {
    flex: 1;
    min-width: 0;
    min-height: 38px;
    padding: 0;
    border: 0;
    outline: none;
    background: transparent;
    color: var(--ffp-fg);
    font: inherit;
}

/* The placeholder is text too, and it sits on the white of the field, where the muted
   ink has 4.95:1. */
.quick-input[b-cinva612w6]::placeholder {
    color: var(--ffp-muted);
    opacity: 1;
}

.quick-empty[b-cinva612w6] {
    margin: var(--ffp-space-sm) var(--ffp-space-sm) 0;
    color: var(--ffp-muted);
}

/* At most about ten rows' worth on screen; the arrows scroll the rest into view. */
.quick-list[b-cinva612w6] {
    max-height: min(60vh, 440px);
    overflow-y: auto;
    padding-top: var(--ffp-space-xs);
}

/* A group heading is a small letterspaced label, the way the reference screens set
   "PROJEKT- & BAUSTELLENKANÄLE" above a list. */
.quick-group[b-cinva612w6] {
    margin-top: var(--ffp-space-sm);
    padding: var(--ffp-space-xs) var(--ffp-space-sm);
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ffp-muted);
}

/* 40px rows: the height of a control, which is what makes a list of them read as one
   column rather than as text with icons in it. */
.quick-row[b-cinva612w6] {
    display: flex;
    align-items: center;
    gap: var(--ffp-space-sm);
    min-height: 40px;
    padding: 0 var(--ffp-space-sm) 0 0;
    border-radius: var(--ffp-radius);
    color: var(--ffp-fg);
    cursor: pointer;
}

/* The row Enter opens: a tint and a bar at its start, not the tint alone. The bar still
   reads where a tint is too faint to tell apart from the surface - and it is the accent
   at full strength, which is 3:1 and more against the dialog in both schemes. */
.quick-bar[b-cinva612w6] {
    flex: none;
    align-self: stretch;
    width: 3px;
    margin: var(--ffp-space-sm) 0;
    border-radius: 0 2px 2px 0;
    background: transparent;
}

.quick-row:hover[b-cinva612w6],
.quick-row.is-active[b-cinva612w6] {
    background-color: color-mix(in srgb, var(--ffp-accent) 10%, transparent);
}

.quick-row.is-active .quick-bar[b-cinva612w6] {
    background: var(--ffp-accent);
}

.quick-row.is-unread .quick-label[b-cinva612w6] {
    font-weight: 700;
}

.quick[b-cinva612w6]  .quick-icon {
    flex: none;
    color: var(--ffp-muted);
}

.quick-label[b-cinva612w6] {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The trailing note on a row - "nicht beigetreten", a job title - is metadata, so it is
   muted and does not compete with the name in front of it. */
.quick-note[b-cinva612w6] {
    flex: none;
    padding-left: var(--ffp-space-sm);
    font-size: 0.8125rem;
    color: var(--ffp-muted);
}

.quick-row:hover .quick-note[b-cinva612w6],
.quick-row.is-active .quick-note[b-cinva612w6],
.quick-row:hover[b-cinva612w6]  .quick-icon,
.quick-row.is-active[b-cinva612w6]  .quick-icon {
    color: var(--ffp-fg);
}

.quick-more[b-cinva612w6] {
    display: flex;
    justify-content: flex-end;
    margin-top: var(--ffp-space-sm);
}

.quick-foot[b-cinva612w6] {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: var(--ffp-space-md);
    margin-top: var(--ffp-space-sm);
    font-size: 0.75rem;
    color: var(--ffp-muted);
}

.quick-count[b-cinva612w6] {
    min-height: 1em;
}

.quick-hint[b-cinva612w6] {
    margin-left: auto;
}

.quick[b-cinva612w6]  .mud-button {
    min-height: 40px;
    border-radius: var(--ffp-radius);
    text-transform: none;
    letter-spacing: 0;
    font-weight: 600;
    box-shadow: none;
}

.quick[b-cinva612w6]  .mud-button-outlined {
    border: 1px solid var(--ffp-border);
    background-color: var(--ffp-bg);
    color: var(--ffp-fg);
}

.quick[b-cinva612w6]  .mud-button-outlined:hover {
    background-color: var(--ffp-surface);
}

@media (max-width: 767px) {
    .quick-row[b-cinva612w6],
    .quick-box[b-cinva612w6] {
        min-height: 48px;
    }

    .quick[b-cinva612w6]  .mud-button {
        min-height: 48px;
    }

    /* A phone has no arrow keys to hint at. */
    .quick-hint[b-cinva612w6] {
        display: none;
    }
}
/* /Layout/AuthLayout.razor.rz.scp.css */
.ffp-auth-page[b-tekzuj3785] {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100dvh;
    padding: 1.5rem;
    background: var(--ffp-surface);
}

/* The 8px container radius, which is the one DESIGN.md gives modals and cards - the 4px
   base radius belongs to the controls inside it. */
[b-tekzuj3785] .ffp-auth-card {
    width: 100%;
    max-width: 26rem;
    padding: 2rem;
    background: var(--ffp-bg);
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius-container);
}
/* /Layout/MainLayout.razor.rz.scp.css */
/* The splash that covers the first second while the refresh cookie is redeemed. */
.ffp-splash[b-ppxqnw44zt] {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 100dvh;
}

/* Everything below reaches through .ffp-shell, the one plain element in MainLayout.razor.
   Without it these rules compile to `[b-…] .x` with nothing in the tree carrying the
   attribute, which is exactly how they managed to do nothing at all until now. */

/* Nothing here sets the bar's height, the drawer's offset or the main content's inset.
   MudBlazor derives all three from one value, and the only way to keep them agreeing is
   to let it - see the note in MainLayout.razor above the app bar. */

/* The message list scrolls, the shell does not: a chat window that scrolls as a whole
   loses the composer off the bottom of the screen.
 *
 * overflow-y is auto rather than hidden, because this box is also where the admin, the
 * settings and the search screens land, and those are as long as their content. A page
 * that wants to own its scrolling - the channel does - sets flex: 1 and min-height: 0 on
 * its own root and fits exactly, so no scrollbar appears there.
 *
 * The height is 100dvh and not 100dvh minus the bar: MudMainContent clears the app bar
 * with its own padding-top, and every box here is border-box. Subtracting
 * --mud-appbar-height would in fact be wrong - the variable says 56px while a dense app
 * bar, which this one is, measures 42. */
.ffp-shell[b-ppxqnw44zt]  .ffp-main {
    height: 100dvh;
    display: flex;
    flex-direction: column;
    overflow-y: auto;
}

/* The block that holds the toggle and the product logo.
 *
 * As wide as the rail underneath it, so whatever comes next in the bar starts on the
 * same vertical line as the content does. The toggle's own padding is the gutter, and the
 * toolbar's is cleared, so the alignment is one number - the rail's width - rather than
 * a sum of MudBlazor's toolbar padding and the negative margin Edge.Start applies.
 *
 * Only where the rail is actually beside it. Below Md the drawer is collapsed, there is
 * no column to line up with, and 275px of lead on a 390px screen would be most of the
 * bar. The number is MudBlazor's Md, which is the same breakpoint the Responsive drawer
 * folds at - they have to agree, so they are written from the same value.
 *
 * It does not follow the rail being folded away by hand at desktop width. There is
 * nothing to align to then, and a bar that reflows every time somebody hides the rail
 * is worse than one that stays put. */
.ffp-appbar-lead[b-ppxqnw44zt] {
    display: flex;
    align-items: center;
    /* 4px, and no padding in front of the toggle. The button's own 12px pads it on
       both sides, so its icon lands at x = 12 - on the line of the workspace tile and
       the section headings below - and the logo keeps 16px of air from the icon. The
       logo needs the room: at 32 px it is 199 px wide, and the block is the rail's 275. */
    gap: var(--ffp-space-xs);
    box-sizing: border-box;
    flex: none;
    min-width: 0;
}

@media (min-width: 960px) {
    .ffp-appbar-lead[b-ppxqnw44zt] {
        width: var(--ffp-sidebar-width);
    }
}

.ffp-shell[b-ppxqnw44zt]  .mud-appbar .mud-toolbar {
    padding-left: 0;
}

/* The quick search in the app bar, where the desktop reference screen puts it.
 *
 * A button dressed as a field. It opens the same dialog Ctrl+K does, which is why the
 * shortcut is printed inside it - the hint is the truth rather than a decoration.
 *
 * The ground is the bar darkened rather than a token, because the bar is Petrol in one
 * scheme and Petrol Tief in the other and a fixed colour would only suit one of them.
 * Measured on both: Dunst reads 7.0:1 on the light scheme's field and 8.9:1 on the
 * dark one.
 *
 * No ::deep. This button is markup MainLayout writes itself; the ::deep rules above
 * exist for MudBlazor's. */
.ffp-quick-search[b-ppxqnw44zt] {
    display: flex;
    align-items: center;
    gap: var(--ffp-space-sm);
    /* Shrinks before it grows: the bar also has to hold the logo and the user
       menu, and a search field that pushes them off is a search field nobody asked to
       be that important. */
    flex: 0 1 26rem;
    min-width: 10rem;
    padding: 5px var(--ffp-space-sm);
    border: 1px solid color-mix(in srgb, var(--ffp-dunst) 32%, transparent);
    border-radius: var(--ffp-radius);
    background: color-mix(in srgb, #000 18%, transparent);
    color: var(--ffp-dunst);
    font: inherit;
    font-size: 0.875rem;
    text-align: left;
    cursor: pointer;
}

.ffp-quick-search:hover[b-ppxqnw44zt] {
    border-color: color-mix(in srgb, var(--ffp-dunst) 55%, transparent);
    color: var(--ffp-weiss);
}

/* An outline and not a border change: the border is already doing the hover, and a
   focus ring that only thickens an existing line is a focus ring somebody misses. */
.ffp-quick-search:focus-visible[b-ppxqnw44zt] {
    outline: 2px solid var(--ffp-weiss);
    outline-offset: 2px;
}

.ffp-quick-search-label[b-ppxqnw44zt] {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The shortcut, quieter than the label it sits beside - it is an aside, not the
   invitation. Hidden where the field is narrow enough that it would crowd the text it
   is explaining. */
.ffp-quick-search-key[b-ppxqnw44zt] {
    flex: none;
    padding: 1px var(--ffp-space-xs);
    border: 1px solid color-mix(in srgb, var(--ffp-dunst) 32%, transparent);
    border-radius: 3px;
    font-family: inherit;
    font-size: 0.6875rem;
    line-height: 1.4;
    color: var(--ffp-dunst);
}

@media (max-width: 1100px) {
    .ffp-quick-search-key[b-ppxqnw44zt] {
        display: none;
    }
}

/* The product's logo at the head of the bar.
 *
 * A block link with no underline and no padding of its own, so its hit area is the logo
 * and the logo is exactly its 32 px. The focus ring is white for the same reason the
 * quick search's is: the accent colour disappears into Petrol. */
.ffp-appbar-brand[b-ppxqnw44zt] {
    display: flex;
    align-items: center;
    flex: none;
    border-radius: var(--ffp-radius);
    text-decoration: none;
}

.ffp-appbar-brand:focus-visible[b-ppxqnw44zt] {
    outline: 2px solid var(--ffp-weiss);
    outline-offset: 4px;
}

/* ::deep, because ProductLogo writes the img and it carries that component's scope.
   A block, so no line box is added under it and it sits centred in the bar. */
.ffp-appbar-brand[b-ppxqnw44zt]  .ffp-product-logo {
    display: block;
}
/* /Layout/ShortcutHelpDialog.razor.rz.scp.css */
/* The shortcut overview. Two short lists, a heading each, the keys in a fixed column so
   the descriptions line up. */

.shortcut-help[b-q4mfy76j7p] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-sm);
    min-width: min(28rem, 100%);
}

/* The letterspaced group label the quick switcher uses too, in the muted text colour,
   which clears 4.5:1 on the dialog surface in both schemes. */
.shortcut-help-heading[b-q4mfy76j7p] {
    margin: var(--ffp-space-sm) 0 0;
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ffp-muted);
}

.shortcut-help-list[b-q4mfy76j7p] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-xs);
    margin: 0;
}

.shortcut-help-line[b-q4mfy76j7p] {
    display: flex;
    align-items: center;
    gap: var(--ffp-space-md);
    min-height: 32px;
}

.shortcut-help-line dt[b-q4mfy76j7p] {
    flex: 0 0 11rem;
    margin: 0;
}

.shortcut-help-line dd[b-q4mfy76j7p] {
    margin: 0;
    color: var(--ffp-fg);
}

.shortcut-help-keys[b-q4mfy76j7p] {
    display: inline-flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ffp-space-xs);
}

/* A key drawn as a key: an outline carries the shape, so the label keeps the full text
   colour rather than a tint that would fall under the contrast line. */
.shortcut-help-key[b-q4mfy76j7p] {
    padding: 1px 6px;
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius);
    background: var(--ffp-surface);
    color: var(--ffp-fg);
    font-family: inherit;
    font-size: 0.8125rem;
    line-height: 1.5;
}

.shortcut-help-or[b-q4mfy76j7p] {
    color: var(--ffp-muted);
}

@media (max-width: 599px) {
    .shortcut-help-line[b-q4mfy76j7p] {
        flex-direction: column;
        align-items: flex-start;
        gap: 0;
    }

    .shortcut-help-line dt[b-q4mfy76j7p] {
        flex-basis: auto;
    }
}
/* /Layout/Sidebar.razor.rz.scp.css */
/* The rail, after the reference screen
   (Planning/design/desktop_slack_workspace_channel_bauprojekte/screen.png).
 *
 * Everything here is written against MudBlazor's own nav markup with ::deep, because the
 * rows are drawn by ChannelNavigation and ChannelRow, which belong to another slice of
 * this phase. Reaching into them from here keeps the rail's look in one file instead of
 * spreading four colours over three components.
 *
 * Colours come from the tokens in wwwroot/css/app.css, which mirror
 * Theme/FlurfunkProTheme.cs. The rail is Petrol Tief in both schemes - that is the one
 * surface the theme does not flip - so the literal tokens are used here rather than the
 * MudBlazor palette variables that follow the light/dark choice. */

.ffp-sidebar[b-8wnqcu325f] {
    display: flex;
    flex-direction: column;
    min-height: 100%;
}

/* --- the head ------------------------------------------------------------- */

/* The workspace, at the top of the rail as both reference screens draw it. 12px in from
   the edge - the list's 4px gutter plus a row's 8px - so the tile stands on the same
   line as KANÄLE and the rest of the section headings. The same hairline as the foot.
   The toggle stays in the app bar: it must not fold away with the rail. */
.ffp-sidebar-head[b-8wnqcu325f] {
    padding: var(--ffp-space-md) var(--ffp-space-sm) var(--ffp-space-sm) calc(var(--ffp-space-xs) + var(--ffp-space-sm));
    border-bottom: 1px solid color-mix(in srgb, var(--ffp-blaugrau-hell) 28%, transparent);
}

/* --- the list ------------------------------------------------------------- */

/* 4px of gutter either side, so an active row is a band with a margin rather than a
   stripe that touches both edges. The screen does the same with px-space-xs. */
.ffp-sidebar[b-8wnqcu325f]  .ffp-sidebar-menu {
    padding: var(--ffp-space-sm) var(--ffp-space-xs);
}

/* Every row in the rail, shortcuts and channels alike. Colour is inherited from the
   drawer by default, which is Dunst; naming it here means a row keeps it when MudBlazor
   decides the row is active and paints it Petrol - which on Petrol Tief is 1.8:1 and the
   pairing the handbook prohibits outright. */
.ffp-sidebar[b-8wnqcu325f]  .mud-nav-link {
    align-items: center;
    min-height: 2rem;
    border-radius: var(--ffp-radius);
    color: var(--ffp-dunst);
    font-size: 0.8125rem;
    line-height: 1.38;
    /* No 250ms slide on the padding; only the fill changes. */
    transition: background-color 120ms ease-in-out, color 120ms ease-in-out;
}

/* MudBlazor indents a row inside a group by 36px and says so with four class names, so
   the override has to be at least as specific. 8px is the screen's own inset, and the
   icon in front of the name carries the hierarchy instead of the whitespace. */
.ffp-sidebar[b-8wnqcu325f]  .mud-nav-group .mud-navmenu .mud-nav-item .mud-nav-link,
.ffp-sidebar[b-8wnqcu325f]  .mud-nav-group .mud-navmenu .mud-nav-link {
    padding: 0.3125rem var(--ffp-space-sm);
    padding-inline-start: var(--ffp-space-sm);
    padding-inline-end: var(--ffp-space-sm);
}

.ffp-sidebar[b-8wnqcu325f]  .mud-navmenu > .mud-nav-link {
    padding: 0.3125rem var(--ffp-space-sm);
}

/* The name is the loudest thing in the row, so the gap in front of it is the screen's
   8px rather than MudBlazor's 12px. */
.ffp-sidebar[b-8wnqcu325f]  .mud-nav-link .mud-nav-link-text {
    margin-inline-start: var(--ffp-space-sm);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The hash, the lock and the person glyph: quieter than the name they label. */
.ffp-sidebar[b-8wnqcu325f]  .mud-nav-link .mud-nav-link-icon {
    color: var(--ffp-blaugrau-hell);
    font-size: 1rem;
    width: 1rem;
    height: 1rem;
}

/* Hover and keyboard focus are the same state here, as they are in the screen: a Petrol
   fill under white text. MudBlazor's own hover is a translucent wash of the text colour,
   which on a dark rail comes out as fog. */
.ffp-sidebar[b-8wnqcu325f]  .mud-navmenu .mud-nav-link:hover:not(.mud-nav-link-disabled),
.ffp-sidebar[b-8wnqcu325f]  .mud-navmenu .mud-nav-link:focus-visible:not(.mud-nav-link-disabled) {
    background-color: var(--ffp-petrol);
    color: var(--ffp-weiss);
}

/* The active channel. White on Petrol is 9.0:1, and the weight carries the state for a
   reader who cannot see the fill (N11). MudBlazor paints the active row in the primary
   colour on a wash of it, which is the prohibited Petrol-on-Petrol pairing.
 *
 * The four selectors, and the doubled :not, are not decoration. MudBlazor writes its own
 * hover, focus and pressed states for an active row as
 * `.mud-navmenu.mud-navmenu-default .mud-nav-link.active:not(…):focus-visible:not(…)` -
 * seven class-weights - and the plain three-part override loses to them. So a clicked
 * channel turned Petrol, then went grey again the moment it had focus, which is the
 * state a channel is in for as long as somebody is reading it. */
.ffp-sidebar[b-8wnqcu325f]  .mud-navmenu .mud-nav-link.active:not(.mud-nav-link-disabled),
.ffp-sidebar[b-8wnqcu325f]  .mud-navmenu .mud-nav-link.active:not(.mud-nav-link-disabled):hover:not(.mud-nav-link-disabled),
.ffp-sidebar[b-8wnqcu325f]  .mud-navmenu .mud-nav-link.active:not(.mud-nav-link-disabled):focus-visible:not(.mud-nav-link-disabled),
.ffp-sidebar[b-8wnqcu325f]  .mud-navmenu .mud-nav-link.active:not(.mud-nav-link-disabled):active:not(.mud-nav-link-disabled) {
    background-color: var(--ffp-petrol);
    color: var(--ffp-weiss);
    font-weight: 700 !important;
}

.ffp-sidebar[b-8wnqcu325f]  .mud-navmenu .mud-nav-link.active:not(.mud-nav-link-disabled) .mud-nav-link-icon {
    color: var(--ffp-weiss);
}

/* MudBlazor's "bordered" nav menu draws a 2px edge on the active row. The fill already
   says which row it is, and a second marker on the far side of the text is the kind of
   decoration this system does without. */
.ffp-sidebar[b-8wnqcu325f]  .mud-nav-link.active {
    border-inline-end: 0;
}

/* --- section headings ----------------------------------------------------- */

/* KANÄLE, DIREKTNACHRICHTEN and a person's own sections: 12px, spaced out, and quiet
   enough that the channel names below them are what the eye lands on. The row is still
   the button that folds the group, so it keeps its hit area and its focus ring - it just
   loses the fill, which on a heading would look like a selected channel. */
.ffp-sidebar[b-8wnqcu325f]  .mud-nav-group > .mud-nav-link {
    padding: var(--ffp-space-sm) var(--ffp-space-sm) var(--ffp-space-xs);
    color: var(--ffp-blaugrau-hell);
    font-size: 0.75rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.ffp-sidebar[b-8wnqcu325f]  .mud-nav-group > .mud-nav-link > .mud-nav-link-text {
    margin-inline-start: 0;
    font-weight: 700;
}

.ffp-sidebar[b-8wnqcu325f]  .mud-nav-group > .mud-nav-link:hover,
.ffp-sidebar[b-8wnqcu325f]  .mud-nav-group > .mud-nav-link:focus-visible {
    background-color: transparent;
    color: var(--ffp-weiss);
}

.ffp-sidebar[b-8wnqcu325f]  .mud-nav-group > .mud-nav-link .mud-nav-link-expand-icon {
    color: var(--ffp-blaugrau-hell);
}

/* The first group starts flush against the shortcuts above it; every later one gets air,
   which is the separation DESIGN.md allows instead of a divider line. */
.ffp-sidebar[b-8wnqcu325f]  .mud-nav-group + .mud-nav-group {
    margin-top: var(--ffp-space-sm);
}

/* --- "add a channel", "new section" --------------------------------------- */

/* Commands, not destinations: dimmer than a channel name, and they never go active. */
.ffp-sidebar[b-8wnqcu325f]  .ffp-nav-add {
    color: var(--ffp-blaugrau-hell);
}

/* --- unread ---------------------------------------------------------------- */

/* The unread dot is Amber, the one accent the handbook allows as a pip, and it sits at
   the end of the row. ChannelRow paints it in currentColor so that it follows the text;
   on the rail that would make it Dunst, and an unread marker that matches the name it
   marks is not a marker. */
.ffp-sidebar[b-8wnqcu325f]  .ffp-channel-dot {
    background: var(--ffp-amber);
}

/* --- the focus ring on a dark rail ----------------------------------------- */

/* app.css draws every focus ring in the accent colour, which is the right answer on the
   white canvas and the wrong one here: Petrol on Petrol Tief is 1.8:1 and the ring
   disappears into the rail it is meant to stand out from (WCAG 2.4.7, 1.4.11). White is
   13.6:1 against the rail and 9.0:1 against the Petrol fill a focused row carries, so it
   is visible in both of the states a row can be in. */
.ffp-sidebar :focus-visible[b-8wnqcu325f],
.ffp-sidebar[b-8wnqcu325f]  :focus-visible {
    outline-color: var(--ffp-weiss);
}

/* --- the foot ------------------------------------------------------------- */

/* Where the reference screen puts "Betriebsbereit / Mandant 104 • Online". Ours names
   the build instead - the product names itself in the app bar; the connection has its own indicator in the app
   bar and does not need a second home.
 *
 * margin-top: auto is what pins it down: .ffp-sidebar is a column with min-height 100%,
 * so the rest of the free space collects above this rather than below it. In a rail with
 * more channels than fit, it simply follows the list - it is a footer, not a bar. */
.ffp-sidebar-footer[b-8wnqcu325f] {
    margin-top: auto;
    display: flex;
    padding: var(--ffp-space-sm) var(--ffp-space-md) var(--ffp-space-md);
    /* Same hairline as under the head, and for the same reason: Blaugrau Hell at full
       strength reads as a signal on this surface. */
    border-top: 1px solid color-mix(in srgb, var(--ffp-blaugrau-hell) 28%, transparent);
}

/* Blaugrau Hell, the pairing the workspace's second line already uses on this surface
   and the one the WCAG sweep has passed on it. Tabular figures so a version does not
   shift its own width between builds. */
.ffp-product-version[b-8wnqcu325f] {
    font-size: 0.6875rem;
    line-height: 1.27;
    font-variant-numeric: tabular-nums;
    color: var(--ffp-blaugrau-hell);
}
/* /Layout/UserMenu.razor.rz.scp.css */
/* The badge in the app bar. */
.ffp-user-menu-activator[b-8cblyh35dg] {
    display: inline-flex;
}

/* A Petrol tile on a Petrol bar would be a tile nobody sees, and a picture with a dark
   edge would lose its corners into the bar. The ring is the quick search field's
   border, so the two things on the bar that can be clicked are drawn alike. */
.ffp-user-menu-activator[b-8cblyh35dg]  .ffp-avatar {
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--ffp-dunst) 45%, transparent);
}
/* /Layout/WorkspaceHead.razor.rz.scp.css */
/* The workspace block at the top of the rail.
 *
 * On Petrol Tief in both schemes. The second line stays Dunst rather than Blaugrau
 * Hell: it spent a while in the app bar, where Blaugrau Hell failed AA on the light
 * scheme's Petrol, and Dunst holds on every surface this block has lived on.
 */

.ffp-workspace-head[b-txmce8lgvf] {
    display: flex;
    align-items: center;
    gap: var(--ffp-space-sm);
    /* The rail's width is the limit; a long name ellipsises rather than widening it. */
    min-width: 0;
}

/* The workspace tile: square with the 4px control radius. A circle here would be the
   one shape the handbook keeps for badges and status pips. */
.ffp-workspace-mark[b-txmce8lgvf] {
    flex: none;
    /* Letters and picture share one grid cell, so the picture covers the letters
       exactly - the same construction as UserAvatar. */
    display: inline-grid;
    place-items: center;
    overflow: hidden;
    width: 1.75rem;
    height: 1.75rem;
    border-radius: var(--ffp-radius);
    background: var(--ffp-petrol-hell);
    color: var(--ffp-weiss);
    font-family: var(--ffp-font-serif);
    font-weight: 700;
    font-size: 0.75rem;
    /* The two letters are set solid so they sit optically centred in the square. */
    line-height: 1;
}

.ffp-workspace-initials[b-txmce8lgvf] {
    grid-area: 1 / 1;
}

/* ::deep, because AuthedImage draws the img. The tile's colour stays behind it, so a
   logo with a transparent background does not show the letters through. */
.ffp-workspace-mark[b-txmce8lgvf]  img {
    grid-area: 1 / 1;
    width: 100%;
    height: 100%;
    object-fit: cover;
    background: inherit;
}

/* The letters already hold the place while the bytes are on their way. */
.ffp-workspace-mark[b-txmce8lgvf]  .ffp-image-pending {
    display: none;
}

.ffp-workspace-names[b-txmce8lgvf] {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

/* Domine, because this is a name rather than a label - DESIGN.md gives the serif every
   headline and nothing that is read as a paragraph. */
.ffp-workspace-name[b-txmce8lgvf] {
    font-family: var(--ffp-font-serif);
    font-weight: 700;
    font-size: 0.9375rem;
    line-height: 1.27;
    color: var(--ffp-weiss);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ffp-workspace-person[b-txmce8lgvf] {
    font-size: 0.6875rem;
    line-height: 1.27;
    color: var(--ffp-dunst);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* /Pages/Admin.razor.rz.scp.css */
/* The administration screen: the densest surface in the client, and the one the
   4/8 rhythm has to be visible on. Two tabs, a form, a table of people.

   Every length below is a multiple of 4 and comes from the space tokens in
   wwwroot/css/app.css, and no colour below is a hex value - the tokens there
   mirror Theme/FlurfunkProTheme.cs, which is where a colour is decided. */

.admin[b-eikkp73cry] {
    display: flex;
    flex-direction: column;
    min-height: 0;
    flex: 1;
}

/* The page title sits on the canvas above the tab strip, so it needs the
   section margin the template asks for on desktop (2rem) rather than the
   16px it had. */
.admin-head[b-eikkp73cry] {
    padding: var(--ffp-space-lg) var(--ffp-space-xl) var(--ffp-space-md);
}

/* ::deep, and this is the trap the whole phase kept falling into: Blazor stamps
   its scope attribute on the markup a component writes itself and on nothing
   else, so a class handed to a MudBlazor component - MudTabs here - is never
   matched by a bare selector in this file. The rule below had been dead since
   the screen was built, which is why the tab body scrolled the page instead of
   itself. Every selector in this file that names a class on a component is
   written the same way. */
.admin[b-eikkp73cry]  .admin-tabs {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
}

.admin-panel[b-eikkp73cry] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-md);
    padding: var(--ffp-space-lg) var(--ffp-space-xl) var(--ffp-space-xl);
    max-width: 60rem;
}

/* A card is a Nebel pane with a 1px Linie edge - never a shadow. DESIGN.md
   ("Elevation & Depth") reserves the one shadow in the system for things that
   float over the page, and an in-page panel does not float. */
.admin-card[b-eikkp73cry] {
    background-color: var(--ffp-surface);
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius-container);
    padding: var(--ffp-space-md);
}

/* The invite row: a field, a role, a button, all on the same 40px line. The
   gap is 8px rather than 12px because everything else on this screen steps in
   8s, and a single odd gap is what makes a dense form look hand-placed. */
.admin-invite[b-eikkp73cry] {
    display: flex;
    gap: var(--ffp-space-sm);
    align-items: flex-start;
    flex-wrap: wrap;
}

/* The button did not sit on that line, and here is the arithmetic. A dense outlined
   MudBlazor field that has a label carries `margin: 8px 0 4px` of its own, so its
   visible box begins 8px below the top of the row. A button carries none, and with
   flex-start it therefore stood 8px above the two fields it belongs to - which is what
   it looked like: a button floating over the form rather than part of it.

   Matching the margins lines the boxes up, and `stretch` makes the button exactly as
   tall as the fields instead of its own 36px. Written as margins rather than as a fixed
   height so it keeps holding if MudBlazor changes what a dense input measures.

   ::deep, because Class on a MudBlazor component lands on that component's own root
   element, which never carries this page's scope attribute. */
.admin[b-eikkp73cry]  .admin-invite-submit {
    margin: 8px 0 4px;
    align-self: stretch;
}

.admin[b-eikkp73cry]  .admin-invite-email {
    flex: 1 1 18rem;
    min-width: 0;
}

.admin[b-eikkp73cry]  .admin-invite-role {
    flex: 0 0 12rem;
}

/* The workspace tab is a column of single settings, each with its hint under
   it. 24px between settings, 4px between a field and its own hint - the
   template's rule that a caption belongs to the control above it. */
.admin-form[b-eikkp73cry] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-lg);
}

.admin-field[b-eikkp73cry] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-xs);
}

/* The row that acts on the card above it, set apart by the same 24px the
   settings inside the card step in. */
.admin-actions[b-eikkp73cry] {
    display: flex;
    gap: var(--ffp-space-sm);
}

.invite-token[b-eikkp73cry] {
    display: inline-block;
    margin-left: var(--ffp-space-sm);
    font-family: ui-monospace, monospace;
    user-select: all;
}

/* The expiry on a line of its own under the code. */
.invite-expires[b-eikkp73cry] {
    display: block;
    margin-top: var(--ffp-space-xs);
}

.ffp-empty[b-eikkp73cry] {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    flex: 1;
    padding: var(--ffp-space-xl);
}

/* --- The control skin -----------------------------------------------------
 *
 * The block below repeats, near enough word for word, in every page stylesheet
 * of phase 2.5b. It is copied rather than shared because a scoped stylesheet
 * reaches exactly one component: the only file that could hold it once is
 * wwwroot/css/app.css, and that file is the token layer three other screens are
 * being rebuilt against at the same time. Copying is the smaller evil - the
 * alternative is one page quietly keeping Material's defaults and nobody
 * noticing until a screenshot.
 */

/* Buttons. 4px corners, 40px on a mouse and 48px on a finger, and sentence
   case: the reference screens label a button "Angebot als PDF erzeugen", never
   "ANGEBOT ALS PDF ERZEUGEN". Material's uppercase is the one typographic
   habit the screens never follow. The shadow goes for the reason above. */
.admin[b-eikkp73cry]  .mud-button {
    min-height: 40px;
    border-radius: var(--ffp-radius);
    text-transform: none;
    letter-spacing: 0;
    font-weight: 600;
    box-shadow: none;
}

/* The secondary button: white, a 1px Linie edge, ink-coloured label, and Nebel
   under the pointer. MudBlazor's outlined variant paints the border and the
   label in the primary colour instead, which turns every second button into a
   competing call to action. */
.admin[b-eikkp73cry]  .mud-button-outlined {
    border: 1px solid var(--ffp-border);
    background-color: var(--ffp-bg);
    color: var(--ffp-fg);
}

.admin[b-eikkp73cry]  .mud-button-outlined:hover {
    background-color: var(--ffp-surface);
}

/* Inputs. A 1px Linie outline at rest and a crisp 1.5px Petrol edge on focus,
   with nothing glowing around it (DESIGN.md, "Input Fields & Controls").
   Material draws 2px; 1.5 is the difference between a frame and a highlight.

   The selector is heavier than it looks like it needs to be, on purpose: the
   rule it has to beat is MudBlazor's own :focus-within rule, and a scoped class
   alone loses to it. Checked in the browser rather than counted on paper - the
   engine reports this rule as the winning one.

   One caveat that the measurement rather than the stylesheet reveals: Blink
   floors a fractional border to whole pixels, so in Chrome the 1.5 is drawn as
   1 and the focused edge differs from the resting one by colour alone. Gecko
   and WebKit draw the 1.5. The declaration stays as the handbook writes it -
   the alternative is rounding it to 2 for one engine and losing the rule. Focus
   is never invisible either way: app.css puts a ring on the input itself. */
.admin[b-eikkp73cry]  .mud-input-outlined-border {
    border-width: 1px;
    border-color: var(--ffp-border);
    border-radius: var(--ffp-radius);
}

.admin[b-eikkp73cry]  .mud-input-control:focus-within .mud-input-outlined-border {
    border-width: 1.5px;
    border-color: var(--ffp-accent);
}

/* The table, which is where this screen earns its keep. A Nebel pane inside a
   1px Linie frame, and column heads set as small letterspaced capitals the way
   the reference screens set them - that is the one place the template does
   shout, because a column head is a label rather than a word to read. */
.admin[b-eikkp73cry]  .mud-table {
    background-color: var(--ffp-surface);
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius-container);
    /* Without this the head row's square corners stick out past the frame. */
    overflow: hidden;
}

.admin[b-eikkp73cry]  .mud-table-head .mud-table-cell {
    height: 36px;
    padding-top: 0;
    padding-bottom: 0;
    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ffp-muted);
}

/* 48px rows: the role dropdown inside a cell is 40px, and 4px of air above and
   below it is what keeps the row from looking packed.

   Only where there is a table. Below 768px MudTable drops the columns and
   stacks every cell as a labelled line, and a fixed height there turns the one
   empty cell - the row whose member cannot be deactivated - into a 48px hole in
   the middle of the list. */
@media (min-width: 768px) {
    .admin[b-eikkp73cry]  .mud-table-body .mud-table-cell {
        height: 48px;
        padding-top: 0;
        padding-bottom: 0;
    }
}

/* Tabs read as words, not as a shouted index. The active tab keeps the Petrol
   underline MudBlazor draws from the palette. */
.admin[b-eikkp73cry]  .mud-tab {
    text-transform: none;
    letter-spacing: 0;
    font-weight: 600;
}

/* MudText renders a caption as a <span>, so a hint under a field is inline: the
   next control lands beside it rather than under it. On the workspace tab that
   put the save button on top of the retention sentence - visible in the browser
   and invisible to the build. A hint is a line of its own. */
.admin[b-eikkp73cry]  .mud-typography-caption {
    display: block;
    color: var(--ffp-muted);
}

/* An alert is a callout: Nebel, a 1px Linie edge, no tinted wash and no
   shadow. */
.admin[b-eikkp73cry]  .mud-alert {
    border-radius: var(--ffp-radius);
    box-shadow: none;
}

/* Field touch targets. DESIGN.md sizes the mobile client at 48px, and a text
   field is as much a target as a button is. */
@media (max-width: 767px) {
    .admin-head[b-eikkp73cry],
    .admin-panel[b-eikkp73cry] {
        padding-left: var(--ffp-space-md);
        padding-right: var(--ffp-space-md);
    }

    .admin[b-eikkp73cry]  .mud-button {
        min-height: 48px;
    }

    .admin[b-eikkp73cry]  .mud-input,
    .admin[b-eikkp73cry]  .mud-input-outlined-border {
        min-height: 48px;
    }
}

/* The workspace's picture, its button and the remove link on one line (#329). */
.admin-logo[b-eikkp73cry] {
    display: flex;
    align-items: center;
    gap: var(--ffp-space-md);
}

/* The same label-as-button as the profile page: InputFile is a component, so the
   input is reached through ::deep from the label, which is ours. */
.avatar-action[b-eikkp73cry]  .hidden-input {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

.avatar-action[b-eikkp73cry] {
    display: inline-flex;
    align-items: center;
    min-height: 40px;
    padding: 0 var(--ffp-space-md);
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius);
    background-color: var(--ffp-bg);
    color: var(--ffp-fg);
    font-weight: 600;
    font-size: 0.875rem;
    cursor: pointer;
}

.avatar-action:hover[b-eikkp73cry] {
    background-color: var(--ffp-surface);
}
/* /Pages/Channel.razor.rz.scp.css */
/* The channel screen, after the reference screen
   (Planning/design/desktop_slack_workspace_channel_bauprojekte/screen.png).
 *
 * Three columns: the rail is the shell's drawer, this file owns the other two. Colours
 * are the role tokens from wwwroot/css/app.css wherever they have to follow the
 * light/dark choice, and the literal brand tokens only where a colour does not flip.
 *
 * Nothing here casts a shadow. DESIGN.md draws every in-page boundary as a 1px Linie
 * border and keeps its single shadow for what floats over the page. The template's own
 * screen uses shadow-sm under the channel head; the rule it is written against says a
 * border, so that one is a border here. */

/* --- the two columns ------------------------------------------------------- */

.ffp-channel-shell[b-qxcqa07iqh] {
    flex: 1;
    min-height: 0;
    display: flex;
}

.ffp-channel-main[b-qxcqa07iqh] {
    flex: 1;
    /* Without this a long channel name or topic widens the column instead of
       ellipsing, and the context drawer gets pushed off the screen. */
    min-width: 0;
    display: flex;
    flex-direction: column;
}

/* --- the channel head ------------------------------------------------------ */

.ffp-channel-header[b-qxcqa07iqh] {
    flex: none;
    display: flex;
    align-items: center;
    gap: var(--ffp-space-md);
    /* 64px in the screen, and the 24px side gutter DESIGN.md gives the desktop. */
    min-height: 4rem;
    padding: var(--ffp-space-sm) var(--ffp-space-lg);
    background: var(--ffp-bg);
    border-bottom: 1px solid var(--ffp-border);
}

/* Name, topic, separator and member count on one baseline. The whole group shrinks
   before the buttons on the right do. */
.ffp-channel-identity[b-qxcqa07iqh] {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: baseline;
    gap: var(--ffp-space-sm);
}

/* Reached through ::deep because MudText is a component: Blazor puts this file's scope
   on the elements written in Channel.razor and never on a component's own root, so
   `.ffp-channel-title` alone matches nothing at all. Same for the glyph and the drawer's
   own heading below.
 *
 * Domine, 20px, bold - the one loud thing in the head. The colour is the palette's
   primary rather than Petrol Schrift, which is the darker ink the screen sets headings
   in: Petrol Schrift exists only in the light scheme, and a component stylesheet cannot
   see which scheme is running, while the palette variable already follows it. The
   difference on white is 9.0:1 against 12.8:1 - both far past AA. */
.ffp-channel-identity[b-qxcqa07iqh]  .ffp-channel-title {
    display: flex;
    align-items: center;
    /* Does not shrink, and the topic beside it does. With both at the default
       flex-shrink the two gave way together, and a long topic ate the channel name
       down to "# gene…" - the one word in the head nobody can do without. The cap stops
       the opposite case, a name so long that there is no room for anything else. */
    flex: none;
    max-width: 50%;
    margin: 0;
    color: var(--ffp-accent);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The hash or the lock in front of the name: present, and quieter than the name. */
.ffp-channel-identity[b-qxcqa07iqh]  .ffp-channel-glyph {
    flex: none;
    margin-inline-end: 0.125rem;
    color: var(--ffp-blaugrau);
}

.ffp-channel-topic[b-qxcqa07iqh] {
    /* The one part of the head that gives way when the window narrows. */
    flex: 1 1 auto;
    min-width: 0;
    font-size: 0.75rem;
    line-height: 1.33;
    color: var(--ffp-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    /* The measure DESIGN.md caps text at; past it the topic is a paragraph in a header. */
    max-width: 60ch;
}

/* The 4px dot between the topic and the member count. A circle, which the handbook
   otherwise reserves for badges and pips - but the screen draws it as one, and
   Planning/10 says the screens win where the two disagree. */
.ffp-channel-sep[b-qxcqa07iqh] {
    flex: none;
    width: 0.25rem;
    height: 0.25rem;
    border-radius: 50%;
    background: var(--ffp-border);
    /* align-items: baseline would drop a box with no text onto the text baseline. */
    align-self: center;
}

.ffp-channel-members[b-qxcqa07iqh] {
    flex: none;
    display: flex;
    align-items: center;
    gap: var(--ffp-space-xs);
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--ffp-muted);
    align-self: center;
}

.ffp-channel-members[b-qxcqa07iqh]  .mud-icon-root {
    font-size: 1rem;
}

.ffp-channel-actions[b-qxcqa07iqh] {
    flex: none;
    display: flex;
    align-items: center;
    gap: var(--ffp-space-sm);
}

/* The head's buttons, as the screen draws them: a quiet Nebel fill inside a Linie
   hairline, square-ish corners, and the label in sentence case. MudBlazor's outlined
   button is a transparent pill-ish control in the primary colour with an uppercase
   label, and the handbook prohibits all three. */
.ffp-channel-actions[b-qxcqa07iqh]  .ffp-channel-action {
    min-height: 2rem;
    padding: 0.25rem 0.625rem;
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius);
    background: var(--ffp-surface);
    color: var(--ffp-fg);
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0;
    text-transform: none;
    white-space: nowrap;
}

.ffp-channel-actions[b-qxcqa07iqh]  .ffp-channel-action:hover {
    background: var(--ffp-border);
}

/* The icon is the one part of the button that carries the brand colour, which is how
   the screen keeps a row of buttons from turning into a row of brand blocks. */
.ffp-channel-actions[b-qxcqa07iqh]  .ffp-channel-action .mud-icon-root {
    font-size: 1rem;
    color: var(--ffp-accent);
}

/* The timeline and the thread panel sit side by side; each scrolls on its own. */
.ffp-channel-columns[b-qxcqa07iqh] {
    flex: 1;
    min-height: 0;
    display: flex;
}

/* The body is what scrolls; the header and the composer stay put. */
.ffp-channel-body[b-qxcqa07iqh] {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/* --- the context drawer ---------------------------------------------------- */

.ffp-channel-context[b-qxcqa07iqh] {
    flex: none;
    width: var(--ffp-context-width);
    display: flex;
    flex-direction: column;
    overflow-y: auto;
    background: var(--ffp-surface);
    border-inline-start: 1px solid var(--ffp-border);
}

/* DESIGN.md gives the three-column layout to windows of 1280px and wider and folds the
   drawer away below that. The message stream is the column that must not be squeezed,
   so this one goes first and the toggle in the head has no effect until there is room. */
@media (max-width: 1279px) {
    .ffp-channel-context[b-qxcqa07iqh] {
        display: none;
    }
}

/* The same height as the channel head next to it, so the two heads line up. No border
   under it: the screen leaves the drawer's head open, and the pane already stands apart
   from the stream by being Nebel. */
.ffp-context-header[b-qxcqa07iqh] {
    flex: none;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--ffp-space-sm);
    min-height: 4rem;
    padding: var(--ffp-space-sm) var(--ffp-space-md);
}

.ffp-context-header[b-qxcqa07iqh]  .ffp-context-title {
    margin: 0;
    color: var(--ffp-accent);
}

.ffp-context-body[b-qxcqa07iqh] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-lg);
    padding: 0 var(--ffp-space-md) var(--ffp-space-md);
}

.ffp-context-section[b-qxcqa07iqh] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-xs);
}

.ffp-context-head[b-qxcqa07iqh] {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--ffp-space-sm);
}

/* The small spaced-out capitals the screen labels every block in the drawer with. No
   rule under them - DESIGN.md forbids a divider attached to a heading and asks for
   spacing and weight to do the separating. */
.ffp-context-label[b-qxcqa07iqh] {
    margin: 0;
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ffp-muted);
}

.ffp-context-count[b-qxcqa07iqh] {
    font-size: 0.6875rem;
    font-weight: 700;
    color: var(--ffp-accent);
    font-variant-numeric: tabular-nums;
}

/* A card on the drawer. DESIGN.md describes an in-page card as a Linie outline over a
   Nebel fill, but the drawer itself is Nebel, so a Nebel card would be invisible on it.
   The screen solves it the same way: white cards on the Nebel pane, outline unchanged. */
.ffp-context-card[b-qxcqa07iqh] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-sm);
    padding: var(--ffp-space-sm) 0.75rem;
    background: var(--ffp-bg);
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius);
}

.ffp-context-kind[b-qxcqa07iqh] {
    margin: 0;
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--ffp-fg);
}

.ffp-context-text[b-qxcqa07iqh] {
    margin: 0;
    font-size: 0.8125rem;
    line-height: 1.38;
    color: var(--ffp-fg);
}

/* Label left, value right, over a hairline - the shape the screen gives every fact in
   the drawer. */
.ffp-context-fact[b-qxcqa07iqh] {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--ffp-space-sm);
    padding-top: var(--ffp-space-sm);
    border-top: 1px solid var(--ffp-border);
    font-size: 0.75rem;
    color: var(--ffp-muted);
}

.ffp-context-fact time[b-qxcqa07iqh] {
    font-weight: 600;
    color: var(--ffp-fg);
    font-variant-numeric: tabular-nums;
}

/* --- the files row --------------------------------------------------------- */

.ffp-context-row[b-qxcqa07iqh] {
    display: flex;
    align-items: center;
    gap: var(--ffp-space-sm);
    padding: var(--ffp-space-sm);
    background: var(--ffp-bg);
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius);
    color: inherit;
    text-decoration: none;
}

.ffp-context-row[b-qxcqa07iqh]  .mud-icon-root {
    flex: none;
    font-size: 1.125rem;
    color: var(--ffp-accent);
}

.ffp-context-names[b-qxcqa07iqh] {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.ffp-context-name[b-qxcqa07iqh] {
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--ffp-fg);
}

.ffp-context-hint[b-qxcqa07iqh] {
    font-size: 0.6875rem;
    line-height: 1.27;
    color: var(--ffp-muted);
}

/* The one place an underline is allowed: this is a link, and DESIGN.md's ban on
   underlines exempts exactly that. */
.ffp-context-row:hover .ffp-context-name[b-qxcqa07iqh],
.ffp-context-row:focus-visible .ffp-context-name[b-qxcqa07iqh] {
    text-decoration: underline;
}

/* --- the channel that is not there ----------------------------------------- */

.ffp-empty[b-qxcqa07iqh] {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    flex: 1;
    padding: 2rem;
}
/* /Pages/ForgotPassword.razor.rz.scp.css */
/* Asking for a reset mail: the field stack of the sign-in form, in the same card
   AuthLayout draws.

   Pages/Register.razor.css again, line for line below this comment. A scoped
   stylesheet reaches exactly one component, so the auth screens change together. */

.auth[b-198tuaa71l] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-md);
}

/* The logo over the heading - see Pages/Login.razor.css. */
.auth-brand[b-198tuaa71l] {
    margin: 0;
    line-height: 0;
}

/* ::deep because these classes sit on MudBlazor components - see the note in
   Pages/Admin.razor.css. */
.auth[b-198tuaa71l]  .auth-fields {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-md);
}

.auth[b-198tuaa71l]  .auth-intro,
.auth[b-198tuaa71l]  .auth-alternative {
    color: var(--ffp-muted);
}

/* --- The control skin -----------------------------------------------------
 *
 * Repeated in every page stylesheet of phase 2.5b; see Pages/Admin.razor.css
 * for why it is copied instead of shared. Nothing here names a hex value.
 */

.auth[b-198tuaa71l]  .mud-button {
    min-height: 40px;
    border-radius: var(--ffp-radius);
    text-transform: none;
    letter-spacing: 0;
    font-weight: 600;
    box-shadow: none;
}

.auth[b-198tuaa71l]  .mud-input-outlined-border {
    border-width: 1px;
    border-color: var(--ffp-border);
    border-radius: var(--ffp-radius);
}

.auth[b-198tuaa71l]  .mud-input-control:focus-within .mud-input-outlined-border {
    border-width: 1.5px;
    border-color: var(--ffp-accent);
}

.auth[b-198tuaa71l]  .mud-alert {
    border-radius: var(--ffp-radius);
    box-shadow: none;
}

/* The password rule sits under its field as a hint, so it needs to be a line of
   its own rather than an inline run - MudBlazor's helper text is a <p>, but the
   colour it picks is the body ink, and a hint is secondary. */
.auth[b-198tuaa71l]  .mud-input-helper-text {
    color: var(--ffp-muted);
}

@media (max-width: 767px) {
    .auth[b-198tuaa71l]  .mud-button {
        min-height: 48px;
    }

    .auth[b-198tuaa71l]  .mud-input,
    .auth[b-198tuaa71l]  .mud-input-outlined-border {
        min-height: 48px;
    }
}
/* /Pages/Home.razor.rz.scp.css */
/* The start page is an empty state and nothing else: the workspace has not been
   entered yet, so there is nothing to put in a card. What it owes the template
   is the serif greeting, a measured line under it, and enough air around both. */

.ffp-empty[b-igyxr698u2] {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    flex: 1;
    gap: var(--ffp-space-sm);
    padding: var(--ffp-space-xl);
}

/* DESIGN.md caps a block of text at about 90 characters; centred on a 1400px
   canvas it would otherwise run the full width and stop being a sentence. */
/* ::deep because the class sits on a MudText, and Blazor stamps its scope
   attribute only on markup this component writes itself - see the note in
   Pages/Admin.razor.css. */
.ffp-empty[b-igyxr698u2]  .ffp-empty-body {
    max-width: 34rem;
    color: var(--ffp-muted);
}
/* /Pages/Login.razor.rz.scp.css */
/* The sign-in form. It sits in the card AuthLayout draws, so this file styles
   only what is inside it: the field stack, and the one primary button.

   The wrapping <div class="auth"> in Login.razor exists for this file. Blazor's
   scoped CSS stamps its attribute on the markup this component writes and on
   nothing else, so without an element of our own there is no way to reach a
   MudBlazor control from here at all. */

.auth[b-7ivma6vf8f] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-md);
}

/* The product's logo, over the name of the screen (#199).
 *
 * The paragraph carries no type of its own any more - the name is inside the SVG, and
 * the logo picks its light or dark cut from the scheme itself, which is what the
 * accent colour used to do here. line-height 0 so the paragraph is exactly as tall as
 * the image rather than as tall as a line of text around it.
 *
 * The full 16px gap of .auth stays between the logo and the heading. The product's name
 * as a line of text was pulled up against the heading to read as one block with it; a
 * logo is heavier, and pulled that close it sat on the heading's focus ring. */
.auth-brand[b-7ivma6vf8f] {
    margin: 0;
    line-height: 0;
}

/* ::deep because both classes sit on MudBlazor components - MudForm and MudText.
   Blazor stamps its scope attribute only on markup this component writes itself,
   so a bare selector would never match either of them; see the note in
   Pages/Admin.razor.css. */
.auth[b-7ivma6vf8f]  .auth-fields {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-md);
}

/* The line under the form that offers the other door. */
.auth[b-7ivma6vf8f]  .auth-alternative {
    color: var(--ffp-muted);
}

/* --- The control skin -----------------------------------------------------
 *
 * Repeated in every page stylesheet of phase 2.5b; see Pages/Admin.razor.css
 * for why it is copied instead of shared. Nothing here names a hex value.
 */

.auth[b-7ivma6vf8f]  .mud-button {
    min-height: 40px;
    border-radius: var(--ffp-radius);
    text-transform: none;
    letter-spacing: 0;
    font-weight: 600;
    box-shadow: none;
}

.auth[b-7ivma6vf8f]  .mud-input-outlined-border {
    border-width: 1px;
    border-color: var(--ffp-border);
    border-radius: var(--ffp-radius);
}

.auth[b-7ivma6vf8f]  .mud-input-control:focus-within .mud-input-outlined-border {
    border-width: 1.5px;
    border-color: var(--ffp-accent);
}

.auth[b-7ivma6vf8f]  .mud-alert {
    border-radius: var(--ffp-radius);
    box-shadow: none;
}

/* Signing in happens on a phone as often as at a desk, and this is the first
   thing a thumb has to hit. */
@media (max-width: 767px) {
    .auth[b-7ivma6vf8f]  .mud-button {
        min-height: 48px;
    }

    .auth[b-7ivma6vf8f]  .mud-input,
    .auth[b-7ivma6vf8f]  .mud-input-outlined-border {
        min-height: 48px;
    }
}
/* /Pages/NotFound.razor.rz.scp.css */
/* The same empty state as the start page, with a way back. */

.ffp-empty[b-0ynptrdrcz] {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    flex: 1;
    gap: var(--ffp-space-sm);
    padding: var(--ffp-space-xl);
}

/* ::deep because the class sits on a MudText - see Pages/Admin.razor.css. */
.ffp-empty[b-0ynptrdrcz]  .ffp-empty-body {
    max-width: 34rem;
    color: var(--ffp-muted);
}

/* The way back is a secondary button, not a bare word: white on a 1px Linie
   edge, 4px corners, 40px tall - and 48 where a finger has to find it. */
.ffp-empty[b-0ynptrdrcz]  .mud-button {
    min-height: 40px;
    margin-top: var(--ffp-space-sm);
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius);
    background-color: var(--ffp-bg);
    color: var(--ffp-fg);
    text-transform: none;
    letter-spacing: 0;
    font-weight: 600;
    box-shadow: none;
}

.ffp-empty[b-0ynptrdrcz]  .mud-button:hover {
    background-color: var(--ffp-surface);
}

@media (max-width: 767px) {
    .ffp-empty[b-0ynptrdrcz]  .mud-button {
        min-height: 48px;
    }
}
/* /Pages/Profile.razor.rz.scp.css */
/* The profile: who you are, and what the others see beside your name. Same two
   cards as Settings - a Nebel pane, a 1px Linie edge, a serif title, no shadow. */

.profile[b-sebku6yj7w] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-md);
    padding: var(--ffp-space-lg) var(--ffp-space-xl) var(--ffp-space-xl);
    max-width: 44rem;
    overflow-y: auto;
}

.profile-card[b-sebku6yj7w] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-md);
    background-color: var(--ffp-surface);
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius-container);
    padding: var(--ffp-space-md);
}

/* ::deep because the class sits on a MudText - see the note in
   Pages/Admin.razor.css. Every selector in this file that names a class on a
   MudBlazor component is written the same way. */
.profile-card[b-sebku6yj7w]  .profile-title {
    margin: 0;
}

.profile-avatar[b-sebku6yj7w] {
    display: flex;
    align-items: center;
    gap: var(--ffp-space-md);
}

/* The real file input has to stay in the document - the label drives it - but
   it must not be seen. It was: the rule used to be written as a plain class,
   and Blazor's scoped CSS only stamps its attribute on elements this component
   writes itself, never on the markup a child component renders. InputFile is a
   component, so ".hidden-input" never matched it and the browser drew the grey
   "Datei auswählen / Keine ausgewählt" control next to the avatar. Reaching it
   needs ::deep from an element that is ours, which the label is. */
.avatar-action[b-sebku6yj7w]  .hidden-input {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

/* The label is the button, so it has to look and measure like one: the same
   40px secondary button as everywhere else, white on a Linie edge. */
.avatar-action[b-sebku6yj7w] {
    display: inline-flex;
    align-items: center;
    min-height: 40px;
    padding: 0 var(--ffp-space-md);
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius);
    background-color: var(--ffp-bg);
    color: var(--ffp-fg);
    font-weight: 600;
    font-size: 0.875rem;
    cursor: pointer;
}

.avatar-action:hover[b-sebku6yj7w] {
    background-color: var(--ffp-surface);
}

/* A field and the sentence that explains it: one thing, 4px apart. */
.profile-field[b-sebku6yj7w] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-xs);
}

.profile-status[b-sebku6yj7w] {
    display: flex;
    gap: var(--ffp-space-sm);
    align-items: flex-start;
}

.profile[b-sebku6yj7w]  .profile-status-text {
    flex: 1;
    min-width: 0;
}

.profile-actions[b-sebku6yj7w] {
    display: flex;
    gap: var(--ffp-space-sm);
}

.emoji-grid[b-sebku6yj7w] {
    display: grid;
    grid-template-columns: repeat(8, 1fr);
    gap: var(--ffp-space-xs);
    padding: var(--ffp-space-sm);
    width: 16rem;
    max-height: 12rem;
    overflow-y: auto;
}

.emoji-grid .emoji[b-sebku6yj7w] {
    background: none;
    border: none;
    border-radius: var(--ffp-radius);
    font-size: 1.1rem;
    cursor: pointer;
    padding: var(--ffp-space-xs);
}

.emoji-grid .emoji:hover[b-sebku6yj7w] {
    background-color: var(--ffp-surface);
}

/* --- The control skin -----------------------------------------------------
 *
 * Repeated in every page stylesheet of phase 2.5b; see Pages/Admin.razor.css
 * for why it is copied instead of shared. Nothing here names a hex value.
 */

.profile[b-sebku6yj7w]  .mud-button {
    min-height: 40px;
    border-radius: var(--ffp-radius);
    text-transform: none;
    letter-spacing: 0;
    font-weight: 600;
    box-shadow: none;
}

.profile[b-sebku6yj7w]  .mud-button-outlined {
    border: 1px solid var(--ffp-border);
    background-color: var(--ffp-bg);
    color: var(--ffp-fg);
}

.profile[b-sebku6yj7w]  .mud-button-outlined:hover {
    background-color: var(--ffp-surface);
}

/* The emoji picker's activator is a square the width of its own glyph, so it
   lines up with the 40px status field beside it instead of stretching to a
   button's minimum width. */
.profile[b-sebku6yj7w]  .status-emoji .mud-button {
    min-width: 40px;
    width: 40px;
    padding: 0;
    font-size: 1.125rem;
}

.profile[b-sebku6yj7w]  .mud-input-outlined-border {
    border-width: 1px;
    border-color: var(--ffp-border);
    border-radius: var(--ffp-radius);
}

.profile[b-sebku6yj7w]  .mud-input-control:focus-within .mud-input-outlined-border {
    border-width: 1.5px;
    border-color: var(--ffp-accent);
}

/* See Pages/Admin.razor.css: a MudText caption is an inline <span>. */
.profile[b-sebku6yj7w]  .mud-typography-caption {
    display: block;
    color: var(--ffp-muted);
}

@media (max-width: 767px) {
    .profile[b-sebku6yj7w] {
        padding-left: var(--ffp-space-md);
        padding-right: var(--ffp-space-md);
    }

    .avatar-action[b-sebku6yj7w],
    .profile[b-sebku6yj7w]  .mud-button {
        min-height: 48px;
    }

    .profile[b-sebku6yj7w]  .status-emoji .mud-button {
        min-width: 48px;
        width: 48px;
    }

    .profile[b-sebku6yj7w]  .mud-input,
    .profile[b-sebku6yj7w]  .mud-input-outlined-border {
        min-height: 48px;
    }
}
/* /Pages/Register.razor.rz.scp.css */
/* Redeeming an invitation: the same field stack as the sign-in form, in the
   same card AuthLayout draws.

   This file is Pages/Login.razor.css again, near enough line for line. A scoped
   stylesheet reaches exactly one component and these are two components, so the
   two files have to be changed together. */

.auth[b-yroxkaz273] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-md);
}

/* The logo over the heading - see Pages/Login.razor.css. */
.auth-brand[b-yroxkaz273] {
    margin: 0;
    line-height: 0;
}

/* ::deep because these classes sit on MudBlazor components - see the note in
   Pages/Admin.razor.css. */
.auth[b-yroxkaz273]  .auth-fields {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-md);
}

.auth[b-yroxkaz273]  .auth-intro,
.auth[b-yroxkaz273]  .auth-alternative {
    color: var(--ffp-muted);
}

/* --- The control skin -----------------------------------------------------
 *
 * Repeated in every page stylesheet of phase 2.5b; see Pages/Admin.razor.css
 * for why it is copied instead of shared. Nothing here names a hex value.
 */

.auth[b-yroxkaz273]  .mud-button {
    min-height: 40px;
    border-radius: var(--ffp-radius);
    text-transform: none;
    letter-spacing: 0;
    font-weight: 600;
    box-shadow: none;
}

.auth[b-yroxkaz273]  .mud-input-outlined-border {
    border-width: 1px;
    border-color: var(--ffp-border);
    border-radius: var(--ffp-radius);
}

.auth[b-yroxkaz273]  .mud-input-control:focus-within .mud-input-outlined-border {
    border-width: 1.5px;
    border-color: var(--ffp-accent);
}

.auth[b-yroxkaz273]  .mud-alert {
    border-radius: var(--ffp-radius);
    box-shadow: none;
}

/* The password rule sits under its field as a hint, so it needs to be a line of
   its own rather than an inline run - MudBlazor's helper text is a <p>, but the
   colour it picks is the body ink, and a hint is secondary. */
.auth[b-yroxkaz273]  .mud-input-helper-text {
    color: var(--ffp-muted);
}

@media (max-width: 767px) {
    .auth[b-yroxkaz273]  .mud-button {
        min-height: 48px;
    }

    .auth[b-yroxkaz273]  .mud-input,
    .auth[b-yroxkaz273]  .mud-input-outlined-border {
        min-height: 48px;
    }
}
/* /Pages/ResetPassword.razor.rz.scp.css */
/* Setting a new password with the code from the mail: the field stack of the
   sign-in form, in the same card AuthLayout draws.

   Pages/Register.razor.css again, line for line below this comment. A scoped
   stylesheet reaches exactly one component, so the auth screens change together. */

.auth[b-0cvr2suev3] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-md);
}

/* The logo over the heading - see Pages/Login.razor.css. */
.auth-brand[b-0cvr2suev3] {
    margin: 0;
    line-height: 0;
}

/* ::deep because these classes sit on MudBlazor components - see the note in
   Pages/Admin.razor.css. */
.auth[b-0cvr2suev3]  .auth-fields {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-md);
}

.auth[b-0cvr2suev3]  .auth-intro,
.auth[b-0cvr2suev3]  .auth-alternative {
    color: var(--ffp-muted);
}

/* --- The control skin -----------------------------------------------------
 *
 * Repeated in every page stylesheet of phase 2.5b; see Pages/Admin.razor.css
 * for why it is copied instead of shared. Nothing here names a hex value.
 */

.auth[b-0cvr2suev3]  .mud-button {
    min-height: 40px;
    border-radius: var(--ffp-radius);
    text-transform: none;
    letter-spacing: 0;
    font-weight: 600;
    box-shadow: none;
}

.auth[b-0cvr2suev3]  .mud-input-outlined-border {
    border-width: 1px;
    border-color: var(--ffp-border);
    border-radius: var(--ffp-radius);
}

.auth[b-0cvr2suev3]  .mud-input-control:focus-within .mud-input-outlined-border {
    border-width: 1.5px;
    border-color: var(--ffp-accent);
}

.auth[b-0cvr2suev3]  .mud-alert {
    border-radius: var(--ffp-radius);
    box-shadow: none;
}

/* The password rule sits under its field as a hint, so it needs to be a line of
   its own rather than an inline run - MudBlazor's helper text is a <p>, but the
   colour it picks is the body ink, and a hint is secondary. */
.auth[b-0cvr2suev3]  .mud-input-helper-text {
    color: var(--ffp-muted);
}

@media (max-width: 767px) {
    .auth[b-0cvr2suev3]  .mud-button {
        min-height: 48px;
    }

    .auth[b-0cvr2suev3]  .mud-input,
    .auth[b-0cvr2suev3]  .mud-input-outlined-border {
        min-height: 48px;
    }
}
/* /Pages/Scheduled.razor.rz.scp.css */
/* The planned things (#87), laid out like the saved and mentions lists. */

.scheduled[b-zbyf3x0vqq] {
    display: flex;
    flex-direction: column;
    height: 100%;
    min-height: 0;
}

.scheduled-header[b-zbyf3x0vqq] {
    padding: var(--ffp-space-md) 1.5rem;
    border-bottom: 1px solid var(--ffp-border);
}

.scheduled-body[b-zbyf3x0vqq] {
    flex: 1;
    overflow-y: auto;
    padding: var(--ffp-space-md) 1.5rem;
}

.scheduled-section + .scheduled-section[b-zbyf3x0vqq] {
    margin-top: 2rem;
}

.section-title[b-zbyf3x0vqq] {
    font-size: 1rem;
    font-weight: 600;
    margin: 0 0 var(--ffp-space-sm);
}

.item[b-zbyf3x0vqq] {
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius-container);
    padding: var(--ffp-space-sm) var(--ffp-space-md);
    margin-bottom: var(--ffp-space-sm);
}

.meta[b-zbyf3x0vqq] {
    display: flex;
    gap: 0.4rem;
    flex-wrap: wrap;
    font-size: 0.8rem;
    color: var(--ffp-muted);
}

.body[b-zbyf3x0vqq] {
    margin: 0.4rem 0;
    white-space: pre-line;
    overflow-wrap: anywhere;
    display: -webkit-box;
    -webkit-line-clamp: 4;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.due[b-zbyf3x0vqq] {
    font-weight: 600;
    color: var(--mud-palette-warning);
}

.gone[b-zbyf3x0vqq],
.empty[b-zbyf3x0vqq] {
    color: var(--ffp-muted);
    font-size: 0.875rem;
}

.message-link[b-zbyf3x0vqq] {
    display: inline-block;
    margin: 0.25rem 0;
    font-size: 0.875rem;
}

.edit-box[b-zbyf3x0vqq] {
    width: 100%;
    margin: 0.4rem 0;
    padding: 0.5rem;
    font: inherit;
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius);
    background: var(--ffp-bg);
    color: inherit;
    resize: vertical;
}

.actions[b-zbyf3x0vqq] {
    display: flex;
    gap: 0.25rem;
    flex-wrap: wrap;
}
/* /Pages/Search.razor.rz.scp.css */
/* Search: a query field, the filter words it understands, and two lists of hits.
   The hit list follows the row pattern of the mobile reference screen - a
   framed pane, rows divided by a hairline, the metadata line above the text. */

.search[b-3l76qwyxz0] {
    display: flex;
    flex-direction: column;
    min-height: 0;
    flex: 1;
}

.search-header[b-3l76qwyxz0] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-sm);
    padding: var(--ffp-space-md) var(--ffp-space-xl);
    border-bottom: 1px solid var(--ffp-border);
}

/* ::deep because the class sits on a MudText, and Blazor stamps its scope
   attribute only on markup this component writes itself - see the note in
   Pages/Admin.razor.css. Every selector in this file that names a class on a
   MudBlazor component is written the same way. */
.search[b-3l76qwyxz0]  .search-hint {
    color: var(--ffp-muted);
}

.search-filters[b-3l76qwyxz0] {
    display: flex;
    flex-wrap: wrap;
    gap: var(--ffp-space-sm);
}

.search[b-3l76qwyxz0]  .search-tabs {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
}

.search-results[b-3l76qwyxz0] {
    padding: var(--ffp-space-lg) var(--ffp-space-xl);
}

/* The list is one framed pane rather than rows floating on the canvas: a Nebel
   surface inside a 1px Linie edge, no shadow. Same card as everywhere else. */
.search-list[b-3l76qwyxz0] {
    background-color: var(--ffp-surface);
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius-container);
    overflow: hidden;
}

.hit[b-3l76qwyxz0] {
    display: block;
    width: 100%;
    padding: var(--ffp-space-sm) var(--ffp-space-md);
    border-bottom: 1px solid var(--ffp-border);
    background: transparent;
    cursor: pointer;
}

/* The pane draws the last edge, so the last row must not draw a second one. */
.hit:last-child[b-3l76qwyxz0] {
    border-bottom: 0;
}

.hit:hover[b-3l76qwyxz0] {
    background-color: var(--ffp-bg);
}

/* Two elements wear this class: the <button> that jumps to a message, and the plain
   <div> under a file name. The reset below is written for the first and does nothing to
   the second - which is the point, because the two lines have to look identical.
   Without it app.css's bare-button rule paints the line Petrol and pads it. */
.hit-meta[b-3l76qwyxz0] {
    display: flex;
    gap: var(--ffp-space-xs);
    align-items: baseline;
    flex-wrap: wrap;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: none;
    font: inherit;
    font-size: 0.75rem;
    text-align: left;
    color: var(--ffp-muted);
    margin-bottom: var(--ffp-space-xs);
    cursor: inherit;
}

/* A file row is not a jump target - the name is a link and the row itself does
   nothing, so it must not offer a pointer that promises otherwise. */
.hit-file[b-3l76qwyxz0] {
    cursor: default;
}

.hit-meta-below[b-3l76qwyxz0] {
    margin-bottom: 0;
    margin-top: var(--ffp-space-xs);
}

.search-more[b-3l76qwyxz0] {
    display: flex;
    padding-top: var(--ffp-space-md);
}

.search[b-3l76qwyxz0]  .search-empty {
    display: block;
    padding: var(--ffp-space-xl);
    color: var(--ffp-muted);
}

/* --- The control skin -----------------------------------------------------
 *
 * Repeated in every page stylesheet of phase 2.5b; see Pages/Admin.razor.css
 * for why it is copied instead of shared. Nothing here names a hex value.
 */

.search[b-3l76qwyxz0]  .mud-button {
    min-height: 40px;
    border-radius: var(--ffp-radius);
    text-transform: none;
    letter-spacing: 0;
    font-weight: 600;
    box-shadow: none;
}

.search[b-3l76qwyxz0]  .mud-button-outlined {
    border: 1px solid var(--ffp-border);
    background-color: var(--ffp-bg);
    color: var(--ffp-fg);
}

.search[b-3l76qwyxz0]  .mud-button-outlined:hover {
    background-color: var(--ffp-surface);
}

.search[b-3l76qwyxz0]  .mud-input-outlined-border {
    border-width: 1px;
    border-color: var(--ffp-border);
    border-radius: var(--ffp-radius);
}

.search[b-3l76qwyxz0]  .mud-input-control:focus-within .mud-input-outlined-border {
    border-width: 1.5px;
    border-color: var(--ffp-accent);
}

/* The filter words, and the worst thing the browser found in this phase.
   MudBlazor's outlined chip is a 12px pill - forbidden outright by DESIGN.md
   ("Shapes": pills are for badges and status pips, nothing else) - and it fills
   itself with Petrol while keeping Tinte for the label, which is 1.5:1. The six
   filter words were sitting on screen unreadable. What they are is DESIGN.md's
   categorical tag: Nebel, a 1px Linie edge, ink text, 4px corners. */
.search[b-3l76qwyxz0]  .mud-chip {
    height: 28px;
    border-radius: var(--ffp-radius);
    border: 1px solid var(--ffp-border);
    background-color: var(--ffp-surface);
    color: var(--ffp-fg);
    box-shadow: none;
}

.search[b-3l76qwyxz0]  .mud-chip:hover {
    background-color: var(--ffp-bg);
}

.search[b-3l76qwyxz0]  .mud-tab {
    text-transform: none;
    letter-spacing: 0;
    font-weight: 600;
}

/* See Pages/Admin.razor.css: MudText draws a caption as an inline <span>, so a
   hint runs into whatever follows it. */
.search[b-3l76qwyxz0]  .mud-typography-caption {
    display: block;
}

@media (max-width: 767px) {
    .search-header[b-3l76qwyxz0],
    .search-results[b-3l76qwyxz0] {
        padding-left: var(--ffp-space-md);
        padding-right: var(--ffp-space-md);
    }

    .search[b-3l76qwyxz0]  .mud-button {
        min-height: 48px;
    }

    .search[b-3l76qwyxz0]  .mud-input,
    .search[b-3l76qwyxz0]  .mud-input-outlined-border {
        min-height: 48px;
    }
}
/* /Pages/Settings.razor.rz.scp.css */
/* Settings: four groups of choices, each in its own card. The template has no
   settings screen, so the pattern comes from the reference screens' cards -
   a Nebel pane, a 1px Linie edge, a serif heading, no shadow anywhere. */

.settings[b-34vlpqyafn] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-md);
    padding: var(--ffp-space-lg) var(--ffp-space-xl) var(--ffp-space-xl);
    max-width: 44rem;
    overflow-y: auto;
}

.settings-card[b-34vlpqyafn] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-md);
    background-color: var(--ffp-surface);
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius-container);
    padding: var(--ffp-space-md);
}

/* The heading is the card's own title, so it sits tight against the card's top
   padding and carries no rule under it - DESIGN.md forbids a decorative divider
   below a heading and lets spacing and weight do the separating. */
/* ::deep because the class sits on a MudText - see the note in
   Pages/Admin.razor.css. Every selector in this file that names a class on a
   MudBlazor component is written the same way. */
.settings-card[b-34vlpqyafn]  .settings-title {
    margin: 0;
}

/* A control and the sentence that explains it are one thing, 4px apart. */
.settings-field[b-34vlpqyafn] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-xs);
}

.settings-row[b-34vlpqyafn] {
    display: flex;
    gap: var(--ffp-space-md);
    flex-wrap: wrap;
}

/* This one was dead before this phase too: the picker is a component, so the
   width never reached it and the two quiet-hours fields filled the row. */
.settings[b-34vlpqyafn]  .settings-time {
    max-width: 10rem;
}

/* A device is a row in a list, divided by a hairline and 48px tall so that the
   revoke button has room without the row growing around it. */
.settings-devices[b-34vlpqyafn] {
    display: flex;
    flex-direction: column;
    background-color: var(--ffp-bg);
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius);
    overflow: hidden;
}

.settings-device[b-34vlpqyafn] {
    display: flex;
    align-items: center;
    gap: var(--ffp-space-sm);
    min-height: 48px;
    padding: var(--ffp-space-sm) var(--ffp-space-md);
    border-bottom: 1px solid var(--ffp-border);
}

.settings-device:last-child[b-34vlpqyafn] {
    border-bottom: 0;
}

.settings-device-name[b-34vlpqyafn] {
    flex: 1;
    min-width: 0;
}

.settings-actions[b-34vlpqyafn] {
    display: flex;
    gap: var(--ffp-space-sm);
}

/* --- The control skin -----------------------------------------------------
 *
 * Repeated in every page stylesheet of phase 2.5b; see Pages/Admin.razor.css
 * for why it is copied instead of shared. Nothing here names a hex value.
 */

.settings[b-34vlpqyafn]  .mud-button {
    min-height: 40px;
    border-radius: var(--ffp-radius);
    text-transform: none;
    letter-spacing: 0;
    font-weight: 600;
    box-shadow: none;
}

.settings[b-34vlpqyafn]  .mud-button-outlined {
    border: 1px solid var(--ffp-border);
    background-color: var(--ffp-bg);
    color: var(--ffp-fg);
}

.settings[b-34vlpqyafn]  .mud-button-outlined:hover {
    background-color: var(--ffp-surface);
}

.settings[b-34vlpqyafn]  .mud-input-outlined-border {
    border-width: 1px;
    border-color: var(--ffp-border);
    border-radius: var(--ffp-radius);
}

.settings[b-34vlpqyafn]  .mud-input-control:focus-within .mud-input-outlined-border {
    border-width: 1.5px;
    border-color: var(--ffp-accent);
}

/* See Pages/Admin.razor.css: a MudText caption is an inline <span>, so the save
   button used to flow up beside the digest hint and sit on top of it. */
.settings[b-34vlpqyafn]  .mud-typography-caption {
    display: block;
    color: var(--ffp-muted);
}

/* The one caption that is genuinely inline: the note beside a push level the
   workspace has put out of reach belongs on the same line as the level. */
.settings[b-34vlpqyafn]  .mud-radio .mud-typography-caption {
    display: inline;
}

/* The card already carries the surface, so an alert inside it only needs the
   hairline and the 4px corner - no wash, no shadow. */
.settings[b-34vlpqyafn]  .mud-alert {
    border-radius: var(--ffp-radius);
    box-shadow: none;
}

/* A radio is 18px with a 1.5px edge and fills with Petrol when it is chosen
   (DESIGN.md, "Checkboxes & Radios"). Material draws it at 20px inside a 42px
   ripple, which is what pushed the three push levels onto two ragged lines. */
.settings[b-34vlpqyafn]  .mud-radio-group {
    display: flex;
    flex-direction: column;
    /* flex-start, because a column flex box centres nothing by itself but the
       radios inherit MudBlazor's centring from the input control they sit in -
       and three choices drifting towards the middle of the card read as a
       caption, not as a list to pick from. */
    align-items: flex-start;
    gap: var(--ffp-space-sm);
}

@media (max-width: 767px) {
    .settings[b-34vlpqyafn] {
        padding-left: var(--ffp-space-md);
        padding-right: var(--ffp-space-md);
    }

    .settings[b-34vlpqyafn]  .mud-button {
        min-height: 48px;
    }

    .settings[b-34vlpqyafn]  .mud-input,
    .settings[b-34vlpqyafn]  .mud-input-outlined-border {
        min-height: 48px;
    }
}
