/* Spec-043 shared UI primitives. Namespaced to avoid changing legacy pages before T028. */
@layer spec043.components {
    .ds-visually-hidden {
        position: absolute;
        inline-size: 1px;
        block-size: 1px;
        padding: 0;
        margin: -1px;
        overflow: hidden;
        clip: rect(0, 0, 0, 0);
        white-space: nowrap;
        border: 0;
    }

    .ds-button,
    .ds-icon-button {
        position: relative;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        min-block-size: var(--control-min-block-size);
        border: 1px solid transparent;
        border-radius: var(--radius-control);
        font: inherit;
        font-weight: var(--weight-semibold);
        cursor: pointer;
        transition: background-color var(--motion-duration-feedback) var(--motion-ease-standard),
                    border-color var(--motion-duration-feedback) var(--motion-ease-standard),
                    color var(--motion-duration-feedback) var(--motion-ease-standard);
    }

    .ds-button {
        gap: var(--space-8);
        padding-block: var(--space-8);
        padding-inline: var(--space-16);
    }

    .ds-button--primary { background: var(--color-primary); color: var(--color-on-primary); }
    .ds-button--primary:hover { background: var(--color-primary-hover); }
    .ds-button--secondary {
        background: var(--color-surface);
        color: var(--color-primary);
        border-color: var(--color-primary);
    }
    .ds-button--ghost { background: transparent; color: var(--color-text); }
    .ds-button--danger { background: var(--color-danger); color: var(--color-on-primary); }
    .ds-button:disabled,
    .ds-icon-button:disabled { opacity: 0.55; cursor: not-allowed; }
    .ds-button__content { display: inline-flex; align-items: center; gap: var(--space-8); }
    .ds-button__content--hidden { visibility: hidden; }
    .ds-button__pending {
        position: absolute;
        inset: 0;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        gap: var(--space-8);
    }
    .ds-button__icon { display: inline-flex; }

    /* design-system.md: "icon buttons 40px visual box with >=44px hit area". The element is the
       44px hit area the accessibility scan measures; `background-clip: content-box` keeps the
       painted chrome inside the 40px content box, so the two sizes cannot drift apart. */
    .ds-icon-button {
        inline-size: var(--touch-target-min);
        block-size: var(--touch-target-min);
        padding: calc((var(--touch-target-min) - var(--icon-control-size)) / 2);
        background: transparent;
        background-clip: content-box;
        color: var(--color-text);
    }
    .ds-icon-button--primary { background: var(--color-primary); color: var(--color-on-primary); }
    .ds-icon-button--ghost:hover { background: var(--color-subtle); }
    .ds-icon-button--danger { color: var(--color-danger); }

    .ds-field { display: grid; gap: var(--space-6); }
    .ds-field__label {
        color: var(--color-text);
        font-size: var(--type-sm);
        line-height: var(--leading-sm);
        font-weight: var(--weight-semibold);
    }
    .ds-field__optional,
    .ds-field__help { color: var(--color-muted); font-weight: var(--weight-regular); }
    .ds-field__help,
    .ds-field__error { font-size: var(--type-sm); line-height: var(--leading-sm); }
    .ds-field__error { color: var(--color-danger); font-weight: var(--weight-semibold); }
    .ds-input,
    .ds-select {
        inline-size: 100%;
        min-block-size: var(--control-min-block-size);
        padding-block: var(--space-8);
        padding-inline: var(--space-12);
        border: 1px solid var(--color-border-strong);
        border-radius: var(--radius-control);
        background: var(--color-surface);
        color: var(--color-text);
        font: inherit;
    }
    .ds-input[aria-invalid='true'],
    .ds-select[aria-invalid='true'] { border-color: var(--color-danger); }
    .ds-input:disabled,
    .ds-select:disabled { background: var(--color-subtle); color: var(--color-muted); cursor: not-allowed; }

    .ds-badge {
        display: inline-flex;
        align-items: center;
        gap: var(--space-4);
        padding-block: var(--space-2);
        padding-inline: var(--space-8);
        border-radius: var(--radius-pill);
        font-size: var(--type-xs);
        line-height: var(--leading-xs);
        font-weight: var(--weight-semibold);
    }
    .ds-badge--neutral { background: var(--color-subtle); color: var(--color-text); }
    .ds-badge--info { background: var(--color-info-subtle); color: var(--color-info); }
    .ds-badge--success { background: var(--color-success-subtle); color: var(--color-success-ink); }
    .ds-badge--warning { background: var(--color-warning-subtle); color: var(--color-warning-ink); }
    .ds-badge--danger { background: var(--color-danger-subtle); color: var(--color-danger-ink); }
    .ds-badge__icon { display: inline-flex; }

    .ds-banner {
        display: flex;
        align-items: flex-start;
        gap: var(--space-12);
        padding: var(--space-12) var(--space-16);
        border: 1px solid var(--color-border);
        border-radius: var(--radius-card);
        color: var(--color-text);
    }
    .ds-banner--info { background: var(--color-info-subtle); border-color: var(--color-info-border); }
    .ds-banner--success { background: var(--color-success-subtle); border-color: var(--color-success-border); }
    .ds-banner--warning { background: var(--color-warning-subtle); border-color: var(--color-warning-border); }
    .ds-banner--danger { background: var(--color-danger-subtle); border-color: var(--color-danger-border); }
    .ds-banner__icon { display: inline-flex; margin-block-start: var(--space-2); }
    .ds-banner__body { flex: 1; min-inline-size: 0; }
    .ds-banner__title { display: block; margin-block-end: var(--space-4); }
    .ds-banner__action { flex: none; }

    .ds-toast {
        position: fixed;
        inset-block-end: calc(var(--space-16) + env(safe-area-inset-bottom));
        inset-inline: var(--space-16);
        z-index: var(--z-toast);
        display: flex;
        align-items: center;
        gap: var(--space-8);
        max-inline-size: min(32rem, calc(100vw - var(--space-32)));
        margin-inline: auto;
        padding-block: var(--space-12);
        padding-inline: var(--space-16);
        border: 1px solid var(--color-border);
        border-radius: var(--radius-pill);
        background: var(--color-surface);
        color: var(--color-text);
        box-shadow: var(--elevation-overlay);
        pointer-events: none;
        overflow-wrap: anywhere;
    }
    .ds-toast--success {
        background: var(--color-success-subtle);
        border-color: var(--color-success-border);
        color: var(--color-success-ink);
    }
    .ds-toast--danger {
        background: var(--color-danger-subtle);
        border-color: var(--color-danger-border);
        color: var(--color-danger-ink);
    }
    .ds-toast__icon {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        flex: none;
        inline-size: var(--space-24);
        block-size: var(--space-24);
        border-radius: var(--radius-pill);
        background: var(--color-surface);
        font-weight: var(--weight-bold);
    }
    .ds-toast__message { min-inline-size: 0; }

    .ds-empty {
        display: grid;
        justify-items: center;
        gap: var(--space-8);
        padding: var(--space-32) var(--space-16);
        text-align: center;
        color: var(--color-muted);
    }
    .ds-empty__illustration { font-size: var(--type-2xl); }
    .ds-empty__title { margin: 0; color: var(--color-text); font-size: var(--type-lg); line-height: var(--leading-lg); }
    .ds-empty__description { max-inline-size: 42rem; margin: 0; }
    .ds-empty__action { margin-block-start: var(--space-8); }

    .ds-spinner-wrap { display: inline-flex; align-items: center; gap: var(--space-8); }
    .ds-spinner {
        display: inline-block;
        inline-size: 1.625rem;
        block-size: 1.625rem;
        border: 3px solid var(--color-border);
        border-block-start-color: var(--color-primary);
        border-radius: 50%;
        animation: ds-spin var(--motion-duration-spinner) linear infinite;
    }
    .ds-spinner--inline { inline-size: 1rem; block-size: 1rem; border-width: 2px; }
    @keyframes ds-spin { to { transform: rotate(360deg); } }

    .ds-skeleton-group { display: grid; gap: var(--space-8); }
    .ds-skeleton {
        position: relative;
        overflow: hidden;
        min-block-size: 3rem;
        border-radius: var(--radius-control);
        background: var(--color-subtle);
    }
    .ds-skeleton--text { min-block-size: var(--leading-md); }
    .ds-skeleton--card { min-block-size: 8rem; border-radius: var(--radius-card); }
    .ds-skeleton::after {
        content: '';
        position: absolute;
        inset: 0;
        transform: translateX(-100%);
        background: linear-gradient(90deg, transparent, var(--color-skeleton-shimmer), transparent);
        animation: ds-shimmer var(--motion-duration-skeleton) infinite;
    }
    @keyframes ds-shimmer { to { transform: translateX(100%); } }

    @media (prefers-reduced-motion: reduce) {
        .ds-button,
        .ds-icon-button { transition: none; }
        .ds-spinner { animation-duration: var(--motion-duration-spinner-reduced); }
        .ds-skeleton::after { animation: none; }
    }

    /* The same reductions, forced by scope rather than by the OS setting, so an internal state
       gallery and a visual baseline can capture reduced motion deterministically. */
    .ds-motion--reduced .ds-button,
    .ds-motion--reduced .ds-icon-button { transition: none; }
    .ds-motion--reduced .ds-spinner { animation-duration: var(--motion-duration-spinner-reduced); }
    .ds-motion--reduced .ds-skeleton::after { animation: none; }
}
@layer spec043.components {
    .ds-overlay {
        position: fixed;
        inset: 0;
        z-index: var(--z-modal);
        display: grid;
        pointer-events: none;
    }
    .ds-overlay__backdrop {
        position: absolute;
        inset: 0;
        background: var(--color-scrim);
        pointer-events: auto;
    }
    .ds-dialog,
    .ds-drawer,
    .ds-bottom-sheet {
        position: relative;
        z-index: 1;
        display: flex;
        flex-direction: column;
        max-block-size: calc(100vh - var(--space-32));
        background: var(--color-surface);
        color: var(--color-text);
        box-shadow: var(--elevation-overlay);
        pointer-events: auto;
        outline: none;
    }
    .ds-dialog {
        align-self: center;
        justify-self: center;
        inline-size: min(36rem, calc(100vw - var(--space-32)));
        border-radius: var(--radius-card);
    }
    .ds-drawer {
        block-size: 100%;
        inline-size: min(24rem, 92vw);
    }
    .ds-drawer--inline-start { justify-self: start; }
    .ds-drawer--inline-end { justify-self: end; }
    .ds-bottom-sheet {
        align-self: end;
        inline-size: 100%;
        max-block-size: min(90vh, 48rem);
        border-radius: var(--radius-prominent) var(--radius-prominent) 0 0;
        padding-block-end: env(safe-area-inset-bottom);
    }
    .ds-bottom-sheet__grabber {
        inline-size: var(--space-40);
        block-size: var(--space-4);
        margin: var(--space-8) auto 0;
        border-radius: var(--radius-pill);
        background: var(--color-border-strong);
    }
    .ds-overlay__header,
    .ds-overlay__footer {
        display: flex;
        align-items: center;
        gap: var(--space-12);
        padding: var(--space-16);
    }
    .ds-overlay__header { justify-content: space-between; border-block-end: 1px solid var(--color-border); }
    .ds-overlay__footer { justify-content: flex-end; border-block-start: 1px solid var(--color-border); }
    .ds-overlay__title { margin: 0; font-size: var(--type-lg); line-height: var(--leading-lg); }
    .ds-overlay__description { margin: var(--space-4) 0 0; color: var(--color-muted); }
    .ds-overlay__body { min-block-size: 0; overflow: auto; padding: var(--space-16); }

    @media (min-width: 37.5rem) {
        .ds-bottom-sheet {
            inline-size: min(42rem, calc(100vw - var(--space-48)));
            margin-inline: auto;
        }
    }

    @media (prefers-reduced-motion: no-preference) {
        .ds-dialog { animation: ds-overlay-enter var(--motion-duration-standard) var(--motion-ease-standard); }
        .ds-drawer { animation: ds-overlay-enter var(--motion-duration-drawer) var(--motion-ease-standard); }
        .ds-bottom-sheet { animation: ds-sheet-enter var(--motion-duration-drawer) var(--motion-ease-standard); }
    }
    @keyframes ds-overlay-enter { from { opacity: 0; transform: scale(.98); } }
    @keyframes ds-sheet-enter { from { transform: translateY(100%); } }
}
@layer spec043.components {
    .ds-branch-context { display: grid; gap: var(--space-6); min-inline-size: 0; }
    .ds-branch-context__label { color: var(--color-muted); font-size: var(--type-xs); line-height: var(--leading-xs); }
    .ds-branch-context__single { min-block-size: var(--control-min-block-size); display: flex; align-items: center; }

    .ds-scope-summary,
    .ds-effective,
    .ds-impact {
        border: 1px solid var(--color-border);
        border-radius: var(--radius-card);
        background: var(--color-surface);
        padding: var(--space-16);
    }
    .ds-scope-summary__title,
    .ds-impact__header h2 { margin: 0 0 var(--space-12); font-size: var(--type-lg); line-height: var(--leading-lg); }
    .ds-scope-summary__list,
    .ds-effective__values,
    .ds-impact__diffs { display: grid; gap: var(--space-8); margin: 0; }
    .ds-scope-summary__list > div,
    .ds-effective__values > div,
    .ds-impact__diffs > div {
        display: grid;
        grid-template-columns: minmax(8rem, 1fr) minmax(0, 2fr);
        gap: var(--space-12);
        align-items: baseline;
    }
    .ds-scope-summary dt,
    .ds-effective dt,
    .ds-impact dt { color: var(--color-muted); font-size: var(--type-sm); }
    .ds-scope-summary dd,
    .ds-effective dd,
    .ds-impact dd { margin: 0; min-inline-size: 0; }
    .ds-scope-summary__capabilities { margin: var(--space-12) 0 0; color: var(--color-muted); }

    .ds-effective { display: grid; gap: var(--space-12); }
    .ds-effective__header { display: flex; align-items: center; justify-content: space-between; gap: var(--space-12); }
    .ds-effective__result { padding-block-start: var(--space-8); border-block-start: 1px solid var(--color-border); }
    .ds-effective__action { justify-self: end; }

    .ds-impact { display: grid; gap: var(--space-16); }
    .ds-impact__header p { margin: 0; color: var(--color-muted); }
    .ds-impact__diffs > div { padding-block: var(--space-8); border-block-end: 1px solid var(--color-border); }
    .ds-impact__diffs dd { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-8); }
    .ds-impact__diffs del { color: var(--color-danger); }
    .ds-impact__diffs ins { color: var(--color-success); font-weight: var(--weight-semibold); text-decoration: none; }
    .ds-impact__groups { display: grid; gap: var(--space-12); }
    .ds-impact-group { border-inline-start: var(--space-4) solid var(--color-border-strong); padding-inline-start: var(--space-12); }
    .ds-impact-group--warning { border-inline-start-color: var(--color-warning); }
    .ds-impact-group--danger { border-inline-start-color: var(--color-danger); }
    .ds-impact-group h3 { margin: 0 0 var(--space-8); font-size: var(--type-md); line-height: var(--leading-md); }
    .ds-impact-group ul { display: grid; gap: var(--space-8); margin: 0; padding-inline-start: var(--space-20); }
    .ds-impact-group li span { display: block; color: var(--color-muted); }
    .ds-impact-group__reason { color: var(--color-danger) !important; }
    .ds-impact-group__empty { margin: 0; color: var(--color-muted); }
    .ds-impact__confirm {
        display: flex;
        align-items: flex-start;
        gap: var(--space-8);
        min-block-size: var(--touch-target-min);
        padding: var(--space-12);
        border-radius: var(--radius-control);
        background: var(--color-warning-subtle);
        cursor: pointer;
    }
    /* A high-impact confirmation needs a real target. The element is 44x44 (what the accessibility
       scan measures and what a thumb hits) while the painted box stays 24x24 via content-box
       clipping — the same visual/hit split the icon button uses. */
    .ds-impact__confirm input {
        appearance: none;
        -webkit-appearance: none;
        flex: none;
        inline-size: var(--touch-target-min);
        block-size: var(--touch-target-min);
        padding: calc((var(--touch-target-min) - var(--space-24)) / 2);
        border: none;
        background: var(--color-surface);
        background-clip: content-box;
        box-shadow: inset 0 0 0 1px var(--color-border-strong);
        border-radius: var(--radius-sm);
        cursor: pointer;
    }
    .ds-impact__confirm input:checked {
        background: var(--color-primary);
        background-clip: content-box;
        box-shadow: inset 0 0 0 1px var(--color-primary);
    }
    /* The mark is drawn, not a glyph, so it stays crisp and inherits the token palette. */
    .ds-impact__confirm input:checked::after {
        content: '';
        display: block;
        inline-size: var(--space-8);
        block-size: var(--space-12);
        margin-block-start: var(--space-2);
        margin-inline-start: var(--space-4);
        border-inline-end: var(--space-2) solid var(--color-on-primary);
        border-block-end: var(--space-2) solid var(--color-on-primary);
        transform: rotate(45deg);
    }

    @media (min-width: 52.5rem) {
        .ds-impact__groups { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    }

    @media (max-width: 37.499rem) {
        .ds-scope-summary__list > div,
        .ds-effective__values > div,
        .ds-impact__diffs > div { grid-template-columns: 1fr; gap: var(--space-2); }
    }
}
@layer spec043.components {
    /* T025 — page header, breadcrumb and route-backed subnavigation. */
    .ds-page-header { display: grid; gap: var(--space-12); min-inline-size: 0; }
    .ds-page-header__row {
        display: grid;
        grid-template-columns: minmax(0, 1fr);
        gap: var(--space-12);
        align-items: start;
    }
    .ds-page-header__titles { min-inline-size: 0; }
    .ds-page-header__title {
        margin: 0;
        font-size: var(--type-xl);
        line-height: var(--leading-xl);
        font-weight: var(--weight-bold);
        color: var(--color-text);
    }
    .ds-page-header__description {
        margin: var(--space-4) 0 0;
        color: var(--color-muted);
        font-size: var(--type-sm);
        line-height: var(--leading-sm);
        max-inline-size: 60ch;
    }
    .ds-page-header__context { min-inline-size: 0; }
    .ds-page-header__actions {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: var(--space-8);
    }
    .ds-page-header__secondary { display: flex; flex-wrap: wrap; gap: var(--space-8); }
    .ds-page-header__primary { display: flex; }
    .ds-page-header__subnav { min-inline-size: 0; }
    .ds-page-header__status { min-inline-size: 0; }

    .ds-breadcrumb__list {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: var(--space-6);
        margin: 0;
        padding: 0;
        list-style: none;
        font-size: var(--type-sm);
        line-height: var(--leading-sm);
        color: var(--color-muted);
    }
    .ds-breadcrumb__item { display: flex; align-items: center; gap: var(--space-6); min-inline-size: 0; }
    .ds-breadcrumb__item a { color: var(--color-muted); }
    .ds-breadcrumb__current { color: var(--color-text); font-weight: var(--weight-semibold); }
    .ds-breadcrumb__separator { color: var(--color-border-strong); }

    .ds-subnav { min-inline-size: 0; }
    .ds-subnav__tabs { display: none; }
    .ds-subnav__list {
        display: flex;
        flex-wrap: wrap;
        gap: var(--space-4);
        margin: 0;
        padding: 0;
        list-style: none;
        border-block-end: 1px solid var(--color-border);
    }
    .ds-subnav__tab {
        display: inline-flex;
        align-items: center;
        min-block-size: var(--touch-target-min);
        padding-inline: var(--space-12);
        border-block-end: var(--space-2) solid transparent;
        color: var(--color-muted);
        font-weight: var(--weight-semibold);
        text-decoration: none;
    }
    .ds-subnav__tab:hover { color: var(--color-text); }
    .ds-subnav__tab--active { color: var(--color-primary); border-block-end-color: var(--color-primary); }
    .ds-subnav__compact { display: grid; gap: var(--space-8); }
    .ds-subnav__steps { display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--space-8); }
    .ds-subnav__step {
        display: inline-flex;
        align-items: center;
        gap: var(--space-6);
        min-block-size: var(--touch-target-min);
        color: var(--color-primary);
        font-weight: var(--weight-semibold);
    }

    @media (min-width: 52.5rem) {
        .ds-page-header__row {
            grid-template-columns: minmax(0, 1fr) auto auto;
            align-items: center;
        }
        .ds-subnav__tabs { display: block; }
        .ds-subnav__compact { display: none; }
    }
}
@layer spec043.components {
    /* T026 — internal component-state gallery shell. Development-only surface; it styles the
       gallery page itself and never leaks into a product page. */
    .ds-gallery {
        display: grid;
        gap: var(--space-24);
        padding: var(--page-gutter);
        max-inline-size: var(--admin-canvas-max);
        margin-inline: auto;
        background: var(--color-canvas);
        color: var(--color-text);
    }
    .ds-gallery__disabled { padding: var(--space-24); color: var(--color-muted); }
    .ds-gallery__axes,
    .ds-gallery__section {
        display: grid;
        gap: var(--space-16);
        padding: var(--card-padding);
        border: 1px solid var(--color-border);
        border-radius: var(--radius-card);
        background: var(--color-surface);
        min-inline-size: 0;
    }
    .ds-gallery__axes > h2,
    .ds-gallery__section > h2 {
        margin: 0;
        font-size: var(--type-lg);
        line-height: var(--leading-lg);
    }
    .ds-gallery__axis-grid {
        display: grid;
        gap: var(--space-16);
        grid-template-columns: repeat(auto-fit, minmax(17.5rem, 1fr));
    }
    .ds-gallery__row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-12); }
    /* The toast floats over the viewport in real screens; in the gallery it is drawn in place so it
       neither covers other primitives nor moves with the scroll position. Gallery-only. */
    .ds-gallery__toasts { display: grid; gap: var(--space-12); }
    .ds-gallery__toasts .ds-toast { position: static; inset: auto; margin-inline: 0; }

    /* T028 — the login screen, first shipped consumer of the primitives. Only layout lives here;
       every control's appearance comes from the shared components above. */
    .login-fields { display: grid; gap: var(--space-16); }
    .login-fields > .ds-button { inline-size: 100%; }

    /* T028 — the representative list's toolbar. Only layout; the controls are shared primitives.
       The shared list toolbar/selection/bulk contract itself is T029's work. */
    .list-toolbar:has(> .ds-field) {
        display: grid;
        gap: var(--space-12);
        grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
        align-items: start;
    }

    /* T028 — the shell scope control sits on the coloured topbar, so the shared select keeps its
       geometry and focus behaviour but inverts its surface to stay legible there. The dropdown
       list itself returns to the normal surface, where the token contrast pairs were validated. */
    .admin-context-picker .ds-branch-context {
        grid-auto-flow: column;
        align-items: center;
        gap: var(--space-8);
    }
    .admin-context-picker .ds-field__label,
    .admin-context-picker .ds-branch-context__label { color: inherit; }
    /* A single branch is plain text beside its label. The auto-flow grid gave it a min-content
       column, so on a phone «الفرع الرئيسي» broke over two lines and pushed the topbar taller.
       One line; a name longer than the bar ends in «…». */
    .admin-context-picker .ds-branch-context:has(> .ds-branch-context__single) {
        grid-auto-flow: row; grid-template-columns: auto minmax(0, 1fr);
    }
    .admin-context-picker .ds-branch-context__single {
        display: block; min-inline-size: 0; min-block-size: 0;
        white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }
    .admin-context-picker .ds-select {
        min-inline-size: 8rem;
        max-inline-size: 14rem;
        border-color: currentColor;
        background: transparent;
        color: inherit;
    }
    .admin-context-picker .ds-select option { color: var(--color-text); background: var(--color-surface); }

    @media (max-width: 38.74rem) {
        .admin-context-picker,
        .admin-context-picker .ds-branch-context { flex: 1; min-inline-size: 0; }
        .admin-context-picker .ds-select { min-inline-size: 0; max-inline-size: none; inline-size: 100%; }
    }

    /* Brand theme axis: a restaurant accent may brand accents only. It deliberately does NOT remap
       the semantic success/warning/danger/focus roles (design-system.md colour rules). The sample
       accent reuses the already contrast-validated info role rather than inventing an unreviewed
       brand hex; real per-restaurant values arrive with their own contrast validation in T046. */
    .ds-gallery--brand {
        --color-primary: var(--color-info);
        --color-primary-hover: var(--color-info);
        --color-primary-subtle: var(--color-info-subtle);
    }
}

