/* GRAPHIX components — the §8 inventory, every state.
   Depends on tokens.css and base.css. BEM: .block__element--modifier.
   Every value comes from a token. Rendered in full at /boshqaruv/style/. */

/* ============================================================== 1. buttons */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  padding: var(--s-3) var(--s-5);
  border: 1px solid transparent; border-radius: var(--r-sm);
  font-size: var(--fs-base); font-weight: var(--fw-semibold);
  line-height: 1.2; text-align: center;
  transition: background var(--t-fast), border-color var(--t-fast), color var(--t-fast);
  min-height: 44px;                      /* tap target floor (plan §5) */
}
.btn--primary   { background: var(--c-brand); color: var(--c-brand-fg); }
.btn--primary:hover:not(:disabled)   { background: var(--c-brand-hover); }
.btn--secondary { background: var(--c-surface-2); color: var(--c-fg); border-color: var(--c-line-control); }
.btn--secondary:hover:not(:disabled) { background: var(--c-surface-3); border-color: var(--c-fg-subtle); }
.btn--ghost     { background: transparent; color: var(--c-fg); }
.btn--ghost:hover:not(:disabled)     { background: var(--c-surface-2); }
.btn--danger    { background: var(--c-danger-bg); color: var(--c-danger); border-color: var(--c-danger); }
.btn--danger:hover:not(:disabled)    { background: var(--c-danger); color: var(--c-bg); }

.btn--sm { padding: var(--s-2) var(--s-3); font-size: var(--fs-sm); min-height: 36px; }
.btn--lg { padding: var(--s-4) var(--s-6); font-size: var(--fs-lg); min-height: 56px; }
.btn--block { display: flex; width: 100%; }
.btn--icon  { padding: 0; width: 48px; min-height: 48px; }

.btn:disabled, .btn[aria-disabled="true"] {
  opacity: .45; cursor: not-allowed;
}
.btn--loading { color: transparent !important; position: relative; pointer-events: none; }
.btn--loading::after {
  content: ""; position: absolute; width: 18px; height: 18px; border-radius: 50%;
  border: 2px solid currentColor; border-top-color: transparent;
  color: var(--c-brand-fg); animation: gx-spin .7s linear infinite;
}
.btn--secondary.btn--loading::after, .btn--ghost.btn--loading::after { color: var(--c-fg); }
@keyframes gx-spin { to { transform: rotate(360deg); } }

/* =============================================================== 2. fields */
.field { display: flex; flex-direction: column; gap: var(--s-2); }
.field__hd { display: flex; justify-content: space-between; align-items: baseline; gap: var(--s-3); }

.input, .textarea, .select {
  width: 100%; padding: var(--s-3) var(--s-4);
  background: var(--c-surface); color: var(--c-fg);
  border: 1px solid var(--c-line-control); border-radius: var(--r-sm);
  font-size: var(--fs-base); min-height: 48px;
  transition: border-color var(--t-fast), background var(--t-fast);
}
.input::placeholder, .textarea::placeholder { color: var(--c-fg-hint); }
.input:hover, .textarea:hover, .select:hover { border-color: var(--c-fg-subtle); }
/* The brand border and lifted ground are the focus treatment under a mouse.
   The brass ring is NOT removed for keyboard focus: a field that only changes
   its 1px border is not enough to navigate by, and every text field on the site
   was losing the ring that every button kept. */
.input:focus, .textarea:focus, .select:focus { border-color: var(--c-brand); background: var(--c-surface-2); }
.input:focus:not(:focus-visible), .textarea:focus:not(:focus-visible),
.select:focus:not(:focus-visible) { outline: none; }
.textarea { min-height: 120px; resize: vertical; line-height: var(--lh-normal); }
.select { appearance: none; padding-right: var(--s-7);
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: right 22px center, right 16px center;
  background-size: 6px 6px, 6px 6px; background-repeat: no-repeat; }
.input:disabled, .textarea:disabled, .select:disabled { opacity: .5; cursor: not-allowed; }
/* The review form put the site's first file input on a page, and the browser
   draws its own grey button inside it — the one control anywhere on the site
   that did not look like the site. `::file-selector-button` restyles the real
   control rather than replacing it, so the field still works with JavaScript
   off, still opens the system picker, and still announces itself properly. */
.input[type="file"] { padding: var(--s-2); align-items: center; }
.input[type="file"]::file-selector-button {
  font: inherit; font-size: var(--fs-sm); font-weight: var(--fw-semibold);
  margin-right: var(--s-3); padding: var(--s-2) var(--s-4);
  background: var(--c-surface-3); color: var(--c-fg);
  border: 1px solid var(--c-line-control); border-radius: var(--r-sm);
  cursor: pointer; transition: border-color var(--t-fast), background var(--t-fast);
}
.input[type="file"]:hover::file-selector-button { border-color: var(--c-fg-subtle); }

/* A file field that speaks the page's language: a button-styled label over
   a visually hidden input, and the name of what was chosen beside it. The
   browser's own button says "Choose File" in the browser's language, whatever
   the page's (§18 #29). On one line it is as tall as a text field, so the
   captions of a row of fields line up. The hidden input still takes the
   keyboard, so the ring is drawn on the button it hides behind. */
.field__file { display: flex; align-items: center; gap: var(--s-3);
  flex-wrap: wrap; min-height: 44px; }
.field__file:has(input:focus-visible) .btn { outline: 2px solid var(--c-brand);
  outline-offset: 2px; }

.field--error .input, .field--error .textarea, .field--error .select { border-color: var(--c-danger); }
.field__error { color: var(--c-danger); font-size: var(--fs-sm); }
.field__hint  { color: var(--c-fg-muted); font-size: var(--fs-sm); }

