/*
    Almas ERP - core control components (Wave 12A.5.2).

    Loaded by BOTH master pages, in this order:

        Bootstrap 5.3.3 (LTR or RTL build)  ->  almas/tokens.css  ->  almas/base.css
            ->  almas/components.css  ->  almas-ui.css  ->  (Site.master only) almas/shell.css

    Bootstrap stays the component system. This file only adds what Bootstrap
    does not provide consistently: it re-points the pieces Bootstrap compiles
    with its default blue palette at the Almas tokens, and gives every core
    control one shared height, state and touch-target contract. It contains no
    shell, authentication-layout, page-specific, navigation or dashboard
    rules. Section 6 (Wave 12A.5.3) adds the field pattern and the validation
    states; the small scripts that go with it live in Scripts/almas-ui.js.
    Section 7 (Wave 12A.5.4) adds the structural components: page header,
    page actions, form section, form action bar, card, badge and empty state.
    Section 8 (Wave 12A.6.2) adds the core table foundation: scroll wrapper,
    base table, header, density, numeric / date / long-text cells, footer and
    row hover / focus. Section 9 (Wave 12A.6.3) adds the row states (selected,
    unavailable, warning, error, success), the row actions column and the
    empty / no-results contract. Section 10 (Wave 12A.6.4) adds the sortable
    column header and the list toolbar with its result information. Section 11
    (Wave 12A.6.5) adds the pager, the page-size control, the bulk bar and the
    selection column.

    Why explicit overrides are needed (verified against the vendored
    bootstrap.min.css): global --bs-* variables are already mapped in
    almas/tokens.css, but .btn-secondary / .btn-outline-* / .btn-danger set
    their OWN per-component variables with static colours, .form-control:focus
    and .form-check-input hard-code Bootstrap blue, and Bootstrap gives
    [readonly] no visual style at all. Each is overridden below with the same
    Bootstrap variable names so sizing, disabled behaviour and the rest of the
    component are inherited untouched.

    Direction: only logical properties are used (block-size, inline-size,
    padding-inline, margin-inline, ...). Nothing here reads a language code or
    a [dir] value; the Bootstrap LTR/RTL build and html[dir] do all mirroring.
    Every colour comes from a token, so the same rules follow [data-bs-theme].

    ASP.NET Web Forms rendering (verified, do not assume otherwise):
      * asp:Button      -> <input type="submit" class="btn ...">      (CssClass is on the input)
      * asp:HyperLink   -> <a class="btn ...">
      * asp:TextBox     -> <input>/<textarea class="form-control">     (CssClass is on the control)
      * asp:DropDownList-> <select class="form-select">
      * asp:CheckBox / asp:RadioButton -> <span class="CssClass"><input><label for></span>
        (a disabled one adds class "aspNetDisabled" to that span and disabled to the
        input). CssClass lands on the WRAPPER span, never on the input, so Bootstrap's
        .form-check-input cannot be applied from markup. Use CssClass="almas-check".
      * asp:CheckBoxList / asp:RadioButtonList: the DEFAULT RepeatLayout ("Table")
        renders a <table> whose <td> holds input and label, which this CSS does
        not target (the glyph ends up above its label), so RepeatLayout="Flow"
        is REQUIRED with CssClass="almas-check-list". Flow renders
        <span class="CssClass"><input><label><br /><input><label>...</span>, and a
        disabled item is wrapped as <span class="aspNetDisabled"><input><label></span>.
        ("UnorderedList" is not honoured; it renders the same Flow shape.)
*/

/* ==========================================================================
   1. Buttons
   ========================================================================== */
/* One height for every button (matches .form-control, so a button beside a
   field lines up), centred content, semibold label, wraps for long localised
   text. .btn-sm is deliberately left dense (WCAG 2.5.8 minimum applies).
   asp:Button renders <input type="submit">, whose user-agent style is
   white-space: pre, so a long label would never wrap and would push the page
   wider than the viewport; white-space and max-inline-size restore wrapping. */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--almas-space-2);
    max-inline-size: 100%;
    min-block-size: var(--almas-control-height);
    font-weight: var(--almas-font-weight-semibold);
    white-space: normal;
    overflow-wrap: break-word;
}

.btn-sm {
    min-block-size: 0;
}

/* One focus ring for buttons: the outline from base.css. Bootstrap's own
   box-shadow ring would draw a second one. */
.btn:focus-visible {
    box-shadow: none;
}

.btn-primary {
    --bs-btn-color: var(--almas-color-on-primary);
    --bs-btn-bg: var(--almas-color-primary);
    --bs-btn-border-color: var(--almas-color-primary);
    --bs-btn-hover-color: var(--almas-color-on-primary);
    --bs-btn-hover-bg: var(--almas-color-primary-hover);
    --bs-btn-hover-border-color: var(--almas-color-primary-hover);
    --bs-btn-active-color: var(--almas-color-on-primary);
    --bs-btn-active-bg: var(--almas-color-primary-active);
    --bs-btn-active-border-color: var(--almas-color-primary-active);
    --bs-btn-focus-shadow-rgb: var(--almas-focus-ring-rgb);
    --bs-btn-disabled-color: var(--almas-color-on-primary);
    --bs-btn-disabled-bg: var(--almas-color-primary);
    --bs-btn-disabled-border-color: var(--almas-color-primary);
}

/* Secondary: a quiet neutral fill. */
.btn-secondary {
    --bs-btn-color: var(--almas-color-text);
    --bs-btn-bg: var(--almas-color-surface-muted);
    --bs-btn-border-color: var(--almas-color-border-strong);
    --bs-btn-hover-color: var(--almas-color-text);
    --bs-btn-hover-bg: var(--almas-color-border);
    --bs-btn-hover-border-color: var(--almas-color-border-strong);
    --bs-btn-active-color: var(--almas-color-text);
    --bs-btn-active-bg: var(--almas-color-border-strong);
    --bs-btn-active-border-color: var(--almas-color-border-strong);
    --bs-btn-focus-shadow-rgb: var(--almas-focus-ring-rgb);
    --bs-btn-disabled-color: var(--almas-color-text);
    --bs-btn-disabled-bg: var(--almas-color-surface-muted);
    --bs-btn-disabled-border-color: var(--almas-color-border-strong);
}

/* Outline secondary: same neutral, transparent until hovered. The border uses
   the control-border token so it meets 3:1 like a field border does. */
.btn-outline-secondary {
    --bs-btn-color: var(--almas-color-text);
    --bs-btn-border-color: var(--almas-control-border-color);
    --bs-btn-hover-color: var(--almas-color-text);
    --bs-btn-hover-bg: var(--almas-color-surface-muted);
    --bs-btn-hover-border-color: var(--almas-control-border-color);
    --bs-btn-active-color: var(--almas-color-text);
    --bs-btn-active-bg: var(--almas-color-border);
    --bs-btn-active-border-color: var(--almas-control-border-color);
    --bs-btn-focus-shadow-rgb: var(--almas-focus-ring-rgb);
    --bs-btn-disabled-color: var(--almas-color-text);
    --bs-btn-disabled-bg: transparent;
    --bs-btn-disabled-border-color: var(--almas-control-border-color);
}

/* Danger: a solid fill that keeps white text at >= 4.5:1 in both themes. */
.btn-danger {
    --bs-btn-color: var(--almas-color-on-primary);
    --bs-btn-bg: var(--almas-color-danger);
    --bs-btn-border-color: var(--almas-color-danger);
    --bs-btn-hover-color: var(--almas-color-on-primary);
    --bs-btn-hover-bg: var(--almas-color-danger-hover);
    --bs-btn-hover-border-color: var(--almas-color-danger-hover);
    --bs-btn-active-color: var(--almas-color-on-primary);
    --bs-btn-active-bg: var(--almas-color-danger-active);
    --bs-btn-active-border-color: var(--almas-color-danger-active);
    --bs-btn-focus-shadow-rgb: var(--almas-focus-ring-rgb);
    --bs-btn-disabled-color: var(--almas-color-on-primary);
    --bs-btn-disabled-bg: var(--almas-color-danger);
    --bs-btn-disabled-border-color: var(--almas-color-danger);
}

/* Link button: already reads the mapped link variables; only the disabled
   colour is compiled statically. */
.btn-link {
    --bs-btn-disabled-color: var(--almas-color-text-muted);
}

/* ==========================================================================
   2. Text, select and textarea
   ========================================================================== */
/* Shared height / padding / surface for every field. Block padding only: the
   inline padding is left to Bootstrap so the select arrow keeps its room in
   both directions. min-inline-size lets a field shrink inside flex/grid. */
.form-control,
.form-select {
    min-block-size: var(--almas-control-height);
    min-inline-size: 0;
    padding-block: var(--almas-control-padding-block);
    background-color: var(--almas-control-bg);
    border-color: var(--almas-control-border-color);
}

.form-control:focus,
.form-select:focus {
    border-color: var(--almas-focus-ring-color);
    box-shadow: var(--almas-focus-ring-shadow);
}

/* Placeholders are text: keep them at the muted-text contrast. */
.form-control::placeholder {
    color: var(--almas-color-text-muted);
    opacity: 1;
}

/* A long option must not widen the field. */
.form-select {
    text-overflow: ellipsis;
}

/* Multi-line: natural height from rows (Web Forms renders rows="2" by default),
   never forced to a single line; the user may resize vertically. */
textarea.form-control {
    resize: vertical;
}

/* Editable  : surface background, strong border.
   Read-only : muted background, light border, FULL text contrast, still focusable.
   Disabled  : muted background, light DASHED border, muted text, not-allowed. */
.form-control[readonly] {
    background-color: var(--almas-color-surface-muted);
    border-color: var(--almas-color-border);
    color: var(--almas-color-text);
    cursor: default;
}

.form-control[readonly]:focus {
    border-color: var(--almas-focus-ring-color);
}

.form-control:disabled,
.form-select:disabled {
    background-color: var(--almas-color-surface-muted);
    border-color: var(--almas-color-border);
    border-style: dashed;
    color: var(--almas-color-text-muted);
    cursor: not-allowed;
    opacity: 1;
}

/* ==========================================================================
   3. Checkbox and radio
   ========================================================================== */
/* The Web Forms pattern. A wrapper (asp:CheckBox / asp:RadioButton) is the row
   and the minimum touch target; the native input keeps its semantics, keyboard
   and forced-colours behaviour and takes the Almas primary through accent-color
   (theme-aware, so the checked colour follows [data-bs-theme]); the label fills
   the rest of the row, so the whole row is clickable. */
.almas-check {
    display: flex;
    align-items: center;
    min-block-size: var(--almas-touch-target-min);
}

/* A list control (CheckBoxList / RadioButtonList) renders input, label and <br />
   as direct children of one span (a disabled item is an .aspNetDisabled span
   holding its own input + label). Wrapping flex: each label takes the rest of
   its line, so every input/label pair is one row. */