/* ────────────────────────────────────────────────────────────────────────────
   Spec-043 T029 — the standard list surface: toolbar, bulk bar, table/cards/grid, pager.
   FR-011 (one toolbar), FR-012 (full footer), FR-013 (geometry + grid columns),
   FR-014 (table→cards below 720px), FR-015 (selection boundary).
   ──────────────────────────────────────────────────────────────────────────── */
@layer spec043.components {

    /* FR-011 — one toolbar shape. Query controls wrap; the count/create block stays at the end of
       the inline axis so "how many" and "add" never move between resources. */
    .ds-list-toolbar {
        display: flex;
        flex-wrap: wrap;
        align-items: flex-end;
        gap: var(--space-12) var(--space-16);
        padding-block: var(--space-12);
    }
    .ds-list-toolbar__query {
        display: flex;
        flex-wrap: wrap;
        align-items: flex-end;
        gap: var(--space-12);
        flex: 1 1 20rem;
        min-inline-size: 0;
    }
    .ds-list-toolbar__display {
        display: flex;
        flex-wrap: wrap;
        align-items: flex-end;
        gap: var(--space-12);
    }
    .ds-list-toolbar__meta {
        display: flex;
        align-items: center;
        gap: var(--space-12);
        margin-inline-start: auto;
    }
    .ds-list-toolbar__count {
        color: var(--color-muted);
        font-size: var(--type-sm);
        line-height: var(--leading-sm);
        font-variant-numeric: tabular-nums;
        unicode-bidi: isolate;
    }
    .ds-list-toolbar__action { display: flex; gap: var(--space-8); }

    @media (max-width: 38.74rem) {
        .ds-list-toolbar__meta { inline-size: 100%; margin-inline-start: 0; justify-content: space-between; }
    }

    /* FR-015 — the selection boundary. Sticky at the block end so it stays reachable while the
       operator scrolls the rows the action will touch. */
    .ds-bulk-bar {
        position: sticky;
        inset-block-end: var(--space-0);
        z-index: var(--z-sticky);
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: var(--space-12);
        padding: var(--space-12) var(--space-16);
        margin-block-start: var(--space-12);
        border: 1px solid var(--color-primary);
        border-radius: var(--radius-card);
        background: var(--color-primary-subtle);
        box-shadow: var(--elevation-2);
    }
    .ds-bulk-bar__count {
        font-size: var(--type-sm);
        line-height: var(--leading-sm);
        font-variant-numeric: tabular-nums;
        unicode-bidi: isolate;
    }
    .ds-bulk-bar__actions {
        display: flex;
        flex-wrap: wrap;
        gap: var(--space-8);
        margin-inline-start: auto;
    }

    /* FR-013 — the surface. One DOM for every view; the class decides the presentation. */
    .ds-list-surface { inline-size: 100%; min-inline-size: 0; }

    .ds-list-table {
        inline-size: 100%;
        border-collapse: collapse;
        table-layout: fixed;           /* stable columns: content length must not move a column */
    }
    .ds-list-table__head { position: sticky; inset-block-start: var(--space-0); z-index: var(--z-base); }
    .ds-list-table th {
        block-size: var(--row-header-block-size);
        padding-inline: var(--space-12);
        text-align: start;
        font-size: var(--type-sm);
        line-height: var(--leading-sm);
        font-weight: var(--weight-semibold);
        color: var(--color-muted);
        background: var(--color-canvas);
        border-block-end: 1px solid var(--color-border-strong);
        white-space: nowrap;
    }
    .ds-list-table td {
        block-size: var(--row-block-size);
        padding-inline: var(--space-12);
        border-block-end: 1px solid var(--color-border);
        font-size: var(--type-md);
        line-height: var(--leading-md);
        overflow-wrap: anywhere;
    }
    /* FR-013 — the table thumbnail is exactly 72x72 so rows never reflow around a tall image. */
    .ds-list-table img,
    .ds-list-thumb--empty {
        inline-size: 72px;
        block-size: 72px;
        object-fit: cover;
        border-radius: var(--radius-sm);
        background: var(--color-canvas);
    }
    /* A row with no image occupies the SAME box as one with an image, so the list geometry does
       not change depending on which rows happen to have been photographed. */
    .ds-list-thumb--empty {
        display: flex;
        align-items: center;
        justify-content: center;
        border: 1px dashed var(--color-border);
        color: var(--color-muted);
    }

    /* FR-014 — below 720px the table becomes semantic cards. The DOM stays a table so row identity,
       selection and header/cell relationships survive; `data-label` on each cell is what keeps the
       column meaning visible once the header row is gone. */
    @media (max-width: 44.99rem) {
        .ds-list-surface--table .ds-list-table,
        .ds-list-surface--table .ds-list-table tbody,
        .ds-list-surface--table .ds-list-table tr,
        .ds-list-surface--table .ds-list-table td { display: block; inline-size: 100%; }
        .ds-list-surface--table .ds-list-table__head { display: none; }
        .ds-list-surface--table .ds-list-table tr {
            padding: var(--card-padding);
            margin-block-end: var(--space-12);
            border: 1px solid var(--color-border);
            border-radius: var(--radius-card);
            background: var(--color-surface);
        }
        .ds-list-surface--table .ds-list-table td {
            block-size: auto;
            display: flex;
            justify-content: space-between;
            gap: var(--space-12);
            padding-inline: var(--space-0);
            border-block-end: none;
        }
        .ds-list-surface--table .ds-list-table td::before {
            content: attr(data-label);
            color: var(--color-muted);
            font-size: var(--type-sm);
            line-height: var(--leading-sm);
            font-weight: var(--weight-medium);
        }
        .ds-list-surface--table .ds-list-table td:empty { display: none; }
    }

    .ds-list-cards { display: grid; gap: var(--space-12); grid-template-columns: 1fr; }

    /* FR-013 — 1 / 2 / 3 / 4 columns at <600 / 600-839 / 840-1199 / >=1200.
       Mirrors ListPresentationRules.GridColumnsAt; the two must be changed together. */
    .ds-list-grid { display: grid; gap: var(--space-16); grid-template-columns: 1fr; }
    @media (min-width: 37.5rem) { .ds-list-grid { grid-template-columns: repeat(2, 1fr); } }
    @media (min-width: 52.5rem) { .ds-list-grid { grid-template-columns: repeat(3, 1fr); } }
    @media (min-width: 75rem)   { .ds-list-grid { grid-template-columns: repeat(4, 1fr); } }

    /* FR-013 — catalog card media is 4:3 and clamped to the 160-240px band, so a grid of cards has
       one horizon line regardless of the uploaded image. */
    .ds-list-grid img,
    .ds-list-cards img {
        inline-size: 100%;
        block-size: clamp(160px, 20vw, 240px);
        aspect-ratio: 4 / 3;
        object-fit: cover;
        border-radius: var(--radius-sm);
        background: var(--color-canvas);
    }

    /* FR-012 — the footer. Range, size, then navigation; numerals tabular and bidi-isolated so
       «41-47 من 47» keeps its order inside the RTL run. */
    .ds-list-pager {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: var(--space-12) var(--space-16);
        padding-block: var(--space-12);
        border-block-start: 1px solid var(--color-border);
    }
    .ds-list-pager__range,
    .ds-list-pager__position {
        font-size: var(--type-sm);
        line-height: var(--leading-sm);
        color: var(--color-muted);
        font-variant-numeric: tabular-nums;
        unicode-bidi: isolate;
    }
    .ds-list-pager__size { min-inline-size: 8rem; }
    .ds-list-pager__nav {
        display: flex;
        align-items: center;
        gap: var(--space-4);
        margin-inline-start: auto;
    }
    /* The chevrons are literal glyphs, so they must be mirrored for the logical direction rather
       than left pointing the wrong way in an RTL list. */
    .ds-list-pager__nav .ds-icon-button { transform: scaleX(-1); }
    .ds-list-pager__nav .ds-list-pager__position { transform: none; }
}

