/* ============================================================
   آراپشتیبان — پایهٔ مشترک (ریست سبک، تایپوگرافی، اسکرول‌بار، فوکس)
   وابسته به tokens.css. طراحی حفظ شده؛ فقط استاندارد و یک‌دست.
   ============================================================ */

/* ============================================================
   اسکرولِ یک‌دست (جایگزینِ ۵ نسخهٔ پراکنده در قالب‌ها)

   موبایل/تبلت (<۹۹۲px): نوارِ بومیِ مرورگر — که خودش روکش و خودمخفی‌شونده
   است. هیچ JSای دخالت نمی‌کند.

   دسکتاپ (≥۹۹۲px): نوارِ بومی پنهان می‌شود چون فضای واقعی در layout اشغال
   می‌کرد و خطوطِ ردیفِ جدول‌ها تا لبه نمی‌رسیدند. به‌جایش نشانگرِ روکشِ
   .ovs-thumb توسطِ client/js/app.js ساخته می‌شود: فضا اشغال نمی‌کند،
   فقط هنگامِ اسکرول/hover دیده می‌شود و با موس قابلِ کشیدن است.
   تخریبِ تدریجیِ امن: اگر JS اجرا نشود محتوا همچنان با موس/تاچ/کیبورد
   اسکرول می‌شود — فقط نشانگر دیده نمی‌شود.
   ============================================================ */
.scroll-thin { overflow: auto; scrollbar-width: thin; scrollbar-color: var(--border-strong) transparent; }
.scroll-thin::-webkit-scrollbar { width: 6px; height: 6px; }
.scroll-thin::-webkit-scrollbar-track { background: transparent; }
.scroll-thin::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 10px; }
.scroll-thin::-webkit-scrollbar-thumb:hover { background: var(--text-faint); }

/* پیش‌فرض خاموش: زیر ۹۹۲px نوارِ بومی سرِ کار است. این قانون بیرونِ
   مدیا-کوئری است تا اگر کاربر پنجره را از دسکتاپ کوچک کند، نشانگرِ
   ساخته‌شده به یک <div> عادیِ درونِ جریان تبدیل نشود (که در فلکس‌های
   gap-دار فاصلهٔ اضافه می‌ساخت). */
.ovs-thumb { display: none; }

@media (min-width: 992px) {
  /* نوارِ بومی پنهان (هر دو موتور) تا محتوا تمام‌عرض بماند */
  .scroll-thin { position: relative; scrollbar-width: none; }
  .scroll-thin::-webkit-scrollbar { width: 0; height: 0; }

  /* نشانگرِ روکش — موقعیت/ارتفاعش را JS ست می‌کند (style-src-attr مجاز است) */
  .ovs-thumb {
    position: absolute; inset-inline-end: 3px; width: 6px;
    border-radius: 10px; background: var(--border-strong);
    opacity: 0; pointer-events: none;
    transition: opacity .25s ease, background-color var(--t-fast);
    /* بالاتر از هدرِ چسبانِ جدول‌ها (th با z-index:10) تا زیرِ آن گم نشود */
    z-index: 20;
  }
  /* دیده‌شدن: هنگامِ اسکرول، hoverِ ناحیه، یا کشیدنِ خودِ نشانگر */
  .scroll-thin.is-scrolling > .ovs-thumb,
  .scroll-thin:hover > .ovs-thumb,
  .scroll-thin.is-dragging > .ovs-thumb { opacity: 1; pointer-events: auto; }
  .ovs-thumb:hover,
  .scroll-thin.is-dragging > .ovs-thumb { background: var(--text-faint); }
  /* فقط وقتی محتوا واقعاً سرریز دارد روشن می‌شود (کلاس را JS می‌گذارد) */
  .scroll-thin.is-scrollable > .ovs-thumb { display: block; }
}

@media (prefers-reduced-motion: reduce) {
  .ovs-thumb { transition: none; }
}

