/* =========================================================
   Gem Store — light + dark, black/white stars
   Tokens first. Nothing below this block picks a raw value.
   ========================================================= */

@font-face{ font-family:'Vazirmatn'; src:url('../fonts/Vazirmatn-Regular.woff2') format('woff2'); font-weight:400; font-display:swap; }
@font-face{ font-family:'Vazirmatn'; src:url('../fonts/Vazirmatn-Medium.woff2')  format('woff2'); font-weight:500; font-display:swap; }
@font-face{ font-family:'Vazirmatn'; src:url('../fonts/Vazirmatn-Bold.woff2')    format('woff2'); font-weight:700; font-display:swap; }
@font-face{ font-family:'Vazirmatn'; src:url('../fonts/Vazirmatn-Black.woff2')   format('woff2'); font-weight:900; font-display:swap; }

/* Registered so the clamp() below resolves to a real length. An unregistered
   custom property hands JS back the literal string "clamp(196px, 23vw, 326px)",
   which parseFloat turns into NaN — and the ray maths silently dies. */
@property --radius{
  syntax: "<length>";
  inherits: true;
  initial-value: 326px;
}

:root{
  /* The four-point star, once, as a mask — the same path the canvas scenes fill
     and the search asterism draws. Anything that needs the shape without an
     <svg> element of its own uses this. */
  --star-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-1.4 -1.4 2.8 2.8'%3E%3Cpath d='M0 -1.15C.18 -.36 .36 -.18 1.15 0 .36 .18 .18 .36 0 1.15 -.18 .36 -.36 .18 -1.15 0 -.36 -.18 -.18 -.36 0 -1.15Z'/%3E%3C/svg%3E");

  --t-sm:   clamp(.8125rem, .79rem + .12vw, .875rem);
  --t-base: clamp(.9375rem, .91rem + .16vw, 1rem);
  --t-lg:   clamp(1.0625rem, 1rem + .3vw, 1.1875rem);
  --t-xl:   clamp(1.375rem, 1.2rem + .9vw, 1.875rem);
  --t-2xl:  clamp(1.75rem, 1.25rem + 2.4vw, 3.375rem);

  --s-2:8px; --s-3:12px; --s-4:16px; --s-5:24px;
  --s-6:32px; --s-7:48px; --s-8:64px; --s-9:96px;
  --r-md:16px; --r-lg:20px; --r-full:999px;

  /* ═══ card fan ═══ rx ≫ ry keeps the arc nearly a straight line */
  --fan-card-w:  clamp(178px, 20vw, 272px);
  --fan-card-h:  calc(var(--fan-card-w) * 1.58);
  --fan-spread:  18;
  --fan-rx:      2.0;
  --fan-ry:      0.28;
  --fan-tilt:    1;
  --fan-dur:     1.65s;
  /* Read back by JS, so these two are in ms — parseFloat('2.1s') is 2.1. */
  --fan-hold:    500ms;    /* the closed pile sits visible this long first */
  --fan-deal:    1250ms;   /* then it sweeps the whole arc in this long */
  --fan-focus:   1.5;      /* scale of the card once it reaches screen centre */
  --fan-spin:    360deg;   /* one full turn about the card's own vertical axis */
  --sun-drift:   1;        /* multiplier on how far the sun wanders */

  /* rotating ring — the radius has to shrink with the viewport. Held fixed, the
     ring keeps its 3D width while its grid column narrows, and the side cards
     walk straight over the text next to them. */
  /* height bumped 290→300: at the width/art-ratio this card is built around,
     title + a real two-line description + price didn't actually fit in the
     old body budget — see .card__art/.card__body below. */
  --card-w:208px; --card-h:300px; --tilt:-7deg;
  /* Radius sets how far apart the slots sit: the chord between two of the eight
     is 2R·sin(22.5°), so tightening the ring is the only way to close the gap
     without growing the cards. */
  --radius:clamp(213px, 21.4vw, 304px);
  --face:0.5;    /* how far each card un-turns to look back at the viewer.
                    0 = a plain cylinder, 1 = every card permanently flat-on */
  --ring-spin:.9s;
  --ring-show:1; /* slots either side of the front that stay fully visible */

  --ease-out: cubic-bezier(.2,.8,.25,1);
  /* Expo.out — منحنی پیشنهادی سبک «تاریکِ سینمایی»: شروع تند و توقف خیلی
     نرم. فقط برای بازخورد فشردن و روشن‌شدن کارت‌ها استفاده می‌شود، نه
     جایگزین سراسری --ease-out که کل حرکت‌های موجود روی آن تنظیم شده‌اند. */
  --ease-expo: cubic-bezier(.16,1,.3,1);
  --ease-fan: cubic-bezier(.18,.9,.28,1);
}

:root, :root[data-theme="light"]{
  --bg:#FFFFFF; --surface:#FFFFFF; --surface-2:#F5F5F8; --line:#E4E4EC;
  --ink:#0B0B10; --on-ink:#FFFFFF;
  --text:#14141C; --text-muted:#6C6C7E;
  --star:#0B0B10;
  --violet:#6D28D9; --cyan:#0E7490; --amber:#B45309; --emerald:#047857; --pink:#BE185D; --teal:#0F766E; --indigo:#4338CA;
  --e-1: 0 1px 2px rgba(11,11,16,.05), 0 2px 6px rgba(11,11,16,.04);
  --e-2: 0 1px 2px rgba(11,11,16,.06), 0 4px 10px rgba(11,11,16,.05), 0 12px 28px rgba(11,11,16,.05);
  --e-3: 0 2px 4px rgba(11,11,16,.07), 0 10px 24px rgba(11,11,16,.08), 0 28px 60px rgba(11,11,16,.10);
  --scrim-bg: rgba(255,255,255,.66);
  --nav-bg: rgba(255,255,255,.78);
  --edge: inset 0 1px 0 rgba(255,255,255,.7);
  --sun:#F59E0B; --sun-deep:#B45309; --sun-hot:#FFFBEB;
  --sun-glow:.42; --ray-op:.5; --corona-op:.14; --flare-op:.3;
  --halo-in:.55; --halo-mid:.18;

  /* ═══ brand panel (Universe Code) ═══
     On the light theme the whole site draws space as black ink on white — an
     astronomy plate, not a window into a black sky. The panel keeps that:
     pale floor, ink particles, no additive glow (screen-blending onto white
     does nothing, so --uc-add turns it off and the canvas paints normally). */
  /* The floor is a shade darker than --surface on purpose: the three cards are
     white, and white cards on a white plate have nothing to sit on. */
  --uc-floor:#F1EEF9; --uc-line:var(--line);
  --uc-wash-a:17%; --uc-wash-b:12%;
  --uc-glass:rgba(255,255,255,.94); --uc-glass-line:rgba(11,11,16,.09);
  /* What the brand copy lies on. Nearly the floor colour, so it reads as the
     panel clearing a space for the text rather than as a box. */
  --uc-scrim:rgba(241,238,249,.94);
  --uc-add:0;      /* read by app.js → globalCompositeOperation */
  --uc-ink-op:.85; /* master alpha on everything the canvas draws */
  --uc-glow:.22;   /* halo strength: an ink smudge here, real light on dark */

  /* ═══ ستاره‌های کارت موبایل و مدارِ کنار قیمت ═══
     استثنای عمدی از قاعده‌ی «جوهر روی کاغذ» بالا. این دو تا در حالت
     روشن هم طلایی درخشان‌اند، چون روی کارت محصول نقش تزئین ندارند —
     چشم را به قیمت می‌برند.

     نکته‌ی فنی: ترکیب افزایشی (lighter) روی زمینه‌ی سفید هیچ کاری
     نمی‌کند؛ به سفید اشباع می‌شود و ستاره ناپدید. پس درخشش اینجا با
     رنگ غلیظ‌تر و یک هاله‌ی واقعی ساخته می‌شود، نه با blend mode. */
  --tile-star:#D97706;
  --tile-halo:rgba(245,158,11,.5);
  --orb-star:#D97706;
  --orb-warm:#F59E0B;
  --orb-add:0;
  --orb-ink:1;
  --orb-glow:.62;
}

/* On dark surfaces shadows barely register, so depth comes from lightness
   steps between surfaces plus a hairline top edge catching the light. */
:root[data-theme="dark"]{
  --bg:#08070E; --surface:#14131F; --surface-2:#1B1A29; --line:#2C2A40;
  --ink:#EFEDF8; --on-ink:#0B0B10;
  --text:#E7E5F2; --text-muted:#9E9AB8;
  --star:#FFFFFF;
  --violet:#A78BFA; --cyan:#22D3EE; --amber:#FBBF24; --emerald:#34D399; --pink:#F472B6; --teal:#2DD4BF; --indigo:#818CF8;
  --e-1: 0 1px 2px rgba(0,0,0,.5);
  --e-2: 0 2px 6px rgba(0,0,0,.55), 0 10px 26px rgba(0,0,0,.45);
  --e-3: 0 4px 10px rgba(0,0,0,.6), 0 24px 60px rgba(0,0,0,.55);
  --scrim-bg: rgba(6,5,12,.7);
  --nav-bg: rgba(8,7,14,.76);
  --edge: inset 0 1px 0 rgba(255,255,255,.07);
  /* The body has to out-shine what it emits. On dark the halo and plasma get
     pushed well up while the rays come DOWN — brighter rays than star reads as
     lit threads floating next to a dim blob, not as a light source. */
  --uc-floor:#06050C; --uc-line:#2C2A40;
  --uc-wash-a:34%; --uc-wash-b:24%;
  --uc-glass:rgba(17,16,27,.82); --uc-glass-line:rgba(255,255,255,.10);
  --uc-scrim:rgba(6,5,12,.90);
  --uc-add:1; --uc-ink-op:1; --uc-glow:1;

  /* در حالت تیره همان رفتار قبلی: بدنه‌ی سفید، هاله‌ی کهربایی، افزایشی. */
  --tile-star:#FFFFFF;
  --tile-halo:rgba(251,191,36,.5);
  --orb-star:#FFFFFF;
  --orb-warm:#FBBF24;
  --orb-add:1;
  --orb-ink:1;
  --orb-glow:1;

  --sun:#FBBF24; --sun-deep:#D97706; --sun-hot:#FFFDF5;
  --sun-glow:1; --ray-op:.42; --corona-op:.55; --flare-op:.9;
  /* The halo's own gradient stops move too — raising the layer opacity alone
     leaves the centre of the glow washed out no matter how opaque the shell is. */
  --halo-in:.92; --halo-mid:.42;
}

*,*::before,*::after{ box-sizing:border-box; }
html{ scroll-behavior:smooth; overflow-x:hidden; }
body{
  margin:0; position:relative;
  background:var(--bg); color:var(--text);
  font-family:Vazirmatn, system-ui, sans-serif;
  font-size:var(--t-base); line-height:1.9;   /* Persian needs far more than Latin's 1.5 */
  -webkit-font-smoothing:antialiased; overflow-x:hidden;
  transition:background .35s var(--ease-out), color .35s var(--ease-out);
}
a{ color:inherit; text-decoration:none; }
h1,h2,h3{ margin:0; line-height:1.5; }
:focus-visible{ outline:2px solid var(--ink); outline-offset:3px; border-radius:6px; }

/* ---------------- starfield ----------------
   Absolute over the whole document, not fixed: scrolling should bring you to
   new stars rather than dragging the same ones along. */
.starfield{ position:absolute; inset-block-start:0; inset-inline:0; block-size:100%; z-index:0; pointer-events:none; overflow:hidden; }
.st{
  position:absolute; display:block; opacity:var(--o);
  animation-duration:var(--dur); animation-delay:var(--delay);
  animation-iteration-count:infinite; animation-timing-function:ease-in-out;
  will-change:opacity;
}
/* The star is a mask on the span itself, not an <svg> inside it. Same shape,
   but the field drops from three elements per star to one — on a long page
   that is a thousand nodes and about a fifth of a second of main thread at
   startup, spent on decoration nobody is looking at yet. */
.st{ background:var(--star); -webkit-mask:var(--star-mask) center / contain no-repeat; mask:var(--star-mask) center / contain no-repeat; }
.st--steady{ animation-name:st-steady; }
.st--fade  { animation-name:st-fade;   }
.st--shine { animation-name:st-shine;  }
@keyframes st-steady{ 0%,100%{ opacity:var(--o); } 50%{ opacity:calc(var(--o) * .55); } }
@keyframes st-fade  { 0%,100%{ opacity:var(--o); } 44%,56%{ opacity:0; } }
@keyframes st-shine { 0%,100%{ opacity:var(--o); } 50%{ opacity:var(--peak); } }

/* ---------------- focus scrim ----------------
   `main` stays z-index:auto on purpose: giving it one would trap .fan-hero in
   its stacking context and the focused card could never rise above this. */
