/**
 * jensik — Feedback & State Components
 * =======================================
 * Modal/Dialog, BottomSheet, Toast, Alert, EmptyState, ErrorState,
 * Skeleton/Loading, Pagination, Tabs, Accordion.
 */

/* =======================================================================
 * MODAL / DIALOG — .nk-modal
 * Built on <dialog> so focus trapping, Esc and inertness come from the
 * platform instead of hand-written JS.
 * Mobile: bottom sheet. Desktop (≥768px): centred card.
 * ===================================================================== */

.nk-modal {
  padding: 0;
  border: none;
  background: transparent;
  max-inline-size: 100%;
  max-block-size: 100%;
  inline-size: 100%;
  margin: 0;
  margin-block-start: auto; /* sheet sits at the bottom on mobile */
  overflow: visible;
}
.nk-modal::backdrop { background-color: var(--nk-surface-overlay); backdrop-filter: blur(2px); }

.nk-modal__panel {
  background-color: var(--nk-surface-raised);
  border-start-start-radius: var(--nk-radius-xl);
  border-start-end-radius: var(--nk-radius-xl);
  padding: var(--nk-space-5) var(--nk-space-5);
  padding-block-end: calc(var(--nk-space-5) + var(--nk-safe-bottom));
  max-block-size: 88vh;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* Grab handle — signals the sheet is dismissible. */
.nk-modal__handle {
  inline-size: 40px;
  block-size: 4px;
  border-radius: var(--nk-radius-pill);
  background-color: var(--nk-navy-200);
  margin: 0 auto var(--nk-space-4);
}

.nk-modal__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--nk-space-4);
  margin-block-end: var(--nk-space-4);
}
.nk-modal__title {
  font-family: var(--nk-font-display);
  font-size: var(--nk-text-xl);
  font-weight: var(--nk-weight-bold);
  color: var(--nk-text-strong);
}
.nk-modal__foot {
  margin-block-start: var(--nk-space-6);
  display: flex;
  gap: var(--nk-space-3);
}
.nk-modal__foot .nk-btn { flex: 1; }

.nk-modal[open] { animation: nk-sheet-in var(--nk-duration-normal) var(--nk-ease-out); }

@media (min-width: 768px) {
  .nk-modal {
    margin: auto;
    max-inline-size: 520px;
  }
  .nk-modal__panel {
    border-radius: var(--nk-radius-xl);
    padding: var(--nk-space-8);
    max-block-size: 80vh;
  }
  .nk-modal__handle { display: none; }
  .nk-modal__foot { justify-content: flex-end; }
  .nk-modal__foot .nk-btn { flex: none; }
  .nk-modal[open] { animation: nk-fade-in var(--nk-duration-fast) var(--nk-ease-out); }
}

@keyframes nk-sheet-in { from { transform: translateY(16px); opacity: 0; } to { transform: none; opacity: 1; } }
@keyframes nk-fade-in { from { opacity: 0; } to { opacity: 1; } }

/* =======================================================================
 * BOTTOM SHEET — .nk-sheet
 * The filter surface on mobile, and the pattern for any future one.
 *
 * Not a <dialog> like .nk-modal, deliberately: this sheet opens on top of the
 * search overlay, and a modal <dialog> would make the overlay behind it inert —
 * which is exactly wrong here, because the whole point is watching the result
 * count change while you tap facets. It shares the drawer's scrim and focus
 * trap instead.
 * ===================================================================== */

/**
 * visibility, not display, so the transform below can animate.
 *
 * The delay is the whole reason the exit is visible at all. Without it,
 * `visibility: hidden` lands on the same frame `data-open` goes false and the
 * outgoing slide is painted on an element nobody can see — measured: the panel
 * travelled 0 → 606px with `visibility: hidden` the entire way. Matches the
 * treatment `.nk-drawer` has always had.
 *
 * With Motion loaded the exit is driven from ui.js, which holds an inline
 * `visibility` of its own for the length of the animation and removes it after;
 * this delay is what covers the CSS-only path.
 */
