/* =============================================================================
   LONDON SPOT: catalog.css                                  [catalog agent]
   The catalog page: the page head, the results toolbar, the facet column and
   the filter sheet, the active-filter chips, the grid and the list view, the
   pager, the wishlist's call block and the states.
   -----------------------------------------------------------------------------
   Load order: base.css, layout.css, components.css, catalog.css.
   Every value is a token of base.css (design.md). Nothing here redefines a
   shared component: the card, the chips, the empty state, the pager, the
   segmented control, the disclosure, the count badge and the scrim belong to
   components.css. This sheet places them and adds the page's own parts.
   Mobile first: every wider rule is a min-width query on the five
   breakpoints of design.md §14.1. A page stylesheet may use --z-pop,
   --z-sticky, --z-bar and --z-modal and nothing above them.

      0  The pinned toolbar and the frame the URL decides
      1  Page frame
      2  Page head
      3  Results toolbar
      4  Count, notes and chips
      5  The wishlist's call block
      6  Product grid and list view
      7  Pager
      8  Facet panel: the column and the sheet's body
      9  Filter bottom sheet
     10  Reduced motion
   ========================================================================== */


/* ── 0. THE PINNED TOOLBAR AND THE FRAME THE URL DECIDES ─────────────────
   The toolbar is a pinned band (design.md §4 "Pinned bars", §14.13): sticky
   under the header (top: --hdr-h, 0px below 768px where the header scrolls
   away), on --z-sticky. It registers its height on :root, where the chrome
   sums resolve, and the shell pays for it once in html's scroll padding: a
   focused card or chip never lands under it, tabbing forward or back
   (audits K8, M1). Only while it is shown: a toolbar with nothing in it
   (zero results, a wishlist of fewer than four items) is hidden and pins
   nothing.
   A short window pins nothing at the top (§14.13): below 500px of height, a
   phone on its side, the toolbar scrolls with the page and registers
   nothing, so it gives its 60px back to the content. The pin and its
   registration stand under ONE query, the height step read the other way
   round, so no rule can pin a toolbar that registered 0px: an override
   placed before the sticky rule lost the cascade and left six pager links
   under a 60px band at 640x360 (verify V1).                               */
@media not all and (max-height:500px){
  :root:not(.cx-few):not(.cx-wish0):has(.cx-tools:not([hidden])){ --pin-top:var(--bar-tool) }
  .cx-tools{ position:sticky; top:var(--hdr-h); z-index:var(--z-sticky) }
}

/* The frame the URL decides before the first paint (the head script of
   catalog.html marks <html>, design.md §14.15). The wishlist has no facet
   column, no filter button and its own note from the first frame; a
   wishlist of fewer than four items has no toolbar at all. It switched
   frames after the data landed and measured 0.70 of layout shift at 768px
   (audit K10). catalog.js keeps the classes in step with the URL. */
.cx-note--wish{ display:none }
html.cx-wish .cx-note--wish{ display:block }
html.cx-wish .cx-fbtn,
html.cx-wish .cx-side{ display:none }
html.cx-few .cx-tools,
html.cx-wish0 .cx-tools{ display:none }

/* The chip row a search word, a brand, a price or the newly arrived filter
   will fill is held from the first paint, so the chips arriving with the
   script move nothing (audit K22). An aisle's own chips are only printed from 768px
   (§14.7), so its reservation starts there too. catalog.js drops both
   classes after its first render: from then on an empty row is empty. */
.cx-chips:empty{ display:none }
html.cx-k-chips .cx-chips:empty{ display:block; min-height:44px }
@media (min-width:768px){
  html.cx-k-aisle .cx-chips:empty{ display:block; min-height:44px }
}
@media (min-width:1024px) and (hover:hover) and (pointer:fine){
  html.cx-k-chips .cx-chips:empty,
  html.cx-k-aisle .cx-chips:empty{ min-height:var(--s-10) }
}

/* The h1 of a view the URL names (an aisle, a search, a brand shelf, the
   wishlist) waits for its words as a skeleton in its own line box, so it
   never paints „კატალოგი" and swaps it a moment later: an aisle's name
   comes with the category file, 90ms after the first paint here and far
   later on a phone network, the other titles with the page script (verify
   R4). The bar is the loading block of components.css §11 (--page-2 fill,
   --r-xs), --s-1 inside the line box; the placeholder word is hidden from
   assistive tech too. catalog.js drops the class once the words are known
   (renderTitle), and a failed load ends it with what the h1 can say. */
html.cx-k-h1 .cx-h1{ position:relative; visibility:hidden }
html.cx-k-h1 .cx-h1::before{
  content:""; visibility:visible;
  position:absolute; inset-block:var(--s-1); inset-inline-start:0;
  width:min(100%, calc(var(--s-24) * 2));
  background:var(--page-2); border-radius:var(--r-xs);
}
/* An aisle's name can take two lines where the held box has one: in
   Georgian 91 of 198 aisle names do at 320px, 39 at 390px, some up to
   768px, and every one of them dropped the results column a line when it
   arrived (0.024 to 0.030 of layout shift on phones, verify-r3 C1). No box
   can be sized for a name that is not here yet, so while an aisle's h1
   waits the column under it waits out of sight: the head script marks
   cx-k-name, names the aisle from the tree the moment it lands (most often
   before the first paint) and drops both marks; catalog.js drops them too
   once it knows the words (renderTitle), or on a failed load. What moves is
   never on screen, and the skeleton h1 says the page is loading. */
