/* mouldshop-variant-table.css — the PDP variant table. TASK-0051, DEC-0026 rulings 37/38.
   Loaded via MouldshopHead. Tokens from mouldshop.css (--ms-*). Zero colour literals.

   ELEMENTS FIRST WAS TASK-0051'S BRIEF, AND TASK-0084 IS THE RESTYLE IT DEFERRED. The
   original note read: "this is the structure and the palette, not the finish — the raised
   active row and the rest of C's polish are deliberately absent rather than half-done." That
   is now done, and it is a SKIN PASS: no column was added, removed or reordered, the cart form
   is untouched, and the facet dropdowns, chips, Load more and count footer behave exactly as
   they did.

   WHAT THE MOCK IS AND IS NOT — operator, 2026-09-18: "we use data we have in shop — design is
   only illustrative". Figma C shows Main Thread / Flow Capacity / BSPP In. / L1 / B / Min. QTY /
   Pack QTY. NONE of those are columns. Every column still comes from the product's own
   VariantGroups(). Three things in the mock are deliberately NOT built, each on the operator's
   word: the Version chevron ("NO VERSION — already explained before"), the FILTER pill ("it was
   only INFO"), and "Buy All" ("mistake, just buy I suppose") — that column is the basket column
   and its header says so.

   THE ACTIVE ROW IN THE MOCK IS THE HOVER STATE. Operator: "same as hovered". There is no
   selected or clicked state to build, so the whole of it is :hover — plus :focus-within, so a
   keyboard reaches the same thing a mouse does. */

:root {
    --ms-vtable-radius: 2px;
    --ms-vtable-cell-y: 0.6rem;
    --ms-vtable-cell-x: 0.75rem;

    /* TASK-0084. The lift is the card's shadow shape at table scale — same idea, shorter
       throw, because a row is 48px tall and a card is 394. Declared here rather than reusing
       --ms-card-shadow-* so a change to the card's hover does not silently move the table. */
    --ms-vtable-row-shadow: 0 6px 18px -6px rgba(10, 41, 121, .28);
    /* The stepper's hover tint. --ms-blue at low alpha: the accent already in the palette,
       not a new colour. */
    --ms-vtable-tint: rgba(108, 178, 222, .22);
    --ms-vtable-stock-gap: 0.4rem;
}

.ms-vtable {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.ms-vtable-title {
    margin: 0;
    font-family: var(--ms-font-head);
    font-weight: 700;
    font-size: 1.25rem;
    color: var(--ms-ink);
}

/* A table is the one element CLAUDE.md allows its own horizontal scroller. The PAGE must not
   scroll, so the overflow lives here and nowhere else. */
.ms-vtable-scroll {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

.ms-vtable-table {
    width: 100%;
    border-collapse: collapse;
    font-family: var(--ms-font-body);
    font-size: 0.9rem;
    color: var(--ms-ink);
    /* TASK-0085b item 1 — NO BACKGROUND. This used to be `background: var(--ms-grey-5)`,
       from TASK-0051. Operator, 2026-09-18, reading the computed styles: "table has BG — it
       has to be the same as section — so remove this". The table painting its own ground
       means a row set to any colour scheme gets a grey block sitting on top of it; with no
       background the section shows through and the table matches whatever the row is set to.
       The CELLS still paint #f8f8f8 (TASK-0085 item 3) — that was proven and is not in
       question here. */
}

/* THE SEPARATOR IS THIN AND IT IS THE ONLY LINE IN THE TABLE. The mock has no vertical
   rules, no header underline heavy enough to read as a border, and no outline around the
   table. --ms-rule at .35 alpha was the right weight for the elements-first pass and reads
   as a hard line against white; the mock's is a hairline, so it drops to --ms-grey-20. */
.ms-vtable-table th,
.ms-vtable-table td {
    padding: var(--ms-vtable-cell-y) var(--ms-vtable-cell-x);
    text-align: left;
    vertical-align: middle;
    white-space: nowrap;
    border-bottom: 1px solid var(--ms-grey-20);
}

/* UNBOXED HEADER — the mock's header sits on the grey page ground with no fill of its own.
   It used to be background: var(--ms-white), which boxed it against the rows. */
.ms-vtable-table thead th {
    font-family: var(--ms-font-head);
    font-weight: 600;
    font-size: 0.8rem;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    color: var(--ms-navy-60);
    background: transparent;
    border-bottom-color: transparent;
    position: relative;
}

/* ITEM.NO is the one header the mock colours. */
.ms-vtable-th--item { color: var(--ms-ink); }

/* RESTING ROWS ARE QUIET. In the mock every resting cell is a muted grey-blue and only the
   hovered row goes to full brand navy — that contrast is the whole of the interaction, so the
   resting state has to give it room. Full navy on every row leaves hover with nowhere to go. */
.ms-vtable-table tbody tr {
    background: var(--ms-white);
    color: var(--ms-navy-60);
}

.ms-vtable-th--num,
.ms-vtable-td--num { text-align: right; }

.ms-vtable-th--qty,
.ms-vtable-td--qty { text-align: right; width: 1%; }

.ms-vtable-num { font-variant-numeric: tabular-nums; }
.ms-vtable-price { font-weight: 700; font-variant-numeric: tabular-nums; }

/* --- the per-column facet dropdown ---------------------------------------
   Live's behaviour, our markup: the menu stays open while values are ticked and
   commits on Apply. */

.ms-vtable-facet-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.25rem 0.5rem;
    border: 1px solid var(--ms-rule);
    border-radius: 999px;
    background: var(--ms-white);
    font: inherit;
    color: inherit;
    text-transform: inherit;
    letter-spacing: inherit;
    cursor: pointer;
}

.ms-vtable-facet-toggle:hover { border-color: var(--ms-navy-40); }

.ms-vtable-facet-toggle:focus-visible {
    outline: 2px solid var(--ms-blue);
    outline-offset: 2px;
}

.ms-vtable-th--filter.is-filtering .ms-vtable-facet-toggle {
    border-color: var(--ms-navy);
    background: var(--ms-navy);
    color: var(--ms-white);
}

.ms-vtable-facet-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.1rem;
    height: 1.1rem;
    padding: 0 0.25rem;
    border-radius: 999px;
    background: var(--ms-blue);
    color: var(--ms-white);
    font-size: 0.7rem;
    font-weight: 700;
}

.ms-vtable-facet-menu {
    position: absolute;
    z-index: 20;
    top: calc(100% - 0.25rem);
    left: var(--ms-vtable-cell-x);
    min-width: 13rem;
    max-height: 18rem;
    overflow-y: auto;
    border: 1px solid var(--ms-rule);
    border-radius: var(--ms-vtable-radius);
    background: var(--ms-white);
    box-shadow: 0 8px 24px -8px rgb(0 0 0 / 0.25);
    text-transform: none;
    letter-spacing: normal;
}

/* Sticky, as live's is — on a long value list the commit must not scroll away. */
.ms-vtable-facet-apply-bar {
    position: sticky;
    top: 0;
    padding: 0.5rem;
    border-bottom: 1px solid var(--ms-rule);
    background: var(--ms-white);
}

.ms-vtable-facet-apply-bar .ms-btn { width: 100%; min-height: 36px; }

.ms-vtable-facet-values { padding: 0.5rem; }

.ms-vtable-facet-value {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.25rem 0;
    font-weight: 400;
    color: var(--ms-ink);
    cursor: pointer;
}

.ms-vtable-facet-n {
    margin-left: auto;
    color: var(--ms-navy-40);
    font-variant-numeric: tabular-nums;
}

/* --- active filter chips -------------------------------------------------- */

.ms-vtable-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.ms-vtable-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.2rem 0.5rem 0.2rem 0.7rem;
    border: 1px solid var(--ms-rule);
    border-radius: 999px;
    background: var(--ms-grey-5);
    font-size: 0.8rem;
    color: var(--ms-ink);
}

