/* ==========================================================================
   sabz-pwa-kit — safe-area.css
   ابزارهای ساختاری برای ناحیه‌ی امن، ارتفاع viewport، اسکرول و هدف لمسی.

   این فایل عمداً «بدون هویت بصری» است: هیچ رنگ، فونت، سایه، border یا
   radius تعریف نمی‌کند. چهار اپ با طراحی‌های متفاوت باید بتوانند همین یک
   فایل را لینک کنند بدون این‌که ظاهرشان تکان بخورد.

   ── الزام قبل از هر چیز ─────────────────────────────────────────────────
   بدون این تگ، تمام مقادیر env(safe-area-inset-*) صفر گزارش می‌شوند و کل
   این فایل بی‌اثر است. viewport-fit=cover اختیاری نیست:

     <meta name="viewport"
           content="width=device-width, initial-scale=1, viewport-fit=cover, interactive-widget=resizes-content">

   و برای این‌که رنگ هدر خودِ اپ ناحیه‌ی status bar را پر کند (ظاهر نیتیو):

     <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">

   ── نحوه‌ی لینک کردن ────────────────────────────────────────────────────
   هر چهار اپ زیر یک prefix سرو می‌شوند (/padmira/ ، /pasmand/iranemp/ ،
   /rahbari/ ، /shahrdari1/)، پس مسیر مطلقی که با «/» شروع شود در سه‌تای
   آنها می‌شکند. ولی «نسبی بودن» به‌تنهایی کافی نیست:

     <link rel="stylesheet" href="pwa-kit/safe-area.css">

   این مسیر نسبت به URL خودِ سند حل می‌شود، نه نسبت به ریشه‌ی اپ. طبق
   بند ۱۰٫۵ استاندارد، مسیر SPA مثل /padmira/reports/42 هم همان
   index.html را می‌گیرد؛ آنجا این href به
   /padmira/reports/pwa-kit/safe-area.css حل می‌شود و 404 می‌گیرد —
   یعنی صفحه بدون هیچ خطای آشکاری بی‌استایل بالا می‌آید. دو راه امن:

     • اپ Vite: `import './pwa-kit/safe-area.css'` (باندلر مسیر را با
       BASE_URL بازنویسی می‌کند) — یا href را با %BASE_URL% بساز.
     • اپ بدون build: یک بار در <head> پایه را تثبیت کن و بعد نسبی بنویس:
         <base href="/padmira/">
         <link rel="stylesheet" href="pwa-kit/safe-area.css">
       (توجه: <base> روی همه‌ی لینک‌ها و fetchهای نسبی سند اثر می‌گذارد،
       پس یک بار و آگاهانه گذاشته شود.)

   ── نکته‌ی مشترک همه‌ی قواعد ──────────────────────────────────────────────
   همیشه env(name, 0px) با fallback نوشته شده. constant() مربوط به iOS 11
   است و مرده — استفاده نشده. هیچ عدد ثابتی (20/44/47/59px) برای inset
   نوشته نشده؛ دستگاه‌های Dynamic Island و SE مقادیر کاملاً متفاوتی
   می‌دهند و مقادیر با چرخش گوشی زنده عوض می‌شوند.
   ========================================================================== */


/* --------------------------------------------------------------------------
   ۱. اکسپوز کردن insetها به‌عنوان custom property
   تا اپ‌ها بتوانند در قواعد خودشان از calc()/max() رویشان استفاده کنند
   بدون تکرار env() در هر فایل.

   left/right عمداً «فیزیکی» نام‌گذاری شده‌اند و به inline-start/end نگاشت
   نشده‌اند: ناچ یک واقعیت سخت‌افزاری است و با جهت متن نمی‌چرخد. در یک اپ
   RTL هم بریدگی همان‌جایی است که هست. این تنها استثنای آگاهانه‌ی این فایل
   از قاعده‌ی «فقط ویژگی‌های منطقی» است.
   -------------------------------------------------------------------------- */