.nk-sheet {
  position: fixed;
  inset: 0;
  z-index: var(--nk-z-drawer);
  display: flex;
  align-items: flex-end;
  visibility: hidden;
  pointer-events: none;
  transition: visibility 0s linear var(--nk-duration-normal);
}
.nk-sheet[data-open="true"] {
  visibility: visible;
  pointer-events: auto;
  transition-delay: 0s;
}

.nk-sheet__panel {
  inline-size: 100%;
  max-block-size: 88vh;
  display: flex;
  flex-direction: column;
  background-color: var(--nk-surface-raised);
  border-start-start-radius: var(--nk-radius-xl);
  border-start-end-radius: var(--nk-radius-xl);
  box-shadow: var(--nk-shadow-xl);
  transform: translateY(100%);
  transition: transform var(--nk-duration-normal) var(--nk-ease-out);
}
.nk-sheet[data-open="true"] .nk-sheet__panel { transform: translateY(0); }

.nk-sheet__handle {
  inline-size: 40px;
  block-size: 4px;
  border-radius: var(--nk-radius-pill);
  background-color: var(--nk-navy-200);
  margin: var(--nk-space-3) auto var(--nk-space-2);
  flex: 0 0 auto;
}

.nk-sheet__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--nk-space-4);
  padding: var(--nk-space-2) var(--nk-space-5) var(--nk-space-3);
  border-block-end: var(--nk-border-hairline);
  flex: 0 0 auto;
}
.nk-sheet__title {
  font-family: var(--nk-font-display);
  font-size: var(--nk-text-xl);
  font-weight: var(--nk-weight-bold);
  color: var(--nk-text-strong);
}

.nk-sheet__body {
  flex: 1 1 auto;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--nk-space-4) var(--nk-space-5);
}

.nk-sheet__foot {
  flex: 0 0 auto;
  display: flex;
  gap: var(--nk-space-3);
  padding: var(--nk-space-4) var(--nk-space-5);
  padding-block-end: calc(var(--nk-space-4) + var(--nk-safe-bottom));
  border-block-start: var(--nk-border-hairline);
}
/* The secondary action keeps its natural width on one line; the primary takes
   the rest. Letting both flex made "حذف همه" wrap into two lines at 360px. */
.nk-sheet__foot > .nk-btn { flex: 0 0 auto; white-space: nowrap; }
.nk-sheet__foot > .nk-btn--primary { flex: 1 1 auto; }

/*
 * ≥768px: EVERY sheet is a centred dialog. This is not a variant.
 *
 * A bottom sheet is a phone answer to a phone problem — it starts at the thumb
 * and it is dismissed by the same gesture that opened it. On a desktop nothing
 * about that holds: a 480px panel welded to the bottom edge of a 1440×900
 * viewport, with a grab handle nobody can grab, reads as a notification that
 * slid in rather than as the thing the page is now about. Reported on the
 * size-guide sheet: «پاپ‌آپ‌های نسخه دسکتاپ … عین گوشی شده».
 *
 * It was an opt-in `--dialog` variant until 2026-08-23, and three of the eight
 * sheets in the theme carried it while the other five — size guide, trust,
 * buy, sort, categories — silently shipped the phone box to the desktop. An
 * opt-in that has to be remembered on every new sheet is the wrong default:
 * the desktop treatment now comes from `.nk-sheet` itself and a sheet has to
 * opt *out*, with `--sticky-bottom`, to stay pinned. Nothing opts out today.
 *
 * What changes here is the **box**, plus the one thing that must follow the
 * box: how the panel arrives and leaves. The layer stack, the scrim, the focus
 * trap and `ui.js`'s open/close path are untouched, so there is still one
 * sheet mechanism in the theme rather than two.
 */
