/* ==========================================================================
   پنل کاربران — نسل بعد — پایهٔ بصری
   --------------------------------------------------------------------------
   این فایل فقط همان چیزی است که در <helmet><style> طرح (Panel.dc.html,
   خطوط ۱۲-۳۱) بود: کی‌فریم‌های انیمیشن، پایهٔ body، رنگ placeholder،
   لینک a، و رعایتِ prefers-reduced-motion. تمامِ استایلِ کامپوننت‌ها
   عمداً inline در خودِ index.php می‌ماند — دقیقاً همان‌طور که طرح
   نوشته بود؛ این فایل چیزی به آن اضافه نمی‌کند و چیزی از آن کم نمی‌کند.

   فونت‌ها محلی سرو می‌شوند (هرگز CDN) — کاربر ایرانی به fonts.googleapis
   و cdn.jsdelivr.net دسترسی پایدار ندارد؛ طرح از آن دو آدرس استفاده
   می‌کرد، اینجا با همان دو خانوادهٔ فونت ولی فایل‌های محلی جایگزین شده.
   ========================================================================== */

@font-face {
  font-family: "Vazirmatn";
  src: url("/codebase/fonts/Vazirmatn-wght-v1.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Estedad";
  src: url("fonts/Estedad-arabic.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0600-06FF, U+0750-077F, U+08A0-08FF, U+FB50-FDFF, U+FE70-FEFF, U+200C-200D, U+2010-2011;
}
@font-face {
  font-family: "Estedad";
  src: url("fonts/Estedad-latin.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+2000-206F, U+2074, U+20AC, U+2122;
}

body {
  margin: 0;
  background: #070a09; /* شیشهٔ یکدست (۱۴۰۵/۰۶/۲۴): #0b0e0d قبلی → دقیقاً --base مکاپ.
                           پایهٔ لایهٔ ثابتِ body::before/::after در انتهای همین فایل. */
  color: #e8ede9;
  font-family: Vazirmatn, "Segoe UI", Tahoma, sans-serif;
}

/* ==========================================================================
   شاخِ ثابتِ اسکرول‌بار — رفعِ «لرزشِ هدر» موقعِ سوییچِ صفحه‌هایِ داشبورد
   --------------------------------------------------------------------------
   این صفحه با اسکرولِ عادیِ سند کار می‌کند (نه یک .cp-root/.cp-app اسکرولرِ
   جدا — دقیقاً به همان دلیلی که کامنتِ بالا توضیح می‌دهد: overflow-x روی
   html/body نمی‌آید چون nav را از ویوپورتِ واقعی جدا می‌کند). یعنی وقتی
   ارتفاعِ محتوایِ صفحه از ویوپورت بلندتر/کوتاه‌تر می‌شود، اسکرول‌بارِ خودِ
   مرورگر ظاهر/محو می‌شود و عرضِ مؤثرِ ویوپورت به‌اندازهٔ عرضِ آن (روی
   دسکتاپ ~۱۵-۱۷px) عوض می‌شود. .cp-root با margin:0 auto وسط‌چین است
   (_shell.html.php:1)، پس آن چند پیکسل مستقیماً به یک جابه‌جاییِ افقیِ کلِ
   ستون از جمله <header> ترجمه می‌شود — یعنی هر تپ رویِ نوارِ پایین که
   صفحه را از یک حالتِ اسکرول‌دار به بدونِ‌اسکرول (یا برعکس) ببرد (مثلاً
   داشبورد ↔ پروفایلِ پر) دقیقاً همان «لرزش روی هدر»ی است که مالک گزارش
   کرد. صفحه‌هایِ ورود/ثبت‌نام همین باگ را ندارند چون هرگز اسکرول نمی‌شوند
   (ارتفاعشان عمداً زیرِ ویوپورت نگه داشته شده — کامنتِ بالایِ همین فایل،
   خطِ ۱۹-۲۹ در _shell.html.php) نه چون رفعِ جداگانه‌ای دارند.

   دقیقاً همین باگ، با همین علامتِ اندازه‌گیری‌شده (~۱۵px)، قبلاً در پنلِ
   نمایندگان (home/reseller) دیده و با همین یک قاعده رفع شد
   (۲۰۲۶-۰۹-۰۸ — ds-scroll.css)؛ اینجا فقط همان رفعِ اثبات‌شده به پنلِ
   کاربران هم می‌رسد. برخلافِ overflow-x، این قاعده هیچ overflowِ جدیدی
   set نمی‌کند (فقط فضایِ اسکرول‌بارِ احتمالی را رزرو می‌کند)، پس در تعریفِ
   «همان یک محور را قفل کردن، دیگری خودبه‌خود auto می‌شود» نمی‌گنجد و
   چسبندگیِ nav به ویوپورتِ واقعی (خطِ ۱۱۵ همین فایل) دست‌نخورده می‌ماند —
   ولی چون آن رفتار حساس است، بازبینیِ زنده (nav هنوز روی صفحهٔ بلند
   sticky است) لازم است. */
html {
  scrollbar-gutter: stable;
}

a { color: #34d399; text-decoration: none; }
a:hover { color: #6ee7b7; }
input::placeholder { color: #5d6761; }

/* حالتِ فوکوس/تایپِ فیلدهای فرم (۱۴۰۵/۰۶/۲۵ — بازخوردِ مالک: «فیلد موقعِ تایپ
   رنگ و افکت داشته باشد»). همهٔ wrapperهای فیلد در این پنل <label>ِ با امضای
   border-radius:14px هستند (ورود/ثبت‌نام/بازیابی/تغییر رمز/درگاه)، پس یک قانون
   همه را می‌پوشاند. باکس‌های OTP این امضا را ندارند و دست‌نخورده می‌مانند.
   inlineها specificity بالا دارند، پس border/background با !important بازنویسی
   می‌شوند؛ ring (box-shadow) و رنگیِ آیکن هم روی فوکوس. transition = افکتِ نرم. */
label[style*="border-radius:14px"] { transition: border-color .18s ease, background-color .18s ease, box-shadow .18s ease; }
label[style*="border-radius:14px"]:focus-within {
  border-color: rgba(52,211,153,.55) !important;
  background: rgba(52,211,153,.06) !important;
  box-shadow: 0 0 0 3px rgba(52,211,153,.14);
}
label[style*="border-radius:14px"]:focus-within > svg { color: #34d399 !important; }

@keyframes fadeUp { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
@keyframes ringIn { from { stroke-dashoffset: 326.7; } }
@keyframes gaugeIn { from { stroke-dashoffset: 150.8; } }
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes modalIn { from { opacity: 0; transform: translateY(14px) scale(.98); } }
@keyframes backdropIn { from { opacity: 0; } }
/* خروجِ مودال (۱۴۰۵/۰۶/۲۵ — رفعِ «چشمکِ بستن»): derive از قبل
   animation:modalOut/backdropOut را هنگامِ بستن ست می‌کرد، ولی این دو کیفریم
   هرگز تعریف نشده بودند؛ پس بستن به‌جایِ محوِ نرم، ناگهانی حذف می‌شد (چشمک).
   duration در derive: modalOut .18s / backdropOut .17s؛ cpSoftClose بعد از
   ۱۹۰ms مودال را می‌بندد، پس هر دو با `both` تا لحظهٔ حذف روی مقصد می‌مانند. */
@keyframes modalOut { to { opacity: 0; transform: translateY(8px) scale(.985); } }
@keyframes backdropOut { to { opacity: 0; } }
@keyframes bellWobble {
  0%, 78%, 100% { transform: rotate(0); }
  81% { transform: rotate(-11deg); }
  84% { transform: rotate(9deg); }
  87% { transform: rotate(-6deg); }
  90% { transform: rotate(3deg); }
  93% { transform: rotate(0); }
}
@keyframes bellPuff { 0% { transform: scale(1); } 35% { transform: scale(1.32); } 65% { transform: scale(.94); } 100% { transform: scale(1); } }
@keyframes bellPuffB { 0% { transform: scale(1); } 35% { transform: scale(1.32); } 65% { transform: scale(.94); } 100% { transform: scale(1); } }
/* راندِ ۶/A3 — notifOut/backdropOut اینجا حذف شدند: بستنِ اختصاصیِ
   مودالِ اعلان‌ها (app.jsِ closeModal) دیگر این دو کی‌فریم را انتخاب
   نمی‌کند — با cpFadeAwayِ پارامتری‌شدهٔ smooth-ui.js (همان ۱۳۰msِ
   بستنِ عمومیِ مودال) جایگزین شده، دقیقاً برایِ رفعِ برخوردِ دوانیمیشنیِ
   AUDIT.md (§۱، ردیفِ «بستنِ اختصاصیِ مودالِ اعلان‌ها»؛ همان دلیلی که
   ثابت‌هایِ ۲۴۰/۲۶۰ms این دو کی‌فریم اصلاً با توکنِ ۱۳۰msِ بستنِ مودال
   یکی نبودند). notifIn وقتِ آن راند «باز شدن، دست‌نخورده می‌ماند» بود؛
   پسا-راندِ-۶ («افکتِ جن») همان بازشدن را هم از این کی‌فریمِ CSS به
   cpGenieِ WAAPیِ smooth-ui.js منتقل کرد (originِ ثابتِ scale(.06) با
   originِ دینامیکِ رویِ زنگوله جایگزین شد) — پس notifIn دیگر هیچ‌جا
   انتخاب نمی‌شود (app.jsِ panelAnim اکنون برایِ notif فقط 'none' است)
   و اینجا هم حذف شد؛ کی‌فریم‌هایِ modalIn/backdropIn برایِ بقیهٔ ۸
   مودال دست‌نخورده می‌مانند. */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    transition-duration: .001ms !important;
  }
}

/* ==========================================================================
   حالتِ «در حالِ اتصال» بایندینگِ رانتایم — نه در طرح، ولی لازم تا صفحه
   قبل از اجرای app.js سفید نماند (کاربرِ اینترنتِ کند). */
[data-cp-if] { transition: none; }
.cp-boot-fallback {
  min-height: 100vh; display: flex; align-items: center; justify-content: center;
  color: #7d877f; font-size: 13px;
}

/* ==========================================================================
   دو اصلاحِ سازگاریِ موبایل — نه در طرح (طرح فقط برایِ یک اندازهٔ ثابتِ
   پیش‌نمایش رسم شده بود)، ولی روی گوشیِ واقعی لازم‌اند. هر دو محافظِ
   ساختاری‌اند: تا وقتی چیزی سرریز نکند/کوتاه نباشد هیچ اثرِ بصری‌ای
   ندارند، پس چیزی از طرح را عوض نمی‌کنند.
   ========================================================================== */

/* ۱) «صفحه به چپ‌وراست کشیده می‌شود» — ستون همیشه max-width:430px است،
   ولی چند عنصرِ موقعیت‌دهی‌شده (مثلاً تول‌تیپِ نمودارِ مصرف که با
   inset-inline-start:50%+translateX(50%) روی آخرین میله می‌نشیند) چند
   پیکسل از لبه بیرون می‌زنند. روی سافاریِ موبایل حتی یکی-دو پیکسل سرریزِ
   افقی یعنی کلِ صفحه قابلِ کشیدن/رابرباند می‌شود. هیچ نیازِ مشروعی به
   اسکرولِ افقی در این اپ نیست.

   عمداً روی html/body نیست (باگِ واقعیِ همین پاس، پیدا و رفع‌شده):
   طبقِ اسپکِ CSS، وقتی overflow-x یک المنت چیزی غیر از visible باشد ولی
   overflow-y آن دست‌نخورده (یعنی visible) بماند، مرورگر مجبور است
   overflow-y را هم به auto تبدیل کند (نمی‌شود یک محور را قفل کرد و
   دیگری را واقعاً visible نگه داشت). این یعنی گذاشتنِ این قاعده روی
   html/body باعث می‌شد body عملاً یک «کانتینرِ اسکرولِ» جدا حساب شود —
   و <nav style="position:sticky;bottom:0"> (نوارِ پایین) دیگر نسبت به
   ویوپورتِ واقعی نمی‌چسبید، نسبت به body می‌چسبید که خودش دقیقاً هم‌قدِ
   محتواست (هیچ‌وقت اضافه‌ای برایِ چسبیدن ندارد) — نتیجه: نوارِ پایین در
   هر صفحه‌ای که محتوایش از ویوپورت بلندتر بود (یعنی تقریباً همه‌جا)
   دیگر «همیشه دیده» نبود؛ کاربر باید تا انتهای صفحه اسکرول می‌کرد تا
   نوارِ ناوبری را ببیند. رفع: این قاعده را از html/body برداشتیم و
   مستقیماً روی خودِ ناحیه‌هایِ محتوا گذاشتیم (header/main/صفحه‌هایِ
   ورود و بوت) — همه‌جایی که واقعاً ممکن است چیزی چند پیکسل بیرون بزند،
   ولی هیچ‌کدام جزوِ زنجیرهٔ اجدادِ <nav> نیستند، پس چسبندگیِ nav به
   ویوپورتِ واقعی دوباره درست کار می‌کند. */
header, main, [data-screen-label="ورود"], [data-screen-label="بوت"] {
  overflow-x: hidden;
}

/* ۲) مودال‌ها روی گوشیِ کوتاه — پنلِ مودال (`[data-cp-click="eatClick"]`،
   هر ۹ مودالِ اپ همین یک ساختار را دارند، به‌علاوهٔ `.cp-wa-panel` که مودالِ
   شارژِ کیف پول در wallet.js با DOM جدا می‌سازد و از همین قاعدهٔ مشترک
   تبعیت می‌کند) نه max-height داشت نه اسکرول؛ محتوای بلند (مثلاً «تأیید
   خرید» با چند ردیف + دو دکمه) روی صفحه‌نمایشِ کوتاه یا در حالتِ افقی از
   پایینِ ویوپورت بیرون می‌افتاد و هیچ راهی برایِ رسیدن به دکمه‌ها نبود.
   overflow-y:auto فقط وقتی فعال می‌شود که واقعاً لازم باشد — روی
   صفحه‌های معمولی هیچ تغییری در ظاهر نمی‌دهد. */
[data-cp-click="eatClick"], .cp-wa-panel {
  max-height: calc(100vh - 32px);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
}

/* ==========================================================================
   دسکتاپ — چیدمانِ هوشمند (≥ ۸۲۰px)
   نکتهٔ breakpoint: عمداً ۸۲۰ (نه ۱۰۲۴/۹۸۰). روی لپ‌تاپ با مقیاسِ ویندوزِ
   ۱۲۵–۱۵۰٪ یا زومِ مرورگر، عرضِ مؤثرِ CSS خیلی کمتر از عددِ فیزیکیِ صفحه
   می‌شود (مثلاً نمایشگرِ ۱۳۶۶ با مقیاسِ ۱۵۰٪ فقط ~۹۱۰px CSS است)؛ با ۹۸۰
   این صفحه‌ها اشتباهاً «موبایل روی دسکتاپ» می‌ماندند. ۸۲۰ همهٔ دسکتاپ/
   لپ‌تاپ‌ها (حتی مقیاس‌دار) و تبلتِ بزرگ را می‌گیرد، ولی موبایل و تبلتِ
   کوچک (پرتره ≤۷۶۸) دست‌نخورده می‌مانند.
   موبایل هیچ تغییری نمی‌کند؛ این بلوک فقط در عرضِ زیاد فعال می‌شود و
   «ستونِ ۴۳۰ کشیده روی دسکتاپ» را به «سایدبارِ راست + محتوایِ متعادل»
   تبدیل می‌کند — تماماً با override روی همان استایل‌هایِ inline، بدونِ
   دست‌زدن به ساختارِ HTML (پس با هیچ کارِ دیگری تداخل ندارد).

   دو نکتهٔ ظریف که این را امن می‌کند:
   • isApp با data-cp-if وقتی «فعال» است display:contents می‌گیرد و وقتی
     «غیرفعال» است display:none (app.js). قاعدهٔ گرید را با
     [style*="contents"] فقط به حالتِ فعال می‌چسبانیم — پس در صفحهٔ ورود
     (که isApp=none است) گرید فعال نمی‌شود و اپ اشتباهی نمایش داده نمی‌شود.
   • مودال‌ها position:fixed اند (خطِ ۵۹۰)، پس تبدیلِ isApp به گرید
     جای‌گذاریِ آن‌ها را به‌هم نمی‌زند.
   ========================================================================== */
@media (min-width: 820px) {
  /* رَپِرِ ریشه پهن می‌شود؛ ولی ورود/بوت باریک و وسط‌چین می‌مانند */
  .cp-root { max-width: 1180px !important; }
  [data-screen-label="ورود"], [data-screen-label="بوت"] {
    width: 100%; max-width: 430px; margin-inline: auto;
  }

  /* isApp فعال → گرید: سایدبارِ راست + هدر + محتوا (RTL: ستونِ اول راست است) */
  [data-cp-if="isApp"][style*="contents"] {
    display: grid !important;
    grid-template-columns: 244px minmax(0, 1fr);
    grid-template-rows: auto 1fr;
    grid-template-areas: "side head" "side main";
    align-items: start;
    min-height: 100vh;
  }

  /* هدر و محتوا هم‌عرض، وسط‌چین در ستونِ محتوا */
  [data-cp-if="isApp"] > header {
    grid-area: head;
    width: 100%; max-width: 720px; margin-inline: auto;
    padding: 26px 30px 10px !important;
  }
  [data-cp-if="isApp"] > main {
    grid-area: main;
    width: 100%; max-width: 720px; margin-inline: auto;
    padding: 8px 30px 34px !important;
  }

  /* نوارِ پایین → سایدبارِ عمودیِ چسبان در سمتِ راست */
  [data-cp-if="isApp"] > nav {
    grid-area: side;
    position: sticky !important;
    top: 0 !important; bottom: auto !important;
    align-self: start;
    height: 100vh;
    flex-direction: column !important;
    justify-content: flex-start !important;
    gap: 4px !important;
    border-top: none !important;
    border-inline-start: 1px solid rgba(255, 255, 255, .07) !important;
    background: transparent !important;
    padding: 22px 12px !important;
  }
  /* هر آیتم: افقی (آیکون + برچسب)، بزرگ‌تر و کلیک‌پذیرتر */
  [data-cp-if="isApp"] > nav > a {
    flex: 0 0 auto !important;
    flex-direction: row !important;
    justify-content: flex-start !important;
    gap: 12px !important;
    padding: 12px 14px !important;
    border-radius: 13px;
    font-size: 13.5px !important;
    transition: background .16s ease;
  }
  [data-cp-if="isApp"] > nav > a:hover { background: rgba(255, 255, 255, .05); }
}

/* ==========================================================================
   تول‌تیپِ نمودارِ «مصرف ۳۰ روز اخیر» — جلوگیری از بیرون‌زدن از کارت
   --------------------------------------------------------------------------
   هر میله یک لیبلِ شناور (em, white-space:nowrap) دارد که با
   inset-inline-start:50%+translateX(50%) روی همان میله وسط‌چین می‌شود.
   روی میله‌هایِ لبه (اولین = «۳۰ روز پیش» در راست، آخرین = «امروز» در چپ)
   نیمی از لیبل از لبهٔ کارت بیرون می‌زد — به‌ویژه با واحدهایِ بلندترِ
   «گیگابایت». این دو قاعده لیبلِ میله‌هایِ لبه را به‌جای وسط‌چین، به لبهٔ
   داخلیِ کارت لنگر می‌کنند تا به‌سمتِ داخل باز شود و بیرون نزند؛ میله‌هایِ
   میانی همان وسط‌چینِ قبلی می‌مانند. (موبایل و دسکتاپ، هر دو.) */
.cp-usebars > span:first-of-type em {
  inset-inline-start: 0 !important;
  inset-inline-end: auto !important;
  transform: none !important;
}
.cp-usebars > span:last-of-type em {
  inset-inline-start: auto !important;
  inset-inline-end: 0 !important;
  transform: none !important;
}

/* ==========================================================================
   رزرو ارتفاعِ نمودارِ روزانه — رفعِ «تیکِ» پرشِ ارتفاع، AUDIT.md §۶/A4
   (ادندومِ راندِ ۶، بندِ ۵: owner این را CRITICAL خطاب کرد)
   --------------------------------------------------------------------------
   [data-cp-if="uDaily"] (نمودارِ روزانهٔ صفحهٔ مصرف — _shell.html.php:544)
   مثلِ هر data-cp-if دیگر بین display:none و display:contents سوییچ
   می‌شود، هرگز خودش یک باکسِ واقعی نیست (تلهٔ مستندِ display:contents —
   حافظهٔ deltasib-users-cpif-contents-trap)، پس min-height مستقیم رویِ
   خودش وقتی مخفی است هیچ اثری ندارد.

   [data-screen-label="مصرف"] یک ستونِ flexِ gap:18px است با این ترتیبِ
   فرزندان: h2 → تب‌بارِ روزانه/ماهانه/سالانه → کارتِ خلاصه → uDaily →
   «ریزِ مصرف». وقتی تبِ ماهانه/سالانه انتخاب می‌شود و uDaily مخفی
   می‌شود، حذفِ آن از جریانِ flex هم ارتفاعِ خودش را می‌برد (padding
   ۱۶+۱۲px + ارتفاعِ صریحِ SVG ۱۲۰px + margin-top ردیفِ تاریخ ۸px +
   ~۱۵px متنِ همان ردیف ≈ ۱۷۱px) و هم یکی از دو gapِ مجاورش را (flex-gap
   فقط بینِ آیتم‌هایِ واقعاً حاضر می‌نشیند؛ حذفِ یک آیتم دو gapِ کناری‌اش
   را به یکی تبدیل می‌کند، یعنی ۱۸px دیگر) — جمعاً ≈۱۸۹px که کارتِ
   خلاصه و «ریزِ مصرف» را به‌هم می‌چسباند و کلِ صفحه (و نوارِ پایین) را
   یک «تیک» به بالا می‌پراند؛ برعکسِ همین وقتِ بازگشت به تبِ روزانه.

   رفع: به‌جایِ هدف‌گیریِ خودِ uDaily (که موقعِ مخفی‌بودن باکس ندارد)،
   والدِ آن (کارتِ خلاصه — طبقِ :has(+…) دقیقاً همان div ای که بلافصل
   قبلِ uDaily می‌آید، فارغ از اینکه چند div دیگر پیش‌ازآن باشند) وقتی
   uDaily مخفی است padding-bottom می‌گیرد؛ ۲۰۰px = ۱۸۹px محاسبه‌شده +
   حاشیهٔ ایمنی. با ظاهرشدنِ دوبارهٔ uDaily (display:contents) سلکتور
   دیگر تطبیق نمی‌خورد و padding خودش را پس می‌دهد — بدونِ JS، بدونِ
   انیمیشنِ اضافه، فقط یک قاعدهٔ CSSِ شرطی که هر دو جهت (نمایان↔مخفی) و
   هر دو مسیرِ ورود به تبِ ماهانه/سالانه (کش‌شده/در-حالِ-فچ) را یکسان
   می‌گیرد چون هرکدام فقط به display روی همین یک عنصر بستگی دارد.
   [style*="display: none"]: کروم/فایرفاکس/سافاری هر سه
   el.style.display='none' را به رشتهٔ attributeِ "display: none;" (با
   فاصله بعدِ دونقطه) سریالایز می‌کنند، پس substring-match مطمئن است. */
[data-screen-label="مصرف"] > div:has(+ [data-cp-if="uDaily"][style*="display: none"]) {
  padding-bottom: 200px;
}

/* ==========================================================================
   چیپ‌هایِ ویژگیِ بسته — صفحهٔ «خرید و تمدید» (assets/screens/buy.js،
   renderMetaChips). یک خطِ متنیِ « مدت · کاربر همزمان · سرعت »-جدا را به
   چند چیپِ کوتاهِ آیکون‌دار تبدیل می‌کند تا هرکدام در یک خط جا شود؛ خودِ
   دادهٔ سرور (p.meta) دست‌نخورده می‌ماند، فقط چیدمانش عوض شده.

   بدونِ ویژگیِ فیزیکیِ چپ/راست: gap و padدینگِ متقارن کارِ RTL را بدونِ
   inset-inline می‌دهد؛ flex-wrap طبقِ جهتِ سندِ RTL خودش راست‌به‌چپ
   می‌چیند. رنگ‌ها عیناً پالتِ تیره‌ی همین فایل (muted #7d877f/#9aa59d،
   تینتِ rgba(255,255,255,..) — نه رنگِ جدید).

   ۱۴۰۵/۰۶/۲۳ — رفعِ چیپِ سوم: سه چیپ (مدت·کاربر·سرعت) با flex-wrap:wrap
   وقتی سومی در ردیفِ اول جا نمی‌شد، تنها روی خطِ دوم می‌ماند — یتیمِ
   نامتوازن. علت: fwrap قبل از هر shrinkی تصمیمِ wrap می‌گیرد (هر چیپ با
   اندازهٔ طبیعیِ خودش سنجیده می‌شود)، پس هیچ‌وقت فرصتِ کوچک‌شدنِ باهم را
   نمی‌گرفتند. راهِ حل: nowrap + اجازهٔ shrink به خودِ چیپ (min-width:0،
   چون flex-item بدونِ آن هرگز از اندازهٔ محتوایش کوچک‌تر نمی‌شود، حتی با
   flex-shrink پیش‌فرضِ ۱) — از این به بعد همیشه یک ردیف، لیبل با
   ellipsisِ ازپیش‌آمادهٔ .cp-feat-chip__label جمع می‌شود اگر واقعاً جا
   نشد؛ زنجیرهٔ min-width:0 از این عنصر تا ستونِ متنِ کارت (کدِ
   _shell.html.php) از قبل بی‌وقفه است. */
.cp-feat-chips {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 5px;
  margin-top: 5px;
  min-width: 0;
}
.cp-feat-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  flex: 0 1 auto;
  min-width: 0;
  max-width: 100%;
  padding: 2px 8px;
  border: 1px solid rgba(255, 255, 255, .08);
  border-radius: 999px;
  background: rgba(255, 255, 255, .04);
  white-space: nowrap;
}
.cp-feat-chip svg {
  flex: 0 0 auto;
  color: #7d877f;
}
.cp-feat-chip__label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 12px;
  font-weight: 400;
  line-height: 1.6;
  color: #9aa59d;
}

/* ==========================================================================
   کارتِ سرویس دوسطری — Concept A (تأییدشدهٔ مالک ۱۴۰۵/۰۶/۲۲؛ مبنا:
   scratchpad/service-card-design/concepts.html → .ca-card/.ca-r1/.ca-name/
   .ca-price/.ca-r2/.chip/.chip--speed/.chip--flat/.radio). buy.js
   (renderPlanCards) این‌ها را بعدِ هر رندر به همان کارتِ ازپیش‌رندرشدهٔ
   _shell.html.php (قفل) اعمال می‌کند - بدونِ افزودنِ هیچ wrapper تازه‌ای
   به شِل، فقط با override رویِ همان دکمهٔ فعلی + جابه‌جاییِ خودِ
   <small> متای موجود به فرزندِ آخرِ همان دکمه.

   سطرِ ۱ همان ردیفِ متمرکزِ فعلیِ دکمه است (radio+ستونِ‌اطلاعات+قیمت)،
   فقط align-items از center به flex-start عوض شده تا نامِ دوخطی
   رادیو/قیمت را به وسطِ خودش نکشد. سطرِ ۲ (.cp-plan-row2) با
   flex:0 0 100% از همان ردیف به خطِ بعد می‌شکند - ترفندِ استانداردِ
   «تمام‌عرض‌کردنِ یک آیتمِ flex-wrap»، نه ساختارِ HTMLِ تازه.

   رنگ‌آمیزیِ حالتِ «غیرفعال» (unpriced) از راهِ planStyle()/data-cp-style
   در buy.js می‌آید (فقط border/bg، هرگز opacity/filter - همان‌جا توضیح
   داده شده) - این قواعد کاری با آن ندارند، پس متنِ «غیرفعال» هرگز کم‌رنگ
   نمی‌شود (مالک ۱۴۰۵/۰۶/۲۳). */
[data-cp-click="p.pick"] {
  flex-wrap: wrap;
  align-items: flex-start !important; /* این‌لاینِ shell: align-items:center */
}
/* نامِ بسته - حداکثر دو خط به‌جایِ کوچک‌شدنِ فونتِ تک‌خطیِ قبلی
   (fitPlanNames، در buy.js حذف شد). فونتِ ثابتِ ۱۴٫۵px همان مقدارِ
   این‌لاینِ shell است؛ اینجا فقط white-space و line-clamp عوض می‌شود. */
[data-cp-click="p.pick"] [data-fitname] {
  /* یک پله کوچک‌تر از ۱۴٫۵ این‌لاین (مالک ۱۴۰۵/۰۷/۰۵) تا نام‌های بلند کامل دیده شوند */
  font-size: 13.5px !important;
  white-space: normal !important; /* این‌لاینِ shell: nowrap */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* سطرِ دوم: چیپ‌هایِ مدت/کاربر + چیپِ اختصاصیِ سرعت. .cp-feat-chips
   بالا عمداً nowrap+shrink است (نه wrap - رفعِ چیپِ سومِ یتیم،
   ۱۴۰۵/۰۶/۲۳)؛ اینجا فقط جایگاهِ کلِ ردیف اضافه می‌شود، رفتارِ داخلیِ
   چیپ‌ها دست‌نخورده می‌ماند. */
.cp-plan-row2 {
  flex: 0 0 100%;
  margin-top: 11px;
  /* چیپ‌های ویژگی وسط‌چین (خواستِ مالک ۱۴۰۵/۰۶/۲۳) — پیش‌تر با
     padding-inline-start:34px کمی به چپ می‌افتادند. */
  justify-content: center;
}
/* چیپِ سرعتِ سقف‌دار - تینتِ کهربایی، عیناً مقدارِ تأییدشدهٔ مالک در
   مکاپ (border/bg/متن/آذرخش هم‌رنگ #e8b34b). سرعتِ بدونِ‌سقف («بدون
   محدودیت سرعت») این کلاس را نمی‌گیرد و همان ظاهرِ خنثیِ .cp-feat-chip
   معمولی را دارد - چیزِ تازه‌ای برایِ آن لازم نیست. */
.cp-feat-chip--speed {
  border-color: rgba(232, 179, 75, .32);
  background: rgba(232, 179, 75, .10);
}
.cp-feat-chip--speed svg {
  color: #e8b34b;
}
.cp-feat-chip--speed .cp-feat-chip__label {
  color: #e8b34b;
  font-weight: 500;
}
/* «پرسرعت» (سرویسِ بدونِ سقفِ سرعت) — بنفشِ همان گروهِ پرسرعتِ صفحهٔ خرید (#c084fc)
   با گرادیانِ ملایم و درخششِ کم تا حسِ سرعت بدهد (مالک ۱۴۰۵/۰۷/۰۵). */
.cp-feat-chip--fast {
  border-color: rgba(192, 132, 252, .42);
  background: linear-gradient(90deg, rgba(192, 132, 252, .20), rgba(192, 132, 252, .06));
  box-shadow: 0 0 10px rgba(192, 132, 252, .16);
}
.cp-feat-chip--fast svg {
  color: #c084fc;
}
.cp-feat-chip--fast .cp-feat-chip__label {
  color: #d8b4fe;
  font-weight: 500;
}

/* ==========================================================================
   فیلدِ کدِ پیامکی — خنثی‌کردنِ پس‌زمینهٔ autofillِ مرورگر
   --------------------------------------------------------------------------
   گامِ ۲ِ ثبت‌نام یک <input> شفافِ واقعی است که روی پنج خانهٔ تزیینی پهن
   شده (نگاه کن به کامنتِ بلندِ _shell.html.php روی همان بلوک): متن و
   caretش transparent است و رقم‌ها را account.js داخلِ خانه‌ها نقاشی
   می‌کند.

   وقتی کد را «خودِ مرورگر» پر می‌کند (کروم اندروید کد را بالایِ کیبورد
   پیشنهاد می‌دهد و از مسیرِ autofill می‌نشاند)، استایلِ UAِ کروم روی فیلد
   می‌نشیند:
       input:-internal-autofill-selected { background-color: … !important }
   یعنی فیلدِ شفاف ناگهان یک نوارِ آبیِ مات می‌شود، پنج خانه پشتش گم
   می‌شوند و چون رنگِ متن هنوز transparent است کاربر یک نوارِ *خالی*
   می‌بیند — یعنی دقیقاً همان «کد autofill نشد»ی که قرار بود رفع شود، این
   بار فقط از نظرِ بصری.

   با یک اعلانِ معمولی نمی‌شود جلویِ !importantِ UA را گرفت؛ راهِ درست
   این است که همان پس‌زمینه به گلیف‌ها clip شود، و گلیف‌ها خودشان
   نامرئی‌اند — پس عملاً چیزی رنگ نمی‌شود. background-clip در قاعدهٔ UA
   نیست، پس این کار می‌کند.
   ========================================================================== */
/* دو قاعدهٔ جدا و نه یک لیستِ سلکتور: اگر یکی از این دو شبه‌کلاس را
   مرورگر نشناسد، *کلِ* لیست (و همراهش قاعدهٔ سالم) دور ریخته می‌شود.
   :-webkit-autofill = کروم/سافاریِ قدیمی‌تر، :autofill = استانداردِ
   امروز (کروم ۱۰۸+/فایرفاکس). */
input[data-cp-field="otpInput"]:-webkit-autofill,
input[data-cp-field="otpInput"]:-webkit-autofill:hover,
input[data-cp-field="otpInput"]:-webkit-autofill:focus {
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  caret-color: transparent;
}
input[data-cp-field="otpInput"]:autofill,
input[data-cp-field="otpInput"]:autofill:hover,
input[data-cp-field="otpInput"]:autofill:focus {
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  caret-color: transparent;
}

/* ==========================================================================
   شیشهٔ یکدست («glass») — طرحِ تأییدشدهٔ مالک، ۱۴۰۵/۰۶/۲۴
   --------------------------------------------------------------------------
   مرجع دقیق: scratchpad/glass-mockup/glass.html (اسکرول‌کن و رنگ‌ها/تایمینگ
   عیناً از همان‌جا). فقط همین فایل عوض شده — هیچ خطی در _shell.html.php،
   app.js، smooth-ui.*، یا assets/screens/*.js دست نخورده. توضیح کامل و
   فهرستِ خط‌به‌خطِ هر سطحِ هدف در scratchpad/glass-impl/NOTES.md.

   دو تکه:
   (۱) بک‌گراندِ ثابتِ متحرک — body::before/::after بجایِ یک wrapperِ تازه:
       body دقیقاً دو شبه‌المنت دارد، هم‌ارزِ عدديِ همان دو <div class="glow">/
       <div class="glow2"> مکاپ. .cp-root شفاف می‌شود تا این لایه دیده شود
       (قبلاً یک گرادیانِ inlineِ opaque و ثابت داشت که هر چیزِ پشتش را
       می‌پوشاند).
   (۲) شیشهٔ یکدستِ کارت‌ها — بازنویسیِ background/border/backdrop-filter/
       shadowِ هر کارت با !important (چون inline است). border-radius دست
       نخورده می‌ماند (رادیوسِ خودِ هر کارت هرچه بود همان می‌ماند).
   ========================================================================== */

/* .cp-root پیش‌تر یک گرادیانِ inline داشت:
     background:radial-gradient(120% 46% at 50% -8%, rgba(52,211,153,.13), transparent 62%) #0b0e0d
   این opaque بود و کاملاً رویِ بک‌گراندِ ثابتِ تازه (پایین‌تر) را می‌پوشاند؛
   این‌جا شفاف می‌شود تا آن لایه از پشتش دیده شود. هیچ چیزِ دیگری از
   .cp-root عوض نمی‌شود (max-width/flex/دسکتاپ همه دست‌نخورده). */
.cp-root {
  background: transparent !important;
}

/* دو لایهٔ تابش، عیناً .glow/.glow2 مکاپ. چون هیچ wrapperِ تازه‌ای در
   HTML مجاز نیست، مستقیماً position:fixed می‌گیرند (نه position:absolute
   داخلِ یک .bgِ fixed مثلِ مکاپ) — از نظرِ هندسه دقیقاً هم‌ارز، چون آن
   .bg هم inset:0 بود (یعنی خودش کاملاً هم‌ترازِ ویوپورت). z-index منفی
   لازم است تا این لایه پشتِ .cp-root (که static و در جریانِ عادی است)
   بنشیند — دقیقاً همان چیزی که مکاپ با z-index:-2 می‌گرفت. */
body::before,
body::after {
  content: "";
  position: fixed;
  z-index: -1;
  pointer-events: none;
}
body::before {
  /* .glow مکاپ — تابشِ پهن/اصلی، ۲۶ ثانیه */
  left: 50%;
  top: -38%;
  width: 150%;
  height: 95%;
  transform: translateX(-50%);
  background: radial-gradient(50% 50% at 50% 50%, rgba(52,211,153,.20), rgba(52,211,153,.07) 42%, transparent 70%);
  filter: blur(30px);
  animation: cp-bg-breathe 26s ease-in-out infinite alternate;
}
body::after {
  /* .glow2 مکاپ — تابشِ باریک‌ترِ ثانویه، ۳۴ ثانیه. left:22% عیناً عددِ
     مکاپ است (بدونِ تبدیل به inset-inline-start) — این یک لکهٔ نرمِ
     تزیینیِ پشتِ صحنه است، نه عنصرِ جهت‌دار؛ اگر مالک آینه‌شدنش را در
     RTL بخواهد، همین یک خط تغییر می‌کند (نگاه کن NOTES.md). */
  left: 22%;
  top: -18%;
  width: 70%;
  height: 60%;
  transform: translateX(-50%);
  background: radial-gradient(50% 50% at 50% 50%, rgba(110,231,183,.10), transparent 66%);
  filter: blur(34px);
  animation: cp-bg-breathe2 34s ease-in-out infinite alternate;
}
@keyframes cp-bg-breathe  { 0% { opacity: .55; transform: translateX(-50%) scale(1);    } 100% { opacity: .8;  transform: translateX(-50%) scale(1.12); } }
@keyframes cp-bg-breathe2 { 0% { opacity: .38; transform: translateX(-50%) scale(1.05); } 100% { opacity: .62; transform: translateX(-50%) scale(1.15); } }
/* قاعدهٔ سراسریِ خطِ ۱۰۰-۱۰۵ همین فایل (animation-duration:.001ms !important
   روی *,*::before,*::after) از قبل این دو انیمیشن را هم منجمد می‌کند؛ این
   بازنویسیِ صریح فقط برایِ وضوح و ایمنیِ دوچندان است — عیناً رفتارِ
   @media همان قاعدهٔ مکاپ. */
@media (prefers-reduced-motion: reduce) {
  body::before, body::after { animation: none !important; }
}

/* کارتِ باقیماندهٔ داشبورد مثلِ بقیه: هالهٔ سبزِ مخصوصِ خودش (dash-ring-impl،
   .cp-aurora داخلِ همان section) حذف می‌شود تا بک‌گراندش هم یک‌رنگ و شیشه‌ایِ
   یکدست مثلِ بقیهٔ کارت‌ها باشد (خواستِ مالک). تابشِ سراسریِ ثابتِ body::before
   پشتِ همهٔ کارت‌ها یکسان دیده می‌شود. */
.cp-aurora { display: none !important; }

/* شیشهٔ یکدستِ کارت‌ها.
   سطوحِ هدف (فهرستِ کاملِ شماره‌خط در NOTES.md):
     • section[style*="border-radius:22px"] — تنها ۴ <section> سراسرِ فایل
       با این شعاع: چهار حالتِ متقابلاً‌انحصاریِ کارتِ وضعیتِ داشبورد
       (رینگِ فعال+رزرو / نامحدود / منقضی / بدونِ سرویس). هر ۴ تا یکسان
       می‌شوند — از جمله «منقضی» (قبلاً کهربایی) تا «همهٔ کارت‌ها یک شکل»
       واقعاً برقرار باشد؛ رنگِ هشدار همچنان از آیکن/متنِ داخلی‌اش می‌آید.
     • div[style*="border-radius:18px"][style*="rgba(255,255,255,.035)"] —
       دو attribute-selector روی یک attribute (AND منطقی): کارتِ نمودارِ
       داشبورد، دو کارتِ صفحهٔ مصرف، لیستِ سوابق، کارتِ هویتِ پروفایل،
       لیستِ تنظیماتِ پروفایل — هر ۷ مورد این دقیقاً همین دو رشته را دارند.
     • [data-cp-click="eatClick"], .cp-wa-panel — پنلِ مودال (سلکتورِ
       از پیش‌موجود در همین فایل، خطِ ۱۵۸؛ هر ۹ حالتِ مودال + پنلِ شارژِ
       کیف‌پولِ wallet.js را می‌گیرد چون هر دو یک‌جا آن‌جا هم استفاده
       شده‌اند).
   عمداً هدف گرفته نشده (کارایی/هم‌‌پوشانی؛ توضیحِ کامل در NOTES.md):
     ردیفِ هر «دستگاهِ متصل» (radius:16px، تکرارشونده)، و نوارِ مشخصات +
     لیستِ بسته‌های اضافه/تراکنش‌هایِ داخلِ مودالِ جزئیاتِ سرویس
     (radius:16px، از قبل داخلِ یک پنلِ شیشه‌ای‌اند — backdrop-filterِ
     تودرتو هم گران است هم بصراً کدر).

   background دو-لایه است: شیشهٔ سفیدِ عیناً مکاپ (rgba(255,255,255,.055))
   جلو، و یک لایهٔ تیرهٔ کم‌رنگِ همرنگِ --base (rgba(7,10,9,.45)) پشتش —
   این لایهٔ دوم صرفاً برایِ تضمینِ کنتراستِ AAِ متنِ کم‌رنگ (#9aa59d/
   #7d877f) روی کارت‌هایی‌ست که دقیقاً زیرِ اوجِ تابشِ سبز قرار می‌گیرند
   (محاسبه در NOTES.md)؛ در نواحیِ دور از تابش تقریباً بی‌اثر است (چون
   رنگش با --base یکی است). border-radius دست‌نخورده می‌ماند. */
section[style*="border-radius:22px"],
div[style*="border-radius:18px"][style*="rgba(255,255,255,.035)"],
[data-cp-click="eatClick"],
.cp-wa-panel {
  background:
    linear-gradient(rgba(255,255,255,.055), rgba(255,255,255,.055)),
    rgba(7,10,9,.45) !important;
  border-color: rgba(255,255,255,.09) !important;
  -webkit-backdrop-filter: blur(16px) saturate(1.25) !important;
  backdrop-filter: blur(16px) saturate(1.25) !important;
  box-shadow: 0 1px 0 rgba(255,255,255,.04) inset, 0 10px 30px rgba(0,0,0,.28) !important;
}

/* ==========================================================================
   اسکلتِ خاکستریِ بارگذاری — صفحهٔ سوابق، AUDIT.md §۷/B4
   --------------------------------------------------------------------------
   history.js/_shell.html.php («در حالِ بارگذاری…»یِ ساده، هم زیرِ
   hLoadingِ فهرست هم زیرِ svcLoadingِ مودالِ جزئیات) تنها نقطه‌هایِ تمامِ
   اپ بودند که هیچ حسِ «چیزی دارد می‌آید» نمی‌دادند — فقط یک خطِ متن،
   بدونِ شکل/اندازه‌ای شبیهِ محتوایِ واقعی. این بلوک فقط ظاهرِ بصریِ آن دو
   حالت را عوض می‌کند (چیزِ تازه‌ای در ساختارِ داده/state نیست)؛ ضربانِ
   ملایمِ opacity عمداً یک @keyframesِ سادهٔ CSSِ ambient است، عیناً همان
   خانوادهٔ spin/bellWobble/cp-bg-breathe همین فایل، نه یک انیمیشنِ
   تعویضِ محتوا — پس قاعدهٔ WAAPI-only اینجا موضوعیت ندارد (آن قاعده
   گذارهایِ سوییچِ حالت را هدف می‌گیرد، نه لوپ‌هایِ تزئینیِ همیشگی). قاعدهٔ
   سراسریِ prefers-reduced-motion (بالایِ همین فایل) این ضربان را هم مثلِ
   بقیهٔ انیمیشن‌ها منجمد می‌کند — رایگان، بدونِ override اضافه. */
.cp-skel {
  background: rgba(255,255,255,.06);
  animation: cp-skel-pulse 1.4s ease-in-out infinite;
}
@keyframes cp-skel-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .4; } }

/* ==========================================================================
   آیتمِ F (بازخوردِ مالک، ۱۴۰۵/۰۶/۲۴) — safe-area در حالتِ نصب‌شده (standalone)
   --------------------------------------------------------------------------
   iOS: viewport-fit=cover + apple-mobile-web-app-status-bar-style=black-
   translucent (index.php <head>) یعنی محتوا از قبل زیرِ status-bar می‌رود؛
   داخلِ اپِ نصب‌شده این باعث می‌شد بالایِ هدر زیرِ نوارِ سیاهِ وضعیت/notch
   قایم شود. فقط در standalone (نه در تبِ معمولیِ مرورگر — مالک صریحاً
   خواسته «مرورگر دست‌نخورده بماند»): env(safe-area-inset-top) به همان
   padding-topِ ثابتِ ۲۴px هدر اضافه می‌شود. نوارِ پایین از قبل درست بود
   (_shell.html.php، nav: calc(8px + env(safe-area-inset-bottom)) بدونِ
   هیچ شرطی) — دست‌نخورده ماند، لازم به تغییر نبود.

   همان سلکتور و همان تکنیکِ override با !important که چند ده‌خط بالاتر
   (بریک‌پوینتِ دسکتاپ @media(min-width:820px)) رویِ همینِ عنصر/همینِ
   ویژگی از قبل استفاده شده — چون padding این هدر inline است (سبکِ کلِ این
   فایل)، نه یک کلاسِ تازه در _shell.html.php. عمداً فقط longhand
   (padding-top)، نه shorthand، تا اگر یک PWAِ نصب‌شده روزی روی صفحهٔ بزرگ
   (≥۸۲۰px) هم باز شود، سه ضلعِ دیگرِ padding را که قاعدهٔ دسکتاپِ بالا
   ست می‌کند دست‌نخورده بگذارد — دو قاعده با همان specificity/!important
   روی دو ویژگیِ متفاوت (padding کامل / فقط padding-top) بی‌تعارض ترکیب
   می‌شوند. */
@media (display-mode: standalone) {
  [data-cp-if="isApp"] > header {
    padding-top: calc(24px + env(safe-area-inset-top)) !important;
  }
}

/* ==========================================================================
   محافظت خانواده (۱۴۰۵/۰۷/۰۵) — کارت داشبورد، طرح «چتر خانواده» (Claude Design، تأیید مالک).
   رنگ‌های هلویی/مرجانی فقط مال همین کارت‌اند.
   ========================================================================== */
/* ==========================================================================
   کارت «محافظت خانواده» (cp-fam) — داشبورد پنل مشتری
   حالت‌ها روی خصیصه‌های data-on / data-busy و aria-* کلید (از data-cp-attr).
   رنگ گرم فقط مخصوص همین کارت است؛ کلید روشن همان سبز پنل می‌ماند.
   ========================================================================== */
@keyframes cpFamSpin { to { transform: rotate(360deg); } }
@keyframes cpFamIn { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }

.cp-fam { position: relative; overflow: hidden; border: 1px solid rgba(255,255,255,.09); border-radius: 18px; background-color: rgba(255,255,255,.035); padding: 14px 16px; transition: border-color .3s ease, background-color .3s ease; }
.cp-fam[data-on="1"] { border-color: rgba(246,184,154,.26); background-color: rgba(246,184,154,.045); }
.cp-fam-row { display: grid; grid-template-columns: auto minmax(0,1fr) auto; column-gap: 12px; row-gap: 2px; align-items: center; }

/* کاشی تصویر */
.cp-fam-tile { grid-row: span 2; width: 56px; height: 56px; box-sizing: border-box; border-radius: 17px; display: flex; align-items: center; justify-content: center; border: 1px solid rgba(255,255,255,.1); background-color: rgba(255,255,255,.05); transition: border-color .3s ease, background-color .3s ease; }
.cp-fam[data-on="1"] .cp-fam-tile { border-color: rgba(246,184,154,.3); background-color: rgba(246,184,154,.13); }
.cp-fam-art { display: block; overflow: visible; }
.cp-fam-art * { transition: fill .35s ease, stroke .35s ease, opacity .35s ease, transform .55s cubic-bezier(.34,1.45,.64,1); }
.cp-fam-p, .cp-fam-k { fill: #5f6a63; }
.cp-fam[data-on="1"] .cp-fam-p { fill: #f6c2a0; }
.cp-fam[data-on="1"] .cp-fam-k { fill: #ffdcbc; }
/* چتر: خاموش = باز و کج بالای سر، خطی؛ روشن = روی سر پدر/مادر و بچه می‌نشیند */
.cp-fam-canopy path { fill: transparent; stroke: rgba(255,255,255,.32); stroke-width: 1.4; stroke-linejoin: round; stroke-linecap: round; }
.cp-fam-canopy path:first-child { stroke-dasharray: 3 2.4; }
.cp-fam-canopy { transform-box: fill-box; transform-origin: 50% 100%; transform: translateY(-3px) rotate(-12deg); }
.cp-fam[data-on="1"] .cp-fam-canopy { transform: none; }
.cp-fam[data-on="1"] .cp-fam-canopy path { stroke: #f2958a; }
.cp-fam[data-on="1"] .cp-fam-canopy path:first-child { fill: #f2958a; stroke-dasharray: none; }
.cp-fam-hrt { fill: #f2958a; opacity: 0; transform-box: fill-box; transform-origin: center; transform: scale(.3); }
.cp-fam[data-on="1"] .cp-fam-hrt { opacity: 1; transform: none; transition-delay: .3s; }
.cp-fam[data-on="1"] .cp-fam-hrt.is-late { transition-delay: .45s; }

/* متن */
.cp-fam-head { min-width: 0; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.cp-fam-title { margin: 0; font-family: Estedad, Vazirmatn, sans-serif; font-size: 14px; font-weight: 500; line-height: 1.5; color: #e7ece8; }
.cp-fam-chip { display: inline-flex; align-items: center; min-height: 20px; padding: 0 8px; border-radius: 999px; font-size: 12px; font-weight: 400; line-height: 1; white-space: nowrap; border: 1px solid rgba(255,255,255,.08); background-color: rgba(255,255,255,.04); color: #9aa59d; }
.cp-fam[data-on="1"] .cp-fam-chip { color: #f6b89a; border-color: rgba(246,184,154,.26); background-color: rgba(246,184,154,.1); }
.cp-fam[data-busy="1"] .cp-fam-chip { color: #cfd8d1; border-color: rgba(255,255,255,.1); background-color: rgba(255,255,255,.05); }
.cp-fam-desc { grid-column: 2 / -1; margin: 0; font-size: 12px; font-weight: 400; line-height: 1.8; color: #9aa59d; }

/* کلید (role=switch) — هدف لمسی ۵۶×۴۴ */
.cp-fam-sw { display: inline-flex; align-items: center; justify-content: center; min-width: 56px; min-height: 44px; margin-inline-end: -4px; padding: 0; border: 0; background: none; cursor: pointer; -webkit-tap-highlight-color: transparent; }
.cp-fam-sw:focus { outline: none; }
.cp-fam-sw:focus-visible .cp-fam-track { outline: 2px solid #34d399; outline-offset: 3px; }
.cp-fam-track { position: relative; display: block; width: 48px; height: 28px; box-sizing: border-box; border-radius: 999px; border: 1px solid rgba(255,255,255,.16); background-color: rgba(255,255,255,.1); transition: background-color .22s ease, border-color .22s ease; }
.cp-fam-sw[aria-checked="true"] .cp-fam-track { background-color: #34d399; border-color: #34d399; }
.cp-fam-knob { position: absolute; top: 2px; inset-inline-start: 2px; width: 22px; height: 22px; border-radius: 50%; background-color: #e7ece8; box-shadow: 0 1px 3px rgba(0,0,0,.35); display: flex; align-items: center; justify-content: center; transition: transform .24s cubic-bezier(.22,.9,.28,1); }
.cp-fam-sw[aria-checked="true"] .cp-fam-knob { transform: translateX(-20px); }
[dir="ltr"] .cp-fam-sw[aria-checked="true"] .cp-fam-knob { transform: translateX(20px); }
.cp-fam-sw[aria-disabled="true"] { cursor: progress; pointer-events: none; }
.cp-fam-sw[aria-disabled="true"] .cp-fam-track { background-color: rgba(255,255,255,.07); border-color: rgba(255,255,255,.14); }
.cp-fam-sw[aria-disabled="true"][aria-checked="true"] .cp-fam-track { background-color: rgba(52,211,153,.36); border-color: rgba(52,211,153,.5); }
.cp-fam-spin { display: none; width: 12px; height: 12px; box-sizing: border-box; border-radius: 50%; border: 2px solid rgba(4,35,26,.18); border-top-color: #0e8f64; animation: cpFamSpin .8s linear infinite; }
.cp-fam[data-busy="1"] .cp-fam-spin { display: block; }

/* یک‌خطیِ بعد از تغییر / خطا */
.cp-fam-msg { display: flex; align-items: center; gap: 7px; margin-top: 12px; padding-top: 10px; border-top: 1px solid rgba(255,255,255,.07); font-size: 12px; font-weight: 400; line-height: 1.7; color: #9aa59d; animation: cpFamIn .3s ease both; }
.cp-fam-msg svg { flex: 0 0 auto; }
.cp-fam-msg.is-note svg { color: #f6b89a; }
.cp-fam-msg.is-err { color: #e5757a; }

@media (prefers-reduced-motion: reduce) {
  .cp-fam, .cp-fam * { transition: none !important; }
  .cp-fam-msg { animation: none; }
  .cp-fam-spin { animation-duration: 2s; }
}
