/* ============================================================================
   STATUS PENDING  ·  v1  ·  APPROVED OPTION 3
   ============================================================================
   The visual for js/status-pending.js — the one global "a status is being
   updated" affordance: a 13px ring spinner (2px, transparent track, accent
   top) + a 0.62rem/700 uppercase label in muted text, inline-flex, 7px gap,
   height-locked so an Excel grid cell never jumps.

   States
     .sp-pending  → spinner + label            (info / warn / success / danger)
     .sp-flash    → brief confirm tint on the host once the server answers
     .sp-failed   → red RETRY affordance, reason in the title tooltip

   THEMING
   Colours come exclusively from css/themes.css tokens, so dark and the Harbor
   light theme both track automatically. NOTE: Poeldijk's --accent / --accent2
   are WHITE in several contexts — this file therefore only ever uses the
   explicit --accent-blue / --accent-orange / --accent-green / --accent-red,
   plus --text-*, --bg-*, --border-color and --radius-*.

   Load AFTER css/themes.css (and after css/main.css, which re-declares the
   token set under :root).
   ============================================================================ */

/* ── shared geometry ───────────────────────────────────────────────────────
   Both the pending and the failed chip use the same box so the transition
   pending → resolved → failed never moves a pixel. JS locks width/height
   inline to the element's measured content box; these rules only make sure
   nothing inside can push that box out. */
.sp-pending,
.sp-failed {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    max-width: 100%;
    min-width: 0;
    line-height: 1;
    overflow: hidden;
    white-space: nowrap;
    vertical-align: middle;
    box-sizing: border-box;
}

/* ── tones ────────────────────────────────────────────────────────────────
   --sp-accent drives the spinner arc; the label stays muted so the pending
   state reads as "in progress", never as a new status value. */
.sp-pending            { --sp-accent: var(--accent-blue); }
.sp-pending.sp-tone-info    { --sp-accent: var(--accent-blue); }
.sp-pending.sp-tone-warn    { --sp-accent: var(--accent-orange); }
.sp-pending.sp-tone-success { --sp-accent: var(--accent-green); }
.sp-pending.sp-tone-danger  { --sp-accent: var(--accent-red); }

/* Surfaces whose rows stay WHITE in both themes — the shipments register
   Excel grid (--table-row-bg is #ffffff in dark and light alike). There the
   dark theme's sky-blue --accent-blue (#38bdf8) is too pale, so the arc uses
   --grid-sel-border, the token that already exists precisely because it has
   to read against a white grid row in both themes (#3b82f6 / #0f7ea2).
   `.status-cell` is the register's own host class (js/table.js createRow), so
   this needs no work at the adoption site; `.sp-on-white` is the generic
   opt-in for any other fixed-light surface. */
.status-cell .sp-pending,
.sp-on-white .sp-pending,
.sp-pending.sp-on-white { --sp-accent: var(--grid-sel-border); }

/* ── register grid: no column shift ───────────────────────────────────────
   The shipments register (index.html) is a table-layout:AUTO grid, and its
   status column is handed SURPLUS space: measured intrinsic ~150.9px, used
   186.9px. The JS locks the chip to the host's measured content box — which
   here is the whole cell — so the chip's min/max-content contribution jumps to
   186.9px, the column's intrinsic rises with it, and the auto algorithm adds
   the same surplus on top again: the column visibly widened by 36px for the
   whole life of every request, even though the cell's own box was locked.

   `.status-cell` is a BLOCK that already fills the cell, so the chip does not
   need a width of its own to hold the geometry. Releasing just the width (the
   inline HEIGHT lock is untouched, so the row height still cannot move) makes
   the chip's contribution smaller than the column's existing intrinsic width,
   and the column does not move at all. Measured: column +0.000px, row -0.016px.

   Scoped to `.status-cell` on purpose. Do NOT widen this selector: hosts that
   are inline-block wrappers (pages/wms.html's .wms-status-host) shrink-to-fit,
   so there the width lock is what holds the box and the cell is pinned in JS
   instead — see WMS._pinHost. */
.status-cell > .sp-pending,
.status-cell > .sp-failed { width: auto !important; }

/* ── badge-sized wrapper hosts: let the label be READABLE ─────────────────
   pages/positions.html and pages/wms.html hang the handle on an inline-block
   wrapper that shrink-to-fits its status badge. For a short status ("NEW",
   "IN STOCK") that wrapper is only ~35px wide, and the JS lock plus the base
   rule's `max-width:100%` clipped the label down to "A…" — a spinner with no
   word next to it, which is exactly what the label is there to prevent.

   Both surfaces are already protected from column movement by something
   stronger than this box: positions.html is table-layout:FIXED, and wms.html
   pins the <td> in WMS._pinHost. So the chip may take its natural width; it
   still cannot move the column, and the inline HEIGHT lock still pins the row.
   Verified: column +0.000px and row +0.000px on both. */
