:root {
    --color-primary: #003c5e;
    --color-primary-light: #004d78;
    --color-primary-dark: #002d47;
    --color-secondary: #0071bc;
    --color-secondary-light: #0085e0;
    --color-secondary-dark: #005a96;
    --color-accent: #f7931e;
    --color-accent-dark: #e07d0a;
    --color-bg: #f5f6f8;
    --color-surface: #ffffff;
    --color-border: #e2e5ea;
    --color-border-subtle: #eef0f3;
    --color-text: #1a1f2b;
    --color-text-secondary: #5a6170;
    --color-text-muted: #8e95a3;
    --color-success: #16a34a;
    --color-success-bg: #f0fdf4;
    --color-warning: #f7931e;
    --color-warning-bg: #fff8ed;
    --color-error: #dc2626;
    --color-error-bg: #fef2f2;
    --color-info: #0071bc;
    --color-info-bg: #eff6ff;
    --color-quarantine: #7c3aed;
    --color-quarantine-bg: #f5f3ff;
    --color-neutral-bg: #f0f1f3;
    /* Bronze is decorative only (Talos, the bronze automaton) — never semantic. */
    --color-bronze: #b08340;
    --color-bronze-light: #d9b27c;
    --space-xs: 4px;
    --space-sm: 8px;
    --space-md: 16px;
    --space-lg: 24px;
    --space-xl: 32px;
    --space-2xl: 48px;
    --radius-sm: 4px;
    --radius-md: 6px;
    --radius-lg: 8px;
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.06);
    --shadow-md: 0 2px 8px rgba(0, 0, 0, 0.08);
    --shadow-lg: 0 4px 16px rgba(0, 0, 0, 0.10);
    --font-family: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
    --font-mono: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;
    --font-size-xs: 0.75rem;
    --font-size-sm: 0.8125rem;
    --font-size-base: 0.875rem;
    --font-size-md: 1rem;
    --font-size-lg: 1.125rem;
    --font-size-xl: 1.25rem;
    --font-size-2xl: 1.5rem;
    --transition-fast: 120ms ease;
    --transition-base: 200ms ease;
}

* {
    box-sizing: border-box;
}

/* The hidden attribute must always win: any class that sets display (flex,
   grid, …) silently overrides the UA's [hidden] { display: none }, which is
   how "hidden" elements end up visible. JS toggles visibility via the
   attribute throughout (serial picker, action modals, comboboxes). */
[hidden] {
    display: none !important;
}

html,
body {
    margin: 0;
    height: 100%;
}

body {
    background: var(--color-bg);
    color: var(--color-text);
    font-family: var(--font-family);
    font-size: var(--font-size-base);
    line-height: 1.5;
    overflow: hidden;
}

.mobile-body {
    overflow: auto;
}

a {
    color: inherit;
    text-decoration: none;
}

button,
input,
select {
    font: inherit;
}

input,
select,
textarea {
    width: 100%;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    color: var(--color-text);
    padding: 0 12px;
}

input,
select {
    height: 36px;
}

/* Radios and checkboxes are controls, not text fields — without this they
   inherit the full-width/36px text-input sizing above and render as an
   oversized control floating on its own line. */
input[type="radio"],
input[type="checkbox"] {
    accent-color: var(--color-secondary);
    height: auto;
    padding: 0;
    width: auto;
}

textarea {
    min-height: 132px;
    padding: 12px;
    resize: vertical;
}

input:focus,
select:focus,
textarea:focus {
    border-color: var(--color-secondary);
    box-shadow: 0 0 0 3px rgba(0, 113, 188, 0.15);
    outline: none;
}

.env-banner {
    position: sticky;
    top: 0;
    z-index: 200;
    height: 28px;
    min-height: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--font-size-xs);
    font-weight: 700;
    letter-spacing: 0.18em;
    text-transform: uppercase;
}

.env-banner--development {
    background: var(--color-success);
    color: #fff;
}

.env-banner--staging {
    background: repeating-linear-gradient(135deg, var(--color-accent) 0 14px, var(--color-accent-dark) 14px 28px);
    color: #3b2200;
}

/* The desktop shell is a fixed 100vh layout with body overflow hidden, so the
   banner's height must come off the shell or the bottom edge is clipped.
   --banner-h (declared on .app-shell) is the single source of truth for that
   chrome height — consume the variable rather than hardcoding 28px. */
.env-banner ~ .app-shell {
    --banner-h: 28px;
}

.env-banner ~ .mobile-shell {
    min-height: calc(100vh - 28px);
}

.env-banner ~ .auth-shell {
    min-height: calc(100vh - 28px);
}

.app-shell {
    --banner-h: 0px;
    /* Allowance for chrome above a primary data table: topbar 48 + content
       padding 64 + page header ~76 + stats row ~110 + panel padding/header
       ~130. Override per panel only when a table sits unusually high or low. */
    --table-offset: 440px;
    display: flex;
    height: calc(100vh - var(--banner-h));
    overflow: hidden;
}

.app-main {
    flex: 1;
    display: flex;
    flex-direction: column;
    min-width: 0;
    height: calc(100vh - var(--banner-h));
}

.sidebar {
    width: 240px;
    min-width: 240px;
    height: calc(100vh - var(--banner-h));
    background: linear-gradient(180deg, var(--color-primary-dark) 0%, var(--color-primary) 100%);
    color: #fff;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    transition: width var(--transition-base), min-width var(--transition-base);
    z-index: 100;
}

.app-shell.is-sidebar-collapsed .sidebar,
.sidebar.collapsed {
    width: 64px;
    min-width: 64px;
}

.sidebar__brand {
    height: 48px;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 0 20px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
    flex-shrink: 0;
}

.panel__eyebrow,
.app-footer,
.stat-card__meta span,
.stat-card__detail,
.timeline span,
.key-value-list dt,
.topbar__user-role {
    color: var(--color-text-muted);
}

.form-help {
    display: block;
    margin-top: 0.35rem;
    font-size: 0.8rem;
    color: var(--color-text-muted);
}

.form-help--warning {
    color: var(--color-error);
    font-weight: 600;
}

.sidebar__logo {
    width: 24px;
    height: 24px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    flex-shrink: 0;
}

.sidebar__logo svg,
.sidebar__icon svg,
.global-search__icon svg,
.topbar__icon svg,
.topbar__chevron svg,
.sidebar__toggle-icon svg {
    width: 100%;
    height: 100%;
}

.sidebar__brand-text {
    font-size: var(--font-size-md);
    font-weight: 700;
    color: #fff;
    letter-spacing: 0.02em;
    white-space: nowrap;
    overflow: hidden;
}

.sidebar__nav {
    flex: 1;
    overflow-y: auto;
    overflow-x: hidden;
    padding: var(--space-sm) 0;
}

.sidebar__nav::-webkit-scrollbar {
    width: 0;
}

.sidebar__section-label {
    color: rgba(255, 255, 255, 0.35);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.08em;
    padding: 20px 20px 6px;
    text-transform: uppercase;
    white-space: nowrap;
    overflow: hidden;
}

/* A foldable section: the label doubles as the toggle, so it keeps the label
   padding and adds a chevron that points down when open. */
.sidebar__section-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    width: 100%;
    background: none;
    border: 0;
    cursor: pointer;
    font-family: inherit;
    padding: 0 20px 0 0;
    text-align: left;
}

.sidebar__section-toggle .sidebar__section-label {
    flex: 1;
}

.sidebar__section-toggle:hover .sidebar__section-label {
    color: rgba(255, 255, 255, 0.65);
}

.sidebar__section-toggle:focus-visible {
    outline: 2px solid rgba(255, 255, 255, 0.5);
    outline-offset: -2px;
}

.sidebar__section-chevron {
    display: inline-flex;
    align-items: center;
    color: rgba(255, 255, 255, 0.35);
    margin-top: 14px;
    transition: transform var(--transition-fast);
}

.sidebar__section-chevron svg {
    width: 14px;
    height: 14px;
}

.sidebar__section.is-folded .sidebar__section-chevron {
    transform: rotate(-90deg);
}

.sidebar__section.is-folded .sidebar__section-items {
    display: none;
}

.sidebar__item,
.sidebar__collapse-button {
    display: flex;
    align-items: center;
    gap: 10px;
    border-left: 3px solid transparent;
    color: rgba(255, 255, 255, 0.72);
    font-size: var(--font-size-base);
    font-weight: 400;
    padding: 9px 20px;
    position: relative;
    transition: background-color var(--transition-fast), color var(--transition-fast), border-color var(--transition-fast);
    white-space: nowrap;
}

.sidebar__item:hover,
.sidebar__collapse-button:hover {
    background: rgba(255, 255, 255, 0.05);
    color: rgba(255, 255, 255, 0.92);
}

.sidebar__item.is-active {
    background: rgba(255, 255, 255, 0.12);
    border-left-color: var(--color-secondary);
    color: #fff;
    font-weight: 600;
}

.sidebar__icon {
    width: 20px;
    height: 20px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}

.sidebar__icon--toggle {
    width: 20px;
    height: 20px;
}

.sidebar__toggle-icon--collapsed,
.sidebar__toggle-text--collapsed {
    display: none;
}

.sidebar__label {
    overflow: hidden;
    text-overflow: ellipsis;
    transition: opacity var(--transition-base);
}

.sidebar__badge {
    margin-left: auto;
    background: var(--color-accent);
    color: #fff;
    font-size: 11px;
    font-weight: 700;
    min-width: 20px;
    height: 20px;
    line-height: 20px;
    text-align: center;
    border-radius: 10px;
    padding: 0 6px;
    flex-shrink: 0;
}

.sidebar__footer {
    border-top: 1px solid rgba(255, 255, 255, 0.08);
    flex-shrink: 0;
}

.sidebar__collapse-button {
    width: 100%;
}

.topbar {
    height: 48px;
    min-height: 48px;
    background: var(--color-primary-dark);
    color: #fff;
    display: flex;
    align-items: center;
    padding: 0 var(--space-lg);
    gap: var(--space-lg);
    z-index: 90;
}