/* ───────────────────────── Spec-043 T039 — the editor form shape ─────────────────────────
   A long authoring form needs two things a list does not: labelled SECTIONS, so the page has a
   scannable structure instead of one undifferentiated column, and a save bar that stays reachable
   while the operator is at the bottom of the form. Both are here rather than in a page's own
   <style>, so every editor migrated after this one inherits the same shape. */
@layer spec043.components {
    .ds-form-section {
        border: 1px solid var(--color-border);
        border-radius: var(--radius-card);
        background: var(--color-surface);
        padding: var(--space-16);
        margin-block-end: var(--space-16);
    }
    .ds-form-section__head {
        display: flex;
        flex-wrap: wrap;
        align-items: baseline;
        gap: var(--space-8) var(--space-12);
        margin-block-end: var(--space-12);
    }
    /* h2 in a page whose only h1 is the DsPageHeader title — the sections ARE the outline a screen
       reader announces, so they are headings and not styled divs. */
    .ds-form-section__title {
        margin: var(--space-0);
        font-size: var(--type-md);
        line-height: var(--leading-md);
        font-weight: var(--weight-semibold);
    }
    .ds-form-section__hint {
        margin: var(--space-0);
        font-size: var(--type-sm);
        line-height: var(--leading-sm);
        color: var(--color-muted);
    }
    .ds-form-section__body { display: grid; gap: var(--space-12); }
    /* Two fields that belong together (price / was-price) collapse to one column when the pair no
       longer fits, rather than shrinking below a usable width. */
    .ds-form-row {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
        gap: var(--space-12);
    }

    /* A checkbox or radio and its label are ONE control, so the whole row is the hit area — not the
       19px box. `min-block-size` is the same touch floor the T027 accessibility scan measures on
       icon buttons; without it a checkbox row passes visually and fails under a thumb. */
    .ds-choice {
        display: flex;
        align-items: center;
        gap: var(--space-8);
        min-block-size: var(--touch-target-min);
        cursor: pointer;
    }

    /* The row being 44px is not enough: the scan measures every INTERACTIVE ELEMENT's own box, and a
       native checkbox reports ~13-20px however large its label is. So the control itself becomes a
       44x44 transparent hit area and the visible box is drawn by ::before — the same
       hit-area-larger-than-paint split `.ds-icon-button` makes with `background-clip: content-box`.
       `appearance: none` is what makes ::before render at all; the element stays a real
       <input type="checkbox|radio">, so `@bind`, the label association and the a11y role are all
       untouched. */
    .ds-choice input[type='checkbox'],
    .ds-choice input[type='radio'] {
        appearance: none;
        -webkit-appearance: none;
        flex: none;
        margin: var(--space-0);
        inline-size: var(--touch-target-min);
        block-size: var(--touch-target-min);
        display: grid;
        place-items: center;
        background: transparent;
        border: 0;
        cursor: pointer;
    }
    .ds-choice input[type='checkbox']::before,
    .ds-choice input[type='radio']::before {
        content: '';
        display: grid;
        place-items: center;
        inline-size: 1.25rem;
        block-size: 1.25rem;
        border: 1px solid var(--color-border-strong);
        border-radius: var(--radius-control);
        background: var(--color-surface);
        font-size: var(--type-sm);
        line-height: 1;
    }
    .ds-choice input[type='radio']::before { border-radius: 50%; }
    .ds-choice input[type='checkbox']:checked::before {
        content: '✓';
        background: var(--color-primary);
        border-color: var(--color-primary);
        color: var(--color-on-primary);
    }
    .ds-choice input[type='radio']:checked::before {
        border-color: var(--color-primary);
        box-shadow: inset 0 0 0 0.25rem var(--color-primary);
    }
    .ds-choice input:disabled { cursor: not-allowed; }
    .ds-choice input:disabled::before { background: var(--color-subtle); border-color: var(--color-border); }
    .ds-choice__note { color: var(--color-muted); font-size: var(--type-sm); line-height: var(--leading-sm); }
    .ds-choice-group {
        display: flex;
        flex-wrap: wrap;
        gap: var(--space-4) var(--space-16);
    }
    .ds-choice-group--stacked { flex-direction: column; gap: var(--space-0); }

    /* ── T040 — the media picker ──
       The crop mask is the load-bearing part. The server resizes with `ResizeMode.Crop`, i.e. a
       CENTRED square, so this overlay shows exactly what survives. It is a preview of a known,
       deterministic algorithm — not a guess — which is the only reason it is allowed to exist:
       an adjustable crop would need the server to accept a rectangle, and it does not
       (contracts/image-crop-gate.md). */
    .ds-media-picker { display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--space-16); }
    .ds-media-picker__body { flex: 1; min-inline-size: 12rem; display: grid; gap: var(--space-8); }
    .ds-media-preview {
        position: relative;
        overflow: hidden;
        flex: none;
        inline-size: 9rem;
        border: 1px solid var(--color-border);
        border-radius: var(--radius-card);
        background: var(--color-subtle);
    }
    .ds-media-preview img { display: block; inline-size: 100%; block-size: auto; }
    .ds-media-preview__empty {
        display: grid;
        place-items: center;
        block-size: 9rem;
        font-size: var(--type-lg);
        color: var(--color-muted);
    }
    /* `inset: 0` + `margin: auto` + `aspect-ratio: 1` centres a square bounded by the SHORTER side —
       the same square the server keeps. The oversized spread darkens everything outside it and is
       clipped by the container's `overflow: hidden`. */
    .ds-media-preview__crop {
        position: absolute;
        inset: var(--space-0);
        margin: auto;
        aspect-ratio: 1;
        max-inline-size: 100%;
        max-block-size: 100%;
        outline: 2px solid var(--color-primary);
        outline-offset: -2px;
        box-shadow: 0 0 0 100vmax var(--color-scrim);
    }
    .ds-media-progress {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: var(--space-8) var(--space-12);
    }
    .ds-media-progress__file {
        font-size: var(--type-sm);
        line-height: var(--leading-sm);
        color: var(--color-muted);
        unicode-bidi: isolate;
    }

    .ds-form-actions {
        position: sticky;
        inset-block-end: var(--space-0);
        z-index: var(--z-sticky);
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: var(--space-8) var(--space-12);
        padding: var(--space-12) var(--space-16);
        margin-block-start: var(--space-16);
        border: 1px solid var(--color-border);
        border-radius: var(--radius-card);
        background: var(--color-surface);
        box-shadow: var(--elevation-2);
    }
    /* Cancel sits away from the commit actions: an accidental click on the control next to «حفظ»
       must not be the one that discards the form. */
    .ds-form-actions__end { margin-inline-start: auto; }

    /* A sticky bar is a fixed cost in vertical space. On a short viewport (a phone in landscape,
       a small laptop) that cost is worth more than the convenience, so it stops being sticky. */
    @media (max-height: 30rem) {
        .ds-form-actions { position: static; box-shadow: none; }
    }

    /* Multi-select CHOICE CARDS (DsChoiceCards). A checkbox row answers "is this flag on?"; these
       answer "which of these does this branch do?" — the same question a diner asks. The card is a
       real button (role="checkbox"), so the whole tile is the hit area rather than a 13px box, and
       there is room for the hint that says what the choice actually does. */
    .ds-optioncard-grid {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
        gap: var(--space-12);
        margin-block: var(--space-8) var(--space-12);
    }

    .ds-optioncard {
        display: flex;
        align-items: flex-start;
        gap: var(--space-12);
        inline-size: 100%;
        min-block-size: var(--touch-target-min);
        padding: var(--space-12) var(--space-16);
        border: 1px solid var(--color-border);
        border-radius: var(--radius-card);
        background: var(--color-surface);
        color: var(--color-text);
        text-align: start;
        cursor: pointer;
        transition: border-color var(--motion-duration-feedback) var(--motion-ease-standard),
                    background-color var(--motion-duration-feedback) var(--motion-ease-standard),
                    box-shadow var(--motion-duration-feedback) var(--motion-ease-standard);
    }

    .ds-optioncard:hover:not(:disabled) { border-color: var(--color-border-strong); }
    .ds-optioncard:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; }

    /* Selected state carries THREE cues, not colour alone: the tinted surface, the coloured border,
       and the tick in the corner box. Colour-blind users and a washed-out screen both still read it. */
    .ds-optioncard.is-on {
        border-color: var(--color-primary);
        background: var(--color-primary-subtle);
        box-shadow: inset 0 0 0 1px var(--color-primary);
    }

    .ds-optioncard:disabled { opacity: .55; cursor: not-allowed; }

    .ds-optioncard__icon { font-size: var(--type-xl); line-height: var(--leading-lg); }

    .ds-optioncard__text { display: grid; gap: var(--space-2); min-inline-size: 0; }

    .ds-optioncard__title {
        font-size: var(--type-md);
        line-height: var(--leading-md);
        font-weight: var(--weight-semibold);
    }

    .ds-optioncard__hint {
        font-size: var(--type-xs);
        line-height: var(--leading-xs);
        color: var(--color-muted);
    }

    .ds-optioncard__warning {
        margin-block-start: var(--space-4);
        font-size: var(--type-xs);
        line-height: var(--leading-xs);
        font-weight: var(--weight-semibold);
        color: var(--color-warning-ink);
    }

    /* The tick box is always drawn, empty when off, so the row keeps one alignment and the control
       reads as "choose any" before anything has been chosen. */
    .ds-optioncard__mark {
        flex: 0 0 auto;
        display: grid;
        place-items: center;
        inline-size: 22px;
        block-size: 22px;
        margin-inline-start: auto;
        border: 1px solid var(--color-border-strong);
        border-radius: var(--radius-sm);
        background: var(--color-surface);
        font-size: var(--type-sm);
        line-height: 1;
        font-weight: var(--weight-bold);
        color: transparent;
    }

    .ds-optioncard.is-on .ds-optioncard__mark {
        border-color: var(--color-primary);
        background: var(--color-primary);
        color: var(--color-on-primary);
    }
}

