/* /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/Account/PasskeyWaitHint.razor.rz.scp.css */
/* Under the buttons, left-aligned like the alerts there. The live region stays in the
   page while empty - hidden with display: none, a screen reader may not announce it
   when it fills. */
.passkey-wait[b-n3pu4rhadu] {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
}
/* /Features/Admin/CollapsibleSection.razor.rz.scp.css */
/* The card's own look (border, surface) comes from the caller through Class; this file
   styles only the fold: the heading row, the toggle inside it and the count beside it. */

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

/* The heading takes the row, so the whole line up to the count is the click target. */
.ffp-collapsible-head[b-sn52d2bn56]  .ffp-collapsible-title {
    flex: 1 1 auto;
    min-width: 0;
    margin: 0;
}

/* A button that looks like the heading it sits in. app.css gives every button the
   filled accent look, so each part of that is taken back here; the focus ring from
   app.css stays. */
.ffp-collapsible-toggle[b-sn52d2bn56] {
    display: flex;
    align-items: center;
    gap: var(--ffp-space-xs);
    width: 100%;
    padding: var(--ffp-space-xs) 0;
    border: 0;
    border-radius: var(--ffp-radius);
    background: none;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.ffp-collapsible-toggle:hover .ffp-collapsible-label[b-sn52d2bn56] {
    text-decoration: underline;
}

.ffp-collapsible-chevron[b-sn52d2bn56] {
    display: inline-flex;
    color: var(--ffp-muted);
    transition: transform 150ms ease-out;
}

/* Pointing right when folded, down when open - the platform convention. */
.ffp-collapsible-toggle[aria-expanded="false"] .ffp-collapsible-chevron[b-sn52d2bn56] {
    transform: rotate(-90deg);
}

@media (prefers-reduced-motion: reduce) {
    .ffp-collapsible-chevron[b-sn52d2bn56] {
        transition: none;
    }
}

.ffp-collapsible-count[b-sn52d2bn56] {
    flex: 0 0 auto;
    min-width: 1.5rem;
    padding: 0 var(--ffp-space-sm);
    border: 1px solid var(--ffp-border);
    border-radius: 999px;
    color: var(--ffp-muted);
    font-size: 0.8125rem;
    line-height: 1.5rem;
    text-align: center;
}

/* Room between the heading row and what it opens onto. */
.ffp-collapsible-body[b-sn52d2bn56] {
    padding-top: var(--ffp-space-xs);
}
/* /Features/Admin/DefaultChannelsPanel.razor.rz.scp.css */
/* Its own card under the workspace settings: the switches save as they are flipped,
   so they do not belong in the card the save button acts on. */
.default-channels[b-hxrdwjkao5] {
    margin-top: var(--ffp-space-lg);
}

/* The page's .admin-card, which is scoped to Admin.razor and does not reach here.
   Through ::deep, because CollapsibleSection draws the card (#656). */
.default-channels[b-hxrdwjkao5]  .default-channels-card {
    background-color: var(--ffp-surface);
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius-container);
    padding: var(--ffp-space-md);
}

.default-channels[b-hxrdwjkao5]  .default-channels-title {
    font-size: 1.125rem;
}

.default-channels-list[b-hxrdwjkao5] {
    list-style: none;
    margin: var(--ffp-space-sm) 0 0;
    padding: 0;
}

.default-channel[b-hxrdwjkao5] {
    padding: var(--ffp-space-xs) 0;
    border-bottom: 1px solid var(--ffp-border);
}

.default-channel:last-child[b-hxrdwjkao5] {
    border-bottom: none;
}
/* /Features/Admin/DefaultNotificationLevelDialog.razor.rz.scp.css */
/* The checkbox between the sentence and the note about people's own choices. */
.default-level-apply[b-310x3zs25x] {
    margin: var(--ffp-space-sm) 0;
}
/* /Features/Admin/DefaultNotificationLevelPanel.razor.rz.scp.css */
/* Its own card under the default channels: the select saves when it is picked, so it
   does not belong in the card the save button acts on. */
.default-level[b-53oojmb5yv] {
    margin-top: var(--ffp-space-lg);
}

/* The page's .admin-card, which is scoped to Admin.razor and does not reach here.
   Through ::deep, because CollapsibleSection draws the card (#656). */
.default-level[b-53oojmb5yv]  .default-level-card {
    background-color: var(--ffp-surface);
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius-container);
    padding: var(--ffp-space-md);
}

.default-level[b-53oojmb5yv]  .default-level-title {
    font-size: 1.125rem;
}

.default-level[b-53oojmb5yv]  .default-level-select {
    margin-bottom: var(--ffp-space-xs);
}
/* /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;
}

/* Through ::deep because the cards are drawn by CollapsibleSection (#656) and carry
   its scope, not this file's. */
.ffp-governance[b-tvwlpakla9]  .ffp-gov-card {
    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. */
/* The admin screen's card, repeated: Admin.razor's .admin-card is scoped to that page
   and does not reach here. Through ::deep, because CollapsibleSection draws it (#656). */
.guests-in-defaults[b-rb6ljgjpbc]  .guests-in-defaults-card {
    background-color: var(--ffp-surface);
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius-container);
    padding: var(--ffp-space-md);
}

.guests-in-defaults[b-rb6ljgjpbc]  .guests-in-defaults-title {
    font-size: 1.125rem;
}

.guests-in-defaults-list[b-rb6ljgjpbc] {
    list-style: none;
    margin: var(--ffp-space-sm) 0 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;
}

/* Through ::deep because the sections are drawn by CollapsibleSection (#576) and carry
   its scope, not this file's; the secret panel is this file's own and matches too. */
.ffp-integrations[b-b3mw07lnl0]  .ffp-int-card {
    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);

    /* Focused only to bring it into view (#569). The card is not something to operate,
       and its warning border already marks it. */
    outline: none;

    /* Some room above it when the browser scrolls it to the edge. */
    scroll-margin-top: var(--ffp-space-lg);
}

.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;
}

/* Through ::deep because the card is drawn by CollapsibleSection (#656). */
.ffp-oidc[b-kuqqfo9tar]  .ffp-oidc-card {
    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/Admin/PendingInvitations.razor.rz.scp.css */
/* One open invitation per line, the address and what it is for on top, the dates
   under it, the action at the end - the rhythm of the guest list above. */
/* The admin screen's card, repeated: Admin.razor's .admin-card is scoped to that page
   and does not reach here. Through ::deep, because CollapsibleSection draws it (#656). */
.pending-invitations[b-gltya8thgs]  .pending-invitations-card {
    background-color: var(--ffp-surface);
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius-container);
    padding: var(--ffp-space-md);
}

.pending-invitations[b-gltya8thgs]  .pending-invitations-title {
    font-size: 1.125rem;
}

.pending-invitations-list[b-gltya8thgs] {
    list-style: none;
    margin: 0;
    padding: 0;
}

.pending-invitations-list li[b-gltya8thgs] {
    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);
}

.pending-invitation-text[b-gltya8thgs] {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.pending-invitation-who[b-gltya8thgs] {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* /Features/Channels/ChannelDetails.razor.rz.scp.css */
/* The channel's details (#562): what it is, who is in it, where its files are. Drawn
   in the context drawer of Channel.razor from 1280px and in ChannelDetailsDialog below
   that, so the rules are here, with the markup, rather than in either host. */

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

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

.ffp-context-head[b-lc19bto5vx] {
    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-lc19bto5vx] {
    margin: 0;
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ffp-muted);
}

.ffp-context-count[b-lc19bto5vx] {
    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-lc19bto5vx] {
    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-lc19bto5vx] {
    margin: 0;
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--ffp-fg);
}

.ffp-context-text[b-lc19bto5vx] {
    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-lc19bto5vx] {
    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-lc19bto5vx] {
    font-weight: 600;
    color: var(--ffp-fg);
    font-variant-numeric: tabular-nums;
}

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

.ffp-context-row[b-lc19bto5vx] {
    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-lc19bto5vx]  .mud-icon-root {
    flex: none;
    font-size: 1.125rem;
    color: var(--ffp-accent);
}

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

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

.ffp-context-hint[b-lc19bto5vx] {
    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-lc19bto5vx],
.ffp-context-row:focus-visible .ffp-context-name[b-lc19bto5vx] {
    text-decoration: underline;
}
/* /Features/Channels/ChannelDetailsDialog.razor.rz.scp.css */
/* The details as a full-screen dialog (#562). The drawer's head gives the details their
   top edge on the desktop; here the dialog's title does, and the body only needs the
   gap below it that the drawer's head leaves. The content is capped at a readable
   measure so a tablet does not stretch the cards across 800px. */
.ffp-details-dialog-body[b-35bjgrrk2c] {
    max-width: 40rem;
    margin: 0 auto;
    padding-top: var(--ffp-space-sm);
}

/* The title on the left, the close button on the right, both on one line - the drawer's
   own head, put at the top of the screen. */
.ffp-details-dialog-head[b-35bjgrrk2c] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--ffp-space-sm);
}

.ffp-details-dialog-head[b-35bjgrrk2c]  .ffp-details-dialog-title {
    margin: 0;
    color: var(--ffp-accent);
}
/* /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 - ChannelDetails.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;
}

/* The name and its status emoji (#790) on one line; the name gives way when it is long. */
.ffp-person-name-line[b-5byoy1cpx0] {
    display: flex;
    align-items: center;
    gap: var(--ffp-space-xs);
    min-width: 0;
}

/* A button that opens the profile card, drawn as the name it was. */
.ffp-person-name[b-5byoy1cpx0] {
    padding: 0;
    border: 0;
    background: none;
    font-family: inherit;
    text-align: start;
    cursor: pointer;
    min-width: 0;
    font-size: 0.75rem;
    font-weight: 700;
    color: var(--ffp-fg);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ffp-person-name:hover[b-5byoy1cpx0] {
    text-decoration: underline;
}

.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;
    }
}

/* "Show all" and "Show fewer" (#777): small and left-aligned like "Add people", a way
   into the list rather than an action on the channel. */
.ffp-member-card[b-5byoy1cpx0]  .ffp-member-more,
.ffp-member-card[b-5byoy1cpx0]  .ffp-member-fewer {
    align-self: flex-start;
    text-transform: none;
    font-size: 0.6875rem;
}

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

.ffp-member-card[b-5byoy1cpx0]  .ffp-member-retry {
    align-self: flex-start;
    text-transform: none;
}

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

