/* =============================================================================================
   app.css — portal content styles only.

   The html/body base, scrollbars, focus ring, link colour and the .tv-btn hierarchy live in
   tv-theme.css; the tokens themselves in tv-tokens.css. Nothing here defines a token at :root,
   and nothing here carries a literal spacing value, font size or hex.
   ============================================================================================= */

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat var(--tv-space-4)/1.8rem,
                var(--palette-red-deep, hotpink);
    /* The left inset clears the 1.8rem glyph plus its gutter — a measured offset, not a step. */
    padding: var(--tv-space-4);
    padding-left: 3.7rem;
    color: var(--brand-white, hotpink);
}

.blazor-error-boundary::after {
    content: "Der er opstået en fejl.";
}

/* The circuit-failure bar Blazor unhides itself. Fixed to the bottom of the viewport, so it has
   to carry its own surface rather than inherit one. */
#blazor-error-ui {
    background: var(--palette-amber, hotpink);
    color: var(--brand-black, hotpink);
    bottom: 0;
    left: 0;
    width: 100%;
    box-sizing: border-box;
    display: none;
    position: fixed;
    z-index: 1000;
    padding: var(--tv-space-3) var(--tv-space-5);
    font-size: var(--tv-text-body);
    box-shadow: var(--tv-shadow-flyout, hotpink);
}

#blazor-error-ui .dismiss {
    cursor: pointer;
    position: absolute;
    right: var(--tv-space-3);
    top: var(--tv-space-2);
}

/* ---------------------------------------------------------------------------------------------
   App shell

   These three classes are handed to MudBlazor components (MudMainContent, MudAvatar) via their
   Class parameter. Blazor's CSS isolation only stamps its scope attribute onto elements authored
   in the .razor file itself, never onto a child component's root element, so they cannot live in
   MainLayout.razor.css — a scoped rule for them would match nothing.
   --------------------------------------------------------------------------------------------- */

.portal-main {
    /* Sides set individually, never the `padding` shorthand. MudBlazor offsets the fixed app bar
       with padding-top: var(--mud-appbar-height) on .mud-main-content; app.css loads after
       MudBlazor.min.css, so a shorthand here silently overwrites that offset and every page's
       h1 and primary action disappear behind the app bar. Our gutter goes on top of Mud's. */
    padding-top: calc(var(--mud-appbar-height, 4rem) + var(--tv-page-gutter));
    padding-inline: var(--tv-page-gutter);
    padding-bottom: var(--tv-page-gutter);
    min-height: 100vh;
}

.portal-avatar {
    cursor: pointer;
    margin-inline-end: var(--tv-space-2);
    border: 2px solid var(--tv-border-color, hotpink);
}

/* Drawer offsets.

   MudDrawerContainer builds the layout's classes from the drawer's Open state, Variant and
   Breakpoint *parameters* only — never from the actual viewport (verified against MudBlazor
   9.4.0 MudDrawerContainer.GetDrawerClass). So both of MudBlazor's offsets apply at every width:

     .mud-drawer-close-mini-md-left .mud-main-content { margin-left: --mud-drawer-width-mini-left }
     .mud-drawer-open-mini-md-left  .mud-main-content { margin-left: --mud-drawer-width-left }

   Two different widths need two different corrections, because the drawer means two different
   things below and above 600px:

     < 960px   MudDrawer renders the *open* drawer as an overlay (OverlayVisible checks
               IsBelowCurrentBreakpoint), yet MudBlazor still shifts the page by the full drawer
               width. On a 360px phone that pushes 240px of a 360px viewport off-screen behind
               the overlay. The open-state offset must go — at every width where the drawer
               overlays, not just on phones.

     < 600px   The 56px mini rail is a pointing-device affordance and simply costs too much of a
               phone's width, so the closed rail collapses entirely and its offset goes with it.
               On a tablet the rail stays: 56px out of 768px is cheap and it keeps navigation one
               tap away.

   The [class*=] selectors match whatever breakpoint suffix the drawer is configured with, so
   changing MudDrawer's Breakpoint cannot silently resurrect an offset. Specificity matches
   MudBlazor's own rules and app.css is loaded after MudBlazor.min.css, so the later origin wins
   without !important. */