.pos-status-host > .sp-pending,
.pos-status-host > .sp-failed,
.wms-status-host > .sp-pending,
.wms-status-host > .sp-failed { width: auto !important; max-width: none !important; }

/* ── the 13px ring ────────────────────────────────────────────────────────── */
.sp-pending .sp-spinner {
    flex: 0 0 auto;
    width: 13px;
    height: 13px;
    box-sizing: border-box;
    border: 2px solid transparent;              /* transparent track          */
    border-top-color: var(--sp-accent);         /* accent arc                 */
    border-radius: 50%;
    animation: sp-spin 0.62s linear infinite;
}

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

/* ── the label ────────────────────────────────────────────────────────────── */
.sp-pending .sp-label {
    font-size: 0.62rem;
    font-weight: 700;
    line-height: 1;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-muted) !important;        /* beats broad theme rules    */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ── confirm flash ────────────────────────────────────────────────────────
   Applied to the HOST element for ~0.9s after resolve() paints the
   server-validated status, so the operator sees exactly where it landed.
   An ::after overlay keeps the tint token-driven (no hardcoded rgba) and
   leaves the freshly painted status markup untouched. */
.sp-flash {
    position: relative;
    --sp-flash-color: var(--accent-green);
}
.sp-flash.sp-flash-info    { --sp-flash-color: var(--accent-blue); }
.sp-flash.sp-flash-warn    { --sp-flash-color: var(--accent-orange); }
.sp-flash.sp-flash-danger  { --sp-flash-color: var(--accent-red); }
.sp-flash.sp-flash-success { --sp-flash-color: var(--accent-green); }

.sp-flash::after {
    content: '';
    position: absolute;
    top: 0; right: 0; bottom: 0; left: 0;
    inset: 0;
    pointer-events: none;
    border-radius: inherit;
    background: var(--sp-flash-color);
    opacity: 0;
    animation: sp-flash-fade 0.9s ease-out 1;
}

@keyframes sp-flash-fade {
    0%   { opacity: 0.26; }
    55%  { opacity: 0.12; }
    100% { opacity: 0; }
}

/* ── failure / RETRY ──────────────────────────────────────────────────────
   Never a silent rollback: the cell holds a red affordance until the operator
   acts. The full server reason lives in the title tooltip. */
.sp-failed {
    --sp-accent: var(--accent-red);
    gap: 6px;
}

.sp-failed .sp-fail-icon {
    flex: 0 0 auto;
    width: 13px;
    height: 13px;
    box-sizing: border-box;
    border: 2px solid var(--accent-red);
    border-radius: 50%;
    color: var(--accent-red);
    font-size: 0.5rem;
    font-weight: 700;
    line-height: 9px;
    text-align: center;
}

.sp-failed .sp-retry,
.sp-failed .sp-fail-label {
    font-size: 0.62rem;
    font-weight: 700;
    line-height: 1;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    font-family: inherit;
    color: var(--accent-red) !important;
    background: transparent !important;
    border: 1px solid var(--accent-red) !important;
    border-radius: var(--radius-sm);
    padding: 2px 6px;
    margin: 0;
    white-space: nowrap;
}

.sp-failed .sp-fail-label {
    border-style: dashed !important;
    opacity: 0.85;
}

.sp-failed .sp-retry {
    cursor: pointer;
    transition: background-color 0.15s ease, color 0.15s ease;
}

.sp-failed .sp-retry:hover,
.sp-failed .sp-retry:focus-visible {
    background: var(--accent-red) !important;
    color: var(--bg-secondary) !important;
    outline: none;
}

.sp-failed .sp-retry:focus-visible {
    box-shadow: 0 0 0 2px var(--bg-secondary), 0 0 0 4px var(--accent-red);
}

/* ── host hooks ───────────────────────────────────────────────────────────
   data-sp is set by JS: "pending" | "failed" | "done". Surfaces can hang
   their own scoped tweaks off these without touching the module. */
[data-sp="pending"] { cursor: progress; }
[data-sp="failed"]  { cursor: default; }

/* Inputs/controls inside a pending status container must not be usable while
   the server has not answered. */
[data-sp="pending"] > input,
[data-sp="pending"] > select,
[data-sp="pending"] > button { pointer-events: none; }

/* ── reduced motion ───────────────────────────────────────────────────────
   Full fallback: the spinner STOPS (it becomes a static accent ring) and the
   uppercase label carries the meaning. The confirm flash becomes a static
   tint that simply disappears when JS drops the class. Nothing animates. */
@media (prefers-reduced-motion: reduce) {
    .sp-pending .sp-spinner {
        animation: none !important;
        border-color: var(--sp-accent) !important;
        opacity: 0.55;
    }

    .sp-flash::after {
        animation: none !important;
        opacity: 0.14;
    }

    .sp-failed .sp-retry {
        transition: none !important;
    }
}
