@import url('open-iconic/font/css/open-iconic-bootstrap.min.css');

:root {
    --transition-speed-hover: 0.1s;
    --nxs-warning: #ffcc33;
    --nxs-surface-radius: 1rem;

    /* ── Design tokens (single source of truth; see docs/plans/design-tokens-retrofit.md) ──
       The MudTheme in MainLayout references these; component CSS must use them, not literals. */

    /* Radius — flat scale */
    --nxs-radius-sm: 4px;    /* chips (non-pill), small/inset controls */
    --nxs-radius-md: 6px;    /* default — buttons, inputs, cards */
    --nxs-radius-lg: 8px;    /* panels, grids, modals, large surfaces */
    --nxs-radius-pill: 999px;

    /* Spacing — 4px-based scale for margin / padding / gap. Token number = pixel value;
       value is rem for accessibility (assumes 16px root). Round raw spacing to the nearest
       step. Structural values (vh/vw, calc(), grid track sizes) are NOT spacing — leave them. */
    --nxs-space-2:  0.125rem; /* 2px  — hairline gaps */
    --nxs-space-4:  0.25rem;  /* 4px  */
    --nxs-space-6:  0.375rem; /* 6px  */
    --nxs-space-8:  0.5rem;   /* 8px  */
    --nxs-space-12: 0.75rem;  /* 12px */
    --nxs-space-16: 1rem;     /* 16px — base rhythm */
    --nxs-space-20: 1.25rem;  /* 20px */
    --nxs-space-24: 1.5rem;   /* 24px */
    --nxs-space-32: 2rem;     /* 32px */
    --nxs-space-48: 3rem;     /* 48px — section spacing */

    /* Type size — font-size scale (see docs/plans/chip-size-tokens.md; broader Pass 9 extends
       it site-wide). Named -size to avoid colliding with the --nxs-text* colour family. */
    --nxs-text-size-xs: 0.75rem;   /* 12px — dense chips / micro-labels */
    --nxs-text-size-sm: 0.8125rem; /* 13px — compact chips / labels */
    --nxs-text-size-md: 0.875rem;  /* 14px — standard chips / base component text */
    --nxs-font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

    /* Control height — standard fixed heights for interactive controls. */
    --nxs-control-h-chip: 20px; /* dense data badge (e.g. dimension-matrix conformance pill) */
    --nxs-control-h-sm: 28px;   /* compact interactive controls */
    --nxs-control-h-md: 32px;   /* standard interactive pill (removable / helper chips) */
    --nxs-control-h-lg: 36px;   /* standard action button */
    --nxs-control-h-form: 2.5rem; /* standard outlined form field and file-upload activator */
    /* Motion — shared durations for repeated decorative treatments. */
    --nxs-motion-duration-shimmer: 2.2s;

    /* Layout — page width policy (see docs/plans/page-width-layout.md). Content is full-width
       by default (data grids use the space); form/detail pages opt into a capped reading
       column via <NxsReadingColumn>. */
    --nxs-content-gutter: 1.5rem; /* horizontal page padding (replaces the old global px-4) */
    --nxs-reading-max: 1100px;    /* max line length for form/detail reading columns */
    --nxs-dialog-workflow-max: 72rem; /* data-bearing forms/dialogs: wide enough for a grid, bounded for readable fields */
    --nxs-tile-min-width: 17rem; /* shared browse tiles: prevents crushed metadata without content-sized tracks */
    --nxs-chart-height-sm: clamp(320px, 42vh, 440px);
    --nxs-chart-height-md: clamp(420px, 58vh, 560px);
    --nxs-chart-height-lg: clamp(510px, 70vh, 640px);
    --nxs-chart-height-xl: clamp(510px, 78vh, 780px);
    --nxs-grid-fill-min-height: 320px; /* floor for FillViewport grids — 100vh shrinks in CSS px under browser zoom, so without a floor calc(100vh - offset) collapses the grid to nothing */
    --nxs-grid-detail-height: 20rem;
    --nxs-grid-dialog-height: clamp(20rem, 58dvh, 35rem);
    --nxs-grid-source-picker-height: 33dvh;
    --nxs-grid-cell-max-width: 20rem;

    /* Surfaces — neutral dark ladder (tinted darks are NOT surfaces — handled as status tokens) */
    --nxs-bg: #222222;            /* page background */
    --nxs-surface: #121212;       /* primary panel/card surface */
    --nxs-surface-inset: #101010; /* wells/insets (absorbs #0f0f0f/#0d0d0d/#141414/#151515) */
    --nxs-surface-raised: #1a1a1a;/* raised panels (absorbs #161616/#181818/#191919/#1b1b1b/#1d1d1d/#1e1e1e/#1f1f1f) */
    --nxs-surface-40: color-mix(in srgb, var(--nxs-surface) 40%, transparent);
    --nxs-surface-80: color-mix(in srgb, var(--nxs-surface) 80%, transparent);
    --nxs-geometry-surface: var(--nxs-surface-raised); /* canonical geometry viewer / thumbnail surface */
    --nxs-surface-2: #2a2a2a;     /* secondary fills (absorbs #232323/#242424/#262626/#272727/#2b2b2b) */
    --nxs-surface-3: #383838;     /* raised/hover fills (absorbs #333333/#353535/#3a3a3a/#444444) */
    --nxs-field-backdrop: var(--nxs-bg); /* inherited mask for outlined Mud field labels on the page surface */

    /* Borders / lines */
    --nxs-border: #363636;
    --nxs-border-60: color-mix(in srgb, var(--nxs-border) 60%, transparent);
    --nxs-border-strong: #4f4f4f; /* absorbs #3a3a3a/#5f5f5f */
    --nxs-divider: var(--nxs-overlay-12); /* = white 12% overlay step (see ladder below) */

    /* Text */
    --nxs-text: #ffffff;          /* headings / primary */
    --nxs-text-body: #e6e6e6;     /* body */
    --nxs-text-muted: #9aa3ad;    /* labels / secondary */
    --nxs-text-subtle: #b8c3cf;   /* absorbs #aeb7c2/#cfd6de/#9aa0a6 */

    /* Accent / status */
    --nxs-accent: #00bcd4;
    --nxs-accent-bright: #00d4ee; /* absorbs #00c8ff/#74edff — hover/glow */
    --nxs-danger: #f05252;        /* fail / out-of-tolerance fill */
    --nxs-danger-strong: #d71920; /* strong error */
    --nxs-danger-bg: #3f1014;     /* error background tint */
    --nxs-success: #2e9e5f;       /* in-tolerance / pass fill */
    --nxs-success-bg: #0f3f25;    /* dark green fill */
    --nxs-info-bg: #0f1720;       /* info/note panels (absorbs #101114/#05070b/#0d1117) */

    /* Turbo heat-map palette — eight evenly spaced stops ordered low → high and shared
       by rendered data and its legend so the scale precisely represents the display. */
    --nxs-heatmap-stop-0: #466be3;
    --nxs-heatmap-stop-1: #29bbec;
    --nxs-heatmap-stop-2: #31f199;
    --nxs-heatmap-stop-3: #a3fd3d;
    --nxs-heatmap-stop-4: #edd03a;
    --nxs-heatmap-stop-5: #fb8022;
    --nxs-heatmap-stop-6: #d23105;
    --nxs-heatmap-stop-7: #7a0403;

    /* Process palette */
    --nxs-process-default: #5ac9dd;
    --nxs-process-am-build: #5de7ff;
    --nxs-process-heat-treatment: #ffb86b;
    --nxs-process-tumbling: #e68ae0;
    --nxs-process-cnc: #94baff;
    --nxs-process-cmm: #7ee4a5;
    --nxs-process-gom: #c7a7ff;
    --nxs-process-ct: #ff8c8c;
    --nxs-process-surface-roughness: #3dbef5;
    --nxs-process-radiography: #ff9ff3;
    --nxs-process-thermal-camera: #ff9f7a;
    --nxs-process-tensile: #ffd86b;
    --nxs-process-fatigue: #8fb2ff;
    --nxs-process-sem: #9ccb4a;

    /* Status foreground/border — light variants for pass/fail chips on the *-bg fills above
       (the dark --nxs-danger/-success can't be used as foreground text; they're fills). */
    --nxs-success-text: #86f7ae;   /* pass chip text/icon */
    --nxs-success-border: #2fbf6a; /* pass chip border */
    --nxs-danger-text: #ff7f7f;    /* fail chip text/icon */
    --nxs-danger-border: #bf4248;  /* fail chip border */

    /* Info / highlight — bright sky-blue foreground (pairs with --nxs-info-bg); absorbs the
       drifting #67d5ff/#7dc9ff/#54d8e6 highlight blues. Tints via color-mix, like the accent. */
    --nxs-info: #67d5ff;
    --nxs-info-8:  color-mix(in srgb, var(--nxs-info) 8%, transparent);
    --nxs-info-20: color-mix(in srgb, var(--nxs-info) 20%, transparent);

    /* White overlays — fixed opacity ladder. Round any raw rgba(255,255,255,a) to the nearest
       step. NOTE: higher white-alphas (>=~0.42) are muted/near-opaque TEXT, not overlays —
       use the --nxs-text-* ladder for those, not these tokens. */
    --nxs-overlay-3:  rgba(255, 255, 255, 0.03); /* faintest fills / zebra (absorbs 0.02–0.04) */
    --nxs-overlay-8:  rgba(255, 255, 255, 0.08); /* hover / active fill (absorbs 0.06–0.10) */
    --nxs-overlay-12: rgba(255, 255, 255, 0.12); /* dividers / hairlines */
    --nxs-overlay-20: rgba(255, 255, 255, 0.20); /* visible borders (absorbs 0.16–0.24) */
    --nxs-overlay-32: rgba(255, 255, 255, 0.32); /* strong borders / emphasis (absorbs 0.28–0.35) */

    /* Back-compat aliases — existing names now map onto the ladder steps */
    --nxs-overlay-subtle: var(--nxs-overlay-3);
    --nxs-overlay-hover:  var(--nxs-overlay-8);

    /* Scrollbars — one site-wide thumb; the transparent track reveals its owning surface. */
    --nxs-scrollbar-thumb: var(--nxs-overlay-20);

    /* Accent tints — brand accent at low alpha, derived from --nxs-accent so they stay in sync.
       Same step numbers as the white ladder. */
    --nxs-accent-8:  color-mix(in srgb, var(--nxs-accent) 8%, transparent);  /* faint wash (absorbs 0.08–0.12) */
    --nxs-accent-20: color-mix(in srgb, var(--nxs-accent) 20%, transparent); /* chip / selected-row fill (absorbs 0.15–0.18) */
    --nxs-accent-30: color-mix(in srgb, var(--nxs-accent) 30%, transparent); /* chip border / strong tint (absorbs 0.30–0.35) */

    /* Row selection — the house selected-row treatment (see .nxs-grid__row--selected):
       accent tint + a white hairline around each contiguous selected block. An accent
       HUE (not a neutral lift) keeps selection visible on any housing surface and distinct
       from hover; adjacent selected rows merge into one surface without doubled seams. */
    --nxs-row-selected-bg: var(--nxs-accent-20);
    --nxs-row-selected-line: rgba(255, 255, 255, 0.75);

    /* Scrims / shadow — black-alpha axis */
    --nxs-scrim:  rgba(0, 0, 0, 0.6);  /* modal / backdrop scrim (absorbs 0.35–0.72) */
    --nxs-shadow: rgba(0, 0, 0, 0.22); /* drop shadows (absorbs 0.2/0.24) */
    --nxs-surface-veil: rgba(18, 18, 18, 0.35); /* translucent dark veil for in-content loading/disabled overlays (absorbs 0.28–0.38) */
}