@media (max-width: 959.98px) {
    [class*="mud-drawer-open-mini-"] .mud-main-content {
        margin-left: 0;
    }
}

@media (max-width: 599.98px) {
    .mud-drawer.mud-drawer-mini.mud-drawer-pos-left.mud-drawer--closed {
        width: 0;
        min-width: 0;
        overflow: hidden;
        border-inline-end: 0;
        /* Zero width clips the rail but leaves every nav link focusable, so a keyboard or switch
           user would tab through nine invisible links before reaching the page (WCAG 2.4.3,
           2.4.7). visibility:hidden takes them out of the tab order too, and unlike display:none
           it is inherited-but-overridable, so the open state needs no counter-rule. */
        visibility: hidden;
    }

    [class*="mud-drawer-close-mini-"] .mud-main-content {
        margin-left: 0;
    }
}

/* ---------------------------------------------------------------------------------------------
   Suggestion description (user-submitted HTML, sanitized server-side)
   --------------------------------------------------------------------------------------------- */

.suggestion-description {
    line-height: var(--tv-leading-normal);
    word-wrap: break-word;
    overflow-wrap: break-word;
}

.suggestion-description p {
    margin-bottom: var(--tv-space-2);
}

.suggestion-description img,
.suggestion-image {
    max-width: 100%;
    height: auto;
    border-radius: var(--tv-default-border-radius, 0.5rem);
    margin: var(--tv-space-2) 0;
    display: block;
}

.suggestion-description a {
    color: var(--tv-accent, hotpink);
    text-decoration: underline;
}

.suggestion-description pre,
.suggestion-description code {
    background-color: var(--tv-surface-alt, hotpink);
    border-radius: var(--tv-default-border-radius, 0.5rem);
    padding: var(--tv-space-1) var(--tv-space-2);
    font-family: var(--tv-font-mono);
    font-size: var(--tv-text-meta);
}

.suggestion-description pre {
    padding: var(--tv-space-4);
    overflow-x: auto;
}

/* ---------------------------------------------------------------------------------------------
   Knowledge-base article — rendered markdown

   The heading ramp is the five type steps and nothing else. h3–h6 all sit on body/meta and are
   told apart by weight, which is the house rule: a sixth size is a sign the hierarchy is being
   solved with size where it should be solved with weight.
   --------------------------------------------------------------------------------------------- */

.article-content {
    line-height: var(--tv-leading-normal);
    word-wrap: break-word;
    overflow-wrap: break-word;
    font-size: var(--tv-text-body);
}

.article-content h1,
.article-content h2,
.article-content h3,
.article-content h4,
.article-content h5,
.article-content h6 {
    margin-top: var(--tv-space-5);
    margin-bottom: var(--tv-space-2);
    line-height: var(--tv-leading-tight);
}

.article-content h1 {
    font-size: var(--tv-text-page-title);
    font-weight: var(--tv-weight-strong);
}

.article-content h2 {
    font-size: var(--tv-text-section);
    font-weight: var(--tv-weight-strong);
}

.article-content h3 {
    font-size: var(--tv-text-body);
    font-weight: var(--tv-weight-strong);
}

.article-content h4 {
    font-size: var(--tv-text-body);
    font-weight: var(--tv-weight-medium);
}

.article-content h5 {
    font-size: var(--tv-text-meta);
    font-weight: var(--tv-weight-strong);
    text-transform: uppercase;
}

.article-content h6 {
    font-size: var(--tv-text-meta);
    font-weight: var(--tv-weight-medium);
    color: var(--tv-color-text-secondary, hotpink);
}

.article-content h1:first-child,
.article-content h2:first-child,
.article-content h3:first-child {
    margin-top: 0;
}

.article-content p {
    margin-top: 0;
    margin-bottom: var(--tv-space-4);
}

.article-content ul,
.article-content ol {
    margin-top: 0;
    margin-bottom: var(--tv-space-4);
    padding-left: var(--tv-space-6);
}

.article-content li {
    margin-bottom: var(--tv-space-1);
}

.article-content li > ul,
.article-content li > ol {
    margin-top: var(--tv-space-1);
    margin-bottom: 0;
}