/* ============================================================
   گذارِ تمِ روز/شب — «موجِ» دایره‌ای از خودِ سوییچ (View Transitions API)

   مشکلی که حل می‌کند: قبلاً فقط `body` (و کلاسِ .ds-themed که هرگز جایی
   استفاده نشد) ترنزیشن داشت. نتیجه این بود که هنگامِ سوییچ، هر بخشِ صفحه با
   زمان‌بندیِ خودش عوض می‌شد — بدنه ۰.۲s، هدر و کارت‌ها پرشِ آنی، آیتم‌های
   سایدبار ۰.۲s، ریلِ سوییچ ۰.۳s و ویجتِ ساعت ۰.۸s ⇒ ظاهرِ «تیکه‌تیکه».

   راه‌حلِ استاندارد ۲۰۲۶: مرورگر یک اسنپ‌شات از کلِ صفحه می‌گیرد و نسخهٔ
   جدید را با یک کلیپِ دایره‌ای که از مرکزِ دکمهٔ سوییچ باز می‌شود رو می‌کند.
   چون کلِ صفحه یک تصویرِ واحد است، هدر/سایدبار/محتوا اجباراً هم‌زمان و
   یک‌دست عوض می‌شوند — «تیکه‌ای» بودن از نظر ساختاری ممکن نیست.
   پشتیبانی: Baseline از اکتبر ۲۰۲۵ (Chrome/Edge/Safari + Firefox ۱۴۴).

   مختصاتِ مرکز و شعاع را JS در سه متغیرِ --vt-* روی <html> می‌گذارد؛
   شبه‌عنصرهای ::view-transition از ریشه ارث می‌برند. مقدارِ fallbackِ هر
   متغیر (وسطِ صفحه) عمدی است تا اگر JS آن‌ها را ست نکرد، انیمیشن به‌جای
   شکستن، از مرکزِ صفحه باز شود.
   ============================================================ */
::view-transition-old(root),
::view-transition-new(root) {
  /* حذفِ کراس‌فیدِ پیش‌فرض + خنثی‌کردنِ plus-lighter (وگرنه لحظه‌ای سفید می‌زند) */
  animation: none;
  mix-blend-mode: normal;
}
/* فقط لایهٔ «جدید» انیمیت می‌شود و روی لایهٔ قدیم می‌نشیند */
::view-transition-new(root) {
  animation: theme-wipe var(--t-theme) var(--e-theme) both;
}
@keyframes theme-wipe {
  from { clip-path: circle(0px at var(--vt-x, 50%) var(--vt-y, 50%)); }
  to   { clip-path: circle(var(--vt-r, 150vmax) at var(--vt-x, 50%) var(--vt-y, 50%)); }
}

/* فالبکِ مرورگرِ بدونِ View Transitions: یک کراس‌فیدِ هم‌زمان روی همهٔ
   عناصر. کلاس را JS فقط در همان لحظهٔ سوییچ می‌گذارد و بعد برمی‌دارد، پس
   این قانونِ سنگین در حالتِ عادی روی هیچ عنصری فعال نیست.
   !important لازم است تا ترنزیشن‌های محلیِ متفاوت (۰.۲s/۰.۳s/۰.۸s) در همین
   یک لحظه هم‌زمان شوند — دقیقاً همان چیزی که باگ را می‌ساخت. */
:root.theme-anim,
:root.theme-anim *,
:root.theme-anim *::before,
:root.theme-anim *::after {
  transition: background-color var(--t-slow), border-color var(--t-slow),
              color var(--t-slow), fill var(--t-slow), box-shadow var(--t-slow) !important;
}

@media (prefers-reduced-motion: reduce) {
  /* JS در این حالت اصلاً View Transition را شروع نمی‌کند؛ این قانون فقط
     برای اطمینان است تا هیچ حرکتی باقی نماند. */
  ::view-transition-old(root),
  ::view-transition-new(root) { animation: none !important; }
  :root.theme-anim,
  :root.theme-anim *,
  :root.theme-anim *::before,
  :root.theme-anim *::after { transition: none !important; }
}

/* حالتِ فوکسِ کیبورد قابل‌دیدن (دسترس‌پذیری) */
:where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

/* اعدادِ هم‌ترازِ ستونی هرجا رقم داریم */
.tnum { font-variant-numeric: tabular-nums; }