.almas-check-list {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
}

.almas-check-list > br {
    display: none;
}

.almas-check-list > .aspNetDisabled {
    display: flex;
    flex: 1 0 100%;
    align-items: center;
}

.almas-check input[type="checkbox"],
.almas-check input[type="radio"],
.almas-check-list input[type="checkbox"],
.almas-check-list input[type="radio"] {
    flex: none;
    inline-size: 1.25rem;
    block-size: 1.25rem;
    margin: 0;
    accent-color: var(--almas-color-primary);
    cursor: pointer;
}

.almas-check > label,
.almas-check-list label {
    display: flex;
    flex: 1 1 auto;
    align-items: center;
    align-self: stretch;
    min-inline-size: 0;
    min-block-size: var(--almas-touch-target-min);
    margin: 0;
    padding-block: var(--almas-space-1);
    padding-inline: var(--almas-space-2);
    font-weight: var(--almas-font-weight-regular);
    cursor: pointer;
}

.almas-check-list > label {
    flex-basis: calc(100% - 1.25rem);
}

/* Disabled: muted label and no pointer, for every shape above. */
.almas-check input:disabled,
.almas-check-list input:disabled {
    cursor: not-allowed;
}

.almas-check input:disabled + label,
.almas-check-list input:disabled + label {
    color: var(--almas-color-text-muted);
    cursor: not-allowed;
}

/* Bootstrap's own markup (.form-check-input) compiles blue for checked/focus:
   re-point it at the same tokens so a native Bootstrap checkbox matches. */
.form-check-input:checked,
.form-check-input[type="checkbox"]:indeterminate {
    background-color: var(--almas-color-primary);
    border-color: var(--almas-color-primary);
}

.form-check-input:focus {
    border-color: var(--almas-focus-ring-color);
    box-shadow: var(--almas-focus-ring-shadow);
}

/* ==========================================================================
   4. Input group (Bootstrap semantics, unchanged) - only the addon surface
   ========================================================================== */
.input-group-text {
    background-color: var(--almas-color-surface-muted);
    border-color: var(--almas-control-border-color);
}

/* ==========================================================================
   5. Labels and help text
   ========================================================================== */
label,
.form-label {
    margin-block-end: var(--almas-space-1);
    color: var(--almas-color-text);
    font-size: var(--almas-font-size-md);
    font-weight: var(--almas-font-weight-semibold);
}

.almas-help-text,
.almas-form-section-description,
.form-text {
    color: var(--almas-color-text-muted);
    font-size: var(--almas-font-size-sm);
}

/* ==========================================================================
   6. Field pattern and validation states (Wave 12A.5.3)
   ==========================================================================
   One field is: label, control, help text, error slot. Markup contract:

     <div class="almas-field">
       <asp:Label AssociatedControlID="X" CssClass="form-label">Text<span class="almas-required" aria-hidden="true">*</span></asp:Label>
       <asp:TextBox ID="X" CssClass="form-control" aria-required="true" aria-describedby="X_help X_errors" />
       <div id="X_help" class="form-text">Help text</div>
       <div id="X_errors" class="almas-field-errors">
         <asp:RequiredFieldValidator CssClass="almas-field-validation" Display="Dynamic" ... />
       </div>
     </div>

   Only what Bootstrap and the sections above do not already provide is
   defined here. Everything is logical CSS and token based, so it follows
   html[dir] and [data-bs-theme] without a single language- or
   direction-specific selector. */
.almas-field {
    margin-block-end: var(--almas-space-4);
}

/* The required marker is a real "*" character (aria-hidden in markup, since
   aria-required on the control already carries the state), so the cue is a
   glyph and never colour alone. */
.almas-required {
    margin-inline-start: var(--almas-space-1);
    color: var(--almas-color-danger-text);
    font-weight: var(--almas-font-weight-bold);
}

/* Error slot: always present and referenced by the control's
   aria-describedby. Its validators are hidden while valid, and a hidden node
   inside a visible slot adds nothing to the description - referencing the
   validator itself would make assistive technology read an error message for
   a valid field. A column flex container makes each visible validator its own
   block (Web Forms toggles them inline) and reserves no space while all of
   them are hidden. */
.almas-field-errors {
    display: flex;
    flex-direction: column;
}

/* Technical input (a username, a code, an e-mail address, a URL, a document
   number - a value whose characters are Latin letters, digits and
   punctuation). Inside a right-to-left page such a value inherits the RTL
   base direction, and the bidirectional algorithm then throws its LEADING and
   TRAILING punctuation to the opposite side: "admin." is drawn ".admin",
   "_svc" as "svc_", "user@" as "@user", and while it is typed the caret jumps.
   The control is therefore laid out left-to-right: its text starts at the left
   edge, the natural start of left-to-right text, in either page direction (no
   physical property is needed - the control's own direction decides). The
   control itself, its label and its messages keep the page direction.
   Opt-in, per control: never on free text or business text (names and
   descriptions keep dir="auto" or the page direction) and never inferred from
   a language. Not for a control that carries an inline-end icon (the password
   field): its padding follows the control's own direction, not the page's. */
.almas-input-ltr {
    direction: ltr;
}

/* Invalid / valid control. Bootstrap's own state icons are suppressed: they
   are hard-coded #dc3545 / #198754 data-URIs (not tokens), add inline padding
   that the select arrow and a password toggle would have to share, and carry
   nothing the visible message does not. The invalid state is instead shown as
   a doubled border (a shape cue, not only a colour change) next to the
   visible error text and aria-invalid. The border colours already come from
   --bs-form-invalid-border-color / --bs-form-valid-border-color, which
   tokens.css maps to the semantic tokens. */
.form-control.is-invalid,
.form-control.is-valid,
textarea.form-control.is-invalid,
textarea.form-control.is-valid {
    padding-inline-end: var(--almas-control-padding-inline);
    background-image: none;
}

.form-select.is-invalid:not([multiple]):not([size]),
.form-select.is-invalid:not([multiple])[size="1"],
.form-select.is-valid:not([multiple]):not([size]),
.form-select.is-valid:not([multiple])[size="1"] {
    --bs-form-select-bg-icon: none;
    padding-inline-end: 2.25rem;    /* Bootstrap's own room for the select arrow */
}

.form-control.is-invalid,
.form-select.is-invalid {
    box-shadow: inset 0 0 0 1px var(--bs-form-invalid-border-color);
}

.form-control.is-invalid:focus,
.form-select.is-invalid:focus {
    box-shadow: inset 0 0 0 1px var(--bs-form-invalid-border-color), 0 0 0 .25rem rgba(var(--bs-danger-rgb), .25);
}

/* Keyboard focus on a field (Wave 12A.7.4). Bootstrap sets outline:0 on
   .form-control:focus, which out-specifies the outline of base.css, so a field
   was left with only a one-pixel border-colour change and a 25%-alpha halo:
   3:1 or less against the unfocused state (2.9:1 in the dark theme), and NO
   change at all when the halo and the colour are removed (forced colours).
   The same solid ring every other control gets is restored for keyboard focus.
   It replaces the halo there (a ring plus a halo would be two rings); pointer
   focus keeps the border and halo above. An invalid field keeps its doubled
   red border - the shape cue - and gains the ring. outline-color is one of the
   properties forced colours maps to a system colour, so this is also the
   focus indicator in forced colours. */
.form-control:focus-visible,
.form-select:focus-visible {
    outline: var(--almas-focus-ring-width) solid var(--almas-focus-ring-color);
    outline-offset: var(--almas-focus-ring-offset);
    box-shadow: none;
}

.form-control.is-invalid:focus-visible,
.form-select.is-invalid:focus-visible {
    box-shadow: inset 0 0 0 1px var(--bs-form-invalid-border-color);
}

/* Form-level alert: a Web Forms ValidationSummary rendered as
   class="alert alert-danger" role="alert". Its header text is followed by a
   <ul> of the validators' ErrorMessage values; reboot's list margin and
   indent would double the alert's own padding. The field-level messages stay
   where they are - the alert adds to them, it never replaces them. */
.alert > ul {
    margin-block: var(--almas-space-2) 0;
    padding-inline-start: 1.25rem;
}

/* ==========================================================================
   7. Structural components (Wave 12A.5.4)
   ==========================================================================
   Composition and hierarchy for ERP pages and forms - no field behaviour and
   no business meaning. Every rule is logical CSS on tokens, so html[dir] and
   [data-bs-theme] do all the mirroring and theming. DOM order is the visual
   order everywhere (nothing here uses order or row-reverse), so keyboard
   order always matches what is seen.

   Page header (one <h1> per page):
     <header class="almas-page-header">
       <div class="almas-page-heading">
         <h1 class="almas-page-title">Title</h1>
         <p class="almas-lede">Optional short description</p>
       </div>
       <div class="almas-page-actions"> ...links / buttons... </div>
     </header>

   Form section (fieldset + legend groups related fields; the grid inside is
   Bootstrap's: row gx-3, col-12 col-md-6 for pairs, col-12 for wide fields):
     <fieldset class="almas-form-section">
       <legend class="almas-form-section-title">Section title</legend>
       <p class="almas-form-section-description">Optional description</p>
       <div class="row gx-3"> ...almas-field columns... </div>
     </fieldset>

   Form action bar (primary action FIRST in the DOM):
     <div class="almas-form-actions"> Save, Save and new, Cancel </div>

   Empty state (the heading level is the page's choice):
     <div class="almas-empty-state">
       <h2 class="almas-empty-state-title">Nothing here yet</h2>
       <p>Short explanation</p>
       <a class="btn btn-primary">Optional action</a>
     </div> */

/* Page header and page actions. The heading group grows to fill the row, so
   the actions sit at the inline end; a long title wraps inside its group and,
   when it cannot share the line, the actions wrap below it. */
.almas-page-header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--almas-space-3);
    margin-block-end: var(--almas-space-4);
}

.almas-page-heading {
    flex: 1 1 auto;
    min-inline-size: 0;
}

.almas-page-title {
    margin: 0;
    overflow-wrap: break-word;
}

/* The description must not decide how wide the heading group wants to be:
   with a long description the group's max-content width would exceed the row
   and push the actions onto their own line even beside a short title. A zero
   inline-size with a 100% minimum contributes nothing to the group's
   intrinsic width, yet still fills the width the title-driven group gets. */
.almas-page-heading .almas-lede {
    inline-size: 0;
    min-inline-size: 100%;
    margin-block: var(--almas-space-1) 0;
}

.almas-page-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--almas-space-2);
}