:root {
  --sabz-safe-top: env(safe-area-inset-top, 0px);
  --sabz-safe-bottom: env(safe-area-inset-bottom, 0px);
  --sabz-safe-left: env(safe-area-inset-left, 0px);
  --sabz-safe-right: env(safe-area-inset-right, 0px);

  /* قابل بازنویسی توسط هر اپ. اینها «اندازه» هستند نه «ظاهر» — هیچ رنگی
     همراهشان نیست. */
  --sabz-header-height: 56px;
  --sabz-bottom-bar-height: 56px;

  /* کف فاصله‌ی نوار پایین از لبه، برای گوشی‌هایی که home indicator ندارند
     و inset پایینشان صفر است. */
  --sabz-bottom-gap: 12px;

  /* stacking نوارهای چسبان. هر اپ می‌تواند با لایه‌بندی خودش هماهنگش کند. */
  --sabz-bar-z: 10;

  /* حداقل هدف لمسی (بخش ۷). اینجا صریح اعلان می‌شود تا مثل بقیه‌ی متغیرها
     قابل کشف و بازنویسی باشد؛ مقدارش عمداً همان fallbackی است که در
     .sabz-tap نوشته شده، پس رفتار فعلی هیچ اپی عوض نمی‌شود. اپی که
     ۴۸×۴۸ توصیه‌شده را می‌خواهد فقط همین یک خط را override می‌کند. */
  --sabz-tap-size: 44px;
}


/* --------------------------------------------------------------------------
   ۲. هدر زیر status bar / Dynamic Island

   منطق درست این است: پس‌زمینه‌ی هدر تا زیر status bar کشیده می‌شود، ولی
   محتوای هدر از زیرِ ناچ شروع می‌شود. یعنی inset به‌صورت padding اضافه
   می‌شود، نه margin و نه offset.

   .sabz-header فقط فاصله و ارتفاع می‌دهد و position را دست نمی‌زند تا
   اپ‌هایی که هدرشان already در جریان عادی صفحه است چیزی نشکند؛ برای
   چسباندن، یکی از دو modifier زیر را اضافه کن.
   -------------------------------------------------------------------------- */
.sabz-header {
  /* border-box لازم است وگرنه min-block-size به content box اعمال می‌شود و
     ارتفاع نهایی به‌اندازه‌ی inset بیشتر از چیزی می‌شود که calc حساب کرده. */
  box-sizing: border-box;
  padding-block-start: var(--sabz-safe-top);
  min-block-size: calc(var(--sabz-header-height) + var(--sabz-safe-top));
}

.sabz-header--fixed {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: var(--sabz-bar-z);
}

.sabz-header--sticky {
  position: sticky;
  inset-block-start: 0;
  z-index: var(--sabz-bar-z);
}

/* محتوای اصلی باید به‌اندازه‌ی کل هدرِ fixed (ارتفاع + ناچ) کنار بکشد،
   وگرنه سطر اول زیر هدر گم می‌شود. روی هدر sticky لازم نیست. */
.sabz-header-offset {
  padding-block-start: calc(var(--sabz-header-height) + var(--sabz-safe-top));
}


/* --------------------------------------------------------------------------
   ۳. نوار پایین / تب‌بار / FAB و home indicator

   max() ضروری است: روی گوشی‌های دارای دکمه‌ی فیزیکی inset پایین صفر است و
   بدون کف، دکمه‌ها می‌چسبند به لبه‌ی شیشه.

   همان max() باید در min-block-size هم بیاید، نه فقط در padding. اگر
   ارتفاع با calc(height + safe) حساب شود ولی padding با max(safe, gap)،
   هرجا safe < gap باشد اختلاف این دو از ارتفاع «مفیدِ» نوار کم می‌شود:
   با safe=0 و gap=12px نوار ۵۶px می‌ماند ولی فقط ۴۴px جا برای محتوا
   دارد، و اگر اپ --sabz-bottom-gap را ۱۶px کند به ۴۰px می‌رسد — زیر
   حداقل ۴۴px هدف لمسی. با نوشتن همان max() در هر دو، ارتفاع مفید همیشه
   دقیقاً --sabz-bottom-bar-height می‌ماند؛ همان رفتاری که .sabz-header
   از قبل دارد.
   -------------------------------------------------------------------------- */
.sabz-bottom-bar {
  box-sizing: border-box;
  padding-block-end: max(var(--sabz-safe-bottom), var(--sabz-bottom-gap));
  min-block-size: calc(var(--sabz-bottom-bar-height)
                       + max(var(--sabz-safe-bottom), var(--sabz-bottom-gap)));
}

.sabz-bottom-bar--fixed {
  position: fixed;
  inset-block-end: 0;
  inset-inline: 0;
  z-index: var(--sabz-bar-z);
}

/* باید دقیقاً برابر ارتفاع نوارِ بالا باشد، پس همان max() اینجا هم تکرار
   می‌شود؛ وگرنه آخرین سطر محتوا زیر نوار fixed گم می‌شود. */
.sabz-bottom-bar-offset {
  padding-block-end: calc(var(--sabz-bottom-bar-height)
                          + max(var(--sabz-safe-bottom), var(--sabz-bottom-gap)));
}