main{ position:relative; }
.scrim{
  position:fixed; inset:0; z-index:2;
  background:var(--scrim-bg);
  backdrop-filter:blur(8px); -webkit-backdrop-filter:blur(8px);
  opacity:0; pointer-events:none;
  transition:opacity .38s var(--ease-out);
}
body.is-focusing .scrim{ opacity:1; }

/* ---------------- آیکون‌های Keyline ----------------
   قرارداد: هر آیکون از اسپرایت با <use href="#ki-NAME"> می‌آید و رنگش
   currentColor است، پس رنگ را از متنِ والدش می‌گیرد و در تم روشن و تیره
   بدون نسخه‌ی دوم درست درمی‌آید.

   ki--flip فقط برای شکل‌های جهت‌دار (فلش، خروج، چرخش). ساعت، تیک، ضربدر،
   سبد خرید و play هرگز آینه نمی‌شوند — فهرست در uc_icon_mirrored().

   اندازه عمداً اینجا ست نمی‌شود: هر آیکون width/height خودش را روی خودِ
   عنصر دارد (uc_icon_html). اگر اینجا اندازه‌ی ثابت بگذاریم، با هر قانونِ
   هم‌وزنِ دیگری که همان آیکون را کوچک‌تر می‌خواهد سرِ ترتیب فایل دعوا
   می‌شود — یک‌بار همین اتفاق افتاد و ذره‌بین جستجو ۲۴px شد به‌جای ۱۷px.

   رنگ: symbolها stroke="currentColor" دارند و این صفتِ نمایشی روی خودِ
   symbol از هر مقدارِ به‌ارث‌رسیده قوی‌تر است. یعنی «stroke: X» روی عنصر
   والد اثری ندارد؛ برای عوض‌کردن رنگ آیکون باید color را ست کرد، نه
   stroke. */
.ki{ display:inline-block; flex:none; vertical-align:middle; }
[dir="rtl"] .ki--flip{ transform:scaleX(-1); }

/* ---------------- nav ---------------- */
.nav{
  position:sticky; top:0; z-index:1;
  background:var(--nav-bg);
  backdrop-filter:blur(16px) saturate(150%); -webkit-backdrop-filter:blur(16px) saturate(150%);
  border-block-end:1px solid var(--line);
}
.nav__inner{ max-width:1240px; margin-inline:auto; padding:var(--s-4) var(--s-5); display:flex; align-items:center; gap:var(--s-4); }
.logo{ display:flex; align-items:center; gap:var(--s-3); font-weight:900; font-size:var(--t-lg); }
.logo__mark{ inline-size:22px; block-size:22px; fill:var(--ink); }

/* نشان لوگوی آپلودی.

   کل مربع نشان داده می‌شود، بدون برش. نسخه‌ی اول فقط نشان UC را قاب
   می‌گرفت تا نوشته‌ی داخل فایل ریز و ناخوانا نشود، ولی آن برش لبه‌ی حلقه‌ی
   مداری را می‌بُرید و مالک سایت درست می‌گوید که لوگو باید کامل دیده شود.
   پس کادر بزرگ‌تر شد (۴۰ به‌جای ۳۲) و تصویر دست‌نخورده داخلش می‌نشیند؛
   نوشته‌ی داخل لوگو در این اندازه بافت آیکون است نه متنِ خواندنی، و نام
   سایت به‌هر‌حال با تایپوگرافی خود قالب همان کنارش نوشته شده. */
.logo__badge{
  inline-size:40px; block-size:40px; flex:0 0 auto;
  border-radius:11px; overflow:hidden; display:block;
}
.logo__img{
  display:block; inline-size:100%; block-size:100%;
  object-fit:contain;
}

/* هر دو فایل چاپ می‌شوند و CSS یکی را نشان می‌دهد.
   پس‌زمینه‌ی هر دو لوگو توپر است (یکی کرم، یکی مشکی)، پس نسخه‌ی اشتباه
   در تم مقابل یک مربع پرت‌افتاده می‌سازد. سوییچ با CSS انجام می‌شود نه
   با PHP، چون تم را کاربر در همان صفحه عوض می‌کند و رفت‌وبرگشت به سرور
   لازم نیست. الگو همان الگوی بقیه‌ی فایل است: پیش‌فرض روشن، بعد
   data-theme، بعد prefers-color-scheme برای حالت «سیستم». */
.logo__img--dark{ display:none; }

:root[data-theme="dark"] .logo__img--light{ display:none; }
:root[data-theme="dark"] .logo__img--dark{ display:block; }

@media (prefers-color-scheme:dark){
  :root:not([data-theme="light"]) .logo__img--light{ display:none; }
  :root:not([data-theme="light"]) .logo__img--dark{ display:block; }
}
.nav__links{ display:flex; gap:var(--s-5); font-size:var(--t-sm); color:var(--text-muted); }
.nav__links a{ transition:color .18s var(--ease-out); }
.nav__links a:hover{ color:var(--ink); }

.search{
  flex:1 1 200px; max-inline-size:340px; margin-inline:auto;
  position:relative; display:flex; align-items:center;
}
/* The asterism behind the field. Fixed 452×72 and centred rather than stretched
   to the field's width: the search flexes, and scaling the box would squash the
   stars out of round. 72px is exactly the nav's inner height, so it fills the
   bar without ever spilling past it. */
.search__stars{
  /* Physical `left`, not inset-inline-start: translate() is physical too, and on
     this RTL page pairing the logical inset with it threw the whole group a
     search-width off to the side. */
  position:absolute; inset-block-start:50%; left:50%;
  inline-size:452px; block-size:72px;
  transform:translate(-50%, -50%);
  pointer-events:none; overflow:visible;
}
.cst__link{ fill:none; stroke:var(--star); stroke-width:1; opacity:.11; }
.cst__star use{
  fill:var(--star); opacity:var(--o);
  animation:st-steady var(--dur) ease-in-out var(--delay) infinite;
}

/* The field and its two icons have to out-paint the constellation. The SVG is
   positioned, so without this it would sit on top of an in-flow input — and the
   icons, which are positioned but come earlier in the DOM, would then be buried
   by the input itself. */
.search__input{ position:relative; z-index:1; }
.search__ico, .search__clear{ z-index:2; }

.search__ico{
  position:absolute; inset-inline-start:14px;
  inline-size:17px; block-size:17px; pointer-events:none;
  /* color و نه stroke: symbolهای اسپرایت stroke="currentColor" روی خودشان
     دارند و صفت نمایشی از مقدار به‌ارث‌رسیده قوی‌تر است، پس stroke اینجا
     نادیده گرفته می‌شد و آیکون رنگ متن را می‌گرفت نه رنگ muted را. */
  color:var(--text-muted);
}
.search__input{
  inline-size:100%; min-block-size:40px;
  padding-inline:42px 38px;
  border:1px solid var(--line); border-radius:var(--r-full);
  background:var(--surface); color:var(--text);
  font-family:inherit; font-size:var(--t-sm);
  transition:border-color .18s var(--ease-out), box-shadow .18s var(--ease-out);
}
.search__input::placeholder{ color:var(--text-muted); }
.search__input:focus{ outline:none; border-color:var(--ink); box-shadow:var(--e-1); }
/* Safari draws its own clear button on type=search; we ship one that matches. */
.search__input::-webkit-search-cancel-button{ display:none; }
.search__clear{
  position:absolute; inset-inline-end:8px;
  inline-size:26px; block-size:26px; border:0; border-radius:50%;
  background:var(--surface-2); color:var(--text-muted);
  display:grid; place-items:center; cursor:pointer;
}
.search__clear svg{ inline-size:13px; block-size:13px; fill:none; stroke:currentColor; stroke-width:2.2; stroke-linecap:round; }

/* هدف لمسی ۴۴×۴۴ بدون تغییر اندازه‌ی دیداری.
   اندازه‌ی کوچکِ دکمه بخشی از طراحی است، ولی ناحیه‌ی قابل لمس نباید کوچک
   بماند (حداقل ۴۴×۴۴ — اولویت ۲ ui-ux-pro-max). ::after ناحیه را بیرون از
   کادر گسترش می‌دهد؛ چیزی دیده نمی‌شود و چیدمان هم جابه‌جا نمی‌شود. */
.search__clear::after{ content:''; position:absolute; inset:-9px; }  /* 26 + 2*9 = 44 */
.theme-btn{ position:relative; }
.theme-btn::after{ content:''; position:absolute; inset:-2px; }      /* 40 + 2*2 = 44 */
.search__clear:hover{ color:var(--ink); }

@media (max-width:900px){
  .nav__links{ display:none; }
  .search{ margin-inline-start:var(--s-3); max-inline-size:none; }
  /* Below this the bar is all field and no margin, so the chain would just run
     under the pill with nothing of it showing. */
  .search__stars{ display:none; }
}

/* On a phone the bar simply has more in it than fits on one line: logo, theme
   toggle, cart, account link and a search field come to ~451px of content in a
   375px viewport. The row used to overflow silently and the account link — the
   only way into login — was pushed clean off the edge at left:-76px, with no
   horizontal scroll to reach it.

   Letting the field shrink instead is not a fix: with min-inline-size:0 it
   collapses to zero width and the search disappears. So the search gets a row
   of its own, which is the ordinary mobile commerce layout and gives it full
   width rather than a sliver. */
@media (max-width:640px){
  .nav__inner{ flex-wrap:wrap; row-gap:var(--s-3); }
  .search{ order:10; flex:1 0 100%; max-inline-size:none; margin-inline:0; }
  .nav__cta{ margin-inline-start:auto; }
}

.theme-btn{
  inline-size:40px; block-size:40px; flex:none; display:grid; place-items:center;
  border-radius:var(--r-full); background:var(--surface);
  border:1px solid var(--line); box-shadow:var(--e-1); color:var(--ink); cursor:pointer;
  transition:background .18s var(--ease-out), transform .18s var(--ease-out);
}
.theme-btn:hover{ background:var(--surface-2); transform:scale(1.06); }
.theme-btn svg{ inline-size:19px; block-size:19px; fill:none; stroke:currentColor; stroke-width:1.9; stroke-linecap:round; stroke-linejoin:round; grid-area:1/1; transition:opacity .2s, transform .3s var(--ease-out); }
:root[data-theme="light"] .i-moon{ opacity:0; transform:rotate(-70deg) scale(.6); }
:root[data-theme="dark"]  .i-sun { opacity:0; transform:rotate(70deg) scale(.6); }
.i-moon{ fill:currentColor; stroke:none; }

/* ---------------- buttons ---------------- */
.btn{
  display:inline-flex; align-items:center; justify-content:center; gap:var(--s-2);
  min-block-size:44px; padding-inline:var(--s-5); border-radius:var(--r-full);
  font-family:inherit; font-weight:700; font-size:var(--t-sm);
  cursor:pointer; border:1px solid transparent;
  transition:transform .18s var(--ease-out), box-shadow .18s var(--ease-out), background .18s var(--ease-out);
}
/* Glass, not flat ink. Still ~88% opaque so the label keeps its contrast
   regardless of what's behind the button (checkout panels, product photos,
   the starfield) — a true see-through fill was tried and lost too much
   label contrast on busy backgrounds to justify it on the site's primary
   CTA. The glass read comes from the blur/saturate pass, the inset top edge
   (--edge, already themed) and a soft brand-colour glow behind the pill
   (violet→cyan, the same pair the fan's CTA button is built from) rather
   than from transparency itself. Border is rgba, never a flat grey line. */
.btn--primary{
  position:relative; isolation:isolate;
  background:color-mix(in srgb, var(--ink) 88%, transparent);
  backdrop-filter:blur(14px) saturate(160%);
  -webkit-backdrop-filter:blur(14px) saturate(160%);
  color:var(--on-ink); border-color:color-mix(in srgb, var(--on-ink) 22%, transparent);
  box-shadow:var(--e-2), var(--edge),
             0 10px 28px -14px color-mix(in oklch, var(--violet) 50%, var(--cyan) 40%);
}
.btn--primary:hover{
  transform:translateY(-2px);
  box-shadow:var(--e-3), var(--edge),
             0 14px 36px -12px color-mix(in oklch, var(--violet) 60%, var(--cyan) 50%);
}
.btn--ghost{ border-color:var(--line); color:var(--ink); background:var(--surface); box-shadow:var(--e-1); }
.btn--ghost:hover{ background:var(--surface-2); transform:translateY(-1px); }

/* ═══════════════ TOP — the fan ═══════════════ */
.fan-hero{
  position:relative; z-index:3;      /* must sit above .scrim (z-index 2) */
  display:grid; justify-items:center; gap:var(--s-6);
  padding:var(--s-8) var(--s-5) var(--s-7);
  /* The open fan is far wider than its container by design. `clip` (not
     `hidden`) keeps the Y axis visible so the focused card can travel out of
     the section, and creates no scroll container. */
  overflow-x:clip; overflow-y:visible;
}
.fan{
  position:relative;
  inline-size:var(--fan-card-w);
  block-size:var(--fan-h, calc(var(--fan-card-h) * 1.2));
  perspective:1400px;
}