/* Form section. Bootstrap's reboot floats <legend> and sizes it fluidly;
   both are reset so the legend is an ordinary block heading. Sections are
   separated by a rule, never nested in cards. A rendered <legend> sits ON the
   fieldset's top border edge, so the rule and its padding belong to the
   legend of every section after the first - a border or padding on the
   fieldset itself would be drawn through the legend or below it. The
   description is pulled up under the legend (legend margin minus description
   margin = one small gap) and keeps the normal gap before the field grid. */
.almas-form-section {
    min-inline-size: 0;
    margin: 0;
    padding: 0;
    border: 0;
}

.almas-form-section + .almas-form-section {
    margin-block-start: var(--almas-space-4);
}

.almas-form-section + .almas-form-section > .almas-form-section-title {
    padding-block-start: var(--almas-space-4);
    border-block-start: var(--almas-border-width) solid var(--almas-color-border);
}

.almas-form-section-title {
    float: none;
    inline-size: 100%;
    margin-block-end: var(--almas-space-3);
    padding: 0;
    color: var(--almas-color-text);
    font-size: var(--almas-font-size-lg);
    font-weight: var(--almas-font-weight-semibold);
    line-height: var(--almas-line-height-snug);
    overflow-wrap: break-word;
}

.almas-form-section-description {
    margin-block: calc(var(--almas-space-1) - var(--almas-space-3)) var(--almas-space-3);
}

/* Form action bar: distinct from the page actions. Aligned to the inline end
   and wrapping on wider screens; below Bootstrap's sm breakpoint the actions
   stack and take the full width. */
.almas-form-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: var(--almas-space-2);
    margin-block-start: var(--almas-space-4);
    padding-block-start: var(--almas-space-3);
    border-block-start: var(--almas-border-width) solid var(--almas-color-border);
}

@media (max-width: 575.98px) {
    .almas-form-actions {
        flex-direction: column;
        align-items: stretch;
    }
}

/* Card: a content surface. The compact variant is for a secondary panel next
   to, or inside, page content. */
.almas-card {
    background: var(--almas-color-surface);
    border: var(--almas-border-width) solid var(--almas-color-border);
    border-radius: var(--almas-radius-lg);
    box-shadow: var(--almas-shadow-sm);
    padding: var(--almas-space-4);
}

.almas-card-compact {
    padding: var(--almas-space-3);
}

/* Badge: a generic status label. It is text - never an icon or a colour dot
   on its own - and never interactive. The variant is only the semantic
   colour; which business status maps to which variant is the page's job.
   The default (no variant) is primary. Text and background come from the
   semantic text/subtle tokens, the border from the semantic colour. */
.almas-badge {
    display: inline-flex;
    align-items: center;
    gap: var(--almas-space-1);
    max-inline-size: 100%;
    padding-block: var(--almas-space-1);
    padding-inline: var(--almas-space-2);
    border: var(--almas-border-width) solid rgba(var(--almas-color-primary-rgb), .30);
    border-radius: var(--almas-radius-sm);
    background-color: var(--almas-color-primary-subtle);
    color: var(--almas-color-primary-strong);
    font-size: var(--almas-font-size-sm);
    font-weight: var(--almas-font-weight-semibold);
    line-height: var(--almas-line-height-snug);
    overflow-wrap: anywhere;
}

.almas-badge-neutral {
    border-color: var(--almas-color-border-strong);
    background-color: var(--almas-color-surface-muted);
    color: var(--almas-color-text);
}

.almas-badge-success {
    border-color: rgba(var(--almas-color-success-rgb), .30);
    background-color: var(--almas-color-success-subtle);
    color: var(--almas-color-success-text);
}

.almas-badge-warning {
    border-color: rgba(var(--almas-color-warning-rgb), .30);
    background-color: var(--almas-color-warning-subtle);
    color: var(--almas-color-warning-text);
}

.almas-badge-danger {
    border-color: rgba(var(--almas-color-danger-rgb), .30);
    background-color: var(--almas-color-danger-subtle);
    color: var(--almas-color-danger-text);
}

.almas-badge-info {
    border-color: rgba(var(--almas-color-info-rgb), .30);
    background-color: var(--almas-color-info-subtle);
    color: var(--almas-color-info-text);
}

/* Empty state: a compact zero-data panel - text and an optional action, no
   illustration or icon. It is also used on a bare Label (SelectOrganization),
   so it has to look right with plain text as its only content. */
.almas-empty-state {
    display: block;
    text-align: center;
    color: var(--almas-color-text-muted);
    background: var(--almas-color-bg);
    border: var(--almas-border-width) dashed var(--almas-color-border-strong);
    border-radius: var(--almas-radius-md);
    padding: var(--almas-space-4);
}

.almas-empty-state > * {
    margin-block: 0;
}

.almas-empty-state > * + * {
    margin-block-start: var(--almas-space-2);
}

.almas-empty-state-title {
    color: var(--almas-color-text);
    font-size: var(--almas-font-size-lg);
    font-weight: var(--almas-font-weight-semibold);
    line-height: var(--almas-line-height-snug);
}

/* ==========================================================================
   8. Core table foundation (Wave 12A.6.2)
   ==========================================================================
   Presentation for read-only data tables. Sorting, filtering, paging,
   selection, row actions and row states belong to later stages of the wave;
   nothing here reads data, calculates, formats or parses. Numbers and dates
   are shown exactly as the server formatted them.

   Markup (native table semantics; a Repeater or ListView renders the body):

     <div class="almas-table-wrap" tabindex="0" role="region"
          aria-labelledby="orders-title">
       <table class="table almas-table">
         <caption class="visually-hidden">Orders</caption>
         <thead>
           <tr>
             <th scope="col">Code</th>
             <th scope="col" class="almas-th-num">Quantity</th>
           </tr>
         </thead>
         <tbody>
           <tr>
             <th scope="row"><span class="almas-ltr">ORD-0001</span></th>
             <td class="almas-td-num"><span class="almas-ltr">-1,234.50</span></td>
           </tr>
         </tbody>
         <tfoot> ...totals, same cell classes... </tfoot>
       </table>
     </div>

   * Every table has a <caption>. When the page heading already names the
     dataset the caption keeps Bootstrap's .visually-hidden.
   * The wrapper is the one scroll region: a wide table scrolls INSIDE it and
     never widens the page. tabindex="0", role="region" and an accessible name
     (aria-labelledby, or aria-label) make that region reachable and usable by
     keyboard. Nothing is hidden, stacked into cards or made sticky.
   * "table" (Bootstrap) supplies the cell layer; "almas-table" is the Almas
     contract and re-points Bootstrap's --bs-table-* variables at Almas tokens
     (Bootstrap's own default is the page background and the emphasis colour,
     not the card surface). There is no bare "table" element selector.
   * Density: add "almas-table-dense" (cell padding only).
   * Cells: "almas-th-num" / "almas-td-num" (end-aligned numbers),
     "almas-td-date" (short date, optionally two child spans: date and time)
     and "almas-td-wrap" (long text that wraps). Any string that is not plain
     text - a code, an invoice number, an IBAN, a UUID, and every number that
     can carry a sign - goes in an inline "almas-ltr" span, one per value,
     never on a cell or the table. The cell keeps the page direction, so "end"
     alignment follows html[dir]; the span keeps the characters in order (an
     unisolated leading minus sign is otherwise drawn AFTER the digits in a
     right-to-left page).
   * In a card the wrapper drops its own frame: the card is the surface, so
     there is no second border or background. */
.almas-table-wrap {
    max-inline-size: 100%;
    overflow-x: auto;
    border: var(--almas-border-width) solid var(--almas-color-border);
    border-radius: var(--almas-radius-md);
    background-color: var(--almas-color-surface);
}

/* The base focus rule draws the ring OUTSIDE the box, where a neighbour could
   cover it; here it is drawn just inside the edge instead. Same width and
   colour. */
.almas-table-wrap:focus-visible {
    outline-offset: calc(var(--almas-focus-ring-width) * -1);
}

.almas-card .almas-table-wrap {
    border: 0;
    border-radius: 0;
    background-color: transparent;
}

.almas-table {
    --bs-table-color: var(--almas-color-text);
    --bs-table-bg: var(--almas-color-surface);
    --bs-table-border-color: var(--almas-color-border);
    --almas-table-cell-padding-block: calc(var(--almas-space-2) + var(--almas-space-1));
    --almas-table-cell-padding-inline: var(--almas-space-3);
    --almas-table-row-hover-bg: rgba(var(--almas-color-text-rgb), .05);
    --almas-table-row-focus-bg: rgba(var(--almas-color-text-rgb), .09);
    margin-block-end: 0;
    font-size: var(--almas-font-size-sm);
    line-height: var(--almas-line-height-base);
}

.almas-table-dense {
    --almas-table-cell-padding-block: var(--almas-space-2);
    --almas-table-cell-padding-inline: var(--almas-space-2);
}

/* Same selector shape as Bootstrap's own cell rule, so it wins by order. The
   controls inside a cell keep their own height: density only changes the
   space around them. */
.almas-table > :not(caption) > * > * {
    padding-block: var(--almas-table-cell-padding-block);
    padding-inline: var(--almas-table-cell-padding-inline);
}

.almas-table > tbody > tr > * {
    vertical-align: middle;
}

/* The wrapper or the card already closes the table, so the last row has no
   rule of its own. */
.almas-table > :last-child > tr:last-child > * {
    border-block-end-width: 0;
}

/* Caption: a visible title above the table (a hidden one is removed from view
   by .visually-hidden, whose declarations take precedence). */
.almas-table > caption {
    caption-side: top;
    padding-block: var(--almas-space-3) var(--almas-space-2);
    padding-inline: var(--almas-table-cell-padding-inline);
    color: var(--almas-color-text);
    font-size: var(--almas-font-size-md);
    font-weight: var(--almas-font-weight-semibold);
    line-height: var(--almas-line-height-snug);
}

/* Header: muted but accessible text on the muted surface, semibold, natural
   case (no forced uppercase - it is meaningless for Persian/Arabic and hurts
   long translated labels), and free to wrap. Alignment stays "start" from
   Bootstrap's th { text-align: inherit }. */
.almas-table > thead {
    --bs-table-bg: var(--almas-color-surface-muted);
    border-color: var(--almas-color-border-strong);
}

.almas-table > thead > tr > th {
    color: var(--almas-color-text-muted);
    font-weight: var(--almas-font-weight-semibold);
    line-height: var(--almas-line-height-snug);
    overflow-wrap: break-word;
}

/* Identifying row header: ordinary weight in the primary text colour, so it
   reads as content, not as a second header row. */
.almas-table > tbody > tr > th {
    color: var(--almas-color-text);
    font-weight: var(--almas-font-weight-medium);
}

/* Footer / totals: set apart from the body by a heavier rule and the muted
   surface. The values are the server's; this only styles them. */
.almas-table > tfoot {
    --bs-table-bg: var(--almas-color-surface-muted);
    border-color: var(--almas-color-border-strong);
}