.ffp-member-card[b-5byoy1cpx0]  .ffp-member-search {
    margin: 0;
}

/* "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/ChannelPictureSetting.razor.rz.scp.css */
/* The channel's picture in its details (#1101): laid out like the details' other
   sections, whose rules are scoped to ChannelDetails and do not reach in here. */
.ffp-channel-picture-setting[b-k8fnco3601] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-xs);
}

.ffp-channel-picture-label[b-k8fnco3601] {
    margin: 0;
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ffp-muted);
}

.ffp-channel-picture-hint[b-k8fnco3601] {
    margin: 0;
    font-size: 0.6875rem;
    line-height: 1.27;
    color: var(--ffp-muted);
}

/* The tile, its button and the remove link on one line, as for the workspace picture. */
.ffp-channel-picture-row[b-k8fnco3601] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ffp-space-sm) var(--ffp-space-md);
}

/* Square with the control radius, as the list draws it: circles are for people. */
.ffp-channel-picture-tile[b-k8fnco3601] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    flex: none;
    overflow: hidden;
    border-radius: var(--ffp-radius);
    background-color: var(--ffp-surface);
    color: var(--ffp-muted);
}

.ffp-channel-picture-tile[b-k8fnco3601]  .ffp-channel-picture-image {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* The label is the button; InputFile is a component, so its input is reached through
   ::deep, as on the profile page. */
.ffp-channel-picture-action[b-k8fnco3601]  .ffp-channel-picture-input {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

.ffp-channel-picture-action[b-k8fnco3601] {
    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;
}

.ffp-channel-picture-action:hover[b-k8fnco3601] {
    background-color: var(--ffp-surface);
}

.ffp-channel-picture-action:focus-within[b-k8fnco3601] {
    outline: 2px solid var(--ffp-accent);
    outline-offset: 2px;
}
/* /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;
}

/* A one-to-one conversation's face (#724) stands where the icon stands, at its size: the
   rail draws the icon at 1rem with the screen's 8px in front of the name. The face is
   inside the text box rather than in front of it, so the box gives up its own gap and
   the face takes it as a margin - the name lands on the same x as behind an icon, and
   the row keeps its height. ::deep, because the class is handed to UserAvatar and lands
   on markup it renders; .mud-nav-link in the selector because the rail's own rule for
   that gap (Sidebar.razor.css) is as specific as this one without it, and won. */
.ffp-channel-row[b-ofl9wdl274]  .mud-nav-link .mud-nav-link-text:has(> .ffp-channel-avatar) {
    margin-inline-start: 0;
}

.ffp-channel-row[b-ofl9wdl274]  .mud-nav-link .mud-nav-link-text:has(> .ffp-channel-picture-frame) {
    margin-inline-start: 0;
}

/* A channel's picture (#1101) where the # or the padlock would be, at the icon's size:
   a square with the control radius, never a circle - circles are for people and pips. */
.ffp-channel-row[b-ofl9wdl274]  .ffp-channel-picture-frame {
    position: relative;
    display: inline-flex;
    flex: none;
    width: 1rem;
    height: 1rem;
    margin-inline-end: var(--ffp-space-sm);
    vertical-align: middle;
}

.ffp-channel-row[b-ofl9wdl274]  .ffp-channel-picture {
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: var(--ffp-radius);
}

/* The padlock of a private channel, small at the picture's bottom right, on a disc of
   the rail's own colour so it reads on any picture. */
.ffp-channel-row[b-ofl9wdl274]  .ffp-channel-picture-lock {
    position: absolute;
    right: -3px;
    bottom: -3px;
    width: 0.625rem;
    height: 0.625rem;
    padding: 1px;
    border-radius: 50%;
    background-color: var(--ffp-sidebar-bg);
    color: var(--ffp-sidebar-fg);
}

.ffp-channel-row[b-ofl9wdl274]  .ffp-channel-avatar {
    width: 1rem;
    height: 1rem;
    font-size: 0.4375rem;
    margin-inline-end: var(--ffp-space-sm);
    vertical-align: middle;
}

/* Unread is weight, not colour: it has to survive a colour-blind reader and a
   high-contrast theme (N11). A muted channel with something unread and no mention is
   this and nothing else (#919). */
.ffp-channel-row[b-ofl9wdl274]  .ffp-unread .ffp-channel-name {
    font-weight: 700;
}

/* The text box is a flex row on every row, with or without a marker (#718, #734). As
   MudNavLink draws it, it is a plain block: an empty dot span stayed inline at 0x18,
   and the mention badge - a zero-size MudBadge root whose pill hangs off its corner -
   was drawn over the last letters of the name, because margin-left: auto does nothing
   to an inline box. As a flex row the name takes what is left and ellipsises, and the
   badge sits at the row's right end. (The dot is gone since #919: every unread row
   shows its number, and a muted one without a mention only its bold name.) */
.ffp-channel-row[b-ofl9wdl274]  .mud-nav-link-text {
    display: flex;
    align-items: center;
    min-width: 0;
}

/* Everything in the box but the name keeps its size - the marker at the end, and
   whatever stands in front of the name - so a long name is what gives way. */
.ffp-channel-row[b-ofl9wdl274]  .mud-nav-link-text > :not(.ffp-channel-name) {
    flex: none;
}

.ffp-channel-row[b-ofl9wdl274]  .ffp-channel-name {
    flex: 0 1 auto;
    min-width: 0;
}

/* Air between a cut-off name and the marker after it. */
.ffp-channel-row[b-ofl9wdl274]  .ffp-channel-name:not(:last-child) {
    margin-inline-end: var(--ffp-space-sm);
}

/* ::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;
}

/* MudBadge hangs its pill off the corner of what it wraps, and here it wraps nothing:
   in the flow instead, the pill takes its own 20px, centred on the name - the same
   move the "Geplant" count makes in Sidebar.razor.css. */
.ffp-channel-row[b-ofl9wdl274]  .ffp-channel-badge .mud-badge-wrapper,
.ffp-channel-row[b-ofl9wdl274]  .ffp-channel-badge .mud-badge {
    position: static;
}

/* Amber, with Tinte on it (#734). The handbook names Amber as the colour for unread
   badges, so a mention reads as the louder form of the same signal; the phone's channel list draws its mention badge the same
   way. The rail is Petrol Tief in light and dark alike, so the numbers hold in both:
   Tinte on Amber is 6.8:1 (AA for the 12px count), the pill stands off the rail at
   5.6:1 and off a hovered or active Petrol row at 4.1:1. The error red it replaces was
   1.9:1 against the rail in light (Fehler) and a salmon in dark. */
.ffp-channel-row[b-ofl9wdl274]  .ffp-channel-badge-mention .mud-badge {
    background-color: var(--ffp-amber);
    color: var(--ffp-tinte);
    font-weight: 700;
}

/* The count of a channel without a mention (#919): neutral, in Blaugrau Hell - the
   colour of the rail's icons - with Tinte on it. It says how much is waiting without
   asking for an answer, so it is quieter than Amber in hue while it holds the same
   numbers as the rail allows: Tinte on Blaugrau Hell is 6.3:1, the pill stands off the
   rail at 5.2:1 and off a hovered or active Petrol row at 3.8:1. Dunst would have been
   brighter than the Amber it is meant to be quieter than. */
.ffp-channel-row[b-ofl9wdl274]  .ffp-channel-badge-quiet .mud-badge {
    background-color: var(--ffp-blaugrau-hell);
    color: var(--ffp-tinte);
    font-weight: 700;
}

/* A muted channel still counts its mentions - it is muted, not ignored.

   Faded to 0.8, not further (#1093). The fade takes the whole row, so it is what every
   pairing in it has to hold up under, against the rail (Petrol Tief in light and dark):
   the name in Dunst comes out at 5.6:1 (AA for the 13px text, bold or not), white on a
   hovered Petrol row at 6.6:1, Tinte on the Amber pill at 4.8:1, Tinte on the neutral
   pill at 4.6:1. At 0.6 the name was 3.9:1, which axe found once #919 made a muted name
   with something unread a row the sweep could not miss. */
.ffp-muted[b-ofl9wdl274]  .mud-nav-link {
    opacity: 0.8;
}

/* 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;
}

/* The draft's pencil (#826) stands at the row's end, as Slack's does, with the unread
   marker after it when there is one. 14px, the size of the name's text, in the row's own
   colour: a note to the reader, not a signal that wants an answer. ::deep, because the
   class is handed to MudIcon. */
.ffp-channel-row[b-ofl9wdl274]  .ffp-channel-draft {
    margin-left: auto;
    font-size: 0.875rem;
    opacity: 0.8;
}

.ffp-channel-row[b-ofl9wdl274]  .ffp-channel-draft ~ .ffp-channel-badge {
    margin-left: var(--ffp-space-xs);
}
/* /Features/Channels/ConversationFiles.razor.rz.scp.css */
/* The files of a conversation in its details (#776): one line per file, the name as the
   thing to press and size, sender and day under it - the search page's file hit, made
   small enough for the drawer. On the drawer's white card, as the member list is. */

.ffp-files[b-yziehtrt6s] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-sm);
    margin: 0;
    padding: var(--ffp-space-sm) 0.75rem;
    list-style: none;
    background: var(--ffp-bg);
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius);
}

.ffp-file[b-yziehtrt6s] {
    display: flex;
    align-items: flex-start;
    gap: var(--ffp-space-sm);
    min-width: 0;
}

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

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

/* A flex row of two spans (#948): the start of the name shrinks and ends in "…" when the
   line is too narrow, the end - the extension and a bit before it - never does, so
   "IMG_8A1C…erdgeschoss.png" keeps what tells the file apart. The button is as wide as
   the column and its content starts at the left (#880): MudBlazor's reset centres it.
   pre, not nowrap, so a space at the seam is not swallowed. The full name is the
   button's title and aria-label. */
.ffp-file-open[b-yziehtrt6s] {
    display: flex;
    justify-content: flex-start;
    width: 100%;
    min-width: 0;
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    font-size: 0.75rem;
    font-weight: 600;
    color: var(--ffp-fg);
    text-align: start;
    cursor: pointer;
    white-space: pre;
    /* Whatever happens to the widths, nothing leaves the row. */
    overflow: hidden;
}