/* MudBlazor draws outlined fields with one uninterrupted border, so its floating label
   must mask the line behind it. Initialise the inherited mask on every Mud component that
   actually paints the standard surface colour. MudDialog, MudTable and MudExpansionPanel
   are independent surfaces and do not render a .mud-paper root. Shared custom surfaces
   override the variable alongside their own background declaration. */
.mud-paper,
.mud-dialog,
.mud-expand-panel,
.mud-table,
.mud-tabs-tabbar {
    --nxs-field-backdrop: var(--mud-palette-surface);
}

.mud-input-label-outlined {
    background-color: var(--nxs-field-backdrop);
}

/* MudBlazor supplies the required marker as a label pseudo-element. Give the asterisk
   deliberate separation and keep it locked to MudBlazor's field error colour. */
.mud-input-control.mud-input-required > .mud-input-control-input-container > .mud-input-label::after {
    content: "✱";
    display: inline-block;
    margin-inline-start: var(--nxs-space-6);
    color: var(--mud-palette-error);
    font-size: var(--nxs-text-size-sm);
    line-height: 1;
    transform: translateY(-0.08em);
    vertical-align: middle;
}

/* A required marker is sufficient when no more specific validation guidance exists.
   Collapse MudBlazor's otherwise empty helper row while retaining genuine error copy. */
.mud-input-control-helper-container:has(> .mud-input-helper-text.mud-input-error > .me-auto:empty:only-child) {
    display: none;
}

/* Global scrollbar appearance. `!important` is intentional: the compiled Telerik theme ships
   component-specific scrollbar rules and is loaded before this stylesheet. The standard thin
   width replaces component geometry while the Nexus thumb stays universal and the track reveals
   its surface. */
* {
    scrollbar-color: var(--nxs-scrollbar-thumb) transparent !important;
    scrollbar-width: thin !important;
}

*::-webkit-scrollbar {
    display: block !important;
    width: auto !important;
    height: auto !important;
}

*::-webkit-scrollbar-thumb {
    background-color: var(--nxs-scrollbar-thumb) !important;
    border: revert !important;
    border-radius: revert !important;
}

*::-webkit-scrollbar-track,
*::-webkit-scrollbar-corner {
    background-color: transparent !important;
}

/* Stable single-edge gutters for bounded vertical scroll owners. Keep this list centralized:
   component/page CSS owns overflow and sizing, while the app stylesheet owns gutter policy. */
:where(
    article.content,
    .dropdown-container,
    .dropdown-container-expanded-animation,
    .component-surface-roughness-selector__body,
    .chart-group-chips,
    .nxs-modal__scroll-region,
    .panx-text-artifact-dialog__content,
    .scanfield-detail-window-content,
    .sfc-avg-comparison-content,
    .nxs-drawer-panel__panel-body,
    .nxs-virtualized-multi-select__list,
    .gom-histogram-dialog-content,
    .caliper-window-content,
    .airtable-attachment-container,
    .modal-dialog-scrollable .modal-body,
    .offcanvas-body,
    .k-list-scroller,
    .k-list-content,
    .k-virtual-content,
    .k-virtual-scroller,
    .k-window-content,
    .k-prompt-container,
    .k-drawer-wrapper
) {
    scrollbar-gutter: stable;
}

/* The primary navigation and conversation rail reclaim the edge when no scrollbar is present and
   may shift horizontally when vertical overflow appears. Grids also keep their full content width:
   a reserved gutter reads as an empty final column. */
:where(
    .nexus-nav,
    .agent-conversation-list,
    .nxs-grid .mud-table-container,
    .k-grid-content,
    .k-grid-content-locked,
    .nxs-sticky-grid-shell__scroll,
    .component-tensile-scroll
) {
    scrollbar-gutter: auto;
}

/* Symmetric gutters are reserved for scroll owners whose contents are intentionally centred.
   Keep the Agent selector explicit so its both-edges policy wins over generic scrollbar rules. */
.agent-messages {
    scrollbar-gutter: stable both-edges;
}

/* Explicit escape hatch for the rare surface where reclaiming the gutter is more important than
   preventing horizontal movement when a vertical scrollbar appears or disappears. */
.nxs-scrollbar-gutter-auto {
    scrollbar-gutter: auto !important;
}

/* NxsChip<T> — the site's standard chip primitive. Semantic wrappers such as
   NxsResultChip and NxsValueChip compose this, so the baseline chrome lives here. */
/* NxsPageHeader — title left, contextual actions right, centred on the title line.
   The row owns the gutter below itself and suppresses NxsTitle's own gutter so the
   actions sit level with the title and clear of tab bars/content underneath. Recipe
   mirrors the component-view header. */
.nxs-title {
    margin-bottom: var(--nxs-space-16);
}

.nxs-page-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--nxs-space-16);
    flex-wrap: wrap;
    margin-bottom: var(--nxs-space-16);
    min-width: 0;
}

.nxs-page-header__title {
    min-width: 0;
}

.nxs-page-header__title .nxs-title {
    margin-bottom: 0;
}

.nxs-page-header__actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--nxs-space-8);
    flex-wrap: wrap;
    max-width: 100%;
    min-width: 0;
}

/* NxsModal — one shared modal shell. Header and footer stay fixed while the body
   owns the scrollbar and follows the centralized stable-gutter policy above. */
