/* =====================================================================
   NaviERP - SO (Sales Order) module styles
   ---------------------------------------------------------------------
   Generated from tender.css with the `tender-` prefix rewritten to `so-`,
   so the two modules stay visually identical without either one's rules
   leaking into the other. Keep them in sync by hand when tender.css
   changes structurally; the SO-only additions live at the bottom of this
   file, below the marker, and are not present in tender.css.

   Reminder from the Tender work: FluentDataGrid renders a plain <table>,
   so any rule written against `fluent-data-grid-*` here is dead code.
   Style the cell wrapper (`.so-cell-wrap`) instead.
   ===================================================================== */

/* =====================================================================
   Tender module - shared form / list styling
   Loaded globally from App.razor (dialog content renders outside the
   owning component's scope, so scoped .razor.css would not reach it).
   Every selector is namespaced under .so-* to keep it off the rest
   of the application.
   ===================================================================== */

.so-form,
.so-list-page {
    --so-accent: #2564cf;
    --so-accent-soft: #eef5ff;
    --so-border: #e1e6ec;
    --so-surface-subtle: #f7f9fc;
    --so-text: #1f2937;
    --so-muted: #667085;
}

.so-form {
    display: flex;
    flex-direction: column;
    gap: 14px;
    padding: 4px 2px 8px 2px;

    /* The dialog body gives the form a fixed box. Without min-width:0 the
       Fluent web components report their intrinsic width (~280px each) as the
       minimum content size and the whole form grows past the dialog. */
    box-sizing: border-box;
    width: 100%;
    max-width: 100%;
    min-width: 0;
}

/* Same reason, one level down: a grid/flex child defaults to min-width:auto. */
.so-form *,
.so-form *::before,
.so-form *::after {
    box-sizing: border-box;
}

.so-section,
.so-grid > *,
.so-alert-row > * {
    min-width: 0;
}

/* -------------------------------------------------------------------------
   FIELD WRAPPER  (required, not cosmetic)

   This build of Microsoft.FluentUI renders a component's Label as a SIBLING
   <label> element rather than nesting it inside the web component:

       <label for="x">Tender Qty</label>
       <fluent-number-field id="x">

   Dropped straight into a CSS grid that is two grid items, so the label lands
   in one column and its input in the next - which is exactly how the columns
   appeared to "go outside" the form. Every labelled Fluent input in the Tender
   forms is wrapped in .so-field so the pair counts as one grid item.
   ------------------------------------------------------------------------- */
.so-field {
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    gap: 2px;
    min-width: 0;
    max-width: 100%;
}

.so-field > label {
    font-size: 12px;
    font-weight: 500;
    line-height: 1.3;
    color: var(--neutral-foreground-rest, #242424);
}

/* A wrapper that also spans columns must not collapse its child. */
.so-span-2 > .so-field,
.so-span-full > .so-field {
    height: 100%;
}

/* -------------------------------------------------------------------------
   DIALOG SHELL

   The Tender forms are taller than any sensible dialog, so the body has to be
   the scroll region - header and footer stay pinned. Without this the content
   simply painted past the bottom edge of the dialog panel.
   ------------------------------------------------------------------------- */
fluent-dialog .so-form {
    max-width: 100%;
}

/* FluentUI already lays the dialog out as header / body / footer grid rows once
   a FluentDialogBody is present - do NOT override ::part(control) or that
   breaks. What it leaves out is containment: the body is height:auto with
   min-height:80px and no overflow, and the body sits in a `1fr` track whose
   automatic minimum is min-content. Tall content therefore grows the track and
   paints straight past the fixed-height dialog panel.

   min-height:0 lets the track shrink to the panel; overflow-y makes the body
   the scroll region so header and footer stay pinned. */
fluent-dialog .fluent-dialog-body {
    overflow-y: auto;
}

fluent-dialog:has(.so-form) .fluent-dialog-body {
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
    padding-right: 6px;
}

.so-dirty-dot {
    color: #d13438;
    margin-left: 6px;
    font-size: 12px;
}

/* ---------- error banner ---------- */

.so-form-error {
    background: #fdf3f4;
    border: 1px solid #f1bcc0;
    border-left: 4px solid #d13438;
    color: #a4262c;
    border-radius: 6px;
    padding: 8px 12px;
    font-size: 13px;
}

/* ---------- ERP record summary ---------- */

.so-form-summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 12px 14px;
    border: 1px solid #cfe0f5;
    border-left: 4px solid var(--so-accent);
    border-radius: 8px;
    background: linear-gradient(90deg, var(--so-accent-soft), #ffffff 72%);
}

.so-form-summary-main {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.so-form-summary-main strong {
    color: var(--so-text);
    font-size: 15px;
    line-height: 1.35;
    overflow-wrap: anywhere;
}

.so-form-summary-main > span:last-child {
    color: var(--so-muted);
    font-size: 12px;
}

.so-form-eyebrow {
    color: var(--so-accent);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
}

.so-form-summary-badges {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    flex-wrap: wrap;
    gap: 6px;
}

.so-summary-chip {
    display: inline-block;
    max-width: 220px;
    padding: 2px 9px;
    overflow: hidden;
    color: #344054;
    background: #ffffff;
    border: 1px solid #d0d5dd;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 600;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ---------- sections ---------- */

.so-section {
    border: 1px solid var(--so-border);
    border-radius: 8px;
    padding: 12px 14px 14px 14px;
    background: #fff;
    box-shadow: 0 1px 2px rgba(16, 24, 40, .03);
}

.so-status-section {
    background: var(--so-surface-subtle);
}

.so-section-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
}

.so-section-title {
    font-size: 12px;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: #4b5563;
    margin-bottom: 10px;
    display: flex;
    align-items: center;
    gap: 8px;
}

.so-section-head .so-section-title {
    margin-bottom: 0;
}

.so-section-actions {
    display: flex;
    gap: 4px;
    margin-bottom: 6px;
}

.so-count-pill {
    background: #eef2ff;
    color: #3730a3;
    border-radius: 999px;
    padding: 1px 8px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0;
    text-transform: none;
}

/* ---------- grids ---------- */

.so-grid {
    display: grid;
    gap: 12px 14px;
    align-items: end;
}

/* Stretch so a two-line label in one column does not misalign its neighbours. */
.so-grid > .so-field,
.so-grid > .so-span-2,
.so-grid > .so-span-full {
    align-self: stretch;
}

.so-grid-2 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
}

.so-grid-3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
}

.so-grid-4 {
    grid-template-columns: repeat(4, minmax(0, 1fr));
}

.so-span-2 {
    grid-column: span 2;
}

.so-span-full {
    grid-column: 1 / -1;
}

/* Fluent inputs default to a fixed intrinsic width; force them to fill their
   cell so the grid columns line up and nothing overflows the dialog. */
.so-form fluent-text-field,
.so-form fluent-number-field,
.so-form fluent-select,
.so-form fluent-text-area,
.so-form fluent-search,
.so-form .fluent-input-wrapper,
.so-form .fluent-popup-root {
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
}