.almas-table > tfoot > tr > * {
    color: var(--almas-color-text);
    font-weight: var(--almas-font-weight-semibold);
}

.almas-table > tfoot > tr:first-child > * {
    border-block-start: calc(var(--almas-border-width) * 2) solid var(--almas-color-border-strong);
}

/* Numbers: aligned to the inline END of the column (right in LTR, left in
   RTL), on tabular figures, never wrapped. One contract for every quantity,
   amount, percentage, debit, credit and balance - no accounting meaning here.
   The header is end-aligned to match but may wrap. The sign / digit order
   comes from the inner .almas-ltr span (see the markup note above); the cell
   itself must keep the page direction, or "end" would stop following it. */
.almas-th-num,
.almas-td-num {
    text-align: end;
}

.almas-td-num {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* Date and time, already formatted by the server. Short values stay on one
   line; a date and a time in two child spans stack, date first. */
.almas-td-date {
    white-space: nowrap;
}

.almas-td-date > span {
    display: block;
}

/* Long text: wraps at words, and a token with no break opportunity (a long
   URL, an unbroken code) breaks anywhere instead of pushing the column wider.
   The text is never cut off or replaced by an ellipsis. Auto table layout
   ignores max-inline-size on a cell and hands spare width to every column,
   so the measure is the cell's preferred inline-size (extra width goes to the
   other columns first) with a floor for when the table is squeezed. */
.almas-td-wrap {
    inline-size: 24rem;
    min-inline-size: 16rem;
    overflow-wrap: anywhere;
}

/* An isolated value in a table (a code, an invoice number, a signed amount)
   is one unit and must not be broken at its hyphens or spaces - the scroll
   wrapper absorbs the extra width. Inside a long-text cell it may wrap. */
.almas-table .almas-ltr {
    white-space: nowrap;
}

.almas-td-wrap .almas-ltr {
    white-space: normal;
}

/* A name cell in a list. Table layout gives the long-text column (almas-td-wrap) its wide preferred measure first, which squeezed a short
   name to a few characters per line; the floor keeps a name readable and still lets a long one wrap. */
.almas-td-name {
    min-inline-size: 9rem;
    overflow-wrap: anywhere;
}

/* A status badge in a cell is a short label and stays whole. The badge's own
   overflow-wrap: anywhere would otherwise let table layout shrink its column
   to a few characters and split the word. */
.almas-table .almas-badge {
    white-space: nowrap;
}

/* Row hover (only where a real hover exists - not on touch, where it would
   stick after a tap) and focus inside a row. Both reuse Bootstrap's own
   background state layer, so they sit over the cell surface in either theme.
   No animation, and no click behaviour on the row. */
@media (hover: hover) {
    .almas-table > tbody > tr:hover > * {
        --bs-table-bg-state: var(--almas-table-row-hover-bg);
    }
}

.almas-table > tbody > tr:focus-within > * {
    --bs-table-bg-state: var(--almas-table-row-focus-bg);
}

/* ==========================================================================
   9. Table row states, row actions and empty results (Wave 12A.6.3)
   ==========================================================================
   Presentation only. A state class says HOW a row looks, never WHY: the page
   (or a business module) decides which row is selected, unavailable, warning,
   error or success and adds the class to the <tr>. No business status is
   named here, and no state may be shown by colour alone - the row must also
   carry a textual status (an .almas-badge in a cell), and a selected row a
   checked checkbox. Authorization is unrelated: an unavailable row is a
   presentation state, and every action is still authorized on the server.

   Row state classes (on the <tr>; several may be combined):
     almas-row-selected     primary-tinted background
     almas-row-unavailable  muted text; its actions are really disabled
     almas-row-warning      3px inline-start marker, warning colour
     almas-row-error        3px inline-start marker, danger colour
     almas-row-success      3px inline-start marker, success colour

   Precedence (background layer - one layer, the row's own hover/focus/select
   colour, later rules win):
     1. resting     the table surface
     2. hover       neutral 5% overlay (pointer devices only)
     3. focus-within neutral 9% overlay, beats hover
     4. selected    primary tint 12%; selected + hover 15%; selected +
                    focus-within 17% (focus beats hover here too; the cap
                    keeps muted text and links at 4.5:1 on the tinted row)
   Everything else is independent of the background layer, so it composes with
   any of the above without another selector:
     - warning / error / success: the inline-start marker (a cell border);
     - unavailable: the text colour;
     - focus: the focus ring drawn on the focused link or button itself.
   The neutral overlays and the tint differ in hue as well as strength, so
   hover, focus and selected stay separable in both themes.

   Row actions (last column; one to three visible actions - anything larger
   belongs on the detail page, there is no dropdown or overflow menu):
     <th scope="col" class="almas-th-actions">Actions</th>
     <td class="almas-td-actions">
       <div class="almas-row-actions"> <a class="btn ...">View</a> ... </div>
     </td>
   Navigation is an <a>; an action that changes something is a button. Actions
   keep the standard button height (touch target), stay on one line at the
   inline end and never shrink their labels - a long translated label widens
   the column, and the wrapper scrolls.

   No dataset: no table is rendered, only the existing empty state inside a
   named region (no tabindex - there is nothing to scroll):
     <div role="region" aria-labelledby="orders-title">
       <div class="almas-empty-state"> ... </div>
     </div> */
.almas-table > tbody > tr.almas-row-selected > * {
    --bs-table-bg-state: rgba(var(--almas-color-link-rgb), .12);
}

@media (hover: hover) {
    .almas-table > tbody > tr.almas-row-selected:hover > * {
        --bs-table-bg-state: rgba(var(--almas-color-link-rgb), .15);
    }
}

.almas-table > tbody > tr.almas-row-selected:focus-within > * {
    --bs-table-bg-state: rgba(var(--almas-color-link-rgb), .17);
}

/* Marker: the first cell's inline-start border, so it sits at the start edge
   in either direction. In the collapsed-border model half of a border is
   taken from the cell, so the padding gives that half back and the text stays
   aligned with the rows that have no marker. */
.almas-row-warning {
    --almas-row-marker-color: var(--almas-color-warning);
}

.almas-row-error {
    --almas-row-marker-color: var(--almas-color-danger);
}

.almas-row-success {
    --almas-row-marker-color: var(--almas-color-success);
}

.almas-table > tbody > tr.almas-row-warning > :first-child,
.almas-table > tbody > tr.almas-row-error > :first-child,
.almas-table > tbody > tr.almas-row-success > :first-child {
    border-inline-start: calc(var(--almas-border-width) * 3) solid var(--almas-row-marker-color);
    padding-inline-start: calc(var(--almas-table-cell-padding-inline) - var(--almas-border-width) * 1.5);
}

/* Unavailable: muted text. Links and badges inside keep their own colours and
   stay readable; nothing is dimmed with opacity. */
.almas-table > tbody > tr.almas-row-unavailable > * {
    color: var(--almas-color-text-muted);
}

/* Row actions column: end-aligned header and cell, and a flex row inside the
   cell (a table cell itself must stay a table cell). */
.almas-th-actions,
.almas-td-actions {
    text-align: end;
}

.almas-td-actions {
    white-space: nowrap;
}

.almas-row-actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--almas-space-2);
    /* Table layout sizes the column from the cell's min-content, and a flex
       row of buttons only reports the longest word of each label there. The
       floor makes the column at least as wide as the whole row of actions, so
       a long translated label widens the column instead of spilling over the
       neighbouring cell. */
    min-inline-size: max-content;
}

.almas-row-actions > * {
    flex: none;
}

/* ==========================================================================
   10. Sortable columns and the list toolbar (Wave 12A.6.4)
   ==========================================================================
   Presentation for read-only list pages whose state lives in the URL. Nothing
   here sorts, filters, parses a query string or builds a URL: the page does
   that on the server and this file only styles what it renders. No script.

   List state (query-string first, so a list is bookmarkable, shareable and
   Back/Forward friendly, and does not depend on ViewState):
     q       search text                    (this stage)
     <name>  one categorical filter         (this stage; technical values)
     sort    a stable technical column code (this stage; never header text)
     dir     asc | desc                     (this stage)
     page, pageSize                         (12A.6.5)
   A page reads only the keys it knows, validates each against an allowlist,
   and builds every link from those validated values - it never copies the raw
   query string. Sorting, clearing the filters and (later) paging are plain
   links. Post-back stays for commands and mutations. The toolbar is the one
   exception: the master page has a single post-back <form runat="server">,
   and forms cannot nest, so the Search button is a Web Forms button with
   CausesValidation="False" whose handler ends in a redirect to the same
   query-string URL (post, redirect, get). The list is then rendered from the
   URL alone. A Repeater that hosts server buttons is NOT rebound on post-back
   (that breaks event validation); a list without server buttons may be.

   Sortable column (the <th> owns the state, the link only navigates):
     <th scope="col" aria-sort="ascending">
       <a class="almas-sort-link" href="?sort=code&dir=desc">Code</a>
     </th>
   aria-sort appears only on the ONE sorted column (ascending or descending)
   and is omitted everywhere else. A click on an unsorted column sorts it
   ascending, on the ascending one descending, on the descending one
   ascending; changing column starts ascending. There is no third state.

   Toolbar (composes the 12A.5 field, input, select and button contracts):
     <div class="almas-table-toolbar" role="search" aria-label="...">
       <div class="almas-table-toolbar-main">
         <div class="almas-field"> label + .form-control </div>
         <div class="almas-field"> label + .form-select </div>
       </div>
       <div class="almas-table-toolbar-actions">
         <button class="btn btn-primary">Search</button>
         <a class="btn btn-outline-secondary" href="...">Clear filters</a>
       </div>
     </div>
     <p class="almas-table-meta">3 of 8 results</p>
   Clear filters removes the search text and the filter and KEEPS sort and
   dir, so the user stays in the ordering they chose (paging, later, resets to
   the first page). No results (records exist, the query returns none) and an
   empty dataset (no records at all) are different states that share the same
   .almas-empty-state; neither renders a table. */
.almas-sort-link {
    display: inline-flex;
    align-items: center;
    gap: var(--almas-space-1);
    min-block-size: var(--almas-touch-target-min);
    padding-block: var(--almas-table-cell-padding-block);
    padding-inline: var(--almas-space-2);
    margin-block: calc(var(--almas-table-cell-padding-block) * -1);
    margin-inline: calc(var(--almas-space-2) * -1);
    border-radius: var(--almas-radius-sm);
    color: inherit;
    text-decoration: underline;
    text-underline-offset: .2em;
}

.almas-sort-link:hover {
    color: var(--almas-color-text);
}

/* The base focus ring is drawn outside the box; the link spans the whole
   header cell, where the scroll wrapper would clip it, so it is drawn inside. */