/* ───────────────────────── Script0048 — the shared on/off switch ─────────────────────────
   One control for "this row is live / this row is not", so items, categories and add-ons stop
   expressing the same idea three different ways. Track + knob + a one-word state label, sized to
   the same --touch-target-min every other list control uses.

   The knob is positioned with LOGICAL properties and the whole panel is RTL, so "on" moves the
   knob toward the reading direction's end without a direction-specific rule. */
@layer spec043.components {
    .ds-toggle {
        display: inline-flex;
        align-items: center;
        gap: var(--space-8);
        min-block-size: var(--touch-target-min);
        padding: 0;
        border: 0;
        background: transparent;
        font: inherit;
        color: var(--color-text);
        cursor: pointer;
    }
    .ds-toggle:disabled { cursor: default; opacity: 0.6; }
    .ds-toggle:focus-visible { outline: 2px solid var(--color-focus); outline-offset: 2px; }

    .ds-toggle__track {
        position: relative;
        flex: 0 0 auto;
        display: grid;
        place-items: center;
        inline-size: 44px;
        block-size: 24px;
        border: 1px solid var(--color-border-strong);
        border-radius: var(--radius-pill);
        background: var(--color-subtle);
        transition: background-color var(--motion-duration-feedback) var(--motion-ease-standard),
                    border-color var(--motion-duration-feedback) var(--motion-ease-standard);
    }

    .ds-toggle__knob {
        position: absolute;
        inset-block-start: 2px;
        inset-inline-start: 2px;
        inline-size: 18px;
        block-size: 18px;
        border-radius: var(--radius-pill);
        /* A border, not a shadow: this stylesheet is token-only (no literal colour may appear in
           it — Spec043DesignSystemSourceTests), and the design system has no shadow token, because
           no other control here uses one. */
        border: 1px solid var(--color-border);
        background: var(--color-surface);
        transition: inset-inline-start var(--motion-duration-feedback) var(--motion-ease-standard);
    }

    .ds-toggle.is-on .ds-toggle__track {
        background: var(--color-success);
        border-color: var(--color-success);
    }
    /* 44 (the track's 46px box minus its 1px borders) − 20 (the knob's box) − 2 (the start inset)
       = 22, so the knob sits exactly as far from the end when it is on as from the start when it is
       off. MEASURED, not derived: `inset-inline-start` counts from the padding edge, so the border
       is already excluded — 24px here left a 1px end gap against a 3px start gap. */
    .ds-toggle.is-on .ds-toggle__knob { inset-inline-start: 22px; }

    .ds-toggle__label {
        font-size: var(--type-sm);
        line-height: var(--leading-sm);
        font-weight: var(--weight-semibold);
        white-space: nowrap;
    }
    .ds-toggle.is-off .ds-toggle__label { color: var(--color-muted); }

    @media (prefers-reduced-motion: reduce) {
        .ds-toggle__track, .ds-toggle__knob { transition: none; }
    }
    .ds-motion--reduced .ds-toggle__track,
    .ds-motion--reduced .ds-toggle__knob { transition: none; }
}

