.file-upload-input {
    position: absolute;
    width: 100%;
    height: 100%;
    overflow: hidden;
    z-index: 10;
    opacity: 0;
}

.overlay-container {
    position: relative;
}

[data-theme="dark"] .js-plotly-plot .main-svg {
    background: transparent !important;
}

.hp-field {
    position: absolute;
    left: -9999px;
    top: -9999px;
}

/* Read-only onboarding progress indicator (see OnboardingProgressStepper). */
.onboarding-stepper {
    display: flex;
    align-items: flex-start;
    width: 100%;
    gap: 4px;
}

.onboarding-stepper__step {
    display: flex;
    flex-direction: column;
    align-items: center;
    flex: 0 1 auto;
    max-width: 7rem;
    min-width: 0;
    text-align: center;
}

.onboarding-stepper__connector {
    flex: 1 1 auto;
    height: 2px;
    margin-top: 17px;
    border-radius: 1px;
    background-color: var(--mud-palette-lines-default);
}

.onboarding-stepper__connector--done {
    background-color: var(--mud-palette-primary);
}

.onboarding-stepper__circle {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    font-size: 0.875rem;
    font-weight: 600;
}

.onboarding-stepper__circle--completed,
.onboarding-stepper__circle--current {
    background-color: var(--mud-palette-primary);
    color: var(--mud-palette-primary-text);
}

.onboarding-stepper__circle--current {
    box-shadow: 0 0 0 4px var(--mud-palette-primary-hover);
}

.onboarding-stepper__circle--upcoming {
    background-color: transparent;
    color: var(--mud-palette-text-secondary);
    border: 2px solid var(--mud-palette-lines-default);
}

.onboarding-stepper__label {
    margin-top: 8px;
    font-size: 0.75rem;
    line-height: 1.2;
    overflow-wrap: anywhere;
    color: var(--mud-palette-text-secondary);
}

.onboarding-stepper__label--current {
    font-weight: 600;
    color: var(--mud-palette-primary);
}

/* Muted explanatory copy - subtitles, hints, descriptions. Rendered through the MutedText component,
   which is the only place that should reference this class. Bound to the palette token generated from
   MudTheme's public Palette.TextSecondary, not to a MudBlazor utility class name. */
.app-text-muted {
    color: var(--mud-palette-text-secondary);
}

/* SysAdmin dashboard (#1089): a hairline between health / subscription rows, none above the first. */
.app-health-row + .app-health-row {
    border-top: 1px dashed var(--mud-palette-divider);
}

/* Feed rows: a long actor name or activity title must not push the chip and the IP off the card. */
.app-truncate {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}

/* Throughput sparkline: fourteen rectangles with no axes. Plotly and OxyPlot are both already in the
   app and both are far more machinery than this picture is worth. */
.app-spark {
    display: flex;
    align-items: flex-end;
    gap: 3px;
    height: 72px;
}

.app-spark-failures {
    height: 24px;
}

.app-spark-bar {
    flex: 1;
    display: block;
    min-height: 1px;
    border-radius: 2px 2px 0 0;
    background-color: var(--mud-palette-primary);
    opacity: 0.55;
}

.app-spark-bar-failed {
    background-color: var(--mud-palette-error);
    opacity: 0.75;
}

/* Meter topology diagram (#1116). Node box size is set inline from the C# layout constants, so it
   is defined in exactly one place; everything visual lives here. */
.topology-scroll {
    overflow: auto;
    max-height: 70vh;
}

.topology-canvas {
    position: relative;
}

.topology-edges {
    position: absolute;
    inset: 0;
    pointer-events: none;
    overflow: visible;
}

.topology-edge {
    fill: none;
    stroke-width: 2px;
}

/* The child draws from its parent. Neutral rather than semantic - but lines-default is a divider tone
   and reads as barely-there at this length, so it takes the secondary TEXT tone instead: still
   neutral, actually legible. */
.topology-edge-draws {
    stroke: var(--mud-palette-text-secondary);
}

/* The child feeds its parent. Context, so Info - and the dash carries the same fact without relying
   on colour, which is why it must agree with the arrow rather than encode something else. */
.topology-edge-feeds {
    stroke: var(--mud-palette-info);
    stroke-dasharray: 5 4;
}

