/* ═══════════════════════════════════════════════════════════════════════
   Jarušica Shop — Mobile design (docs/mobile/SPEC.md v2, 2026-09-26)

   Loaded after style.css and jarusica-components.css (dependency
   `storefront-child-style` in functions.php), so every rule here can win
   on source order alone at equal specificity — no `!important` unless the
   rule it beats is itself `!important` (noted inline where that happens).

   Desktop stays pixel-identical except colour, radius and buttons
   (SPEC §1, ACCEPTANCE WP1). Everything that changes SIZE or SPACING sits
   inside the `@media (max-width:720px)` block, the design's own breakpoint.
   One commented section per WP, in WP order (ORCHESTRATE-BRIEF).
   ═══════════════════════════════════════════════════════════════════════ */

/* ── Token values (review fix H1, 2026-09-26) ──────────────────────────────
   `theme/jarusica-child/assets/jarusica-tokens.css` is CI-gated
   (.github/workflows/deploy.yml:73-82) to stay byte-identical to
   `design/jarusica-tokens.css` — SPEC §1 only allows child-theme changes,
   not the shared design source. It was previously edited in place, which
   would fail that gate; restored to `design/`'s values (`git checkout
   origin/main --`) and every new value from SPEC §2's token mapping moved
   here instead, as a plain `:root` override loaded after it. Colour,
   radius, gutter and focus are global per SPEC §1 (desktop stays
   pixel-identical "except colour, radius and buttons"); the SIZE tokens
   (`--jr-size-body/small/micro`) stay scoped ≤720px further down, unchanged
   from before this fix. */
:root {
  --jr-sky:         #0067A8; /* was #0073BB */
  --jr-ink:         #12314D; /* was #15324B */
  --jr-muted:       #4A6377; /* was #62798D */
  --jr-muted-2:     #4A6377; /* was #536D81 */
  --jr-mist:        #E7F2FA; /* was #F0F4FF */
  --jr-panel:       #F3F8FC; /* was #F4F8FB */
  --jr-line:        #DCE7F0; /* was #D8E4EF */
  --jr-pill-line:   #BFD3E3; /* was #B9CCDC */
  --jr-stock:       #0B6B3E; /* was #0F6B3F */
  /* SPEC §2.1: #9ECBEA measured 2.14:1 on white, fails WCAG 1.4.11 (needs
     3:1). #0067A8 (= --jr-sky above) reaches 5.99:1. */
  --jr-focus:       #0067A8; /* was #9ECBEA */
  --jr-r-sm:  12px; /* was 4px */
  --jr-r-md:  14px; /* was 6px */
  --jr-r-lg:  20px; /* was 8px */
  --jr-r-xl:  28px; /* was 11px */
  --jr-gutter: clamp(16px, 4vw, 40px); /* was 40px, same value at desktop */
}

/* ── WP1 — Tokens and base ────────────────────────────────────────────── */

/* Buttons — design's `.btn` base (SPEC §4 WP1). Applies globally: colour,
   radius and button geometry are the one thing this WP intentionally
   changes on desktop too (ACCEPTANCE WP1).

   `style.css:356` squares every `.button`/`button`/`.btn` with
   `border-radius:0 !important`. Selectors below repeat that rule's list so
   specificity ties and source order (this file loads after style.css)
   decides.

   NIEDRIG (review fix, 2026-09-26): the comment here used to claim this was
   "the only `!important` in this file" — true only at the point WP1 was
   written; the primary/secondary button colours two blocks down, and
   several later WPs' fixes for the same class of bug (M9b/c, this file's
   own review-fixes section), all needed one too, to beat other
   `!important` rules in style.css the same way. Corrected to not overclaim.

   Review fix M6d: the bare `button` selector also matched `.jar-qtybtn`
   (functions.php:895/907, the PDP quantity stepper) and `.jar-burger`
   (functions.php:532/547) — neither carries a `.button`/`.btn` class, both
   are self-contained icon controls with their own fixed dimensions
   (`.jar-qtybtn` 44×44px live, `#80`/ACCEPTANCE's own floor; `.jar-burger`
   48px live), not instances of the design's CTA button component this WP
   targets. The base's `min-height:48px; padding:0 22px` stretched
   `.jar-qtybtn` to 44→38→44px oddly and `.jar-burger` at 721–1080px
   (`.jar-burger`'s own desktop-adjacent breakpoint, style.css) on DESKTOP,
   where SPEC §1 says nothing besides colour/radius/buttons may change.
   Radius stays on the bare `button`/`input[type=submit]` selector below —
   radius is one of the properties SPEC §1 allows to change everywhere,
   including desktop, and ACCEPTANCE WP1's own floor ("no button has radius
   0") covers `.jar-qtybtn`/`.jar-burger` too (they were part of the
   baseline's 55/55 radius-0 count). Only the SIZE rule (min-height/padding,
   the thing that actually stretched them) and the `:active` transform drop
   the bare `button`/`input[type=submit]` down to a submit-only selector —
   every real CTA button in this codebase already carries `.button` or
   `.btn` (grepped: `.jar-filters-close` and the search button are the only
   other bare `<button>`s besides qtybtn/burger, and both fully self-style
   their own size already, independent of this base rule).

   Review round 2, #8: a bare `[type=submit]` attribute selector still has
   real specificity (0,1,0) — same as `.jar-perpage-go` (inc/per-page.php's
   `<button type="submit" class="jar-perpage-go">`, style.css:2399, its own
   32px/44px-on-touch sizing). Tied specificity + this file loading after
   style.css meant the base's 48px/22px silently beat that button's own,
   smaller, deliberately-scoped size. `:where()` contributes zero
   specificity, so `button:where([type=submit])`/`input:where([type=submit])`
   still match exactly the same elements (a `<button>` or `<input>` whose
   `type` is `submit`) but at `button`/`input`'s own bare specificity
   (0,0,1) — below `.jar-perpage-go`'s (0,1,0), so its own rule wins on
   specificity alone, no source order or `!important` needed either way. */
.btn,
.woocommerce .button, .woocommerce a.button, .woocommerce button.button,
.woocommerce input.button, .woocommerce #respond input#submit,
button, input[type=submit] {
  border-radius: var(--jr-r-md) !important;
}

.btn,
.woocommerce .button, .woocommerce a.button, .woocommerce button.button,
.woocommerce input.button, .woocommerce #respond input#submit,
button:where([type=submit]), input:where([type=submit]) {
  min-height: 48px;
  padding: 0 22px;
  font-family: var(--jr-font-body);
  font-weight: 600;
  font-size: 16px;
  line-height: 1.2;
}
.btn:active,
.woocommerce .button:active, .woocommerce a.button:active, .woocommerce button.button:active,
.woocommerce input.button:active, .woocommerce #respond input#submit:active,
button:where([type=submit]):active, input:where([type=submit]):active {
  transform: translateY(1px);
}

/* Primary — btn-1 (SPEC §4 WP1, §2 btn-1 hover). Same selector list style.css
   already uses for `.btn-primary`/`.button.alt`, plus the four extra
   primary actions the spec names by their own classes/ids. */
.btn-primary, .woocommerce .button.alt, .woocommerce a.button.alt,
.woocommerce button.button.alt, .woocommerce input.button.alt,
.woocommerce #respond input#submit, .single_add_to_cart_button,
.woocommerce #payment #place_order, .checkout-button, #place_order,
.add_to_cart_button {
  background: var(--jr-sky) !important;
  border-color: var(--jr-sky) !important;
  color: #fff !important;
  box-shadow: 0 10px 24px -12px rgba(0, 103, 168, .7);
}
.btn-primary:hover, .woocommerce .button.alt:hover, .woocommerce a.button.alt:hover,
.woocommerce button.button.alt:hover, .single_add_to_cart_button:hover,
.woocommerce #payment #place_order:hover, .checkout-button:hover, #place_order:hover,
.add_to_cart_button:hover {
  background: #00548A !important;
  border-color: #00548A !important;
}

/* Secondary — btn-2 (SPEC §4 WP1). `.button` alone, minus the primary
   classes already claimed above, plus `.btn-secondary`. */
.btn-secondary,
.woocommerce .button:not(.alt):not(.single_add_to_cart_button):not(.add_to_cart_button):not(.checkout-button) {
  background: #fff !important;
  color: var(--jr-navy) !important;
  border: 2px solid var(--jr-pill-line) !important;
}
.btn-secondary:hover,
.woocommerce .button:not(.alt):not(.single_add_to_cart_button):not(.add_to_cart_button):not(.checkout-button):hover {
  border-color: var(--jr-navy) !important;
  background: #fff !important;
  color: var(--jr-navy) !important;
}

/* Focus ring (SPEC §2.1) — 2.14:1 on the design's own ring fails WCAG
   1.4.11; --jr-focus is already the checked #0067A8 (jarusica-tokens.css). */
:focus-visible {
  outline: 3px solid var(--jr-focus);
  outline-offset: 3px;
}

html {
  text-size-adjust: 100%;
  -webkit-text-size-adjust: 100%;
}

/* Review fix M6c: `scroll-padding-top` is a spacing value that only makes
   sense against WP2's sticky header, which itself only applies ≤720px
   (`.site-header{position:sticky}` lives inside that query) — kept global
   before this fix, it had no visible effect on desktop (no sticky header to
   clear) but broke SPEC §1's own rule that "size and spacing tokens are
   overridden inside the mobile media query, not globally." Moved in. */
