/* Starruk.Platform.Blazor.DesignSystem — component-style layer (TRAP-63 / amendment A1).
 *
 * Styles the shared kit's structural classes (PageHero, cards, filter bar, states, tables) so any
 * markup using them renders in the Ocean visual language, reading exclusively from the --s-* custom
 * properties this package's StarrukThemeRoot emits (see StarrukThemeFactory.BuildCssVariables) — no
 * hardcoded hex here. Ported from ocean-platform's oc-page/.d-card/.page-hero rules
 * (wwwroot/app.css); see docs/ocean-token-mapping.md for the full --d-*->--s-* mapping.
 *
 * Class-name contract with Starruk.Platform.Blazor.Components (P2, TRAP-59): components render
 * these class names on their root/slot elements. Recorded under ## Beslissingen (P2b) in the
 * trapwatch-platform build-runbook so both packages stay aligned.
 */

/* ===== Accent layer (meaning tokens) — STP schil, ported from ocean-platform's semantic accent
 * layer (its app.css --s-* block) =====
 *
 * Six meanings: five that are earned, and a resting state. Anything that colours MEANING — a
 * plate that says a person must decide, a border that says another system owns the record —
 * reads these tokens and never a palette colour directly. Only this block binds a meaning to the
 * house style, so a product rebrands by changing its StarrukPalette, not by rewriting a
 * component. A brand colour written into a rule that consumes these tokens is a mistake, not a
 * taste call.
 *
 * Per meaning three tokens: the base accent (borders, dots, rules), -tint (the soft background
 * behind it) and -ink (text and icons on that tint). Every value resolves to a --s-* palette
 * token or a color-mix over one, so the meanings follow the active house style — no meaning owns
 * a colour of its own. Naming is deliberate and part of the contract: --s-human is "a person is
 * needed", not "gold". All six bases travel even where no rule reads one yet, because the donor
 * writes that rule into its own source: "Promotion rule (Starruk): the six meanings and their
 * --s-* names move to the foundation verbatim; every customer-platform keeps declaring these
 * bindings against its own --d-* tokens in its two theme blocks."
 *
 * Both modes are written out in full, along the pattern the emitted theme block uses (:root,
 * :root[data-theme="dark"], and the OS fallback under :root:not([data-theme="light"])). Most
 * values are identical because the underlying --s-* tokens already flip; what the mode changes is
 * the translucent tints, which need more alpha to stay visible on the dark surfaces — and the one
 * documented divergence on --s-done-ink.
 *
 * The gate under this block is SemanticAccentLayerTests, the port of the donor's own
 * SemanticAccentContrastTests (its app.css names that gate here): it re-reads this file, resolves
 * every binding against StarrukThemeFactory.BuildCssVariables, and measures each -ink on its own
 * -tint composited over the surface. The promise it holds: every pair clears WCAG AA (4.5:1) on
 * --s-surface — the surface a plate actually sits on — in both modes, and the 3:1 non-text floor
 * (SC 1.4.11) on all four surfaces of both modes. Small -ink text placed directly on
 * --s-page-background or --s-surface-2 is deliberately NOT covered: several pairs measure between
 * 4.17:1 and 4.5:1 there (the README carries the measured table).
 */

/* Rhythm defaults also work without StarrukThemeRoot. Zero specificity lets configured
   theme values and host overrides win regardless of stylesheet order. */
:where(:root) {
    --s-page-stack: 16px;
    --s-space-section: 24px;
    --s-grid-gutter: 24px;
}

:root {
    /* A person has to decide or act. The warning triple: the palette derives its warning tint
       from the customer's accent colour, which is exactly the "look at me" surface this needs. */
    --s-human: var(--s-warning);
    --s-human-tint: var(--s-tint-warning);
    --s-human-ink: var(--s-warning-text);

    /* The system did it by itself. The brand secondary — the accent a product already uses for
       its own doing — with the text-safe variant as ink. */
    --s-auto: var(--s-secondary);
    --s-auto-tint: color-mix(in srgb, var(--s-secondary) 10%, transparent);
    --s-auto-ink: var(--s-secondary-text);

    /* Another system owns the data or the next step. Informational, not alarming. The palette has
       no text-safe Info variant (Info is an accent surface colour, ADR-006 A2 §5), so the ink is
       mixed toward the mode's own ink: that darkens it on light surfaces and lightens it on dark
       ones, in one expression. */
    --s-external: var(--s-info);
    --s-external-tint: var(--s-tint-info);
    --s-external-ink: color-mix(in srgb, var(--s-info) 45%, var(--s-ink-900));

    /* Past its moment. The error triple — late is the mildest member of that family, which is why
       it takes the tint and never the filled surface. */
    --s-late: var(--s-error);
    --s-late-tint: color-mix(in srgb, var(--s-error) 10%, transparent);
    --s-late-ink: var(--s-error-text);

    /* Finished; nothing is waiting. */
    --s-done: var(--s-success);
    --s-done-tint: var(--s-tint-success);
    --s-done-ink: var(--s-success-text);

    /* The resting state: no claim about who is needed. Everything that is not earned lands here.
       Its base is the strong line tone, the way the donor binds it — a resting border is a line,
       not an accent. */
    --s-neutral: var(--s-line-strong);
    --s-neutral-tint: var(--s-fill);
    --s-neutral-ink: var(--s-ink-500);
}

:root[data-theme="dark"] {
    --s-human: var(--s-warning);
    --s-human-tint: var(--s-tint-warning);
    --s-human-ink: var(--s-warning-text);

    --s-auto: var(--s-secondary);
    --s-auto-tint: color-mix(in srgb, var(--s-secondary) 16%, transparent);
    --s-auto-ink: var(--s-secondary-text);

    --s-external: var(--s-info);
    --s-external-tint: var(--s-tint-info);
    --s-external-ink: color-mix(in srgb, var(--s-info) 45%, var(--s-ink-900));

    --s-late: var(--s-error);
    --s-late-tint: color-mix(in srgb, var(--s-error) 16%, transparent);
    --s-late-ink: var(--s-error-text);

    --s-done: var(--s-success);
    --s-done-tint: var(--s-tint-success);
    /* ONE deliberate divergence from the light block, taken over from the donor, which found and
       fixed the same defect on the same pair: --s-success-text is the dark success accent itself
       here, and on --s-tint-success over the dark surfaces it measures 4.15:1 (--s-surface) and
       3.80:1 (--s-surface-2) — the donor recorded 4.16:1 for the same pair, the rounding of one
       composite step apart — under the WCAG AA bar the word "ink" promises. Mixed toward
       --s-ink-900, in the same form --s-external-ink already uses, it measures 7.24:1 / 6.63:1 /
       8.38:1. The light block needs no such lift and keeps --s-success-text. */
    --s-done-ink: color-mix(in srgb, var(--s-success) 45%, var(--s-ink-900));

    --s-neutral: var(--s-line-strong);
    --s-neutral-tint: var(--s-fill);
    --s-neutral-ink: var(--s-ink-500);
}