.article-content a {
    color: var(--tv-accent, hotpink);
    text-decoration: underline;
}

.article-content .video-embed {
    position: relative;
    width: 100%;
    max-width: 800px;
    aspect-ratio: 16 / 9;
    margin: 1rem 0;
}

.article-content .video-embed iframe {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border: 0;
    border-radius: 4px;
}

.article-content img {
    max-width: 100%;
    height: auto;
    border-radius: var(--tv-default-border-radius, 0.5rem);
    margin: var(--tv-space-2) 0;
    display: block;
}

.article-content blockquote {
    margin: 0 0 var(--tv-space-4) 0;
    padding: var(--tv-space-1) var(--tv-space-4);
    border-left: 4px solid var(--tv-accent, hotpink);
    background-color: var(--tv-surface-alt, hotpink);
    color: var(--tv-color-text-secondary, hotpink);
}

.article-content code {
    background-color: var(--tv-surface-alt, hotpink);
    border-radius: var(--tv-default-border-radius, 0.5rem);
    padding: var(--tv-space-1) var(--tv-space-2);
    font-size: var(--tv-text-meta);
    font-family: var(--tv-font-mono);
}

.article-content pre {
    background-color: var(--tv-surface-alt, hotpink);
    border-radius: var(--tv-default-border-radius, 0.5rem);
    padding: var(--tv-space-4);
    overflow-x: auto;
    margin: 0 0 var(--tv-space-4) 0;
}

.article-content pre code {
    background-color: transparent;
    padding: 0;
    font-size: var(--tv-text-meta);
}

.article-content table {
    border-collapse: collapse;
    width: auto;
    max-width: 100%;
    margin: 0 0 var(--tv-space-4) 0;
}

/* A table's minimum content width is the sum of its columns, and `max-width` cannot shrink it
   below that — so an author's four-column table is a horizontal scrollbar on the *page* at phone
   widths, dragging every other element sideways with it. Making the table its own scroll
   container confines the overflow to the table. `display: block` is what buys the scrolling, and
   it is why this is scoped to narrow viewports: it drops the table's own layout algorithm, so
   columns stop sizing to the widest cell.

   Only .article-content is listed: it is the one surface that renders author HTML through
   MarkupString. Suggestion descriptions go through a sanitizer whose allow-list has no `table`,
   and changelog descriptions are HTML-encoded plain text. */
@media (max-width: 599.98px) {
    .article-content table {
        display: block;
        width: 100%;
        overflow-x: auto;
    }
}

.article-content th,
.article-content td {
    border: 1px solid var(--tv-border-color, hotpink);
    padding: var(--tv-space-2) var(--tv-space-3);
    text-align: left;
}

.article-content th {
    background-color: var(--tv-surface-alt, hotpink);
    font-weight: var(--tv-weight-strong);
}

.article-content hr {
    border: none;
    border-top: 1px solid var(--tv-border-color, hotpink);
    margin: var(--tv-space-5) 0;
}

.article-content > *:last-child {
    margin-bottom: 0;
}

/* ---------------------------------------------------------------------------------------------
   Quill editor wrapper
   --------------------------------------------------------------------------------------------- */

.quill-editor-wrapper {
    width: 100%;
}

.quill-editor-wrapper .ql-container {
    /* A measured minimum editor height, not spacing. */
    min-height: 150px;
    font-size: var(--tv-text-body);
}

/* Snow hardcodes `font-family: Helvetica, Arial, sans-serif` on both the toolbar and the
   container, so the one place users write long-form prose was the one place rendering in a
   different typeface than the rest of the app. */
.quill-editor-wrapper .ql-toolbar.ql-snow,
.quill-editor-wrapper .ql-container.ql-snow,
.quill-editor-wrapper .ql-snow .ql-picker {
    font-family: inherit;
}

.quill-editor-wrapper .ql-editor img {
    max-width: 100%;
    height: auto;
}

/* Quill's Snow toolbar buttons are 28×24 — fine under a mouse, well under any touch guideline.
   The toolbar is `display: block` with inline-block groups, so it already wraps rather than
   overflowing; only the hit area needs growing. Coarse pointers only, as elsewhere. */