.ffp-file-name-start[b-yziehtrt6s] {
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

.ffp-file-name-end[b-yziehtrt6s] {
    flex: none;
}

.ffp-file-open:hover[b-yziehtrt6s],
.ffp-file-open:focus-visible[b-yziehtrt6s] {
    text-decoration: underline;
}

.ffp-file-meta[b-yziehtrt6s] {
    font-size: 0.6875rem;
    color: var(--ffp-muted);
}

.ffp-files-failed[b-yziehtrt6s] {
    margin: 0;
    font-size: 0.75rem;
    color: var(--ffp-fehler);
}

.ffp-files-none[b-yziehtrt6s] {
    margin: 0;
    font-size: 0.75rem;
    color: var(--ffp-muted);
}

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

.ffp-files-box[b-yziehtrt6s]  .ffp-files-more,
.ffp-files-box[b-yziehtrt6s]  .ffp-files-retry {
    align-self: flex-start;
    text-transform: none;
}
/* /Features/Channels/GroupNameSetting.razor.rz.scp.css */
/* A group conversation's name in its details (#1275): laid out like the channel picture
   above it, whose rules are scoped to its own component. */
.ffp-group-name-setting[b-qeupojrvvu] {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--ffp-space-xs);
}

.ffp-group-name-label[b-qeupojrvvu] {
    margin: 0;
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ffp-muted);
}

.ffp-group-name-row[b-qeupojrvvu] {
    display: flex;
    align-items: center;
    gap: var(--ffp-space-sm);
    width: 100%;
}

.ffp-group-name-row[b-qeupojrvvu]  .ffp-group-name-input {
    flex: 1 1 auto;
    min-width: 0;
}

.ffp-group-name-hint[b-qeupojrvvu] {
    margin: 0;
    font-size: 0.6875rem;
    line-height: 1.27;
    color: var(--ffp-muted);
}
/* /Features/Files/ImageLightbox.razor.rz.scp.css */
/* The image preview (#600). Dark in both themes, as every picture viewer is: the picture
   is what the eye should land on, and a white frame round a photo competes with it. The
   surface is the dark theme's background, the text white, the metadata Dunst - 5.7:1 on
   Petrol per the design system, more on this darker ground. */
.ffp-lightbox-frame[b-wtpxnx6euw] {
    display: flex;
    flex-direction: column;
    width: 100%;
    height: 100%;
    min-height: 0;
    color: #FFFFFF;
}

.ffp-lightbox-head[b-wtpxnx6euw] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--ffp-space-sm);
    padding: var(--ffp-space-sm) var(--ffp-space-sm) var(--ffp-space-sm) var(--ffp-space-md);
    border-bottom: 1px solid rgba(184, 211, 217, 0.2);
}

.ffp-lightbox-caption[b-wtpxnx6euw] {
    min-width: 0;
}

.ffp-lightbox-title[b-wtpxnx6euw] {
    margin: 0;
    font-size: 1rem;
    font-weight: 600;
    line-height: 1.4;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ffp-lightbox-meta[b-wtpxnx6euw] {
    margin: 0;
    font-size: 0.8125rem;
    color: var(--ffp-dunst);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Where in the conversation's pictures this one is (#607): "3 / 17". */
.ffp-lightbox-position[b-wtpxnx6euw] {
    margin: 0;
    font-size: 0.8125rem;
    font-variant-numeric: tabular-nums;
    color: var(--ffp-dunst);
}

.ffp-lightbox-actions[b-wtpxnx6euw] {
    display: flex;
    flex-shrink: 0;
    gap: var(--ffp-space-xs);
}

/* White icons on the dark ground, and a focus ring that can be seen on it. The design
   system asks for 40px targets on the desktop and 48px on touch screens. */
.ffp-lightbox-frame[b-wtpxnx6euw]  .mud-icon-button {
    color: #FFFFFF;
    min-width: 40px;
    min-height: 40px;
}

.ffp-lightbox-frame[b-wtpxnx6euw]  .mud-icon-button.mud-disabled {
    color: rgba(255, 255, 255, 0.4);
}

.ffp-lightbox-frame[b-wtpxnx6euw]  .mud-icon-button:focus-visible {
    outline: 2px solid #FFFFFF;
    outline-offset: 2px;
}

@media (pointer: coarse) {
    .ffp-lightbox-frame[b-wtpxnx6euw]  .mud-icon-button {
        min-width: 48px;
        min-height: 48px;
    }
}

.ffp-lightbox-stage[b-wtpxnx6euw] {
    position: relative;
    flex: 1;
    min-height: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--ffp-space-md);
    /* Two fingers zoom the page, one swipes to the next picture (ImageLightbox.razor). */
    touch-action: pan-y pinch-zoom;
}

/* No box of its own, so a click just beside the picture still reaches the stage and
   closes, and the picture is the flex item that gets fitted. */
.ffp-lightbox-picture[b-wtpxnx6euw] {
    display: contents;
}

.ffp-lightbox-stage[b-wtpxnx6euw]  .ffp-lightbox-image {
    display: block;
    max-width: 100%;
    max-height: 100%;
    height: auto;
    object-fit: contain;
}

.ffp-lightbox-stage[b-wtpxnx6euw]  .ffp-image-pending.ffp-lightbox-image {
    background: rgba(184, 211, 217, 0.08);
}

.ffp-lightbox-nav[b-wtpxnx6euw] {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 1;
}

.ffp-lightbox-nav-prev[b-wtpxnx6euw] {
    left: var(--ffp-space-sm);
}

.ffp-lightbox-nav-next[b-wtpxnx6euw] {
    right: var(--ffp-space-sm);
}

.ffp-lightbox-nav[b-wtpxnx6euw]  .mud-icon-button {
    background: rgba(16, 29, 33, 0.7);
}

.ffp-lightbox-status[b-wtpxnx6euw] {
    position: absolute;
    left: 50%;
    bottom: var(--ffp-space-md);
    transform: translateX(-50%);
    display: flex;
    align-items: center;
    gap: var(--ffp-space-sm);
    margin: 0;
    padding: var(--ffp-space-xs) var(--ffp-space-sm);
    border-radius: var(--ffp-radius);
    background: rgba(16, 29, 33, 0.85);
    color: var(--ffp-dunst);
    font-size: 0.8125rem;
    max-width: calc(100% - 2 * var(--ffp-space-md));
}
/* /Features/Files/TextFileViewer.razor.rz.scp.css */
/* The text preview (#779). The theme's surface and text colours, a reading column of a
   comfortable width, and the file scrolling inside the dialog rather than the page. */
.ffp-textviewer-frame[b-nv17znyaaz] {
    display: flex;
    flex-direction: column;
    width: 100%;
    height: 100%;
    min-height: 0;
    background: var(--ffp-bg);
    color: var(--ffp-fg);
}

.ffp-textviewer-head[b-nv17znyaaz] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--ffp-space-sm);
    padding: var(--ffp-space-sm) var(--ffp-space-sm) var(--ffp-space-sm) var(--ffp-space-md);
    border-bottom: 1px solid var(--ffp-border);
}

.ffp-textviewer-caption[b-nv17znyaaz] {
    min-width: 0;
}

.ffp-textviewer-title[b-nv17znyaaz] {
    margin: 0;
    font-size: 1rem;
    font-weight: 600;
    line-height: 1.4;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ffp-textviewer-meta[b-nv17znyaaz] {
    margin: 0;
    font-size: 0.8125rem;
    color: var(--ffp-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ffp-textviewer-actions[b-nv17znyaaz] {
    display: flex;
    flex-shrink: 0;
    gap: var(--ffp-space-xs);
}

.ffp-textviewer-actions[b-nv17znyaaz]  .mud-icon-button {
    min-width: 40px;
    min-height: 40px;
}

@media (pointer: coarse) {
    .ffp-textviewer-actions[b-nv17znyaaz]  .mud-icon-button {
        min-width: 48px;
        min-height: 48px;
    }
}

.ffp-textviewer-body[b-nv17znyaaz] {
    flex: 1;
    min-height: 0;
    overflow: auto;
    padding: var(--ffp-space-md);
}

.ffp-textviewer-markdown[b-nv17znyaaz] {
    max-width: 50rem;
    margin-inline: auto;
}

/* Long lines scroll sideways rather than wrap: a log or a CSV wrapped is harder to read
   than one that runs on. */
.ffp-textviewer-plain[b-nv17znyaaz] {
    margin: 0;
    font-family: ui-monospace, Menlo, "Courier New", monospace;
    font-size: 0.8125rem;
    line-height: 1.5;
    white-space: pre;
    tab-size: 4;
}

/* A CSV or TSV (#833): a header row that stays in view, cells on one line and the table
   scrolling sideways like a long log line. */
.ffp-textviewer-tablewrap[b-nv17znyaaz] {
    overflow: auto;
}

.ffp-textviewer-table[b-nv17znyaaz] {
    border-collapse: collapse;
    font-size: 0.8125rem;
    line-height: 1.5;
}

.ffp-textviewer-table th[b-nv17znyaaz],
.ffp-textviewer-table td[b-nv17znyaaz] {
    padding: 2px var(--ffp-space-sm);
    border: 1px solid var(--ffp-border);
    text-align: start;
    vertical-align: top;
    white-space: pre;
}

.ffp-textviewer-table th[b-nv17znyaaz] {
    position: sticky;
    top: 0;
    background: var(--ffp-bg);
    font-weight: 600;
}

/* Above a CSV too big for a table, shown as text instead. */
.ffp-textviewer-note[b-nv17znyaaz] {
    margin: 0 0 var(--ffp-space-sm);
    color: var(--ffp-muted);
    font-size: 0.875rem;
}

.ffp-textviewer-status[b-nv17znyaaz] {
    display: flex;
    align-items: center;
    gap: var(--ffp-space-sm);
    color: var(--ffp-muted);
}

.ffp-textviewer-truncated[b-nv17znyaaz] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ffp-space-sm);
    margin-top: var(--ffp-space-md);
    padding-top: var(--ffp-space-sm);
    border-top: 1px solid var(--ffp-border);
    color: var(--ffp-muted);
    font-size: 0.875rem;
}
/* /Features/Files/VideoPlayer.razor.rz.scp.css */
/* The video preview (#1048), dark like the image preview (ImageLightbox.razor.css) and
   for the same reason: the film is what the eye should land on. */
.ffp-videoplayer-frame[b-p4i209ld5g] {
    display: flex;
    flex-direction: column;
    width: 100%;
    height: 100%;
    min-height: 0;
    color: #FFFFFF;
}

.ffp-videoplayer-head[b-p4i209ld5g] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--ffp-space-sm);
    padding: var(--ffp-space-sm) var(--ffp-space-sm) var(--ffp-space-sm) var(--ffp-space-md);
    border-bottom: 1px solid rgba(184, 211, 217, 0.2);
}