/* OS-dark fallback, same guard as the theme block: an explicit light choice still wins. Declares
   the dark block's values verbatim (a parity test pins that). */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --s-human: var(--s-warning);
        --s-human-tint: var(--s-tint-warning);
        --s-human-ink: var(--s-warning-text);

        --s-auto: var(--s-secondary);
        --s-auto-tint: color-mix(in srgb, var(--s-secondary) 16%, transparent);
        --s-auto-ink: var(--s-secondary-text);

        --s-external: var(--s-info);
        --s-external-tint: var(--s-tint-info);
        --s-external-ink: color-mix(in srgb, var(--s-info) 45%, var(--s-ink-900));

        --s-late: var(--s-error);
        --s-late-tint: color-mix(in srgb, var(--s-error) 16%, transparent);
        --s-late-ink: var(--s-error-text);

        --s-done: var(--s-success);
        --s-done-tint: var(--s-tint-success);
        --s-done-ink: color-mix(in srgb, var(--s-success) 45%, var(--s-ink-900));

        --s-neutral: var(--s-line-strong);
        --s-neutral-tint: var(--s-fill);
        --s-neutral-ink: var(--s-ink-500);
    }
}

/* ===== Plate (s-plate) — the shape of the accent layer =====
 *
 * A small square carrying one icon: the icon says what the work is, the tone says who is needed.
 * Ported from ocean-platform's .oc-plate. It reads only the meaning tokens above, so the plate
 * moves between products unchanged. Neutral is the base rule and is earned by nothing — a tone is
 * earned by the item. The three sizes keep their geometry fixed (a plate is a shape, not a
 * surface): only the colours are themeable.
 */

.s-plate {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 28px;
    height: 28px;
    border-radius: 9px;
    background: var(--s-neutral-tint);
    color: var(--s-neutral-ink);
}

.s-plate .mud-icon-root {
    font-size: 1rem;
}

.s-plate.is-human {
    background: var(--s-human-tint);
    color: var(--s-human-ink);
}

.s-plate.is-auto {
    background: var(--s-auto-tint);
    color: var(--s-auto-ink);
}

.s-plate.is-external {
    background: var(--s-external-tint);
    color: var(--s-external-ink);
}

.s-plate.is-late {
    background: var(--s-late-tint);
    color: var(--s-late-ink);
}

.s-plate.is-done {
    background: var(--s-done-tint);
    color: var(--s-done-ink);
}

.s-plate.is-sm {
    width: 20px;
    height: 20px;
    border-radius: 6px;
}

.s-plate.is-sm .mud-icon-root {
    font-size: 0.8rem;
}

.s-plate.is-lg {
    width: 44px;
    height: 44px;
    border-radius: 14px;
}

.s-plate.is-lg .mud-icon-root {
    font-size: 1.5rem;
}

/* ===== Activity timeline (s-timeline) — the accent layer read as a history =====
 *
 * An ordered list where every line is a marker, a time and a sentence. The marker is a small
 * .s-plate, so "a person did this" and "the system did this" are the same two tones a plate uses
 * everywhere else instead of a second colour language. Ported from ocean-platform's .oc-timeline
 * (which already leads its lines with the plate) with starruk-crm's tighter time column.
 *
 * Colours come from the meaning tokens and the ink scale only — the timeline owns no colour of
 * its own, and every text pair here is one the palette gate already measures on all four
 * surfaces (--s-ink-900, --s-muted, --s-secondary-text).
 */

.s-timeline {
    list-style: none;
    margin: 0;
    padding: 4px 0;
    display: flex;
    flex-direction: column;
}

.s-timeline-event {
    display: grid;
    /* 20px is exactly .s-plate.is-sm; the middle column holds a short date and time. */
    grid-template-columns: 20px 110px minmax(0, 1fr);
    gap: 10px;
    align-items: start;
    padding: 7px 0;
    font-size: 0.85rem;
    color: var(--s-ink-900);
}

.s-timeline-mark {
    display: inline-flex;
    /* The 1px drop optically aligns the plate's icon with the cap height of the sentence. */
    margin-top: 1px;
}

/* Aligned digits: a stack of timestamps is a column of numbers, which is the one place tabular
   figures belong — never on a sentence that happens to contain one. */
.s-timeline-when {
    font-size: 0.75rem;
    color: var(--s-muted);
    font-variant-numeric: tabular-nums;
    padding-top: 3px;
    white-space: nowrap;
}

.s-timeline-what {
    min-width: 0;
    line-height: 1.45;
    overflow-wrap: anywhere;
}

.s-timeline-what a {
    /* The text-safe secondary token: the raw accent measures ~2.6:1 as text. */
    color: var(--s-secondary-text);
    text-decoration: none;
}

.s-timeline-what a:hover {
    text-decoration: underline;
}

/* On a phone the time drops under the sentence rather than squeezing it to three words. */
@media (max-width: 599.98px) {
    .s-timeline-event {
        grid-template-columns: 20px minmax(0, 1fr);
        gap: 2px 10px;
    }

    .s-timeline-when,
    .s-timeline-what {
        grid-column: 2;
    }

    .s-timeline-when {
        padding-top: 0;
    }
}

