/* ==========================================================================
   ShopFront — Stylesheet
   --------------------------------------------------------------------------
   Depends on tokens.css. Nothing below may contain a raw colour, a raw
   shadow, or an off-grid spacing value: if a value is needed that the
   system does not have, the system gains a token.

   RTL note: this document is `dir="rtl"`, so logical properties resolve
   with inline-start = right and inline-end = left. Physical `left`/`right`
   is used deliberately in two places only — absolute centring, and the
   decorative gradient flares — because those must not mirror.

   Contents
     01  Reset & base
     02  Utilities
     03  Icon system
     04  Buttons
     05  Badges, chips, ratings
     06  Price
     07  Shell & container
     08  Header
     09  Promo bar & countdown
     10  Hero
     11  Assurance strip
     12  Section heading
     13  Category rail
     14  Product card
     15  Store picks
     16  Product rows
     17  Amazing deals
     18  Popular categories
     19  Laptops
     20  Promo tiles
     21  Brands
     22  Showcase
     23  FAQ
     24  Blog
     25  Footer
     26  Drawer, backdrop, toast
     27  Reveal on scroll
     28  Responsive
     29  Intro curtain
     30  Motion, contrast, print
   ========================================================================== */


/* ==========================================================================
   01 · RESET & BASE
   ========================================================================== */

*,
*::before,
*::after {
  box-sizing: border-box;
}

* {
  margin: 0;
}

html {
  scroll-behavior: smooth;
  /* Reserve the scrollbar gutter so locking scroll for the drawer cannot
     shift the whole page sideways. */
  scrollbar-gutter: stable;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  background: var(--bg-shell);
  color: var(--fg-body);
  font-family: var(--font-sans);
  font-size: var(--text-md);
  line-height: var(--leading-normal);
  font-weight: var(--weight-normal);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  transition: background-color var(--duration-slow) var(--ease-standard),
              color var(--duration-slow) var(--ease-standard);
}

h1, h2, h3, h4, h5, h6 {
  color: var(--fg-default);
  font-weight: var(--weight-extrabold);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-normal);
  text-wrap: balance;
}

p {
  text-wrap: pretty;
}

img,
svg,
picture,
video {
  display: block;
  max-width: 100%;
}

img {
  height: auto;
}

/* Neutral hold while a photograph decodes, so the card does not flash
   white-on-white.

   Opt-in, not blanket. A background on an <img> paints the whole content box,
   not the pixels: behind a cutout PNG it is no longer a hold, it is a grey
   slab filling the silhouette's bounding box — and a near-black one in dark
   mode. This used to sit on the bare `img` selector, and every slot holding a
   cutout had to cancel it again by hand. Now that all product artwork is
   cutout photography, the blog card is the only slot left that takes a
   full-bleed photograph and still wants the hold. */
.blog-card__media img {
  background-color: var(--bg-surface-2);
}

button,
input,
select,
textarea {
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
}

button {
  border: 0;
  background: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

a {
  color: inherit;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
}

ul, ol {
  list-style: none;
  padding: 0;
}

del {
  text-decoration-thickness: from-font;
}

::selection {
  background: var(--brand);
  color: var(--fg-on-brand);
}

/* ---- Focus ------------------------------------------------------------
   The single largest accessibility gap in the previous build was the
   complete absence of a keyboard focus indicator. One ring, everywhere,
   with an inverse variant for saturated surfaces. */

:focus-visible {
  outline: var(--ring-width) solid var(--ring-color);
  outline-offset: var(--ring-offset);
  border-radius: var(--radius-xs);
}

:where(.promo-bar, .site-footer, .slide, .deals, .picks__panel) :focus-visible {
  outline-color: var(--ring-color-inverse);
}

/* Anchor targets must clear the sticky header. */
:where(section, article, footer, div)[id] {
  scroll-margin-block-start: calc(var(--header-height-compact) + var(--space-6));
}

/* Horizontal rails hide their scrollbar.

   Safe to do here because the bar is the least useful of four affordances
   these rails already have: prev/next buttons, touch swipe, a focusable
   container so arrow keys scroll it, and shift+wheel. It is the only one
   that intrudes on the layout, and on platforms with classic scrollbars it
   also stole 6px from every card row.

   Both declarations are needed: scrollbar-width covers Firefox and
   Chrome 121+, ::-webkit-scrollbar covers older Chrome and Safari. */
.scroll-rail,
.rail,
.promo-tiles {
  scrollbar-width: none;
}
.scroll-rail::-webkit-scrollbar,
.rail::-webkit-scrollbar,
.promo-tiles::-webkit-scrollbar {
  display: none;
}


/* ==========================================================================
   02 · UTILITIES
   ========================================================================== */

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: fixed;
  z-index: var(--z-skip-link);
  inset-block-start: var(--space-3);
  /* Physical centring: must not mirror with direction. */
  left: 50%;
  translate: -50% -200%;
  padding: var(--space-3) var(--space-6);
  border-radius: var(--radius-pill);
  background: var(--bg-inverse);
  color: var(--fg-on-inverse);
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  box-shadow: var(--shadow-overlay);
  transition: translate var(--duration-base) var(--ease-out);
}
.skip-link:focus-visible {
  translate: -50% 0;
}

.container {
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
}

/* Horizontal rail with snap — the shared mobile carousel behaviour. */
.scroll-rail {
  overflow-x: auto;
  overscroll-behavior-inline: contain;
  scroll-snap-type: inline mandatory;
  scroll-padding-inline: var(--space-1);
}
.scroll-rail > * {
  scroll-snap-align: start;
}

/* Tinted square holding an icon. Used wherever an icon needs a plate
   rather than sitting bare on the surface. */
.icon-tile {
  flex: none;
  display: grid;
  place-items: center;
  inline-size: var(--space-11);
  block-size: var(--space-11);
  border-radius: var(--radius-md);
  background: var(--bg-brand-subtle);
  color: var(--brand);
  font-size: var(--icon-lg);
}
.icon-tile--sm {
  inline-size: var(--space-10);
  block-size: var(--space-10);
  font-size: var(--icon-md);
}
.icon-tile--accent {
  background: var(--bg-accent-subtle);
  color: var(--accent-text);
}

/* Skeleton shimmer, applied to any block awaiting content. */
.skeleton {
  border-radius: var(--radius-md);
  background: var(--gradient-skeleton);
  background-size: 220% 100%;
  animation: skeleton-sweep 1.4s var(--ease-in-out) infinite;
}

@keyframes skeleton-sweep {
  from { background-position: 100% 0; }
  to   { background-position: -120% 0; }
}


/* ==========================================================================
   03 · ICON SYSTEM
   --------------------------------------------------------------------------
   Replaces the previous emoji and dingbat glyphs (⌕ ♙ ▣ ➤ ◉ ☰ ↓), which
   rendered at unpredictable sizes across platforms, carried no
   semantics, and in several cases were simply the wrong symbol — ♙ is a
   chess pawn, not an account. Every icon is a 24×24 stroked symbol in
   the sprite, inheriting currentColor and font-size.
   ========================================================================== */

.icon {
  inline-size: 1em;
  block-size: 1em;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
  /* Optical centring against Persian baselines. */
  vertical-align: -0.125em;
}

.icon--fill {
  fill: currentColor;
  stroke: none;
}

.icon--sm  { font-size: var(--icon-sm); }
.icon--md  { font-size: var(--icon-md); }
.icon--lg  { font-size: var(--icon-lg); }
.icon--xl  { font-size: var(--icon-xl); }
.icon--2xl { font-size: var(--icon-2xl); }


/* ==========================================================================
   04 · BUTTONS
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--gap-inline-sm);
  min-block-size: var(--btn-height);
  padding-inline: var(--btn-pad-inline);
  border-radius: var(--btn-radius);
  font-size: var(--btn-font-size);
  font-weight: var(--btn-weight);
  line-height: var(--leading-none);
  white-space: nowrap;
  transition: var(--transition-elevate), var(--transition-colors);
}

/* `display: inline-flex` above outranks the UA's `[hidden] { display: none }`
   — the same trap `.badge--count[hidden]` is guarded against below. The
   account pages hide a button whenever its list is empty. */
.btn[hidden] {
  display: none;
}

.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
  box-shadow: none;
}

.btn--primary {
  background: var(--btn-primary-bg);
  color: var(--btn-primary-fg);
  box-shadow: var(--btn-primary-shadow);
}
.btn--primary:hover:not(:disabled) {
  transform: translateY(-3px);
  box-shadow: var(--btn-primary-shadow-hover);
}
/* Pressed states drop to a flat, darker fill: a shadow change alone is
   too weak a signal that the press registered. */
.btn--primary:active:not(:disabled) {
  transform: translateY(-1px);
  background: var(--btn-primary-bg-active);
  box-shadow: var(--shadow-brand-sm);
}

/* Light pill sitting on a saturated field. */
.btn--onbrand {
  background: var(--btn-onbrand-bg);
  color: var(--btn-onbrand-fg);
  box-shadow: var(--shadow-lg);
}
.btn--onbrand:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-xl);
}
.btn--onbrand:active {
  transform: translateY(-1px);
}

.btn--neutral {
  background: var(--btn-neutral-bg);
  color: var(--btn-neutral-fg);
}
.btn--neutral:hover {
  background: var(--btn-neutral-bg-hover);
  transform: translateY(-2px);
}

.btn--lg {
  min-block-size: var(--btn-height-lg);
  padding-inline: var(--space-7);
  font-size: var(--text-xl);
}

.btn--block {
  width: 100%;
}

/* ---- Icon button ------------------------------------------------------ */
.icon-btn {
  display: inline-grid;
  place-items: center;
  /* Never shrink: as a flex child beside a growing search field, the
     default flex-shrink:1 collapses these to a few pixels. */
  flex: none;
  inline-size: var(--btn-height-lg);
  block-size: var(--btn-height-lg);
  border-radius: var(--radius-circle);
  background: var(--bg-surface-3);
  color: var(--fg-body);
  font-size: var(--icon-md);
  transition: var(--transition-elevate), var(--transition-colors);
}
.icon-btn:hover {
  transform: translateY(-3px);
  background: var(--bg-inset);
}
.icon-btn:active {
  transform: translateY(-1px);
}

.icon-btn--brand {
  position: relative;
  background: var(--gradient-brand-vivid);
  color: var(--fg-on-brand);
  box-shadow: var(--shadow-brand-md);
}
.icon-btn--brand:hover {
  background: var(--gradient-brand-vivid);
  box-shadow: var(--shadow-brand-lg);
}
.icon-btn--brand:active {
  background: var(--brand-active);
}

/* Off at every width by default. The phone breakpoint turns it on, and only
   while .is-scrolled has taken the search row away. */
.search-toggle {
  display: none;
}

.icon-btn--square {
  border-radius: var(--btn-radius-square);
}

/* ---- Section "view all" ----------------------------------------------
   One of these closes every section heading. As a bare 13px grey label it
   was invisible beside a 26px black title, so it is now a real pill
   button: system button height and weight, a hairline edge that separates
   it from the page, and a circular arrow chip that slides forward on
   hover. Sized off the button tokens so it stays in step with .btn. */
.link-more {
  display: inline-flex;
  /* Markup order is icon-then-label; row-reverse moves the arrow to the
     trailing edge (physical left in RTL), where it points forward. */
  flex-direction: row-reverse;
  align-items: center;
  gap: var(--gap-inline-sm);
  min-block-size: var(--btn-height);
  padding-block: var(--space-1);
  /* Asymmetric: the arrow chip brings its own visual inset, so that edge
     needs far less padding than the label edge. */
  padding-inline: var(--space-5) var(--space-2);
  border: var(--border-hairline) solid var(--border-default);
  border-radius: var(--btn-radius);
  background: var(--bg-surface);
  color: var(--fg-default);
  font-size: var(--btn-font-size);
  font-weight: var(--btn-weight);
  line-height: var(--leading-none);
  white-space: nowrap;
  box-shadow: var(--shadow-xs);
  transition: var(--transition-elevate), var(--transition-colors);
}
.link-more .icon {
  inline-size: var(--space-8);
  block-size: var(--space-8);
  padding: var(--space-1-5);
  border-radius: var(--radius-circle);
  background: var(--brand);
  color: var(--fg-on-brand);
  transition: var(--transition-transform), var(--transition-colors);
}
.link-more:hover {
  transform: translateY(-2px);
  border-color: var(--border-brand);
  background: var(--bg-brand-subtle);
  color: var(--brand-text);
  box-shadow: var(--shadow-md);
}
.link-more:hover .icon {
  transform: translateX(-4px);
  background: var(--brand-hover);
}
.link-more:active {
  transform: translateY(0);
  box-shadow: var(--shadow-xs);
}

/* For a pill on a saturated field, where the bordered white-on-grey
   treatment above has nothing to sit against. Borrows the solid light
   fill from .btn--onbrand; the base teal chip already reads correctly
   against it, so that stands as-is. */
.link-more--onbrand,
.link-more--onbrand:hover {
  border-color: transparent;
  background: var(--btn-onbrand-bg);
  color: var(--btn-onbrand-fg);
}
.link-more--onbrand {
  box-shadow: var(--shadow-lg);
}
.link-more--onbrand:hover {
  box-shadow: var(--shadow-xl);
}
.link-more--onbrand:active {
  box-shadow: var(--shadow-lg);
}

/* ---- Notch button ----------------------------------------------------
   The recurring "circle punched through a panel edge" affordance. */
.notch-btn {
  display: grid;
  place-items: center;
  inline-size: var(--space-10);
  block-size: var(--space-10);
  border: var(--notch-ring) solid var(--notch-color);
  border-radius: var(--radius-circle);
  background: var(--bg-surface);
  /* --accent-text, not --accent: the latter is a dark navy that vanishes
     against the dark-theme surface. */
  color: var(--accent-text);
  font-size: var(--icon-md);
  transition: var(--transition-elevate), var(--transition-colors);
}
.notch-btn:hover {
  background: var(--brand);
  color: var(--fg-on-brand);
  scale: 1.08;
}


/* ==========================================================================
   05 · BADGES, CHIPS, RATINGS
   ========================================================================== */

.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--badge-pad);
  border-radius: var(--badge-radius);
  font-size: var(--badge-font-size);
  font-weight: var(--badge-weight);
  line-height: var(--leading-none);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.badge--discount {
  /* Clipped inner corner, pointing at the price it modifies. */
  border-end-end-radius: var(--space-0-5);
  background: var(--badge-discount-bg);
  color: var(--badge-discount-fg);
}

.badge--stock {
  background: var(--success-subtle);
  color: var(--success-text);
}

.badge--count {
  position: absolute;
  inset-block-start: -2px;
  inset-inline-start: -2px;
  display: grid;
  place-items: center;
  min-inline-size: 1.375rem;
  block-size: 1.375rem;
  padding-inline: var(--space-1);
  border: var(--border-heavy) solid var(--bg-surface);
  border-radius: var(--radius-pill);
  background: var(--accent);
  color: var(--fg-on-accent);
  font-size: var(--text-2xs);
  font-weight: var(--weight-extrabold);
  font-variant-numeric: tabular-nums;
}
/* `display: grid` above outranks the UA's `[hidden] { display: none }`, so
   the attribute app.js sets on an empty basket needs restating. Reachable
   only since the mini-cart gave the count a way back down to zero. */
.badge--count[hidden] {
  display: none;
}

.rating {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  color: var(--fg-muted);
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  font-variant-numeric: tabular-nums;
}
.rating .icon {
  color: var(--rating);
  font-size: var(--icon-sm);
}

.wishlist-btn {
  position: relative;
  display: grid;
  place-items: center;
  inline-size: var(--space-8);
  block-size: var(--space-8);
  /* Sits at the card's inline-end; pull it back into the padding so the
     glyph aligns optically with the card edge. */
  margin-inline-end: calc(var(--space-2) * -1);
  border-radius: var(--radius-circle);
  color: var(--fg-subtle);
  font-size: var(--icon-md);
  transition: var(--transition-colors), var(--transition-transform);
}

/* 32px painted, --tap-min tapped. Grown outward rather than by resizing the
   button, because the hover tint is the visible circle and a 44px disc would
   read as a filled corner rather than a glyph. It shares the card's top row
   with the rating, which is text — so there is nothing to steal a hit from. */
.wishlist-btn::after {
  content: "";
  position: absolute;
  inset: calc((var(--tap-min) - var(--space-8)) / -2);
  border-radius: inherit;
}
.wishlist-btn:hover {
  background: var(--bg-brand-subtle);
  color: var(--brand);
}
.wishlist-btn[aria-pressed="true"] {
  color: var(--brand);
}
.wishlist-btn[aria-pressed="true"] .icon {
  fill: currentColor;
  animation: heart-pop var(--duration-slow) var(--ease-spring);
}

@keyframes heart-pop {
  0%   { scale: 1; }
  45%  { scale: 1.32; }
  100% { scale: 1; }
}


/* ==========================================================================
   06 · PRICE
   --------------------------------------------------------------------------
   One component, three sizes. Laid out RTL-native: the discount badge
   leads (reading-order first, therefore rightmost), the struck price
   follows, the payable price sits beneath. The previous build forced
   `direction: ltr` on every price group and then flipped children back,
   which inverted Persian reading order.
   ========================================================================== */

.price {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-1);
  min-inline-size: 0;
}

.price__was {
  display: flex;
  align-items: center;
  gap: var(--gap-inline-sm);
}

.price__old {
  color: var(--fg-subtle);
  font-size: var(--text-xs);
  font-variant-numeric: tabular-nums;
}

/* No `white-space: nowrap`. .price is a flex item with min-inline-size: 0,
   so it may shrink below its content; an unbreakable number then spills
   sideways into the add-to-cart button rather than reflowing. Allowing the
   currency to drop to a second line is the graceful failure. The digit run
   itself has no break opportunity, so it never splits. */
.price__now {
  color: var(--fg-default);
  font-size: var(--text-xl);
  font-weight: var(--weight-black);
  line-height: var(--leading-tight);
  font-variant-numeric: tabular-nums;
}
.price__now small {
  margin-inline-start: var(--space-1);
  color: var(--fg-muted);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
}

.price--sm .price__now { font-size: var(--text-lg); }
.price--sm .price__old { font-size: var(--text-2xs); }
.price--lg .price__now { font-size: var(--text-2xl); }


/* ==========================================================================
   07 · SHELL
   ========================================================================== */