.ffp-videoplayer-caption[b-p4i209ld5g] {
    min-width: 0;
}

.ffp-videoplayer-title[b-p4i209ld5g] {
    margin: 0;
    font-size: 1rem;
    font-weight: 600;
    line-height: 1.4;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ffp-videoplayer-meta[b-p4i209ld5g] {
    margin: 0;
    font-size: 0.8125rem;
    color: var(--ffp-dunst);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ffp-videoplayer-actions[b-p4i209ld5g] {
    display: flex;
    flex-shrink: 0;
    gap: var(--ffp-space-xs);
}

.ffp-videoplayer-frame[b-p4i209ld5g]  .mud-icon-button {
    color: #FFFFFF;
    min-width: 40px;
    min-height: 40px;
}

.ffp-videoplayer-frame[b-p4i209ld5g]  .mud-icon-button:focus-visible {
    outline: 2px solid #FFFFFF;
    outline-offset: 2px;
}

@media (pointer: coarse) {
    .ffp-videoplayer-frame[b-p4i209ld5g]  .mud-icon-button {
        min-width: 48px;
        min-height: 48px;
    }
}

.ffp-videoplayer-stage[b-p4i209ld5g] {
    position: relative;
    flex: 1;
    min-height: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--ffp-space-md);
}

.ffp-videoplayer-video[b-p4i209ld5g] {
    display: block;
    max-width: 100%;
    max-height: 100%;
    background: #000000;
}

.ffp-videoplayer-video:focus-visible[b-p4i209ld5g] {
    outline: 2px solid #FFFFFF;
    outline-offset: 2px;
}

.ffp-videoplayer-status[b-p4i209ld5g] {
    display: flex;
    align-items: center;
    gap: var(--ffp-space-sm);
    margin: 0;
    padding: var(--ffp-space-xs) var(--ffp-space-sm);
    border-radius: var(--ffp-radius);
    background: rgba(16, 29, 33, 0.85);
    color: var(--ffp-dunst);
    font-size: 0.8125rem;
    max-width: 100%;
}
/* /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/EmojiPicker.razor.rz.scp.css */
/*
    The emoji grid (#771).

    Fixed columns rather than 1fr: with 1fr the grid took the box's width, the buttons
    their glyph's, and when the glyphs won the box grew a horizontal scrollbar - the
    reaction picker had one in every browser. Eight columns of 2.25rem, and the box as
    wide as they are.

    The list under the tabs scrolls (#828): every category, each under a heading that
    sticks to the top while its emoji go by, as in Slack. Its width is the grid's plus a
    gutter kept for the vertical scrollbar, so where scrollbars take space (Windows,
    Linux) the eighth column is not squeezed under it. Where they float over the content
    (macOS) the gutter is nothing. Six rows and a heading show at a time.
*/

/* A column flexbox that may shrink: where its host caps the height (the composer's
   panel), the grid gives way and scrolls, the search field above it keeps its size. */
.emoji-picker[b-bjja5xq24y] {
    padding: 0.5rem;
    width: max-content;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

.emoji-picker[b-bjja5xq24y]  .mud-input-control {
    flex: none;
}

.emoji-tabs[b-bjja5xq24y] {
    display: flex;
    flex: none;
    justify-content: space-between;
    margin-bottom: 0.25rem;
    border-bottom: 1px solid var(--ffp-border);
}

/* The one in view is marked by the script (aria-current), which follows the scroll. */
.emoji-tab[b-bjja5xq24y] {
    width: 1.85rem;
    height: 1.85rem;
    padding: 0;
    background: none;
    border: none;
    border-bottom: 2px solid transparent;
    font-size: 1.05rem;
    line-height: 1;
    cursor: pointer;
    opacity: 0.6;
}

.emoji-tab:hover[b-bjja5xq24y],
.emoji-tab:focus-visible[b-bjja5xq24y],
.emoji-tab[aria-current="true"][b-bjja5xq24y] {
    opacity: 1;
}

.emoji-tab[aria-current="true"][b-bjja5xq24y] {
    border-bottom-color: var(--mud-palette-primary);
}

/* Positioned, so that a section's offsetTop is measured from here - the script's jump
   to a category uses it. */
.emoji-scroll[b-bjja5xq24y] {
    position: relative;
    max-height: calc(1.75rem + 6 * 2.25rem + 5 * 0.125rem);
    min-height: 2.25rem;
    width: max-content;
    overflow-x: hidden;
    overflow-y: auto;
    scrollbar-gutter: stable;
}

.emoji-heading[b-bjja5xq24y] {
    position: sticky;
    top: 0;
    z-index: 1;
    height: 1.75rem;
    line-height: 1.75rem;
    padding: 0 0.25rem;
    background: var(--mud-palette-surface);
    color: var(--ffp-muted);
    font-size: 0.8rem;
    font-weight: 600;
}

.emoji-grid[b-bjja5xq24y] {
    display: grid;
    grid-template-columns: repeat(8, 2.25rem);
    grid-auto-rows: 2.25rem;
    gap: 0.125rem;
    width: max-content;
}

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

.emoji:hover[b-bjja5xq24y],
.emoji:focus-visible[b-bjja5xq24y] {
    background: var(--mud-palette-action-default-hover);
}

.emoji-none[b-bjja5xq24y] {
    padding: 0.5rem 0.25rem;
    color: var(--ffp-muted);
    font-size: 0.85rem;
}
/* /Features/Messages/FormatToolbar.razor.rz.scp.css */
/* A row of small text buttons. In the composer the group is display: contents on the
   desktop and a row of its own on a phone (MessageComposer.razor.css, #561); under a
   message being edited (#788) it is simply this row. */
.composer-format[b-joc4g5nbub] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.15rem;
}

.tool[b-joc4g5nbub] {
    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-joc4g5nbub] {
    background: var(--mud-palette-action-default-hover);
}

/* The 48px-class target the handbook asks for in the field ("Dimensions"). */
@media (max-width: 600px) {
    .tool[b-joc4g5nbub] {
        min-width: 2.5rem;
        height: 2.5rem;
        font-size: 1rem;
    }
}
/* /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 day separators - but drawn like it since #742:
    no hairline between two messages and no card around one, the space and the metadata
    line doing the separating, and the timeline's light wash on the row under the
    pointer. Until then it was a table of rows with a Linie rule under each.
*/

.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: 0;
    background: transparent;
    border-radius: 0;
}

/* The timeline's wash (MessageRow.razor.css): Tinte at 4% over the canvas, which keeps
   the muted metadata line at 4.60:1 in light and 9.93:1 in dark. Nebel, which this was
   before, is a card colour, and it took the same line to 4.55:1. */
[b-44d683qsyi] .collection-item:hover,
[b-44d683qsyi] .collection-item:focus-within {
    background: color-mix(in srgb, var(--ffp-fg) 4%, var(--ffp-bg));
}

.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);
}

/*
    The top edge, dragged to make the box taller (#1196). The browser's own grip
    (resize: vertical) grew the box downwards from its lower corner - towards the bottom
    of the window, where the toolbar leaves a few pixels - and dragging it up shrank it.
    So the box does not offer that one, and this edge grows it upwards instead. A strip
    over the composer's border, wider than the line it shows, so it is easy to hit.
*/
.composer-grip[b-dxakswacde] {
    position: absolute;
    top: -4px;
    left: 0;
    right: 0;
    height: 8px;
    cursor: ns-resize;
    touch-action: none;
    z-index: 1;
}

.composer-grip:hover[b-dxakswacde],
.composer-grip:focus-visible[b-dxakswacde] {
    outline: none;
    background: linear-gradient(var(--ffp-accent), var(--ffp-accent)) center / 100% 2px no-repeat;
}

/* A file dragged over the box says so, or the drop looks like it did nothing. */
.composer-input[b-dxakswacde] {
    display: block;
    width: 100%;
    border: none;
    outline: none;
    resize: none;
    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;
}

/* The two groups of the bar (#561). On the desktop they are not boxes at all, and
   their buttons are items of the bar as they were before the groups existed; the phone
   rules below make each a row. */
.composer-toolbar[b-dxakswacde]  .composer-format,
.composer-actions[b-dxakswacde] {
    display: contents;
}

.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);
}

/* A picture somebody is about to send is the point of the card (#778), so it is big
   enough to recognise rather than an icon-sized square next to its file name. */
.attachment-thumb.is-local[b-dxakswacde] {
    width: 3.5rem;
    height: 3.5rem;
    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);
}

/* A private channel's lock (#993), muted like the note it replaced. The row lines up on
   the text's baseline, which an icon has none of, so it centres itself. */
.mention-lock[b-dxakswacde] {
    display: inline-flex;
    align-self: center;
    color: var(--ffp-muted);
}

/*
    The emoji panel (#771): above the composer, at its left edge, like the mention list -
    so the box and the line in it stay visible while it is open. The grid's own size is
    EmojiPicker's; this is only the frame. The backdrop is invisible and covers the page,
    so a click anywhere else closes the panel rather than doing whatever it landed on.

    Capped to the room above the composer, which the script measures on opening
    (--ffp-emoji-room): the channel's body clips with overflow: hidden, and in a low
    window the search field went off its top. A column flexbox, so the grid shrinks and
    scrolls while the search field keeps its height.
*/
.composer-emoji-panel[b-dxakswacde] {
    position: absolute;
    bottom: 100%;
    left: 0;
    margin-bottom: 0.25rem;
    max-height: var(--ffp-emoji-room, none);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    background: var(--mud-palette-surface);
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius-container);
    box-shadow: var(--ffp-shadow-overlay);
    z-index: 11;
}

.composer-emoji-backdrop[b-dxakswacde] {
    position: fixed;
    inset: 0;
    z-index: 10;
}