.ms-vtable-chip--reset { background: var(--ms-white); border-color: var(--ms-navy-40); }

.ms-vtable-chip-remove {
    border: 0;
    background: none;
    padding: 0;
    line-height: 1;
    font-size: 1rem;
    color: var(--ms-navy-60);
    cursor: pointer;
}

.ms-vtable-chip-remove:hover { color: var(--ms-ink); }

/* --- qty + basket, per row ------------------------------------------------ */

.ms-vtable-cart {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.5rem;
    margin: 0;
}

.ms-vtable-qty {
    display: inline-flex;
    align-items: center;
    border: 1px solid var(--ms-rule);
    border-radius: 999px;
    background: var(--ms-white);
}

.ms-vtable-qty-btn {
    width: 1.75rem;
    height: 1.75rem;
    border: 0;
    background: none;
    color: var(--ms-ink);
    font-size: 0.95rem;
    line-height: 1;
    cursor: pointer;
}

/* The spinner arrows are removed because the two buttons already do that job, and two
   controls for one value is how a shopper ends up with a quantity they did not choose. */
.ms-vtable-qty-input {
    width: 2.5rem;
    border: 0;
    background: transparent;
    text-align: center;
    font: inherit;
    color: inherit;
    -moz-appearance: textfield;
    appearance: textfield;
}

.ms-vtable-qty-input::-webkit-outer-spin-button,
.ms-vtable-qty-input::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

.ms-vtable-add {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    border: 1px solid var(--ms-navy);
    border-radius: 999px;
    background: var(--ms-navy);
    color: var(--ms-white);
    cursor: pointer;
    transition: background-color 0.15s linear, border-color 0.15s linear;
}

.ms-vtable-add:hover { background: var(--ms-blue); border-color: var(--ms-blue); }

.ms-vtable-add:focus-visible {
    outline: 2px solid var(--ms-blue);
    outline-offset: 2px;
}

/* --- foot ----------------------------------------------------------------- */

.ms-vtable-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
}

.ms-vtable-count {
    margin: 0;
    font-family: var(--ms-font-body);
    font-size: 0.85rem;
    color: var(--ms-navy-60);
    font-variant-numeric: tabular-nums;
}

@media (prefers-reduced-motion: reduce) {
    .ms-vtable-add { transition: none; }
}

/* ==========================================================================
   TASK-0084 — the hovered row, and the Stock cell's dot.
   ========================================================================== */

/* --- the Stock cell: the product card's dot, plus the number ---------------
   REUSED, NOT RE-DERIVED. `.ms-dot` and `.ms-dot--in|incoming|out` come from
   Custom/mouldshop-card-markers.css and are not redefined here — no size, no
   colour, no ring. Redeclaring any of them would create a second stock
   indicator that agrees with the card today and drifts from it on the first
   palette change, which is exactly what TASK-0084 item 3 forbids.

   Only the LAYOUT of the cell belongs to the table: the column is numeric and
   right-aligned, so the dot and the number sit as one right-aligned pair.

   THE NUMBER IS NOT THE DOT'S COLOUR — item 3, and it is also the accessibility
   position: colour alone does not carry the state, which is why the dot keeps
   the aria-label/title the card gives it. */
.ms-vtable-stock {
    white-space: nowrap;
}

.ms-vtable-stock .ms-dot {
    margin-inline-end: var(--ms-vtable-stock-gap);
    vertical-align: middle;
}

.ms-vtable-stock-n {
    font-variant-numeric: tabular-nums;
    vertical-align: middle;
}

/* --- the hovered row -------------------------------------------------------
   :hover AND :focus-within, together, so tabbing into a row's quantity field
   or its basket button lights the same row a pointer would. The quantity input
   and both steppers are focusable, so this fires for real keyboard use rather
   than being a gesture toward it.

   EVERY DECLARATION HERE IS SCOPED TO THE HOVERED ROW. That is what makes the
   check "a non-hovered row keeps the resting values" able to fail: if any of
   this leaked to `tbody tr`, the resting row would match the hover values and
   the check would go red. */
.ms-vtable-table tbody tr:hover,
.ms-vtable-table tbody tr:has(:focus-visible) {
    background: var(--ms-white);
    color: var(--ms-ink);
    box-shadow: var(--ms-vtable-row-shadow);
    /* The row lifts off the grey ground, so its own hairline would cut across the
       shadow. The mock has no line under the active row. */
    position: relative;
    z-index: 1;
}

.ms-vtable-table tbody tr:hover > td,
.ms-vtable-table tbody tr:has(:focus-visible) > td {
    border-bottom-color: transparent;
}

/* The item number is the mock's loudest cell on the active row: brand navy and
   heavier, where it is muted and regular at rest. */
.ms-vtable-table tbody tr:hover .ms-vtable-num,
.ms-vtable-table tbody tr:has(:focus-visible) .ms-vtable-num {
    color: var(--ms-ink);
    font-weight: 700;
}

/* The stepper tints. Border goes to the accent so the control reads as active
   rather than merely filled. */
.ms-vtable-table tbody tr:hover .ms-vtable-qty,
.ms-vtable-table tbody tr:has(:focus-visible) .ms-vtable-qty {
    background: var(--ms-vtable-tint);
    border-color: var(--ms-blue);
}

/* --- the basket button -----------------------------------------------------
   AT REST IT IS A QUIET PILL, ON THE ACTIVE ROW IT IS FILLED NAVY. This is the
   one place the elements-first pass and the mock disagreed outright: the button
   used to be filled navy on EVERY row and went light blue on its own :hover,
   which is the inverse of the mock — there the resting basket is a pale grey
   pill and filling it is what marks the active row.

   The button's own :hover rule further up the file still applies when the
   pointer is on the button itself; it is left alone. What changes is the
   resting appearance and the row-level active state. */
.ms-vtable-add {
    background: var(--ms-grey-20);
    border-color: var(--ms-grey-20);
    color: var(--ms-navy-40);
}

.ms-vtable-table tbody tr:hover .ms-vtable-add,
.ms-vtable-table tbody tr:has(:focus-visible) .ms-vtable-add {
    background: var(--ms-navy);
    border-color: var(--ms-navy);
    color: var(--ms-white);
}

/* Keyboard focus on the button keeps its own visible ring — the row tint is a
   state, not a focus indicator, and must not be read as one. */
.ms-vtable-add:focus-visible {
    outline: 2px solid var(--ms-blue);
    outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
    .ms-vtable-table tbody tr:hover,
    .ms-vtable-table tbody tr:has(:focus-visible) { transition: none; }
}

/* ==========================================================================
   TASK-0085 — the values, not the impression.

   TASK-0084 was skinned from a raster mock and got the impression right and the
   numbers wrong. Every value below is the operator's own measurement, used
   literally. Where the project already had a token at that value it is used and
   said so; where none existed a token is added here rather than the literal
   being scattered through the rules.

   THE ONE THAT MATTERED MOST: the resting row was white and the hovered row was
   white, so the hover read as nothing happening. Resting is #f8f8f8; white now
   belongs to hover alone, and that contrast is the whole interaction.
   ========================================================================== */

:root {
    /* operator-measured, TASK-0085. None of these matched an existing --ms-* token:
       the palette's greys are #F2F4F7 and #E4E7EC, and these are different values. */
    --ms-vtable-cell-bg:      #f8f8f8;   /* resting row and cell */
    --ms-vtable-gap:          10px;      /* between cells, in place of the dividers */
    --ms-vtable-basket-rest:  #EAECEA;   /* basket at rest; hover keeps TASK-0084's navy */
    --ms-vtable-line:         #959595;   /* stepper border AND stepper text */
    --ms-vtable-step-hover:   #E2F0F8;   /* stepper hover fill */
    --ms-vtable-control-h:    40px;      /* basket and stepper, the same height */
}