/* ============================================================
   مثلثِ جهت‌نما (.tri) — جایگزینِ آیکونِ فونتیِ chevron/caret.

   چرا فونت کنار گذاشته شد: bootstrap-icons روی ::before مقدارِ
   vertical-align: -.125em می‌گذارد، پس گلیف داخلِ باکسِ خودش بالا
   می‌نشیند. اندازه‌گیریِ پیکسلی (بزرگ‌نماییِ ۸ برابر) نشان داد گلیف
   در .risk-nav حدودِ ۱.۰۶px و در کاشیِ داشبورد ۱.۸۸px بالاتر از مرکزِ
   هندسی رسم می‌شد — باکسِ <i> دقیقاً وسط بود، مشکل از متریکِ فونت بود.
   چهار راه‌حل سنجیده شد؛ clip-path تنها گزینه‌ای بود که روی هر دو محور
   زیرِ نیم‌پیکسل ماند (عمودی ۰.۰۶px، افقی ۰.۲۵px) و چون هندسی است
   هیچ‌وقت با تغییرِ فونت/نسخهٔ آیکون‌ها دوباره خراب نمی‌شود.

   رنگ از currentColor می‌آید، پس خودکار تم‌آگاه است و در hover همراهِ
   والد عوض می‌شود — بدونِ هیچ قانونِ اضافه.
   ============================================================ */
.tri {
  display: block; width: 9px; height: 11px; flex-shrink: 0;
  background: currentColor;
  clip-path: polygon(100% 0, 100% 100%, 0 50%);   /* رو به چپ (پیش‌فرضِ RTL: «بعدی») */
}
.tri-start { clip-path: polygon(0 0, 0 100%, 100% 50%); }  /* رو به راست («قبلی» در RTL) */
.tri-sm { width: 7px; height: 9px; }

/* کمک‌کلاس‌های متن بر پایهٔ توکن (به‌جای هگزِ خام در قالب‌ها) */
.t-ink { color: var(--ink); }
.t-muted { color: var(--text-muted); }
.t-faint { color: var(--text-faint); }
.t-primary { color: var(--primary); }

/* ============================================================
   App-Shell layout (best-practice ۲۰۲۶) — پایهٔ چیدمانِ همهٔ صفحاتِ پنل.
   هدف: در دسکتاپ صفحه اسکرول نمی‌شود؛ کلِ ویوپورت پر می‌شود و اسکرول
   فقط داخلِ .scroll-area رخ می‌دهد (مثلِ «درخواست‌های اخیر» در داشبورد).
   نکاتِ کلیدی (تأییدشده از منابع + تجربهٔ فایرفاکسِ همین پروژه):
     • پُرکردنِ ارتفاع با flex-fill (flex:1; min-height:0)، نه height:100%
       (Firefox درصدِ ارتفاع روی فرزندِ flex-item را قابل‌اعتماد resolve نمی‌کند).
     • min-height:0 روی هر حلقهٔ فلکس تا عنصرِ اسکرول‌شونده (باگِ اسپکِ فلکس‌باکس).
     • ردیف‌های گرید با minmax(0,fr) تا track با min-contentِ محتوا باد نکند.
   همهٔ قوانین پشتِ کلاسِ opt-in (.app-shell/.fill-box/.scroll-area/.is-*) هستند،
   پس تا صفحه‌ای این کلاس‌ها را نگیرد هیچ‌چیزی تغییر نمی‌کند (بلاست‌ریدیوس صفر).
   دامنهٔ کاملِ قرارداد: docs/FRONTEND_APPSHELL_STANDARD.md
   ============================================================ */