@media (pointer: coarse) {
    .quill-editor-wrapper .ql-toolbar.ql-snow button,
    .quill-editor-wrapper .ql-toolbar.ql-snow .ql-picker-label {
        min-width: 44px;
        min-height: 44px;
    }
}

/* Quill ships its own opaque Snow theme: #ccc borders, #444 glyphs, a white canvas and a #06c
   link colour, none of which know about our tokens. Untouched, the rich-text editor is a white
   box with black text in dark mode, and its border measures 1.61:1 against the surface — below
   the 3:1 WCAG 1.4.11 needs for a form control's boundary.

   These rules re-point Snow at the token set. They are scoped under .quill-editor-wrapper, which
   both raises specificity above Quill's own selectors and keeps the override off any future Quill
   instance that wants the stock look. Glyphs go to currentColor so they inherit the themed text
   colour instead of needing a second dark rule. */

.quill-editor-wrapper .ql-toolbar.ql-snow,
.quill-editor-wrapper .ql-container.ql-snow {
    border: 1px solid var(--tv-border-color-strong, hotpink);
}

/* Snow squares off both boxes; every other surface in the app is rounded. Round the outer
   corners only, so the toolbar and the canvas still read as one control. */
.quill-editor-wrapper .ql-toolbar.ql-snow {
    border-radius: var(--tv-default-border-radius, 0.5rem) var(--tv-default-border-radius, 0.5rem) 0 0;
}

.quill-editor-wrapper .ql-container.ql-snow {
    border-radius: 0 0 var(--tv-default-border-radius, 0.5rem) var(--tv-default-border-radius, 0.5rem);
}

.quill-editor-wrapper .ql-container.ql-snow {
    background-color: var(--tv-surface, hotpink);
    color: var(--tv-body-fg, hotpink);
}

.quill-editor-wrapper .ql-toolbar.ql-snow {
    background-color: var(--tv-surface-alt, hotpink);
    color: var(--tv-body-fg, hotpink);
}

.quill-editor-wrapper .ql-snow .ql-stroke {
    stroke: currentColor;
}

.quill-editor-wrapper .ql-snow .ql-fill,
.quill-editor-wrapper .ql-snow .ql-stroke.ql-fill {
    fill: currentColor;
}

.quill-editor-wrapper .ql-snow .ql-picker,
.quill-editor-wrapper .ql-snow .ql-picker-label {
    color: currentColor;
}

.quill-editor-wrapper .ql-snow .ql-picker-options {
    background-color: var(--tv-surface, hotpink);
    border-color: var(--tv-border-color-strong, hotpink);
}

/* The active/hover accent. Quill hardcodes #06c for all three. */
.quill-editor-wrapper .ql-snow .ql-toolbar button:hover,
.quill-editor-wrapper .ql-snow button:hover,
.quill-editor-wrapper .ql-snow button.ql-active,
.quill-editor-wrapper .ql-snow .ql-picker-label:hover,
.quill-editor-wrapper .ql-snow .ql-picker-item:hover {
    color: var(--tv-accent, hotpink);
}

.quill-editor-wrapper .ql-snow button:hover .ql-stroke,
.quill-editor-wrapper .ql-snow button.ql-active .ql-stroke,
.quill-editor-wrapper .ql-snow .ql-picker-label:hover .ql-stroke {
    stroke: var(--tv-accent, hotpink);
}

.quill-editor-wrapper .ql-snow button:hover .ql-fill,
.quill-editor-wrapper .ql-snow button.ql-active .ql-fill {
    fill: var(--tv-accent, hotpink);
}

.quill-editor-wrapper .ql-snow a {
    color: var(--tv-accent, hotpink);
}

/* The "skriv her" placeholder. Quill's rgba(0,0,0,0.6) is invisible on a dark canvas. */
.quill-editor-wrapper .ql-snow .ql-editor.ql-blank::before {
    color: var(--tv-color-text-secondary, hotpink);
    font-style: normal;
}

/* Retained: still applied by pages this pass does not touch. */
.cursor-pointer {
    cursor: pointer;
}

