/* ==========================================================================
   OstuGrupp — THE ARCHIVE FRAME (archive-polish.css)

   The page this file is about: /pood/, every product category, every product
   tag. ~3472 products (counted by card-d3.css:31 against live on 2026-08-14),
   and the place a shopper actually browses. The design pass has covered the
   hero, the product card, the cart, the checkout and the product page. The
   CARD is card-d3.css and is not touched by a single selector here. What was
   never designed is everything AROUND the grid — the header, the control row,
   the band between them, and the page a shopper lands on when a filter
   returns nothing.

   PURE STYLE LAYER. It renders nothing, hooks no template, prints no markup
   and invents no text. Every selector below already exists in the DOM that
   inc/shop-extras.php, inc/ajax-filters.php and WooCommerce's own
   archive-product.php emit today:

     div.container.section-y.woo-content         inc/woocommerce.php:43
       nav.woocommerce-breadcrumb                inc/woocommerce.php:45
       header.woocommerce-products-header
         h1.woocommerce-products-header__title
         div.term-description                    taxonomy archives
         div.page-description                    the /pood/ page's own intro
       div.ostu-shop-layout                      inc/ajax-filters.php:797
         button.ostu-filters-mobile-trigger      inc/ajax-filters.php:798
         aside.ostu-filters                      inc/ajax-filters.php:807
         div.ostu-shop-main                      inc/ajax-filters.php:832
           div.ostu-shop-toolbar                 inc/shop-extras.php:99
             p.woocommerce-result-count          WC core, priority 20
             form.woocommerce-ordering > select.orderby   WC core, priority 30
           ul.products > li.product-card         (card-d3.css owns these)
           nav.woocommerce-pagination
           div.ostu-load-more                    inc/ajax-filters.php:851

   ------------------------------------------------------------------------
   THREE MOMENTS, NOT THIRTY IMPROVEMENTS
   ------------------------------------------------------------------------
   1 · THE CONTROL ROW. inc/shop-extras.php:99 wraps the result count and the
       sort select in .ostu-shop-toolbar, and assets/css/shop.css:15-27 draws
       that wrapper as a CARD — white, 1px border, --radius-lg, --shadow-card.
       The pack's own elevation ladder says a card is level 1 and lists what
       lives there: "tootekaart, paneel, kleepuv riba" (D4, sample:1436-1444,
       transcribed in docs/DISAIN-KOMPONENDID.md section D4). A toolbar is
       none of the three; it is "sisuplokk lehel" — level 0, "ainult piir".
       So above the grid a shopper currently meets three floating white boxes
       in a row (breadcrumb pill, toolbar card, then the cards themselves)
       before the first product, and that stack is a large part of what
       "thrown together" looks like. Here the toolbar becomes a bounded rule
       row: no fill, no shadow, no corner, one hairline along the bottom that
       spans exactly the grid width and reads as the lid of the results.

       And the control inside it. The sort select is the single most-used
       control on this page and it announces itself with nothing at all: a
       white box whose only glyph is a chevron shared with every other select
       in the shop. It gets a sort glyph on its leading edge, the pack's field
       border (1.5px --border-strong, D2) and a hover state it has never had.
       Its FONT SIZE STAYS 16px — D2's reason is not aesthetic: "telefonis on
       see piir, millest allpool iOS lehe sisse suumib ja ostja kaotab
       konteksti", and 73% of this shop is a phone.

   2 · THE BAND. On a phone the filter button and the control row are the same
       gesture split across two parents: .ostu-filters-mobile-trigger is a
       child of .ostu-shop-layout (inc/ajax-filters.php:798) and the toolbar is
       a child of .ostu-shop-main (inc/shop-extras.php:99), with the layout's
       28px gap (filters.css:15) between them. They are pulled into one band.
       More importantly the button gains the state it never had: with filters
       ON it looks EXACTLY as it does with filters off, apart from a 20px
       badge. On a phone the chips live inside the off-canvas drawer
       (inc/ajax-filters.php:816) — invisible until the drawer is opened — so
       that badge is the ONLY thing on screen telling a shopper they are
       looking at a filtered subset of 3472 products. It should not be the
       only thing. See section 3.

   3 · THE PAGE WITH NOTHING ON IT. Filter down to zero and this is a real
       page a real shopper reaches, and it currently looks like an error: a
       460px card holding a 40px magnifying glass inside a grey circle
       (shop.css:49-69). The shop already owns a purpose-drawn illustration
       for exactly this state — assets/img/illustrations/search-empty.svg, the
       pack's C set (sample:809-833) — and empty-states.css installed the other
       three (cart, wishlist, 404) while this one, the fifth, kept the glyph.
       It joins them: same drawing, same clamp(104px, 26vw, 136px) as
       empty-states.css:46-48, same 560px measure that empty-states.css:166
       chose so that "all four now share one measure". Five now.

   ------------------------------------------------------------------------
   WHAT THIS FILE DELIBERATELY DOES NOT TOUCH
   ------------------------------------------------------------------------
   · THE PRODUCT CARD. Not one selector below matches inside li.product-card.
     card-d3.css owns it.
   · THE FILTER CHIPS. pack-components.css:433-600 rebuilt them to D5 in this
     same release. Restyling them here would be two authors on one component,
     which is how a value drifts. The one chip fact this file acts on is a
     COMPOSITION fact, not a chip fact: on a phone they are inside a drawer.
   · FOCUS RINGS. a11y.css:33-46 owns :focus-visible shop-wide at (0,2,0) with
     outline + a double box-shadow. Nothing here declares outline or a focus
     box-shadow. The select's :focus-visible rule below sets border-colour
     ONLY, which a11y.css does not set, so the ring stays exactly one ring.
   · PAGINATION MATERIAL. It is NOT unstyled core markup — woocommerce.css:
     386-442 already builds 44px pills with --radius, tabular-nums and a solid
     green .current, and motion.css:218-262 owns their hover/press/focus. It
     sits three values away from D10 (1.5px --border-strong instead of 1px
     --border; --brand-a1 + --brand-a3 on hover instead of white + green text;
     the dots at --text-subtle). Three values inside a file I may not edit is
     a report, not a rule — see the handover. The one pagination fact this
     file acts on is again composition: the gap between the numbers and "Lae
     rohkem", which D10 says are ONE control, not two.
   · THE BREADCRUMB. woocommerce.css:26-39 gives it a pill; it appears on the
     product page too, so it is shop-wide furniture, not archive frame.
   · THE RESULT-COUNT SENTENCE ITSELF. D10 marks the "Näidatud 48 toodet
     287-st" counter as ⏸ waiting on server data (sample:1596-1599) because
     AJAX sends a per-page number that would be stale on arrival. Rule 2 of
     the pack: a wrong number is worse than no number. Nothing here invents
     one, and no ::before/::after adds a word to it — a CSS-injected Estonian
     plural is how you get "1 toodet" (docs/DISAIN-KOMPONENDID.md 5.2c).

   ------------------------------------------------------------------------
   SPECIFICITY (Trap 1) AND FALLBACKS
   ------------------------------------------------------------------------
   Every rule states what it had to beat. NOTHING BELOW WAS MEASURED IN A
   BROWSER — there was none in this session — so the arithmetic is arithmetic,
   not a computed value, exactly as CLAUDE.md Trap 1 warns. Where a rule only
   has to beat a sheet this file is guaranteed to load after, it is still
   written one class heavier, because load order is the thing that stops being
   true when a plugin recombines stylesheets.

   Every var() carries its literal. tokens.css is NOT deployed live yet
   (verified for card-d3.css:143-146 on 2026-08-14: the live page enqueues
   main.css but no tokens.css), so --brand, --text-muted, --space-*, --tap-min
   and the --text-* type steps do not resolve on the live site today and an
   unresolvable custom property is the guaranteed-invalid value, not a
   fallback to the old one.

   NOTHING HERE GOES NEAR THE CHECKOUT. The stylesheet is enqueued on shop and
   product-taxonomy archives only (inc/archive-polish.php), where is_cart(),
   is_checkout() and is_account_page() are all false, and it names no payment
   selector of any kind (CLAUDE.md invariant 1).
   ========================================================================== */