/*
    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;
    }

    /* #561: the composer took a third of a phone's screen - three rows of text box, a
       bar wrapped over three lines with send alone on the last, and a hint about the
       Enter key on a device without one. Now: two lines of box (it scrolls past that),
       the formatting on one row sharing its width evenly, and everything else on a
       second row with send at its right end, as an arrow. */
    /* !important: a height dragged in a wider window is an inline style (#1196), and it
       is not this screen's. */
    .composer-input[b-dxakswacde] {
        height: calc(3em + 1.4rem) !important;
    }

    /* A finger scrolls the box instead; and the edge would sit where the timeline is
       swiped. */
    .composer-grip[b-dxakswacde] {
        display: none;
    }

    .composer-toolbar[b-dxakswacde] {
        flex-direction: column;
        align-items: stretch;
        flex-wrap: nowrap;
        gap: 0;
        padding: 0 var(--ffp-space-xs) var(--ffp-space-xs);
    }

    .composer-toolbar[b-dxakswacde]  .composer-format,
    .composer-actions[b-dxakswacde] {
        display: flex;
        align-items: center;
        gap: 0.15rem;
    }

    /* Eight buttons at 40px are 320px, which a 320px screen does not have once the
       margins are off it. Shared out evenly they are 34px wide at worst and still 40px
       tall. */
    .composer-toolbar[b-dxakswacde]  .composer-format .tool {
        flex: 1 1 0;
        min-width: 0;
        /* "{ }" broke across two lines once the button got narrower than its text. */
        white-space: nowrap;
    }

    .composer-send-label[b-dxakswacde] {
        display: none;
    }

    [b-dxakswacde] .composer-send {
        min-width: 48px;
        padding: 0 var(--ffp-space-sm);
    }

    [b-dxakswacde] .composer-send .mud-button-icon-end {
        margin: 0;
    }
}

/* The hint says what Enter and Shift+Enter do, which is news only to somebody with a
   keyboard. Gone on a phone and wherever the pointer is a finger; an upload or an error
   still takes the line. */
@media (max-width: 600px), (pointer: coarse) {
    .composer-footer.is-idle[b-dxakswacde] {
        display: none;
    }
}

/*
    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;
}

/* A rendered answer (#621) brings its own paragraphs and line breaks; pre-line would
   add the whitespace between them on top. A day's agenda can be long, so it scrolls
   rather than pushing the box off the screen - the same cap the phone has. */
.ephemeral-text.is-rendered[b-dxakswacde] {
    white-space: normal;
    max-height: 15rem;
    overflow-y: auto;
}

.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/MessageEditBox.razor.rz.scp.css */
/* The edited text in a box like the composer's (#788): its formatting bar under the
   text, and the preview over it. */
.edit[b-r49rg7kaxb] {
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius-container);
    background: var(--ffp-bg);
}

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

.edit-toolbar[b-r49rg7kaxb] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.15rem;
    padding: var(--ffp-space-xs) var(--ffp-space-sm);
    border-top: 1px solid var(--ffp-border);
}

.edit-preview[b-r49rg7kaxb] {
    padding: var(--ffp-space-sm) 0.75rem;
    border-bottom: 1px solid var(--ffp-border);
    background: var(--ffp-surface);
    border-radius: var(--ffp-radius-container) var(--ffp-radius-container) 0 0;
}

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

.edit-actions[b-r49rg7kaxb] {
    display: flex;
    justify-content: flex-end;
    gap: 0.5rem;
    padding: 0 var(--ffp-space-sm) var(--ffp-space-sm);
}

.edit-tools[b-r49rg7kaxb] {
    display: flex;
    align-items: center;
    gap: 0.15rem;
}

/* The suggestions under the text (#834), in the box's flow: the composer's float above
   it, which in the scrolling timeline would be cut off over the first visible row. */
.edit-mentions[b-r49rg7kaxb] {
    max-height: 14rem;
    overflow-y: auto;
    border-top: 1px solid var(--ffp-border);
}

/* As in the composer; the bare button undoes app.css's primary fill. */
.mention-option[b-r49rg7kaxb] {
    display: flex;
    align-items: baseline;
    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-r49rg7kaxb],
.mention-option:focus-visible[b-r49rg7kaxb] {
    background: var(--ffp-surface);
}

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

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

/* A private channel's lock (#993), muted like the note it replaced. The row lines up on
   the text's baseline, which an icon has none of, so it centres itself. */
.mention-lock[b-r49rg7kaxb] {
    display: inline-flex;
    align-self: center;
    color: var(--ffp-muted);
}

/* The picker (#801) under the toolbar, in the box's flow for the same reason. */
.edit-emoji-panel[b-r49rg7kaxb] {
    display: flex;
    flex-direction: column;
    border-top: 1px solid var(--ffp-border);
}
/* /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.

    No frame and no card (#742). Messages stand flush one under another, the way Slack
    draws them: what separates them is the space between them, the avatar and the author
    line, and the one surface a row has of its own is the light wash it gets while the
    pointer or the keyboard is on it (.message:hover below).

    The handbook's two gaps - 4px between paragraphs of the same sender, 16px between
    blocks (DESIGN.md, "Rhythmic Grid") - are split between padding and margin, because
    the hover wash covers the padding and not the margin: a wash that stopped at the
    first line of text would sit on the words with no air around them. Every row has
    2px top and bottom, so two follow-ups are 4px apart; the first row of a block adds
    6px of padding and 8px of margin on top, which makes 16px between blocks.

    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: calc(var(--ffp-space-xs) + 2px) var(--ffp-row-gutter) 2px;
    margin-top: var(--ffp-space-sm);
    /* The anchor for the two floating clusters - the row actions here and ReactionBar's
       "add a reaction" button beside them - on a touch screen only. Everywhere else they
       hang on the text's frame, .message-content below (#670). */
    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] {
    padding-top: 2px;
    margin-top: 0;
}

/*
    The one surface a row has: a light wash while the pointer is on it, or the keyboard
    anywhere inside it, so the action bar that appears is seen to belong to this row and
    not to the one above it.

    Tinte at 4% over the canvas rather than a transparent tint: in light #F6F6F7 on
    white, in dark #19262A on the dark canvas - a change of 1.08:1 and 1.10:1 against the
    ground, enough to see and not enough to read as a card. Every ink on the row stays
    legible on it: the body text at 13.7:1 (light) and 13.8:1 (dark), the muted time at
    4.60:1 and 9.93:1, links and the accent at 8.36:1 and 9.17:1.

    A variable so that a list on another ground can say what its wash is: the thread
    panel stands on Nebel, where darkening would take the muted time under 4.5:1, so it
    lifts its rows to the canvas instead (ThreadPanel.razor.css).

    No transition: the list is virtualised, and a fade per row while the pointer sweeps
    over it is painting the R3 benchmark measured (Planning/16).
*/
.message:hover[b-847vjevnuj],
.message:focus-within[b-847vjevnuj] {
    background: var(--ffp-row-hover, color-mix(in srgb, var(--ffp-fg) 4%, var(--ffp-bg)));
}

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

/* Not positioned: the floating clusters hang on .message-content, whose top is the
   text's top, not on the body, whose top is the author line. */
.message-body[b-847vjevnuj] {
    min-width: 0;
}

/*
    The text's frame (#670): the text and what hangs under it - attachments,
    reactions, the thread link - and the anchor of the hover actions.

    It is exactly as wide as the text, which is what puts the actions on the text's
    corner instead of at the far edge of the timeline. .text is a block capped at 90ch,
    so it fills this frame, and the frame carries the same cap: the two right edges are
    the same line at every width. On a narrow screen both are the full column and the
    actions end up at the right edge anyway. "ch" is measured in the font of the element
    that states it, and neither this frame nor .text sets a font-size of its own, so the
    two 90ch are the same length.

    A poll has no .text; its card is capped at 36rem in PollView.razor.css, and its frame
    takes that cap instead, plus the card's 4px top margin, so the actions land on the
    card's corner rather than a little above it.

    Nothing under the text minds the cap: attachments stop at 22rem, and reaction chips
    wrap at the text's width instead of the column's, which is where the eye expects
    them anyway.
*/
.message-content[b-847vjevnuj] {
    /* Named because ReactionBar has to put its floating button beside the actions and
       cannot read this file; custom properties inherit into the child component.

       The bar is 1.45rem of button (a 1.05rem icon and 0.2rem of padding each side)
       plus 1px of padding and 1px of border top and bottom. Its lower edge stops at
       the top of the text, so it never covers the first line of the message it belongs
       to. Until #742 the text sat in a bubble with 0.45rem of padding and the bar
       overlapped the bubble's top border; with the bubble gone it hangs over the gap
       above the text instead, the way Slack's does. On a row with an author line it
       stands within that line's height, at the right end, where the name and the time
       never reach - the first row of every day and of every list has one, so the
       scroll container's clip never reaches it. On a follow-up row it rises into the
       row above, over the right end of its last line, and only while this row is
       hovered or focused; that row's own bar sits at least a line higher. */
    --ffp-hover-bar-top: calc(-1.45rem - 4px);
    --ffp-hover-bar-inset: var(--ffp-space-sm);

    position: relative;
    max-width: 90ch;
}

.message-content.poll-content[b-847vjevnuj] {
    --ffp-hover-bar-top: calc(var(--ffp-space-xs) + 0.45rem - 1.45rem - 4px);

    max-width: 36rem;
}

/* 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;
}

/* The name and the face are buttons that open the profile card (#790), drawn as what
   they were: no frame, no fill, the row's own type. The name underlines under the
   pointer and shows the focus ring, so it reads as something to click. */
.person-link[b-847vjevnuj] {
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    text-align: start;
    cursor: pointer;
}

.avatar-link[b-847vjevnuj] {
    align-self: start;
    line-height: 0;
}

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

.person-link:focus-visible[b-847vjevnuj] {
    outline: 2px solid var(--ffp-accent);
    outline-offset: 2px;
    border-radius: var(--ffp-radius);
}

/* "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;
}

/* A message pinned to the channel (#775): a faint amber wash on the row and a small
   line above the author, "Angeheftet von Anna" - Slack's mark. Amber is the beacon
   colour; at 8% it says "this one matters" without reading as the jump highlight. */
.message.pinned[b-847vjevnuj] {
    background: color-mix(in srgb, var(--ffp-amber) 8%, var(--ffp-bg));
}

/* Still a change under the pointer, so the action bar is seen to belong to this row. */
.message.pinned:hover[b-847vjevnuj],
.message.pinned:focus-within[b-847vjevnuj] {
    background: color-mix(in srgb, var(--ffp-amber) 12%, var(--ffp-bg));
}

.pinned-by[b-847vjevnuj] {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    color: var(--ffp-muted);
    font-size: 0.75rem;
    margin-bottom: 0.15rem;
}