/* ---------------------------------------------------------------------------------------------
   Changelog — detail

   Classes handed to MudBlazor/Tv components via Class="…"; CSS isolation cannot stamp its scope
   attribute onto a child component's own markup, so a scoped rule for them would match nothing.
   See MainLayout.razor.css.
   --------------------------------------------------------------------------------------------- */

.changelog-detail-back {
    margin-bottom: var(--tv-space-3);
}

.changelog-detail-card {
    border: 1px solid var(--tv-border-color);
    border-radius: var(--tv-default-border-radius, 0.5rem);
    margin-bottom: var(--tv-section-gap);
}

.changelog-detail-card .mud-card-content {
    padding: var(--tv-space-4);
}

.changelog-detail-suggestion-link {
    display: flex;
    align-items: center;
    gap: var(--tv-space-1);
    font-size: var(--tv-text-body);
    margin-bottom: var(--tv-space-2);
}

.changelog-detail-github {
    margin-top: var(--tv-space-2);
}

.changelog-detail-delete-btn {
    margin-top: var(--tv-space-3);
}

/* MudCard is a flex column, so a button child stretches to the full card width unless the
   cross-axis alignment is pinned. Matches .suggestion-detail-comment-form. */
.changelog-detail-comment-form {
    background-color: var(--tv-surface);
    border: 1px solid var(--tv-border-color);
    border-radius: var(--tv-default-border-radius, 0.5rem);
    padding: var(--tv-space-3);
    margin-bottom: var(--tv-space-4);
    align-items: flex-start;
    gap: var(--tv-space-2);
}

.changelog-detail-reply-form {
    background-color: var(--tv-surface);
    border: 1px solid var(--tv-border-color);
    border-left: 2px solid var(--tv-border-color-strong);
    border-radius: var(--tv-default-border-radius, 0.5rem);
    padding: var(--tv-space-3);
    margin: 0 0 var(--tv-space-2) var(--tv-space-6);
    align-items: flex-start;
    gap: var(--tv-space-2);
}

.changelog-detail-vote-card,
.changelog-detail-subscribe-btn {
    border: 1px solid var(--tv-border-color);
    border-radius: var(--tv-default-border-radius, 0.5rem);
}

.changelog-detail-vote-card {
    margin-bottom: var(--tv-space-3);
}

.changelog-detail-vote-content {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--tv-space-1);
    padding: var(--tv-space-4) !important;
}

.changelog-detail-subscribe-btn {
    width: 100%;
}

/* ---------------------------------------------------------------------------------------------
   Knowledgebase — editor

   Classes handed to MudBlazor/Tv components via Class="…"; CSS isolation cannot stamp its scope
   attribute onto a child component's own markup, so a scoped rule for them would match nothing.
   See MainLayout.razor.css.
   --------------------------------------------------------------------------------------------- */

.kb-editor-breadcrumbs {
    margin-bottom: var(--tv-space-4);
}

.kb-editor-status-banner {
    margin-bottom: var(--tv-space-4);
    display: flex;
    align-items: flex-start;
    gap: var(--tv-space-2);
}

.kb-editor-paper {
    border: 1px solid var(--tv-border-color, hotpink);
    border-radius: var(--tv-default-border-radius, 0.5rem);
    padding: var(--tv-space-4);
}

.kb-editor-tabs {
    margin-bottom: var(--tv-space-4);
}

.kb-editor-tabs .mud-tab {
    display: inline-flex;
    align-items: center;
    gap: var(--tv-space-1);
}

.kb-editor-upload-progress {
    margin-top: var(--tv-space-2);
}

.kb-editor-upload-error {
    margin: var(--tv-space-2) 0 var(--tv-space-1);
}

.kb-editor-preview {
    padding: var(--tv-space-4);
    margin-top: var(--tv-space-2);
    min-height: 200px;
    border-radius: var(--tv-default-border-radius, 0.5rem);
}

.kb-editor-change-summary {
    margin-bottom: var(--tv-space-4);
}

.kb-editor-divider {
    margin: var(--tv-space-4) 0;
}

.kb-editor-discard-confirm {
    margin-top: var(--tv-space-2);
}

.kb-editor-error {
    margin-top: var(--tv-space-2);
}