/* The required mark. One character, in the colour the form already uses to
   mean "this needs your attention", against a label that is small, uppercase
   and muted — so it reads without a legend beside every field. It is bumped
   off the baseline rather than set as a superscript: `vertical-align: super`
   shifts the line box and would push a marked label a pixel above its
   unmarked neighbours, which is visible in a column of fields.

   It is always `aria-hidden` in the markup. A screen reader is told a field
   is required by `required` or `aria-required`; without hiding it, the
   listener hears the label, then "star", then "required". */
.req { color: var(--c-danger); font-weight: var(--fw-semibold);
  position: relative; top: -0.15em; }

.check { display: flex; align-items: flex-start; gap: var(--s-3); cursor: pointer; }
.check input { appearance: none; width: 22px; height: 22px; flex: none; margin-top: 1px;
  background: var(--c-surface); border: 1px solid var(--c-line-control); border-radius: var(--r-sm);
  transition: background var(--t-fast), border-color var(--t-fast); }
.check input[type="radio"] { border-radius: 50%; }
.check input:checked { background: var(--c-brand); border-color: var(--c-brand); }
.check input:checked::after { content: ""; display: block; width: 100%; height: 100%;
  background: var(--c-brand-fg); clip-path: polygon(19% 52%, 38% 71%, 81% 28%, 88% 37%, 38% 86%, 12% 60%); }
.check input[type="radio"]:checked::after { clip-path: circle(26% at 50% 50%); }

.stepper { display: inline-flex; align-items: center; border: 1px solid var(--c-line-control);
  border-radius: var(--r-sm); background: var(--c-surface); }
.stepper button { width: 44px; height: 44px; display: grid; place-items: center; color: var(--c-fg); }
.stepper button:hover:not(:disabled) { background: var(--c-surface-3); }
.stepper button:disabled { color: var(--c-fg-subtle); cursor: not-allowed; }
.stepper__val { min-width: 44px; text-align: center; font-weight: var(--fw-semibold);
  font-variant-numeric: tabular-nums; }
/* As an <input> it would otherwise take the browser's white default, which reads
   as a broken white bar on this ground. The stepper's own border is the frame. */
input.stepper__val { width: 52px; padding: var(--s-2) 0; background: none; border: 0;
  color: inherit; }
input.stepper__val:focus:not(:focus-visible) { outline: none; }
.stepper:focus-within { border-color: var(--c-brand); }
input.stepper__val:disabled { color: var(--c-fg-subtle); }

.otp { display: flex; gap: var(--s-2); }
.otp input { width: 52px; height: 62px; text-align: center; font-size: var(--fs-2xl);
  font-weight: var(--fw-semibold); background: var(--c-surface); color: var(--c-fg);
  border: 1px solid var(--c-line-control); border-radius: var(--r-sm); font-variant-numeric: tabular-nums; }
.otp input:focus { border-color: var(--c-brand); }
.otp input:focus:not(:focus-visible) { outline: none; }

.search { display: flex; align-items: center; gap: var(--s-2); width: 100%;
  padding: 0 var(--s-4); background: var(--c-surface); border: 1px solid var(--c-line-control);
  border-radius: var(--r-full); min-height: 44px; color: var(--c-fg-muted); }
/* The input inside the pill is borderless and has `outline: none`, so the
   keyboard ring belongs on the pill: drawn here it frames the whole control
   instead of a bare field, and every other control on the page keeps a ring
   this one was missing. */
.search:focus-within { border-color: var(--c-brand); color: var(--c-fg);
  outline: 2px solid var(--c-brand); outline-offset: 2px; }
.search input { flex: 1; background: none; border: 0; outline: none; min-width: 0; }

/* ========================================================= 3. product card */
/* The card is the positioned ancestor the heart floats against. It cannot be
   .card__media: the heart is a form, a form inside an anchor is invalid HTML,
   and the whole media block lives inside the card's link. */
.card { position: relative; }
.card__media { position: relative; aspect-ratio: var(--ar-product); overflow: hidden;
  background: var(--c-surface); border: 1px solid var(--c-line); border-radius: var(--r-sm);
  margin-bottom: var(--s-3); }
/* Pinned, so the photograph cannot size the card: every card in a grid has to
   be the same height whatever shape its image happens to be. */
.card__media img { position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
  transition: transform var(--t-slow) var(--ease-out); }
.card a:hover .card__media img { transform: scale(1.035); }
.card__name { font-family: var(--f-display); font-weight: var(--fw-semibold); font-size: var(--fs-lg); }
.card__price{ font-size: var(--fs-sm); color: var(--c-fg-muted); font-variant-numeric: tabular-nums;
  margin-top: 2px; }
.card__badge{ position: absolute; top: var(--s-2); left: var(--s-2); }
.card__meta { margin-top: var(--s-3); }