/* --- items 2 + 3: SUPERSEDED BY TASK-0085b — gaps out, dividers back ---------

   WHAT THIS BLOCK USED TO SAY, AND WHY IT IS LEFT HERE RATHER THAN DELETED. TASK-0085
   replaced TASK-0084's hairline separators with 10px of page ground, using
   `border-collapse: separate` + `border-spacing` — a margin cannot do it, because table
   cells do not take margins. TASK-0085a then narrowed the 10px to rows only, after I had
   flagged that applying it to both axes was my inference rather than an instruction.

   Operator, 2026-09-18, on the shipped result: "gaps are wrong — remove them and bring
   back dividers". So the whole approach is withdrawn: `border-collapse` returns to
   `collapse`, the spacing goes, and TASK-0084's hairline applies again.

   TWO OF MY PROPOSALS REVERSED IN A ROW, on the same surface. The pattern is worth naming:
   both were read off a raster mock and neither was checked against the page it had to live
   on. A gap between cells and a line between rows look interchangeable in a flat image and
   are not once a colour scheme sits behind them.

   The --ms-vtable-gap token is deliberately LEFT DECLARED above but no longer referenced,
   so the value the operator measured is still on record if the question reopens. Nothing
   reads it; it is documentation, not a live value. */
.ms-vtable-table {
    /* TASK-0085b items 2 + 3 — REVERTED, AND THE REVERSAL IS RECORDED HERE RATHER THAN BY
       DELETING THE BLOCK. TASK-0085 set `border-collapse: separate` with `border-spacing`
       to put 10px of page ground between the cells, and TASK-0085a narrowed that to rows
       only. Operator, 2026-09-18: "gaps are wrong — remove them and bring back dividers".
       Both were my proposals and both were wrong on the page.

       This block is LEFT IN PLACE, agreeing with the one above rather than being merged
       into it, because TASK-0085b item 5 says to report the duplicate and not to touch it.
       See the report in the WORKLOG: both declarations are in THIS file, at the two
       `.ms-vtable-table` rules — not in two stylesheets as the inspector suggests. */
    border-collapse: collapse;
}

/* TASK-0085b item 3: the `border-bottom: 0` that TASK-0085 put here is gone, so the hairline
   TASK-0084 established higher up this file applies again — 1px solid var(--ms-grey-20) on
   every th/td, which under `border-collapse: collapse` reads as ONE line between rows and no
   vertical rules at all, because only the BOTTOM edge is ever set. The header keeps its
   transparent bottom and the hovered row keeps its transparent bottom, both unchanged. */

.ms-vtable-table tbody tr,
.ms-vtable-table tbody td {
    background: var(--ms-vtable-cell-bg);
}

/* The header keeps no fill of its own — it sits on the page ground, as TASK-0084
   established and this packet does not change. */
.ms-vtable-table thead th {
    background: transparent;
}

/* --- item 1: white is the hover, and only the hover ----------------------- */
.ms-vtable-table tbody tr:hover,
.ms-vtable-table tbody tr:has(:focus-visible),
.ms-vtable-table tbody tr:hover > td,
.ms-vtable-table tbody tr:has(:focus-visible) > td {
    background: var(--ms-white);
}

/* --- items 4-7: the two controls ------------------------------------------
   THEY ARE THE SAME HEIGHT NOW. They were 1.75rem (28px) of stepper button
   inside an auto-height pill, against a 2.25rem (36px) basket — different by
   8px on every row, which is what the operator saw. Both are pinned to 40px
   rather than left to their content. */
.ms-vtable-add {
    width: var(--ms-vtable-control-h);
    height: var(--ms-vtable-control-h);
    background: var(--ms-vtable-basket-rest);
    border-color: var(--ms-vtable-basket-rest);
    color: var(--ms-navy-40);
}

.ms-vtable-qty {
    height: var(--ms-vtable-control-h);
    background: transparent;
    border: 1px solid var(--ms-vtable-line);
    color: var(--ms-vtable-line);
}

/* The number and the − / + all take the stepper's line colour. `color: inherit`
   on the input is what carries it there — the input's own colour was already
   inherit, the buttons' was not. */
.ms-vtable-qty-btn,
.ms-vtable-qty-input {
    color: inherit;
}

.ms-vtable-qty-btn {
    height: 100%;
}

/* --- hover, per the packet ------------------------------------------------
   Basket keeps TASK-0084's navy fill. Stepper loses its border and takes the
   pale blue — so the border is replaced by the fill rather than both applying. */
.ms-vtable-table tbody tr:hover .ms-vtable-add,
.ms-vtable-table tbody tr:has(:focus-visible) .ms-vtable-add {
    background: var(--ms-navy);
    border-color: var(--ms-navy);
    color: var(--ms-white);
}

.ms-vtable-table tbody tr:hover .ms-vtable-qty,
.ms-vtable-table tbody tr:has(:focus-visible) .ms-vtable-qty {
    background: var(--ms-vtable-step-hover);
    border-color: transparent;
    color: var(--ms-ink);
}

/* --- item 8: the stock label takes the dot's colour ------------------------
   TASK-0084 rendered this text in ordinary cell colour, on the operator's
   earlier instruction ("text as other texts"). TASK-0085 reverses it on the
   evidence of a 1:1 capture, and that reversal is recorded rather than quietly
   applied — the TASK-0084 check that asserted the opposite is inverted in
   pdp-variant-table.mjs with the same note.

   THE CARD ITSELF HAS NO RULE TO FOLLOW, and the packet asked me to report that
   case. `MouldshopCardMarkers.cshtml` renders the dot ALONE — no label, no text
   node, only aria-label/title. So there is no card text-colour rule to conflict
   with; the capture is the only authority, and it says the text is the dot's
   colour. The same three tokens are reused, so the label cannot drift from the
   dot it sits beside. */
.ms-vtable-stock--in       .ms-vtable-stock-n { color: var(--ms-dot-in); }
.ms-vtable-stock--incoming .ms-vtable-stock-n { color: var(--ms-dot-incoming); }
.ms-vtable-stock--out      .ms-vtable-stock-n { color: var(--ms-dot-out); }

/* The label is now words as well as a number, so it must not be forced onto one
   line at the expense of the column. */
.ms-vtable-stock { white-space: nowrap; }

/* ==========================================================================
   TASK-0086 — the facet row loses its chrome and its Apply button, columns get
   vertical separators, and the footer copies the listing's layout.
   ========================================================================== */

/* --- item 1: the facet trigger is TEXT, not a button ----------------------
   It shipped as a pill: 1px --ms-rule border, border-radius 999px, white fill.
   Against an unboxed header sitting on page ground that reads as four buttons
   in a row of headings, which is what the operator's screenshot shows.

   THE AFFORDANCE IS NOT REMOVED WITH THE CHROME, and the packet is explicit
   that it must not be: "an affordance that disappears entirely is a defect,
   not a simplification". Hover and focus take the navy the ITEM.NO header
   already uses, plus an underline on the label only — the chevron is not text
   and an underline under it looks like a dropped glyph.

   The focus RING stays. It is an outline, not a border, it exists only for
   keyboard users, and dropping it to satisfy "no border" would trade the
   packet's own accessibility floor for a literal reading it does not ask for. */
.ms-vtable-facet-toggle {
    padding: 0;
    border: 0;
    border-radius: 0;
    background: transparent;
    color: inherit;
}

.ms-vtable-facet-toggle:hover {
    border-color: transparent;          /* the pill rule above is still in the cascade */
    color: var(--ms-ink);
}

.ms-vtable-facet-toggle:hover .ms-vtable-facet-label,
.ms-vtable-facet-toggle:focus-visible .ms-vtable-facet-label {
    text-decoration: underline;
    text-underline-offset: 0.2em;
}

/* THE ACTIVE STATE LOSES ITS PILL TOO. It was a solid navy fill with white
   text — the loudest button on the page, on a row that is now all text. The
   count badge already says a column is filtering, in brand blue, so the label
   only has to stop being muted. */
.ms-vtable-th--filter.is-filtering .ms-vtable-facet-toggle {
    border-color: transparent;
    background: transparent;
    color: var(--ms-ink);
}