/* ---------------------------------------------------------------------------------------------
   Knowledgebase — article detail

   Classes handed to MudBlazor/Tv components via Class="…"; CSS isolation cannot stamp its scope
   attribute onto a child component's own markup, so a scoped rule for them would match nothing.
   See MainLayout.razor.css.
   --------------------------------------------------------------------------------------------- */

.kb-article-breadcrumbs {
    margin-bottom: var(--tv-space-4);
}

.kb-article-draft-banner {
    margin-bottom: var(--tv-space-4);
}

.kb-article-draft-indicator {
    font-size: var(--tv-text-meta);
    font-weight: var(--tv-weight-medium);
    color: color-mix(in srgb, var(--article-status-draft, hotpink) 55%, var(--tv-body-fg));
    display: inline-flex;
    align-items: center;
    gap: var(--tv-space-1);
}

.kb-article-paper {
    border: 1px solid var(--tv-border-color, hotpink);
    border-radius: var(--tv-default-border-radius, 0.5rem);
    padding: var(--tv-space-5);
    margin-bottom: var(--tv-space-4);
}

.kb-article-category-tag {
    display: inline-flex;
    align-items: center;
    gap: var(--tv-space-1);
    font-size: var(--tv-text-meta);
    color: var(--tv-color-text-secondary, hotpink);
    background: var(--tv-surface-alt, hotpink);
    border-radius: var(--tv-default-border-radius, 0.5rem);
    padding: var(--tv-space-1) var(--tv-space-2);
}

.kb-article-divider {
    margin: var(--tv-space-4) 0;
}

.kb-article-comment-input {
    margin-bottom: var(--tv-space-2);
}

.kb-article-comment-list {
    margin-top: var(--tv-space-4);
}

/* ---------------------------------------------------------------------------------------------
   Suggestions — detail

   Classes handed to MudBlazor/Tv components via Class="…"; CSS isolation cannot stamp its scope
   attribute onto a child component's own markup, so a scoped rule for them would match nothing.
   See MainLayout.razor.css.
   --------------------------------------------------------------------------------------------- */

.suggestion-detail-back {
    margin-bottom: var(--tv-space-2);
}

.suggestion-detail-card,
.suggestion-detail-vote-card,
.suggestion-detail-sidebar-card {
    border: 1px solid var(--tv-border-color);
    border-radius: var(--tv-default-border-radius);
    margin-bottom: var(--tv-space-4);
}

.suggestion-detail-changelog-alert {
    margin-top: var(--tv-space-3);
    display: flex;
    align-items: center;
    gap: var(--tv-space-2);
}

.suggestion-detail-vote-card-content {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--tv-space-2);
    padding: var(--tv-space-4);
}

.suggestion-detail-sidebar-item {
    margin-bottom: var(--tv-space-3);
}

.suggestion-detail-full-width {
    width: 100%;
}

.suggestion-detail-sidebar-divider {
    margin: var(--tv-space-3) 0;
}

/* ---------------------------------------------------------------------------------------------
   Knowledgebase — index

   Classes handed to MudBlazor/Tv components via Class="…"; CSS isolation cannot stamp its scope
   attribute onto a child component's own markup, so a scoped rule for them would match nothing.
   See MainLayout.razor.css.
   --------------------------------------------------------------------------------------------- */

.kb-index-sidebar {
    border: 1px solid var(--tv-border-color, hotpink);
    border-radius: var(--tv-default-border-radius, 0.5rem);
    padding: var(--tv-space-4);
}

.kb-index-category-depth-1 {
    padding-inline-start: var(--tv-space-4);
}

.kb-index-category-depth-2 {
    padding-inline-start: var(--tv-space-6);
}

.kb-index-article-card {
    border: 1px solid var(--tv-border-color, hotpink);
    border-radius: var(--tv-default-border-radius, 0.5rem);
    margin-bottom: var(--tv-space-3);
    transition: box-shadow 0.15s ease-in-out;
}

.kb-index-article-card:hover {
    box-shadow: var(--tv-shadow-flyout, hotpink);
}

.kb-index-article-title {
    font-size: var(--tv-text-section);
    font-weight: var(--tv-weight-strong);
}