.topbar__left,
.topbar__right {
    flex: 1;
    min-width: 0;
}

.topbar__centre {
    display: flex;
    align-items: center;
    justify-content: center;
}

.topbar__right {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--space-md);
}

.breadcrumb {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: var(--font-size-sm);
    min-width: 0;
}

.breadcrumb__link {
    color: rgba(255, 255, 255, 0.5);
}

.breadcrumb__link:hover {
    color: rgba(255, 255, 255, 0.82);
}

.breadcrumb__sep {
    color: rgba(255, 255, 255, 0.25);
}

.breadcrumb__current {
    color: rgba(255, 255, 255, 0.9);
    font-weight: 500;
    overflow: hidden;
    text-overflow: ellipsis;
}

.global-search {
    display: flex;
    align-items: center;
    gap: 8px;
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: var(--radius-md);
    height: 34px;
    width: 400px;
    padding: 0 12px;
    transition: background var(--transition-fast), border-color var(--transition-fast);
}

.global-search:focus-within {
    background: rgba(255, 255, 255, 0.12);
    border-color: rgba(255, 255, 255, 0.25);
}

.global-search__icon {
    width: 16px;
    height: 16px;
    color: rgba(255, 255, 255, 0.4);
    flex-shrink: 0;
}

.global-search__input {
    background: none;
    border: none;
    color: #fff;
    flex: 1;
    height: auto;
    padding: 0;
}

.global-search__input:focus {
    box-shadow: none;
}

.global-search__input::placeholder {
    color: rgba(255, 255, 255, 0.35);
}

.global-search__shortcut {
    font-size: 11px;
    font-family: var(--font-mono);
    color: rgba(255, 255, 255, 0.3);
    border: 1px solid rgba(255, 255, 255, 0.15);
    border-radius: 3px;
    padding: 1px 5px;
    flex-shrink: 0;
}

.topbar__icon-button,
.topbar__user-trigger,
.sidebar__collapse-button {
    background: none;
    border: none;
    color: inherit;
    cursor: pointer;
    font: inherit;
}

.topbar__icon-button {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    padding: 0;
    border-radius: var(--radius-sm);
    transition: background-color var(--transition-fast);
}

.topbar__icon-button:hover,
.topbar__user-trigger:hover {
    background: rgba(255, 255, 255, 0.08);
}

.topbar__icon {
    width: 20px;
    height: 20px;
    color: rgba(255, 255, 255, 0.72);
}

.topbar__notification-dot {
    display: none;
    position: absolute;
    top: 5px;
    right: 5px;
    width: 8px;
    height: 8px;
    background: var(--color-accent);
    border-radius: 50%;
    border: 2px solid var(--color-primary-dark);
}

.topbar__notification-dot.is-visible {
    display: block;
}

.topbar__user-menu {
    position: relative;
}

.topbar__user-trigger {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 8px;
    border-radius: var(--radius-sm);
}

.topbar__avatar {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: var(--color-secondary);
    color: #fff;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 11px;
    font-weight: 700;
    flex-shrink: 0;
    text-transform: uppercase;
}

.topbar__user-meta {
    display: grid;
    gap: 1px;
    text-align: left;
}

.topbar__user-name {
    color: rgba(255, 255, 255, 0.88);
    font-size: 13px;
    font-weight: 500;
    white-space: nowrap;
}

.topbar__user-role {
    color: rgba(255, 255, 255, 0.54);
    font-size: 11px;
    white-space: nowrap;
}

.topbar__chevron {
    width: 14px;
    height: 14px;
    color: rgba(255, 255, 255, 0.4);
}

.topbar__dropdown {
    position: absolute;
    top: 40px;
    right: 0;
    width: max-content;
    min-width: 220px;
    max-width: 320px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    padding: var(--space-xs) 0;
    display: none;
    z-index: 200;
}

.topbar__dropdown.is-open {
    display: block;
}

.topbar__dropdown-header {
    display: grid;
    gap: 2px;
    padding: var(--space-sm) var(--space-md);
}

.topbar__dropdown-header strong {
    color: var(--color-text);
    font-size: var(--font-size-base);
}

.topbar__dropdown-header span {
    color: var(--color-text-muted);
    font-size: var(--font-size-xs);
    overflow-wrap: anywhere;
}

.topbar__dropdown-divider {
    height: 1px;
    background: var(--color-border-subtle);
    margin: var(--space-xs) 0;
}

.topbar__dropdown-item {
    display: block;
    padding: 8px var(--space-md);
    color: var(--color-text);
    font-size: var(--font-size-base);
}

.topbar__dropdown-item:hover {
    background: var(--color-bg);
}

.topbar__dropdown-item--danger {
    color: var(--color-error);
}

.button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 36px;
    border-radius: var(--radius-md);
    border: 1px solid transparent;
    font-size: var(--font-size-sm);
    font-weight: 600;
    padding: 0 14px;
    transition: background-color var(--transition-fast), border-color var(--transition-fast), color var(--transition-fast);
}

/* No hover tint, and pointer events fall through to a wrapper that may carry
   the explanation as a hint bubble. */
.button:disabled {
    opacity: 0.55;
    pointer-events: none;
}

.button--primary {
    background: var(--color-primary);
    color: #fff;
}

.button--primary:hover {
    background: var(--color-primary-light);
}

.button--secondary {
    background: transparent;
    border-color: var(--color-secondary);
    color: var(--color-secondary);
}

.button--secondary:hover {
    background: rgba(0, 113, 188, 0.08);
}

.button--danger {
    background: var(--color-error);
    color: #fff;
}

.button--danger:hover {
    background: #b91c1c;
}

/* Square, icon-only button — pairs with a variant (e.g. --secondary) for colour.
   Give it an accessible name via title + aria-label since there is no text. */
.button--icon {
    width: 36px;
    min-width: 36px;
    padding: 0;
}

.button--icon svg {
    width: 18px;
    height: 18px;
}

.app-content {
    flex: 1;
    overflow-y: auto;
    overflow-x: hidden;
    background: var(--color-bg);
    padding: var(--space-xl);
}

.panel,
.stat-card,
.list-card,
.detail-card,
.alert {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
}

.alert {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin-bottom: 20px;
    padding: 16px 18px;
}

.alert--success {
    border-left: 4px solid var(--color-success);
    background: var(--color-success-bg);
}

.alert--warning {
    border-left: 4px solid var(--color-warning);
    background: var(--color-warning-bg);
}

.alert--error {
    border-left: 4px solid var(--color-error);
    background: var(--color-error-bg);
}

.alert--info {
    border-left: 4px solid var(--color-info);
    background: var(--color-info-bg);
}

.alert__list {
    margin: 0;
    padding-left: 18px;
}

.page-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-lg);
    margin-bottom: 24px;
}

.page-title,
.panel h3,
.detail-card h4 {
    margin: 0;
}

.page-header__main {
    min-width: 0;
}

.page-header__description,
.panel__header p,
.scan-panel p,
.list-card p,
.timeline p,
.detail-card p {
    color: var(--color-text-secondary);
}

.page-title {
    font-size: var(--font-size-2xl);
    font-weight: 700;
    line-height: 1.3;
}

.page-header__description {
    margin: 8px 0 0;
    max-width: 980px;
}

.page-actions,
.page-header__actions {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
}

.panel__eyebrow {
    font-size: var(--font-size-xs);
    letter-spacing: 0.08em;
    margin: 0 0 6px;
    text-transform: uppercase;
}

.stats-grid,
.content-grid,
.list-grid,
.detail-grid,
.form-grid {
    display: grid;
    gap: 24px;
}

.stats-grid {
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    margin-bottom: 24px;
}

.stats-grid--compact {
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}

/* A stats row nested inside a panel (the dashboard's "Today at a glance"
   strip) — the panel's own padding provides the spacing below. */
.stats-grid--flush {
    margin-bottom: 0;
}

.stat-card {
    padding: 20px 24px;
}

.stat-card--link {
    display: block;
    color: inherit;
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast), transform var(--transition-fast);
}

.stat-card--link:hover {
    border-color: var(--color-secondary);
    box-shadow: var(--shadow-md);
    transform: translateY(-1px);
}

.stat-card--link:focus-visible {
    outline: none;
    border-color: var(--color-secondary);
    box-shadow: 0 0 0 3px rgba(0, 113, 188, 0.15);
}

.stat-card__meta,
.panel__header,
.list-card,
.app-footer,
.queue-list__item,
.queue-list__meta {
    display: flex;
    justify-content: space-between;
    gap: 16px;
}

.stat-card__meta {
    align-items: center;
}

.stat-card__meta span,
.stat-card__detail {
    font-size: var(--font-size-sm);
}

.stat-card__trend {
    font-size: var(--font-size-xs);
    font-weight: 700;
    text-transform: uppercase;
}

.stat-card__trend.is-positive {
    color: var(--color-success);
}

.stat-card__trend.is-warning {
    color: var(--color-warning);
}

.stat-card__trend.is-neutral {
    color: var(--color-text-secondary);
}

.stat-card__value {
    display: block;
    font-size: var(--font-size-2xl);
    line-height: 1.15;
    margin-top: 10px;
}

.stat-card__detail {
    margin: 6px 0 0;
}

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

/* Separate stacked full-width grids (e.g. the valuation block above the
   exceptions queue on Reports), which otherwise sit flush together. */
.content-grid + .content-grid {
    margin-top: 24px;
}

.content-grid--split .panel--wide {
    grid-column: span 8;
}

/* Exclude the wide panel here: it also carries the base `.panel` class, so
   without :not() this equal-specificity rule would override the span-8 above
   (source order) and collapse the wide panel to a third-width column. */
.content-grid--split .panel:not(.panel--wide) {
    grid-column: span 4;
}

/* Explicit column spans for grids whose panel order/count varies (e.g. the
   dashboard, where panels are added over time). Prefer these over :nth-child. */
.panel--span-4 {
    grid-column: span 4;
}

.panel--span-6 {
    grid-column: span 6;
}

