/* Shared interaction & motion rules for every stack (storefront, supplier + admin portals, legacy layouts, sign-in).
   Loaded by ui_theme_head() after all other stylesheets. Motion only communicates state (selection, progress,
   hierarchy, completion); it never delays access to a control. Opacity/transform only, no `transition: all`.
   Durations: hover/focus feedback = fast, menus/tabs/compact state = base, dialogs/drawers = slow. */
:root {
    --ui-dur-fast: 140ms;
    --ui-dur-base: 190ms;
    --ui-dur-slow: 240ms;
    --ui-ease: cubic-bezier(.2, 0, 0, 1);      /* entering / settling */
    --ui-ease-in: cubic-bezier(.4, 0, 1, 1);   /* leaving */
    --ui-focus: var(--brand, var(--tl-brand, #177245));
    --ui-highlight: color-mix(in srgb, var(--ui-focus) 16%, transparent);
    --ui-danger: var(--coral, var(--tblr-danger, #C2410C));
    --ui-on-danger: #FFFFFF;
    /* No forced smooth scrolling: in-page jumps land immediately where the user asked to go. */
    scroll-behavior: auto;
}
html[data-bs-theme="dark"] { --ui-highlight: color-mix(in srgb, var(--brand, #4FBF86) 22%, transparent); --ui-danger: var(--coral, #F08A6B); --ui-on-danger: #0F141B; }

/* ---------- Light/Dark switch: swap colours in one frame instead of tweening every surface ---------- */
html.ui-theme-switching,
html.ui-theme-switching *,
html.ui-theme-switching *::before,
html.ui-theme-switching *::after { transition: none !important; }

/* ---------- focus: visible ring for keyboard users only; existing component rings keep priority ---------- */
:where(a, button, summary, [role="button"], [role="tab"], [role="menuitem"], [tabindex]:not([tabindex="-1"])):focus-visible {
    outline: 2px solid var(--ui-focus);
    outline-offset: 2px;
}
.dropdown-item:focus-visible { outline: 2px solid var(--ui-focus); outline-offset: -2px; }

/* ---------- pressed / disabled ---------- */
.btn { transition-property: color, background-color, border-color, box-shadow, translate; transition-duration: var(--ui-dur-fast); transition-timing-function: var(--ui-ease); }
.btn:not(:disabled):not(.disabled):not(.ui-pending):active { translate: 0 1px; }
.btn:disabled, .btn.disabled, button:disabled { cursor: not-allowed; }

/* ---------- menus, tabs, collapses, dialogs, drawers (Bootstrap/Tabler read these durations) ---------- */
.fade { transition: opacity var(--ui-dur-base) linear; }
.collapsing { transition-duration: var(--ui-dur-base); transition-timing-function: var(--ui-ease); }
.dropdown-menu.show { animation: ui-menu-in var(--ui-dur-base) var(--ui-ease); }
@keyframes ui-menu-in { from { opacity: 0; translate: 0 -4px; } to { opacity: 1; translate: 0 0; } }
.modal.fade .modal-dialog { transition: transform var(--ui-dur-slow) var(--ui-ease); }
.modal.fade:not(.show) .modal-dialog { transform: translate(0, 12px); }
.modal-backdrop.fade, .offcanvas-backdrop.fade { transition: opacity var(--ui-dur-slow) linear; }
.offcanvas { transition-duration: var(--ui-dur-slow); transition-timing-function: var(--ui-ease); }

/* ---------- button-level pending (UIMotion.pending): label stays, spinner added, width locked by JS ---------- */
.ui-pending { cursor: progress !important; pointer-events: none; }
.ui-pending:not(:disabled) { opacity: .85; }
.ui-spinner {
    display: inline-block; flex-shrink: 0; width: 1em; height: 1em; vertical-align: -.15em;
    margin-inline-end: .45em; border: 2px solid currentColor; border-inline-end-color: transparent;
    border-radius: 50%; animation: ui-spin .7s linear infinite;
}
@keyframes ui-spin { to { transform: rotate(360deg); } }

/* quantity line waiting for the server: the value stays editable (further clicks are queued), it just looks busy */
.ui-qty-busy { cursor: progress; }
.ui-qty-busy input { opacity: .6; transition: opacity var(--ui-dur-fast) linear 120ms; }

/* a list line / table row waiting for its own request */
:is(.oc-line, tr, .list-group-item)[aria-busy="true"] { cursor: progress; }
:is(.oc-line, tr, .list-group-item)[aria-busy="true"] > * { opacity: .6; transition: opacity var(--ui-dur-fast) linear 120ms; }

/* ---------- section-level loading (UIMotion.loading): dims only if the request is not near-instant ---------- */
.ui-loading { position: relative; }
.ui-loading > :not(.ui-loading-bar) { opacity: .55; transition: opacity var(--ui-dur-base) linear 160ms; }
.ui-loading-bar {
    position: absolute; top: 0; inset-inline: 0; height: 2px; overflow: hidden; z-index: 3; pointer-events: none;
    background: color-mix(in srgb, var(--ui-focus) 18%, transparent);
}
.ui-loading-bar::after {
    content: ""; position: absolute; top: 0; bottom: 0; width: 35%; background: var(--ui-focus);
    animation: ui-indeterminate 1.1s var(--ui-ease) infinite;
}
@keyframes ui-indeterminate { from { inset-inline-start: -35%; } to { inset-inline-start: 100%; } }

/* ---------- confirmed change: one short highlight, then gone; leaving rows fade before removal ---------- */
.ui-flash, tr.ui-flash > td, tr.ui-flash > th { animation: ui-flash 1.4s var(--ui-ease); }
@keyframes ui-flash { from { background-color: var(--ui-highlight); } to { background-color: transparent; } }
.ui-leaving { opacity: 0 !important; transition: opacity var(--ui-dur-base) var(--ui-ease-in) !important; pointer-events: none; }
.ui-badge-updated { animation: ui-ring 700ms var(--ui-ease); }
@keyframes ui-ring { from { box-shadow: 0 0 0 0 color-mix(in srgb, var(--ui-focus) 55%, transparent); } to { box-shadow: 0 0 0 7px transparent; } }

/* ---------- inline validation next to the field (text + icon, never colour alone) ---------- */
.ui-field-error { display: flex; align-items: flex-start; gap: .4em; margin: .4rem 0 0; color: var(--ui-danger); font-size: .875rem; font-weight: 500; line-height: 1.4; }
.ui-field-error::before { content: "!"; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; width: 1.15em; height: 1.15em; margin-top: .1em; border-radius: 50%; background: var(--ui-danger); color: var(--ui-on-danger); font-size: .8em; line-height: 1; font-weight: 700; }
.ui-field-invalid { outline: 2px solid var(--ui-danger); outline-offset: 3px; border-radius: 6px; }
[aria-invalid="true"]:not(.is-invalid) { border-color: var(--ui-danger) !important; }

/* ---------- shared live regions (UIMotion.announce) ---------- */
.ui-live-region { position: absolute !important; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* storefront product card image: subtle zoom at the shared slow duration (was .35s) */
.pc .th .pc-img { transition-duration: var(--ui-dur-slow); }

/* ---------- storefront selection controls (selling units, time slots, payment options) ---------- */
.unit-opt, .slot, .payopt { transition: border-color var(--ui-dur-fast) var(--ui-ease), background-color var(--ui-dur-fast) var(--ui-ease); }
@media (hover: hover) {
    .unit-opt:not(.oos):not(.on):hover { border-color: color-mix(in srgb, var(--ui-focus) 45%, var(--line, #D8D2C4)); }
}

/* ---------- touch: hover-only effects must not stick after a tap ---------- */
@media (hover: none) {
    .pc:hover .th .pc-img { transform: none; }
    .pc:hover { box-shadow: none; }
}

/* ---------- reduced motion: same states, no movement (spinners slow down but keep showing activity) ---------- */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { animation-duration: 1ms !important; animation-iteration-count: 1 !important; transition-duration: 1ms !important; transition-delay: 0s !important; scroll-behavior: auto !important; }
    .ui-spinner, .spinner-border, .fa-spin { animation: ui-spin 1.6s linear infinite !important; }
    .ui-loading-bar::after { animation: none !important; inset-inline-start: 0; width: 100%; opacity: .6; }
    .ui-flash, tr.ui-flash > td, tr.ui-flash > th { animation: none !important; background-color: var(--ui-highlight) !important; }
    .modal.fade:not(.show) .modal-dialog { transform: none; }
    /* SweetAlert2 v9 only removes a closed dialog/toast on `animationend`; with `animation: none` (storefront
       reduced-motion rule) its overlay stayed on screen and blocked the page. Keep a 1ms animation instead. */
    .swal2-popup.swal2-show { animation: swal2-show 1ms !important; }
    .swal2-popup.swal2-hide { animation: swal2-hide 1ms forwards !important; }
    .swal2-popup.swal2-toast.swal2-show { animation: swal2-toast-show 1ms !important; }
    .swal2-popup.swal2-toast.swal2-hide { animation: swal2-toast-hide 1ms forwards !important; }
}

/* ---------- print / PDF: no motion, no live regions ---------- */
@media print {
    *, *::before, *::after { animation: none !important; transition: none !important; }
    .ui-live-region, .ui-loading-bar, .ui-spinner { display: none !important; }
}
