
*,*::before,*::after{box-sizing:border-box}
/* The hidden attribute has to beat the rules below, and on its own it does not.
   A browser hides [hidden] through its own stylesheet, and ANY author
   declaration outranks a UA one — so an element carrying hidden but styled
   display:flex is simply visible.
   Both prompts were: .install and .join-sheet are flex, so every visitor got
   the sheet over the page on arrival — installed or not, asked before or not —
   and closing it set hidden = true, which was already true and did nothing.
   An unclosable dialog over a shop, from one line of CSS that reads correct.
   Here rather than on each element, so the next thing rendered hidden and laid
   out with flex or grid does not repeat it. */
[hidden]{display:none!important}
/* Android's WebView inflates text it decides is in a narrow column — "font
   boosting" — and it is on by default. It is why the same page reads a size
   larger inside a Telegram Mini App than in the browser beside it, and why the
   inflation is uneven: a heading grows, a caption does not. This turns it off
   and lets the sizes below mean what they say. */
html{-webkit-text-size-adjust:100%;text-size-adjust:100%}
body{margin:0;font-family:system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
  background:var(--bg,#fff);color:var(--fg,#111);-webkit-font-smoothing:antialiased}
a{color:inherit;text-decoration:none}
.wrap{max-width:1100px;margin:0 auto;padding:0 12px}
/* Above the floating contact bar, which is z-index 40. The drawer and its scrim
   live inside this header — one menu, laid out two ways — so the header's own
   layer is what decides whether the scrim covers the page or the buttons cover
   the scrim. */
header.bar{position:sticky;top:0;z-index:50;background:var(--bg,#fff);
  border-bottom:1px solid var(--line,#e6e6e6)}
.bar-in{display:flex;align-items:center;gap:10px;height:56px}
.icon-btn{background:none;border:0;font-size:22px;line-height:1;padding:6px;cursor:pointer;color:inherit}
.brand{display:flex;align-items:center;gap:8px;min-width:0;flex:1}
.brand img{height:32px;width:auto;border-radius:6px}
.brand-name{font-weight:650;font-size:17px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.searchbar{display:none;padding:0 0 12px}
.searchbar.is-open{display:block}
/* The account chip, at the end of the bar. A pill rather than an icon: the
   word دخول has to read as sign-in, not as another button in a row of them,
   and the signed-in state has to name the account it would sign out. */
.member-form{display:flex;margin:0}
.member-chip{display:inline-flex;align-items:center;gap:4px;padding:6px 11px;border:1px solid var(--line,#e6e6e6);
  border-radius:999px;background:none;color:inherit;font-size:12.5px;font-weight:600;text-decoration:none;
  cursor:pointer;white-space:nowrap}
.searchbar form{display:flex;gap:8px}
.searchbar input{flex:1;padding:11px 13px;font-size:16px;border:1px solid var(--line,#e6e6e6);
  border-radius:10px;background:var(--bg,#fff);color:inherit}
.searchbar button{padding:0 16px;border:0;border-radius:10px;background:var(--accent,#111);
  color:var(--accent-fg,#fff);font-size:15px;cursor:pointer}
.toolbar{display:flex;align-items:center;gap:10px;padding:12px 0;flex-wrap:wrap}
.count{color:var(--muted,#666);font-size:14px}
.toolbar select{margin-left:auto;padding:8px 10px;border-radius:8px;
  border:1px solid var(--line,#e6e6e6);background:var(--bg,#fff);color:inherit;font-size:14px}
/* auto-fill rather than a fixed count. The column count should follow how much
   room there is, not what device we guessed we were on: a Telegram Mini App
   window on desktop is around 420–520px, narrower than every breakpoint below,
   so a fixed two columns made 250px tiles there. minmax caps a tile instead of
   stretching it to fill whatever it is given. */
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(146px,1fr));
  gap:10px;padding-bottom:24px}
@media(min-width:640px){.grid{grid-template-columns:repeat(auto-fill,minmax(170px,1fr));gap:12px}}
@media(min-width:900px){.grid{grid-template-columns:repeat(4,1fr)}}
.tile{display:block;border:1px solid var(--line,#e6e6e6);border-radius:12px;overflow:hidden;background:var(--card,#fff)}
/* contain, not cover. Product photos arrive from a dozen suppliers in every
   aspect ratio there is, and cover crops to fill — which takes the toe off a
   portrait shoe and the label off a box. The square box is what makes every
   tile the same size however different the photographs are.

   The panel behind is white rather than the theme's tint, in both themes:
   these photos are shot on white almost without exception, so white is the
   colour that makes the letterboxing disappear instead of framing every
   product in grey bars. */
.tile-img{display:block;width:100%;aspect-ratio:1;object-fit:contain;background:#fff}
.tile-img-none{display:flex;align-items:center;justify-content:center;font-size:30px}
/* A closer look at the shoe without leaving the grid.
   The tile already clips, so the photo grows inside its own square and the row
   never shifts. Only where a pointer can hover — on a touch screen :hover
   sticks after a tap and would leave one tile zoomed until you tapped
   elsewhere; there the same move is bound to the press instead, which reads as
   the tile responding rather than as a stuck state. */
@media(hover:hover){
  .tile-img{transition:transform .28s ease}
  .tile:hover .tile-img{transform:scale(1.07)}
}
@media(hover:none){
  .tile-img{transition:transform .12s ease}
  .tile:active .tile-img{transform:scale(1.05)}
}
@media(prefers-reduced-motion:reduce){
  .tile-img{transition:none}
  .tile:hover .tile-img,.tile:active .tile-img{transform:none}
}
.tile-body{padding:9px 10px 11px}
.tile-name{font-size:13.5px;font-weight:600;line-height:1.4;color:var(--fg,#111);
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
  display:-moz-box;-moz-line-clamp:2;-moz-box-orient:vertical;overflow:hidden}
.tile-ref{font-size:12px;color:var(--muted,#666);margin-top:1px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.tile-price{font-weight:650;margin-top:2px}
/* Where a price would be and is not. Same reasoning as .price-ask below, at
   tile size: the cell says where the price went instead of collapsing to
   nothing, which is what an empty div would read as. */
.price-lock{color:var(--muted,#666);font-weight:550;font-size:12px;cursor:pointer}
.price-lock span{text-decoration:underline;text-underline-offset:2px}
.tile-meta{font-size:12px;color:var(--muted,#666);margin-top:2px}
.page-h1{font-size:19px;font-weight:650;margin:14px 0 0}
.empty{padding:56px 12px;text-align:center;color:var(--muted,#666)}
.pager{display:flex;align-items:center;justify-content:center;gap:14px;padding:8px 0 32px}
.page-btn{padding:9px 14px;border:1px solid var(--line,#e6e6e6);border-radius:9px;font-size:14px}
.page-btn.is-off{opacity:.4}
.page-of{font-size:14px;color:var(--muted,#666)}
.scrim{position:fixed;inset:0;background:rgba(0,0,0,.45);opacity:0;pointer-events:none;
  transition:opacity .18s;z-index:30}
.scrim.is-open{opacity:1;pointer-events:auto}
/* From the same side as the button that opens it. The page is RTL, so the ☰ is
   the top-right corner and a panel arriving from the left came from nowhere. */
.drawer{position:fixed;top:0;bottom:0;right:0;width:80%;max-width:310px;background:var(--bg,#fff);
  z-index:31;transform:translateX(100%);transition:transform .18s;overflow-y:auto;
  border-left:1px solid var(--line,#e6e6e6)}
.drawer.is-open{transform:none}
.drawer-head{display:flex;align-items:center;justify-content:space-between;
  padding:14px 14px 10px;border-bottom:1px solid var(--line,#e6e6e6)}
.drawer-title{font-weight:650}
.menu-link{display:flex;align-items:center;justify-content:space-between;gap:8px;
  padding:13px 15px;border-bottom:1px solid var(--line,#f0f0f0);font-size:15px}
.menu-link.is-on{font-weight:650}
.menu-count{color:var(--muted,#666);font-size:13px}
/* A button wearing a menu link's clothes: without the reset, the browser's own
   button font and colours would make it read as a control from another page. */
.menu-install{width:100%;background:none;border:0;border-bottom:1px solid var(--line,#f0f0f0);
  color:inherit;font:inherit;cursor:pointer}
/* The moving strip on the front page. Two identical runs side by side; the
   animation slides exactly one run's width and resets, so the seam never
   arrives. */
.showcase{display:flex;overflow:hidden;gap:10px;margin:4px 0 14px;
  /* Fades at both edges, so products enter and leave rather than being cut off
     against the page. mask-image is ignored where unsupported, which loses the
     fade and nothing else. */
  -webkit-mask-image:linear-gradient(to left,transparent,#000 6%,#000 94%,transparent);
  mask-image:linear-gradient(to left,transparent,#000 6%,#000 94%,transparent)}
.showcase-run{display:flex;gap:10px;flex:0 0 auto;animation:showcase-slide 42s linear infinite}
.showcase:hover .showcase-run{animation-play-state:paused}
.showcase-item{flex:0 0 auto;width:110px;position:relative;border-radius:11px;overflow:hidden;
  background:var(--card,#fff);border:1px solid var(--line,#e6e6e6)}
.showcase-item img{width:100%;height:110px;object-fit:cover;display:block;background:var(--sk,#f2f2f2)}
.showcase-none{display:flex;align-items:center;justify-content:center;height:110px;font-size:26px}
.showcase-price{display:block;padding:6px 8px;font-size:12.5px;font-weight:600;text-align:center;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* RTL: the page runs right to left, so the strip travels the same way. */
@keyframes showcase-slide{from{transform:translateX(0)}to{transform:translateX(calc(100% + 10px))}}
/* Somebody who has asked their system for less motion has asked for this too.
   The strip becomes an ordinary row that can be swiped by hand. */
@media (prefers-reduced-motion: reduce){
  .showcase{overflow-x:auto}
  .showcase-run{animation:none}
}
/* The price filter, in the drawer under the collections. */
.price-filter{padding:14px 15px;border-bottom:1px solid var(--line,#f0f0f0)}
.price-filter-title{display:block;font-size:13px;color:var(--muted,#666);margin-bottom:8px}
.price-filter-row{display:flex;gap:8px;align-items:center}
.price-filter input{width:100%;min-width:0;padding:9px 10px;border-radius:9px;font-size:15px;
  border:1px solid var(--line,#e6e6e6);background:var(--card,#fff);color:inherit}
.price-filter button{padding:9px 14px;border-radius:9px;font-size:14px;font-weight:600;
  white-space:nowrap;border:1px solid var(--line,#e6e6e6);background:var(--card,#fff);
  color:inherit;cursor:pointer}
/* The filter as it stands, above the grid — the drawer is closed by the time a
   customer is looking at an empty result. */
.filter-pills{display:flex;gap:8px;flex-wrap:wrap;margin:0 0 10px}
.filter-pill{display:inline-flex;align-items:center;gap:8px;padding:6px 12px;border-radius:999px;
  font-size:13.5px;border:1px solid var(--line,#e6e6e6);background:var(--card,#fff)}
.filter-pill a{color:var(--muted,#666);font-size:15px;line-height:1;text-decoration:none}
.detail{padding:14px 0 40px}
.detail img{width:100%;border-radius:12px;margin-bottom:10px;background:var(--sk,#f2f2f2)}
.detail h1{font-size:19px;margin:2px 0 6px}
.detail .price{font-size:23px;font-weight:700;margin:8px 0}
.detail dl{display:grid;grid-template-columns:auto 1fr;gap:6px 14px;margin:14px 0;font-size:15px}
.detail dt{color:var(--muted,#666)}
.detail dd{margin:0}
.cta{display:block;text-align:center;padding:15px;border-radius:12px;background:#25D366;
  color:#fff;font-weight:650;font-size:16px;margin-top:18px}
.back{display:inline-block;padding:10px 0;color:var(--muted,#666);font-size:14px}
footer{padding:22px 12px 34px;color:var(--muted,#666);font-size:13px;text-align:center;
  border-top:1px solid var(--line,#e6e6e6)}
/* One column with the picture beside the details. Chosen per shop, because a
   catalogue of near-identical shoes reads better as a list than as a wall of
   thumbnails — and a shop with twelve products looks sparse in a four-column
   grid. */
/* ── Product page ─────────────────────────────────────────────────────────── */
.gallery{margin:6px 0 14px}
/* The whole photo, never cropped — this is the picture somebody is deciding on.
   The box stays square so the page does not jump between products of different
   shapes, and the padding stops a white-background photo bleeding into the
   panel behind it. */
.hero{width:100%;aspect-ratio:1;object-fit:contain;border-radius:12px;
  background:#fff;display:block}
.hero-none{display:flex;align-items:center;justify-content:center;font-size:52px}
.thumbs{display:flex;gap:8px;overflow-x:auto;padding:10px 2px 2px;-webkit-overflow-scrolling:touch}
.thumb{width:72px;height:72px;flex:0 0 72px;object-fit:contain;border-radius:9px;cursor:pointer;
  background:var(--sk,#f2f2f2);padding:4px;border:2px solid transparent;opacity:.65;
  transition:opacity .12s,border-color .12s}
.thumb.is-on{opacity:1;border-color:var(--accent,#111)}
.per{font-size:14px;font-weight:400;color:var(--muted,#666)}
/* An invitation, not a gap. A blank where the price goes reads as broken. */
.price-ask{font-size:19px;color:var(--muted,#666)}
/* The 19px invitation above is sized for the product page's own price slot; a
   tile or a showcase pill wearing the same class keeps its cell's size
   instead. */
.tile-price.price-ask,.showcase-price.price-ask{font-size:12px;font-weight:550}
.member-link{color:inherit;text-decoration:underline;text-underline-offset:2px}
.unknown{color:var(--muted,#666)}
.alt-row{display:flex;gap:10px;margin-top:10px;flex-wrap:wrap}
.btn-alt{flex:1 1 45%;text-align:center;padding:12px;border-radius:11px;font-size:15px;font-weight:600;
  border:1px solid var(--line,#e6e6e6);color:inherit;background:var(--card,#fff)}
.back{font-size:15px}
.foot-links{margin-top:10px;display:flex;gap:16px;justify-content:center;flex-wrap:wrap}
.foot-links a{text-decoration:underline}
/* Installing. Hidden until the browser says the shop qualifies — a button that
   cannot do anything is worse than no button.

   Fixed to the bottom of the screen rather than sitting in the footer, where it
   was only seen by somebody who had already scrolled the whole catalogue. Above
   the safe area so it clears a phone's gesture bar, and off to one side so it
   never covers the middle of a product. It has its own dismiss, and the dismiss
   is remembered: an install prompt that comes back after being closed is the
   thing a customer leaves over. */
.install{position:fixed;z-index:40;inset-inline-end:12px;
  bottom:calc(12px + env(safe-area-inset-bottom,0px));
  display:flex;align-items:center;gap:6px}
.install button{padding:11px 16px;border-radius:999px;font-size:14px;font-weight:700;
  border:1px solid var(--line,#e6e6e6);color:inherit;background:var(--card,#fff);cursor:pointer;
  box-shadow:0 6px 20px rgba(0,0,0,.18)}
/* A slow tilt, twice, then still. Enough to catch an eye that is scrolling
   past; not a thing that keeps moving while somebody reads a price. */
.install button{animation:install-notice 1.1s ease-in-out .8s 2}
@keyframes install-notice{
  0%,100%{transform:rotate(0)}
  25%{transform:rotate(-6deg)}
  75%{transform:rotate(6deg)}
}
/* Somebody who has asked for less motion has asked for exactly this. */
@media (prefers-reduced-motion:reduce){.install button{animation:none}}
.install-x{width:26px;height:26px;border-radius:999px;line-height:1;font-size:15px;
  border:1px solid var(--line,#e6e6e6);background:var(--card,#fff);color:var(--muted,#666);
  cursor:pointer;padding:0;box-shadow:0 6px 20px rgba(0,0,0,.12)}
/* iOS never offers the button, so it gets the sentence instead. It stays in the
   footer: it is an instruction to read, not a control to press. */
.install-ios{font-size:12.5px;line-height:1.7;max-width:30rem;margin:0 auto 14px}

/* Offered once the shop is installed, and only then. Somebody who has just put
   the shop on their home screen is the one customer in a hundred who wants the
   group as well — asking everybody else is how a shop becomes the site that
   opens a dialog before it shows a price. */
.join-sheet{position:fixed;inset:0;z-index:60;display:flex;align-items:flex-end;
  justify-content:center;background:rgba(8,10,20,.55);
  -webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);
  animation:join-fade .18s ease-out}
@keyframes join-fade{from{opacity:0}to{opacity:1}}
.join-box{position:relative;background:var(--card,#fff);color:inherit;width:100%;max-width:26rem;
  border-radius:22px 22px 0 0;padding:22px 18px calc(20px + env(safe-area-inset-bottom,0px));
  box-shadow:0 -10px 40px rgba(0,0,0,.3);animation:join-rise .22s cubic-bezier(.2,.8,.3,1)}
@keyframes join-rise{from{transform:translateY(14px)}to{transform:translateY(0)}}
/* The grab bar a phone user reads as "this panel moves". Decorative only —
   the sheet is dismissed by the X, the backdrop, Escape or "later". */
.join-box::before{content:"";position:absolute;top:9px;inset-inline-start:50%;
  transform:translateX(50%);width:38px;height:4px;border-radius:99px;
  background:var(--line,#e6e6e6)}
@media (prefers-reduced-motion:reduce){
  .join-sheet,.join-box{animation:none}}
/* The close a customer looks for first. "من بعد" says the same thing, but it
   sits under the links: somebody who wants out of a dialog reaches for the
   corner, and not finding one there reads as a dialog that traps them.
   inset-inline-end puts it top-left on this RTL page, which is where an Arabic
   reader looks. */
.join-x{position:absolute;top:14px;inset-inline-end:14px;width:34px;height:34px;
  border-radius:999px;line-height:1;font-size:15px;border:1px solid var(--line,#e6e6e6);
  background:var(--card,#fff);color:var(--muted,#666);cursor:pointer;padding:0;
  display:grid;place-items:center;transition:background .15s,color .15s}
.join-x:hover,.join-x:focus-visible{background:var(--line,#f0f0f0);color:var(--fg,#111)}
@media(min-width:560px){.join-sheet{align-items:center}
  .join-box{border-radius:20px;padding-bottom:20px}
  .join-box::before{display:none}}
.join-box h2{font-size:18.5px;margin:4px 0 5px;padding-inline-end:40px;letter-spacing:-.2px}
.join-box p{color:var(--muted,#666);font-size:13.5px;margin:0 0 16px;line-height:1.65}
.join-list{display:grid;gap:8px}
.join-list a{display:flex;align-items:center;gap:11px;padding:13px 14px;border-radius:14px;
  border:1px solid var(--line,#e6e6e6);text-decoration:none;color:inherit;font-weight:600;
  font-size:14.5px;transition:border-color .15s,background .15s}
.join-list a:hover,.join-list a:focus-visible{border-color:var(--accent,#4a5bd6);
  background:color-mix(in srgb,var(--accent,#4a5bd6) 7%,transparent)}
.join-list a:active{transform:scale(.99)}
/* The icon in its own tinted disc, so a row reads as a destination rather than
   as a line of text that happens to start with an emoji. */
.join-list a>span:first-child{flex:0 0 34px;height:34px;display:grid;place-items:center;
  border-radius:50%;background:var(--line,#f0f0f0);font-size:16px}
.join-list a>span:nth-child(2){flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;
  white-space:nowrap}
/* Points the way out of the sheet; flips with the page because it is a glyph
   in a RTL flow, not a rotated arrow. */
.join-list a::after{content:"›";color:var(--muted,#999);font-size:19px;line-height:1}
.join-later{display:block;width:100%;margin-top:14px;padding:12px;border:0;background:none;
  color:var(--muted,#666);font-size:13.5px;cursor:pointer;border-radius:10px}
.join-later:hover{background:var(--line,#f2f2f2)}

/* The contact page. Rows rather than a table: a phone, an email and four
   channels are a list of ways to reach somebody, and each one is a tap. */
.contact h1{margin-bottom:6px}
.ct-lede{color:var(--muted,#666);font-size:14.5px;margin:0 0 18px}
.ct-address{display:flex;gap:10px;align-items:flex-start;padding:13px 14px;margin-bottom:16px;
  border:1px solid var(--line,#e6e6e6);border-radius:12px;background:var(--card,#fff);
  font-size:15px;line-height:1.7}
.ct-list{list-style:none;padding:0;margin:0;display:grid;gap:9px}
.ct-row{display:flex;gap:11px;align-items:center;padding:12px 14px;
  border:1px solid var(--line,#e6e6e6);border-radius:12px;background:var(--card,#fff)}
.ct-ico{font-size:19px;line-height:1;flex:0 0 auto}
.ct-body{display:flex;flex-direction:column;min-width:0}
.ct-label{font-size:12.5px;color:var(--muted,#666)}
/* Wraps rather than overflowing: an email address on a narrow phone is wider
   than the card, and a row that scrolls sideways is one nobody can read. */
.ct-value{font-size:15.5px;font-weight:600;word-break:break-word}
a.ct-value{text-decoration:none}
a.ct-value:hover{text-decoration:underline}

.gone{padding:26px 0 8px;text-align:center}
.gone h1{font-size:20px;margin:0 0 8px}
.gone p{color:var(--muted,#666);margin:0 0 16px}
.gone .cta{max-width:320px;margin:0 auto}
.gone-h2{font-size:17px;margin:26px 0 12px}

/* Prose, same measure as an article: past about 68 characters a line stops
   being easy to track back from. */
.policy{padding:14px 0 40px;max-width:68ch;font-size:16px;line-height:1.8}
.policy h1{font-size:22px;margin:6px 0 14px}
.policy h2{font-size:17px;margin:24px 0 6px}
.policy p{margin:0 0 12px;color:var(--muted,#666)}
/* A rule above each section, so a page of eight headings scans as eight
   sections rather than as one column of text with bold lines in it. */
.policy h2{padding-top:16px;border-top:1px solid var(--line,#f0f0f0)}
.policy h1 + h2{padding-top:0;border-top:0}
/* The way back to the catalogue. Somebody reading the privacy policy is
   deciding whether to send a stranger their address — the shop is where they
   were and where they are going next. */
.policy-back{display:inline-block;margin-top:22px;font-size:15px;text-decoration:underline}

.ship-head{font-size:16px;margin:26px 0 6px}
.ship{color:var(--muted,#666);font-size:14.5px;line-height:1.7;margin:0}

/* ── The bar that follows you down the page ───────────────────────────────── */
.fabs{position:fixed;left:0;right:0;bottom:0;z-index:40;display:flex;gap:8px;
  padding:9px 12px calc(9px + env(safe-area-inset-bottom));
  /* Solid first: color-mix arrived in Safari 16.4, and a browser that does not
     understand the second declaration keeps this one rather than falling back
     to transparent — which would leave the buttons floating over the grid. */
  background:var(--bg,#fff);
  background:color-mix(in srgb, var(--bg,#fff) 88%, transparent);
  backdrop-filter:blur(10px);border-top:1px solid var(--line,#e6e6e6)}
.fab{flex:1;display:flex;align-items:center;justify-content:center;gap:7px;
  padding:12px 8px;border-radius:11px;font-size:14.5px;
  font-weight:650;color:#fff;white-space:nowrap;overflow:hidden}
.fab-i{flex:0 0 auto}
.fab span{overflow:hidden;text-overflow:ellipsis}
/* Under about 380px three labels do not fit and each truncates to two
   characters, which reads as broken. The marks alone are unambiguous. */
@media(max-width:379px){.fab span{display:none}.fab{gap:0}}
.fab-wa{background:#25D366}
.fab-wac{background:#128C7E}
.fab-tg{background:#229ED9}
/* The bell is a <button>, not a link, so it needs the three things a button
   does not inherit. It starts hidden and the script reveals it only once the
   SDK is up and there is actually a permission left to ask for. */
.fab-bell{background:var(--accent,#1d6fc4);color:var(--accent-fg,#fff);
  border:0;font-family:inherit;cursor:pointer}
.fab-bell[hidden]{display:none}
/* Room for the bar, so the last row of the grid and the order button are not
   sitting underneath it. */
body{padding-bottom:78px}
@media(min-width:900px){
  /* On a desktop the bar becomes a corner stack: a full-width bar across a
     1100px page is a banner, and this is a shortcut, not the page's subject.
     Not inside Telegram, where the app is a phone-shaped sheet however wide the
     window Telegram Desktop opened it in. */
  body:not(.is-mini) .fabs{left:auto;right:16px;bottom:16px;flex-direction:column;width:190px;
    border:1px solid var(--line,#e6e6e6);border-radius:14px;padding:8px}
  body:not(.is-mini){padding-bottom:0}
}

/* ── The size of a Mini App ────────────────────────────────────────────────── */
/* Inside Telegram the shop is a sheet, not a window. Its height is the client's
   to decide and it changes — the sheet is dragged, the keyboard opens, the app
   is expanded. 100vh is the wrong number there: it is the window's height, and
   against a short catalogue it leaves Telegram's own background showing under
   the footer, which reads as a page that failed to load.

   --tg-h is the client's own figure, set by the script on every viewportChanged
   and falling back to 100vh before the first one arrives.

   After the min-width:900px block, deliberately: this padding and that block's
   body:not(.is-mini) padding-bottom:0 have equal specificity, so order is what
   decides a Mini App in a wide Telegram Desktop window. */
body.is-mini{min-height:var(--tg-h,100vh);padding-bottom:86px}
/* Telegram's clients draw a gesture bar and, on iOS, a home indicator across
   the bottom of the sheet. env() reports nothing for those inside the webview,
   so the room is taken here rather than hoped for. */
body.is-mini .fabs{padding-bottom:calc(15px + env(safe-area-inset-bottom))}

/* ── The scale inside a Mini App ───────────────────────────────────────────── */
/* A sheet is not a browser window and it should not be filled like one. The
   same two-column grid that reads well on a phone's whole screen reads as
   oversized in a sheet the reader is scanning rather than browsing — a Mini App
   is opened from a conversation, glanced at, and closed.
   So: three across, and everything around it a notch down. Nothing here changes
   the website; it applies only under the class the script adds inside Telegram. */
body.is-mini .grid{grid-template-columns:repeat(3,1fr);gap:8px}
/* Except the list look, which is one product per row by definition. */
body.is-mini .grid.is-list{grid-template-columns:1fr}
body.is-mini .tile{border-radius:10px}
body.is-mini .tile-body{padding:7px 8px 9px}
body.is-mini .tile-name{font-size:12.5px}
body.is-mini .tile-ref{font-size:11px}
body.is-mini .tile-price{font-size:13.5px}
body.is-mini .tile-meta{font-size:11px}
body.is-mini .bar-in{height:48px;gap:8px}
body.is-mini .brand img{height:26px}
body.is-mini .brand-name{font-size:15px}
body.is-mini .icon-btn{font-size:19px}
body.is-mini .page-h1{font-size:16px;margin-top:12px}
body.is-mini .toolbar{padding:9px 0}
body.is-mini .count,body.is-mini .toolbar select{font-size:13px}
body.is-mini .menu-link{font-size:14px;padding:11px 13px}
body.is-mini .fab{font-size:13px;padding:10px 6px}
body.is-mini .detail h1{font-size:17px}
body.is-mini .detail .price{font-size:20px}
body.is-mini .detail dl,body.is-mini .cta,body.is-mini .btn-alt{font-size:14.5px}
body.is-mini .policy{font-size:15px}
/* 16px is the smallest a text input can be before iOS zooms the whole page when
   it is focused, which on a sheet looks like the app jumping. It stays. */
body.is-mini .searchbar input{font-size:16px;padding:9px 11px}

/* Separated from the categories above it: it leaves the site, and a link that
   navigates away should not sit flush against the ones that filter in place. */
.menu-link-tg{margin-top:10px;border-top:1px solid var(--line,#e6e6e6);padding-top:14px}
.tg-link{text-decoration:underline}

/* ── The menu, laid out as a bar ───────────────────────────────────────────── */
/* Same element, same links, same document — a screen with room for them shows
   the menu beside the logo instead of behind a button. Nothing is duplicated,
   so there is no second copy of the navigation to fall out of step.
   :not(.is-mini) because a Telegram Mini App is a phone-shaped surface however
   wide the window it opens in is — see the script. */
@media(min-width:900px){
  body:not(.is-mini) .only-narrow{display:none}
  body:not(.is-mini) .scrim{display:none}
  body:not(.is-mini) .brand{flex:0 0 auto}
  body:not(.is-mini) .drawer{position:static;transform:none;width:auto;max-width:none;
    flex:1 1 auto;min-width:0;border:0;background:none;z-index:auto;transition:none;
    display:flex;align-items:center;gap:2px;overflow-x:auto;overflow-y:visible;
    scrollbar-width:none}
  body:not(.is-mini) .drawer::-webkit-scrollbar{display:none}
  body:not(.is-mini) .drawer-head{display:none}
  /* The price filter, laid out for a row rather than for a drawer.
     
     It had no desktop treatment at all, and the drawer it sits in becomes a
     horizontal flex row at this width — so a block form with a heading, its own
     padding and a bottom border was dropped into the header bar. It doubled the
     bar's height and pushed the category links into an overflow scroll, which
     is the header looking broken on every desktop.
     
     The heading goes because the inputs say the same thing: they are
     placeholdered من and إلى and carry aria-labels, so nothing is lost to a
     reader or to a screen reader. */
  body:not(.is-mini) .price-filter{padding:0;border-bottom:0;flex:0 0 auto;
    margin-inline-start:6px;padding-inline-start:8px;
    border-inline-start:1px solid var(--line,#e6e6e6)}
  body:not(.is-mini) .price-filter-title{display:none}
  body:not(.is-mini) .price-filter-row{gap:5px}
  body:not(.is-mini) .price-filter input{width:4.6rem;padding:6px 8px;font-size:13.5px}
  body:not(.is-mini) .price-filter button{padding:6px 11px;font-size:13.5px;white-space:nowrap}
  body:not(.is-mini) .menu-group{display:flex;align-items:center;gap:2px}
  body:not(.is-mini) .menu-link{border-bottom:0;padding:8px 11px;border-radius:9px;
    white-space:nowrap;font-size:14.5px;gap:5px}
  body:not(.is-mini) .menu-link:hover{background:var(--sk,#f2f2f2)}
  body:not(.is-mini) .menu-link.is-on{background:var(--sk,#f2f2f2)}
  /* The dividers that made sense in a vertical list are stray marks in a row. */
  body:not(.is-mini) .menu-link-tg,body:not(.is-mini) .menu-link-blog{
    margin-top:0;border-top:0;padding-top:8px}
  /* One hairline between the size collections and the supplier categories:
     they answer different questions and should not read as one list. */
  body:not(.is-mini) .menu-group+.menu-group{margin-inline-start:6px;padding-inline-start:8px;
    border-inline-start:1px solid var(--line,#e6e6e6)}
}

.grid.is-list{grid-template-columns:1fr;gap:10px}
.grid.is-list .tile{display:flex;align-items:stretch}
.grid.is-list .tile-img,.grid.is-list .tile-img-none{width:104px;flex:0 0 104px;aspect-ratio:1}
.grid.is-list .tile-body{display:flex;flex-direction:column;justify-content:center;padding:10px 12px}
@media(min-width:640px){.grid.is-list{grid-template-columns:1fr}}
@media(min-width:900px){.grid.is-list{grid-template-columns:1fr 1fr}}

.blog{padding:14px 0 40px}
.blog h1{font-size:22px;margin:6px 0 16px}
.posts{display:grid;gap:14px;grid-template-columns:1fr}
@media(min-width:700px){.posts{grid-template-columns:1fr 1fr}}
.post{display:block;border:1px solid var(--line,#e6e6e6);border-radius:12px;overflow:hidden;background:var(--card,#fff)}
.post-img{display:block;width:100%;aspect-ratio:16/10;object-fit:contain;background:var(--sk,#f2f2f2);padding:6px}
.post-body{padding:12px 13px 14px}
.post-body h2{font-size:16px;margin:0 0 6px;line-height:1.35}
.post-body p{margin:0;color:var(--muted,#666);font-size:14px}
.post-tag{display:inline-block;margin-top:9px;font-size:12px;color:var(--muted,#666);
  border:1px solid var(--line,#e6e6e6);border-radius:999px;padding:2px 9px}
.post-full h1{font-size:24px;line-height:1.35;margin:8px 0 6px}
.post-date{color:var(--muted,#666);font-size:13px;margin-bottom:14px}
.post-full .post-img{aspect-ratio:16/10;border-radius:12px;margin-bottom:14px}
/* Wider than the grid is comfortable for prose. 68 characters is about where a
   line stops being easy to track back from. */
.post-text{max-width:68ch;font-size:16.5px;line-height:1.75}
.post-text h2{font-size:19px;margin:26px 0 8px}
.post-text h3{font-size:17px;margin:22px 0 6px}
.post-text p{margin:0 0 14px}
.post-text ul{margin:0 0 14px;padding-inline-start:20px}
.post-text li{margin-bottom:6px}
.post-full .cta{max-width:68ch}
.post-join{margin-top:28px;padding:18px 16px;border:1px solid var(--line,#e6e6e6);
  border-radius:16px;background:var(--card,#fff);max-width:68ch}
.post-join h2{font-size:16.5px;margin:0 0 4px}
.post-join p{color:var(--muted,#666);font-size:13.5px;margin:0 0 12px;line-height:1.6}
.post-join-list{display:grid;gap:8px}
.post-join-list a{display:flex;align-items:center;gap:10px;padding:11px 13px;border-radius:12px;
  border:1px solid var(--line,#e6e6e6);text-decoration:none;color:inherit;font-weight:600;
  font-size:14.5px}
.post-join-list a>span:first-child{flex:0 0 30px;height:30px;display:grid;place-items:center;
  border-radius:50%;background:var(--line,#f0f0f0);font-size:15px}
.post-more{margin-top:30px}
.post-more h2{font-size:17px;margin:0 0 10px}
.blog-pager{display:flex;align-items:center;justify-content:center;gap:12px;margin-top:22px}

.groups{padding:14px 0 40px}
.groups h1{font-size:22px;margin:6px 0 8px}
.grp-lede{color:var(--muted,#666);font-size:14.5px;margin:0 0 14px;max-width:60ch}
.grp-cats{display:flex;flex-wrap:wrap;gap:7px;margin-bottom:14px}
.grp-cat{font-size:13px;border:1px solid var(--line,#e6e6e6);border-radius:999px;padding:5px 12px;
  color:var(--muted,#666);text-decoration:none;background:var(--card,#fff)}
.grp-cat.is-on{background:var(--accent,#111);color:var(--accent-fg,#fff);border-color:transparent}
.grp-list{display:grid;gap:12px;grid-template-columns:1fr}
@media(min-width:700px){.grp-list{grid-template-columns:1fr 1fr}}
.grp{display:flex;gap:12px;align-items:center;border:1px solid var(--line,#e6e6e6);
  border-radius:12px;padding:11px 12px;background:var(--card,#fff);text-decoration:none;color:inherit}
.grp-face{flex:0 0 auto;width:56px;height:56px;border-radius:12px;background:var(--sk,#f2f2f2);
  display:flex;align-items:center;justify-content:center;overflow:hidden}
.grp-img{width:100%;height:100%;object-fit:cover}
.grp-emoji{font-size:26px;line-height:1}
.grp-body{min-width:0}
.grp-body h2{font-size:15.5px;margin:0 0 3px;line-height:1.35}
.grp-body p{margin:5px 0 0;color:var(--muted,#666);font-size:13.5px;line-height:1.6}
.grp-meta{display:flex;flex-wrap:wrap;gap:5px;align-items:center;color:var(--muted,#666);font-size:12.5px}
.grp-plat{border:1px solid var(--line,#e6e6e6);border-radius:999px;padding:1px 8px}
.grp-dot{opacity:.5}
.grp-full{max-width:68ch}
.grp-head{display:flex;gap:14px;align-items:center;margin:10px 0 16px}
.grp-hero{width:84px;height:84px;border-radius:18px;object-fit:cover;background:var(--sk,#f2f2f2)}
.grp-hero-emoji{display:flex;align-items:center;justify-content:center;font-size:40px}
.grp-text{font-size:16px;line-height:1.75;margin-bottom:18px}
.grp-text p{margin:0 0 12px}
.grp-join{display:block;text-align:center;background:var(--accent,#111);color:var(--accent-fg,#fff);
  text-decoration:none;padding:14px 18px;border-radius:12px;font-weight:700;font-size:16px}
.grp-note{color:var(--muted,#666);font-size:12.5px;margin:9px 0 22px;text-align:center}
.grp-more{margin-top:26px}
.grp-more h2{font-size:17px;margin:0 0 10px}
.grp-shop{margin-top:30px;padding-top:22px;border-top:1px solid var(--line,#e6e6e6)}
.grp-shop h2{font-size:17px;margin:0 0 12px}
.grp-shop .cta{margin-top:14px}
body{--bg:#0f0f10;--fg:#f4f4f5;--card:#161618;--line:#2a2a2e;--muted:#a1a1aa;--sk:#1c1c1f;--radius:6px;--font:system-ui,-apple-system,"Segoe UI",Roboto,sans-serif}@media(prefers-color-scheme:dark){body{--bg:#0f0f10;--fg:#f4f4f5;--card:#161618;--line:#2a2a2e;--muted:#a1a1aa;--sk:#1c1c1f;--radius:6px;--font:system-ui,-apple-system,"Segoe UI",Roboto,sans-serif}}.tile,.grp,.post,.showcase-item,.grp-face{border-radius:var(--radius,12px)}body{font-family:var(--font,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif)}