/* --- item 3: vertical separators, full row height -------------------------
   A BORDER ON THE CELL, NOT A PSEUDO-ELEMENT. Under border-collapse: collapse
   the cell box IS the row height, so its right edge runs corner to corner with
   no padding-box inset — which is exactly what the packet asks for and what a
   ::after inset by the cell's padding would have failed to do.

   :not(:last-child) keeps the line off the table's outer edges. Left edges are
   never set, so adjacent cells share one line rather than drawing two.

   Same --ms-grey-20 on both axes, as instructed. No measurement suggested the
   vertical needed to differ, so nothing is reported under the "report before
   diverging" clause.

   HEADER CELLS GET IT AS WELL. The packet says "between adjacent cells in a
   row" without excluding the header, and stopping the columns at the first
   body row would read as a table whose grid starts late. Reported for the
   smoke rather than decided silently.

   border-spacing is pinned to 0 beside the collapse. It has no effect under
   collapse, but the packet asks for 0 on both axes and the computed value was
   still the 2px initial. */
.ms-vtable-table {
    border-spacing: 0;
}

.ms-vtable-table thead th:not(:last-child),
.ms-vtable-table tbody td:not(:last-child) {
    border-right: 1px solid var(--ms-grey-20);
}

/* The hovered row is a lifted white card and the mock draws no lines on it.
   Its bottom hairline already goes transparent for that reason a few rules up;
   the verticals go with it for the same one, or the lift would be crossed by
   three rules it is supposed to float above. */
.ms-vtable-table tbody tr:hover > td,
.ms-vtable-table tbody tr:has(:focus-visible) > td {
    border-right-color: transparent;
}

/* --- item 4: the footer is the listing's, centred and stacked --------------
   Vendor's Swift-v2_ProductListItemRepeater.cshtml wraps both in one
   text-center div with the count above the button. This was a space-between
   row with the count on the left. Column plus centre reproduces the listing's
   layout; the BUTTON is untouched, so ms-btn--ghost keeps its radius. */
.ms-vtable-foot {
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
}

/* ==========================================================================
   TASK-0086a — three corrections to TASK-0086, all on the operator's word.
   ========================================================================== */

/* --- item 1: NO BOXES ON THE FACET ROW, IN ANY STATE ----------------------
   AC 0 asked what the operator was still seeing, because TASK-0086 had already
   claimed this row was clean. Measured on 8885543, and TASK-0086 was only ever
   about the TRIGGER:

     trigger      border 0, radius 0, transparent      — clean at rest and hover
     trigger      outline 2px solid #6CB2DE on FOCUS   — MINE, from TASK-0086
     chip         1px solid rgba(10,41,121,.35), r999, bg #F2F4F7
     reset chip   1px solid rgba(10,41,121,.4),  r999, bg #FFF
     chip remove  no border, and NO FOCUS INDICATOR AT ALL

   So "remove borders from filters above" is the CHIPS. They were never in
   TASK-0086's scope and kept the pill the triggers lost, which is why the row
   still read as boxes after a packet that said it had removed them.

   THE FOCUS RING I ARGUED FOR IS WITHDRAWN. TASK-0086's note said dropping it
   "would trade the packet's own accessibility floor for a literal reading it
   does not ask for". This packet asks for it, and gives the floor a different
   floor: focus moves onto the text. That is the operator's call and it is a
   better one, because the row now has one visual language instead of two.

   AND THE REMOVE BUTTONS HAD NOTHING. Both the per-chip and the reset-all
   remove computed `outline 0px none` when focused — identical focused and
   unfocused, which is the defect this item names, present before the packet
   rather than caused by it. They get the same text treatment. */
.ms-vtable-chip,
.ms-vtable-chip--reset {
    border: 0;
    border-radius: 0;
    background: transparent;
}

/* THE BOX I MISSED WAS A BOX-SHADOW, AND IT IS VENDOR'S, NOT OURS. AC 0 read
   border and outline in all three states but printed box-shadow only at rest,
   where it is `none` — so it reported the trigger clean and it was not. The
   check caught it on focus:

     rgba(10, 41, 121, 0.25) 0px 0px 0px 4px

   Source, in vendor swift.css, site-wide on every button:

     button:focus-visible, [role=button]:focus-visible, [tabindex]:focus-visible,
     :where(:focus-visible) { outline: 0;
       box-shadow: 0 0 0 .25rem rgba(var(--bs-body-color-rgb), 0.25) }

   A 4px spread shadow is a focus RING — the thing item 1 removes — wearing a
   property name the packet does not list. `outline: 0` alone would have left it
   and the row would still have boxed under the keyboard, which is state 3 of
   the three the AC asks for and the one nobody looks at.

   Vendor file is read-only (DEC-0037), so this is overridden from here. Our
   selectors are 0,2,0 against vendor's 0,1,1 and win on specificity.

   GENERALISABLE: "no border and no outline" is not "no box". A ring can be a
   border, an outline or a box-shadow, and a sweep that names two of the three
   passes a build that has the third. */
.ms-vtable-facet-toggle:focus-visible,
.ms-vtable-facet-toggle:focus,
.ms-vtable-chip-remove:focus-visible,
.ms-vtable-chip-remove:focus {
    outline: 0;
    box-shadow: none;
}

/* Focus and hover now say the same thing in the same way: full navy, underlined
   label. Hover was already this; focus joins it instead of drawing a ring. */
.ms-vtable-facet-toggle:focus-visible {
    color: var(--ms-ink);
}

.ms-vtable-chip-remove:focus-visible {
    color: var(--ms-ink);
    text-decoration: underline;
    text-underline-offset: 0.2em;
}

/* The chip's own text follows its remove button, so tabbing to the × makes the
   whole chip legible rather than lighting one glyph. */
.ms-vtable-chip:focus-within {
    color: var(--ms-ink);
}

/* --- item 3: the divider is HALF the cell, centred ------------------------
   REVERSES TASK-0086 item 3's full-height rule. The operator's words were
   "dividers between cells", and I read that as edge to edge; it was not.

   border-right CANNOT DO THIS — a border is the whole edge of the box by
   definition, so the rule moves to a pseudo-element. top/bottom at 25% rather
   than `height: 50%` because it makes the centring structural: equal clearance
   is the declaration, not an arithmetic consequence of it, and it cannot drift
   if the cell's box model changes.

   NO FIXED PIXEL HEIGHT ANYWHERE. The AC measures the ratio on two rows of
   different heights precisely so a hardcoded px fails, and the header row is a
   different height from a body row on this table, so it is that second case. */
.ms-vtable-table thead th:not(:last-child),
.ms-vtable-table tbody td:not(:last-child) {
    border-right: 0;
}

/* thead th already carries position: relative for the dropdown panel; body
   cells did not, and an un-positioned cell would hand the pseudo-element to the
   nearest positioned ancestor and draw one line down the whole table. */
.ms-vtable-table tbody td {
    position: relative;
}

.ms-vtable-table thead th:not(:last-child)::after,
.ms-vtable-table tbody td:not(:last-child)::after {
    content: "";
    position: absolute;
    right: 0;
    top: 25%;
    bottom: 25%;
    width: 1px;
    background: var(--ms-grey-20);
    pointer-events: none;
}

/* The hovered row still carries none — operator ruled "for now is fine" on the
   TASK-0086 call, so it inherits the new height like every other rule. It was
   border-right-color: transparent; with no border to colour, the pseudo goes
   instead. */
.ms-vtable-table tbody tr:hover > td::after,
.ms-vtable-table tbody tr:has(:focus-visible) > td::after {
    background: transparent;
}

/* ==========================================================================
   TASK-0086b — chip strip out, badge and per-column X at the chevron, the
   table's resting ground goes.
   ========================================================================== */