/* ======================================================= 5. size selector */
.sizes { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.sizes__btn { min-width: 60px; padding: var(--s-3) var(--s-2); background: var(--c-surface);
  color: var(--c-fg); border: 1px solid var(--c-line-control); border-radius: var(--r-sm);
  font-weight: var(--fw-semibold); min-height: 48px;
  transition: border-color var(--t-fast), background var(--t-fast), color var(--t-fast); }
.sizes__btn:hover:not(:disabled) { border-color: var(--c-fg-muted); }
.sizes__btn[aria-pressed="true"] { background: var(--c-brand); color: var(--c-brand-fg);
  border-color: var(--c-brand); }
.sizes__btn:disabled { background: transparent; color: var(--c-fg-subtle);
  border-color: var(--c-line); text-decoration: line-through;
  text-decoration-thickness: 1px; cursor: not-allowed; }

/* ============================================================ 6. spec strip */
.spec__row { display: flex; justify-content: space-between; gap: var(--s-5);
  padding: var(--s-3) 0; border-top: 1px solid var(--c-line); font-size: var(--fs-sm); }
/* The global reset makes every svg display:block, so an icon inside this
   block-level dt took its own line and left the label sitting under it. Flex
   puts them back on one line, which is what every other icon+label pair does. */
.spec__row dt { display: flex; align-items: center; gap: var(--s-2);
  color: var(--c-fg-muted); }
.spec__row dd { font-weight: var(--fw-semibold); text-align: right; }
/* A second, smaller line under a label: the delivery time under its name
   on the product page (§17 #301). Muted, like the label it belongs to. */
.spec__sub { display: block; font-size: var(--fs-xs); }

/* =========================================================== 7. star rating */
.stars { display: inline-flex; gap: 2px; color: var(--c-brand); }
.stars .i { width: 17px; height: 17px; fill: currentColor; stroke: none; }
.stars__empty { color: var(--c-fg-subtle); }
.rating { display: inline-flex; align-items: center; gap: var(--s-2); font-size: var(--fs-sm); }
.rating__count { color: var(--c-fg-muted); }
/* Input: radios reversed so ~ can light the stars to the left of the hovered one */
/* `align-self` because `display: inline-flex` does not survive a flex parent:
   `.field` is a column flex container, which blockifies its children, so the
   row stretched to the full field width and `row-reverse` then packed the five
   stars against the RIGHT edge — a rating control that looked misplaced rather
   than designed. Pinned to the start, it shrink-wraps again. */
.stars--input { display: inline-flex; flex-direction: row-reverse;
  align-self: start; }
.stars--input input { position: absolute; opacity: 0; width: 0; }
/* 26 px of star inside a 44 px target. The star stays the size it was drawn;
   the box around it is the project's floor (§8), because these five labels ARE
   the control — the radios are 0x0 — and five 30 px targets in a row is a
   rating somebody gives by accident. The panel's own sweep found this only
   after the tap-target check was taught to measure the label rather than the
   invisible radio inside it; §17 #127 is the same defect on the link that
   scrolls to this section. */
.stars--input label { display: inline-grid; place-items: center;
  min-width: 44px; min-height: 44px; color: var(--c-fg-subtle); cursor: pointer; }
.stars--input label .i { fill: currentColor; stroke: none; width: 26px; height: 26px; }
.stars--input input:checked ~ label,
.stars--input label:hover, .stars--input label:hover ~ label { color: var(--c-brand); }
/* The input is 0x0 and transparent, so a keyboard user tabbing into the row
   would otherwise have nothing to look at. The ring goes on the label, which
   is the thing they can actually see. */
.stars--input input:focus-visible + label { outline: 2px solid var(--c-brand);
  outline-offset: 2px; border-radius: var(--r-sm); }

/* Both shells use it — the storefront's and the panel's — which is why it
   is a component and not a page style. It was in pages.css, which the
   panel does not load, so the panel showed it as an ordinary link above
   the header on every screen. */
/* ------------------------------------------------------------- skip link */
/* First focusable element on every page, so a keyboard user can jump the nav. */
.skip {
  position: absolute;
  left: var(--s-4);
  top: -100%;
  z-index: calc(var(--z-drawer) + 1);
  padding: var(--s-3) var(--s-4);
  background: var(--c-brand);
  color: var(--c-brand-fg);
  font-weight: var(--fw-semibold);
  border-radius: 0 0 var(--r-md) var(--r-md);
}
.skip:focus { top: 0; }

/* ============================================================ 8. review card */
.review { padding: var(--s-5) 0; border-top: 1px solid var(--c-line); }
.review__hd { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap;
  margin-bottom: var(--s-2); }
.review__who { font-weight: var(--fw-semibold); }
.review__date{ color: var(--c-fg-hint); font-size: var(--fs-sm); margin-left: auto; }
.review__text{ color: var(--c-fg-muted); }
/* A photograph that opens full screen is a control, and viewer.js makes it
   one - the keyboard, a role and a name (§18 #24). The pointer belongs here
   rather than in an inline style written by that script: a cursor is a design
   value and design values live in CSS (§4). */
.zoomable { cursor: zoom-in; }

.review__pics{ display: flex; gap: var(--s-2); margin-top: var(--s-3); }
.review__pics img { width: 72px; height: 72px; object-fit: cover; border-radius: var(--r-sm);
  border: 1px solid var(--c-line); }

/* ============================================== 9. heart / share / badges */
.like { display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  color: var(--c-fg); padding: var(--s-2); border-radius: var(--r-sm);
  min-width: 44px; min-height: 44px; transition: color var(--t-fast); }
.like:hover { color: var(--c-brand); }
.like[aria-pressed="true"] { color: var(--c-brand); }
.like[aria-pressed="true"] .i { fill: currentColor; }
.like__count { font-size: var(--fs-sm); font-variant-numeric: tabular-nums; }
/* The floating heart on a card. It is the <form>, not the button: the button
   inside keeps .like's own sizing and pressed state, and z-index puts the whole
   thing above the card's link so the tap lands on the heart. */
.like--float { position: absolute; top: var(--s-2); right: var(--s-2); z-index: 1;
  background: color-mix(in srgb, var(--c-bg) 66%, transparent);
  border-radius: var(--r-full); backdrop-filter: blur(4px); }
.like--float:hover { background: var(--c-brand); color: var(--c-brand-fg); }
/* A count of zero is hidden rather than absent, so the toggle can reveal it
   without rebuilding the button. */
.like__count[hidden] { display: none; }
/* While the toggle is in flight the heart is not clickable, but it must not
   look disabled either — it has already changed to the state it is confirming. */
.like[aria-busy="true"] { pointer-events: none; }

/* Share. On a phone the button hands off to navigator.share and this menu is
   never built; on desktop it is the whole control, so it is anchored to the
   button rather than floated over the page. */
.share { position: relative; }
.share__menu { position: absolute; right: 0; top: calc(100% + var(--s-2));
  z-index: var(--z-drawer); min-width: 220px; padding: var(--s-2);
  display: flex; flex-direction: column;
  background: var(--c-surface-2); border: 1px solid var(--c-line-strong);
  border-radius: var(--r-md); box-shadow: var(--sh-lg); }
.share__menu[hidden] { display: none; }
.share__menu > * { display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-3); min-height: 44px; border-radius: var(--r-sm);
  color: var(--c-fg); text-align: left; white-space: nowrap; }