/* ===== Proposal card (s-proposal) — what is asked, why, how sure, by when =====
 *
 * The card anatomy both donors arrived at: kind, title, reason, certainty, expiry, and one
 * primary action on the right with the way out underneath the content. Ported from
 * starruk-crm's .w-card-item (the domain-free one) with ocean-platform's clamp decision.
 *
 * The reason is clipped at two lines and the certainty is its sibling, never its child: when a
 * reason runs long it is the reason that loses lines, and the number stays readable. Every chip
 * here paints an accent tint with the matching ink — the pairs SemanticAccentLayerTests measures
 * at AA on the surface a card sits on — so the card carries meaning without owning a colour.
 */

.s-proposal {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 8px 16px;
    align-items: start;
    padding: 12px 16px;
    border-bottom: 1px solid var(--s-line);
}

/* Cards stack into one lane; the last one closes the lane instead of drawing a line under it. */
.s-proposal:last-child {
    border-bottom: 0;
}

.s-proposal-main {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
    min-width: 0;
}

.s-proposal-kind {
    display: inline-flex;
    align-items: center;
    flex: none;
    padding: 1px 8px;
    border-radius: 999px;
    font-family: var(--s-font-display);
    font-weight: 700;
    font-size: 0.68rem;
    letter-spacing: 0.03em;
    white-space: nowrap;
    background: var(--s-neutral-tint);
    color: var(--s-neutral-ink);
}

.s-proposal-kind.is-human {
    background: var(--s-human-tint);
    color: var(--s-human-ink);
}

.s-proposal-kind.is-auto {
    background: var(--s-auto-tint);
    color: var(--s-auto-ink);
}

.s-proposal-kind.is-external {
    background: var(--s-external-tint);
    color: var(--s-external-ink);
}

.s-proposal-kind.is-late {
    background: var(--s-late-tint);
    color: var(--s-late-ink);
}

.s-proposal-kind.is-done {
    background: var(--s-done-tint);
    color: var(--s-done-ink);
}

.s-proposal-title {
    font-family: var(--s-font-display);
    font-weight: 700;
    font-size: 1rem;
    line-height: 1.3;
    color: var(--s-ink-900);
    min-width: 0;
    overflow-wrap: anywhere;
}

.s-proposal-why {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 8px;
    margin: 2px 0 0;
    max-width: 68ch;
    font-size: 0.8rem;
    line-height: 1.45;
    color: var(--s-ink-500);
}

/* Clamp the reason, never the figure. */
.s-proposal-why-text {
    flex: 1 1 16rem;
    min-width: 0;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
}

.s-proposal-confidence {
    flex: none;
    padding: 0 6px;
    border-radius: 4px;
    font-size: 0.72rem;
    line-height: 1.6;
    white-space: nowrap;
}

/* Settled reads as done; below the threshold the reader still has to confirm, which is the
   human meaning and not a warning colour of its own. */
.s-proposal-confidence.is-sure {
    background: var(--s-done-tint);
    color: var(--s-done-ink);
}

.s-proposal-confidence.is-unsure {
    background: var(--s-human-tint);
    color: var(--s-human-ink);
}

.s-proposal-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin-top: 4px;
}

.s-proposal-side {
    display: flex;
    flex: none;
    flex-direction: column;
    align-items: flex-end;
    justify-content: space-between;
    gap: 8px;
    min-height: 100%;
}

/* The resting expiry keeps the padding it will need once it is late, so the line does not shift
   under the reader at the moment it starts to matter. */
.s-proposal-due {
    padding: 0 6px;
    border-radius: 4px;
    font-size: 0.74rem;
    color: var(--s-muted);
    white-space: nowrap;
}

.s-proposal-due.is-late {
    background: var(--s-late-tint);
    color: var(--s-late-ink);
    font-weight: 600;
}

.s-proposal-primary {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: auto;
}

/* On a narrow lane the action column drops under the content instead of squeezing the title. */
@media (max-width: 599.98px) {
    .s-proposal {
        grid-template-columns: minmax(0, 1fr);
    }

    .s-proposal-side {
        flex-direction: row;
        flex-wrap: wrap;
        align-items: center;
        justify-content: flex-start;
    }

    .s-proposal-primary {
        margin-top: 0;
    }
}

/* ===== Page hero (s-page-hero) ===== */

.s-page-hero {
    background: linear-gradient(135deg, var(--s-brand-primary) 0%, var(--s-brand-primary-deep) 100%);
    border-radius: var(--s-r-hero);
    padding: 28px 32px;
    margin-bottom: 24px;
    position: relative;
    overflow: hidden;
}

.s-page-hero::before {
    content: '';
    position: absolute;
    right: -40px;
    top: -40px;
    width: 220px;
    height: 220px;
    border-radius: 50%;
    background: color-mix(in srgb, var(--s-secondary) 12%, transparent);
    pointer-events: none;
}

.s-page-hero::after {
    content: '';
    position: absolute;
    right: 80px;
    bottom: -60px;
    width: 160px;
    height: 160px;
    border-radius: 50%;
    background: color-mix(in srgb, var(--s-secondary) 7%, transparent);
    pointer-events: none;
}

/* STPF-13 V3: hero text reads --s-hero-text — derived to clear AA on both gradient ends, so a
   light brand primary gets ink text instead of unreadable white. The soft subtitle/label steps
   mix the same token. */
.s-page-hero-title {
    font-family: var(--s-font-display);
    font-weight: 800;
    font-size: 1.6rem;
    letter-spacing: 0.02em;
    color: var(--s-hero-text, var(--s-white));
    line-height: 1.2;
    margin: 0;
    position: relative;
    z-index: 1;
}

.s-page-hero-subtitle {
    font-size: 0.9rem;
    color: color-mix(in srgb, var(--s-hero-text, var(--s-white)) 70%, transparent);
    margin-top: 4px;
    margin-bottom: 0;
    position: relative;
    z-index: 1;
}

.s-page-hero-sm {
    padding: 18px 28px 20px;
}

.s-hero-stats {
    display: flex;
    gap: 12px;
    margin-top: 20px;
    flex-wrap: wrap;
    position: relative;
    z-index: 1;
}

.s-hero-stat {
    background: color-mix(in srgb, var(--s-white) 10%, transparent);
    border: 1px solid color-mix(in srgb, var(--s-white) 14%, transparent);
    border-radius: var(--s-r-tile);
    padding: 10px 18px;
    min-width: 80px;
    text-align: center;
}