/* --------------------------------------------------------------------------
   ۴. landscape — ناچ می‌رود به کنار

   padding-left/right اینجا عمداً فیزیکی است (به دلیلی که بالا در بخش ۱
   توضیح داده شد). اگر به‌جایش padding-inline می‌نوشتیم، در سند RTL دو مقدار
   جابه‌جا اعمال می‌شدند و دقیقاً همان سمتی که ناچ دارد بدون فاصله می‌ماند.

   معمولاً روی body یا ظرف اصلی گذاشته می‌شود.
   -------------------------------------------------------------------------- */
.sabz-safe-inline {
  padding-left: var(--sabz-safe-left);
  padding-right: var(--sabz-safe-right);
}

/* برای نوارهای تمام‌عرضِ fixed که خودشان padding افقی دارند و نباید محتوای
   داخلی‌شان زیر ناچ برود.

   ⚠ این کلاس را روی خودِ نوار fixed نگذار: margin کل جعبه — از جمله
   پس‌زمینه‌اش — را تو می‌کشد، و در landscape یک نوار باریک از صفحه‌ی پشت
   کنار ناچ دیده می‌شود. روی یک wrapper داخل نوار بگذارش تا پس‌زمینه
   لبه‌به‌لبه بماند و فقط محتوا کنار بکشد. */
.sabz-safe-inline-margin {
  margin-left: var(--sabz-safe-left);
  margin-right: var(--sabz-safe-right);
}


/* --------------------------------------------------------------------------
   ۵. ارتفاع viewport

   دو اعلان پشت‌سرهم با یک نام، «اشتباه» و «تکراری» نیست — مکانیزم fallback
   است: مرورگری که واحد dvh/svh را نمی‌شناسد کل اعلان دوم را به‌عنوان
   value نامعتبر دور می‌ریزد و همان 100vh اول برایش می‌ماند؛ مرورگری که
   می‌شناسد، اعلان دوم اولی را override می‌کند. اگر کسی خط اول را به‌عنوان
   کد مرده حذف کند، روی مرورگرهای قدیمی چیدمان تمام‌ارتفاع کاملاً از بین
   می‌رود. هیچ‌کدام را حذف نکنید.

   اینجا از height فیزیکی استفاده شده نه block-size: واحدهای viewport
   خودشان فیزیکی‌اند، نمونه‌کد نرماتیو استاندارد هم همین را می‌نویسد، و
   پشتیبانی height از block-size وسیع‌تر است (مرورگر خیلی قدیمی که
   block-size را نمی‌فهمد، با این نسخه دست‌کم fallback را می‌گیرد).

   dvh در برابر svh: dvh با ظاهر و ناپدید شدن نوار ابزار مرورگر تغییر
   می‌کند — برای پوسته، مودال و overlay ثابت درست است. svh پایدار است و
   برای بخش‌های داخل صفحه‌ی اسکرول‌شونده (hero) درست است، چون dvh آنجا باعث
   بازمحاسبه‌ی مداوم چیدمان حین اسکرول می‌شود.
   -------------------------------------------------------------------------- */
.sabz-vh-full {
  height: 100vh;   /* fallback — حذف نکن */
  height: 100dvh;
}

.sabz-min-svh {
  min-height: 100vh;   /* fallback — حذف نکن */
  min-height: 100svh;
}

/* پوسته‌ی اپ: ارتفاع ثابت viewport، و اسکرول به عهده‌ی یک اسکرولر داخلی.
   زنجیره‌ی height:100% از html/body عمداً استفاده نشده — روی iOS با نوار
   ابزار متغیر می‌شکند. */
.sabz-app-shell {
  height: 100vh;   /* fallback — حذف نکن */
  height: 100dvh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}


/* --------------------------------------------------------------------------
   ۶. بهداشت اسکرول

   ⚠ این کلاس‌ها را هرگز روی html یا body نگذارید. مهار overscroll روی ریشه
   ژست swipe-to-navigate مرورگر را می‌بلعد و کاربر اندروید/iOS دیگر نمی‌تواند
   با کشیدن از لبه برگردد.

   ⚠ به همین دلیل اینجا overscroll-behavior-y نوشته شده و نه
   overscroll-behavior سراسری: مهار محور x — حتی روی یک پنل عمودی که اصلاً
   اسکرول افقی ندارد — chaining افقی را قطع می‌کند و همان ژست بازگشت را
   می‌دزدد. محور x را فقط روی اسکرولرهایی دست می‌زنیم که واقعاً افقی‌اند
   (کلاس بعدی) و آنها هم باید از لبه‌ی صفحه فاصله داشته باشند.

   ⚠ و touch-action: none روی هیچ ظرف تمام‌صفحه‌ای ننویسید. باندی حدود ۲۴px
   از هر دو لبه‌ی افقی باید از عنصر گیرنده‌ی لمس آزاد بماند وگرنه ژست بازگشت
   سیستم‌عامل از کار می‌افتد. touch-action فقط روی عنصر کوچکِ واقعاً
   درگ‌شونده (بوم، نقشه، دستگیره) مجاز است.
   -------------------------------------------------------------------------- */