.nxs-modal-overlay {
    position: fixed;
    inset: calc(var(--mud-appbar-height) - var(--mud-appbar-height) / 8) 0 0;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--nxs-space-24);
    overflow: hidden;
    overscroll-behavior: contain;
    background: var(--nxs-scrim);
    z-index: 1200;
}

.mud-paper.nxs-modal {
    --nxs-field-backdrop: var(--nxs-surface-2);
    position: relative;
    display: flex;
    flex-direction: column;
    width: min(960px, 100%);
    max-height: 100%;
    overflow: hidden;
    border: 1px solid var(--nxs-divider);
    border-radius: var(--nxs-radius-lg);
    background: var(--nxs-surface-2);
    color: var(--nxs-text-body);
}

/* Match MudMainContent's responsive app-bar offset so the scrim, centring and
   available modal height are always relative to the region below the header. */
@media (min-width: 0) and (orientation: landscape) {
    .nxs-modal-overlay {
        inset-block-start: calc(var(--mud-appbar-height) - var(--mud-appbar-height) / 4);
    }
}

@media (min-width: 600px) {
    .nxs-modal-overlay {
        inset-block-start: var(--mud-appbar-height);
    }
}

.nxs-modal__header {
    display: flex;
    flex: 0 0 auto;
    align-items: center;
    justify-content: space-between;
    gap: var(--nxs-space-16);
    padding: var(--nxs-space-16) var(--nxs-space-20);
    border-bottom: 1px solid var(--nxs-overlay-hover);
    background: var(--nxs-surface-2);
}

.nxs-modal__header h4 {
    margin: 0;
    color: var(--nxs-text);
    font-size: 1.1rem;
    font-weight: 700;
}

.nxs-modal__header-actions {
    display: flex;
    align-items: center;
    gap: var(--nxs-space-12);
}

.nxs-modal__scroll-region {
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    gap: var(--nxs-space-24);
    min-width: 0;
    min-height: 0;
    padding: var(--nxs-space-20) var(--nxs-space-20) var(--nxs-space-24);
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-color: var(--nxs-overlay-20) var(--nxs-surface-2);
}

.nxs-modal__footer {
    flex: 0 0 auto;
    padding: var(--nxs-space-16) var(--nxs-space-20);
    border-top: 1px solid var(--nxs-divider);
    background: var(--nxs-surface-2);
}

.nxs-modal__veil {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    background: var(--nxs-surface-veil);
    z-index: 1;
}

@media (max-width: 600px) {
    .nxs-modal-overlay {
        padding: var(--nxs-space-16);
    }

    .mud-paper.nxs-modal {
        width: 100%;
        max-height: 100%;
    }
}

.nxs-chip.mud-chip {
    border: 1px solid var(--nxs-border-strong);
    border-radius: var(--nxs-radius-pill);
    background: var(--nxs-overlay-8);
    color: var(--nxs-text-body);
    font-weight: 700;
}

/* NxsChip<T> selected state — neutral selectable pills use MudChip's built-in Selected
   property plus this shared visual treatment on the whole chip. */
.nxs-chip.is-selected.mud-chip {
    border-color: var(--nxs-accent);
    color: var(--nxs-text);
    opacity: 1;
}

.nxs-chip.mud-chip-color-success {
    border-color: var(--nxs-success-border);
    background: color-mix(in srgb, var(--nxs-success-text) 20%, transparent);
    color: var(--nxs-success-text);
}

.nxs-chip.mud-chip-color-error {
    border-color: var(--nxs-danger-border);
    background: color-mix(in srgb, var(--nxs-danger-text) 20%, transparent);
    color: var(--nxs-danger-text);
}

.nxs-chip.mud-chip-color-warning {
    border-color: var(--nxs-warning);
    background: color-mix(in srgb, var(--nxs-warning) 24%, transparent);
    color: color-mix(in srgb, var(--nxs-warning) 45%, var(--nxs-text));
}

.nxs-chip.mud-chip-color-info {
    border-color: var(--nxs-info);
    background: var(--nxs-info-20);
    color: var(--nxs-info);
}

.nxs-chip.mud-chip-color-secondary {
    border-color: var(--nxs-overlay-32);
    background: var(--nxs-overlay-12);
    color: var(--nxs-text-subtle);
}

.nxs-chip.mud-chip-color-primary {
    border-color: var(--nxs-accent-30);
    background: var(--nxs-accent-20);
    color: var(--nxs-accent);
}

.nxs-chip.nxs-sem-sample-condition--as-built {
    border-color: color-mix(in srgb, var(--nxs-process-am-build) 30%, transparent);
    background: color-mix(in srgb, var(--nxs-process-am-build) 20%, transparent);
    color: var(--nxs-process-am-build);
}

.nxs-chip.nxs-sem-sample-condition--heat-treated {
    border-color: color-mix(in srgb, var(--nxs-process-heat-treatment) 30%, transparent);
    background: color-mix(in srgb, var(--nxs-process-heat-treatment) 20%, transparent);
    color: var(--nxs-process-heat-treatment);
}

.nxs-chip.nxs-result-chip--pass {
    border-color: var(--nxs-success-border);
    background: color-mix(in srgb, var(--nxs-success-text) 20%, transparent);
    color: var(--nxs-success-text);
}

.nxs-chip.nxs-result-chip--fail {
    border-color: var(--nxs-danger-border);
    background: color-mix(in srgb, var(--nxs-danger-text) 20%, transparent);
    color: var(--nxs-danger-text);
}

/* NxsRemovableChip — dismissible token pills (applied search filters, saved presets).
   Structure/radius come from .nxs-chip above; these supply the neutral base look and an
   accent variant, plus tame the built-in MudChip close button to the token scale. Colour
   isn't a bespoke prop — it rides MudBlazor's Color: Color.Default = neutral base here,
   Color.Primary adds .mud-chip-color-primary which we repaint with the accent tokens.
   Placed after the .nxs-chip rules so the background/border/colour win on equal specificity. */
.nxs-chip.nxs-removable-chip {
    min-height: var(--nxs-control-h-md);
    height: auto;
    font-size: var(--nxs-text-size-md);
    font-weight: 500;
    border-color: var(--nxs-overlay-20);
    background: var(--nxs-overlay-subtle);
    color: var(--nxs-text);
}

.nxs-chip.nxs-removable-chip.mud-chip-color-primary {
    border-color: var(--nxs-accent-30);
    background: var(--nxs-accent-20);
    color: var(--nxs-accent);
}

.nxs-removable-chip .mud-chip-close-button {
    color: inherit;
    opacity: 0.7;
}

.nxs-removable-chip .mud-chip-close-button:hover {
    opacity: 1;
}

.nxs-removable-chip:not(.mud-chip-color-primary) .mud-chip-close-button:hover {
    color: var(--nxs-danger);
}

/* NxsDataGrid<T> — the site's standard data grid: one cohesive dark rounded panel
   (toolbar + header + rows), matching the metrology grids. Striping off / elevation 0 /
   outlined are forced in the component; the rules here cover what props can't express. */
.nxs-grid.mud-table {
    --nxs-field-backdrop: var(--nxs-surface);
    background: var(--nxs-surface);
    border-radius: var(--nxs-radius-lg);
    overflow: hidden; /* clip the square table to the rounded panel corners */
}

/* A parent container such as an expansion panel may own the single visible boundary. */
.nxs-grid--flush.mud-table {
    border-radius: 0;
}

.nxs-grid .mud-table-container,
.nxs-grid .mud-table-body .mud-table-row {
    background: var(--nxs-surface);
}

/* The root panel does ALL the rounding (overflow:hidden clip). Everything inside stays
   square — inner radii draw curved seams where they meet the optional toolbar. Square=true
   in the component disables Mud's normal panel rounding, but FixedHeader grids get a
   SEPARATE rounding from `.mud-table-sticky-header * ... .mud-table-cell:first-child`
   (specificity 0,5,0 — independent of Square), so the sticky-header selectors below must
   carry 0,6,0 to win. */
.nxs-grid .mud-table-container,
.nxs-grid .mud-table-toolbar,
.nxs-grid.mud-table-sticky-header .mud-table-root .mud-table-head .mud-table-cell:first-child,
.nxs-grid.mud-table-sticky-header .mud-table-root .mud-table-head .mud-table-cell:last-child,
.nxs-grid .mud-table-head .mud-table-cell:first-child,
.nxs-grid .mud-table-head .mud-table-cell:last-child {
    border-radius: 0;
}