.s-hero-stat-value {
    font-family: var(--s-font-display);
    font-weight: 800;
    font-size: 1.4rem;
    color: var(--s-hero-text, var(--s-white));
    line-height: 1.1;
}

.s-hero-stat-label {
    font-size: 0.70rem;
    color: color-mix(in srgb, var(--s-hero-text, var(--s-white)) 60%, transparent);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    margin-top: 3px;
}

.s-hero-actions {
    position: relative;
    z-index: 1;
}

/* ===== Page header (s-page-header) — the compact page-top row =====
 *
 * The successor form to the gradient hero: one 56 px row carrying back, trail, title, subtitle,
 * meta and actions. Ported from starruk-crm's Shell/PageHeader (the leading form; its .w-head),
 * with ocean-platform's back affordance. Wrapping is the base behaviour, not a media-query
 * afterthought: this row lives inside a content column that is narrower than the window, so a
 * grown action group must fold instead of scrolling the page sideways. min-height (not height)
 * keeps the familiar 56 px whenever everything still fits on one line.
 */

.s-page-header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 14px;
    min-height: 56px;
    padding: 8px 24px;
    background: var(--s-surface);
    border-bottom: 1px solid var(--s-line);
    min-width: 0;
}

.s-page-header-back {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 32px;
    height: 32px;
    border: 1px solid var(--s-line-strong);
    border-radius: var(--s-r-tile);
    background: transparent;
    color: var(--s-muted);
    text-decoration: none;
}

.s-page-header-back:hover {
    background: var(--s-fill-faint);
    color: var(--s-ink-900);
}

.s-page-header-trail {
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
    font-size: 0.78rem;
    color: var(--s-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.s-page-header-trail a {
    color: var(--s-muted);
    text-decoration: none;
}

.s-page-header-trail a:hover {
    color: var(--s-ink-900);
}

/* The separator is decoration between two crumbs (aria-hidden in the markup), so it sits on the
   line tone rather than the text tone. */
.s-page-header-trail-sep {
    color: var(--s-ink-300);
}

.s-page-header-title {
    font-family: var(--s-font-display);
    font-weight: 800;
    font-size: 1.05rem;
    line-height: 1.2;
    letter-spacing: 0.01em;
    margin: 0;
    min-width: 0;
    color: var(--s-ink-900);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.s-page-header-subtitle {
    min-width: 0;
    font-size: 0.78rem;
    color: var(--s-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.s-page-header-meta {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px 14px;
    min-width: 0;
    font-size: 0.78rem;
    color: var(--s-muted);
}

.s-page-header-actions {
    margin-left: auto;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 8px;
    flex: 0 1 auto;
    max-width: 100%;
    min-width: 0;
}

/* Tablet width: the trail and the subtitle are the first things that may go — the title and the
   actions are what the row is for. display:none, not visually-hidden, and that is the deliberate
   half of the choice: it takes the <nav> out of the accessibility tree too, so a screen-reader
   user on a narrow viewport loses the breadcrumb entirely, not just visually. The back link
   stays, so there is always a way up; a host that needs the trail announced at every width
   overrides this rule rather than the markup. Recorded in both READMEs. */
@media (max-width: 899.98px) {
    .s-page-header-trail,
    .s-page-header-subtitle {
        display: none;
    }
}

/* Below the sm breakpoint (600px, as elsewhere in this sheet) the actions take their own line. */
@media (max-width: 599.98px) {
    .s-page-header {
        padding: 10px 14px;
    }

    .s-page-header-actions {
        width: 100%;
        margin-left: 0;
        justify-content: flex-start;
    }
}

/* ===== Cards (s-card) ===== */

.s-card {
    background: var(--s-surface);
    border-radius: var(--s-r-card);
    border: 1px solid var(--s-line);
    box-shadow: var(--s-shadow-1);
    position: relative;
    overflow: hidden;
}

.s-card.s-card-padded {
    padding: 18px 20px;
}

.s-card-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 14px 18px 10px;
}

.s-card-head .s-card-title {
    font-family: var(--s-font-display);
    font-weight: 800;
    font-size: 0.92rem;
    letter-spacing: 0.02em;
    color: var(--s-ink-900);
}

.s-card-head .s-card-meta {
    font-size: 0.78rem;
    color: var(--s-muted);
}

.s-card-head .s-card-link {
    font-size: 0.78rem;
    /* STPF-13 AC-3 (B245): text-safe secondary token — the raw accent measures ~2.6:1 as text. */
    color: var(--s-secondary-text);
    cursor: pointer;
    font-weight: 500;
    text-decoration: none;
}

.s-kpi-card {
    background: var(--s-surface);
    border-radius: var(--s-r-card);
    border: 1px solid var(--s-line);
    box-shadow: var(--s-shadow-card);
    padding: 16px 18px;
    border-left: 3px solid var(--s-kpi-accent, var(--s-secondary));
    transition: box-shadow 0.15s;
}

.s-kpi-card:hover {
    box-shadow: var(--s-shadow-hover);
}

/* ===== Measured KPI (s-measured-kpi, STP-187) — a key figure with its definition, source and
   measurement moment. The empty period reuses s-empty-state, so it never takes the error colour. */

.s-measured-kpi {
    display: flex;
    flex-direction: column;
    gap: 8px;
    min-width: 0;
}

.s-measured-kpi-label {
    margin: 0;
    font-weight: 600;
    color: var(--s-ink-900);
}

.s-measured-kpi-meta {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
    font-size: 0.84rem;
    color: var(--s-muted);
}

.s-measured-kpi-meta p {
    margin: 0;
    overflow-wrap: anywhere;
}

.s-measured-kpi-meta a,
.s-measured-kpi-toggle {
    /* The text-safe secondary token: the raw accent measures ~2.6:1 as text. */
    color: var(--s-secondary-text);
}

.s-measured-kpi-toggle {
    appearance: none;
    background: none;
    border: 0;
    /* A 24px-high target (WCAG 2.5.8) without a visible box. */
    padding: 3px 0;
    font: inherit;
    text-decoration: underline;
    cursor: pointer;
}

.s-measured-kpi-definition {
    color: var(--s-ink-900);
}

/* ===== Dashboard (s-dashboard, STP-188) — a period chooser above a wrapping grid of measured key
   figures. The chooser reuses s-form-field; the grid collapses to one column on a phone. */

.s-dashboard {
    display: flex;
    flex-direction: column;
    gap: var(--s-space-control);
    min-width: 0;
}

.s-dashboard-period {
    max-width: 16rem;
}

.s-dashboard-widgets {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 16rem), 1fr));
    gap: var(--s-space-control);
    min-width: 0;
}

/* STP-189's personal layout. The toolbar puts the edit button beside the period chooser and the edit
   bar and hidden list sit above the widgets; every row wraps, so a phone never scrolls sideways. The
   toolbar selector is scoped to beat .s-inline's own align-items, which is defined further down. */

.s-dashboard .s-dashboard-toolbar {
    align-items: flex-end;
    justify-content: space-between;
}

.s-dashboard-edit {
    display: flex;
    flex-direction: column;
    gap: var(--s-space-inline);
    padding: var(--s-space-control);
    background: var(--s-fill-faint);
    border: 1px solid var(--s-line);
    border-radius: var(--s-r-card);
}

.s-dashboard-widget {
    display: flex;
    flex-direction: column;
    gap: var(--s-space-inline);
    min-width: 0;
}

.s-dashboard .s-dashboard-widget-controls {
    gap: 6px;
}

/* The edit mode's own controls. They are native buttons with no MudBlazor shape behind them, so
   without this they read as plain text and a reader cannot see what can be pressed — the same reason
   the account activator gets its own reset here instead of in a style attribute (handbook rule U18:
   an inline style wins from every class and is unreachable for the theme). Scoped to the dashboard's
   three edit groups, so no other bare button in a host changes. */
.s-dashboard :is(.s-dashboard-edit, .s-dashboard-widget-controls, .s-dashboard-hidden) button {
    padding: 4px 10px;
    border: 1px solid var(--s-line-strong);
    border-radius: var(--s-r-card);
    background: var(--s-surface);
    color: var(--s-ink-900);
    font: inherit;
    cursor: pointer;
}

.s-dashboard :is(.s-dashboard-edit, .s-dashboard-widget-controls, .s-dashboard-hidden) button:hover:not(:disabled) {
    background: var(--s-fill-faint);
}

.s-dashboard :is(.s-dashboard-edit, .s-dashboard-widget-controls, .s-dashboard-hidden) button:disabled {
    color: var(--s-muted);
    cursor: not-allowed;
}

.s-dashboard-hidden {
    display: flex;
    flex-direction: column;
    gap: var(--s-space-inline);
    padding: var(--s-space-control);
    border: 1px dashed var(--s-line-strong);
    border-radius: var(--s-r-card);
}

.s-dashboard-hidden-title {
    margin: 0;
    font-family: var(--s-font-display);
    font-weight: 800;
    color: var(--s-ink-900);
}

.s-dashboard-hidden ul {
    display: flex;
    flex-direction: column;
    gap: var(--s-space-inline);
    margin: 0;
    padding: 0;
    list-style: none;
}

.s-dashboard-hidden li {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--s-space-inline);
    min-width: 0;
    overflow-wrap: anywhere;
}