.kb-index-draft-indicator {
    font-size: var(--tv-text-meta);
    color: color-mix(in srgb, var(--article-status-draft, hotpink) 55%, var(--tv-body-fg));
    display: inline-flex;
    align-items: center;
    gap: var(--tv-space-1);
}

/* ---------------------------------------------------------------------------------------------
   Knowledgebase — category listing

   Classes handed to MudBlazor/Tv components via Class="…"; CSS isolation cannot stamp its scope
   attribute onto a child component's own markup, so a scoped rule for them would match nothing.
   See MainLayout.razor.css.
   --------------------------------------------------------------------------------------------- */

.kb-category-breadcrumbs {
    margin-bottom: var(--tv-space-4);
}

.kb-category-article-list {
    margin-top: var(--tv-space-4);
}

.kb-category-article-card {
    border: 1px solid var(--tv-border-color, hotpink);
    border-radius: var(--tv-default-border-radius, 0.5rem);
    transition: box-shadow 0.15s ease-in-out;
}

.kb-category-article-card:hover {
    box-shadow: var(--tv-shadow-flyout, hotpink);
}

.kb-category-article-title {
    font-size: var(--tv-text-section);
    font-weight: var(--tv-weight-strong);
}

.kb-category-draft-indicator {
    font-size: var(--tv-text-meta);
    color: color-mix(in srgb, var(--article-status-draft, hotpink) 55%, var(--tv-body-fg));
    display: inline-flex;
    align-items: center;
    gap: var(--tv-space-1);
}

/* ---------------------------------------------------------------------------------------------
   Suggestions — create / edit form

   Classes handed to MudBlazor/Tv components via Class="…"; CSS isolation cannot stamp its scope
   attribute onto a child component's own markup, so a scoped rule for them would match nothing.
   See MainLayout.razor.css.
   --------------------------------------------------------------------------------------------- */

.suggestion-create-warning {
    display: flex;
    align-items: flex-start;
    gap: var(--tv-space-2);
    margin-bottom: var(--tv-space-4);
}

.suggestion-create-card {
    border: 1px solid var(--tv-border-color);
    border-radius: var(--tv-default-border-radius);
}

.suggestion-create-description-label {
    margin-top: var(--tv-space-4);
    margin-bottom: var(--tv-space-1);
}

.suggestion-create-status {
    margin-top: var(--tv-space-4);
}

.suggestion-create-actions {
    display: flex;
    gap: var(--tv-space-2);
    padding: var(--tv-space-4);
}

/* ---------------------------------------------------------------------------------------------
   Knowledgebase — revision history

   Classes handed to MudBlazor/Tv components via Class="…"; CSS isolation cannot stamp its scope
   attribute onto a child component's own markup, so a scoped rule for them would match nothing.
   See MainLayout.razor.css.
   --------------------------------------------------------------------------------------------- */

.kb-history-breadcrumbs {
    margin-bottom: var(--tv-space-4);
}

.kb-history-revision-paper {
    border: 1px solid var(--tv-border-color, hotpink);
    border-radius: var(--tv-default-border-radius, 0.5rem);
    padding: var(--tv-space-4);
    margin-bottom: var(--tv-space-4);
}

.kb-history-revision-divider {
    margin-bottom: var(--tv-space-4);
}

/* ---------------------------------------------------------------------------------------------
   Admin — audit log

   Classes handed to MudBlazor/Tv components via Class="…"; CSS isolation cannot stamp its scope
   attribute onto a child component's own markup, so a scoped rule for them would match nothing.
   See MainLayout.razor.css.
   --------------------------------------------------------------------------------------------- */

.audit-log-filters-clear {
    align-self: center;
    justify-self: start;
}

.audit-log-cell-mono {
    font-family: var(--tv-font-mono);
    font-size: var(--tv-text-meta);
}

/* ---------------------------------------------------------------------------------------------
   Suggestions — list

   Classes handed to MudBlazor/Tv components via Class="…"; CSS isolation cannot stamp its scope
   attribute onto a child component's own markup, so a scoped rule for them would match nothing.
   See MainLayout.razor.css.
   --------------------------------------------------------------------------------------------- */