@media (min-width: 992px) {
  /* قابِ صفحه: main-content فقط در صفحاتی که ریشه‌شان .app-shell است فلکس‌ستون می‌شود */
  .main-content:has(> .app-shell) { display: flex; flex-direction: column; }
  /* ریشهٔ صفحه: با flex کلِ قاب را پر می‌کند (نه height:100%) */
  .app-shell { flex: 1 1 auto; min-height: 0; }
  .app-shell.is-grid  { display: grid; gap: clamp(8px, 1.4vh, 16px); }                          /* چند-باکسی مثلِ داشبورد */
  .app-shell.is-stack { display: flex; flex-direction: column; gap: clamp(8px, 1.4vh, 16px); }  /* تک-باکسِ بزرگ مثلِ آرشیو */
  /* ردیف‌های بوت‌استرپِ مستقیمِ app-shell: گاترِ عمودی صفر + قابلیتِ جمع‌شدن */
  .app-shell > .row { --bs-gutter-y: 0; margin-top: 0; margin-bottom: 0 !important; min-height: 0; }
  .app-shell > .row > [class*="col-"] { min-height: 0; }
  /* ستونِ حاملِ باکسِ پرشونده → فلکس‌ستون تا باکس با flex پر کند */
  .app-shell [class*="col-"]:has(> .fill-box) { display: flex; flex-direction: column; }
  /* باکسی که فضای خود را پر می‌کند و می‌کلیپد؛ محتوایش داخلِ خودش اسکرول می‌شود */
  .fill-box { flex: 1 1 0; min-height: 0; overflow: hidden; display: flex; flex-direction: column; }
  /* تنها ناحیهٔ مجازِ اسکرولِ داخلی در دسکتاپ */
  .scroll-area { flex: 1 1 auto; min-height: 0; overflow: auto; }
}
/* موبایل/تبلت (<992px): قاب رها، اسکرولِ طبیعیِ صفحه؛ ارتفاعِ کارت‌ها per-page */
@media (max-width: 991.98px) {
  .app-shell.is-grid, .app-shell.is-stack { display: block; }
  .fill-box, .scroll-area { overflow: visible; }
}

/* ============================================================
   دکمه‌های استانداردِ app-shell — منبعِ یگانه
   ------------------------------------------------------------
   تا ۲۰۲۶-۰۸-۲۰ این سه قاعده در هفت قالبِ client بایت‌به‌بایت کپی شده بودند
   (btn-back در ۷ فایل، btn-submit و foot-hint در ۲). نتیجهٔ عملیِ آن تکرار:
   reports.html قاعدهٔ .btn-back:focus-visible را **نداشت**، پس دکمهٔ بازگشتِ
   آن صفحه حلقهٔ فوکوسِ درست نمی‌گرفت و هیچ‌کس متوجه نشد. اینجا یک‌بار تعریف
   می‌شوند و صفحات فقط مصرفشان می‌کنند.

   نامِ کلاس‌ها همان‌هایی است که قالب‌ها از قبل داشتند ⇒ هیچ HTMLی تغییر نکرد.
   اگر صفحه‌ای نیاز به تفاوت داشت، در <style> خودش override کند (specificity
   برابر است و base.css زودتر لود می‌شود، پس قاعدهٔ صفحه برنده است).
   ⚠️ فقط پنلِ client — پنلِ expert طبقِ ADR-018 دارایی مستقلِ خودش را دارد.
   ============================================================ */

/* دکمهٔ «بازگشت» در نوارِ عنوانِ صفحات */
.btn-back {
  display: inline-flex; align-items: center; white-space: nowrap;
  padding: 0.5rem 1.4rem; border-radius: var(--r-md);
  background: var(--primary-2); border: 1px solid transparent;
  color: #fff; font-size: 0.83rem; font-weight: 700;
  text-decoration: none; cursor: pointer; transition: var(--t-fast);
}
.btn-back:hover { background: var(--primary); color: #fff; }
.btn-back:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--primary-ring); }

/* دکمهٔ اصلیِ ثبت/ذخیره در فوترِ پین‌شدهٔ فرم‌ها */
.btn-submit {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 46px; padding: 0.6rem 2.4rem; border: none; border-radius: var(--r-md);
  background: var(--primary); color: #fff;
  font-size: 0.95rem; font-weight: 800; font-family: inherit;
  box-shadow: var(--shadow-sm); cursor: pointer; transition: var(--t-fast);
}
.btn-submit:hover:not(:disabled) { background: var(--primary-ink); color: #fff; }
.btn-submit:disabled { opacity: 0.5; cursor: not-allowed; }
.btn-submit:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--primary-ring); }