@media (min-width: 768px) {
  .nk-sheet:not(.nk-sheet--sticky-bottom) {
    align-items: center;
    /*
     * Read by `closedOffset()` in ui.js to pick the exit. The breakpoint stays
     * here, in CSS, rather than being restated as a matchMedia query in the
     * script — the hero already carries one breakpoint in three places and it
     * is the first thing to drift.
     */
    --nk-sheet-exit: dialog;
  }
  .nk-sheet:not(.nk-sheet--sticky-bottom) > .nk-sheet__panel {
    /*
     * One width knob, set per variant below, instead of a specificity ladder
     * of `max-inline-size` overrides — a sheet holding one paragraph and a
     * sheet holding the whole review list do not want the same card.
     */
    max-inline-size: var(--nk-sheet-width, var(--nk-container-narrow));
    max-block-size: 84vh;
    margin-inline: auto;
    border-radius: var(--nk-radius-xl);
  }
  /* A grab handle on a centred card is an affordance for a gesture that is not
     available there. */
  .nk-sheet:not(.nk-sheet--sticky-bottom) > .nk-sheet__panel > .nk-sheet__handle { display: none; }
  .nk-sheet:not(.nk-sheet--sticky-bottom) > .nk-sheet__panel > .nk-sheet__head { padding-block-start: var(--nk-space-4); }
  /* The safe-area padding is a phone inset; on a centred card it is dead space
     under the buttons. */
  .nk-sheet:not(.nk-sheet--sticky-bottom) > .nk-sheet__panel > .nk-sheet__foot {
    padding-block-end: var(--nk-space-4);
  }

  /* Card widths. A short surface centred at the full 768px reads as a page,
     not a dialog; these keep each one the size of what it holds. */
  .nk-sheet--compact { --nk-sheet-width: 520px; }  /* size guide, trust items */
  .nk-sheet--sort    { --nk-sheet-width: 420px; }  /* five options, never more */
  .nk-sheet--cats    { --nk-sheet-width: 560px; }  /* two/three card columns */
  /* Only 768–1023 sees this as a dialog; at 1024 it is the archive sidebar and
     `archive.css` drops the cap entirely. A column of facets at the full 768px
     is a page, not a panel. */
  .nk-sheet--filters { --nk-sheet-width: 520px; }

  /*
   * A centred card cannot leave by `translateY(100%)`.
   *
   * That is the bottom sheet's exit, and it is exact there: the panel's top
   * edge sits on the viewport's bottom edge after travelling its own height.
   * Centred, the same transform moves the card by its own height from the
   * MIDDLE of the screen, which does not clear it. Measured at 1280 on the
   * 401px sort sheet: it slid from top 250 to top 650 in a 900px viewport and
   * came to rest with 250px still on screen, where `visibility: hidden` then
   * blinked it out. Reported, twice, as the sheet not closing properly.
   *
   * So the dialog uses the dialog motion the theme already has — `.nk-modal`'s
   * `nk-sheet-in`, a 16px settle with a fade. It is a shorter move than the
   * sheet's and it ends at `opacity: 0`, so nothing has to travel far enough
   * to leave a 1440px-tall viewport.
   */
  .nk-sheet:not(.nk-sheet--sticky-bottom) > .nk-sheet__panel {
    transform: translateY(var(--nk-space-4));
    opacity: 0;
    transition: transform var(--nk-duration-normal) var(--nk-ease-out),
                opacity var(--nk-duration-normal) var(--nk-ease-out);
  }
  .nk-sheet:not(.nk-sheet--sticky-bottom)[data-open="true"] > .nk-sheet__panel {
    transform: none;
    opacity: 1;
  }
}


/* =======================================================================
 * CATEGORY SHEET — .nk-sheet--cats / .nk-catcards / .nk-catcard
 * The department picker behind the bottom nav's "دسته‌بندی‌ها" tab.
 *
 * Cards rather than the drawer's rows. A department is a place a parent is
 * choosing between, and at 360px a column of text links gives them nothing to
 * choose on — the artwork is what separates "کفش" from "اکسسوری" at a glance.
 *
 * Every thumbnail arrives in data-src and is promoted to src by ui.js the
 * first time the sheet opens. See nk_category_sheet().
 * ===================================================================== */

.nk-catcards {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  /* Two up at 360px. auto-fill with a floor rather than a fixed count, so a
     sixth department reflows instead of needing a new breakpoint. */
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: var(--nk-space-3);
}