/* ── Spec-046 «الطلب المسبق» — owner screens (tokens only) ──────────────────────────────── */
.order-card-schedule {
    display: inline-flex; align-items: center; gap: .3rem; padding: .2rem .6rem;
    border-radius: var(--radius-pill); font-weight: var(--weight-semibold); font-size: .85rem;
    background: var(--color-primary-subtle); color: var(--color-primary);
}
.order-card-deposit.is-paid { color: var(--color-success-ink); font-weight: var(--weight-semibold); }
.order-card-deposit.is-none { color: var(--color-muted); }
.order-deposit-row { display: flex; gap: .5rem; align-items: center; }
.order-deposit-input { max-inline-size: 10rem; }
.order-deposit-hint { margin: .4rem 0 0; font-size: .85rem; }
.order-upcoming-strip {
    display: flex; justify-content: space-between; align-items: center; text-decoration: none;
    background: var(--color-primary-subtle); color: var(--color-text);
}
.po-days { display: flex; gap: .5rem; overflow-x: auto; padding-block: .25rem .75rem; }
.po-day {
    flex: 0 0 auto; display: inline-flex; align-items: center; gap: .4rem;
    min-block-size: var(--touch-target-min); padding: .4rem .9rem;
    border: 1.5px solid var(--color-border); border-radius: var(--radius-control);
    background: var(--color-surface); color: var(--color-text);
    font: inherit; font-weight: var(--weight-semibold); cursor: pointer;
}
.po-day.is-selected { border-color: var(--color-primary); background: var(--color-primary); color: var(--color-on-primary); }
.po-day-count {
    min-inline-size: 1.4rem; padding: 0 .35rem; border-radius: var(--radius-pill);
    background: color-mix(in srgb, currentColor 16%, transparent); font-size: .8rem; text-align: center;
}
.po-summary-table { inline-size: 100%; border-collapse: collapse; }
.po-summary-table th, .po-summary-table td { padding: .5rem .4rem; border-block-end: 1px solid var(--color-border); text-align: start; }
.po-summary-table .po-qty { font-size: 1.15rem; font-weight: var(--weight-bold); text-align: center; inline-size: 4rem; font-variant-numeric: tabular-nums; }
.po-orders { display: grid; gap: .75rem; margin-block-start: 1rem; }
.po-print-title { display: none; }
@media print {
    .no-print, nav, header.admin-topbar, .order-card-footer, .order-detail-actions { display: none !important; }
    .po-print-title { display: block; }
}