.share__menu > *:hover { background: var(--c-surface-3); }

.badge { display: inline-flex; align-items: center; gap: 6px; padding: 4px var(--s-2);
  border-radius: var(--r-full); font-size: var(--fs-xs); font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-wide); text-transform: uppercase;
  background: var(--c-surface-2); color: var(--c-fg-muted); border: 1px solid var(--c-line-strong); }
.badge--brand   { background: var(--c-brand-bg); color: var(--c-brand);   border-color: var(--c-brand); }
.badge--success { background: var(--c-success-bg); color: var(--c-success); border-color: var(--c-success); }
.badge--warning { background: var(--c-warning-bg); color: var(--c-warning); border-color: var(--c-warning); }
.badge--danger  { background: var(--c-danger-bg);  color: var(--c-danger);  border-color: var(--c-danger); }
.badge--info    { background: var(--c-info-bg);    color: var(--c-info);    border-color: var(--c-info); }

/* ============================================ 9b. full-screen viewer */
/* One overlay serves the size chart and the product photograph. The stage is
   an ordinary scroll container: `touch-action: pinch-zoom` hands zooming to
   the browser, which does it better than any transform we would write and
   costs nothing. */
.viewer { position: fixed; inset: 0; z-index: var(--z-modal);
  background: var(--c-bg); display: flex; flex-direction: column; }
.viewer[hidden] { display: none; }
.viewer__close { position: absolute; top: var(--s-3); right: var(--s-3);
  z-index: 1; width: 48px; height: 48px; display: grid; place-items: center;
  color: var(--c-fg); background: var(--c-surface-2);
  border: 1px solid var(--c-line-control); border-radius: var(--r-full); }
.viewer__close:hover { background: var(--c-surface-3); }
.viewer__stage { flex: 1; overflow: auto; touch-action: pinch-zoom;
  padding: var(--s-7) var(--s-4) var(--s-5);
  display: flex; flex-direction: column; align-items: center;
  gap: var(--s-5); }
.viewer__stage img { max-width: 100%; height: auto; border-radius: var(--r-sm); }
.viewer__stage > * { width: 100%; max-width: 900px; }

/* The chart block the viewer clones. Present in the document so the viewer has
   real markup to show, and hidden until it is asked for. */
.sizeguide[hidden] { display: none; }
.sizeguide__table tr.is-current th,
.sizeguide__table tr.is-current td { color: var(--c-brand); font-weight: var(--fw-bold); }
.sizeguide__table tr.is-current th { box-shadow: inset 3px 0 0 var(--c-brand); }

/* ================================================ 10. modal / mobile sheet */
.overlay { position: fixed; inset: 0; z-index: var(--z-modal);
  background: var(--c-scrim); display: grid; place-items: end center; }
.modal { width: 100%; max-width: 560px; max-height: 88vh; overflow-y: auto;
  background: var(--c-surface); border: 1px solid var(--c-line-strong);
  border-radius: var(--r-lg) var(--r-lg) 0 0; box-shadow: var(--sh-lg); }
.modal__hd { display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-4) var(--s-5); border-bottom: 1px solid var(--c-line); position: sticky; top: 0;
  background: var(--c-surface); }
.modal__hd h2 { font-family: var(--f-display); font-size: var(--fs-xl); font-weight: var(--fw-semibold); }
.modal__close { margin-left: auto; color: var(--c-fg-muted); padding: var(--s-2); }
.modal__close:hover { color: var(--c-fg); }
.modal__body { padding: var(--s-5); }
@media (min-width: 720px) {
  .overlay { place-items: center; }
  .modal { border-radius: var(--r-lg); }
}

/* =============================================================== 11. toast */
.toasts { position: fixed; z-index: var(--z-toast); left: var(--s-4); right: var(--s-4);
  bottom: var(--s-4); display: flex; flex-direction: column; gap: var(--s-2); pointer-events: none; }
.toast { display: flex; align-items: center; gap: var(--s-3); padding: var(--s-3) var(--s-4);
  background: var(--c-surface-2); color: var(--c-fg); border: 1px solid var(--c-line-strong);
  border-left: 3px solid var(--c-brand); border-radius: var(--r-sm); box-shadow: var(--sh-md);
  pointer-events: auto; animation: gx-rise var(--t-base) var(--ease-out); }
.toast--success { border-left-color: var(--c-success); }
.toast--danger  { border-left-color: var(--c-danger); }
@keyframes gx-rise { from { opacity: 0; transform: translateY(8px); } }
@media (min-width: 720px) { .toasts { left: auto; width: 380px; } }