.pinned-by svg[b-847vjevnuj] {
    width: 0.8rem;
    height: 0.8rem;
    fill: var(--ffp-amber);
}

/* 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 text. No border, no background and no padding of its own (#742): it starts
    where the author line starts, and the row around it is the only surface there is.
    Until then incoming messages sat inside a 1px Linie hairline on the canvas and the
    reader's own on Nebel; the owner asked for Slack's timeline instead, messages
    directly under one another without frames.

    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.

    The reader's own messages ("mine") look like everybody else's, as they do in Slack.
    The Nebel container was the only thing that told them apart, and the author line
    already does that; a tinted block per own message would be exactly the card the
    owner asked to take away, and against Nebel the muted time falls to 4.55:1, with
    the hover wash on top of it under 4.5:1. The class stays on the row for anything
    that needs to know. Not mirrored either, at any width (decided 21.09.2026, see
    Planning/10): a browser window is resizable, and tying the side a message sits on
    to a breakpoint throws half the conversation across the screen and back while it is
    dragged.
*/
.text[b-847vjevnuj] {
    max-width: 90ch;
    overflow-wrap: anywhere;
}

/*
    Written by a program, not by a person: a 3px vertical edge on the left of the text,
    which is what the handbook prescribes for programmatic content. It is a mark, not a
    frame - there is no edge on the other three sides and no fill behind the words.

    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-left: 3px solid var(--ffp-accent);
    padding-left: 0.6rem;
}

/* 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 -
    white, 1px Linie, 4px - is the file's, not the message's: the screens draw a file as
    a second surface under the message rather than as a decorated link, and #742, which
    took the frame off the message, left it there for that reason.
*/
[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).

    On the top right corner of the text's frame (#670), rather than at the right edge
    of the timeline, where they stood a screen's width away from a short message. Where
    they hang and how far they rise is .message-content's business, above. They are given
    the page's own surface and a hairline so that they stay legible over the hover wash
    and whatever of the row above they rise into - no shadow,
    which the timeline cannot afford per row and the handbook does not allow on an
    in-page element anyway ("Elevation & Depth": borders over shadows).

    With no reactions yet, ReactionBar's "add a reaction" button floats too, and it is
    drawn inside the end of this bar: "with-reaction-add" keeps a slot of one button and
    one gap free on the right, so the two read as one toolbar in one frame.
*/
.message-actions[b-847vjevnuj] {
    display: flex;
    gap: 0.125rem;
    position: absolute;
    top: var(--ffp-hover-bar-top);
    right: var(--ffp-hover-bar-inset);
    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-actions.with-reaction-add[b-847vjevnuj] {
    padding-right: calc(1px + 0.125rem + 1.45rem);
}

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

/*
    Invisible is not absent: a bar at opacity 0 still takes the pointer. Now that it
    rises into the row above, it would catch a pointer meant for that row and light up
    the wrong message, and the same goes for the hidden "add a reaction" button. So on a
    screen with a pointer neither takes it until its own row is hovered. Keyboard focus
    ignores pointer-events, which keeps both reachable by Tab (N11).
*/
@media (hover: hover) {
    .message-actions[b-847vjevnuj],
    .message[b-847vjevnuj]  .reaction-add {
        pointer-events: none;
    }

    .message:hover .message-actions[b-847vjevnuj],
    .message:focus-within .message-actions[b-847vjevnuj],
    .message:hover[b-847vjevnuj]  .reaction-add,
    .message:focus-within[b-847vjevnuj]  .reaction-add {
        pointer-events: auto;
    }
}

/* Appearing is the whole of the effect; a fade adds nothing anybody needs to see. */
@media (prefers-reduced-motion: reduce) {
    .message-actions[b-847vjevnuj] {
        transition: none;
    }
}

/*
    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;
}

/* 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;
    }
}

/*
    A touch screen keeps the placement from before #670: the bar and the "add a
    reaction" button at the row's top right.

    There is no hover there, so ReactionBar shows its button all the time, on every row.
    Hung on the text's corner it would rise into the message above on every row of the
    screen and cover the end of its text. Unpositioning the frame hands both clusters
    back to .message, and these are the offsets they had there.
*/
@media (hover: none) {
    .message-content[b-847vjevnuj] {
        position: static;
    }

    .message-actions[b-847vjevnuj],
    .message-actions.with-reaction-add[b-847vjevnuj] {
        top: 0;
        right: calc(var(--ffp-row-gutter) + 2.1rem);
        padding: 1px;
    }
}
/* /Features/Messages/PanelResizeHandle.razor.rz.scp.css */
/*
    The draggable left edge of a side panel (#774). An 8px strip over the panel's own
    border, invisible until it is pointed at or focused, then the border thickens in the
    brand colour - the place to grab says where it is only when it is needed.
*/
.ffp-resize-handle[b-t2d5008ivo] {
    position: absolute;
    top: 0;
    bottom: 0;
    left: -4px;
    width: 8px;
    z-index: 2;
    cursor: col-resize;
    touch-action: none;
}

.ffp-resize-handle[b-t2d5008ivo]::after {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: 3px;
    width: 2px;
    background: transparent;
    transition: background-color 120ms ease;
}

.ffp-resize-handle:hover[b-t2d5008ivo]::after,
.ffp-resize-handle:focus-visible[b-t2d5008ivo]::after {
    background: var(--ffp-accent);
}

.ffp-resize-handle:focus-visible[b-t2d5008ivo] {
    outline: none;
}

/* Where the panel takes the whole width there is nothing to drag. */
@media (max-width: 900px) {
    .ffp-resize-handle[b-t2d5008ivo] {
        display: none;
    }
}
/* /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. A pinned message is drawn
    the way the timeline draws a message since #742: no frame, no fill of its own, the
    author and the time above the text, and the space between two pins doing the
    separating. It used to be a bubble - Nebel, a hairline, 4px - because that was what a
    message looked like; it is not any more.
*/

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

.pin-body[b-3411h5o88a] {
    flex: 1;
    min-width: 0;
    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 onto the top right corner of the message's text (#670), into the slot
    the row's hover actions keep free at the end of their bar, so the two read as one
    toolbar. The positioned ancestor is MessageRow's .message-content, the text's
    frame; --ffp-hover-bar-top and --ffp-hover-bar-inset are where that bar sits,
    published by MessageRow.razor.css because this file cannot read it. The 2px are the
    bar's own border and padding, and the button is drawn at the bar's button size, so
    it lines up with its neighbours on every edge.

    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: calc(var(--ffp-hover-bar-top, 0px) + 2px);
    right: calc(var(--ffp-hover-bar-inset, 0px) + 2px);
    margin: 0;
}

@media (hover: hover) {
    .reactions.add-only[b-i4h6fnvfsw]  .mud-icon-button {
        padding: 0.2rem;
    }

    .reactions.add-only[b-i4h6fnvfsw]  .mud-icon-button svg {
        width: 1.05rem;
        height: 1.05rem;
    }
}

/* A touch screen keeps the button where it was before #670, at the row's top right
   (see the matching rule at the end of MessageRow.razor.css), and at its full size,
   which is the target a finger needs. The positioned ancestor there is .message, and
   --ffp-row-gutter is the row's side padding. */
@media (hover: none) {
    .reactions.add-only[b-i4h6fnvfsw] {
        top: 0;
        right: var(--ffp-row-gutter, 1.5rem);
    }
}

.reaction[b-i4h6fnvfsw] {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    /* No border (#674, owner's call): the chip is a tinted pill. A transparent border
       keeps the size the same as before, so nothing moves when one appears. */
    border: 1px solid transparent;
    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] {
    background: color-mix(in srgb, var(--ffp-accent) 8%, var(--ffp-surface));
}

/* Your own reaction: without a border it is marked by a stronger tint and by the count
   in the accent colour - two signals, so it does not rest on the tint alone for
   anyone with a contrast problem (N11). The tint is mixed from the accent rather than
   taken from MudBlazor's hover colour, so it follows the theme into the dark scheme. */
.reaction.mine[b-i4h6fnvfsw] {
    background: color-mix(in srgb, var(--ffp-accent) 22%, var(--ffp-bg));
}

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

.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;
}

/* 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;
    }
}

/* Like the row's actions: appearing is the whole of the effect. */
@media (prefers-reduced-motion: reduce) {
    [b-i4h6fnvfsw] .reaction-add {
        transition: none;
    }
}
/* /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.
*/
/* The width is the reader's once they drag the edge (#774): wwwroot/js/panels.js sets
   --ffp-panel-width, and 26rem stays the default. Positioned for the handle on the edge,
   and not shrunk by the stream beside it, so the width dragged is the width shown. */
.thread-panel[b-01j41c2r4f] {
    position: relative;
    flex: 0 0 auto;
    width: var(--ffp-panel-width, 26rem);
    /* 360px stay for the conversation beside it, however wide the panel was dragged and
       however the room changes after - the script measures against the same rule. */
    max-width: calc(100% - 360px);
    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.
   ::deep because .message-list belongs to MessageList.

   The rows have no frame (#742), and their hover wash is the one thing that has to know
   which ground it is on: the timeline's darkens the canvas a little, and on Nebel that
   would take the muted time from 4.55:1 to 4.23:1. So here a hovered row is lifted to
   the canvas instead - white on Nebel in light, where the time reads at 4.95:1, and the
   dark canvas on the dark surface in dark, at 10.95:1. Either way the step against the
   panel is about the same as the timeline's (1.09:1 and 1.10:1). */
.thread-body[b-01j41c2r4f]  .message-list {
    --ffp-row-hover: var(--ffp-bg);

    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%;
        max-width: 100%;
        border-left: none;
    }
}

/* A root that could not be read (#916): the reason and a way to ask again. */
.thread-load-failed[b-01j41c2r4f] {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.25rem;
    padding: 1rem 1.5rem;
}
/* /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/AvatarCropper.razor.rz.scp.css */
/* The crop step (#914): a frame in the avatar tile's shape, the picture moving under
   it, and the result at the size the profile draws it. */

.avatar-cropper[b-1lckob610x] {
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-sm);
}

.avatar-crop-stage[b-1lckob610x] {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--ffp-space-md);
}

/* The picture is laid out by AvatarCrop in pixels, so the frame is a fixed square
   (240px - narrower than any phone's content area with its gutter). touch-action:
   none, or a finger dragging the picture scrolls the page instead. */