html.cx-k-name .cx-body{ visibility:hidden }


/* ── 1. PAGE FRAME ───────────────────────────────────────────────────────
   The page is at least one screen tall, as a status page is (design.md
   §14.12): whatever arrives after the first paint (an empty state, the
   cards, a shorter wishlist) never pulls the footer into view. No bottom
   padding: the footer's --gap-page-end is the whole gap before it.       */
.cx{ min-height:100svh }

.cx-body{ display:grid; grid-template-columns:minmax(0,1fr) }

/* the facet column exists from 1024px; below that the very same panel lives
   in the bottom sheet (moved by catalog.js, never duplicated) */
.cx-side{ display:none }
.cx-main{ min-width:0 }

/* Beside the facet column (design.md §14.3): --facet-w, then --s-6 of gap
   from 1024px and --s-8 from 1280px. The column scrolls with the page and
   ends with its last facet, so it never runs on as an empty strip. */
@media (min-width:1024px){
  .cx-body{ grid-template-columns:var(--facet-w) minmax(0,1fr); column-gap:var(--s-6) }
  .cx-side{ display:block; position:relative; align-self:start; min-width:0 }
  /* the wishlist and the error state have no facet column and no track */
  .cx-body--wide,
  html.cx-wish .cx-body{ grid-template-columns:minmax(0,1fr) }
}
@media (min-width:1280px){ .cx-body{ column-gap:var(--s-8) } }


/* ── 2. PAGE HEAD ────────────────────────────────────────────────────────
   design.md §14.6: the h1 directly under the crumb row with no margin of its
   own, a note when there is one (--gap-head-in under the h1), then
   --gap-head-out to the page's first block. No rule under the head: the
   hairline and the toolbar's own rule framed one band with two lines
   (audit L25). The h1 takes the page title role from base.css.

   RHYTHM IS MEASURED ON INK (design.md §4, §14.5), as home's is, so the
   head and the blocks of the results give back what their boxes add
   around their words: a line's half leading, a 44px target's air, a
   pinned band's inner padding. They were spaced box to box, and the ink
   gaps ran 33 to 52px where the head asks 24 and 32, and 20 to 22px where
   the blocks ask 16 (review B8). A line's first and last ink sit where
   base.css measured them on --lh-h2 boxes (--words-top, --words-foot),
   moved by the half leading its own line height adds.                   */
.cx{
  --cx-line-top:  calc(var(--t-body) * (var(--words-top) + (var(--lh-body) - var(--lh-h2)) / 2));
  --cx-line-foot: calc(var(--t-body) * (var(--words-foot) + (var(--lh-body) - var(--lh-h2)) / 2));
  --cx-lede-foot: calc(var(--t-lede) * (var(--words-foot) + (var(--lh-lede) - var(--lh-h2)) / 2));
}
/* The head's own margin is the join, measured from what ends it: the h1
   (its foot is within a pixel of its box), the wishlist's lede, or the
   unknown link's note, and on a phone or a tablet the control under that
   note, whose 44px target leaves air under its words. */
.cx-head{ margin-bottom:var(--gap-head-out) }
html.cx-wish .cx-head{ margin-bottom:calc(var(--gap-head-out) - var(--cx-lede-foot)) }
.cx-head:has(> .cx-lost:not([hidden])){
  margin-bottom:calc(var(--gap-head-out) - var(--cx-line-foot) - (44px - var(--t-body) * var(--lh-body)) / 2);
}
@media (min-width:1024px){
  .cx-head:has(> .cx-lost:not([hidden])){ margin-bottom:calc(var(--gap-head-out) - var(--cx-line-foot)) }
}
.cx-h1{ overflow-wrap:break-word }

.cx-note{
  margin-top:var(--gap-head-in);
  max-width:var(--measure);
  font-size:var(--t-body); line-height:var(--lh-body); color:var(--ink-2);
}
/* The wishlist's line is the sentence under a page title, so it is the
   lede role (design.md §3): 16px, 18px from 768px, as home's lead, the
   information pages' ledes and the status pages' sentences are. It was
   body text at every width (review B17). */
.cx-note--wish{ font-size:var(--t-lede); line-height:var(--lh-lede) }
/* The unknown-category note is a status sentence a shopper reads to
   decide, so it stays body copy (audit K21), and below 1024px it carries
   the way to the category menu. From 1024px the whole tree stands in the
   facet column beside it, so the control is not needed there. */
.cx-lost__go{ margin-top:var(--s-1) }
@media (min-width:1024px){ .cx-lost__go{ display:none } }


/* ── 3. RESULTS TOOLBAR ──────────────────────────────────────────────────
   One band, --bar-tool tall, opaque over the grid it pins above (section 0
   says where and when it pins; design.md §14.7, §14.13). Below 768px it
   bleeds to the screen's edges so nothing shows through at the gutters
   (§14.2 lets a page's sticky toolbar cross the gutter there). A
   container, so the filter button can answer the toolbar's own width.
   Its first ink is the edge of the 44px controls it centres (40px under a
   fine pointer from 1024px), so it gives back the band's padding above
   them and the head's join is measured from those edges (review B8): the
   ink gap ran 33px on phones and 41px on a desktop where 24 and 32 are
   asked. The band's --page fill over that margin is no ink. Its last ink
   is its rule, --s-4 over the next block's first ink. */