.panel--span-8 {
    grid-column: span 8;
}

.panel--span-12 {
    grid-column: 1 / -1;
}

/* Let panels hug their content instead of stretching to the tallest cell in
   the row — otherwise short side panels render as large empty cards beside
   tall tables and forms.

   Direct children only: this is a grid-axis rule, and `.content-grid__aside`
   is a flex column, where align-self resolves against the *cross* (horizontal)
   axis instead — a descendant selector here shrink-wraps the aside's panels to
   their own content width rather than the column's. */
.content-grid--dashboard > .panel,
.content-grid--split > .panel,
.content-grid--receiving-detail > .panel {
    align-self: start;
}

.content-grid--receiving-detail .panel--wide {
    grid-column: span 8;
}

.content-grid--receiving-detail .panel:not(.panel--wide) {
    grid-column: span 4;
}

/* Right-hand column on the receipt detail page: side panels stack in a flex
   column beside the tall main panel — as plain grid children, auto-placement
   would drop them underneath it instead. Flex also neutralises the span-4
   rule above on the panels inside. */
.content-grid__aside {
    grid-column: span 4;
    display: flex;
    flex-direction: column;
    gap: 24px;
    align-self: start;
    min-width: 0;
}

.panel {
    padding: 24px;
    min-width: 0;
}

.panel__header {
    align-items: flex-start;
    margin-bottom: 20px;
}

/* Separates a second header used as an in-panel section divider (e.g. the
   history block beneath a serial's detail list) from the content above it. */
.panel__header--spaced {
    margin-top: var(--space-xl);
}

/* Collapsible side panels (assignment, movement history): reference material
   that earns its height only when someone asks for it. Built on <details>, so
   keyboard, focus, and find-in-page behaviour come from the browser; the
   summary *is* the panel header, which is why the closed state still has to
   read as informative — put the answer in the title (the assignee's name, a
   record count), not just the topic. initCollapsiblePanels() remembers the
   choice; a control inside the summary acts instead of toggling. */
.panel--collapsible > summary {
    cursor: pointer;
    list-style: none;
    margin-bottom: 0;
    /* The header goes to a column below 1280px; the chevron must stay on the
       title's line, so this one keeps its row. */
    flex-direction: row;
    align-items: center;
}

.panel--collapsible > summary::-webkit-details-marker {
    display: none;
}

.panel--collapsible[open] > summary {
    margin-bottom: 20px;
}

.panel--collapsible > summary::after {
    content: "";
    flex: none;
    width: 8px;
    height: 8px;
    margin-left: auto;
    border-right: 2px solid var(--color-text-muted);
    border-bottom: 2px solid var(--color-text-muted);
    transform: translateY(-2px) rotate(45deg);
    transition: transform var(--transition-fast);
}

.panel--collapsible[open] > summary::after {
    transform: translateY(2px) rotate(-135deg);
}

.panel--collapsible > summary:focus-visible {
    outline: 2px solid var(--color-secondary);
    outline-offset: 2px;
}

.panel__link {
    color: var(--color-secondary);
    font-size: var(--font-size-sm);
    font-weight: 600;
}

/* The single link style for content — table cells, timelines, prose. Needed
   because the global `a` rule strips colour and underline, and every content
   link carries the same cue so one row never reads as more clickable than
   another. Buttons and `.panel__link` are the deliberate exceptions. */
.link {
    color: var(--color-secondary);
    font-weight: 600;
    text-decoration: underline;
    text-underline-offset: 2px;
}

.link:hover,
.link:focus-visible {
    text-decoration: none;
}

.scan-panel,
.detail-card,
.queue-list,
.timeline,
.key-value-list,
.stack-block,
.receipt-line-list,
.workflow-list {
    display: grid;
    gap: 16px;
}

/* Photo evidence capture (mobile). Two label-buttons each wrap a file input:
   the first carries capture="environment" so a tap opens the camera, the
   second opens the gallery. The input is visually hidden but stays in the
   tab order, so the label shows focus on its behalf. */
.photo-capture {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 10px;
    margin-bottom: 12px;
}

.photo-capture__form {
    display: grid;
    gap: 8px;
    min-width: 0;
}

.photo-capture__pick {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    cursor: pointer;
}

.photo-capture__icon {
    display: inline-flex;
}

.photo-capture__icon svg {
    width: 20px;
    height: 20px;
}

.photo-capture__input {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    border: 0;
    opacity: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
}

.photo-capture__pick:focus-within {
    outline: 2px solid currentColor;
    outline-offset: 2px;
}

.photo-capture__form.is-submitting .photo-capture__pick {
    opacity: 0.6;
    pointer-events: none;
}

/* Per-device preference row on the mobile hub. */
.mobile-toggle {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    cursor: pointer;
}

.mobile-toggle input {
    flex: 0 0 auto;
    margin-top: 2px;
}

.mobile-toggle > span {
    display: grid;
    gap: 4px;
}

.photo-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
    gap: 12px;
}

.photo-grid__item {
    display: grid;
    gap: 8px;
    margin: 0;
}

.photo-grid__item img {
    aspect-ratio: 1 / 1;
    border: 1px solid var(--color-border);
    border-radius: 8px;
    display: block;
    height: auto;
    object-fit: cover;
    width: 100%;
}

.scan-panel label,
.form-grid span,
.detail-card label {
    color: var(--color-text-secondary);
    display: block;
    font-size: var(--font-size-sm);
    font-weight: 500;
    margin-bottom: 8px;
}

.data-table {
    overflow: auto;
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-md);
}

/* Tall displays (2560×2160 half-5K2K primary window, 3440×1440 ultrawide):
   bound the table scrollport to the viewport so the page header, stats,
   filters, and side panels stay on screen and the sticky header row engages.
   max-height (not height) leaves short tables hugging their rows. */
@media (min-height: 1200px) {
    .app-shell .data-table {
        max-height: calc(100vh - var(--banner-h) - var(--table-offset));
    }
}

@media print {
    .data-table {
        max-height: none;
    }
}

.data-table table {
    width: 100%;
    /* separate, not collapse: collapsed borders don't travel with the sticky
       header row, which would lose its underline once the table scrolls. */
    border-collapse: separate;
    border-spacing: 0;
    min-width: 720px;
}

.data-table th,
.data-table td {
    border-bottom: 1px solid var(--color-border-subtle);
    padding: 10px 16px;
    text-align: left;
    vertical-align: middle;
}

.data-table th {
    position: sticky;
    top: 0;
    background: var(--color-bg);
    color: var(--color-text-secondary);
    font-size: var(--font-size-sm);
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.data-table tbody tr:hover {
    background: #f8f9fb;
}

.data-table__empty {
    color: var(--color-text-muted);
    text-align: center;
}

/* A numeric column's header sits flush over its right-aligned figures. The
   data_table component adds this itself for any column whose cells carry
   .u-align-right; hand-written tables set it on the <th> directly. */
.data-table th.data-table__numeric {
    text-align: right;
}

/* Interactive tables (data_table with 'interactive' => true + app.js hooks) */
.data-table-shell__controls {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px;
    margin-bottom: 12px;
}

.data-table-shell__search {
    flex: 0 1 280px;
    min-width: 180px;
    width: auto;
}

.data-table-shell__filter {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    color: var(--color-text-secondary);
    font-size: var(--font-size-sm);
}

.data-table-shell__filter select {
    width: auto;
}

.data-table-shell__count {
    margin-left: auto;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    white-space: nowrap;
}

/* Compact single-row lookup form (e.g. the serial trace above the serials
   table): input, submit, and hint on one wrapping line, left-aligned. */
.inline-lookup {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 12px;
    margin-bottom: 12px;
}

.inline-lookup input {
    flex: 0 1 280px;
    min-width: 180px;
    width: auto;
}

.inline-lookup .form-hint {
    margin: 0;
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
}

.data-table-shell__pagination {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 12px;
    margin-top: 12px;
}

.data-table-shell__pagination button[disabled] {
    opacity: 0.5;
    cursor: default;
}

.data-table-shell__pages {
    display: inline-flex;
    align-items: center;
    gap: 4px;
}

.data-table-shell__page-number {
    min-width: 32px;
    height: 32px;
    padding: 0 8px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--color-text-secondary);
    font-size: var(--font-size-sm);
    cursor: pointer;
    transition: background var(--transition-fast), border-color var(--transition-fast);
}

.data-table-shell__page-number:hover {
    background: rgba(0, 113, 188, 0.08);
    border-color: var(--color-secondary);
}

.data-table-shell__page-number.is-current {
    background: var(--color-secondary);
    border-color: var(--color-secondary);
    color: #fff;
    cursor: default;
}

.data-table-shell__page-gap {
    color: var(--color-text-muted);
    font-size: var(--font-size-sm);
    padding: 0 2px;
}

.data-table th[data-table-sort] {
    cursor: pointer;
    user-select: none;
    white-space: nowrap;
}

.data-table th[data-table-sort]::after {
    content: '\2195';
    margin-left: 6px;
    opacity: 0.35;
    font-size: 11px;
}

.data-table th[aria-sort='ascending']::after {
    content: '\25B2';
    opacity: 1;
}

.data-table th[aria-sort='descending']::after {
    content: '\25BC';
    opacity: 1;
}

/* Header hint markers. The bubble itself is not a pseudo-element on the
   marker — it lives on <body> with fixed positioning, placed by
   initTableHints(), for the same reason the row Actions menu does:
   .data-table is a scroll container, which clipped it. Worse, an absolutely
   positioned bubble hanging below a sticky header counts towards the
   container's scrollable area even at opacity 0, so a table only a row or two
   tall grew a scrollbar it had no content for. */
.data-table__hint {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 14px;
    height: 14px;
    margin-left: 5px;
    border: 1px solid currentColor;
    border-radius: 50%;
    color: var(--color-text-muted);
    font-size: 10px;
    font-weight: 600;
    line-height: 1;
    letter-spacing: normal;
    text-transform: none;
    vertical-align: middle;
    cursor: help;
}