.site-shell {
  min-block-size: 100vh;
  padding-inline: var(--container-inline);
  padding-block-end: var(--space-16);
  border-start-start-radius: var(--radius-2xl);
  border-start-end-radius: var(--radius-2xl);
  /* Kept shallow on purpose: the slider's notch tab paints flat
     var(--bg-page), so the tint must have faded out well above it. */
  background:
    radial-gradient(ellipse 60rem 15rem at 82% -5rem, var(--bg-brand-subtle), transparent 70%),
    var(--bg-page);
  box-shadow: inset 0 var(--border-heavy) 0 var(--brand);
  /* `clip` rather than `hidden`: it contains stray decoration without
     creating a scroll container, which would break the sticky header. */
  overflow-x: clip;
}


/* ==========================================================================
   08 · HEADER
   --------------------------------------------------------------------------
   Now sticky. On a catalogue this long, search and cart being two
   screens away is a real friction cost. Height collapses on scroll so it
   never occupies more than 72px of a phone viewport.
   ========================================================================== */

.site-header {
  position: sticky;
  z-index: var(--z-sticky);
  inset-block-start: 0;
  margin-inline: calc(var(--container-inline) * -1);
  padding-inline: var(--container-inline);
  background: var(--header-bg);
  border-block-end: var(--border-hairline) solid transparent;
  transition: background-color var(--duration-base) var(--ease-standard),
              border-color var(--duration-base) var(--ease-standard),
              box-shadow var(--duration-base) var(--ease-standard);
}

@supports (backdrop-filter: blur(1px)) {
  .site-header {
    backdrop-filter: blur(var(--header-blur)) saturate(160%);
  }
}

.site-header.is-scrolled {
  background: var(--header-bg-scrolled);
  /* Flat: the hairline is what separates the header from the page now,
     so it does the whole job rather than backing up a shadow. */
  border-block-end-color: var(--border-default);
}

/* Explicit areas rather than auto-placement: the search moves to its own
   row on narrow screens, and named areas make that reflow legible. */
.header-inner {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto;
  grid-template-areas: "brand nav search tools";
  align-items: center;
  gap: var(--gap-inline-lg);
  min-block-size: var(--header-height);
  transition: min-block-size var(--duration-base) var(--ease-standard);
}

.header-inner > .brand { grid-area: brand; }
.primary-nav          { grid-area: nav; }
.header-inner > .search { grid-area: search; }
.header-tools         { grid-area: tools; }

.site-header.is-scrolled .header-inner {
  min-block-size: var(--header-height-compact);
}

/* ---- Logo -------------------------------------------------------------
   assets/logo.svg lives in the sprite as a symbol with no fill of its own,
   so it inherits currentColor. That is what lets one mark serve the header
   (brand teal), the footer (white on the teal field) and dark mode (light
   teal) without shipping three separate files. */
.brand {
  display: block;
  inline-size: var(--logo-width);
  color: var(--brand-text);
  user-select: none;
  transition: var(--transition-transform),
              inline-size var(--duration-base) var(--ease-standard);
}
.brand:hover {
  transform: translateY(-2px);
}

.brand__mark {
  inline-size: 100%;
  block-size: auto;
  /* Locks the box to the artwork's ratio so the header cannot shift while
     the sprite resolves. */
  aspect-ratio: 519.14 / 141.15;
  fill: currentColor;
  stroke: none;
}

.site-header.is-scrolled .brand {
  inline-size: var(--logo-width-compact);
}

/* ---- Primary nav ------------------------------------------------------ */
.primary-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  /* Tight, because Yekan Bakh sets Persian roughly 35% wider than the
     previous face and these labels are long. */
  gap: clamp(var(--space-2), 0.9vw, var(--space-4));
  min-inline-size: 0;
}

.primary-nav a {
  position: relative;
  padding: var(--space-2) var(--space-1);
  color: var(--fg-muted);
  font-size: var(--text-md);
  font-weight: var(--weight-medium);
  white-space: nowrap;
  transition: var(--transition-colors);
}

.primary-nav a::after {
  content: "";
  position: absolute;
  inset-block-end: 0;
  inset-inline: 50%;
  block-size: var(--border-thick);
  border-radius: var(--radius-pill);
  background: var(--brand);
  transition: inset-inline var(--duration-base) var(--ease-out);
}

.primary-nav a:hover,
.primary-nav a[aria-current="page"] {
  color: var(--brand-text);
}
.primary-nav a:hover::after,
.primary-nav a[aria-current="page"]::after {
  inset-inline: var(--space-1);
}

/* ---- Tools ------------------------------------------------------------ */
.header-tools {
  display: flex;
  align-items: center;
  gap: var(--gap-inline-md);
}

/* ---- Search / combobox ----------------------------------------------- */
.search {
  position: relative;
  /* Yields width to the nav on laptop screens, where the wider typeface
     leaves the nav column short. */
  inline-size: clamp(14rem, 17vw, 19rem);
}

.search__field {
  display: flex;
  align-items: center;
  gap: var(--gap-inline-md);
  block-size: var(--input-height);
  padding-inline: var(--input-pad-inline);
  border: var(--border-thin) solid var(--input-border);
  border-radius: var(--input-radius);
  background: var(--input-bg);
  color: var(--fg-muted);
  box-shadow: var(--input-shadow);
  transition: border-color var(--duration-fast) var(--ease-standard),
              box-shadow var(--duration-fast) var(--ease-standard);
}

/* The ring lives on the wrapper, so the inner input must not draw one. */
.search__field:focus-within {
  border-color: var(--input-border-focus);
  box-shadow: var(--ring);
}

.search__input {
  flex: 1;
  min-inline-size: 0;
  block-size: 100%;
  border: 0;
  outline: 0;
  background: none;
  color: var(--input-fg);
  font-size: var(--text-md);
}
.search__input::placeholder {
  color: var(--input-placeholder);
  opacity: 1;
}
.search__input::-webkit-search-cancel-button,
.search__input::-webkit-search-decoration {
  -webkit-appearance: none;
  appearance: none;
}

.search__clear {
  display: grid;
  place-items: center;
  inline-size: var(--space-7);
  block-size: var(--space-7);
  border-radius: var(--radius-circle);
  color: var(--fg-subtle);
  font-size: var(--icon-sm);
  transition: var(--transition-colors);
}
.search__clear:hover {
  background: var(--bg-inset);
  color: var(--fg-default);
}
.search__clear[hidden] {
  display: none;
}

.search__panel {
  position: absolute;
  z-index: var(--z-dropdown);
  inset-block-start: calc(100% + var(--space-2-5));
  inset-inline: 0;
  padding: var(--space-3);
  border: var(--border-hairline) solid var(--border-subtle);
  border-radius: var(--radius-xl);
  background: var(--bg-surface);
  box-shadow: var(--shadow-overlay);
}
.search__panel[hidden] {
  display: none;
}

.search__panel-title {
  display: block;
  padding: var(--space-1) var(--space-2-5) var(--space-2-5);
  color: var(--fg-subtle);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
}

.search__option {
  display: flex;
  align-items: center;
  gap: var(--gap-inline-md);
  inline-size: 100%;
  padding: var(--space-2-5);
  border-radius: var(--radius-md);
  color: var(--fg-body);
  font-size: var(--text-md);
  text-align: start;
  transition: var(--transition-colors);
}
.search__option .icon {
  color: var(--fg-subtle);
  font-size: var(--icon-md);
}
/* aria-activedescendant means the option never takes DOM focus, so the
   highlight has to be a class the script drives. */
.search__option:hover,
.search__option.is-active {
  background: var(--bg-brand-subtle);
  color: var(--brand-text);
}
.search__option:hover .icon,
.search__option.is-active .icon {
  color: var(--brand);
}

.search__empty {
  padding: var(--space-3);
  color: var(--fg-muted);
  font-size: var(--text-md);
}

/* ---- Cart summary ---------------------------------------------------- */
.cart-summary {
  flex: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-0-5);
  min-inline-size: 5.875rem;
  white-space: nowrap;
}
.cart-summary__label {
  color: var(--fg-body);
  font-size: var(--text-md);
  font-weight: var(--weight-bold);
}
.cart-summary__total {
  color: var(--fg-muted);
  font-size: var(--text-xs);
}
.cart-summary__total b {
  color: var(--brand-text);
  font-size: var(--text-lg);
  font-weight: var(--weight-extrabold);
  font-variant-numeric: tabular-nums;
}

.menu-btn {
  display: none;
}


/* ==========================================================================
   09 · PROMO BAR & COUNTDOWN
   ========================================================================== */

.promo-bar {
  position: relative;
  z-index: var(--z-raised);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--gap-inline-lg);
  min-block-size: var(--promobar-height);
  margin-block-start: var(--space-4);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--promobar-radius);
  background: var(--promobar-bg);
  color: var(--fg-on-brand);
  padding-left: 20px;
}

.promo-bar__links {
  display: flex;
  align-items: center;
  gap: clamp(var(--space-4), 2.4vw, var(--space-8));
  min-inline-size: 0;
}

.promo-bar__links a {
  display: inline-flex;
  align-items: center;
  gap: var(--gap-inline-sm);
  padding: var(--space-2);
  border-radius: var(--radius-sm);
  color: var(--fg-on-brand);
  font-size: var(--text-lg);
  font-weight: var(--weight-extrabold);
  white-space: nowrap;
  transition: opacity var(--duration-fast) var(--ease-standard),
              transform var(--duration-base) var(--ease-out);
}
.promo-bar__links a:hover {
  opacity: 1;
  transform: translateY(-2px);
}

.category-trigger {
  display: inline-flex;
  align-items: center;
  gap: var(--gap-inline-sm);
  min-block-size: var(--btn-height-lg);
  padding-inline: var(--space-5);
  border-radius: var(--radius-pill);
  background: var(--bg-surface);
  color: var(--fg-default);
  font-size: var(--text-lg);
  font-weight: var(--weight-extrabold);
  white-space: nowrap;
  box-shadow: var(--shadow-lg);
  transition: var(--transition-elevate);
}
.category-trigger:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-xl);
}
.category-trigger__caret {
  font-size: var(--icon-sm);
  transition: rotate var(--duration-base) var(--ease-out);
}
.category-trigger[aria-expanded="true"] .category-trigger__caret {
  rotate: 180deg;
}

/* ---- Countdown ------------------------------------------------------- */
.countdown {
  display: flex;
  align-items: center;
  gap: var(--gap-inline-lg);
}

.countdown__label {
  display: flex;
  align-items: center;
  gap: var(--gap-inline-sm);
  font-size: var(--text-sm);
  line-height: var(--leading-tight);
  font-weight: var(--weight-bold);
}
.countdown__label strong {
  display: block;
  color: inherit;
  font-size: var(--text-xl);
}

/* row-reverse, deliberately: the markup lists units in logical order
   (hours → minutes → seconds), but a clock must read left-to-right even
   inside an RTL document, because the digits themselves do. */
.timer {
  display: flex;
  flex-direction: row-reverse;
  align-items: center;
  gap: var(--gap-inline-sm);
}

.timer__unit {
  display: grid;
  justify-items: center;
  gap: var(--space-1-5);
}

.timer__value {
  display: grid;
  place-items: center;
  min-inline-size: var(--timer-cell-w);
  block-size: var(--timer-cell-h);
  padding-inline: var(--space-1);
  border-radius: 20%;
  background: var(--timer-cell-bg);
  color: var(--timer-cell-fg);
  font-size: var(--timer-value-size);
  font-weight: var(--weight-black);
  font-variant-numeric: tabular-nums;
  box-shadow: var(--shadow-sm);
}

.timer__unit small {
  color: inherit;
  font-size: var(--text-2xs);
  font-weight: var(--weight-bold);
}

/* Sized to the cell and centred inside it, so the colon sits on the digits'
   optical centre instead of drifting with the unit label beneath. */
.timer__sep {
  align-self: start;
  display: grid;
  place-items: center;
  block-size: var(--timer-cell-h);
  font-size: var(--timer-value-size);
  font-weight: var(--weight-bold);
  opacity: 0.55;
}

/* ---- Countdown block -------------------------------------------------
   The timer alone reads as three floating pills. Wrapping it with its own
   label in a darkening scrim turns it into one object that states what it
   is counting down to — and a dark scrim, not a light one, so the white
   label above it gains contrast rather than losing it. */
.countdown-block {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  inline-size: 100%;
  padding: var(--space-4);
  border: var(--border-hairline) solid var(--border-inverse);
  border-radius: var(--radius-xl);
  background: oklch(0 0 0 / 0.16);
}

.countdown-block__label {
  display: none;
  align-items: center;
  gap: var(--gap-inline-sm);
  color: inherit;
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
}
.countdown-block__label .icon {
  font-size: var(--icon-md);
}

/* Large variant, used in the flash-sale panel. */
.timer--lg {
  --timer-cell-w: 3.25rem;
  --timer-cell-h: 3.25rem;
  --timer-value-size: var(--text-4xl);
  --timer-cell-radius: var(--radius-md);
  gap: var(--gap-inline-xs);
}

/* ---- Category menu --------------------------------------------------
   Aligned to the trigger, which sits at the bar's inline-start. */
.category-menu {
  position: absolute;
  z-index: var(--z-dropdown);
  inset-block-start: calc(100% + var(--space-3));
  inset-inline-start: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-2);
  inline-size: 21rem;
  padding: var(--space-3);
  border: var(--border-hairline) solid var(--border-subtle);
  border-radius: var(--radius-xl);
  background: var(--bg-surface);
  color: var(--fg-body);
  box-shadow: var(--shadow-overlay-lg);
  animation: menu-in var(--duration-base) var(--ease-out);
}
.category-menu[hidden] {
  display: none;
}

/* The promo bar is `position: relative; z-index: --z-raised`, so it opens a
   stacking context and the menu's own --z-dropdown only ever ranks it
   *within* that context — against the page it is pinned at --z-raised.
   The slider controls sit at that same level and paint later in DOM order,
   and on the stacked mobile hero they land directly under the open menu,
   which disappeared behind them. Lift the whole bar for exactly as long as
   the menu is open, so --z-dropdown means what it says. */
.promo-bar:has(.category-menu:not([hidden])) {
  z-index: var(--z-dropdown);
}

@keyframes menu-in {
  from { opacity: 0; translate: 0 -0.5rem; }
  to   { opacity: 1; translate: 0 0; }
}

.category-menu a {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-block-size: 5rem;
  border-radius: var(--radius-md);
  background: var(--bg-surface-2);
  color: var(--fg-body);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  transition: var(--transition-colors), var(--transition-transform);
}
.category-menu a .icon {
  color: var(--fg-muted);
  font-size: var(--icon-lg);
  transition: var(--transition-colors);
}
.category-menu a:hover {
  background: var(--bg-brand-subtle);
  color: var(--brand-text);
  transform: translateY(-2px);
}
.category-menu a:hover .icon {
  color: var(--brand);
}


/* ==========================================================================
   10 · HERO
   --------------------------------------------------------------------------
   Slider occupies the reading-start column (right, in RTL); the
   spotlight product sits at the inline-end. The previous build got this
   arrangement by setting `direction: ltr` on the grid and then flipping
   each child back — the same result, reached without fighting the
   document direction.
   ========================================================================== */

.hero {
  display: grid;
  /* 20rem rather than 18.75: at the 1440 container the slider would
     otherwise run 3.7 : 1 against the spotlight card and unbalance the row. */
  grid-template-columns: minmax(0, 1fr) 20rem;
  gap: var(--gap-grid-lg);
  margin-block-start: var(--space-12);
  align-items: stretch;
}

/* ---- Slider ---------------------------------------------------------- */
.slider {
  position: relative;
  grid-column: 1;
  min-inline-size: 0;
  min-block-size: var(--slider-h);
}

/* Clipping lives on the viewport, not the slider, so the control tab can
   bleed past the top edge without being cut. */
/* block-size: 100% only resolves when the parent's height is definite. On
   the stacked mobile layout .slider is a column flex item sized by
   min-block-size, which is not definite, so the percentage fell back to auto
   — and with every slide absolutely positioned there was no in-flow content
   left to hold the box open, collapsing the hero to zero height. The
   matching minimum is the floor that cannot happen again. */
.slider__viewport {
  position: relative;
  block-size: 100%;
  min-block-size: var(--slider-h);
  border-radius: var(--radius-3xl);
  overflow: hidden;
  box-shadow: var(--shadow-lg);
}

/* The glow is a background layer, not a ::after overlay. As an overlay it
   painted on top of the product photograph and visibly washed it out. */
.slide {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: 47% 53%;
  background: var(--glow-brand), var(--gradient-brand);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--duration-slower) var(--ease-standard),
              visibility var(--duration-slower) step-end;
}

.slide.is-active {
  opacity: 1;
  visibility: visible;
  transition: opacity var(--duration-slower) var(--ease-standard),
              visibility 0s;
}

.slide__copy {
  position: relative;
  z-index: var(--z-raised);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  padding: var(--space-10) var(--space-10) var(--space-10) var(--space-6);
  color: var(--fg-on-brand);
  text-align: start;
      background: #167c5c;
}

.slide__eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  margin-block-end: var(--space-3);
  padding: var(--space-1-5) var(--space-3);
  border: var(--border-hairline) solid var(--border-inverse);
  border-radius: var(--radius-pill);
  /* A dark scrim, not a white one: a translucent white chip behind white
     text lightens the backdrop and costs contrast instead of adding it. */
  background: oklch(0 0 0 / 0.16);
  color: inherit;
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
}
.slide__eyebrow .icon {
  font-size: var(--icon-sm);
}

.slide__title {
  max-inline-size: 24rem;
  margin-block-end: var(--space-3);
  color: inherit;
  font-size: var(--text-6xl);
  font-weight: var(--weight-black);
  /* Persian display type at this weight needs positive leading and zero
     tracking; the previous -2px was crushing the letter joins. */
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-normal);
}

.slide__lede {
  max-inline-size: 22rem;
  color: inherit;
  font-size: var(--text-2xl);
  font-weight: var(--weight-medium);
}

.slide__copy .btn {
  margin-block-start: var(--space-7);
}

.slide__media {
  position: relative;
  z-index: var(--z-base);
  min-inline-size: 0;
  overflow: hidden;
}

/* Feather the photo into the red field. The copy column is at the
   physical right, so the fade must run toward the right — a physical
   direction that must not mirror. */
.slide__media::before {
  content: "";
  position: absolute;
  z-index: var(--z-raised);
  inset-block: 0;
  inset-inline-start: 0;
  inline-size: 4.75rem;
  background: linear-gradient(to right, transparent, var(--teal-600));
}