.almas-sort-link:focus-visible {
    outline-offset: calc(var(--almas-focus-ring-width) * -1);
}

/* Active column: stronger text and a decorative triangle that points up for
   ascending and down for descending. It is made of logical borders (no arrow
   glyph, no icon, no left/right), so it needs no mirroring, and it exists only
   on the sorted column: an unsorted column shows no direction at all. The
   meaning is aria-sort on the <th>; the triangle is never announced. */
th[aria-sort] > .almas-sort-link {
    color: var(--almas-color-text);
}

th[aria-sort] > .almas-sort-link::after {
    content: "";
    flex: none;
    align-self: flex-end;      /* beside the last line of a wrapped label */
    margin-block-end: .45em;
    inline-size: 0;
    block-size: 0;
    border-inline: .3125rem solid transparent;
}

th[aria-sort="ascending"] > .almas-sort-link::after {
    border-block-end: .4rem solid currentColor;
}

th[aria-sort="descending"] > .almas-sort-link::after {
    border-block-start: .4rem solid currentColor;
}

/* Toolbar: the field group starts at the inline start with the actions right
   after it while there is room, and everything wraps. The field group has a
   definite basis (room for two fields) and a ceiling (two fields at their
   widest): with an automatic basis a wrapping flex container reports an odd
   intrinsic width, which stacked the fields at mid widths and pushed the
   actions to the far edge on wide ones. The fields keep their normal
   label-over-control shape; align-items: flex-end lines the buttons up with the
   bottom edge of the controls. */
.almas-table-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--almas-space-3);
    margin-block-end: var(--almas-space-3);
}

.almas-table-toolbar-main {
    display: flex;
    flex: 1 1 24rem;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--almas-space-3);
    min-inline-size: 0;
    max-inline-size: 45rem;
}

.almas-table-toolbar-main > .almas-field {
    flex: 1 1 12rem;
    min-inline-size: 0;
    max-inline-size: 22rem;
    margin-block-end: 0;
}

.almas-table-toolbar-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--almas-space-2);
}

/* Result information: quiet text above the table or the empty state. */
.almas-table-meta {
    margin-block: 0 var(--almas-space-2);
    color: var(--almas-color-text-muted);
    font-size: var(--almas-font-size-sm);
}

/* Below Bootstrap's sm breakpoint the fields take the full width and the
   actions stack, like the form action bar, so every control keeps its size
   and touch target and nothing pushes the page wider. */
@media (max-width: 575.98px) {
    .almas-table-toolbar-main,
    .almas-table-toolbar-actions {
        flex-basis: 100%;
    }

    .almas-table-toolbar-main > .almas-field {
        flex-basis: 100%;
        max-inline-size: none;
    }

    .almas-table-toolbar-actions {
        flex-direction: column;
        align-items: stretch;
    }
}

/* ==========================================================================
   11. Pagination, page size and row selection (Wave 12A.6.5)
   ==========================================================================
   Presentation for a paged list. The page decides everything - the page
   window, the ranges, the counts - on the server and renders plain links; this
   file styles them. There is no client-side paging and no arithmetic here.

   List state (query-string first, see section 10) gains two keys:
     page      a positive integer, default 1 (omitted from URLs when 1);
               beyond the last page it is clamped to the last page
     pageSize  10 | 25 | 50 | 100, default 25 (omitted when it is the default)
   Reset rules: changing the search, a filter, the sort, the page size or
   clearing the filters sets page back to 1 and keeps every other valid key;
   changing the page changes nothing else. Invalid values are dropped, never
   preserved, and unknown keys are never copied.

   Pager (ordinary GET links; a disabled item is a span without href, so it is
   not focusable; the current page is a span and is not a link to itself):
     <nav aria-label="Pagination">
       <ul class="almas-pager">
         <li><a class="almas-pager-link" href="...">Previous</a></li>
         <li class="almas-pager-page"><a class="almas-pager-link" href="..."
             aria-label="Page 1">1</a></li>
         <li class="almas-pager-page"><span class="almas-pager-link"
             aria-current="page">2</span></li>
         <li class="almas-pager-page"><span class="almas-pager-link
             almas-pager-gap" aria-hidden="true">...</span></li>
         <li class="almas-pager-status">Page 2 of 9</li>
         <li><span class="almas-pager-link" role="link"
             aria-disabled="true">Next</span></li>
       </ul>
     </nav>
   The labels are text, so Previous and Next mean the same in both directions,
   and the row simply follows the document direction. Below Bootstrap's sm
   breakpoint the numbered items are replaced by the "Page X of Y" item (the
   server renders both; only the numbered ones are hidden, never a data column).

   Page size and range: a labelled .form-select plus an Apply button (post,
   redirect, get - the same model as the toolbar), and a range such as
   "26-50 of 126" in .almas-table-meta.

   Selection (only for a page that really has bulk actions). The checkbox
   column is the first column in the DOM, so the logical direction places it.
   Select-all means the rows of THIS PAGE only - never all filtered rows and
   never all pages; there is no cross-page selection, and moving to another page
   starts a new selection. The row checkboxes are ordinary posted controls, so
   the page works without script; Scripts/almas-ui.js only adds the live count,
   the row highlight and the enabled state of the bulk button. The server
   MUST parse, validate, authorize, scope-check and state-check every posted id
   whatever the client showed:
     <div data-almas-selection>
       <div class="almas-table-bulk">
         <p class="almas-table-meta">1-25 of 126</p>
         <div class="almas-table-bulk-actions">
           <span class="almas-table-meta" data-almas-selection-count
                 data-almas-template="{0} selected" aria-live="polite" hidden></span>
           <button class="btn btn-secondary" data-almas-bulk-action>...</button>
         </div>
       </div>
       <div class="almas-table-wrap"> <table ...>
         <th scope="col" class="almas-th-select"><span class="almas-check"><label>
           <input type="checkbox" data-almas-select-all>
           <span class="visually-hidden">Select all rows on this page</span></label></span></th>
         <td class="almas-td-select"><span class="almas-check"><label>
           <input type="checkbox" name="..." value="<id>" data-almas-select-row>
           <span class="visually-hidden">Select <id></span></label></span></td>
       </table></div>
     </div> */
.almas-pager {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--almas-space-2);
    margin: 0;
    padding: 0;
    list-style: none;
}

.almas-pager-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-inline-size: var(--almas-touch-target-min);
    min-block-size: var(--almas-touch-target-min);
    padding-inline: var(--almas-space-3);
    border: var(--almas-border-width) solid var(--almas-control-border-color);
    border-radius: var(--almas-radius-md);
    background-color: var(--almas-color-surface);
    color: var(--almas-color-link);
    font-weight: var(--almas-font-weight-medium);
    text-decoration: none;
}

a.almas-pager-link:hover {
    background-color: var(--almas-color-surface-muted);
    color: var(--almas-color-link-hover);
}

/* Current page: a solid fill - a shape and weight change as well as a colour,
   and aria-current carries the meaning. */
.almas-pager-link[aria-current="page"] {
    border-color: var(--almas-color-primary);
    background-color: var(--almas-color-primary);
    color: var(--almas-color-on-primary);
    font-weight: var(--almas-font-weight-semibold);
}

/* Disabled (first / last page): muted text and a dashed outline, like a
   disabled control; not focusable because it is not a link. */
.almas-pager-link[aria-disabled="true"] {
    border-style: dashed;
    border-color: var(--almas-color-border);
    background-color: transparent;
    color: var(--almas-color-text-muted);
    cursor: not-allowed;
}

.almas-pager-gap {
    min-inline-size: 0;
    padding-inline: var(--almas-space-1);
    border-color: transparent;
    background-color: transparent;
    color: var(--almas-color-text-muted);
}

/* The compact "Page X of Y" item exists for narrow screens only. */
.almas-pager-status {
    display: none;
    padding-inline: var(--almas-space-2);
    color: var(--almas-color-text);
}

/* Pager and page size share one row while there is room. */
.almas-table-pagination {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--almas-space-3);
    margin-block-start: var(--almas-space-3);
}

.almas-table-pagesize {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--almas-space-2);
    margin-inline-start: auto;
}

.almas-table-pagesize > .almas-field {
    margin-block-end: 0;
}

.almas-table-pagesize .form-select {
    inline-size: auto;
    min-inline-size: 6rem;
}

/* Bulk bar: the range at the inline start, the selected count and the bulk
   action at the inline end. */
.almas-table-bulk {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--almas-space-2) var(--almas-space-3);
    margin-block-end: var(--almas-space-2);
}

.almas-table-bulk-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--almas-space-3);
}

.almas-table-bulk .almas-table-meta {
    margin: 0;
}

/* Selection column: as narrow as its control, whose label is the whole
   touch target (a 44px square), so the cell needs no block padding of its
   own. The selector is as specific as the table's own cell rule. */
.almas-table > * > tr > .almas-th-select,
.almas-table > * > tr > .almas-td-select {
    inline-size: 1%;
    padding-block: 0;
    white-space: nowrap;
    vertical-align: middle;
}

.almas-th-select > .almas-check > label,
.almas-td-select > .almas-check > label {
    justify-content: center;
    min-inline-size: var(--almas-touch-target-min);
    padding-inline: 0;
}

/* Small screens: numbered pages give way to "Page X of Y" between Previous and
   Next, and the page size, the bulk bar and their buttons take the full width. */
@media (max-width: 575.98px) {
    .almas-pager {
        justify-content: space-between;
    }

    .almas-pager-page {
        display: none;
    }

    .almas-pager-status {
        display: block;
    }

    .almas-table-pagination,
    .almas-table-pagesize,
    .almas-table-bulk-actions {
        flex-direction: column;
        align-items: stretch;
    }

    .almas-table-bulk-actions {
        flex-basis: 100%;
    }

    .almas-table-pagesize {
        margin-inline-start: 0;
    }

    .almas-table-pagesize .form-select {
        inline-size: 100%;
    }
}

/* ==========================================================================
   12. Forced colours (Wave 12A.7.4)
   ==========================================================================
   Under forced colours (Windows High Contrast and its kin) the browser
   replaces authored colours, backgrounds, box-shadows and text-shadows with
   the user's system palette. What survives is borders, outlines, text, shape
   and native control rendering, so only the states that were carried by a
   background tint, a shadow or a colour alone need one more cue here:

     invalid field / danger alert  a thicker border (the doubled red border is
                                   a box-shadow, which is removed)
     current page                  Highlight / HighlightText fill (authored
                                   system colours are honoured)
     disabled page link            GrayText, keeping its dashed border
     selected row                  a Highlight outline inside the row (the
                                   tint is a shadow, which is removed)
     unavailable row               GrayText

   Everything else already keeps a border or a native rendering and is left to
   the browser: buttons, badges, cards, alerts, checkboxes and radios, the
   sort-arrow, the row markers (borders) and the disabled / read-only field
   distinction (dashed versus solid border). Only system colour keywords are
   used - no colour value - and forced-color-adjust is never changed. */