.nk-catcard {
  display: flex;
  flex-direction: column;
  gap: var(--nk-space-1);
  padding: var(--nk-space-2);
  border-radius: var(--nk-radius-lg);
  border: var(--nk-border-width) solid var(--nk-border-subtle);
  background-color: var(--nk-surface-raised);
  color: var(--nk-text-strong);
  text-decoration: none;
  transition: border-color var(--nk-transition-base), box-shadow var(--nk-transition-base),
    transform var(--nk-transition-base);
}
@media (hover: hover) {
  .nk-catcard:hover { border-color: var(--nk-border-accent); box-shadow: var(--nk-shadow-sm); }
}
/* A press that moves is the only feedback a touch device gets — there is no
   hover state under a finger. */
.nk-catcard:active { transform: scale(0.98); }

.nk-catcard__media {
  position: relative;
  display: block;
  /* Reserved before the image lands, so promoting data-src cannot reflow the
     grid under someone's thumb. Square, because the department artwork is
     square (320×320) — a 4:3 well cropped a quarter of every illustration
     away, top and bottom. */
  aspect-ratio: 1 / 1;
  border-radius: var(--nk-radius-md);
  overflow: hidden;
  background-color: var(--nk-surface-sunken);
}
/*
 * `contain`, not `cover`: these are illustrations on their own ground, not
 * photography, and the shop owner can set any Woo category image here. Cover
 * would crop whatever ratio they upload; contain shows all of it, centred in
 * the well, with the sunken surface behind it doing the framing.
 */
.nk-catcard .nk-catcard__img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: contain;
  object-position: center;
  display: block;
}
/**
 * The Lucide mark sits under the image as the fallback layer.
 *
 * Not a JS onerror handler: the placeholder artwork is generated from this same
 * sprite symbol, so if the image is missing, slow, or has not been promoted
 * yet, the card still shows the right mark instead of an empty well.
 */
.nk-catcard__icon {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: var(--nk-text-muted);
}
.nk-catcard__img ~ .nk-catcard__icon { display: none; }

.nk-catcard__name {
  font-size: var(--nk-text-base);
  font-weight: var(--nk-weight-semibold);
  line-height: var(--nk-leading-snug);
  padding-inline: var(--nk-space-1);
}
.nk-catcard__meta {
  font-size: var(--nk-text-2xs);
  color: var(--nk-text-subtle);
  padding-inline: var(--nk-space-1);
  padding-block-end: var(--nk-space-1);
}

/* The category sheet is taller than the filter sheet — it is a grid, not a
   list of checkboxes — and it never becomes an inline desktop surface, so it
   keeps its own ceiling rather than inheriting the filter sheet's. */
.nk-sheet--cats .nk-sheet__panel { max-block-size: 82vh; }