.slide__media img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  object-position: 50% 46%;
}

/* ---- Slider controls ------------------------------------------------
   Notched tab bleeding out of the viewport's top edge. The two flares
   use physical left/right: they are mirror images of one another, so
   mirroring them again under RTL would swap the wrong curves. */
.slider__controls {
  position : absolute;
    z-index: var(--z-raised);
    inset-block-start: calc(var(--space-3) * -1);
    inset-inline-end: var(--space-12);
    display: flex;
    align-items: center;
    gap: var(--gap-inline-sm);
    padding: 0 var(--space-4) var(--space-2-5);
    border-end-start-radius: var(--radius-3xl);
    border-end-end-radius: var(--radius-3xl);
    background: var(--bg-page);
    margin-top: 12px;
    right: 79px;
    left: 76%;
}

.slider__controls::before,
.slider__controls::after {
  content: "";
  position: absolute;
  inset-block-start: var(--space-3);
  inline-size: var(--space-7);
  block-size: var(--space-7);
  pointer-events: none;
}


/* 16px gap, not 8: it buys each 10px dot a 24px hit area (below) without
   the areas overlapping — WCAG 2.2 SC 2.5.8. */
.slider__dots {
  display: flex;
  align-items: center;
  gap: var(--gap-inline-lg);
}

.slider__dot {
  position: relative;
  inline-size: var(--space-2-5);
  block-size: var(--space-2-5);
  border-radius: var(--radius-pill);
  background: var(--border-strong);
  transition: inline-size var(--duration-base) var(--ease-out),
              background-color var(--duration-base) var(--ease-standard);
}

/* Invisible target around the visible 10px dot. The dot is 10px and the
   selected one stretches to 36, so a symmetric inset cannot produce a
   square: the block axis is what falls short, and that is the axis a thumb
   misses on. Pad the block axis to --tap-min and leave the inline axis to
   the gap, which is already --gap-inline-lg between neighbours. */
.slider__dot::after {
  content: "";
  position: absolute;
  inset-inline: -7px;
  inset-block: calc((var(--tap-min) - var(--space-2-5)) / -2);
}
.slider__dot:hover {
  background: var(--fg-subtle);
}
.slider__dot[aria-selected="true"] {
  inline-size: var(--space-9);
  background: var(--accent);
}

.slider__toggle {
  position: relative;
  display: grid;
  place-items: center;
  inline-size: var(--space-6);
  block-size: var(--space-6);
  margin-inline-start: var(--space-1);
  border-radius: var(--radius-circle);
  color: var(--fg-muted);
  font-size: var(--text-xs);
  transition: var(--transition-colors);
}

/* Same treatment as the dots: the tab is only ~34px tall, so the target is
   grown outward rather than the button, which would push the tab taller and
   change the notch it is cut into. */
.slider__toggle::after {
  content: "";
  position: absolute;
  inset: calc((var(--tap-min) - var(--space-6)) / -2);
}
.slider__toggle:hover {
  background: var(--bg-inset);
  color: var(--fg-default);
}
.slider__toggle .icon--play { display: none; }
.slider__toggle[aria-pressed="true"] .icon--play { display: block; }
.slider__toggle[aria-pressed="true"] .icon--pause { display: none; }


/* ==========================================================================
   11 · ASSURANCE STRIP
   ========================================================================== */

.assurance {
  display: none;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--gap-grid);
  margin-block-start: var(--space-6);
  padding: var(--space-5) var(--space-7);
  border: var(--border-hairline) solid var(--border-subtle);
  border-radius: var(--radius-xl);
  background: var(--bg-surface);
  box-shadow: var(--shadow-xs);
}

.assurance__item {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--gap-inline-md);
  color: var(--fg-muted);
  font-size: var(--text-md);
}

.assurance__item strong {
  display: block;
  color: var(--fg-default);
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
}


/* ==========================================================================
   12 · SECTION HEADING
   ========================================================================== */

/* A full-bleed track, not a plain container. The negative inline margin
   cancels the shell's gutter so this grid spans the viewport, then the named
   lines hand every section the same centred `content` column it had before.
   Any section that opts into `full` reaches the screen edge.

   Deliberately not `margin-inline: calc(50% - 50vw)`: html sets
   scrollbar-gutter: stable, so 100vw counts the scrollbar and that technique
   would push ~15px of content under the shell's overflow clip. Grid tracks
   resolve against the real content box and have no such error. */
.catalog {
  display: grid;
  grid-template-columns:
    [full-start] minmax(var(--container-inline), 1fr)
    [content-start] minmax(0, var(--container-max)) [content-end]
    minmax(var(--container-inline), 1fr) [full-end];
  margin-block-start: var(--section-gap);
  margin-inline: calc(var(--container-inline) * -1);
}

.catalog > * {
  grid-column: content;
}

.catalog > section + section {
  margin-block-start: var(--section-gap);
}

.section-head {
  display: grid;
  grid-template-columns: auto minmax(var(--space-4), 1fr) auto;
  align-items: center;
  gap: var(--gap-inline-lg);
  margin-block-end: var(--section-head-gap);
}

.section-head__title {
  display: flex;
  align-items: center;
  gap: var(--gap-inline-md);
  font-size: var(--text-4xl);
  font-weight: var(--weight-black);
}

/* Third grid cell. .section-head is a three-column grid — title, rule,
   actions — so a bare extra child wraps to a second row and lands under the
   title. Anything that sits beside "view all" belongs in here. */
.section-head__actions {
  display: flex;
  align-items: center;
  gap: var(--gap-inline-md);
}

.section-head__rule {
  block-size: var(--border-hairline);
  /* Fades away from the title, i.e. toward the physical left in RTL. */
  background: linear-gradient(to left, var(--border-default), transparent);
}

.section-head__sub {
  display: block;
  margin-block-start: var(--space-1);
  color: var(--fg-muted);
  font-size: var(--text-sm);
  font-weight: var(--weight-normal);
}


/* ==========================================================================
   13 · CATEGORY RAIL
   ========================================================================== */

.category-showcase {
  display: flex;
  align-items: stretch;
  gap: var(--gap-grid-lg);
}

/* Pill-rounded on the inline-end edge, where the notch button breaks
   out toward the rail. */
.category-showcase__intro {
  position: relative;
  flex: 0 0 13.125rem;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: var(--space-5) var(--space-7) var(--space-5) var(--space-5);
  border-start-start-radius: var(--radius-lg);
  border-end-start-radius: var(--radius-lg);
  border-start-end-radius: var(--radius-pill);
  border-end-end-radius: var(--radius-pill);
  background: var(--bg-brand-subtle);
}
.category-showcase__intro span {
  color: var(--fg-muted);
  font-size: var(--text-xs);
}
.category-showcase__intro strong {
  color: var(--brand-text);
  font-size: var(--text-4xl);
  font-weight: var(--weight-black);
}
.category-showcase__intro .notch-btn {
  position: absolute;
  inset-inline-end: calc(var(--space-5) * -1);
  inset-block-start: 50%;
  translate: 0 -50%;
  background: var(--accent);
  color: var(--fg-on-accent);
}
.category-showcase__intro .notch-btn:hover {
  background: var(--brand);
  color: var(--fg-on-brand);
}

.category-rail {
  flex: 1;
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: var(--gap-grid);
  min-inline-size: 0;
}

.category-tile {
  position: relative;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--gap-inline-md);
  min-inline-size: 0;
  block-size: 8.25rem;
  padding: var(--space-3) var(--space-2);
  border: var(--border-hairline) solid var(--border-default);
  border-radius: var(--radius-xl);
  background: var(--bg-surface);
  color: var(--fg-muted);
  transition: var(--transition-elevate), var(--transition-colors);
}

/* The brand fill lives on a pseudo-element rather than on `background`,
   because background-image gradients do not interpolate: swapping the
   surface for the gradient on :hover would snap in and snap back out.
   Fading a layer's opacity gives the same look in both directions. */
.category-tile::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  background: var(--gradient-brand-vivid);
  opacity: 0;
  transition: opacity var(--duration-base) var(--ease-out);
}
.category-tile .icon {
  font-size: var(--icon-2xl);
  transition: var(--transition-transform);
}
.category-tile span {
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  white-space: nowrap;
}

/* Pointer- and keyboard-driven only: the tile lights up while it is
   hovered or focused and fades back out on leave. The pressed tile is
   deliberately left unstyled so no category sits permanently filled. */
.category-tile:hover,
.category-tile:focus-visible {
  border-color: transparent;
  color: var(--fg-on-brand);
  box-shadow: var(--shadow-brand-md);
}
.category-tile:hover::before,
.category-tile:focus-visible::before {
  opacity: 1;
}

/* The lift is pointer feedback, so it stays off the focus ring path. */
.category-tile:hover {
  transform: translateY(-4px);
}
.category-tile:hover .icon {
  scale: 1.1;
}


/* ==========================================================================
   14 · PRODUCT CARD
   --------------------------------------------------------------------------
   One implementation with variants. The previous build had five
   near-identical copies (.product-card, .featured-card, .mini-product,
   .laptop-card, .deal-card) that had drifted apart in padding, radius
   and type size.
   ========================================================================== */

.product-card {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: var(--card-pad);
  border: var(--border-hairline) solid var(--card-border);
  border-radius: var(--card-radius);
  background: var(--card-bg);
  box-shadow: var(--card-shadow);
  transition: var(--transition-elevate);
}

/* Hover outline: a ring that traces the card's own rounded silhouette, bold
   brand along the bottom edge and faded out by mid-height.

   It sits ON the border box — pulled out by the hairline width, with the
   matching outer radius — so it replaces the border instead of drawing a
   second line inside it. The card therefore carries no `overflow: hidden`:
   clipping would slice the ring back to the padding box. `__media` does its
   own clipping for the image zoom, so nothing else needed it. */
.product-card::after {
  content: "";
  position: absolute;
  inset: calc(var(--border-hairline) * -1);
  border-radius: calc(var(--card-radius) + var(--border-hairline));
  padding: var(--product-accent-width);
  background: var(--product-accent-ring);
  /* Punch the middle out of the gradient so only the ring paints; without
     this the card would get a wash of brand across its whole face. */
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--duration-slow) var(--ease-out);
}

.product-card:hover,
.product-card:focus-within {
  transform: translateY(var(--card-lift));
  border-color: var(--card-border-hover);
  box-shadow: var(--card-shadow-hover);
}
.product-card:hover::after,
.product-card:focus-within::after {
  opacity: 1;
}

.product-card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--gap-inline-sm);
  min-block-size: var(--space-8);
}

.product-card__media {
  display: grid;
  place-items: center;
  block-size: var(--product-media-height);
  margin-block: var(--space-2) var(--space-3);
  overflow: hidden;
}
.product-card__media img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: contain;
  transition: var(--transition-transform);
}
.product-card:hover .product-card__media img {
  transform: scale(1.05);
}

/* Photographic shots fill a rounded frame edge to edge; cutout PNGs keep
   the default `contain` and float on the card surface. */
.product-card__media--cover {
  inline-size: 100%;
  border-radius: var(--radius-xl);
}
.product-card__media--cover img {
  object-fit: cover;
  object-position: center;
}

.product-card__title {
  display: -webkit-box;
  -webkit-line-clamp: var(--product-title-lines);
  line-clamp: var(--product-title-lines);
  -webkit-box-orient: vertical;
  overflow: hidden;
  min-block-size: calc(var(--text-md) * var(--leading-snug) * var(--product-title-lines));
  color: var(--fg-default);
  font-size: var(--text-md);
  font-weight: var(--weight-bold);
  line-height: var(--leading-snug);
}
.product-card__title a {
  transition: var(--transition-colors);
}
.product-card__title a:hover {
  color: var(--brand-text);
}

.product-card__sub {
  margin-block-start: var(--space-1);
  color: var(--fg-subtle);
  font-size: var(--text-xs);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.product-card__foot {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--gap-inline-md);
  margin-block-start: auto;
  padding-block-start: var(--space-4);
}

.product-card__cart {
  flex: none;
  display: grid;
  place-items: center;
  inline-size: var(--btn-height-lg);
  block-size: var(--btn-height-lg);
  border-radius: var(--btn-radius-square);
  background: var(--gradient-brand-vivid);
  color: var(--fg-on-brand);
  font-size: var(--icon-md);
  box-shadow: var(--shadow-brand-sm);
  transition: var(--transition-elevate);
}
.product-card__cart:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-brand-md);
}
.product-card__cart:active {
  transform: translateY(-1px);
  background: var(--brand-active);
}

/* ---- Spotlight variant (tall promoted product) ---------------------- */
.product-card--spotlight {
  grid-column: 2;
  min-inline-size: 0;
  padding-block-start: var(--space-4);
}

.product-card__banner {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--gap-inline-sm);
  block-size: var(--space-12);
  margin-block-end: var(--space-2);
  padding-inline: var(--space-5);
  border-radius: var(--radius-md);
  background: var(--gradient-brand-flat);
  color: var(--fg-on-brand);
  font-size: var(--text-2xl);
  font-weight: var(--weight-black);
  box-shadow: var(--shadow-brand-sm);
}

.product-card--spotlight .product-card__media {
  block-size: 11.875rem;
  padding-inline: var(--space-6);
}

.product-card--spotlight .product-card__title {
  font-size: var(--text-xl);
  min-block-size: calc(var(--text-xl) * var(--leading-snug) * 2);
}

.product-card--spotlight .product-card__sub {
  font-size: var(--text-sm);
}

/* ---- Row variant (horizontal list item) ------------------------------ */
.product-card--row {
  flex-direction: row;
  align-items: center;
  gap: var(--gap-inline-lg);
  min-block-size: 8.5rem;
  padding: var(--space-5);
  border: 0;
  border-radius: var(--radius-xl);
  background: none;
  box-shadow: none;
}
.product-card--row::after {
  display: none;
}
.product-card--row:hover,
.product-card--row:focus-within {
  transform: none;
  z-index: var(--z-raised);
  background: var(--bg-surface);
  box-shadow: var(--shadow-md);
}

/* A tinted tile, so a cutout PNG and a photo on white both read as the same
   kind of object; overflow stays visible so the rank badge can sit on the
   corner. */
/* No tile behind the thumbnail. It was there to give the generated
   placeholders a common ground, so a flat silhouette and a cropped photo read
   as the same kind of object. Real cutout photography does not need it and is
   hurt by it: the shot already carries its own shadow, and a tinted square
   behind it reads as a second frame. Overflow stays visible so the rank badge
   can sit on the corner. */
.product-card--row .product-card__media {
  position: relative;
  flex: 0 0 5.5rem;
  inline-size: 5.5rem;
  block-size: 5.5rem;
  margin-block: 0;
  border-radius: var(--radius-lg);
  overflow: visible;
}
.product-card--row .product-card__media img {
  border-radius: var(--radius-md);
}
/* A cropped photo fills the tile edge to edge; only cutouts sit inset on it,
   otherwise the tile reads as a second frame around the picture. */
.product-card--row .product-card__media--cover {
  padding: 0;
}
.product-card--row .product-card__media--cover img {
  border-radius: var(--radius-lg);
}

/* min-inline-size plus padding, not a fixed width: the list runs to twelve,
   and a hard 24px circle clipped the second digit off ۱۰–۱۲. Two digits
   widen it into a short pill instead. */
.rank {
  position: absolute;
  z-index: var(--z-raised);
  inset-block-start: calc(var(--space-2) * -1);
  inset-inline-start: calc(var(--space-2) * -1);
  display: grid;
  place-items: center;
  min-inline-size: var(--space-6);
  padding-inline: var(--space-1);
  block-size: var(--space-6);
  border: var(--border-thick) solid var(--bg-page);
  border-radius: var(--radius-circle);
  background: var(--accent);
  color: var(--fg-on-accent);
  font-size: var(--text-2xs);
  font-weight: var(--weight-extrabold);
  font-variant-numeric: tabular-nums;
}
/* The top three carry the brand colour — the only thing that makes a
   "best sellers" list worth ranking. */
.product-rows > :nth-child(-n + 3) .rank {
  background: var(--brand);
  color: var(--fg-on-brand);
}

.product-card__body {
  min-inline-size: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.product-card--row .product-card__title {
  min-block-size: 0;
  font-size: var(--text-md);
  font-weight: var(--weight-bold);
  line-height: var(--leading-snug);
}

/* Wraps as a group. Three columns across a laptop leave this body about
   235px, which the badge, the struck price and the payable price overrun
   together — and .price__now deliberately carries no `nowrap`, so what gave
   way first was the currency word, orphaned on its own line under the
   digits. Wrapping drops the whole `was` group above instead, which is the
   same shape the phone layout already uses. */
.product-card--row .price {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--gap-inline-sm);
}
.product-card--row .price__now {
  font-size: var(--text-xl);
}


/* ==========================================================================
   15 · STORE PICKS
   --------------------------------------------------------------------------
   The dark panel sits behind the first card column. Previously this was
   an absolutely positioned block with a hardcoded height: 530px,
   retuned to 920px at one breakpoint and unset at another — it broke the
   moment card content changed. The panel is now pinned to the grid's own
   block size (inset-block: 0), so it cannot disagree with its content.
   ========================================================================== */

/* The only full-bleed section. The rail runs to the screen edge so the
   peeked card is cut by the viewport rather than by a centred container,
   while --pad-edge puts the panel and controls back on the content column so
   they still line up with every other section.

   --pad-edge is the width of the grid's side track. Inside .picks and its
   absolutely positioned children, `100%` resolves against .picks' own box —
   the full viewport width — so the expression reproduces that track exactly. */
.picks {
  position: relative;
  padding-block-start: 8.25rem;
  /* Breathing room under the rail so the panel (inset-block: 0) reaches past
     the last card instead of stopping flush against it. */
  padding-block-end: var(--space-8);
}

.picks__panel {
  position: absolute;
  z-index: var(--z-base);
  inset-block: 0;
  inset-inline-start: var(--pad-edge);
  inline-size: calc(var(--picks-lead) + var(--picks-card-w) + var(--space-6));
  padding: var(--panel-pad);
  border-radius: var(--panel-radius);
  background: var(--panel-bg);
  color: var(--panel-fg);
  box-shadow: var(--shadow-accent-md);
}

/* --- Decorative field ----------------------------------------------------
   The panel carries z-index: var(--z-base), so it is a stacking context and a
   negative z-index here lands the ornament above the panel's own background
   but under every one of its children. Nothing else in the panel needs a
   z-index, and the heading keeps painting over the mark. */