/* The shared bubble. One per page, moved to whichever marker is hovered or
   focused; initTableHints() flips it above the marker when the viewport
   bottom is close and keeps it clear of both side edges. */
.table-tooltip {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 70;
    max-width: 260px;
    background: var(--color-text);
    color: #fff;
    font-family: var(--font-family);
    font-size: 12px;
    font-weight: 500;
    line-height: 1.4;
    letter-spacing: normal;
    text-transform: none;
    white-space: normal;
    text-align: left;
    padding: 6px 10px;
    border-radius: var(--radius-sm);
    box-shadow: 0 4px 12px rgba(15, 23, 42, 0.18);
    pointer-events: none;
    visibility: hidden;
    opacity: 0;
    transition: opacity 0.12s ease, visibility 0s linear 0.12s;
}

.table-tooltip[data-open] {
    visibility: visible;
    opacity: 1;
    transition: opacity 0.12s ease;
}

.status-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 22px;
    border-radius: 999px;
    font-size: var(--font-size-xs);
    font-weight: 600;
    padding: 4px 10px;
    text-transform: uppercase;
    white-space: nowrap;
}

/* A badge carrying a value under its label — "Not before" over a date. Stacking
   keeps the pill roughly as wide as the plain badges beside it in the same
   column instead of stretching the cell to fit one long line. The second line
   is the value, so it carries the weight; the label above it reads as the
   caption and steps back. */
.status-badge--stacked {
    flex-direction: column;
    gap: 1px;
    padding: 4px 12px;
    line-height: 1.2;
    text-align: center;
    white-space: normal;
}

.status-badge--stacked > :first-child {
    font-weight: 500;
    opacity: 0.8;
}

.status-badge__sub {
    font-weight: 700;
}

/* Keeps several badges together as one flex item — in a .panel__header they
   would otherwise be spread apart by its space-between. */
.badge-row {
    display: inline-flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
}

.status-badge--success {
    background: var(--color-success-bg);
    color: var(--color-success);
}

.status-badge--warning {
    background: var(--color-warning-bg);
    color: var(--color-warning);
}

.status-badge--error {
    background: var(--color-error-bg);
    color: var(--color-error);
}

/* The one badge that must break out of the tinted family. Every other tone is
   pale-on-pale, which is right for a state the reader scans past; this is for a
   state someone has to act on and must not miss in a dense table — solid fill,
   like .button--danger. Use it sparingly: a page with two of these has none. */
.status-badge--critical {
    background: var(--color-error);
    color: #fff;
}

.status-badge--info {
    background: var(--color-info-bg);
    color: var(--color-info);
}

.status-badge--quarantine {
    background: var(--color-quarantine-bg);
    color: var(--color-quarantine);
}

.status-badge--neutral {
    background: var(--color-neutral-bg);
    color: var(--color-text-secondary);
}

/* A badge that is also the way in to its own detail — the receipt line's
   Put-away badge, where the bins and quantities behind it will not fit in the
   cell. Resets the button chrome and wears a chevron so it reads as a trigger
   rather than a plain state. */
.status-badge--action {
    border: 0;
    font-family: inherit;
    gap: 5px;
    cursor: pointer;
    transition: filter var(--transition-fast);
}

.status-badge--action::after {
    content: "";
    width: 5px;
    height: 5px;
    border-right: 1.5px solid currentColor;
    border-bottom: 1.5px solid currentColor;
    transform: translateY(-1px) rotate(-45deg);
    opacity: 0.7;
}

.status-badge--action:hover {
    filter: brightness(0.94);
}

.status-badge--action:focus-visible {
    outline: 2px solid var(--color-secondary);
    outline-offset: 2px;
}

.list-grid {
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
}

/* Capped three-across variant (reports gateway): auto-fit packs four cards
   across on a wide screen, which is too dense for tiles that carry a few
   lines of copy. Collapses back to the auto-fit flow on narrower screens. */
.list-grid--three {
    grid-template-columns: repeat(3, minmax(0, 1fr));
}

@media (max-width: 1280px) {
    .list-grid--three {
        grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    }
}

.list-card {
    align-items: flex-start;
    padding: 18px 20px;
}

/* The text block yields to the badge without crushing: the badge keeps its
   nowrap width and the copy wraps across the full remaining card width. */
.list-card > div {
    flex: 1;
    min-width: 0;
}

.list-card .status-badge {
    flex-shrink: 0;
}

.list-card strong,
.timeline strong,
.queue-list__item strong,
.user-chip strong {
    display: block;
    margin-bottom: 4px;
}

.list-card p,
.timeline p,
.queue-list__item p {
    font-size: var(--font-size-sm);
    margin: 0;
}

/* Stocktake-by-customer report: one section per customer, each a header
   (customer link + status totals) over its balances table. A rule between
   sections keeps each customer's header visually tied to its own table. */
.stocktake-groups {
    display: flex;
    flex-direction: column;
    gap: 28px;
}

.stocktake-group + .stocktake-group {
    border-top: 1px solid var(--color-border);
    padding-top: 28px;
}

.stocktake-group__header {
    margin-bottom: 12px;
}

.stocktake-group__header p {
    color: var(--color-text-secondary);
    font-size: var(--font-size-sm);
    margin: 4px 0 0;
}

.timeline {
    list-style: none;
    margin: 0;
    padding: 0;
}

.timeline li {
    border-left: 3px solid var(--color-border);
    padding-left: 14px;
}

.timeline span,
.queue-list__item span,
.key-value-list dt,
.app-footer,
.app-footer__meta {
    font-size: var(--font-size-xs);
}

.queue-list__item {
    align-items: center;
    border: 1px solid var(--color-border);
    border-left: 3px solid transparent;
    border-radius: var(--radius-md);
    padding: 14px 16px;
}

.queue-list__item.is-selected {
    background: var(--color-info-bg);
    border-left-color: var(--color-secondary);
}

.queue-list__meta {
    align-items: center;
    flex-wrap: wrap;
}

.form-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    margin-bottom: 24px;
}

.detail-grid {
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    align-items: start;
    margin-bottom: 24px;
}

.form-grid--wide {
    align-items: start;
}

.form-grid--compact {
    gap: 16px;
}

/* Single-field lookup rows (the receiving scan box). The two-column .form-grid
   split stretches one field across half a full-width panel and leaves its button
   stranded at the far edge, aligned with the label rather than the input — so
   these lay out as a flex row instead: a capped field with the button on the
   same bottom edge (input and .button are both 36px tall). */
.form-grid--inline {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 12px;
}

.form-grid--inline label {
    flex: 1 1 320px;
    max-width: 420px;
}

.form-grid__full {
    grid-column: 1 / -1;
}

.checkbox-field {
    display: flex;
    align-items: center;
    gap: 10px;
}

.checkbox-field input {
    width: 18px;
    height: 18px;
    margin: 0;
}

.checkbox-field span {
    margin: 0;
}

/* Choice group — mutually exclusive radio options shown as selectable cards
   (e.g. receipt stock ownership). Strips the default fieldset chrome, then
   lays the options out side by side when the panel is wide enough. */
.choice-group {
    border: 0;
    margin: 0;
    min-width: 0;
    padding: 0;
}

.choice-group > legend {
    color: var(--color-text-secondary);
    font-size: var(--font-size-sm);
    font-weight: 500;
    margin-bottom: 8px;
    padding: 0;
}

.choice-group__options {
    display: grid;
    gap: 12px;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}

.choice {
    align-items: start;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    cursor: pointer;
    display: grid;
    gap: 12px;
    grid-template-columns: auto minmax(0, 1fr);
    padding: 14px 16px;
    transition: background var(--transition-fast), border-color var(--transition-fast), box-shadow var(--transition-fast);
}

.choice:hover {
    border-color: var(--color-secondary);
}

.choice:focus-within {
    border-color: var(--color-secondary);
    box-shadow: 0 0 0 3px rgba(0, 113, 188, 0.15);
}

/* :has() carries the checked state to the card; without it the option is
   still usable, it just loses the tint. */
.choice:has(input:checked) {
    background: var(--color-info-bg);
    border-color: var(--color-secondary);
    box-shadow: inset 0 0 0 1px var(--color-secondary);
}

.choice input {
    /* 3px nudges the control onto the optical centre of the title line. */
    margin: 3px 0 0;
}

.choice__body {
    display: grid;
    gap: 2px;
}

.choice span {
    margin: 0;
}

.choice__title {
    color: var(--color-text);
    font-weight: 600;
}

.choice__hint {
    color: var(--color-text-secondary);
    font-size: var(--font-size-sm);
    line-height: 1.45;
}

.form-actions,
.table-actions {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
}

.table-actions form {
    margin: 0;
}

.table-meta {
    color: var(--color-text-muted);
    font-size: var(--font-size-xs);
    margin-top: 4px;
}

.stack-block p {
    color: var(--color-text-secondary);
    margin: 0;
}

.subsection-header {
    border-top: 1px solid var(--color-border-subtle);
    margin-top: 24px;
    padding-top: 24px;
    /* Matches .panel__header so the content below breathes the same way. */
    margin-bottom: 20px;
}

.empty-state-inline {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 16px;
    background: var(--color-bg);
    border: 1px dashed var(--color-border);
    border-radius: var(--radius-md);
    padding: 16px;
}

.empty-state-inline--success {
    border-color: var(--color-success);
    background: var(--color-success-bg);
}

.empty-state-inline p {
    margin: 0;
}

/* A lead-in note or inline empty state that sits directly above a table
   (the dispatch lines panel) needs room before the search toolbar. */
.empty-state-inline + .data-table-shell,
.form-help + .data-table-shell {
    margin-top: 16px;
}

.receipt-line-card {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    padding: 18px;
}

.receipt-line-card--active {
    border-color: var(--color-secondary);
    box-shadow: 0 0 0 2px rgba(54, 94, 195, 0.12);
}

.receipt-line-card--complete,
.mobile-line-card--complete {
    opacity: 0.75;
    background: rgba(0, 0, 0, 0.02);
}