@media (min-width: 480px) {
  .nk-catcards { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
}
/* The desktop card width is set once, with the other sheet widths, as
   `--nk-sheet-width` in the ≥768px block above. */

/* =======================================================================
 * TOAST — .nk-toast
 * ===================================================================== */

/**
 * Above the bottom nav, not over it.
 *
 * `--nk-safe-bottom` is the iOS home-indicator inset, not the height of our own
 * bar — a toast offset by that alone printed across the four tabs, hiding the
 * navigation at the moment it told the shopper something had happened. The bar
 * is gone from 1024px, and so is the extra offset.
 */
/*
 * `--nk-toast-lift` is how much other floating chrome in this corner has
 * claimed. It is `0px` unless something sets it — today only `.nk-scrolltop`
 * does, from layout.css. Stated as one variable rather than a second copy of
 * the sum, because this offset has already been got wrong once by being
 * recomputed somewhere else.
 */
.nk-toast-region {
  position: fixed;
  inset-block-end: calc(
    var(--nk-space-4) + var(--nk-bottom-nav-space) + var(--nk-toast-lift, 0px)
  );
  inset-inline: var(--nk-gutter);
  z-index: var(--nk-z-toast);
  display: grid;
  gap: var(--nk-space-2);
  pointer-events: none;
}
.nk-toast {
  display: flex;
  align-items: center;
  gap: var(--nk-space-3);
  padding: var(--nk-space-4);
  background-color: var(--nk-navy-800);
  color: var(--nk-text-inverse);
  border-radius: var(--nk-radius-md);
  box-shadow: var(--nk-shadow-lg);
  font-size: var(--nk-text-base);
  pointer-events: auto;
  animation: nk-sheet-in var(--nk-duration-normal) var(--nk-ease-out);
}
.nk-toast--success .nk-icon { color: var(--nk-green-500); }
.nk-toast--error .nk-icon { color: var(--nk-coral-400); }
/* The message wraps; the icon never shrinks to make room for it. A product
   name is long in Persian and a saved-to-wishlist toast quotes one. */
.nk-toast__text { flex: 1; line-height: var(--nk-leading-snug); }
.nk-toast .nk-icon { flex: none; }
/* Tappable to dismiss early — on a phone it sits over the bottom nav. */
.nk-toast { cursor: pointer; }

@media (min-width: 768px) {
  .nk-toast-region { inset-inline-start: auto; inline-size: 360px; }
}

@media (min-width: 1024px) {
  /* No bottom nav from here up, so the bar's offset comes back off. */
  .nk-toast-region {
    inset-block-end: calc(
      var(--nk-space-4) + var(--nk-safe-bottom) + var(--nk-toast-lift, 0px)
    );
  }
}

/* =======================================================================
 * ALERT — .nk-alert  (inline, in-flow messaging)
 * ===================================================================== */

.nk-alert {
  display: flex;
  align-items: flex-start;
  gap: var(--nk-space-3);
  padding: var(--nk-space-4);
  border-radius: var(--nk-radius-md);
  font-size: var(--nk-text-base);
  border: var(--nk-border-width) solid transparent;
}
.nk-alert__icon { flex: none; margin-block-start: 2px; }
.nk-alert--info    { background-color: var(--nk-status-info-bg); color: var(--nk-text-default); }
.nk-alert--success { background-color: var(--nk-status-success-bg); color: var(--nk-status-success-text); }
.nk-alert--warning { background-color: var(--nk-status-warning-bg); color: var(--nk-status-warning-text); }
.nk-alert--danger  { background-color: var(--nk-status-danger-bg); color: var(--nk-status-danger-text); }

/* =======================================================================
 * WOOCOMMERCE NOTICES — Woo's own messages, in .nk-alert's language
 *
 * WooCommerce prints its notices with its own classes and its own markup, on
 * every commerce route: the archive, the product page, the cart, the checkout
 * and the account pages. There is no filter that changes the wrapper, so the
 * only way to make them ours is to map the classes.
 *
 * They are mapped onto `.nk-alert` above rather than given a look of their
 * own. A shopper should not be able to tell that the message about a coupon
 * came from WooCommerce and the one about stock came from us — and `.nk-alert`
 * is already the documented answer to "an in-flow message", so a second one
 * would be the local exception `principles.md` forbids.
 *
 * **This used to be scoped to `body.single-product`**, which is why the cart
 * and the checkout showed Woo's 2013 grey bar with a webfont tick in it: the
 * rules existed and simply did not reach. It is unscoped now, so every route
 * that can print a notice gets the same one, `/my-account/` included.
 *
 * ## Specificity
 *
 * Woo's `woocommerce.css` styles these at `.woocommerce .woocommerce-error`
 * — (0,2,0) — and `body.woocommerce-page` is on every route that prints one.
 * `body.woocommerce-page .woocommerce-error` is (0,2,1) and wins. The `ul`
 * variants carry the element name as well, because `.woocommerce-error` *is* a
 * `<ul>` and `base.css`'s list reset is (0,1,1).
 * ===================================================================== */

body.woocommerce-page .woocommerce-notices-wrapper:empty { display: none; }

body.woocommerce-page .woocommerce-message,
body.woocommerce-page .woocommerce-error,
body.woocommerce-page .woocommerce-info,
body.woocommerce-page .wc-block-components-notice-banner {
  display: flex;
  align-items: flex-start;
  gap: var(--nk-space-3);
  margin: 0 0 var(--nk-space-4);
  padding: var(--nk-space-4);
  border: var(--nk-border-width) solid transparent;
  border-radius: var(--nk-radius-md);
  font-family: var(--nk-font-ui);
  font-size: var(--nk-text-base);
  line-height: var(--nk-leading-normal);
  /* Woo's own sheet hangs a webfont glyph off ::before and pads 3.5em to clear
     it. Both go: the icon is a real element in the block-notice markup, and
     for the older classes there is no icon to make room for. */
  background-image: none;
  box-shadow: none;
}

body.woocommerce-page .woocommerce-message::before,
body.woocommerce-page .woocommerce-error::before,
body.woocommerce-page .woocommerce-info::before { content: none; }

/* The three states, from `.nk-alert`'s tokens and no others. */
body.woocommerce-page .woocommerce-info,
body.woocommerce-page .wc-block-components-notice-banner.is-info {
  background-color: var(--nk-status-info-bg);
  color: var(--nk-text-default);
}
body.woocommerce-page .woocommerce-message,
body.woocommerce-page .wc-block-components-notice-banner.is-success {
  background-color: var(--nk-status-success-bg);
  color: var(--nk-status-success-text);
}
body.woocommerce-page .woocommerce-error,
body.woocommerce-page .wc-block-components-notice-banner.is-error {
  background-color: var(--nk-status-danger-bg);
  color: var(--nk-status-danger-text);
}
body.woocommerce-page .wc-block-components-notice-banner.is-warning {
  background-color: var(--nk-status-warning-bg);
  color: var(--nk-status-warning-text);
}

/*
 * `.woocommerce-error` is a `<ul>`, one `<li>` per failed rule — which is
 * exactly the shape the checkout's «this field is required» list arrives in.
 * It is a list and stays one for a screen reader; only the bullet goes.
 */
body.woocommerce-page ul.woocommerce-error,
body.woocommerce-page ul.woocommerce-message,
body.woocommerce-page ul.woocommerce-info {
  flex-direction: column;
  gap: var(--nk-space-2);
  list-style: none;
}
body.woocommerce-page ul.woocommerce-error > li,
body.woocommerce-page ul.woocommerce-message > li,
body.woocommerce-page ul.woocommerce-info > li {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Woo bolds the field name inside each message; keep it, at our weight. */
body.woocommerce-page .woocommerce-error strong,
body.woocommerce-page .wc-block-components-notice-banner strong {
  font-weight: var(--nk-weight-semibold);
}

/*
 * The block notice's icon is WooCommerce's own inline `<svg>`, not a Lucide
 * one, and there is no filter over that markup — so it is sized and recoloured
 * rather than replaced. `brand-and-icons.md` records this as the second
 * standing exception to "no inline SVG", alongside `brands.svg`: the rule is
 * about what *we* author, and this is markup we can only style.
 */
body.woocommerce-page .wc-block-components-notice-banner > svg {
  flex: none;
  inline-size: var(--nk-icon-md);
  block-size: var(--nk-icon-md);
  /* 2px, matching `.nk-alert__icon`: optically centres a round icon on the
     first line of text rather than on the box. */
  margin: 2px 0 0;
  fill: currentColor;
}
body.woocommerce-page .wc-block-components-notice-banner__content { flex: 1 1 auto; }

/*
 * The block notice's own body: a summary line and, on the checkout, a list of
 * every rule that failed with a link to each field. The links are the useful
 * part — they jump to the input — so they stay links and only stop looking
 * like 1997.
 */
body.woocommerce-page .wc-block-components-notice-banner__summary {
  margin: 0;
  font-weight: var(--nk-weight-semibold);
}
body.woocommerce-page .wc-block-components-notice-banner__content ul {
  margin: var(--nk-space-2) 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
}
body.woocommerce-page .wc-block-components-notice-banner__content li { margin: 0; }
/*
 * Each list item is a link that jumps to the field it names — genuinely useful,
 * and 23px tall as Woo ships it. The design system's floor is 44px and this is
 * a control a shopper uses on a phone with a failed form in front of them, so
 * it gets a real row. Found by putting Woo's own markup on
 * `design-system-preview.html`: the live checkout audits clean because the
 * notice group only exists *after* a failed submit, which a static audit never
 * triggers.
 */
body.woocommerce-page .wc-block-components-notice-banner__content a {
  display: flex;
  align-items: center;
  min-block-size: var(--nk-touch-target);
  color: inherit;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.25em;
}
body.woocommerce-page .wc-block-components-notice-banner__content p + ul { margin-block-start: var(--nk-space-2); }

/* Woo's «×» dismiss, when a notice carries one. */
body.woocommerce-page .wc-block-components-notice-banner__dismiss {
  flex: none;
  min-inline-size: var(--nk-touch-target);
  min-block-size: var(--nk-touch-target);
  color: inherit;
}

/* -----------------------------------------------------------------
 * FIELD-LEVEL VALIDATION — .checkout-inline-error-message
 *
 * WooCommerce 11 writes one of these into every failing `.form-row` on submit,
 * with the sentence for the rule that failed. It is a real feature and it
 * arrives unstyled: 12px `#a00` with a 16px gap above it, which on a form of
 * cream 48px fields reads as a rendering fault rather than a message.
 *
 * Given `.nk-error`'s look — the theme's own inline field message — so a
 * required-field complaint from WooCommerce and one from our own form code are
 * the same thing on screen. Not merged into `.nk-error` itself: that class is
 * ours to put on an element, and this one is written by a plugin we do not
 * control, so they are two selectors sharing one appearance rather than one
 * class we would have to make Woo emit.
 *
 * `cart.js` points the field's `aria-describedby` at it — Woo gives it an id
 * and then never references it.
 * -------------------------------------------------------------- */

.checkout-inline-error-message {
  display: block;
  margin-block: var(--nk-space-1) 0;
  font-family: var(--nk-font-ui);
  font-size: var(--nk-text-sm);
  line-height: var(--nk-leading-snug);
  color: var(--nk-status-danger-text);
}

/*
 * The inline links Woo puts in a notice — «مشاهده سبد خرید», «ورود». Woo's
 * sheet floats them to the far end and gives them a button look from 2013;
 * they read better as what they are.
 */
body.woocommerce-page .woocommerce-message .button,
body.woocommerce-page .woocommerce-info .button,
body.woocommerce-page .woocommerce-error .button {
  float: none;
  margin: 0;
  padding: 0;
  background: none;
  border: 0;
  color: inherit;
  font-weight: var(--nk-weight-semibold);
  text-decoration: underline;
  text-underline-offset: 0.2em;
}


/* =======================================================================
 * EMPTY STATE — .nk-empty
 * Always: icon, what happened, and a way forward.
 * ===================================================================== */

.nk-empty {
  display: grid;
  justify-items: center;
  text-align: center;
  gap: var(--nk-space-3);
  padding-block: var(--nk-space-12);
  padding-inline: var(--nk-gutter);
  max-inline-size: 42ch;
  margin-inline: auto;
}
.nk-empty__icon {
  display: grid;
  place-items: center;
  inline-size: 72px;
  block-size: 72px;
  border-radius: var(--nk-radius-circle);
  background-color: var(--nk-surface-accent);
  color: var(--nk-action-primary);
  margin-block-end: var(--nk-space-2);
}
.nk-empty__icon .nk-icon { inline-size: var(--nk-icon-xl); block-size: var(--nk-icon-xl); }
.nk-empty__title {
  font-family: var(--nk-font-display);
  font-size: var(--nk-text-xl);
  font-weight: var(--nk-weight-bold);
  color: var(--nk-text-strong);
}
.nk-empty__text { color: var(--nk-text-muted); }
.nk-empty__actions { margin-block-start: var(--nk-space-3); display: flex; gap: var(--nk-space-3); flex-wrap: wrap; justify-content: center; }

/* Error state reuses .nk-empty with a red icon well. */
.nk-empty--error .nk-empty__icon { background-color: var(--nk-status-danger-bg); color: var(--nk-status-danger); }

/* =======================================================================
 * SKELETON / LOADING — .nk-skeleton
 * Skeletons must match the real component's box so nothing shifts (CLS).
 * ===================================================================== */

.nk-skeleton {
  background-color: var(--nk-navy-100);
  border-radius: var(--nk-radius-sm);
  position: relative;
  overflow: hidden;
}
.nk-skeleton::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent, rgb(255 255 255 / 0.6), transparent);
  animation: nk-shimmer 1.4s infinite;
}
@keyframes nk-shimmer { from { transform: translateX(-100%); } to { transform: translateX(100%); } }