.cx-tools{
  container:cxtools / inline-size;
  display:flex; align-items:center; gap:var(--s-2);
  height:var(--bar-tool);
  margin-top:calc((44px - var(--bar-tool)) / 2);
  margin-inline:calc(var(--gut) * -1);
  margin-bottom:var(--s-4);
  padding-inline:var(--gut);
  background:var(--page);
  border-bottom:var(--bw-1) solid var(--bd);
}
@media (min-width:1024px) and (hover:hover) and (pointer:fine){
  .cx-tools{ margin-top:calc((var(--s-10) - var(--bar-tool)) / 2) }
}

/* the filter button: a ghost button with its glyph, its word and the count
   badge (components.css .count, inline, no ring) */
.cx-fbtn{ flex:none; padding-inline:var(--s-3) }
@media (min-width:480px){ .cx-fbtn{ padding-inline:var(--s-4) } }
/* The word goes before the sort would ellipsise its value (design.md §14.7,
   audits K13, M28): under 320px of toolbar, the button, its badge and the
   sort's longest label („შესაბამისი") do not fit side by side, so the
   button keeps its glyph and its badge and says the rest in its name. */
@container cxtools (max-width:319.98px){
  .cx-fbtn:has(.count:not([hidden])) .cx-fbtn__w{ display:none }
}

.cx-sort{ display:flex; align-items:center; gap:var(--s-2); flex:1 1 auto; min-width:0 }
/* the label is spoken on phones and shown from 768px, where there is room */
.cx-sort__l{
  position:absolute; width:1px; height:1px; margin:-1px; padding:0;
  overflow:hidden; clip-path:inset(50%); white-space:nowrap;
}
/* a select cannot ellipsise its own value nicely; the ellipsis is only the
   fallback, the word drop above is the plan */
.cx-sort__s{ width:100%; min-width:0; text-overflow:ellipsis }

/* the view toggle is the segmented control (components.css .seg); the list
   view exists from 768px, so the toggle does too */
.cx-view{ display:none }

@media (min-width:768px){
  .cx-tools{ margin-inline:0; padding-inline:0; gap:var(--s-3) }
  .cx-sort{ flex:0 1 auto }
  .cx-sort__l{
    position:static; width:auto; height:auto; margin:0;
    overflow:visible; clip-path:none;
    font-size:var(--t-small-ka); line-height:var(--lh-ui); font-weight:500; color:var(--ink-2);
  }
  .cx-sort__s{ width:auto }
  .cx-view{ display:inline-flex; margin-inline-start:auto }
}
@media (min-width:1024px){ .cx-fbtn{ display:none } }


/* ── 4. COUNT, NOTES AND CHIPS ───────────────────────────────────────────
   Blocks inside one section are --s-4 apart, ink to ink (design.md §14.5):
   a line of text gives back the half leading above and under its words
   (§2), a chip, a card or a panel starts and ends on its own edge. The
   count stood 22px under the toolbar's rule and 20 to 21px over the next
   block (review B8).                                                     */

/* The live count. Not a heading: it is the one thing on this page that
   changes on every facet, and a heading that is also a live region is read
   out whole on each change. At zero it leaves the screen (the empty state
   says it, audit K30) and stays in the accessibility tree, so the change is
   still announced. As the first block of a short wishlist it stands
   --gap-head-out under the lede by the same give back. */
.cx-count{
  min-height:calc(var(--t-body) * var(--lh-body));
  margin-top:calc(var(--cx-line-top) * -1);
  margin-bottom:calc(var(--s-4) - var(--cx-line-foot));
  font-size:var(--t-body); line-height:var(--lh-body); color:var(--ink-2);
}
.cx-count .num{ font-weight:600; color:var(--ink) }
.cx-count.is-zero{
  position:absolute; width:1px; height:1px; min-height:0; margin:-1px; padding:0;
  overflow:hidden; clip-path:inset(50%); white-space:nowrap;
}

/* saved items that left the catalogue, once, above the list (audit K24) */
.cx-pruned{
  margin-top:calc(var(--cx-line-top) * -1);
  margin-bottom:calc(var(--s-4) - var(--cx-line-foot));
  max-width:var(--measure);
  font-size:var(--t-body); line-height:var(--lh-body); color:var(--ink-2);
}

/* The active filters: removable chips (components.css), one wrapping row,
   „ყველას გასუფთავება" inline after the last one with its label on the
   row's edge when it wraps (design.md §14.7, audits D33, K14). The quiet
   button's own padding is cancelled by a negative start margin, which is
   why the row's column gap is --s-3: the button's box then meets the chip
   before it and never overlaps it, so a tap on a chip's end cannot reach
   the control that clears everything. */
.cx-chips{
  display:flex; flex-wrap:wrap; align-items:center;
  column-gap:var(--s-3); row-gap:var(--s-2);
  min-width:0;
  margin-bottom:var(--s-4);
}
.cx-clear{ flex:none; padding-inline:var(--s-3); margin-inline-start:calc(var(--s-3) * -1) }
/* The chip an aisle wears for the moment before its name has arrived is a
   skeleton in the removable chip's box: its height and its pill, in the
   --page-2 fill every loading block has (components.css §11), so the row
   is a row from the first paint. It painted a bar in --scrim, the white
   hover wash on dark fills, which is not a loading colour (verify V11). */