/* ===== Dashboard charts (s-chart, ENT-26.02b) — the line and bar widget of a composed dashboard.
   The markup names no colour: every mark takes a brand token here, so a chart follows the
   customer's palette and the dark theme. The marks use the text-safe secondary, which also clears
   the 3:1 a graphical object needs against the surface (WCAG 1.4.11). */

.s-chart {
    display: block;
    width: 100%;
    height: auto;
}

.s-chart-baseline {
    stroke: var(--s-line-strong);
    stroke-width: 1;
}

.s-chart-bar {
    fill: var(--s-secondary-text);
}

.s-chart-line {
    fill: none;
    stroke: var(--s-secondary-text);
    stroke-width: 2;
}

.s-chart-point {
    fill: var(--s-secondary-text);
}

.s-chart-axis {
    display: flex;
    justify-content: space-between;
    gap: var(--s-space-inline);
    color: var(--s-muted);
}

/* ===== Filter bar (s-filter-bar) ===== */

.s-filter-bar {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    align-items: center;
}

.s-filter-bar > * {
    flex: 0 0 auto;
    margin: 0;
}

.s-filter-bar .s-filter-grow {
    flex: 1 1 240px;
    min-width: 220px;
}

.s-filter-bar .s-filter-date {
    flex: 0 1 180px;
    min-width: 140px;
}

.s-filter-bar-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
    margin-top: 8px;
}

.s-filter-notice {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 14px;
    border-radius: var(--s-r-card);
    background: var(--s-tint-warning);
    border: 1px solid color-mix(in srgb, var(--s-tertiary) 22%, transparent);
    color: var(--s-ink-900);
    font-size: 0.86rem;
    margin: 12px 0;
}

.s-filter-notice .s-filter-notice-icon {
    /* STPF-13 AC-3 (B248): Tertiary is an accent surface color, never a text/icon color
       (#ffba00 on white measures ~1.7:1) — the warning-text token carries the amber cue. */
    color: var(--s-warning-text);
    display: inline-flex;
}

/* STPF-13 AC-3 (B248): filter chips sit on the info tint with regular ink instead of the raw
   Info accent (~3.2:1 as text). (0,2,0) specificity out-ranks MudBlazor's chip color classes. */
.mud-chip.s-filter-chip {
    background: var(--s-tint-info);
    color: var(--s-ink-900);
}

.mud-chip.s-filter-chip .mud-icon-root {
    color: inherit;
}

/* ===== Text utilities (STPF-13 AC-3) — the token-contract replacements for MudBlazor
   Color.Secondary/Info/Warning text. They follow [data-theme="dark"] automatically because
   the underlying tokens do. ===== */

.s-text-secondary {
    color: var(--s-secondary-text);
}

.s-battery-value {
    color: var(--s-ink-900);
}

.s-filter-notice .s-filter-notice-spacer {
    flex: 1;
}

/* ===== States: loading / empty / error (s-*-state) ===== */