.picks__decor {
  position: absolute;
  z-index: -1;
  inset: 0;
  overflow: hidden;
  border-radius: inherit;
  color: var(--panel-fg);
  /* Halftone rather than the ruled grid on the deals panel: this one is a
     narrow upright block, and rules at this width would read as lined
     paper. Dots give it texture without implying a direction. */
  background-image: radial-gradient(oklch(1 0 0 / 0.13) 1.2px, transparent 1.3px);
  background-size: 1.375rem 1.375rem;
  /* Held to the margins the rail card does not cover. */
  -webkit-mask-image: radial-gradient(ellipse 70% 62% at 50% 50%, transparent 30%, #000 100%);
  mask-image: radial-gradient(ellipse 70% 62% at 50% 50%, transparent 30%, #000 100%);
  pointer-events: none;
}

/* A target, bled off the block-end corner so only the sweep of the outer two
   rings crosses the panel — a whole set of concentric circles sitting inside
   the box would read as a logo. */
.picks__rings {
  position: absolute;
  inset-block-end: -4.25rem;
  inset-inline-start: -4.75rem;
  inline-size: 13.5rem;
  block-size: auto;
  opacity: 0.16;
}

/* Two of the six lines, in the band above the rail that is otherwise flat
   gradient. Hairline stroke: at this size the UI weight reads as a drawing
   rather than a watermark. */
.picks__glyph {
  position: absolute;
  opacity: 0.11;
  stroke-width: 1.1;
}
.picks__glyph--mug {
  inset-block-start: 1.25rem;
  inset-inline-end: 3.25rem;
  font-size: 2.75rem;
  transform: rotate(-10deg);
}
/* Block-end band, not alongside the first one: stacked in the head they land
   under the subtitle, and a glyph behind a line of text reads as a smudge. */
.picks__glyph--cap {
  inset-block-end: 0.75rem;
  inset-inline-end: 2.5rem;
  font-size: 2.25rem;
  opacity: 0.085;
  transform: rotate(9deg);
}

.picks__panel h2 {
  color: inherit;
  font-size: var(--text-4xl);
  font-weight: var(--weight-black);
}
.picks__panel p {
  margin-block-start: var(--space-1-5);
  color: inherit;
  opacity: 0.8;
  font-size: var(--text-md);
}

/* Bite taken out of the panel's inner edge, holding the notch button. */
.picks__notch {
  position: absolute;
  inset-block-start: var(--space-14);
  inset-inline-end: calc(var(--space-6) * -1);
  inline-size: var(--space-14);
  block-size: 4.625rem;
  border-radius: var(--radius-circle);
  background: var(--bg-page);
}

.picks__panel .notch-btn {
  position: absolute;
  z-index: var(--z-raised);
  inset-block-start: 4.4375rem;
  inset-inline-end: calc(var(--space-5) * -1);
  color: var(--brand);
}

/* View-all and the rail arrows share one row at the top inline-end. */
.picks__controls {
  position: absolute;
  z-index: var(--z-raised);
  inset-block-start: var(--space-12);
  inset-inline-end: calc(var(--pad-edge) + var(--space-2));
  display: flex;
  align-items: center;
  gap: var(--gap-inline-md);
}



/* ---- Rail: the shared product slider ---------------------------------
   Peek carousel. Cards do not flex, so the next one is deliberately cut by
   the rail's edge — that cut is what says there is more to scroll.

   overflow-y is pinned to hidden because setting only overflow-x makes the
   block axis compute to auto, and the cards' hover lift and shadow would
   then raise a stray vertical scrollbar. The block padding is what gives
   that lift and shadow room to render un-clipped. */
.rail {
  display: flex;
  gap: var(--rail-gap);
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-inline: contain;
  scroll-snap-type: inline mandatory;
  /* Block padding is for the cards' hover lift and shadow, not the
     scrollbar — it stays after the bar is hidden. */
  padding-block: var(--space-5) var(--space-7);
}

.rail > * {
  flex: 0 0 var(--rail-card-w);
  scroll-snap-align: start;
}

/* ---- Rail navigation -------------------------------------------------- */
.rail-nav {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.rail-nav__btn {
  display: grid;
  place-items: center;
  inline-size: var(--tap-min);
  block-size: var(--tap-min);
  border: var(--border-hairline) solid var(--border-default);
  border-radius: var(--radius-circle);
  background: var(--bg-surface);
  color: var(--fg-body);
  font-size: var(--icon-md);
  box-shadow: var(--shadow-xs);
  transition: var(--transition-elevate), var(--transition-colors);
}
.rail-nav__btn:hover:not(:disabled) {
  border-color: var(--border-brand);
  background: var(--brand);
  color: var(--fg-on-brand);
  transform: translateY(-2px);
}
/* Muted fill rather than low opacity: a 0.4-opacity white circle on a
   near-white page is invisible, which reads as a rendering fault rather
   than as a disabled control. The disabled attribute carries the semantics. */
/* On the saturated deals panel a white circle would out-shout the CTA. */
.rail-nav__btn--onbrand {
  border-color: var(--border-inverse);
  background: oklch(0 0 0 / 0.16);
  color: var(--fg-on-brand);
  box-shadow: none;
}
.rail-nav__btn--onbrand:hover:not(:disabled) {
  border-color: var(--border-inverse);
  background: var(--bg-surface);
  color: var(--brand-text);
}
.rail-nav__btn--onbrand:disabled {
  border-color: var(--border-inverse);
  background: oklch(0 0 0 / 0.08);
  color: oklch(1 0 0 / 0.45);
}

.rail-nav__btn:disabled {
  border-color: var(--border-subtle);
  background: var(--bg-surface-2);
  color: var(--fg-subtle);
  cursor: not-allowed;
  box-shadow: none;
  transform: none;
}

/* ---- Full-bleed section ----------------------------------------------
   Opts a .catalog child into the grid's `full` track, then puts its own
   content back on the content column. --pad-edge reproduces the width of
   the grid's side track: inside the section and its absolutely positioned
   children, `100%` resolves against the section's own box — the full page
   width — so the expression matches that track exactly. */
.section--bleed {
  --pad-edge: max(var(--container-inline), (100% - var(--container-max)) / 2);
  grid-column: full;
  padding-inline-start: var(--pad-edge);
}

.section--bleed > .section-head {
  margin-inline-end: var(--pad-edge);
}

/* End padding measured from inside the rail: its containing block is the
   section's content box, already short by one track, so the halving drops
   out. Scrolled to the end, the last card lands on the content column. */
.section--bleed > .rail {
  padding-inline-end: max(var(--container-inline), 100% - var(--container-max));
}

/* Only the picks-specific parts; .rail supplies the rest. The lead offset is
   what leaves a slice of the dark panel visible before the first card. */
.picks__rail {
  position: relative;
  z-index: var(--z-raised);
  padding-inline-start: var(--picks-lead);
  scroll-padding-inline-start: var(--picks-lead);
}


/* ==========================================================================
   16 · PRODUCT ROWS  (best sellers)
   --------------------------------------------------------------------------
   The same rail as every other product slider, but three rows deep: the grid
   flows in columns, so the twelve items stack 3 x 4 and the ranking reads
   *down* each column. Three columns sit on the content column at a time and
   the fourth is cut by the container edge — the peek is what says there is
   more to scroll. app.js drives it through the shared [data-rail] handler,
   which steps by one card width plus one column gap: exactly one column.

   Dividers on the inline-end edge, dropped from the final column, so the
   outer edges of the grid stay clean.
   ========================================================================== */

/* display:grid overrides .rail's flex — this is the one rail whose track is
   more than one card tall. The negative inline-start margin cancels the
   first column's own card padding, so its artwork lines up with the section
   heading and the container edge instead of sitting 20px inside it; the
   hover surface still bleeds symmetrically into the shell gutter. That
   overhang is part of the rail's content box, so it comes back out of the
   width before it is split three ways. */
.product-rows {
  --rail-gap: var(--space-6);
  --rows-overhang: var(--space-5);
  --rows-visible: 3;
  --rail-card-w: calc(
    (100% - var(--rows-overhang) - var(--rail-gap) * (var(--rows-visible) - 1))
    / var(--rows-visible)
  );
  display: grid;
  grid-template-rows: repeat(3, minmax(0, auto));
  grid-auto-flow: column;
  grid-auto-columns: var(--rail-card-w);
  row-gap: 0;
  margin-inline-start: calc(var(--rows-overhang) * -1);
}

/* Drawn as a pseudo-element, not a border: the row has a 20px radius for
   its hover surface, and a border on a rounded box renders as a curved
   segment with visible corner hooks. Centred in the gap rather than on the
   card's own edge, so it reads as one rule between two columns rather than
   as a frame around each card. Inset from the ends for the same reason. */
.product-rows > *::before {
  content: "";
  position: absolute;
  inset-block: var(--space-4);
  inset-inline-end: calc(var(--rail-gap) / -2);
  inline-size: var(--border-hairline);
  background: var(--border-subtle);
}
/* Three at a time, because a column is three items: nothing follows the last
   one, so a rule there would hang off the end of the track. */
.product-rows > :nth-last-child(-n + 3)::before {
  display: none;
}


/* ==========================================================================
   17 · AMAZING DEALS
   ========================================================================== */

.deals {
  position: relative;
  display: grid;
  grid-template-columns: 17rem minmax(0, 1fr);
  gap: var(--space-8);
  padding: var(--space-14) var(--space-12);
  border-radius: var(--radius-4xl);
  /* A highlight over the flat gradient, thrown from just outside the corner
     the copy sits in. A single linear ramp across a panel this wide reads as
     a printed swatch; the pool of light gives it a light source. */
  background:
    radial-gradient(ellipse 42rem 24rem at 88% -12%, oklch(1 0 0 / 0.13), transparent 64%),
    var(--gradient-brand);
  overflow: hidden;
  box-shadow: var(--shadow-brand-md);
}

/* --- Decorative field ----------------------------------------------------
   One absolutely positioned layer holding everything ornamental, so the
   panel's own box keeps a single background declaration and the ornament can
   be sized, masked and switched off as one thing. */
.deals__decor {
  position: absolute;
  inset: 0;
  z-index: 0;
  color: var(--fg-on-brand);
  /* Hairline rules on a wide cell: a plotting grid, not a checkerboard. */
  background-image:
    linear-gradient(to right, oklch(1 0 0 / 0.055) var(--border-hairline), transparent 0),
    linear-gradient(to bottom, oklch(1 0 0 / 0.055) var(--border-hairline), transparent 0);
  background-size: 3.75rem 3.75rem;
  /* Cleared out of the middle, where the title, the countdown and the cards
     live — a rule crossing the timer digits is the one place this reads as
     noise rather than texture. */
  -webkit-mask-image: radial-gradient(ellipse 78% 72% at 50% 50%, transparent 22%, #000 100%);
  mask-image: radial-gradient(ellipse 78% 72% at 50% 50%, transparent 22%, #000 100%);
  pointer-events: none;
}

/* The band under the cards is the widest piece of bare gradient in the panel,
   so the chart runs along it and bleeds off two edges at once — a crop of
   something bigger, not a small drawing parked in a corner. */
.deals__candles {
  position: absolute;
  inset-block-end: -1.75rem;
  inset-inline-end: -1.5rem;
  inline-size: 19rem;
  block-size: auto;
  opacity: 0.14;
}

/* Glyphs, thinned to a hairline stroke: at this size the UI weight reads as
   a drawing, and the point is a watermark. One large mark bled off the
   corner below the CTA, two small ones in the clear band above the eyebrow,
   each at its own size and angle so the group reads as scattered. */
.deals__glyph {
  position: absolute;
  opacity: 0.09;
  stroke-width: 1.1;
}
.deals__glyph--percent {
  inset-block-end: -3rem;
  inset-inline-start: -2.5rem;
  font-size: 10.5rem;
  transform: rotate(-8deg);
}
.deals__glyph--spark {
  inset-block-start: 0.75rem;
  inset-inline-start: 2.5rem;
  font-size: 3rem;
  opacity: 0.1;
  transform: rotate(6deg);
}
.deals__glyph--gift {
  inset-block-start: 1.5rem;
  inset-inline-start: 9rem;
  font-size: 2.25rem;
  opacity: 0.075;
  transform: rotate(-12deg);
}

.deals__copy {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  gap: var(--space-5);
  min-inline-size: 0;
  color: var(--fg-on-brand);
}

/* Eyebrow, title, countdown and CTA are one evenly spaced stack. They were
   four fragments floating at arbitrary offsets, which is what made the
   panel read as unfinished. */

/* Label and rail arrows share the top line: the arrows on their own row cost
   a full 44px band above the title for two controls that read as secondary,
   and paired with the eyebrow they sit at the two edges of the copy column
   and give the stack a defined top rule. */
.deals__topline {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  inline-size: 100%;
}

.deals__eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1-5) var(--space-3);
  border: var(--border-hairline) solid var(--border-inverse);
  border-radius: var(--radius-pill);
  background: oklch(0 0 0 / 0.16);
  font-size: var(--text-xs);
  font-weight: var(--weight-bold);
  white-space: nowrap;
}
.deals__eyebrow .icon {
  font-size: var(--icon-sm);
}

.deals__title {
  color: inherit;
  font-size: var(--text-5xl);
  font-weight: var(--weight-black);
  line-height: var(--leading-tight);
}

/* Full width of the copy column. The label cannot then overrun the column
   at laptop widths, and a promo panel reads stronger with one wide CTA. */
.deals__copy .btn {
  inline-size: 100%;
  padding-inline: var(--space-4);
}

/* The timer sits a touch narrower than the countdown block that frames it,
   so the block's padding reads as a deliberate inset on all four sides
   instead of the digits crowding its inline edges. */
.deals .timer--lg {
  --timer-cell-w: 2.75rem;
  align-self: center;
  max-inline-size: calc(100% - var(--space-4));
}

/* Contained, not full-bleed: this rail lives inside the promo panel, so the
   peek is cut by the panel's own padding edge. */
.deals__grid {
  --rail-card-w: 17rem;
  --rail-gap: var(--space-5);
  min-inline-size: 0;
  padding-block-start: 0;
  /* Positioned so it paints above .deals__decor. Unpositioned, it would sit
     in the block layer underneath every positioned sibling — the ornament
     would draw over the cards. */
  position: relative;
  z-index: 1;
}

/* Cards on a saturated field lose their border and take a deeper shadow,
   so they read as lifted rather than pasted on. */
.deals__grid .product-card {
  border-color: transparent;
  box-shadow: var(--shadow-xl);
}
.deals__grid .product-card:hover {
  box-shadow: var(--shadow-2xl);
}


/* ==========================================================================
   18 · POPULAR CATEGORIES
   ========================================================================== */

.popular-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--gap-grid-lg);
}

.popular-card {
  position: relative;
  display: block;
  /* The only card that was held together by its shadow alone. Flat, its
     white label band sits on the page background at 1.00:1 and the card
     simply disappears, so it takes the hairline every other card has. */
  border: var(--border-hairline) solid var(--border-subtle);
  border-radius: var(--radius-2xl);
  background: var(--bg-surface);
  overflow: hidden;
  transition: var(--transition-elevate);
}
.popular-card:hover {
  transform: translateY(-6px);
  border-color: var(--border-brand);
}

/* Icon-on-tint rather than photography, and by choice rather than for want
   of assets. The four cards name four production lines whose objects have
   nothing in common in shape, scale or backdrop — a wide illuminated sign
   beside a mug beside a folded shirt — so four photographs read as four
   unrelated pictures. The line glyph on a brand tint reads as one system.
   Drop in real photography and this rule is the only thing to change. */
.popular-card__visual {
  /* Explicit: it is a <span>, and block-size does not apply to an inline
     box. The grid display it used to carry was blockifying it. */
  display: block;
  position: relative;
  inline-size: 100%;
  block-size: 11.875rem;
  overflow: hidden;
  background:
    radial-gradient(circle at 30% 22%, oklch(1 0 0 / 0.20), transparent 58%),
    var(--tint, var(--teal-500));
  color: var(--fg-on-brand);
}
/* Absolutely positioned rather than centred as a grid item: a percentage
   block-size on a grid item resolves against a content-sized row, which is
   circular, so it silently falls back to the intrinsic ratio and overflows. */
.popular-card__visual img {
  position: absolute;
  inset: 0;
  /* Both axes pinned. `inset: 0` alone is not enough: the base `img`
     reset sets height:auto, and for a replaced element that resolves from
     the intrinsic ratio and drops `bottom`, overflowing the tile. */
  inline-size: 100%;
  block-size: 80%;
  padding: var(--space-5) var(--space-4) var(--space-3);
  object-fit: contain;
  /* Standing on the floor of the tile, so all four line up. */
  object-position: 50% 100%;
  transition: var(--transition-transform);
}
.popular-card:hover .popular-card__visual img {
  transform: scale(1.06);
}

.popular-card__notch {
  position: absolute;
  inset-block-start: 10.75rem;
  inset-inline: 0;
  margin-inline: auto;
  display: grid;
  place-items: center;
  inline-size: var(--space-9);
  block-size: var(--space-9);
  border: var(--notch-ring) solid var(--bg-surface);
  border-radius: var(--radius-circle);
  background: var(--tint, var(--teal-500));
  color: var(--fg-on-brand);
  font-size: var(--icon-sm);
  transition: var(--transition-transform);
}
.popular-card:hover .popular-card__notch {
  scale: 1.12;
}

.popular-card__label {
  display: grid;
  place-items: center;
  block-size: 4.375rem;
  color: var(--fg-default);
  font-size: var(--text-xl);
  font-weight: var(--weight-bold);
}

/* Category tints — the only per-instance colour in the component. Deep
   enough that the white icon and notch arrow clear 3:1 as graphics. Named
   for the fill, since which category sits on which tile is content. */
.popular-card--teal      { --tint: var(--teal-500); }
.popular-card--teal-deep { --tint: var(--teal-700); }
.popular-card--navy      { --tint: var(--navy-600); }
.popular-card--navy-deep { --tint: var(--navy-800); }


/* ==========================================================================
   19 · LAPTOPS
   ========================================================================== */

.lineup-grid {
  --rail-gap: var(--gap-product-grid);
}

.lineup-promo {
  position: relative;
  /* Made a stacking context on purpose: the two ornaments below sit at
     z-index -1, which paints them over this box's own gradient and under
     the artwork. Without it they would drop behind the panel entirely. */
  isolation: isolate;
  display: block;
  border-radius: var(--radius-2xl);
  background: var(--gradient-brand-vivid);
  color: var(--fg-on-brand);
  overflow: hidden;
  box-shadow: var(--shadow-brand-md);
  transition: var(--transition-elevate);
}
.lineup-promo:hover {
  transform: translateY(-5px);
  box-shadow: var(--shadow-brand-lg);
}

/* Twill: hairline diagonals at a weave's pitch. A third motif rather than a
   reuse of either panel above, and the one that belongs on the apparel tile
   — the texture is the product. Cleared out of the middle so it never sits
   behind the garment itself. */
.lineup-promo::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset: 0;
  background-image: repeating-linear-gradient(
    135deg,
    oklch(1 0 0 / 0.075) 0 var(--border-hairline),
    transparent var(--border-hairline) 0.625rem
  );
  -webkit-mask-image: radial-gradient(ellipse 62% 52% at 50% 38%, transparent 25%, #000 100%);
  mask-image: radial-gradient(ellipse 62% 52% at 50% 38%, transparent 25%, #000 100%);
}

/* Stitch line: a dashed hoop struck through the label pill and bled off the
   block-end edge, so the pill reads as pinned to it rather than floating on
   bare gradient. Centred on the same axis as the pill, which is what keeps
   the two reading as one mark. */
.lineup-promo::after {
  content: "";
  position: absolute;
  z-index: -1;
  inset-inline: 0;
  inset-block-end: -5rem;
  margin-inline: auto;
  inline-size: 16.5rem;
  aspect-ratio: 1;
  border: 2px dashed oklch(1 0 0 / 0.16);
  border-radius: var(--radius-circle);
}

/* A cutout icon floating on the panel's own gradient, not a photograph
   filling the frame — which is what the gradient and the brand shadow on
   `.lineup-promo` were for in the first place; the old opaque JPG covered
   both. `contain` plus asymmetric padding: the block-end inset clears the
   label pill (80px tall, 20px up from the bottom) so the artwork cannot sit
   behind it. */
.lineup-promo img {
  inline-size: 100%;
  block-size: 18.25rem;
  /* The block-end inset is the label's own height plus its offset from the
     bottom edge — that is exactly where the pill's top lands, so the two can
     never overlap however tall the artwork is. */
  padding: var(--space-5) var(--space-6) calc(var(--space-20) + var(--space-5));
  object-fit: contain;
  transition: var(--transition-transform);
  padding-bottom: 10px;
}
.lineup-promo:hover img {
  transform: scale(1.04);
}

.lineup-promo__label {
  position: absolute;
  z-index: var(--z-raised);
  inset-inline: 0;
  margin-inline: auto;
  inset-block-end: var(--space-5);
  display: grid;
  place-items: center;
  inline-size: 7.375rem;
  block-size: var(--space-20);
  border-radius: var(--radius-circle);
  background: oklch(1 0 0 / 0.14);
  color: inherit;
  font-size: var(--text-4xl);
  font-weight: var(--weight-black);
}

/* Flush to the inline-end edge, so the visible half reads as a dome. */
.lineup-promo .notch-btn {
  position: absolute;
  z-index: var(--z-raised);
  inset-inline-end: 0;
  inset-block-end: var(--space-14);
  border-start-end-radius: 0;
  border-end-end-radius: 0;
}


/* ==========================================================================
   20 · PROMO TILES
   ========================================================================== */

.promo-tiles {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--gap-grid-lg);
}

/* A frame, nothing more. The banner artwork supplies its own background,
   headline and call to action, so anything painted here would sit on top
   of type that is already in the image. */
.promo-tile {
  display: block;
  border-radius: var(--radius-2xl);
  overflow: hidden;
  box-shadow: var(--shadow-md);
  transition: var(--transition-elevate);
}
.promo-tile:hover {
  transform: translateY(-5px);
  box-shadow: var(--shadow-lg);
}
.promo-tile img {
  inline-size: 100%;
  block-size: auto;
  /* Matches the encoded frame, so the row cannot go ragged and no crop is
     applied on top of the one already baked in. */
  aspect-ratio: 1200 / 470;
  object-fit: cover;
  transition: var(--transition-transform);
}
.promo-tile:hover img {
  transform: scale(1.03);
}


/* ==========================================================================
   21 · BRANDS
   ========================================================================== */

.brands-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--gap-grid-lg);
}