/* A dispatch request parked behind a not-before date: present, searchable, and
   still real demand, but not work for today. Same dim-but-legible treatment as
   a completed line card above — enough to skip past when scanning the queue,
   not so much that the row becomes hard to read. Hovering restores it in full,
   because the moment someone is looking at this row deliberately the whole
   point of the dimming has passed. */
.data-table tbody tr.is-not-before {
    opacity: 0.72;
    background: rgba(0, 0, 0, 0.02);
}

.data-table tbody tr.is-not-before:hover,
.data-table tbody tr.is-not-before:focus-within {
    opacity: 1;
}

.receipt-line-divider {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 6px 0 0;
    color: var(--color-text-secondary);
    font-size: 0.8rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.receipt-line-divider::before,
.receipt-line-divider::after {
    content: '';
    flex: 1;
    border-top: 1px solid var(--color-border);
}

.receipt-line-card__summary,
.receipt-line-card__metrics {
    display: flex;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
}

.receipt-line-card__summary {
    margin-bottom: 18px;
}

.receipt-line-card__summary p,
.receipt-line-card__metrics span {
    margin: 0;
    color: var(--color-text-secondary);
}

.receipt-line-card__meta {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-top: 8px;
}

.receipt-line-card__meta span,
.receipt-line-card__context {
    color: var(--color-text-secondary);
    font-size: var(--font-size-xs);
}

.receipt-line-card__context {
    background: var(--color-bg);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-md);
    padding: 12px 14px;
}

.receive-form {
    padding-top: 10px;
}

.workflow-list {
    margin: 0;
    padding-left: 18px;
}

.workflow-list li {
    color: var(--color-text-secondary);
}

.detail-card {
    padding: 20px;
}

.key-value-list {
    margin: 0;
}

.key-value-list div {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    border-bottom: 1px solid var(--color-border-subtle);
    padding-bottom: 10px;
}

.key-value-list dt,
.key-value-list dd {
    margin: 0;
}

.key-value-list dd {
    color: var(--color-text);
    font-weight: 600;
    text-align: right;
}

.app-footer {
    align-items: center;
    border-top: 1px solid var(--color-border);
    margin-top: 32px;
    padding: 20px 0 0;
}

.app-footer span {
    color: var(--color-text-secondary);
}

.app-footer__meta {
    display: flex;
    gap: 16px;
    flex-wrap: wrap;
}

.u-mono {
    font-family: var(--font-mono);
}

.u-nowrap {
    white-space: nowrap;
}

.u-align-right {
    display: inline-block;
    min-width: 100%;
    text-align: right;
}

.u-muted {
    color: var(--color-text-muted);
}

.filter-bar {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 16px;
}

.mobile-shell {
    max-width: 720px;
    margin: 0 auto;
    min-height: 100vh;
    padding: 16px 16px 32px;
}

.mobile-header {
    display: flex;
    flex-direction: column;
    gap: 16px;
    margin-bottom: 20px;
}

.mobile-header__row {
    display: flex;
    justify-content: space-between;
    gap: 10px;
}

.mobile-header__back,
.mobile-header__desktop {
    min-width: 96px;
    text-align: center;
}

.mobile-header__copy {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.mobile-title {
    font-size: 1.75rem;
    line-height: 1.15;
    margin: 0;
}

.mobile-description {
    color: var(--color-text-secondary);
    margin: 0;
}

/* The mobile shell has no footer chrome, so the build stamp sits on its own
   quiet line at the bottom of the page — enough for an operator to read it
   back during an incident, without competing with the workflow above. */
.mobile-footer {
    color: var(--color-text-secondary);
    font-size: 0.75rem;
    margin: 24px 0 0;
    padding-top: 16px;
    border-top: 1px solid var(--color-border);
    text-align: center;
}

.mobile-stack {
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.mobile-stack--spacious {
    gap: 20px;
}

.mobile-card,
.mobile-line-card,
.mobile-stat-card,
.mobile-list-card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: 14px;
    box-shadow: var(--shadow-sm);
}

/* Parked behind a not-before date. Still tappable and still pickable — just
   not what the operator should be working on next. */
.mobile-list-card--not-before {
    opacity: 0.72;
}

.mobile-card,
.mobile-line-card {
    padding: 18px;
}

.mobile-card--focus,
.mobile-line-card--focused {
    border-color: var(--color-secondary);
    box-shadow: 0 0 0 2px rgba(0, 113, 188, 0.14);
}

.mobile-card__header,
.mobile-line-card__header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 12px;
    margin-bottom: 14px;
}

.mobile-card__header h2,
.mobile-line-card__header p,
.mobile-line-card__header strong {
    margin: 0;
}