/* متنِ راهنمای کنارِ دکمهٔ ثبت */
.foot-hint { color: var(--text-muted); font-size: 0.76rem; font-weight: 600; }
@media (max-width: 767.98px) {
  .foot-hint { text-align: center; }
}

/* ============ سوییچ اعلان در منوی حساب کاربری + پیام درخواست مجوز ============
   جایگزین دکمه زنگ هدر (حذف ۲۰۲۶-۰۹-۰۹). آن دکمه سه کار می‌کرد و هر سه باید
   اینجا زنده بمانند، وگرنه شکست‌ها دوباره بی‌صدا می‌شوند:
     ۱) گرفتن مجوز نوتیف — مرورگر فقط پس از ژست کاربر اجازه می‌دهد.
     ۲) قطع/وصل صدا.
     ۳) **دیده‌شدن شکست** — اگر مرورگر پخش صدا را مسدود کند یا کاربر مجوز را
        رد کرده باشد، متن زیر سوییچ همان را می‌گوید.
   ============================================================================ */
/* سوییچ کوچک — هم‌ابعاد یک آیتم منو.
   ⚠️ رنگ حالت روشن عمدا `currentColor` است، نه رنگ برند: خواسته کاربر
   (۲۰۲۶-۰۹-۰۹) این بود که سوییچ هم‌رنگِ خودِ نوشته «اعلان و صدا» باشد. با
   `currentColor` این هم‌رنگی در تم روشن و تاریک و در hover **خودکار** درست
   می‌ماند؛ نوشتن رنگ ثابت یعنی یک جا از قلم می‌افتد. */
.hdr-sw {
    margin-inline-start: auto; flex-shrink: 0; align-self: center;
    width: 34px; height: 19px; border-radius: 50rem;
    background: var(--surface-2); border: 1px solid var(--border);
    position: relative; transition: var(--t-fast);
}
.hdr-sw::after {
    content: ''; position: absolute; top: 2px; inset-inline-start: 2px;
    width: 13px; height: 13px; border-radius: 50%;
    background: var(--text-faint); transition: var(--t-fast);
}
.hdr-menu-notif[aria-checked="true"] .hdr-sw {
    background: color-mix(in srgb, currentColor 14%, transparent);
    border-color: color-mix(in srgb, currentColor 45%, transparent);
}
.hdr-menu-notif[aria-checked="true"] .hdr-sw::after {
    background: currentColor; transform: translateX(calc(-1 * (34px - 19px)));
}
/* RTL: خانه دکمه سمت راست است، پس روشن یعنی حرکت به چپ. جهت با translate
   منفی می‌آید چون `inset-inline-start` خودش قبلا آینه شده. */

@media (prefers-reduced-motion: reduce) {
    .hdr-sw, .hdr-sw::after { transition: none; }
}

/* ---- پیام درخواست مجوز (pre-prompt) ----
   ⚠️ چرا اصلا وجود دارد: فایرفاکس از نسخه ۷۲ و کروم هم عملا، درخواست مجوز
   بدون ژست کاربر را رد می‌کنند. پس prompt واقعی مرورگر بدون یک کلیک ممکن
   نیست. و اگر مستقیم prompt مرورگر را باز کنیم و کاربر رد کند، دیگر هیچ راهی
   از داخل سایت برای برگشت نیست. این کارت آن کلیک را می‌گیرد. */
.notif-ask {
    position: fixed; inset: 0; z-index: 1090;
    display: grid; place-items: center; padding: 16px;
}
.notif-ask[hidden] { display: none; }
/* ⚠️ `blur` روی همین لایه است، نه روی صفحه: فیلتر روی خودِ صفحه یک
   containing-block نو می‌سازد و هر `position: fixed`ی داخلش (هدر چسبان،
   سایدبار) را جابه‌جا می‌کند. اینجا فقط پشتِ همین پرده تار می‌شود. */
