/* App-wide design polish. Sits on top of the MudBlazor theme in
   ThemeDefinitions.cs — palette values come from the --mud-palette-* vars so
   the two stay in sync. Global sheet (no scoped-CSS bundle in this app). */

/* ── StravixVMS.Player theme ─────────────────────────────────────────────── */
/* The package paints with its own --stravix-* tokens; these map them onto the MudBlazor
   palette so the player and timeline follow the app theme. */

:root {
    --stravix-player-accent: var(--mud-palette-primary, #7c4dff);
    --stravix-player-recording: var(--mud-palette-error, #f44336);
    --stravix-timeline-coverage: var(--mud-palette-success, #4caf50);
    --stravix-timeline-coverage-darken: var(--mud-palette-success-darken, #388e3c);
    --stravix-timeline-coverage-hover: var(--mud-palette-success-lighten, #66bb6a);
    --stravix-timeline-note: var(--mud-palette-info, #2196f3);
    --stravix-timeline-note-hover: var(--mud-palette-info-lighten, #64b5f6);
    --stravix-timeline-cursor: var(--mud-palette-warning, #ff9800);
    --stravix-timeline-segment: var(--mud-palette-secondary, #9c27b0);
    --stravix-timeline-segment-hover: var(--mud-palette-secondary-lighten, #ba68c8);
}

/* ── Base ────────────────────────────────────────────────────────────────── */

html {
    background: var(--mud-palette-background);
}

html::before {
    content: '';
    position: fixed;
    inset: 0;
    background: url('/img/wolf_only.png') no-repeat center center;
    background-size: contain;
    opacity: 0.05;
    pointer-events: none;
    z-index: 0;
}

::selection {
    background: rgba(93, 124, 249, 0.35);
}

/* Subtle slate scrollbars instead of the browser default */
* {
    scrollbar-width: thin;
    scrollbar-color: rgba(148, 163, 184, 0.25) transparent;
}

*::-webkit-scrollbar {
    width: 8px;
    height: 8px;
}

*::-webkit-scrollbar-thumb {
    background: rgba(148, 163, 184, 0.25);
    border-radius: 4px;
}

*::-webkit-scrollbar-track {
    background: transparent;
}

/* ── App bar / nav ───────────────────────────────────────────────────────── */

.mud-appbar {
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border-bottom: 1px solid rgba(148, 163, 184, 0.10);
}

.app-brand {
    display: flex;
    align-items: center;
    gap: 10px;
    text-decoration: none;
    color: inherit;
    margin-right: 16px;
}

.app-brand__logo {
    height: 36px;
    width: auto;
    flex-shrink: 0;
}

.app-brand__version {
    font-size: 0.66rem;
    color: var(--mud-palette-text-secondary);
    background: rgba(148, 163, 184, 0.12);
    padding: 2px 7px;
    border-radius: 999px;
    line-height: 1.3;
    white-space: nowrap;
}

.nav-btn {
    color: var(--mud-palette-text-secondary) !important;
    border-radius: 8px;
    transition: color 0.15s ease, background 0.15s ease;
}

.nav-btn:hover {
    color: var(--mud-palette-text-primary) !important;
    background: rgba(148, 163, 184, 0.08);
}

.nav-btn--active {
    color: var(--mud-palette-text-primary) !important;
    background: rgba(93, 124, 249, 0.14);
}

.nav-btn--active:hover {
    background: rgba(93, 124, 249, 0.20);
}

.nav-btn--active .mud-icon-root {
    color: var(--mud-palette-primary);
}

/* ── Page header ─────────────────────────────────────────────────────────── */

.page-header {
    display: flex;
    align-items: center;
    gap: 14px;
    margin-bottom: 24px;
}

.page-header__icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 42px;
    height: 42px;
    border-radius: 12px;
    background: rgba(93, 124, 249, 0.12);
    border: 1px solid rgba(93, 124, 249, 0.25);
    flex-shrink: 0;
}

.page-header__icon .mud-icon-root {
    color: var(--mud-palette-primary);
}

.page-header__text {
    flex: 1;
    min-width: 0;
}

.page-header__subtitle {
    color: var(--mud-palette-text-secondary);
    margin-top: 1px;
}

.page-header__actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-shrink: 0;
    flex-wrap: wrap;
}

/* ── Cards / surfaces ────────────────────────────────────────────────────── */

.surface-card {
    background: var(--mud-palette-surface);
    border: 1px solid rgba(148, 163, 184, 0.10);
    border-radius: 12px;
}

.camera-card {
    transition: transform 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}

.camera-card:hover {
    transform: translateY(-2px);
    border-color: rgba(93, 124, 249, 0.45);
    box-shadow: 0 8px 28px rgba(0, 0, 0, 0.35);
}

/* ── Status dots ─────────────────────────────────────────────────────────── */

.status-dot {
    display: inline-block;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: #566073;
    flex-shrink: 0;
}

.status-dot--ok {
    background: var(--mud-palette-success);
    box-shadow: 0 0 6px rgba(45, 190, 114, 0.7);
}

.status-dot--bad {
    background: var(--mud-palette-error);
    box-shadow: 0 0 6px rgba(239, 83, 80, 0.7);
}

.status-dot--rec {
    background: var(--mud-palette-error);
    animation: rec-pulse 1.4s ease-in-out infinite;
}

/* Switch/LED test dots: explicit "released/open" (grey) and "unknown" (amber). */
.status-dot--off {
    background: #566073;
}

.status-dot--unknown {
    background: var(--mud-palette-warning);
    box-shadow: 0 0 6px rgba(255, 152, 0, 0.6);
}

@keyframes rec-pulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(239, 83, 80, 0.55); }
    50%      { box-shadow: 0 0 0 6px rgba(239, 83, 80, 0); }
}

/* ── Video frames ────────────────────────────────────────────────────────── */

.video-frame {
    overflow: hidden;
    border-radius: 12px;
    border: 1px solid rgba(148, 163, 184, 0.12);
    background: #000;
    box-shadow: 0 10px 36px rgba(0, 0, 0, 0.45);
}

/* ── Empty states ────────────────────────────────────────────────────────── */

.empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    gap: 10px;
    padding: 56px 24px;
    border: 1px dashed rgba(148, 163, 184, 0.25);
    border-radius: 14px;
}

.empty-state__icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 64px;
    height: 64px;
    border-radius: 18px;
    background: rgba(93, 124, 249, 0.10);
    margin-bottom: 4px;
}

.empty-state__icon .mud-icon-root {
    font-size: 2rem;
    color: var(--mud-palette-primary);
}

/* Drawer panel-header actions (Export, Share) stack vertically, so they are given one
   width rather than each sizing to its own label. The right margin insets them from the
   panel's expand chevron, which they otherwise sit flush against. */

.drawer-header-action {
    min-width: 96px;
    margin-right: 16px;
}

/* Drawer panel tables size to their content and only start scrolling once they would
   push the rest of the panel off-screen. MudTable's own Height would make a one-row table
   as tall as an empty ten-row one, which is what left the drawer mostly whitespace. The
   sticky header is what FixedHeader gave us and has to be re-stated here, since that
   option needs the fixed Height this replaces. */

.panel-table-scroll {
    max-height: 320px;
    overflow-y: auto;
}

.panel-table-scroll thead th {
    position: sticky;
    top: 0;
    z-index: 1;
    background-color: var(--mud-palette-surface);
}

/* ── Modal scrim ─────────────────────────────────────────────────────────── */

/* The palette's OverlayDark is near-identical to Background, so the default scrim dims a
   near-black page with near-black. Must out-specify MudBlazor's own
   .mud-overlay .mud-overlay-scrim.mud-overlay-dark or it silently does nothing. */

.mud-overlay.mud-overlay-dialog .mud-overlay-scrim.mud-overlay-dark {
    background-color: rgba(4, 6, 10, 0.72);
    backdrop-filter: blur(2px);
    -webkit-backdrop-filter: blur(2px);
}

/* Match the 24px the dialog title and content already use (MudBlazor ships 8px here).
   The .mud-dialog prefix is required to match its selector's specificity. */

.mud-dialog .mud-dialog-actions {
    padding: 12px 24px 16px;
}

/* MudBlazor draws an outlined input's border as <fieldset><legend>. The legend is
   height:0 + visibility:hidden and only reserves the floating label's gap, but its text
   still lays out an ~18px line box that escapes as scrollable overflow — which reaches
   .mud-dialog-content (overflow:auto, sized to its content) and shows a scrollbar on a
   dialog that fits. One untouched input is enough. Clipping costs nothing: the legend has
   no visible content, and its width still draws the notch. */

.mud-input-outlined-border > legend {
    overflow: hidden;
}

/* ── TabbedEditDialog — tab strip ────────────────────────────────────────── */

.tabbed-edit-dialog__tabs .mud-tab {
    justify-content: flex-start;
}

/* Horizontal rail: MudBlazor's width:100% would give each tab the full bar, leaving one
   on screen and the rest behind the scroll arrows. (The 160px floor beside it is the
   MinimumTabWidth parameter, which lands inline and can't be overridden here.) */
.tabbed-edit-dialog__tabs:not(.mud-tabs-vertical) .mud-tab {
    width: auto;
}

/* Icons only, so every tab fits without the scroll arrows. The label is a bare text node
   with no element to hide, hence font-size:0 — which leaves the tab its accessible name and
   doesn't touch the rem-sized icon. The trimmed padding is what fits the 8-tab camera editor
   (edit mode) in a 390px phone's 374px bar: 44px a tab, not MudBlazor's 48px. */
.tabbed-edit-dialog__tabs:not(.mud-tabs-vertical) .mud-tab:has(> .mud-icon-root) {
    font-size: 0;
    padding-inline: 10px;
}

.tabbed-edit-dialog__tabs:not(.mud-tabs-vertical) .mud-tab:has(> .mud-icon-root) .mud-tab-icon-text {
    margin: 0;
}

/* The dialog's own margin, which the shell can't set: MudBlazor renders .mud-dialog itself. */
.mud-dialog.mud-dialog-width-full:has(.tabbed-edit-dialog__tabs:not(.mud-tabs-vertical)) {
    width: calc(100% - 16px);
}

/* mud-tab-panel renders as display:contents, so its direct child becomes a flex
   item of the 420px-tall panels row and gets stretched to full height. Pinning
   align-self to flex-start gives it a natural height so flex-grow on inner
   mud-input-control elements has nothing to grow into. width:100% keeps it
   filling the horizontal space. */
.tabbed-edit-dialog__tabs .mud-tab-panel[role="tabpanel"] > * {
    align-self: flex-start;
    width: 100%;
}

/* The error badge sits on the icon's top-right corner, clear of the label (it ends at 44px,
   where the desktop rail's text starts). In normal flow it adds ~28px to the tab, which on a
   phone pushes a 7-tab editor past the bar — and MudTabs then keeps the scroll arrows on for
   the rest of the session, even once the errors are fixed. */
.tabbed-edit-dialog__tabs .mud-tab:has(> .mud-icon-root) {
    position: relative;
}

.tabbed-edit-dialog__tabs .mud-tab:has(> .mud-icon-root) .mud-tab-badge {
    position: absolute;
    top: calc(50% - 22px);
    left: 24px;
    margin: 0;
}

/* A childless MudBadge spans zero size, so these give it its own dimensions instead of
   letting it collapse. */
.tabbed-edit-dialog__tabs .mud-tab .mud-tab-badge .mud-badge-wrapper {
    position: static;
    width: auto;
    height: auto;
}

.tabbed-edit-dialog__tabs .mud-tab .mud-tab-badge .mud-badge {
    position: static;
}

/* Fixed-height variant: the dialog doesn't resize between tabs — content scrolls
   instead of expanding the dialog. Applied when FixedHeight="true" is set on
   TabbedEditDialog. max-height:100% constrains the panels div to its flex parent's
   height (which has an explicit height set inline); without it, overflow-y:auto alone
   does nothing because the div can still grow taller than the parent. A short tab
   leaves empty space; a tall one scrolls within the panel area. */
.tabbed-edit-dialog__fixed .mud-tabs-panels {
    overflow-y: auto;
    max-height: 100%;
}

/* NetworkDiagnostics' TraceSummaryDialog: tcpdump's lines kept whole, scrolling sideways
   rather than wrapping so each packet stays on one line. */
.trace-summary {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.75rem;
    line-height: 1.4;
    white-space: pre;
    overflow: auto;
    max-height: 60vh;
    margin: 0;
}

/* ── Tables ──────────────────────────────────────────────────────────────── */

/* CrudTable scroll viewport: cap the grid's OWN .mud-table-container (value supplied
   per instance via the --crud-table-max-height var) instead of scrolling the wrapping
   paper. FixedHeader's position:sticky headers stick to the top of the nearest scroll
   container — a scrolling paper would carry them away with the content. Overrides
   MudBlazor's sticky-header max-height:100% (same specificity; this file loads later). */
.crud-table__scroll .mud-table-container {
    max-height: var(--crud-table-max-height);
    overflow-y: auto;
}

.mud-table-root .mud-table-head .mud-table-cell {
    font-size: 0.68rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--mud-palette-text-secondary);
}

/* Acknowledged alert rows (Alerts.razor) stay in the list — acknowledging only silences
   the app-bar indicator, see AlertService — but are dimmed so an operator can tell what's
   still new at a glance. */
.alert-row--ack {
    opacity: 0.55;
}

/* ── Section headers inside cards ────────────────────────────────────────── */

.section-title {
    display: flex;
    align-items: center;
    gap: 8px;
}

.section-title .mud-icon-root {
    color: var(--mud-palette-primary);
    font-size: 1.1rem;
}

/* ── Live container-log console ──────────────────────────────────────────── */

.log-console {
    margin: 0;
    height: 360px;
    overflow: auto;
    padding: 12px 14px;
    background: #0a0d12;
    border: 1px solid rgba(148, 163, 184, 0.12);
    border-radius: 10px;
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;
    font-size: 0.78rem;
    line-height: 1.45;
    color: #cdd6e4;
    white-space: pre-wrap;
    word-break: break-word;
}

/* Live-log colorization (logViewer.js): only the timestamp + level token are tinted;
   the message text inherits the console's near-white .log-console color. */
.log-console .log-line { display: block; }
.log-console .log-ts { color: #6b7a90; }
.log-console .log-punct { color: #6b7a90; }
.log-console .log-lvl-err { color: #f87171; font-weight: 600; }
.log-console .log-lvl-wrn { color: #fbbf24; font-weight: 600; }
.log-console .log-lvl-inf { color: #60a5fa; }
.log-console .log-lvl-dbg { color: #8a97a8; }

/* ── Licence offline-lock banner ─────────────────────────────────────────────
   Inlined colors: it also renders in the static-SSR account layout. */

.license-offline-banner {
    padding: 10px 24px;
    background: #7a4b00;
    color: #fff3d6;
    font-size: 0.9rem;
    text-align: center;
}

/* ── Login / logout (static-SSR account pages) ───────────────────────────────
   Self-contained colors (no --mud-palette-* vars): these pages render without
   the MudBlazor theme provider, so the dark palette is inlined to match. */

.login-shell {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    background:
        radial-gradient(1200px 600px at 50% -10%, rgba(93, 124, 249, 0.18), transparent 60%),
        #0e1117;
}

.login-card {
    width: 100%;
    max-width: 380px;
    background: #151a23;
    border: 1px solid rgba(148, 163, 184, 0.12);
    border-radius: 16px;
    padding: 32px 28px;
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.45);
    color: #e6ebf4;
    font-family: "Inter", "Roboto", "Arial", sans-serif;
}

/* The recipient's file list needs more room than the login form the card is shared with. */
.share-card {
    max-width: 560px;
}

.login-brand {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 20px;
}

.login-brand__logo {
    height: 48px;
    width: auto;
}

.login-title {
    margin: 0 0 18px;
    font-size: 1.35rem;
    font-weight: 700;
}

.login-subtitle {
    margin: -10px 0 18px;
    font-size: 0.85rem;
    color: #9aa6ba;
    line-height: 1.45;
}

.login-label {
    display: block;
    margin: 14px 0 6px;
    font-size: 0.8rem;
    color: #9aa6ba;
}

.login-input {
    width: 100%;
    box-sizing: border-box;
    padding: 10px 12px;
    background: #0e131b;
    border: 1px solid rgba(148, 163, 184, 0.22);
    border-radius: 9px;
    color: #e6ebf4;
    font-size: 0.95rem;
    outline: none;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

.login-input:focus {
    border-color: #5d7cf9;
    box-shadow: 0 0 0 3px rgba(93, 124, 249, 0.25);
}

.login-remember {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 16px 0 4px;
    font-size: 0.85rem;
    color: #9aa6ba;
    cursor: pointer;
}

.login-button {
    width: 100%;
    margin-top: 20px;
    padding: 11px 12px;
    border: none;
    border-radius: 9px;
    background: linear-gradient(135deg, #5d7cf9 0%, #4f6ef7 100%);
    color: #fff;
    font-size: 0.95rem;
    font-weight: 600;
    cursor: pointer;
    transition: filter 0.15s ease;
}

.login-button:hover {
    filter: brightness(1.08);
}

/* OIDC / SSO sign-in button + "or" divider on the login page. */
.login-divider {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 18px 0 4px;
    color: #94a3b8;
    font-size: 0.8rem;
}

.login-divider::before,
.login-divider::after {
    content: "";
    flex: 1;
    height: 1px;
    background: rgba(148, 163, 184, 0.25);
}

.login-button--sso {
    display: block;
    text-align: center;
    text-decoration: none;
    background: transparent;
    border: 1px solid rgba(148, 163, 184, 0.45);
    color: #e2e8f0;
}

.login-button--sso:hover {
    background: rgba(148, 163, 184, 0.12);
    filter: none;
}

.login-error {
    margin-bottom: 14px;
    padding: 10px 12px;
    border-radius: 9px;
    background: rgba(239, 68, 68, 0.12);
    border: 1px solid rgba(239, 68, 68, 0.35);
    color: #fca5a5;
    font-size: 0.85rem;
}

.login-field-error {
    display: block;
    margin-top: 4px;
    color: #fca5a5;
    font-size: 0.78rem;
}

/* Recipient-facing /share/{token} page (Features/Export/SharePage.razor) — layered
   onto the .login-* idiom above, same self-contained dark palette. */

.share-masked-email {
    color: #e6ebf4;
    font-weight: 600;
}

.share-code-input {
    text-align: center;
    letter-spacing: 0.5em;
    font-family: "SFMono-Regular", "Consolas", "Menlo", monospace;
    font-size: 1.4rem;
}

.share-download-button {
    display: block;
    text-align: center;
    text-decoration: none;
}

.share-meta {
    margin-top: 16px;
    font-size: 0.78rem;
    color: #9aa6ba;
    line-height: 1.5;
}

/* Row is inert; the checkbox picks for the zip and the button downloads. */
.share-items {
    list-style: none;
    margin: 4px 0 20px;
    padding: 0;
}

.share-item {
    display: flex;
    align-items: stretch;
    gap: 8px;
}

.share-item + .share-item {
    margin-top: 8px;
}

/* Wraps the checkbox and text so tapping anywhere on the row toggles it. */
.share-item__row {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    align-items: baseline;
    gap: 12px;
    padding: 12px 14px;
    border: 1px solid #2a3242;
    border-radius: 9px;
    background: #171d28;
    color: #e6ebf4;
    cursor: pointer;
}

.share-item__row:hover,
.share-item__row:focus-within {
    border-color: #5d7cf9;
    background: #1c2331;
}

/* A file still building: named, but nothing to tick or click. */
.share-item__row--preparing {
    cursor: default;
}

.share-item__row--preparing:hover {
    border-color: #2a3242;
    background: #171d28;
}

/* Keeps a preparing row's name in line with the ticked rows above and below it. */
.share-item__check-spacer {
    flex: none;
    width: 16px;
}

.share-item__check {
    flex: none;
    accent-color: #5d7cf9;
    width: 16px;
    height: 16px;
}

.share-item__text {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
}

.share-item__name {
    font-weight: 600;
    word-break: break-word;
}

.share-item__meta {
    flex: none;
    font-size: 0.78rem;
    color: #9aa6ba;
    white-space: nowrap;
}

.share-item__download {
    flex: none;
    display: flex;
    align-items: center;
    padding: 0 14px;
    border: 1px solid #2a3242;
    border-radius: 9px;
    background: #171d28;
    color: #9db2ff;
    font-size: 1.1rem;
    text-decoration: none;
}

.share-item__download:hover,
.share-item__download:focus-visible {
    border-color: #5d7cf9;
    background: #1c2331;
    color: #e6ebf4;
}

.share-select-all {
    display: flex;
    justify-content: flex-start;
    margin-bottom: 6px;
    padding-left: 14px;
}

/* A button that reads as a link — it's a view-state toggle, not a second action
   competing with the zip button below it. */
.share-linkish {
    background: none;
    border: 0;
    padding: 0;
    color: #9db2ff;
    font: inherit;
    font-size: 0.82rem;
    cursor: pointer;
}

.share-linkish:hover { color: #e6ebf4; }

.share-download-button:disabled {
    opacity: 0.45;
    cursor: not-allowed;
}

/* Settings layout: a left sub-nav rail beside the page body. */
.settings-layout {
    display: flex;
    align-items: flex-start;
    gap: 24px;
}

.settings-layout__rail {
    flex: 0 0 220px;
    position: sticky;
    top: 88px;
    padding-bottom: 8px;
}

.settings-layout__body {
    flex: 1 1 auto;
    min-width: 0;
}

@media (max-width: 900px) {
    .settings-layout {
        flex-direction: column;
    }
    .settings-layout__rail {
        position: static;
        flex: 1 1 auto;
        width: 100%;
    }
}

/* Static section headers in the settings rail (replaced the old expandable groups). */
.settings-nav__group-label {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 16px;
    margin-top: 12px;
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--mud-palette-text-secondary, #9e9e9e);
}
.settings-nav__group-label:first-child {
    margin-top: 2px;
}
.settings-nav__group-label .mud-icon-root {
    font-size: 1.05rem;
}

/* ── Audio level bar (camera edit dialog Audio tab) ────────────────── */
.audio-level-bar {
    display: flex;
    align-items: center;
    gap: 3px;
}
.audio-seg {
    width: 14px;
    height: 12px;
    border-radius: 2px;
    background: var(--mud-palette-text-disabled);
    opacity: 0.25;
    transition: background 0.08s, opacity 0.08s;
}
.audio-seg--green  { background: var(--mud-palette-success); opacity: 1; }
.audio-seg--yellow { background: var(--mud-palette-warning); opacity: 1; }
.audio-seg--red    { background: var(--mud-palette-error);   opacity: 1; }

/* ── Per-entity audit trail (AuditTrail.razor) ─────────────────────────
   Only Time / User / Action are shown so the table fills the available
   width and never scrolls horizontally; long User/Action values truncate
   with an ellipsis (table-layout:fixed pins the column widths). The full
   record opens in AuditDetailDialog from the row's Details button. */
.audit-trail-table table { table-layout: fixed; width: 100%; }
.audit-trail-cell {
    max-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.audit-trail-actions { text-align: right; }

/* ── /audit grid (Audit.razor) ───────────────────────────────────────────────
   table-layout:fixed is load-bearing. MudBlazor ships no table-layout rule, and under the
   default `auto` the column widths become minimums: a long message pushes the table past
   the viewport and the resizer can't drag a column narrower than its text. */
.audit-grid table { table-layout: fixed; width: 100%; }

/* One line per event. max-width:0 is what lets a fixed-layout cell shrink below its
   content so the ellipsis appears; the full value is on the title attribute. */
.audit-grid .audit-cell {
    max-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Chips would otherwise stretch the row taller than a text cell. */
.audit-grid .audit-cell .mud-chip {
    max-width: 100%;
    margin-top: 0;
    margin-bottom: 0;
}

/* The button is the whole cell, and the default padding leaves no room for it — which
   spills it past the table and grows a horizontal scrollbar at narrow widths. */
.audit-grid .audit-cell--actions {
    text-align: right;
    overflow: visible;
    padding-left: 0;
    padding-right: 8px;
}

/* ── Constant grid height ────────────────────────────────────────────────────
   The card fills the content area whatever the row count, so its bottom edge lines up with
   the Filters drawer's at every "rows per page". A max-height can't do that: it caps a tall
   page but lets a short one end mid-screen. The wrapper's own 12px bottom padding is what
   lands the edge on the drawer's, so no calc() has to track the toolbar's height. */
.audit-results {
    height: 100%;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

/* min-height:0 at every level is load-bearing: a flex item's auto min-height refuses to
   shrink below its content, so the table would push the card past its container. */
.audit-results__grid {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

.audit-results__grid .mud-data-grid {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

/* The rows viewport stays the scroll container, but sized by flex basis — hence
   max-height:none, and hence the paper no longer carrying .crud-table__scroll, whose
   max-height would otherwise be decided against this one by file order. */
.audit-results__grid .mud-table-container {
    flex: 1 1 auto;
    min-height: 0;
    max-height: none;
    overflow-y: auto;
}

/* Pinned to the bottom edge, so "rows per page" sits in one place at every page size. */
.audit-results__grid .mud-table-pagination {
    flex: 0 0 auto;
}

/* The empty state replaces the card, so it fills the same box. */
.audit-results .empty-state {
    flex: 1 1 auto;
    min-height: 0;
}

/* ── Responsive / mobile ─────────────────────────────────────────────────────
   Everything below is gated behind a max-width media query, so the desktop
   experience is byte-for-byte unchanged — these rules only switch on for narrow
   (phone / small-tablet) viewports. Breakpoints mirror MudBlazor's: sm = 600px,
   md = 960px. The drawer-stacking rules for the full-bleed playback/case views
   live in multiview.css (next to the layout they override). */

@media (max-width: 599.98px) {
    /* App bar: the brand version chip + wide logo eat space the hamburger needs
       (the desktop button rows collapse to a hamburger in AppNav.razor via
       MudBlazor's d-sm-* display utilities). Trim the brand to match. */
    .app-brand { margin-right: 8px; }
    .app-brand__logo { height: 30px; }
    .app-brand__version { display: none; }

    /* Centered page container (MainLayout): big gutters + 32px vertical margins
       waste a phone's limited width/height. Tighten both. */
    .app-page-container.mud-container {
        padding-left: 12px;
        padding-right: 12px;
    }
    .app-page-container.my-8 {
        margin-top: 16px !important;
        margin-bottom: 16px !important;
    }

    /* Page header: wrap the actions onto their own row beneath the title, and
       dial back the oversized h4 + icon chip so the header doesn't dominate. */
    .page-header {
        flex-wrap: wrap;
        gap: 10px;
        margin-bottom: 16px;
    }
    .page-header__icon {
        width: 34px;
        height: 34px;
        border-radius: 10px;
    }
    .page-header .mud-typography-h4 {
        font-size: 1.35rem;
        line-height: 1.25;
    }
    .page-header__actions {
        flex-basis: 100%;
    }

    /* Right-anchored persistent drawers (Live rail · Playback/Cases/Audit
       filters). When open they normally PUSH the content aside — fine on a wide
       screen, but a 320–360px drawer leaves a phone almost nothing. Float the
       drawer OVER the content instead (overlay) and stop the content being
       squeezed; the existing chevron toggle still opens/closes it. */
    .mud-drawer:not(.mud-drawer-fixed).mud-drawer-persistent.mud-drawer-pos-right.mud-drawer--open {
        width: min(360px, 86vw);
        background-color: var(--mud-palette-background) !important;
        box-shadow: -8px 0 28px rgba(0, 0, 0, 0.5);
    }
    .mud-drawer:not(.mud-drawer-fixed).mud-drawer-persistent.mud-drawer-pos-right.mud-drawer--open
        ~ div:not(.mud-main-content) {
        margin-right: 0 !important;
    }
}

/* ── Expanded session detail (SessionGrid's ChildRowContent) ─────────────────
   The panel spans the full grid width, so a single stacked column left every
   label hard against the left edge with the rest of the row empty. Two columns
   use that space: what the session IS on the left, what has come OUT of it on
   the right. One column on narrow screens, where side-by-side would squeeze
   both halves. */
.sg-detail-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 4px 40px;
    margin-bottom: 12px;
}

@media (max-width: 900px) {
    .sg-detail-grid {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* Label above value, with room between fields so they don't run together. */
.sg-detail-field {
    margin-bottom: 14px;
    min-width: 0;
}

/* Label sits ON ITS OWN LINE above the value. MudText caption renders a <span>,
   so without this the label and its value ran together ("CaseINT-2026-0142"). */
.sg-detail-field > .mud-typography-caption {
    display: block;
    margin-bottom: 2px;
}

/* Three columns for the expanded session detail: what the session IS (case + integrity),
   what has been BUILT from it (clips), who it has been SENT to (shares). Each column
   carries its own action button, so Export sits with clips and Share with shares. One
   column below ~1100px, where three would squeeze every table into uselessness. */
.sg-detail-grid-3 {
    display: grid;
    /* NOT an even third. Details is a short label/value list whose max-content is ~220px,
       so a third of a 1500px panel handed it ~250px it cannot use while both tables beside
       it scrolled sideways. It gets a cap; the two tables split everything else. */
    grid-template-columns: minmax(0, 260px) minmax(0, 1fr) minmax(0, 1fr);
    gap: 4px 24px;
    margin-bottom: 12px;
    /* A DEFINITE height is what lets each column fill to the bottom of the panel and
       scroll its own list there, rather than every list capping at some arbitrary size
       partway down. Without it the panel grew to the tallest of the three lists and left
       the other two columns as dead space below their content. Down from 420px, which two
       since-removed tables needed. */
    height: 320px;
    align-items: stretch;
}

/* Each column is a flex stack: fixed content at the top, ONE growing scroll area, and
   its action button pinned underneath. */
.sg-detail-grid-3 > div {
    display: flex;
    flex-direction: column;
    min-height: 0;
}

/* The part of a column that takes the leftover height. */
.sg-detail-fill {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    margin-bottom: 0;
}

/* The single scrollbar of a column. Nothing nested inside this may scroll itself — the
   shares table shipped with its own 360px scroller, which put two bars on one list. */
.sg-detail-col-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    /* Auto, not hidden: a column that genuinely cannot fit stays reachable rather than
       silently losing its right-hand column. The Details cap and the cell padding keep it
       from firing at 1080p; a window small enough still reaches it, as it always did. */
    overflow-x: auto;
}

/* MudBlazor pads cells for a table that owns the page width; two of them side by side in
   one panel spend ~60px of it on padding alone, which is most of what pushed each into its
   own horizontal scrollbar. Trimmed BETWEEN columns, where the gap only has to separate two
   values, and kept at the outer edges, where it is the margin the eye reads the list
   against — flush to the column edge buys 32px and looks like a rendering fault.

   Three classes deep and written as padding-inline deliberately: the rule being overridden
   is `.mud-table-dense * .mud-table-row .mud-table-cell`, which sets `padding-inline` and
   outweighs a two-class selector — a shorter one silently keeps MudBlazor's 24px end. */
.sg-detail-col-body .mud-table-container .mud-table-cell {
    padding-inline: 8px;
}

.sg-detail-col-body .mud-table-container .mud-table-cell:first-child {
    padding-inline-start: 16px;
}

.sg-detail-col-body .mud-table-container .mud-table-cell:last-child {
    padding-inline-end: 16px;
}

@media (max-width: 1100px) {
    .sg-detail-grid-3 {
        grid-template-columns: minmax(0, 1fr);
        /* Stacked, a fixed height would crop the columns below the first — let the panel
           grow and cap each list instead. */
        height: auto;
    }

    .sg-detail-col-body {
        max-height: 240px;
    }
}

/* The row's action icons are one group and must stay on one line — they wrapped onto two,
   which made every row taller and read as two unrelated controls. Fixed here rather than
   by giving Play a column of its own: a column is a column, not a layout workaround. */
.sg-row-actions {
    white-space: nowrap;
    text-align: right;
}

/* The Details column's label/value pairs. A table, not a stack of divs, so the values
   line up in a column instead of ragging against labels of different lengths — this
   mirrors the recording viewer's own Details panel, which is what it is showing. */
.sg-detail-facts {
    border-collapse: collapse;
    width: 100%;
}

.sg-detail-facts td {
    padding: 2px 12px 2px 0;
    vertical-align: top;
    border: none;
}

.sg-detail-facts td:first-child {
    color: var(--mud-palette-text-secondary);
    font-size: 0.75rem;
    white-space: nowrap;
    width: 1%;
    padding-top: 4px;
}

/* Export and Share are the same control in two columns and must read as a pair: same
   height, same width, tops and bottoms in line. They sit at the foot of their own flex
   column, so without a shared fixed margin and a fixed basis each took its height from
   whatever sat above it and the two ended up a couple of pixels apart. */
.sg-detail-action {
    margin-top: 8px;
    flex: 0 0 auto;
    align-self: stretch;
    height: 32px;
}

/* CrudTable's empty state is a 56px-padded panel with a 64px icon — right for a full page,
   far too large for one third of an expanded row, where it pushed the table below it off
   the bottom. Inside the detail panel it collapses to its text. */
.sg-detail-col-body .empty-state {
    padding: 10px 0;
    border: none;
    align-items: flex-start;
    text-align: left;
    gap: 2px;
}

.sg-detail-col-body .empty-state__icon {
    display: none;
}

.sg-detail-col-body .empty-state .mud-typography-h5 {
    font-size: 0.875rem;
    font-weight: 400;
    color: var(--mud-palette-text-secondary);
}