.mobile-form {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.mobile-form--tight {
    gap: 12px;
}

.mobile-form span {
    display: block;
    font-size: var(--font-size-sm);
    font-weight: 600;
    margin-bottom: 6px;
}

.mobile-actions {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
}

.mobile-button--wide {
    width: 100%;
    justify-content: center;
}

.mobile-actions .mobile-button--wide {
    flex: 1 1 180px;
    width: auto;
}

.mobile-stat-grid {
    display: grid;
    gap: 12px;
    grid-template-columns: repeat(3, minmax(0, 1fr));
}

.mobile-stat-card {
    padding: 14px;
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.mobile-stat-card strong {
    font-size: 1.25rem;
}

.mobile-stat-card span {
    font-size: var(--font-size-sm);
    font-weight: 600;
}

.mobile-stat-card p,
.mobile-list-card p,
.mobile-bin-banner p,
.mobile-empty-state p,
.mobile-inline-message,
.mobile-chip {
    color: var(--color-text-secondary);
    margin: 0;
}

.mobile-list {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.mobile-list-card {
    padding: 16px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.mobile-list-card__header,
.mobile-list-card__meta,
.mobile-chip-row {
    display: flex;
    justify-content: space-between;
    gap: 8px;
    flex-wrap: wrap;
}

.mobile-key-value {
    margin: 0;
}

.mobile-key-value div {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    padding: 10px 0;
    border-bottom: 1px solid var(--color-border-subtle);
}

.mobile-key-value dt,
.mobile-key-value dd {
    margin: 0;
}

.mobile-key-value dd {
    text-align: right;
    font-weight: 600;
}

.mobile-bin-banner {
    background: var(--color-info-bg);
    border: 1px solid rgba(0, 113, 188, 0.14);
    border-radius: 12px;
    padding: 14px;
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.mobile-bin-banner--sticky {
    position: sticky;
    top: 12px;
    z-index: 10;
    box-shadow: var(--shadow-md);
}

.mobile-bin-banner--compact {
    margin-bottom: 4px;
}

.mobile-chip-row {
    margin-bottom: 14px;
}

.mobile-chip-row--tight {
    margin-bottom: 0;
}

.mobile-chip {
    background: var(--color-bg);
    border-radius: 999px;
    padding: 6px 10px;
    font-size: var(--font-size-xs);
}

.mobile-focus-copy {
    color: var(--color-text-secondary);
    margin: 0 0 12px;
}

.mobile-inline-message {
    border-radius: 10px;
    padding: 12px 14px;
    font-size: var(--font-size-sm);
}

.mobile-inline-message--success {
    background: var(--color-success-bg);
    color: var(--color-success);
}

.mobile-inline-message--warning {
    background: var(--color-warning-bg);
    color: var(--color-warning);
}

.mobile-inline-message--error {
    background: var(--color-error-bg);
    color: var(--color-error);
}

.mobile-inline-message--info {
    background: var(--color-info-bg);
    color: var(--color-info);
}

.mobile-empty-state {
    padding: 18px;
}

/* ── Wide displays (2560×2160 half-5K2K primary window and up) ─────────────
   Cap the side column, stat cards, and form columns so extra width goes to
   the primary table/form column instead of inflating secondary content. */
@media (min-width: 2200px) {
    /* Split layouts: fluid main column + capped side column. Auto-placement
       pairs each .panel--wide (col 1) with the following side panel (col 2),
       matching every existing --split page's markup order. */
    .content-grid--split,
    .content-grid--receiving-detail {
        grid-template-columns: minmax(0, 1fr) minmax(360px, 460px);
    }

    .content-grid--split .panel--wide,
    .content-grid--receiving-detail .panel--wide {
        grid-column: 1;
    }

    .content-grid--split .panel:not(.panel--wide),
    .content-grid--receiving-detail .panel:not(.panel--wide) {
        grid-column: 2;
    }

    .content-grid__aside {
        grid-column: 2;
    }

    /* Cap stat cards instead of letting auto-fit inflate them to ~550px. */
    .stats-grid {
        grid-template-columns: repeat(auto-fit, minmax(240px, 380px));
    }

    .stats-grid--compact {
        grid-template-columns: repeat(auto-fit, minmax(220px, 340px));
    }

    /* Three columns keeps inputs a sane width in wide/full panels; forms in
       the capped side column stay two-column. */
    .panel--wide .form-grid,
    .panel--span-8 .form-grid,
    .panel--span-12 .form-grid {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}

/* Between the 1280px single-column collapse and comfortable split widths:
   the 8/4 split would crush the primary table beside a ~350px side column,
   so primary panels take the full row and side panels pair up below. The
   1281px lower bound is load-bearing — these selectors outrank the 1280px
   query's .content-grid > .panel, so they must not apply inside it. */
@media (min-width: 1281px) and (max-width: 1500px) {
    .content-grid--split .panel--wide,
    .content-grid--receiving-detail .panel--wide {
        grid-column: 1 / -1;
    }

    .content-grid--split .panel:not(.panel--wide),
    .content-grid--receiving-detail .panel:not(.panel--wide) {
        grid-column: span 6;
    }

    .content-grid__aside {
        grid-column: 1 / -1;
    }
}

@media (max-width: 1440px) {
    .global-search {
        width: 320px;
    }
}

@media (max-width: 1280px) {
    .topbar,
    .page-header,
    .panel__header,
    .app-footer {
        flex-direction: column;
        align-items: flex-start;
    }

    .topbar__left,
    .topbar__centre,
    .topbar__right,
    .global-search {
        width: 100%;
    }

    .topbar__right {
        justify-content: space-between;
    }

    .global-search {
        min-width: 0;
    }

    .content-grid,
    .detail-grid,
    .form-grid {
        grid-template-columns: 1fr;
    }

    .content-grid > .panel,
    .content-grid__aside {
        grid-column: 1 / -1;
    }
}

@media (max-width: 640px) {
    .mobile-shell {
        padding: 12px 12px 28px;
    }

    .mobile-stat-grid {
        grid-template-columns: 1fr;
    }

    .mobile-header__row,
    .mobile-actions {
        flex-direction: column;
    }

    .mobile-button--wide,
    .mobile-header__back,
    .mobile-header__desktop {
        width: 100%;
    }
}

.app-shell.is-sidebar-collapsed .sidebar__brand,
.sidebar.collapsed .sidebar__brand {
    padding: 0;
    justify-content: center;
}

.app-shell.is-sidebar-collapsed .sidebar__brand-text,
.app-shell.is-sidebar-collapsed .sidebar__label,
.app-shell.is-sidebar-collapsed .sidebar__section-label,
.sidebar.collapsed .sidebar__brand-text,
.sidebar.collapsed .sidebar__label,
.sidebar.collapsed .sidebar__section-label {
    opacity: 0;
    width: 0;
    overflow: hidden;
}

/* In rail mode there is no label to click, so a folded section would be
   unreachable — every section shows all of its items and the toggle goes away. */
.app-shell.is-sidebar-collapsed .sidebar__section-toggle,
.sidebar.collapsed .sidebar__section-toggle {
    display: none;
}

.app-shell.is-sidebar-collapsed .sidebar__section.is-folded .sidebar__section-items,
.sidebar.collapsed .sidebar__section.is-folded .sidebar__section-items {
    display: block;
}

.app-shell.is-sidebar-collapsed .sidebar__item,
.app-shell.is-sidebar-collapsed .sidebar__collapse-button,
.sidebar.collapsed .sidebar__item,
.sidebar.collapsed .sidebar__collapse-button {
    justify-content: center;
    padding: 12px 0;
    border-left-width: 0;
}

.app-shell.is-sidebar-collapsed .sidebar__badge,
.sidebar.collapsed .sidebar__badge {
    position: absolute;
    top: 4px;
    right: 8px;
    min-width: 16px;
    height: 16px;
    line-height: 16px;
    font-size: 10px;
    padding: 0 4px;
}

.app-shell.is-sidebar-collapsed .sidebar__toggle-icon--expanded,
.app-shell.is-sidebar-collapsed .sidebar__toggle-text--expanded,
.sidebar.collapsed .sidebar__toggle-icon--expanded,
.sidebar.collapsed .sidebar__toggle-text--expanded {
    display: none;
}

.app-shell.is-sidebar-collapsed .sidebar__toggle-icon--collapsed,
.app-shell.is-sidebar-collapsed .sidebar__toggle-text--collapsed,
.sidebar.collapsed .sidebar__toggle-icon--collapsed,
.sidebar.collapsed .sidebar__toggle-text--collapsed {
    display: inline;
}

/* Nav tooltips. Must be position: fixed, not absolute: the bubble sits outside
   the sidebar, and .sidebar { overflow: hidden } + .sidebar__nav { overflow-x:
   hidden } clip any absolutely-positioned child. A fixed box with top: auto
   keeps the item's own vertical (static) position but escapes the clipping. */
.sidebar__item:hover::after,
.sidebar__collapse-button:hover::after {
    content: attr(data-tooltip);
    position: fixed;
    left: 248px;
    background: var(--color-text);
    color: #fff;
    font-size: 13px;
    font-weight: 500;
    padding: 4px 10px;
    border-radius: var(--radius-sm);
    white-space: nowrap;
    z-index: 200;
    pointer-events: none;
    animation: sidebar-tooltip-in 120ms ease-out 400ms both;
}

.app-shell.is-sidebar-collapsed .sidebar__item:hover::after,
.app-shell.is-sidebar-collapsed .sidebar__collapse-button:hover::after,
.sidebar.collapsed .sidebar__item:hover::after,
.sidebar.collapsed .sidebar__collapse-button:hover::after {
    left: 72px;
    animation-delay: 150ms;
}

@keyframes sidebar-tooltip-in {
    from {
        opacity: 0;
        transform: translateX(-4px);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

/* ---- Receiving workflow enhancements ---- */

.receipt-progress {
    margin: var(--space-md) 0;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.receipt-progress__meta {
    display: flex;
    justify-content: space-between;
    font-size: var(--font-size-sm);
    font-weight: 600;
    color: var(--color-text-secondary);
}

.receipt-progress__track {
    height: 8px;
    border-radius: 999px;
    background: var(--color-neutral-bg);
    overflow: hidden;
}

.receipt-progress__fill {
    height: 100%;
    border-radius: 999px;
    background: var(--color-secondary);
    transition: width var(--transition-base);
}

.receipt-progress--complete .receipt-progress__fill {
    background: var(--color-success);
}

.mobile-card--complete,
.detail-card--complete {
    border-color: var(--color-success);
    box-shadow: 0 0 0 2px rgba(22, 163, 74, 0.12);
}

.mobile-customer {
    font-weight: 600;
    margin: 0 0 var(--space-sm);
}

/* Collapsed receipt meta / "scan serials instead" disclosures */
.mobile-meta,
.flag-issue,
.disclosure {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    padding: 10px 12px;
    background: var(--color-surface);
}

.mobile-meta > summary,
.flag-issue > summary,
.disclosure > summary {
    cursor: pointer;
    font-size: var(--font-size-sm);
    font-weight: 600;
    color: var(--color-text-secondary);
    list-style-position: inside;
    min-height: 24px;
}

.flag-issue {
    background: var(--color-warning-bg);
    border-color: var(--color-warning);
}

.flag-issue > summary {
    color: var(--color-warning);
}

.flag-issue[open] > label,
.flag-issue[open] > .scan-camera,
.mobile-meta[open] > *,
.disclosure[open] > *:not(summary) {
    margin-top: 10px;
}

/* Primary-action disclosure pill (e.g. Put Away on a receipt line): solid
   primary while collapsed so it reads like the primary buttons beside it;
   reverts to a bordered panel when open so the form inside sits on a normal
   surface. */
.disclosure--primary:not([open]) {
    background: var(--color-primary);
    border-color: var(--color-primary);
}

.disclosure--primary:not([open]) > summary {
    color: #fff;
}

.disclosure--primary:not([open]):hover {
    background: var(--color-primary-light);
    border-color: var(--color-primary-light);
}

.disclosure--primary[open] {
    border-color: var(--color-primary);
}

.disclosure--primary[open] > summary {
    color: var(--color-primary);
}

/* Supervisory line actions (amend / force put-away / edit): one compact row of
   collapsed pills instead of stacked full-width bars; an opened pill expands to
   take the full row while the others stay collapsed beside it. */
.line-actions {
    display: flex;
    flex-wrap: wrap;
    /* Centred, not flex-start: the row mixes collapsed pills with the primary
       receive button, which is a couple of pixels taller. */
    align-items: center;
    gap: 8px;
    margin-top: 14px;
}

.line-actions > .flag-issue,
.line-actions > .disclosure {
    padding: 6px 12px;
}

.line-actions > .flag-issue[open],
.line-actions > .disclosure[open] {
    flex-basis: 100%;
    padding: 10px 12px;
}

.line-actions > .form-help {
    flex-basis: 100%;
    margin: 0;
}

/* Scan input + camera button row */
.scan-field {
    display: flex;
    gap: 8px;
    align-items: stretch;
}

.scan-field input {
    flex: 1 1 auto;
    min-width: 0;
}

.scan-camera {
    flex: 0 0 auto;
    white-space: nowrap;
}

/* Camera scanner overlay */
.scan-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.78);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
    padding: var(--space-md);
}

.scan-overlay__panel {
    width: min(420px, 100%);
    background: var(--color-surface);
    border-radius: var(--radius-lg);
    padding: var(--space-md);
    display: flex;
    flex-direction: column;
    gap: var(--space-md);
}

.scan-overlay__reader {
    width: 100%;
}

.scan-overlay__status {
    margin: 0;
    text-align: center;
    font-size: 0.9rem;
    color: var(--color-text-secondary);
    overflow-wrap: anywhere;
}

.scan-overlay__close {
    width: 100%;
    justify-content: center;
}

.lightbox {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.85);
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-sm);
    z-index: 1100;
    padding: var(--space-lg);
}

.lightbox__figure {
    margin: 0;
    max-width: min(1100px, 100%);
    max-height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-sm);
}

.lightbox__image {
    max-width: 100%;
    max-height: 82vh;
    object-fit: contain;
    border-radius: var(--radius-md);
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);
}

.lightbox__caption {
    color: rgba(255, 255, 255, 0.85);
    font-size: var(--font-size-sm);
    text-align: center;
}

.lightbox__close,
.lightbox__nav {
    appearance: none;
    border: 0;
    cursor: pointer;
    color: #fff;
    background: rgba(255, 255, 255, 0.12);
    border-radius: var(--radius-md);
    line-height: 1;
}

.lightbox__close:hover,
.lightbox__nav:hover {
    background: rgba(255, 255, 255, 0.24);
}

.lightbox__close {
    position: absolute;
    top: var(--space-md);
    right: var(--space-md);
    width: 44px;
    height: 44px;
    font-size: var(--font-size-xl);
}

.lightbox__nav {
    flex: 0 0 auto;
    width: 48px;
    height: 64px;
    font-size: var(--font-size-xl);
}

.lightbox__nav[hidden] {
    visibility: hidden;
}

@media (max-width: 640px) {
    .lightbox {
        padding: var(--space-sm);
    }

    .lightbox__nav {
        width: 40px;
        height: 52px;
    }
}

/* Larger tap targets for gloved warehouse use */
@media (max-width: 640px) {
    .mobile-form input,
    .mobile-form select,
    .mobile-form textarea,
    .mobile-button--wide,
    .scan-camera {
        min-height: 48px;
    }

    .mobile-meta > summary,
    .flag-issue > summary,
    .disclosure > summary {
        min-height: 44px;
        display: flex;
        align-items: center;
    }
}

/* == Auth (login) == */

.auth-body {
    overflow: auto;
}

.auth-shell {
    display: flex;
    min-height: 100vh;
}

.auth-hero {
    flex: 1 1 55%;
    position: relative;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: 48px 56px;
    color: #fff;
    background:
        radial-gradient(ellipse 90% 70% at 15% 10%, rgba(0, 113, 188, 0.28), transparent 60%),
        linear-gradient(160deg, var(--color-primary-dark), var(--color-primary) 55%, var(--color-primary-light));
}

.auth-hero__brand {
    position: relative;
    z-index: 1;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.auth-hero__eyebrow {
    margin: 0;
    font-size: var(--font-size-xs);
    font-weight: 700;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--color-bronze-light);
}

.auth-hero__wordmark {
    margin: 0;
    font-size: clamp(2rem, 3.5vw, 2.6rem);
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    line-height: 1.05;
}

.auth-hero__tagline {
    margin: 0;
    font-size: var(--font-size-md);
    color: rgba(255, 255, 255, 0.78);
    max-width: 42ch;
}

.auth-hero__myth {
    position: absolute;
    z-index: 1;
    left: 56px;
    right: 56px;
    bottom: 24px;
    margin: 0;
    max-width: 58ch;
    font-size: var(--font-size-xs);
    line-height: 1.6;
    color: rgba(255, 255, 255, 0.55);
}

.auth-hero__art {
    position: absolute;
    right: -40px;
    bottom: -20px;
    width: min(560px, 85%);
    height: auto;
    pointer-events: none;
}

.auth-main {
    flex: 1 1 45%;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 48px 24px;
    background: var(--color-bg);
}

.auth-column {
    width: 100%;
    max-width: 420px;
    display: flex;
    flex-direction: column;
    gap: 20px;
}

.auth-column .alert {
    margin: 0;
}

.auth-card {
    position: relative;
    overflow: hidden;
    background: var(--color-surface);
    border: 1px solid var(--color-border-subtle);
    border-radius: 16px;
    padding: 28px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05), 0 16px 40px -12px rgba(0, 60, 94, 0.18);
}

.auth-card::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 3px;
    background: linear-gradient(90deg, var(--color-bronze-light), var(--color-bronze));
}

.button--microsoft {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    width: 100%;
    background: #fff;
    border-color: var(--color-border);
    color: var(--color-text);
    font-weight: 600;
}

.button--microsoft:hover {
    border-color: var(--color-secondary);
    box-shadow: var(--shadow-sm);
}

.button--microsoft:focus-visible {
    outline: 2px solid var(--color-secondary);
    outline-offset: 2px;
}

.ms-logo {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
}

.button--block {
    width: 100%;
}

.auth-card__section-label {
    margin: 0 0 10px;
    font-size: var(--font-size-xs);
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--color-primary);
}