@media (prefers-reduced-motion: reduce) {
  .nk-skeleton::after { animation: none; }
}

.nk-skeleton--text { block-size: 1em; margin-block: 0.25em; }
.nk-skeleton--title { block-size: 1.4em; inline-size: 60%; }
.nk-skeleton--media { aspect-ratio: var(--nk-ratio-product); border-radius: var(--nk-radius-lg); }

/* Inline spinner */
.nk-spinner {
  inline-size: var(--nk-icon-lg);
  block-size: var(--nk-icon-lg);
  border: 2px solid var(--nk-navy-200);
  border-block-start-color: var(--nk-action-primary);
  border-radius: var(--nk-radius-circle);
  animation: nk-spin 700ms linear infinite;
}

/* =======================================================================
 * PAGINATION — .nk-pagination
 * "Previous/Next" arrows mirror in RTL via .nk-icon--directional.
 * ===================================================================== */

.nk-pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--nk-space-1);
  flex-wrap: wrap;
  padding-block: var(--nk-space-8);
}
.nk-pagination__item {
  min-inline-size: var(--nk-touch-target);
  min-block-size: var(--nk-touch-target);
  display: grid;
  place-items: center;
  padding-inline: var(--nk-space-2);
  border-radius: var(--nk-radius-md);
  font-family: var(--nk-font-numeric);
  font-size: var(--nk-text-base);
  font-weight: var(--nk-weight-medium);
  color: var(--nk-text-default);
  text-decoration: none;
  transition: background-color var(--nk-transition-base);
}
@media (hover: hover) {
  .nk-pagination__item:hover { background-color: var(--nk-navy-50); }
}
.nk-pagination__item[aria-current="page"] {
  background-color: var(--nk-navy-800);
  color: var(--nk-text-inverse);
}