.brand-card {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--space-4);
  min-block-size: 9.25rem;
  padding: var(--space-5);
  border: var(--border-hairline) solid var(--border-subtle);
  border-radius: var(--radius-2xl);
  background: var(--bg-surface);
  text-align: start;
  box-shadow: var(--shadow-sm);
  transition: var(--transition-elevate);
}
.brand-card:hover {
  transform: translateY(-4px);
  border-color: var(--border-brand);
  box-shadow: var(--shadow-lg);
}

.brand-card__head {
  display: flex;
  align-items: center;
  gap: var(--gap-inline-lg);
}
.brand-card__logo {
  display: grid;
  place-items: center;
  inline-size: var(--space-12);
  block-size: var(--space-12);
  border-radius: var(--radius-md);
  background: var(--mark, var(--fg-muted));
  color: var(--fg-on-brand);
  /* Sized for a three-letter process code (SUB, DTF, EMB, CNC), not the
     single initial this plate previously held: at --text-3xl three black
     uppercase glyphs overrun the 48px plate. */
  font-size: var(--text-xl);
  font-weight: var(--weight-black);
  font-style: normal;
}
/* Deliberately not --mark. The plate colours are chosen to hold a white
   glyph at 3:1, which makes every one of them too light to serve as 15px
   text on either theme's surface (the amber measures 3.1:1 on white, the
   crimson 2.8:1 on the dark surface). The colour signature lives on the
   plate; the name stays legible. */
.brand-card__name {
  color: var(--fg-default);
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
}

/* Named for the fill, not for what sits on it — which process occupies
   which tile is content. */
.brand-card--amber    { --mark: var(--brandmark-amber); }
.brand-card--crimson  { --mark: var(--brandmark-crimson); }
.brand-card--graphite { --mark: var(--brandmark-graphite); }
.brand-card--indigo   { --mark: var(--brandmark-indigo); }

.brand-card__foot {
  display: flex;
  align-items: center;
  gap: var(--gap-inline-md);
}
.brand-card__count {
  color: var(--fg-muted);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.brand-card__dots {
  flex: 1;
  border-block-start: var(--border-thick) dotted var(--border-strong);
}
.brand-card__go {
  display: grid;
  place-items: center;
  inline-size: var(--space-7);
  block-size: var(--space-7);
  border-radius: var(--radius-xs);
  background: var(--accent);
  color: var(--fg-on-accent);
  font-size: var(--icon-sm);
  transition: var(--transition-transform);
}
.brand-card:hover .brand-card__go {
  transform: translateX(-3px);
}


/* ==========================================================================
   22 · SHOWCASE
   ========================================================================== */

.showcase {
  border-radius: var(--radius-3xl);
  overflow: hidden;
}

/* The banner artwork already carries the headline and CTA, so the whole
   image is the control — no overlaid copy layer. */
.showcase__banner {
  display: block;
  inline-size: 100%;
  padding: 0;
  border: 0;
  border-radius: 32px !important;
    background: #72e9c56b;
  cursor: pointer;
  transition: translate var(--duration-base) var(--ease-out),
              box-shadow var(--duration-base) var(--ease-out);
}
.showcase__banner:hover {
  translate: 0 -0.25rem;
  box-shadow: var(--shadow-md);
}

.showcase__banner img {
  display: block;
  inline-size: 100%;
  block-size: auto;
  border-radius: 45px;
}


/* ==========================================================================
   23 · FAQ
   --------------------------------------------------------------------------
   New. The header nav linked to #faq, which had no target anywhere in
   the document — every click was a dead scroll.
   ========================================================================== */

.faq-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--gap-inline-lg) var(--gap-grid-lg);
  align-content: start;
}

.faq-item {
  block-size: fit-content;
  border: var(--border-hairline) solid var(--border-subtle);
  border-radius: var(--radius-xl);
  background: var(--bg-surface);
  box-shadow: var(--shadow-xs);
  transition: var(--transition-colors), box-shadow var(--duration-base) var(--ease-out);
}
.faq-item[open] {
  border-color: var(--border-brand);
  box-shadow: var(--shadow-md);
}

.faq-item__q {
  display: flex;
  align-items: center;
  gap: var(--gap-inline-md);
  padding: var(--space-4) var(--space-5);
  color: var(--fg-default);
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  cursor: pointer;
  list-style: none;
  transition: var(--transition-colors);
}
.faq-item__q::-webkit-details-marker {
  display: none;
}
.faq-item__q:hover {
  color: var(--brand-text);
}
.faq-item__q .icon {
  flex: none;
  margin-inline-start: auto;
  color: var(--brand);
  font-size: var(--icon-md);
  transition: rotate var(--duration-base) var(--ease-out);
}
.faq-item[open] .faq-item__q .icon {
  rotate: 180deg;
}

.faq-item__a {
  padding: 0 var(--space-5) var(--space-5);
  color: var(--fg-muted);
  font-size: var(--text-md);
  line-height: var(--leading-relaxed);
}


/* ==========================================================================
   24 · BLOG
   ========================================================================== */

.blog-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--gap-grid-lg);
}

.blog-card {
  display: flex;
  flex-direction: column;
  border: var(--border-hairline) solid var(--border-subtle);
  border-radius: var(--radius-2xl);
  background: var(--bg-surface);
  overflow: hidden;
  box-shadow: var(--shadow-md);
  transition: var(--transition-elevate);
}
.blog-card:hover,
.blog-card:focus-within {
  transform: translateY(-5px);
  box-shadow: var(--shadow-lg);
}

.blog-card__media {
  block-size: 15rem;
  overflow: hidden;
}
.blog-card__media img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
  transition: transform var(--duration-slow) var(--ease-out);
}
.blog-card:hover .blog-card__media img {
  transform: scale(1.04);
}

.blog-card__title {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  padding: var(--space-4) var(--space-5) 0;
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  line-height: var(--leading-normal);
}
.blog-card__title a {
  transition: var(--transition-colors);
}
.blog-card__title a:hover {
  color: var(--brand-text);
}

.blog-card__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--gap-inline-lg);
  margin-block-start: auto;
  padding: var(--space-4) var(--space-5) var(--space-5);
}

.blog-card__date {
  display: inline-flex;
  align-items: center;
  gap: var(--gap-inline-xs);
  color: var(--fg-subtle);
  font-size: var(--text-xs);
  white-space: nowrap;
}
.blog-card__date .icon {
  font-size: var(--icon-sm);
}

.blog-card__more {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  color: var(--brand-text);
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
}
.blog-card__more .icon {
  font-size: var(--icon-sm);
  transition: var(--transition-transform);
}
.blog-card__more:hover .icon {
  transform: translateX(-3px);
}


/* ==========================================================================
   25 · FOOTER
   ========================================================================== */

.site-footer {
  position: relative;
  margin-block-start: var(--section-gap);
  padding: var(--space-20) var(--space-16) var(--space-9);
  border-radius: var(--footer-radius);
  background: var(--footer-bg);
  color: var(--footer-fg);
  box-shadow: var(--shadow-brand-md);
}

/* Page-coloured bite at the top edge, holding the back-to-top button. */
.site-footer::before {
  content: "";
  position: absolute;
  inset-block-start: calc(var(--space-6) * -1);
  inset-inline: 0;
  margin-inline: auto;
  inline-size: 4.5rem;
  block-size: var(--space-12);
  border-radius: var(--radius-circle);
  background: var(--bg-page);
}

.back-to-top {
  position: absolute;
  z-index: var(--z-raised);
  inset-block-start: calc(var(--space-3-5) * -1);
  inset-inline: 0;
  margin-inline: auto;
  display: grid;
  place-items: center;
  inline-size: var(--space-11);
  block-size: 3.375rem;
  border-radius: var(--radius-xl);
  background: var(--bg-surface);
  color: var(--accent-text);
  font-size: var(--icon-md);
  box-shadow: var(--shadow-md);
  transition: var(--transition-elevate), var(--transition-colors);
margin-bottom: -200px !important;
    bottom: -20px;
    top: -37px;
    background: #0f5f45;
    color: white;
    width: 55px;
    border-radius: 32px;
}
.back-to-top:hover {
  background: var(--accent-hover);
  color: var(--fg-on-accent);
  transform: translateY(-3px);
}

.footer-columns {
  display: grid;
  grid-template-columns: 1fr 1fr 1.45fr 0.72fr;
  gap: var(--space-10);
}

.footer-column h2 {
  margin-block-end: var(--space-5);
  color: inherit;
  font-size: var(--text-3xl);
  font-weight: var(--weight-extrabold);
}

.footer-column a {
  display: flex;
  align-items: center;
  gap: var(--gap-inline-sm);
  inline-size: max-content;
  padding-block: var(--space-2);
  color: inherit;
  font-size: var(--text-sm);
  transition: opacity var(--duration-fast) var(--ease-standard),
              transform var(--duration-base) var(--ease-out);
}
.footer-column a::before {
  content: "";
  inline-size: var(--space-1-5);
  block-size: var(--space-1-5);
  border-radius: var(--radius-circle);
  background: currentColor;
}
.footer-column a:hover {
  transform: translateX(calc(var(--space-1) * -1));
}

.footer-contact p {
  display: flex;
  align-items: flex-start;
  gap: var(--gap-inline-md);
  padding-block: var(--space-1-5);
  color: inherit;
  font-size: var(--text-sm);
  line-height: var(--leading-relaxed);
}
.footer-contact .icon {
  flex: none;
  margin-block-start: var(--space-1);
  font-size: var(--icon-md);
}
.footer-contact a {
  display: inline;
  inline-size: auto;
  padding: 0;
  opacity: 1;
  font-size: inherit;
  text-decoration: underline;
  text-decoration-color: oklch(1 0 0 / 0.4);
  text-underline-offset: 0.2em;
}
.footer-contact a::before {
  display: none;
}
.footer-contact a:hover {
  transform: none;
  text-decoration-color: currentColor;
}

.footer-trust {
  display: grid;
  gap: var(--gap-inline-md);
  align-content: start;
}

.trust-badge {
  display: grid;
  place-items: center;
  inline-size: 5.75rem;
  block-size: 5.625rem;
  padding: var(--space-2);
  border-radius: var(--radius-md);
  background: var(--trustbadge-bg);
  color: var(--trustbadge-fg);
  line-height: var(--leading-none);
  box-shadow: var(--shadow-sm);
  transition: var(--transition-transform);
}
.trust-badge:hover {
  transform: translateY(-3px);
}
.trust-badge b {
  font: italic var(--weight-black) 2.375rem/1 Georgia, "Times New Roman", serif;
}
.trust-badge span {
  font-size: var(--text-2xs);
  font-weight: var(--weight-extrabold);
}
.trust-badge__stars {
  display: flex;
  gap: 1px;
  color: var(--rating);
  font-size: 0.5rem;
}

.footer-bottom {
  display: grid;
  grid-template-columns: 0.75fr 2.2fr;
  align-items: end;
  gap: var(--space-11);
  margin-block-start: var(--space-9);
}

.socials > span {
  display: block;
  margin-block-end: var(--space-4);
  color: inherit;
  font-size: var(--text-sm);
}
.socials__links {
  display: flex;
  align-items: center;
  gap: var(--gap-inline-md);
}
.socials__links a {
  display: grid;
  place-items: center;
  inline-size: var(--tap-min);
  block-size: var(--tap-min);
  border: var(--border-hairline) solid var(--border-inverse);
  border-radius: var(--radius-circle);
  background: oklch(1 0 0 / 0.1);
  color: inherit;
  font-size: var(--icon-md);
  transition: var(--transition-elevate), var(--transition-colors);
}
.socials__links a:hover {
  background: var(--bg-surface);
  color: var(--brand-text);
  transform: translateY(-3px);
}

.footer-about {
  display: flex;
  align-items: center;
  gap: var(--space-7);
  padding: var(--space-7) var(--space-9);
  border: var(--border-hairline) solid var(--border-inverse);
  border-radius: var(--radius-2xl);
  /* Darkening scrim rather than a lightening one, so the 12px copy inside
     gains contrast against the red footer instead of losing it. */
  background: oklch(0 0 0 / 0.12);
}
.footer-about p {
  color: inherit;
  font-size: var(--text-xs);
  line-height: var(--leading-relaxed);
}

/* White, because it sits on the brand-teal footer field. */
.footer-logo {
  flex: 0 0 9.5rem;
  inline-size: 9.5rem;
  color: inherit;
}
.footer-logo .brand__mark {
  fill: currentColor;
}


/* ==========================================================================
   26 · DRAWER, BACKDROP, TOAST
   ========================================================================== */

.backdrop {
  position: fixed;
  z-index: var(--z-drawer-backdrop);
  inset: 0;
  display: block;
  inline-size: 100%;
  background: var(--overlay);
  animation: fade-in var(--duration-base) var(--ease-standard);
}
.backdrop[hidden] {
  display: none;
}

@supports (backdrop-filter: blur(1px)) {
  .backdrop {
    backdrop-filter: blur(4px);
  }
}

@keyframes fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Enters from the inline-start edge (right, in RTL). The 100% translate
   is physical, which is correct: the panel is pinned to the right, so
   +x is off-screen. */
.drawer {
  position: fixed;
  z-index: var(--z-drawer);
  inset-block: 0;
  inset-inline-start: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  inline-size: var(--drawer-width);
  padding: var(--space-9) var(--space-7);
  background: var(--drawer-bg);
  box-shadow: var(--drawer-shadow);
  overflow-y: auto;
  animation: drawer-in var(--duration-slow) var(--ease-out);
}
.drawer[hidden] {
  display: none;
}

@keyframes drawer-in {
  from { translate: 100% 0; }
  to   { translate: 0 0; }
}

.drawer__close {
  align-self: flex-end;
  display: grid;
  place-items: center;
  inline-size: var(--tap-min);
  block-size: var(--tap-min);
  border-radius: var(--radius-md);
  background: var(--bg-surface-3);
  color: var(--fg-body);
  font-size: var(--icon-md);
  transition: var(--transition-colors);
}
.drawer__close:hover {
  background: var(--bg-inset);
}

.drawer .brand {
  margin: var(--space-3) auto var(--space-8);
}