.avatar-crop-frame[b-1lckob610x],
.avatar-crop-preview[b-1lckob610x] {
    position: relative;
    flex: none;
    overflow: hidden;
    border-radius: var(--ffp-radius);
    background: var(--ffp-petrol);
}

.avatar-crop-frame[b-1lckob610x] {
    border: 1px solid var(--ffp-border);
    box-sizing: content-box;
    cursor: grab;
    touch-action: none;
    user-select: none;
}

/* Reached with Tab; the arrow keys move the picture from here. */
.avatar-crop-frame:focus-visible[b-1lckob610x] {
    outline: 2px solid var(--ffp-petrol);
    outline-offset: 2px;
}

.avatar-crop-frame:active[b-1lckob610x] {
    cursor: grabbing;
}

.avatar-crop-preview[b-1lckob610x] {
    width: 64px;
    height: 64px;
}

.avatar-crop-frame img[b-1lckob610x],
.avatar-crop-preview img[b-1lckob610x] {
    position: absolute;
    max-width: none;
    pointer-events: none;
}

.avatar-crop-zoom[b-1lckob610x] {
    display: flex;
    align-items: center;
    gap: var(--ffp-space-sm);
    max-width: 240px;
}

.avatar-crop-zoom input[b-1lckob610x] {
    flex: 1;
    accent-color: var(--ffp-petrol);
}

.avatar-crop-actions[b-1lckob610x] {
    display: flex;
    gap: var(--ffp-space-sm);
}
/* /Features/People/PersonCard.razor.rz.scp.css */
/* The face, the names beside it, the close button at the far end - the head of the
   card, one row. */
.ffp-person-card-head[b-ntkbncckb8] {
    display: flex;
    align-items: flex-start;
    gap: var(--ffp-space-md);
}

.ffp-person-card-names[b-ntkbncckb8] {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-width: 0;
    gap: 2px;
}

.ffp-person-card-names[b-ntkbncckb8]  .ffp-person-card-name {
    margin: 0;
    color: var(--ffp-fg);
    overflow-wrap: anywhere;
}

.ffp-person-card-fullname[b-ntkbncckb8] {
    color: var(--ffp-fg);
}

.ffp-person-card-title[b-ntkbncckb8] {
    color: var(--ffp-muted);
}

/* Label beside value, on one baseline per line, under the head. */
.ffp-person-card-facts[b-ntkbncckb8] {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: baseline;
    gap: var(--ffp-space-sm) var(--ffp-space-md);
    margin: var(--ffp-space-md) 0 0;
}

.ffp-person-card-facts dt[b-ntkbncckb8] {
    color: var(--ffp-muted);
    font-size: 0.75rem;
}

.ffp-person-card-facts dd[b-ntkbncckb8] {
    margin: 0;
}

.ffp-person-card-emoji[b-ntkbncckb8] {
    margin-inline-end: var(--ffp-space-xs);
}

.ffp-person-card-until[b-ntkbncckb8] {
    color: var(--ffp-muted);
    margin-inline-start: var(--ffp-space-xs);
}

.ffp-person-card-time[b-ntkbncckb8] {
    font-variant-numeric: tabular-nums;
}

/* The presence in words, muted like the title: the colour is on the face's dot, and
   Erfolg as type would not hold AA on the dark dialog. */
.ffp-person-card-presence[b-ntkbncckb8],
.ffp-person-card-paused[b-ntkbncckb8] {
    font-size: 0.8125rem;
    color: var(--ffp-muted);
}
/* /Features/People/StatusEmoji.razor.rz.scp.css */
/* As tall as the name it follows, and never the part of a line that is cut off. */
.ffp-status-emoji[b-5xlsbd8x1b] {
    flex: none;
    font-size: 0.9em;
    line-height: 1;
    cursor: default;
}
/* /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.

   The one cell is the tile, whatever is in it (#868). An auto track grew to what the
   picture asked for: a picture taller than wide made the row taller than the tile, and
   the presence dot, at the end of that row and 40% of it high, hung out of the tile's
   bottom - an oval cut off at the corner, and only on faces with a portrait photo.

   Nothing is clipped here either. The tile used to round its corners with overflow:
   hidden, which also cut into the dot wherever the dot was smaller than the corner -
   the 1rem face in the sidebar. The picture rounds its own corners instead (below). */
.ffp-avatar[b-j9arjwh71l] {
    flex: none;
    display: inline-grid;
    grid-template-columns: 100%;
    grid-template-rows: 100%;
    place-items: center;
    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;
}

/* The product's mark on a system line (#1158). The file is its own Petrol tile with a
   thin transparent margin; the same Petrol behind it closes that margin, where the
   tile's own colour would leave a ring of a second Petrol. The general img rule
   further down sizes it, as it does a picture. */
.ffp-avatar .ffp-avatar-mark[b-j9arjwh71l] {
    background: var(--ffp-petrol);
}

.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. The corners are the tile's, taken here
   because the tile no longer clips (#868). */
.ffp-avatar[b-j9arjwh71l]  img {
    grid-area: 1 / 1;
    width: 100%;
    height: 100%;
    min-width: 0;
    min-height: 0;
    object-fit: cover;
    border-radius: inherit;
    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)); }

/* Presence (#773): a dot in the bottom right corner, in the grid cell the picture and the
   initials share, so it lies over both. A white ring sets it off the Petrol tile and any
   picture alike. Erfolg for online, as the handbook names it for presence; Amber for
   away. Sized to the tile's width and kept round by its aspect ratio, so no picture can
   stretch it; a little smaller and with a thinner ring since #868. */
.ffp-presence[b-j9arjwh71l] {
    grid-area: 1 / 1;
    align-self: end;
    justify-self: end;
    box-sizing: border-box;
    width: 34%;
    min-width: 0.4375rem;
    aspect-ratio: 1;
    border: 1.5px solid var(--ffp-weiss);
    border-radius: 50%;
    z-index: 1;
}

.ffp-avatar-small .ffp-presence[b-j9arjwh71l] {
    border-width: 1px;
}

.ffp-presence-active[b-j9arjwh71l] {
    background: var(--ffp-erfolg);
}

.ffp-presence-away[b-j9arjwh71l] {
    background: var(--ffp-amber);
}

/* Notifications paused (#946): the dot turns into a crescent moon in its own colour, so
   it still says online or away. A white disc laid over its upper right cuts the crescent
   out - white like the ring, so the cut reads as part of the ring rather than as a hole
   onto the picture beneath. */
.ffp-presence-paused.ffp-presence-active[b-j9arjwh71l] {
    background: radial-gradient(circle at 72% 28%, var(--ffp-weiss) 0 38%, var(--ffp-erfolg) 41%);
}

.ffp-presence-paused.ffp-presence-away[b-j9arjwh71l] {
    background: radial-gradient(circle at 72% 28%, var(--ffp-weiss) 0 38%, var(--ffp-amber) 41%);
}

/* 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;
}

/* The narrowest phones (#561). At 32px the bar's logo is 199px wide, and beside the
   toggle, the magnifier and the avatar that is more than 320px: the avatar was cut off
   at the right edge. At 24px it is 149px, still past the package's 120px floor for the
   wordmark, and everything fits. */
@media (max-width: 374px) {
    .ffp-appbar-brand[b-ppxqnw44zt]  .ffp-product-logo {
        width: auto;
        height: 24px;
    }
}
/* /Layout/OnlineNow.razor.rz.scp.css */
/* The faces and the count in the app bar (#874), drawn like the badge beside them: a
   ring in the quick search field's border colour, so a tile with a dark edge does not
   lose its corners into the bar. */
.ffp-online-now-activator[b-8q4uus34ay] {
    display: inline-flex;
    align-items: center;
    gap: var(--ffp-space-xs);
    margin-right: var(--ffp-space-sm);
    padding: 2px var(--ffp-space-xs);
    border-radius: var(--ffp-radius);
    color: var(--ffp-weiss);
    cursor: pointer;
}

.ffp-online-now-activator:hover[b-8q4uus34ay] {
    background: color-mix(in srgb, #000 18%, transparent);
}

.ffp-online-now-faces[b-8q4uus34ay] {
    display: inline-flex;
}

/* Overlapping, like a fanned hand of cards: three faces in the room of two. */
.ffp-online-now-faces[b-8q4uus34ay]  .ffp-online-now-face {
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--ffp-dunst) 45%, transparent);
}

.ffp-online-now-faces[b-8q4uus34ay]  .ffp-online-now-face + .ffp-online-now-face {
    margin-left: -8px;
}