.sabz-scroll-panel {
  overflow-y: auto;
  overscroll-behavior-y: contain;
}

/* جدول پهن یا کاروسل. inline به‌درستی با جهت سند flip می‌شود. */
.sabz-scroll-x {
  overflow-x: auto;
  overscroll-behavior-inline: contain;
}


/* --------------------------------------------------------------------------
   ۷. هدف لمسی

   حداقل ۴۴×۴۴ (ترجیح ۴۸×۴۸ که با --sabz-tap-size قابل تنظیم است).

   align-items/justify-content عمداً بدون display نوشته شده‌اند: روی عنصری
   که flex یا grid نیست کاملاً بی‌اثرند، پس چیدمان فعلی اپ را تغییر
   نمی‌دهند؛ ولی اگر دکمه از قبل flex باشد، متن در مرکز ناحیه‌ی ۴۴px
   می‌نشیند به‌جای این‌که بالا بچسبد.

   نکته: روی یک عنصر inline (مثل <a> ساده) min-block-size اثر ندارد. برای
   لینک متنی، display: inline-flex را در CSS خود اپ اضافه کنید.
   -------------------------------------------------------------------------- */
.sabz-tap {
  box-sizing: border-box;
  min-inline-size: var(--sabz-tap-size, 44px);
  min-block-size: var(--sabz-tap-size, 44px);
  align-items: center;
  justify-content: center;
}

/* --------------------------------------------------------------------------
   ۸. صفت hidden

   مرورگر خودش [hidden]{display:none} دارد، ولی آن قاعده از استایل‌شیتِ خودِ
   مرورگر (UA) می‌آید و در آبشار CSS **هر** قاعده‌ی نویسنده بر آن مقدم است —
   حتی با ویژگی (specificity) کمتر. یعنی یک display ساده در CSS اپ، صفت
   hidden را بی‌اثر می‌کند: عنصر از دید کد پنهان است ولی روی صفحه می‌ماند.

   این تله در این ناوگان بارها زده و هر بار تک‌موردی وصله شده است —
   .sa-fab[hidden] در بسته‌ی دستیار، #update-banner[hidden] در EIMS،
   .pct-sign[hidden] و .ai-panel[hidden] در SPA/IEMP. آخری در ۲۱ شهریور ۱۴۰۵
   دکمه‌ی بستنِ پنل دستیار را از کار انداخته بود: .ai-panel مقدار display:flex
   داشت، پس panel.hidden = true اجرا می‌شد و پنل باز می‌ماند.

   این قاعده در قرارداد «بدون هویت بصری» این فایل می‌گنجد: نه رنگ است، نه
   فونت، نه سایه — فقط تضمین می‌کند صفتی که خودِ HTML تعریفش کرده همان کاری
   را بکند که قرار است.

   !important لازم است، چون کل مسئله همین است که قاعده‌ی اپ بر UA مقدم
   می‌شود؛ بدون آن این خط هم به همان سرنوشت دچار می‌شد. بی‌خطر است مادام که
   عنصری «hidden ولی دیده‌شدنی» نداشته باشیم — که ترکیب بی‌معنایی است. اپی که
   می‌خواهد عنصری را با CSS پنهان و آشکار کند باید کلاس خودش را بسازد و اصلاً
   سراغ صفت hidden نرود.

   ⚠ عمداً به شکل [hidden]:where(:not([hidden="until-found"])) نوشته نشده
   (کاری که Tailwind می‌کند تا قابلیت hidden="until-found" را حفظ کند). هیچ
   اپی اینجا از until-found استفاده نمی‌کند، و :where() روی WebView قدیمی
   پشتیبانی نمی‌شود — آنجا کل انتخابگر نامعتبر و دور انداخته می‌شود و این
   اصلاح بی‌صدا ناپدید می‌شد، دقیقاً روی دستگاه‌هایی که کسی تستشان نمی‌کند.
   -------------------------------------------------------------------------- */
[hidden] {
  display: none !important;
}