/* ==========================================================================
   1 · THE HEADER BLOCK — a title, a paragraph, and a measure
   ==========================================================================
   Two archive types print two different descriptions and the theme styles
   only one of them:

     .term-description   woocommerce.css:72 — 720px, --slate-600, 8px top
     .page-description   NOTHING, anywhere. Verified: grep over assets/css
                         returns zero matches.

   .page-description is the intro text of the /pood/ page itself, i.e. the
   most-visited archive in the shop, and it currently runs at the full
   .container measure — 1440px minus 2x32px padding = 1376px at desktop,
   roughly 170 characters per line at 16px. That is the exact defect
   type-rhythm.css:42-47 measured on the product description and fixed with
   68ch, and this is the same defect on a different block, so it takes the
   same value rather than a new one. Below ~900px the container is already
   narrower than 68ch and the cap does nothing (type-rhythm.css:196-200).

   The two descriptions also disagree on colour: .term-description is muted,
   .page-description inherits body --fg. One role, two colours, decided by
   which archive you happened to open. Both become --text-muted.

   Specificity: both the (0,2,0) scoped form and the (0,1,0) bare form are in
   the list, and a selector list is evaluated per selector, so each applies at
   its own weight — the scoped form beats woocommerce.css:72 (0,1,0) outright
   and the bare form covers the case where a plugin moves the description out
   of <header>. Same both-shapes technique as type-rhythm.css:49-56.        */