.notif-ask-backdrop {
    position: absolute; inset: 0; background: rgba(15, 23, 42, .58);
    backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
    opacity: 0; transition: opacity .18s ease;
}
.notif-ask.is-open .notif-ask-backdrop { opacity: 1; }
.notif-ask-card {
    position: relative; width: min(400px, 100%);
    background: var(--surface); border: 1px solid var(--border);
    border-radius: var(--r-xl); box-shadow: var(--shadow-lg);
    padding: var(--sp-5); text-align: center;
    max-height: calc(100dvh - 32px); overflow-y: auto;
    opacity: 0; transform: translateY(10px) scale(.97);
    transition: opacity .22s ease, transform .22s cubic-bezier(.2, .8, .3, 1);
}
.notif-ask.is-open .notif-ask-card { opacity: 1; transform: none; }
.notif-ask-ico {
    width: 48px; height: 48px; border-radius: 50%; margin: 0 auto var(--sp-3);
    display: grid; place-items: center; font-size: 1.35rem;
    background: var(--primary-soft); color: var(--primary-ink);
}
.notif-ask-title { font-size: .95rem; font-weight: 800; color: var(--ink); margin-bottom: 6px; }
.notif-ask-actions { display: flex; gap: var(--sp-2); }
/* متن‌های داخل کارت — سه حالت، هر کدام بدنه خودش */
.notif-ask-body { text-align: start; }
.notif-ask-body p { margin: 0 0 var(--sp-3); }
.notif-ask-body p:last-child { margin-bottom: 0; }
.notif-ask-text { font-size: .8rem; color: var(--text); line-height: 1.9; }
.notif-ask-text b { color: var(--ink); font-weight: 800; }

.notif-ask-steps {
    font-size: .78rem; color: var(--ink); line-height: 2;
    background: var(--surface-2); border: 1px solid var(--border);
    border-radius: var(--r-md); padding: var(--sp-3);
}

.notif-ask-note {
    display: flex; gap: 8px; align-items: flex-start;
    font-size: .72rem; color: var(--text-muted); line-height: 1.9;
}
.notif-ask-note i { flex-shrink: 0; margin-top: 4px; font-size: .85rem; }

.notif-ask-rec {
    display: flex; gap: 8px; align-items: center;
    font-size: .8rem; font-weight: 800; color: var(--success);
}
.notif-ask-rec i { flex-shrink: 0; }

.notif-ask-actions { margin-top: var(--sp-4); }
.notif-ask-yes[hidden] { display: none; }

.notif-ask-actions button {
    flex: 1 1 0; padding: 9px 12px; border-radius: var(--r-md);
    font-family: inherit; font-size: .82rem; font-weight: 700;
    cursor: pointer; transition: var(--t-fast);
}
.notif-ask-yes { background: var(--primary); color: #fff; border: 1px solid var(--primary); }
.notif-ask-yes:hover { filter: brightness(1.08); }
.notif-ask-no { background: transparent; color: var(--text-muted); border: 1px solid var(--border); }
.notif-ask-no:hover { background: var(--surface-2); color: var(--ink); }
.notif-ask-yes:focus-visible, .notif-ask-no:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--primary-ring); }

@media (prefers-reduced-motion: reduce) {
    .notif-ask-backdrop, .notif-ask-card { transition: none; transform: none; }
}

/* ---- اندازه متن و کاهش حرکت (پاپ‌آپ تنظیمات، ۲۰۲۶-۰۹-۲۷) ----
   صفت‌ها را اسکریپت pre-paint در `client/base.html` پیش از اولین رنگ‌آمیزی
   می‌گذارد. اندازه‌های پروژه `rem`اند، پس تغییر ریشه کل رابط را هم‌اندازه
   بزرگ و کوچک می‌کند. «معمولی» صفتی ندارد تا پیش‌فرض دست نخورد. */
html[data-textsize="sm"] { font-size: 93.75%; }
html[data-textsize="lg"] { font-size: 112.5%; }

/* هم‌وزن `prefers-reduced-motion` سیستم، ولی به انتخاب خود کاربر. */
html[data-motion="reduce"] *,
html[data-motion="reduce"] *::before,
html[data-motion="reduce"] *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
}