/* Toolbar geometry is OURS. Mud's .mud-toolbar sets a FIXED height (64px) and gutter
   padding (0 8px 0 16px) — both read unbalanced next to the content. Content-driven
   height, uniform 12/16 padding. */
.nxs-grid .mud-table-toolbar {
    height: auto;
    min-height: 0;
    padding: var(--nxs-space-12) var(--nxs-space-16);
    gap: var(--nxs-space-12);
    border-bottom: 1px solid var(--nxs-border);
}

/* NxsGridTitle — compact, consistently weighted title for embedded grid toolbars. */
.nxs-grid-title {
    font-weight: 700;
}

/* Grid toolbars own their inter-control gap. Neutralise MudSwitch's asymmetric
   inline margins so switches do not add a second, invisible spacing layer. */
.nxs-grid .mud-table-toolbar .mud-switch {
    margin-inline: 0 !important;
}

/* Standard search field (NxsSearchField): fixed width WHEREVER it appears — a default
   MudTextField is full-width and stretches edge-to-edge. Pages may override per layout
   (e.g. full-width on narrow screens) via a ::deep rule on their own class. */
.nxs-search {
    flex: 0 1 360px;
    width: 360px;
    min-width: 180px;
}

/* MudSelect popovers render outside the component subtree, so NxsFilterDropdown
   supplies this class via PopoverClass rather than relying on scoped CSS. */
.nxs-filter-dropdown__popover,
.nxs-filter-dropdown__popover .mud-paper,
.nxs-filter-dropdown__popover .mud-list {
    border-top-left-radius: 0 !important;
    min-width: 190px;
    max-width: min(320px, calc(100vw - var(--nxs-space-32)));
}

.nxs-filter-dropdown__popover .mud-list-item,
.nxs-filter-dropdown__popover .mud-list-item-text {
    min-width: 0;
}