.ffp-online-now-count[b-8q4uus34ay] {
    font-size: 0.875rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

/* The search from twenty people up: a plain field, drawn like the quick search's
   border on the menu's surface, so typing does not depend on MudBlazor's own input. */
.ffp-online-now-search[b-8q4uus34ay] {
    width: 100%;
    padding: 6px var(--ffp-space-sm);
    border: 1px solid var(--mud-palette-lines-inputs);
    border-radius: var(--ffp-radius);
    background: var(--mud-palette-surface);
    color: var(--mud-palette-text-primary);
    font: inherit;
}

.ffp-online-now-search:focus-visible[b-8q4uus34ay] {
    outline: 2px solid var(--mud-palette-primary);
    outline-offset: 1px;
}

/* On a phone the bar has the toggle, the logo, the magnifier and the badge already:
   one face and the count. */
@media (max-width: 599.98px) {
    .ffp-online-now-faces[b-8q4uus34ay]  .ffp-online-now-face:nth-child(n + 2) {
        display: none;
    }
}

.ffp-online-now-row[b-8q4uus34ay] {
    display: inline-flex;
    align-items: center;
    gap: var(--ffp-space-sm);
    min-width: 12rem;
}

.ffp-online-now-name[b-8q4uus34ay] {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 16rem;
}
/* /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);
}

/* Docked beside the conversation, the hairline under the head runs on as the one under
   the channel's head (#890): the same height from the same token, the block centred in
   it. Its padding and two lines of type used to make it 58px against the channel head's
   64, six pixels of step in what should be one line. Below 960px the rail lies over the
   page with no head beside it, and stays as it was. */
@media (min-width: 960px) {
    .ffp-sidebar-head[b-8wnqcu325f] {
        flex: none;
        display: flex;
        align-items: center;
        box-sizing: border-box;
        height: var(--ffp-pane-head-height);
        padding-block: 0;
    }

    .ffp-sidebar-head[b-8wnqcu325f]  .ffp-workspace-head {
        flex: 1;
    }
}

/* --- 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);
}

/* A folded group's heading carries the marker of the channels it hides (#979): the same
   pill with Tinte on it as a row (#919) - so it reads as "the rows in here have this". The heading is a flex row like a channel's text box, with the name
   giving way and the marker at its end, in front of the fold chevron. The heading's own
   colour does not touch the marker, so the numbers from ChannelRow.razor.css hold. */
.ffp-sidebar[b-8wnqcu325f]  .mud-nav-group > .mud-nav-link > .mud-nav-link-text:has(> .ffp-nav-group-title) {
    display: flex;
    align-items: center;
    min-width: 0;
}

.ffp-sidebar[b-8wnqcu325f]  .ffp-nav-group-title {
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ffp-sidebar[b-8wnqcu325f]  .ffp-nav-group-badge {
    flex: none;
    margin-inline: auto var(--ffp-space-xs);
}

/* In the flow, as the row's pill, and without the heading's capitals and spacing. */
.ffp-sidebar[b-8wnqcu325f]  .ffp-nav-group-badge .mud-badge-wrapper,
.ffp-sidebar[b-8wnqcu325f]  .ffp-nav-group-badge .mud-badge {
    position: static;
}

.ffp-sidebar[b-8wnqcu325f]  .ffp-nav-group-badge .mud-badge {
    color: var(--ffp-tinte);
    font-weight: 700;
    letter-spacing: normal;
}

/* Amber with a mention behind it, the neutral Blaugrau Hell otherwise (#919) - the two
   pills of ChannelRow.razor.css, with its numbers. */
.ffp-sidebar[b-8wnqcu325f]  .ffp-nav-group-badge-mention .mud-badge {
    background-color: var(--ffp-amber);
}

.ffp-sidebar[b-8wnqcu325f]  .ffp-nav-group-badge-quiet .mud-badge {
    background-color: var(--ffp-blaugrau-hell);
}

/* --- the count behind "Geplant" (#627) ------------------------------------ */

/* The name takes the room, so the pill sits at the end of the row where a channel's
   mention count sits. */
.ffp-sidebar[b-8wnqcu325f]  .ffp-scheduled-link .mud-nav-link-text {
    display: flex;
    align-items: center;
}

.ffp-scheduled-name[b-8wnqcu325f] {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.ffp-sidebar[b-8wnqcu325f]  .ffp-scheduled-badge {
    flex: none;
    margin-left: auto;
}

/* MudBadge hangs its pill off the corner of whatever it wraps, and here it wraps
   nothing: a box of no size, so the pill would sit above the line and past the edge of
   the row. In the flow instead, it takes its own 20px and is centred on the name. */
.ffp-sidebar[b-8wnqcu325f]  .ffp-scheduled-badge .mud-badge-wrapper,
.ffp-sidebar[b-8wnqcu325f]  .ffp-scheduled-badge .mud-badge {
    position: static;
}

/* The mention count's pill in Petrol Hell instead of the error colour: a count, not a
   call for attention. White on Petrol Hell is 5.0:1, and the pill stands off the rail at
   2.5:1 - more than the red one does (1.9:1). */
.ffp-sidebar[b-8wnqcu325f]  .ffp-scheduled-badge .mud-badge {
    background-color: var(--ffp-petrol-hell);
    color: var(--ffp-weiss);
}

/* --- 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);
}

/* Paused notifications (#660): a bell-off mark on the tile's lower corner, in Amber
   like the connection indicator - a state the person chose, but one they should not
   forget about. The ring is the bar's own colour, so the mark reads as lying on the
   tile rather than as a second control beside it. */
.ffp-user-menu-activator[b-8cblyh35dg] {
    position: relative;
}

.ffp-user-menu-paused[b-8cblyh35dg] {
    position: absolute;
    right: -6px;
    bottom: -6px;
    display: inline-flex;
    border-radius: 50%;
    padding: 1px;
    background: var(--mud-palette-appbar-background);
    color: var(--ffp-amber);
    line-height: 0;
}

.ffp-user-menu-paused[b-8cblyh35dg]  .mud-icon-root {
    font-size: 14px;
    width: 14px;
    height: 14px;
}
/* /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.

   Through ::deep because every card is drawn by CollapsibleSection (#656) and carries
   its scope, not this file's - the integrations tab's lesson from #576. */
.admin[b-eikkp73cry]  .admin-card {
    background-color: var(--ffp-surface);
    border: 1px solid var(--ffp-border);
    border-radius: var(--ffp-radius-container);
    padding: var(--ffp-space-md);
}

/* The card headings the integrations tab uses, so the tabs read alike. */
.admin[b-eikkp73cry]  .admin-card-title {
    font-size: 1.125rem;
}

/* 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 settings 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);
    margin-top: var(--ffp-space-lg);
}

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

/* Several addresses at once (#746): a line per address, 4px apart. */
.invite-outcomes[b-eikkp73cry] {
    list-style: none;
    margin-bottom: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--ffp-space-xs);
}

/* 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);
}

/* The member table's search and bots switch (#727): one line above the table, the
   field taking what the switch leaves, wrapping onto two on a phone. */
.admin-members-filter[b-eikkp73cry] {
    display: flex;
    gap: var(--ffp-space-sm) var(--ffp-space-md);
    align-items: center;
    flex-wrap: wrap;
}

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

.admin[b-eikkp73cry]  .admin-members-empty {
    color: var(--ffp-muted);
}

/* Under the table, at its right edge, where MudBlazor's own pager would sit. */
.admin-members-pager[b-eikkp73cry] {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    gap: var(--ffp-space-xs);
    margin-top: var(--ffp-space-sm);
}

.admin-members-range[b-eikkp73cry] {
    color: var(--ffp-muted);
    font-size: 0.875rem;
    margin-right: var(--ffp-space-sm);
}
/* /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. A height
       rather than a minimum, from the token the rail's head takes its own from: the two
       hairlines are one line across the window (#890). */
    box-sizing: border-box;
    height: var(--ffp-pane-head-height);
    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 name itself, which is what gives way when it is genuinely long. The heading
   around it is a flex row, and a flex container clips bare text without an ellipsis. */
.ffp-channel-identity[b-qxcqa07iqh]  .ffp-channel-title-text {
    min-width: 0;
    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;
}

/* The bare number is the phone's form; see the rules at the end of this section. */
.ffp-channel-members-short[b-qxcqa07iqh] {
    display: none;
}

.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);
}

/* A phone (#561). At 390px the name, the count and two labelled buttons do not fit on
   one line, and what gave way was the name - cut to "# ger" - while "Angeheftet" slid
   over the count. Here the name comes first: the topic goes (it is in the details), the
   count is the icon and the number with the words kept for a screen reader, and the two
   buttons are their icons, 40px square for a thumb. The name ellipsizes only when it is
   longer than what is left. The width is MudBlazor's Sm, the same one the composer's
   phone rules use. */
@media (max-width: 600px) {
    .ffp-channel-header[b-qxcqa07iqh] {
        gap: var(--ffp-space-sm);
        padding: var(--ffp-space-sm) var(--ffp-space-md);
    }

    .ffp-channel-identity[b-qxcqa07iqh]  .ffp-channel-title {
        flex: 0 1 auto;
        min-width: 0;
        max-width: none;
    }

    .ffp-channel-topic[b-qxcqa07iqh] {
        display: none;
    }

    .ffp-channel-members-full[b-qxcqa07iqh] {
        position: absolute;
        width: 1px;
        height: 1px;
        overflow: hidden;
        clip: rect(0 0 0 0);
        white-space: nowrap;
    }

    .ffp-channel-members-short[b-qxcqa07iqh] {
        display: inline;
        font-variant-numeric: tabular-nums;
    }

    .ffp-channel-actions[b-qxcqa07iqh] {
        gap: var(--ffp-space-xs);
    }

    .ffp-channel-action-label[b-qxcqa07iqh] {
        display: none;
    }

    .ffp-channel-actions[b-qxcqa07iqh]  .ffp-channel-action {
        min-width: 2.5rem;
        min-height: 2.5rem;
        padding: 0;
    }

    .ffp-channel-actions[b-qxcqa07iqh]  .ffp-channel-action .mud-button-icon-start {
        margin: 0;
    }

    .ffp-channel-actions[b-qxcqa07iqh]  .ffp-channel-action .mud-icon-root {
        font-size: 1.25rem;
    }
}

/* 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 ---------------------------------------------------- */

/* The width is the reader's once they drag the edge (#830), as the thread's is:
   wwwroot/js/panels.js sets --ffp-panel-width, and the token stays the default. Positioned
   for the handle on the edge; 360px stay for the conversation beside it, as the script
   measures too. */
.ffp-channel-context[b-qxcqa07iqh] {
    position: relative;
    flex: none;
    width: var(--ffp-panel-width, var(--ffp-context-width));
    max-width: calc(100% - 360px);
    display: flex;
    flex-direction: column;
    min-height: 0;
    background: var(--ffp-surface);
    border-inline-start: 1px solid var(--ffp-border);
}

/* With a thread open beside the stream, the thread keeps its narrowest width as well
   (#847 review): a drawer dragged wide once - the width is kept - squeezed every thread
   opened after it to a few pixels at 1280px. 320px is ThreadPanel.MinWidth; the script
   reads the same number from the thread's handle. The drawer gives way, not the thread. */
.ffp-channel-shell:has(.thread-panel) .ffp-channel-context[b-qxcqa07iqh] {
    max-width: calc(100% - 360px - 320px);
}

/* What scrolls, under the head; the drawer itself holds still for its edge. */
.ffp-context-body[b-qxcqa07iqh] {
    flex: 1;
    min-height: 0;
    overflow-y: auto;
}

/* 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);
    box-sizing: border-box;
    height: var(--ffp-pane-head-height);
    padding: var(--ffp-space-sm) var(--ffp-space-md);
}

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

/* --- 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 opens the file and the row itself does
   nothing, so it must not offer a pointer that promises otherwise. */
.hit-file[b-3l76qwyxz0] {
    cursor: default;
}

/* The name is a button (#604) drawn as the link it used to be: app.css's button.link
   gives it the accent and the underline, this keeps a long name reading from the left. */
.hit-file-open[b-3l76qwyxz0] {
    text-align: left;
    word-break: break-word;
}

.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;
    }
}