.woocommerce-products-header .term-description,
.woocommerce-products-header .page-description,
.term-description,
.page-description {
  color: var(--text-muted, #475569);
  margin: var(--space-12, 12px) 0 0;
}

.woocommerce-products-header .term-description :is(p, ul, ol),
.woocommerce-products-header .page-description :is(p, ul, ol),
.term-description :is(p, ul, ol),
.page-description :is(p, ul, ol) {
  max-width: 68ch;
}

.woocommerce-products-header .term-description p,
.woocommerce-products-header .page-description p,
.term-description p,
.page-description p {
  /* Stops an Estonian paragraph ending on a single orphaned word. Browsers
     that do not know the value ignore the declaration. Same line, same
     reason, as type-rhythm.css:213-218. */
  text-wrap: pretty;
}

/* The gap the brief names: header block to first row of products. The
   sequence this sets, top to bottom, is
       h1 (--h1, type-rhythm.css:101)
       12px   description
       32px   control row
       14px   the hairline
       24px   first card       (ul.products margin-top, woocommerce.css:89)
   Was 24px from description to the toolbar CARD, then 4px, then 24px — three
   gaps that never agreed on what belonged to what. 32px is the pack's own
   step; nothing new is introduced.
   Specificity: (0,2,0) over woocommerce.css:70 (0,1,0) and its <=768px
   override at woocommerce.css:1580 (0,1,0), which is why the phone value is
   restated in the media query at the end of this file rather than left to
   the sheet it now outranks. */
.woo-content .woocommerce-products-header {
  margin-bottom: var(--space-32, 32px);
}


/* ==========================================================================
   2 · THE CONTROL ROW — a card becomes a rule
   ==========================================================================
   ⚠️ THE RISKIEST CALL IN THIS FILE, and the first thing to look at on the
   live site. It is one rule block and the module has an owner switch, so it
   is also the cheapest thing in this file to take back.

   Two container forms are given because the toolbar has two possible
   parents: inc/ajax-filters.php:832 opens .ostu-shop-main at priority 5 on
   woocommerce_before_shop_loop and inc/shop-extras.php:99 opens the toolbar
   at priority 19, so on a normal archive the toolbar is inside it — but the
   layout wrapper is fenced behind ostugrupp_filters_is_catalog()
   (inc/ajax-filters.php:42-44) while the toolbar is not, so the .woo-content
   form is what keeps this from being conditional on a predicate that can
   drift away from the enqueue predicate.

   Specificity: (0,2,0) beats shop.css:15 (0,1,0) without depending on load
   order. background/border/box-shadow/radius are all reset explicitly rather
   than by omission — shop.css declares each of them and the cascade is
   per-property.                                                            */

.ostu-shop-main .ostu-shop-toolbar,
.woo-content .ostu-shop-toolbar {
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--border, #e2e8f0);
  border-radius: 0;
  box-shadow: none;
  padding: 0 0 var(--space-12, 12px);
  margin: 0;
  gap: var(--space-12, 12px);
}

/* The count. It was already 14px/600/muted (shop.css:29-35); the one thing it
   was missing is the thing it is made of. tabular-nums stops "1-12" and
   "3472" from shifting the sentence sideways every time the AJAX layer
   rewrites this element (assets/js/filters.js:164-165 replaces its innerHTML
   on every filter, sort and page change).
   font-variant-numeric MUST come after the font shorthand: the shorthand
   resets font-variant, and font-variant-numeric is part of it.
   Specificity: (0,3,0) over shop.css:29 (0,2,0) — an equal-weight tie broken
   only by load order is exactly what a plugin that concatenates stylesheets
   silently reverses. */
.woo-content .ostu-shop-toolbar .woocommerce-result-count {
  font: var(--text-sm, 400 14px/20px "DM Sans", system-ui, sans-serif);
  font-weight: var(--fw-semibold, 600);
  font-variant-numeric: tabular-nums;
  color: var(--text-muted, #475569);
  margin: 0;
  min-width: 0;
}

/* ---- The sort control --------------------------------------------------
   Two background images: a sort glyph on the leading edge, the chevron on the
   trailing one. The glyph is what turns "a white box that says Vaikimisi
   järjestus" into "the sort control" at a glance — the <select> carries an
   aria-label (WC core) so a screen reader has always had this information;
   a sighted shopper has not.

   Both glyphs are drawn at --text-muted (#475569). woocommerce.css:80 draws
   its chevron at #334155 and is restated here so the control does not carry
   two greys. A hard hex inside a data: URI is unavoidable — a background SVG
   cannot read currentColor or a custom property — and it is the same
   technique, in the same property, that woocommerce.css:80 already uses.

   16px type, deliberately: D2, "Kõik väljad 52px kõrged ja 16px kirjaga —
   telefonis on see piir, millest allpool iOS lehe sisse suumib". The control
   inherits 16px today; anything smaller would introduce an iOS zoom-on-focus
   bug on 73% of the traffic to save four pixels.

   44px, deliberately: --tap-min, and D1's size S ("tootekaart, tabelirida").
   This is a toolbar control, not a form field, so it keeps the 44px height
   woocommerce.css:81 already gives it. 10 + 24 (the --text-md line box) + 10
   = 44 exactly, so the min-height is a floor and not a stretch.

   appearance is restated rather than inherited from woocommerce.css:78 so
   that this control cannot silently lose its custom chrome if that line is
   ever edited while these two background images stay.

   Specificity: (0,3,1). It must beat woocommerce.css:77's second selector
   `.woocommerce select.orderby` (0,2,1) — .woocommerce is a BODY class here,
   so that rule reaches this control — and it does so on weight, not order. */
.woo-content .ostu-shop-toolbar select.orderby,
.woo-content .ostu-shop-toolbar .woocommerce-ordering select {
  appearance: none;
  -webkit-appearance: none;
  box-sizing: border-box;
  min-height: var(--tap-min, 44px);
  max-width: 100%;
  padding: 10px 36px 10px 40px;
  border: 1.5px solid var(--border-strong, #cbd5e1);
  border-radius: var(--radius-12, 12px);
  background-color: var(--surface, #ffffff);
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 24 24' fill='none' stroke='%23475569' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M4 7h13M4 12h9M4 17h5'/%3E%3C/svg%3E"),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%23475569' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat, no-repeat;
  background-position: left 12px center, right 12px center;
  background-size: 18px 18px, 14px 14px;
  font: var(--text-md, 400 16px/24px "DM Sans", system-ui, sans-serif);
  font-weight: var(--fw-semibold, 600);
  color: var(--text, #0f172a);
  cursor: pointer;
  transition: border-color var(--dur, 200ms) var(--ease, cubic-bezier(.2, .6, .3, 1));
}

/* Hover is fenced behind (hover: hover). A latched :hover after a tap is the
   bug motion.css:666-724 exists to clean up; a rule that never matches on
   touch needs no cleanup. Same fence, same reason, as card-d3.css:168. */
@media (hover: hover) {
  .woo-content .ostu-shop-toolbar select.orderby:hover,
  .woo-content .ostu-shop-toolbar .woocommerce-ordering select:hover {
    border-color: var(--brand-a3, rgba(4, 120, 87, .20));
  }
}

/* border-colour ONLY. a11y.css:33-46 owns the ring (outline + a double
   box-shadow) at (0,2,0) and sets neither border nor border-color, so this is
   additive and the shop keeps exactly one focus ring. */
.woo-content .ostu-shop-toolbar select.orderby:focus-visible,
.woo-content .ostu-shop-toolbar .woocommerce-ordering select:focus-visible {
  border-color: var(--brand, #047857);
}


/* ==========================================================================
   3 · THE BAND — the filter button and the control row are one gesture
   ==========================================================================
   Below 1024px filters.css:405-423 makes .ostu-filters position:fixed and
   .ostu-filters-backdrop is fixed too (filters.css:392), so both are
   out-of-flow grid children and contribute no track. The grid's in-flow rows
   are therefore exactly two: the "Filtrid" button and .ostu-shop-main. The
   28px shorthand gap at filters.css:15 is the distance between them.

   At >=1024px the layout is `280px 1fr` (filters.css:18) — one row, two
   columns — so row-gap has nothing to act on and the column gap is left at
   28px by setting row-gap alone rather than the shorthand.

   16px instead of 28px: the two controls read as one band, and a phone gets
   12px of screen back above the fold.
   Specificity: (0,2,0) over filters.css:11 (0,1,0).                        */

.woo-content .ostu-shop-layout {
  row-gap: var(--space-16, 16px);
}

/* THE STATE THE BUTTON NEVER HAD.
   assets/js/filters.js:140-155 creates .ostu-filters-mobile-trigger__count
   as a child of the trigger when — and only when — at least one filter is
   active, and removes the element again at zero. So the badge's EXISTENCE is
   already a truthful signal of filtered-ness; nothing here invents state.
   Until now that signal was carried by a 20px pill and nothing else, while
   the chips that say WHICH filters are on are inside the off-canvas drawer
   (inc/ajax-filters.php:816) and cannot be seen without opening it.

   Colours are D5's active-filter family (--brand-a1 fill, --brand-a3 border,
   --brand text), so the button in its on-state matches the chips it stands
   for rather than inventing a fourth way to say "active".

   :has() is deliberately alone in its own rule: an unsupported selector
   invalidates the whole selector LIST it appears in, so pairing it with
   anything would take that something down with it in an older browser. On a
   browser without :has() the button simply keeps today's appearance and the
   badge still shows — the fallback is the status quo, never a broken state.
   Specificity: (0,3,0) — :has() itself adds nothing, its argument adds one
   class — over filters.css:358 (0,1,0) and motion.css:207 (0,1,0). */
.ostu-shop-layout .ostu-filters-mobile-trigger:has(.ostu-filters-mobile-trigger__count) {
  background: var(--brand-a1, rgba(4, 120, 87, .06));
  border-color: var(--brand-a3, rgba(4, 120, 87, .20));
  color: var(--brand, #047857);
}


/* ==========================================================================
   4 · THE KEEP-GOING BLOCK — numbers and "Lae rohkem" are one control
   ==========================================================================
   D10, verbatim: "«Lae rohkem» ja lehitsemine on koos, mitte kumbki eraldi:
   nupp laeb järgmise 48, numbrid jäävad alles, et ostja saaks tagasi tulla ja
   lingi jagada." (sample:1590, transcribed in DISAIN-KOMPONENDID.md D10.)
   The theme prints both, in the right order — woocommerce_after_shop_loop at
   priority 10 for the numbers, inc/ajax-filters.php:860 at priority 50 for
   the button — and then separates them by 32px (filters.css:449) on top of
   the 40px that already lifted the numbers off the grid
   (woocommerce.css:393). Two gaps of near-equal size read as two unrelated
   afterthoughts. 16px binds them into the single control the pack describes,
   while the 40px above still separates that control from the grid.
   Specificity: (0,2,0) over filters.css:446 (0,1,0).                       */

.ostu-shop-main .ostu-load-more {
  margin-top: var(--space-16, 16px);
}


/* ==========================================================================
   5 · THE PAGE WITH NOTHING ON IT
   ==========================================================================
   .ostu-empty-shop is rendered from TWO places with byte-identical markup:
     · inc/shop-extras.php:112-130 on woocommerce_no_products_found — first
       paint, e.g. an empty category. NOTE: archive-product.php only fires
       woocommerce_before_shop_loop when there ARE products, so on this path
       .ostu-shop-layout never opens and the panel is a direct flex child of
       .woo-content, with no sidebar and no toolbar anywhere on the page.
     · inc/ajax-filters.php:889-907 when a filter returns zero — the panel
       replaces ul.products INSIDE .ostu-shop-main, with the sidebar and the
       toolbar still on screen.
   Every rule below therefore carries both container forms, and the panel
   centres itself with margin-inline:auto so it is correct as a flex child and
   as a block child alike.

   Specificity: (0,2,0) over shop.css:49-80 (0,1,0).                        */

.woo-content .ostu-empty-shop,
.ostu-shop-main .ostu-empty-shop {
  max-width: 560px;
  margin: var(--space-32, 32px) auto var(--space-40, 40px);
  padding: var(--space-48, 48px) var(--space-32, 32px);
  background: var(--surface, #ffffff);
  border: 1px solid var(--border, #e2e8f0);
  border-radius: var(--radius-16, 16px);
  /* The pack's level-1 recipe written as a literal pair, byte-identical to
     card-d3.css:152-154. NOT var(--shadow-card): main.css:98 redefines that
     name to a lighter recipe and wins on load order
     (docs/DISAIN-KOMPONENDID.md section 1), so the panel would sit at a
     different elevation from the very cards it replaces. */
  box-shadow:
    0 1px 2px var(--ink-a1, rgba(15, 23, 42, .04)),
    0 2px 8px var(--ink-a2, rgba(15, 23, 42, .08));
  text-align: center;
}

/* 460px -> 560px: empty-states.css:160-167 measured the other empty states
   and settled on 560px explicitly so that "all four now share one measure".
   This is the fifth and it was not in that count. */

/* The drawing. The icon box is aria-hidden="true" in both renderers
   (inc/shop-extras.php:116, inc/ajax-filters.php:890), so a decorative
   background image is the correct mechanism and the 40px inline glyph it
   replaces is hidden rather than restyled.
   The illustration carries its own colours as hard hex — #94a3b8 line,
   #047857 accent — which is the pack's documented exception for the C set
   ("illustratsioon ei võta värvi kontekstist"), and is also what makes it
   usable as a background at all: a background SVG cannot inherit
   currentColor.
   clamp(104px, 26vw, 136px) is empty-states.css:46-48 verbatim: at 375px it
   resolves to the 104px floor, so the drawing can never dominate a phone.
   The `background:` shorthand is what removes shop.css:67's --muted fill; the
   radius is reset because shop.css:66 made this box a circle. */
.woo-content .ostu-empty-shop .ostu-empty-shop__icon,
.ostu-shop-main .ostu-empty-shop .ostu-empty-shop__icon {
  width: clamp(104px, 26vw, 136px);
  height: clamp(104px, 26vw, 136px);
  margin: 0 auto var(--space-20, 20px);
  border-radius: 0;
  background: transparent url("../img/illustrations/search-empty.svg") center / contain no-repeat;
}

.woo-content .ostu-empty-shop .ostu-empty-shop__icon > svg,
.ostu-shop-main .ostu-empty-shop .ostu-empty-shop__icon > svg {
  display: none;
}

/* The copy. The heading's SIZE is left exactly where shop.css:70-73 put it —
   type-rhythm.css owns heading rhythm shop-wide and this is an <h2>; changing
   its metrics here would take one heading out of that system for no reason
   the shopper can see. Only the rhythm around it moves, because the block
   above it went from a 72px circle to a ~120px drawing. */
.woo-content .ostu-empty-shop .ostu-empty-shop__text,
.ostu-shop-main .ostu-empty-shop .ostu-empty-shop__text {
  font: var(--text-md, 400 16px/24px "DM Sans", system-ui, sans-serif);
  color: var(--text-muted, #475569);
  margin: 0 0 var(--space-24, 24px);
}

/* A rule with nothing under it.
   On the AJAX path the toolbar survives into the empty state carrying an
   EMPTY result count — inc/ajax-filters.php:926-928 returns '' at zero — and
   a sort control for zero results, above a hairline that is now the lid of
   nothing. On the first-paint path that same situation prints no toolbar at
   all, because the hook that creates it never fires. Same situation, two
   appearances, decided by which code path produced it; that is the exact
   inconsistency inc/ajax-filters.php:894-901 already records for the copy of
   this component.
   Hiding it makes the two paths agree on everything except the sidebar, which
   is a PHP difference and is reported rather than patched here.
   :has() alone in its own rule again; without support the toolbar stays, i.e.
   today's behaviour. */
.ostu-shop-main:has(.ostu-empty-shop) .ostu-shop-toolbar {
  display: none;
}


/* ==========================================================================
   6 · PHONE — 375 / 768
   ==========================================================================
   The <=768px block at woocommerce.css:1569-1585 sets the phone values this
   file now outranks, so the two it would have silently dropped are restated
   here at the same breakpoint.

   The toolbar itself needs no stacking rule: form.woocommerce-ordering is
   width:100% below 768px (woocommerce.css:1582), the toolbar is flex-wrap:
   wrap (shop.css:17), and an item whose hypothetical size fills the line
   wraps onto its own line — so the count sits above a full-width control
   already, and shop.css:43-46 makes the same arrangement explicit below
   520px. Nothing here needs to fight either of them.                       */

@media (max-width: 768px) {
  /* woocommerce.css:1580 had this at 18px; the 32px above would otherwise
     apply on a phone, where the header block is tighter by design. */
  .woo-content .woocommerce-products-header {
    margin-bottom: var(--space-24, 24px);
  }

  /* woocommerce.css:1584 sets width:100% at (0,2,1); the base rule above is
     (0,3,1) and does not set width, so per-property that still applies — this
     restates it anyway so the control's phone width does not depend on
     someone else's file staying as it is. */
  .woo-content .ostu-shop-toolbar select.orderby,
  .woo-content .ostu-shop-toolbar .woocommerce-ordering select {
    width: 100%;
  }

  /* The empty panel had 28px of inset on a 375px screen and shop.css:88-90
     already trimmed it below 400px. One value, one breakpoint. */
  .woo-content .ostu-empty-shop,
  .ostu-shop-main .ostu-empty-shop {
    padding: var(--space-40, 40px) var(--space-20, 20px);
    margin-top: var(--space-24, 24px);
  }
}


/* ==========================================================================
   7 · REDUCED MOTION
   ==========================================================================
   tokens.css:303-305 zeroes --dur under prefers-reduced-motion, but tokens.css
   is not on the live site yet, so the literal fallback in the transition above
   would still animate for a shopper who asked it not to. The one transition
   this file declares is switched off explicitly.                           */

@media (prefers-reduced-motion: reduce) {
  .woo-content .ostu-shop-toolbar select.orderby,
  .woo-content .ostu-shop-toolbar .woocommerce-ordering select {
    transition: none;
  }
}


/* ==========================================================================
   8 · FORCED COLOURS
   ==========================================================================
   Forced-colours mode drops background images. Two things in this file are
   drawn with one, and both would otherwise become nothing at all:

   · The empty state's illustration. The inline 40px glyph it replaced is
     still in the DOM, so it is simply put back — a magnifying glass beats an
     empty box.
   · The sort control's two glyphs. Restoring the native appearance is better
     than restoring the images: the platform then draws its own dropdown arrow
     in the user's own colours, which is the entire point of the mode. The
     leading inset goes back to a plain 12px because there is no longer a
     glyph sitting in it.

   Same pattern, same reason, as cart-sticky-bar.css:162 and
   surfaces-d4-d12.css:276-286.                                             */

@media (forced-colors: active) {
  .woo-content .ostu-empty-shop .ostu-empty-shop__icon,
  .ostu-shop-main .ostu-empty-shop .ostu-empty-shop__icon {
    background-image: none;
  }

  .woo-content .ostu-empty-shop .ostu-empty-shop__icon > svg,
  .ostu-shop-main .ostu-empty-shop .ostu-empty-shop__icon > svg {
    display: block;
  }

  .woo-content .ostu-shop-toolbar select.orderby,
  .woo-content .ostu-shop-toolbar .woocommerce-ordering select {
    appearance: auto;
    -webkit-appearance: auto;
    background-image: none;
    padding-inline: var(--space-12, 12px);
  }
}


/* ==========================================================================
   9 · PRINT
   ==========================================================================
   The sort control and the filter button do nothing on paper. The result
   count does — it is the one line that says what the printed list is.
   Same treatment, same file position, as pack-components.css:606-610.      */

@media print {
  .woo-content .ostu-shop-toolbar .woocommerce-ordering,
  .ostu-shop-layout .ostu-filters-mobile-trigger {
    display: none !important;
  }

  .ostu-shop-main .ostu-shop-toolbar,
  .woo-content .ostu-shop-toolbar {
    border-bottom-color: #94a3b8;
  }
}