@media (forced-colors: active) {
    .form-control.is-invalid,
    .form-select.is-invalid,
    .alert-danger {
        border-width: calc(var(--almas-border-width) * 3);
    }

    .almas-pager-link[aria-current="page"] {
        border-color: Highlight;
        background-color: Highlight;
        color: HighlightText;
    }

    .almas-pager-link[aria-disabled="true"] {
        border-color: GrayText;
        color: GrayText;
    }

    .almas-table > tbody > tr.almas-row-selected {
        outline: calc(var(--almas-border-width) * 2) solid Highlight;
        outline-offset: calc(var(--almas-border-width) * -2);
    }

    .almas-table > tbody > tr.almas-row-unavailable > * {
        color: GrayText;
    }
}

/* ==========================================================================
   13. Page anatomy (Step 4 pilot)
   ==========================================================================
   The canonical MADARIX business-page anatomy, OPT-IN: a page adopts it by
   adding .almas-page-anatomy to its .almas-page root, so a page that has not
   been converted renders exactly as before. Presentation only - no business
   state, no behaviour, no permission. DOM order is the visual order.

     div.almas-page.almas-page-anatomy
       header.almas-page-header                  title (h1), optional lede, primary action
       div.almas-page-feedback                   success / error panels (role=status / alert)
       panel.almas-card.almas-page-editor        inline editor (Create / Edit), when open
       panel.almas-table-toolbar.almas-page-toolbar   search + filters + Search / Reset (role=search)
       panel.almas-data-surface                  ONE surface for the result:
         p.almas-table-meta                          range / count
         div.almas-table-wrap > table.almas-table   the data (scrolls inside its wrapper)
         div.almas-table-pagination                  pager + page size
       panel (empty state, instead of the data surface)

   The breadcrumb belongs to the shell (above the page), never to the page.
   Row actions (last column): the row's main action keeps the outline button
   (Edit); lifecycle actions are quiet buttons (.almas-btn-quiet), a
   destructive one the danger variant (.almas-btn-quiet-danger); at most three
   are visible, so there is no overflow menu. A destructive action asks for
   confirmation through a generic hook: the button carries .almas-confirm-KEY
   and the page holds one hidden element [data-almas-confirm-messages] whose
   data-KEY attribute is the localized question (almas-ui.js, wireConfirm).
   Everything is logical (html[dir] alone mirrors it) and on tokens. */

/* Quiet row / inline actions: no frame until hover, link colour; the danger
   variant uses the accessible danger text colour. Bootstrap's button variables
   do the work, like the other variants in section 1. */
.almas-btn-quiet {
    --bs-btn-color: var(--almas-color-link);
    --bs-btn-bg: transparent;
    --bs-btn-border-color: transparent;
    --bs-btn-hover-color: var(--almas-color-link-hover);
    --bs-btn-hover-bg: var(--almas-color-surface-muted);
    --bs-btn-hover-border-color: transparent;
    --bs-btn-active-color: var(--almas-color-link-hover);
    --bs-btn-active-bg: var(--almas-color-border);
    --bs-btn-active-border-color: transparent;
    --bs-btn-focus-shadow-rgb: var(--almas-focus-ring-rgb);
    --bs-btn-disabled-color: var(--almas-color-text-muted);
    --bs-btn-disabled-bg: transparent;
    --bs-btn-disabled-border-color: transparent;
}

.almas-btn-quiet-danger {
    --bs-btn-color: var(--almas-color-danger-text);
    --bs-btn-bg: transparent;
    --bs-btn-border-color: transparent;
    --bs-btn-hover-color: var(--almas-color-danger-text);
    --bs-btn-hover-bg: var(--almas-color-danger-subtle);
    --bs-btn-hover-border-color: transparent;
    --bs-btn-active-color: var(--almas-color-danger-text);
    --bs-btn-active-bg: var(--almas-color-danger-subtle);
    --bs-btn-active-border-color: transparent;
    --bs-btn-focus-shadow-rgb: var(--almas-focus-ring-rgb);
    --bs-btn-disabled-color: var(--almas-color-text-muted);
    --bs-btn-disabled-bg: transparent;
    --bs-btn-disabled-border-color: transparent;
}

/* A quiet read-only note inside an editor (for example a record's lifecycle history). */
.almas-editor-note {
    margin-block-start: var(--almas-space-2);
    padding: var(--almas-space-2) var(--almas-space-3);
    background: var(--almas-color-surface-muted);
    border-radius: var(--almas-radius-md);
    color: var(--almas-color-text-muted);
    font-size: var(--almas-font-size-sm);
    line-height: var(--almas-line-height-base);
}

.almas-editor-note > p {
    margin: 0;
}

/* ---- Header: compact, a hairline below, the primary action at the inline end ---- */
.almas-page-anatomy > .almas-page-header {
    gap: var(--almas-space-2) var(--almas-space-3);
    margin-block-end: var(--almas-space-3);
    padding-block-end: var(--almas-space-3);
    border-block-end: var(--almas-border-width) solid var(--almas-color-border);
}

.almas-page-anatomy .almas-page-title {
    font-size: var(--almas-font-size-xl);
    line-height: var(--almas-line-height-snug);
}

.almas-page-anatomy .almas-page-heading .almas-lede {
    margin-block: var(--almas-space-1) 0;
    font-size: var(--almas-font-size-sm);
}

/* ---- Feedback: close to the context, compact, same width as the content ---- */
.almas-page-feedback > .alert {
    margin-block-end: var(--almas-space-3);
}

/* ---- Editor: inline, stable width, an accent edge, compact fields ---- */
.almas-page-editor {
    max-inline-size: 64rem;
    margin-block-end: var(--almas-space-4);
    padding: var(--almas-space-3) var(--almas-space-4);
    border-inline-start: calc(var(--almas-border-width) * 3) solid var(--almas-color-primary);
    border-radius: var(--almas-radius-md);
    box-shadow: none;
}

.almas-page-editor .almas-form-section-title {
    margin-block-end: var(--almas-space-1);
    font-size: var(--almas-font-size-lg);
}

.almas-page-editor .almas-form-section > .form-text {
    margin-block: 0 var(--almas-space-3);
}

.almas-page-editor .almas-field {
    margin-block-end: var(--almas-space-3);
}

.almas-page-editor .almas-form-actions {
    margin-block-start: var(--almas-space-2);
    padding-block-start: var(--almas-space-3);
}

/* ---- Toolbar: one compact bar above the data surface ---- */
.almas-page-anatomy .almas-page-toolbar {
    padding: var(--almas-space-3);
    background: var(--almas-color-surface);
    border: var(--almas-border-width) solid var(--almas-color-border);
    border-radius: var(--almas-radius-md);
}

.almas-page-toolbar .form-label {
    margin-block-end: var(--almas-space-1);
    color: var(--almas-color-text-muted);
    font-size: var(--almas-font-size-sm);
    font-weight: var(--almas-font-weight-semibold);
}

/* ---- Data surface: the result as ONE bordered surface (meta, table, pager) ---- */
.almas-data-surface {
    background: var(--almas-color-surface);
    border: var(--almas-border-width) solid var(--almas-color-border);
    border-radius: var(--almas-radius-md);
    overflow: clip;
}

.almas-data-surface > .almas-table-meta {
    margin: 0;
    padding: var(--almas-space-2) var(--almas-space-3);
    border-block-end: var(--almas-border-width) solid var(--almas-color-border);
}

.almas-data-surface > .almas-table-wrap {
    border: 0;
    border-radius: 0;
    background-color: transparent;
}

.almas-data-surface > .almas-table-pagination {
    margin: 0;
    padding: var(--almas-space-2) var(--almas-space-3);
    border-block-start: var(--almas-border-width) solid var(--almas-color-border);
    background: var(--almas-color-surface-muted);
}

/* ---- Empty state: the same surface language, no dashed frame ---- */
.almas-page-anatomy .almas-empty-state {
    background: var(--almas-color-surface);
    border-style: solid;
    border-color: var(--almas-color-border);
}

/* ---- Small screens: the primary action takes the full width and stays reachable ---- */
@media (max-width: 575.98px) {
    .almas-page-anatomy > .almas-page-header .almas-page-actions {
        flex: 1 1 100%;
    }

    .almas-page-anatomy > .almas-page-header .almas-page-actions .btn {
        flex: 1 1 auto;
    }

    .almas-page-editor {
        padding-inline: var(--almas-space-3);
    }
}