.cx-chipsk{
  display:inline-block; flex:none;
  width:calc(var(--s-16) * 2); height:44px;
  border-radius:var(--r-pill);
}
@media (min-width:1024px) and (hover:hover) and (pointer:fine){
  .cx-chipsk{ height:var(--s-10) }
}

/* The empty state in the results region (design.md §6 "Empty state",
   §14.12) stands where the first result would, so one gap wins, the one
   the block above already owns: --gap-head-out under the page head (the
   empty wishlist, a load error, an empty aisle on a phone), --s-4 under
   the chip row or the stale-items line. Its own --s-10 was added on top
   of those and stood the mark 56 to 80px under whatever preceded it,
   against 21.5px under the crumbs on the 404 (review B4, verify-r3 C2);
   it applies only under a toolbar with nothing between, a case the page
   meets only in theory: at zero results the toolbar steps aside, or a
   chip row stands under it. The mark's circle is its first ink. */
#cxState > .empty{ margin-top:0 }
html:not(.cx-wish) .cx-tools:not([hidden]) ~ .cx-chips:empty ~ #cxState > .empty{ margin-top:var(--s-10) }


/* ── 5. THE WISHLIST'S CALL BLOCK ────────────────────────────────────────
   In a shop that sells by phone the wishlist is the cart, so it says how to
   buy: the call, the note under it, then every saved item's name and
   article code, and one control that copies them (audit K11, N18). It is
   the product page's buy box drawn with the buy box's own parts and
   numbers (product.css §5; review B11, P4): the panel (--paper, a --bd
   hairline, --r, --sh-1) padded --s-5 and --s-6 from 768px, the call as
   the view's one primary at 48px (.btn--lg, design.md §6 "Button"), its
   note hanging --s-3 under it, and the code in the buy box's figure. It
   was padded 16 and 20px, its call 44px with its sentence above it, and
   its codes 14px in --ink-strong. At most --measure wide, so it stays a
   panel on a wide screen and a saved item's name keeps its words. Its rows
   are drawn from the stored ids before the data lands and are the same
   height as the rows that replace them, so the grid under it does not move
   (design.md §14.15). Its edge is its ink: --s-4 to the grid (§14.5).    */
.cx-call{
  container:cxcall / inline-size;
  display:flex; flex-direction:column; align-items:stretch; gap:var(--s-4);
  max-width:var(--measure);
  margin-bottom:var(--s-4);
  padding:var(--s-5);
  background:var(--paper);
  border:var(--bw-1) solid var(--bd);
  border-radius:var(--r);
  box-shadow:var(--sh-1);
}
@media (min-width:768px){ .cx-call{ padding:var(--s-6) } }
/* the panel title role */
.cx-call__t{
  font-size:var(--t-h3); line-height:var(--lh-h3); font-weight:700; color:var(--ink);
}
/* The call's note: what to say once the shop answers, so it hangs --s-3
   under the button rather than a row gap away, as the buy box's code row
   does. */
.cx-call__tel + .cx-call__d{ margin-top:calc(var(--s-3) - var(--s-4)) }
.cx-call__d{ font-size:var(--t-body); line-height:var(--lh-body); color:var(--ink-2) }

/* The call's number, by width (design.md §6 "Button", the product page's
   rule). Below 768px a call button says „დარეკვა" alone: the tel: link
   dials, and „დარეკვა +995 591 58 25 82" took two lines in a full-width
   button at 320 and 360px (verify V4). The wishlist then prints the number
   in its note, as the buy box prints it over the article code. From 768px
   the button carries the number. The button's two labels are the shared
   call button's (components.css, .btn .l-short and .l-long); the note's
   two sentences are this block's. */
.cx-call__d--l{ display:none }
@media (min-width:768px){
  .cx-call__d--s{ display:none }
  .cx-call__d--l{ display:inline }
}

/* The saved items: a list of rows between hairlines. Below 420px of panel
   the code goes under the name, from 420px it stands at the row's end. */
.cx-call__list{ display:flex; flex-direction:column; border-block:var(--bw-1) solid var(--bd) }
.cx-call__i{
  display:flex; flex-direction:column; justify-content:center; gap:var(--s-1);
  padding-block:var(--s-2);
}
.cx-call__i + .cx-call__i{ border-top:var(--bw-1) solid var(--bd) }
.cx-call__n{
  min-width:0;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  font-size:var(--t-body); line-height:var(--lh-ui); color:var(--ink);
}
/* The article code is the buy box's figure (product.css .pcode__v), one
   size and one ink wherever the code is printed: --t-body 600 in --ink
   below 768px, --t-num 700 from 768px, on --lh-num. */
.cx-call__c{
  flex:none; min-width:0;
  font-size:var(--t-body); line-height:var(--lh-num); font-weight:600; color:var(--ink);
  overflow-wrap:anywhere;
}
@media (min-width:768px){ .cx-call__c{ font-size:var(--t-num); font-weight:700 } }
@container cxcall (min-width:420px){
  .cx-call__i{ flex-direction:row; align-items:center; gap:var(--s-4) }
  .cx-call__n{ flex:1 1 auto }
}
/* A skeleton bar holds one line box of its row, so a drawn row and a
   loading row are the same height; the bar paints inside it, --s-1 clear
   of both edges. */
