/* ── Nxt.Presentation.Blazor — THE shared control chrome (#2575) ────────────────────────────
   The Blazor twin of Nxt.Presentation.Wpf/Themes/ControlStyles.xaml. The two are meant to render
   the same control; where a renderer genuinely cannot reproduce the other, the difference is recorded in
   doc/Common/design-decisions.md rather than left to be read as an oversight.

   HOW THE TWO HALVES LINE UP, because it decides every selector below.

     A WPF IMPLICIT style (`<Style TargetType="TextBox">`) reaches every control of that type with no
     view change at all. Its exact CSS equivalent is an ELEMENT selector — `input[type="text"]` — and
     that is deliberately what this sheet uses for them. It is what lets the chrome reach three running
     applications through one line per host (their stylesheet declaration) and not one line of view or
     component code. A class-per-control would have required editing every .razor in the repository,
     which is the change AC-3 says must not happen.

     A WPF KEYED style (`Nxt.DefaultButton`) is opted into. Its equivalent is a CLASS — `.nxt-btn` — and
     the `.nxt-` prefix is the namespace this sheet OWNS. No app-local stylesheet may declare a
     top-level selector in it, asserted by ControlChromeTests.

   ELEMENT SELECTORS ARE PAGE-GLOBAL, AND THAT IS THE POINT HERE. CompanionStylesheetTests forbids them
   in an operation companion for a good reason — a companion sheet is INLINED into whatever page mounts
   it, so an element selector there restyles a host that never asked. This sheet is the opposite case: it
   is linked by a host that declared it, and reaching every control on the page is its job. An app sheet
   that wants a different button still wins, because a class beats an element and every host loads its
   own sheet after this one.

   NO COLOUR LITERAL. Every value resolves from a --nxt-* token in palette.css. Until #2575 a .css under
   wwwroot/css was exempt from the literal scan by its PATH; the exemption is now that a file DECLARES
   the palette, and this file declares nothing. Adding a hex here reds the build.
   ────────────────────────────────────────────────────────────────────────────────────────────────── */

/* ══ Type ═════════════════════════════════════════════════════════════════════════════════════════
   The family is --nxt-font-ui (#2962), never a literal: a rule that names a face does not switch
   when the theme does. The sizes are tokens too. */
.nxt-control,
button,
select,
textarea,
input[type="text"],
input[type="search"],
input[type="number"],
input[type="email"],
input[type="password"] {
    font-family: var(--nxt-font-ui);
    font-size: var(--nxt-font-size);
}

/* ══ Text and password input ══════════════════════════════════════════════════════════════════════
   White face, 1px control outline, radius 4, min height 26, and the three stacked lines inside the top
   edge. WPF draws those as three 1px rectangles because it has no inset-shadow resource; CSS composes
   the same three opacities into one inset box-shadow, which is why #2574 shipped them as OPACITIES
   rather than as a shadow token — three numbers are value-compared, a shadow pair would have had to be
   exempted. */
input[type="text"],
input[type="search"],
input[type="number"],
input[type="email"],
input[type="password"],
textarea {
    box-sizing: border-box;
    min-height: var(--nxt-control-height);
    padding: 3px 4px;
    color: var(--nxt-text-primary);
    background: var(--nxt-bg-panel);
    border: var(--nxt-border-thickness) solid var(--nxt-border-control);
    border-radius: var(--nxt-corner-radius);
    box-shadow:
        inset 0 1px 0 color-mix(in srgb, var(--nxt-chrome-inset-shadow)
              calc(var(--nxt-input-inset-outer-opacity) * 100%), transparent),
        inset 0 2px 0 color-mix(in srgb, var(--nxt-chrome-inset-shadow)
              calc(var(--nxt-input-inset-middle-opacity) * 100%), transparent),
        inset 0 3px 0 color-mix(in srgb, var(--nxt-chrome-inset-shadow)
              calc(var(--nxt-input-inset-inner-opacity) * 100%), transparent);
}