.nxs-filter-dropdown__popover .nxs-filter-dropdown__option-text {
    display: block;
    min-width: 0;
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Uniform with toolbar + rows; row hover is the only lift. The sticky-header variant must
   carry 0,5,0 specificity: FixedHeader grids get their header background from Mud's
   `.mud-table-sticky-header * .mud-table-root .mud-table-head * .mud-table-cell` (0,4,0),
   which beats a plain 0,3,0 override. Keep this OPAQUE — rows scroll under the sticky head. */
.nxs-grid .mud-table-head .mud-table-cell,
.nxs-grid.mud-table-sticky-header .mud-table-root .mud-table-head .mud-table-cell {
    background: var(--nxs-surface);
}

.nxs-grid .mud-table-body .mud-table-row:hover > .mud-table-cell {
    background: var(--nxs-surface-raised);
}

/* Links in body cells inherit the cell's typography instead of MudLink's body1
   default, so linked and plain values in the same column render at the same size.
   Pages should not set Typo on grid row links. */
.nxs-grid .mud-table-body .mud-table-cell .mud-link {
    font-size: inherit;
    line-height: inherit;
}

/* Master-detail row selection — the house treatment: accent-tint fill + one hairline
   around each contiguous selected block. Both classes are emitted by NxsDataGrid itself: the root
   `nxs-grid--clickable-rows` whenever a RowClick handler is wired, and
   `nxs-grid__row--selected` on rows matching the page's IsRowSelected predicate. Pages
   never write these class names. The :hover variant must be stated (and sit after the
   generic hover lift above) so the selected fill survives hover. Mud's ordinary cell
   divider is suppressed around selected blocks so it cannot double the hairline. */
.nxs-grid.nxs-grid--clickable-rows .mud-table-body .mud-table-row {
    cursor: pointer;
}

.nxs-grid .mud-table-body .nxs-grid__row--selected > .mud-table-cell,
.nxs-grid .mud-table-body .nxs-grid__row--selected:hover > .mud-table-cell {
    --nxs-selected-block-top-line: transparent;
    --nxs-selected-block-bottom-line: transparent;

    background: var(--nxs-row-selected-bg);
    border-bottom-color: transparent;
    box-shadow: inset 0 1px 0 var(--nxs-selected-block-top-line),
                inset 0 -1px 0 var(--nxs-selected-block-bottom-line);
}

/* Remove the normal separator immediately above a selected block. The selected block's
   own top hairline is the only boundary that should remain. */
.nxs-grid .mud-table-body .mud-table-row:has(+ .nxs-grid__row--selected) > .mud-table-cell {
    border-bottom-color: transparent;
}

/* A selected row starts a block when it is the first row or follows an unselected row. */
.nxs-grid .mud-table-body > .nxs-grid__row--selected:first-child > .mud-table-cell,
.nxs-grid .mud-table-body > .mud-table-row:not(.nxs-grid__row--selected) + .nxs-grid__row--selected > .mud-table-cell {
    --nxs-selected-block-top-line: var(--nxs-row-selected-line);
}

/* A selected row ends a block when the next row is not selected (or there is no next row). */
.nxs-grid .mud-table-body > .nxs-grid__row--selected:not(:has(+ .nxs-grid__row--selected)) > .mud-table-cell {
    --nxs-selected-block-bottom-line: var(--nxs-row-selected-line);
}

/* No-records and loading rows: Mud paints both var(--mud-palette-surface) — a lighter
   shade than the grid panel, so they read as a stray band. Pin them to the panel surface.
   The sticky-header loading selector needs 0,5,0 to beat Mud's 0,4,0 (same story as the
   header cells above). */
.nxs-grid .nxs-unit-header,
.nxs-grid .nxs-unit-header * {
    --mud-typography-body2-text-transform: none;
    --mud-typography-button-text-transform: none;
    --mud-typography-subtitle2-text-transform: none;
    text-transform: none !important;
}

.nxs-grid .mud-table-empty-row,
.nxs-grid.mud-table-sticky-header .mud-table-root .mud-table-head .mud-table-loading {
    background-color: var(--nxs-surface);
}

.nxs-grid:has(.mud-table-loading) {
    cursor: wait;
}

.nxs-grid .mud-table-loading {
    display: none !important;
}

.nxs-grid .mud-table-empty-row .mud-table-cell {
    padding-block: var(--nxs-space-24);
}

/* Empty and first-load grids must collapse to their message/spinner instead of reserving a
   viewport/grid footprint. This overrides both NxsDataGrid's FillViewport height and any
   100% shell sizing while the no-records or first-load path is active. */
.nxs-grid:has(.nxs-grid-empty),
.nxs-grid:has(.nxs-grid-loading) {
    height: auto !important;
}

.nxs-grid:has(.nxs-grid-empty) .mud-table-container,
.nxs-grid:has(.nxs-grid-loading) .mud-table-container {
    flex: 0 0 auto;
    min-height: 0;
    height: auto !important;
}

/* MudSwitch unchecked track: Mud paints it var(--mud-palette-black) at 48% opacity — and
   our palette Black IS --nxs-surface, so on surface panels the track is invisible (only the
   thumb shows). Lift it to the raised-fill token, full opacity. Checked state is unaffected:
   Mud's .mud-checked rules out-specify this and keep the accent track. */
.mud-switch-span .mud-switch-track {
    background-color: var(--nxs-surface-3);
    opacity: 1;
}

/* NxsButton — the standard primary action button: accent fill, dark text.
   .mud-button-root included for specificity over Mud's filled-primary colours. */
.mud-button-root.nxs-btn {
    --nxs-button-boundary-color: var(--nxs-accent);
    background: var(--nxs-button-boundary-color);
    color: var(--nxs-surface);
}

.mud-button-root.nxs-btn:hover,
.mud-button-root.nxs-btn:focus-visible {
    --nxs-button-boundary-color: var(--nxs-accent-bright);
    background: var(--nxs-button-boundary-color);
}

/* Nexus action buttons have one exact shared height rather than Mud's
   typography-and-padding-derived size variants. */
.mud-button-root.nxs-btn-action {
    box-sizing: border-box;
    height: var(--nxs-control-h-lg);
    min-height: var(--nxs-control-h-lg);
    padding: 0 var(--nxs-space-16);
}

/* Compact standalone icon actions share one outlined, tokenized control boundary. */
.mud-icon-button.nxs-icon-button--outlined {
    box-sizing: border-box;
    width: var(--nxs-control-h-sm);
    min-width: var(--nxs-control-h-sm);
    height: var(--nxs-control-h-sm);
    min-height: var(--nxs-control-h-sm);
    padding: 0;
    border: 1px solid var(--nxs-border);
    border-radius: var(--nxs-radius-md);
}

/* NxsAsyncButton keeps a fixed trailing slot so replacing its action icon with the
   standard spinner never changes the button's dimensions. */
.mud-button-root.nxs-async-button {
    padding: 0 var(--nxs-space-12);
}

.nxs-async-button__content {
    display: inline-flex;
    align-items: center;
    gap: var(--nxs-space-12);
}

.nxs-async-button__icon {
    display: inline-flex;
    flex: 0 0 var(--nxs-space-20);
    align-items: center;
    justify-content: center;
    width: var(--nxs-space-20);
    height: var(--nxs-space-20);
    min-width: 0;
    min-height: 0;
    line-height: 0;
}

.nxs-async-button__icon .mud-icon-root,
.nxs-async-button__icon .nxs-async-button__spinner {
    display: block;
    flex: none;
    margin: 0;
}

/* NxsButton secondary (Kind="Secondary"): outlined accent for actions beside a primary button. */
.mud-button-root.nxs-btn-secondary {
    --nxs-button-boundary-color: var(--nxs-border-strong);
    background: transparent;
    color: var(--nxs-accent);
    border: 1px solid var(--nxs-button-boundary-color);
}

.mud-button-root.nxs-btn-secondary:hover,
.mud-button-root.nxs-btn-secondary:focus-visible {
    --nxs-button-boundary-color: var(--nxs-accent);
    background: var(--nxs-accent-8);
    border-color: var(--nxs-button-boundary-color);
    color: var(--nxs-accent-bright);
}

/* NxsButton text (Kind="Text"): borderless accent text for low-emphasis inline actions
   (e.g. an alert's retry). */
.mud-button-root.nxs-btn-text {
    --nxs-button-boundary-color: var(--nxs-accent);
    background: transparent;
    color: var(--nxs-button-boundary-color);
}

.mud-button-root.nxs-btn-text:hover,
.mud-button-root.nxs-btn-text:focus-visible {
    --nxs-button-boundary-color: var(--nxs-accent-bright);
    background: var(--nxs-accent-8);
    color: var(--nxs-button-boundary-color);
}

.mud-button-root.nxs-btn--attention-pulse-a {
    animation: nxs-button-attention-pulse-a 700ms ease-in-out 2;
}

.mud-button-root.nxs-btn--attention-pulse-b {
    animation: nxs-button-attention-pulse-b 700ms ease-in-out 2;
}

@keyframes nxs-button-attention-pulse-a {
    0%,
    100% {
        box-shadow: 0 0 0 0 transparent, 0 0 0 0 transparent;
    }

    50% {
        box-shadow:
            0 0 0 var(--nxs-space-4) color-mix(in srgb, var(--nxs-button-boundary-color) 34%, transparent),
            0 0 var(--nxs-space-16) var(--nxs-space-6) color-mix(in srgb, var(--nxs-button-boundary-color) 22%, transparent);
    }
}

@keyframes nxs-button-attention-pulse-b {
    0%,
    100% {
        box-shadow: 0 0 0 0 transparent, 0 0 0 0 transparent;
    }

    50% {
        box-shadow:
            0 0 0 var(--nxs-space-4) color-mix(in srgb, var(--nxs-button-boundary-color) 34%, transparent),
            0 0 var(--nxs-space-16) var(--nxs-space-6) color-mix(in srgb, var(--nxs-button-boundary-color) 22%, transparent);
    }
}

@media (prefers-reduced-motion: reduce) {
    .mud-button-root.nxs-btn--attention-pulse-a,
    .mud-button-root.nxs-btn--attention-pulse-b {
        animation: none;
    }
}

/* NxsInputShell — the house input pill shared by the global-search bar and the agent
   composer: flat outlined surface (no drop shadow, per the house panel style), leading
   action / input / trailing actions in one row. The --soft modifier is the rounded-rect
   variant for content that grows tall (multiline composer); narrow viewports soften the
   pill the same way. */
.mud-paper.nxs-input-shell {
    display: flex;
    align-items: center;
    gap: var(--nxs-space-8);
    width: 100%;
    min-width: 0;
    padding: var(--nxs-space-4) var(--nxs-space-12);
    background: var(--nxs-surface-2);
    border: 1px solid var(--nxs-border-strong);
    border-radius: var(--nxs-radius-pill);
    transition: border-color 120ms ease;
}

.mud-paper.nxs-input-shell:focus-within {
    border-color: var(--nxs-accent);
}

.mud-paper.nxs-input-shell.nxs-input-shell--soft {
    border-radius: var(--nxs-radius-lg);
    /* Tall-content variant: the input can grow to several lines, so the leading and
       trailing actions pin to the bottom edge (beside the newest line) instead of
       floating in the vertical middle of the block. */
    align-items: flex-end;
}

/* The bottom-pinned action slots are sized to exactly one text line's box (1.5rem
   line-height plus the input slot's two 8px pads) with the icon centred inside it.
   That centres the icons on the text at a single line AND keeps them centred on the
   newest line as the field grows — one rule, no per-state offsets. */
.nxs-input-shell--soft .nxs-input-shell__leading,
.nxs-input-shell--soft .nxs-input-shell__trailing {
    height: calc(1.5rem + (2 * var(--nxs-space-8)));
    align-items: center;
}

.nxs-input-shell__leading,
.nxs-input-shell__trailing {
    display: flex;
    align-items: center;
    flex: 0 0 auto;
}

.nxs-input-shell__content {
    flex: 1 1 auto;
    min-width: 0;
}

/* Inputs inside the shell render bare — the shell IS the field chrome, so a Mud
   outlined field placed in it must not draw a second border (focus feedback comes from
   the shell's focus-within accent). These target Mud's input internals deliberately:
   no Mud parameter can remove the outlined border, and the global-search input already
   establishes this bare-inside-the-pill treatment. !important because NxsFieldShell
   pins this element's border with its own !important state rules. */
.nxs-input-shell__content .mud-input-outlined-border {
    display: none !important;
    border: 0 !important;
}

.nxs-input-shell__content .mud-input-control {
    margin: 0;
}

.nxs-input-shell__content .mud-input-control-input-container {
    padding: 0;
}

.nxs-input-shell__content .mud-input-slot {
    padding: var(--nxs-space-8) 0;
    background: transparent;
}

@media (max-width: 640px) {
    .mud-paper.nxs-input-shell {
        border-radius: var(--nxs-radius-lg);
    }
}

/* NxsCard — the site's standard content card: outlined, padded, rounded panel with an
   optional titled header (MudText + divider) and an optional divider-anchored footer.
   Chrome is token-driven; lives here (global) so it reaches the MudPaper root without ::deep. */
.mud-paper.nxs-card {
    --nxs-field-backdrop: var(--nxs-surface-raised);
    padding: var(--nxs-space-24);
    background: var(--nxs-surface-raised);
    border: 1px solid var(--nxs-border);
    border-radius: var(--nxs-radius-lg);
}

/* Chart cards keep NxsCard spacing/chrome, but use the same surface as Plotly charts so
   chart margins and surrounding card padding read as one continuous chart surface. */
.mud-paper.nxs-card.nxs-chart-card {
    --nxs-field-backdrop: var(--nxs-surface);
    background: var(--nxs-surface);
}

/* Geometry-viewer cards use their header divider as the exact top edge of the
   visualisation. The viewer host owns any spacing inside the body. */
.mud-paper.nxs-card.nxs-card--flush-viewer > .nxs-card__header {
    margin-bottom: 0;
}

/* Part-overview timeline card: zero the card padding so the horizontal scrollbar runs edge-to-edge
   along the card's bottom edge. The timeline's inset comes uniformly from .part-overview-flow's
   padding instead, so the spacing reads consistent on all four sides. */
.mud-paper.nxs-card.part-overview-card {
    padding: 0;
}

/* NxsCard semantic edge variants — a status-coloured left edge for stat/status cards
   (e.g. the SPC machine-health tiles). The variant owns ONLY the edge; surface,
   padding, and content styling stay with the card and the consuming page. Pair the
   colour with text or an icon (UX principle 10). */
.mud-paper.nxs-card.nxs-card--edge-danger {
    border-left: 3px solid var(--nxs-danger-border);
}

.mud-paper.nxs-card.nxs-card--edge-warning {
    border-left: 3px solid var(--nxs-warning);
}

.mud-paper.nxs-card.nxs-card--edge-success {
    border-left: 3px solid var(--nxs-success-border);
}

.nxs-card__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--nxs-space-16);
    margin-bottom: var(--nxs-space-16);
    padding-bottom: var(--nxs-space-12);
    border-bottom: 1px solid var(--nxs-border);
}