/* Centred on the point the layout hands over, so the badge reads as belonging to the line rather than
   to whatever box happens to be nearest. The label itself is not a pointer target - the line underneath
   is not clickable either - but it sits above the boxes, because a badge in the gap between two layers
   can still overlap one and a control that is half-covered cannot be hit. */
.topology-edge-sign {
    position: absolute;
    z-index: 1;
    transform: translate(-50%, -50%);
    display: inline-flex;
    align-items: center;
    gap: 2px;
    color: var(--mud-palette-info);
    font-size: .875rem;
    font-weight: 700;
    line-height: 1;
    pointer-events: none;
}

/* The one part of the badge that IS a target. Scoped to the wrapper rather than lifted onto the badge,
   so the sign stays click-through and the line underneath keeps its hover highlight. Without this the
   pointer-events:none above swallowed every click on the menu it contains. */
.topology-edge-actions {
    display: inline-flex;
    pointer-events: auto;
}

/* Carrying a control, the badge stops being a label and has to look like something you can operate:
   a chip on the line, legible against whatever it happens to sit over. */
.topology-edge-sign-interactive {
    padding: 0 2px 0 7px;
    background-color: var(--mud-palette-surface);
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: 999px;
}

/* An Untermessung carries no sign, so its chip is the button alone and needs no room to its left. */
.topology-edge-sign-interactive:not(:has(.topology-edge-sign-symbol)) {
    padding-left: 2px;
}

.topology-arrow-fill-draws {
    fill: var(--mud-palette-text-secondary);
}

.topology-arrow-fill-feeds {
    fill: var(--mud-palette-info);
}

.topology-legend-key {
    display: flex;
    align-items: center;
    gap: 7px;
}

.topology-node {
    position: absolute;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 11px;
    text-align: left;
    background-color: var(--mud-palette-surface);
    border: 1px solid var(--mud-palette-primary);
    border-radius: var(--mud-default-borderradius);
    color: var(--mud-palette-text-primary);
    cursor: pointer;
    transition: border-color .12s, background-color .12s, opacity .12s;
}

/* Every node carries the primary border now, so the pointed-at one needs a cue of its own. */
.topology-node:hover,
.topology-node:focus-visible {
    background-color: var(--mud-palette-background-gray);
    box-shadow: 0 0 0 2px var(--mud-palette-primary-hover);
}

.topology-node-body {
    min-width: 0;
    flex: 1;
}

.topology-node-name {
    display: block;
    font-size: .8125rem;
    font-weight: 600;
    line-height: 1.3;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.topology-node-chip {
    display: block;
    margin-top: 2px;
    font-size: .6875rem;
    color: var(--mud-palette-text-secondary);
}

/* "Ungezaehlte Abgaenge" (#1109): what a parent's Untermessungen do not account for. Nothing measures
   it, so it is a box that cannot be opened - dashed and muted, with no pointer and no hover cue. It is
   context rather than state, so it takes no state colour. */
.topology-node-unmetered {
    border-style: dashed;
    border-color: var(--mud-palette-lines-default);
    color: var(--mud-palette-text-secondary);
    cursor: default;
}

.topology-node-unmetered:hover,
.topology-node-unmetered:focus-visible {
    background-color: var(--mud-palette-surface);
    box-shadow: none;
}

/* Resting state is full strength for everything. Hovering a node mutes what is NOT on its path, so
   the highlight is subtractive - which is what makes the surviving edges and arrowheads read as
   highlighted rather than merely unchanged. Opacity on a path dims its marker with it. */
.topology-node.topology-dim,
.topology-edge.topology-dim {
    opacity: .18;
}

/* Only reveal the chart's loading overlay if the update actually takes a moment. Most toggles redraw
   in well under a quarter second, and an overlay that flashes on every click reads as jank rather
   than as feedback. `both` keeps it fully transparent through the delay. */
.plot-loading-overlay {
    animation: plot-loading-fade-in 150ms ease-in 250ms both;
}

@keyframes plot-loading-fade-in {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}

/* MudBlazor upper-cases every button through --mud-typography-button-text-transform, which is right for
   an action ("SAVE") and wrong for a label on a selection chip - LOAD DURATION CURVE shouts, and capitals
   are ~8 % wider, which costs a row on the thirteen month chips. Scoped to an opt-in class rather than
   changed on the theme, so ordinary buttons keep the app-wide treatment. */
.text-transform-none {
    text-transform: none !important;
}