/* The WATERMARK. Left-aligned at the caret, upright, in the secondary text colour, and gone the moment
   the field has focus or content (FR-4, ruled 2026-08-29). All three are the browser's own placeholder
   defaults, which is why this rule sets only the colour: the reference centres it, italicises it and
   keeps it at 30% under a focused caret, and this port refuses all three. WPF needs an attached
   property and a template trigger to reach the same behaviour — see Controls/Watermark.cs. */
input::placeholder,
textarea::placeholder {
    color: var(--nxt-text-secondary);
    font-style: normal;
    opacity: 1;   /* Firefox dims a placeholder by default; the colour token decides, not the browser. */
}

input[type="text"]:focus,
input[type="search"]:focus,
input[type="number"]:focus,
input[type="email"]:focus,
input[type="password"]:focus,
textarea:focus,
select:focus {
    outline: none;
    border-color: var(--nxt-accent-primary);
    box-shadow: var(--nxt-shadow-focus-ring);
}

/* A refused value takes the SEMANTIC error colour, not a chrome red of its own: an invalid input is a
   failure that already happened, which is what error means. (The reference draws #CC0000 here; taking
   it would have put a ninth red in a closed vocabulary.) */
input:invalid,
input[aria-invalid="true"],
textarea[aria-invalid="true"],
select[aria-invalid="true"] {
    border-color: var(--nxt-semantic-error);
    box-shadow: var(--nxt-shadow-error-ring);
}

/* Disabled changes the BORDER only — the fill and the text are left alone so the value stays readable.
   That is the whole reason this is four stated colours and not one opacity. */
input:disabled,
textarea:disabled,
select:disabled {
    border-color: var(--nxt-border-secondary);
}

/* ══ Select (the ComboBox twin) ═══════════════════════════════════════════════════════════════════
   Border, height and type — the three things FR-1 asks for. The drop-down itself stays the browser's,
   exactly as the WPF half keeps the stock ComboBox template: re-templating one means owning its popup
   and its item container, which is a control rewrite for the sake of an arrow. */
select {
    box-sizing: border-box;
    min-height: var(--nxt-control-height);
    padding: 3px 4px;
    color: var(--nxt-text-primary);
    background: var(--nxt-bg-panel);
    border: var(--nxt-border-thickness) solid var(--nxt-border-control);
    border-radius: var(--nxt-corner-radius);
}

select:disabled {
    color: var(--nxt-text-disabled);
}

/* ══ Check box and radio ══════════════════════════════════════════════════════════════════════════
   18px, white fill, an ink stroke. `appearance: none` is what lets the two hosts draw the same box —
   the native control takes its colours from the operating system and cannot be told otherwise. */
input[type="checkbox"],
input[type="radio"] {
    appearance: none;
    -webkit-appearance: none;
    box-sizing: border-box;
    width: var(--nxt-checkbox-size);
    height: var(--nxt-checkbox-size);
    margin: 0;
    display: inline-grid;
    place-content: center;
    background: var(--nxt-bg-panel);
    border: var(--nxt-border-thickness) solid var(--nxt-chrome-ink);
    cursor: pointer;
}

input[type="checkbox"] { border-radius: var(--nxt-corner-radius-list); }
input[type="radio"]    { border-radius: 50%; }

input[type="checkbox"]::before {
    content: "";
    width: 10px;
    height: 6px;
    border-left: 2px solid var(--nxt-chrome-ink);
    border-bottom: 2px solid var(--nxt-chrome-ink);
    transform: rotate(-45deg) translate(1px, -1px);
    visibility: hidden;
}

/* The radio's inner dot takes the shell gradient, which is what the reference draws — a filled dot,
   not a flat disc. */
input[type="radio"]::before {
    content: "";
    width: var(--nxt-radio-dot-size);
    height: var(--nxt-radio-dot-size);
    border-radius: 50%;
    background: linear-gradient(to bottom,
                var(--nxt-chrome-shell-start), var(--nxt-chrome-shell-end));
    visibility: hidden;
}

input[type="checkbox"]:checked::before,
input[type="radio"]:checked::before { visibility: visible; }

input[type="checkbox"]:focus-visible,
input[type="radio"]:focus-visible {
    outline: none;
    border-color: var(--nxt-accent-primary);
    box-shadow: var(--nxt-shadow-focus-ring);
}