.nxs-card__title {
    margin: 0;
    font-weight: 400;
}

.nxs-card__header-actions {
    display: flex;
    align-items: center;
    gap: var(--nxs-space-8);
    flex-shrink: 0;
}

.nxs-card__actions {
    margin-top: var(--nxs-space-24);
    padding-top: var(--nxs-space-16);
    border-top: 1px solid var(--nxs-border);
    display: flex;
    flex-wrap: wrap;
    gap: var(--nxs-space-12);
}

@media (max-width: 900px) {
    .mud-paper.nxs-card {
        padding: var(--nxs-space-20);
    }
}

/* Standard grid empty state — calm, muted, centered. NxsDataGrid renders this by default;
   pages only override the wording (keep this class on the override). */
.nxs-grid-empty {
    padding: var(--nxs-space-24);
    text-align: center;
    color: var(--nxs-text-muted);
    font-size: 0.9rem;
    line-height: 1.5;
}

/* Standard grid first-load state — NxsDataGrid renders this by default when Loading is true
   and there are no rows yet (refreshes with rows visible stay still; the built-in header
   progress bar is suppressed by the .nxs-grid .mud-table-loading rule above). */
.nxs-grid-loading {
    display: grid;
    place-items: center;
    padding: var(--nxs-space-24);
}

/* Standard bounded workflow dialog. It keeps form fields readable on wide screens while
   preserving enough room for a data grid once the workflow produces results. */
.mud-dialog.nxs-dialog--workflow {
    --nxs-field-backdrop: var(--mud-palette-surface);
    width: min(calc(100vw - var(--nxs-space-48)), var(--nxs-dialog-workflow-max));
    max-width: var(--nxs-dialog-workflow-max);
}

.mud-dialog.nxs-dialog--workflow .mud-dialog-title {
    padding: var(--nxs-space-16) var(--nxs-space-20) var(--nxs-space-12);
}

.mud-dialog.nxs-dialog--workflow .mud-dialog-content {
    padding: 0 var(--nxs-space-20) var(--nxs-space-20);
}

.mud-dialog.nxs-dialog--workflow .mud-dialog-actions {
    gap: var(--nxs-space-8);
    padding: 0 var(--nxs-space-20) var(--nxs-space-16);
}

/* ── NxsTabs — the site's shared tab strip ─────────────────────────────────────────
   Two variants, one modifier:
     --primary    framed rounded bar; page-level sections.
     --secondary  dense hairline strip; sub-sections inside an already-framed area.
     --contained  (modifier, Contained="true") bar + panel area join into one bordered
                  panel — the bar is the top edge, content shares the raised surface.
                  Tab-bar toolbars go in MudTabs' native Header slot, which renders
                  inline inside the bar (no overlay/reserved-width tricks). */
.nxs-tabs {
    min-width: 0;
}

.nxs-tabs > .mud-tabs-panels > div {
    min-width: 0;
}

/* Header slot (MudTabs Header fragment, HeaderPosition defaults to After): inline
   toolbar at the right end of the bar row. */
.nxs-tabs .mud-tabs-header {
    display: flex;
    align-items: center;
    flex: 1 1 auto;
    justify-content: flex-end;
    min-width: 0;
    gap: 8px;
    padding: 4px 12px 4px 16px;
}

.nxs-tabs--primary > .mud-tabs-tabbar {
    background: var(--nxs-surface-raised);
    border: 1px solid var(--nxs-border);
    border-radius: var(--nxs-radius-lg);
    overflow: hidden;
}

/* Tab cells must fill the bar's FULL height even when Header content (toolbars) makes
   the bar taller than the tabs — the active underline is the cell's bottom edge and has
   to sit on the bar's bottom border. Mud breaks the height-stretch chain in two places:
   .mud-tabs-tabbar-content is inline-block, and each tab sits in a `d-inline-block`
   utility wrapper (display:inline-block !important — hence the !important here). */
.nxs-tabs .mud-tabs-tabbar .mud-tabs-tabbar-inner {
    min-height: 0;
    align-items: stretch;
}

.nxs-tabs .mud-tabs-tabbar-content {
    display: flex;
    align-items: stretch;
}

.nxs-tabs .mud-tabs-tabbar-content .mud-tabs-tabbar-wrapper {
    align-items: stretch;
}

.nxs-tabs .mud-tabs-tabbar-wrapper > div {
    display: inline-flex !important;
    align-items: stretch;
}

.nxs-tabs .mud-tab {
    flex: 0 0 auto;
    min-height: var(--nxs-tabs-tab-min-height, 38px);
    padding: var(--nxs-tabs-tab-padding, 6px 14px);
    font-size: var(--nxs-tabs-tab-font-size, inherit);
    color: var(--nxs-text-subtle);
    transition: background-color 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,
                color 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
}

/* The active underline is drawn on the tab cell itself (inset shadow), not Mud's
   JS-measured .mud-tab-slider — the slider is positioned from pixel measurements that
   drift out of line with the cell (and with the hover highlight, which IS the cell).
   Same element for highlight and underline = aligned by construction. */
/* Specificity must beat the wrapper-children stretch rule below (also !important, and
   its `> div` type selector adds 0,2,1 — a bare .nxs-tabs .mud-tab-slider at 0,2,0
   LOSES and the slider quietly comes back). */
.nxs-tabs .mud-tabs-tabbar-wrapper > div.mud-tab-slider {
    display: none !important;
}

.nxs-tabs .mud-tab.mud-tab-active {
    color: var(--nxs-text);
    box-shadow: inset 0 -2px 0 var(--nxs-accent);
}

@media (hover: hover) and (pointer: fine) {
    .nxs-tabs .mud-tab:hover {
        background-color: var(--nxs-overlay-hover);
        color: var(--nxs-text);
    }

    .nxs-tabs .mud-tab.mud-tab-active:hover {
        background-color: var(--nxs-overlay-hover);
        box-shadow: inset 0 -2px 0 var(--nxs-accent);
    }
}

/* The bar's children stretch (so tab cells reach the bottom border) — the scroll-button
   wrappers stretch too, so they must centre their fixed-size chevrons themselves. */
.nxs-tabs .mud-tabs-scroll-button {
    display: flex;
    align-items: center;
}

.nxs-tabs .mud-tabs-scroll-button .mud-button-root {
    width: 36px;
    height: 36px;
    border-radius: var(--nxs-radius-pill);
    color: var(--nxs-text-subtle);
}

@media (hover: hover) and (pointer: fine) {
    .nxs-tabs .mud-tabs-scroll-button .mud-button-root:hover {
        background: var(--nxs-overlay-hover);
        color: var(--nxs-text);
    }
}

/* Secondary: dense strip anchored by a hairline so it never reads as floating text. */
.nxs-tabs--secondary > .mud-tabs-tabbar {
    background: transparent;
    border-bottom: 1px solid var(--nxs-divider);
}

.nxs-tabs--secondary .mud-tab {
    font-weight: 600;
}

.nxs-tabs--secondary .mud-tabs-scroll-button .mud-button-root {
    width: 36px;
    height: 36px;
}

/* Contained: bar + panels join into one framed panel. The bar keeps only its top
   corners; the panel area carries the remaining frame and its own padding (the strip's
   pt-* gap utility is suppressed by the component in this mode). */
.nxs-tabs--contained {
    --nxs-tabs-panel-pad: 16px;
}

.nxs-tabs--contained > .mud-tabs-tabbar {
    border-radius: var(--nxs-radius-lg) var(--nxs-radius-lg) 0 0;
}

.nxs-tabs--contained > .mud-tabs-panels {
    --nxs-field-backdrop: var(--nxs-surface-raised);
    padding: var(--nxs-tabs-panel-pad);
    background: var(--nxs-surface-raised);
    border: 1px solid var(--nxs-border);
    border-top: 0;
    border-radius: 0 0 var(--nxs-radius-lg) var(--nxs-radius-lg);
}

/* A secondary strip LEADING a contained panel (Docked="true") sits flush under the
   parent's tab bar while keeping its divider aligned to the same inset as the content
   below. The pad var inherits from the containing .nxs-tabs--contained. */
.nxs-tabs--docked > .mud-tabs-tabbar {
    margin-block: calc(-1 * var(--nxs-tabs-panel-pad, 16px)) var(--nxs-tabs-panel-pad, 16px);
    margin-inline: 0;
    padding: 0;
}