.drawer__nav {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
.drawer__nav a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-block-size: 3.375rem;
  padding-inline: var(--space-4);
  border-radius: var(--radius-md);
  color: var(--fg-body);
  font-size: var(--text-xl);
  font-weight: var(--weight-medium);
  transition: var(--transition-colors);
}
.drawer__nav a:hover {
  background: var(--bg-brand-subtle);
  color: var(--brand-text);
}
.drawer__nav .icon {
  color: var(--fg-subtle);
  font-size: var(--icon-sm);
}

/* ---- Drawer disclosure ------------------------------------------------
   The product lines, folded. Shares the row metrics of a plain nav link so
   the group does not read as a different kind of thing from its siblings —
   only the caret says it opens rather than navigates. */
.drawer__group > summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-block-size: 3.375rem;
  padding-inline: var(--space-4);
  border-radius: var(--radius-md);
  color: var(--fg-body);
  font-size: var(--text-xl);
  font-weight: var(--weight-medium);
  cursor: pointer;
  transition: var(--transition-colors);
}
/* The default marker is a physical-left triangle that does not mirror, and
   it would sit beside a caret that already says the same thing. */
.drawer__group > summary::-webkit-details-marker { display: none; }
.drawer__group > summary::marker { content: ""; }

.drawer__group > summary:hover,
.drawer__group[open] > summary {
  background: var(--bg-brand-subtle);
  color: var(--brand-text);
}

.drawer__caret {
  transition: rotate var(--duration-base) var(--ease-out);
}
.drawer__group[open] .drawer__caret {
  rotate: 180deg;
}

.drawer__sub {
  display: flex;
  flex-direction: column;
  padding-block: var(--space-1);
  /* A rule down the group's inline-start edge, so six indented rows read as
     belonging to the summary above them rather than as six more top-level
     destinations. */
  margin-inline-start: var(--space-6);
  padding-inline-start: var(--space-3);
  border-inline-start: var(--border-hairline) solid var(--border-subtle);
}

/* Icon leads here, unlike the top-level rows where a chevron trails. These
   are destinations of the same kind as the category tiles on the page, and
   the tiles lead with the same glyph. */
.drawer__sub a {
  justify-content: flex-start;
  gap: var(--gap-inline-md);
  min-block-size: var(--tap-min);
  font-size: var(--text-md);
}
.drawer__sub a .icon {
  font-size: var(--icon-md);
}

.drawer__foot {
  margin-block-start: auto;
  padding-block-start: var(--space-6);
  border-block-start: var(--border-hairline) solid var(--border-subtle);
}

/* ---- Toast ----------------------------------------------------------- */
.toast {
  position: fixed;
  z-index: var(--z-toast);
  inset-block-end: var(--space-7);
  left: 50%;
  translate: -50% 0;
  display: flex;
  align-items: center;
  gap: var(--gap-inline-md);
  max-inline-size: calc(100vw - var(--space-8));
  padding: var(--space-4) var(--space-5);
  border-radius: var(--toast-radius);
  background: var(--toast-bg);
  color: var(--toast-fg);
  font-size: var(--text-md);
  font-weight: var(--weight-medium);
  box-shadow: var(--shadow-overlay-lg);
  animation: toast-in var(--duration-slow) var(--ease-spring);
}
.toast[hidden] {
  display: none;
}
.toast .icon {
  flex: none;
  color: var(--brand-bright);
  font-size: var(--icon-md);
}

@keyframes toast-in {
  from { opacity: 0; translate: -50% var(--space-4); }
  to   { opacity: 1; translate: -50% 0; }
}

/* ---- Theme toggle ---------------------------------------------------- */
.theme-toggle .icon--moon { display: none; }
:root[data-theme="dark"] .theme-toggle .icon--sun { display: none; }
:root[data-theme="dark"] .theme-toggle .icon--moon { display: block; }


/* ==========================================================================
   26b · HEADER MENUS, MINI-CART & CONTACT DIALOG
   --------------------------------------------------------------------------
   Numbered as an extension of 26 rather than renumbering the four sections
   that follow: these are the same family of overlays, built on the same
   backdrop, the same surface tokens and the same `hidden`-attribute state
   the drawer and the toast already use.

   Three pieces:
     · .tool-menu / .menu-pop — the shared anchor and panel behind the cart
       and account dropdowns.
     · .cart-pop — the mini-cart, one row per basket line.
     · .modal / .channels — the contact dialog and its six channels.
   ========================================================================== */

/* ---- Anchor -----------------------------------------------------------
   .header-tools is a flex row, so without a positioned wrapper the panel
   would be placed against the header and not against the button that
   opened it. `flex: none` for the same reason .icon-btn needs it: a
   growing search field beside it would otherwise squeeze the wrapper. */
.tool-menu {
  position: relative;
  flex: none;
}

/* ---- Shared panel -----------------------------------------------------
   Anchored by its inline-end edge — the left edge, in RTL. The header
   tools sit against the viewport's inline-end, so a panel that grew
   further that way would run off the screen; growing back towards the
   middle of the page is the only direction with room for it. */
.menu-pop {
  position: absolute;
  z-index: var(--z-dropdown);
  inset-block-start: calc(100% + var(--space-3));
  inset-inline-end: 0;
  inline-size: min(24rem, calc(100vw - var(--space-8)));
  padding: var(--space-4);
  border: var(--border-hairline) solid var(--border-subtle);
  border-radius: var(--radius-2xl);
  background: var(--bg-surface);
  color: var(--fg-body);
  box-shadow: var(--shadow-overlay-lg);
  animation: menu-in var(--duration-base) var(--ease-out);
}
.menu-pop[hidden] {
  display: none;
}

/* A rotated square rather than a border triangle, so one shape carries
   both the panel's fill and its hairline. Offset to sit under the middle
   of a 52px trigger: half the button, less half the caret. */
.menu-pop::before {
  content: "";
  position: absolute;
  inset-block-start: -0.4rem;
  inset-inline-end: 1.25rem;
  inline-size: 0.75rem;
  block-size: 0.75rem;
  rotate: 45deg;
  border: var(--border-hairline) solid var(--border-subtle);
  border-block-end: 0;
  border-inline-start: 0;
  background: var(--bg-surface);
}


/* ---- Mini-cart -------------------------------------------------------- */
.cart-pop {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-3) var(--space-3);
}

.cart-pop__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--gap-inline-md);
  padding-inline: var(--space-2);
}
.cart-pop__title {
  color: var(--fg-default);
  font-size: var(--text-lg);
  font-weight: var(--weight-extrabold);
}
.cart-pop__count {
  color: var(--fg-subtle);
  font-size: var(--text-xs);
}

/* Roughly four rows, then it scrolls: a panel hanging from a sticky header
   must never grow taller than the viewport it hangs from. */
.cart-pop__list {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  max-block-size: min(21rem, 44vh);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.cart-pop__list[hidden] {
  display: none;
}

.cart-line {
  display: grid;
  grid-template-columns: 3.5rem minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--gap-inline-md);
  padding: var(--space-2);
  border-radius: var(--radius-lg);
  transition: var(--transition-colors);
}
.cart-line:hover {
  background: var(--bg-surface-2);
}

.cart-line__art {
  inline-size: 3.5rem;
  block-size: 3.5rem;
  padding: var(--space-1);
  border-radius: var(--radius-md);
  background: var(--bg-surface-2);
  /* Contained, not cropped: every product shot is a cutout, and cropping
     one pushes the silhouette's edges outside the frame. */
  object-fit: contain;
}
.cart-line__art--blank {
  display: grid;
  place-items: center;
  color: var(--fg-subtle);
}

.cart-line__title {
  color: var(--fg-default);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  line-height: var(--leading-snug);
  /* Two lines, then ellipsis. A product page adds its variant to the name
     ("ماگ … (سفید، بزرگ)") and an unclamped row would push the total out
     of the panel. */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

.cart-line__unit {
  margin-block-start: var(--space-1);
  color: var(--brand-text);
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  font-variant-numeric: tabular-nums;
}
.cart-line__unit small {
  color: var(--fg-subtle);
  font-size: var(--text-2xs);
  font-weight: var(--weight-medium);
}

.cart-line__side {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-1-5);
}

.cart-qty {
  display: inline-flex;
  align-items: center;
  gap: var(--space-0-5);
  padding: var(--space-0-5);
  border: var(--border-hairline) solid var(--border-default);
  border-radius: var(--radius-pill);
  background: var(--bg-surface);
}
.cart-qty__btn {
  display: grid;
  place-items: center;
  inline-size: 1.5rem;
  block-size: 1.5rem;
  border-radius: var(--radius-circle);
  color: var(--fg-muted);
  font-size: 0.8125rem;
  transition: var(--transition-colors);
}
.cart-qty__btn:hover {
  background: var(--bg-brand-subtle);
  color: var(--brand-text);
}
.cart-qty__value {
  min-inline-size: 1.25rem;
  color: var(--fg-default);
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  font-variant-numeric: tabular-nums;
  text-align: center;
}

.cart-line__remove {
  display: grid;
  place-items: center;
  inline-size: 1.75rem;
  block-size: 1.75rem;
  border-radius: var(--radius-sm);
  color: var(--fg-subtle);
  font-size: var(--icon-sm);
  transition: var(--transition-colors);
}
.cart-line__remove:hover {
  background: var(--danger-subtle);
  color: var(--danger-text);
}

.cart-pop__empty {
  display: grid;
  justify-items: center;
  gap: var(--space-2);
  padding: var(--space-6) var(--space-4) var(--space-3);
  text-align: center;
}
.cart-pop__empty[hidden] {
  display: none;
}
.cart-pop__empty-mark {
  display: grid;
  place-items: center;
  inline-size: 3.75rem;
  block-size: 3.75rem;
  border-radius: var(--radius-circle);
  background: var(--bg-brand-subtle);
  color: var(--brand);
}
.cart-pop__empty b {
  color: var(--fg-default);
  font-size: var(--text-md);
  font-weight: var(--weight-bold);
}
.cart-pop__empty span {
  color: var(--fg-muted);
  font-size: var(--text-sm);
  line-height: var(--leading-relaxed);
}
.cart-pop__empty .btn {
  margin-block-start: var(--space-2);
}

.cart-pop__foot {
  display: grid;
  gap: var(--space-3);
  padding-block-start: var(--space-3);
  border-block-start: var(--border-hairline) solid var(--border-subtle);
}
.cart-pop__foot[hidden] {
  display: none;
}
.cart-pop__sum {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding-inline: var(--space-2);
  color: var(--fg-muted);
  font-size: var(--text-sm);
}
.cart-pop__sum b {
  color: var(--fg-default);
  font-size: var(--text-xl);
  font-weight: var(--weight-extrabold);
  font-variant-numeric: tabular-nums;
}
.cart-pop__sum small {
  color: var(--fg-muted);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
}
.cart-pop__note {
  display: flex;
  align-items: center;
  gap: var(--gap-inline-sm);
  padding-inline: var(--space-2);
  color: var(--fg-subtle);
  font-size: var(--text-xs);
}
.cart-pop__note .icon {
  flex: none;
  color: var(--brand);
  font-size: var(--icon-sm);
}
.cart-pop__actions {
  display: grid;
  grid-template-columns: 1.3fr 1fr;
  gap: var(--space-2);
}


/* ---- Account panel ---------------------------------------------------- */
.account-pop {
  display: grid;
  gap: var(--space-3);
}

.account-pop__head {
  display: flex;
  align-items: center;
  gap: var(--gap-inline-md);
  padding: var(--space-1) var(--space-1) var(--space-3);
  border-block-end: var(--border-hairline) solid var(--border-subtle);
}
.account-pop__avatar {
  display: grid;
  place-items: center;
  flex: none;
  inline-size: 2.75rem;
  block-size: 2.75rem;
  border-radius: var(--radius-circle);
  background: var(--bg-brand-subtle);
  color: var(--brand);
}
/* An anchor since the profile page exists — faq.html sends the visitor to
   their details "from the account section in the header", and this block is
   where they look first. */
.account-pop__greeting {
  display: grid;
  gap: var(--space-0-5);
  min-inline-size: 0;
  border-radius: var(--radius-md);
  transition: var(--transition-colors);
}
a.account-pop__greeting:hover b {
  color: var(--brand-text);
}
a.account-pop__greeting:hover small {
  color: var(--fg-muted);
}
.account-pop__greeting b {
  color: var(--fg-default);
  font-size: var(--text-md);
  font-weight: var(--weight-bold);
}
.account-pop__greeting small {
  color: var(--fg-subtle);
  font-size: var(--text-xs);
}

.account-pop__links {
  display: grid;
  gap: var(--space-0-5);
}
/* Rows are anchors now that the four account pages exist; the selector
   keeps buttons too, because a row that acts rather than navigates —
   signing out, say — still belongs in this list and must not look
   different from its neighbours. */
.account-pop__links :is(a, button) {
  display: flex;
  align-items: center;
  gap: var(--gap-inline-md);
  inline-size: 100%;
  min-block-size: 2.75rem;
  padding-inline: var(--space-3);
  border-radius: var(--radius-md);
  color: var(--fg-body);
  font-size: var(--text-md);
  font-weight: var(--weight-medium);
  text-align: start;
  transition: var(--transition-colors);
}
.account-pop__links span {
  flex: 1;
  min-inline-size: 0;
}
.account-pop__links .icon {
  color: var(--fg-subtle);
  transition: var(--transition-colors);
}
/* The trailing chevron, which is furniture rather than a category mark. */
.account-pop__links .icon:last-child {
  font-size: var(--icon-sm);
}
.account-pop__links :is(a, button):hover {
  background: var(--bg-brand-subtle);
  color: var(--brand-text);
}
.account-pop__links :is(a, button):hover .icon {
  color: var(--brand);
}

/* ---- Support tab -------------------------------------------------------
   The dialog's permanent trigger, pinned to the inline-start edge (the
   right, in RTL) at mid-height. It used to close the account panel, which
   meant support was two clicks deep and invisible on a phone, where that
   panel folds into the drawer.

   Flush against the screen on the outer side and pill-rounded on the
   inner one, so it reads as attached to the edge rather than floating
   near it. Mid-height keeps it clear of the two other fixed things on the
   page: the footer's back-to-top chip and the product page's buy bar. */
.support-tab {
  position: fixed;
  /* Above the page, below --z-drawer-backdrop: an open drawer or dialog
     should cover this, not have it hover over its own scrim. */
  z-index: var(--z-sticky);
  inset-inline-start: 0;
  inset-block-start: 50%;
  translate: 0 -50%;
  display: grid;
  /* The label lives in a second track that is zero-wide at rest. `fr`
     tracks in a shrink-to-fit box resolve to max-content, so the pair
     animates open to exactly the label's width without it being measured
     in script. */
  grid-template-columns: auto 0fr;
  align-items: center;
  column-gap: 0;
  min-block-size: 3.25rem;
  padding-inline: var(--space-4);
  /* A surface-coloured ring on the three exposed sides. This floats over
     whatever the page happens to have at mid-height, and the hero's teal
     panel is close enough to the brand fill that the tab disappears into
     it; against the page itself the ring reads as part of the background.
     The screen edge keeps no border, so the tab stays flush. */
  border: var(--border-thick) solid var(--bg-surface);
  border-inline-start: 0;
  border-start-end-radius: var(--radius-pill);
  border-end-end-radius: var(--radius-pill);
  background: var(--gradient-brand-vivid);
  color: var(--fg-on-brand);
  font-size: var(--icon-md);
  box-shadow: var(--shadow-brand-md);
  transition:
    grid-template-columns var(--duration-base) var(--ease-out),
    column-gap var(--duration-base) var(--ease-out),
    padding-inline-end var(--duration-base) var(--ease-out),
    box-shadow var(--duration-base) var(--ease-out);
}

/* Clipped rather than hidden: `overflow` keeps the text in the
   accessibility tree, so it is still the button's accessible name at rest
   and no aria-label is needed to stand in for it. */
.support-tab__label {
  overflow: hidden;
  min-inline-size: 0;
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  white-space: nowrap;
}

.support-tab:hover,
.support-tab:focus-visible {
  grid-template-columns: auto 1fr;
  column-gap: var(--gap-inline-sm);
  padding-inline-end: var(--space-5);
  box-shadow: var(--shadow-brand-lg);
}


/* ---- Contact dialog ---------------------------------------------------
   Centred rather than anchored: it is a destination, not a disclosure, and
   it carries more than fits under a header button. Same z-layer as the
   drawer's backdrop family — --z-modal sits above --z-drawer so a trigger
   inside the drawer can hand over to it. */
.modal {
  position: fixed;
  z-index: var(--z-modal);
  inset-block-start: 50%;
  /* Physical `left`, not `inset-inline-start`: in RTL the logical property
     resolves to `right`, and pairing that with a negative translate walks
     the panel off the inline-start edge instead of centring it. The toast
     centres itself the same way, for the same reason. */
  left: 50%;
  translate: -50% -50%;
  inline-size: min(38rem, calc(100vw - var(--space-8)));
  max-block-size: min(88vh, 46rem);
  padding: var(--space-6);
  border: var(--border-hairline) solid var(--border-subtle);
  border-radius: var(--radius-4xl);
  background: var(--bg-surface);
  color: var(--fg-body);
  box-shadow: var(--shadow-overlay-lg);
  overflow-y: auto;
  overscroll-behavior: contain;
  animation: modal-in var(--duration-slow) var(--ease-spring);
}
.modal[hidden] {
  display: none;
}

/* `translate` carries the centring, so the keyframes must restate it —
   animating `scale` alone would leave the panel pinned by its top-left
   corner for the length of the animation. */
@keyframes modal-in {
  from { opacity: 0; translate: -50% calc(-50% + var(--space-5)); scale: 0.97; }
  to   { opacity: 1; translate: -50% -50%; scale: 1; }
}

.modal__head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  align-items: start;
  gap: var(--gap-inline-lg);
  padding-block-end: var(--space-5);
  border-block-end: var(--border-hairline) solid var(--border-subtle);
}
.modal__mark {
  display: grid;
  place-items: center;
  inline-size: 3rem;
  block-size: 3rem;
  border-radius: var(--radius-lg);
  background: var(--gradient-brand-vivid);
  color: var(--fg-on-brand);
  box-shadow: var(--shadow-brand-sm);
}
.modal__heading {
  display: grid;
  gap: var(--space-1);
  min-inline-size: 0;
}
.modal__title {
  color: var(--fg-default);
  font-size: var(--text-2xl);
  font-weight: var(--weight-extrabold);
}
.modal__lede {
  color: var(--fg-muted);
  font-size: var(--text-md);
  line-height: var(--leading-relaxed);
}
.modal__close {
  display: grid;
  place-items: center;
  inline-size: var(--tap-min);
  block-size: var(--tap-min);
  border-radius: var(--radius-md);
  background: var(--bg-surface-3);
  color: var(--fg-body);
  font-size: var(--icon-md);
  transition: var(--transition-colors);
}
.modal__close:hover {
  background: var(--bg-inset);
}