/* Position comes from --tx/--ty, computed in JS as a point on the ellipse.
   Rotation is separate: on an ellipse the surface normal and the parametric
   angle diverge, and only the normal reads correctly. */
.fan__card{
  position:absolute; inset-block-start:0; inset-inline-start:0;
  inline-size:var(--fan-card-w); block-size:var(--fan-card-h);
  transform-origin:50% 50%;
  /* The function list here MUST match the focused state below, item for item.
     When two transform lists differ in shape the browser gives up on
     per-function interpolation and tweens the composed matrices instead — and
     the matrix for rotateY(360deg) is identical to rotateY(0deg), so a full
     turn silently animates as no turn at all. */
  transform:translate(var(--tx,0px), var(--ty,0px)) perspective(1200px)
            rotateY(0deg) rotate(var(--a,0deg)) scale(var(--sc,1));
  transition:transform var(--fan-dur) var(--ease-fan),
             opacity calc(var(--fan-dur) * .45) var(--ease-out),
             box-shadow .3s var(--ease-out);
  border-radius:var(--r-md); overflow:hidden;
  /* Solid, not glass — a translucent fill was tried and it washed the card
     out (worst in light theme, where it read as a pale slab and veiled the
     card's own art). "Glassy" here means lit, not see-through: the surface
     stays the opaque --surface it always was, so the art and body text keep
     full contrast, and the shine comes entirely from box-shadow below — the
     inset --edge top-light plus the colour-cycling --uc-glow outer bloom. */
  background:var(--surface); border:1px solid var(--line);
  will-change:transform;
  /* Hit-testing belongs to .fan__hit below, never to the card itself — the
     card moves away from the cursor, the hit area must not. */
  pointer-events:none;
}
/* No resting-state rule for the closed pile: while the deal runs, --tx/--ty/--a
   are written frame by frame from JS, so the pile, the sweep and the landed
   card are all the same three numbers. A CSS override here would have to fight
   that every frame. */

/* Invisible twin parked at the card's resting position. It never moves, so the
   pointer keeps "holding" the card even after the card has flown to the middle
   of the screen. Chasing the moving element is impossible for a user. */
.fan__hit{
  position:absolute; inset-block-start:0; inset-inline-start:0;
  inline-size:var(--fan-card-w); block-size:var(--fan-card-h);
  transform:translate(var(--tx,0px), var(--ty,0px)) rotate(var(--a,0deg));
  border-radius:var(--r-md);
  background:transparent; border:0; padding:0;
  cursor:pointer;
}
/* Dead until the deal finishes — the hit twins sit at the cards' *final*
   positions, so an early hover would fling a card out of a pile that is still
   on its way there. */
.fan.is-dealing .fan__hit{ opacity:0; pointer-events:none; }

@media (hover:hover) and (pointer:fine){
  /* --fx/--fy are the offset from this card's resting centre to the centre of
     the viewport, measured in JS at hover time.
     perspective() sits inside the transform list so the 3D turn is reckoned
     from the card itself; the parent's perspective-origin would skew a card
     that has been translated this far. rotateY spins it about the vertical
     line down its middle — rotate() would spin it flat about its centre. */
  .fan__card.is-focused{
    transform:translate(var(--fx,0px), var(--fy,0px)) perspective(1200px)
              rotateY(var(--fan-spin)) rotate(0deg) scale(var(--fan-focus));
    transition:transform .95s var(--ease-fan), box-shadow .4s var(--ease-out);
    transition-delay:0ms !important;
    box-shadow:var(--e-3); z-index:40 !important;
  }
  .fan.has-focus .fan__card:not(.is-focused){ opacity:.28; }
}

/* block-size:50.6% of --fan-card-h works out to width:height ≈ 1.25 (5:4) at
   this card's own fixed w:h (1 : 1.58) — same frame ratio as .card__art below
   and .prod__art in the catalogue grid, so every image on the homepage sits
   in an identical frame regardless of which section it's in. */
.fan__art{
  position:relative; block-size:50.6%;
  overflow:hidden;
  display:grid; place-items:center;
  background:
    radial-gradient(circle at 50% 40%, color-mix(in oklch, var(--accent) 16%, transparent), transparent 70%),
    var(--surface-2);
  border-block-end:1px solid var(--line);
  transition:block-size .55s var(--ease-fan);
}
/* Shrink the art so the description has somewhere to go — the card grows, but
   not enough to fit prose without giving the body more of the card. Same
   shrink proportion as before (~0.677×), just rebased off the new resting height. */
.fan__card.is-focused .fan__art{ block-size:34%; }
.fan__art svg{ inline-size:clamp(48px,5.6vw,78px); block-size:auto; color:var(--accent); }
/* absolute + inset, not a percentage block-size on the img itself: a
   percentage height resolved against a container mid-transition (block-size
   animating 50.6% → 34% on hover) could lag and stay at the old, taller
   value — on one card in testing the image kept the resting-state height
   even after the container had shrunk, spilling past .fan__art's bottom
   edge and covering the title. Anchoring to the box via inset ties the
   image to whatever height the container actually has, every frame,
   instead of a percentage computed once. */
.fan__art img{
  position:absolute; inset:0;
  inline-size:100%; block-size:100%;
  object-fit:cover;
}

/* Physical `left`, not inset-inline-start: on this RTL page the logical start
   is the right edge, and the badge is a visual anchor rather than something
   that should follow reading direction. */
.fan__badge{
  position:absolute; inset-block-start:10px; left:10px;
  padding:3px 11px; border-radius:var(--r-full);
  font-size:.75rem; font-weight:700; background:var(--ink); color:var(--on-ink);
}
.fan__body{ padding:var(--s-4); display:flex; flex-direction:column; gap:4px; }
.fan__title{ font-weight:700; font-size:var(--t-sm); line-height:1.6; }
.fan__price{ font-weight:900; font-size:var(--t-sm); color:var(--accent); }
.fan__desc{
  margin:0; font-size:.6875rem; line-height:1.85; color:var(--text-muted);
  max-block-size:0; opacity:0; overflow:hidden;
  transition:max-block-size .55s var(--ease-fan), opacity .35s var(--ease-out), margin .4s var(--ease-fan);
}
.fan__card.is-focused .fan__desc{ max-block-size:14em; opacity:1; margin-block-start:6px; }


/* ---- the comet's canvas ----
   Over the whole hero, not just the deck: the powder has to be able to leave
   the cards and fall onto the button underneath them. It never takes a click. */
/* z-index 30 clears the cards (1–7) AND their hit twins (21+), which share this
   stacking context — anything lower and the comet would fly under half the deck
   and over the other half. */
.fan__comet{
  position:absolute; inset:0; inline-size:100%; block-size:100%;
  z-index:30; pointer-events:none;
}
/* Height reserved from the start: the button arrives four seconds in, and a
   foot that grows when it lands would shove the whole page down. */
.fan__foot{ position:relative; z-index:3; display:grid; place-items:center; min-block-size:54px; }

/* ---- what the powder settles into ----
   The button is a piece of sky rather than a pill with a ring drawn round it:
   a nebula gradient that drifts, four pin-prick stars in the surface, and one
   slow sweep of light going round behind the words. Everything moving here is
   a background-position or a rotation — nothing that costs layout, on an
   element that sits on screen permanently. */