/* ==========================================================================
   14. Enterprise data grid (Step 5)
   ==========================================================================
   The table of a business list, inside the Step 4 .almas-data-surface. Still a
   real server-rendered <table>: paging, search and every action are the page's
   own server work, and nothing here sorts, selects, measures or scripts. All of
   it is scoped to the data surface, so a table outside one is unchanged.

   Regions of the surface, in DOM order (each one optional except the table):
     p.almas-table-meta                 result range "1-25 of 148"
     div.almas-table-wrap               the ONLY scroll viewport (inline axis)
       table.table.almas-table [almas-table-min-*]
         caption.visually-hidden        the accessible name of the data
         thead > tr > th[scope=col]     wraps at words, never truncated
         tbody > tr > th[scope=row] / td
     div.almas-table-pagination         pager (start) + page size (end), ONE line

   Density. One canonical default for ERP work on a desktop, comfortable on
   touch: 6px / 12px cell padding, the compact line-height token, a 14px body.
   A single-line row is ~44px (a 30px action button + padding) on a pointer
   device; on a coarse pointer (touch) the action buttons grow to the 44px
   touch target, so a row is ~56px there. .almas-table-dense (4px / 8px) stays
   available as a variant; there is no user setting.

   Column semantics (classes on the <td> / <th>; one rule per kind of value):
     text      no class (wraps at words; .almas-td-name for a name column)
     reference .almas-td-ref      "code - name" of another record (a country, a
                                  company): the code in .almas-ltr, the name in <bdi>
     code     .almas-td-code     medium weight, tabular figures, never wrapped;
                                  the value itself sits in .almas-ltr (also on
                                  the row-header <th>)
     number    .almas-th-num / .almas-td-num      end-aligned, tabular figures
     currency  the same as number; the currency CODE is a separate .almas-ltr
               value (never inside the amount); the amount is never altered
     date      .almas-td-date     one .almas-ltr span: ISO, no calendar change
     datetime  .almas-td-date     two spans, date then time, stacked
     status    .almas-td-status   one or more .almas-badge; each badge is whole,
                                  several wrap between badges, never inside one
     actions   .almas-th-actions / .almas-td-actions   see below
   Secondary text inside a cell (a legal name, a description, a path) is
   .almas-cell-secondary - or the existing .almas-help-text, which gets the same
   look inside a table: muted, 13px, directly under the primary value, with its
   own direction isolated, so a Latin legal name never reorders a Persian name.

   Bidirectional values: codes, ISO dates, phones, e-mails, URLs and currency
   codes go in an inline .almas-ltr span (base.css); a person or company NAME is
   never forced to LTR - it is wrapped in <bdi>, which isolates it and lets its
   own first strong letter decide, so a mixed Persian + English name keeps its
   words in order and a trailing punctuation mark stays at the right end.

   Row actions: the cell shrinks to its content (the widest row) at the inline
   end; Edit, quiet lifecycle actions and a quiet danger action stay visible,
   on one line, with their full text labels (a long translated label widens the
   column and the viewport scrolls; nothing is replaced by an icon). The most
   that stays sensible in a row is THREE visible actions (the pilots show two or
   three); a transaction list needing more should open the record (a link in the
   code column) and keep its actions on the detail page - there is deliberately
   no overflow menu here.

   Width. A table has a floor so the browser never compresses it into slivers:
   .almas-table-min-sm | -md | -lg | -xl (36 | 48 | 60 | 72rem). Narrower than
   the floor the wrapper scrolls inside itself; the page never does. Rows are
   never turned into cards.

   Sticky header: DEFERRED. A sticky <th> sticks to its nearest scrolling
   ancestor, and that is the wrapper - which scrolls only horizontally and is as
   tall as the table - so it would never stick to the page. Bounding the wrapper's
   height would make it a second vertical scroll area inside the page (scroll
   traps on touch, scroll chaining on wheels, one more tab stop for the keyboard
   region, print), which a 10-100 row page does not justify. To be revisited with
   the transaction lists, with the bounded variant as an opt-in.

   Sorting and selection foundations (sections 10 and 11) are kept as they are
   and are NOT shown by the pilots: a sortable header needs the page to read,
   validate and apply sort / dir on the server, and a selection column needs a
   real bulk action, so neither appears on a table that cannot act on it. Both
   are inert until a page writes their markup.

   Pager: the page navigation at the inline start and the page size at the inline
   end share ONE line from 576px up (the label sits BESIDE its select, not above
   it); below that they wrap into rows. Pointer devices get a 36px control, a
   coarse pointer the 44px touch target. All the server controls are unchanged.
   ========================================================================== */
.almas-data-surface .almas-table {
    --almas-table-cell-padding-block: calc(var(--almas-space-1) * 1.5);
    --almas-table-cell-padding-inline: calc(var(--almas-space-2) * 1.5);
    --almas-grid-secondary-size: .8125rem;
    line-height: var(--almas-line-height-snug);
}

.almas-data-surface .almas-table-dense {
    --almas-table-cell-padding-block: var(--almas-space-1);
    --almas-table-cell-padding-inline: var(--almas-space-2);
}

/* Header: wraps at words (a long translated label never widens the page) and
   sits on the bottom edge of its cell, so one- and two-line headers align. */
.almas-data-surface .almas-table > thead > tr > th {
    border-block-end: var(--almas-border-width) solid var(--almas-color-border-strong);
    vertical-align: bottom;
}

/* Floors for the whole table (see "Width"). */
.almas-table-min-sm {
    min-inline-size: 36rem;
}

.almas-table-min-md {
    min-inline-size: 48rem;
}

.almas-table-min-lg {
    min-inline-size: 60rem;
}

.almas-table-min-xl {
    min-inline-size: 72rem;
}

/* Code: a plain value that can be told apart at a glance, not a code block. */
.almas-td-code {
    font-variant-numeric: tabular-nums;
    font-weight: var(--almas-font-weight-medium);
    letter-spacing: .02em;
    white-space: nowrap;
}

/* Status: whole badges, with a little room so one badge never gets a sliver. */
.almas-td-status {
    min-inline-size: 5.5rem;
}

.almas-data-surface .almas-badge {
    padding-block: calc(var(--almas-space-1) / 2);
    font-size: var(--almas-grid-secondary-size, var(--almas-font-size-sm));
}

/* Secondary text and metadata under a primary value. */
.almas-cell-secondary,
.almas-data-surface .almas-table .almas-help-text {
    display: block;
    margin-block-start: 0;
    color: var(--almas-color-text-muted);
    font-size: var(--almas-grid-secondary-size, var(--almas-font-size-sm));
    line-height: var(--almas-line-height-snug);
    overflow-wrap: anywhere;
    unicode-bidi: isolate;
}

/* Actions: as narrow as the widest row of buttons, at the inline end, with
   compact gaps; the button text keeps its full width (the cell is nowrap). */
.almas-data-surface .almas-th-actions,
.almas-data-surface .almas-td-actions {
    inline-size: 1%;
}

.almas-data-surface .almas-row-actions {
    gap: var(--almas-space-1);
}

/* Pager: the page navigation and the page size on ONE line from 576px up, the
   size label beside its select at every width. Below 576px the navigation takes
   its own row and the page size - label, select, Apply - the next one; a long
   translated label wraps inside its own field instead of pushing Apply down. */
.almas-data-surface > .almas-table-pagination {
    align-items: center;
    gap: var(--almas-space-2) var(--almas-space-3);
}

.almas-data-surface .almas-table-pagesize {
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: center;
}

.almas-data-surface .almas-table-pagesize > .almas-field {
    display: flex;
    flex: 1 1 auto;
    align-items: center;
    min-inline-size: 0;
    gap: var(--almas-space-2);
}

.almas-data-surface .almas-table-pagesize .form-label {
    margin: 0;
    color: var(--almas-color-text-muted);
    font-size: var(--almas-font-size-sm);
    white-space: nowrap;
}

.almas-data-surface .almas-table-pagesize .form-select {
    flex: 1 1 auto;
    inline-size: auto;
}

@media (max-width: 575.98px) {
    .almas-data-surface > .almas-table-pagination {
        align-items: stretch;
    }

    .almas-data-surface .almas-table-pagesize .form-label {
        white-space: normal;
    }

    .almas-data-surface .almas-table-pagesize .form-select {
        min-inline-size: 4.5rem;
    }
}

@media (min-width: 576px) {
    .almas-data-surface .almas-table-pagesize > .almas-field {
        flex: none;
    }
}

/* A pointer device does not need the 44px touch target on every control: a
   36px one is comfortable and keeps rows and the pager compact. A coarse
   pointer (touch) keeps the full target. */
@media (pointer: fine) {
    .almas-data-surface .almas-pager-link {
        min-inline-size: 2.25rem;
        min-block-size: 2.25rem;
    }

    .almas-data-surface .almas-pager-gap {
        min-inline-size: 0;
    }

    .almas-data-surface .almas-table-pagesize .form-select,
    .almas-data-surface .almas-table-pagesize .btn {
        min-block-size: 2.25rem;
        padding-block: var(--almas-space-1);
    }

    .almas-data-surface .almas-sort-link {
        min-block-size: 2rem;
    }
}

@media (pointer: coarse) {
    .almas-data-surface .almas-row-actions .btn {
        min-block-size: var(--almas-touch-target-min);
    }
}

/* A reference to another record in "code - name" form (a country, a company):
   wide enough that the code and the name do not fall onto two lines on every
   row; a long name still wraps at its own words. */
.almas-td-ref {
    min-inline-size: 10rem;
}

/* A name column in a grid may be a little narrower than in a card list; a long
   name still wraps at its own words. */
.almas-data-surface .almas-td-name {
    min-inline-size: 8rem;
}

/* ==========================================================================
   15. Forms and the inline editor (Step 6)
   ==========================================================================
   The canonical MADARIX form, as rendered by classic Web Forms. Presentation
   only: validators, the ValidationSummary, DefaultButton, Save / Cancel and
   every postback are the page's own server work. Opt-in: a form adopts the
   grid with .almas-form-grid, the editor density with .almas-page-editor (or
   the neutral .almas-form-container for a form that is not an inline editor),
   so a page that was not converted renders as before.

   Editor, in DOM order (which is also the visual and the keyboard order):
     panel.almas-card.almas-page-editor [DefaultButton=Save]
       asp:ValidationSummary.almas-validation-summary     only when invalid
       fieldset.almas-form-section
         legend.almas-form-section-title                  the MODE: "New X" / "Edit X"
         p.form-text                                      "* = required" (localized)
         div.almas-form-grid                              the fields
           div.almas-span-N > div.almas-field             one field
           div.almas-span-12.almas-editor-note            optional read-only history
       div.almas-form-actions                             Save (primary) first, Cancel

   One field (names that already existed are kept):
     .almas-field            wrapper                       (label above its control)
     label.form-label        the label, tied to the control by AssociatedControlID
     .almas-required         the "*" next to a REQUIRED field's label; aria-hidden,
                             because the state is carried by aria-required on the
                             control and by the legend - never by colour alone
     .form-control|.form-select|textarea   the control
     .form-text              help text (muted, 13px)
     .almas-field-errors     the slot of the field's validators, referenced by the
                             control's aria-describedby
     .almas-field-validation one validator message (danger text, 14px)
   Required means a field with a RequiredFieldValidator (or a server rule): the
   marker, aria-required and the validator are always written together.

   Grid. The editor is a CONTAINER, so the columns follow the width of the
   editor itself (a docked sidebar narrows it without changing the viewport):
     below 36rem (576px)   every field is full width
     from 36rem            .almas-span-8 / -6 / -4 keep their share of 12;
                           .almas-span-3 becomes half width
     from 52rem            .almas-span-3 is a quarter
     .almas-span-lg-N      N = 6 or 3, FROM 52rem only (full width below it): for
                           a group that should stay together on a narrow editor,
                           e.g. a wide field followed by two narrow ones -
                           class="almas-span-6 almas-span-lg-3" is half width
                           from 36rem and a quarter from 52rem
     .almas-span-12        always full width (a long text, a selector, a note)
   Without container-query support the form is one column, never broken.
   Related fields are grouped by the page's markup order; nothing here moves
   a field.

   Density (pointer devices; a coarse pointer keeps the 44px touch targets):
   38px controls and buttons, 14px text, 12px between rows, 16px between
   columns, 14px labels. Disabled = dashed border, muted text and a
   not-allowed cursor; read-only = muted surface with full-contrast text and a
   solid border (section 2) - neither depends on opacity.
   ========================================================================== */
.almas-page-editor,
.almas-form-container {
    container: almas-form / inline-size;
    --almas-form-row-gap: calc(var(--almas-space-2) * 1.5);
    --almas-form-column-gap: var(--almas-space-3);
}

.almas-form-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--almas-form-row-gap, var(--almas-space-3)) var(--almas-form-column-gap, var(--almas-space-3));
}