.modal__foot {
  display: grid;
  gap: var(--space-2-5);
  padding-block-start: var(--space-5);
  border-block-start: var(--border-hairline) solid var(--border-subtle);
}
.modal__foot p {
  display: flex;
  align-items: flex-start;
  gap: var(--gap-inline-md);
  color: var(--fg-muted);
  font-size: var(--text-sm);
  line-height: var(--leading-relaxed);
}
.modal__foot .icon {
  flex: none;
  margin-block-start: 0.15em;
  color: var(--brand);
  font-size: var(--icon-md);
}
.modal__foot a {
  color: var(--brand-text);
  font-weight: var(--weight-medium);
}
.modal__foot a:hover {
  text-decoration: underline;
}


/* ---- Channels ---------------------------------------------------------
   Scoped to .contact-modal on purpose. contact.html has its own .channel
   cards for the same six services, styled by contact.css, and an unscoped
   rule here would fight that page every time either component changed.

   One card per channel, tinted from a single custom property so the tile,
   the hover wash and the border all move together.

   Telegram and WhatsApp carry their official colours. Rubika and Bale do
   not: both brands are full-colour gradients that no single hex represents
   honestly, so those two take accents from this palette and are told apart
   by their glyph. */
.contact-modal .channels {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-3);
  padding-block: var(--space-5);
}

.contact-modal .channel {
  --channel: var(--brand);
  display: flex;
  align-items: center;
  gap: var(--gap-inline-md);
  block-size: 100%;
  padding: var(--space-3);
  border: var(--border-hairline) solid var(--border-subtle);
  border-radius: var(--radius-xl);
  background: var(--bg-surface);
  color: var(--fg-body);
  transition: var(--transition-elevate), var(--transition-colors);
}
.contact-modal .channel:hover {
  transform: translateY(-3px);
  border-color: color-mix(in oklab, var(--channel) 45%, transparent);
  background: color-mix(in oklab, var(--channel) 7%, var(--bg-surface));
  box-shadow: var(--shadow-lg);
}

.contact-modal .channel__mark {
  display: grid;
  place-items: center;
  flex: none;
  inline-size: 2.75rem;
  block-size: 2.75rem;
  border-radius: var(--radius-md);
  background: color-mix(in oklab, var(--channel) 15%, transparent);
  color: var(--channel);
}
.contact-modal .channel__body {
  display: grid;
  gap: var(--space-0-5);
  min-inline-size: 0;
}
.contact-modal .channel__name {
  color: var(--fg-default);
  font-size: var(--text-md);
  font-weight: var(--weight-bold);
}
.contact-modal .channel__value {
  color: var(--fg-subtle);
  font-size: var(--text-xs);
  overflow-wrap: anywhere;
}
/* The glyph points along the reading direction, so "forward" is -x here. */
.contact-modal .channel__go {
  margin-inline-start: auto;
  color: var(--fg-subtle);
  font-size: var(--icon-sm);
  transition: var(--transition-colors), var(--transition-transform);
}
.contact-modal .channel:hover .channel__go {
  color: var(--channel);
  transform: translateX(-3px);
}