input[type="checkbox"]:disabled,
input[type="radio"]:disabled {
    background: var(--nxt-bg-secondary);
    border-color: var(--nxt-border-secondary);
    cursor: default;
}

input[type="checkbox"]:disabled::before {
    border-left-color: var(--nxt-icon-disabled);
    border-bottom-color: var(--nxt-icon-disabled);
}

/* ══ Buttons ══════════════════════════════════════════════════════════════════════════════════════
   A bare <button> is the TOOLBAR button — transparent, no border, radius 3 — because that is what the
   WPF implicit Button style is, and re-pointing either at the face-gradient default would restyle three
   applications in one line. The face-gradient button is `.nxt-btn`, opted into. */
button {
    padding: 4px 6px;
    color: var(--nxt-text-secondary);
    background: transparent;
    border: 0;
    border-radius: var(--nxt-corner-radius-toolbar);
    font-size: var(--nxt-font-size-small);
    cursor: pointer;
}

button:hover:not(:disabled)  { background: var(--nxt-btn-hover); }
button:active:not(:disabled) { background: var(--nxt-btn-pressed); }

button:disabled {
    color: var(--nxt-text-disabled);
    cursor: default;
}

/* The default button: the control face, a 1px control outline, radius 4, height 26, the UI face at 14. Pressed
   takes the accent face with white text; disabled takes the panel grey, the secondary border and the
   disabled text colour. */
.nxt-btn {
    box-sizing: border-box;
    min-height: var(--nxt-control-height);
    min-width: var(--nxt-text-button-min-width);
    padding: 3px 10px;
    color: var(--nxt-text-primary);
    background: linear-gradient(to bottom,
                var(--nxt-bg-panel) 0%,
                var(--nxt-bg-secondary) calc(var(--nxt-chrome-gradient-mid-offset) * 100%),
                var(--nxt-bg-panel) 100%);
    border: var(--nxt-border-thickness) solid var(--nxt-border-control);
    border-radius: var(--nxt-corner-radius);
    font-size: var(--nxt-font-size);
}

.nxt-btn:active:not(:disabled),
.nxt-btn--dark:active:not(:disabled) {
    color: var(--nxt-text-on-accent);
    background: linear-gradient(to bottom,
                var(--nxt-chrome-accent-face-start) 0%,
                var(--nxt-btn-primary-end) calc(var(--nxt-chrome-gradient-mid-offset) * 100%),
                var(--nxt-chrome-accent-face-end) 100%);
}

.nxt-btn--dark {
    box-sizing: border-box;
    min-height: var(--nxt-control-height);
    min-width: var(--nxt-text-button-min-width);
    padding: 3px 10px;
    color: var(--nxt-text-on-accent);
    background: linear-gradient(to bottom,
                var(--nxt-chrome-dark-action-start) 0%,
                var(--nxt-chrome-dark-action-mid) calc(var(--nxt-chrome-gradient-mid-offset) * 100%),
                var(--nxt-chrome-dark-action-end) 100%);
    border: var(--nxt-border-thickness) solid var(--nxt-border-control);
    border-radius: var(--nxt-corner-radius);
    font-size: var(--nxt-font-size);
}

.nxt-btn--primary {
    box-sizing: border-box;
    min-height: var(--nxt-control-height);
    padding: 5px 14px;
    color: var(--nxt-text-on-accent);
    background: linear-gradient(to bottom,
                var(--nxt-btn-primary-start) 0%,
                var(--nxt-btn-primary-mid) 50%,
                var(--nxt-btn-primary-end) 100%);
    border: var(--nxt-border-thickness) solid var(--nxt-btn-primary-border);
    border-radius: var(--nxt-corner-radius-toolbar);
    font-size: var(--nxt-font-size-small);
}

.nxt-btn--primary:hover:not(:disabled)  { opacity: 0.88; }
.nxt-btn--primary:active:not(:disabled) { opacity: 0.75; }

.nxt-btn:disabled,
.nxt-btn--dark:disabled,
.nxt-btn--primary:disabled {
    color: var(--nxt-text-disabled);
    background: var(--nxt-bg-secondary);
    border-color: var(--nxt-border-secondary);
    opacity: 1;
}