/* ========================================== 12. breadcrumb / pagination */
.crumbs { display: flex; flex-wrap: wrap; gap: var(--s-2); font-size: var(--fs-sm);
  color: var(--c-fg-muted); }
.crumbs a:hover { color: var(--c-fg); }
.crumbs li:not(:last-child)::after { content: "/"; margin-left: var(--s-2); color: var(--c-fg-subtle); }

.pager { display: flex; gap: var(--s-2); align-items: center; justify-content: center; }
.pager a, .pager span { min-width: 44px; min-height: 44px; display: grid; place-items: center;
  padding: 0 var(--s-3); border: 1px solid var(--c-line-strong); border-radius: var(--r-sm);
  color: var(--c-fg-muted); font-variant-numeric: tabular-nums; }
.pager a:hover { color: var(--c-fg); border-color: var(--c-fg-subtle); }
.pager .is-current { background: var(--c-fg); color: var(--c-bg); border-color: var(--c-fg); }
.pager .is-gap { border: 0; min-width: auto; }

/* ================================================ 13. empty / skeleton */
.empty { text-align: center; padding: var(--s-9) var(--s-4); }
.empty .i { width: 40px; height: 40px; margin: 0 auto var(--s-4); color: var(--c-fg-subtle); }
.empty h3 { font-family: var(--f-display); font-size: var(--fs-2xl); margin-bottom: var(--s-2); }
.empty p { color: var(--c-fg-muted); max-width: 44ch; margin: 0 auto var(--s-5); }

.skel { background: linear-gradient(90deg, var(--c-surface) 25%, var(--c-surface-2) 50%, var(--c-surface) 75%);
  background-size: 200% 100%; animation: gx-shimmer 1.3s linear infinite; border-radius: var(--r-sm); }
.skel--media { aspect-ratio: var(--ar-product); }
.skel--line  { height: 12px; margin-top: var(--s-2); }
.skel--line.is-short { width: 45%; }
@keyframes gx-shimmer { to { background-position: -200% 0; } }

/* ========================================================== 14. data table */
.table th, .table td { padding: var(--s-3) var(--s-4); text-align: left;
  border-bottom: 1px solid var(--c-line); }
.table th { font-size: var(--fs-xs); letter-spacing: var(--ls-caps); text-transform: uppercase;
  color: var(--c-fg-muted); font-weight: var(--fw-semibold); }
.table .num { text-align: right; }
/* Stacks into cards on mobile — every cell carries its label in data-label. */
@media (max-width: 719px) {
  .table, .table tbody, .table tr, .table td { display: block; width: 100%; }
  .table thead { display: none; }
  .table tr { border: 1px solid var(--c-line); border-radius: var(--r-sm);
    margin-bottom: var(--s-3); padding: var(--s-2) var(--s-3); }
  .table td { display: flex; justify-content: space-between; gap: var(--s-4);
    border: 0; padding: var(--s-2) 0; }
  .table td::before { content: attr(data-label); color: var(--c-fg-muted); font-size: var(--fs-sm); }
  .table .num { text-align: right; }
}