.contact-modal .channel--telegram { --channel: #229ed9; }
.contact-modal .channel--whatsapp { --channel: #25d366; }
.contact-modal .channel--phone    { --channel: var(--brand); }
.contact-modal .channel--mobile   { --channel: var(--accent-text); }
.contact-modal .channel--rubika   { --channel: #7a4dd6; }
.contact-modal .channel--bale     { --channel: #d98324; }

/* Lifted a step on the dark surface: the light-mode values are mixed for
   contrast against white and go muddy against slate-900. */
:root[data-theme="dark"] .contact-modal .channel--telegram { --channel: #4fb6e8; }
:root[data-theme="dark"] .contact-modal .channel--whatsapp { --channel: #4bdd85; }
:root[data-theme="dark"] .contact-modal .channel--rubika   { --channel: #a183e8; }
:root[data-theme="dark"] .contact-modal .channel--bale     { --channel: #e8a452; }


/* ---- Drawer & footer triggers -----------------------------------------
   The drawer foot used to hold one button; it holds two now, so it needs
   a gap of its own. */
.drawer__foot {
  display: grid;
  gap: var(--space-2);
}

.footer-contact__cta {
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  gap: var(--gap-inline-sm);
  margin-block-start: var(--space-2);
  min-block-size: var(--btn-height);
  padding-inline: var(--space-5);
  border: var(--border-thin) solid var(--border-inverse);
  border-radius: var(--radius-pill);
  color: inherit;
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  transition: var(--transition-colors), var(--transition-transform);
}
.footer-contact__cta:hover {
  background: oklch(1 0 0 / 0.14);
  transform: translateY(-2px);
}


/* ---- Responsive ------------------------------------------------------- */

@media (max-width: 800px) {
  /* Anchor the panels to the header row rather than to a 52px button. At
     this width the tools cluster is pressed against the viewport edge, and
     a 24rem panel hung off one of those buttons runs straight off the
     screen; spanning the header instead is both wider and always in view. */
  .header-inner {
    position: relative;
  }
  .tool-menu {
    position: static;
  }
  .menu-pop {
    inset-inline: 0;
    inline-size: auto;
  }
  .menu-pop::before {
    display: none;
  }

  /* Matches the existing rule that folds every tool but cart and theme
     into the drawer — without this the wrapper survives its own button. */
  .tool-menu--account {
    display: none;
  }

  .cart-pop__actions {
    grid-template-columns: minmax(0, 1fr);
  }
  .contact-modal .channels {
    grid-template-columns: minmax(0, 1fr);
  }
  .modal {
    padding: var(--space-5) var(--space-4);
  }
}

@media (max-width: 540px) {
  .modal {
    inline-size: calc(100vw - var(--space-5));
    border-radius: var(--radius-2xl);
  }
  /* The badge is decoration, and at this width the title needs the column. */
  .modal__mark {
    display: none;
  }
  .modal__head {
    grid-template-columns: minmax(0, 1fr) auto;
  }
  .cart-line {
    grid-template-columns: 3rem minmax(0, 1fr) auto;
  }
  /* No hover to expand it on a phone, so it stays the icon it starts as —
     and a smaller one, because it is sitting over the content. */
  .support-tab {
    min-block-size: 2.875rem;
    padding-inline: var(--space-3);
  }
}

/* ==========================================================================
   27 · REVEAL ON SCROLL
   --------------------------------------------------------------------------
   Scoped to :root.js — the class is set by the inline bootstrap script.
   Without it the starting opacity of 0 would hide half the page whenever
   scripting is unavailable or app.js fails to load.
   ========================================================================== */

:root.js .reveal {
  opacity: 0;
  translate: 0 var(--space-5);
  transition: opacity var(--duration-slower) var(--ease-standard),
              translate var(--duration-slower) var(--ease-out);
}
:root.js .reveal.is-visible {
  opacity: 1;
  translate: 0 0;

}


/* ==========================================================================
   28 · RESPONSIVE
   ========================================================================== */

@media (max-width: 1240px) {
  :root {
    --gap-grid: var(--space-4);
    --gap-grid-lg: var(--space-5);
    --gap-product-grid: var(--space-8);
  }
  .hero { grid-template-columns: minmax(0, 1fr) 17.5rem; }
  .search { inline-size: 17.5rem; }
  .cart-summary { display: none; }
  /* 16rem, not 14: the eyebrow and the two rail arrows share one line and
     need ~15rem before the pill is squeezed against the buttons. */
  .deals { grid-template-columns: 16rem minmax(0, 1fr); padding-inline: var(--space-8); }
  .footer-columns { grid-template-columns: 1fr 1fr 1.4fr 0.6fr; gap: var(--space-7); }
  .site-footer { padding-inline: var(--space-11); }
}

@media (max-width: 1150px) {
  .primary-nav { display: none; }

  /* Nav column drops out; the search takes over the flexible track and
     the tools claim the inline-end edge. */
  .header-inner {
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas: "brand search tools";
  }
  .header-inner > .search { inline-size: auto; }

  .menu-btn {
    display: grid;
    place-items: center;
    flex: none;
    inline-size: var(--btn-height-lg);
    block-size: var(--btn-height-lg);
    border-radius: var(--btn-radius-square);
    background: var(--gradient-brand-vivid);
    color: var(--fg-on-brand);
    font-size: var(--icon-md);
    box-shadow: var(--shadow-brand-sm);
    transition: var(--transition-elevate);
  }
  .menu-btn:hover { transform: translateY(-2px); box-shadow: var(--shadow-brand-md); }

  /* The nav column collapses, so the tools must claim the inline-end
     edge rather than sit stranded beside the wordmark. */
  .header-tools { justify-content: flex-end; }

  .hero { grid-template-columns: minmax(0, 1fr) 16.25rem; }
  .slide { grid-template-columns: 56% 44%; }
  .slide__copy { padding-inline: var(--space-7) var(--space-3); }

  .category-rail {
    display: flex;
    overflow-x: auto;
    padding-block: var(--space-1) var(--space-3);
  }
  .category-tile { flex: 0 0 7.5rem; }

  :root {
    --picks-card-w: 16.5rem;
    --picks-lead: var(--space-8);
  }

  /* Rails size by card width, not column count. */
  .deals__grid { --rail-card-w: 15.5rem; }

  /* Two columns, not three: a third of a tablet's content column leaves the
     row card about 230px, and the name wraps to four lines next to the
     thumbnail. Still three rows deep — only the columns in view change. */
  .product-rows { --rows-visible: 2; }

  .popular-grid,
  .brands-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  .promo-tiles { gap: var(--space-4); }

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

/* A range, not a max-width: the three shortcuts drop out of the promo bar
   at tablet widths because the row is one line and the countdown needs it,
   but the phone layout below stacks that row and hands them back as a
   swipeable chip rail. Written as a band so the two rules cannot disagree. */
@media (min-width: 801px) and (max-width: 1150px) {
  .promo-bar__links a { display: none; }
}

@media (max-width: 800px) {
  :root {
    --card-pad: var(--space-4);
    --pad-panel: var(--space-6);
    --section-head-gap: var(--space-6);
    --gap-product-grid: var(--space-6);
    --logo-width: 9rem;
    --logo-width-compact: 8rem;
  }

  .site-shell {
    border-start-start-radius: var(--radius-xl);
    border-start-end-radius: var(--radius-xl);
  }

  /* Brand + full-size search + three 52px buttons do not fit one row on a
     phone. The search gets its own full-width row, then folds away once
     the visitor scrolls so the sticky header stays slim. */
  .header-inner {
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas:
      "brand  tools"
      "search search";
    row-gap: var(--space-3);
    padding-block: var(--space-3);
    min-block-size: auto;
    gap: var(--space-3);
  }
  .site-header.is-scrolled .header-inner {
    grid-template-areas: "brand tools";
    row-gap: 0;
    min-block-size: auto;
  }
  .site-header.is-scrolled .header-inner > .search { display: none; }

  /* …unless the visitor asks for it back. The row returns exactly as it was
     rather than as an overlay, so the suggestions panel keeps the field it
     is anchored to and nothing about the combobox has to know it was away. */
  .site-header.is-scrolled.is-search-open .header-inner {
    grid-template-areas:
      "brand  tools"
      "search search";
    row-gap: var(--space-3);
  }
  .site-header.is-scrolled.is-search-open .header-inner > .search { display: block; }

  .header-tools { gap: var(--space-2); }
  /* Keep only cart, theme and the search toggle reachable; the rest lives
     in the drawer. */
  .header-tools .icon-btn:not(.cart-btn):not(.theme-toggle):not(.search-toggle) {
    display: none;
  }
  /* The toggle is for the collapsed header alone — while the field is on
     screen, a button that reveals it is a second control for one job. */
  .site-header.is-scrolled .search-toggle { display: grid; }

  /* Two rows rather than one. In a single row the countdown and the category
     trigger sat at opposite ends of the bar with ~176px of dead brand green
     between them, and the timer had lost its label — three numbers on a
     green field, counting down to nothing the visitor could name. Stacked,
     the label comes back on the first row and the shortcuts return on the
     second as a swipeable rail. The `padding` shorthand is also what clears
     the physical `padding-left` on the base rule. */
  .promo-bar {
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-2-5);
    min-block-size: 0;
    padding: var(--space-3);
    /* The pill radius is measured against the bar's height, so on a two-row
       box the corner curve reached far enough inward to clip the "ساعت"
       caption off the last timer unit. */
    border-radius: var(--radius-2xl);
  }

  /* Label beside the timer, not opposite it: pushed apart they read as two
     unrelated things on one line, and the label is the only thing that says
     what the digits are counting down to. */
  .countdown {
    justify-content: flex-start;
    gap: var(--space-3);
  }
  .countdown__label { font-size: var(--text-xs); }
  .countdown__label strong { font-size: var(--text-md); }
  .timer__unit small { font-size: var(--text-2xs); }

  /* Swipeable, because four shortcuts in this typeface run past 358px. The
     negative margin lets the rail bleed to the bar's own padding edge, so
     the last chip is cut by the bar rather than stopping short of it —
     the same "there is more" cue the product rails use. */
  .promo-bar__links {
    gap: var(--space-2);
    margin-inline: calc(var(--space-3) * -1);
    padding-inline: var(--space-3);
    overflow-x: auto;
    overscroll-behavior-inline: contain;
    scrollbar-width: none;
  }
  .promo-bar__links::-webkit-scrollbar { display: none; }

  .promo-bar__links a {
    flex: none;
    min-block-size: var(--tap-min);
    padding-inline: var(--space-3);
    border-radius: var(--radius-pill);
    background: oklch(1 0 0 / 0.14);
    font-size: var(--text-sm);
  }

  /* 40px is a pointer size. The notch is a primary "go to the whole line"
     control on the category band and there is nothing beside it to crowd. */
  .notch-btn {
    inline-size: var(--tap-min);
    block-size: var(--tap-min);
  }

  /* The title box is already ~46px — two clamped lines — but the anchor only
     ever wrapped the text it happened to contain, so a one-line name gave a
     28px target for what is the main way into a product on a phone. Filling
     the box costs nothing: the anchor is the box's only child. */
  .product-card__title a {
    display: block;
    min-block-size: inherit;
  }

  /* Hero stacks: slider first, spotlight product beneath. */
  .hero {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
    margin-block-start: var(--space-8);
  }
  :root { --slider-h: 27rem; }
  .slide { grid-template-columns: 1fr; grid-template-rows: 48% 52%; }
  .slide__copy {
    align-items: center;
    padding: var(--space-8) var(--space-7) var(--space-3);
    text-align: center;
  }
  .slide__title,
  .slide__lede { max-inline-size: none; }
  .slide__media::before {
    inset-block: 0 auto;
    inset-inline: 0;
    inline-size: auto;
    block-size: var(--space-11);
    background: linear-gradient(to bottom, var(--teal-600), transparent);
  }
  .slider__controls {
    inset-inline: 0;
    margin-inline: auto;
    inline-size: fit-content;
  }

  /* The dots' target is 44px tall everywhere, but its inline size is capped
     by the gap to the next dot — expand past that and two dots share pixels,
     which is worse than a small target. So the gap is what grows on touch,
     and the target follows it to a full --tap-min square. Deliberately not
     done at desktop widths: a pointer does not need it, and the tab would
     stretch for nothing. */
  .slider__dots { gap: var(--space-9); }
  .slider__dot::after {
    inset-inline: calc((var(--tap-min) - var(--space-2-5)) / -2);
  }

  .assurance { grid-template-columns: 1fr; gap: var(--space-4); }
  .assurance__item { justify-content: flex-start; }

  /* The intro stops being a column beside the rail and becomes a header band
     above it. At 210px it was an orphan: a third of the content column, with
     148px of empty page beside it and its notch button parked in the middle
     of that gap, breaking out of an edge that no longer had anything on the
     far side to break into. Full width puts the button back on a real edge. */
  .category-showcase { display: block; }
  .category-showcase__intro {
    flex: none;
    inline-size: 100%;
    /* Grid, not the desktop column flex: the eyebrow and title still stack,
       but the button has to sit beside *both* of them rather than after the
       pair, and a two-row area is the only way to say that without adding a
       wrapper element the desktop layout has no use for. */
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "eyebrow  btn"
      "title    btn";
    align-items: center;
    column-gap: var(--space-4);
    margin-block-end: var(--space-5);
    padding: var(--space-4) var(--space-5);
    border-radius: var(--radius-xl);
  }
  .category-showcase__intro span   { grid-area: eyebrow; }
  .category-showcase__intro strong { grid-area: title; font-size: var(--text-2xl); }
  .category-showcase__intro .notch-btn {
    grid-area: btn;
    position: static;
    translate: none;
  }

  /* Picks panel rejoins the flow — no absolute positioning on mobile, and
     the rail runs full width beneath it with no lead offset. */
  :root {
    --picks-card-w: min(78vw, 19rem);
    --picks-lead: 0px;
  }
  .picks { padding-block: 0; }
  .picks__panel {
    position: relative;
    /* Reset: on a relatively positioned box inset-inline-start is an offset,
       not an anchor, so the desktop realignment would shove it sideways. */
    inset-inline-start: 0;
    inline-size: 100%;
    margin-block-end: var(--space-5);
  }
  /* Short, wide and filled with text at this width. The target shrinks so its
     arcs stay off the heading, and the second glyph goes — stacked inside a
     100px band the two read as one blob rather than as two marks. */
  .picks__rings {
    inline-size: 9rem;
    inset-block-end: -3rem;
    inset-inline-start: -3rem;
    opacity: 0.12;
  }
  .picks__glyph--cap { display: none; }
  .picks__glyph--mug {
    inset-block-start: 50%;
    translate: 0 -50%;
    inset-inline-end: 2rem;
    font-size: 2.5rem;
  }
  /* Both go. The notch is a bite out of the panel's *inline-end* edge, which
     on desktop faces the rail — there is panel on one side of it and page on
     the other, which is the whole idea. Once the panel is full-width that
     edge is the screen edge, and the pair rendered at x:-24 and x:-20 on a
     390px viewport: a half-circle of page colour and a half-visible arrow
     sliced by the shell's overflow clip. The destination is not lost —
     .picks__controls carries the same "مشاهده همه" link, in the flow,
     directly beneath. */
  .picks__notch,
  .picks__panel .notch-btn { display: none; }
  /* Rejoins the flow too. Pinned over the panel it collided with the notch
     button — two "view all" controls fighting for the same 30px. */
  .picks__controls {
    position: static;
    margin-block-end: var(--space-6);
  }
  /* Arrows are redundant where the rail is swipeable. */
  /* Arrows are redundant where the rail is swipeable. */
  .rail-nav { display: none; }
  .picks__rail { padding-block-start: var(--space-2); }

  /* One column in view, swiped a screen at a time — the peek would leave the
     name under 150px. Sized in vw rather than by --rows-visible so a slice of
     the next column still shows. No outward bleed: the shell gutter is only
     16px here. */
  .product-rows {
    --rail-card-w: min(84vw, 21rem);
    margin-inline-start: 0;
  }
  .product-card--row { padding: var(--space-5) var(--space-4) var(--space-5) 0; }

  /* The row's only control is the title link — the thumbnail is a plain
     <img> — so a 136px row carried a 22px target, and the twelve
     best-sellers were the hardest things on the page to hit. The card is
     already styled as one interactive unit (:hover and :focus-within both
     light the whole row), so stretching the anchor over it states what the
     styling already implies. Nothing else in the card is interactive, so
     there is nothing for the overlay to swallow. */
  .product-card--row { position: relative; }
  .product-card--row .product-card__title a::after {
    content: "";
    position: absolute;
    inset: 0;
  }
  /* Stacked, not inline: with the badge and old price on one line there is
     no room left for the payable price, and its currency word wrapped. */
  .product-card--row .price {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-1);
  }

  .deals {
    grid-template-columns: 1fr;
    gap: var(--space-5);
    padding: var(--space-7) var(--space-4) var(--space-5);
    border-radius: var(--radius-3xl);
  }
  .deals__copy { padding-block-start: 0; }
  .deals__copy { align-items: center; text-align: center; gap: var(--space-4); }
  /* The stack is centred and full-width here, so the panel has no free
     corners left. The ornament scales down to the two it still has — the
     grid tightens with the padding, the candles shrink, and the two small
     glyphs go, since at this width they land on the copy rather than beside
     it. */
  .deals__decor { background-size: 2.75rem 2.75rem; }
  .deals__candles { inline-size: 10.5rem; inset-block-end: -1.25rem; inset-inline-end: -1.25rem; opacity: 0.12; }
  /* Moved to the head of the panel: stacked, the cards run all the way into
     the block-end corner and would cover it there. */
  .deals__glyph--percent {
    font-size: 6.5rem;
    inset-block-start: -1.75rem;
    inset-block-end: auto;
    inset-inline-start: -1.5rem;
  }
  .deals__glyph--gift,
  .deals__glyph--spark { display: none; }
  /* Arrows are hidden here, so the eyebrow is the only thing left on the top
     line — space-between would strand it at the inline-start edge of a
     centred stack. */
  .deals__topline { justify-content: center; }
  .deals__title { text-align: center; }
  .countdown-block { inline-size: 100%; align-items: center; }
  .deals__grid {
    --rail-card-w: min(82vw, 19.375rem);
    --rail-gap: var(--gap-inline-lg);
    padding-block: var(--space-2) var(--space-4);
  }

  .popular-grid { gap: var(--space-3-5); }
  .lineup-grid { --rail-card-w: min(80vw, 18rem); --rail-gap: var(--space-4); }
  .popular-card__visual { block-size: 9.375rem; }
  .popular-card__notch { inset-block-start: 8.3125rem; }
  .popular-card__label { block-size: var(--space-14); font-size: var(--text-lg); }
  /* Shorter panel, and the label does not shrink with it — so the top inset
     is what gives way, never the block-end clearance. */
  .lineup-promo img {
    block-size: 15rem;
    padding-block-start: var(--space-3);
  }

  .promo-tiles {
    display: flex;
    gap: var(--space-3-5);
    overflow-x: auto;
    overscroll-behavior-inline: contain;
    scroll-snap-type: inline mandatory;
    padding-block-end: var(--space-3-5);
  }
  /* Wider than the other rails: the banner type is baked into the image,
     so every pixel of width is legibility. */
  .promo-tile { flex: 0 0 min(88vw, 26rem); scroll-snap-align: start; }

  .brands-grid { gap: var(--space-3-5); }

  .showcase { border-radius: var(--radius-2xl); }

  .blog-grid {
    display: flex;
    gap: var(--space-4);
    overflow-x: auto;
    overscroll-behavior-inline: contain;
    scroll-snap-type: inline mandatory;
    padding-block-end: var(--space-4);
  }
  .blog-card { flex: 0 0 min(84vw, 22.5rem); scroll-snap-align: start; }

  .site-footer {
    padding: var(--space-16) var(--space-6) var(--space-7);
    border-radius: var(--radius-3xl);
  }
  .footer-columns { grid-template-columns: 1fr 1fr; gap: var(--space-8) var(--space-6); }
  /* Two columns all the way down to 320px rather than one. Stacked, the
     eleven links plus the contact block ran the footer to 1526px on a 390px
     screen — a sixth of the whole page spent on a link list. `max-content`
     is what the desktop rule uses to keep the bullet tight against the
     label; it needs a ceiling once the track is 163px wide, or the longest
     labels punch out of their column. */
  .footer-column a {
    max-inline-size: 100%;
    padding-block: var(--space-3);
  }
  .footer-contact { grid-column: 1 / -1; }
  .footer-trust { grid-column: 1 / -1; grid-auto-flow: column; justify-content: center; }
  .footer-bottom { grid-template-columns: 1fr; gap: var(--space-7); }
  .socials { text-align: center; }
  .socials__links { justify-content: center; }
  .footer-about { flex-direction: column; align-items: flex-start; }
}

@media (max-width: 540px) {
  :root {
    --logo-width: 8rem;
    --logo-width-compact: 7.25rem;
  }
  .search__field { block-size: var(--space-12); padding-inline: var(--space-3-5); }
  .category-trigger { padding-inline: var(--space-3); font-size: var(--text-sm); }
  /* The label stays. It was dropped when the trigger had to share one line
     with the countdown; it now sits at the head of a rail that scrolls, so
     width is no longer the constraint — and "دسته‌بندی محصولات" is the one
     shortcut on that rail a bare grid glyph cannot stand in for. */
  .category-menu { inline-size: min(21rem, calc(100vw - var(--space-8))); }
  .timer__value { min-inline-size: var(--space-8); font-size: var(--text-xl); }
  :root { --slider-h: 26rem; }

  /* A 26px black title and a full-size pill cannot share a 360px row: the
     title's track shrinks past its min-content and the text runs under the
     pill. So the head stacks — title first, pill on its own row — and the
     decorative rule goes with the single-column layout. Stacking rather
     than shrinking the pill is deliberate: the phone is exactly where a
     "view all" needs to stay thumb-sized. */
  .section-head {
    grid-template-columns: minmax(0, 1fr);
    justify-items: start;
    gap: var(--gap-inline-lg);
  }
  .section-head__rule { display: none; }

  /* The sub is a `display: block` span inside a flex row, so on desktop it
     rides *beside* the title — which is the intent there. At 390px the two
     split a 358px line and both wrapped: "سوالات متداول" over two lines and
     its own lede over two more, interleaved. Wrapping the title lets the
     100% basis push the sub onto its own line, where the margin-block-start
     it already carries finally does something. */
  .section-head__title { flex-wrap: wrap; }
  .section-head__sub { flex-basis: 100%; }

  .popular-grid,
  .lineup-grid,
  .brands-grid { grid-template-columns: 1fr; }
}

/* (The wide-screen shell padding is now handled by the upper bound of
   --container-inline, so no breakpoint is needed for it.) */


/* ==========================================================================
   29 · INTRO CURTAIN
   --------------------------------------------------------------------------
   The full-screen mark animation that covers the page until it has loaded.

   Visibility is driven by one class — `is-loading` on <html> — and nothing
   else. The inline head script adds it before first paint and arms a
   watchdog that removes it; app.js removes it on load. A visitor whose
   scripts never arrive therefore never meets the curtain at all, because
   the rules below leave it hidden unless the class is present. That is
   the whole failure model: no JS, no curtain.

   The mark is inlined here rather than pulled from the sprite with <use>,
   because each shape animates on its own delay and shapes inside a
   referenced <symbol> cannot be addressed. Every shape carries
   pathLength="1", so one dash length draws all seventeen regardless of
   their real perimeters.
   ========================================================================== */

.curtain {
  position: fixed;
  inset: 0;
  z-index: var(--z-preloader);
  display: grid;
  place-items: center;
  overflow: hidden;
  background: var(--curtain-canvas);
  /* The resting state is the *exit* state, so removing `is-loading` plays
     the dissolve: the curtain lifts towards the viewer, blurs and clears. */
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transform: scale(1.04);
  filter: blur(8px);
  transition:
    opacity var(--curtain-exit) var(--ease-out),
    transform 760ms var(--ease-out),
    filter var(--curtain-exit) var(--ease-out),
    visibility 0s linear 760ms;
}

:root.is-loading .curtain {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: none;
  filter: none;
  /* The curtain is already on screen at first paint; there is nothing to
     transition into. */
  transition: none;
}

:root.is-loading,
:root.is-loading body {
  overflow: hidden;
}

/* ---- Halo -------------------------------------------------------------
   A single slow conic sweep, blurred past the point where its edges read
   as edges. It is what keeps a static wordmark from sitting on a dead
   field for two seconds. */
.curtain__halo {
  position: absolute;
  inline-size: min(160vmin, 60rem);
  aspect-ratio: 1;
  border-radius: var(--radius-circle);
  background: var(--curtain-halo);
  filter: blur(90px);
  opacity: 0;
  animation:
    curtain-halo-in 1000ms var(--ease-out) 80ms both,
    curtain-spin 11s linear infinite;
}

.curtain__stage {
  position: relative;
  display: grid;
  justify-items: center;
  gap: var(--space-6);
  padding-inline: var(--space-6);
  /* Two things depend on this: the glow below can sit at z-index -1 without
     falling behind the curtain's own background, and the sheen's `screen`
     blend resolves against the mark rather than the whole page. */
  isolation: isolate;
  animation: curtain-rise 720ms var(--ease-out) both;
}

/* Warm pool directly under the mark, so the lettering sits in light
   rather than on top of it. */
.curtain__stage::before {
  content: "";
  position: absolute;
  z-index: -1;
  inset-block-start: -18%;
  inset-inline-start: -14%;
  inline-size: 128%;
  block-size: 128%;
  background: var(--curtain-glow);
  filter: blur(28px);
  animation: curtain-breathe 4.5s var(--ease-in-out) infinite;
  pointer-events: none;
}

/* ---- The mark ---------------------------------------------------------- */
.curtain__logo {
  position: relative;
  inline-size: min(22rem, 64vw);
  aspect-ratio: 519.14 / 141.15;
  color: var(--curtain-mark);
  /* Also the stacking context the sheen screens against, which is why the
     glint lightens the mark and not the canvas behind it. */
  filter: var(--curtain-mark-shadow);
}

.curtain__art {
  inline-size: 100%;
  block-size: 100%;
}

/* Each shape draws its own outline, then floods. The stroke fades to
   nothing at the end, so the finished mark is exactly the logo — no
   permanent hairline the brand does not have. */
.curtain__art > g > * {
  fill: currentColor;
  fill-opacity: 1;
  stroke: var(--curtain-stroke);
  stroke-width: 1.9;
  stroke-opacity: 0;
  stroke-linejoin: round;
  stroke-dasharray: 1;
  stroke-dashoffset: 0;
  animation: curtain-draw var(--curtain-draw) var(--ease-in-out) both;
}

/* The cart builds first — tab, canopy, body, then the wheels — and the
   lettering follows it left to right. Written out because CSS has no
   index arithmetic; the two runs are 70ms and 42ms apart respectively. */
.curtain__mark > *:nth-child(1) { animation-delay:   0ms; }
.curtain__mark > *:nth-child(2) { animation-delay:  70ms; }
.curtain__mark > *:nth-child(3) { animation-delay: 150ms; }
.curtain__mark > *:nth-child(4) { animation-delay: 260ms; }
.curtain__mark > *:nth-child(5) { animation-delay: 310ms; }

.curtain__word > *:nth-child(1)  { animation-delay: 380ms; }
.curtain__word > *:nth-child(2)  { animation-delay: 422ms; }
.curtain__word > *:nth-child(3)  { animation-delay: 464ms; }
.curtain__word > *:nth-child(4)  { animation-delay: 506ms; }
.curtain__word > *:nth-child(5)  { animation-delay: 548ms; }
.curtain__word > *:nth-child(6)  { animation-delay: 590ms; }
.curtain__word > *:nth-child(7)  { animation-delay: 632ms; }
.curtain__word > *:nth-child(8)  { animation-delay: 674ms; }
.curtain__word > *:nth-child(9)  { animation-delay: 716ms; }
.curtain__word > *:nth-child(10) { animation-delay: 758ms; }
.curtain__word > *:nth-child(11) { animation-delay: 800ms; }

/* ---- Sheen ------------------------------------------------------------
   A gold band swept across the finished mark, clipped to the mark by
   masking with the logo file itself — the same artwork the sprite was cut
   from, so the glint can never drift out of register with the shapes.
   Screened, so it only ever adds light. */
.curtain__sheen {
  position: absolute;
  inset: 0;
  background-image: var(--curtain-sheen);
  background-repeat: no-repeat;
  background-size: 230% 100%;
  background-position: 130% 0;
  mix-blend-mode: screen;
  pointer-events: none;
  -webkit-mask-image: url("assets/logo.svg");
          mask-image: url("assets/logo.svg");
  -webkit-mask-size: 100% 100%;
          mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  animation: curtain-sheen 3s var(--ease-in-out) 1150ms infinite;
}

/* ---- Caption ----------------------------------------------------------
   No letter-spacing: this is Persian, and spacing joined script pulls the
   letters apart. The hairlines carry the formality instead. */
.curtain__label {
  display: flex;
  align-items: center;
  gap: var(--gap-inline-md);
  color: var(--curtain-label);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  animation: curtain-rise 720ms var(--ease-out) 900ms both;
}

.curtain__label::before,
.curtain__label::after {
  content: "";
  inline-size: clamp(1.5rem, 9vw, 3.5rem);
  block-size: var(--border-hairline);
  background: var(--curtain-rule);
}

/* ---- Progress ---------------------------------------------------------
   Driven from app.js against real load milestones, not a fixed animation:
   a bar that finishes before the page does is a lie the visitor can see. */
.curtain__track {
  position: relative;
  inline-size: min(13.5rem, 48vw);
  block-size: 3px;
  border-radius: var(--radius-pill);
  background: var(--curtain-track);
  overflow: hidden;
  animation: curtain-rise 720ms var(--ease-out) 1000ms both;
}

.curtain__fill {
  display: block;
  inline-size: 6%;
  block-size: 100%;
  border-radius: inherit;
  background: var(--curtain-fill);
  transition: inline-size var(--duration-slower) var(--ease-out);
}

/* ---- Print ------------------------------------------------------------- */
@media print {
  .curtain {
    display: none;
  }
}

/* ---- Keyframes --------------------------------------------------------- */
@keyframes curtain-draw {
  0% {
    fill-opacity: 0;
    stroke-opacity: 0.85;
    stroke-dashoffset: 1;
  }
  55% {
    fill-opacity: 0;
    stroke-opacity: 0.85;
    stroke-dashoffset: 0;
  }
  80% {
    fill-opacity: 1;
    stroke-opacity: 0.4;
  }
  100% {
    fill-opacity: 1;
    stroke-opacity: 0;
    stroke-dashoffset: 0;
  }
}

@keyframes curtain-sheen {
  0%, 22% { background-position: 130% 0; }
  70%, 100% { background-position: -40% 0; }
}

@keyframes curtain-spin {
  to { transform: rotate(1turn); }
}

@keyframes curtain-halo-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes curtain-breathe {
  0%, 100% { opacity: 0.55; }
  50%      { opacity: 0.9; }
}

@keyframes curtain-rise {
  from {
    opacity: 0;
    translate: 0 var(--space-3);
  }
  to {
    opacity: 1;
    translate: none;
  }
}


/* ==========================================================================
   30 · MOTION, CONTRAST, PRINT
   --------------------------------------------------------------------------
   Kept last so these declarations win. app.js reads the same media query
   and additionally stops the carousel auto-advancing — a zero-duration
   transition on a slide that still changes every 6.5s is not a fix.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  /* Reveal-on-scroll must never leave content invisible. */
  :root.js .reveal {
    opacity: 1;
    translate: none;
  }
  .product-card:hover,
  .blog-card:hover,
  .popular-card:hover,
  .brand-card:hover,
  .promo-tile:hover,
  .lineup-promo:hover,
  .btn:hover,
  .icon-btn:hover,
  .link-more:hover,
  .link-more:hover .icon,
  .channel:hover,
  .channel:hover .channel__go,
  .footer-contact__cta:hover {
    transform: none;
  }
}

@media (prefers-contrast: more) {
  :root {
    --border-subtle: var(--slate-400);
    --border-default: var(--slate-500);
    --fg-muted: var(--slate-700);
    --fg-subtle: var(--slate-600);
  }
  .product-card,
  .blog-card,
  .brand-card,
  .faq-item {
    border-width: var(--border-thick);
  }
}

@media (forced-colors: active) {
  .product-card,
  .blog-card,
  .brand-card,
  .category-tile,
  .faq-item,
  .search__panel,
  .menu-pop,
  .modal,
  .channel {
    border: 1px solid CanvasText;
  }
  .slider__dot[aria-selected="true"] {
    background: Highlight;
  }
}

@media print {
  .site-header,
  .promo-bar,
  .slider__controls,
  .drawer,
  .backdrop,
  .toast,
  .modal,
  .support-tab,
  .back-to-top,
  .menu-btn,
  .theme-toggle,
  .skip-link {
    display: none !important;
  }
  body {
    background: #fff;
    color: #000;
  }
  .site-shell {
    padding: 0;
    background: none;
    box-shadow: none;
  }
  .product-card,
  .blog-card,
  .brand-card {
    break-inside: avoid;
    box-shadow: none;
    border: 1px solid #999;
  }
  .reveal {
    opacity: 1;
    translate: none;
  }
  a[href^="http"]::after {
    content: " (" attr(href) ")";
    font-size: 0.8em;
  }
}