.cx-call__i .skeleton{
  display:block;
  height:calc(var(--lh-ui) * 1em);
  padding-block:var(--s-1);
  background-clip:content-box;
}
.cx-call__n .skeleton{ width:88% }
.cx-call__c .skeleton{ width:var(--s-24); height:calc(var(--lh-num) * 1em) }
.cx-call__copy{ align-self:flex-start }

/* A long list shows its first four rows and a toggle for the rest, so the
   products are not pushed down the page by their own codes: twelve saved
   items made the panel 976px tall and put the first card at y 1319 at
   390px (verify V9). The toggle is a quiet button whose label stands on
   the panel's content edge (§14.2), its chevron turned while it is open. */
.cx-call__more{
  align-self:flex-start;
  padding-inline:var(--s-3); margin-inline-start:calc(var(--s-3) * -1);
}
.cx-call__more .icon{ transition:transform var(--d-ui) var(--ease) }
.cx-call__more[aria-expanded="true"] .icon{ transform:rotate(180deg) }


/* ── 6. PRODUCT GRID AND LIST VIEW ───────────────────────────────────────
   design.md §14.3: 2 columns, 3 from 480px; beside the facet column 3 from
   1024px and 4 from 1280px; at full width (the wishlist) 4 from 1024px and
   5 from 1280px. Every card stays inside the band of --card-min to
   --card-max from 320 to 1440px (218px and wider at 1024), and no grid
   crosses the gutter at any width: the 10px bleed below 360px is gone
   (audits L10, L13, K35). Gaps are --grid-gap: 12, then 16 from 480px.     */
.catgrid{
  display:grid; gap:var(--grid-gap);
  grid-template-columns:repeat(2,minmax(0,1fr));
}
@media (min-width:480px) { .catgrid{ grid-template-columns:repeat(3,minmax(0,1fr)) } }
@media (min-width:1280px){ .catgrid{ grid-template-columns:repeat(4,minmax(0,1fr)) } }
@media (min-width:1024px){
  .cx-body--wide .catgrid,
  html.cx-wish .catgrid{ grid-template-columns:repeat(4,minmax(0,1fr)) }
}
@media (min-width:1280px){
  .cx-body--wide .catgrid,
  html.cx-wish .catgrid{ grid-template-columns:repeat(5,minmax(0,1fr)) }
}

.catgrid[aria-busy="true"]{ pointer-events:none }

/* A subcategory page names its aisle in the h1 and the back link, so its
   cards carry no aisle line: the same name under every card said nothing
   (verify V6; catalog.js passes chip:false). The loading cards drop the
   line too, so the cards that replace them are the same height. */
.catgrid--noaisle .skel-card .card__cat{ display:none }

/* the supplier half of the product name: the list row's own line */
.cx-sub{ display:none }

/* The list row, from 768px, where its toggle appears (design.md §6 "Product
   card", §14.3): one row per product, the photo well square at 160px (180px
   from 1024px) and never stretched to the row's height, a one-line title at
   --t-h3, the supplier line under it, the same price row with the heart.
   The card's own container step at 400px keeps the well square. */
@media (min-width:768px){
  .catgrid--list{ grid-template-columns:minmax(0,1fr) }
  .catgrid--list .card{ flex-direction:row }
  .catgrid--list .card__media,
  .catgrid--list .skel-media{ flex:none; width:calc(var(--s-20) * 2); align-self:flex-start }
  /* The photograph is inset by 8% of the WELL (design.md §6). The card's
     own inset is a percentage, and a percentage padding is taken from the
     card's width: in a grid that is the well's, in a 720px row it was 57px a
     side and left the photograph 46px wide. The row says it in pixels. */
  .catgrid--list .card__media{ padding:max(var(--s-2), calc(var(--s-20) * 2 * .08)) }
  .catgrid--list .card__title{
    font-size:var(--t-h3);
    -webkit-line-clamp:1; line-clamp:1; min-height:0;
  }
  .catgrid--list .cx-sub{
    display:-webkit-box; -webkit-box-orient:vertical;
    -webkit-line-clamp:2; line-clamp:2; overflow:hidden;
    margin-top:var(--s-1);
    font-size:var(--t-sm); line-height:var(--lh-sm); color:var(--ink-2);
    overflow-wrap:break-word;
  }
  .catgrid--list .skel-card{ flex-direction:row }
}
@media (min-width:1024px){
  .catgrid--list .card__media,
  .catgrid--list .skel-media{ width:calc(var(--s-20) * 2 + var(--s-5)) }
  .catgrid--list .card__media{ padding:max(var(--s-2), calc((var(--s-20) * 2 + var(--s-5)) * .08)) }
}

/* a card leaving the wishlist collapses instead of vanishing */
.card.is-going{
  opacity:0; transform:scale(.97);
  transition:opacity var(--d-ui) var(--ease-out), transform var(--d-ui) var(--ease-out);
  pointer-events:none;
}


/* ── 7. PAGER ────────────────────────────────────────────────────────────
   components.css draws the pager, and below 480px its one compact row with
   the page sentence in li.pager__of (design.md §6 "Pagination"). A grid to
   its pager is --gap-tight (§14.5).
   On a middle page the row is „წინა", „გვერდი 5 / 80", „შემდეგი", and at
   the catalogue's widest count it is 342px („გვერდი 120 / 240"): past both
   gutters at 320 and at 360px, 1.7px from the screen's edge, its focus ring
   cut (K34). The controls keep their words, which is what makes them read
   as controls (design.md §6); under 342px of pager the sentence's word
   leaves the screen instead and stays in what a screen reader hears, so
   the row is „120 / 240" between the two, 284px at most. The first and the
   last page have one control and keep the whole sentence (245px at most).
   The step is the pager's own width, a container query, as the filter
   button's word drop is (§14.7); a middle page is one whose first and last
   items are the chevron links, the structure components.css reads too. */