/* =======================================================================
 * TABS — .nk-tabs
 * Mobile: scrollable strip. Used for description / specs / reviews.
 * ===================================================================== */

.nk-tabs__list {
  display: flex;
  gap: var(--nk-space-1);
  overflow-x: auto;
  scrollbar-width: none;
  border-block-end: var(--nk-border-hairline);
  margin-block-end: var(--nk-space-5);
}
.nk-tabs__list::-webkit-scrollbar { display: none; }
.nk-tabs__tab {
  flex: none;
  min-block-size: var(--nk-touch-target);
  padding-inline: var(--nk-space-4);
  font-size: var(--nk-text-md);
  font-weight: var(--nk-weight-medium);
  color: var(--nk-text-muted);
  border-block-end: 2px solid transparent;
  white-space: nowrap;
  transition: color var(--nk-transition-base), border-color var(--nk-transition-base);
}
.nk-tabs__tab[aria-selected="true"] {
  color: var(--nk-text-strong);
  font-weight: var(--nk-weight-semibold);
  border-block-end-color: var(--nk-action-primary);
}

/* =======================================================================
 * ACCORDION — .nk-accordion
 * Native <details>. Mobile filter groups and footer columns use this.
 * ===================================================================== */

.nk-accordion { border-block-end: var(--nk-border-hairline); }
.nk-accordion__summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--nk-space-3);
  min-block-size: var(--nk-touch-target-lg);
  padding-block: var(--nk-space-3);
  font-size: var(--nk-text-md);
  font-weight: var(--nk-weight-semibold);
  color: var(--nk-text-strong);
  cursor: pointer;
  list-style: none;
}
.nk-accordion__summary::-webkit-details-marker { display: none; }
.nk-accordion__summary .nk-icon { transition: transform var(--nk-transition-base); color: var(--nk-text-muted); }
.nk-accordion[open] > .nk-accordion__summary .nk-icon { transform: rotate(180deg); }
.nk-accordion__body { padding-block-end: var(--nk-space-4); color: var(--nk-text-default); }