/* --- item 2: the four-part facet head -------------------------------------
   The chevron left the toggle button so the badge and the clear-X could sit to
   its left; the X is a control and a <button> inside a <button> is invalid
   HTML. The head is the flex row that used to be the button itself, so the
   gap and alignment move here.

   ORDER IS label / X / badge / chevron. The packet's sketch puts the badge
   against the chevron; the operator's words put the X against the badge. That
   is the only arrangement satisfying both, and flipping it is one `order`. */
.ms-vtable-facet-head {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
}

/* The toggle keeps its own rules from TASK-0086/0086a — no border, no radius,
   no background, hover and focus on the text. It is only narrower now. */
.ms-vtable-facet-toggle {
    padding: 0;
}

/* The chevron is a second opener, mouse-only: aria-hidden and tabindex -1 in
   the markup, so the keyboard meets this control exactly once. It inherits the
   no-box rules by carrying no box of its own. */
.ms-vtable-facet-chev {
    display: inline-flex;
    align-items: center;
    padding: 0;
    border: 0;
    background: transparent;
    color: inherit;
    cursor: pointer;
    line-height: 0;
}

.ms-vtable-facet-chev:focus-visible,
.ms-vtable-facet-chev:focus {
    outline: 0;
    box-shadow: none;
}

/* THE WHITE CIRCLE IS THE X'S OWN GROUND, NOT A BORDER — the packet is explicit,
   and TASK-0086a's no-border rule still binds. So: a filled round background,
   no border, no outline, no shadow ring. Vendor's site-wide
   button:focus-visible box-shadow is overridden here for the same reason it was
   overridden for the trigger. */
.ms-vtable-facet-clear {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.1rem;
    height: 1.1rem;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: var(--ms-white);
    color: var(--ms-navy-60);
    cursor: pointer;
    line-height: 0;
}

.ms-vtable-facet-clear:hover { color: var(--ms-ink); }

/* Focus on the text, per TASK-0086a — here the "text" is the glyph, so it takes
   the navy and the circle stays flat. No ring of any kind. */
.ms-vtable-facet-clear:focus-visible,
.ms-vtable-facet-clear:focus {
    outline: 0;
    box-shadow: none;
    color: var(--ms-ink);
}

.ms-vtable-facet-clear[hidden] { display: none; }

/* --- item 1: the value chips are gone, Reset all is not --------------------
   The wrapper and the reset chip stay exactly where they were, on the
   operator's ruling; only the per-value chips stop being built (in the JS).
   Nothing to declare here — recorded so the absence is not read as an
   oversight by whoever greps this file for ms-vtable-chip next. */

/* --- item 3: the table rests on the SECTION's ground -----------------------
   REVERSES TASK-0085's #f8f8f8. Operator: "Remove background from table — it
   has to have same gray as entire block when inactive."

   TRANSPARENT, NOT A LITERAL #EFEFEF. AC 0.3 measured the section at
   rgb(239, 239, 239), but hardcoding that would re-create precisely the fault
   TASK-0085b removed from the table element: a row set to a different colour
   scheme would get a grey block sitting on top of it. Transparent cells let the
   section through, so "same gray as entire block" stays true by construction
   rather than by a value that has to be maintained.

   HOVER IS UNTOUCHED. White plus the lift is the whole interaction and the
   packet puts it in "must not change"; it is declared elsewhere and still wins
   on specificity here. */
.ms-vtable-table tbody tr,
.ms-vtable-table tbody td {
    background: transparent;
}

/* ==========================================================================
   TASK-0086c — one line style for every rule the table draws, and a white
   boundary between header and body.
   ========================================================================== */

/* AC 0 — THE INVENTORY, read from this stylesheet rather than the browser
   because the operator has ruled out verification runs on my side. Stated as a
   source read, because that is the one thing about it that could be wrong.

     rule                    declared at   property          colour           live?
     row hairline            line 84       border-bottom     --ms-grey-20     YES
     header bottom           line 97       border-bottom     transparent      YES -> item 3
     hovered row bottom      line 391      border-bottom     transparent      YES, stays
     column rule (TASK-0086) line 674       border-right      --ms-grey-20     no, killed at 799
     column rule (0086a)     line 816-7    ::after bg        --ms-grey-20     YES
     dropdown panel          line 175      border            --ms-rule        YES, already right
     facet toggle / chips    130, 225      border            --ms-rule        no, zeroed by 0086a
     stepper / basket        259, 300      border            --ms-vtable-line, --ms-navy   YES,
                                                             and PROTECTED by "must not change"

   AC 0.2 — THE TWO TOKENS ARE NOT THE SAME COLOUR, which is the whole of item 2:
     --ms-rule     rgba(10, 41, 121, .35)   brand navy at 35%   <- the category grid's crosses
     --ms-grey-20  #E4E7EC                  a flat grey
   Both render as a hairline; only one of them is the value the operator pointed at.

   AC 0.3 — NOTHING MARKS THE HEADER/BODY BOUNDARY TODAY. The header row's own
   bottom hairline is set transparent (line 97, TASK-0084's unboxed header), and
   the first body row's hairline sits BELOW that row, not above it. So item 3
   adds the only line there rather than replacing one, and there is nothing to
   stack against.

   SCOPE, stated because item 2 says "ALL" and the packet also protects things:
   the stepper's and basket's borders are named in "must not change" and are
   component chrome, not rules the table draws. They keep their own colours. */

/* --- item 2: one line style for the table's own rules ---------------------
   Row hairline and column rule both move from --ms-grey-20 to --ms-rule. They
   keep their property, their width and their geometry — only the token moves.
   The column rule in particular keeps TASK-0086a's 50% height and centring;
   nothing below touches top/bottom. */
.ms-vtable-table th,
.ms-vtable-table td {
    border-bottom: 1px solid var(--ms-rule);
}

.ms-vtable-table thead th:not(:last-child)::after,
.ms-vtable-table tbody td:not(:last-child)::after {
    background: var(--ms-rule);
}

/* The hovered row still draws none. Re-declared after the colour change above
   so the order of the cascade cannot leave a navy hairline under a lifted row —
   the earlier transparent rule would still win on source order, but stating it
   here keeps the two facts next to each other. */
.ms-vtable-table tbody tr:hover > td,
.ms-vtable-table tbody tr:has(:focus-visible) > td {
    border-bottom-color: transparent;
}

/* --- item 3: the header/body boundary, and it is WHITE ---------------------
   THE ONE EXCEPTION TO ITEM 2, and the packet names it as such. Everything else
   the table draws is 1px solid var(--ms-rule); this is 1px solid white.

   It goes on the HEADER's bottom edge, not the first row's top. Under
   border-collapse those two edges are the same line, so putting it on one and
   leaving the other unset is what keeps it a single boundary — AC 0.3 confirmed
   nothing is drawn there today, so there is nothing to stack with.

   This overrides the transparent from TASK-0084's unboxed header, which is the
   "whatever marks that boundary today" the packet refers to. */
.ms-vtable-table thead th {
    border-bottom: 1px solid var(--ms-white);
}

/* ==========================================================================
   TASK-0086d — the header/body divider goes light blue, and the hovered row
   draws its dividers again.
   ========================================================================== */

/* --- item 1: the boundary is --ms-blue -------------------------------------
   TASK-0086c made this white; the operator, reading the rendered table:
   "Change white border to light blue we are using." That is --ms-blue,
   #6CB2DE, Blue 02 in mouldshop.css — the palette's accent, and the only
   light blue that is a named brand colour rather than a tint of one.

   NOT SUBSTITUTED, AND THE ALTERNATIVES ARE NAMED so the packet's "report
   rather than eyedrop a fourth" is satisfied if this reads wrong:
       --ms-blue   #6CB2DE   the accent          <- used here
       icon tone   #c4e0f2   the two-tone icons
       step hover  #E2F0F8   the stepper's hover tint
   The last two are tints and would read as almost-white at 1px; the accent is
   the one that will actually be visible as a line. If it is too strong, that
   is an operator call between the three, not a fourth value I invent.

   IT REMAINS THE NAMED EXCEPTION to TASK-0086c item 2 — every other rule the
   table draws stays on var(--ms-rule). Width, style and position unchanged:
   1px, on the header's bottom edge, one line. */