/* ── NxsTabHeader — one frame for a two-row tab header ────────────────────────────
   NxsTabs still owns tab selection; rows render strip-only (their empty Mud panel
   areas are hidden). Page content lives OUTSIDE the stack.

   Geometry: deliberately simple. The whole stack is ONE bordered rounded panel on
   the raised surface; the rows are separated by a hairline divider. Active tabs use
   the standard .nxs-tabs accent underline in both rows — same indicator as the rest
   of the app. Grouping is communicated by containment, not per-tab borders. */
.nxs-tab-header {
    min-width: 0;
    background: var(--nxs-surface-raised);
    border: 1px solid var(--nxs-border);
    border-radius: var(--nxs-radius-lg);
    overflow: hidden; /* clip row hover overlays to the rounded corners */
}

.nxs-tab-header__row {
    display: flex;
    align-items: stretch;
    min-width: 0;
}

.nxs-tab-header__tabs {
    flex: 1 1 auto;
    min-width: 0;
}

/* Rows host bare strips: the stack owns the chrome, so strip chrome comes off and
   the (empty) panel areas are hidden. */
.nxs-tab-header__tabs > .nxs-tabs,
.nxs-tab-header__tabs > .nxs-tabs > .mud-tabs-tabbar {
    height: 100%;
    min-width: 0;
}

.nxs-tab-header__tabs > .nxs-tabs > .mud-tabs-tabbar {
    background: transparent;
    border: 0;
    border-radius: 0;
}

.nxs-tab-header__tabs > .nxs-tabs > .mud-tabs-panels {
    display: none;
}

/* With a child row present, the parent's selection is shown by the shared highlight
   alone (active tab + sub row share the same fill) — the accent underline belongs to
   the child row only, so it comes OFF the parent here, including on hover (the shared
   .nxs-tabs :hover rule would otherwise re-add it). */
.nxs-tab-header--has-subtabs .nxs-tabs--primary .mud-tab.mud-tab-active {
    background-color: var(--nxs-surface-2);
    box-shadow: none;
}

@media (hover: hover) and (pointer: fine) {
    .nxs-tab-header--has-subtabs .nxs-tabs--primary .mud-tab.mud-tab-active:hover {
        background-color: var(--nxs-surface-2);
        box-shadow: none;
    }
}

/* No divider between the rows: the sub row's lighter fill separates them tonally, and
   a hairline would cut through the active tab's connection to its sub row (they share
   the same fill so the selection reads as one region). */
.nxs-tab-header__row--subtabs {
    background-color: var(--nxs-surface-2);
}

html, body {
    font-family: Roboto, serif;
    /* Lock the window so the only vertical scrollbar is the content area's (article.content),
       not a full-height window scrollbar running behind the fixed app bar. */
    height: 100%;
    overflow: hidden;
}

/* Opt-in reading column for form/detail content — caps line length and centres within the
   full-width page. Grids/tables stay full-width (the page default) and must not use this. */
.nxs-reading-column {
    width: 100%;
    max-width: var(--nxs-reading-max);
    margin-inline: auto;
}

/* NxsFormSections / NxsFormSection — grouped form sections on create/detail pages: an
   accordion whose sections are all expanded by default. Each section is its own rounded
   house panel; selectors carry the .mud-expansion-panels/.mud-expand-panel prefixes to
   out-specify MudBlazor's stock rules (joined panels, 16px expand margins, 24px gutters). */
.nxs-form-sections.mud-expansion-panels {
    display: flex;
    flex-direction: column;
    gap: var(--nxs-space-16);
}

.nxs-form-sections.mud-expansion-panels .mud-expand-panel {
    background: var(--nxs-surface-2);
    border: 1px solid var(--nxs-border);
    border-radius: var(--nxs-radius-lg);
    overflow: hidden; /* the panel owns its rounding; everything inside stays square */
}

/* Sections keep a fixed gap; Mud's expand/collapse margin choreography and the
   joined-panel border/radius juggling would make the list jump and seam. */
.nxs-form-sections.mud-expansion-panels .mud-expand-panel.mud-panel-expanded {
    margin: 0;
}

.nxs-form-sections.mud-expansion-panels .mud-expand-panel:last-child,
.nxs-form-sections.mud-expansion-panels .mud-expand-panel.mud-panel-next-expanded {
    border-bottom: 1px solid var(--nxs-border);
}

.nxs-form-sections.mud-expansion-panels .mud-expand-panel-header {
    min-height: 3rem;
    padding: var(--nxs-space-12) var(--nxs-space-16);
    font-size: var(--nxs-text-size-md);
    font-weight: 700;
}

/* Chevron leads the section title (same reading order as the heater-plate accordion) */
.nxs-form-sections.mud-expansion-panels .mud-expand-panel-header .mud-expand-panel-icon {
    order: -1;
    margin-left: 0;
    margin-right: var(--nxs-space-8);
}

.nxs-form-sections.mud-expansion-panels .mud-expand-panel-header .mud-expand-panel-text {
    flex: 0 1 auto;
}

/* A section carrying header actions needs the full title row to push them to the
   end; a plain titled section keeps the hug-content width above. */
.nxs-form-sections.mud-expansion-panels .mud-expand-panel-header .mud-expand-panel-text:has(.nxs-form-section__header) {
    flex: 1 1 auto;
    min-width: 0;
}

.nxs-form-section__header {
    display: flex;
    align-items: center;
    gap: var(--nxs-space-8);
    width: 100%;
    min-width: 0;
}

.nxs-form-section__title {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}

.nxs-form-section__actions {
    display: flex;
    align-items: center;
    gap: var(--nxs-space-4);
    /* The header sets a bold section weight; actions are chrome, not title text. */
    margin-left: auto;
    font-weight: 400;
}

/* The trigger anchors the popover, so the host establishes the positioning context. */
.nxs-info-popover {
    position: relative;
    display: inline-flex;
}

/* Portalled out of the component subtree, so these rules cannot live in scoped CSS. */
.nxs-info-popover__surface.mud-popover {
    max-width: min(32rem, calc(100vw - var(--nxs-space-32)));
    background: var(--nxs-surface-raised);
    border: 1px solid var(--nxs-border);
    border-radius: var(--nxs-radius-md);
}

.nxs-info-popover__content {
    display: flex;
    flex-direction: column;
    gap: var(--nxs-space-8);
    padding: var(--nxs-space-12) var(--nxs-space-16);
}

.nxs-info-popover__title {
    color: var(--nxs-text);
}

/* Present only to catch the dismissing click; it must not tint the page. */
.nxs-info-popover__overlay.mud-overlay {
    background: transparent;
}

.nxs-form-sections.mud-expansion-panels .mud-expand-panel .mud-expand-panel-content {
    padding: var(--nxs-space-4) var(--nxs-space-16) var(--nxs-space-16);
    /* Floating field labels inside the panel mask the outline against the panel fill. */
    --nxs-field-backdrop: var(--nxs-surface-2);
}

/* Form sections inside a raised modal need an intermediate surface: still inset
   from the modal shell, without falling back to the near-black page-card surface. */
.nxs-modal .nxs-form-sections.mud-expansion-panels .mud-expand-panel {
    background: var(--nxs-surface-raised);
}

.nxs-modal .nxs-form-sections.mud-expansion-panels .mud-expand-panel .mud-expand-panel-content {
    --nxs-field-backdrop: var(--nxs-surface-raised);
}

table {
    width: auto;
    border-collapse: collapse;
}

td {
    vertical-align: middle;
    border: none;
}

input {
    width: 100%;
    height: 100%;
    padding-left: 1em;
    
    background-color: var(--bs-secondary-bg);
    border: none;
}

select {
    background-color: var(--bs-secondary-bg);
    border: none;
}

input[type="file"] {
    padding: 0;
    border: 2px solid #444444;
    border-radius: var(--nxs-radius-lg);

    &:hover {
        cursor: pointer;
    }

    &::file-selector-button {
        margin-right: 1em;
        padding: 0.75em;
        border: none;

        color: var(--nxs-text);
        background-color: var(--nxs-surface-3);
    }

    &::file-selector-button:hover {
        cursor: pointer;
    }
}

ul, .submenu{
    list-style: none;
}

textarea {
    height: 10em;
    min-height: 10em;
    max-height: 25em;

    padding: 0.5em;
    border: none;
    background-color: var(--bs-secondary-bg);
}

/* Mud owns the height of auto-growing Nxs fields. Neutralise the legacy native-textarea
   dimensions above only for this explicit house component mode. */