/* ══ List box and its rows ════════════════════════════════════════════════════════════════════════
   A row is 26 high with a 12px inset, the UI face at 12 in the chrome ink, and a 2px rule underneath. There is
   NO alternating fill: the rule is what separates rows, and a zebra stripe on top of it is two
   mechanisms saying one thing. */
.nxt-list {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
    list-style: none;
    background: var(--nxt-bg-panel);
    border: var(--nxt-border-thickness) solid var(--nxt-border-list);
    border-radius: var(--nxt-corner-radius-list);
    color: var(--nxt-chrome-ink);
    font-family: var(--nxt-font-ui);
    font-size: var(--nxt-font-size-small);
}

/* The focus affordance is on the WHOLE box, not on a row: it answers "where is the keyboard", and the
   selected row already answers "which one". */
.nxt-list:focus-within {
    outline: none;
    box-shadow: var(--nxt-shadow-focus-ring);
}

.nxt-list__row {
    box-sizing: border-box;
    display: flex;
    align-items: center;
    min-height: var(--nxt-control-height);
    padding: 0 var(--nxt-list-row-inset);
    border-style: solid;
    border-color: var(--nxt-border-row);
    border-width: var(--nxt-list-row-rule-thickness);
    cursor: pointer;
}

.nxt-list__row:hover {
    color: var(--nxt-text-primary);
    background: linear-gradient(to bottom,
                var(--nxt-chrome-accent-face-start) 0%,
                var(--nxt-btn-primary-end) calc(var(--nxt-chrome-gradient-mid-offset) * 100%),
                var(--nxt-chrome-accent-face-end) 100%);
    border-color: var(--nxt-accent-primary);
}

/* Selected is a FLAT fill. A selected row is a state; a gradient would make it read as a raised
   control. */
.nxt-list__row--selected {
    color: var(--nxt-text-primary);
    background: var(--nxt-bg-selected);
}

/* ══ Tabs (#2954) ═════════════════════════════════════════════════════════════════════════════════
   AssemblyLab's tab, the twin of the WPF TabControl / TabItem styles.

   .nxt-tab is the FACE, and it stands on its own: a button carrying it gets the face rounded at its
   top-left, the left and top edges, the rule along the bottom and the selected state, with no other
   markup. That keeps markup written against the class before #2954 working. The curved right edge
   needs the reference's curve, which CSS cannot draw, so it is the .nxt-tab__slope child that
   Controls/TabControl.razor renders; inside its strip each tab leaves room for its slope less the
   overlap, and the next tab covers the rest.

   Selected: the flat active face and no bottom rule. Otherwise: the face gradient and the rule. Tabs stack
   in document order, so the next tab covers the foot of the selected tab's slope, as in the reference;
   nothing raises the selected tab (Bas, 2026-10-01, #2957).
   The bare button's hover and pressed faces are undone; the reference has no hover state. */
.nxt-tab,
.nxt-tab:hover:not(:disabled),
.nxt-tab:active:not(:disabled),
.nxt-tab:disabled {
    background: linear-gradient(to bottom, var(--nxt-bg-panel), var(--nxt-chrome-tab-inactive-end));
}

.nxt-tab {
    position: relative;
    display: inline-flex;
    align-items: center;
    box-sizing: border-box;
    height: var(--nxt-tab-height);
    padding: 0 var(--nxt-tab-header-inset) 0 calc(var(--nxt-tab-lead-width) + var(--nxt-tab-header-inset));
    color: var(--nxt-text-primary);
    white-space: nowrap;
    border-style: solid;
    border-color: var(--nxt-border-control);
    border-width: var(--nxt-border-thickness) 0 0 var(--nxt-border-thickness);
    border-radius: var(--nxt-corner-radius-tab);
    font-family: var(--nxt-font-ui);
    font-size: var(--nxt-font-size-small);
    cursor: pointer;
}

/* The rule along the bottom, from the outer edge of the left border. */
.nxt-tab::after {
    content: "";
    position: absolute;
    left: calc(-1 * var(--nxt-border-thickness));
    right: 0;
    bottom: 0;
    border-bottom: var(--nxt-border-thickness) solid var(--nxt-border-control);
}