.so-form fluent-text-field::part(root),
.so-form fluent-number-field::part(root),
.so-form fluent-select::part(control),
.so-form fluent-text-area::part(control) {
    width: 100%;
    min-width: 0;
}

/* Tab panels are grid/flex children too. */
.so-form fluent-tabs,
.so-form fluent-tab-panel {
    width: 100%;
    min-width: 0;
    max-width: 100%;
}

.so-form fluent-tab-panel {
    overflow-x: hidden;
    padding-top: 12px;
}

.so-form fluent-tab-panel > .so-section + .so-section {
    margin-top: 12px;
}

/* Long tab strips wrap instead of stretching the dialog. */
.so-form fluent-tabs::part(tablist) {
    flex-wrap: wrap;
}

/* The dialog is the containing block, so these breakpoints are driven by the
   dialog width via the min(px, vw) sizing the forms request. */
@media (max-width: 1100px) {
    .so-grid-4 {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}

@media (max-width: 900px) {
    .so-grid-3,
    .so-grid-4 {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    .so-span-full {
        grid-column: 1 / -1;
    }
}

@media (max-width: 620px) {
    .so-grid-2,
    .so-grid-3,
    .so-grid-4 {
        grid-template-columns: minmax(0, 1fr);
    }

    .so-span-2 {
        grid-column: span 1;
    }

    .so-form-summary {
        align-items: flex-start;
        flex-direction: column;
    }

    .so-form-summary-badges {
        justify-content: flex-start;
    }
}

/* ---------- documents for dispatch ---------- */

/* GenericPopup's dropdown sizes itself to its widest column, which can reach
   past the right edge of the dialog. Cap it and let the table scroll. */
.so-form .fluent-popup {
    max-width: min(560px, 88vw);
}

.so-form .fluent-table-wrapper {
    overflow: auto;
    max-width: 100%;
}

.so-audit {
    margin-top: 8px;
    font-size: 11px;
    color: #9ca3af;
}

.so-doc-extra {
    margin-top: 10px;
    padding: 7px 10px;
    color: #475467;
    background: var(--so-surface-subtle);
    border-left: 3px solid #98a2b3;
    border-radius: 4px;
    font-size: 12px;
}

/* ---------- penalty table ---------- */

.so-penalty-table {
    width: 100%;

    /* Fixed layout stops the number fields from dictating column widths;
       min-width keeps the row readable and lets the wrapper scroll instead
       of the dialog growing. */
    table-layout: fixed;
    min-width: 660px;

    border-collapse: separate;
    border-spacing: 0;
    font-size: 12px;
    border: 1px solid #e5e7eb;
    border-radius: 8px;
    overflow: hidden;
}

.so-penalty-table th:first-child,
.so-penalty-table td:first-child {
    width: 170px;
}

.so-penalty-table td fluent-number-field {
    width: 100% !important;
    min-width: 0 !important;
}

.so-penalty-table th {
    text-align: left;
    background: #f3f4f6;
    color: #4b5563;
    font-weight: 600;
    padding: 7px 10px;
    white-space: nowrap;
}

.so-penalty-table td {
    padding: 5px 10px;
    border-top: 1px solid #f1f2f4;
    vertical-align: middle;
}

.so-penalty-table tbody tr:nth-child(even) {
    background: #fbfbfc;
}

.so-penalty-label {
    font-weight: 600;
    color: #374151;
    white-space: nowrap;
}

.so-penalty-nil {
    color: #9ca3af;
    font-style: italic;
}

.so-table-scroll {
    overflow-x: auto;
    max-width: 100%;
    min-width: 0;
}

/* ---------- alert day swatches ---------- */

.so-alert-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 10px;
}

.so-alert-cell {
    border-radius: 6px;
    padding: 8px 10px;
    border: 1px solid transparent;
}

.so-alert-green  { background: #dcfce7; border-color: #86efac; }
.so-alert-yellow { background: #fef9c3; border-color: #fde047; }
.so-alert-orange { background: #ffedd5; border-color: #fdba74; }
.so-alert-blue   { background: #dbeafe; border-color: #93c5fd; }
.so-alert-red    { background: #fee2e2; border-color: #fca5a5; }

.so-alert-caption {
    font-size: 11px;
    font-weight: 600;
    color: #374151;
    margin-bottom: 4px;
}

/* ---------- list page header ---------- */

.so-context-bar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px 16px;
    padding: 10px 16px;
    margin-bottom: 10px;
    background: #fff;
    border: 1px solid #e5e7eb;
    border-left: 4px solid var(--so-accent);
    border-radius: 8px;
}

.so-list-page {
    min-width: 0;
}

.so-list-page .erp-master-page {
    min-width: 0;
}

.so-context-name {
    font-size: 15px;
    font-weight: 600;
    color: #111827;
}

.so-context-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 14px;
    font-size: 12px;
    color: #6b7280;
}

.so-context-meta strong {
    color: #374151;
}

.so-context-spacer {
    margin-left: auto;
}

/* -------------------------------------------------------------------------
   GRID ROWS THAT CARRY A MERGED CELL

   IMPORTANT - what the grid actually renders.
   Microsoft.FluentUI 4.x does NOT render <fluent-data-grid> web components.
   FluentDataGrid emits a real  <table class="fluent-data-grid grid">  whose
   thead / tbody / tr are display:contents, so every <td> is a grid item of
   the table itself. Any selector written against fluent-data-grid-row /
   fluent-data-grid-cell (there are some left in app.css and
   MasterListBase.razor.css) matches NOTHING and is dead code - that is why
   the earlier row-height override never took effect.

   The package styles every cell as
       td { padding: 6px 16px; overflow: hidden; text-overflow: ellipsis;
            white-space: nowrap; align-content: center; }
   so a two-line cell is sliced in half: the primary line shows, the second
   one is cut by the row divider.

   Fix: match the real <td>, let it wrap, pin the content to the top of the
   cell and give the cell a min-height. min-height on a grid item feeds the
   row track sizing, so the row is guaranteed to have room for two lines
   instead of relying on the browser measuring wrapped text correctly.
   ------------------------------------------------------------------------- */

.so-cell-wrap {
    /* two text lines at 1.3 line-height */
    --so-line: 1.3em;
    --so-lines: 2;
}

.so-list-page td:has(.so-cell-wrap) {
    height: auto !important;
    min-height: 46px;
    white-space: normal !important;
    overflow: hidden;
    text-overflow: clip !important;
    align-content: start !important;
    align-items: start;
    line-height: 1.3;
    vertical-align: top;
}

/* -------------------------------------------------------------------------
   ONE CONTINUOUS ROW DIVIDER

   The package draws the divider as  td { border-bottom }  and the grid host
   is  align-items: center , so every cell is only as tall as its OWN content
   and each one paints its border at a different height: a two-line text cell
   ends 20px lower than the chip cell beside it, and the "row line" comes out
   as a staircase. It also means the hover highlight only covers part of the
   row.

   Stretching the cells to the row track fixes both. This is deliberately a
   plain selector rather than the :has() version below - :has() is a nice to
   have, this has to work everywhere, and stretch is invisible on the
   single-line rows every other module shows.
   ------------------------------------------------------------------------- */
.so-list-page table.fluent-data-grid,
.so-list-page table.fluent-data-grid > thead,
.so-list-page table.fluent-data-grid > tbody {
    align-items: stretch !important;
}

.so-list-page table.fluent-data-grid td,
.so-list-page table.fluent-data-grid th {
    align-self: stretch !important;
}

/* -------------------------------------------------------------------------
   THE SAME FIX, WITHOUT DEPENDING ON THE TABLE'S CLASS

   The two blocks above only work if the grid host really carries
   class="fluent-data-grid". Measuring the live page showed it does not take
   effect: every cell WITHOUT a .so-cell-wrap (select checkbox, Lines,
   Status, Open, Actions) stayed exactly 40px tall inside an 84px row, so each
   painted its border-bottom 44px above the text cells - the staircase divider,
   and the "Status / Open / Actions are out of line" report.

   align-self on the ITEM beats align-items on the container, so the row-scoped
   rule below needs nothing from the table at all: it matches the <tr> that
   carries a merged cell and stretches every one of that row's cells to the
   row track. height:auto is restated because a grid item with a definite
   height cannot stretch - it silently falls back to start.

   Scoped to :has(.so-cell-wrap) on the ROW, so single-line grids in other
   modules keep the package's centred look.
   ------------------------------------------------------------------------- */
.so-list-page tr:has(.so-cell-wrap) > td,
.so-list-page tr:has(.so-cell-wrap) > th {
    align-self: stretch !important;
    height: auto !important;
    align-content: start !important;
}

/* The action icons are 23px tall against a ~16px first text line, so a flush
   top would read as 4px low. Centre them inside one line box instead. */
.so-list-page tr:has(.so-cell-wrap) > td .erp-row-actions {
    min-height: 1.3em;
    align-items: center;
}

/* The checkbox column is narrow and the package pads every cell 16px on each
   side, so a 42px track left ~10px for a 20px checkbox: it overflowed and
   text-overflow painted an ellipsis next to every checkbox. Give the first
   column its own padding instead of widening the track. */
.so-list-page table.fluent-data-grid td:first-child,
.so-list-page table.fluent-data-grid th:first-child {
    padding-left: 10px !important;
    padding-right: 4px !important;
    overflow: visible;
    text-overflow: clip;
}

/* Content stays vertically centred in the now full-height cell (that is the
   package default) - except a multi-line cell, which reads better from the
   top. .so-cell-wrap fills its cell so the two behave the same way. */
.so-cell-wrap {
    height: 100%;
    justify-content: flex-start;
}

/* Never let a cell be cut mid-line: clamp it to a whole number of lines and
   ellipsise the rest. The full value stays available through the cell's
   title attribute. */
.so-cell-strong,
.so-cell-wrap > span {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    overflow-wrap: anywhere;
    max-width: 100%;
}

.so-cell-sub,
.so-cell-wrap > span.so-cell-sub {
    -webkit-line-clamp: 1;
    line-clamp: 1;
}

/* A chip is not text - clamping would crop its rounded background. */
.so-cell-wrap > span.so-chip {
    display: inline-block;
    overflow: visible;
}

/* ---------- grid cell chips ---------- */

.so-chip {
    display: inline-block;
    border-radius: 999px;
    padding: 1px 9px;
    font-size: 11px;
    font-weight: 600;
    white-space: nowrap;
}

.so-chip-active   { background: #dcfce7; color: #166534; }
.so-chip-inactive { background: #fee2e2; color: #991b1b; }
.so-chip-count    { background: #eef2ff; color: #3730a3; }
.so-chip-zero     { background: #f3f4f6; color: #6b7280; }
.so-chip-expired  { background: #fee2e2; color: #991b1b; }
.so-chip-soon     { background: #ffedd5; color: #9a3412; }
.so-chip-ok       { background: #f3f4f6; color: #374151; }

.so-cell-strong {
    font-weight: 600;
    color: #111827;
}

.so-cell-sub {
    /* display is set by the clamp rule above - do not reset it to block here,
       that is what re-enabled the mid-line slicing. */
    font-size: 11px;
    color: #6b7280;
}

/* A merged cell holds a primary line and an optional secondary line. Flex
   column keeps them stacked while a chip child still sizes to its own text
   (a block-level chip would stretch to the full column width). */
.so-cell-wrap {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    width: 100%;
    max-width: 100%;
    min-width: 0;
    white-space: normal;
    line-height: 1.3;
    padding: 2px 0;
}

/* Helpers return "" when there is nothing to say - do not reserve a line. */
.so-cell-sub:empty,
.so-cell-wrap > span:empty {
    display: none;
}

.so-num {
    font-variant-numeric: tabular-nums;
}

.so-cell-wrap > .so-num {
    align-self: stretch;
}

/* Chips inside a merged cell sit under their line, not beside it. */
.so-cell-wrap > .so-chip {
    margin-top: 1px;
}


/* -------------------------------------------------------------------------
   RICH TEXT EDITOR  (RichTextEditor.razor + js/richTextEditor.js)

   Quill's snow theme ships its own chrome from the CDN stylesheet; these
   rules only make it sit in the Tender dialog like the Fluent inputs around
   it - matching border, radius and label, and a fixed typing height so the
   dialog body stays the single scroll region instead of growing by 400px the
   moment two editors appear.

   --navi-rte-height comes from the component's Height parameter.
   ------------------------------------------------------------------------- */
.so-form .navi-rte {
    min-width: 0;
    max-width: 100%;

    /* .so-field bottom-aligns its content so single-line inputs line up
       across a grid row; an editor is a block, so start from the top. */
    justify-content: flex-start;
}

.so-form .navi-rte > label {
    font-size: 12px;
    font-weight: 500;
    line-height: 1.3;
    color: var(--neutral-foreground-rest, #242424);
}

.so-form .navi-rte-shell {
    min-width: 0;
    max-width: 100%;
}

.so-form .navi-rte .ql-toolbar.ql-snow {
    border: 1px solid #d1d5db;
    border-bottom: 0;
    border-radius: 6px 6px 0 0;
    background: #f8f9fa;
    padding: 4px 6px;

    /* The pickers (font, size, colour) drop out of the toolbar absolutely;
       without a stacking context of their own they land behind the editor. */
    position: relative;
    z-index: 3;
}

.so-form .navi-rte .ql-container.ql-snow {
    /* Quill's core CSS sets .ql-container{height:100%}. The shell is a
       stretched grid item, so 100% resolves to the WHOLE cell and the toolbar
       then pushes the editor 57px past it. Size to .ql-editor instead. */
    height: auto;

    border: 1px solid #d1d5db;
    border-radius: 0 0 6px 6px;
    background: #fff;

    /* Inherit the form's font rather than Quill's Helvetica stack. */
    font-family: inherit;
    font-size: 13px;
}

.so-form .navi-rte .ql-editor {
    height: var(--navi-rte-height, 190px);
    overflow-y: auto;
    padding: 8px 10px;
}

.so-form .navi-rte .ql-editor.ql-blank::before {
    font-style: normal;
    color: #9ca3af;
}

/* View mode: no toolbar, and the body reads as a disabled field. */
.so-form .navi-rte-readonly .ql-toolbar.ql-snow {
    display: none;
}

.so-form .navi-rte-readonly .ql-container.ql-snow {
    border-radius: 6px;
    background: #f3f4f6;
}


/* =====================================================================
   SO-ONLY ADDITIONS - no equivalent in tender.css
   ===================================================================== */

/* The calculation strip on the SO Item form: goods amount, tax, excise
   and the running total, mirroring the blue block on the legacy screen. */
.so-calc {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 4px 16px;
    padding: 10px 14px;
    border: 1px solid var(--neutral-stroke-rest, #d6d6d6);
    border-radius: 6px;
    background: rgba(0, 90, 158, 0.04);
}

.so-calc-row {
    display: contents;
}

.so-calc-label {
    font-size: 12px;
    color: #4a4a4a;
}

.so-calc-value {
    font-size: 13px;
    font-variant-numeric: tabular-nums;
    text-align: right;
}

.so-calc-total .so-calc-label,
.so-calc-total .so-calc-value {
    font-weight: 600;
    font-size: 14px;
    padding-top: 6px;
    border-top: 1px solid var(--neutral-stroke-rest, #d6d6d6);
}

/* The red helper text under Quantity In Nos. on the legacy form:
   "61 Box" and "Six Thousand One Hundred". */
.so-qty-hint {
    display: block;
    margin-top: 2px;
    font-size: 11px;
    line-height: 1.35;
    color: #b91c1c;
}

/* Hold block - visually separated because a held line does not ship. */
.so-hold {
    border-left: 3px solid #d97706;
    padding-left: 10px;
}

.so-chip-hold {
    background: #fef3c7;
    color: #92400e;
    border-color: #fcd34d;
}


/* =====================================================================
   SO DESIGN SYSTEM  -  added 2026-09-02
   ---------------------------------------------------------------------
   Everything below is a *presentation* layer for the SO module: one set
   of tokens, one label style, one control height, one required marker.
   It is deliberately appended at the end of the LAST stylesheet the app
   loads (App.razor loads so.css after app.css / erp-theme.css /
   tender.css), so it wins on cascade order without needing !important
   except where the FluentUI package itself uses one.

   Nothing here changes markup semantics or behaviour - the razor files
   only gain wrapper classes that these rules hang off.
   ===================================================================== */

/* ---------------------------------------------------------------------
   1. TOKENS
   One height, one label size, one gap. Every SO form and both registers
   read from these, which is what makes the screens measurably identical
   rather than "about the same".
   --------------------------------------------------------------------- */
.so-form,
.so-list-page {
    --so-danger: #b3261e;

    /* Control geometry.

       Do NOT hard-code a pixel height here. FluentUI sizes every control
       as (base-height-multiplier + density) * design-unit, and the design
       system provider publishes those three as inheritable custom
       properties - so the same expression gives us EXACTLY the Fluent
       control height, whatever the theme or density is set to. The
       fallbacks only matter if the provider is missing.

       This is what GenericPopup, .so-stat-value and .so-check-row measure
       themselves against; the Fluent controls are left alone entirely. */
    --so-field-h: calc((var(--base-height-multiplier, 8) + var(--density, 0)) * var(--design-unit, 4) * 1px);
    --so-input-border: var(--neutral-stroke-input-rest, #c8c6c4);
    --so-input-border-hover: var(--neutral-stroke-input-hover, #8a8886);

    /* Type scale */
    --so-label-size: 12px;
    --so-label-weight: 500;
    /* Same value a Fluent control renders its own text at, so a read-only
       .so-stat-value reads as the same size as the fields around it. */
    --so-input-size: var(--type-ramp-base-font-size, 14px);
    --so-hint-size: 11px;

    /* Rhythm */
    --so-row-gap: 14px;
    --so-col-gap: 16px;
}

/* ---------------------------------------------------------------------
   2. ONE LABEL STYLE FOR EVERY CONTROL

   Three different things render a field label in these forms:
     - FluentUI, as a sibling <label> inside .so-field
     - GenericPopup, as its own <label class="fluent-label">
     - hand-written captions (radio groups, read-only stats)
   They arrived at three different sizes/weights, which is most of why
   the forms looked "not quite aligned". One rule now covers all three.
   --------------------------------------------------------------------- */
.so-form .so-field > label,
.so-form .fluent-label,
.so-form .so-label {
    display: block;
    margin: 0 0 3px 0;
    padding: 0;

    font-size: var(--so-label-size);
    font-weight: var(--so-label-weight);
    line-height: 1.35;
    color: #374151;

    /* One line, always. Every label in the SO forms is short enough to fit
       its column, and pinning the label to a single line is what makes the
       inputs across a grid row start at exactly the same y - which in turn
       lets a field carry a hint underneath without dragging its neighbours
       out of line. */
    min-height: calc(var(--so-label-size) * 1.35);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* With the label height fixed, fields align from the TOP of the stack.
   (The old bottom-alignment existed only to cope with wrapped labels, and
   it is what pushed any field carrying a hint out of line with its row.) */
.so-form .so-field,
.so-form .so-span-2 > .so-field,
.so-form .so-span-full > .so-field {
    justify-content: flex-start;
}

/* ---------------------------------------------------------------------
   3. THE REQUIRED MARKER

   A small red asterisk, always in the same place (immediately after the
   label text, never inside the control), always the same colour and
   size. Put `so-required` on the .so-field wrapper, or `so-req` on any
   hand-written caption; GenericPopup keeps using its own Required
   parameter and is re-coloured to match.
   --------------------------------------------------------------------- */
.so-form .so-required > label::after,
.so-form .so-required > div:first-child > label:first-of-type::after,
.so-form .so-req::after {
    content: "*";
    margin-left: 3px;
    color: var(--so-danger);
    font-weight: 700;
    font-size: var(--so-label-size);
    line-height: 1;
}

/* GenericPopup prints the glyph itself, so it only needs re-colouring to
   the same red - never a second asterisk from a ::after. */
.so-form .fluent-required,
.so-list-page .fluent-required {
    margin-left: 3px;
    color: var(--so-danger);
    font-weight: 700;
    font-size: var(--so-label-size);
    line-height: 1;
}

/* A legend under the summary bar, so the asterisk is explained once per
   form instead of being folk knowledge. */
.so-required-note {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: var(--so-hint-size);
    color: var(--so-muted);
}

.so-required-note::before {
    content: "*";
    color: var(--so-danger);
    font-weight: 700;
}

/* ---------------------------------------------------------------------
   4. ONE CONTROL HEIGHT AND ONE TEXT STYLE

   Fluent inputs, date pickers, selects and GenericPopup share one
   baseline, one border, one corner radius and one text style, so a grid
   row reads as a row instead of a set of boxes at four heights.
   --------------------------------------------------------------------- */
/* The Fluent controls are deliberately NOT restyled here.

   An earlier version of this block set a height on
   `fluent-text-field::part(control)` - but `control` is the inner
   <input>; the bordered box is `part(root)`, and its own `.control` rule
   is `height: calc(100% - 4px)`. Forcing the inner input taller than its
   root therefore INFLATED the whole field, which is exactly what made
   Order No. sit taller than the Party popup beside it. The package sizes
   these correctly on its own - conform the popup to them, not the other
   way round. */

/* GenericPopup's input is a plain <div><input> pair, not a web component,
   so it is matched directly rather than through ::part. Every value below
   is the FluentUI token the text field itself uses, so the two controls
   are the same height, the same border, the same corner radius and - the
   part that actually showed - the same typeface and text size. */
.so-form .fluent-input-wrapper {
    display: flex;
    align-items: center;
    box-sizing: border-box;
    height: var(--so-field-h);
    min-height: var(--so-field-h);

    /* Fluent's own field padding is calc(design-unit * 2px + 1px); the
       right side is widened to clear the chevron. */
    padding: 0 26px 0 calc(var(--design-unit, 4) * 2px + 1px);

    border: calc(var(--stroke-width, 1) * 1px) solid var(--so-input-border);
    border-radius: calc(var(--control-corner-radius, 4) * 1px);
    background: var(--neutral-fill-input-rest, #fff);
    transition: border-color .12s ease-in-out;
}

.so-form .fluent-input-wrapper:hover {
    border-color: var(--so-input-border-hover);
}

.so-form .fluent-input-wrapper:focus-within {
    border-color: var(--so-accent);
    box-shadow: inset 0 0 0 1px var(--so-accent);
}

/* GenericPopup's own <style> block sets `font-family: Segoe UI, sans-serif`
   on .fluent-popup-root and 13px on .fluent-input, while a Fluent text
   field renders at --body-font / --type-ramp-base-font-size (14px). Side
   by side in one row - Party next to Order No. on the master form - that
   reads as two different controls. Both now come from the same tokens. */
.so-form .fluent-popup-root {
    font-family: var(--body-font, "Segoe UI Variable", "Segoe UI", sans-serif);
}

.so-form .fluent-input {
    height: 100%;
    padding: 0;
    font-family: var(--body-font, "Segoe UI Variable", "Segoe UI", sans-serif);
    font-size: var(--type-ramp-base-font-size, 14px);
    line-height: var(--type-ramp-base-line-height, 20px);
    color: var(--neutral-foreground-rest, #1f2937);
    background: transparent;
}

.so-form .fluent-input::placeholder {
    color: var(--neutral-foreground-hint, #9aa1ab);
}

/* Disabled popups should read like disabled Fluent fields, not like
   editable ones that happen to ignore typing. */
.so-form .fluent-input-wrapper:has(.fluent-input:disabled) {
    background: #f5f5f5;
    border-color: #e5e7eb;
}

.so-form .fluent-input:disabled {
    color: #9aa1ab;
    cursor: not-allowed;
}

.so-form .fluent-input-wrapper:has(.fluent-input:disabled) .fluent-chevron {
    color: #b9bec6;
    cursor: not-allowed;
}

/* ---------------------------------------------------------------------
   5. RHYTHM

   One row gap and one column gap everywhere, and a rule under every
   section title so the visual hierarchy is section > field, not a wall
   of equally-weighted boxes.
   --------------------------------------------------------------------- */
.so-form .so-grid {
    gap: var(--so-row-gap) var(--so-col-gap);
}

.so-form .so-section {
    padding: 14px 16px 16px 16px;
}

.so-form .so-section-title {
    margin-bottom: 12px;
    padding-bottom: 8px;
    border-bottom: 1px solid #eef1f5;

    font-size: 11px;
    font-weight: 700;
    letter-spacing: .06em;
    color: #5b6675;
}

.so-form .so-section-title .so-section-note {
    margin-left: auto;
    font-size: var(--so-hint-size);
    font-weight: 500;
    letter-spacing: 0;
    text-transform: none;
    color: var(--so-muted);
}

/* The hold block keeps its amber spine but stops looking like it was
   indented by accident. */
.so-form .so-hold {
    border-left: 3px solid #d97706;
    padding-left: 13px;
}

/* ---------------------------------------------------------------------
   6. HINTS

   A hint under a field was being drawn in error red (.so-qty-hint), so
   the "61 Box" helper looked like a validation failure. Hints are muted;
   only .so-hint-warn is coloured, and it is the one that really is a
   warning.
   --------------------------------------------------------------------- */
.so-form .so-hint,
.so-form .so-qty-hint {
    display: block;
    margin-top: 3px;
    font-size: var(--so-hint-size);
    line-height: 1.35;
    font-weight: 400;
    color: var(--so-muted);
}

.so-form .so-hint-warn {
    color: #a15c07;
}

.so-form .so-hint-strong {
    color: #475467;
    font-weight: 500;
}

/* ---------------------------------------------------------------------
   7. READ-ONLY STAT

   Derived values that are shown but never typed into (entered-line
   count, running totals). A dashed border says "display only" without
   needing a disabled input that users still try to click.
   --------------------------------------------------------------------- */
.so-stat {
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    gap: 3px;
    min-width: 0;
}

.so-stat > .so-label {
    margin-bottom: 0;
}

.so-stat-value {
    display: flex;
    align-items: center;
    gap: 8px;
    box-sizing: border-box;
    height: var(--so-field-h);
    padding: 0 10px;
    border: 1px dashed #d7dde5;
    border-radius: 4px;
    background: var(--so-surface-subtle);
    font-size: var(--so-input-size);
    color: #344054;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ---------------------------------------------------------------------
   8. CHECKBOX / RADIO ROWS

   A checkbox stacked straight under a select looked glued to it. Give
   the group its own row with the same height as a control.
   --------------------------------------------------------------------- */
.so-form .so-check-row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px 20px;
    min-height: var(--so-field-h);
}

.so-form .so-check-row fluent-checkbox,
.so-form .so-check-row fluent-radio-group {
    margin: 0;
}

.so-form fluent-radio-group {
    margin: 0;
}

.so-form .so-inline-group {
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    gap: 3px;
    min-width: 0;
}

/* ---------------------------------------------------------------------
   9. SUMMARY BAR + STATUS FOOTER

   Identical on every SO form, so the eye lands in the same place no
   matter which one is open.
   --------------------------------------------------------------------- */
.so-form-summary {
    padding: 12px 16px;
}

.so-form-summary-main strong {
    font-size: 15px;
    font-weight: 600;
}

.so-form .so-status-section .so-status-row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: 8px 16px;
    margin-top: 12px;
}

.so-form .so-audit {
    margin-top: 0;
    font-size: var(--so-hint-size);
    color: #98a2b3;
    white-space: nowrap;
}

/* ---------------------------------------------------------------------
   10. DIALOG FOOTER

   Buttons were wrapping at inconsistent widths and two of them carried
   the accent colour, so nothing read as "the" primary action. One
   accent button, one minimum width, right aligned, one gap.
   --------------------------------------------------------------------- */
fluent-dialog:has(.so-form) .fluent-dialog-footer {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 8px;
    padding-top: 12px;
    border-top: 1px solid var(--so-border, #e1e6ec);
}

fluent-dialog:has(.so-form) .fluent-dialog-footer fluent-button::part(control) {
    min-width: 104px;
    justify-content: center;
}

/* Pushes everything after it to the right edge, so "Close" is always
   the rightmost control on every SO dialog. */
fluent-dialog:has(.so-form) .fluent-dialog-footer .so-footer-spacer {
    margin-left: auto;
}

/* ---------------------------------------------------------------------
   11. CALCULATION STRIP

   Right-aligned, tabular, with the total set apart. Same block is used
   on the item form and can be reused anywhere else in the module.
   --------------------------------------------------------------------- */
.so-calc {
    margin-top: 14px;
    padding: 12px 16px;
    border: 1px solid #d9e4f2;
    border-radius: 6px;
    background: var(--so-accent-soft, #eef5ff);
    gap: 6px 24px;
}

.so-calc-label {
    font-size: 12px;
    color: #475467;
}

.so-calc-value {
    font-size: 13px;
    font-weight: 500;
    color: #1f2937;
}

.so-calc-total .so-calc-label,
.so-calc-total .so-calc-value {
    margin-top: 2px;
    padding-top: 8px;
    border-top: 1px solid #c9d9ee;
    font-size: 14px;
    font-weight: 700;
    color: #12305e;
}

/* ---------------------------------------------------------------------
   12. REGISTERS (list pages)
   --------------------------------------------------------------------- */

/* Column headers as a quiet uppercase rail rather than body-weight text
   the same size as the data underneath it. FluentDataGrid puts the
   sortable title in a <button class="col-title"> inside the <th>. */
.so-list-page table.fluent-data-grid th,
.so-list-page table.fluent-data-grid th .col-title,
.so-list-page table.fluent-data-grid th .col-title-text {
    /* .col-title-text already clips with an ellipsis, so the tighter
       letter-spacing here is deliberate - uppercase at .05em was wide
       enough to clip "ORDER VALUE" in its own column. */
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .02em;
    text-transform: uppercase;
    color: #667085;
}

.so-list-page table.fluent-data-grid tbody tr:hover > td {
    background: #f4f8fe;
}

/* Money and quantity read right-aligned, so the decimal points line up
   down the column. Pair with .so-num for tabular figures. */
.so-list-page .so-cell-right,
.so-list-page .so-cell-wrap.so-cell-right {
    align-items: flex-end;
    text-align: right;
}

.so-list-page .so-cell-right > span {
    text-align: right;
    align-self: stretch;
}

.so-list-page th.so-col-right,
.so-list-page td.so-col-right {
    text-align: right;
}

/* Chips carry a hairline border so they hold their shape on the white
   grid background as well as on the tinted summary bar. */
.so-chip {
    border: 1px solid transparent;
    line-height: 1.55;
}

.so-chip-active   { background: #e7f7ed; color: #0f7b3f; border-color: #bfe6cd; }
.so-chip-inactive { background: #fdeceb; color: #a4262c; border-color: #f5c6c3; }
.so-chip-count    { background: #eef2ff; color: #3730a3; border-color: #cdd5fb; }
.so-chip-zero     { background: #f2f4f7; color: #667085; border-color: #e4e7ec; }
.so-chip-soon     { background: #fff4e5; color: #93500b; border-color: #fbdcb0; }
.so-chip-ok       { background: #f2f4f7; color: #344054; border-color: #e4e7ec; }
.so-chip-hold     { background: #fef3c7; color: #92400e; border-color: #fcd34d; }

/* The order context bar above the item register. */
.so-context-bar {
    padding: 12px 16px;
    gap: 10px 18px;
}

.so-context-name {
    font-size: 15px;
    font-weight: 600;
    line-height: 1.3;
    color: #101828;
}

.so-context-meta {
    margin-top: 3px;
    gap: 4px 16px;
    font-size: 12px;
}

.so-context-meta > span {
    white-space: nowrap;
}

.so-context-error {
    color: #a4262c;
    font-weight: 500;
}

/* The "n more item(s) to enter" strip is advice, not an order header -
   make it read as a notice. */
.so-notice-bar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px 12px;
    padding: 10px 16px;
    margin-bottom: 10px;
    border: 1px solid #fbdcb0;
    border-left: 4px solid #d97706;
    border-radius: 8px;
    background: #fffaf2;
    font-size: 12px;
    color: #7a4a09;
}

.so-notice-bar .so-chip {
    flex: none;
}

.so-loading-bar {
    padding: 12px 16px;
    margin-bottom: 10px;
    border: 1px solid var(--so-border, #e1e6ec);
    border-radius: 8px;
    background: #fff;
    font-size: 13px;
    color: var(--so-muted, #667085);
}

/* ---------------------------------------------------------------------
   13. NARROW DIALOGS

   The forms request min(px, vw) widths, so these breakpoints follow the
   dialog rather than the window. Keep the control height constant and
   let the columns collapse.
   --------------------------------------------------------------------- */
@media (max-width: 620px) {
    .so-form .so-status-section .so-status-row {
        align-items: flex-start;
        flex-direction: column;
    }

    .so-form .so-audit {
        white-space: normal;
    }
}

/* ---------------------------------------------------------------------
   14. RIGHT-ALIGNED COLUMN HEADERS

   DynamicColumn has no Align or CssClass property, and CSS cannot match
   a <th> by its text, so the numeric headers are addressed positionally.
   Each register carries its own modifier class (.so-register-master /
   .so-register-items) so the two column maps can never be confused, and
   the maps are written out here in full - if a column is added or moved
   in the razor file, update the map below with it.

   Column order includes the select checkbox column (1) that
   SelectColumnWidth adds, and the actions column at the end.

   MASTER : 1 select | 2 Order No | 3 Party | 4 Location | 5 Order Date
            6 Supply By | 7 Lines | 8 Order Value | 9 Modified
            10 Status | 11 Open | 12 actions
   ITEMS  : 1 select | 2 Station | 3 Item | 4 Order Type | 5 Quantity
            6 Rate | 7 Goods / Tax | 8 Total | 9 Supply By | 10 Hold
            11 Status | 12 actions
   --------------------------------------------------------------------- */
.so-register-master table.fluent-data-grid th:nth-child(8),
.so-register-items table.fluent-data-grid th:nth-child(5),
.so-register-items table.fluent-data-grid th:nth-child(6),
.so-register-items table.fluent-data-grid th:nth-child(7),
.so-register-items table.fluent-data-grid th:nth-child(8) {
    justify-content: flex-end;
    text-align: right;
}

/* FluentDataGrid renders the header as
       <th><div class="col-title"><span class="col-title-text">..</span></div>
   and .col-title is the flex box that lays the title out - the package's own
   `th.col-justify-end > div` rule works the same way. So the alignment has to
   land on that inner div, not on the <th>. */
.so-register-master table.fluent-data-grid th:nth-child(8) > .col-title,
.so-register-items table.fluent-data-grid th:nth-child(5) > .col-title,
.so-register-items table.fluent-data-grid th:nth-child(6) > .col-title,
.so-register-items table.fluent-data-grid th:nth-child(7) > .col-title,
.so-register-items table.fluent-data-grid th:nth-child(8) > .col-title {
    justify-content: flex-end;
    text-align: right;
}

/* An action button in a merged cell is ~28px against a ~16px first text
   line; nudge it so it optically sits on the same baseline as the text
   in the columns beside it. */
.so-list-page .so-cell-wrap > fluent-button {
    align-self: flex-start;
    margin-top: -2px;
}

/* ---------------------------------------------------------------------
   15. NEW WRAPPERS AS GRID ITEMS

   The original stretch rule only knew about .so-field / .so-span-*, so
   the wrappers introduced above (.so-stat, .so-inline-group,
   .so-check-row) would not stretch to their row and would sit at a
   different height from the inputs beside them.
   --------------------------------------------------------------------- */
.so-grid > .so-stat,
.so-grid > .so-inline-group,
.so-grid > .so-check-row {
    align-self: stretch;
    min-width: 0;
}

/* ---------------------------------------------------------------------
   16. GenericPopup AS A GRID ITEM

   A GenericPopup is dropped straight into .so-grid - it is NOT wrapped in
   a .so-field, because it renders its own label. That meant it was the
   one grid item the stretch rule in section 15 (and the original one it
   extends) never matched: every .so-field beside it stretched to the row
   track and the popup did not, so its label and input sat at a different
   height from the text field next to them.

   Give it the same box the .so-field wrapper gets: a stretched flex
   column that starts from the top, so label-over-input lines up across a
   row whatever mix of controls the row happens to hold.
   --------------------------------------------------------------------- */
.so-grid > .fluent-popup-root {
    align-self: stretch;
    min-width: 0;
    max-width: 100%;
}

.so-form .fluent-popup-root {
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
}

/* The dropdown is absolutely positioned against this box, so the flex
   column must not try to lay it out as a third item in the stack. */
.so-form .fluent-popup-root > .fluent-popup {
    position: absolute;
}

/* The label carries the shared bottom margin, so the wrapper adds none. */
.so-form .fluent-popup-root > .fluent-input-wrapper {
    flex: none;
}

/* =====================================================================
   17. REFERENCE LAYOUT  -  added 2026-09-03
   ---------------------------------------------------------------------
   Brief: match the ERP reference screen (numbered blue section headers,
   one compact title bar, no wasted vertical space, no dialog scrollbar
   on a normal laptop), and keep SOMaster_Form and SOItem_Form identical.

   Two rules were followed throughout, both learned the hard way in
   section 4 above:
     - NOTHING here changes a Fluent control's metrics. The control
       height still comes from FluentUI's own tokens. Every pixel saved
       below is chrome - padding, gaps, title rules, duplicated headers -
       never the fields themselves.
     - Anything new that is dropped into .so-grid gets the same box a
       .so-field gets (see sections 15/16).

   Space reclaimed per form vs. the 2026-09-02 layout, roughly:
     duplicated summary bar + its gap ......... 92px
     stand-alone "marked fields" note row ..... 30px
     rule + margin under 4 section titles ..... 40px
     section padding x4 ....................... 24px
     row / section gaps ....................... 28px
   which is what takes the master form under the dialog's max height.
   ===================================================================== */

/* ---------------------------------------------------------------------
   17.1 COMPACT RHYTHM

   The tokens from section 1 are re-declared, not replaced: everything
   that reads --so-row-gap / --so-label-size (grids, .so-stat,
   .so-check-row, the label min-height) tightens with them, so the form
   stays internally consistent instead of drifting.
   --------------------------------------------------------------------- */
.so-form,
.so-list-page {
    --so-row-gap: 10px;
    --so-col-gap: 18px;
    --so-label-size: 12.5px;

    /* Gap between two sibling section cards. One number, so the master
       form and every tab of the item form breathe the same amount. */
    --so-section-gap: 8px;

    /* The reference's header blue - a touch deeper than --so-accent so a
       section title reads as a heading, not as a link. */
    --so-accent-strong: #1a6ae0;
}

.so-form {
    gap: var(--so-section-gap);
    padding: 2px 2px 4px 2px;
}

/* ---------------------------------------------------------------------
   17.2 THE TITLE BAR - REMOVED

   There is no dialog title bar on the SO forms any more. It carried an
   icon tile, the title, a subtitle, the required-fields legend and a
   Status chip; none of it told the user anything the form body does not,
   and it cost ~60px at the top of every dialog.

   The <FluentDialogHeader> element itself is still in both razor files -
   remove it and FluentDialog renders its own default title bar instead -
   so it is collapsed here. Nothing is rendered inside it, so this is the
   only rule the header needs; every old .so-head-* rule is deleted
   rather than left behind as dead CSS.
   --------------------------------------------------------------------- */
fluent-dialog:has(.so-form) .fluent-dialog-header {
    display: none !important;
}

/* ---------------------------------------------------------------------
   17.3 NUMBERED SECTION HEADERS

   The reference numbers each section and sets the title in blue. The
   number is written into the razor as <span class="so-step">n</span>
   rather than generated by a CSS counter, because the item form's
   sections live inside tab panels and a counter would keep counting
   across tabs instead of restarting at 1 on each one.

   The rule under the title is gone: the section card already has a
   border, so the rule was a second boundary 8px inside the first.
   --------------------------------------------------------------------- */
.so-form .so-section-title {
    display: flex;
    align-items: center;
    gap: 8px;

    margin: 0 0 8px 0;
    padding: 0;
    border-bottom: 0;

    font-size: 14px;
    font-weight: 600;
    letter-spacing: 0;
    text-transform: none;
    color: var(--so-accent-strong, #1a6ae0);
}

.so-form .so-step {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    border: 1px solid #bcd3f5;
    border-radius: 50%;
    background: #fff;

    font-size: 11px;
    font-weight: 700;
    line-height: 1;
    color: var(--so-accent-strong, #1a6ae0);
}

/* A note parked on the right of a section title stays quiet grey. */
.so-form .so-section-title .so-section-note {
    color: var(--so-muted, #667085);
}

/* ---------------------------------------------------------------------
   17.4 SECTION CARDS

   Flatter and tighter: the shadow plus the border read as two edges at
   dialog scale, and the padding was sized for a full page.
   --------------------------------------------------------------------- */
.so-form .so-section {
    padding: 10px 14px 12px 14px;
    border-color: #e6eaf0;
    box-shadow: none;
}

/* .so-form is a flex column, so its own gap already separates sibling
   sections; inside a tab panel there is no such gap, so it is added. */
.so-form fluent-tab-panel > .so-section + .so-section {
    margin-top: var(--so-section-gap);
}

/* White, like every other card. The grey fill made the last section read
   as a footer that had come loose from the form. */
.so-form .so-status-section {
    background: #fff;
}

.so-form .so-status-section .so-status-row {
    margin-top: 10px;
}

/* ---------------------------------------------------------------------
   17.5 LABELS AND FIELDS

   Slightly larger and darker than the 2026-09-02 label (the reference's
   labels are body-weight dark grey, not micro-caps), and the gap to the
   control is 4px on every one of the three things that render a label.
   --------------------------------------------------------------------- */
.so-form .so-field > label,
.so-form .fluent-label,
.so-form .so-label {
    margin-bottom: 3px;
    color: #3f4a59;
}

/* Remarks boxes: a fixed compact height, and vertical-only resize so a
   drag can never introduce a horizontal scrollbar on the dialog. */
.so-form fluent-text-area::part(control) {
    min-height: 56px;
    resize: vertical;
}

/* ---------------------------------------------------------------------
   17.6 TABS  (item form)

   Sized to match the reference's tab strip, and given a floor so that
   switching to a shorter tab does not make the dialog jump. The floor is
   what lets the item dialog run at auto height with no scrollbar and no
   dead space at the bottom.
   --------------------------------------------------------------------- */
.so-form fluent-tabs {
    margin-top: 2px;
}

.so-form fluent-tab-panel {
    padding-top: 10px;

    /* Measured: the tallest tab needs ~570px and the shortest ~430px. The
       floor is set just under the tallest MINUS the difference the eye
       forgives - high enough that the short tab does not shrink the
       dialog to a stub, low enough that it never adds dead space to a
       tab that is already taller than it. Raising it to the tallest tab
       would stop the dialog resizing altogether, but it would also put
       a scrollbar back on a 768px-tall laptop, which is the thing this
       whole pass exists to remove. */
    min-height: 470px;
}

.so-form fluent-tabs.medium fluent-tab {
    font-size: 13px;
    font-weight: 600;
}

/* ---------------------------------------------------------------------
   17.7 SHELL

   The body is still the scroll region (a very short window has to scroll
   somewhere), but at this density neither form reaches it on a normal
   screen. overflow-x stays hidden so a wide GenericPopup dropdown can
   never produce a horizontal bar.
   --------------------------------------------------------------------- */
fluent-dialog:has(.so-form) .fluent-dialog-body {
    padding-top: 12px;
    padding-right: 4px;
    overflow-x: hidden;
}

fluent-dialog:has(.so-form) .fluent-dialog-footer {
    padding-top: 10px;
}

/* ---------------------------------------------------------------------
   17.8 NARROW DIALOG

   Below the 620px breakpoint the title bar stacks rather than squeezing
   the status chips off the right edge.
   --------------------------------------------------------------------- */
/* ---------------------------------------------------------------------
   17.9 SHORT VIEWPORTS

   The measured height of the master form at default density is ~636px,
   which clears a maximised browser on a 1080p screen. A 768px-tall
   laptop leaves roughly 700px for the whole dialog - header and footer
   included - so below that the chrome tightens one more notch rather
   than handing the user a scrollbar.

   Still not one pixel off a Fluent control: only gaps, padding and the
   title bar shrink.
   --------------------------------------------------------------------- */
@media (max-height: 820px) {
    .so-form,
    .so-list-page {
        --so-row-gap: 7px;
        --so-section-gap: 6px;
    }

    .so-form .so-section {
        padding: 7px 12px 9px 12px;
    }

    .so-form .so-section-title {
        margin-bottom: 5px;
        font-size: 13px;
    }

    .so-form .so-step {
        width: 18px;
        height: 18px;
        font-size: 10px;
    }

    .so-form fluent-text-area::part(control) {
        min-height: 48px;
    }

    .so-form fluent-tab-panel {
        min-height: 430px;
        padding-top: 8px;
    }

    /* One pixel off each label gap is worth ~7px over a form with seven
       stacked label rows - cheaper than anything else left to cut. */
    .so-form .so-field > label,
    .so-form .fluent-label,
    .so-form .so-label {
        margin-bottom: 2px;
    }

    .so-form .so-status-section .so-status-row {
        margin-top: 6px;
    }

    fluent-dialog:has(.so-form) .fluent-dialog-body {
        padding-top: 8px;
    }

    fluent-dialog:has(.so-form) .fluent-dialog-footer {
        padding-top: 8px;
    }
}

@media (max-width: 620px) {
    .so-form fluent-tab-panel {
        min-height: 0;
    }
}

/* ---------------------------------------------------------------------
   17.10 STATUS ROW

   The Active / InActive radios now carry a "Status" caption, as the
   reference does, so the row aligns from the bottom - the caption stack
   is taller than the audit note beside it.
   --------------------------------------------------------------------- */
.so-form .so-status-section .so-status-row {
    align-items: flex-end;
}

.so-form .so-status-section .so-inline-group > .so-label {
    margin-bottom: 3px;
}

/* Everywhere else a .so-check-row sits in a grid row beside real inputs,
   so it carries a control-height floor to stay on their baseline. The
   status row sits alone under a full-width textarea, where that floor is
   12px of nothing. */
.so-form .so-status-section .so-check-row {
    min-height: 0;
}

.so-form .so-status-section .so-status-row {
    margin-top: 8px;
}

/* =====================================================================
   FOOTNOTE - the stray BOM, removed 2026-09-03

   so.css was assembled by prepending a header comment to a copy of
   tender.css, and tender.css began with a UTF-8 BOM. That left a U+FEFF
   at byte 819, in the middle of the file, on the line that opens the
   "Tender module - shared form / list styling" banner.

   A BOM anywhere except byte 0 is not whitespace to a CSS tokeniser; it
   is an invalid token at the top level, and the parser recovers by
   discarding everything up to the end of the NEXT rule block. The next
   rule block was:

       .so-form, .so-list-page {
           --so-accent; --so-accent-soft; --so-border;
           --so-surface-subtle; --so-text; --so-muted;
       }

   so those six custom properties have never been defined, and every
   `var(--so-x)` written WITHOUT a fallback silently resolved to an
   invalid value - which makes the whole declaration behave as `unset`.
   The visible casualty was `.so-section { border: 1px solid
   var(--so-border) }`: the section cards have been drawn with no border
   at all. `.so-list-page`'s accent left-border was gone the same way.

   The BOM is deleted and the file is now plain UTF-8. Two consequences
   worth expecting on first run: the section cards gain their hairline
   border, and the register page header gains its blue left rule. Both
   are the original design finally arriving.

   If this file is ever regenerated or concatenated again, check byte 0
   only - and keep writing it without a BOM.
   ===================================================================== */