.cx-pagerwrap{ container:cxpager / inline-size; margin-top:var(--gap-tight) }
@container cxpager (max-width:341.98px){
  .pager:has(> li:first-child > .pager__i > .icon):has(> li:last-child > .pager__i > .icon) .cx-pgw{
    position:absolute; width:1px; height:1px; margin:-1px; padding:0;
    overflow:hidden; clip-path:inset(50%); white-space:nowrap;
  }
}


/* ── 8. FACET PANEL: THE COLUMN AND THE SHEET'S BODY ─────────────────────
   The groups are compact disclosures (components.css .acc--compact,
   LS.disclosure): 48px triggers on touch, 44px under a fine pointer. The
   column orders them category, price, brand, new; the sheet price, brand,
   new, then the tree (design.md §14.7, OD14). catalog.js moves the nodes,
   so the tab order is the visual order.                                    */
.cx-filters{ min-width:0 }
.cx-groups .acc__c{ color:var(--ink) }
.cx-sk{ margin-bottom:var(--s-3) }
/* In the column the tree comes first, and it arrives with the category file,
   after the first paint: 22 rows that pushed the price group 700px down the
   page under the shopper's eye (0.5 of layout shift on an unknown category
   at 1024px, audit K22). Until the tree is drawn, the groups under it wait
   out of the layout, so they arrive in their place and nothing moves. */