.nxt-tab--selected,
.nxt-tab--selected:hover:not(:disabled),
.nxt-tab--selected:active:not(:disabled) {
    background: var(--nxt-bg-tab-active);
}

.nxt-tab--selected::after { content: none; }
.nxt-tab--selected .nxt-tab__slope-fill path { fill: var(--nxt-bg-tab-active); }

.nxt-tab:disabled {
    color: var(--nxt-text-disabled);
    cursor: default;
}

.nxt-tab:disabled::after { content: ""; }

/* The curved right edge: outside the face, the tab's full height from the outer top edge. */
.nxt-tab__slope {
    position: absolute;
    top: calc(-1 * var(--nxt-border-thickness));
    left: 100%;
    width: var(--nxt-tab-slope-width);
    height: var(--nxt-tab-height);
}

.nxt-tab__slope svg {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    overflow: visible;
}

.nxt-tab__face-start { stop-color: var(--nxt-bg-panel); }
.nxt-tab__face-end   { stop-color: var(--nxt-chrome-tab-inactive-end); }

.nxt-tab__slope-outline path {
    fill: none;
    stroke: var(--nxt-border-control);
    stroke-width: var(--nxt-tab-slope-stroke);
    vector-effect: non-scaling-stroke;
}

/* The strip: one row, in front of the panel, which it overlaps by the panel's border. */
.nxt-tabs {
    display: flex;
    flex-direction: column;
    min-width: 0;
    min-height: 0;
}

.nxt-tabs__strip {
    position: relative;
    z-index: 1;
    display: flex;
    align-items: flex-end;
}

.nxt-tabs__strip > .nxt-tab {
    margin-right: calc(var(--nxt-tab-slope-width) - var(--nxt-tab-overlap));
}

.nxt-tabs__strip > .nxt-tab::after {
    right: calc(-1 * var(--nxt-tab-slope-width));
}

/* A column, so content that fills its view (a log, a scrolling list) can fill the panel: give the root a
   class that lets it grow, and the content flex: 1 (#2957). */
.nxt-tabs__panel {
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    margin-top: calc(-1 * var(--nxt-border-thickness));
    padding: var(--nxt-tab-content-inset);
    background: var(--nxt-bg-panel);
    border: var(--nxt-border-thickness) solid var(--nxt-border-control);
    border-radius: var(--nxt-corner-radius-tab-panel);
}

/* ══ Panel / group ════════════════════════════════════════════════════════════════════════════════
   The reference's group box without its dropped header: the secondary fill, a hairline, square
   corners, and the 10px content inset. */
.nxt-panel {
    background: var(--nxt-bg-secondary);
    border: var(--nxt-border-thickness) solid var(--nxt-border-primary);
    border-radius: 0;
    padding: var(--nxt-content-inset);
}

/* ══ Toolbar chip ═════════════════════════════════════════════════════════════════════════════════
   The left-aligned white strip the reference runs a toolbar along: radius "5 0 5 0" and no bottom
   border, so it opens downward into whatever it sits above. */
.nxt-chip {
    display: inline-block;
    padding: 3px 6px;
    background: var(--nxt-bg-panel);
    border: var(--nxt-border-thickness) solid var(--nxt-border-control);
    border-bottom: 0;
    border-radius: var(--nxt-corner-radius-chip);
}

/* ══ Progress bar ═════════════════════════════════════════════════════════════════════════════════
   Determinate is <progress value max>; indeterminate is <progress> with no value, which the browser
   animates on its own — the twin of WPF keeping the stock ProgressBar template and restyling it. */
progress {
    appearance: none;
    -webkit-appearance: none;
    width: 100%;
    height: 6px;
    background: var(--nxt-bg-deep);
    border: var(--nxt-border-thickness) solid var(--nxt-border-control);
}

progress::-webkit-progress-bar   { background: var(--nxt-bg-deep); }
progress::-webkit-progress-value { background: var(--nxt-accent-primary); }
progress::-moz-progress-bar      { background: var(--nxt-accent-primary); }