.suggestion-list-toolbar {
    margin-bottom: var(--tv-space-4);
}

.suggestion-list-pagination {
    display: flex;
    justify-content: center;
    margin-top: var(--tv-space-4);
}

/* ---------------------------------------------------------------------------------------------
   Changelog — list

   Classes handed to MudBlazor/Tv components via Class="…"; CSS isolation cannot stamp its scope
   attribute onto a child component's own markup, so a scoped rule for them would match nothing.
   See MainLayout.razor.css.
   --------------------------------------------------------------------------------------------- */

.changelog-list-entry {
    margin-bottom: var(--tv-space-3);
    border: 1px solid var(--tv-border-color);
    border-radius: var(--tv-default-border-radius, 0.5rem);
    cursor: pointer;
    transition: box-shadow 0.15s ease-in-out;
}

.changelog-list-entry:hover,
.changelog-list-entry:focus-within {
    box-shadow: var(--tv-shadow-flyout);
}

/* ---------------------------------------------------------------------------------------------
   Admin — dashboard

   Classes handed to MudBlazor/Tv components via Class="…"; CSS isolation cannot stamp its scope
   attribute onto a child component's own markup, so a scoped rule for them would match nothing.
   See MainLayout.razor.css.
   --------------------------------------------------------------------------------------------- */

.admin-index-card-action {
    margin-top: var(--tv-space-2);
}

/* ---------------------------------------------------------------------------------------------
   Users — list

   Classes handed to MudBlazor/Tv components via Class="…"; CSS isolation cannot stamp its scope
   attribute onto a child component's own markup, so a scoped rule for them would match nothing.
   See MainLayout.razor.css.
   --------------------------------------------------------------------------------------------- */

.user-list-search-field {
    flex: 1 1 auto;
}

/* ---------------------------------------------------------------------------------------------
   Quill editor — upload progress

   Classes handed to MudBlazor/Tv components via Class="…"; CSS isolation cannot stamp its scope
   attribute onto a child component's own markup, so a scoped rule for them would match nothing.
   See MainLayout.razor.css.
   --------------------------------------------------------------------------------------------- */

.quill-editor-upload-progress {
    margin-top: var(--tv-space-1);
}

/* ---------------------------------------------------------------------------------------------
   Changelog — comment card

   Classes handed to MudBlazor/Tv components via Class="…"; CSS isolation cannot stamp its scope
   attribute onto a child component's own markup, so a scoped rule for them would match nothing.
   See MainLayout.razor.css.
   --------------------------------------------------------------------------------------------- */

.changelog-comment-content {
    padding: var(--tv-space-3);
}

/* Set on <MudCard Class="…"> and <TvButton Class="…"> in ChangelogCommentCard.razor, so they
   cannot be scoped from that component's own .razor.css. */
.changelog-comment-card {
    padding: var(--tv-space-2) 0 var(--tv-space-2) var(--tv-space-3);
    margin-bottom: var(--tv-space-2);
    border-left: 2px solid var(--tv-border-color, hotpink);
}

.changelog-comment-card-reply {
    margin-left: var(--tv-space-6);
}

.changelog-comment-voted {
    color: var(--tv-accent, hotpink);
}

/* Set on <MudCard Class="…"> and <TvButton Class="…"> in Knowledgebase/Article.razor. The
   feedback-active rule carries the "you already voted" state that used to be a MudBlazor
   Color.Success / Color.Error. */
.kb-article-comment-card {
    padding: var(--tv-space-2) 0 var(--tv-space-2) var(--tv-space-3);
    margin-bottom: var(--tv-space-2);
    border-left: 2px solid var(--tv-border-color, hotpink);
}

.kb-article-comment-reply {
    margin-left: var(--tv-space-6);
}

.kb-article-feedback-active {
    color: var(--tv-accent, hotpink);
    border-color: var(--tv-accent, hotpink);
}

/* Set on <MudNavMenu Class="…"> in Knowledgebase/Index.razor, so a ::deep rule anchored on it
   in that file's .razor.css never matches. */
.kb-index-category-nav .mud-nav-link {
    display: flex;
    align-items: center;
    gap: var(--tv-space-2);
}