.s-empty-state,
.s-loading-state,
.s-error-state {
    padding: 28px 18px;
    text-align: center;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
}

.s-state-mark {
    width: 44px;
    height: 44px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 4px;
}

.s-empty-state .s-state-mark {
    background: var(--s-tint-success);
    color: var(--s-success);
}

.s-loading-state .s-state-mark {
    background: var(--s-tint-info);
    color: var(--s-info);
}

.s-error-state .s-state-mark {
    background: color-mix(in srgb, var(--s-error) 10%, transparent);
    color: var(--s-error);
}

.s-empty-state h4,
.s-loading-state h4,
.s-error-state h4 {
    font-size: 0.95rem;
    margin: 0;
    font-family: var(--s-font-display);
    font-weight: 800;
    color: var(--s-ink-900);
}

.s-empty-state p,
.s-loading-state p,
.s-error-state p {
    color: var(--s-muted);
    font-size: 0.84rem;
    margin: 0;
    max-width: 32ch;
}

/* ===== Tables (s-table / s-data-grid) ===== */

.s-data-grid.s-card {
    padding: 0;
    overflow: hidden;
}

.s-data-grid .mud-table-container {
    background: transparent;
}

.s-data-grid .mud-table-cell {
    border-bottom-color: var(--s-line);
    font-size: 0.86rem;
}

.s-data-grid .mud-table-head .mud-table-cell {
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-size: 0.7rem;
    color: var(--s-muted);
    font-weight: 600;
    background: var(--s-fill-faint);
}

.s-data-grid .mud-table-row:hover {
    background: color-mix(in srgb, var(--s-secondary) 4%, transparent) !important;
}

/* MudBlazor hides an unsorted column's sort button until hover. Keyboard users must
   see the focused action before sorting, including its token-driven focus ring. */
.s-data-grid .mud-table-head .mud-table-cell .sort-direction-icon:focus-visible {
    opacity: 1;
    outline: 2px solid var(--s-focus-ring, var(--s-primary));
    outline-offset: 2px;
}

/* Keep sort controls available above MudBlazor's stacked mobile rows. Lists without
   sort controls retain the existing responsive presentation. */
@media (max-width: 599.98px) {
    .s-data-grid .mud-xs-table .mud-table-root .mud-table-head .mud-table-row:has(.sort-direction-icon) {
        display: flex;
        flex-wrap: wrap;
    }

    .s-data-grid .mud-xs-table .mud-table-head .mud-table-cell:has(.sort-direction-icon) {
        flex: 1 1 10rem;
        min-width: 0;
    }

    .s-data-grid .mud-xs-table .mud-table-head .column-header:has(.sort-direction-icon) {
        flex: 1;
        min-width: 0;
        flex-wrap: wrap;
        overflow-wrap: anywhere;
    }
}

/* ===== List items (s-list-item) — used inside s-card as an alternative to s-data-grid ===== */

.s-list-item {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    padding: 14px 18px;
    border-top: 1px solid var(--s-line);
    cursor: pointer;
    transition: background 0.12s;
    text-decoration: none;
    color: inherit;
}

.s-list-item:hover {
    background: color-mix(in srgb, var(--s-secondary) 4%, transparent);
}

.s-list-item:first-child {
    border-top: 0;
}

.s-list-avatar {
    width: 32px;
    height: 32px;
    border-radius: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--s-fill);
    color: var(--s-ink-900);
    font-weight: 600;
    font-size: 0.78rem;
    flex-shrink: 0;
}

.s-list-title {
    font-size: 0.9rem;
    font-weight: 500;
    color: var(--s-ink-900);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.s-list-sub {
    font-size: 0.78rem;
    color: var(--s-muted);
    margin-top: 2px;
}

/* ===== Accessibility layer (STPF-13 AC-4) ===== */

/* Keyboard focus for everything interactive the kit renders — :focus-visible only, so pointer
   users keep a clean outline-free surface. Token-driven: no hardcoded hex (AC-1.1 rule for this
   layer). .s-tooltip-anchor is the focusable wrapper the kit's tooltip hosts render (TermHint,
   BatteryIndicator, StatusChip) so their hints reach keyboard users too; .s-kpi-card is the
   clickable card root, .s-msf-trigger the multi-select trigger, .s-signal-strength the signal
   meter's focusable div; .s-focus-target is the shared scope class on the kit's own bare
   MudButtons that render no other scoped class — ConfirmDialog's Cancel/Confirm,
   FilterBar's "Clear all", MultiSelectFilter's popover "Clear" and OnboardingChecklist's "Go".
   STPF-13 R4: scoped to the kit's own classes and outline-only — the earlier form also ringed
   every MudBlazor button shape, link and ARIA role app-wide and rewrote border-radius, reshaping
   elements this kit never rendered. Hosts that want the app-wide ring add the opt-in class
   below. The outline follows the element's own border-radius (CSS UI level 4), so no shape
   property is set here. */
.s-card-link:focus-visible,
.s-page-header-back:focus-visible,
.s-filter-chip:focus-visible,
.s-tooltip-anchor:focus-visible,
.s-kpi-card:focus-visible,
.s-msf-trigger:focus-visible,
.s-signal-strength:focus-visible,
.s-focus-target:focus-visible {
    outline: 2px solid var(--s-focus-ring, var(--s-primary));
    outline-offset: 2px;
}

/* A MudCheckBox focuses its hidden input, so a ring on the component's own class never shows.
   Scoped to the kit's own containers (EntityDataGrid's column chooser, ShortcutPreferencesPanel),
   like the shell's navigation rings: not an app-wide checkbox ring. */
.s-data-grid-columns .mud-checkbox .mud-icon-button:has(input:focus-visible),
.s-shortcut-preferences .mud-checkbox .mud-icon-button:has(input:focus-visible) {
    outline: 2px solid var(--s-focus-ring, var(--s-primary));
    outline-offset: 2px;
}

/* STPF-13 R4 opt-in: the same token-driven ring app-wide. Add s-app-focus-rings to any ancestor
   (e.g. <body class="s-app-focus-rings">) to cover the MudBlazor interactive classes, links and
   the ARIA roles non-native interactive elements fall back to. Outline-only by the same rule —
   no border, box-shadow or border-radius is touched, element shapes stay the host's own. */
.s-app-focus-rings .mud-button:focus-visible,
.s-app-focus-rings .mud-icon-button:focus-visible,
.s-app-focus-rings .mud-chip[tabindex]:focus-visible,
.s-app-focus-rings a:focus-visible,
.s-app-focus-rings [role="button"]:focus-visible,
.s-app-focus-rings [role="link"]:focus-visible {
    outline: 2px solid var(--s-focus-ring, var(--s-primary));
    outline-offset: 2px;
}

.s-tooltip-anchor {
    display: inline-flex;
    outline-offset: 2px;
}

/* Visually hidden, screen-reader-available text (STPF-13 V6) — the accessible-name mechanism
   for the kit's role-less (generic) tooltip anchors, where ARIA 1.2 disallows aria-label. */
.s-visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    border: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
}