.auth-divider {
    display: flex;
    align-items: center;
    margin: 20px 0;
    color: var(--color-text-muted);
    font-size: var(--font-size-xs);
}

.auth-divider::before,
.auth-divider::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--color-border);
}

.auth-compact-brand {
    display: none;
}

.auth-compact-brand__coin {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 52px;
    height: 52px;
    border-radius: 50%;
    border: 1.5px solid rgba(217, 178, 124, 0.75);
    flex-shrink: 0;
}

.auth-compact-brand__mark {
    width: 30px;
    height: 30px;
    flex-shrink: 0;
}

.auth-compact-brand__copy {
    display: flex;
    flex-direction: column;
}

.auth-compact-brand__wordmark {
    margin: 0;
    font-size: 1.35rem;
    font-weight: 700;
    line-height: 1.1;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #fff;
}

.password-field {
    position: relative;
}

.password-field input {
    padding-right: 42px;
}

.password-toggle {
    position: absolute;
    top: 0;
    right: 0;
    height: 100%;
    width: 38px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: 0;
    padding: 0;
    color: var(--color-text-muted);
    cursor: pointer;
    border-radius: var(--radius-md);
}

.password-toggle:hover {
    color: var(--color-text-secondary);
}

.password-toggle:focus-visible {
    outline: 2px solid var(--color-secondary);
    outline-offset: -2px;
}

.password-toggle svg {
    width: 18px;
    height: 18px;
}

.password-toggle .password-toggle__icon-hide {
    display: none;
}

.password-toggle--visible .password-toggle__icon-show {
    display: none;
}

.password-toggle--visible .password-toggle__icon-hide {
    display: block;
}

.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

@media (prefers-reduced-motion: no-preference) {
    @keyframes auth-rise {
        from {
            opacity: 0;
            transform: translateY(10px);
        }
    }

    .auth-card {
        animation: auth-rise 0.45s ease-out 0.08s backwards;
    }

    .auth-hero__brand {
        animation: auth-rise 0.55s ease-out backwards;
    }
}

@media (max-width: 960px) {
    .auth-hero {
        display: none;
    }

    .auth-main {
        display: block;
        padding: 0;
    }

    .auth-compact-brand {
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 12px;
        padding: 22px 16px;
        background:
            radial-gradient(ellipse 120% 160% at 10% 0%, rgba(0, 113, 188, 0.28), transparent 60%),
            linear-gradient(160deg, var(--color-primary-dark), var(--color-primary) 70%, var(--color-primary-light));
    }

    .auth-column {
        margin: 28px auto 0;
        padding: 0 16px 48px;
    }
}

@media (max-width: 640px) {
    .auth-card input,
    .auth-card .button {
        min-height: 44px;
    }
}
/* Modal dialogs — native <dialog> opened via showModal(), so focus trapping,
   Escape-to-close, and the backdrop come from the browser. */
.modal {
    margin: auto;
    width: min(420px, calc(100vw - 32px));
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    background: var(--color-surface);
    color: var(--color-text);
    font-family: var(--font-family);
    font-size: var(--font-size-base);
    box-shadow: var(--shadow-lg);
    padding: var(--space-lg);
}

.modal::backdrop {
    background: rgba(26, 31, 43, 0.45);
}

.modal h3 {
    font-size: var(--font-size-lg);
    margin-bottom: var(--space-sm);
}

.modal p {
    color: var(--color-text-secondary);
    margin-bottom: var(--space-lg);
}

.modal__actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--space-sm);
}

/* Help pop-ups carry more prose than a confirmation dialog. */
.modal--help {
    width: min(560px, calc(100vw - 32px));
}

.modal--help .workflow-list {
    margin-bottom: var(--space-lg);
}

/* Stock action modals hold a two-column form-grid, so they run wider than the
   confirmation base. */
.modal--form {
    width: min(680px, calc(100vw - 32px));
}

/* Complete Dispatch carries a line-selection table alongside its form. */
.modal--wide {
    width: min(920px, calc(100vw - 32px));
}

.modal--form .alert {
    margin-bottom: var(--space-md);
}

/* While the serial lookup decides the modal's final shape, only the context
   strip, the loading line, and the (Cancel) actions show — the decision
   fields appear once, already configured, instead of rearranging mid-open. */
.modal--form form.is-pending > *:not(.modal-context):not(.modal-loading):not(.modal__actions) {
    display: none !important;
}

.modal-loading {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: var(--space-sm) 0;
    color: var(--color-text-secondary);
    font-size: var(--font-size-sm);
}

.modal-loading__spinner {
    flex: none;
    width: 16px;
    height: 16px;
    border: 2px solid var(--color-border);
    border-top-color: var(--color-secondary);
    border-radius: 50%;
    animation: talos-spin 700ms linear infinite;
}

@keyframes talos-spin {
    to {
        transform: rotate(360deg);
    }
}

@media (prefers-reduced-motion: reduce) {
    .modal-loading__spinner {
        animation: none;
    }
}

/* Read-only strip describing the balance an action modal operates on. */
.modal-context {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--space-sm) var(--space-md);
    margin: 0 0 var(--space-xs);
    padding: 12px 14px;
    background: var(--color-neutral-bg);
    border: 1px solid var(--color-border-subtle);
    border-radius: var(--radius-md);
}

.modal-context dt {
    font-size: var(--font-size-xs);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--color-text-muted);
    font-weight: 700;
}

.modal-context dd {
    margin: 0;
    font-size: var(--font-size-sm);
    font-weight: 600;
    overflow-wrap: anywhere;
}

/* Second line of a context slot: customer code under the name, product
   description under the SKU. Resets the mono face the SKU line uses. */
.modal-context__meta {
    font-family: var(--font-family);
    font-size: var(--font-size-xs);
    font-weight: 400;
    color: var(--color-text-muted);
}