/* ====================================================== 15. filters / chips */
.chips { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.chips button { padding: var(--s-2) var(--s-4); min-height: 40px; border-radius: var(--r-full);
  border: 1px solid var(--c-line-control); color: var(--c-fg-muted); font-size: var(--fs-sm);
  font-weight: var(--fw-semibold); transition: color var(--t-fast), border-color var(--t-fast); }
.chips button:hover { color: var(--c-fg); border-color: var(--c-fg-muted); }
.chips button[aria-pressed="true"] { background: var(--c-fg); color: var(--c-bg); border-color: var(--c-fg); }

.filters__group { padding: var(--s-4) 0; border-bottom: 1px solid var(--c-line); }
.filters__group > .label { display: block; margin-bottom: var(--s-3); }

/* ==================================================== 16. pickup picker */
/* List-first, map-second: the list is the control, the map is confirmation
   (§17 #22). Everything here must work with the map absent. */
.picker { display: grid; gap: var(--s-4); }
.picker__list { max-height: 340px; overflow-y: auto; border: 1px solid var(--c-line);
  border-radius: var(--r-sm); background: var(--c-surface); }
.picker__item { display: block; width: 100%; text-align: left; padding: var(--s-3) var(--s-4);
  border-bottom: 1px solid var(--c-line); }
.picker__item:last-child { border-bottom: 0; }
.picker__item:hover { background: var(--c-surface-2); }
.picker__item[aria-pressed="true"] { background: var(--c-brand-bg); box-shadow: inset 3px 0 0 var(--c-brand); }
.picker__name { display: block; font-weight: var(--fw-semibold); }
.picker__meta { display: block; font-size: var(--fs-sm); color: var(--c-fg-muted); }
/* A heading inside the list: Tumanlar, then Shaharlar. The reason these are
   drawers rather than native selects — a <select> cannot group, and cannot
   carry a search field only where one is warranted (§17 #87). */
.picker__group { position: sticky; top: 0; z-index: 1;
  padding: var(--s-2) var(--s-4); background: var(--c-surface);
  font-size: var(--fs-xs); font-weight: var(--fw-semibold);
  letter-spacing: var(--ls-caps); text-transform: uppercase;
  color: var(--c-fg-hint); border-bottom: 1px solid var(--c-line); }

/* The button a drawerised <select> becomes. It keeps .select's own look so the
   two fields do not read as different kinds of control. */
.select--drawer { display: flex; align-items: center; text-align: left;
  padding-right: var(--s-7); }
.select--drawer.is-empty { color: var(--c-fg-hint); }

/* The native select the drawer replaced. It stays in the document — it is
   still what submits, and a screen reader should still meet a real select —
   but it must take no space.

   `.sr-only` on its own is not enough here: `.select` also sets a width, the
   two tie on specificity, and the "hidden" select ended up 1 425 px wide and
   absolutely positioned, which put 144 px of horizontal scroll on a page that
   is supposed to have none. §17 #72 in a new costume — a rule that exists to
   be overridden must not be able to win — so this one is written specifically
   enough to settle it. */
.select.sr-only { width: 1px; height: 1px; min-height: 0; padding: 0;
  border: 0; clip-path: inset(50%); }

/* The drawer itself: a sheet on a phone, a centred dialog from 720 px. */
.picker-drawer { position: fixed; inset: 0; z-index: var(--z-modal);
  background: var(--c-scrim); display: grid; place-items: end stretch; }
.picker-drawer[hidden] { display: none; }
.picker-drawer__panel { display: flex; flex-direction: column; gap: var(--s-3);
  max-height: 86vh; padding: var(--s-4);
  background: var(--c-surface); border-top: 1px solid var(--c-line-strong);
  border-radius: var(--r-lg) var(--r-lg) 0 0; box-shadow: var(--sh-lg); }
.picker-drawer__hd { display: flex; align-items: center; gap: var(--s-3); }
.picker-drawer__hd h2 { margin-right: auto; }
.picker-drawer .picker__list { max-height: none; flex: 1; }
@media (min-width: 720px) {
  .picker-drawer { place-items: center; padding: var(--s-5); }
  .picker-drawer__panel { width: min(560px, 100%); border-radius: var(--r-lg);
    border: 1px solid var(--c-line-strong); }
}

/* The line the dropped pin resolves to, under the map, and the way back into
   the fields it summarises (§17 #252). A button and not a link: it opens a
   sheet on this page, it does not go anywhere. Full width and 44 px tall
   because it is the only way to reach four fields — a chevron on the right,
   the way every row that opens something on this site is drawn. */
.addr { display: flex; align-items: center; gap: var(--s-3);
  width: 100%; min-height: 44px; padding: var(--s-3) var(--s-4);
  text-align: start; color: var(--c-fg);
  background: var(--c-surface); border: 1px solid var(--c-line-control);
  border-radius: var(--r-md); cursor: pointer;
  transition: border-color var(--t-fast); }
.addr:hover { border-color: var(--c-fg-subtle); }
.addr[hidden] { display: none; }
.addr__body { display: flex; flex-direction: column; gap: var(--s-1);
  min-width: 0; margin-right: auto; }
/* Wraps rather than truncates. An address cut off mid-street is exactly the
   thing somebody opened this row to check. */
.addr__text { overflow-wrap: anywhere; }
.addr .i { flex: none; color: var(--c-fg-muted); }

/* The sheet is `.picker-drawer`, the component the region and district
   already open (§17 #87) — one bottom sheet on this page, not two that drift
   apart. Only its body differs: real fields rather than a list of choices. */
.addr-sheet__body { overflow-y: auto; }

/* The map, on the home half of checkout only. It is never rendered when the
   API key is blank, so there is no grey box to explain (§17 #93). */
.map { display: flex; flex-direction: column; gap: var(--s-3); }
.map[hidden] { display: none; }
.map__canvas { aspect-ratio: 4 / 3; max-height: 360px;
  background: var(--c-surface); border: 1px solid var(--c-line-strong);
  border-radius: var(--r-md); overflow: hidden; }

.picker__map { aspect-ratio: 4 / 3; background: var(--c-surface); border: 1px solid var(--c-line);
  border-radius: var(--r-sm); display: grid; place-items: center; color: var(--c-fg-subtle); }
@media (min-width: 860px) { .picker { grid-template-columns: 380px minmax(0, 1fr); } }

/* ================================================== 17. tabs / accordion */
.tabs { display: flex; gap: var(--s-5); border-bottom: 1px solid var(--c-line); }
.tabs button { padding: var(--s-3) 0; color: var(--c-fg-muted); font-weight: var(--fw-semibold);
  border-bottom: 2px solid transparent; margin-bottom: -1px; }
.tabs button:hover { color: var(--c-fg); }
.tabs button[aria-selected="true"] { color: var(--c-fg); border-bottom-color: var(--c-brand); }

.acc__item { border-bottom: 1px solid var(--c-line); }
.acc__btn { display: flex; width: 100%; align-items: center; justify-content: space-between;
  gap: var(--s-4); padding: var(--s-4) 0; font-weight: var(--fw-semibold); text-align: left; }
.acc__btn .i { transition: transform var(--t-base); color: var(--c-fg-muted); }
.acc__btn[aria-expanded="true"] .i { transform: rotate(180deg); }
.acc__panel { padding-bottom: var(--s-4); color: var(--c-fg-muted); }

/* =============================================== 18. nav / drawer / footer */
/* The bar is full-bleed but its contents line up with `.wrap`: without the max()
   the logo sat 80 px to the left of the page's own left edge at 1440, which is
   the sort of misalignment nobody can name but everybody sees. */
.nav { position: sticky; top: 0; z-index: var(--z-sticky);
  display: flex; align-items: center; gap: var(--s-4);
  /* Pinned to the token so that anything scrolling a target clear of the
     header can trust the token instead of guessing. It is the height the
     header already had; this only stops the two drifting apart. */
  min-height: var(--nav-h);
  padding-block: var(--s-3);
  padding-inline: max(var(--pad-x), calc((100% - var(--container)) / 2 + var(--pad-x)));
  border-bottom: 1px solid var(--c-line);
  background: color-mix(in srgb, var(--c-bg) 94%, transparent); backdrop-filter: blur(8px); }
.nav__brand { display: flex; align-items: center; gap: var(--s-2);
  min-height: 44px; }   /* the logo is a link, so it is a tap target too */
.nav__brand .i { color: var(--c-fg); }
.nav__brand span { font-family: var(--f-display); font-weight: var(--fw-bold);
  font-size: var(--fs-lg); letter-spacing: .10em; }
.nav__links { display: none; gap: var(--s-5); margin-left: var(--s-6); color: var(--c-fg-muted); }
.nav__links a:hover, .nav__links .is-active { color: var(--c-fg); }
.nav__actions { margin-left: auto; display: flex; gap: var(--s-1); }
:where(.nav__actions) :where(button, a) { padding: var(--s-2); border-radius: var(--r-sm);
  display: grid; place-items: center; position: relative;
  /* The icon stays 20px; the box around it is the tap target. */
  min-width: 44px; min-height: 44px; }
:where(.nav__actions) :where(button, a):hover { background: var(--c-surface-2); }
/* `:where()` on purpose. As `.nav__actions button` this rule scored (0,2,1) and
   silently beat every `display: none` written for an individual control, so the
   burger and the search icon showed on desktop next to the links and the field
   they replace, and the account icon showed on mobile next to the drawer that
   already holds it. Zero specificity puts those rules back in charge. */
.nav__count { position: absolute; top: 2px; right: 0; background: var(--c-brand);
  color: var(--c-brand-fg); font-size: 10px; font-weight: var(--fw-bold); line-height: 1;
  padding: 2px 4px; border-radius: var(--r-full); }
@media (min-width: 860px) { .nav__links { display: flex; } .nav__burger { display: none; } }

.drawer { position: fixed; inset: 0 0 0 auto; width: min(84vw, 340px); z-index: var(--z-drawer);
  background: var(--c-surface); border-left: 1px solid var(--c-line-strong);
  padding: var(--s-5); transform: translateX(100%);
  transition: transform var(--t-base) var(--ease-out); }
.drawer.is-open { transform: none; }
/* Pulled back by its own overhang so the icon still lines up with the
   drawer's padding edge while the target around it is full size. */
.drawer__hd button { width: 44px; height: 44px; display: grid; place-items: center;
  margin: calc(var(--s-3) * -1) calc(var(--s-3) * -1) calc(var(--s-3) * -1) 0; }
.drawer a { display: block; padding: var(--s-3) 0; border-bottom: 1px solid var(--c-line);
  font-size: var(--fs-lg); }

/* No horizontal padding here: the footer's contents are already inside a
   `.wrap`, and setting `--pad-x` on both indented the footer past every other
   block on the page. */
.foot { border-top: 1px solid var(--c-line); padding-block: var(--s-7) var(--s-6);
  color: var(--c-fg-muted); font-size: var(--fs-sm); }
.foot__grid { display: grid; gap: var(--s-5); grid-template-columns: 1fr 1fr; }
.foot h2 { font-family: var(--f-display); color: var(--c-fg); font-size: var(--fs-base);
  font-weight: var(--fw-semibold); margin-bottom: var(--s-3); }
.foot li { margin-bottom: var(--s-2); }
.foot a:hover { color: var(--c-fg); }
.foot__bot { display: flex; flex-wrap: wrap; gap: var(--s-3); justify-content: space-between;
  margin-top: var(--s-6); padding-top: var(--s-4); border-top: 1px solid var(--c-line);
  font-size: var(--fs-xs); color: var(--c-fg-hint); }
@media (min-width: 720px) { .foot__grid { grid-template-columns: repeat(4, 1fr); } }

/* ================================================= 19. language switcher */
.lang { display: inline-flex; border: 1px solid var(--c-line-control); border-radius: var(--r-sm);
  overflow: hidden; }
/* One <form> per language, because each button posts a different destination
   (§17 #115) — `display: contents` keeps the buttons as the flex children the
   rule below is written for, so the wrapper is invisible to the layout. */
.lang form { display: contents; }
.lang button { padding: var(--s-2) var(--s-3); font-size: var(--fs-sm); font-weight: var(--fw-semibold);
  color: var(--c-fg-muted); border-right: 1px solid var(--c-line-control); min-height: 40px; }
.lang form:last-child button { border-right: 0; }
.lang button[aria-pressed="true"] { background: var(--c-surface-3); color: var(--c-fg); }

/* The header's copy: two-letter codes in a pill, the active one filled. There
   is room for six characters beside the cart and the account icon and not for
   "Oʻzbekcha", and a language control somebody has to scroll to the bottom of
   the page for is a language control they do not find.
   Hidden below the desktop breakpoint, where the drawer carries it instead:
   the header row already holds five controls at 390 px.

   The active code is a circle, and it has to be one by construction rather
   than by arithmetic: a minimum width plus horizontal padding gave "uz" a box
   wider than it was tall, so the fill read as an oval sitting in a pill. A
   fixed square with no padding and the minimums cleared is round at every font
   size, and the container gains its own padding so the circle sits inside the
   pill instead of touching its border. */
.lang--compact { display: none; border-radius: var(--r-full); border-color: var(--c-line);
  padding: var(--s-1); gap: var(--s-1); }
@media (min-width: 860px) { .lang--compact { display: inline-flex; } }
.lang--compact button { display: inline-flex; align-items: center; justify-content: center;
  flex: none; width: 34px; height: 34px; min-width: 0; min-height: 0; padding: 0;
  border-right: 0; border-radius: var(--r-full);
  font-size: var(--fs-sm); letter-spacing: .04em; }
.lang--compact button:hover { color: var(--c-fg); }
.lang--compact button[aria-pressed="true"] { background: var(--c-fg);
  color: var(--c-bg); font-weight: var(--fw-bold); }

/* ================================================= 20. messages / notices */
.msg { display: flex; gap: var(--s-3); padding: var(--s-3) var(--s-4); border-radius: var(--r-sm);
  border: 1px solid var(--c-line-strong); background: var(--c-surface-2); font-size: var(--fs-sm); }
.msg--ok   { border-color: var(--c-success); background: var(--c-success-bg); color: var(--c-success); }
.msg--warn { border-color: var(--c-warning); background: var(--c-warning-bg); color: var(--c-warning); }
.msg--err  { border-color: var(--c-danger);  background: var(--c-danger-bg);  color: var(--c-danger); }

/* ============================================ 21. product grid / sticky bar */
.grid-products { display: grid; grid-template-columns: repeat(2, 1fr);
  gap: var(--s-5) var(--s-3); }
@media (min-width: 720px) { .grid-products { grid-template-columns: repeat(3, 1fr); gap: var(--s-6) var(--s-4); } }
@media (min-width: 860px) { .grid-products { grid-template-columns: repeat(4, 1fr); } }

.buybar { position: fixed; left: 0; right: 0; bottom: 0; z-index: var(--z-sticky);
  display: flex; align-items: center; gap: var(--s-3); padding: var(--s-3) var(--pad-x);
  background: color-mix(in srgb, var(--c-bg) 96%, transparent);
  border-top: 1px solid var(--c-line); backdrop-filter: blur(10px); }
.buybar__price { display: flex; flex-direction: column; line-height: var(--lh-snug); }
.buybar__price b { font-size: var(--fs-base); font-weight: var(--fw-bold); font-variant-numeric: tabular-nums; }
.buybar__price span { font-size: var(--fs-xs); color: var(--c-fg-muted); }
.buybar .btn { margin-left: auto; }
@media (min-width: 860px) { .buybar { display: none; } }

/* ===================================================== 19. touch sizing
   Plan §9 sets the floor at 44 x 44. Most controls already meet it; these are
   the ones that did not, and they are corrected only where a finger is the
   likely input. A 36 px button is fine under a mouse and wrong under a thumb,
   so the desktop sizes are left alone rather than inflated to match. */
@media (max-width: 720px), (pointer: coarse) {
  /* Filter, sort and the account page's ghost buttons. */
  .btn--sm { min-height: 44px; }

  .lang button { min-height: 44px; }
  .lang--compact button { min-width: 44px; min-height: 44px; }

  /* The whole label is the target for a checkbox or radio, not the 22 px box. */
  .check { min-height: 44px; }

  /* Footer links were 18 px tall on a 26 px pitch - three of them fit inside one
     fingertip. Taller links, tighter margins: the column reads the same, the
     targets stop overlapping. */
  .foot li { margin-bottom: var(--s-1); }
  .foot li a { display: inline-flex; align-items: center; min-height: 44px; }

  /* Two stacked calls to action that differ by ten pixels look like a mistake
     rather than a choice. Full width makes the pair deliberate. This moved
     off `.home__hero` with the hero itself (§17 #251); the pair is the same
     pair, now sitting under the slides. */
  .home__cta .btn { flex: 1 0 100%; }

  /* Tag chips keep their pill proportions - a 44 px chip looks like a button -
     and grow their hit area with a pseudo-element instead. */
  a.badge { position: relative; }
  a.badge::after { content: ""; position: absolute; inset: -8px; }

  /* Standalone text links - "Barchasi", "Koʻrish", "Parolni unutdingizmi?" -
     are actions, not prose, and at 27 px tall they are a poor target. Growing
     the box would break the typographic rhythm they sit in, so the hit area
     grows instead and the text stays exactly where it was. Links inside running
     text are excluded: expanding those would make neighbouring lines overlap. */
  /* The hit area is declared as a height, not as a negative inset. An inset of
     -9 px gave a 27 px link a 45 px target and a `.small` 24 px link a 42 px
     one, which is how the product page's "Oʻlcham jadvali" link ended up two
     pixels short of a thumb — a target derived from the text size is only ever
     right for one text size. A card name can wrap to two lines, so it keeps the
     inset: a fixed 44 px would make a tall target smaller. */
  a.link, a.card__name { position: relative; }
  a.link::after { content: ""; position: absolute; left: -4px; right: -4px;
    top: 50%; height: 44px; transform: translateY(-50%); }
  a.card__name::after { content: ""; position: absolute; inset: -9px -4px; }
  /* A checkbox's label is running text too. The signup consent names two
     documents on neighbouring lines, and the second link's target lay over
     the first: a tap on the terms opened the privacy policy (§17 #203). */
  p a.link::after, li a.link::after, td a.link::after, .prose a.link::after,
  .check a.link::after { content: none; }

  /* The rating under the product title is a link to the reviews section, and
     it is 22 px tall — stars and a number, sized to sit quietly under the
     title. Same fixed-height target as a.link and for the same reason (§17
     #110): nothing above or below it is interactive, so it may grow over them. */
  a.rating { position: relative; }
  a.rating::after { content: ""; position: absolute; left: -4px; right: -4px;
    top: 50%; height: 44px; transform: translateY(-50%); }

  /* Breadcrumbs are 18 px of text doing navigation work. Vertical-only growth:
     the crumbs sit in a row, so widening them would let neighbours overlap. */
  .crumbs a { position: relative; }
  .crumbs a::after { content: ""; position: absolute; inset: -13px 0; }
}