.almas-form-grid > * {
    grid-column: 1 / -1;
    min-inline-size: 0;
}

.almas-form-grid .almas-field {
    margin-block-end: 0;
}

/* The field's own text metrics are the compact ones, so the inline label, the
   required marker and a wrapped second line keep a tight rhythm (a 16px / 1.6
   strut would double the spacing of a wrapped label). */
.almas-page-editor .almas-field,
.almas-form-container .almas-field {
    font-size: var(--almas-font-size-sm);
    line-height: var(--almas-line-height-snug);
}

@container almas-form (min-width: 36rem) {
    .almas-form-grid {
        grid-template-columns: repeat(12, minmax(0, 1fr));
    }

    .almas-form-grid > .almas-span-8 {
        grid-column: span 8;
    }

    .almas-form-grid > .almas-span-6,
    .almas-form-grid > .almas-span-3 {
        grid-column: span 6;
    }

    .almas-form-grid > .almas-span-4 {
        grid-column: span 4;
    }
}

@container almas-form (min-width: 52rem) {
    .almas-form-grid > .almas-span-3 {
        grid-column: span 3;
    }

    .almas-form-grid > .almas-span-lg-6 {
        grid-column: span 6;
    }

    .almas-form-grid > .almas-span-lg-3 {
        grid-column: span 3;
    }
}

/* Labels: compact, above the control, wrapping at words (never over the control).
   The label is INLINE, so the required "*" (a sibling span written after it)
   follows the last word of a wrapped label instead of dropping to a line of its
   own; the control that follows gets the small gap instead of the label. */
.almas-page-editor .form-label,
.almas-form-container .form-label {
    display: inline;
    margin-block-end: 0;
    font-size: var(--almas-font-size-sm);
    line-height: var(--almas-line-height-snug);
    overflow-wrap: break-word;
}

.almas-page-editor .almas-field > .form-label + .almas-required + *,
.almas-page-editor .almas-field > .form-label + :not(.almas-required),
.almas-form-container .almas-field > .form-label + .almas-required + *,
.almas-form-container .almas-field > .form-label + :not(.almas-required) {
    margin-block-start: var(--almas-space-1);
}

/* Help text sits under the control, quieter than a validation message. */
.almas-page-editor .almas-field > .form-text,
.almas-form-container .almas-field > .form-text {
    margin-block-start: var(--almas-space-1);
    font-size: var(--almas-grid-secondary-size, .8125rem);
    line-height: var(--almas-line-height-snug);
}

/* A long text: at least ~80px high, resizable only vertically (section 2). */
.almas-page-editor textarea.form-control,
.almas-form-container textarea.form-control {
    min-block-size: 5rem;
}

/* A number: end-aligned tabular figures. Put it on the input together with
   .almas-input-ltr for an amount, so a sign and the digits keep their order. */
.almas-input-num {
    font-variant-numeric: tabular-nums;
    text-align: end;
}

/* The ValidationSummary: a calm list above the fields - a surface with a danger
   edge, normal text - so it reads as "fix these", not as a failed page (that
   is the .alert-danger panel). The messages are the validators' own. */
.almas-validation-summary {
    margin-block-end: var(--almas-space-3);
    padding: calc(var(--almas-space-2) * 1.25) var(--almas-space-3);
    background: var(--almas-color-surface);
    border: var(--almas-border-width) solid var(--almas-color-border);
    border-inline-start: calc(var(--almas-border-width) * 3) solid var(--almas-color-danger);
    border-radius: var(--almas-radius-md);
    color: var(--almas-color-text);
    font-size: var(--almas-font-size-sm);
    line-height: var(--almas-line-height-snug);
    overflow-wrap: anywhere;
}

.almas-validation-summary > ul {
    margin-block: 0;
    padding-inline-start: 1.25rem;
}

.almas-validation-summary > ul > li + li {
    margin-block-start: var(--almas-space-1);
}

/* A native checkbox row inside a form: the control, its label and (below) its
   help text; the whole row is the hit area. */
.almas-page-editor .almas-check > label,
.almas-form-container .almas-check > label {
    font-size: var(--almas-font-size-sm);
    font-weight: var(--almas-font-weight-semibold);
}

.almas-page-editor .almas-check + .form-text,
.almas-form-container .almas-check + .form-text {
    margin-block-start: 0;
    padding-inline-start: calc(1.25rem + var(--almas-space-2));
    font-size: var(--almas-grid-secondary-size, .8125rem);
}

/* A disabled Web Forms checkbox renders its input inside a span flagged disabled
   (aspNetDisabled), so the label is the span's sibling, not the input's. */
.almas-check > .aspNetDisabled + label,
.almas-check > [disabled] + label {
    color: var(--almas-color-text-muted);
    cursor: not-allowed;
}

/* Information inside an editor (a lifecycle history): quieter than the fields. */
.almas-page-editor .almas-editor-note {
    margin-block-start: 0;
}

/* Editor actions: compact bar, Save first. */
.almas-page-editor .almas-form-actions {
    margin-block-start: var(--almas-space-3);
    padding-block-start: calc(var(--almas-space-2) * 1.5);
}

/* Pointer devices: 38px / 14px controls and buttons. A coarse pointer keeps the
   44px touch targets and 16px text (which also stops a phone from zooming in
   on focus). */
@media (pointer: fine) {
    .almas-page-editor .form-control,
    .almas-page-editor .form-select,
    .almas-form-container .form-control,
    .almas-form-container .form-select {
        min-block-size: 2.375rem;
        padding-block: calc(var(--almas-space-1) * 1.25);
        font-size: var(--almas-font-size-sm);
    }

    .almas-page-editor .btn,
    .almas-form-container .btn {
        min-block-size: 2.375rem;
        padding-block: calc(var(--almas-space-1) * 1.25);
        font-size: var(--almas-font-size-sm);
    }

    .almas-page-editor .almas-check,
    .almas-page-editor .almas-check > label,
    .almas-form-container .almas-check,
    .almas-form-container .almas-check > label {
        min-block-size: 2.375rem;
    }
}

/* Two-column-in-twelve field (an ISO code, a phone code) from 52rem; on a narrower editor three of them share a row. */
@container almas-form (min-width: 52rem) {
    .almas-form-grid > .almas-span-lg-2 {
        grid-column: span 2;
    }
}

/* ==========================================================================
   16. Entry pages: organization selection, Home and the status marks (Step 9)
   ==========================================================================
   Presentation only, on tokens and logical properties (html[dir] mirrors it).
   Home shows context, navigation and links - never business figures. */

/* The round mark above an access-denied / error message: decorative (aria-hidden). */
.almas-auth-card-mark {
    display: flex;
    align-items: center;
    justify-content: center;
    inline-size: 3rem;
    block-size: 3rem;
    margin-block-end: var(--almas-space-3);
    border-radius: 50%;
    background: var(--almas-color-surface-muted);
    color: var(--almas-color-primary);
}

.almas-auth-card-mark > svg {
    inline-size: 1.5rem;
    block-size: 1.5rem;
}

/* Organization selection: one centred column, the user and the way out are visible. */
.almas-select-org-wrap {
    max-inline-size: 36rem;
    margin-inline: auto;
}

.almas-select-org-user {
    margin-block: 0 var(--almas-space-2);
    color: var(--almas-color-text-muted);
    font-size: var(--almas-font-size-sm);
}

.almas-select-org-exit {
    display: grid;
    margin-block-start: var(--almas-space-3);
    padding-block-start: var(--almas-space-3);
    border-block-start: var(--almas-border-width) solid var(--almas-color-border);
}

/* Home */
.almas-home-section-title {
    margin-block: 0 var(--almas-space-2);
    font-size: var(--almas-font-size-lg);
    font-weight: var(--almas-font-weight-semibold);
    line-height: var(--almas-line-height-snug);
}

.almas-home-context {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--almas-space-2) var(--almas-space-5);
    margin-block-end: var(--almas-space-4);
    padding: var(--almas-space-3) var(--almas-space-4);
    background: var(--almas-color-surface);
    border: var(--almas-border-width) solid var(--almas-color-border);
    border-inline-start: calc(var(--almas-border-width) * 3) solid var(--almas-color-primary);
    border-radius: var(--almas-radius-md);
}

.almas-home-context > .almas-home-section-title {
    flex: 1 0 100%;
}

.almas-home-context-list {
    display: flex;
    flex: 1 1 auto;
    flex-wrap: wrap;
    gap: var(--almas-space-2) var(--almas-space-5);
    margin: 0;
}

.almas-home-context-list dt {
    color: var(--almas-color-text-muted);
    font-size: var(--almas-font-size-sm);
    font-weight: var(--almas-font-weight-regular);
}

.almas-home-context-list dd {
    margin: 0;
    font-weight: var(--almas-font-weight-medium);
    overflow-wrap: anywhere;
}

.almas-home-code {
    color: var(--almas-color-text-muted);
    font-size: var(--almas-font-size-sm);
    font-weight: var(--almas-font-weight-regular);
}

.almas-home-modules {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
    gap: var(--almas-space-3);
}

.almas-home-module {
    padding: var(--almas-space-3) var(--almas-space-4);
    background: var(--almas-color-surface);
    border: var(--almas-border-width) solid var(--almas-color-border);
    border-radius: var(--almas-radius-md);
}

.almas-home-module-title {
    display: flex;
    align-items: center;
    gap: var(--almas-space-2);
    margin: 0 0 var(--almas-space-2);
    font-size: var(--almas-font-size-md);
    font-weight: var(--almas-font-weight-semibold);
}

.almas-home-module-icon {
    flex: none;
    inline-size: 1.25rem;
    block-size: 1.25rem;
    color: var(--almas-color-primary);
}

.almas-home-group-title {
    margin: var(--almas-space-3) 0 var(--almas-space-1);
    color: var(--almas-color-text-muted);
    font-size: var(--almas-font-size-sm);
    font-weight: var(--almas-font-weight-semibold);
}

.almas-home-links {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 14rem), 1fr));
    gap: 0 var(--almas-space-3);
    margin: 0;
    padding: 0;
    list-style: none;
}

.almas-home-link {
    display: flex;
    flex-direction: column;
    justify-content: center;
    min-block-size: 2.25rem;
    padding: var(--almas-space-1) var(--almas-space-2);
    border-radius: var(--almas-radius-sm);
    color: var(--almas-color-link);
    text-decoration: none;
}

.almas-home-link:hover {
    background: var(--almas-color-surface-muted);
    color: var(--almas-color-link-hover);
}

.almas-home-link-desc {
    color: var(--almas-color-text-muted);
    font-size: var(--almas-grid-secondary-size, .8125rem);
    line-height: var(--almas-line-height-snug);
}

@media (pointer: coarse) {
    .almas-home-link {
        min-block-size: var(--almas-touch-target-min);
    }
}