.ms-vtable-table thead th {
    border-bottom: 1px solid var(--ms-blue);
}

/* --- item 2: the hovered row draws its dividers ---------------------------
   REVERSES TASK-0086b's ruling, which was "for now is fine" rather than a
   decision, and TASK-0086c's carry-forward of it. The operator has now looked
   at the rendered row and wants the lines.

   TWO RULES ARE BEING UNDONE, not one, and they were written in different
   packets for the same reason. TASK-0085b/0086c made the bottom hairline
   transparent under a hovered row, and TASK-0086a did the same to the column
   pseudo-elements — both because "the row lifts off the ground, so its own
   hairline would cut across the shadow". That argument was mine both times and
   the operator has overruled it.

   THE ROW ABOVE IS THE TRAP THE PACKET NAMES, and it needs its own rule rather
   than falling out of the two below. Under border-collapse the hovered row's
   TOP edge and the previous row's BOTTOM edge are one shared line — and the
   hovered row carries `position: relative; z-index: 1` plus a white background,
   so it paints over that shared line whatever the neighbour declares. Restoring
   only the bottom edge would leave the boundary above the hovered row still
   swallowed. Declaring the top edge explicitly puts the line back on the side
   the lift was covering; same token, same width, so the collapse resolver has
   nothing to choose between and it cannot double up. */
.ms-vtable-table tbody tr:hover > td,
.ms-vtable-table tbody tr:has(:focus-visible) > td {
    border-bottom-color: var(--ms-rule);
    border-top: 1px solid var(--ms-rule);
}

/* The column rules come back with them — TASK-0086a took these to transparent
   on hover for the same shadow argument. Geometry is untouched: the 50% height
   and the centring live on the base rule and nothing here restates them. */
.ms-vtable-table tbody tr:hover > td::after,
.ms-vtable-table tbody tr:has(:focus-visible) > td::after {
    background: var(--ms-rule);
}

/* ==========================================================================
   OPERATOR-DIRECTED, 2026-09-19, no packet. Two instructions, verbatim:
     "top light blue you just have added - change to navy blue like the rest
      of them"
     "basket inactive BG to white"
   Logged in the WORKLOG as operator-directed. NO TASK ID IS MINTED HERE —
   Q-0129 records what happens when one is invented for unpacketed work.
   ========================================================================== */

/* --- the header/body boundary joins the rest ------------------------------
   THIS RULE HAS NOW BEEN THREE COLOURS IN ONE DAY: transparent (TASK-0084's
   unboxed header, and for four packets nobody noticed it was the boundary),
   white (TASK-0086c item 3), light blue (TASK-0086d item 1), and now the same
   navy hairline as every other rule in the table.

   "like the rest of them" IS THE INSTRUCTION, so this takes --ms-rule rather
   than --ms-navy: --ms-rule is what TASK-0086c item 2 put on every other line
   the table draws, and matching them is the whole of the ask. Full --ms-navy
   would be a fourth new value and would read heavier than the lines either
   side of it.

   THE EXCEPTION IS WITHDRAWN. TASK-0086c item 2 carved this out as "the one
   exception"; there is no exception any more, and the table draws exactly one
   line style throughout. */
.ms-vtable-table thead th {
    border-bottom: 1px solid var(--ms-rule);
}

/* --- the basket rests on white --------------------------------------------
   OVERRIDES A VALUE THAT FOUR PACKETS LISTED IN "must not change". TASK-0085
   item 5 set the resting basket to #EAECEA and TASK-0086 / 0086a / 0086b /
   0086c each carried it forward as protected. The operator has now looked at
   it and said white. His call outranks the carried-forward constraint, and it
   is recorded here rather than quietly applied, because the next packet that
   copies its "must not change" block from the last one will otherwise
   re-protect a value he has already changed.

   The --ms-vtable-basket-rest token is deliberately LEFT DECLARED above at
   #EAECEA and no longer referenced for the resting state, so the value is
   still on record if the question reopens.

   HOVER IS UNTOUCHED: the basket still fills --ms-navy under the pointer,
   which is TASK-0084 behaviour and is not in question. Against white the
   resting-to-hover contrast is larger than it was, not smaller. */
.ms-vtable-add {
    background: var(--ms-white);
    border-color: var(--ms-white);
}

/* ==========================================================================
   TASK-0087 — the variant table below 992. The table scrolls sideways; the Qty
   column (stepper + basket) pins to the right; the filter row wraps.
   ========================================================================== */

/* WHAT WAS MEASURED, AND WHY THE PIN IS ONE COLUMN AND NOT THREE.
   AC 0.2 is a halt gate and it tripped: Stock (114.2px) + Qty (170px) = 284.2px,
   against a 55% bar. That is 57.3% of the width the page reports at a 390
   override and 72.9% of 390 itself — over either way, and the packet forbids
   shrinking operator-set values to make it fit. Q-0143 costed the options; the
   operator answered "quantity and basket" and Stock scrolls with the rest.

   Qty alone is 170px = 43.6% of 390. Under the bar with room.

   THE BASKET IS INSIDE THE QTY COLUMN — 170px is stepper 98 + basket 40 +
   padding — so "pin quantity and basket" is one sticky column, not two.

   Column widths do not change with the viewport: cells are white-space: nowrap,
   so the table is 978.1px wide at 991, 768, 390 and 320 alike. The pin is a
   fixed 170px whatever the phone is, which is what makes the 43.6% durable. */