/* Honor prefers-reduced-motion: the kit's only motion is the card hover shadow and the list
   item hover tint — both turn off without losing their state colors. */
@media (prefers-reduced-motion: reduce) {
    .s-kpi-card,
    .s-list-item,
    .s-card-link,
    .s-tooltip-anchor,
    .s-filter-chip {
        transition: none;
    }
}

/* ===== Responsive layer (TRAP-53) — shared layouts collapse below the sm breakpoint (600px).
   Class-name contract as above: EntityListLayout renders s-list-toolbar, EntityDetailLayout
   renders s-detail-header/s-detail-actions on its own root elements; this package owns the
   media queries that react to them. ===== */

@media (max-width: 599.98px) {
    .s-list-toolbar {
        flex-direction: column;
        align-items: stretch;
    }

    /* MudStack's row/alignment utilities are !important. Without matching those utilities,
       full-width toolbar children remain side by side and widen the entire mobile page. */
    .s-list-toolbar.flex-row { flex-direction: column !important; }
    .s-list-toolbar.align-center { align-items: stretch !important; }

    .s-list-toolbar > * {
        width: 100%;
        flex: none;
    }

    .s-list-toolbar .mud-spacer {
        display: none;
    }

    .s-detail-header {
        flex-wrap: wrap;
        gap: 12px;
    }

    .s-detail-header .s-detail-actions {
        flex-wrap: wrap;
        width: 100%;
    }

    .s-detail-header .s-detail-actions > * {
        flex: 1 1 auto;
    }

    .s-filter-bar {
        flex-direction: column;
        align-items: stretch;
    }

    .s-filter-bar > *,
    .s-filter-bar .s-filter-grow,
    .s-filter-bar .s-filter-date {
        width: 100%;
        flex: none;
    }

    .s-filter-bar .s-filter-date {
        max-width: none;
    }
}

/* ===== App shell (s-app-shell) — the chrome StarrukAppShell renders =====

   Class-name contract with Starruk.Platform.Blazor.Components, as above: that package owns the
   markup, this one owns the paint. Every colour reads a --s-* token (handbook rule U1: a colour
   literal outside the token blocks bypasses the theme, and rule 7 of the rule library now fails
   on one). Inside the app bar the ink is currentColor rather than --s-focus-ring: the focus ring
   token is derived from the brand primary, which is also what paints the app bar, so a ring in
   that colour would be invisible exactly where it is needed (handbook rule U4 in spirit — the
   measurement is over the plate the mark actually sits on).
   ===== */

/* The root is a scope hook, not a box: display:contents keeps MudLayout's own flex/fixed
   geometry intact instead of introducing a containing block between it and <body>. */
.s-app-shell {
    display: contents;
}

/* Skip link — off-screen until it takes focus, then the first stop of the tab order. */
.s-app-shell-skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 1200;
    padding: 8px 16px;
    border-radius: var(--s-r-tile);
    background: var(--s-surface);
    color: var(--s-ink-900);
    font-family: var(--s-font-body);
    text-decoration: none;
}

.s-app-shell-skip-link:focus {
    left: 8px;
    top: 8px;
}

/* Brand block — logo plus wordmark, on the app bar's own ink. */
.s-app-shell-brand {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 10px;
    min-width: 0;
    margin-left: 8px;
    color: var(--s-appbar-text);
    text-decoration: none;
}

.s-app-shell-brand-logo {
    display: block;
    height: 32px;
    width: auto;
    max-width: 160px;
    object-fit: contain;
}

.s-app-shell-brand-text {
    display: flex;
    flex-direction: column;
    gap: 3px;
    min-width: 0;
    line-height: 1;
}

/* Handbook rule U16 — three voices, not seven sizes within half a step: the product name and the
   vendor line differ in size, weight and tracking, so the hierarchy survives without a second
   colour. */