@media (max-width: 720px) {
  html { scroll-padding-top: 132px; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Size tokens — mobile only (SPEC §2 token mapping, "scope: ≤ 720 px").
   Desktop keeps jarusica-tokens.css's own values untouched. */
@media (max-width: 720px) {
  :root {
    --jr-size-body:  16px;
    --jr-size-small: 14px;
    --jr-size-micro: 13px;
  }
}

/* ── WP2 — Topbar and header ───────────────────────────────────────────
   Measured facts (ORCHESTRATE-BRIEF, 2026-09-26) override SPEC §3's
   selectors: `.jar-util` sits OUTSIDE `#masthead` (59 px today), and
   `#masthead` === `.site-header` (Storefront gives the header both the id
   and the class on the same element — style.css only ever uses the class,
   so this file matches that convention). Sticky header therefore never
   carries the topbar along; the 120 px budget below is `.site-header`
   alone, exactly what `tests/wp-metrics.mjs`'s `headerH` measures. */
@media (max-width: 720px) {
  /* Topbar (SPEC WP2 `.jar-util`): keep only the phone number. The spec
     also asks for "language links (44×44 px)" inside `.jar-util` — but
     `.jar-util`'s markup (functions.php, storefront_before_header) renders
     only the phone, hours and location; `.jar-lang` exists ONLY inside
     `#jar-mobile` (the burger overlay). There is nothing to show without
     adding PHP output, which is outside this WP's file area (CSS/JS only).
     NOT VERIFIED / not doable by injection — flagged, not invented. */
  .jar-util { background: var(--jr-navy-deep); }
  .jar-util .inner { min-height: 44px; }
  .jar-util a { color: #D6E4F0; font-size: 14px; }
  .jar-util .muted, .jar-util .sp { display: none; }

  /* Sticky header, header shadow, bottom border (SPEC WP2 `#masthead`).
     `padding` needs `!important`: style.css's base `.site-header` rule
     (unconditional, not media-scoped) sets `padding:0 !important` — same
     specificity, so only another `!important` from a later-loaded file can
     move it.

     Review fix M7: `padding:4px 0 !important` zeroed the HORIZONTAL side
     too, killing style.css's own left/right gutter ladder (style.css:445/
     522/532 — the `--space-*` values that keep the burger and "Traži" off
     the screen edge at narrow widths). This WP only ever needed to touch
     the vertical side (shrinking the tall desktop header padding to fit
     WP2's compact grid row); `padding-block` leaves the existing
     horizontal padding from style.css's own rule alone.

     Grid, not flex: a flex row with `flex-wrap:wrap` decides whether a line
     wraps using each item's UNSHRUNK (content) size, before flex-shrink is
     resolved — measured: brand (154.75px) + cart/burger (173.67px) summed
     to 328.4px against a 327px row and wrapped to two lines instead of
     shrinking the brand by 1.4px (header 161–177px). A 2-column grid with
     `minmax(0, 1fr)` on the brand track has no such content-size floor, so
     the existing brand-truncation rules (style.css:498–537) can do their
     job in the space that is actually left. */
  .site-header {
    position: sticky; top: 0; z-index: 20;
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "brand nav" "search search";
    align-items: center; column-gap: 8px; row-gap: 4px;
    background: #fff;
    box-shadow: 0 8px 18px -14px rgba(0, 40, 80, .45);
    border-bottom: 1px solid var(--jr-line);
    padding-block: 4px !important; min-height: 0;
  }
  .site-header > .col-full { grid-area: brand; width: auto; min-width: 0; }
  .site-header > .jar-navcta { grid-area: nav; margin-left: 0; }

  /* `form.jar-search` lives inside `.storefront-primary-navigation >
     .col-full`, next to `.main-navigation` (already `display:none` ≤1080px,
     style.css:695). Decision already made (ORCHESTRATE-BRIEF): the CSS
     route — keep the wrapper visible, hide only `.main-navigation` — so
     `#jar-suggest` (positioned relative to `form.jar-search`) keeps working
     without being re-parented. Placing the wrapper in its own grid row
     makes the search the "third row" the spec asks for. */
  .site-header .storefront-primary-navigation {
    grid-area: search; width: 100%; margin-left: 0;
  }
  .site-header .storefront-primary-navigation > .col-full { width: 100%; }

  .jar-search { display: flex; max-width: none; margin-left: 0; }
  form.jar-search {
    align-items: stretch;
    background: #fff; border: 1.5px solid var(--jr-pill-line);
    border-radius: var(--jr-r-pill); padding: 2px;
  }
  form.jar-search:focus-within {
    border-color: var(--jr-sky);
    box-shadow: 0 0 0 4px var(--jr-mist);
  }
  .jar-search input[type="search"] {
    min-height: 44px; font-size: 16px; padding: 0 14px;
    border: 0; background: transparent;
  }
  .jar-search button {
    min-height: 44px; padding: 0 18px; border: 0;
    background: var(--jr-navy); color: #fff;
    border-radius: var(--jr-r-pill); font: 600 15px var(--jr-font-body);
  }

  /* Cart + burger, right of the logo (SPEC WP2). `.jar-navcta .btn`
     ("Servisni upit") already hides ≤1080px (style.css:695) — it resurfaces
     in the WP9 bottom bar, nothing to add here. */
  /* "Compact" (SPEC WP2): style.css's own budget for this row is the one
     that already ships (`.jar-brand`/`.col-full` truncation logic tuned for
     320–430 px, style.css:465–537) — the 48 px btn-2 chip has to fit
     alongside it without re-opening that tuning, so padding and gap stay
     tight rather than the WP1 button defaults. */
  .jar-cart {
    min-height: 48px; padding: 0 10px; gap: 4px;
    border-radius: var(--jr-r-md); border: 2px solid var(--jr-pill-line);
    background: #fff; color: var(--jr-navy);
    font: 600 14px var(--jr-font-body);
  }
  .jar-cart .n {
    background: var(--jr-sky); color: #fff; font-weight: 600;
    border-radius: var(--jr-r-pill); padding: 1px 6px; font-size: 12px;
  }
  .jar-burger {
    width: 48px; height: 48px;
    border-radius: var(--jr-r-md); border: 1px solid var(--jr-line);
  }

  /* `#jar-mobile` (SPEC WP2 last bullet: style like the design's `.mnav
     ul` — card radius 14, 46 px items radius 10). Cosmetic only; the
     overlay's function (open/close, search, category links) is untouched. */
  #jar-mobile .inner {
    background: #fff; border-radius: var(--jr-r-md);
    margin-block: 12px; padding: var(--space-4);
  }
  #jar-mobile a.m-link { min-height: 46px; border-radius: var(--jr-r-sm); }
}

/* U5 point 4 (lead decision, 2026-09-27): style.css's own brand-subline
   rules ("KORNATI YACHT SERVICE…", `.jar-brand .txt span`) truncate it with
   an ellipsis ≤420px and only hide it outright ≤389px — WP2/SPEC only ever
   asks for the logo + "JARUŠICA", the subline is a nice-to-have that should
   disappear rather than clip mid-word. Same selector as style.css's own two
   rules (equal specificity); this file loads after style.css, so it wins
   at any width ≤430px regardless of which of style.css's own breakpoints
   would otherwise have applied there. */
@media (max-width: 430px) {
  .site-header .jar-brand .txt span { display: none; }
}

/* ── WP3 — Category bar ────────────────────────────────────────────────
   `.jar-cats` renders on the product-taxonomy pages, on search, and on the
   catalogue ONLY once a category filter is active (inc/shop-hero.php:
   `is_shop() && !$selected` shows the tiles instead). [measured 2026-09-26,
   probe against live]: `/` carries `nav.jar-cat-tiles` only, no `.jar-cats`;
   `/?filter_product_cat=...` carries `.jar-cats`, no tiles. "also show it
   above the catalogue" (SPEC WP3) needs a PHP change to shop-hero.php's
   `woocommerce_before_main_content` condition — outside this WP's file area
   (CSS/JS only). NOT VERIFIED / not doable by injection. */
@media (max-width: 720px) {
  .jar-cats .inner { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; }
  /* U4 addendum: a few px of safety margin for `firstProductY` at the
     narrowest gate width (360px) — style.css's own `padding:10px 0`
     (desktop value, unscoped) left the row 20px taller than the 48px
     select alone needs. */
  .jar-cats.has-select .inner { padding-block: 6px; }
  .jar-cats .inner::-webkit-scrollbar { display: none; }
  /* Pill height and hit area: style.css:288 already forces `min-height:46px
     !important` here (a prior "tap target < 44px" fix) — this WP only adds
     shape and colour, it does not need to fight that rule. */
  .jar-cats a { border-radius: var(--jr-r-pill); font: 500 15px var(--jr-font-body); border-color: transparent; }
  .jar-cats a:hover, .jar-cats a.on { background: var(--jr-mist); color: var(--jr-navy); }
  .jar-cats .n { color: var(--jr-muted); }
  .jar-cats a.on .n { color: var(--jr-muted); opacity: 1; }
}

/* ── U4/F33 — Category bar (Manuel, 2026-09-26): desktop wraps, mobile gets
   a native select ─────────────────────────────────────────────────────────
   Owner decision from `docs/mobile/variants-f33/F33.md` (Tome's own
   feedback that the single scrolling row of pills is "not elegant/clear"):
   variant (a) on desktop, variant (b) on mobile — different fixes per
   breakpoint, not the same one scaled.

   Desktop (≥721px): style.css's own `.jar-cats .inner{overflow-x:auto}`
   (unconditional, no media query, measured live: 7/14 categories cut off
   past the viewport edge) becomes `flex-wrap:wrap` — this is a DELIBERATE
   desktop change (SPEC §1 says desktop stays pixel-identical except colour/
   radius/buttons; this is neither, it's the owner's own explicit call for
   this one component, recorded here and in progress.md as the exception).
   `align-content` isn't needed here (unlike the mobile buy-row min-height
   case below) — this row has no forced `min-height`, its own content sets
   the height, wrapping to ~2 rows for 14 categories on a 1440px viewport
   (measured, `docs/mobile/variants-f33/f33-metrics.json`). Same
   specificity as style.css's own rule, this file loads after it, so
   source order decides — no `!important` needed (unlike the F33 PREVIEW
   file, which layered on top of an already-loaded live page and needed it
   to win against injected CSS instead of a normal cascade). */
@media (min-width: 721px) {
  .jar-cats .inner { flex-wrap: wrap; overflow-x: visible; row-gap: 8px; }
}

/* Deck F54 (29.09.): "Oprema za obalu51" — style.css:288 makes every pill
   `display:inline-flex !important` ≤767px (and so on 721–767px, where the
   pills stay visible, plus ≤720px without JS). In a flex box the space
   before `<span class="n">` is trailing white space of an anonymous flex
   item and collapses away. A column gap restores it; it does nothing
   while the pill is plain inline (desktop), where the space still renders. */
.jar-cats a { column-gap: .35em; }
/* "Sve kategorije" is a way out, not a category: no count, set apart. */
.jar-cats a.all { font-weight: 600; }

/* Mobile (≤720px): jarusica-mobile.js builds a native `<select>` from the
   EXISTING `.jar-cats a` pills (progressive enhancement — without JS the
   pill row above stays exactly as it is today, scrollable). The JS only
   hides the pills once the select is actually built and inserted (via
   `.jar-cats.has-select`), so a JS failure/block never leaves the category
   list invisible. */
@media (max-width: 720px) {
  /* style.css:288's own tap-target fix sets `display:inline-flex !important`
     on every `.jar-cats a` — a plain `display:none` here loses to that
     regardless of specificity (measured live: the pills stayed visible
     until this got its own `!important`). */
  .jar-cats.has-select .inner > a { display: none !important; }
  /* Round 6, MITTEL 2: the sr-only `<label>` jarusica-mobile.js inserts
     already announces "Kategorije" to assistive tech — the visible `.lbl`
     span (shop-hero.php's own strip label) sitting NEXT to the select would
     have doubled that announcement for sighted mouse/keyboard users too
     (two "Kategorije" texts, one visual one not) once the select replaces
     the pill row it used to label. Hidden only when the select exists. */
  .jar-cats.has-select .lbl { display: none; }
  .jar-cats-select {
    flex: 1 1 auto; min-width: 0; width: 100%;
    min-height: 48px; font-size: 16px; font-family: var(--jr-font-body);
    /* Round 6, MITTEL 2: the option-text-length heuristic in
       jarusica-mobile.js is gone (real ellipsis below instead) — a native
       select needs room on the right for its own dropdown arrow, or long
       text runs under/behind it ("(51)" landing under the arrow, the
       reviewer's own example). 40px clears the arrow with margin to spare
       (U5 point 3 asked for ≥40px, satisfies round 6's ≥36px too). */
    padding: 0 40px 0 14px; border: 1px solid var(--jr-line);
    border-radius: var(--jr-r-md); background: #fff; color: var(--jr-ink);
    overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
  }
}

/* ── WP4 — Catalogue hero ──────────────────────────────────────────────
   Measured facts: `.woocommerce-products-header` is empty (0 px); the real
   hero is `header.jar-hero > .inner > h1` (182 px today). This WP targets
   `.jar-hero`, not the spec's literal (wrong) selector. Desktop keeps the
   full-bleed band (SPEC §1: unchanged except colour/radius/buttons) — this
   turns it into the "navy panel inside the gutter" only ≤720 px. */
@media (max-width: 720px) {
  /* U5 point 5 (lead decision, 2026-09-27): first-screen budget on the
     category pages — spacing only, content untouched (the "isporuka iz
     Betine · GLS · Gebrüder Weiss" meta line is existing live copy, out of
     this fix's scope, flagged to Manuel as §6.1 per the lead). 16px margin
     → 12px, 26px padding → 20px; text/colour/layout otherwise identical. */
  .jar-hero {
    margin: 12px var(--jr-gutter);
    border-radius: var(--jr-r-xl);
    border-bottom: 0;
  }
  .jar-hero .inner { width: auto; padding: 20px; }
  .jar-hero .eyebrow { color: #D6E4F0; }
  .jar-hero h1 { font-size: 28px; color: #fff; }
  .jar-hero .meta { font-size: 16px; color: #D6E4F0; }
}

/* ── WP5 — Category tiles ──────────────────────────────────────────────
   Root cause of the overflow (measured: right edge 405 px at 390 px
   viewport, 7 of 14 tiles past the edge): `grid-template-columns:repeat(2,
   1fr)` lets a track's MIN-CONTENT win over its share of the row when a
   child (image, name) cannot shrink that far — `minmax(0,1fr)` removes that
   floor. Targets `nav.jar-cat-tiles > div.inner > a.tile` (measured
   selector), not the spec's `.jar-cat …` (that is the catalogue wrapper,
   `aside.jar-filters` + `div.jar-results`, see ORCHESTRATE-BRIEF).

   U1/D1 (2026-09-26, docs/mobile/RESEARCH-UX.md): the square-card version
   above pushed `firstProductY` to 2,455px at 390 (5,941 at 360, its own
   1-column fallback ≤360px making it worse, not better) — the final gate's
   own reported ✗. Baymard's evidence for keeping intermediary category
   tiles on the unfiltered landing page (RESEARCH-UX D1) doesn't require a
   full square card — a compact row (56px image box beside the name/count)
   gets the same information in roughly a third of the vertical space.
   2 columns now stay at EVERY width down to 360px (the old `@media
   (max-width:360px)` single-column fallback is removed) — a single column
   at 360px would have made the height problem worse specifically at the
   narrowest gate width, the opposite of this fix's own goal. */
@media (max-width: 720px) {
  .jar-cat-tiles .inner {
    grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px;
    /* style.css:1739's desktop `padding-block:var(--space-6)` (20.4px each
       side) is card-grid overhead this compact row doesn't need — cut to
       the gate's own budget (see comment below). */
    padding-block: 6px;
  }
  .jar-cat-tiles .tile {
    display: flex; flex-direction: row; align-items: center; gap: 10px;
    background: var(--jr-panel); border: 0; border-radius: var(--jr-r-lg);
    padding: 6px; min-height: 44px;
  }
  .jar-cat-tiles .ph {
    width: 56px; height: 56px; flex: none; aspect-ratio: 1;
    padding: 0; border-bottom: 0; background: var(--jr-mist);
    border-radius: var(--jr-r-md);
    display: flex; align-items: center; justify-content: center;
  }
  .jar-cat-tiles .ph img { max-width: 100%; max-height: 100%; width: auto; height: auto; object-fit: contain; }
  .jar-cat-tiles .txt { flex: 1 1 auto; min-width: 0; flex-direction: column; align-items: flex-start; padding: 0; gap: 2px; }
  /* Clamped to one line (`ellipsis`, same technique as `.jar-specline`
     elsewhere in this file) — with 14 categories at 2 columns, wrapping
     names to 2-3 lines each was the difference between landing under and
     over the 1,200px `firstProductY` target (measured: 2 lines pushed the
     tile row to ~140-160px each × 7 rows). A long name still reads (it's
     the exact same text, just truncated visually), and every tile stays
     well above the 44px floor on its image box alone. */
  .jar-cat-tiles .name {
    font: 600 14px/1.3 var(--jr-font-display); color: var(--jr-navy); min-height: 0;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
  }
  .jar-cat-tiles .n { font-size: 12px; color: var(--jr-muted); white-space: normal; }

  /* U3 (2026-09-26, Folie 29): a category with no term thumbnail (shop-
     hero.php's own fallback-photo removal) renders without a `.ph` span at
     all — no image box, no letter placeholder. `.txt` alone needs the
     row's own `min-height:44px` (base `.tile` rule above) to still read as
     a proper tile, not a collapsed sliver next to its image-carrying
     siblings. */
  .jar-cat-tiles .tile.no-img { min-height: 68px; }
}

/* Desktop: `.tile` is a flex COLUMN there (style.css, out of this file's
   area) with `.txt{margin-top:auto}` pinning the caption under a 190px
   `.ph`. Without `.ph` at all, that same rule would leave the text sitting
   at the very bottom of the grid row's own stretched height (CSS grid's
   default `align-items:stretch` matches every tile in a row to the
   tallest), a blank gap above it — global (not media-gated, this file
   loads on every page) since the underlying markup change is unconditional,
   not a mobile-only one; centring the text is a layout necessity from that
   change, not a colour/radius tweak. */
.jar-cat-tiles .tile.no-img { justify-content: center; }

/* ── Gate fix — pagination overflow (pre-existing since WP2/WP5) ─────────
   `over` stayed at 7/7/5 (360·390·412) through WP2–WP5, root-caused
   (progress.md) to `ul.page-numbers`: `display:inline-flex` with no wrap
   and up to 7 pages × (38px min-width + 6px gap) doesn't fit a 360–412px
   viewport. `flex-wrap:wrap` fits it inside its own box (no scroll
   container needed, no inline `style="overflow"` to fake past the gate's
   own exclusion list) — recorded in the WP7 row per the lead's decision. */
@media (max-width: 720px) {
  .woocommerce nav.woocommerce-pagination ul.page-numbers {
    flex-wrap: wrap; justify-content: center;
  }
}

/* ── WP6 — Filters as a bottom sheet ───────────────────────────────────
   Markup for the toggle, sheet header, close button, backdrop and the
   sticky "Prikaži rezultate" button is built by jarusica-mobile.js (no
   PHP) — decided already (ORCHESTRATE-BRIEF). `aside.jar-filters` keeps
   its existing markup (`.jar-filters-title`, `.fgroup`, `a.fopt` links);
   this only repositions it. At ≤980px style.css already sets
   `.jar-filters{position:static}` and `.jar-cat{grid-template-columns:1fr}`
   — `position:fixed` below removes the aside from that flow entirely, so
   `.jar-results` no longer sits after 4,011px of filter content (WP6 ✓). */
/* Review fix M8: jarusica-mobile.js now builds the toggle/close/backdrop/
   apply DOM unconditionally (not only when the page loads <=720px), so a
   resize past 720px always finds a consistent state via `matchMedia`
   instead of leaving whatever was already built. These four are mobile-
   only controls with no desktop equivalent -- hidden here instead of by
   the JS width-check that used to gate whether they existed at all. */
@media (min-width: 721px) {
  .jar-filter-toggle,
  .jar-filters-close,
  .jar-filters-backdrop,
  .jar-filters-apply {
    display: none !important;
  }
}
@media (max-width: 720px) {
  .jar-filters-backdrop {
    position: fixed; inset: 0; z-index: 29;
    background: rgba(18, 49, 77, .45);
    opacity: 0; visibility: hidden;
    transition: opacity .2s ease, visibility 0s linear .2s;
  }
  .jar-filters-backdrop.is-open {
    opacity: 1; visibility: visible;
    transition: opacity .2s ease, visibility 0s;
  }

  aside.jar-filters {
    position: fixed; inset: auto 0 0; top: auto;
    max-height: 85vh; overflow: auto;
    width: auto; margin: 0;
    background: #fff;
    border-radius: var(--jr-r-xl) var(--jr-r-xl) 0 0;
    box-shadow: 0 -18px 40px -22px rgba(0, 40, 80, .35); /* upward shadow */
    padding: 0 20px calc(20px + env(safe-area-inset-bottom));
    z-index: 30;
    transform: translateY(100%);
    visibility: hidden;
    transition: transform .25s ease, visibility 0s linear .25s;
  }
  aside.jar-filters.is-open {
    transform: translateY(0);
    visibility: visible;
    transition: transform .25s ease, visibility 0s;
  }

  /* Sheet header: the existing `h2.jar-filters-title` gets wrapped into
     this row by jarusica-mobile.js, next to the close button — no
     duplicate "Filtriraj" text. */
  .jar-filters-head {
    position: sticky; top: 0; z-index: 1;
    display: flex; align-items: center; justify-content: space-between;
    gap: 12px; padding: 16px 0; background: #fff;
    border-bottom: 1px solid var(--jr-line);
  }
  .jar-filters-head .jar-filters-title { margin: 0; }
  .jar-filters-close {
    flex: none; width: 48px; height: 48px;
    border-radius: var(--jr-r-md); border: 1px solid var(--jr-line);
    background: #fff; font-size: 22px; line-height: 1; color: var(--jr-navy);
  }

  /* `.box` border (SPEC WP6 + §2.1: #6C99C3's 3.01:1 is borderline,
     #5E8AB4 reaches 3.64:1 — the checked value, not the token). */
  .jar-filters .fopt { min-height: 44px; }
  .jar-filters .fopt .box { border-color: #5E8AB4; }
  .jar-filters .ct { margin-left: auto; }
  /* "Poništi sve filtre ×" (catalog-filters.php, `a.mono`, only rendered
     when a filter is active) measured 93×14px — below the 24px `tap24`
     floor. Fixed here since it only ever renders inside `aside.jar-filters`
     (WP6's own component), never elsewhere. */
  .jar-filters > a.mono {
    display: inline-flex; align-items: center; min-height: 24px;
  }

  .jar-filters-apply {
    position: sticky; bottom: 0; left: 0; right: 0;
    display: flex; width: 100%; margin: 16px 0 0;
    z-index: 1;
  }

  /* U5 point 2 (lead decision, 2026-09-27): the old `flex-wrap` row let the
     4 existing controls (Filtriraj, sort select, result count, "Samo na
     zalihi" toggle — functions.php:669/jarusica-mobile.js, nothing added or
     removed here) reflow unpredictably across 1-3 lines depending on text
     length, pushing the first product row further down than the toggle +
     sort control alone need. Same DOM, a fixed 2×2 grid instead: row 1 =
     Filtriraj + sort (the two primary actions, side by side, each ≥44px
     tall via their own existing rules below); row 2 = the stock chip +
     result count, compact and secondary. */
  .jar-catbar {
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-template-areas: "toggle sort" "chip count";
    align-items: center;
    column-gap: 8px; row-gap: 6px;
  }
  .jar-catbar .jar-filter-toggle {
    grid-area: toggle; width: 100%; justify-content: center;
  }
  .jar-catbar .woocommerce-ordering { grid-area: sort; margin: 0; }
  .jar-catbar .woocommerce-ordering select { width: 100%; }
  .jar-catbar .fopt { grid-area: chip; justify-self: start; }
  /* The "n od N proizvoda" result count — secondary information, smaller
     and muted, sharing row 2 with the stock chip instead of its own line. */
  .jar-catbar .count {
    grid-area: count; justify-self: end; text-align: right;
    font-size: 14px; color: var(--jr-muted);
  }
  .jar-filter-toggle {
    display: inline-flex; align-items: center; gap: 8px;
    min-height: 48px; padding: 0 18px;
  }
  .jar-filter-count {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 20px; height: 20px; padding: 0 5px;
    background: var(--jr-sky); color: #fff; font-size: 12px; font-weight: 600;
    border-radius: var(--jr-r-pill);
  }
  /* Review fix M9c: `select` is in style.css:357's `border-radius:0
     !important` list — a bare `!important` beats specificity regardless of
     how targeted a selector is, so the un-`!important` radius below lost
     unconditionally. `!important` added; both selectors already tie or
     exceed style.css's bare `select` on specificity, and this file loads
     after it, so it also wins any tie. */
  .jar-catbar .woocommerce-ordering select,
  .woocommerce .woocommerce-ordering select {
    min-height: 48px; border-radius: var(--jr-r-md) !important; font-size: 16px;
    /* P1 (lead decision, 2026-09-27): same reasoning as `.jar-cats-select`'s
       own R6 MITTEL 2 fix — "Zadano sortiranje" (the default option) ran
       under the native dropdown arrow with no room reserved for it.
       Real ellipsis instead of trusting the text to always fit. */
    padding: 0 40px 0 14px;
    overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
  }

  /* `#jar-perpage-top`/`-bottom` (inc/per-page.php, `.jar-perpage select`)
     inherits `--jr-size-small` (WP1: 14px on mobile by design for regular
     text) — but this is a real `<select>`, not a text label, and 14px
     triggers iOS Safari's auto-zoom on focus (`smallFont`, the same reason
     WP2's search input and this WP's ordering select are 16px). A
     deliberately narrow exception to the text-size token, not a change to
     it. Per-page COUNT logic stays untouched (`needs_review` 6.3). */
  .jar-perpage select { font-size: 16px; }

  /* U5 point 2: the TOP "Po stranici" chooser (inc/per-page.php's
     `.jar-perpage--top`) duplicated the bottom one (after the loop, kept)
     for one more row of toolbar height most shoppers never touch before
     scrolling past the first product row anyway — per-page COUNT logic is
     untouched (§6.3), this only hides the redundant top instance ≤720px. */
  .jar-perpage--top { display: none; }

  body.jar-scroll-lock { overflow: hidden; }

  /* Review fix H3a: `aside.jar-filters` (z30) and `.jar-filters-backdrop`
     (z29) both sat under `.jar-mbar` (z40, WP9) — the bar covered "Prikaži
     rezultate" whenever the sheet was open. Rather than raise the sheet's
     z-index (which would also put it above the consent banner, 9999, the
     one thing that must always win), hide the bar for the sheet's own
     lifetime the same way WP10 already hides it for the consent banner's:
     `body.jar-scroll-lock` is added/removed exactly when the sheet
     opens/closes (jarusica-mobile.js WP6), so this needs no new state. */
  body.jar-scroll-lock .jar-mbar { display: none; }
}

/* ── WP7 — Product list as compact rows ────────────────────────────────
   Measured facts (probe 2026-09-26): the loop anchor is
   `a.woocommerce-LoopProduct-link.woocommerce-loop-product__link`, wraps
   `.jar-thumb`, `.jar-sku`, the title, `.price`, `.jar-brand`,
   `.jar-specline`, `.jar-stock` — same anchor `display:contents` pattern
   the desktop table view already uses (style.css:848, ≥940px). The
   add-to-cart button sits OUTSIDE the anchor as a sibling: purchasable
   in-stock items get `.button.add_to_cart_button`, everything else gets a
   plain `.button` reading "Pročitaj više" (hidden here per spec — the row
   itself, via the anchor's children, is already the link).

   Review fix H4: `.woocommerce ul.products li.product` (the row's own grid
   rule) was the one selector in this block not scoped to `.jar-results` —
   `ul.products` is WooCommerce's generic loop markup, reused unchanged for
   related products (PDP), "Pribor" (inc/accessories.php:88/106) and cart
   cross-sells. All three rendered as compact catalogue rows instead of the
   default card grid. Scoped to `.jar-results ul.products …`, matching
   every other rule in this section. */
@media (max-width: 720px) {
  .jar-results ul.products {
    display: flex; flex-direction: column; gap: 12px;
  }
  .jar-results ul.products li.product {
    display: grid;
    grid-template-columns: 80px 1fr;
    grid-template-areas:
      "img sku"
      "img name"
      "img spec"
      "img price"
      "img stock"
      "img added";
    gap: 2px 16px;
    padding: 12px 16px;
    background: #fff;
    border: 1px solid var(--jr-line);
    /* Review fix M9c: `.woocommerce ul.products li.product` is itself in
       style.css:357's `border-radius:0 !important` list — a bare
       `!important` beats specificity outright, so the un-`!important`
       radius below lost even though this selector ties style.css's own on
       specificity and loads after it. */
    border-radius: var(--jr-r-lg) !important;
    /* P1 (lead decision, 2026-09-27): the right column (SKU, title,
       brand/specline, price, stock pill) read centred — WooCommerce/
       Storefront's own product-loop CSS centres several of these elements
       individually, not just the row as a whole, so a plain `text-align:
       left` on the row alone would not have beaten same-or-higher-
       specificity rules on the children themselves. Set explicitly below,
       per element, not just inherited from the row. */
    text-align: left;
  }
  .jar-results ul.products li.product > a.woocommerce-LoopProduct-link,
  .jar-results ul.products li.product > a.woocommerce-loop-product__link {
    display: contents;
  }
  /* #152: same fix as the desktop list view (style.css, ≥940px) — the link
     gets a real box spanning the whole card grid, rows and columns both
     `subgrid`, so the named areas above still place every cell exactly
     where it was. "Dodaj" (a later sibling in the same cell as the price)
     paints above the link box and stays its own click target. */
  @supports (grid-template-columns: subgrid) {
    .jar-results ul.products li.product > a.woocommerce-LoopProduct-link,
    .jar-results ul.products li.product > a.woocommerce-loop-product__link {
      display: grid;
      grid-column: 1 / -1; grid-row: 1 / -1;
      grid-template-columns: subgrid;
      grid-template-rows: subgrid;
    }
  }

  .jar-results ul.products li.product .jar-thumb {
    grid-area: img; width: 80px; height: 80px;
    border-radius: var(--jr-r-md); overflow: hidden; margin: 0;
  }
  .jar-results ul.products li.product .jar-thumb img {
    width: 100%; height: 100%; object-fit: contain;
  }

  /* P1: `justify-self:start` (below, `.price`/`.jar-stock`) only places the
     grid ITEM's own box inside its cell — it says nothing about the TEXT
     inside that box, which WooCommerce/Storefront's own loop CSS centres
     on several of these elements individually (the lead's own live
     finding). `text-align:left` added explicitly per element, not just
     inherited from the row's own rule above, so a same-or-higher-
     specificity core rule on the CHILD can't silently win the way the
     row-level attempt alone would have. */
  .jar-results ul.products li.product .jar-sku {
    grid-area: sku; font-size: 14px; color: var(--jr-muted); margin: 0;
    text-align: left;
  }
  .jar-results ul.products li.product .woocommerce-loop-product__title {
    grid-area: name;
    font: 600 16px/1.35 var(--jr-font-body); color: var(--jr-ink);
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden; text-align: left;
  }
  .jar-results ul.products li.product .price {
    grid-area: price; justify-self: start; align-self: center; margin: 0;
    font-family: var(--jr-font-display); font-size: 17px; font-weight: 700;
    color: var(--jr-navy); text-align: left;
  }
  .jar-results ul.products li.product .price .jar-vat {
    font-size: 14px; color: var(--jr-muted);
  }
  /* U1/E4 (2026-09-26, docs/mobile/RESEARCH-UX.md): a bolder in-stock flag
     — same pill pattern the PDP buybox already uses (`.jar-buybox .jar-stock`
     below, #EAF6EF/--jr-stock in, --jr-warn-bg/--jr-warn out), scaled down
     for the list row. Existing text only, `justify-self:start` keeps it
     left-aligned like the row's other fields (the price/button pair on the
     row above is the one exception, already justified apart on purpose). */
  .jar-results ul.products li.product .jar-stock {
    grid-area: stock; justify-self: start; align-self: center;
    display: inline-flex; align-items: center;
    font: 600 14px var(--jr-font-body); margin: 0; text-align: left;
    border-radius: var(--jr-r-md); padding: 4px 10px;
  }
  .jar-results ul.products li.product .jar-stock::before {
    width: 8px; height: 8px; border-radius: 50%;
  }
  .jar-results ul.products li.product .jar-stock.in { background: #EAF6EF; color: var(--jr-stock); }
  .jar-results ul.products li.product .jar-stock.out { background: var(--jr-warn-bg); color: var(--jr-warn); }

  /* Brand stays out on mobile (spec); style.css:806 already hides
     `.jar-brand` unconditionally — repeated here for the mobile section's
     own record, not a fight against it. The brand-as-SKU-prefix (only
     when the brand text isn't already in the title) is jarusica-mobile.js
     — it is DOM content, not styling, so CSS alone can't decide it. */
  .jar-results ul.products li.product .jar-brand { display: none; }

  /* U1/D4 (2026-09-26, docs/mobile/RESEARCH-UX.md): WP7 originally hid
     `.jar-specline` too, alongside `.jar-brand` — but functions.php:651's
     own comment calls this line "non-negotiable #5" (brand + Napon/
     Kapacitet attributes, the spec-driven info Baymard's list-item-design
     evidence calls for on spec-heavy products), and RESEARCH-UX D4 says to
     show it, clamped to one line, rather than drop it. Un-hidden here,
     given its own grid row (`spec`, above) so it doesn't collide with the
     title or price, clamped with `text-overflow:ellipsis` (a single line,
     `-webkit-line-clamp` is 2+ lines only) since it's plain unwrapped text,
     not a flex/block multi-line title. */
  .jar-results ul.products li.product .jar-specline {
    grid-area: spec; margin: 0; font-size: 14px; color: var(--jr-muted);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    text-align: left;
  }

  /* "Pročitaj više" (no add_to_cart_button class, see probe above) hidden;
     the row's anchor content is the link. */
  .jar-results ul.products li.product > a.button:not(.add_to_cart_button) {
    display: none;
  }
  /* Beside the price (spec's `.pcard-foot` alternative to a 5th "btn" row) —
     both share `grid-area:price`; grid allows more than one item per named
     area and `justify-self` places them at opposite ends of the same cell.
     Cuts the row's own dedicated button row + its margin-top entirely
     (measured: 186px → within the 150px budget, see progress.md).

     Review fix M9a/M9b: 36px was below SPEC §5's own "primary actions ≥44px"
     floor ("Dodaj" is a buy action, not decoration) — raised to 44px; see
     progress.md's review-fixes section for the re-measured row height this
     produced. M9b: style.css:791's own `.woocommerce ul.products
     li.product .add_to_cart_button{background:transparent!important;…}`
     (0,4,2 specificity, `!important`) kept this button transparent instead
     of btn-1 — a bare `!important` wins over a plain declaration
     regardless of specificity, and WP1's global `.add_to_cart_button` rule
     (0,1,0) lost outright. Re-declared here at matching (0,4,2)
     specificity with `!important` of its own; this file loads after
     style.css, so the tie resolves by source order. */
  .jar-results ul.products li.product .add_to_cart_button {
    grid-area: price; justify-self: end; align-self: center;
    min-height: 44px; padding: 0 16px; margin: 0; font-size: 14px;
    background: var(--jr-sky) !important;
    border-color: var(--jr-sky) !important;
    color: #fff !important;
  }
  .jar-results ul.products li.product .add_to_cart_button:hover {
    background: #00548A !important;
    border-color: #00548A !important;
  }

  /* U1/D4 (2026-09-26, docs/mobile/RESEARCH-UX.md): style WooCommerce's own
     post-add-to-cart states, CSS only — no new text, `.added`/`a.added_
     to_cart` are core WC classes (`assets/js/frontend/add-to-cart.js`
     toggles `.added` on the button and inserts the `wc-forward` link as its
     sibling, both already handled by `assets/shop.js`'s Store-API bridge
     elsewhere in this theme). The button itself confirms success in place
     (green, same `--jr-stock` as the in-stock pill above); "Pogledaj
     košaricu" gets its own grid row so it never overlaps the button (the
     row only grows when the link is actually in the DOM — an absent grid
     area's track collapses to 0). */
  .jar-results ul.products li.product .add_to_cart_button.added {
    background: var(--jr-stock) !important;
    border-color: var(--jr-stock) !important;
  }
  .jar-results ul.products li.product a.added_to_cart {
    grid-area: added; justify-self: end; margin: 4px 0 0;
    font-size: 13px; font-weight: 600; color: var(--jr-sky);
  }
}

/* ── WP8 — Product page ────────────────────────────────────────────────
   Measured facts (probe 2026-09-26, ProMariner 1230P, 375×812, no
   injection): single column already (style.css:927/940 stack the gallery
   and `.summary` ≤980px) — the gap to close is vertical HEIGHT, not
   columns. Baseline: price top 977 · add-to-cart top 1,303, both below
   the 812px fold. Biggest single item: `.product_title` at 40px
   (style.css:968) wrapping to ~173px for a long SKU-style name; spec's
   24px H1 is this WP's main lever, the gallery panel is the second.
   PDP add-to-cart/qty stay ≥44px (#80, ACCEPTANCE — this WP only ever
   raises the 48px qty height, never the 44px floor). */
@media (max-width: 720px) {
  .woocommerce div.product .woocommerce-product-gallery {
    background: var(--jr-panel); border: 0;
    border-radius: var(--jr-r-lg); padding: 24px;
  }
  .woocommerce div.product .woocommerce-product-gallery__image {
    background: transparent;
  }
  .woocommerce div.product .woocommerce-product-gallery__image img {
    aspect-ratio: 1; object-fit: contain; padding: 0;
    max-height: 42vh; width: auto; margin: 0 auto;
  }
  .woocommerce div.product .flex-control-thumbs {
    grid-template-columns: repeat(auto-fill, 64px); gap: 8px; margin-top: 12px;
  }
  .woocommerce div.product .flex-control-thumbs li { width: 64px; }
  .woocommerce div.product .flex-control-thumbs img {
    aspect-ratio: 1; border-radius: var(--jr-r-md); border: 1px solid var(--jr-line);
  }

  /* Order (spec): 1 brand · 2 H1 · 3 short description · 4 price · 5 stock
     box · 6 buy row — unchanged from the existing DOM order (kicker → h1 →
     ids/short-description → buybox); this WP restyles, it doesn't reorder. */
  .jar-kicker {
    font: 600 14px var(--jr-font-body); color: var(--jr-sky);
    text-transform: none; letter-spacing: 0; margin-bottom: 6px;
  }
  .woocommerce div.product .product_title {
    font-family: var(--jr-font-display); font-size: 24px; line-height: 1.25;
    color: var(--jr-navy); margin-bottom: 8px;
  }
  .woocommerce div.product .jar-ids { margin-bottom: 8px; }

  .jar-buybox {
    padding: 16px; margin-top: 12px; gap: 12px;
    border-color: var(--jr-line); border-radius: var(--jr-r-lg);
  }
  .woocommerce div.product p.price, .woocommerce div.product span.price {
    font-family: var(--jr-font-display); font-size: 32px; line-height: 1.15;
    font-weight: 700; color: var(--jr-navy);
  }
  /* "s PDV-om, X € bez PDV-a" (spec) — WooCommerce computes the net price
     itself in `.jar-vatline`'s existing markup; this WP only restyles the
     text WooCommerce already renders, it never computes ÷1.25 in CSS/JS. */
  .jar-vatline { font-size: 14px; color: var(--jr-muted); }

  /* Scoped to the PDP buybox — unscoped `.jar-stock.in`/`.out` also matches
     the WP7 loop rows (`.jar-results ul.products li.product .jar-stock`),
     which inflated `rowMaxH` 150px→174px measured on `/` (regression,
     caught by the WP8 gate run, fixed before commit — see progress.md). */
  .jar-buybox .jar-stock.in {
    display: inline-flex; background: #EAF6EF; color: var(--jr-stock);
    border-radius: var(--jr-r-md); padding: 12px 16px; font-weight: 700;
  }
  .jar-buybox .jar-stock.out {
    display: inline-flex; background: var(--jr-warn-bg); color: var(--jr-warn);
    border-radius: var(--jr-r-md); padding: 12px 16px; font-weight: 700;
  }

  /* Review fix M9c: `input` (and `.quantity input`) is in style.css:357's
     `border-radius:0 !important` list — beats a non-`!important` radius
     regardless of specificity. `!important` added below. */
  .woocommerce .quantity input.qty {
    width: 64px; height: 48px; font: 600 16px var(--jr-font-body);
    border: 1px solid var(--jr-pill-line); border-radius: var(--jr-r-md) !important;
  }
  .woocommerce div.product form.cart .button { flex: 1 1 auto; min-width: 0; }

  /* Fixed buy row (lead decision, round 3): `position:sticky` can never
     leave its own containing block (`.jar-buybox`), and `.jar-buybox`
     itself only scrolls into range well past the fold — no
     ancestor-overflow fix changes that; sticky's stuck range only opens up
     ~150–186px into the scroll, never at load (rounds 1–2's finding,
     confirmed by a scroll-range scan, not guessed). `position:fixed` has
     no such containing-block limit — none of the ancestors between
     `form.cart` and `html` set `transform`/`filter`/`perspective`/
     `contain`/`will-change` (checked in the round-2 ancestor probe,
     progress.md), so `fixed` resolves against the true viewport from the
     first paint, unconditionally.
     Two selectors, per the lead's split: `form.cart:not(.variations_form)`
     for simple products — the variations FORM itself never becomes the
     buy row, only its per-variation add-to-cart panel does, so an
     unselected `form.cart.variations_form` stays static, out of this rule.
     `.storefront-sticky-add-to-cart` is redundant with this row now and
     hidden ≤720px — one sticky buy control, not two.
     Exception: pages with `.jar-sticky-upit` (#113, po narudžbi, no buy
     button — sticky-bar.js only builds it when there's nothing to add to
     cart) keep the row static; `:has()` is live, so this self-corrects if
     `.jar-sticky-upit` is added to the DOM after the stylesheet loads.
     `.jar-sticky-upit` itself is untouched, exactly as instructed.

     Review fix H3b: the 76px offset was gated on `body:has(.jar-mbar)` —
     `.jar-mbar` (WP9) exists on every page `is_checkout()` doesn't match,
     which is statically true for every single-product page (a product page
     is never the checkout), so the `:has()` check never actually varied
     here; it only added a dependency on `:has()` support with no fallback.
     76px is now the plain ≤720px default, and the ONE page where the bar
     is genuinely absent (checkout) is excluded by WooCommerce core's own
     `body.woocommerce-checkout` class instead — no feature detection
     needed because which pages carry the bar is already known statically. */
  form.cart:not(.variations_form),
  .woocommerce-variation-add-to-cart {
    position: fixed; left: 8px; right: 8px;
    bottom: calc(76px + env(safe-area-inset-bottom));
    z-index: 5; gap: 10px; align-items: stretch;
    background: #fff; border: 1px solid var(--jr-line);
    border-radius: var(--jr-r-lg); padding: 8px;
    box-shadow: 0 12px 30px -12px rgba(0, 40, 80, .45); /* m-bar shadow, SPEC §2 */
    /* Review round 5, MITTEL: `align-content` defaults to `normal` (≈
       `stretch` for a wrapping flex container) — with `min-height` reserving
       more room than a genuinely single-line row needs (measured: a
       450-720px-wide landscape phone fits [Preis][−qty+][Button] on one
       line, no `.jar-buyrow-wrap`), the browser stretched that ONE line to
       fill the leftover space, which then stretched the qty/button flex
       ITEMS themselves (align-items:stretch) to ~96px tall — a real,
       measured touch-target-size regression the round 4 fix introduced
       without noticing, because every width tested there (360-412px)
       happened to wrap and never exercised the single-line case. Pinning
       the line to the top leaves the extra reserved space empty below it
       instead of stretching into it. */
    align-content: flex-start;
  }
  /* Review round 5, MITTEL: `min-height` reservation narrowed to widths
     where wrapping is the REAL common case (360-412px, confirmed live) —
     at 450-720px (landscape phones/small tablets) the row fits one line
     with room to spare, and reserving the wrapped row's own height there
     was the direct cause of the stretch bug above (extra empty space is
     still extra space to stretch INTO without `align-content`, even with
     it fixed above, reserving room that's never used is its own kind of
     wrong). 440px sits between the widest CONFIRMED-wrapping width (412px)
     and the narrowest one measured to fit one line. */
  @media (max-width: 440px) {
    form.cart:not(.variations_form),
    .woocommerce-variation-add-to-cart {
      min-height: 114px;
    }
  }
  body.woocommerce-checkout form.cart:not(.variations_form),
  body.woocommerce-checkout .woocommerce-variation-add-to-cart {
    bottom: calc(8px + env(safe-area-inset-bottom));
  }
  /* Review round 2, #7: metre goods (`.jar-metre-total`, H3d) added ONLY to
     the fixed row's own top offset (76→290px) — leaving the row itself
     fixed still means a ~192px-tall bar-shaped box parked over roughly a
     third of a 375×812 viewport for as long as the product page is open,
     the exact "no clicks on buy/submit, but also don't cover a third of
     the screen" the sticky-upit exception below was already built to
     avoid. Folded into the SAME exception instead: metre-goods forms stay
     in normal flow (`position:static`), just like po-narudžbi pages. */
  body:has(.jar-sticky-upit) form.cart:not(.variations_form),
  body:has(.jar-sticky-upit) .woocommerce-variation-add-to-cart,
  form.cart:not(.variations_form):has(.jar-metre-total) {
    position: static; left: auto; right: auto; bottom: auto; z-index: auto;
    background: none; border: 0; border-radius: 0; padding: 0; box-shadow: none;
  }
  .single-product .storefront-sticky-add-to-cart { display: none !important; }

  /* Room for the fixed row so it doesn't cover the tabs/footer (lead
     decision #3). Row's own visual height ≈ 64px (8px padding + 48px
     qty/button) + 8px offset from the edge.
     `.single-product:has(.jar-sticky-upit) .site-footer` already reserves
     its own space for the upit-bar case (style.css ~2143) — harmless to
     also carry this rule there, it only adds a little unused whitespace.

     Review fix H3b: same reasoning as above — `.jar-mbar` exists on every
     single-product page unconditionally (a product page is never
     checkout), so `:has(.jar-mbar)` never actually varied; 160px (bar's
     own ~140px top edge + 20px margin, per the WP9 comment this rule
     absorbed) is now the plain default. `.jar-sticky-upit` products never
     render `form.cart` at all (WP8 round 3, progress.md), so that case
     only ever needs the bar's own room (90px), kept as its own rule.

     Review round 5 fix (found while validating the landscape case below):
     `body.single-product` is (0,1,1) — the EXACT same specificity as WP9's
     own `body:not(.woocommerce-checkout){padding-bottom:84px}` further
     down this file. They tied, and WP9's rule, being later in the file,
     silently won on every page where `.jar-buyrow-wrap` wasn't active (the
     higher-specificity `:has()` variant below masked this at every width
     already confirmed to wrap) — measured live at 667×375: computed
     `padding-bottom` was 84px, not 160px, with the fixed buy row sitting
     well inside that gap. `:not(.woocommerce-checkout)` added here (a
     single-product page is never checkout, so this changes nothing about
     which pages match) raises this rule to (0,2,1), unconditionally above
     WP9's tie, regardless of file order. */
  body.single-product:not(.woocommerce-checkout) { padding-bottom: calc(160px + env(safe-area-inset-bottom)); }
  body.single-product:has(.jar-sticky-upit) { padding-bottom: calc(90px + env(safe-area-inset-bottom)); }

  /* Review fix H3d, revised in round 2 (#7): goods sold by the metre
     (inc/unit-price.php: a `label.metre-label` "Duljina (m)" and a
     `.jar-metre-total` line inside `form.cart`, measured live on
     `ancor-tinned-copper-wire-14awg-2mm2-grey` at 192px total, not the
     ~64px every other product's buy row is) no longer get a fixed row at
     all — see the `position:static` exception two blocks up. With the row
     back in normal document flow, the page only needs the bottom bar's own
     90px, exactly like the sticky-upit exception right above (same
     reasoning: no fixed buy row parked over the screen means no extra
     clearance to reserve for one).
     Review round 4, MITTEL 6 correction: that reasoning stopped being
     complete once D5 (docs/mobile/RESEARCH-UX.md) added `.jar-metre-bar` —
     a SECOND fixed element (up to 64px tall, sitting at the same 76px
     offset the regular buy row uses) that shows up on these exact pages
     once `form.cart` scrolls out of view, i.e. precisely when a shopper
     has scrolled to the end of the page and would expect to reach the
     footer. 90px only cleared `.jar-mbar` — not this bar stacked above it
     — so the metre-bar covered the footer's last link at the page's own
     end. Same 160px the regular buy-row case above reserves (mbar's own
     ~84px + this bar's own ~64px + margin), not a new number. */
  body.single-product:has(.jar-metre-total) { padding-bottom: calc(160px + env(safe-area-inset-bottom)); }

  /* Review fix H3c, revised in round 2 (#10): `.jar-sticky-upit`
     (style.css:2166/2195, po narudžbi sticky bar, z9990,
     `bottom:var(--jar-consent-h,0px)` ≤780px) sat under `.jar-mbar` (z40,
     WP9) on the same po-narudžbi PDPs — style.css is out of this task's
     file area, so the fix lives here instead, at the same specificity as
     style.css's own ≤780px rule; this file loads after style.css
     (`storefront-child-style` dependency), so a narrower ≤720px media
     query at the same specificity wins on source order alone, no
     `!important`.
     Round 1 replaced the whole expression with a flat `76px +
     env(safe-area-inset-bottom)`, which (a) threw away sticky-bar.js's own
     consent-lift (`--jar-consent-h`, measured live off the consent card's
     own rect, `.jar-mbar` is hidden whenever consent is open per WP10, so
     the upit no longer needs mbar clearance at that moment — it should sit
     right above the card, not further up) and (b) added
     `env(safe-area-inset-bottom)` a second time: style.css's own
     `padding-bottom: … + max(0, safeArea - consentH)` already accounts for
     it, tucked into the bar's PADDING rather than its `bottom` position,
     specifically so it does not double up with a bar that isn't flush
     against the true edge — untouched here.
     `max()` picks whichever obstruction is actually present: `.jar-mbar`
     and the open consent card are mutually exclusive (WP10 hides the bar
     while consent is open) — 76px when the bar is what's below (consent
     closed, `--jar-consent-h` is 0) or the card's own measured height when
     it's open and taller than 76px. */
  .jar-sticky-upit { bottom: max(76px, var(--jar-consent-h, 0px)); }
}

/* ── U2/D2 — Price joins the fixed buy row (2026-09-26, RESEARCH-UX.md) ───
   jarusica-mobile.js clones `.summary .price`/`.woocommerce-variation-price
   .price` (aria-hidden, the real price stays in `.jar-buybox` for a screen
   reader) and prepends it into the fixed row.

   Brief's own layout bracket, "[Preis][Menge 64][Button flex]" — "Menge 64"
   read (round 3 of this WP) as the qty NUMBER INPUT alone, with the
   `+`/`−` steppers hidden in this bar only. **Review round 4 correction
   (HOCH): that hid the steppers on EVERY simple/metre-goods product's PDP
   — `form.cart:not(.variations_form)` is this same fixed row (line 866
   above), not a different element, so the earlier fix silently removed a
   44px control from the buy row of every purchasable product on mobile.
   Lead's decision: keep the steppers (#80, the 44px floor is theirs to
   begin with) — the JS-measured `.jar-buyrow-wrap` fallback below (not a
   fixed media query, see its own comment) already exists for exactly the
   case where price+qtygroup+button don't fit one line; it now does the
   work the stepper-hiding used to.** The button's own base 22px/side
   padding (line 110 above) is trimmed here — same text, same 44px floor
   (`min-height` untouched) — narrower padding buys back some of the room
   the steppers take, without touching a touch target. */
@media (max-width: 720px) {
  form.cart:not(.variations_form) .single_add_to_cart_button,
  .woocommerce-variation-add-to-cart .single_add_to_cart_button {
    padding: 0 8px;
  }
  /* Specificity note: the clone is `<p class="price jar-price-inbar">` —
     WP8's own `.woocommerce div.product p.price{font-size:32px;…}` above
     is (0,3,2) and would otherwise win over a bare `.jar-price-inbar`
     (0,1,0), rendering the clone at the buybox's full 32px size (measured:
     that alone was wide enough to force the button onto its own line even
     after the padding/stepper fixes above — found by inspecting the
     clone's actual computed font-size, not guessed). Matched to the same
     (0,3,2) shape, one more element (`form`) added so this file's own
     later source order breaks the tie. */
  .woocommerce div.product form .jar-price-inbar {
    order: -1; flex: 0 1 auto; min-width: 0;
    display: flex; align-items: center;
    font: 700 18px var(--jr-font-display) !important; color: var(--jr-navy);
    white-space: nowrap; overflow: hidden; margin: 0;
  }
  /* No unconditional ≤360px "always two lines" media query — a first
     version tried that and broke the COMMON case (measured against the
     real catalogue: it forced even the ~600€ product, which already fits
     one line at 360px, into an unwanted two-line row — a media query can
     ask "how wide is the viewport", never "did this content actually
     overflow"). jarusica-mobile.js measures the REAL rendered layout after
     every insert (and again on resize) and only then adds
     `.jar-buyrow-wrap` — this rule stays out of the way for every price
     that already fits (measured: every real purchasable price on this
     shop, up to 5.248,75€, the priciest in-stock item — everything pricier
     is "po narudžbi", no buy row at all) and only engages for one that
     genuinely doesn't. */
  form.cart.jar-buyrow-wrap .jar-price-inbar,
  .woocommerce-variation-add-to-cart.jar-buyrow-wrap .jar-price-inbar {
    flex: 1 1 100% !important; margin-bottom: 2px;
  }
  /* Round 6, NIEDRIG (3): this used to sit ONLY inside the ≤440px media
     query below, matching the width range where wrapping is the common
     case (round 5's own comment) — but `.jar-buyrow-wrap` is added by real
     measurement (`checkWrap()`, jarusica-mobile.js), not by a media query,
     and can engage at any width ≤720px for an unusually long price. Left
     scoped to ≤440 only, a genuinely wrapped row at, say, 500px would have
     kept the 160px default padding-bottom, covering the footer/tabs behind
     it. Un-scoped so the 196px compensation always matches whenever the
     row is actually wrapped, at any width. */
  body.single-product:has(.jar-buyrow-wrap) {
    padding-bottom: calc(196px + env(safe-area-inset-bottom));
  }
  /* Review round 5, MITTEL (kept, base default only): at 450-720px the row
     fits one line (confirmed live), so the plain 160px default above
     already matches its real height there — only ≤440px, where wrapping is
     the confirmed COMMON case, gets 196px as the base default too, so the
     brief window before `checkWrap()` runs (or a wrap it doesn't catch)
     never shows the smaller value first.

     Round 7, NIEDRIG: `body.single-product:not(.woocommerce-checkout)` and
     `body.single-product:has(.jar-sticky-upit)`/`:has(.jar-metre-total)`
     above are the SAME specificity (0,2,1) — at ≤440px on a po-narudžbi or
     metre-goods product page, this rule being LATER in the file silently
     won regardless, overwriting their own 90px/160px with 196px (a budget
     for a form these pages don't even render, per WP8's own "these never
     render form.cart" comment above). Excluding both here is not about
     specificity order — it makes this rule simply not MATCH those pages at
     all, so their own rules above govern unconditionally. */
  @media (max-width: 440px) {
    body.single-product:not(.woocommerce-checkout):not(:has(.jar-sticky-upit)):not(:has(.jar-metre-total)) {
      padding-bottom: calc(196px + env(safe-area-inset-bottom));
    }
  }
}

/* ── U2 — Breadcrumb: one scrollable line, nothing hidden ─────────────────
   jarusica-mobile.js scrolls it to the end (the current page) once on
   load. No level removed — this is horizontal overflow, not a truncation. */
@media (max-width: 720px) {
  .woocommerce-breadcrumb {
    display: block; white-space: nowrap; overflow-x: auto;
    -webkit-overflow-scrolling: touch; scrollbar-width: none;
  }
  .woocommerce-breadcrumb::-webkit-scrollbar { display: none; }
}

/* ── U2 — Gallery thumbnails: a swipeable row with a peek ─────────────────
   `flex-control-thumbs` (FlexSlider's own thumbnail nav, built client-side
   — WP8's original rule above used a wrapping grid; a horizontal scroll
   row is what "swipeable, next one peeking" needs). The main gallery image
   stays untouched (WP8's own `aspect-ratio:1; object-fit:contain` above,
   owner rule: the photo is always shown whole). */
@media (max-width: 720px) {
  .woocommerce div.product .flex-control-thumbs {
    display: flex; flex-wrap: nowrap; overflow-x: auto; grid-template-columns: none;
    scroll-snap-type: x proximity; -webkit-overflow-scrolling: touch;
    gap: 8px; margin-top: 12px; padding-bottom: 2px;
  }
  .woocommerce div.product .flex-control-thumbs li {
    flex: 0 0 64px; width: 64px; scroll-snap-align: start;
  }
  /* Deck F32 (Tome, 29.09.): `cover` cropped each photo to fill the square,
     so a wide plug or a small icon came out zoomed in ("too big") next to a
     photo with white around it. `contain` + a little padding shows every
     photo whole, scaled into the same box — like the desktop row. */
  .woocommerce div.product .flex-control-thumbs img {
    aspect-ratio: 1; object-fit: contain; box-sizing: border-box; padding: 4px; background: var(--jr-paper);
    border-radius: 12px; border: 1px solid var(--jr-line);
  }
}

/* Deck F32: Storefront's woocommerce.css gives every thumbnail
   `margin-right: 14.29%` (11.1% full-width), reset to 0 only on every 4th,
   through `.woocommerce-product-gallery--columns-N … li:nth-child(4n)` —
   more specific than this theme's own `li { margin: 0 }` (style.css), so it
   won: 50px holes between the mobile thumbnails, and on desktop the first
   three grid thumbnails shrank by that margin (82px) while every 4th stayed
   full size (96px). The row/grid `gap` already spaces them; the vendor
   margin has to go, with `!important` because matching its per-column
   selector list here would copy four vendor variants. */
.single-product div.product .woocommerce-product-gallery .flex-control-thumbs li {
  margin: 0 !important;
}

/* ── Follow-up: PDP gallery visible before slider init (LCP, 27.09.) ──────
   Lighthouse mobile on a PDP: FCP 1.9s but LCP 4.4s — the gallery's main
   image is the LCP element, and the fetchpriority fix alone didn't move it
   (live already had fetchpriority=high + eager). The real cause:
   WooCommerce's own core template `single-product/product-image.php`
   inlines `style="opacity:0;transition:opacity .25s ease-in-out"` on
   `.woocommerce-product-gallery` (not something this theme's PHP outputs),
   removed client-side only once WooCommerce's own gallery JS has run —
   0.9-1.8s of pure render delay AFTER the bytes are already decoded,
   nothing to do with network or fetchpriority at all. With more than one
   image that JS is FlexSlider's init; with exactly one image WooCommerce
   disables FlexSlider entirely and the `ProductGallery` constructor itself
   (`wc-single-product.js`) removes the inline style, not a slider init —
   same delay either way, different code path setting the opacity.

   Lead decision (28.09.), after measured live A/B (controlled, same
   delivery path, injection proven per run, docs/mobile/progress.md
   "Follow-up: PDP LCP render delay", 09:07–09:10): scoped to galleries
   with exactly ONE image only. ProMariner 1230P (single image): LCP
   median 5,780 → 3,132 ms (−46%, all 3 pairs better), layout-shift sum
   unchanged at 0.028. RELiON RB80 (multi-image, the case this used to
   also cover via "hide every image but the first" plus a `padding-bottom:
   calc(24px + 89px)` thumbs-strip reserve): LCP only 12,072 → 11,052 ms
   (−8%, one pair worse), and layout-shift sum went 0.006 → 0.129 (0.123
   measured AFTER slider init) even though the reserved padding matched
   the measured strip height (89.3px) — the shift comes from FlexSlider's
   own thumbnail-strip re-layout at init, not from an unreserved strip, so
   no amount of padding math fixes it. Fails the "shifts not worse" rule.
   Multi-image galleries therefore keep today's WooCommerce
   opacity:0 → fade-in behaviour untouched; `:not(:has(… ~ …))` below
   ("no image has an image before it") is both the single-image test and
   the guard against needing to hide siblings or reserve a strip at all —
   a gallery with no second image never grows either.

   `!important` is required: an inline style attribute (WooCommerce core's
   `single-product/product-image.php`, `style="opacity:0;transition:
   opacity .25s ease-in-out"`) always wins over an external stylesheet
   regardless of selector specificity, so a plain override here would
   silently lose.

   `:has()` feature-detected via `@supports` — browsers without it
   (Safari <15.4) keep today's original opacity:0 → fade-in behaviour
   untouched, no regression there either. Mobile only (≤720px) — desktop
   unchanged, matches the WP8 gallery frame's own scope.

   Marker below (`PDP-LCP-CSS:START/END`) brackets ONLY the functional rule
   (not this comment) so tests/pdp-lcp-check.mjs can extract the exact live
   CSS text for its variant B (injected into the real page's <head> before
   first paint) instead of hand-duplicating it — same "test the real source,
   not a copy" reasoning tests/render-sizes.php and tests/render-gallery.php
   already use for the PHP side of this theme. */
/* PDP-LCP-CSS:START */
@supports selector(:has(a)) {
  @media (max-width: 720px) {
    .woocommerce-product-gallery:not(:has(.woocommerce-product-gallery__image ~ .woocommerce-product-gallery__image)) {
      opacity: 1 !important;
    }
  }
}
/* PDP-LCP-CSS:END */

/* ── U2/D5 — Metre-goods compact bar (2026-09-26, RESEARCH-UX.md) ─────────
   Only for `body:has(.jar-metre-total)` pages, jarusica-mobile.js toggles
   `.is-visible` via an IntersectionObserver on `form.cart` itself (which
   stays in normal flow for this product type, WP8's own exception above —
   no fixed row to cover ⅓ of the screen). Sits at the SAME 76px offset the
   ordinary fixed buy row uses, since the two never coexist (metre goods
   never get that row). Height stays under 64px (8px padding + a 44px
   button + line-height), per the brief's own ceiling. */
@media (max-width: 720px) {
  .jar-metre-bar {
    position: fixed; left: 8px; right: 8px;
    bottom: calc(76px + env(safe-area-inset-bottom));
    z-index: 5; display: none; align-items: center; justify-content: space-between;
    gap: 10px; max-height: 64px; padding: 8px 12px;
    background: #fff; border: 1px solid var(--jr-line); border-radius: var(--jr-r-lg);
    box-shadow: 0 12px 30px -12px rgba(0, 40, 80, .45); /* m-bar shadow, SPEC §2 */
  }
  .jar-metre-bar.is-visible { display: flex; }
  .jar-metre-bar-price {
    font: 700 16px var(--jr-font-display); color: var(--jr-navy);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0;
  }
  .jar-metre-bar-btn {
    flex: none; min-height: 44px; padding: 0 16px;
    border-radius: var(--jr-r-md); border: 0;
    background: var(--jr-sky); color: #fff; font: 600 14px var(--jr-font-body);
  }
}

/* ── WP9 — Bottom action bar ────────────────────────────────────────────
   Pattern `.mbar` (SPEC §4 WP9). Markup: `jarusica_mbar_html()`
   (functions.php), rendered in `wp_footer` site-wide and hidden on
   `is_checkout()` by the PHP itself (a checkout page never gets the node
   at all — nothing to hide here). Main action = Košarica (§6 item 4,
   needs_review, decided already; the design highlights "Servisni upit"
   instead, recorded there, not re-litigated in CSS). Styles 1:1 from the
   spec's own values.

   Review fix H2: `jarusica_mbar_html()` prints on EVERY page (`wp_footer`
   site-wide, not gated to ≤720px by PHP), but every rule that turns the
   three links into a fixed bar previously lived only inside the mobile
   media query — so on desktop it rendered as three raw links stacked under
   the footer instead of never appearing. `display:none` global,
   `display:grid` only ≤720px; same pattern WP6's filter-toggle JS already
   uses (`matchMedia('(max-width:720px)')` guard) for the same class of
   bug. */
.jar-mbar { display: none; }
@media (max-width: 720px) {
  .jar-mbar {
    display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 6px;
    position: fixed; left: 8px; right: 8px;
    bottom: calc(8px + env(safe-area-inset-bottom));
    z-index: 40; background: rgba(255, 255, 255, .97);
    border: 1px solid var(--jr-line); border-radius: 18px; padding: 6px;
    box-shadow: 0 12px 30px -12px rgba(0, 40, 80, .45); /* m-bar shadow, SPEC §2 */
  }
  .jar-mbar a {
    display: flex; align-items: center; justify-content: center;
    min-height: 50px; border-radius: var(--jr-r-md);
    font: 600 15px var(--jr-font-body); color: var(--jr-navy);
    text-align: center; text-decoration: none;
  }
  .jar-mbar a.main { background: var(--jr-sky); color: #fff; }

  /* Spec: "body{padding-bottom:84px}" so the bar never covers the last
     footer link. Single-product's own, larger clearance (it also carries
     the WP8 fixed buy row above this bar) lives with the rest of WP8's
     padding rule, adjusted there rather than duplicated here.
     "Hide `.jar-mbar` while the banner is open" is WP10's own bullet, not
     this one — that rule lives in the WP10 section below, once
     `#jar-consent` is this WP's own concern.

     Review fix H3b: `.jar-mbar` doesn't exist on checkout at all
     (`is_checkout()` → empty string, PHP), so reserving 84px of body
     padding there padded a footer the bar was never going to sit over —
     excluded via WooCommerce core's own `body.woocommerce-checkout` class,
     the same static-knowledge approach used for the buy row above. */
  body:not(.woocommerce-checkout) { padding-bottom: 84px; }
}

/* ── WP10 — Consent banner `#jar-consent` ───────────────────────────────
   The banner's OWN markup/text stays exactly as inc/cookie-consent.php
   renders it (verbatim legal wording, doc 07 — not this WP's to touch);
   only the container and the two primary buttons change here. Existing
   full-bleed, `border-top`-only design (style.css:1455) becomes the
   spec's floating card: 8px from the left/right/bottom edges, radius 18,
   the m-bar shadow. Same specificity as style.css's own `.jar-consent`/
   `.jar-consent-inner` rules — this file loads after style.css
   (`storefront-child-style` dependency), so source order decides without
   needing `!important`. */
@media (max-width: 720px) {
  .jar-consent {
    inset: auto 8px calc(8px + env(safe-area-inset-bottom)) 8px;
    border-top: 0;
    border-radius: var(--jr-r-lg);
    box-shadow: 0 12px 30px -12px rgba(0, 40, 80, .45); /* m-bar shadow, SPEC §2 */
    max-height: 80vh;
    max-height: 80dvh;
  }
  /* Flat 16px on every side (spec). The old `calc(8px + env(safe-area-
     inset-bottom))` bottom padding (style.css:1597, home-indicator
     clearance) moves to `.jar-consent`'s own `inset` above instead — the
     whole floating card is now offset from the edge by the safe area, the
     same pattern `.jar-mbar` uses, so the buttons clear the indicator
     without the inner padding having to carry it too. */
  .jar-consent-inner { padding: 16px; }

  /* Odbij sve / Prihvati sve: equal size and prominence already comes from
     style.css's own `grid-template-columns:1fr 1fr` + identical
     `.jar-consent-primary` styling on both (measured equal in the same
     harness that rule's own comment documents) — this WP only raises the
     shared floor from 44px to the spec's 48px. Scoped to Layer 1 so
     Layer 2's Natrag/Spremi postavke row (a settings sub-screen, not the
     always-visible banner) is untouched, same scoping style.css itself
     already uses one line up. */
  .jar-consent-layer[data-layer="1"] .jar-consent-actions .jar-consent-btn {
    min-height: 48px;
  }

  /* "Its z-index sits above `.jar-mbar`. Hide `.jar-mbar` while the banner
     is open" (spec WP10). z-index is already true by construction —
     `.jar-consent` is 9999 (style.css:1456), `.jar-mbar` is 40 (WP9) — the
     only thing left to do here is the hide. `#jar-consent` ships `hidden`
     in its initial markup (inc/cookie-consent.php) and only the banner's
     own script toggles that attribute, so reading it directly needs no
     second "is it open" state; `:has()` is live, so this tracks the
     attribute correctly even though the toggle happens well after this
     stylesheet loads. */
  body:has(#jar-consent:not([hidden])) .jar-mbar { display: none; }
}

/* ── WP11 — Cart and checkout ────────────────────────────────────────────
   No `.wc-block-*` markup exists anywhere on this shop — confirmed both by
   probe (`/kosarica/`, `/moj-racun/`, live 2026-09-26) and by
   inc/performance.php's own docblock ("cart and checkout are pinned to the
   classic [woocommerce_cart]/[woocommerce_checkout] shortcodes... CorvusPay
   has no block support"). The spec's "if built with blocks" branch does not
   apply; nothing below targets `.wc-block-*`.

   Cart line-item card: WooCommerce's classic `table.cart` (no child-theme
   override — grepped, only style.css's thumbnail-box and remove-button
   rules touch it) turns from a table row into a grid card, the same
   table→grid technique modern browsers support for turning a <table>
   responsive without a duplicated `data-title` label (the spec's own card —
   image/name/qty/subtotal/remove — needs no label text). NOT VERIFIED
   against a filled cart: the read-only rule forbids adding an item to
   measure it, and ACCEPTANCE.md says so explicitly ("no staging, no test
   order live") — built from WooCommerce's own stable classic-cart markup
   (`td.product-{remove,thumbnail,name,price,quantity,subtotal}`), not
   measured against a rendered row. */
@media (max-width: 720px) {
  .woocommerce-cart table.cart thead { display: none; }
  .woocommerce-cart table.cart,
  .woocommerce-cart table.cart tbody,
  .woocommerce-cart table.cart tr.cart_item,
  .woocommerce-cart table.cart td {
    display: block; width: auto; border: 0;
  }
  .woocommerce-cart table.cart tr.cart_item {
    display: grid;
    grid-template-columns: 64px 1fr;
    grid-template-areas:
      "thumb remove"
      "thumb name"
      "thumb price"
      "thumb qty"
      "thumb subtotal";
    gap: 4px 16px;
    padding: 16px; margin-bottom: 12px;
    background: #fff; border: 1px solid var(--jr-line);
    border-radius: var(--jr-r-lg);
  }
  .woocommerce-cart table.cart td { padding: 0; }
  .woocommerce-cart table.cart td.product-thumbnail { grid-area: thumb; width: 64px; }
  /* Storefront's own `assets/css/woocommerce/woocommerce.css` sets
     `table.cart .product-thumbnail img{max-width:3.70633em;…}` (~55.6px at
     this page's body font-size) — `max-width` caps a specified `width`
     regardless of which stylesheet declares which and in what order (the
     used width is the smaller of the two), so 64px was silently clamped to
     ~55.6px. `max-width:none` removes that cap; the 64px is otherwise
     unchanged. */
  .woocommerce-cart table.cart td.product-thumbnail img { width: 64px; height: 64px; max-width: none; }
  .woocommerce-cart table.cart td.product-remove { grid-area: remove; justify-self: end; }
  .woocommerce-cart table.cart td.product-remove a.remove {
    display: inline-flex; align-items: center; justify-content: center;
    width: 44px; height: 44px; font-size: 20px;
  }
  .woocommerce-cart table.cart td.product-name {
    grid-area: name; font: 600 16px/1.35 var(--jr-font-body); color: var(--jr-ink);
  }
  .woocommerce-cart table.cart td.product-price {
    grid-area: price; font: 700 16px var(--jr-font-display); color: var(--jr-navy);
  }
  .woocommerce-cart table.cart td.product-quantity { grid-area: qty; }
  /* Same M9c fix as the PDP qty input: `input` is in style.css:357's
     `border-radius:0 !important` list. */
  .woocommerce-cart table.cart td.product-quantity .quantity input.qty {
    width: 64px; height: 48px; font: 600 16px var(--jr-font-body);
    border: 1px solid var(--jr-pill-line); border-radius: var(--jr-r-md) !important;
  }
  .woocommerce-cart table.cart td.product-subtotal {
    grid-area: subtotal; font: 700 17px var(--jr-font-display); color: var(--jr-navy);
  }

  /* Checkout `.form-row` (spec: 100% width, inputs ≥16px/≥48px). style.css's
     own ≤780px rule (F5 fix) already reaches 16px/44px — this raises the
     shared floor to the spec's 48px the same way WP10 raised the consent
     buttons' floor, via a later ≤720px rule at higher specificity, no
     `!important` needed (this file loads after style.css). Measured on
     `/moj-racun/`'s login/register forms — same `.form-row`/`input.input-text`
     markup the checkout uses (decision already made: checkout itself
     redirects with an empty cart and the read-only rule forbids adding an
     item to reach it directly). */
  .woocommerce input.input-text,
  .woocommerce textarea.input-text,
  .woocommerce select,
  .woocommerce .select2-container--default .select2-selection--single {
    font-size: 16px; min-height: 48px;
  }
  /* Paired fields (first/last name, etc.) stack instead of sitting side by
     side at 47%/48% (WooCommerce core's own base rule, ~0,1,0 specificity —
     raising specificity here per project rule, not `!important`). */
  .woocommerce form .form-row-first,
  .woocommerce form .form-row-last { width: 100%; float: none; }

  /* `#place_order` is already full width (style.css:1067) — this WP only
     raises its height to the spec's 56px, on its own selector so the shared
     48px floor every other primary button keeps (WP1) is untouched. */
  .woocommerce #payment #place_order,
  #place_order {
    min-height: 56px; font-size: 17px;
  }
}

/* ── WP12 — Footer ────────────────────────────────────────────────────────
   Pattern `.ft` (SPEC §4 WP12). The existing footer (functions.php's
   `storefront_footer` callback, `.jar-foot-grid`) already reaches most of
   this WP's floor from an earlier, pre-mobile-design fix: 44px link targets
   ≤767px and one column ≤560px (style.css:1198-1200/1184-1185, dated before
   this spec). This WP raises what the spec itself asks for beyond that —
   the design's own 720px breakpoint for one column (560px left a 2-column
   band between 560–720px), the spec's 46px link floor (above the existing
   44px, never below it), the white logo pill, and the 14px legal row —
   without re-doing what already passes. */
@media (max-width: 720px) {
  .jar-foot-grid { grid-template-columns: 1fr; }
  .jar-foot-grid li > a { min-height: 46px; }
  .jar-fine { font-size: 14px; }
}
/* Logo pill. 16px is the spec's own literal value for this one decorative
   pill, not one of the `--jr-r-*` steps (12/14/20/28) — same as the shadow
   values elsewhere in this file that quote SPEC §2 directly.

   Review fix M6b: `padding`/`box-sizing` grow the element's own box, not
   just its colour or corners — a real spacing change, unlike `background`/
   `border-radius`. Split off into the mobile query.

   Review round 3, #1: `background`/`border-radius` were left global on the
   theory that colour/radius are one of SPEC §1's allowed everywhere-
   changes — true in general, but wrong for THIS element specifically once
   worker-a's `<picture>` (420020d) started serving a light-on-transparent
   footer logo variant on desktop: a white pill behind an already-light mark
   is white-on-white there, not a colour change anyone asked for at that
   width. The pill is a mobile pattern (SPEC §4 WP12, "white logo pill" —
   read in context of the mobile footer this WP is about), not a global
   token swap; moved the whole rule into the same ≤720px query as its own
   padding. */
@media (max-width: 720px) {
  .jar-foot-grid .jar-brand img {
    background: #fff; border-radius: 16px;
    padding: 8px 14px; box-sizing: content-box;
  }
}

/* Review round 3, #3 (N3): worker-a's `<picture>` (420020d) wraps the footer
   logo `img` in a `<picture>` element. `.jar-brand` is `display:flex`
   (style.css:465) and style.css:466-467 size only the `img`, not `picture` —
   so the `<picture>` itself becomes the flex item (sized to the inline
   `img`'s own inline-level box, which carries a small baseline gap under
   the image), not the image directly as before the swap. `display:contents`
   removes the `<picture>` box from layout entirely, so `.jar-brand`'s flex
   children go back to being exactly what they were pre-`<picture>` (the
   `img` and `.txt`) on every width, not just ≤720px.

   Found live while proving this fix (DOM probe, not guessed): `<source>`
   has no UA-stylesheet `display:none` of its own (verified: a bare
   `<picture><source>…</picture>` outside any `<video>`/`<audio>`/`<picture>`
   special-casing computes `display:inline`) — browsers normally skip it via
   the picture/video/audio selection ALGORITHM, not CSS, and that algorithm
   only applies while `<picture>` still generates its own box. Once
   `display:contents` removes that box, `<source>` is promoted to an
   ordinary flex item of `.jar-brand` right alongside the `img`, adding one
   extra `gap:12px` (style.css:465) and shifting the logo 12px off its
   pre-`<picture>` x-position — confirmed by a live probe comparing the
   plain `<img>` against the `<picture>` version under the SAME injected
   stylesheet at 1440px and 390px. Hiding `<source>` explicitly restores the
   exact pre-`<picture>` box. */
.jar-foot-grid .jar-brand picture { display: contents; }
.jar-foot-grid .jar-brand picture source { display: none; }

/* ── WP13 — Performance ───────────────────────────────────────────────────
   `sizes`/`fetchpriority` live in functions.php (file-area rule) — this file
   has nothing to add for WP13; the section marker stays for the "one
   commented section per WP, in WP order" convention (ORCHESTRATE-BRIEF). */

/* ── U1 — Catalogue UX (2026-09-26, docs/mobile/RESEARCH-UX.md) ──────────
   E3: applied-filter chips (jarusica-mobile.js builds `.jar-filter-chips`
   from the existing `a.fopt[aria-current="true"]` sidebar options — see
   that file's own comment for why this counts as "built from the filter_*
   params"). Chip text/aria-label come from the existing option's own text,
   nothing invented. */
@media (max-width: 720px) {
  .jar-filter-chips {
    /* Round 4, MITTEL 5: now a real `<ul role="list">` (jarusica-mobile.js)
       — reset the browser's own list box model (padding/list-style), the
       flex row layout is unaffected either way. */
    display: flex; flex-wrap: wrap; gap: 8px;
    margin: 0 0 12px; padding: 0; list-style: none;
  }
  .jar-filter-chip {
    display: inline-flex; align-items: center; gap: 6px;
    min-height: 44px; padding: 0 12px 0 14px;
    background: var(--jr-mist); color: var(--jr-navy);
    border-radius: var(--jr-r-pill); border: 1px solid var(--jr-line);
    font: 600 14px var(--jr-font-body); text-decoration: none;
  }
  .jar-filter-chip span[aria-hidden] {
    display: inline-flex; align-items: center; justify-content: center;
    width: 20px; height: 20px; border-radius: 50%;
    background: var(--jr-sky); color: #fff; font-size: 14px; line-height: 1;
  }

  /* E3, stock toggle: `.jar-catbar .fopt` ("Samo na zalihi", functions.php)
     is ALREADY the toggle this finding asked for, sitting next to the
     ordering select in the exact row named — style.css:329 already gives it
     a 46px floor. No JS clone needed (see progress.md: cloning would have
     duplicated an existing, already-wired control). Restyled as a pill to
     read as a chip alongside the new filter chips above, active state from
     the box's own inline `style="background:…"` (PHP only adds that
     attribute when the toggle is on — `:has()` reads that fact directly,
     no new class needed).
     Review round 4, NIEDRIG (a): hiding `.box` was NOT conditional on
     `:has()` support — on a browser without it, that left the toggle with
     NO visual on/off indicator at all (the box hidden, and the only thing
     that would have replaced it, the `:has()` background rule, inert).
     Both now live inside the same `@supports` block, so an unsupporting
     browser keeps the original checkbox-style box as its fallback
     indicator instead of showing neither. `functions.php`'s own
     `aria-pressed` (added in the same fix) states the on/off fact for
     assistive tech independently of whichever CSS path renders. */
  .jar-catbar .fopt {
    border: 1px solid var(--jr-line); border-radius: var(--jr-r-pill);
    padding-inline: 14px !important; background: #fff;
  }

  /* E4: bolder in-stock/out-of-stock flag on the catalogue rows — CSS-only,
     the pill treatment lives with the row rules themselves (WP7 section
     above, `.jar-results ul.products li.product .jar-stock`). */

  /* D4: `.jar-specline` restored (WP7 section above) and the WooCommerce
     post-add-to-cart states (also WP7 section above). */
}
@supports selector(:has(*)) {
  @media (max-width: 720px) {
    .jar-catbar .fopt .box { display: none; }
    .jar-catbar .fopt:has(.box[style]) {
      background: var(--jr-sky); color: #fff; border-color: var(--jr-sky);
    }
  }
}

/* ── Live test cart, checkout checkboxes (Manuel FREIGABE 27.09.,
   tests/cart-checkout-check.mjs run) ────────────────────────────────────
   Finding: `#terms`, `#createaccount`, `#ship-to-different-address-checkbox`
   measured 13px on the real checkout at 390px (checkout390.fieldViolations
   in the run's JSON) — same markup class (`input[type=checkbox]` inside
   `form.checkout`) as `/moj-racun/`'s own `jar_is_business` registration
   checkbox (WP11's own `checkoutInputs` block measures that page instead of
   checkout, see its comment above). Radios are intentionally left alone —
   WooCommerce hides them visually and the LABEL is the real tap target
   already; touching them here isn't part of this finding. */
@media (max-width: 720px) {
  form.checkout input[type="checkbox"] {
    width: 24px; height: 24px;
    accent-color: var(--jr-sky);
    flex: 0 0 24px;
  }
  form.checkout input[type="checkbox"] + label,
  form.checkout label:has(> input[type="checkbox"]) {
    display: flex; align-items: center; gap: 10px;
    min-height: 44px;
  }
}

/* ANCHOR-PRICE:START ── NN 101/2026 no. 1212 (inc/anchor-price.php) ─────────
   "Cijena na 10.9.2026.: X €" right at every consumer price, "jasno,
   vidljivo i čitljivo": its own line directly under the price, --jr-muted-2
   (≥4.5:1 on every surface it sits on, measured with the :root override at
   the top of this file, #4A6377: white 6.28, --jr-panel 5.88, --jr-mist
   5.53, the highlighted search row .jar-sg.on 5.26; with its base token
   value #536D81 still ≥4.59 — --jr-muted's base #62798D fell to 3.83 on
   .jar-sg.on, review 29.09.), not struck through (it is not a sale price),
   never smaller than the VAT line beside it (13px, 14px ≤767px). */
.jar-anchor {
  display: block;
  font-family: var(--jr-font-body); font-size: 13px; font-weight: 400; line-height: 1.4;
  letter-spacing: 0; text-transform: none; text-decoration: none;
  color: var(--jr-muted-2); white-space: normal;
}
@media (max-width: 767px) {
  .jar-anchor { font-size: 14px; }
}
/* Loop price cells are flex rows (style.css): the anchor takes its own line. */
.woocommerce ul.products li.product .price:has(> .jar-anchor) { flex-wrap: wrap; }
.woocommerce ul.products li.product .price > .jar-anchor { flex-basis: 100%; }
/* Catalogue rows ≤720px: "Dodaj" (44px) sits at the right end of the price
   cell (WP7). With an anchor line both align to the top of the cell and
   the anchor line starts below the button, at full width — measured:
   centred, the button covered the line's end. */
@media (max-width: 720px) {
  .jar-results ul.products li.product:has(> .add_to_cart_button) .price:has(> .jar-anchor) {
    align-self: start; row-gap: 24px; /* without `lh` support */ row-gap: max(4px, calc(44px - 1lh));
  }
  .jar-results ul.products li.product:has(.price > .jar-anchor) > .add_to_cart_button { align-self: start; }
}
/* Search dropdown: under the price; ≤720px (price and stock in one row) a
   line of its own below them. */
.jar-sg .an { font-family: var(--jr-font-body); font-size: 13px; color: var(--jr-muted-2); white-space: normal; }
@media (max-width: 720px) {
  .jar-sg .rt:has(> .an) { flex-wrap: wrap; }
  .jar-sg .an { flex-basis: 100%; order: 3; font-size: 14px; }
}
/* Fixed buy row ≤720px clones the product page's price (jarusica-mobile.js):
   price over anchor instead of side by side. */
@media (max-width: 720px) {
  .woocommerce div.product form .jar-price-inbar:has(> .jar-anchor) {
    flex-direction: column; align-items: flex-start; justify-content: center;
  }
}
/* Metre goods' compact bar ≤720px (jarusica-mobile.js clones the price, the
   anchor with it): at 360px price + anchor are 58px tall, 46px fit inside
   the bar's 64px ceiling — the lines ran into its padding. The bar grows
   instead (76px); the page's 160px padding-bottom still clears it plus its
   76px offset. The clone is a <p>: without margin:0 Storefront's paragraph
   margin (~23px) would grow the bar too. Measured: tests/anchor-price-offline.mjs. */
@media (max-width: 720px) {
  .jar-metre-bar:has(.jar-anchor) { max-height: none; }
  .jar-metre-bar:has(.jar-anchor) > .jar-metre-bar-price { margin: 0; }
}
/* Fixed buy row ≤720px, wrapped: the anchor adds a line — 134px instead of
   114px, measured at 360/390/412px. Room under the page = row + its 76px
   offset = 212px, as padding-bottom and as scroll-padding-bottom (a focused
   field or link never ends up behind the row). Wider than 440px: once
   jarusica-mobile.js marks the row as wrapped. ≤440px, where the row wraps
   as a rule: from first paint, keyed on the server-rendered anchor; not on
   po-narudžbi and metre pages (no fixed row there). */
@media (max-width: 720px) {
  body.single-product:has(.jar-buyrow-wrap > .jar-price-inbar > .jar-anchor) { padding-bottom: calc(212px + env(safe-area-inset-bottom)); }
  html:has(body.single-product .jar-buyrow-wrap > .jar-price-inbar > .jar-anchor) { scroll-padding-bottom: calc(212px + env(safe-area-inset-bottom)); }
}
@media (max-width: 440px) {
  body.single-product:has(.summary .price > .jar-anchor):not(:has(.jar-sticky-upit, .jar-metre-total)) { padding-bottom: calc(212px + env(safe-area-inset-bottom)); }
  html:has(body.single-product .summary .price > .jar-anchor):not(:has(.jar-sticky-upit, .jar-metre-total)) { scroll-padding-bottom: calc(212px + env(safe-area-inset-bottom)); }
}
/* ANCHOR-PRICE:END */