@media (max-width: 991.98px) {

    /* --- item 2: Qty pins right ------------------------------------------
       THE PINNED CELL MUST PAINT, and that is the one place TASK-0086b's
       transparent-cells rule cannot hold: a sticky column with no ground has
       the scrolling columns sliding visibly underneath it. The packet says so
       explicitly ("needs a ground that is not transparent").

       --ms-vtable-pin-bg IS A LITERAL AND THAT IS A KNOWN COST. The section's
       ground is rgb(239,239,239), which comes from a colour scheme and cannot
       be read from CSS; #efefef is already in mouldshop.css twice as an
       operator eyedrop. So this is ONE value to change if a row is ever set to
       a different scheme — declared here, alone, rather than scattered. Above
       992 nothing paints and the scheme still shows through, because this whole
       block is inside the media query. */
    .ms-vtable-table {
        --ms-vtable-pin-bg: #efefef;

        /* STICKY TABLE CELLS DO NOT WORK UNDER border-collapse: collapse IN CHROMIUM.
           This is the defect the operator caught on the first phone smoke: the rule
           was written, the cell computed `position: sticky`, and nothing pinned —
           Stock sat clipped at the right edge and Qty stayed off-screen. Chromium
           has never supported sticky on a cell of a collapsed table; the cell is
           laid out by the table algorithm and the sticky offset is discarded.

           So below 992 the table goes to `separate` with ZERO spacing. That is not
           a reversal of TASK-0085b, which removed the GAPS: border-spacing stays 0
           on both axes, so no gap comes back. What changes is only which box draws
           a shared edge, and since this file only ever sets `border-bottom` (plus
           the header's own and the pinned cell's left), no edge is declared twice
           and nothing doubles. Above 992 `collapse` is untouched. */
        border-collapse: separate;
        border-spacing: 0;
    }

    /* SPECIFICITY, and this is what actually stopped the pin working on the first
       phone smoke. TASK-0086a declares `.ms-vtable-table tbody td { position:
       relative }` so the column dividers have a containing block. That is 0-1-2;
       `.ms-vtable-td--qty` alone is 0-1-0, so `relative` won and the cell never
       became sticky. Measured, not deduced: the cell reported
       `position: relative` with `right: 0px` sitting uselessly beside it.

       I had blamed border-collapse and said it was fixed without looking. The
       collapse change WAS a real prerequisite — Chromium discards sticky offsets
       on cells of a collapsed table — but it was not the blocker, and claiming a
       fix from a plausible cause instead of a measurement is what cost the
       operator two reload cycles. */
    .ms-vtable-table thead th.ms-vtable-th--qty,
    .ms-vtable-table tbody td.ms-vtable-td--qty {
        position: sticky;
        right: 0;
        z-index: 2;
        background: var(--ms-vtable-pin-bg);
    }

    /* The header's pinned cell sits above the body's so a scrolled body column
       cannot ride over the header while both are stuck to the same edge. */
    .ms-vtable-table thead .ms-vtable-th--qty {
        z-index: 3;
    }

    /* THE LEFT RULE IS WITHDRAWN — operator, 2026-09-19, on the first working
       phone smoke: "the divider between price and quantity on mobile, it
       doesn't make sense anymore."

       He is right and it was mine, added in this packet an hour earlier on the
       reasoning that "a pinned column with no boundary reads as text floating
       over the row". Two things wrong with it:

         1. IT NAMES A RELATIONSHIP THAT NO LONGER EXISTS. A rule between two
            columns separates those two columns. Once Qty is pinned, Price is
            not beside it — whatever happens to be scrolled underneath is. The
            line separated the pinned column from nothing in particular.
         2. IT DID NOT MATCH THE RULES AROUND IT. Every other divider in this
            table is 50% of the cell height and centred (TASK-0086a). This one
            was full height, so it read as a different kind of line in a table
            that TASK-0086c had just finished making consistent.

       Nothing replaces it. The pinned cell's opaque ground is what separates it
       from the scrolling columns, and that is the only separation that is
       true. A left shadow was offered as the conventional frozen-column
       treatment and not taken up; if it is ever wanted it belongs here. */

    /* AND THE HOVER'S TOP BORDER COMES OFF, because `separate` removes the very
       thing it was compensating for. TASK-0086d added `border-top` to the
       hovered row after the operator saw the row ABOVE lose its line: under
       `collapse` the two rows share one edge, and the hovered row's white
       background and z-index painted over it. With `separate` there is no
       shared edge — the row above draws its own bottom, which nothing covers —
       so the top border would be a SECOND line stacked against it, 2px where
       every other boundary is 1px. Desktop keeps it; it is still needed there. */
    .ms-vtable-table tbody tr:hover > td,
    .ms-vtable-table tbody tr:has(:focus-visible) > td {
        border-top: 0;
    }

    /* Hover parity: the row goes white, so the pinned cell goes white with it
       or the hovered row reads as two different colours. :focus-within too, as
       everywhere else in this file. */
    .ms-vtable-table tbody tr:hover > td.ms-vtable-td--qty,
    .ms-vtable-table tbody tr:has(:focus-visible) > td.ms-vtable-td--qty {
        background: var(--ms-white);
    }

    /* --- item 3: the filter controls stay on ONE line ---------------------
       WITHDRAWN AFTER THE FIRST SMOKE. This was `flex-wrap: wrap`, reading the
       operator's "same as desktop, wraps" as the head wrapping. It wrapped the
       wrong thing: the head is label + chevron + number + X, so wrapping split
       each column's LABEL from its own chevron — visible in his screenshot as a
       row of bare arrows under a heading.

       And there is nothing for the row to wrap onto. The filter row IS the
       table header, and below 992 the header scrolls sideways with its columns
       rather than reflowing — so "wraps" has no meaning here once item 1 makes
       the table a scroller. The controls stay on one line and travel with their
       column, which is what the operator is actually looking at. */
    .ms-vtable-facet-head {
        flex-wrap: nowrap;
    }

    /* 44px TAP TARGETS WITHOUT GROWING THE ROW. Setting min-width/min-height on
       the controls themselves would push the header row to 44px+ and shove the
       whole table down the page. The hit area is expanded with a centred
       pseudo-element instead, so the chevron stays 10px and the X stays ~18px
       on screen while both take a 44px press. */
    .ms-vtable-facet-chev,
    .ms-vtable-facet-clear {
        position: relative;
    }

    .ms-vtable-facet-chev::before,
    .ms-vtable-facet-clear::before {
        content: "";
        position: absolute;
        top: 50%;
        left: 50%;
        width: 44px;
        height: 44px;
        transform: translate(-50%, -50%);
    }
}

/* ==========================================================================
   Operator-directed, 2026-09-19, no packet: the scroll-edge fade.
   "when the table is hidden - not scrolled yet - there is a fade in opacity
    field - that shows the user that something is hidden"
   "it needs to disappear on scroll end - but not suddenly - in progress"
   ========================================================================== */