.s-app-shell-brand-title {
    font-family: var(--s-font-display);
    font-size: 1.05rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    line-height: 1;
    text-transform: uppercase;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.s-app-shell-brand-vendor {
    font-family: var(--s-font-display);
    font-size: 0.62rem;
    font-weight: 400;
    letter-spacing: 0.16em;
    line-height: 1;
    text-transform: uppercase;
    color: color-mix(in srgb, var(--s-appbar-text) 72%, transparent);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* The sidebar. A menu with every work area in it outgrows a laptop window, and a drawer without
   this scrolls its last items out of reach with nothing to reveal that there were more — the
   lesson CRM and Ocean both landed on. */
.s-app-shell-drawer {
    overflow-y: auto;
}

.s-app-shell-nav {
    display: block;
}

/* Keyboard focus on the shell's own navigation (STP-27 AC-6, measured on a generated host).
   MudBlazor marks a focused nav link with the same background tint it gives the active route, so
   in a screenshot "where I am" and "where I would go" are the same picture and a keyboard user
   cannot tell them apart. An outline is a second channel: it never collides with that tint, and
   it reads the same --s-focus-ring token the kit's own controls already use. The offset is inset
   because a drawer row is full-bleed — a positive offset would be clipped by the drawer's own
   overflow. Scoped to .s-app-shell-nav on purpose: this is the shell's own sidebar, not every
   MudNavLink a host happens to render elsewhere. */
.s-app-shell-nav .mud-nav-link:focus-visible {
    outline: 2px solid var(--s-focus-ring, var(--s-primary));
    outline-offset: -2px;
}

/* The app bar's two own icon buttons. They sit on --s-appbar-background, where the accent ring
   can drop below the contrast the bar's ink already carries, so they ring in that ink instead —
   exactly like the account activator beside them. */
.s-app-shell-toggle:focus-visible,
.s-app-shell-theme:focus-visible {
    outline: 2px solid var(--s-appbar-text);
    outline-offset: 2px;
}

/* The account-menu activator. A native button, reset to look like the icon buttons beside it —
   the reset lives here and not in a style attribute (handbook rule U18: an inline style wins from
   every class and is unreachable for the theme). */
.s-app-shell-user {
    display: inline-flex;
    align-items: center;
    margin-left: 8px;
    padding: 2px;
    border: 0;
    border-radius: 999px;
    background: transparent;
    color: inherit;
    font: inherit;
    cursor: pointer;
}

.s-app-shell-user:focus-visible,
.s-app-shell-signin:focus-visible {
    outline: 2px solid currentColor;
    outline-offset: 2px;
}

.s-app-shell-user-head {
    padding: 12px 16px 8px;
}

/* MudMainContent's own top padding already clears the fixed app bar; this only adds the page's
   own breathing room below the fold. */
.s-app-shell-content {
    padding-bottom: 24px;
}

/* Shared page spacing. Hosts configure StarrukDesignSystem:Spacing, not page-local numbers. */
/* Both names select the same block-flow rule; a legacy wrapper carrying both receives one margin.
   The shell can contain an existing page wrapper: its single child adds no second page stack. */
:is(.s-page, .oc-page) { min-width: 0; overflow-wrap: anywhere; }
:is(.s-page, .oc-page) > * + * { margin-block-start: var(--s-page-stack); }
/* MudGrid balances each item's top padding with a negative container margin. Keep that balance
   when a grid is a page block, so its first visible row starts one page stack below the header. */
:is(.s-page, .oc-page) > .mud-grid:not(:first-child) {
    margin-block-start: calc(var(--s-page-stack) - var(--s-grid-gutter, 24px));
}
.mud-grid-spacing-xs-0 { --s-grid-gutter: 0px; }
.mud-grid-spacing-xs-1 { --s-grid-gutter: 4px; }
.mud-grid-spacing-xs-2 { --s-grid-gutter: 8px; }
.mud-grid-spacing-xs-3 { --s-grid-gutter: 12px; }
.mud-grid-spacing-xs-4 { --s-grid-gutter: 16px; }
.mud-grid-spacing-xs-5 { --s-grid-gutter: 20px; }
.mud-grid-spacing-xs-6 { --s-grid-gutter: 24px; }
.mud-grid-spacing-xs-7 { --s-grid-gutter: 28px; }
.mud-grid-spacing-xs-8 { --s-grid-gutter: 32px; }
.mud-grid-spacing-xs-9 { --s-grid-gutter: 36px; }
.mud-grid-spacing-xs-10 { --s-grid-gutter: 40px; }
.mud-grid-spacing-xs-11 { --s-grid-gutter: 44px; }
.mud-grid-spacing-xs-12 { --s-grid-gutter: 48px; }
.mud-grid-spacing-xs-13 { --s-grid-gutter: 52px; }
.mud-grid-spacing-xs-14 { --s-grid-gutter: 56px; }
.mud-grid-spacing-xs-15 { --s-grid-gutter: 60px; }
.mud-grid-spacing-xs-16 { --s-grid-gutter: 64px; }
.mud-grid-spacing-xs-17 { --s-grid-gutter: 68px; }
.mud-grid-spacing-xs-18 { --s-grid-gutter: 72px; }
.mud-grid-spacing-xs-19 { --s-grid-gutter: 76px; }
.mud-grid-spacing-xs-20 { --s-grid-gutter: 80px; }
.s-page-link:focus-visible { outline: 2px solid currentColor; outline-offset: -2px; }
.s-table-scroll { min-width: 0; max-width: 100%; overflow-x: auto; }
.s-table { width: 100%; border-collapse: collapse; }
.s-table th, .s-table td { padding: var(--s-space-control); text-align: start; border-bottom: 1px solid var(--s-line); }
.s-detail-header { min-width: 0; flex-wrap: wrap; gap: var(--s-space-control); }
.s-detail-header h1 { min-width: 0; overflow-wrap: anywhere; }
.s-detail-actions { flex-wrap: wrap; }
.s-stack { display: flex; flex-direction: column; gap: var(--s-page-stack); }
.s-inline { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-space-inline); }
.s-section { padding: var(--s-space-section); }

/* Native extension fields share the token palette and remain usable in narrow forms. */
.s-form-field {
    display: flex;
    flex-direction: column;
    gap: var(--s-space-control);
    min-width: 0;
    margin-block-end: var(--s-space-control);
    overflow-wrap: anywhere;
}

/* Extra-field help text: plain text under the label, described by the input. */
.s-form-help {
    margin: 0;
    color: var(--s-muted);
    font-size: 0.875em;
}

/* Server messages that belong to no rendered field, announced once above the fields. */
.s-form-summary {
    padding: var(--s-space-control);
    margin-block-end: var(--s-space-control);
    color: var(--s-error-text);
    border: 1px solid var(--s-error);
    overflow-wrap: anywhere;
}

/* Keep the initialled avatar shape on MudMenu's native, keyboard-aware activator. */
.s-app-shell-user .mud-menu-button-activator {
    min-width: 32px;
    width: 32px;
    height: 32px;
    padding: 0;
    border-radius: 999px;
}

.s-app-shell-user .mud-menu-button-activator:focus-visible {
    outline: 2px solid var(--s-appbar-text);
    outline-offset: 2px;
}
.s-form-field > input,
.s-form-field > select {
    box-sizing: border-box;
    width: 100%;
    min-width: 0;
    max-width: 100%;
    padding: var(--s-space-control);
    font: inherit;
    color: var(--s-ink-900);
    background: var(--s-surface);
    border: 1px solid var(--s-line);
}
.s-form-field > input:focus-visible,
.s-form-field > select:focus-visible {
    outline: 2px solid var(--s-ink-900);
    outline-offset: 2px;
}