.nxs-text-field--auto-grow textarea {
    height: auto;
    min-height: 0;
    max-height: none;
}

select {
    padding-left: 1em;
}

h1:focus {
    outline: none;
}

a, .btn-link {
    text-decoration: none;
    color: var(--nxs-accent);
}

.warning{
    color: orange;
}

.nxs-button {
    width: 100%;
}

.nxs-section {
    --nxs-field-backdrop: var(--nxs-surface-2);
    padding: 3em;
    border-radius: var(--nxs-radius-lg);

    background-color: var(--nxs-surface-2);
    box-shadow: 0 0 5px var(--bs-box-shadow);
    /*border: 1px var(--bs-emphasis-color) solid;*/

    transition: box-shadow 0.1s;
    margin-bottom: 4em;
}

.nxs-section-label {
    padding-left: 0.5em;
    font-weight: bold;
    margin-bottom: 0.5em;
}

.nxs-section-as-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 3em;
}

.nxs-section-as-flex {
    display: flex;
    flex-direction: column;
    gap: 3em;
}

.nxs-section-as-grid-triple {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr);
    row-gap: 3em;
    column-gap: 3em;
}

.nxs-section-as-grid-quad {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr);
    row-gap: 3em;
    column-gap: 2em;
}

.nxs-section-item {
    flex: auto;
    display: flex;
    flex-direction: column;
    row-gap: 0.5em;
    max-width: 100%;
}

.airtable-source-selector {
    width: 100%;
}

.airtable-source-selector__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1em;
    width: 100%;
}

.airtable-source-selector__label-row {
    display: inline-flex;
    align-items: center;
    gap: 0.5em;
    min-width: 0;
}

.airtable-source-selector__show-all {
    display: inline-flex;
    align-items: center;
    gap: 0.4em;
    margin-left: auto;
    white-space: nowrap;
    font-weight: normal;
}

.airtable-source-selector__checkbox[type="checkbox"] {
    width: 1rem;
    height: 1rem;
    padding-left: 0;
    margin: 0;
    flex: 0 0 auto;
}

.nxs-section-chart {
    width: 45em;
    height: 45em;
    align-self: center;
}

.nxs-section-item-read-only-value {
    padding: 0.75em;
    background-color: var(--bs-body-bg);

    box-shadow: 0 0 10px var(--bs-box-shadow);
    border-radius: var(--nxs-radius-md);
    user-select: none;

    overflow: hidden;
    text-overflow: ellipsis;

}


.nxs-section-item-read-only-value:hover {
    cursor: pointer;
    box-shadow: 0 0 10px 2px var(--bs-box-shadow);
}

.nxs-section-item-read-only-value:active {
    box-shadow: 0 0 5px 0 var(--bs-box-shadow);
}

.nxs-section-item-read-only-note {
    padding: 0.75em;
    background-color: var(--bs-body-bg);
    box-shadow: 0 0 10px var(--bs-box-shadow);
    border-radius: var(--nxs-radius-md);
    height: 10em;
    white-space: pre-wrap;
    user-select: none;
    overflow: hidden;
    text-overflow: ellipsis;
}
.nxs-section-item-read-only-note:hover {
    cursor: pointer;
    box-shadow: 0 0 10px 2px var(--bs-box-shadow);
}

.nxs-section-item-read-only-note:active {
    box-shadow: 0 0 5px 0 var(--bs-box-shadow);
}



.nxs-center-content-vertically {
    display: flex;
    flex-direction: column;
    justify-content: center;
    
    height: 100%;
}

.nxs-center-content-horizontally {
    margin: auto;
}

.nxs-row-flex {
    display: flex;
    flex-direction: row;
    column-gap: 1em;
}

/* ----- Anemometer Diagram ----- */

.AnemometerDiagram {
    display: grid;
    width: 34em;
    grid-template-rows: 2em 32em;
    align-content: center;
}

.AnemometerDiagram-row {
    display: grid;
    grid-template-columns: 32em 2em;
    align-items: center;
}

/* ------------------------------ */


/* -------- Renderer CSS -------- */

.center-in-container {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
}

.part-listbox {
    position: absolute;
    margin-left: 1em;
    margin-top: 1em;
    
    max-height: calc(100% - 2.5em) !important;
}



/* ------------------------------ */


.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
    box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}

.content {
    padding-top: 1.1rem;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid var(--nxs-success);
}

.invalid {
    outline: 1px solid var(--nxs-danger);
}

.italic {
    font-style: italic;
}

.validation-message {
    color: var(--nxs-danger);
}

.fill-splitter-pane {
    height: 100% !important;
}

.file-input-zone {
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: var(--nxs-accent);
    color: black;
    border-color: black;
    cursor: pointer;
    position: relative;
    text-transform: uppercase;
    font-weight: 500;
    padding-block: 8px;
    padding-inline: 18px;
    font-size: 14px;
    line-height: 1.4285714286;
    box-sizing: border-box;
    border-width: 1px;
    border-style: solid;
    gap: 8px;
    border-radius: var(--nxs-radius-sm);
}

    .file-input-zone:hover {
        background-color: var(--nxs-accent);
    }

    .file-input-zone input[type=file] {
        position: absolute;
        width: 100%;
        height: 100%;
        opacity: 0;
        cursor: pointer;
    }



/* --- Blazor Overrides --- */

.combo-box-input-text {
    display: inline-block;
    border: none;
    border-radius: var(--nxs-radius-sm) 0 0 var(--nxs-radius-sm);
}

.combo-box-input-text:focus {
    outline: none;
}

/* --- Measurement Grid Layout --- */

.measurement-grid-scroll {
    width: 100%;
    min-width: 0;
}

.measurement-grid-scroll .k-table,
.measurement-grid-scroll .k-grid-table {
    min-width: 100%;
}

/* For japanese localization font override */
.lang-ja {
    --bs-font-sans-serif: "Roboto", "Noto Sans JP", sans-serif;
}

.lang-ja,
.lang-ja body,
.lang-ja button,
.lang-ja input,
.lang-ja select,
.lang-ja textarea,
.lang-ja .mud-typography,
.lang-ja .k-widget {
    font-family: "Roboto", "Noto Sans JP", sans-serif !important;
}

/* --- App shell chrome (MudAppBar/MudDrawer are component roots, so scoped
       ::deep rules from MainLayout.razor.css cannot reach them) --- */

.mud-appbar.app-header {
    border-bottom: 1px solid var(--nxs-border);
}

.mud-drawer.app-nav-drawer {
    border-right: 1px solid var(--nxs-border);
}

/* Confine scrolling to the content area below the fixed app bar. The layout fills the viewport;
   main-content fills the layout and keeps MudBlazor's app-bar-height top padding but does NOT
   scroll itself — the inner article (article.content, a plain element that already sits below the
   bar) is the scroll container. So its scrollbar spans only from the bar's bottom edge down. */
.mud-layout {
    height: 100vh;
}

/* The agent chat is a full-bleed, app-like page that owns all of its scrolling internally
   (rail list, transcript). The content region therefore drops its gutter padding and the
   page fills it exactly — if the document kept even a few hidden overflow pixels, any
   focus or scrollIntoView (e.g. MudList revealing a selected row) would shift the whole
   page sideways/upwards to spend them. */
/* The .mud-main-content prefix lifts this above the scoped MainLayout gutter rule
   (article.content[scope]) — without it the two tie on specificity and the winner
   depends on stylesheet link order. */
.mud-main-content article.content:has(.agent-page) {
    padding: 0;
}

.mud-main-content {
    height: 100%;
    box-sizing: border-box;
    overflow: hidden;
}

.component-spc-chart-card__rule-definitions-tooltip {
    box-sizing: border-box;
    width: min(28rem, calc(100vw - var(--nxs-space-32)));
    max-width: min(28rem, calc(100vw - var(--nxs-space-32)));
    max-height: 18rem;
    overflow-y: auto;
    white-space: normal;
}

.component-spc-chart-card__rule-definitions-tooltip-title {
    display: block;
    margin-bottom: var(--nxs-space-8);
}

.component-spc-chart-card__rule-definitions-tooltip-list {
    display: grid;
    gap: var(--nxs-space-8);
    margin: 0;
    padding-inline-start: var(--nxs-space-16);
}

.component-spc-chart-card__rule-definition {
    display: grid;
    gap: var(--nxs-space-4);
    line-height: 1.4;
}

.component-spc-chart-card__rule-definition span {
    color: var(--nxs-text-subtle);
}