@media (max-width: 640px) {
    .modal-context {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

/* Read-only detail dialog: a context strip, a table, a footnote — the rhythm
   the form modals get from their form grid. Scoped to [open] because the flex
   display would otherwise beat the UA's dialog:not([open]) { display: none }
   and leave the dialog on the page. */
.modal--detail[open] {
    display: flex;
    flex-direction: column;
    gap: var(--space-md);
}

.modal--detail > h3,
.modal--detail > p,
.modal--detail > .modal-context {
    margin: 0;
}

/* The row Actions button wears a dropdown caret; it flips while the menu is
   open ([aria-expanded] is maintained by createRowActionsMenu). */
[data-stock-actions]::after,
[data-reservation-actions]::after,
[data-serial-actions]::after,
[data-customer-actions]::after,
[data-dispatch-actions]::after,
[data-dispatch-manage]::after,
[data-dispatch-line-actions]::after,
[data-config-actions]::after,
[data-config-manage]::after,
[data-config-line-actions]::after,
[data-receiving-manage]::after,
[data-receiving-line-actions]::after,
[data-rma-actions]::after,
[data-rma-line-actions]::after {
    content: "";
    width: 0;
    height: 0;
    margin-left: 6px;
    border-left: 4px solid transparent;
    border-right: 4px solid transparent;
    border-top: 5px solid currentColor;
    opacity: 0.65;
    transition: transform var(--transition-fast);
}

[data-stock-actions][aria-expanded="true"]::after,
[data-reservation-actions][aria-expanded="true"]::after,
[data-serial-actions][aria-expanded="true"]::after,
[data-customer-actions][aria-expanded="true"]::after,
[data-dispatch-actions][aria-expanded="true"]::after,
[data-dispatch-manage][aria-expanded="true"]::after,
[data-dispatch-line-actions][aria-expanded="true"]::after,
[data-config-actions][aria-expanded="true"]::after,
[data-config-manage][aria-expanded="true"]::after,
[data-config-line-actions][aria-expanded="true"]::after,
[data-receiving-manage][aria-expanded="true"]::after,
[data-receiving-line-actions][aria-expanded="true"]::after,
[data-rma-actions][aria-expanded="true"]::after,
[data-rma-line-actions][aria-expanded="true"]::after {
    transform: rotate(180deg);
}

/* Per-row Actions dropdown (stock and reservations pages). Lives on <body>
   with fixed positioning (set by createRowActionsMenu) because the tables are
   scroll containers that would clip an absolutely positioned menu. */
.action-menu {
    position: fixed;
    z-index: 60;
    min-width: 220px;
    display: flex;
    flex-direction: column;
    padding: 4px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
}

.action-menu[hidden] {
    display: none;
}

.action-menu button {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--space-md);
    border: 0;
    background: none;
    color: var(--color-text);
    text-align: left;
    border-radius: var(--radius-md);
    padding: 8px 12px;
    font-size: var(--font-size-sm);
    cursor: pointer;
}

.action-menu button:hover,
.action-menu button:focus-visible {
    background: var(--color-neutral-bg);
    color: var(--color-secondary);
    outline: none;
}

.action-menu__hint {
    font-size: var(--font-size-xs);
    color: var(--color-text-muted);
    font-weight: 400;
}

/* Serial selector inside the stock action modals: toggle chips for small
   sets, scan-first (input + suggestions + paste + select-all) at scale. */
.serial-picker {
    display: flex;
    flex-direction: column;
    gap: var(--space-sm);
}

.serial-picker__label {
    font-size: var(--font-size-sm);
    font-weight: 600;
    color: var(--color-text-secondary);
}

.serial-picker__chips,
.serial-picker__suggest {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    align-items: center;
}

.serial-picker__chip {
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    color: var(--color-text-secondary);
    font-family: var(--font-mono);
    font-size: var(--font-size-xs);
    border-radius: var(--radius-md);
    padding: 4px 10px;
    cursor: pointer;
}

.serial-picker__chip.is-selected {
    background: var(--color-info-bg);
    border-color: var(--color-secondary);
    color: var(--color-secondary);
    font-weight: 600;
}

.serial-picker__many {
    font-family: var(--font-mono);
    font-size: var(--font-size-sm);
    font-weight: 600;
}

.serial-picker__scan {
    display: flex;
    flex-direction: column;
    gap: var(--space-sm);
}

.serial-picker__scan-row {
    display: flex;
    gap: var(--space-sm);
    flex-wrap: wrap;
}

.serial-picker__scan-row input {
    flex: 1 1 220px;
    min-width: 160px;
}

.serial-picker__scan-row .button {
    flex: 0 0 auto;
}

.serial-picker__none {
    font-size: var(--font-size-xs);
    color: var(--color-text-muted);
}

.serial-picker__paste summary {
    font-size: var(--font-size-sm);
    color: var(--color-text-secondary);
    cursor: pointer;
}

.serial-picker__paste textarea {
    margin-top: var(--space-sm);
    min-height: 72px;
    font-family: var(--font-mono);
    font-size: var(--font-size-sm);
}

.serial-picker__paste-actions {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    margin-top: var(--space-sm);
    flex-wrap: wrap;
}

.serial-picker__paste-report {
    font-size: var(--font-size-xs);
    color: var(--color-text-muted);
}

.serial-picker__count {
    font-size: var(--font-size-xs);
    color: var(--color-text-muted);
    margin: 0;
}

.serial-picker__count--alert {
    color: var(--color-error);
    font-weight: 600;
}

/* Type-to-search combobox over long selects ([data-combobox], wired by
   initComboboxes). The native select is kept in the flow but visually hidden
   so it stays the submitted, validated field. */
.combobox {
    position: relative;
}

.combobox__native {
    position: absolute;
    left: 0;
    top: 0;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

/* Fixed, and placed by initComboboxes(): an absolute panel is clipped by any
   scrolling ancestor — a modal, most of all — and adds to its scroll height. */
.combobox__panel {
    position: fixed;
    z-index: 50;
    max-height: 260px;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    padding: 4px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
}

.combobox__panel[hidden] {
    display: none;
}

.combobox__option {
    border: 0;
    background: none;
    color: var(--color-text);
    text-align: left;
    border-radius: var(--radius-sm);
    padding: 7px 12px;
    font-size: var(--font-size-sm);
    cursor: pointer;
}

.combobox__option:hover,
.combobox__option.is-active {
    background: var(--color-neutral-bg);
    color: var(--color-secondary);
}

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

.combobox__more {
    padding: 6px 12px;
    font-size: var(--font-size-xs);
    color: var(--color-text-muted);
}

/* ------------------------------------------------------------------ */
/* Customer portal shell — a two-row header instead of the staff sidebar. */
/* ------------------------------------------------------------------ */

/* The body is overflow: hidden for the staff shell, so the portal shell is the
   scroll container; the header sticks inside it. */
.portal-shell {
    height: 100vh;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    background: var(--color-bg);
}

/* Same allowance the staff shell makes: the environment banner sits above
   the shell, so its height comes off or the bottom edge is clipped. */
.env-banner ~ .portal-shell {
    height: calc(100vh - 28px);
}

.portal-header {
    position: sticky;
    top: 0;
    z-index: 20;
}

/* Row 1: brand and account, on navy. */
.portal-topbar {
    display: flex;
    align-items: center;
    gap: var(--space-lg);
    padding: 0 var(--space-lg);
    min-height: 64px;
    background: var(--color-primary);
    color: #ffffff;
}

/* Company mark left of the Talos brand: the 210x37 source renders at 36px
   tall (about 204px wide), so it is never upscaled. */
.portal-topbar__company-logo {
    flex-shrink: 0;
    height: 36px;
    width: auto;
}

.portal-topbar__brand {
    display: flex;
    align-items: center;
    gap: var(--space-sm);
    padding-left: var(--space-lg);
    border-left: 1px solid rgba(255, 255, 255, 0.25);
    color: #ffffff;
    text-decoration: none;
    font-weight: 700;
    white-space: nowrap;
}

.portal-topbar__logo svg {
    width: 28px;
    height: 28px;
}

.portal-topbar__brand-text small {
    display: block;
    font-size: var(--font-size-xs);
    font-weight: 500;
    opacity: 0.8;
}

.portal-topbar__right {
    display: flex;
    align-items: center;
    gap: var(--space-md);
    margin-left: auto;
}

.portal-topbar__customer {
    font-weight: 600;
    white-space: nowrap;
}

.portal-switcher {
    display: flex;
    align-items: center;
    gap: var(--space-xs);
}

.portal-switcher__label {
    font-size: var(--font-size-xs);
    opacity: 0.85;
}

.portal-switcher__select {
    padding: 6px 8px;
    border-radius: 6px;
    border: 1px solid rgba(255, 255, 255, 0.35);
    background: rgba(255, 255, 255, 0.95);
    color: var(--color-text);
    max-width: 240px;
}

.portal-topbar .topbar__user-name,
.portal-topbar .topbar__user-role {
    color: #ffffff;
}

/* Row 2: navigation on a light strip. The links wrap rather than scroll, so
   a narrow screen gets two rows of links and never a horizontal scrollbar. */
.portal-nav {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 2px;
    padding: 4px var(--space-lg);
    background: var(--color-surface);
    border-bottom: 1px solid var(--color-border);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
}

.portal-nav__link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 9px 12px;
    border-radius: 8px;
    color: var(--color-text-secondary);
    text-decoration: none;
    font-weight: 500;
    font-size: var(--font-size-sm);
    line-height: 1;
    white-space: nowrap;
}

.portal-nav__icon {
    display: inline-flex;
    align-items: center;
}

.portal-nav__link:hover {
    background: var(--color-neutral-bg);
    color: var(--color-text);
}

.portal-nav__link.is-active {
    color: var(--color-primary);
    font-weight: 600;
    background: var(--color-info-bg);
}

.portal-nav__icon svg {
    width: 18px;
    height: 18px;
}

.portal-content {
    flex: 1;
    padding: var(--space-lg);
    max-width: 1400px;
    width: 100%;
    margin: 0 auto;
    box-sizing: border-box;
}

.data-table th.data-table__numeric {
    text-align: right;
}

@media (max-width: 900px) {
    .portal-topbar {
        flex-wrap: wrap;
        padding: var(--space-sm) var(--space-md);
        gap: var(--space-sm);
    }

    .portal-topbar__brand {
        padding-left: var(--space-sm);
    }

    .portal-topbar__company-logo {
        height: 28px;
    }

    .portal-nav {
        padding: 4px var(--space-md);
    }
}

/* Compact variant for buttons that sit inside table cells and the portal bar. */
.button--small {
    padding: 4px 10px;
    font-size: var(--font-size-xs);
    min-height: 0;
}

/* Serial lists inside a dialog: hundreds of entries flow into columns and
   scroll within the dialog rather than stretching it off the screen. */
.serial-list {
    list-style: none;
    margin: var(--space-md) 0 0;
    padding: 0;
    max-height: 55vh;
    overflow-y: auto;
    columns: 3 12rem;
    column-gap: var(--space-lg);
    font-size: var(--font-size-sm);
}

.serial-list li {
    break-inside: avoid;
    padding: 2px 0;
}