@media (max-width: 991.98px) {

    /* THE FADE IS PAINTED BY THE PINNED CELL, on its left edge, because that is
       where the columns actually go. Putting it on the scroll container's right
       edge — the usual place — would have put it BEHIND the pin, fading nothing
       the user can see.

       ::before is free here: the 50%-height column divider uses ::after, and
       the pinned cell is :last-child so it never had one.

       right: 100% parks the band immediately left of the cell and it travels
       with it, because the cell is sticky. Every row paints its own slice;
       stacked, they read as one continuous band down the table.

       OPACITY COMES FROM THE JS, as a 0..1 ratio of remaining scroll distance —
       progressive, per the operator, rather than a class that snaps off on the
       last pixel. Default 0 so that a build with the script blocked shows no
       band rather than a permanent one. */
    .ms-vtable-table tbody td.ms-vtable-td--qty::before,
    .ms-vtable-table thead th.ms-vtable-th--qty::before {
        content: "";
        position: absolute;
        top: 0;
        bottom: 0;
        right: 100%;
        /* Operator, 2026-09-19: "decrease size of it 25% and intensity by 30%".
           Both applied as stated rather than rounded to something tidier —
           28 x 0.75 = 21px, and .14 x 0.70 = .098. Leaving .098 rather than
           calling it .1 keeps the arithmetic checkable against his instruction;
           the difference is below a rendered step either way. */
        width: 21px;
        pointer-events: none;
        opacity: var(--ms-vtable-fade-r, 0);
        background: linear-gradient(to right,
                    rgba(10, 41, 121, 0) 0%,
                    rgba(10, 41, 121, .098) 100%);
    }

    /* THE BAND IS SHORTER THAN THE TABLE, top and bottom. Operator, 2026-09-19:
       "the shadow is cut off on top and bottom because table overflow hidden —
       and this is ok, but the shadow looks bad, so we need to offset it".

       The band is painted per CELL and the slices stack, so only the FIRST and
       LAST slices touch the container edge — those are the two that get inset.
       Everything between them keeps top/bottom 0 and the band stays continuous.

       THE LAST VISIBLE ROW IS NOT :last-child, and that is the whole difficulty.
       Rows are hidden with the `hidden` attribute rather than removed, so
       tr:last-child is variant 65, which is not rendered and whose ::before
       never paints. `:has(+ tr[hidden])` selects a visible row followed by a
       hidden one — the last visible one — and it re-evaluates on its own as
       Load more and filtering change which rows carry the attribute. A JS class
       set in render() would do the same and would be one more thing to keep in
       step; this cannot drift because it is the same state the rows already
       carry.

       The :last-child arm covers the case where nothing is hidden at all — a
       product with fewer variants than the page size, or a filter that leaves
       exactly one page. Without it the bottom slice would sit flush again on
       precisely those tables.

       10px is a feel value, chosen not measured, and it is symmetric. */
    /* SUPERSEDED BY THE MASK BELOW — the 10px inset was the wrong fix, and the
       measurement said so. The operator reported the band still looking cut off
       after it shipped, and the container was NOT clipping it: container rect
       1185-1822, band 1195-1812, ten pixels of clearance at both ends. So there
       was nothing to move it away from.

       WHAT HE IS SEEING IS THE BAND'S OWN FLAT ENDS. A 21px column of gradient
       that simply stops reads as a rectangle, not as a shadow — the cut is in
       the shape, not in the clipping. Moving a hard edge inwards keeps it a
       hard edge.

       The inset is kept at a smaller value so the band still stands off the
       container slightly, and the ends are now softened by a mask. */
    .ms-vtable-table thead th.ms-vtable-th--qty::before {
        top: 4px;
        -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 18px);
        mask-image: linear-gradient(to bottom, transparent 0, #000 18px);
    }

    .ms-vtable-table tbody tr:not([hidden]):has(+ tr[hidden]) > td.ms-vtable-td--qty::before,
    .ms-vtable-table tbody tr:not([hidden]):last-child > td.ms-vtable-td--qty::before {
        bottom: 4px;
        -webkit-mask-image: linear-gradient(to top, transparent 0, #000 18px);
        mask-image: linear-gradient(to top, transparent 0, #000 18px);
    }

    /* THE MASK GOES ON THE END SLICES ONLY, which is the whole reason this works
       without the separate overlay element the operator suggested. Masking every
       slice would fade the band out and back in at every row boundary — ten
       ghosts down the table instead of one shadow. The middle slices stay
       solid, so the three pieces read as one band with soft ends.

       If this still reads as cut, the next step is his version: one element in a
       wrapper outside the scroll container, sized independently of the rows,
       with its own soft ends. That needs the pinned column's width published to
       CSS so the overlay can sit to its left, and it is the right answer if the
       per-slice approach ever needs a shape the rows cannot express. */

    /* The band is a shadow of the content going under the pin, so it should not
       animate its own opacity — the number changes on every scroll frame and a
       transition would lag behind the finger. A transition is declared only for
       the case where the value jumps without a scroll: Load more and filtering
       both change scrollWidth in one step. 120ms is short enough not to trail. */
    @media (prefers-reduced-motion: no-preference) {
        .ms-vtable-table tbody td.ms-vtable-td--qty::before,
        .ms-vtable-table thead th.ms-vtable-th--qty::before {
            transition: opacity 120ms linear;
        }
    }
}

/* ==========================================================================
   TASK-0137 — the row no longer sticks after a stepper click; hover eases.

   ITEM 1, THE STUCK ROW. Every active-row rule in this file was written as
   `tr:hover, tr:focus-within`. A mouse click on − / + FOCUSES that <button>
   (Chromium, Firefox), and :focus-within then holds the row in its hover look
   after the pointer leaves — until a click on another row's stepper moves the
   focus there. That is the operator's report exactly, and it is our rule, not
   vendor's: the stepper JS (mouldshop-variant-table.js) only nudges the value.
   The pair is now `tr:hover, tr:has(:focus-visible)`, replaced in place at all
   17 selectors: a keyboard still lights the row, a mouse click no longer
   does. Same specificity (0,1,0 either way), so no cascade order moves.
   Clicking INTO the quantity field still holds the row while the caret is
   there — a text input matches :focus-visible on click, by design; you are
   editing that row. The comments above that say :focus-within describe the
   rule as it was.

   ITEM 2, THE EASE. One timing, 200ms ease-out, both directions, on what the
   hover changes: row + cell ground, row text colour, lift shadow, cell bottom
   line and column rules, item-number colour, stepper and basket colours.
   font-weight on the item number is NOT eased — Roboto ships static weights,
   so it would snap mid-transition anyway.

   THE ROW MUST STAY ON TOP WHILE ITS SHADOW FADES. The hovered row carries
   `position: relative; z-index: 1`; if either dropped at hover-out, the next
   row's opaque cells would cover the fading shadow at once and the exit would
   read as a cut. So every body row is `position: relative` (no z-index at
   rest, so no new stacking context and the pinned Qty cell below 992 is
   untouched), and z-index returns to auto only after the 200ms — a discrete
   step held by its delay. On entry the delay is 0.
   ========================================================================== */
@media (prefers-reduced-motion: no-preference) {
    .ms-vtable-table tbody tr {
        position: relative;
        transition:
            background-color 200ms ease-out,
            color 200ms ease-out,
            box-shadow 200ms ease-out,
            z-index 0s linear 200ms;
        transition-behavior: allow-discrete;
    }

    .ms-vtable-table tbody tr:hover,
    .ms-vtable-table tbody tr:has(:focus-visible) {
        transition-delay: 0s;
    }

    .ms-vtable-table tbody td {
        transition:
            background-color 200ms ease-out,
            border-bottom-color 200ms ease-out;
    }

    .ms-vtable-table tbody td::after {
        transition: background-color 200ms ease-out;
    }

    .ms-vtable-num {
        transition: color 200ms ease-out;
    }

    .ms-vtable-qty,
    .ms-vtable-add {
        transition:
            background-color 200ms ease-out,
            border-color 200ms ease-out,
            color 200ms ease-out;
    }
}

@media (prefers-reduced-motion: reduce) {
    .ms-vtable-table tbody tr,
    .ms-vtable-table tbody td,
    .ms-vtable-table tbody td::after,
    .ms-vtable-num,
    .ms-vtable-qty,
    .ms-vtable-add { transition: none; }
}

/* ==========================================================================
   OPERATOR, 2026-09-26 - the column filter dropdown (a) looks exactly like the category page's sort
   dropdown, and (b) is no longer cut off by the table.

   (b) WHY IT WAS CUT OFF. The menu was absolute inside .ms-vtable-scroll, whose overflow-x: auto clips
   everything that leaves it - which also forces a vertical scrollbar onto the table (the operator's
   screenshot). Not a z-index problem: no z-index escapes an overflow clip. The open menu is now
   position: fixed, placed by mouldshop-variant-table.js from the header cell's rect, and closed on any
   outside scroll or resize so it never drifts from its column. z-index 900: the sort panel's value,
   above a lifted card, under the sticky header (1000). No ancestor of the table sets transform /
   filter / contain, so fixed is relative to the viewport.

   (a) THE SORT PANEL'S LOOK, from its own tokens (mouldshop-listbar-sort.css), so the two cannot
   drift: white, 1.5rem radius, soft shadow, no border; options in the sort's grey between light
   rules, the ticked value blue, the pointer/keyboard highlight navy. The checkbox stays in the DOM
   (it is still what filters, and what a keyboard and a screen reader use) but is not drawn - the
   sort panel draws no boxes. The count stays, in the same grey, right-aligned. */
.ms-vtable-facet-menu.is-fixed {
    position: fixed;
    z-index: 900;
}

.ms-vtable-facet-menu {
    min-width: 12rem;
    padding: .5rem 0;
    border: 0;
    border-radius: var(--ms-sort-panel-radius, 1.5rem);
    background: var(--ms-sort-panel-bg, #FFFFFF);
    box-shadow: var(--ms-sort-panel-shadow, 0 6px 18px rgba(0, 0, 0, .16));
    font-family: var(--ms-font-body);
}

.ms-vtable-facet-values { padding: 0; }

.ms-vtable-facet-value {
    position: relative;
    gap: 1rem;
    padding: .75rem 1.5rem;
    color: var(--ms-sort-option-ink, #A6A6A6);
    font-size: .9375rem;
    font-weight: 500;
    line-height: 1.5;
    white-space: nowrap;
}

/* The rule between options, inset from the panel's rounded end - the sort's own construction. */
.ms-vtable-facet-value + .ms-vtable-facet-value::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    right: .625rem;
    height: 1px;
    background: var(--ms-sort-rule, #E4E7EC);
}

/* Not drawn, still there: focusable, clickable through its label, read by assistive tech. */
.ms-vtable-facet-value input[type="checkbox"] {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

.ms-vtable-facet-value:hover,
.ms-vtable-facet-value:has(input:focus-visible) {
    color: var(--ms-sort-option-hover, #0A2979);
}

.ms-vtable-facet-value:has(input:checked) {
    color: var(--ms-sort-option-on, #6CB2DE);
}

.ms-vtable-facet-n {
    color: var(--ms-sort-option-ink, #A6A6A6);
    font-size: .8125rem;
}