.fan__cta, .hero__cta{
  position:relative; isolation:isolate; overflow:hidden;
  display:inline-flex; align-items:center; justify-content:center; gap:var(--s-2);
  min-block-size:54px; padding:0 var(--s-7); border-radius:var(--r-full);
  color:#FFFFFF; font-weight:900; font-size:var(--t-base);
  background:
    radial-gradient(circle at 14% 32%, rgba(255,255,255,.95) 0 1.1px, transparent 1.7px),
    radial-gradient(circle at 34% 74%, rgba(255,255,255,.7)  0 1px,   transparent 1.5px),
    radial-gradient(circle at 68% 24%, rgba(255,255,255,.85) 0 1.2px, transparent 1.8px),
    radial-gradient(circle at 88% 66%, rgba(255,255,255,.65) 0 1px,   transparent 1.5px),
    linear-gradient(100deg, #1B1147 0%, #4C1D95 26%, #6D28D9 46%, #0E7490 74%, #14143A 100%);
  background-size:auto, auto, auto, auto, 260% 100%;
  background-position:0 0, 0 0, 0 0, 0 0, 12% 0;
  box-shadow:0 0 0 1px rgba(255,255,255,.16),
             0 12px 34px color-mix(in oklch, var(--violet) 38%, transparent),
             inset 0 1px 0 rgba(255,255,255,.2);
  opacity:0; transform:translateY(10px) scale(.96);
  transition:opacity .6s var(--ease-out), transform .6s var(--ease-out),
             box-shadow .25s var(--ease-out);
  animation:cta-drift 11s ease-in-out infinite alternate;
}
.fan__cta.is-on, .hero__cta.is-on{ opacity:1; transform:none; }
.fan__cta:hover, .hero__cta:hover{
  transform:translateY(-2px);
  box-shadow:0 0 0 1px rgba(255,255,255,.28),
             0 16px 42px color-mix(in oklch, var(--violet) 52%, transparent),
             inset 0 1px 0 rgba(255,255,255,.26);
}
@keyframes cta-drift{ to{ background-position:0 0, 0 0, 0 0, 0 0, 88% 0; } }

/* The sweep. A conic gradient turning behind the text — the same light a comet
   would throw crossing the face of it, which is where the button came from. */
.fan__cta::before, .hero__cta::before{
  content:''; position:absolute; inset:-60%; z-index:-1;
  background:conic-gradient(from 0turn,
    transparent 0 58%,
    rgba(255,255,255,.16) 70%,
    rgba(255,255,255,.30) 74%,
    transparent 86%);
  animation:cta-sweep 7s linear infinite;
}
@keyframes cta-sweep{ to{ transform:rotate(1turn); } }

.fan__cta-star{
  inline-size:15px; block-size:15px; fill:#FFFFFF; flex:none;
  filter:drop-shadow(0 0 6px rgba(255,255,255,.85));
  animation:cta-twinkle 3.4s ease-in-out infinite;
}
@keyframes cta-twinkle{ 0%,100%{ opacity:1; transform:scale(1); } 50%{ opacity:.55; transform:scale(.86); } }
.fan__cta-t{ position:relative; }
body.is-focusing .fan__foot{ opacity:0; pointer-events:none; }

/* ═══════════════ TOP — the shelf (mobile) ═══════════════
   Four products on a rail you swipe, one nearly full-width at a time, with
   page dots under it. No orbit, no decoration around the cards: the card is
   the object, the background is empty sky, and the only thing that ever
   crosses it is a comet every few seconds. */
.grid-hero{ position:relative; display:none; padding:var(--s-6) 0 var(--s-5); }

/* ---- cover flow ----
   The card in front stands square-on to you; the two beside it are turned away
   in real 3D and pushed back, so you see three products at once and it is never
   in doubt which one is being offered. Everything is driven by one number per
   card — --o, how many places it is from the front — and the browser
   interpolates the whole transform between two values of it.

   The perspective lives on the container, not the cards: children of a single
   perspective share one vanishing point, which is what makes them read as
   objects in one space rather than three separately warped pictures. */
.flow{
  position:relative; z-index:1;
  block-size:var(--flow-h, 430px);
  perspective:1100px; perspective-origin:50% 44%;
  touch-action:pan-y;               /* the page must still scroll vertically */
}
.tile{
  position:absolute; inset-block-start:0; left:50%;
  inline-size:min(262px, 70%);
  background:var(--surface); border:1px solid var(--line);
  border-radius:22px; box-shadow:var(--e-2); padding:var(--s-4);
  /* Ring colour for this card family only: not its own --accent but the same
     violet→cyan family the .fan__cta button is built from (its linear-gradient
     drifts through var(--violet) and var(--cyan) at those exact stops), so the
     highlight around the fan reads as "the same colour as the buy button"
     without copying its multi-layer background. Both tokens are redefined
     under [data-theme="dark"], so this follows the theme automatically. */
  --uc-cta-ring: color-mix(in oklch, var(--violet) 55%, var(--cyan) 45%);
  --o:0; --od:0;                    /* signed offset, and its absolute value */
  transform:
    translate(-50%, 0)
    translateX(calc(var(--o) * 52%))
    translateZ(calc(var(--od) * -132px))
    rotateY(calc(var(--o) * -38deg))
    scale(calc(1 - var(--od) * 0.05));
  transition:transform .58s var(--ease-out), opacity .58s var(--ease-out), box-shadow .58s var(--ease-out);
  will-change:transform;
}
/* Whole tile is one link now (same .prod__hit/.card__hit reasoning) — the
   flex layout that used to sit on .tile itself moves one level in with it. */
.tile__hit{ display:flex; flex-direction:column; gap:var(--s-2); block-size:100%; color:inherit; text-decoration:none; }
.tile.is-on{ box-shadow:var(--e-3); }
/* A card turned away from the light is darker. Without this the side cards are
   just small copies of the front one and the row reads flat. */
.tile::after{
  content:''; position:absolute; inset:0; border-radius:inherit;
  pointer-events:none; background:var(--bg);
  opacity:calc(var(--od) * 0.28);
  transition:opacity .58s var(--ease-out);
}
/* While a finger is on it the transform comes from the pointer, not from CSS. */
.flow.is-drag .tile{ transition:none; }
.tile.is-on{ cursor:grab; }
.tile.is-on:active{ cursor:grabbing; }
/* Only the card in front is reachable — the others are scenery until they are. */
.tile:not(.is-on){ pointer-events:none; }

.flow__dots{
  display:flex; justify-content:center; gap:6px; margin-block-start:var(--s-4);
}
.flow__dots button{
  inline-size:26px; block-size:26px; padding:0; border:0; background:none;
  cursor:pointer; display:grid; place-items:center;
}
.flow__dots button::before{
  content:''; inline-size:6px; block-size:6px; border-radius:var(--r-full);
  background:var(--line); transition:all .3s var(--ease-out);
}
.flow__dots button[aria-selected="true"]::before{ inline-size:22px; background:var(--ink); }

/* ---- the star on the price line ----
   The price sits at the reading edge and leaves the other half of its line
   empty; this fills it with the site's own motif — one body lit from the upper
   left with three smaller ones going round it at three different rates. Three
   rotating wrappers, one child each: the child is offset by its radius and the
   wrapper turns, which is an orbit for the cost of a transform. */
.tile__row{ display:flex; align-items:center; justify-content:space-between; gap:var(--s-3); }
.orb{
  flex:none; inline-size:62px; block-size:62px;
  opacity:0; transition:opacity .55s var(--ease-out);
}
/* Only the card being offered gets it — on the turned cards it would be noise,
   and only the front one is drawn, so the others are an empty canvas anyway. */
.tile.is-on .orb{ opacity:1; }

/* ---- the tile's own patch of sky ----
   Same trick as the panel: pin-prick radial gradients in the ink colour, so on
   the light theme it is an engraving and on the dark one it is space. */
.tile__art{
  position:relative; overflow:hidden;
  /* 1.25 (5:4) — same frame ratio as .fan__art/.card__art/.prod__art, so every
     image box on the homepage (desktop and mobile alike) is one shape. */
  inline-size:100%; aspect-ratio:1.25; border-radius:11px;
  display:grid; place-items:center; border:1px solid var(--line);
  background:
    /* بدنه‌ی تیز ستاره‌ها */
    radial-gradient(circle at 18% 24%, var(--tile-star) 0 1.1px, transparent 1.6px),
    radial-gradient(circle at 74% 16%, var(--tile-star) 0 1.4px, transparent 1.9px),
    radial-gradient(circle at 86% 58%, var(--tile-star) 0 1px,   transparent 1.5px),
    radial-gradient(circle at 32% 78%, var(--tile-star) 0 1.3px, transparent 1.8px),
    radial-gradient(circle at 58% 88%, var(--tile-star) 0 1px,   transparent 1.5px),
    /* هاله‌ی نرم پشت هرکدام — همین لایه است که «درخشان» را می‌سازد.
       شعاعش عمداً چند برابر نقطه است تا نور پخش شود نه اینکه نقطه چاق شود. */
    radial-gradient(circle at 18% 24%, var(--tile-halo) 0 .5px, transparent 6.5px),
    radial-gradient(circle at 74% 16%, var(--tile-halo) 0 .5px, transparent 8px),
    radial-gradient(circle at 86% 58%, var(--tile-halo) 0 .5px, transparent 6px),
    radial-gradient(circle at 32% 78%, var(--tile-halo) 0 .5px, transparent 7.5px),
    radial-gradient(circle at 58% 88%, var(--tile-halo) 0 .5px, transparent 6px),
    radial-gradient(115% 90% at 74% 12%, color-mix(in oklch, var(--accent) 34%, transparent), transparent 62%),
    radial-gradient(95% 80% at 12% 94%, color-mix(in oklch, var(--violet) 22%, transparent), transparent 66%),
    var(--surface-2);
}
/* The orbit the gem sits inside. Tilted and turning, so the tile has one thing
   moving on it even while it is just sitting there. Only for the decorative
   gem — a real product photo has its own thing going on and doesn't need a
   ring drawn across it. */
.tile__art::before{
  content:''; position:absolute; inset-block-start:50%; inset-inline-start:50%;
  inline-size:104%; aspect-ratio:2.6; border-radius:50%;
  border:1px solid color-mix(in oklch, var(--accent) 42%, transparent);
  transform:translate(50%, -50%) rotate(-14deg);
  animation:tile-orbit 14s linear infinite;
}
.tile__art:has(img)::before{ display:none; }
.tile__art img{ inline-size:100%; block-size:100%; object-fit:cover; }
@keyframes tile-orbit{
  from{ transform:translate(50%,-50%) rotate(-14deg) rotateX(66deg) rotateZ(0deg); }
  to  { transform:translate(50%,-50%) rotate(-14deg) rotateX(66deg) rotateZ(360deg); }
}
.tile__art svg{
  position:relative; z-index:1;
  inline-size:52px; block-size:52px; color:var(--accent);
  filter:drop-shadow(0 0 14px color-mix(in oklch, var(--accent) 55%, transparent));
}
.tile__badge{
  position:absolute; inset-block-start:20px; left:18px;
  font-size:.625rem; font-weight:700; padding:2px 8px;
  border-radius:var(--r-full); background:var(--ink); color:var(--on-ink);
}
.tile__title{ font-size:var(--t-base); font-weight:700; line-height:1.5; }
.tile__meta{ margin:0; font-size:.75rem; color:var(--text-muted); }
.tile__price{ font-size:var(--t-lg); font-weight:900; color:var(--accent); }
.tile__price small{ font-weight:400; font-size:.6875rem; color:var(--text-muted); }
.tile__buy, .prod__buy{
  /* gap برای فاصله‌ی آیکون تا متن — بدون آن آیکون به واژه می‌چسبد. */
  margin-block-start:auto; display:flex; align-items:center; justify-content:center; gap:var(--s-2);
  min-block-size:40px; border-radius:var(--r-full);
  background:var(--ink); color:var(--on-ink);
  font-family:inherit; font-weight:700; font-size:.8125rem;
  border:0; cursor:pointer; box-shadow:var(--e-1), var(--edge);
  transition:transform .16s var(--ease-out);
}
.tile__buy:active, .prod__buy:active{ transform:scale(.97); }

/* ---------------- intro ---------------- */
.intro{ max-width:760px; margin-inline:auto; text-align:center; padding:0 var(--s-5) var(--s-9); }
/* The intro's button stands where the delivery pill used to. It is the same
   object as the fan's, so it takes the same rules; only the space around it is
   set here — and it is the phone's copy, hidden wherever the fan's own button
   is on screen. */
.intro .hero__cta{ margin-block-end:var(--s-5); }
@media (min-width:769px){ .intro .hero__cta{ display:none; } }
h1{ font-size:var(--t-2xl); font-weight:900; }
.lede{ color:var(--text-muted); margin:var(--s-4) auto var(--s-6); max-inline-size:52ch; }
.intro__actions{ display:flex; gap:var(--s-3); flex-wrap:wrap; justify-content:center; }

/* ---------------- trust ---------------- */
.trust{ border-block:1px solid var(--line); background:var(--surface-2); }
/* گریدِ ثابتِ ۲/۴ ستونه جای خالی می‌ساخت: قالب آماری را که مقدار ندارد رد
   می‌کند، پس تعداد واقعی بین ۱ تا ۴ متغیر است و با سه آمار، یکی از سلول‌ها
   خالی می‌ماند و ردیف از مرکز می‌افتد. فلکسِ وسط‌چین هر تعدادی را متوازن
   می‌چیند. */
.trust__list{
  max-width:1240px; margin-inline:auto; padding:var(--s-6) var(--s-5);
  list-style:none; display:flex; flex-wrap:wrap; justify-content:center;
  row-gap:var(--s-5);
}
.trust__list li{ flex:1 1 150px; max-inline-size:280px; }
/* نوار اعتماد قبلاً چهار ستون متنِ راست‌چین بود روی یک باندِ تخت؛ خودِ عددها
   کنار هم می‌افتادند ولی چیزی ردیف را به هم وصل نمی‌کرد و لبه‌شان دندانه‌دار
   می‌شد. وسط‌چین‌کردن سلول‌ها و یک خط مویی بین آن‌ها ردیف را یکپارچه می‌کند.
   جداکننده از لبه‌ی آغازین می‌آید (border-inline-start) تا در RTL هم سمت
   درست بنشیند و اولین آیتم هر ردیف خط اضافه نگیرد. */
.trust__list li{ display:flex; flex-direction:column; align-items:center; text-align:center; gap:2px; }
.trust__list strong{
  font-size:var(--t-xl); font-weight:900; line-height:1.3;
  font-variant-numeric:tabular-nums; font-feature-settings:"tnum";
}
.trust__list span{ font-size:var(--t-sm); color:var(--text-muted); }
@media (min-width:760px){
  .trust__list li + li{ border-inline-start:1px solid var(--line); }
}

/* ═══════════════ rotating ring ═══════════════ */
.ring-section{ max-width:1240px; margin-inline:auto; padding:var(--s-9) var(--s-5); }
.ring-grid{ display:grid; gap:var(--s-8); align-items:center; grid-template-columns:minmax(0,1fr); }
/* The ring needs the wider half: with the cards turned back toward the camera
   they stay broad all the way round instead of going edge-on at the sides, so
   the track is far wider than a plain cylinder of the same radius. */
@media (min-width:1000px){ .ring-grid{ grid-template-columns:minmax(0,.45fr) minmax(0,1fr); } }
.kicker{ margin:0; font-size:var(--t-sm); font-weight:700; color:var(--text-muted); }
.ring-copy h2{ font-size:var(--t-xl); font-weight:900; margin:var(--s-2) 0 var(--s-4); }
.ring-copy > p{ color:var(--text-muted); margin:0 0 var(--s-5); max-inline-size:46ch; }
.ring-facts{ list-style:none; padding:0; margin:0 0 var(--s-6); display:flex; gap:var(--s-7); }
.ring-facts li{ display:flex; flex-direction:column; }
.ring-facts strong{ font-size:var(--t-lg); font-weight:900; }
.ring-facts span{ font-size:var(--t-sm); color:var(--text-muted); }
.ring-hint{ display:flex; align-items:center; gap:7px; margin:var(--s-5) 0 0; font-size:var(--t-sm); color:var(--text-muted); }
.ring-hint svg{ inline-size:16px; block-size:16px; fill:none; stroke:currentColor; stroke-width:2; stroke-linecap:round; stroke-linejoin:round; }

/* min-inline-size:0 matters: a grid item defaults to min-width:auto, so the
   controls row underneath could force this column wider than its track. It
   then centred that overflow and dragged the whole ring off to one side. */
.ring-stack{ display:grid; justify-items:center; gap:var(--s-5); min-inline-size:0; }
.stage{
  position:relative; inline-size:100%;
  /* perspective-origin sits at 42%, above the ring's middle, so the divide
     throws the card BOTTOMS about 55px past this element's own box — the arrow
     buttons underneath were being laid out against a box the cards had already
     overflowed, and ended up buried in them. Room is reserved at the foot the
     same way --pendulum-room reserves it at the head; block-size grows by the
     same amount so the ring itself does not move. */
  --stage-h:clamp(430px, 46vw, 500px);
  --card-room:72px;
  block-size:calc(var(--stage-h) + var(--card-room));
  padding-block:var(--pendulum-room, 0px) var(--card-room);
  perspective:1500px;               /* the 3D context lives on the PARENT */
  perspective-origin:50% 42%;
  display:grid; place-items:center;
  touch-action:pan-y;               /* page must still scroll vertically */
  /* Backstop for the radius clamp above: cards swinging round the far side get
     cut at the column edge instead of reaching the copy. `clip` keeps the Y
     axis open so the star's halo isn't sliced off. */
  overflow-x:clip; overflow-y:visible;
}
/* The suspended star. Drawn over the stage but it never intercepts pointer
   events, so dragging the ring still works underneath it.
   Nothing holds it up: it drifts on a slow two-frequency path so the motion
   never visibly repeats, which is what separates "floating in space" from
   "swinging on a string". */
.pendulum{
  position:absolute; inset:0; inline-size:100%; block-size:100%;
  pointer-events:none; z-index:5; overflow:visible;
}
.pendulum .halo{ opacity:var(--sun-glow); }
/* Filled plasma shells rather than outlines — a stroked ring reads as a drawn
   circle, and the whole point is that this body has no clean edge. */
.pendulum .corona{ fill:var(--sun); opacity:var(--corona-op); }
.pendulum .flare{ fill:var(--sun); opacity:var(--flare-op); }
/* Each ray is a filled ribbon, not a stroked line: a stroke has one uniform
   width, and a beam of light has to be thick where it leaves the star and
   taper to nothing. The body is built point-by-point in JS and filled with a
   per-ray gradient that fades out along its length. */
.pendulum .ray{ stroke:none; }

.carousel{
  position:relative; inline-size:var(--card-w); block-size:var(--card-h);
  transform-style:preserve-3d;
  transform:rotateX(var(--tilt)) rotateY(var(--rot,0deg));
  cursor:grab;
}
.carousel.is-dragging{ cursor:grabbing; }
/* Swing out to the card's slot, push it to the radius, then turn part of the
   way back toward the camera (--cface). Without that last rotation every card
   faces straight out of the cylinder and the side ones go edge-on, which reads
   as a deep barrel instead of a shallow arc.

   --cface is computed in JS, not from --rot here: --rot is cumulative and
   never wraps, so deriving the correction from it in CSS made the angle drift
   by a whole extra turn's worth on every revolution. */
.card{
  position:absolute; inset:0;
  transform:rotateY(calc(var(--i) * var(--step)))
            translateZ(var(--radius))
            rotateY(var(--cface, 0deg));
  border-radius:var(--r-lg); overflow:hidden;
  background:var(--surface); border:1px solid var(--line); box-shadow:var(--e-2);
  /* The card's own turn-back has to ease on the same clock as the ring, or the
     two halves of the motion visibly desync. */
  transition:transform var(--ring-spin) cubic-bezier(.2,.8,.25,1),
             opacity var(--ring-spin) var(--ease-out),
             border-color .3s var(--ease-out), box-shadow .3s var(--ease-out);
  backface-visibility:hidden; user-select:none;
}
/* The card's own children (.card__art/.card__body) size themselves as a
   percentage of this — an anchor doesn't stretch to fill its parent's height
   on its own, so without block-size:100% here those percentages would
   resolve against an indefinite height and just collapse. */
.card__hit{ display:block; block-size:100%; color:inherit; text-decoration:none; }
/* Used for a single frame when a card's normalised angle wraps past ±180°, so
   the correction snaps instead of unwinding the long way round. It happens at
   the back of the ring where the card is edge-on, so the snap isn't visible. */
.card.no-anim{ transition:none !important; }
/* کارت جلوی چرخ‌فلک. قبلاً قابش مشکی (--ink) می‌شد؛ حالا که حلقه‌ی اکسنت
   دور همه‌ی کارت‌هاست، آن قاب با حلقه سر رنگ دعوا می‌کرد — پس تمایزِ کارت
   فعال از راه درخشش هم‌رنگِ خودش می‌آید، نه یک قاب رنگ دیگر. */
.card.is-active{
  border-color:color-mix(in oklch, var(--accent) 35%, var(--line));
  box-shadow:var(--e-3), 0 12px 34px -14px color-mix(in oklch, var(--accent) 60%, transparent);
}
/* block-size:55.5% of --card-h ≈ 1.25 (5:4) at this card's own fixed w:h —
   same frame ratio as .fan__art above and .prod__art in the catalogue grid,
   kept as one percentage since the desktop (208×300) and mobile (158×228)
   --card-w/--card-h pairs share almost the same aspect ratio already. The
   remaining ~44.5% is .card__body — sized to actually fit title + a real
   two-line description + price, not just whatever was left over at 57.4%. */
.card__art{
  position:relative; block-size:55.5%; display:grid; place-items:center;
  background:radial-gradient(circle at 50% 40%, color-mix(in oklch, var(--accent) 18%, transparent), transparent 70%), var(--surface-2);
  border-block-end:1px solid var(--line);
}
.card__art svg{ inline-size:76px; block-size:76px; color:var(--accent); }
.card__art img{ inline-size:100%; block-size:100%; object-fit:cover; }
.card__badge{
  position:absolute; inset-block-start:12px; left:12px;
  padding:3px 10px; border-radius:var(--r-full);
  font-size:.75rem; font-weight:700; background:var(--ink); color:var(--on-ink);
}
/* Grid, not flex: title/price are auto rows (their real content height,
   nothing more), meta is the 1fr row and gets whatever's actually left —
   predictable even when that's very little, unlike flexbox's shrink-basis
   arithmetic above, which was inflating title/price past their own content
   height while starving meta down to a literal 0px box. */
.card__body{ padding:var(--s-4); display:grid; grid-template-rows:auto 1fr auto; gap:4px; block-size:44.5%; }
/* One line, ellipsis if it doesn't fit — at 208px wide, a title like "اکانت
   ChatGPT Plus" wraps to two lines by default and eats half the body's
   height by itself, leaving meta almost nothing (see .card__meta below). */
.card__title{
  font-weight:700; font-size:var(--t-base); line-height:1.6; margin:0;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
/* The variable-length one — a longer product description used to push past
   .card__body's fixed 42% and out through .card's own overflow:hidden,
   taking the price with it. Now it's the grid's 1fr row (see .card__body
   above): title and price above/below it are fixed to their own content
   height, so meta gets exactly whatever's left, never less than 0 and never
   stealing from the other two. overflow:hidden here is the hard backstop;
   fitCardMeta() in app.js shrinks the font to actually fit inside it instead
   of relying on that backstop to silently clip mid-word. */
.card__meta{ margin:0; font-size:.8125rem; color:var(--text-muted); line-height:1.55; overflow:hidden; }
.card__price{ margin:0; font-weight:900; font-size:var(--t-lg); line-height:1.35; color:var(--accent); }
.card__price small{ font-weight:400; font-size:.75rem; color:var(--text-muted); }

/* Controls sit below the ring. `direction:ltr` so the left-pointing chevron is
   physically on the left — in an RTL container the DOM order would flip them. */
.stage__controls{
  direction:ltr;
  display:flex; align-items:center; gap:var(--s-4);
  max-inline-size:100%;
}
/* Eight dots at a legal 32px touch size don't fit a phone. Swap them for a
   counter there rather than shrinking them below the tap threshold. */
.ring-count{ display:none; font-size:var(--t-sm); color:var(--text-muted); font-variant-numeric:tabular-nums; }
.ctl{
  inline-size:44px; block-size:44px; border-radius:50%; display:grid; place-items:center;
  background:var(--surface); border:1px solid var(--line); box-shadow:var(--e-1);
  color:var(--ink); cursor:pointer;
  transition:background .18s var(--ease-out), transform .18s var(--ease-out);
}
.ctl:hover{ background:var(--surface-2); transform:scale(1.06); }
.ctl svg{ inline-size:20px; block-size:20px; fill:none; stroke:currentColor; stroke-width:2; stroke-linecap:round; stroke-linejoin:round; }
.dots{ display:flex; gap:2px; }
.dots button{ inline-size:32px; block-size:32px; padding:0; border:0; background:none; cursor:pointer; display:grid; place-items:center; }
.dots button::before{ content:""; inline-size:7px; block-size:7px; border-radius:50%; background:var(--line); transition:all .25s var(--ease-out); }
.dots button[aria-selected="true"]::before{ inline-size:24px; border-radius:var(--r-full); background:var(--ink); }

/* ═══════════════ sections ═══════════════ */
.section-head{ text-align:center; margin-block-end:var(--s-7); }
.section-head h2{ font-size:var(--t-xl); font-weight:900; }
.section-head p{ color:var(--text-muted); font-size:var(--t-sm); margin:var(--s-2) 0 0; }
.section-head--row{ display:flex; flex-wrap:wrap; gap:var(--s-4); align-items:flex-end; justify-content:space-between; text-align:start; }

.cats{ max-width:1240px; margin-inline:auto; padding:var(--s-8) var(--s-5); }
/* auto-fit ستون‌های خالی را جمع می‌کند و باقی‌مانده را کش می‌آورد — با دو
   دسته یعنی دو کاشیِ ۵۸۸ پیکسلی که محتوایشان به یک لبه چسبیده و وسطشان
   خالی است. سقفِ ۳۰۰ پیکسل جلوی آن کشیده‌شدن را می‌گیرد و ردیف وسط‌چین
   می‌شود، هم‌راستا با تیتر بخش که خودش وسط‌چین است. با زیادشدن دسته‌ها
   همین قاعده ستون‌های بیشتری می‌سازد و چیزی دستی لازم نیست. */
.cats__grid{
  display:grid; gap:var(--s-4);
  grid-template-columns:repeat(auto-fit, minmax(168px, 300px));
  justify-content:center;
}
/* همان سطح شیشه‌ای .prod — کاشی دسته‌بندی هم‌خانواده‌ی کارت محصول است و
   وقتی یکی شیشه بود و دیگری --surface مات، دو ردیف پشت‌سرهم دو جنس متفاوت
   می‌شدند. اینجا سه‌بعدی در کار نیست، پس backdrop-filter بی‌خطر است. */
.cat{
  position:relative; isolation:isolate;
  display:flex; flex-direction:column; gap:var(--s-2);
  padding:var(--s-5); border-radius:var(--r-md);
  background:color-mix(in srgb, var(--uc-glass) 84%, transparent);
  backdrop-filter:blur(14px) saturate(150%);
  -webkit-backdrop-filter:blur(14px) saturate(150%);
  border:1px solid var(--uc-glass-line); box-shadow:var(--e-1), var(--edge);
  transition:transform .2s var(--ease-out), box-shadow .2s var(--ease-out);
}
/* The lead category gets a full column of its own and an accent wash — it is
   the one the business actually wants clicked. حلقه‌ی اکسنت کار قاب را
   می‌کند، پس اینجا فقط یک لایه‌ی رنگِ نازک روی همان شیشه می‌نشیند. */
.cat--lead{
  grid-column:span 2;
  background:
    linear-gradient(140deg, color-mix(in oklch, var(--accent) 14%, transparent), transparent 62%),
    color-mix(in srgb, var(--uc-glass) 84%, transparent);
}
.cat--lead .cat__name{ font-size:var(--t-lg); }
.cat__tag{
  position:absolute; inset-block-start:var(--s-5); left:var(--s-5);
  font-size:.6875rem; font-weight:700; padding:2px 10px;
  border-radius:var(--r-full); background:var(--accent); color:var(--bg);
}
.cat__ico{
  inline-size:44px; block-size:44px; border-radius:13px; display:grid; place-items:center;
  background:color-mix(in oklch, var(--accent) 14%, transparent);
  color:var(--accent); margin-block-end:var(--s-2);
}
.cat__ico svg{ inline-size:23px; block-size:23px; fill:none; stroke:currentColor; stroke-width:1.9; stroke-linecap:round; stroke-linejoin:round; }
.cat__name{ font-weight:700; font-size:var(--t-base); }
.cat__count{ font-size:var(--t-sm); color:var(--text-muted); }

/* همان ۱۰۴px که article.css برای پاک‌ماندن از زیر نوار چسبان بالا استفاده
   می‌کند — بدونش، رسیدن به این بخش (مثلاً از ریدایرکت صفحه‌ی فروشگاه)
   تیترش را درست زیر نوار ناوبری پنهان می‌کرد. */
.all{ max-width:1240px; margin-inline:auto; padding:var(--s-8) var(--s-5) var(--s-9); scroll-margin-block-start:104px; }

/* ---------------- صفحات ثابت (page.php) ----------------
   تا امروز page.php اصلاً وجود نداشت؛ این‌ها اولین باری‌اند که یک
   صفحه‌ی ثابت وردپرسی (نه محصول، نه نوشته) واقعاً چیزی محتوایی
   رندر می‌کند. حداقلی و خوانا — قرار نیست هر صفحه‌ی ساده هم صحنه‌ی
   کهکشانی داشته باشد. */
.page-wrap{ max-width:900px; margin-inline:auto; padding:var(--s-7) var(--s-5) var(--s-9); }
.page-title{ font-size:var(--t-xl); font-weight:900; margin-block-end:var(--s-6); }
.page-content{ font-size:var(--t-base); line-height:2; max-inline-size:70ch; color:var(--text); }
.page-content > * + *{ margin-block-start:var(--s-4); }
.page-content h2{ font-size:var(--t-lg); font-weight:900; margin-block-start:var(--s-6); }
.page-content h3{ font-size:var(--t-base); font-weight:700; margin-block-start:var(--s-5); }
.page-content ul,
.page-content ol{ padding-inline-start:var(--s-5); display:grid; gap:var(--s-2); }
.page-content a{
  color:var(--ink); font-weight:700;
  border-block-end:1px solid var(--line);
}
.page-content a:hover{ border-block-end-color:var(--ink); }

/* پرداخت — ستون‌های صورت‌حساب/سفارش را خودِ ووکامرس می‌سازد (col2-set)،
   اینجا فقط عرض کلی صفحه. بخش انتخاب درگاه در cart.css بازطراحی شده. */
.checkout-wrap{ max-width:1080px; margin-inline:auto; padding:var(--s-7) var(--s-5) var(--s-9); }

/* پربازدیدترین مقاله‌ها — همان قاب .all، فقط روی صفحه‌ی اصلی. کارت‌های
   داخلش (.post/.pane) از blog.css می‌آیند، خودکفا و بدون وابستگی به
   این فایل — همان دلیلی که آنجا نوشته شده. */
.posts-home{ max-width:1240px; margin-inline:auto; padding:0 var(--s-5) var(--s-9); }
.filters{ display:flex; gap:var(--s-2); flex-wrap:wrap; }
.chip{
  min-block-size:38px; padding-inline:var(--s-4); border-radius:var(--r-full);
  background:var(--surface); border:1px solid var(--line); color:var(--text-muted);
  font-family:inherit; font-size:var(--t-sm); font-weight:500; cursor:pointer;
  transition:all .18s var(--ease-out);
}
.chip:hover{ color:var(--ink); border-color:var(--ink); }
.chip[aria-pressed="true"]{ background:var(--ink); color:var(--on-ink); border-color:var(--ink); }

.all__grid{ display:grid; gap:var(--s-4); grid-template-columns:repeat(auto-fill, minmax(210px,1fr)); }
/* ---------------- کارت محصول: شیشه‌ی مات + حلقه‌ی اکسنت ----------------
   سطح کارت دیگر --surface مات نیست؛ همان شیشه‌ای است که پنل برند
   (.uc__fact) و پنل‌های مقاله استفاده می‌کنند — --uc-glass / --uc-glass-line —
   تا کارت‌ها با بقیه‌ی سایت یک زبان داشته باشند و در هر دو تم درست دربیایند.
   توکن با color-mix کمی رقیق‌تر می‌شود چون پشت این کارت‌ها ستاره‌زار است
   (.starfield تمام ارتفاع سند را می‌پوشاند) و در ۹۴٪ کدری چیزی از آن پیدا
   نبود؛ اینجا بلورِ پشت‌زمینه واقعاً کاری می‌کند.

   تعداد کارت‌های این گرید کم است، پس برخلاف .pane--lite در blog.css یک پاس
   واقعی backdrop-filter اینجا مقرون‌به‌صرفه است. */
.prod{
  position:relative; isolation:isolate;
  background:color-mix(in srgb, var(--uc-glass) 84%, transparent);
  backdrop-filter:blur(14px) saturate(150%);
  -webkit-backdrop-filter:blur(14px) saturate(150%);
  border:1px solid var(--uc-glass-line);
  border-radius:var(--r-md); box-shadow:var(--e-1), var(--edge); padding:var(--s-3);
  transition:transform .2s var(--ease-out), box-shadow .2s var(--ease-out);
}
/* ---------------- حلقه‌ی اکسنت: یک قاعده برای همه‌ی کارت‌های صفحه ----------
   رنگ از همان --accent‌ای می‌آید که PHP روی خود عنصر می‌گذارد (uc_accent /
   uc_cat_accent) — پس هر کارت حلقه‌ی رنگ خودش را دارد و هیچ رنگی اینجا
   هاردکد نشده. پنج خانواده‌ی کارتِ صفحه‌ی اصلی همگی از همین قاعده تغذیه
   می‌شوند تا زبان بصری یکی بماند:
     .prod      کارت محصول در گرید
     .cat       کاشی دسته‌بندی
     .card      کارت چرخ‌فلک سه‌بعدی
     .tile      کارت بادبزنِ بالای صفحه
     .uc__fact  کارت‌های بخش برند

   ترفند «قاب گرادیانی»: یک لایه‌ی هم‌اندازه‌ی کارت با padding:1px که ماسکِ
   دوگانه وسطش را می‌بُرد، چون border-image با border-radius کار نمی‌کند.
   pointer-events:none لازم است چون کل کارت یک <a> است.

   نکته‌ی سه‌بعدی: .card و .tile داخل perspective/preserve-3d هستند. خودِ
   ماسک یک grouping property است، ولی روی همین شبه‌عنصر اعمال می‌شود نه روی
   کارت، پس فقط ::before تخت می‌شود و چرخش خود کارت دست‌نخورده می‌ماند. به
   همین دلیل backdrop-filter به .card/.tile داده نشده — آن یکی روی خود کارت
   می‌نشیند و زمینه را در متن سه‌بعدی اشتباه نمونه‌برداری می‌کند. */
/* زاویه‌ی حلقه به‌عنوان یک متغیر تایپ‌دار ثبت می‌شود؛ بدون @property مرورگر
   custom property را رشته می‌بیند و بینشان درون‌یابی نمی‌کند، یعنی انیمیشن
   به‌جای چرخیدن، پرش می‌کند. */
@property --uc-ring-angle{
  syntax:'<angle>'; initial-value:0deg; inherits:false;
}
/* گرادیان مخروطی به‌جای خطی: کل دور کارت یک نوار کم‌رنگِ پیوسته است و یک
   کمانِ روشن داخلش می‌چرخد — همان حلقه‌ی قبلی، فقط با یک نقطه‌ی پررنگ که
   دور تا دور می‌گردد. */
.prod::before,
.cat::before,
.card::before,
.tile::before,
.uc__fact::before{
  content:""; position:absolute; inset:0; z-index:0; pointer-events:none;
  border-radius:inherit; padding:1px;
  -webkit-mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite:xor; mask-composite:exclude;
  opacity:.85; transition:opacity .2s var(--ease-out);
  animation:uc-ring-travel 7s linear infinite;
}
/* .prod / .cat / .card / .uc__fact keep their own per-product --accent ring.
   .tile (the fan cards) is the one exception: the owner asked for the ring to
   read as "the CTA button's colour" instead, so it gets its own background
   built from --uc-cta-ring (set above, on .tile) rather than --accent. Same
   conic-gradient shape/stops as the shared ring, just a different source
   colour, so the travelling-arc animation and the rest of the shared rule
   above still apply unchanged. */
.prod::before, .cat::before, .card::before, .uc__fact::before{
  background:conic-gradient(from var(--uc-ring-angle),
    color-mix(in oklch, var(--accent) 20%, transparent) 0deg,
    color-mix(in oklch, var(--accent) 20%, transparent) 176deg,
    color-mix(in oklch, var(--accent) 62%, transparent) 232deg,
    var(--accent) 268deg,
    color-mix(in oklch, var(--accent) 62%, transparent) 302deg,
    color-mix(in oklch, var(--accent) 20%, transparent) 352deg,
    color-mix(in oklch, var(--accent) 20%, transparent) 360deg);
}
/* کف آلفا عمداً بالاست. رنگ CTA (بنفش آمیخته با فیروزه‌ای) از اکسنت‌های
   صورتی/بنفشِ بقیه‌ی کارت‌ها تیره‌تر است، و با کف ۲۰٪ — که برای آن‌ها کافی
   بود — یک خط یک‌پیکسلی روی کارت تیره عملاً دیده نمی‌شد. اینجا کف ۵۲٪ است
   تا خودِ حلقه همیشه پیدا باشد و کمانِ روشن روی آن حرکت کند، نه اینکه تنها
   چیز قابل‌دیدن باشد. */
/* حلقه‌ی رنگین‌کمانِ برند.
   قبلاً یک رنگ ثابت بود با یک کمان روشنِ چرخان. حالا خودِ گرادیان از شش
   اکسنت پالت ساخته شده، پس با چرخیدن، هر نقطه از قاب هم رنگش عوض می‌شود —
   همان «تغییر رنگ» خواسته‌شده، بدون اینکه به رنگین‌کمان عمومی و بی‌ربط به
   برند بیفتد. تعمداً از همان توکن‌هایی استفاده می‌شود که بقیه‌ی سایت دارد،
   و چون همه‌شان در تم تاریک بازتعریف شده‌اند، حلقه خودش را با تم وفق می‌دهد.
   آلفا هم در طول دور تغییر می‌کند تا یک «سرِ روشن» مثل دنباله‌دار بماند و
   قاب یکنواخت و تخت به‌نظر نرسد. */
.tile::before{
  background:conic-gradient(from var(--uc-ring-angle),
    color-mix(in oklch, var(--indigo) 55%, transparent)   0deg,
    color-mix(in oklch, var(--violet) 78%, transparent)  55deg,
    var(--pink)                                         115deg,
    color-mix(in oklch, var(--amber)  72%, transparent) 175deg,
    color-mix(in oklch, var(--teal)   58%, transparent) 235deg,
    color-mix(in oklch, var(--cyan)   85%, transparent) 292deg,
    color-mix(in oklch, var(--indigo) 55%, transparent) 360deg);
}
/* روی گوشی، حلقه‌ی رنگین‌کمان از کاشی‌های بالای صفحه‌ی اصلی برداشته می‌شود.
   روی نمایشگر بزرگ کارت‌ها کنار هم و کوچک‌اند و حلقه یک لهجه‌ی ظریف است؛ روی
   گوشی همان کاشی تقریباً تمام عرض را می‌گیرد و در چرخش سه‌بعدی هم هست، پس
   نوار رنگی متحرک دور یک کارتِ بزرگ به‌جای لهجه به شلوغی می‌خورد و چشم را از
   خود محصول می‌گیرد. مرز ۷۶۸ همان مرزی است که بقیه‌ی قالب «موبایل» می‌شناسد.

   فقط ::before برداشته می‌شود که یک قاب یک‌پیکسلی ماسک‌شده و کاملاً تزئینی
   است؛ خودِ کارت border و box-shadow خودش را دارد، پس بدون آن هم لبه دارد. */
@media (max-width:768px){
  .tile::before{ display:none; }
}

@keyframes uc-ring-travel{ to{ --uc-ring-angle:360deg; } }
/* اگر همه‌ی کارت‌ها هم‌زمان شروع کنند، ردیف مثل یک چراغ چشمک‌زن به نظر
   می‌رسد. تأخیر منفی یعنی هرکدام از وسط انیمیشن شروع می‌شود، پس از همان
   فریم اول پخش‌اند و چیزی برای شروع منتظر نمی‌ماند. */
.prod:nth-child(3n+2)::before,
.cat:nth-child(3n+2)::before,
.uc__fact:nth-child(3n+2)::before{ animation-delay:-2.4s; }
.prod:nth-child(3n+3)::before,
.cat:nth-child(3n+3)::before,
.uc__fact:nth-child(3n+3)::before{ animation-delay:-4.7s; }
@media (prefers-reduced-motion:reduce){
  .prod::before, .cat::before, .card::before, .tile::before, .uc__fact::before{
    animation:none; --uc-ring-angle:150deg;
  }
}
/* کارت‌های بادبزن و چرخ‌فلک که جلوی صحنه نیستند نباید حلقه‌شان به‌اندازه‌ی
   کارت فعال بدرخشد — همان منطق تیره‌شدن .tile::after، این‌بار روی حلقه. */
/* کارت‌های عقب هنوز کم‌رنگ‌ترند تا کارت جلو برجسته بماند، ولی نه آن‌قدر که
   حلقه‌شان کلاً ناپدید شود — قبلاً ۰.۴ روی کفِ کم‌رنگِ قبلی یعنی نامرئی. */
.tile:not(.is-on)::before{ opacity:.62; }
/* هاله‌ی بیرونی هم‌رنگ حلقه. پایگاه‌داده‌ی اسکیل برای همین سبک «هاله‌ی
   اکسنت» را به‌عنوان یکی از اقلام چک‌لیست می‌آورد و «قاب خاکستریِ تخت» را رد
   می‌کند — پس نور از خود رنگ حلقه می‌آید، نه از یک سایه‌ی خنثی. سایه روی
   خودِ کارت است نه شبه‌عنصر، بنابراین overflow:hidden کارت آن را نمی‌بُرد. */
/* هاله‌ی بیرونی که هم‌قدمِ حلقه رنگ عوض می‌کند. رنگ در یک متغیر تایپ‌دار
   نگه داشته می‌شود چون بدون @property مرورگر بین دو رنگِ custom property
   درون‌یابی نمی‌کند و هاله به‌جای محو شدن، پرش می‌زند. دوره‌اش سه برابر
   چرخش حلقه است تا دو حرکت روی هم قفل نشوند و الگوی تکراری حس نشود. */
@property --uc-glow{ syntax:'<color>'; initial-value:transparent; inherits:false; }
@keyframes uc-glow-cycle{
  0%   { --uc-glow:var(--indigo); }
  18%  { --uc-glow:var(--violet); }
  36%  { --uc-glow:var(--pink);   }
  54%  { --uc-glow:var(--amber);  }
  72%  { --uc-glow:var(--teal);   }
  88%  { --uc-glow:var(--cyan);   }
  100% { --uc-glow:var(--indigo); }
}
/* Lit, not glass: --edge is the same inset top-light every other card token
   uses ("the light catching the top"), stacked with the existing --uc-glow
   outer bloom — a hairline glow ring plus a soft colour-cycling halo behind
   the card. Bumped a bit stronger than the old glow-only version (18%/54% and
   28px vs. 16%/48% and 34px→38px) so the card actually reads as shining
   rather than merely accented, per the owner's correction. */
.fan__card{
  animation:uc-glow-cycle 21s linear infinite;
  box-shadow:var(--e-2), var(--edge),
             0 0 0 1px color-mix(in oklch, var(--uc-glow) 18%, transparent),
             0 10px 38px -14px color-mix(in oklch, var(--uc-glow) 58%, transparent);
  transition:transform var(--fan-dur) var(--ease-fan),
             opacity calc(var(--fan-dur) * .45) var(--ease-out),
             box-shadow .35s var(--ease-expo);
}
/* کارت جلو بیشتر می‌درخشد تا سلسله‌مراتب حفظ شود. */
.fan__card.is-focused,
.fan__card:hover{
  box-shadow:var(--e-3), var(--edge),
             0 0 0 1px color-mix(in oklch, var(--uc-glow) 42%, transparent),
             0 18px 50px -14px color-mix(in oklch, var(--uc-glow) 88%, transparent);
}
@media (prefers-reduced-motion:reduce){
  .fan__card{ animation:none; --uc-glow:var(--violet); }
}
/* «افکت جذاب» که خواسته شده: یک هاله‌ی خیلی نازک هم‌رنگ حلقه، فقط روی کارت
   جلو. خودِ حلقه یک پیکسل می‌ماند؛ این فقط لبه را از زمینه جدا می‌کند. */
.tile.is-on{
  box-shadow:var(--e-3),
             0 0 0 1px color-mix(in oklch, var(--uc-cta-ring) 22%, transparent),
             0 8px 30px -14px color-mix(in oklch, var(--uc-cta-ring) 65%, transparent);
}
.card:not(.is-active)::before{ opacity:.45; }
.prod:hover::before,
.cat:hover::before,
.tile.is-on::before,
.card.is-active::before,
.uc__fact:hover::before{ opacity:1; }

.prod:hover,
.cat:hover{
  transform:translateY(-3px);
  box-shadow:var(--e-2), var(--edge),
             0 10px 30px -12px color-mix(in oklch, var(--accent) 55%, transparent);
}
/* بازخورد فشردن. کارت زیر انگشت/ماوس کمی جمع می‌شود و بعد برمی‌گردد —
   بدون این، حالت فعال صفر میلی‌ثانیه است و لمس روی موبایل هیچ پاسخی
   نمی‌گیرد. جمع‌شدن با همان بالا‌آمدن hover ترکیب می‌شود، پس هر دو در یک
   transform نوشته شده‌اند وگرنه دومی اولی را پاک می‌کند.
   .card و .tile عمداً کنار گذاشته شده‌اند: transform آن‌ها موقعیت سه‌بعدی
   خودشان است و افزودن scale، چیدمان چرخ‌فلک و بادبزن را جابه‌جا می‌کند. */
.prod:active,
.cat:active{
  transform:translateY(-1px) scale(.985);
  transition-duration:.09s;
  transition-timing-function:var(--ease-expo);
}
/* نور محیطیِ پشت نگین وقتی کارت زیر ماوس است. همان گرادیان شعاعیِ موجود
   است، فقط پررنگ‌تر — کارت «روشن می‌شود» به‌جای اینکه فقط بالا بپرد، و با
   کمانِ چرخانِ حلقه یک زبان تصویری می‌سازد. */
.prod__art, .cat__ico{
  transition:background .28s var(--ease-expo), box-shadow .28s var(--ease-expo);
}
.prod:hover .prod__art{
  background:radial-gradient(circle at 50% 42%,
    color-mix(in oklch, var(--accent) 30%, transparent), transparent 72%);
}
.cat:hover .cat__ico{
  box-shadow:0 0 0 1px color-mix(in oklch, var(--accent) 30%, transparent),
             0 6px 18px -8px color-mix(in oklch, var(--accent) 60%, transparent);
}
/* همان قاعده‌ی product.css/blog.css: بدون پشتیبانی backdrop-filter، یک سطح
   مات به‌جای جعبه‌ی نیمه‌شفافی که متنش روی ستاره‌ها خوانده نمی‌شود. */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .prod, .cat{ background:var(--surface); }
}
/* The whole card is one link (same pattern as .post__link in blog.css) —
   .prod used to carry its own flex layout, but once every child moved inside
   a single wrapping <a>, the anchor has to be the flex container instead, or
   the layout collapses to the anchor's own content size. */
.prod__hit{
  display:flex; flex-direction:column; gap:var(--s-2); block-size:100%;
  color:inherit; text-decoration:none;
}
/* 5:4 rather than the old 1.55. The art is the only thing on a product
   card that carries any weight at a glance, and at 1.55 it was a letterbox
   strip with a small gem floating in the middle of it. */
.prod__art{
  inline-size:100%; aspect-ratio:1.25; border-radius:12px;
  display:grid; place-items:center; border:1px solid var(--line);
  background:radial-gradient(circle at 50% 42%, color-mix(in oklch, var(--accent) 16%, transparent), transparent 70%), var(--surface-2);
  overflow:hidden;
}
.prod__art svg{ inline-size:62px; block-size:62px; color:var(--accent); }
/* عکس واقعی محصول (get_the_post_thumbnail) — تا اینجا هیچ قانونی
   نداشت، پس با سایز طبیعی خودش رندر می‌شد و از کادر بیرون می‌زد. */
.prod__img{
  inline-size:100%; block-size:100%;
  object-fit:cover; object-position:center;
  border-radius:inherit;
}
.prod__badge{
  position:absolute; inset-block-start:20px; left:20px;
  font-size:.6875rem; font-weight:700; padding:2px 9px;
  border-radius:var(--r-full); background:var(--ink); color:var(--on-ink);
}
.prod__title{ font-weight:700; font-size:var(--t-sm); line-height:1.55; }
/* Fixed two-line reservation, not just a clamp — a one-line description and a
   two-line one used to leave the price sitting at two different heights from
   card to card. min-block-size holds the space open even when the text is
   shorter; -webkit-line-clamp stops it if the text is longer. */
.prod__meta{
  font-size:.75rem; line-height:1.6; color:var(--text-muted);
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical;
  overflow:hidden; min-block-size:calc(1.6em * 2);
}
.prod__price{ font-weight:900; font-size:var(--t-base); color:var(--accent); }
.prod__price small{ font-weight:400; font-size:.6875rem; color:var(--text-muted); }
.prod__buy{ margin-block-start:var(--s-2); min-block-size:42px; font-size:var(--t-sm); }
.all__empty{ text-align:center; color:var(--text-muted); padding-block:var(--s-7); line-height:2; }
/* ---------------- footer ----------------
   Four columns that collapse to two and then one. The trust badges are last in
   the source but the first thing a shopper looks for, so on a phone — where
   everything is one column anyway — they end up directly above the fold of the
   legal line rather than buried under three other blocks. */
.foot{
  border-block-start:1px solid var(--line);
  background:var(--surface-2);
  color:var(--text-muted); font-size:var(--t-sm);
}
/* پیشخوان حساب کاربری (auth.css/.account-wrap): کارت‌ها و فوتر هر دو
   تیره‌اند و بلاب‌های آئورا/دانه‌های ستاره روی هر دو کشیده می‌شوند، پس
   مرز نازکِ بالا در این صفحات عملاً گم می‌شود — به‌خصوص در حالت خالیِ
   سفارش‌ها که فاصله‌ی زیادی از پس‌زمینه‌ی یکنواخت بین کارت و فوتر
   می‌ماند و کل چیز یک قاب واحد به‌نظر می‌رسد. این‌جا مرز را با کنتراست
   واقعی پررنگ می‌کنیم (خط ضخیم‌تر و پررنگ‌تر + همان لبه‌ی روشنِ داخلیِ
   --edge که کارت‌های بالاتر از آن استفاده می‌کنند، «حاشیه‌ای که نور را
   می‌گیرد» — نگاه کنید به توضیح بالای :root[data-theme="dark"])، نه
   فقط فاصله‌ی خالی. */
body.woocommerce-account .foot{
  border-block-start-width:2px;
  border-block-start-color:color-mix(in oklch, var(--ink) 40%, transparent);
  box-shadow:var(--edge);
}
.foot__grid{
  max-width:1240px; margin-inline:auto;
  padding:var(--s-8) var(--s-5) var(--s-6);
  display:grid; gap:var(--s-6) var(--s-7);
  grid-template-columns:repeat(auto-fit, minmax(210px, 1fr));
  align-items:start;
}
.foot__brand{ display:flex; flex-direction:column; gap:var(--s-3); }
.logo--foot{ font-size:var(--t-lg); }
.foot__note{ margin:0; max-inline-size:34ch; line-height:1.9; }
.foot__col h3{ font-size:var(--t-sm); font-weight:900; color:var(--text); margin-block-end:var(--s-3); }
.foot__col p{ margin:0 0 var(--s-2); line-height:1.9; }
.foot address{ font-style:normal; }
.foot__muted{ color:var(--text-muted); opacity:.75; font-size:.75rem; }
.foot__link{ color:var(--text); font-weight:700; font-variant-numeric:tabular-nums; }
.foot__link:hover{ color:var(--ink); text-decoration:underline; text-underline-offset:3px; }

.foot__trust{ display:flex; flex-wrap:wrap; gap:var(--s-3); }
/* Sized like the real thing: the issuers' badges are about this big, so
   swapping the placeholder for the live image later changes nothing else. */
.badge{
  display:flex; align-items:center; gap:var(--s-3);
  padding:var(--s-3); border-radius:var(--r-md);
  background:var(--surface); border:1px solid var(--line); box-shadow:var(--e-1);
  transition:border-color .18s var(--ease-out), transform .18s var(--ease-out);
}
.badge:hover{ border-color:var(--ink); transform:translateY(-2px); }
.badge__mark{ inline-size:34px; block-size:38px; color:var(--emerald); flex:none; }
.badge:last-child .badge__mark{ color:var(--indigo); }
.badge span{ display:flex; flex-direction:column; gap:2px; font-size:.6875rem; line-height:1.6; }
.badge strong{ font-size:.75rem; font-weight:900; color:var(--text); }

.foot__legal{
  margin:0; padding:var(--s-4) var(--s-5) var(--s-6);
  border-block-start:1px solid var(--line);
  text-align:center; font-size:.75rem;
}

/* ═══════════════ the brand panel — Universe Code ═══════════════
   One rectangle, one canvas, and four blocks of real text laid over it.
   The scene (app.js §8) runs top-right → bottom-left in PHYSICAL coordinates,
   not logical ones: it is a fall, and gravity does not flip with the reading
   direction. Everything below therefore uses left/right, not start/end.

   Nothing here uses backdrop-filter, unlike the article panes. Those float
   over a static starfield and blur it once; this one floats over a canvas that
   repaints every frame, which would mean re-blurring a 1240px panel 60 times a
   second. Near-opaque backgrounds cost nothing and look the same over ink. */
.uc{ max-width:1240px; margin-inline:auto; padding:0 var(--s-5) var(--s-9); }

.uc__panel{
  position:relative; isolation:isolate; overflow:hidden;
  border:1px solid var(--uc-line); border-radius:var(--r-lg);
  box-shadow:var(--e-2), var(--edge);
  padding-block-end:var(--s-6);
  /* Three washes pinned to the story: one where the cluster departs, one where
     it lands, one over the middle where the meteors catch it. */
  background:
    radial-gradient(115% 88% at 92% 2%,  color-mix(in oklch, var(--violet) var(--uc-wash-a), transparent), transparent 58%),
    radial-gradient(100% 80% at 4% 98%,  color-mix(in oklch, var(--cyan)   var(--uc-wash-b), transparent), transparent 62%),
    radial-gradient(70% 60% at 46% 40%,  color-mix(in oklch, var(--indigo) var(--uc-wash-b), transparent), transparent 70%),
    var(--uc-floor);
}
/* Covers the whole panel — the particles have to be able to drift out of the
   scene and down into the cards. The fall is confined to .uc__stage. */
.uc__sky{ position:absolute; inset:0; inline-size:100%; block-size:100%; z-index:0; pointer-events:none; }
/* The scene is as tall as the copy inside it plus a band of sky above and
   below — not a fixed height. Fixed, the frame had to be tall enough for the
   longest the paragraph ever gets, and at every width where it wrapped shorter
   that difference became dead space between the text and the cards. */
.uc__stage{
  position:relative;
  display:grid; place-items:center;
  padding-block:var(--s-7) var(--s-6);
  min-block-size:clamp(340px, 32vw, 440px);
}
.uc__frame{ position:absolute; inset:0; }

/* ---- the copy, inside the stage ----
   It sits over the stretch of the diagonal the meteors reach, because that is
   where its own dust comes from: the text is hidden until the ash thrown by
   the impacts has finished spelling it, and then it fades up in the same place
   the dust is already holding. No card, no border — a soft scrim is all the
   contrast body text needs over a starfield, and a hard-edged panel would
   land on the scene like a sticker. */
.uc__copy{
  position:relative; z-index:2;
  inline-size:min(760px, 76%); text-align:center;
  padding:var(--s-4) var(--s-6);
  background:radial-gradient(ellipse 74% 82% at 50% 50%,
    var(--uc-scrim) 0 48%,
    color-mix(in srgb, var(--uc-scrim) 52%, transparent) 70%,
    transparent 86%);
}
.uc__side{
  position:relative; z-index:2;
  display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between;
  gap:var(--s-5) var(--s-7);
  margin:var(--s-6) var(--s-6) 0; padding-block-start:var(--s-6);
  border-block-start:1px solid var(--uc-glass-line);
}
.uc__mark{ margin-block:6px var(--s-3); line-height:1.25; }
.uc__mark-en{
  display:block; font-size:var(--t-2xl); font-weight:900; letter-spacing:-.01em;
  color:var(--ink);
  /* dir="ltr" so the Latin name never gets reordered; centred like the block
     around it — `end` would push it to the right of a centred column. */
  text-align:center;
  transition:opacity .9s var(--ease-out), text-shadow .9s var(--ease-out);
  /* Only visible on dark, where --uc-glow is 1: the wordmark is the one thing
     in the panel allowed to look like it is emitting. */
  text-shadow:0 0 calc(38px * var(--uc-glow)) color-mix(in oklch, var(--violet) calc(38% * var(--uc-glow)), transparent);
}
.uc__mark-fa{ display:block; font-size:var(--t-sm); font-weight:500; color:var(--text-muted); letter-spacing:.04em; }
.uc__lede{ margin:0 0 var(--s-3); font-size:var(--t-lg); font-weight:500; }
.uc__body{ margin:0; font-size:var(--t-sm); color:var(--text-muted); }
.uc__stats{
  list-style:none; display:flex; flex-wrap:wrap; gap:var(--s-4) var(--s-7);
  margin:0; padding:0;
}
.uc__stats li{ display:flex; flex-direction:column; }
.uc__stats strong{ font-size:var(--t-base); font-weight:900; }
.uc__stats span{ font-size:.75rem; color:var(--text-muted); }
.uc__actions{ display:flex; flex-wrap:wrap; align-items:center; gap:var(--s-3); }

/* ---- the three released cards ----
   A plain three-up row rather than free-floating callouts: the impacts land on
   a diagonal, and any text block big enough to be worth reading would be cut
   by that diagonal wherever it sat. So the cards line up underneath, in the
   same right-to-left order as the impacts that release them, and app.js draws
   a hairline from each impact down into its card to keep the two tied. */
.uc__facts{
  position:relative; z-index:2;
  display:grid; grid-template-columns:repeat(3, 1fr); gap:var(--s-4);
  margin:0 var(--s-6); padding:0; list-style:none;
}
/* نوارِ ۲ پیکسلیِ اکسنت که قبلاً روی لبه‌ی آغازین بود حذف شد: حالا حلقه‌ی
   مشترک همان رنگ را دور تا دور کارت می‌برد، و نگه‌داشتن هر دو یعنی یک ضلع
   دو برابر بقیه پررنگ باشد. */
.uc__fact{
  position:relative; isolation:isolate;
  padding:var(--s-4); border-radius:var(--r-md);
  background:var(--uc-glass); border:1px solid var(--uc-glass-line);
  box-shadow:var(--e-1);
  transition:opacity .5s var(--ease-out), transform .5s var(--ease-out);
}
/* ---- what the scene hides ----
   Only under .uc--armed, which app.js adds to the panel at startup. Written
   into the stylesheet on its own, this would leave a panel with no cards and a
   ghost wordmark to anyone whose JS never ran. */
.uc--armed .uc__fact{ opacity:0; transform:translateY(14px) scale(.97); }
.uc--armed .uc__fact.is-on{ opacity:1; transform:none; }
/* The copy is not there until its dust has written it. */
.uc--armed .uc__copy{ opacity:0; transition:opacity .9s var(--ease-out); }
.uc--armed .uc__copy.is-on{ opacity:1; }
.uc__fact-star{ inline-size:16px; block-size:16px; fill:var(--accent, var(--ink)); display:block; margin-block-end:6px; }
.uc__fact h3{ font-size:var(--t-sm); font-weight:900; margin-block-end:4px; }
.uc__fact p{ margin:0; font-size:.75rem; line-height:1.85; color:var(--text-muted); }

/* ═══════════════ desktop-only pendulum room ═══════════════ */
@media (min-width:769px){ .stage{ --pendulum-room:118px; } }

/* ═══════════════ mobile ═══════════════ */
@media (max-width:768px){
  /* Plain px, not calc(): --card-h is read back with parseFloat for the ray
     maths, and a calc() string parses to NaN.
     Bumped 158×248 → 184×289 → 212×333 (same w:h ratio throughout) — the ring read too small on
     a phone. --radius and --stage-h are scaled up by the same factor so the
     chord between slots (2R·sin(22.5°)) and the vertical room the perspective
     projection needs both grow with the cards instead of the ring suddenly
     feeling cramped or clipping top/bottom. */
  :root{
    --card-w:212px; --card-h:333px;
    --radius:196px;
  }
  /* Desktop's 55.5%/44.5% split assumed the desktop --card-w:208px/--card-h:300px
     pair; at this breakpoint's narrower 184px width the same 1.25 art ratio
     needs a different split (147px of 289px, not 55.5%) to leave .card__body
     enough room for a real two-line description without the meta row
     collapsing to a sliver again. */
  .card__art{ block-size:51%; }
  .card__body{ block-size:49%; }
  /* Same reservation, scaled up with the cards: the ring is bigger here now,
     so the projection overshoots by more. */
  .stage{ --stage-h:438px; --card-room:46px; }
  .dots{ display:none; }
  .ring-count{ display:inline; }

  .fan-hero{ display:none; }
  .grid-hero{ display:block; }
  .pendulum{ display:none; }          /* no room, and no hover to justify it */
  .section-head--row{ flex-direction:column; align-items:stretch; }
  .ring-facts{ gap:var(--s-6); }
  /* Force two columns — auto-fit collapses to one at 375px, and a single-column
     product list halves how many products get seen per scroll. */
  .cats__grid, .all__grid{ grid-template-columns:1fr 1fr; }
  .cat--lead{ grid-column:span 2; }
  /* Bigger products out of the same width: the card's own padding and the gap
     between cards come in, and everything that was paying for that margin —
     the art, the gem, the title — gets the room instead. */
  /* Wider and shorter: the section's own side padding and the gap between cards
     come in so the two columns get the width back, and the art goes from square
     to landscape so the card stops being a tall strip. */
  .all{ padding-inline:var(--s-3); }
  .all__grid{ gap:var(--s-2); }
  .prod{ padding:var(--s-2); gap:5px; }
  .prod__art{ aspect-ratio:1.25; border-radius:10px; }
  .prod__art svg{ inline-size:50px; block-size:50px; }
  .prod__title{ font-size:var(--t-sm); line-height:1.5; }
  .prod__meta{ font-size:.75rem; line-height:1.6; }
  .prod__price{ font-size:var(--t-base); }
  .prod__buy{ min-block-size:40px; margin-block-start:4px; }
  .prod__badge{ inset-block-start:16px; left:16px; font-size:.625rem; }
  .cats{ padding-block:var(--s-7); }
  .all{ padding-block:var(--s-7) var(--s-8); }

  /* The brand panel unstacks: a shorter sky, then one card per row. Three
     cards side by side on a phone would be three columns of two-word lines.
     The scene needs no change — app.js measures .uc__stage, so the fall just
     runs inside a smaller frame. */
  .uc{ padding-inline:var(--s-4); }
  /* The copy comes back out of the stage: a phone has no room for a text block
     floating inside a scene, and dropping it below keeps the sky legible. The
     dust doesn't care — it is aimed at wherever the copy actually is, so the
     swarm simply streams down out of the band instead of gathering inside it. */
  /* Everything here is about height. On a phone the panel is the tallest thing
     on the page by a long way, and a scene you have to scroll through in three
     screenfuls is not one scene. The sky loses a third of its depth, the copy
     drops its tail, and the gaps between the blocks come in. */
  /* The copy stays inside the scene here too, exactly as it does on desktop:
     the words belong where the impacts happened. Dropping it below the sky
     meant a band of sky and a block of text each needing their own height —
     overlaid, they share one, and the panel loses everything that separation
     was costing. The name is high in the frame because the copy is, and the
     star that survives the fall climbs up to sit either side of it (app.js §8). */
  .uc__stage{ padding-block:var(--s-5) var(--s-4); min-block-size:300px; }
  .uc__copy{
    display:flex; flex-direction:column;
    inline-size:96%; padding:var(--s-3) 0;
  }
  .uc__mark{ order:-1; margin-block:0 var(--s-2); }   /* the name, first of all */
  .uc__lede{ font-size:var(--t-base); margin-block-end:var(--s-2); }
  .uc__body{ line-height:1.8; }
  /* The three stay side by side — stacked they cost 300px of height on their
     own. What gives way instead is their copy: each card keeps its first
     sentence and drops the tail. */
  .uc__facts{ gap:6px; margin-inline:var(--s-4); }
  .uc__fact{ padding:10px 9px; }
  .uc__fact-star{ inline-size:12px; block-size:12px; margin-block-end:4px; }
  .uc__fact h3{ font-size:.75rem; line-height:1.65; }
  .uc__fact p{ font-size:.6875rem; line-height:1.75; }
  .uc__more{ display:none; }
  .uc__side{
    margin:var(--s-4) var(--s-4) 0; padding-block-start:var(--s-4);
    gap:var(--s-4);
  }
  .uc__stats{ gap:var(--s-3) var(--s-5); }
  .uc__panel{ padding-block-end:var(--s-4); }
}

/* ---------------- reduced motion ---------------- */
@media (prefers-reduced-motion: reduce){
  html{ scroll-behavior:auto; }
  .st{ animation:none; }
  .scrim{ backdrop-filter:none; }
  .pendulum{ display:none; }
  .stage{ --pendulum-room:0px; }

  .fan{
    inline-size:100%; max-inline-size:1240px; block-size:auto; perspective:none;
    display:flex; gap:var(--s-3); overflow-x:auto;
    scroll-snap-type:x mandatory; padding-block-end:var(--s-3);
  }
  .fan__card{
    position:relative; inset:auto; transform:none !important;
    flex:0 0 var(--fan-card-w); block-size:var(--fan-card-h);
    opacity:1 !important; scroll-snap-align:center;
  }

  /* The brand panel keeps its composition — app.js paints the scene once, at
     the frame the fall has already finished — so only the reveal is dropped. */
  .uc--armed .uc__fact{ opacity:1; transform:none; }
  .uc--armed .uc__copy{ opacity:1; }
  .uc__fact, .uc__copy{ transition:none; }
  /* The comet never runs here, so its button has to be there from the start. */
  .fan__cta, .hero__cta{ opacity:1; transform:none; animation:none; }
  .fan__cta::before, .hero__cta::before, .fan__cta-star{ animation:none; }

  .stage{ perspective:none; block-size:auto; padding-block:var(--s-4); }
  .carousel{
    transform:none !important; inline-size:100%; block-size:auto;
    display:flex; gap:var(--s-4); overflow-x:auto;
    scroll-snap-type:x mandatory; padding-block-end:var(--s-4);
  }
  .card{
    position:relative; inset:auto; transform:none !important;
    flex:0 0 var(--card-w); block-size:var(--card-h); scroll-snap-align:center;
  }
  *{ transition-duration:.01ms !important; animation-duration:.01ms !important; }
}

/* نوار وضعیت فیلتر دسته در سربرگ «همه محصولات». تا وقتی فیلتری فعال نشده
   با hidden پنهان است و جای خالی نمی‌سازد؛ display:flex باید بعد از
   [hidden] بیاید وگرنه در بعضی مرورگرها روی آن سوار می‌شود. */
.all__filter{ display:flex; align-items:center; gap:var(--s-3); flex-wrap:wrap; }
.all__filter[hidden]{ display:none; }
.all__filter-label{ font-size:var(--t-sm); color:var(--text-muted); }
.all__filter-label b{ color:var(--ink); font-weight:700; }