@media (min-width:1024px){
  .cx-side .cx-groups:has(.cx-sk) > :not(#cxGCat){ display:none }
}

/* The column's skip link (design.md §12, §14.7; audit K25): the first stop
   of the column, visible only while it has focus, in the skip link's own
   look, and it lands on the results' heading, one stop before the first
   card. */
.cx-skip{
  position:absolute; width:1px; height:1px; margin:-1px; padding:0;
  overflow:hidden; clip-path:inset(50%); white-space:nowrap;
}
.cx-skip:focus{
  inset-block-start:0; inset-inline-start:0; z-index:var(--z-pop);
  display:inline-flex; align-items:center;
  width:auto; height:auto; min-height:44px; margin:0;
  padding-inline:var(--s-5);
  overflow:visible; clip-path:none;
  background:var(--slate-800); color:var(--on-fill); font-weight:600;
  border-radius:var(--r-sm);
}

/* Every row starts on the gutter, as the drawer's do (design.md §14.2: a
   full-screen layer or a sheet puts every inner edge on --gut): its words,
   its checkbox, its back arrow on the sheet's and the column's edge, under
   the group headings, the field labels and the price bands. A row with a
   highlight (the tree, the back row, the row to the brand list's second
   level) keeps its --s-2 of padding round the words by a negative inline
   margin, so the fill reaches --s-2 past the edge, as the drawer's
   secondary rows and a see-all link's wash do (§14.2 "Optical
   alignment"); a checkbox row has no fill and no padding. They started
   --s-2 inside the gutter, 24 against 16 at 390 and 136 against 128 at
   768, and 40 against 32 in the column (review B10). */

/* ── categories: the whole tree, every row a link with its count ── */
.cx-cats{ display:flex; flex-direction:column }
.cx-cat{
  display:flex; align-items:center; gap:var(--s-3);
  min-height:44px; padding:var(--s-2);
  margin-inline:calc(var(--s-2) * -1);
  border-radius:var(--r-sm);
  color:var(--ink); font-size:var(--t-body); line-height:var(--lh-name);
  transition:background-color var(--d-hover) var(--ease), color var(--d-hover) var(--ease);
}
@media (hover:hover){
  .cx-cat:hover{ background:var(--page-2); color:var(--ink-strong) }
}
.cx-cat:active{ background:var(--bd) }
.cx-cat[aria-current]{ background:var(--page-2); color:var(--ink-strong); font-weight:600 }
/* A name wraps between words to two lines in the 264px column and never
   breaks inside one; the count keeps its own slot, so a count arriving with
   the data does not re-wrap the name beside it. */
.cx-cat__n{
  flex:1 1 auto; min-width:0;
  display:-webkit-box; -webkit-box-orient:vertical;
  -webkit-line-clamp:2; line-clamp:2; overflow:hidden;
  overflow-wrap:break-word;
}
.cx-cat__c{
  flex:none; min-width:var(--s-10); text-align:end;
  font-family:var(--f-disp); font-size:var(--t-sm); line-height:var(--lh-ui);
  font-variant-numeric:tabular-nums; font-feature-settings:"tnum" 1;
  color:var(--ink-3);
}
/* A row with 0 is an ordinary link with its 0 (design.md §7): the rows with
   results lead, the others step back to --ink-2 (audit K36). */
.cx-cat.is-zero:not([aria-current]){ color:var(--ink-2) }
/* the category's own row inside its subcategory list */
.cx-cat--all{ font-weight:600; margin-bottom:var(--s-1) }

/* the way back up a level: the tree's „ყველა კატეგორია" and the brand
   list's second level */
.cx-back{
  display:flex; align-items:center; gap:var(--s-2);
  width:calc(100% + var(--s-4)); min-height:44px;
  padding-inline:var(--s-2);
  margin-inline:calc(var(--s-2) * -1);
  margin-bottom:var(--s-2);
  border-radius:var(--r-sm);
  color:var(--ink-2); font-size:var(--t-small-ka); line-height:var(--lh-ui); font-weight:500;
  text-align:start;
  transition:background-color var(--d-hover) var(--ease), color var(--d-hover) var(--ease);
}
.cx-back .icon{ transition:transform var(--d-hover) var(--ease) }
@media (hover:hover){
  .cx-back:hover{ background:var(--page-2); color:var(--ink-strong) }
  .cx-back:hover .icon{ transform:translateX(calc(var(--s-1) * -1)) }
}
.cx-back:active{ background:var(--bd) }

/* 14px is the Georgian floor and what a facet list in a 264px column wants:
   at 16px a third of the names take two lines (design.md §3). Rows trade the
   44px touch row for 40px only under a fine pointer, the step .btn--sm and
   the fields take; a 1024px tablet keeps 44 (design.md §6 "List rows"). */
@media (min-width:1024px){
  .cx-side .cx-cat{ font-size:var(--t-sm) }
}
@media (min-width:1024px) and (hover:hover) and (pointer:fine){
  .cx-side .cx-cat,
  .cx-side .cx-back,
  .cx-side .cx-more,
  .cx-side .cx-blist .opt,
  .cx-side .cx-onlynew .opt{ min-height:var(--s-10) }
}

/* ── price: four bands in two columns, then the range ── */
/* Two columns at every width: 140px a band at 320px, 128px in the column.
   A band's count follows its label by --s-2 and sits at the band's end; it
   may wrap under the label and never touches it (§6 chips, audit K3). */
.cx-zones{ display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:var(--s-2) }
.cx-zone{ width:100%; justify-content:flex-start; padding-inline:var(--s-2) }
.cx-zone .chip__t{ text-align:start }
.cx-zone .chip__n{ margin-inline-start:auto; font-size:var(--t-sm); color:var(--ink-3) }
.cx-zone[aria-pressed="true"] .chip__n{ color:var(--on-dark-2) }

/* the pair of fields shares a row at every width, each with its unit glyph
   inside it (design.md §14.10); the labels say from and to */
.cx-range{ display:flex; gap:var(--s-3); margin-top:var(--s-4) }
.cx-range__f{ flex:1 1 0; min-width:0 }
.cx-num{ position:relative; display:flex; align-items:center; min-width:0 }
.cx-num__g{
  position:absolute; inset-inline-start:var(--s-3);
  font-size:var(--t-body); line-height:var(--lh-ui); color:var(--ink-3);
  pointer-events:none;
}
.cx-num__i{ padding-inline-start:calc(var(--s-3) + var(--s-4)) }
.cx-rangeerr{ margin-top:var(--s-2) }

/* ── brands: the field, the chosen first, the top eight, a row onward ── */
.cx-blist{ display:flex; flex-direction:column }
.cx-blist .opt{ min-height:44px; font-size:var(--t-body) }
.cx-blist .opt__lb{ overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
.cx-bnone{ font-size:var(--t-body); line-height:var(--lh-body); color:var(--ink-2) }

/* the row that opens the brand list's second level: a row of the list,
   its label, the number it holds and a chevron that says it goes on */
.cx-more{
  display:flex; align-items:center; gap:var(--s-2);
  width:calc(100% + var(--s-4)); min-height:44px;
  padding-inline:var(--s-2);
  margin-inline:calc(var(--s-2) * -1);
  border-radius:var(--r-sm);
  font-size:var(--t-body); line-height:var(--lh-ui); font-weight:600; color:var(--ink);
  text-align:start;
  transition:background-color var(--d-hover) var(--ease), color var(--d-hover) var(--ease);
}
.cx-more__t{ flex:1 1 auto; min-width:0 }
.cx-more__n{ flex:none; font-size:var(--t-sm); font-weight:400; color:var(--ink-3) }
.cx-more .icon{ color:var(--ink-2) }
@media (hover:hover){
  .cx-more:hover{ background:var(--page-2); color:var(--ink-strong) }
}
.cx-more:active{ background:var(--bd) }

/* The second level: every brand, searched, forty at a time. It takes the
   panel's place while it is open, in the column and in the sheet. */
.cx-l2{ display:flex; flex-direction:column; gap:var(--s-3); padding-block:var(--s-2) var(--s-4) }
.cx-l2 .cx-back{ margin-bottom:0; font-size:var(--t-body); font-weight:600; color:var(--ink) }
.cx-l2__n{ font-size:var(--t-small-ka); line-height:var(--lh-ui); color:var(--ink-2) }
.cx-l2__more{ align-self:flex-start }

/* the newly arrived filter: one row under a hairline, while it is shown */
.cx-onlynew{ padding-block:var(--s-1); border-bottom:var(--bw-1) solid var(--bd) }
.cx-onlynew .opt{ font-weight:500 }


/* ── 9. FILTER BOTTOM SHEET (below 1024px) ───────────────────────────────
   design.md §6 "Bottom sheet" on the overlay primitive (LS.overlay: the
   counted lock, the inert page, focus once visible, Escape, the return).
   A page-owned modal on --z-modal, over the header and under the toast.
   Full width on a phone, and at most 560px wide and centred wherever the
   screen is wider than that: a tablet keeps a phone's proportions (audit
   K37), and so does a phone on its side, where the full-width sheet put
   a band's count 217 and 267px from its label and stretched the apply to
   608 and 708px at 640 and 740px (verify R2).                            */
.cx-sheet{ position:fixed; inset:0; z-index:var(--z-modal) }
/* the exit is an animation, not an interaction: while the panel slides away
   the sheet takes no taps */
.cx-sheet:not(.is-open){ pointer-events:none }

.cx-sheet__panel{
  position:absolute; inset-inline:0; bottom:0;
  margin-inline:auto;
  display:flex; flex-direction:column;
  max-width:calc(var(--s-20) * 7);
  max-height:min(86dvh, 720px);
  background:var(--paper);
  border-radius:var(--r-lg) var(--r-lg) 0 0;
  box-shadow:var(--sh-3);
  transform:translateY(100%);
  transition:transform var(--d-panel) var(--ease);
}
.cx-sheet.is-open .cx-sheet__panel{ transform:none }
.cx-sheet.is-drag .cx-sheet__panel{ transition:none }

/* The head: the grab bar (the drag surface), the title, the clear action as
   a quiet text button when there is something to clear, and the close
   control. The clear lives here and never beside the apply button: two
   buttons in one row cut the apply label to „დეგების ჩვენება (" at 320px
   (audits L2, M3, C3, K2). Inner edges on --gut; the close glyph stands on
   the edge (§14.2). */
.cx-sheet__head{
  position:relative; flex:none;
  display:flex; align-items:center; gap:var(--s-2);
  padding:var(--s-5) var(--gut) var(--s-2);
  border-bottom:var(--bw-1) solid var(--bd);
  touch-action:none;
}
.cx-sheet__grab{
  position:absolute; inset-block-start:var(--s-2); inset-inline-start:50%;
  width:var(--s-10); height:var(--s-1);
  transform:translateX(-50%);
  border-radius:var(--r-pill);
  background:var(--bd-2);
}
.cx-sheet__t{
  flex:1 1 auto; min-width:0;
  font-size:var(--t-h3); line-height:var(--lh-h3); font-weight:700;
}
.cx-sheet__clear{ flex:none }
.cx-sheet__x{ flex:none; margin-inline-end:calc((44px - 24px) / -2) }

.cx-sheet__body{
  flex:1 1 auto; min-height:0;
  overflow-y:auto; overscroll-behavior:contain;
  padding:var(--s-2) var(--gut) var(--s-8);
  scroll-padding-block:var(--s-4);
}
/* the body scrolls and clips, so its rings draw inside */
.cx-sheet__body :focus-visible{ outline-offset:var(--ring-in) }
/* The column's groups clip too: an open panel clips at --s-2 past its edge
   (components.css §14), and the rows that bleed --s-2 into the gutter (the
   tree, the back row, the brand list's row onward) used all of it, so their
   outside ring lost both sides (final accessibility check, 3). The same
   rule as the sheet's, so the column and the sheet draw every ring the same
   way. */
.cx-side .acc__inner :focus-visible{ outline-offset:var(--ring-in) }
/* the head's hairline already opens the list */
.cx-sheet .cx-groups{ border-top:0 }

/* The foot: one full-width primary with the count, paying the safe area, on
   --sh-up like every band on the bottom edge (design.md §4). Its label fits
   288px at 320px on one line; a .btn--block may wrap before it ever clips. */
.cx-sheet__foot{
  flex:none;
  padding:var(--s-3) var(--gut) calc(var(--s-3) + env(safe-area-inset-bottom));
  background:var(--paper);
  border-top:var(--bw-1) solid var(--bd);
  box-shadow:var(--sh-up);
}

/* A short screen pins nothing at the top (design.md §14.13), inside the
   sheet as on the page: below 500px of height, a phone on its side, the
   head scrolls away with the choices and only the foot, the apply, stays.
   The pinned head (73px) and foot (69px) left the choices 168px of the
   310px panel at 640x360 and 740x360, one row of bands at a time (verify
   R2). The panel is the one scroller, the foot sticks to its bottom edge,
   and the panel's scroll padding keeps a focused choice --s-3 clear of the
   foot (its 44px button, its padding and its hairline). The close control
   scrolls away with the head; the scrim, Escape and the apply still close
   the sheet, and focus reaching the close control brings the head back. */
@media (max-height:500px){
  .cx-sheet__panel{
    overflow-y:auto; overscroll-behavior:contain;
    scroll-padding-block:var(--s-3) calc(44px + var(--s-3) * 3 + var(--bw-1) + env(safe-area-inset-bottom));
  }
  .cx-sheet__body{ flex:none; overflow:visible }
  .cx-sheet__foot{ position:sticky; bottom:0 }
}

@media (min-width:1024px){ .cx-sheet{ display:none } }


/* ── 10. REDUCED MOTION ──────────────────────────────────────────────────
   base.css collapses every duration to 0s; what is left here is travel. The
   sheet simply appears and leaves (the overlay primitive shows it before
   focus moves in, so focus lands in both modes, audit K9). */
@media (prefers-reduced-motion: reduce){
  .cx-sheet__panel{ transform:none }
  .card.is-going{ transform:none }
}
@media (prefers-reduced-motion: reduce) and (hover:hover){
  .cx-back:hover .icon{ transform:none }
}
