/* ============================================================
   دیبا — سیستمِ طراحی
   ============================================================
   یک فایل، بدون build، بدون CDN، بدون npm.

   ⚠ چرا Tailwind نیست: Tailwindِ درست یعنی زنجیره‌ی npm + مرحله‌ی
   build. روی ویندوز و با یک نگهدارنده و مهلتِ ۱ ژانویه، هر مرحله‌ی
   build یک جای دیگر برای شکستن است. نسخه‌ی CDN هم برای production
   نیست و هر بار بارگذاری اینترنت می‌خواهد.

   قواعدِ ثابت:
   * راست‌به‌چپ **بومی** — همه‌جا خصوصیت‌های منطقی
     (margin-inline · padding-inline · border-inline · inset-inline).
     هیچ left/right سخت‌کدشده‌ای نیست، پس نسخه‌ی آلمانی/انگلیسی
     خودبه‌خود درست می‌شود.
   * ارقام در ستون‌های عددی tabular-nums.
   * ساعت و مبلغ و تاریخ داخلِ .num — با direction: ltr و isolate.
   ============================================================ */

/* ═══════════════════════════════════════════ فونت — از خودِ دیبا

   ⚠⚠ **فونت دیگر از CDNِ گوگل نمی‌آید.** سه دلیل، و هر سه واقعی:

   ۱. سرورِ اتاق به «fonts.googleapis.com» نمی‌رسد. در همان
      لحطه‌ای که دیبا روی VPS بالا بیاید، همه‌ی صفحه‌ها با
      فونتِ پیش‌فرضِ سیستم رندر می‌شدند.
   ۲. «@import» **مسدودکننده** است: مرورگر تا نگیردش، هیچ چیزِ
      دیگری از این فایل را اعمال نمی‌کند.
   ۳. همین فایل‌ها را لایه‌ی PDF («core/exporting.py») هم لازم دارد.
      یک نسخه، دو مصرف‌کننده — تا فونتِ صفحه و فونتِ PDF دو چیز
      نباشند.

   ⚠ «font-display: swap» عمدی است: متن اول با فونتِ سیستم دیده
     می‌شود و بعد جا می‌افتد. «block» یعنی نیم‌ثانیه صفحه‌ی خالی.
   ────────────────────────────────────────────────────────── */
@font-face {
  font-family: 'Vazirmatn'; font-style: normal; font-weight: 200;
  font-display: swap; src: url("fonts/Vazirmatn-ExtraLight.3d8ceebeffb4.woff2") format('woff2');
}
@font-face {
  font-family: 'Vazirmatn'; font-style: normal; font-weight: 300;
  font-display: swap; src: url("fonts/Vazirmatn-Light.6458eed8cf92.woff2") format('woff2');
}
@font-face {
  font-family: 'Vazirmatn'; font-style: normal; font-weight: 400;
  font-display: swap; src: url("fonts/Vazirmatn-Regular.cdc140628f11.woff2") format('woff2');
}
@font-face {
  font-family: 'Vazirmatn'; font-style: normal; font-weight: 500;
  font-display: swap; src: url("fonts/Vazirmatn-Medium.dd2193b32cbe.woff2") format('woff2');
}
@font-face {
  font-family: 'Vazirmatn'; font-style: normal; font-weight: 600;
  font-display: swap; src: url("fonts/Vazirmatn-SemiBold.9260c1927872.woff2") format('woff2');
}
@font-face {
  font-family: 'Vazirmatn'; font-style: normal; font-weight: 700;
  font-display: swap; src: url("fonts/Vazirmatn-Bold.7958481fe611.woff2") format('woff2');
}

/* ═══════════════════════════════════════════ توکن‌ها — دو حالته

   ⚠ سه بلوک، نه دو:

   ۱. :root                       → پایه (تیره؛ طرحِ دیبا تیره‌محور است)
   ۲. @media prefers-color-scheme: light  +  :not([data-theme='dark'])
                                  → سلیقه‌ی سیستم‌عاملِ کاربر
   ۳. :root[data-theme='light']   → کلیدِ خودِ دیبا، که بر سیستم‌عامل
                                    **مقدم** است

   اگر بلوکِ سوم نباشد، کلیدِ «روشن» روی ویندوزی که تیره است کار
   نمی‌کند. مقدارهای هِکس فقط یک جا (--d-* و --l-*) نوشته شده‌اند تا
   دو نسخه از هم جدا نیفتند.
   ─────────────────────────────────────────────────────────── */
:root {
  /* --- پالتِ تیره */
  --d-bg: #0a0d12;      --d-surface: #10141b;   --d-surface-2: #161d26;
  --d-line: #1c232e;    --d-line-2: #2a3340;    --d-idle: #1a212b;
  --d-fg: #e8ecf2;      --d-fg-muted: #8d97a5;  --d-fg-faint: #5c6675;
  --d-accent: #46b18e;  --d-info: #5aa8e0;      --d-warn: #d8a13c;
  --d-bad: #d98479;     --d-on-accent: #06110c;
  --d-ok-bg: #12211c;   --d-ok-line: #22463a;
  --d-warn-bg: #241f11; --d-warn-line: #453a1c;
  --d-bad-bg: #241715;  --d-bad-line: #42241f;
  --d-info-bg: #101e29; --d-info-line: #1e3a4d;
  --d-cell-part: #2c5a4d;
  --d-shadow: 0 10px 30px rgba(0, 0, 0, .45);
  --d-z-1: 0 1px 2px rgba(0, 0, 0, .5);
  --d-z-2: 0 4px 14px rgba(0, 0, 0, .45);

  /* --- پالتِ روشن. تأکیدها تیره‌ترند تا کنتراستِ متن روی سفید از
         ۴٫۵:۱ کم‌تر نشود — عینِ همان رنگِ حالتِ تیره روی سفید
         خوانا نیست. */
  --l-bg: #f4f5f7;      --l-surface: #ffffff;   --l-surface-2: #fafbfc;
  --l-line: #e3e6ea;    --l-line-2: #cfd5dd;    --l-idle: #e8ebef;
  --l-fg: #171c23;      --l-fg-muted: #5b6472;  --l-fg-faint: #8a929e;
  --l-accent: #147a5e;  --l-info: #1f6fa8;      --l-warn: #8a6114;
  --l-bad: #a8443a;     --l-on-accent: #ffffff;
  --l-ok-bg: #e8f4ef;   --l-ok-line: #b8ddcd;
  --l-warn-bg: #fbf2e0; --l-warn-line: #ecd9ac;
  --l-bad-bg: #fdeceb;  --l-bad-line: #f2c4bf;
  --l-info-bg: #eaf3f9; --l-info-line: #bcd9ec;
  --l-cell-part: #8fc9b6;
  --l-shadow: 0 4px 14px rgba(23, 28, 35, .12);
  --l-z-1: 0 1px 2px rgba(23, 28, 35, .06);
  --l-z-2: 0 6px 18px rgba(23, 28, 35, .10);

  /* --- شکل و فاصله — مشترک در هر دو حالت */
  --r-sm: 6px;  --r: 8px;  --r-lg: 10px;  --r-full: 999px;
  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px;
  --sp-4: 16px; --sp-5: 22px; --sp-6: 30px;

  /* --- مقیاسِ اندازه‌ی قلم — نسبتِ ثابتِ ۱٫۲
     ⚠ چرا مقیاس و نه عددِ دلبخواه: تا این جلسه اندازه‌ها ۸٫۵ و
       ۱۰٫۵ و ۱۱ و ۱۲٫۵ و ۲۷ بودند — هیچ نسبتی بینشان نبود. چشم
       نظم را می‌بیند حتی وقتی نمی‌تواند نامش را بگوید؛ بی‌نظمی را
       هم می‌بیند و اسمش را می‌گذارد «یک‌جای کار می‌لنگد». */
  /* ⚠⚠ دو ضریبِ کاربر، از «شخصی‌سازیِ ظاهر».

     خواستهٔ پرسا: «می‌خوام اعداد و چیزهایی که نوشتی واضح‌تر باشن؛
     توی شخصی‌سازیِ ظاهری قابلیتِ درشت‌کردنِ این اعداد رو هم داشته
     باش.»

     ⚠ چرا ضریب و نه یک مجموعهٔ دومِ اندازه‌ها: مقیاس نسبتِ ثابتِ
       ۱٫۲ دارد و همین نسبت است که صفحه را مرتب نشان می‌دهد.
       نوشتنِ ده عددِ تازه برای «درشت»، همان بی‌نظمی‌ای را برمی‌گرداند
       که مقیاس برای نبودنش ساخته شد. ضرب، نسبت‌ها را دست‌نخورده
       نگه می‌دارد.

     ⚠ و **دو** ضریب و نه یکی: متن و عدد دو خواستهٔ جدا بودند.
       کسی که جدولِ مالی می‌خواند، عددِ درشت لازم دارد ولی متنِ
       درشت ستون‌هایش را می‌شکند. */
  --tz: 1;    /* کلِ متن */
  --nz: 1;    /* فقط عددها — روی --tz سوار می‌شود */

  /* ⚠ `4xs` و `ml` در جلسه ۲۷ اضافه شدند — نه برای زیبایی، بلکه
     چون سی‌ودو اندازهٔ **ثابتِ پیکسلی** در فایل مانده بود که به
     مقیاس وصل نبودند. همان‌ها بودند که باعث شدند کلیدِ «درشت‌تر»
     روی صفحه هیچ کاری نکند: ضریب فقط روی توکن‌ها می‌نشیند، و
     متنی که عددِ خام دارد ضریب را نمی‌بیند. */
  --t-4xs: calc(9px  * var(--tz));   /* ریزترین — نشانِ ریل، برچسبِ ستون */
  --t-3xs: calc(10px * var(--tz));   /* برچسبِ ریزِ نمودار */
  --t-2xs: calc(11px * var(--tz));   /* برچسبِ کاشی، افسانه */
  --t-xs:  calc(12px * var(--tz));   /* راهنما، زیرنویس */
  --t-sm:  calc(13px * var(--tz));   /* متنِ کمکی */
  --t-md:  calc(14px * var(--tz));   /* متنِ پایه */
  --t-lg:  calc(15px * var(--tz));   /* عنوانِ کارت */
  --t-ml:  calc(16px * var(--tz));   /* نامِ برند، ورودیِ پالتِ فرمان */
  --t-xl:  calc(20px * var(--tz));   /* عددِ کاشی */
  --t-2xl: calc(21px * var(--tz));   /* عنوانِ صفحه */
  --t-3xl: calc(26px * var(--tz));   /* نمایشی — فقط صفحه‌ی ورود و خطا */
  --t-4xl: calc(31px * var(--tz));   /* نمایشی */

  /* ── اندازهٔ عدد ──────────────────────────────────────────

     دو شکل، و هر دو لازم:

     ۱. `--t-num` برای عددی که **داخلِ متن** نشسته — «۱۲ سند»،
        مبلغِ داخلِ خانهٔ جدول. `em` است، پس نسبتش با متنِ همان‌جا
        حفظ می‌شود.

     ۲. `--n-*` برای عددی که **خودش یک نمایشگر است** — عددِ درشتِ
        کاشی، شمارِ بالای صفحه، روزِ تقویم.

     ⚠⚠ چرا دومی لازم شد و اولی کافی نبود:

        `.tile__value` هم `.num` است هم اندازهٔ خودش را دارد
        (`--t-xl`). اگر `.num` را پرزورتر می‌کردیم، `1em` روی
        والدش حساب می‌شد و عددِ درشتِ کاشی **کوچک** می‌شد — یعنی
        کلیدِ «درشت‌تر»، عددها را ریزتر می‌کرد. در مرورگر دیده شد. */
  --t-num: calc(1em * var(--nz));

  --n-2xs: calc(11px * var(--tz) * var(--nz));
  --n-xs:  calc(12px * var(--tz) * var(--nz));
  --n-sm:  calc(13px * var(--tz) * var(--nz));
  --n-md:  calc(14px * var(--tz) * var(--nz));
  --n-lg:  calc(15px * var(--tz) * var(--nz));
  --n-xl:  calc(20px * var(--tz) * var(--nz));
  --n-2xl: calc(21px * var(--tz) * var(--nz));

  /* --- سایه: سه پله، نه یکی
     تخت‌بودنِ همه‌ی سطح‌ها همان چیزی است که «بی‌روح» به نظر می‌رسد. */
  --z-0: none;
  --font: 'Vazirmatn', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  --mono: ui-monospace, 'Cascadia Code', Consolas, monospace;
  --topbar-h: 58px;


  /* ═══════════ حالهٔ رنگی روی کلِ پوسته (جلسهٔ ۳۱)
     ──────────────────────────────────────────────────────────────
     ⚠⚠ ایرادی که پرسا دید و درست دید: «رنگ‌هایی که هست باید تمِ
        کلی رو عوض کنه، نه فقط نمودار رو — یه حاله‌ای باشه.»

        تا امروز رنگِ تأکید فقط `--accent` را عوض می‌کرد؛ زمینه و
        سطح‌ها (`--bg`, `--surface`, `--line`) هِکسِ **خاکستریِ
        ثابت** بودند. پس هر شش رنگ، یک برنامهٔ خاکستری می‌ساختند
        با چند نقطهٔ رنگی. کاربر رنگ را عوض می‌کرد و «چیزی عوض
        نمی‌شد».

        حالا خودِ سطح‌ها درصدِ کوچکی از رنگِ تأکید را در خود
        می‌گیرند. یک `color-mix`، نه شش پالتِ دستی.

     ⚠ درصدِ حالت روشن **کم‌تر** است و این عمدی است: در حالتِ تیره
       رنگِ تأکید روشن است و روی زمینهٔ سیاه محو می‌شود؛ در حالتِ
       روشن رنگِ تأکید تیره است و همان درصد، کارت‌های سفید را
       کدر می‌کند.

     ⚠⚠ و هر چهار ترکیب سنجیده شد، نه تخمین زده: بدترین حالت
        (متنِ کم‌رنگ روی زمینهٔ پررنگ‌ترین حاله) ۴٫۹۵:۱ درمی‌آید —
        بالای حدِ ۴٫۵:۱. اگر روزی درصدها بالا رفت، باید دوباره
        سنجیده شود.                                              */
  --tint-d: 10%;
  --tint-l: 8%;

  /* مشتق‌ها — یک بار نوشته می‌شوند و هر سه بلوکِ نگاشت از همین‌ها
     می‌خوانند، تا دو نسخه از هم جدا نیفتند. */
  --dt-bg:        color-mix(in oklab, var(--d-accent) var(--tint-d), var(--d-bg));
  --dt-surface:   color-mix(in oklab, var(--d-accent) var(--tint-d), var(--d-surface));
  --dt-surface-2: color-mix(in oklab, var(--d-accent) var(--tint-d), var(--d-surface-2));
  --dt-line:      color-mix(in oklab, var(--d-accent) var(--tint-d), var(--d-line));
  --dt-line-2:    color-mix(in oklab, var(--d-accent) var(--tint-d), var(--d-line-2));
  --dt-idle:      color-mix(in oklab, var(--d-accent) var(--tint-d), var(--d-idle));
  --lt-bg:        color-mix(in oklab, var(--l-accent) var(--tint-l), var(--l-bg));
  --lt-surface:   color-mix(in oklab, var(--l-accent) var(--tint-l), var(--l-surface));
  --lt-surface-2: color-mix(in oklab, var(--l-accent) var(--tint-l), var(--l-surface-2));
  --lt-line:      color-mix(in oklab, var(--l-accent) var(--tint-l), var(--l-line));
  --lt-line-2:    color-mix(in oklab, var(--l-accent) var(--tint-l), var(--l-line-2));
  --lt-idle:      color-mix(in oklab, var(--l-accent) var(--tint-l), var(--l-idle));

  /* --- نگاشتِ پیش‌فرض: تیره */
  color-scheme: dark;
  --bg: var(--dt-bg);               --surface: var(--dt-surface);
  --surface-2: var(--dt-surface-2); --line: var(--dt-line);
  --line-2: var(--dt-line-2);       --idle: var(--dt-idle);
  --fg: var(--d-fg);               --fg-muted: var(--d-fg-muted);
  --fg-faint: var(--d-fg-faint);   --accent: var(--d-accent);
  --info: var(--d-info);           --warn: var(--d-warn);
  --bad: var(--d-bad);             --on-accent: var(--d-on-accent);
  --ok-bg: var(--d-ok-bg);         --ok-line: var(--d-ok-line);
  --warn-bg: var(--d-warn-bg);     --warn-line: var(--d-warn-line);
  --bad-bg: var(--d-bad-bg);       --bad-line: var(--d-bad-line);
  --info-bg: var(--d-info-bg);     --info-line: var(--d-info-line);
  --cell-part: var(--d-cell-part); --shadow: var(--d-shadow);
  --z-1: var(--d-z-1);             --z-2: var(--d-z-2);
}

/* ۲ — سلیقه‌ی سیستم‌عامل، فقط وقتی کاربر در دیبا چیزی انتخاب نکرده */
@media (prefers-color-scheme: light) {
  :root:not([data-theme='dark']) {
    color-scheme: light;
    --bg: var(--lt-bg);               --surface: var(--lt-surface);
    --surface-2: var(--lt-surface-2); --line: var(--lt-line);
    --line-2: var(--lt-line-2);       --idle: var(--lt-idle);
    --fg: var(--l-fg);               --fg-muted: var(--l-fg-muted);
    --fg-faint: var(--l-fg-faint);   --accent: var(--l-accent);
    --info: var(--l-info);           --warn: var(--l-warn);
    --bad: var(--l-bad);             --on-accent: var(--l-on-accent);
    --ok-bg: var(--l-ok-bg);         --ok-line: var(--l-ok-line);
    --warn-bg: var(--l-warn-bg);     --warn-line: var(--l-warn-line);
    --bad-bg: var(--l-bad-bg);       --bad-line: var(--l-bad-line);
    --info-bg: var(--l-info-bg);     --info-line: var(--l-info-line);
    --cell-part: var(--l-cell-part); --shadow: var(--l-shadow);
    --z-1: var(--l-z-1);             --z-2: var(--l-z-2);
  }
}

/* ۳ — کلیدِ خودِ دیبا. بر سیستم‌عامل مقدم است. */
:root[data-theme='light'] {
  color-scheme: light;
  --bg: var(--lt-bg);               --surface: var(--lt-surface);
  --surface-2: var(--lt-surface-2); --line: var(--lt-line);
  --line-2: var(--lt-line-2);       --idle: var(--lt-idle);
  --fg: var(--l-fg);               --fg-muted: var(--l-fg-muted);
  --fg-faint: var(--l-fg-faint);   --accent: var(--l-accent);
  --info: var(--l-info);           --warn: var(--l-warn);
  --bad: var(--l-bad);             --on-accent: var(--l-on-accent);
  --ok-bg: var(--l-ok-bg);         --ok-line: var(--l-ok-line);
  --warn-bg: var(--l-warn-bg);     --warn-line: var(--l-warn-line);
  --bad-bg: var(--l-bad-bg);       --bad-line: var(--l-bad-line);
  --info-bg: var(--l-info-bg);     --info-line: var(--l-info-line);
  --cell-part: var(--l-cell-part); --shadow: var(--l-shadow);
  --z-1: var(--l-z-1);             --z-2: var(--l-z-2);
}

/* ═══════════════════════════════════════════════════════ پایه */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  font-family: var(--font);
  font-size: var(--t-md);
  line-height: 1.6;
  color: var(--fg);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
}

/* ⚠⚠ این یک خط، یک باگِ خاموشِ چندجلسه‌ای را بست.
   قاعده‌ی مرورگر برای `[hidden]` فقط `display: none` است — و هر
   `display`ِ نوشته‌شده‌ی ما بر آن می‌چربد. `.palette__item` دقیقاً
   `display: flex` دارد، پس جاوااسکریپت `el.hidden = true` می‌گذاشت،
   ویژگی در DOM می‌نشست، و آیتم **همان‌جا می‌ماند**. فیلترِ پالت
   هیچ‌وقت واقعاً چیزی را پنهان نکرده بود.

   `!important` اینجا عمدی است: `[hidden]` یعنی «نباشد»، و هیچ
   قاعده‌ی دیگری نباید بتواند آن را نقض کند.                       */
[hidden] { display: none !important; }

h1, h2, h3, h4 { margin: 0; font-weight: 600; line-height: 1.3; }
h1 { font-size: var(--t-2xl); letter-spacing: -.02em; }
h2 { font-size: var(--t-lg); letter-spacing: -.01em; }
h3 { font-size: var(--t-sm); }
p  { margin: 0 0 var(--sp-3); }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

/* اعدادِ لاتین داخلِ متنِ فارسی نباید جهتِ جمله را به‌هم بزنند،
   و ستونِ عددی باید هم‌تراز بماند. */
.num, .clock, .mono, code {
  direction: ltr;
  unicode-bidi: isolate;
  font-variant-numeric: tabular-nums;
  font-size: var(--t-num);
}

/* ⚠⚠ **مبلغ `direction: ltr` نمی‌گیرد** — و این یک باگِ واقعیِ
   پیداشده در مرورگر است، نه سلیقه.

   «۹ میلیون ریال» عدد **و یکای فارسی** است. تا جلسه ۲۲، `.money`
   با `.num` یک قاعده داشت و `direction: ltr` می‌گرفت؛ نتیجه در
   جمله‌ی فارسی این می‌شد:

       الف میلیون ریال ۹ ب      ← غلط، و هیچ خطایی هم نمی‌دهد

   چون در ظرفِ چپ‌به‌راست، دو دنباله‌ی راست‌به‌چپ («میلیون ریال»)
   و عددی («۹») جابه‌جا چیده می‌شوند. فقط وقتی متن **می‌شکست** و
   یکا به خطِ دوم می‌رفت، تصادفاً درست به نظر می‌رسید — که همان
   دلیلِ ماه‌ها دیده نشدنش بود.

   `unicode-bidi: isolate` **بی** `direction`، هم مبلغ را از
   جمله‌ی دور و برش جدا می‌کند (که هدفِ اصلی بود) و هم جهتش را از
   صفحه می‌گیرد — پس نسخه‌ی آلمانیِ آینده خودبه‌خود درست می‌شود. */
/* `.nph` = «عبارتِ عددی»: عدد به‌همراه کلمه — «۱۲ سند»، «۳ از ۵»،
   «۹ میلیون ریال». همان قاعده‌ی `.money` است و همان دلیل؛ `.money`
   فقط نامِ خواناترش برای مبلغ است. */
.money, .nph {
  unicode-bidi: isolate;
  font-variant-numeric: tabular-nums;
  font-size: var(--t-num);
}
.mono, code { font-family: var(--mono); font-size: .92em; }

/* ⚠ `.jd` برای تاریخِ **خوانا**ی شمسی («۱۰ شهریور ۱۴۰۵»)، نه `.num`.
   این باگ در چهار قالب بود و در عکسِ پرونده‌ی کار دیده شد: `.num`
   جهت را به چپ‌به‌راست قفل می‌کند، پس رشته‌ای که هم واژه دارد هم
   عدد، **جابه‌جا** نمایش داده می‌شد — «۱۰ شهریور ۱۴۰۵» می‌شد
   «شهریور ۱۴۰۵ ۱۰». `.num` فقط برای رشته‌ی تمام‌عددی است
   (۱۴۰۵/۰۶/۱۰، ۰۷:۳۰، مبلغ). */
.jd {
  font-variant-numeric: tabular-nums; white-space: nowrap;
  font-size: var(--t-num);
}

/* ── ضریب‌های «درشت‌تر» — روی <html> می‌نشینند.
   ⚠ پله‌ها کوچک‌اند (٪۹ و ٪۱۸) و عمداً: ٪۵۰ چیدمان را می‌شکند و
     کاربر فکر می‌کند سامانه خراب شده، نه اینکه تنظیمش زیاد بوده. */
/* ⚠⚠ **پله‌ها در جلسه ۲۷ بزرگ‌تر شدند.**

   نسخهٔ اول ۹٪ و ۱۸٪ بود — با این استدلال که «۵۰٪ چیدمان را
   می‌شکند». درست بود ولی نتیجه‌اش بدتر شد: پرسا کلید را زد و گفت
   «اصلاً تغییری نمی‌کنه». تنظیمی که کاربر نتواند اثرش را **ببیند**،
   از نبودنش بدتر است — چون فکر می‌کند خراب است.

   حالا ۱۵٪ / ۳۲٪ / ۵۰٪. سه پله، و هر سه بی‌تردید دیدنی. چیدمان
   تا ۵۰٪ آزموده شد (تقویم، جدولِ تردد، کاشی‌های میزکار). */
:root[data-tsize='lg']  { --tz: 1.15; }
:root[data-tsize='xl']  { --tz: 1.32; }
:root[data-tsize='2xl'] { --tz: 1.50; }

/* ⚠ «عددهای درشت‌تر» حالا یک **کلیدِ روشن/خاموش** است و نه یک
   مقیاسِ دوم.

   دو مقیاسِ سه‌پله‌ای کنارِ هم، نُه ترکیب می‌سازد و کاربر باید
   بفهمد کدامشان چه می‌کند. یک کلید که «عددها یک پله درشت‌تر از
   متن» را روشن کند، همان کار را می‌کند و توضیح هم نمی‌خواهد. */
:root[data-bignum='1'] { --nz: 1.30; }

.muted   { color: var(--fg-muted); }
.faint   { color: var(--fg-faint); }
.small   { font-size: var(--t-xs); }
.nowrap  { white-space: nowrap; }
.mb0     { margin-bottom: 0; }
.between { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); }
.inline  { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
/* ⚠⚠ ستونِ کنشِ جدول‌ها **هرگز نباید بشکند**. `table.t` به هر ستونِ
   غیرِ اول `width: 1%` می‌دهد (تا ستونِ اول کشیده شود)، و آن یعنی
   ستونِ آخر به باریک‌ترین حالت می‌رود: در «نامه‌های رسمی»، «برگه» و
   مداد و سطلِ آشغال سه طبقه روی هم افتادند و ردیف سه برابر بلند شد.
   در کرومِ خودِ پرسا دیده شد. */
.acts { flex-wrap: nowrap; white-space: nowrap; }
.stack   { display: flex; flex-direction: column; gap: var(--sp-3); }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: var(--r-sm); }

/* ═══════════════════════════════════════════════ چیدمانِ کل */
/* ⚠⚠ `grid-template-columns` اینجا **اجباری** است، و نبودش یک باگِ
   خاموشِ چندجلسه‌ای بود که تازه با اندازه‌گیریِ واقعیِ ۳۷۵ پیکسل
   پیدا شد:

   شبکه‌ای که ستونش تعریف نشده، یک ستونِ **ضمنی** با اندازه‌ی `auto`
   می‌سازد، و `auto` یعنی «به‌اندازه‌ی max-content». پس هر جدول یا
   هر عنوانِ بی‌شکستِ داخلِ صفحه، **کلِ پوسته** را پهن می‌کرد: در
   موبایل `.app` عرضش ۳۷۵ بود ولی ستونش ۴۳۸، و بدنه‌ی صفحه افقی
   می‌لولید. هیچ خطایی هم نمی‌داد.

   با `minmax(0, 1fr)` ستون هرگز از عرضِ شبکه بیشتر نمی‌شود، و
   محتوایِ پهن داخلِ ظرفِ **خودش** می‌لولد — که همان چیزی است که
   `.table-wrap` و `.cal-wrap` برایش ساخته شدند.                   */
.app {
  min-height: 100vh;
  display: grid;
  grid-template-rows: var(--topbar-h) 1fr;
  grid-template-columns: minmax(0, 1fr);
}
.body-row { display: flex; min-height: 0; min-width: 0; }
.page {
  flex-grow: 1; min-width: 0;
  padding: var(--sp-5) var(--sp-6);
  display: flex; flex-direction: column; gap: var(--sp-4);
}

/* ─────────────────────────────────────────────── نوارِ بالا */
.topbar {
  border-bottom: 1px solid var(--line);
  display: flex; align-items: center; gap: var(--sp-4);
  padding-inline: var(--sp-5);
  background: var(--bg);
  position: sticky; inset-block-start: 0; z-index: 20;
  min-width: 0;
}

.brand { display: flex; align-items: center; gap: 9px; text-decoration: none; color: var(--fg); }
.brand:hover { text-decoration: none; }
/* نشانِ اتاق در نوارِ بالا.
   ⚠⚠ لوگو **در هیچ حالتی رنگ نمی‌گیرد**: آبیِ برندِ اتاق است، نه
      یک آیکونِ رابطِ کاربری. یک بار `filter: brightness()` برای
      حالتِ تیره گذاشتم و نتیجه در مرورگر دیده شد: نشان شسته و
      بی‌رنگ شد. خودِ SVG زمینهٔ روشن دارد، پس روی نوارِ تیره هم
      خوانده می‌شود — و در هر دو حالت **یک شکل** است.
   ⚠ `flex-shrink: 0` لازم است: بی‌آن روی ۳۷۵ پیکسل نشان کشیده
     می‌شد. */
.brand__logo {
  block-size: 26px; inline-size: auto; flex-shrink: 0;
  display: block; border-radius: 3px;
}
/* ⚠ کلاسِ قدیمی نگه داشته شد: پنلِ ادمین و صفحهٔ ورود هم از آن
   استفاده می‌کنند. */
.brand__mark {
  width: 25px; height: 25px; border-radius: var(--r-sm);
  background: var(--accent); color: var(--on-accent);
  display: flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: var(--t-md); flex-shrink: 0;
}
.brand__name { font-size: var(--t-ml); font-weight: 600; letter-spacing: -.01em; }
.brand__sub  { font-size: var(--t-3xs); color: var(--fg-faint); }

/* ورودیِ فرمان — قلبِ ناوبری. جای منوی چهل‌شاخه را می‌گیرد. */
.cmd {
  /* ⚠ `min-inline-size: 0` اجباری است و نبودنش یک باگِ واقعی بود:
     پیش‌فرضِ یک آیتمِ فلکس `min-width: auto` است، یعنی **کوچک‌تر از
     محتوایش نمی‌شود**. با `flex-grow: 1` ولی بی این خط، جعبه‌ی
     جستجو روی ۳۷۵ پیکسل به اندازه‌ی متنِ راهنما + دو کلیدِ Ctrl و K
     پهن می‌ماند و ۲۲ پیکسل نوارِ بالا را از صفحه بیرون می‌زد.

     ⚠⚠ و این **دقیقاً همان خانواده‌ی** باگِ `.app` در جلسه ۱۷ است:
     آنجا ستونِ ضمنیِ گرید `auto` بود (= max-content) و ده صفحه را
     می‌لولاند. اندازه‌ی پیش‌فرضِ «به‌اندازه‌ی محتوا» در فلکس و گرید،
     دشمنِ چیدمانِ واکنش‌گراست. */
  flex-grow: 1; min-inline-size: 0; max-width: 420px; margin-inline: auto;
  height: 32px;
  border: 1px solid var(--line-2);
  background: var(--surface);
  border-radius: var(--r);
  display: flex; align-items: center; gap: 9px;
  padding-inline: 11px;
  color: var(--fg-faint);
  font-family: var(--font); font-size: var(--t-xs);
  cursor: text; text-align: start;
}
.cmd:hover { border-color: var(--accent); }
.cmd__hint {
  flex-grow: 1; min-inline-size: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.kbd {
  font-family: var(--mono); font-size: var(--t-3xs);
  color: var(--fg-muted); background: var(--idle);
  border-radius: 4px; padding: 1px 6px;
}

.dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); flex-shrink: 0; }
.dot--off { background: var(--fg-faint); }

.topbar__meta { font-size: var(--t-2xs); color: var(--fg-muted); }
.topbar__spacer { flex-grow: 1; }

.avatar {
  width: 27px; height: 27px; border-radius: 50%;
  background: var(--surface-2); border: 1px solid var(--line-2);
  display: flex; align-items: center; justify-content: center;
  font-size: var(--t-2xs); color: var(--fg-muted); flex-shrink: 0;
}

.iconbtn {
  width: 30px; height: 30px; border-radius: var(--r-sm);
  border: 1px solid transparent; background: none;
  color: var(--fg-muted); cursor: pointer;
  display: flex; align-items: center; justify-content: center; flex-shrink: 0;
}
.iconbtn:hover { background: var(--surface-2); color: var(--fg); }

/* ─────────────────────────────────────────── نوارِ کناری

   ⚠ بازطراحیِ جلسه ۱۸: ریلِ آیکونیِ بی‌برچسب به نوارِ دو‌حالته
   تبدیل شد. عرض از یک توکن می‌آید تا هر دو حالت یک جا تعریف شوند
   و انیمیشنِ عرض هم روی همان یک مقدار بنشیند.

   ⚠ در حالتِ جمع، برچسب‌ها با `opacity`+`width:0` می‌روند، نه با
   `display:none` — چون `display` انیمیشن نمی‌گیرد و منو «تِلِپ»
   می‌شود. با این روش عرض نرم باز و بسته می‌شود.
   ─────────────────────────────────────────────────────────── */
.side {
  --side-w: 232px;
  width: var(--side-w); flex-shrink: 0;
  border-inline-start: 1px solid var(--line);
  background: var(--surface);
  display: flex; flex-direction: column;
  position: sticky; inset-block-start: var(--topbar-h);
  align-self: flex-start;
  height: calc(100vh - var(--topbar-h));
  transition: width .22s cubic-bezier(.4, 0, .2, 1);
  overflow: hidden;
}
.side--tight { --side-w: 58px; }

.side__scroll {
  flex-grow: 1; overflow-y: auto; overflow-x: hidden;
  padding: 10px 8px;
  display: flex; flex-direction: column; gap: 2px;
}

.side__group + .side__group { margin-block-start: 12px; }

/* ── سرگروه، حالا تاشو
   ⚠ `<summary>` است نه `<div>`: باز و بسته شدن بی هیچ جاوااسکریپتی
   کار می‌کند، با Enter و Space هم کار می‌کند، و صفحه‌خوان خودش
   «باز/بسته» را می‌گوید. جاوااسکریپت فقط **یاد** را نگه می‌دارد. */
.side__head {
  font-size: var(--t-3xs); font-weight: 700; letter-spacing: .04em;
  color: var(--fg-faint); text-transform: none;
  padding: 6px 10px 5px; white-space: nowrap;
  transition: opacity .16s ease, color .15s ease;
  display: flex; align-items: center; gap: 6px;
  cursor: pointer; border-radius: var(--r);
  list-style: none;                 /* فایرفاکس */
}
/* ⚠ مثلثِ پیش‌فرضِ وبکیت. بی این خط، کنارِ هر گروه یک مثلثِ سیاهِ
   بی‌ربط می‌نشیند که با هیچ آیکونِ دیگری در دیبا جور نیست. */
.side__head::-webkit-details-marker { display: none; }
.side__head:hover { color: var(--fg-muted); background: var(--surface-2); }
.side__head > span:first-child { flex-grow: 1; }
.side__head > svg {
  flex-shrink: 0; opacity: .7;
  transition: transform .18s ease;
}
/* ⚠ چرخش با `rotate` و نه دو آیکونِ متفاوت: در چیدمانِ راست‌به‌چپ
   آیکونِ chevron به سمتِ آغاز نگاه می‌کند، پس ۹۰ درجه‌ی منفی آن را
   رو به پایین می‌آورَد — و در حالتِ چپ‌به‌راست هم همین درست است،
   چون خودِ آیکون آینه می‌شود. */
.side__group[open] > .side__head > svg { transform: rotate(-90deg); }

.side--tight .side__head { opacity: 0; height: 10px; padding: 0; overflow: hidden; }

.side__item {
  position: relative;
  display: flex; align-items: center; gap: 10px;
  height: 36px; padding-inline: 10px;
  border-radius: var(--r);
  color: var(--fg-muted); text-decoration: none;
  white-space: nowrap;
  transition: background-color .15s ease, color .15s ease;
}
.side__item > svg { flex-shrink: 0; }
.side__item:hover { background: var(--surface-2); color: var(--fg); text-decoration: none; }
.side__item--on { background: var(--ok-bg); color: var(--accent); font-weight: 600; }
.side__item--on::before {
  content: ''; position: absolute;
  inset-inline-end: 0; inset-block: 8px;
  width: 2px; border-radius: 2px; background: var(--accent);
}

.side__label {
  font-size: var(--t-sm);
  flex-grow: 1; overflow: hidden; text-overflow: ellipsis;
  transition: opacity .16s ease;
}
.side--tight .side__label { opacity: 0; width: 0; flex-grow: 0; }

.side__count {
  min-width: 19px; height: 19px; padding-inline: 5px;
  border-radius: var(--r-full);
  background: var(--warn-bg); border: 1px solid var(--warn-line); color: var(--warn);
  font-size: var(--t-3xs); font-weight: 700;
  display: flex; align-items: center; justify-content: center; flex-shrink: 0;
  transition: opacity .16s ease;
}
.side--tight .side__count {
  position: absolute; inset-block-start: 2px; inset-inline-start: 2px;
  min-width: 15px; height: 15px; padding-inline: 3px;
}

/* ── مقصدهای هنوز نساخته
   ⚠ کم‌رنگ‌اند ولی **کلیک‌پذیر**. یک مقصدِ غیرفعال فقط سؤال می‌سازد
   («چرا کار نمی‌کند؟»)؛ مقصدی که صفحه‌ای باز می‌کند و می‌گوید چه
   خواهد بود و منتظرِ چیست، جواب می‌دهد. */
.side__item--soon { opacity: .58; }
.side__item--soon:hover { opacity: 1; }
.side__soon {
  font-size: var(--t-4xs); font-weight: 700; line-height: 1;
  padding: 3px 5px; border-radius: var(--r-sm);
  background: var(--surface-2); border: 1px solid var(--line);
  color: var(--fg-faint); flex-shrink: 0;
  transition: opacity .16s ease;
}
.side--tight .side__soon { opacity: 0; width: 0; padding: 0; border: 0; }

/* ── مسیرِ بالای صفحه */
.crumbs {
  display: flex; align-items: center; gap: 5px; flex-wrap: wrap;
  font-size: var(--t-2xs); color: var(--fg-faint);
  margin-block-end: var(--sp-3);
}
.crumbs a { color: var(--fg-muted); }
.crumbs [aria-current] { color: var(--fg-muted); font-weight: 600; }
.crumbs__sep { display: inline-flex; opacity: .5; }

/* ── صفحه‌ی «هنوز ساخته نشده» */
.soon {
  display: grid; gap: var(--sp-3);
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  align-items: start;
}
.soon__block {
  margin-block-start: var(--sp-3); padding: 10px 12px;
  border-radius: var(--r); background: var(--surface-2);
  border: 1px solid var(--line);
  font-size: var(--t-xs); line-height: 1.7;
  display: flex; gap: 7px; flex-wrap: wrap;
}
.soon__block b { color: var(--fg-muted); flex-shrink: 0; }
.soon__block--wait { background: var(--warn-bg); border-color: var(--warn-line); }
.soon__block--wait b { color: var(--warn); }
.soon__list {
  margin: 0; padding-inline-start: 1.3em;
  display: flex; flex-direction: column; gap: 9px;
  font-size: var(--t-xs); line-height: 1.75;
}
.soon__list li::marker { color: var(--fg-faint); font-size: var(--t-3xs); }
/* ── بندهایی که هشدارند نه خواسته — دام‌هایی که باید یادمان بماند */
.soon__caution { color: var(--warn); }
.soon__caution::marker { color: var(--warn); }
p.soon__caution {
  margin: 0; padding: 9px 11px; border-radius: var(--r);
  background: var(--warn-bg); border: 1px solid var(--warn-line);
  line-height: 1.75;
}
.soon__foot {
  margin-block-start: var(--sp-4); font-size: var(--t-2xs); line-height: 1.8;
}
.soon__foot code {
  font-size: var(--t-3xs); padding: 2px 5px; border-radius: var(--r-sm);
  background: var(--surface-2); border: 1px solid var(--line);
  /* ⚠ مسیرِ فایل لاتین است و باید در متنِ فارسی چپ‌به‌راست بماند */
  direction: ltr; unicode-bidi: isolate;
}

/* ── بلوکِ کاربر، پایینِ نوار */
.side__user {
  display: flex; align-items: center; gap: 9px;
  padding: 10px; margin: 8px;
  border-radius: var(--r);
  background: var(--surface-2); border: 1px solid var(--line);
  flex-shrink: 0; overflow: hidden;
}
.side__who {
  display: flex; flex-direction: column; min-width: 0; flex-grow: 1;
  transition: opacity .16s ease;
}
.side__who b     { font-size: var(--t-xs); font-weight: 600;
                   white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.side__who small { font-size: var(--t-3xs); color: var(--fg-faint); }
.side--tight .side__who { opacity: 0; width: 0; flex-grow: 0; }
/* ⚠ حالا یک `<button>` داخلِ فرم است، پس ظاهرِ پیش‌فرضِ دکمه باید
   پاک شود؛ وگرنه یک مستطیلِ خاکستری کنارِ نامِ کاربر می‌نشیند. */
.side__outf { display: flex; flex-shrink: 0; }
.side__out {
  color: var(--fg-faint); display: flex; flex-shrink: 0;
  transition: color .15s ease;
  background: none; border: 0; padding: 0; cursor: pointer;
}
.side__out:hover { color: var(--bad); }
.side--tight .side__out, .side--tight .side__outf { display: none; }
.side--tight .side__user { justify-content: center; padding-inline: 0; }

/* ─────────────────────────────────────── سرصفحه‌ی صفحه */
/* ⚠ `.page-head` حذف شد (مرحله ۴۲): سرِ صفحه‌ی تختِ قدیمی. هر
   چهارده صفحه‌ی دیبا حالا `.phead`ِ کارتی دارند. اگر این دو قاعده
   می‌ماندند، اولین صفحه‌ی تازه‌ای که کسی از روی صفحه‌ی قدیم کپی
   می‌کرد، دو‌دستگی را برمی‌گرداند — و یک تست هم گذاشتیم که نگذارد. */

/* ═══════════════════════════════════════════════ کاشی‌ها

   ⚠ بازطراحیِ جلسه ۱۶ — چرا «نوارِ آمار» و نه شش جعبه‌ی شناور:

   نسخه‌ی قبلی `repeat(auto-fit, minmax(150px, 1fr))` بود. روی
   نمایشگرِ پرسا پنج ستون جا می‌شد و کاشیِ ششم **تنها** به خطِ دوم
   می‌افتاد و یک حفره‌ی بزرگ کنارش می‌ماند. auto-fit تعدادِ ستون را
   به عرضِ پنجره می‌سپارد؛ وقتی تعدادِ آیتم‌ها ثابت است، این یعنی
   چیدمان به بختِ اندازه‌ی پنجره بسته است.

   حالا شمارشِ ستون **صریح** است و در هر پله مقسوم‌علیهِ شش:
   ۶ → ۳ → ۲. هیچ‌وقت ردیفِ ناقص نمی‌ماند.

   و شکل: به‌جای شش کارتِ جدا با شش سایه و شش حاشیه، یک سطحِ
   پیوسته با جداکننده‌ی مو‌باریک. این الگوی داشبوردهای متراکم است
   (همان چیزی که جست‌وجوی سبک هم «Data-Dense Dashboard» نامید) و
   نویزِ بصری را حذف می‌کند بی‌آن‌که دادهٔ کمتری نشان دهد.
   ─────────────────────────────────────────────────────────── */
.tiles {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  /* ⚠ جداکننده‌ها **خودِ فاصله‌اند**: زمینه‌ی شبکه رنگِ خط است و هر
     کاشی زمینه‌ی سطح دارد، پس درزِ یک‌پیکسلی بینشان دیده می‌شود.

     تلاشِ اول `border-inline-start` روی هر کاشی بود با
     `nth-child(6n+1)` برای حذفِ اولین. سه ایراد داشت: در هر نقطه‌ی
     شکست باید از نو نوشته می‌شد، برای تابلو که **هشت** کاشی دارد
     غلط بود، و هر بار کسی کاشی اضافه می‌کرد باید یادش می‌ماند.
     این روش به تعدادِ ستون و تعدادِ کاشی **کاری ندارد**. */
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--z-1);
  overflow: hidden;
}
@media (max-width: 1180px) { .tiles { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 620px)  { .tiles { grid-template-columns: repeat(2, 1fr); } }

.tile {
  position: relative;
  padding: 11px 14px 12px;
  color: var(--fg); display: block;
  background: var(--surface);
  transition: background-color .15s ease;
}

a.tile:hover { text-decoration: none; background: var(--surface-2); }

/* لهجه‌ی رنگ: یک نوارِ نازک در **بالای** کاشی، نه حاشیه‌ی کاملِ
   رنگی. رنگ باید اشاره کند، نه فریاد بزند. */
.tile--ok::before, .tile--warn::before,
.tile--bad::before, .tile--info::before {
  content: ''; position: absolute;
  inset-block-start: 0; inset-inline: 0; height: 2px;
}
.tile--ok::before   { background: var(--accent); }
.tile--warn::before { background: var(--warn); }
.tile--bad::before  { background: var(--bad); }
.tile--info::before { background: var(--info); }

.tile__top   { display: flex; align-items: center; justify-content: space-between; gap: 6px; }
.tile__ico   { color: var(--fg-faint); display: flex; flex-shrink: 0; }
.tile__label { font-size: var(--t-2xs); color: var(--fg-muted); letter-spacing: .01em;
               white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tile__row   { display: flex; align-items: baseline; gap: 6px; margin-top: 3px; }
.tile__value { font-size: var(--n-xl); font-weight: 600; line-height: 1.2;
               letter-spacing: -.015em; }
.tile__value--warn { color: var(--warn); }
.tile__value--bad  { color: var(--bad); }
/* ⚠ «۰»ِ فارسی یک دایره‌ی ریز است. در نسخه‌ی وزنِ ۲۰۰ تقریباً ناپیدا
   بود و کاربر فکر می‌کرد خانه خالی است — و در حالتِ روشن حتی بدتر،
   چون --fg-faint روی سفید کم‌جان است. پس صفر **هم‌وزنِ** بقیه‌ی
   اعداد است و فقط یک پله کم‌رنگ‌تر: «صفر» یک خبر است، نه یک غیاب. */
.tile__value--mute { color: var(--fg-muted); }
.tile__sub   { font-size: var(--t-2xs); color: var(--fg-faint); }

/* ── خطِ چهارمِ کاشی: «بستر»
   ⚠ چرا هست (جلسه ۱۷): در هر پنج قالبِ مرجعی که بررسی شد، زیرِ
   عددِ کاشی یک **جمله** بود نه یک عددِ دیگر. عددِ تنها معنا ندارد؛
   «۱۷» خوب است یا بد؟ این خط جواب می‌دهد. */
.tile__note {
  margin-top: 7px; padding-top: 7px;
  border-top: 1px solid var(--line);
  font-size: var(--t-2xs); color: var(--fg-muted);
  line-height: 1.5;
}

/* ── چیپِ تغییر نسبت به دورهٔ قبل
   ⚠ رنگ از **خوب/بد بودن** می‌آید، نه از بالا/پایین بودن: بالا رفتنِ
   «حاضر» خوب است و بالا رفتنِ «تأخیر» بد. جهتِ فلش را جدا می‌گوید. */
.chip {
  display: inline-flex; align-items: center; gap: 2px;
  height: 19px; padding-inline: 6px;
  border-radius: var(--r-full);
  font-size: var(--t-3xs); font-weight: 600;
  border: 1px solid var(--line-2); color: var(--fg-muted);
  background: var(--surface-2);
  flex-shrink: 0;
}
.chip--ok   { background: var(--ok-bg);   border-color: var(--ok-line);   color: var(--accent); }
.chip--bad  { background: var(--bad-bg);  border-color: var(--bad-line);  color: var(--bad); }
.chip--warn { background: var(--warn-bg); border-color: var(--warn-line); color: var(--warn); }
.chip--mute { color: var(--fg-faint); }

/* ═══════════════════════════════════════════ نمودارِ ماه */
.chart {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--z-1);
  padding: 14px 18px 12px;
}
.chart__head { display: flex; align-items: center; justify-content: space-between;
               gap: var(--sp-3); margin-bottom: 14px; flex-wrap: wrap; }
.chart__legend { display: flex; gap: 14px; font-size: var(--t-2xs); color: var(--fg-muted); flex-wrap: wrap; }
.chart__key { display: flex; align-items: center; gap: 5px; }

.swatch { width: 8px; height: 8px; border-radius: 2px; background: var(--idle);
          border: 1px solid var(--line-2); flex-shrink: 0; }
.swatch--ok   { background: var(--accent); border-color: var(--accent); }
.swatch--warn { background: var(--warn);   border-color: var(--warn); }
.swatch--bad  { background: var(--bad);    border-color: var(--bad); }
.swatch--info { background: var(--info);   border-color: var(--info); }
.swatch--part { background: var(--cell-part); border-color: var(--cell-part); }
.swatch--bad-soft { background: var(--bad); border-color: var(--bad); opacity: .38; }

/*  ⚠ میله‌ها و برچسب‌های روز دو ردیفِ **جدا** هستند.
    اگر میله را داخلِ ستونی با ارتفاعِ خودکار بگذاری، ارتفاعِ درصدی
    مرجعی برای حساب کردن ندارد و میله به صفر می‌افتد. پس ردیفِ میله
    ارتفاعِ قطعی دارد و برچسب‌ها زیرش می‌آیند.

    ⚠ بازطراحیِ جلسه ۱۶ — نمودار «خراب» به نظر می‌رسید و نبود:

    ۱. رنگ از «کسرِ کار بیش از اضافه» می‌آمد. روی **جمعِ ۱۹ نفر**
       این تقریباً همیشه درست است، پس هر روزِ داده‌دار قرمز می‌شد و
       نمودار دائماً هشدار می‌داد. حالا حجمِ کار یک رنگ دارد
       (تأکیدِ دیبا) و اضافه‌کار قطعه‌ی دومِ همان میله است — یعنی
       رنگ **ترکیب** را نشان می‌دهد، نه قضاوت را.
    ۲. روزِ بی‌داده ارتفاعِ ۸٪ داشت و توپُر بود؛ ردیفی از میله‌های
       ریز شبیهِ یک خط‌چینِ بی‌معنا می‌شد. حالا یک نشانِ توخالیِ
       ۳ پیکسلی روی خطِ پایه است: «نمی‌دانم»، نه «صفر بود».
    ۳. خطِ راهنما اضافه شد؛ بی‌آن، ارتفاعِ میله عددی نمی‌گوید. */
.chart__plot { position: relative; }
.chart__plot::before,
.chart__plot::after {
  content: ''; position: absolute; inset-inline: 0; height: 1px;
  background: var(--line); pointer-events: none;
}
.chart__plot::before { inset-block-start: 0; }
.chart__plot::after  { inset-block-start: 50%; }

.chart__bars {
  display: flex; align-items: flex-end; gap: 3px; height: 86px;
  position: relative; border-block-end: 1px solid var(--line-2);
}
.chart__bar {
  flex-grow: 1; min-width: 2px; min-height: 2px;
  border-radius: 3px 3px 0 0;
  background: var(--accent);
  display: flex; flex-direction: column; justify-content: flex-start;
  transition: filter .15s ease;
}
.chart__bar:hover { filter: brightness(1.15); }
/* قطعه‌ی اضافه‌کار — بالای همان میله، نه میله‌ی جدا */
.chart__over {
  width: 100%; background: var(--warn);
  border-radius: 3px 3px 0 0; flex-shrink: 0;
}
/* روزی که کسرِ کارش چشمگیر است: زیرخطِ قرمز، نه میله‌ی قرمز */
.chart__bar--short { box-shadow: inset 0 -3px 0 var(--bad); }
/* روزِ بی‌داده / تعطیل */
.chart__bar--off {
  background: none; min-height: 3px; height: 3px;
  border: 1px dashed var(--line-2); border-block-end: 0;
  border-radius: 0;
}
.chart__days { display: flex; gap: 3px; margin-top: 6px; }
.chart__day  { flex-grow: 1; text-align: center; font-size: var(--t-3xs);
               color: var(--fg-faint); min-width: 2px; }
.chart__day--off   { color: var(--bad); opacity: .65; }
.chart__day--today { color: var(--accent); font-weight: 700; }

/* ═══════════════════════════════════════════════ کارتابل */
.tasks { display: flex; flex-direction: column; }
.task {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: 11px var(--sp-4);
  border-bottom: 1px solid var(--line);
}
.task:last-child { border-bottom: 0; }
.task__tone { width: 3px; align-self: stretch; border-radius: 2px; background: var(--fg-faint); flex-shrink: 0; }
.task__tone--warn { background: var(--warn); }
.task__tone--info { background: var(--info); }
.task__tone--bad  { background: var(--bad); }
.task__main  { flex-grow: 1; min-width: 0; }
.task__title { font-size: var(--t-sm); }
.task__meta  { font-size: var(--t-2xs); color: var(--fg-muted); margin-top: 1px; }
.task__side  { text-align: center; flex-shrink: 0; }
.task__side small { display: block; font-size: var(--t-3xs); color: var(--fg-faint); }
.task__acts  { display: flex; gap: 5px; flex-shrink: 0; }

/* ═══════════════════════════════════════════ نوارِ رویداد */
.strip {
  border: 1px solid var(--line); border-radius: var(--r);
  padding: 9px var(--sp-4);
  display: flex; align-items: center; gap: var(--sp-4); overflow: hidden;
}
.strip__label { font-size: var(--t-2xs); color: var(--fg-muted); font-weight: 600;
                letter-spacing: .02em; flex-shrink: 0; }
.strip__items { display: flex; gap: var(--sp-4); flex-grow: 1; overflow: hidden; }
.strip__item  { font-size: var(--t-xs); color: var(--fg-muted); white-space: nowrap; }
.strip__item--warn { color: var(--warn); }
.strip__item--bad  { color: var(--bad); }

.pill {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--surface-2); border-radius: var(--r-full);
  padding-block: 3px; padding-inline: 5px 11px;
  font-size: var(--t-2xs); white-space: nowrap; color: var(--fg);
}
.pill:hover { text-decoration: none; }
.pill__mark {
  width: 18px; height: 18px; border-radius: 50%;
  background: var(--idle); color: var(--fg-muted);
  font-size: var(--t-4xs); display: flex; align-items: center; justify-content: center; flex-shrink: 0;
}

/* ═══════════════════════════ ماتریسِ ماه (حضور و غیاب) */
.matrix { display: flex; flex-direction: column; min-width: 0; }
.matrix__head, .matrix__row, .matrix__foot { display: flex; align-items: center; }
.matrix__head { padding-bottom: 6px; border-bottom: 1px solid var(--line-2); align-items: flex-end; }
.matrix__row  { height: 30px; border-bottom: 1px solid var(--line); }
.matrix__row:hover { background: var(--surface-2); }
.matrix__foot { height: 38px; border-top: 1px solid var(--line-2); margin-top: 3px; font-weight: 600; }

.matrix__name  { width: 148px; flex-shrink: 0; display: flex; align-items: center; gap: 7px; min-width: 0; }
.matrix__name b { font-size: var(--t-xs); font-weight: 400; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.matrix__cells { flex-grow: 1; display: flex; gap: 2px; align-items: center; min-width: 0; }
.matrix__nums  { width: 190px; flex-shrink: 0; display: flex; align-items: center; font-size: var(--n-2xs); }
.matrix__nums > span { text-align: center; }
.matrix__nums .c1 { width: 60px; }
.matrix__nums .c2 { width: 54px; color: var(--accent); }
.matrix__nums .c3 { width: 54px; }
.matrix__nums .c4 { flex-grow: 1; display: flex; justify-content: flex-end; color: var(--fg-faint); }
.matrix__day { flex-grow: 1; text-align: center; font-size: var(--t-4xs); color: var(--fg-muted); min-width: 2px; }
.matrix__day--off   { color: var(--fg-faint); }
.matrix__day--today { color: var(--fg); font-weight: 600; }

.cell { flex-grow: 1; height: 18px; border-radius: 3px; background: var(--accent);
        min-width: 2px; border: 1px solid transparent; }
.cell--part    { background: var(--cell-part); }
.cell--leave   { background: var(--warn); }
.cell--mission { background: var(--info); }
/* ⚠ کسرِ کار **دو پله** دارد. رنگِ سیر فقط برای کسرِ چشمگیر
   (≥۲۰٪ شیفت). ده دقیقه زودتر رفتن و سه ساعت غیبت نباید یک شکل
   باشند — وگرنه کلِ ماتریس قرمز می‌شود و هیچ‌چیز نمی‌گوید. */
/* ⚠ تلاشِ اول `--bad-bg` بود؛ در حالتِ تیره آن رنگ تقریباً سیاه
   است و خانه شبیهِ «خالی» می‌شد — یعنی از بد بدتر. رقیق‌کردنِ خودِ
   رنگِ هشدار با opacity در **هر دو** حالت درست کار می‌کند و نیازی
   به توکنِ تازه ندارد. */
.cell--deficit    { background: var(--bad); opacity: .38; }
.cell--deficit-hi { background: var(--bad); }
.cell--off     { background: var(--surface-2); }
.cell--future  { background: var(--idle); opacity: .5; }

/* ═══════════════════════════════════════════════════ کارت */
.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--z-1);
  overflow: hidden;
}
/* ⚠ سرِ کارت **عنوان** است، نه تیتر. قبلاً هم‌وزنِ عددهای کاشی بود
   و با آن‌ها رقابت می‌کرد؛ حالا یک پله عقب می‌ایستد تا محتوا جلو
   بیاید. سلسله‌مراتب یعنی همین: چیزی باید کوتاه بیاید. */
.card__head {
  min-height: 40px;
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  padding: 8px var(--sp-4);
  border-bottom: 1px solid var(--line);
  font-size: var(--t-xs); font-weight: 600;
  color: var(--fg-muted); letter-spacing: .02em;
}
.card__body { padding: var(--sp-4); }
.card__body--flush { padding: 0; }
.card__foot { padding: 10px var(--sp-4); border-top: 1px solid var(--line);
              font-size: var(--t-2xs); color: var(--fg-muted);
              background: var(--surface-2); }

/* ⚠ `align-items: start` — بی‌این، کوتاه‌ترین ستون تا قدِ بلندترین
   کشیده می‌شود. روی میزکار همین باعث می‌شد کارتِ «کارهای من» وقتی
   خالی است یک مستطیلِ بلندِ تهی شود با یک تیکِ ریز وسطش. کارت باید
   به اندازه‌ی محتوایش باشد. */
.grid { display: grid; gap: var(--sp-4); align-items: start; }
.grid--half { grid-template-columns: 1.6fr 1fr; }
/* ⚠ فرم روی نمایشگرِ عریض نباید تا آخر کش بیاید. ورودیِ ۸۳۰ پیکسلی
   برای یک تاریخِ ده‌کاراکتری هم زشت است هم سخت‌خوان: چشم باید از
   برچسبِ سمتِ راست تا انتهای کادر سفر کند و برگردد. سقفِ ۶۲۰
   پیکسل همان بازه‌ای است که فرم‌های خوب در آن می‌مانند. */
.grid--form { grid-template-columns: minmax(0, 620px) minmax(230px, 1fr); }
@media (max-width: 1000px) { .grid--form { grid-template-columns: 1fr; } }
.grid--kpi  { grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }
.row2 { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-4); align-items: start; }
@media (max-width: 1100px) { .grid--half, .row2 { grid-template-columns: 1fr; } }

/* ═════════════════════════════════════════════════ دکمه‌ها */
.btn {
  height: 32px;
  border: 1px solid var(--line-2);
  background: var(--surface); color: var(--fg);
  border-radius: var(--r-sm);
  padding-inline: 12px;
  font-family: var(--font); font-size: var(--t-xs); font-weight: 500;
  display: inline-flex; align-items: center; gap: 6px;
  cursor: pointer; text-decoration: none;
  transition: border-color .15s ease, background-color .15s ease,
              box-shadow .15s ease;
}
.btn:hover { border-color: var(--accent); background: var(--surface-2);
             text-decoration: none; }
.btn--primary { background: var(--accent); border-color: var(--accent);
                color: var(--on-accent); font-weight: 600;
                box-shadow: var(--z-1); }
.btn--primary:hover { background: var(--accent); filter: brightness(1.08);
                      box-shadow: var(--z-2); }
.btn--ghost { border-color: transparent; color: var(--fg-muted); }
.btn--ghost:hover { background: var(--surface-2); color: var(--fg); }
.btn--sm { height: 27px; font-size: var(--t-2xs); padding-inline: 10px; }

/* ═══════════════════════════════════════════════ نشان‌ها */
.badge {
  display: inline-flex; align-items: center;
  height: 22px; border-radius: var(--r-full); padding-inline: 9px;
  font-size: var(--t-2xs);
  background: var(--surface-2); border: 1px solid var(--line-2); color: var(--fg-muted);
}
.badge--ok   { background: var(--ok-bg);   border-color: var(--ok-line);   color: var(--accent); }
.badge--warn { background: var(--warn-bg); border-color: var(--warn-line); color: var(--warn); }
.badge--bad  { background: var(--bad-bg);  border-color: var(--bad-line);  color: var(--bad); }
.badge--info { background: var(--info-bg); border-color: var(--info-line); color: var(--info); }
.badge--mute { color: var(--fg-faint); }

/* ═══════════════════════════════════════════════ جدول‌ها */
.table-wrap { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; font-size: var(--t-sm); }
thead th {
  text-align: start;
  font-size: var(--t-2xs); font-weight: 600; color: var(--fg-muted);
  letter-spacing: .02em;
  padding: 8px var(--sp-3);
  border-bottom: 1px solid var(--line-2);
  background: var(--surface-2);
  position: sticky; inset-block-start: 0; z-index: 1;
  white-space: nowrap;
}
/* ⚠ تراکم: ۹ پیکسل بالا و پایین یعنی سطرِ ۴۰ پیکسلی. در دفترچه‌ی
   ۲۷٬۷۰۳ نفره این یعنی نصفِ صفحه فضای خالیِ بینِ سطرهاست و کاربر
   برای دیدنِ ده نفر باید اسکرول کند. ۶ پیکسل سطر را به ۳۲ می‌رساند
   — همچنان بالای هدفِ لمسیِ ۴۴ نیست، پس در صفحه‌ی لمسی بازتر
   می‌شود (پایین‌تر). */
tbody td { padding: 6px var(--sp-3); border-bottom: 1px solid var(--line); }
tbody tr { transition: background-color .12s ease; }
tbody tr:hover { background: var(--surface-2); }
tbody tr:last-child td { border-bottom: 0; }

/* ⚠ ستونِ اول (نام) همه‌ی فضای اضافه را می‌گیرد و بقیه به اندازه‌ی
   محتوایشان جمع می‌شوند. بی‌این، عرض بینِ شش ستون **مساوی** پخش
   می‌شد و ستونِ «کد ملی» به‌اندازه‌ی ستونِ نام جا می‌گرفت، با یک
   دشتِ خالی وسطِ هر سطر. */
table.t th:not(:first-child),
table.t td:not(:first-child) { width: 1%; white-space: nowrap; }
table.t th:first-child,
table.t td:first-child { width: auto; }

@media (pointer: coarse) {
  tbody td { padding: 11px var(--sp-3); }
}

.empty { padding: var(--sp-6); text-align: center; color: var(--fg-muted); font-size: var(--t-sm); }

/* ═════════════════════════════════════════ نوارِ نسبت */
/* ⚠ تعریفِ `.bar` **یکی** است و پایین‌ترِ همین فایل. تا جلسه ۲۳
   دو تعریف داشت (اینجا و بخشِ پروژه‌ها) و دومی اولی را می‌پوشاند —
   دو تعریفِ یک کلاس یعنی روزی کسی اولی را عوض می‌کند و هیچ اثری
   نمی‌بیند. تستِ `test_the_progress_bar_is_a_block` هم روی تعریفِ
   اول می‌افتاد و درست را نمی‌دید. */
.bar__fill { height: 100%; background: var(--accent); border-radius: var(--r-full); }
.bar__fill--info { background: var(--info); }
.bar__fill--bad  { background: var(--bad); }

/* ══════════════════════════════ KPIِ صفحه‌های دیگر */
.kpi {
  background: var(--surface); border: 1px solid var(--line);
  border-top: 2px solid var(--line-2);
  border-radius: var(--r-sm); padding: 10px 12px 12px;
  display: block; color: var(--fg);
}
a.kpi:hover { text-decoration: none; border-color: var(--accent); }
.kpi--accent { border-top-color: var(--accent); }
.kpi--ok     { border-top-color: var(--accent); }
.kpi--warn   { border-top-color: var(--warn); }
.kpi--bad    { border-top-color: var(--bad); }
.kpi__label { font-size: var(--t-3xs); color: var(--fg-muted); letter-spacing: .03em; }
.kpi__value { font-size: var(--n-xl); font-weight: 600; line-height: 1.2;
              direction: ltr; unicode-bidi: isolate; font-variant-numeric: tabular-nums; }
.kpi__hint  { font-size: var(--t-2xs); color: var(--fg-faint); }

/* ═══════════════════════════════════════════════════ فرم */
.field { margin-bottom: var(--sp-4); }
.field > label { display: block; font-size: var(--t-xs); color: var(--fg-muted); margin-bottom: 4px; }
.field input, .field select, .field textarea {
  width: 100%; min-height: 34px;
  background: var(--surface); border: 1px solid var(--line-2);
  border-radius: var(--r-sm); color: var(--fg);
  font-family: var(--font); font-size: var(--t-sm);
  padding: 6px 10px;
}
.field input:focus, .field select:focus, .field textarea:focus { outline: none; border-color: var(--accent); }
.field textarea { line-height: 1.8; resize: vertical; }
.field__hint { font-size: var(--t-2xs); color: var(--fg-faint); margin-top: 4px; }
.field__err  { font-size: var(--t-2xs); color: var(--bad); margin-top: 4px; }
.check { display: flex; align-items: center; gap: var(--sp-2); }
.check input { width: auto; min-height: auto; }

/* ═══════════════════════════════════════════════════ پیام */
.alert {
  border-radius: var(--r); padding: 11px var(--sp-4);
  font-size: var(--t-xs);
  border: 1px solid var(--line-2); background: var(--surface-2);
  margin-bottom: var(--sp-3);
  /* آیکون و متن کنارِ هم، با تراز از بالا تا متنِ چندخطی هم درست بماند */
  display: flex; align-items: flex-start; gap: 8px; line-height: 1.7;
}
.alert > svg { flex-shrink: 0; margin-block-start: 2px; }
.alert--ok   { background: var(--ok-bg);   border-color: var(--ok-line);   color: var(--accent); }
.alert--warn { background: var(--warn-bg); border-color: var(--warn-line); color: var(--warn); }
.alert--bad  { background: var(--bad-bg);  border-color: var(--bad-line);  color: var(--bad); }
.alert--info { background: var(--info-bg); border-color: var(--info-line); color: var(--info); }

.note {
  display: flex; gap: 9px;
  padding: 10px 12px; border-radius: var(--r-sm);
  background: var(--bad-bg); border: 1px solid var(--bad-line);
  color: var(--bad); font-size: var(--t-2xs); line-height: 1.65;
}
.note--ok   { background: var(--ok-bg);   border-color: var(--ok-line);   color: var(--accent); }
.note--info { background: var(--info-bg); border-color: var(--info-line); color: var(--info); }
.note svg { flex-shrink: 0; margin-top: 2px; }

/* ═════════════════════════════════ پالتِ فرمان (Ctrl+K) */
.palette-back {
  position: fixed; inset: 0;
  background: rgba(0, 0, 0, .55);
  display: flex; align-items: flex-start; justify-content: center;
  padding-top: 12vh; z-index: 100;
}
.palette-back[hidden] { display: none !important; }

.palette {
  width: min(620px, 92vw);
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow);
  overflow: hidden;
}
.palette input {
  width: 100%; height: 48px;
  border: 0; border-bottom: 1px solid var(--line);
  background: none; color: var(--fg);
  font-family: var(--font); font-size: var(--t-lg);
  padding-inline: var(--sp-4);
}
.palette input:focus { outline: none; }
.palette__list { max-height: 46vh; overflow-y: auto; padding: 6px; }
.palette__item {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 10px; border-radius: var(--r-sm);
  font-size: var(--t-sm); color: var(--fg); text-decoration: none;
}
.palette__item:hover, .palette__item--on { background: var(--surface-2); text-decoration: none; }
.palette__item small { margin-inline-start: auto; color: var(--fg-faint); font-size: var(--t-2xs); }
.palette__empty { padding: var(--sp-5); text-align: center; color: var(--fg-faint); font-size: var(--t-xs); }

/* ══════════════════════════════════════════ صفحه‌ی ورود */
/* ═══════════════════════════════════════════ صفحه‌ی ورود

   بازطراحیِ جلسه ۱۷ — بر پایه‌ی سه الگویی که در شش قالبِ مرجع دیده
   شد: کارتِ وسطِ صفحه (Vex · Dreams ERP) و **صفحه‌ی دوتکه**
   (MaterialPro). دوتکه انتخاب شد چون:

   * صفحه‌ی ورود تنها جایی است که کاربرِ بیرونی می‌بیند؛ جای هویت است
   * کارتِ ۴۰۰ پیکسلی وسطِ صفحه‌ی خالی، «موقتی» به نظر می‌رسد

   ⚠ و یک قاعده‌ی سخت: **هیچ داده‌ای روی این صفحه نیست.** نه تعدادِ
   کارمند، نه آمار. صفحه‌ی پیش از ورود نباید چیزی از داخل لو بدهد.
   پس پنلِ کنار فقط هویت است: نشان، نام، و نامِ آلمانیِ اتاق.
   ─────────────────────────────────────────────────────────── */
.auth {
  min-height: 100vh;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}
/* روی صفحه‌ی باریک پنلِ هویت می‌رود و فرم تمامِ عرض را می‌گیرد */
@media (max-width: 900px) {
  .auth { grid-template-columns: 1fr; }
  .auth__aside { display: none; }
}

.auth__main {
  display: flex; flex-direction: column; justify-content: center;
  padding: var(--sp-6);
  background: var(--bg);
}
.auth__form { width: min(360px, 100%); margin-inline: auto; }

.auth__brand { display: flex; align-items: center; gap: 11px; margin-bottom: var(--sp-6); }
.auth__title { font-size: var(--t-2xl); font-weight: 600; letter-spacing: -.02em; }
.auth__sub   { font-size: var(--t-xs); color: var(--fg-muted); margin-top: 4px;
               margin-bottom: var(--sp-5); }

/* ورودیِ رمز + کلیدِ دیدن — کلید داخلِ محفظه‌ی کنترل لنگر می‌اندازد،
   نه داخلِ .field (که برچسب و راهنما هم در آن است). همان درسِ
   آیکونِ تقویم در جلسه ۱۴. */
.auth__reveal {
  position: absolute; inset-block-start: 50%; inset-inline-end: 8px;
  transform: translateY(-50%);
  width: 26px; height: 26px; border: 0; background: none;
  color: var(--fg-faint); cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--r-sm);
}
.auth__reveal:hover { color: var(--fg); background: var(--surface-2); }

.auth__submit { inline-size: 100%; height: 38px; margin-block-start: var(--sp-4);
                justify-content: center; }
.auth__foot { margin-block-start: var(--sp-5); font-size: var(--t-xs);
              color: var(--fg-muted); line-height: 1.9; }

/* ── پنلِ هویت
   ⚠ نقشِ تزئینی با CSS ساخته می‌شود، نه با تصویر: یک فایلِ تصویرِ
   دیگر یعنی یک درخواستِ دیگر و یک چیزِ دیگر که ممکن است نیاید. */
.auth__aside {
  position: relative; overflow: hidden;
  display: flex; flex-direction: column; justify-content: flex-end;
  padding: var(--sp-6);
  /* ⚠ زمینهٔ تیره زیرِ تصویر می‌ماند تا اگر تصویر نیامد، متنِ روشن
     روی سفید نیفتد. */
  background: #0a0d12;
  border-inline-start: 1px solid var(--line);
}
/* ⚠ تصویر با `object-fit: cover` و لنگرِ **بالا**: مرکزِ ثقلِ این
   نقاشی گل‌های بالای دسته است و برشِ وسط سرِ گل‌ها را می‌زد. */
.auth__img {
  position: absolute; inset: 0;
  inline-size: 100%; block-size: 100%;
  object-fit: cover; object-position: 50% 22%;
}
/* ⚠ پرده لازم است، تزئین نیست: متنِ سفید روی یک نقاشیِ پرجزئیات
   جایی خوانا و جایی نه — و «جایی نه» یعنی کنتراستِ زیرِ ۴٫۵:۱ در
   همان‌جایی که نامِ سامانه نوشته شده. گرادیانِ عمودی تیرگی را جایی
   می‌گذارد که متن هست. */
.auth__veil {
  position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(180deg,
      rgba(6, 9, 13, .30) 0%,
      rgba(6, 9, 13, .74) 46%,
      rgba(6, 9, 13, .92) 100%);
}
/* ⚠ متنِ این پنل در **هر دو** حالتِ نمایش روشن است، چون زیرش یک
   تصویرِ تیره است نه توکنِ زمینه. استفاده از `--fg` اینجا یعنی در
   حالتِ روشن، متنِ تیره روی نقاشیِ تیره — نامرئی. */
.auth__aside { color: #f2f5f8; }
.auth__hero { position: relative; text-align: center;
              width: min(340px, 100%); margin-inline: auto;
              padding-block-end: var(--sp-6); }
.auth__hero .auth__org { color: rgba(242, 245, 248, .82); }
.auth__hero .auth__org--de { color: rgba(242, 245, 248, .58); }
.auth__hero .auth__tag { color: rgba(242, 245, 248, .74);
                         border-block-start-color: rgba(242, 245, 248, .18); }
.auth__mark {
  width: 62px; height: 62px; border-radius: var(--r-lg);
  background: var(--accent); color: var(--on-accent);
  display: flex; align-items: center; justify-content: center;
  font-size: var(--t-4xl); font-weight: 700;
  margin-inline: auto; margin-bottom: var(--sp-4);
  box-shadow: var(--z-2);
}
.auth__name { font-size: var(--t-3xl); font-weight: 700; letter-spacing: -.02em; }
.auth__org  { font-size: var(--t-md); color: var(--fg-muted); margin-top: 6px; }
.auth__org--de { font-size: var(--t-xs); color: var(--fg-faint); margin-top: 3px;
                 direction: ltr; }
.auth__tag {
  margin-block-start: var(--sp-6); padding-block-start: var(--sp-4);
  border-block-start: 1px solid var(--line);
  font-size: var(--t-xs); color: var(--fg-muted); line-height: 1.9;
}

/* ══════════════════════════════════════════════════ چاپ */
@media print {
  .topbar, .side, .palette-back, .btn, .iconbtn { display: none !important; }
  body { background: #fff; color: #000; }
  .card, .tile, .chart, .kpi { border-color: #ccc; break-inside: avoid; }
  .page { padding: 0; }
}

/* ═══════════════════════════════════ تابلوِ فرمان (جهتِ C)

   نمای متراکم: همه‌چیز در یک نگاه، بدونِ اسکرول. عمداً **صفحه‌ی
   جدا** است و صفحه‌ی خانه نشد — کارمندِ منابع انسانی روزی ده بار
   خانه را باز می‌کند و اگر همه‌چیز هم‌زمان فریاد بزند اشتباه می‌زند.
   ─────────────────────────────────────────────────────────── */
.board { display: flex; flex-direction: column; gap: 10px; }

.board .tiles { grid-template-columns: repeat(8, 1fr); }
@media (max-width: 1250px) { .board .tiles { grid-template-columns: repeat(4, 1fr); } }
@media (max-width: 700px)  { .board .tiles { grid-template-columns: repeat(2, 1fr); } }
.board .tile { padding: 8px 10px 10px; }
.board .tile__value { font-size: var(--n-xl); }
.board .tile__label { font-size: var(--t-3xs); }

.board-grid { display: grid; grid-template-columns: 1.25fr 1fr 1fr; gap: 10px; }
@media (max-width: 1250px) { .board-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 820px)  { .board-grid { grid-template-columns: 1fr; } }

.panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  display: flex; flex-direction: column;
  min-width: 0; overflow: hidden;
}
.panel__head {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2);
  padding: 7px 11px;
  border-bottom: 1px solid var(--line);
  font-size: var(--t-2xs); font-weight: 600;
}
.panel__body { padding: 9px 11px; display: flex; flex-direction: column; gap: 7px; }
.panel__body--flush { padding: 0; }

/* ردیفِ کلید-مقدار — نانِ روزانه‌ی این صفحه */
.kv { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2); font-size: var(--t-2xs); }
.kv > span:first-child { color: var(--fg-muted); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kv b { font-weight: 500; }
.kv--ok b   { color: var(--accent); }
.kv--warn b { color: var(--warn); }
.kv--bad b  { color: var(--bad); }

/* میله‌ی نسبت با برچسبِ درصد */
.meter { display: flex; flex-direction: column; gap: 3px; }
.meter__top { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-2); font-size: var(--t-2xs); }
.meter__top span:first-child { color: var(--fg-muted); min-width: 0; overflow: hidden;
                               text-overflow: ellipsis; white-space: nowrap; }
.meter .bar { height: 5px; }

/* لاگِ رویداد — تک‌فاصله، ریز، بی‌سروصدا */
.log { display: flex; flex-direction: column; }
.log__row {
  display: flex; align-items: center; gap: 8px;
  padding: 5px 11px;
  border-bottom: 1px solid var(--line);
  font-size: var(--t-2xs);
}
.log__row:last-child { border-bottom: 0; }
.log__time { font-family: var(--mono); direction: ltr; unicode-bidi: isolate;
             font-variant-numeric: tabular-nums; color: var(--fg-faint); flex-shrink: 0; }
.log__name { flex-grow: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.log__tag  { font-size: var(--t-3xs); padding: 0 6px; border-radius: var(--r-full); flex-shrink: 0; }
.log__tag--in  { background: var(--ok-bg);  color: var(--accent); }
.log__tag--out { background: var(--idle);   color: var(--fg-muted); }
.log__tag--bad { background: var(--bad-bg); color: var(--bad); }

/* فهرستِ حاضرها — متراکم، دو ستون اگر جا بود */
.who { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 0; }
.who__row {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 11px; border-bottom: 1px solid var(--line);
  font-size: var(--t-2xs); min-width: 0;
}
.who__name { flex-grow: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.who__time { font-family: var(--mono); direction: ltr; unicode-bidi: isolate;
             font-variant-numeric: tabular-nums; color: var(--fg-muted); flex-shrink: 0; }

/* ═══════════════════════════════ تقویمِ شمسی و ساعت‌گیر

   ⚠ ظاهرِ ویجت به static/diba/jalali.css منتقل شد، چون **پنلِ ادمین
   هم همان ویجت را لازم دارد** و app.css را نمی‌خواند. یک فایل، دو
   مصرف‌کننده — تا هیچ‌وقت دو نسخه‌ی واگرا نداشته باشیم.
   ─────────────────────────────────────────────────────────── */

/* ═══════════════════════════════ فرم — نسخه‌ی آراسته

   ورودی‌های خامِ جنگو («---------» و چک‌باکسِ سیستمی) زشت‌اند و
   هم‌خانواده‌ی بقیه‌ی صفحه نیستند. اینجا همان کلاس‌ها بازآرایی
   می‌شوند.
   ─────────────────────────────────────────────────────────── */
.form { display: flex; flex-direction: column; gap: 2px; }

.field { margin-bottom: 0; padding-block: 8px; }
.field + .field { border-top: 1px solid var(--line); }
.field > label {
  display: block; font-size: var(--t-xs); color: var(--fg-muted);
  margin-bottom: 5px; font-weight: 500;
}
.field--req > label::after {
  content: '*'; color: var(--bad); margin-inline-start: 3px; font-size: var(--t-sm);
}

/* select — فلشِ خودمان، نه فلشِ سیستمی

   ⚠ نسخه‌ی اول با دو `linear-gradient` مثلث می‌ساخت. روی صفحه به
   دو نویسه‌ی ناخوانا شبیه شد («۳۹»). SVGِ درون‌خطی به‌عنوان
   data-URI هم دقیق است، هم رنگش را از توکن می‌گیرد. */
.field select, select.sel {
  appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%238d97a5' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: left 12px center;
  background-size: 10px 6px;
  padding-inline-start: 30px;
  cursor: pointer;
}
html[dir="ltr"] .field select, html[dir="ltr"] select.sel {
  background-position: right 12px center;
}
.field select:hover { border-color: var(--line-2); }

/* ⚠⚠ **گزینه‌های کشویی باید رنگِ صریح داشته باشند.**

   باگِ واقعیِ جلسه ۲۸، از عکسِ پرسا: کشویی «سالن» باز می‌شد و
   **هیچ‌چیز دیده نمی‌شد** — یک مستطیلِ سیاه با یک نوارِ خاکستری.
   گزینه‌ها آنجا بودند؛ متنشان نامرئی بود.

   دلیلش این است که فهرستِ بازشوی `<select>` را **سیستم‌عامل**
   می‌کشد، نه صفحه. تا وقتی `<select>` ظاهرِ پیش‌فرض دارد، مرورگر
   از `color-scheme` می‌فهمد که تیره است. ولی ما روی `<select>`
   `appearance: none` و `background` گذاشته‌ایم (برای فلشِ سفارشی)
   و همان لحظه این پیوند می‌شکند: پس‌زمینه از ما می‌آید و رنگِ متن
   از پیش‌فرضِ سیستم — مشکی روی مشکی.

   ⚠ و این فقط «زشت» نبود: کاربر فکر می‌کرد **سالنی ثبت نشده**.
     در حالی که هر پنج سالن آنجا بودند.

   ⚠ رنگ‌ها از توکن می‌آیند، پس در هر دو حالتِ روشن و تیره درست
     می‌مانند و یک تعریفِ دوم لازم ندارند. */
.field select option,
select.sel option,
.field select optgroup,
select.sel optgroup {
  background-color: var(--surface);
  color: var(--fg);
}

/* ورودیِ تاریخ و ساعت — با آیکون

   ⚠ آیکون باید در محفظه‌ی **خودِ کنترل** لنگر بیندازد، نه در کلِ
   فیلد. اگر به `.field` بچسبد، برچسب و راهنما هم داخلِ همان جعبه‌اند
   و آیکون زیرِ ورودی می‌افتد — که یک بار افتاد. */
.ctl { position: relative; display: block; }
.ctl input { padding-inline-end: 36px; }
/* ⚠ ورودیِ تاریخ/ساعت `direction: ltr` دارد (کلاسِ .num)، پس متنش
   چپ‌چین می‌شود و درست روی آیکونِ سمتِ چپ می‌نشیند. `text-align: end`
   ارقام را به سمتِ راست می‌برد و برخورد را برمی‌دارد. */
.ctl input.num { text-align: end; }
.ctl .field__icon {
  position: absolute;
  inset-inline-end: 11px;
  inset-block-start: 50%;
  transform: translateY(-50%);
  color: var(--fg-faint);
  pointer-events: none;
  display: flex;
}
.ctl input:focus + .field__icon { color: var(--accent); }
.ctl input[data-jdate], .ctl input[data-jtime] { cursor: pointer; }

/* کلید کشویی به‌جای چک‌باکسِ سیستمی */
/* ⚠ دو تلهٔ این کلید، هر دو دیده شد:

   ۱. `position: relative` اجباری است — ورودیِ مخفی `absolute` است و
      بی‌والدِ نسبی نسبت به **کلِ صفحه** جا می‌گیرد.

   ۲. انتخابگر باید `.field > label.switch` باشد، نه `.switch` تنها.
      قاعده‌ی `.field > label { display: block }` تخصصِ (۰,۱,۱) دارد و
      `.switch` تخصصِ (۰,۱,۰) — پس `block` برنده می‌شد، کلید از حالتِ
      flex می‌افتاد و روی برچسب سوار می‌شد. */
.switch,
.field > label.switch {
  display: flex; align-items: center; gap: 10px;
  cursor: pointer; position: relative;
  margin-bottom: 0;
  font-size: var(--t-xs); font-weight: 400; color: var(--fg);
}
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
/* ⚠ رنگِ خاموشِ اولیه `--idle` بود که در حالتِ تیره تقریباً همرنگِ
   سطحِ کارت است — نتیجه: فقط دستگیره دیده می‌شد و کلید شبیهِ یک
   لکه‌ی سفید بود، نه کلید. */
.switch__track {
  width: 36px; height: 20px; flex-shrink: 0;
  border-radius: var(--r-full);
  background: var(--line-2);
  border: 1px solid var(--line-2);
  position: relative;
  transition: background .15s, border-color .15s;
}
.switch__track::after {
  content: '';
  position: absolute;
  inset-block-start: 2px; inset-inline-start: 2px;
  width: 14px; height: 14px; border-radius: 50%;
  background: var(--fg-muted);
  transition: inset-inline-start .15s, background .15s;
}
.switch input:checked + .switch__track {
  background: var(--accent); border-color: var(--accent);
}
.switch input:checked + .switch__track::after {
  inset-inline-start: 18px; background: var(--on-accent);
}
.switch input:focus-visible + .switch__track { outline: 2px solid var(--accent); outline-offset: 2px; }
.switch__label { font-size: var(--t-xs); }

.form__actions {
  display: flex; gap: 8px; align-items: center;
  margin-top: var(--sp-4); padding-top: var(--sp-4);
  border-top: 1px solid var(--line);
}

/* ═══════════════════════════════ حالتِ خالی، با شخصیت */
.empty {
  padding: var(--sp-6) var(--sp-4);
  text-align: center;
  color: var(--fg-faint);
  font-size: var(--t-sm);
  display: flex; flex-direction: column; align-items: center; gap: 10px;
}
.empty__mark {
  width: 40px; height: 40px; border-radius: 50%;
  background: var(--surface-2);
  border: 1px solid var(--line);
  display: flex; align-items: center; justify-content: center;
  color: var(--fg-faint);
}
.empty b { color: var(--fg-muted); font-weight: 500; }

/* ═══════════════════════════════ ریزه‌کاری‌های حرکت و حال */
.tile, .card, .panel, .btn, .side__item, .strip { transition: border-color .12s, background .12s; }
.tile:hover  { border-color: var(--line-2); }
.task:hover  { background: var(--surface-2); }
.cell        { transition: transform .08s; }
.cell:hover  { transform: scaleY(1.35); }
.chart__bar  { transition: filter .1s; }
.chart__bar:hover { filter: brightness(1.25); }

/* عددِ بزرگ نباید در حالتِ روشن بی‌جان شود: وزنِ ۲۰۰ روی سفید محو
   است، پس در روشن کمی سنگین‌تر می‌شود. */
:root[data-theme='light'] .tile__value,
:root[data-theme='light'] .kpi__value { font-weight: 300; }
@media (prefers-color-scheme: light) {
  :root:not([data-theme='dark']) .tile__value,
  :root:not([data-theme='dark']) .kpi__value { font-weight: 300; }
}

/* سرِ کاشی: برچسب و آیکونِ ریز کنارِ هم */
.tile__top { display: flex; align-items: center; justify-content: space-between; gap: 6px; }
.tile__ico { color: var(--fg-faint); display: flex; flex-shrink: 0; }
a.tile:hover .tile__ico { color: var(--accent); }

/* نوارِ ریزِ روندِ زیرِ کاشی */
.spark { display: flex; align-items: flex-end; gap: 1px; height: 14px; margin-top: 7px; }
.spark__b { flex-grow: 1; background: var(--line-2); border-radius: 1px; min-height: 1px; }
.spark__b--ok { background: var(--accent); }
.spark__b--warn { background: var(--warn); }
.spark__b--bad { background: var(--bad); }

/* ⚠ روزِ بی‌داده نباید میله‌ی توپُر باشد: نمودارِ ماهی که دادهٔ ترددش
   نیمه‌تمام است شبیهِ نمودارِ شکسته می‌شود. توخالی و کم‌رنگ یعنی
   «نمی‌دانم»، توپُرِ خاکستری یعنی «صفر بود» — و این دو یکی نیستند. */
.chart__bar--off {
  background: transparent;
  border-block-end: 2px dotted var(--line-2);
  border-radius: 0;
  min-height: 2px;
}

/* ═══════════════════════════ صیقلِ نهایی — چک‌لیستِ ui-ux-pro-max

   سه چیز که نداشتیم و در چک‌لیست بود:
   ۱. احترام به «حرکتِ کمتر» (prefers-reduced-motion)
   ۲. هدفِ لمسیِ ۴۴ پیکسل روی دستگاهِ لمسی
   ۳. رفتارِ درست زیرِ ۷۶۸ پیکسل — ریلِ کناری روی موبایل کار نمی‌کند
   ─────────────────────────────────────────────────────────── */

/* ── ۱ — حرکتِ کمتر. اجباری است، نه تزئینی: کاربرِ حساس به حرکت
      با انیمیشن دچار سرگیجه می‌شود. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
  .jcal { animation: none; }
}

/* ── ۲ — هر چیزِ کلیک‌شدنی باید مثلِ کلیک‌شدنی به نظر برسد */
.tile[href], .kpi[href], .btn, .iconbtn, .side__item, .cmd,
.palette__item, .switch, summary, .task__acts a {
  cursor: pointer;
}

/* روی دستگاهِ لمسی هدف‌ها بزرگ‌تر می‌شوند — روی ماوس دست‌نخورده،
   چون در یک صفحه‌ی متراکمِ ERP فضا گران است. */
@media (pointer: coarse) {
  .btn { height: 40px; }
  .btn--sm { height: 34px; }
  .iconbtn, .side__item { min-width: 44px; height: 44px; }
  .jcal__day { height: 38px; }
  .switch__track { width: 44px; height: 26px; }
  .switch__track::after { width: 20px; height: 20px; }
  .switch input:checked + .switch__track::after { inset-inline-start: 20px; }
}

/* ── ۳ — زیرِ ۷۶۸: ریلِ عمودی به نوارِ پایین تبدیل می‌شود.
      قاعده‌ی «حداکثر ۵ مقصد در نوارِ پایین» رعایت شده: چهار موردِ
      اصلی می‌مانند و بقیه از پالتِ فرمان (Ctrl+K یا «/») در دسترس‌اند. */
@media (max-width: 767px) {
  :root { --topbar-h: 52px; }

  .topbar { gap: 10px; padding-inline: 12px; }
  .brand__sub, .topbar__meta { display: none; }

  /* ⚠ کلیدِ «؟» روی گوشی می‌رود، و دو دلیل دارد:
     ۱. گوشی صفحه‌کلید ندارد، پس راهنمای میان‌بر بی‌مصرف است؛
     ۲. همین سی پیکسل بود که نوارِ بالا را از ۳۷۵ پیکسل بیرون
        می‌زد و **همه‌ی چهارده صفحه** را افقی می‌لولاند. با
        اندازه‌گیریِ واقعی پیدا شد، نه با خواندنِ کد. */
  #keys-btn { display: none; }
  .cmd { max-width: none; }
  .cmd__hint { font-size: var(--t-xs); }
  /* ⚠ کلیدهای Ctrl و K روی موبایل بی‌معنا هستند (صفحه‌کلیدی در کار
     نیست) و ۵۰ پیکسل از نوارِ تنگ می‌گیرند. */
  .cmd .kbd { display: none; }
  .kbd { display: none; }

  .body-row { flex-direction: column; }

  /* ⚠ روی موبایل نوارِ کناری **نوارِ پایین** می‌شود. منوی کشویی
     روی صفحه‌ی کوچک یعنی دو کلیک برای هر مقصد؛ نوارِ پایین یک
     کلیک است و انگشت هم به آن می‌رسد. */
  .side {
    position: fixed;
    inset-inline: 0;
    inset-block-end: 0;
    --side-w: auto;
    width: auto; height: auto;
    border-inline-start: 0;
    border-block-start: 1px solid var(--line);
    padding-block-end: max(0px, env(safe-area-inset-bottom));
    z-index: 30;
  }
  .side__scroll {
    flex-direction: row; justify-content: space-around;
    overflow: visible; padding: 5px 4px;
  }
  .side__group { display: contents; }
  .side__group + .side__group { margin-block-start: 0; }
  .side__head, .side__label, .side__user, .side__soon { display: none; }
  .side__item { width: 44px; justify-content: center; padding-inline: 0; }
  /* ⚠ روی نوارِ پایین فقط مقصدهای **اصلی**؛ بقیه از پالتِ فرمان
     (Ctrl+K). قبلاً «دو گروهِ اول» بود و آن با ۱۰ گروه از کار
     افتاد: ده آیکون در ۳۷۵ پیکسل جا نمی‌شود و منو دوردیفه شد.
     حالا خودِ مقصد می‌گوید اصلی هست یا نه — `primary` در
     `core/navigation.py`. */
  .side__item:not(.side__item--primary) { display: none; }
  .side__item--on::before {
    inset-inline-end: auto;
    inset-block: auto;
    inset-block-start: -5px;
    inset-inline-start: 50%;
    transform: translateX(50%);
    width: 18px; height: 2px;
  }

  .page { padding: var(--sp-4) var(--sp-3) 76px; gap: var(--sp-3); }
  .tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* ⚠ سرِ صفحه روی موبایل ستونی می‌شود، وگرنه عنوان و دکمه‌ها روی
     ۳۷۵ پیکسل کنارِ هم فشرده می‌شوند و هیچ‌کدام خوانده نمی‌شوند. */
  .phead { flex-direction: column; align-items: stretch; gap: var(--sp-3); }
  .phead__acts { margin-block-start: 0; }
  .phead__stats { justify-content: flex-start; }
  .matrix__nums { width: 128px; }
  .matrix__nums .c1 { width: 52px; }
  .matrix__nums .c2, .matrix__nums .c3 { width: 38px; }
  .matrix__name { width: 104px; }
  .jcal { width: min(300px, calc(100vw - 24px)); }
}

/* ۳۷۵ پیکسل — باریک‌ترین چیزی که واقعاً استفاده می‌شود */
@media (max-width: 400px) {
  .tiles { grid-template-columns: 1fr; }
  .strip { flex-wrap: wrap; }
}

/* ── انتقال‌های نرم‌تر (۱۵۰ تا ۳۰۰ میلی‌ثانیه، طبقِ چک‌لیست) */
.tile, .card, .panel, .btn, .side__item, .strip, .task, .palette__item {
  transition: border-color .18s ease, background-color .18s ease,
              color .18s ease;
}

/* ── نوارِ اسکرول، هم‌رنگِ پوسته — نه سفیدِ سیستمی وسطِ صفحه‌ی تیره */
* { scrollbar-width: thin; scrollbar-color: var(--line-2) transparent; }
*::-webkit-scrollbar { width: 9px; height: 9px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
  background: var(--line-2); border-radius: 999px;
  border: 2px solid var(--bg);
}
*::-webkit-scrollbar-thumb:hover { background: var(--fg-faint); }

/* ── انتخابِ متن با رنگِ برند */
::selection { background: var(--accent); color: var(--on-accent); }

/* ═══════════════════════════════════════════════════════════════════
   حرکت — سیستمِ انیمیشن
   ═══════════════════════════════════════════════════════════════════

   ⚠ این بخش با **بررسیِ مستقیمِ Eleventra** نوشته شد (جلسه ۱۸).
   شکلِ کی‌فریم‌ها، مدت‌ها و منحنی‌های آن عیناً از همان‌جا خوانده شد،
   چون همان چیزی است که پرسا «smooth» می‌نامد:

     fadeInPage   ۰٫۴۵s ease-out        — ورودِ کلِ صفحه
     cardEnter    ۰٫۴۵s ease-out        — کارت، با کمی بزرگ‌شدن
     slideDown    ۰٫۴۰s ease-out        — از بالا به پایین
     listReveal   ۰٫۳۵s ease-out        — سطرِ فهرست و جدول
     bentoPop     ۰٫۳۲s cubic-bezier(.22,.68,0,1.2)  — با کمی جهش
     progressFill ۰٫۸۰s cubic-bezier(.4,0,.2,1)      — پر شدنِ میله

   ⚠ منحنیِ bentoPop از ۱ عبور می‌کند (۱٫۲) — همین «جهشِ» ریز است که
   حرکت را زنده می‌کند. اگر ease-out ساده بگذاری، درست است ولی
   بی‌روح.

   ⚠ پله‌ای‌شدن با `--i` روی خودِ عنصر انجام می‌شود، نه با کلاسِ
   شماره‌دار (Eleventra کلاسِ `k1`…`k8` داشت که یعنی سقفِ ثابت).
   `style="--i:3"` هیچ سقفی ندارد.

   ⚠ همه‌ی این‌ها زیرِ بلوکِ `prefers-reduced-motion`ِ بالای همین فایل
   خودبه‌خود خنثی می‌شوند. آن بلوک تزئینی نیست: کاربرِ حساس به حرکت
   با انیمیشن دچار سرگیجه می‌شود.
   ─────────────────────────────────────────────────────────────────── */

@keyframes dibaPageIn   { from { opacity: 0; transform: translateY(12px); } }
@keyframes dibaCardIn    { from { opacity: 0; transform: translateY(20px) scale(.98); } }
@keyframes dibaDown      { from { opacity: 0; transform: translateY(-16px); } }
@keyframes dibaListIn    { from { opacity: 0; transform: translateY(10px); } }
@keyframes dibaPop       { from { opacity: 0; transform: translateY(10px) scale(.97); } }
@keyframes dibaFill      { from { width: 0; } }
@keyframes dibaGrow      { from { transform: scaleY(0); } }
@keyframes dibaShimmer   { from { background-position: 200% 0; } to { background-position: -200% 0; } }
@keyframes dibaPulse     { 50% { opacity: .35; transform: scale(.82); } }

/* ── کلاس‌های حرکت. `--i` شمارهٔ عنصر است و پله را می‌سازد. */
.anim-page { animation: dibaPageIn .45s ease-out backwards; }
.anim-down { animation: dibaDown .4s ease-out both; }
.anim-card {
  animation: dibaCardIn .45s ease-out both;
  animation-delay: calc(var(--i, 0) * 55ms);
}
.anim-pop {
  animation: dibaPop .32s cubic-bezier(.22, .68, 0, 1.2) both;
  animation-delay: calc(var(--i, 0) * 45ms);
}
.anim-list {
  animation: dibaListIn .35s ease-out both;
  animation-delay: calc(var(--i, 0) * 22ms);
}

/* ── کاشی‌های نوارِ آمار یکی‌یکی می‌آیند */
.tiles > .tile {
  animation: dibaPop .32s cubic-bezier(.22, .68, 0, 1.2) both;
  animation-delay: calc(var(--i, 0) * 45ms);
}

/* ── میله‌های درصد از صفر پر می‌شوند
   ⚠ چرا `width` و نه `transform: scaleX()`: مرکزِ مقیاس با
   `transform-origin` تعیین می‌شود و آن خصوصیت مقدارِ **منطقی**
   ندارد (چپ/راستِ فیزیکی است). با `width` جهت خودبه‌خود درست
   می‌ماند — و در راست‌به‌چپ همین درستی از چند میلی‌ثانیه‌ی
   کارایی مهم‌تر است. تعدادشان در هر صفحه انگشت‌شمار است. */
.bar__fill { animation: dibaFill .8s cubic-bezier(.4, 0, .2, 1) both; }

/* ── میله‌های نمودار از پایین بالا می‌آیند */
.chart__bar {
  transform-origin: bottom;
  animation: dibaGrow .5s cubic-bezier(.22, .68, 0, 1.06) both;
  animation-delay: calc(var(--i, 0) * 12ms);
}

/* ── خانه‌های ماتریسِ حضور، موج‌وار */
.matrix__row .cell {
  animation: dibaListIn .3s ease-out both;
  animation-delay: calc(var(--i, 0) * 8ms);
}

/* ── نقطه‌ی «زنده» نبض می‌زند
   ⚠ فقط جایی که واقعاً زنده است. نبضِ تزئینی دروغ است. */
.dot--live { animation: dibaPulse 2s ease-in-out infinite; }

/* ── اسکلتِ بارگذاری — برای وقتی صفحه‌ای داده را دیر می‌آورد */
.skeleton {
  position: relative; overflow: hidden;
  background: var(--surface-2); border-radius: var(--r-sm);
  min-height: 12px;
}
.skeleton::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, var(--line-2), transparent);
  background-size: 200% 100%;
  animation: dibaShimmer 1.5s ease-in-out infinite;
}

/* ── بالا آمدنِ کارت زیرِ نشانگر
   ⚠ فقط روی کارتِ **قابلِ کلیک**. کارتی که کاری نمی‌کند نباید
   واکنش بدهد — وگرنه کاربر کلیک می‌کند و هیچ اتفاقی نمی‌افتد. */
a.card, a.tile, .card--link {
  transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
}
a.card:hover, .card--link:hover {
  transform: translateY(-2px);
  box-shadow: var(--z-2);
  border-color: var(--line-2);
}

/* ═══════════════════════════════════════════════════════════════════
   کارتِ شخص و شرکت — نمای شبکه‌ای
   ═══════════════════════════════════════════════════════════════════

   ⚠ الگو از صفحه‌ی Contacts در Eleventra گرفته شد (جلسه ۱۸) — همان
   چیزی که پرسا مشخصاً گفت «ببین چه قدر تمیز درست کرده». ساختارش:

     [چک‌باکس]                        [نشانِ وضعیت]
                   (آواتار)
                     نام
                     نقش
                    شرکت
                   [برچسب]
        ریزآمار ۱ | ریزآمار ۲ | ریزآمار ۳
        ─────────────────────────────────
          ایمیل   تلفن   پرونده
   ─────────────────────────────────────────────────────────────────── */
.cards {
  display: grid; gap: var(--sp-3);
  grid-template-columns: repeat(auto-fill, minmax(212px, 1fr));
}

.pcard {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--sp-4) var(--sp-3) 10px;
  text-align: center;
  box-shadow: var(--z-1);
  animation: dibaPop .32s cubic-bezier(.22, .68, 0, 1.2) both;
  animation-delay: calc(var(--i, 0) * 28ms);
  transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
}
.pcard:hover {
  transform: translateY(-3px);
  box-shadow: var(--z-2);
  border-color: var(--line-2);
}

.pcard__flag {
  position: absolute; inset-block-start: 9px; inset-inline-start: 10px;
}

/* ── آواتار: رنگ از نامِ خودِ شخص می‌آید و همیشه همان می‌ماند
   ⚠ این هشت رنگ **در هر دو حالت** ثابت‌اند و توکنِ حالت ندارند:
   میان‌تُن‌اند و متنِ سفید روی همه‌شان کنتراستِ ۴٫۵:۱ می‌دهد. اگر
   تیره/روشن می‌کردیم، هویتِ رنگیِ هر نفر با تغییرِ حالت عوض می‌شد. */
/* ⚠⚠ **`margin-inline: auto` روی `.av` نیست، و این یک باگِ واقعیِ
   پیداشده در مرورگر است.**

   تا جلسه ۲۳ همین‌جا بود، برای کارتِ وسط‌چینِ دفترچه. ولی آواتار در
   **هر ردیفِ فلکسِ دیبا** هم می‌آید (اشاره، شرکت‌کننده، کامنت،
   یادداشت، مسئولان) و در فلکس، `margin: auto` **همه‌ی فضای آزاد را
   می‌خورد**: کارتِ اشاره‌ی جلسه ۲۱ روی صفحه این شکل بود —

       [متن ................. ۴۴۰px فاصله ... (آواتار) ... ۴۴۰px]

   یعنی آواتار وسطِ کارت و متن ته آن. با اندازه‌گیریِ
   `getComputedStyle` پیدا شد: حاشیه‌ی هر دو سمتش ۴۴۰٫۹۵ پیکسل
   شده بود.

   ⚠ و نامِ آن خصوصیتِ فیزیکی عمداً اینجا نوشته نشد: تستِ
     `test_no_hardcoded_physical_direction` کلِ فایل را می‌خواند،
     توضیح‌ها را هم — و درست می‌کند.

   حالا وسط‌چینی فقط در جایی است که واقعاً لازم است — کارتِ
   شبکه‌ای، که خودش `text-align: center` دارد. */
.av {
  width: 52px; height: 52px; border-radius: var(--r-full);
  display: flex; align-items: center; justify-content: center;
  font-size: var(--t-lg); font-weight: 700; color: #fff;
  background: #4a5b8c; flex-shrink: 0;
  letter-spacing: -.02em;
}
.pcard > .av { margin-inline: auto; }
.av--0 { background: #2f6f5b; }
.av--1 { background: #2a6496; }
.av--2 { background: #7a4f9c; }
.av--3 { background: #a1523f; }
.av--4 { background: #8a6114; }
.av--5 { background: #1f6f6f; }
.av--6 { background: #94406a; }
.av--7 { background: #4a5b8c; }
.av--sm { width: 34px; height: 34px; font-size: var(--t-xs); }
/* ⚠ سازمان مربعِ گردگوشه است، شخص دایره. در فهرستِ مخلوط، شکل به
   یک نگاه می‌گوید کدام است — بی‌آنکه لازم باشد برچسب بخوانی. */
.av--org { border-radius: var(--r); }

.pcard__name {
  /* ⚠ حالا <a> است نه <div> — نامِ کارت به پروندهٔ فارسی می‌رود.
     پس display باید block باشد، وگرنه ellipsis روی عنصرِ درون‌خطی
     کار نمی‌کند. */
  display: block; color: var(--fg);
  margin-block-start: 10px;
  font-size: var(--t-md); font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
a.pcard__name:hover { color: var(--accent); text-decoration: none; }
.pcard__role { font-size: var(--t-xs); color: var(--fg-muted); margin-top: 2px; }
.pcard__org  { font-size: var(--t-xs); color: var(--fg-faint); margin-top: 1px;
               white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pcard__tags { display: flex; gap: 5px; justify-content: center; flex-wrap: wrap;
               margin-block-start: 9px; min-height: 22px; }

.pcard__stats {
  /* ⚠ `auto-fit` نه `repeat(3,…)`: کارتی که یک ریزآمار دارد نباید
     دو ستونِ خالی کنارش بگذارد. */
  display: grid; grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
  gap: 4px; margin-block-start: 11px;
  padding-block-start: 11px; border-block-start: 1px solid var(--line);
}
.pcard__stat  { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.pcard__num   { font-size: var(--t-sm); font-weight: 600; }
.pcard__cap   { font-size: var(--t-3xs); color: var(--fg-faint); }

.pcard__acts {
  display: flex; justify-content: center; gap: 4px;
  margin-block-start: 10px; padding-block-start: 9px;
  border-block-start: 1px solid var(--line);
}
.pcard__act {
  width: 30px; height: 30px; border-radius: var(--r-sm);
  display: flex; align-items: center; justify-content: center;
  color: var(--fg-muted); text-decoration: none;
  transition: background-color .15s ease, color .15s ease;
}
.pcard__act:hover { background: var(--surface-2); color: var(--accent);
                    text-decoration: none; }
/* ⚠ کنشی که راهِ انجامش نیست کم‌رنگ و **غیرقابلِ کلیک** است، نه
   پنهان: جای خالی جابه‌جا شدنِ آیکون‌ها را باعث می‌شود و چشم
   عادت نمی‌کند. */
.pcard__act--off { opacity: .28; pointer-events: none; }

/* ── کلیدِ تغییرِ نما (شبکه / جدول) */
.viewsw {
  display: inline-flex; gap: 2px; padding: 2px;
  background: var(--surface-2); border: 1px solid var(--line-2);
  border-radius: var(--r);
}
.viewsw__btn {
  width: 30px; height: 26px; border-radius: var(--r-sm);
  display: flex; align-items: center; justify-content: center;
  color: var(--fg-faint); text-decoration: none; border: 0; background: none;
  cursor: pointer; transition: background-color .15s ease, color .15s ease;
}
.viewsw__btn:hover { color: var(--fg); text-decoration: none; }
.viewsw__btn--on { background: var(--surface); color: var(--accent);
                   box-shadow: var(--z-1); }

/* ═══════════════════════════════════ سرصفحه‌ی کارتیِ صفحه
   الگوی صفحه‌ی Contacts: عنوان و توضیح یک طرف، ریزآمارها طرفِ
   دیگر، و دکمه‌های کنش زیرشان. */
.phead {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-lg); box-shadow: var(--z-1);
  padding: var(--sp-4) var(--sp-5);
  display: flex; flex-wrap: wrap; gap: var(--sp-4);
  align-items: flex-start; justify-content: space-between;
}
/* ⚠⚠ `flex: 1 1 260px` و نه فقط `min-width: 0`.
   بی این، بلوکِ عنوان به اندازهٔ **متنش** پهن می‌شد و روی نمایشگرِ
   ۱۳۲۹ پیکسل، کاشیِ آمار به خطِ دوم می‌افتاد و سرِ صفحه یک حفرهٔ
   خالی می‌شد — در کرومِ خودِ پرسا دیده شد، نه در تست.
   حالا توضیح دو خط می‌شود و کاشی سرِ جایش می‌مانَد. */
.phead__main { min-width: 0; flex: 1 1 260px; }
/* ⚠ اندازه و وزن **روی خودِ کلاس** است، نه فقط روی h1ِ داخلش.
   نیمی از صفحه‌ها سرِ صفحه‌ی قدیمی (`<h1>` با ۲۱ پیکسل) داشتند و
   نیمی سرِ صفحه‌ی تازه (`<div>` که ۱۴ پیکسل ارث می‌برد). دو نیمه‌ی
   یک سامانه با دو اندازه‌ی عنوان — و همین است که «یکپارچه نبودن»
   را می‌سازد، نه رنگ و آیکون. */
.phead__title {
  display: flex; align-items: center; gap: 9px;
  font-size: var(--t-2xl); font-weight: 600; letter-spacing: -.02em;
  line-height: 1.3;
}
.phead__title h1 { margin: 0; font-size: inherit; }
.phead__ico {
  width: 34px; height: 34px; border-radius: var(--r);
  background: var(--ok-bg); color: var(--accent);
  display: flex; align-items: center; justify-content: center; flex-shrink: 0;
}
.phead__sub { font-size: var(--t-xs); color: var(--fg-muted); margin-top: 5px; }
.phead__acts { display: flex; gap: var(--sp-2); flex-wrap: wrap;
               margin-block-start: var(--sp-3); }

.phead__stats { display: flex; gap: var(--sp-2); flex-wrap: wrap; }
.phead__stat {
  min-width: 88px; padding: 9px 12px; text-align: center;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r);
}
.phead__stat b     { display: block; font-size: var(--n-lg); font-weight: 700;
                     color: var(--accent); }
.phead__stat small { font-size: var(--t-3xs); color: var(--fg-faint);
                     letter-spacing: .03em; }

/* ── کارتی که یک منوی بازِ شناور دارد ─────────────────────────
   ⚠⚠ **این رفعِ یک باگِ واقعی است که در عکسِ صفحه دیده شد**، نه
      یک احتیاط: منوی «خروجی» در «نامه‌های رسمی» زیرِ نوارِ فیلتر و
      جدول می‌رفت و فقط قلمِ اول و آخرش پیدا بود.

   ⚠ و دلیلش `z-index`ِ خودِ منو نبود — منو ۳۰ داشت. هر
     `.anim-card` با انیمیشنِ `transform` **زمینه‌ی چیدمانِ تازه**
     می‌سازد؛ منو داخلِ زمینه‌ی کارتِ خودش حبس می‌شود و آن زمینه در
     لایهٔ صفر و به ترتیبِ سند رسم می‌شود، پس هر کارتِ **بعدی** روی
     آن می‌افتد. علاج، بالا بردنِ خودِ کارت است نه منو.

   ⚠ `:has()` در کرومِ امروز هست، ولی تنها راه نیست: `app.js` هم
     کلاسِ `.zlift` را روی همان کارت می‌گذارد. دو راه، چون این خطا
     بی‌صدا برمی‌گردد. */
:where(.phead, .card, .filters):has(.expo[open]),
:where(.phead, .card, .filters):has(.men-drop:not([hidden])),
.zlift {
  position: relative; z-index: 60;
  /* ⚠⚠ و **این خط جدا از بالایی لازم است**: کارت `overflow: hidden`
     دارد (برای گوشه‌های گرد)، پس منویی که از پایینش بیرون می‌زند
     بریده می‌شود — حتی وقتی لایه‌اش درست است. در فهرستِ اشاره‌ی
     فرمِ کار همین اتفاق افتاد: نامِ دوم نصفه بریده شده بود.
     ⚠ فقط **تا وقتی منو باز است**؛ بعدش کارت به قیچیِ خودش
       برمی‌گردد. */
  overflow: visible;
}

/* ═══════════════════════════════════════════════════════════════════
   گفت‌وگوی داخلی
   ═══════════════════════════════════════════════════════════════════
   ⚠ چیدمانِ دو‌ستونی با ارتفاعِ **قطعی**: ستونِ پیام‌ها باید خودش
   اسکرول شود، نه کلِ صفحه. اگر ارتفاع را به محتوا بسپاریم، با صد
   پیام کلِ صفحه بلند می‌شود و کادرِ نوشتن از دسترس خارج.
   ─────────────────────────────────────────────────────────────────── */
.chat {
  display: grid;
  grid-template-columns: 258px minmax(0, 1fr);
  gap: var(--sp-3);
  height: calc(100vh - var(--topbar-h) - 172px);
  min-height: 380px;
}
@media (max-width: 860px) {
  .chat { grid-template-columns: 1fr; height: auto; }
  .chat__list { max-height: 220px; }
}

.chat__list, .chat__room {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-lg); box-shadow: var(--z-1);
  display: flex; flex-direction: column; min-height: 0; overflow: hidden;
}
/* ⚠ `position: relative` روی ستونِ گفت‌وگو، تا فلشِ «برو پایین»
   نسبت به همین ستون بنشیند نه نسبت به کلِ صفحه. بی این، فلش روی
   نوارِ نوشتن یا بیرونِ قاب می‌افتاد — و روی نمایشگرِ کوچک روی
   منوی کنار. */
.chat__room { position: relative; }

/* ── فلشِ «برو پایین»
   ⚠ `inset-block-end` را از بالای نوارِ نوشتن حساب کرده‌ایم، نه از
   پایینِ قاب: فلش باید **روی جریانِ پیام‌ها** شنا کند، نه روی
   جعبه‌ی نوشتن.

   ⚠ و ۴۴ پیکسل است: کمینه‌ی هدفِ لمسی. فلشِ ۲۸پیکسلی روی موبایل
   قابلِ زدن نیست. */
.jumpdn {
  position: absolute; inset-inline-end: 18px; inset-block-end: 86px;
  z-index: 4; cursor: pointer;
  inline-size: 44px; block-size: 44px; border-radius: var(--r-full);
  display: grid; place-items: center;
  background: var(--surface); color: var(--fg-muted);
  border: 1px solid var(--line-2); box-shadow: var(--z-2);
}
.jumpdn:hover { color: var(--accent); border-color: var(--accent); }
.jumpdn__n {
  position: absolute; inset-block-start: -5px; inset-inline-end: -5px;
  min-inline-size: 19px; padding-inline: 4px; block-size: 19px;
  border-radius: var(--r-full);
  /* ⚠ `--on-accent` و نه `#fff`: در حالتِ روشن رنگِ تأکید تیره است و
     در حالتِ تیره روشن — با سفیدِ ثابت، عددِ روی نشان در یکی از دو
     حالت خوانا نبود. همان توکنی که دکمه‌ی اصلی هم از آن می‌خواند. */
  background: var(--accent); color: var(--on-accent);
  font-size: var(--t-3xs); line-height: 19px; text-align: center;
}

.chat__listhead, .chat__roomhead {
  display: flex; align-items: center; gap: 9px;
  padding: 10px var(--sp-3);
  border-block-end: 1px solid var(--line);
  font-size: var(--t-xs); font-weight: 600; color: var(--fg-muted);
  flex-shrink: 0;
}
.chat__roomhead { padding: 9px var(--sp-4); }

.chat__threads { flex-grow: 1; overflow-y: auto; padding: 6px; }

.chat__thread {
  display: flex; align-items: center; gap: 9px;
  padding: 8px 9px; border-radius: var(--r);
  color: var(--fg); text-decoration: none;
  animation: dibaListIn .3s ease-out both;
  animation-delay: calc(var(--i, 0) * 25ms);
  transition: background-color .15s ease;
}
.chat__thread:hover { background: var(--surface-2); text-decoration: none; }
.chat__thread--on { background: var(--ok-bg); }

.chat__meta { display: flex; flex-direction: column; min-width: 0; }
.chat__meta b     { font-size: var(--t-sm); font-weight: 600;
                    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chat__meta small { font-size: var(--t-3xs); color: var(--fg-faint); }

.chat__new {
  display: flex; gap: 6px; padding: 8px;
  border-block-start: 1px solid var(--line); flex-shrink: 0;
}
.chat__new select { flex-grow: 1; min-width: 0; height: 30px; font-size: var(--t-xs); }

/* ── حبابِ پیام
   ⚠ پیامِ خودم سمتِ **پایانِ** خط، پیامِ طرفِ دیگر سمتِ **آغاز** —
   با خصوصیت‌های منطقی، تا نسخه‌ی آلمانی هم خودبه‌خود درست باشد. */
.chat__body {
  flex-grow: 1; overflow-y: auto;
  padding: var(--sp-4);
  display: flex; flex-direction: column; gap: 9px;
}
/* ── حالتِ خالی
   ⚠ `margin: auto` در یک ستونِ فلکس، بلوک را در **هر دو** محور وسط
   می‌گذارد. با `position: absolute` هم می‌شد، ولی آن‌وقت با آمدنِ
   اولین پیام روی حباب می‌افتاد. */
.chat__empty {
  margin: auto; text-align: center; max-inline-size: 34ch;
  display: flex; flex-direction: column; align-items: center; gap: 6px;
}
.chat__empty__ico {
  display: grid; place-items: center;
  inline-size: 54px; block-size: 54px; border-radius: var(--r-full);
  background: var(--surface-2); border: 1px solid var(--line);
  color: var(--fg-faint); margin-block-end: 4px;
}
.chat__empty b { font-size: var(--t-sm); }
.chat__empty p { margin: 0; font-size: var(--t-xs); line-height: 1.6; }

.chat__day {
  align-self: center; font-size: var(--t-3xs); color: var(--fg-faint);
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r-full); padding: 3px 11px;
  margin-block: 5px;
}

.msg {
  display: flex; align-items: flex-end; gap: 7px;
  max-width: min(78%, 560px);
  animation: dibaListIn .3s ease-out both;
  animation-delay: calc(var(--i, 0) * 15ms);
}
.msg--mine { align-self: flex-end; flex-direction: row-reverse; }

.msg__bubble {
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: 8px 11px 6px;
  display: flex; flex-direction: column; gap: 2px;
  min-width: 0;
}
.msg--mine .msg__bubble {
  background: var(--ok-bg); border-color: var(--ok-line);
}
.msg__who  { font-size: var(--t-3xs); color: var(--accent); font-weight: 700; }
.msg__text { font-size: var(--t-sm); line-height: 1.75; word-break: break-word; }
.msg__time { font-size: var(--t-3xs); color: var(--fg-faint); align-self: flex-end; }

.chat__send {
  display: flex; gap: 8px; align-items: flex-end;
  padding: 10px var(--sp-4);
  border-block-start: 1px solid var(--line); flex-shrink: 0;
}
.chat__send textarea {
  flex-grow: 1; min-height: 38px; max-height: 140px;
  resize: vertical; line-height: 1.7;
}


/* ═══════════════════════════════ داشبوردِ منابعِ انسانی (مرحله ۲۹)

   چهار المانِ تازه، هر چهار از قالب‌های مرجع:

     .bento  → شبکه‌ی نامتقارن (Eleventra / Modernize)
     .trend  → نمودارِ سطحیِ SVG با خطِ کشیده‌شونده
     .ring   → حلقه‌ی درصد (donut) — SVG، نه conic-gradient
     .rank   → فهرستِ رتبه‌بندی با میله‌ی داخلِ ردیف

   ⚠ چرا حلقه SVG است و نه `conic-gradient`: انیمیشنِ
   conic-gradient به `@property` نیاز دارد که در فایرفاکسِ قدیم
   نیست، و بی‌انیمیشن حلقه یک‌باره ظاهر می‌شود. SVG با
   `stroke-dashoffset` همه‌جا کار می‌کند.

   ⚠ چرا نمودار SVG است و نه میله‌ی CSS: میله «هر روز چقدر» را خوب
   می‌گوید ولی **شیب** را نه. برای «تا حالا جلوتریم یا عقب» چشم به
   خط نیاز دارد.
   ──────────────────────────────────────────────────────────── */

/* ── شبکه‌ی نامتقارن ─────────────────────────────────────────── */
.bento {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--sp-4);
}
.bento__wide { grid-column: span 2; }
/* ⚠ گونه‌ی «پرکن»: وقتی تعدادِ کارت‌ها از پیش معلوم نیست. با سه ستونِ
   ثابت، ردیفی که فقط یک کارت دارد دو ستونِ خالی کنارش می‌گذارد و
   صفحه شبیهِ نیمه‌بارگذاری‌شده می‌شود. */
.bento--fill { grid-template-columns: repeat(auto-fit, minmax(min(100%, 330px), 1fr)); }
/* ⚠ گونه‌ی «سرچسب»: کارت‌ها به بلندترینِ ردیف کشیده نمی‌شوند.

   پیش‌فرضِ شبکه `stretch` است و برای کاشی‌های هم‌جنس درست؛ ولی در
   ردیفی که یک کارتِ بلند (متنِ صورت‌جلسه، ۱۶ سطر) کنارِ یک کارتِ
   کوتاه (سرصفحه، ۱۰ ردیف) دارد، کارتِ کوتاه یک حفره‌ی ۳۰۰ پیکسلی
   ته خودش می‌گذارد که در مرورگر دیده شد و شبیهِ کارتِ نیمه‌بارگذاری
   بود. */
.bento--top { align-items: start; }

@media (max-width: 1080px) {
  .bento { grid-template-columns: minmax(0, 1fr); }
  .bento__wide { grid-column: span 1; }
}

/* ── نمودارِ سطحی ───────────────────────────────────────────── */
.trend { display: block; width: 100%; height: auto; overflow: visible; }
/* جای برچسبِ روز در پایین — ارتفاعِ viewBox این را دارد. */

/* ⚠ خطِ راهنما پیش از داده کشیده می‌شود تا زیرِ آن بماند. */
.trend__grid { stroke: var(--line); stroke-width: 1; }
/* برچسبِ خطِ راهنما — بی‌آن، خطِ افقی هیچ عددی نمی‌گوید و فقط
   تزئین است. تزئینی که شبیهِ داده باشد، بدتر از نبودن است. */
.trend__tick {
  font-size: var(--t-3xs); fill: var(--fg-faint);
  font-variant-numeric: tabular-nums;
}

.trend__area  { fill: url(#trend-fill); }
.trend__line  {
  fill: none; stroke: var(--accent); stroke-width: 2.2;
  stroke-linecap: round; stroke-linejoin: round;
}
/* خطِ «موظف» — نقطه‌چین و کم‌رنگ، چون **مبنا** است نه دستاورد.
   ⚠ گزینشگرِ دوکلاسی عمدی است: قاعده‌ی `.trend__line` پایین‌تر در
   همین فایل `stroke-dasharray: 1` می‌گذارد (برای انیمیشنِ کشیدن) و
   با ویژه‌گیِ برابر، **بعدی** برنده می‌شد — نتیجه: خطِ موظف توپُر
   می‌شد و از خطِ محقق قابلِ تفکیک نبود. */
.trend__line.trend__line--ghost {
  stroke: var(--fg-faint); stroke-width: 1.6;
  stroke-dasharray: 5 5; opacity: .85;
}
.trend__dot {
  fill: var(--accent); stroke: var(--surface); stroke-width: 2.5;
}
.trend__foot {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-4); flex-wrap: wrap;
  padding-block-start: 10px; margin-block-start: 10px;
  border-block-start: 1px solid var(--line);
  font-size: var(--t-xs); color: var(--fg-muted);
}

/* انیمیشنِ کشیده‌شدنِ خط. `pathLength="1"` در قالب گذاشته شده، پس
   طولِ هر مسیر — هر چه باشد — برای CSS دقیقاً ۱ است. */
@keyframes dibaDraw { from { stroke-dashoffset: 1; } }
@keyframes dibaFade { from { opacity: 0; } }

.trend__line {
  stroke-dasharray: 1; stroke-dashoffset: 0;
  animation: dibaDraw 1.1s cubic-bezier(.4, 0, .2, 1) both;
}
.trend__line.trend__line--ghost {
  /* ⚠ نقطه‌چین با انیمیشنِ کشیدن جمع نمی‌شود (هر دو dasharray
     می‌خواهند)، پس این یکی محو می‌شود نه کشیده. */
  stroke-dasharray: 5 5;
  stroke-dashoffset: 0;
  animation: dibaFade .8s ease-out .3s both;
}
.trend__area { animation: dibaFade 1.1s ease-out .25s both; }
.trend__dot  { animation: dibaPop .35s cubic-bezier(.22,.68,0,1.2) 1s both; }

/* ── حلقه‌ی درصد ───────────────────────────────────────────── */
.rings {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
  gap: var(--sp-4);
}
.ring {
  display: flex; flex-direction: column; align-items: center;
  gap: 8px; text-align: center; min-width: 0;
}
.ring__svg { width: 104px; height: 104px; flex-shrink: 0; }
.ring__track { fill: none; stroke: var(--line); stroke-width: 9; }
.ring__arc {
  fill: none; stroke-width: 9; stroke-linecap: round;
  stroke: var(--accent);
  stroke-dasharray: 100;
  stroke-dashoffset: var(--off, 100);
  animation: dibaRing 1s cubic-bezier(.4, 0, .2, 1) both;
}
@keyframes dibaRing { from { stroke-dashoffset: 100; } }
.ring--warn .ring__arc { stroke: var(--warn); }
.ring--bad  .ring__arc { stroke: var(--bad); }

.ring__pct {
  font-size: var(--t-xl); font-weight: 700;
  font-variant-numeric: tabular-nums;
  fill: var(--fg);
}
.ring__label { font-size: var(--t-xs); font-weight: 600; }
.ring__val   { font-size: var(--t-2xs); color: var(--fg-muted); }
.ring__note  { font-size: var(--t-3xs); color: var(--fg-faint); }

/* ── توزیعِ روزهای هفته ────────────────────────────────────── */
.wk {
  display: grid; grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 8px;
}
.wk__col {
  display: flex; flex-direction: column; align-items: center;
  gap: 6px; min-width: 0;
}
/* ⚠ این لایه‌ی میانی **الزامی** است، تزئینی نیست: ارتفاعِ درصدیِ میله
   فقط وقتی معنا دارد که والدش ارتفاعِ **معین** داشته باشد. بی‌این،
   میله‌ها همه به کمینه‌ی ۳ پیکسل می‌افتادند و نمودار صاف می‌شد —
   بی‌هیچ خطایی. (همان قاعده‌ای که .chart__bars از اول رعایت می‌کرد.) */
.wk__track {
  display: flex; align-items: flex-end; justify-content: center;
  inline-size: 100%; block-size: 118px;
}
.wk__bar {
  inline-size: 100%; max-inline-size: 30px;
  min-block-size: 3px;
  background: linear-gradient(to top, var(--accent), color-mix(in oklab, var(--accent) 55%, var(--surface)));
  border-radius: var(--r-sm) var(--r-sm) 0 0;
  transform-origin: bottom;
  animation: dibaGrow .55s cubic-bezier(.22, .68, 0, 1.06) both;
  animation-delay: calc(var(--i, 0) * 55ms);
}
.wk__col--off .wk__bar {
  background: none;
  border: 1px dashed var(--line-2);
  min-block-size: 3px; block-size: 3px;
}
.wk__col--today .wk__bar { box-shadow: 0 0 0 2px var(--ok-line); }
.wk__name { font-size: var(--t-2xs); color: var(--fg-muted); }
.wk__col--today .wk__name { color: var(--accent); font-weight: 700; }
.wk__val  { font-size: var(--t-3xs); color: var(--fg-faint); font-variant-numeric: tabular-nums; }

/* ── فهرستِ رتبه‌بندی ──────────────────────────────────────── */
.rank { display: flex; flex-direction: column; }
.rank__row {
  display: grid;
  grid-template-columns: 26px minmax(0, 1fr) auto;
  align-items: center; gap: 10px;
  padding: 9px var(--sp-4);
  border-block-end: 1px solid var(--line);
  animation: dibaListIn .35s ease-out both;
  animation-delay: calc(var(--i, 0) * 35ms);
}
.rank__row:last-child { border-block-end: 0; }
.rank__row:hover { background: var(--surface-2); }

.rank__pos {
  inline-size: 26px; block-size: 26px;
  display: grid; place-items: center;
  border-radius: var(--r-sm);
  background: var(--surface-2); border: 1px solid var(--line);
  font-size: var(--t-2xs); font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--fg-muted); flex-shrink: 0;
}
/* سه‌ی اول نشانِ رنگی می‌گیرند — ولی **فقط** سه‌ی اول، وگرنه
   رنگ دیگر خبری نمی‌دهد. */
.rank__row:nth-child(1) .rank__pos {
  background: var(--ok-bg); border-color: var(--ok-line); color: var(--accent);
}
.rank__row:nth-child(2) .rank__pos,
.rank__row:nth-child(3) .rank__pos { color: var(--fg); }

.rank__main { min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.rank__name {
  font-size: var(--t-sm); font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.rank__line {
  /* ⚠ display:block لازم است — این یک <span> است و عنصرِ درون‌خطی
     ارتفاع نمی‌گیرد. بی‌این خطِ ۴ پیکسلی و پرشدگی‌اش هیچ‌وقت
     دیده نمی‌شدند. */
  display: block;
  block-size: 4px; border-radius: var(--r-full);
  background: var(--line); overflow: hidden;
}
.rank__fill {
  display: block;
  block-size: 100%; border-radius: var(--r-full);
  background: var(--accent);
  animation: dibaFill .8s cubic-bezier(.4, 0, .2, 1) both;
  animation-delay: calc(var(--i, 0) * 35ms);
}
.rank__side {
  display: flex; flex-direction: column; align-items: flex-end; gap: 3px;
  flex-shrink: 0;
}
.rank__val {
  font-size: var(--t-sm); font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.rank__meta { display: flex; gap: 5px; }

/* ── فهرستِ واحدها ────────────────────────────────────────── */
.dept { display: flex; flex-direction: column; gap: 11px; }
.dept__row { display: flex; flex-direction: column; gap: 5px; }
.dept__top {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px; font-size: var(--t-xs);
}
.dept__name {
  font-weight: 600; min-width: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.dept__num { color: var(--fg-muted); font-variant-numeric: tabular-nums; flex-shrink: 0; }

/* ── کاشیِ چهارتایی — همان `.tiles` با چهار ستون ──────────── */
.tiles--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 900px) {
  .tiles--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}


/* ═══════════════════════════════ زنگِ اعلان (مرحله ۳۲)

   ⚠ `.pop` عمداً عمومی نوشته شده، نه `.bell__pop`: همین قاب برای
   منوی کاربر و منوی فیلتر هم به کار می‌آید. یک قابِ بازشو در کلِ
   سامانه، نه چهار قابِ کمی متفاوت.
   ──────────────────────────────────────────────────────────── */
.bellwrap { position: relative; display: inline-flex; }

.bell__dot {
  position: absolute;
  inset-block-start: -4px; inset-inline-end: -5px;
  min-inline-size: 16px; block-size: 16px;
  padding: 0 4px;
  border-radius: var(--r-full);
  background: var(--bad); color: #fff;
  font-size: var(--t-3xs); font-weight: 700; line-height: 16px;
  text-align: center;
  border: 2px solid var(--surface);
  box-sizing: content-box;
  pointer-events: none;
}

.pop {
  position: absolute;
  inset-block-start: calc(100% + 8px);
  inset-inline-end: 0;
  inline-size: 330px; max-inline-size: calc(100vw - 24px);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--z-2);
  z-index: 60;
  overflow: hidden;
  animation: dibaDown .22s cubic-bezier(.22, .68, 0, 1.08) both;
}
.pop__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; padding: 10px var(--sp-4);
  border-block-end: 1px solid var(--line);
  font-size: var(--t-xs); font-weight: 700;
}
.pop__body { max-block-size: min(60vh, 420px); overflow-y: auto; }
.pop__list { display: flex; flex-direction: column; }

.pop__item {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 10px var(--sp-4);
  border-block-end: 1px solid var(--line);
  color: var(--fg);
  animation: dibaListIn .28s ease-out both;
  animation-delay: calc(var(--i, 0) * 30ms);
}
.pop__item:last-child { border-block-end: 0; }
a.pop__item:hover { background: var(--surface-2); text-decoration: none; }

.pop__ico {
  inline-size: 26px; block-size: 26px; flex-shrink: 0;
  display: grid; place-items: center;
  border-radius: var(--r-sm);
  background: var(--surface-2); border: 1px solid var(--line);
  color: var(--fg-muted);
}
.pop__ico--ok   { background: var(--ok-bg);   border-color: var(--ok-line);   color: var(--accent); }
.pop__ico--warn { background: var(--warn-bg); border-color: var(--warn-line); color: var(--warn); }
.pop__ico--bad  { background: var(--bad-bg);  border-color: var(--bad-line);  color: var(--bad); }

.pop__main  { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pop__title { font-size: var(--t-xs); line-height: 1.6; }
.pop__meta  { font-size: var(--t-3xs); color: var(--fg-faint); }

.pop__empty {
  padding: var(--sp-5) var(--sp-4);
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  text-align: center;
  font-size: var(--t-xs); color: var(--fg-muted);
}
.pop__empty svg { color: var(--accent); }

.pop__foot {
  display: flex; gap: var(--sp-4);
  padding: 9px var(--sp-4);
  border-block-start: 1px solid var(--line);
  background: var(--surface-2);
  font-size: var(--t-2xs);
}

/* روی موبایل، قاب تمامِ عرض می‌شود — ۳۳۰ پیکسل روی ۳۷۵ بیرون می‌زند */
@media (max-width: 420px) {
  .pop {
    position: fixed;
    inset-block-start: calc(var(--topbar-h) + 6px);
    inset-inline: 8px;
    inline-size: auto;
  }
}


/* ═══════════════════════════════ تقویمِ ماهانه (مرحله ۳۳)

   ⚠ شبکه‌ی ۷ ستونی. در صفحه‌ی راست‌به‌چپ، ستونِ اول (شنبه) خودبه‌خود
   **راست** می‌نشیند — هیچ کارِ دستی لازم نیست، و همین دلیلِ استفاده از
   grid به‌جای جدولِ HTML است.
   ──────────────────────────────────────────────────────────── */
/* ═══════════════════════ تقویمِ یکپارچه (جلسه ۲۶)

   خواستهٔ پرسا: «باید یک تقویمِ خیلی زیبا داشته باشیم که تمامی
   کارهامون رو توش انجام بدیم.»

   ⚠⚠ سه تقویمِ قبلی اینجا یکی شدند. رنگِ هر رویداد از **لایه**اش
      می‌آید و لایه‌ها در `core/agenda.py` تعریف شده‌اند — این فایل
      هیچ اسمِ لایه‌ای را نمی‌داند، فقط شش لحنِ موجود را.
   ──────────────────────────────────────────────────────────── */

.cal { display: block; }

.cal__head, .cal__grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 1px;
  background: var(--line);
}
.cal__head { border-block-start: 1px solid var(--line); }

.cal__wd {
  background: var(--surface-2);
  padding: 7px 6px; text-align: center;
  font-size: var(--t-2xs); font-weight: 700; color: var(--fg-muted);
}

.cal__cell {
  background: var(--surface);
  min-block-size: 116px;
  padding: 5px 6px 6px;
  display: flex; flex-direction: column; gap: 3px;
  min-inline-size: 0;
  position: relative;
}
.cal__cell--void { background: var(--bg); }
.cal__cell.is-off  { background: var(--bad-bg); }
.cal__cell.is-half { background: var(--warn-bg); }
/* ⚠ روزِ گذشته کم‌رنگ می‌شود ولی **خوانا** می‌مانَد: سابقه هم
   خوانده می‌شود، فقط امروز و آینده مهم‌ترند. */
.cal__cell.is-past .cal__n { color: var(--fg-faint); }
.cal__cell.is-past .ev { opacity: .72; }
/* ⚠ حاشیهٔ داخلی، نه border: border ارتفاعِ خانه را عوض می‌کند و
   کلِ سطر یک پیکسل جابه‌جا می‌شود. */
.cal__cell.is-today { box-shadow: inset 0 0 0 2px var(--accent); }

.cal__top { display: flex; align-items: center; gap: 5px; min-block-size: 19px; }
.cal__n { font-size: var(--t-sm); font-weight: 700; }
.cal__cell.is-off .cal__n { color: var(--bad); }
.cal__lbl {
  font-size: var(--t-3xs); color: var(--fg-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  min-inline-size: 0;
}

/* ⚠ «+» همیشه در DOM است و فقط **دیده** نمی‌شود: روی لمسی هاور
   وجود ندارد، و دکمه‌ای که با صفحه‌کلید هم نشود بهش رسید یعنی
   ساختِ کار از تقویم برای نیمی از کاربران وجود ندارد. */
.cal__add {
  margin-inline-start: auto; flex-shrink: 0;
  inline-size: 20px; block-size: 20px;
  display: grid; place-items: center;
  border-radius: var(--r-sm);
  color: var(--fg-faint); text-decoration: none;
  opacity: 0; transition: opacity .12s ease, background .12s ease;
}
.cal__cell:hover .cal__add,
.cal__add:focus-visible { opacity: 1; }
.cal__add:hover { background: var(--surface-3); color: var(--accent); }
@media (hover: none) { .cal__add { opacity: .55; } }

.cal__evs { display: flex; flex-direction: column; gap: 3px; min-inline-size: 0; }

/* ⚠⚠ **عنوان اول، ساعت زیرش** — و نه کنارِ هم.

   در جلسه ۲۷ ساعتِ رویداد از «۰۹:۰۰» به بازهٔ «۰۹:۰۰–۱۱:۰۰» رفت
   (چون برای رزروِ سالن، ساعتِ پایان لازم است). در مرورگر دیده شد
   که همان بازه تقریباً تمامِ عرضِ خانه را می‌گیرد و عنوان می‌شود
   «نش…» — یعنی تقویم دیگر نمی‌گوید آن روز چه خبر است.

   ⚠ ساعت `order: 2` می‌گیرد و نه اینکه در HTML جابه‌جا شود: در
     `title` و در خروجیِ اکسل ترتیبِ منطقی (ساعت، بعد عنوان)
     می‌مانَد؛ فقط چیدمانِ دیداری عوض می‌شود. */
.ev {
  display: flex; flex-wrap: wrap; align-items: baseline;
  column-gap: 5px;
  border-radius: var(--r-sm);
  padding: 2px 6px;
  border-inline-start: 2px solid var(--line-2);
  background: var(--surface-2);
  font-size: var(--t-2xs); line-height: 1.5;
  color: var(--fg); text-decoration: none;
  min-inline-size: 0;
}
.ev:hover { background: var(--surface-3); }
.ev__t {
  order: 1; flex: 1 1 100%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  min-inline-size: 0; unicode-bidi: plaintext;
}
.ev__at {
  order: 2; flex: 0 0 auto;
  font-size: var(--t-3xs); color: var(--fg-faint);
}
.ev__w  { order: 3; margin-inline-start: auto; flex-shrink: 0;
          font-size: var(--t-3xs); color: var(--fg-faint); }

.ev--bad    { background: var(--bad-bg);  border-inline-start-color: var(--bad); }
.ev--warn   { background: var(--warn-bg); border-inline-start-color: var(--warn); }
.ev--info   { background: var(--info-bg); border-inline-start-color: var(--info); }
.ev--ok     { background: var(--ok-bg);   border-inline-start-color: var(--accent); }
.ev--accent { background: var(--ok-bg);   border-inline-start-color: var(--accent); }
.ev--mute   { background: var(--surface-2); border-inline-start-color: var(--line-2); }

.cal__more { margin-block-start: 1px; }
.cal__more > summary {
  font-size: var(--t-3xs); color: var(--fg-faint);
  cursor: pointer; list-style: none; padding: 1px 6px;
  border-radius: var(--r-sm);
}
.cal__more > summary::-webkit-details-marker { display: none; }
.cal__more > summary:hover { color: var(--fg); background: var(--surface-3); }
/* ⚠ فهرستِ کامل **روی** خانه می‌نشیند و ارتفاعِ سطر را عوض نمی‌کند:
   بازکردنِ یک روز نباید شش روزِ دیگر را تکان بدهد. */
.cal__all {
  position: absolute; inset-inline: 4px; inset-block-start: 26px;
  z-index: 5;
  display: flex; flex-direction: column; gap: 3px;
  padding: 6px; border-radius: var(--r);
  background: var(--surface); border: 1px solid var(--line-2);
  box-shadow: var(--shadow-lg, 0 8px 28px rgba(0,0,0,.22));
  max-block-size: 260px; overflow-y: auto;
}

/* ── نوارِ بالا و چیپ‌های لایه ─────────────────────────────── */

.cal__nav { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.cal__gap { flex: 1 1 auto; }
.cal__pick { display: flex; gap: 6px; }
.cal__pick select { min-inline-size: 88px; }

.cal__layers {
  display: flex; flex-wrap: wrap; gap: 6px;
  margin-block-start: 12px;
  padding-block-start: 12px;
  border-block-start: 1px solid var(--line);
}

.lchip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 10px; border-radius: 99px;
  border: 1px solid var(--line-2); background: var(--surface-2);
  font-size: var(--t-2xs); color: var(--fg-muted);
  text-decoration: none; cursor: pointer;
  transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.lchip:hover { background: var(--surface-3); color: var(--fg); }
/* ⚠ نقطهٔ رنگی **همیشه** هست، حتی وقتی لایه خاموش است: رنگ چیزی
   است که کاربر روی خودِ تقویم می‌بیند، و چیپِ خاموشِ بی‌نقطه
   نمی‌گوید روشن‌کردنش چه رنگی می‌آورَد. */
.lchip__dot {
  inline-size: 8px; block-size: 8px; border-radius: 99px;
  background: var(--line-2); flex-shrink: 0;
}
.lchip__n { font-size: var(--t-3xs); opacity: .75; }
.lchip--on { color: var(--fg); border-color: var(--line-2); background: var(--surface-3); }
.lchip--on.lchip--info   .lchip__dot { background: var(--info); }
.lchip--on.lchip--warn   .lchip__dot { background: var(--warn); }
.lchip--on.lchip--ok     .lchip__dot { background: var(--accent); }
.lchip--on.lchip--accent .lchip__dot { background: var(--accent); }
.lchip--on.lchip--bad    .lchip__dot { background: var(--bad); }
.lchip--on.lchip--mute   .lchip__dot { background: var(--fg-faint); }

/* روی موبایل ۷ ستونِ ۱۱۶ پیکسلی جا نمی‌شود. تقویم افقی می‌لولد،
   ولی **درونِ ظرفِ خودش** — بدنه‌ی صفحه هرگز افقی نمی‌لولد. */
.cal-wrap { overflow-x: auto; }
@media (max-width: 760px) {
  .cal__head, .cal__grid { min-inline-size: 660px; }
  .cal__cell { min-block-size: 92px; }
}


/* ═══════════════════════ پروندهٔ شخص و شرکت (مرحله ۳۴)

   دو المانِ تازه:
     .kvs → فهرستِ «برچسب / مقدار»
     .tl  → خطِ زمان (فهرستِ رویداد با نقطه و خطِ پیوند)
   ──────────────────────────────────────────────────────────── */

/* ── فهرستِ برچسب/مقدار ──────────────────────────────────────

   ⚠ شبکه‌ی «پرکن» نه ستونِ تک. نسخه‌ی اول یک ستون بود؛ در کارتِ باریک
   خوب بود، ولی در کارتِ تمام‌عرضِ «پروندهٔ کارمندی» برچسب می‌چسبید به
   لبه‌ی راست و مقدار کنارش، و دو سومِ عرضِ کارت خالی می‌ماند. حالا
   خودش تعدادِ ستون را از عرضِ ظرف می‌گیرد.

   ⚠ خطِ جداکننده از `gap: 1px` روی پس‌زمینه می‌آید، نه از border —
   همان تکنیکِ `.tiles`. با border، خطِ عمودیِ بینِ ستون‌ها را باید
   با `nth-child` می‌ساختیم که شکننده است.                          */
.kvs {
  margin: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 290px), 1fr));
  gap: 1px;
  /* ⚠ پس‌زمینه‌ی ظرف **سطح** است نه رنگِ خط. با رنگِ خط، خانه‌های
     خالیِ آخرین سطر (وقتی تعدادِ ردیف‌ها مضربِ ستون‌ها نیست) یک بلوکِ
     خاکستری می‌شدند — عیناً همان چیزی که در اولین رندر دیدم. خطِ
     جداکننده حالا از حلقه‌ی box-shadow خودِ ردیف می‌آید. */
  background: var(--surface);
}
.kvs__row {
  background: var(--surface);
  box-shadow: 0 0 0 1px var(--line);
  display: grid;
  grid-template-columns: minmax(0, 120px) minmax(0, 1fr);
  gap: 10px;
  padding: 8px var(--sp-4);
  align-items: baseline;
}
.kvs__k { font-size: var(--t-2xs); color: var(--fg-faint); margin: 0; }
.kvs__v {
  margin: 0; font-size: var(--t-xs); line-height: 1.7;
  word-break: break-word;
}

/* ── خطِ زمان ──────────────────────────────────────────────── */
.tl { display: flex; flex-direction: column; }
.tl__row {
  display: grid;
  grid-template-columns: 14px minmax(0, 1fr);
  gap: 10px;
  padding: 8px var(--sp-4);
  position: relative;
  animation: dibaListIn .3s ease-out both;
  animation-delay: calc(var(--i, 0) * 35ms);
}
/* خطِ پیوندِ بینِ نقطه‌ها — از ::before خودِ ردیف، نه یک عنصرِ اضافه */
.tl__row::before {
  content: '';
  position: absolute;
  inset-block: 0;
  inset-inline-start: calc(var(--sp-4) + 6px);
  inline-size: 1px;
  background: var(--line);
}
.tl__row:first-child::before { inset-block-start: 50%; }
.tl__row:last-child::before  { inset-block-end: 50%; }

.tl__dot {
  inline-size: 9px; block-size: 9px;
  border-radius: var(--r-full);
  background: var(--idle);
  border: 2px solid var(--surface);
  box-shadow: 0 0 0 1px var(--line);
  margin-block-start: 5px;
  margin-inline-start: 2px;
  z-index: 1;
}
.tl__dot--in  { background: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.tl__dot--out { background: var(--info);   box-shadow: 0 0 0 1px var(--info); }

.tl__main  { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.tl__title { font-size: var(--t-xs); display: flex; align-items: center; gap: 6px; }
.tl__meta  { font-size: var(--t-3xs); color: var(--fg-faint); }


/* ═══════════════ جستجوی زندهٔ پالت (مرحله ۳۵) */
.palette__group {
  padding: 8px var(--sp-4) 4px;
  font-size: var(--t-3xs); font-weight: 700;
  color: var(--fg-faint);
  text-transform: none;
}
.palette__item--hit { gap: 9px; }
/* ⚠ آواتارِ ۳۴ پیکسلیِ کارت‌ها در ردیفِ پالت غول است — ردیفِ پالت
   ارتفاعش از متن می‌آید و با آواتارِ بزرگ دو برابر می‌شود. */
.palette__item--hit .av {
  inline-size: 24px; block-size: 24px;
  font-size: var(--t-3xs); flex-shrink: 0;
}
/* نشانِ «در حال جستجو» — نواری نازک زیرِ ورودی، نه چرخانِ وسطِ قاب.
   چرخان جای نتایج را می‌گیرد و قاب می‌پرد. */
.palette__busy {
  block-size: 2px;
  background: linear-gradient(90deg, transparent, var(--accent), transparent);
  background-size: 40% 100%;
  animation: dibaSweep 1s linear infinite;
}
@keyframes dibaSweep {
  from { background-position: -40% 0; }
  to   { background-position: 140% 0; }
}


/* ═══════════════ فهرستِ رویدادها (مرحله ۳۶) — گونه‌های خطِ زمان */
.tl__row {
  /* ⚠ ستونِ سومِ زمان اضافه شد. الگوی پایه دو ستونی بود؛ اینجا سه
     ستون است و باید صریح بازنویسی شود، وگرنه زمان زیرِ متن می‌افتد. */
  grid-template-columns: 14px minmax(0, 1fr) auto;
  align-items: start;
}
a.tl__row--go { color: var(--fg); }
a.tl__row--go:hover { background: var(--surface-2); text-decoration: none; }
.tl__time {
  font-size: var(--t-2xs); color: var(--fg-faint);
  margin-block-start: 2px; flex-shrink: 0;
}
/* ⚠ `--info` را جا انداخته بودم و «خروج»ها نقطه‌ی توخالیِ خاکستری
   می‌گرفتند — یعنی رنگ که قرار بود ورود و خروج را جدا کند، هیچ‌چیز
   نمی‌گفت. نامِ tone در ویو و نامِ کلاس در CSS باید **کامل** بخوانند. */
.tl__dot--info { background: var(--info); box-shadow: 0 0 0 1px var(--info); }
.tl__dot--warn { background: var(--warn); box-shadow: 0 0 0 1px var(--warn); }
.tl__dot--bad  { background: var(--bad);  box-shadow: 0 0 0 1px var(--bad); }
.tl__dot--ok   { background: var(--accent); box-shadow: 0 0 0 1px var(--accent); }


/* ═══════════════ سرِ ستونِ مرتب‌شدنی (مرحله ۳۷) */
th > a {
  display: inline-flex; align-items: center; gap: 4px;
  color: inherit; text-decoration: none;
}
th > a:hover { color: var(--accent); text-decoration: none; }
.th--on { color: var(--accent); }
.th__ar { font-size: var(--t-3xs); line-height: 1; }
/* فلشِ ستونِ غیرفعال فقط با نگه‌داشتنِ ماوس دیده می‌شود — وگرنه پنج
   فلشِ خاکستری کنارِ هم، چشم را از ستونِ **فعال** می‌گیرد. */
.th__ar--off { opacity: 0; transition: opacity .12s ease; }
th > a:hover .th__ar--off { opacity: .45; }


/* ═══════════════════════════════ توست‌ها (مرحله ۳۸)

   ⚠ فقط «موفقیت» و «اطلاع» توست می‌شوند. «هشدار» و «خطا» در قابِ
   ثابتِ بالای صفحه می‌مانند — پیامی که می‌گوید کاری انجام **نشد**
   اگر بپرد، کاربر فکر می‌کند انجام شده.

   ⚠ `position: fixed` یعنی آمدن و رفتنشان چیدمان را تکان نمی‌دهد.
   ──────────────────────────────────────────────────────────── */
.toasts {
  position: fixed;
  inset-block-end: 16px;
  inset-inline-start: 16px;
  z-index: 90;
  display: flex; flex-direction: column; gap: 8px;
  max-inline-size: min(380px, calc(100vw - 32px));
  pointer-events: none;          /* کلیک از رویشان رد شود، مگر خودِ توست */
}

.toast {
  pointer-events: auto;
  position: relative; overflow: hidden;
  display: flex; align-items: flex-start; gap: 9px;
  padding: 10px 12px;
  border-radius: var(--r-lg);
  background: var(--surface);
  border: 1px solid var(--line);
  box-shadow: var(--z-2);
  font-size: var(--t-xs); line-height: 1.7;
  animation: dibaToastIn .28s cubic-bezier(.22, .68, 0, 1.12) both;
  animation-delay: calc(var(--i, 0) * 70ms);
}
/* ⚠ از inset-inline-start می‌آید، پس در نسخه‌ی چپ‌به‌راست هم از همان
   سمتی می‌آید که می‌نشیند. با translateX ثابت، در LTR از سمتِ
   اشتباه وارد می‌شد. */
@keyframes dibaToastIn {
  from { opacity: 0; transform: translateY(14px) scale(.97); }
}
@keyframes dibaToastOut {
  to { opacity: 0; transform: translateY(6px) scale(.97); }
}
.toast--out { animation: dibaToastOut .2s ease-in both; }

.toast--ok   { border-color: var(--ok-line);   background: var(--ok-bg); }
.toast--info { border-color: var(--info-line); background: var(--info-bg); }

.toast__ico { flex-shrink: 0; margin-block-start: 1px; }
/* دکمهٔ «برگردان» داخلِ توست — کنشِ اصلیِ پیامِ حذف است، پس دیده
   می‌شود و از متن جدا می‌ایستد. */
.toast__undo { flex-shrink: 0; align-self: center; }
.toast__undo .btn { background: var(--surface); }
.toast--ok   .toast__ico { color: var(--accent); }
.toast--info .toast__ico { color: var(--info); }

.toast__text { flex-grow: 1; min-width: 0; }

.toast__x {
  flex-shrink: 0;
  background: none; border: 0; cursor: pointer;
  color: var(--fg-faint); font-size: var(--t-lg); line-height: 1;
  padding: 0 2px;
}
.toast__x:hover { color: var(--fg); }

/* نوارِ نازکِ عمرِ توست — کاربر می‌بیند که دارد می‌رود، پس اگر
   می‌خواهد بخواندش وقت دارد نگه‌اش دارد.

   ⚠ `inline-size` انیمیشن می‌شود، نه `scaleX`. دلیلش همان است که در
   `.bar__fill` نوشتیم: `transform-origin` نسخهٔ **منطقی** ندارد، پس
   با `scaleX` نوار در راست‌به‌چپ از سمتِ اشتباه آب می‌رفت. با
   `inline-size` و `inset-inline-start: 0`، جهت خودبه‌خود درست است. */
.toast::after {
  content: '';
  position: absolute;
  inset-inline-start: 0; inset-block-end: 0;
  inline-size: 100%; block-size: 2px;
  background: currentColor; opacity: .25;
  animation: dibaToastLife var(--life, 5200ms) linear both;
  animation-delay: calc(var(--i, 0) * 70ms);
}
@keyframes dibaToastLife { to { inline-size: 0; } }
/* با نگه‌داشتنِ ماوس، شمارش می‌ایستد */
.toast:hover::after { animation-play-state: paused; }


/* ═══════════════ راهنمای میان‌برها (مرحله ۴۰) */
.keys-back {
  position: fixed; inset: 0; z-index: 95;
  background: color-mix(in oklab, var(--bg) 72%, transparent);
  backdrop-filter: blur(3px);
  display: grid; place-items: center;
  padding: var(--sp-4);
  animation: dibaFade .18s ease-out both;
}
.keys {
  inline-size: min(520px, 100%);
  max-block-size: min(80vh, 620px);
  display: flex; flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--r-lg);
  box-shadow: var(--z-2);
  overflow: hidden;
  animation: dibaCardIn .26s cubic-bezier(.22, .68, 0, 1.1) both;
}
.keys__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; padding: 11px var(--sp-4);
  border-block-end: 1px solid var(--line);
  font-size: var(--t-md); font-weight: 700;
}
.keys__list { margin: 0; overflow-y: auto; }
.keys__row {
  display: grid;
  grid-template-columns: minmax(0, 132px) minmax(0, 1fr);
  gap: 12px; align-items: baseline;
  padding: 9px var(--sp-4);
  border-block-end: 1px solid var(--line);
}
.keys__row:last-child { border-block-end: 0; }
/* ⚠ ترکیبِ کلید مثلِ عدد است: باید **چپ‌به‌راست** خوانده شود.
   بی‌این، «Ctrl» و «K» در جریانِ فارسی جابه‌جا رندر می‌شدند و روی
   صفحه «K Ctrl» می‌نوشت — همان قاعده‌ای که برای `.num` داریم. */
.keys__row dt {
  margin: 0; display: flex; gap: 4px; flex-wrap: wrap;
  direction: ltr; unicode-bidi: isolate;
  justify-content: flex-end;
}
/* و در متنِ پایین هم، هر گروهِ کلید جداگانه ایزوله می‌شود */
.keys__foot .kbd { direction: ltr; unicode-bidi: isolate; }
.keys__row dd { margin: 0; font-size: var(--t-xs); line-height: 1.7; }
.keys__foot {
  padding: 9px var(--sp-4);
  border-block-start: 1px solid var(--line);
  background: var(--surface-2);
  font-size: var(--t-2xs);
}
/* ⚠ روی موبایل ستونِ ۱۳۲ پیکسلی جا نمی‌شود؛ کلیدها بالای شرح می‌روند */
@media (max-width: 480px) {
  .keys__row { grid-template-columns: minmax(0, 1fr); gap: 5px; }
}


/* ═══════════════════ تنظیماتِ من + تراکمِ نمایش (مرحله ۴۱)

   ⚠ «فشرده» فقط تزئین نیست: گزارشِ حضورِ ۱۹ کارمند × ۳۱ روز روی یک
   صفحه‌ی معمولی جا نمی‌شود. با تراکمِ فشرده همان جدول یک نگاه می‌شود.

   ⚠ تراکم از **بازنویسیِ توکن‌ها** می‌آید، نه از بازنویسیِ ده‌ها
   قاعده. هر چیزی که از `--sp-*` استفاده کرده — و همه‌شان کرده‌اند —
   خودبه‌خود فشرده می‌شود. اگر قاعده‌به‌قاعده می‌نوشتیم، اولین ویجتِ
   تازه از قلم می‌افتاد.
   ──────────────────────────────────────────────────────────── */
:root[data-density='compact'] {
  --sp-1: 3px;  --sp-2: 6px;  --sp-3: 9px;
  --sp-4: 12px; --sp-5: 16px; --sp-6: 20px;
  --topbar-h: 50px;
}
:root[data-density='compact'] thead th,
:root[data-density='compact'] tbody td { padding-block: 5px; }
:root[data-density='compact'] .kvs__row,
:root[data-density='compact'] .rank__row,
:root[data-density='compact'] .tl__row,
:root[data-density='compact'] .pop__item { padding-block: 6px; }
:root[data-density='compact'] .cal__cell { min-block-size: 84px; }

/* ── ردیفِ تنظیم ───────────────────────────────────────────── */
.setrow {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-4); flex-wrap: wrap;
}
.setrow__main { min-width: 0; flex: 1 1 260px; }
.setrow__name { font-size: var(--t-sm); font-weight: 600; }
.setrow__note { font-size: var(--t-2xs); color: var(--fg-muted); line-height: 1.7; }

/* ── کلیدِ چندحالته ───────────────────────────────────────── */
.seg {
  display: inline-flex; flex-shrink: 0;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: 2px;
  gap: 2px;
}
.seg__btn {
  border: 0; background: none; cursor: pointer;
  font-family: var(--font); font-size: var(--t-2xs);
  color: var(--fg-muted);
  padding: 5px 11px;
  border-radius: var(--r-sm);
  min-block-size: 26px;
}
.seg__btn:hover { color: var(--fg); }
.seg__btn--on {
  background: var(--surface); color: var(--fg); font-weight: 700;
  box-shadow: var(--z-1);
}

/* بلوکِ کاربرِ نوارِ کناری حالا لینک است */
.side__me {
  display: flex; align-items: center; gap: 9px;
  min-width: 0; flex-grow: 1;
  color: var(--fg); text-decoration: none;
}
.side__me:hover { text-decoration: none; color: var(--accent); }
a.avatar { text-decoration: none; }
a.avatar:hover { text-decoration: none; border-color: var(--accent); }


/* ═══════════════════════ نمودارِ ماهانهٔ دوتایی (مرحله ۴۴)

   ⚠ دو میلهٔ **کنارِ هم**، نه یک میلهٔ انباشته: «دریافت» و «فاکتور»
   جمع‌شدنی نیستند — یکی پولی است که آمده و دیگری پولی که باید
   بیاید. انباشتنشان یک عددِ بی‌معنا می‌سازد.

   ⚠ ارتفاعِ درصدیِ میله والدِ **معین** می‌خواهد؛ `.mbar__pair` همان
   لایه است. (همان درسی که در `.wk__track` خوردیم.)
   ──────────────────────────────────────────────────────────── */
.mbars {
  display: flex; align-items: flex-end; gap: 6px;
  overflow-x: auto;
  padding-block-end: 2px;
}
.mbar {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  min-inline-size: 34px; flex: 1 0 34px;
}
/* مرزِ سالِ جلالی — فروردین خطِ جداکننده می‌گیرد، وگرنه هجده ماه
   یک نوارِ پیوسته است و چشم نمی‌فهمد سال کجا عوض شد. */
.mbar--year { border-inline-end: 1px solid var(--line-2); padding-inline-end: 6px; }

.mbar__pair {
  display: flex; align-items: flex-end; justify-content: center; gap: 3px;
  inline-size: 100%; block-size: 168px;
}
.mbar__b {
  flex: 1 1 0; max-inline-size: 14px; min-block-size: 2px;
  border-radius: var(--r-sm) var(--r-sm) 0 0;
  transform-origin: bottom;
  animation: dibaGrow .5s cubic-bezier(.22, .68, 0, 1.06) both;
  animation-delay: calc(var(--i, 0) * 26ms);
}
.mbar__b--recv { background: var(--accent); }
.mbar__b--inv  { background: var(--info); }
.mbar:hover .mbar__b { filter: brightness(1.14); }

.mbar__lbl {
  font-size: var(--t-3xs); color: var(--fg-faint);
  white-space: nowrap;
}


/* ═══════════════════════ کارتابلِ کارها (مرحله ۴۵)

   ⚠ چرا اجزای تازه و نه استفاده از `.rank` و `.dept`: آن دو برای
   «رتبه‌بندیِ یک عدد» ساخته شده‌اند. کار سه چیز هم‌زمان دارد —
   عنوان، اولویت، و فاصله تا مهلت — و هیچ‌کدام قابلِ حذف نیست.
   ریختنشان در `.rank` یعنی یا ستون‌ها بهم می‌ریزند یا اطلاعات
   حذف می‌شود.
   ──────────────────────────────────────────────────────────── */

/* --- نوارِ سطل‌های مهلت: یک نوارِ انباشته
   ⚠ اینجا انباشتن **درست** است، برعکسِ نمودارِ مالی: این پنج عدد
     پارتیشنِ یک مجموعه‌اند (هر کارِ باز دقیقاً در یک سطل است)، پس
     جمعشان معنا دارد و کلِ نوار «همهٔ کارهای باز» است. */
.split {
  display: flex; inline-size: 100%; block-size: 10px;
  border-radius: var(--r-full); overflow: hidden;
  background: var(--idle);
}
.split__seg {
  min-inline-size: 2px;
  animation: dibaFill .7s cubic-bezier(.4, 0, .2, 1) both;
  animation-delay: calc(var(--i, 0) * 60ms);
}
.split__seg--bad  { background: var(--bad); }
.split__seg--warn { background: var(--warn); }
.split__seg--info { background: var(--info); }
.split__seg--ok   { background: var(--accent); }
/* ⚠ «کم» با رنگِ خطِ جدول یکی بود و روی زمینهٔ `--idle` عملاً دیده
   نمی‌شد — سطرِ «بی‌مسئول» در عکس یک نوارِ خالی به نظر می‌رسید.
   خاکستریِ متن، نه خاکستریِ خط. */
.split__seg--mute { background: var(--fg-faint); }

.split-keys {
  display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4);
  margin-block-start: var(--sp-3);
}
/* ⚠ افسانه **کلیک‌شدنی نیست**، پس `cursor: pointer` هم ندارد.
   دستِ اشاره روی چیزی که کاری نمی‌کند، یک وعده‌ی دروغ است. */
.split-key {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--t-2xs); color: var(--fg-muted);
}
.split-key__dot {
  inline-size: 8px; block-size: 8px; border-radius: var(--r-full);
  flex-shrink: 0;
}
.split-key__dot--bad  { background: var(--bad); }
.split-key__dot--warn { background: var(--warn); }
.split-key__dot--info { background: var(--info); }
.split-key__dot--ok   { background: var(--accent); }
.split-key__dot--mute { background: var(--line-2); }
.split-key__n { font-variant-numeric: tabular-nums; color: var(--fg); font-weight: 600; }

/* --- فهرستِ کار */
.tsk { display: flex; flex-direction: column; }
.tsk__row {
  display: grid;
  grid-template-columns: 3px minmax(0, 1fr) auto;
  align-items: center; gap: var(--sp-3);
  padding: 9px var(--sp-4);
  border-block-end: 1px solid var(--line);
  text-decoration: none; color: inherit;
  min-inline-size: 0;
}
.tsk__row:last-child { border-block-end: 0; }
.tsk__row:hover { background: var(--surface-2); text-decoration: none; }

/* ⚠ نوارِ اولویت یک **میله‌ی رنگی** است، نه فقط رنگِ متن: در فهرستی
   با بیست سطر، رنگِ متن دیده نمی‌شود ولی لبه دیده می‌شود. */
.tsk__pri { align-self: stretch; border-radius: var(--r-full); background: var(--line-2); }
.tsk__pri--bad  { background: var(--bad); }
.tsk__pri--warn { background: var(--warn); }
.tsk__pri--info { background: var(--info); }
.tsk__pri--mute { background: var(--line-2); }

.tsk__main { min-inline-size: 0; display: flex; flex-direction: column; gap: 3px; }
.tsk__title {
  font-size: var(--t-sm); color: var(--fg); font-weight: 500;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tsk__meta {
  display: flex; flex-wrap: wrap; align-items: center; gap: 5px var(--sp-2);
  font-size: var(--t-2xs); color: var(--fg-faint);
}
.tsk__side {
  display: flex; align-items: center; gap: var(--sp-2);
  flex-shrink: 0;
}
.tsk__due {
  font-size: var(--t-2xs); color: var(--fg-muted);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.tsk__due--over { color: var(--bad); font-weight: 600; }
.tsk__due--today { color: var(--warn); font-weight: 600; }

/* --- بارِ کاری: شمار + ترکیبِ اولویت در یک سطر
   ⚠ پنج کارِ فوری با پنج کارِ کم یکی نیستند. اگر هر دو «۵» نشان
     داده شوند صفحه دروغ می‌گوید، پس ترکیب هم دیده می‌شود. */
.wl { display: flex; flex-direction: column; }
.wl__row {
  display: grid; grid-template-columns: 22px minmax(0, 1fr) auto;
  align-items: center; gap: var(--sp-3);
  padding: 10px var(--sp-4);
  border-block-end: 1px solid var(--line);
}
.wl__row:last-child { border-block-end: 0; }
.wl__pos { font-size: var(--t-2xs); color: var(--fg-faint); font-variant-numeric: tabular-nums; }
.wl__main { min-inline-size: 0; display: flex; flex-direction: column; gap: 5px; }
.wl__name {
  font-size: var(--t-sm); color: var(--fg);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.wl__side { display: flex; align-items: center; gap: var(--sp-2); flex-shrink: 0; }
.wl__n { font-size: var(--t-base); font-weight: 600; font-variant-numeric: tabular-nums; }

/* --- یک میله‌ی تنها (روندِ بسته‌شدن) */
.mbar__b--task { background: var(--accent); }
/* دو ستونِ داشبوردِ مدیریتی: «ثبت‌شده» و «بسته‌شده».
   ⚠ دو رنگِ متفاوت و نه دو سایه‌ی یک رنگ: این‌ها دو مجموعه‌ی
   متفاوت‌اند، نه دو بخشِ یک کل. */
.mbar__b--made { background: var(--info); }
.mbar__b--shut { background: var(--accent); }
.bar__fill--warn { background: var(--warn); }
.bar__fill--mute { background: var(--fg-faint); }

/* --- کارتِ تاشو: `<summary>` باید مثلِ `card__head` بنشیند و
   مثلثِ پیش‌فرضِ مرورگر را در سمتِ درست نشان دهد. */
/* ⚠ نشانِ باز/بسته **آیکونِ خودِ دیبا** است، نه مربعی با دو ضلعِ
   حاشیه. نسخه‌ی اول همان مربع بود و در راست‌به‌چپ یک «‹» درآمد —
   که یعنی «برگرد»، نه «باز کن». چرخاندنِ یک آیکونِ واقعی هم در
   RTL و هم در LTR درست است. */
summary.card__head { list-style: none; }
summary.card__head::-webkit-details-marker { display: none; }
.sum__ar {
  margin-inline-start: auto; display: inline-flex; color: var(--fg-faint);
  transform: rotate(-90deg);
  transition: transform .18s ease, color .18s ease;
}
details[open] > summary .sum__ar { transform: rotate(90deg); }
summary.card__head:hover .sum__ar { color: var(--accent); }

/* ⚠ زیرِ ۷۶۷ پیکسل ستونِ رتبه حذف می‌شود، نه اینکه فشرده شود:
   عددِ رتبه کم‌اهمیت‌ترین چیزِ سطر است و ۲۲ پیکسل در موبایل گران است. */
@media (max-width: 767px) {
  .wl__row { grid-template-columns: minmax(0, 1fr) auto; }
  .wl__pos { display: none; }
}


/* ═══════════════════════ شخصی‌سازیِ ظاهر (مرحله ۴۶)

   ⚠ چرا رنگِ تأکید فقط **دو مقدار** به‌ازای هر تم عوض می‌کند و نه
   ده تا: نگاشتِ سه‌بلوکی `--accent: var(--d-accent)` است، و
   متغیرهای CSS **در لحظهٔ مصرف** حل می‌شوند. پس اگر `--d-accent` را
   عوض کنیم، هر سه بلوک خودبه‌خود دنبالش می‌آیند. هیچ بلوکِ نگاشتِ
   چهارمی لازم نیست — و همان چیزی است که تستِ «سه بلوک یک مجموعه
   توکن دارند» را سالم نگه می‌دارد.

   ⚠ زمینه و خطِ «سبزِ موفقیت» با `color-mix` از خودِ تأکید ساخته
   می‌شود، نه با هِکسِ دستی. ده هِکسِ دست‌چین به‌ازای هر رنگ یعنی
   شصت عدد که هیچ‌کس دیگر نگهشان نمی‌دارد.
   ──────────────────────────────────────────────────────────── */

/* مشتق‌ها — برای هر تأکیدی جز پیش‌فرضِ خودِ دیبا */
:root[data-accent]:not([data-accent='green']) {
  --d-ok-bg:     color-mix(in oklab, var(--d-accent) 13%, var(--d-bg));
  --d-ok-line:   color-mix(in oklab, var(--d-accent) 34%, var(--d-bg));
  --d-cell-part: color-mix(in oklab, var(--d-accent) 55%, var(--d-bg));
  --l-ok-bg:     color-mix(in oklab, var(--l-accent) 11%, #ffffff);
  --l-ok-line:   color-mix(in oklab, var(--l-accent) 32%, #ffffff);
  --l-cell-part: color-mix(in oklab, var(--l-accent) 42%, #ffffff);
}

/* ⚠ نسخهٔ روشنِ هر رنگ **تیره‌تر** از نسخهٔ تیره است، نه همان رنگ:
   رنگی که روی زمینهٔ مشکی می‌درخشد، روی سفید کنتراستِ ۴٫۵:۱ ندارد.
   همان قاعده‌ای که از اول برای سبز رعایت شده. */
:root[data-accent='indigo'] { --d-accent: #7d8cf5; --l-accent: #3f47bd;
                              --d-on-accent: #060a18; --l-on-accent: #ffffff; }
:root[data-accent='violet'] { --d-accent: #a582f0; --l-accent: #6636b8;
                              --d-on-accent: #0d0618; --l-on-accent: #ffffff; }
:root[data-accent='teal']   { --d-accent: #46b3c4; --l-accent: #10707f;
                              --d-on-accent: #041417; --l-on-accent: #ffffff; }
:root[data-accent='amber']  { --d-accent: #d9a33f; --l-accent: #8a5d0e;
                              --d-on-accent: #170f02; --l-on-accent: #ffffff; }
:root[data-accent='rose']   { --d-accent: #e0819a; --l-accent: #a72c4d;
                              --d-on-accent: #1a060c; --l-on-accent: #ffffff; }
/* ⚠⚠ «صورتیِ باربی» — هِکسِ خودِ پرسا: #EC4399. عیناً همان در حالتِ
   تیره می‌نشیند (کنتراستِ ۵٫۴:۱ روی زمینهٔ تیره).

   ⚠ ولی در حالتِ روشن **نمی‌شود همان را گذاشت**: #EC4399 روی سفید
     فقط ۳٫۶:۱ است، زیرِ حدِ ۴٫۵:۱. یعنی دکمه‌ای که در حالتِ تیره
     خوانا بود، در حالتِ روشن نیمه‌خوانا می‌شد و کسی هم نمی‌فهمید
     چرا. #B01366 همان صورتی است با ۶٫۷۱:۱ — دقیقاً هم‌ترازِ جفتِ
     «گلی» که از قبل داشتیم. */
:root[data-accent='barbie'] { --d-accent: #ec4399; --l-accent: #b01366;
                              --d-on-accent: #1a0510; --l-on-accent: #ffffff; }

/* ═══════════ شدتِ حاله
   ⚠ چرا کنترلِ جدا و نه یک عددِ ثابت: «حاله» سلیقه است. کسی که
     پوستهٔ خاکستریِ امروز را دوست دارد باید بتواند خاموشش کند،
     وگرنه این تغییر برای او یک پس‌رفت است. */
/* ⚠⚠ جلسهٔ ۳۲ — پرسا: «توی حالتِ شب بهتر است ولی روزش نه، فقط سفید
   است؛ حالهٔ سبزش را بیشتر کن. توی حالتِ شب هم همین‌طور. ولی یک
   حالت هم بگذار که سفیدِ معمولی باشد، شاید کسی اصلاً دوست نداشته
   باشد.»

   درصدها بالا رفتند — ولی نه دلبخواهی. سقف را کنتراست تعیین کرد،
   با محاسبه روی هر سه رنگِ پرکاربرد و بدترین متن (کم‌رنگ روی
   زمینه):

       روشن  ۸٪ → ۴٫۸۷:۱ ✓      ۱۱٪ → ۴٫۵۵:۱ ✓      ۱۴٪ → ۴٫۳۱:۱ ✗
       تیره ۱۰٪ → ۵٫۴۸:۱ ✓      ۱۷٪ → ۴٫۷۶:۱ ✓      ۲۲٪ → ۴٫۳۲:۱ ✗

   یعنی «پررنگ» تا جایی رفته که هنوز خوانا بماند، نه تا جایی که
   قشنگ به‌نظر برسد. اگر روزی کسی خواست بیشترش کند، باید دوباره
   همین را بسنجد. */
:root[data-tint='off']  { --tint-d: 0%;  --tint-l: 0%; }
:root[data-tint='bold'] { --tint-d: 17%; --tint-l: 11%; }

/* ── قلم: فقط وقتی کاربر انتخاب کند بارگذاری می‌شود (app.js)،
   وگرنه چهار فایلِ قلم به هر بارگذاری می‌چسبد برای چیزی که
   احتمالاً هیچ‌وقت عوض نمی‌شود. */
/* ⚠⚠ هیچ‌کدام از این‌ها از شبکه نمی‌آید — دلیلش کنارِ خودِ
   دکمه‌ها در «base.html» نوشته شده و در مرورگر دیده شد.

   ⚠ «Vazirmatn» ته هر دو زنجیره هست: اگر تاهوما یا قلمِ سیستم
     حرفی را نداشته باشد، به قلمِ بسته‌بندی‌شدهٔ خودمان می‌افتد و
     نه به قلمی که فارسی نمی‌داند. */
:root[data-font='tahoma'] { --font: Tahoma, 'Iranian Sans', 'Vazirmatn', sans-serif; }
:root[data-font='system'] { --font: system-ui, -apple-system, 'Segoe UI', 'Vazirmatn', sans-serif; }

/* ── سبکِ نوارِ کناری */
:root[data-side-style='accent'] .side { background: var(--ok-bg); }
:root[data-side-style='accent'] .side__item--on { background: var(--surface); }
:root[data-side-style='glass'] .side {
  background: color-mix(in oklab, var(--surface) 62%, transparent);
  backdrop-filter: blur(14px);
}

/* ── نوارِ بالا: شناور یا ثابت
   ⚠ `sticky` پیش‌فرض است. اگر کسی «ثابت نباشد» را انتخاب کند،
   نوار با صفحه بالا می‌رود — که در صفحه‌های بلندِ گزارش گاهی
   همان چیزی است که آدم می‌خواهد. */
:root[data-navbar='static'] .topbar { position: static; }

/* ═══════════════════════ کشوی شخصی‌سازی */
.skin-back {
  position: fixed; inset: 0; z-index: 60;
  background: color-mix(in oklab, #000 46%, transparent);
  display: flex; justify-content: flex-end;
  animation: dibaFade .16s ease both;
}
.skin {
  inline-size: min(330px, 100%); block-size: 100%;
  background: var(--surface);
  border-inline-start: 1px solid var(--line);
  box-shadow: var(--shadow);
  display: flex; flex-direction: column;
  animation: dibaSlideIn .22s cubic-bezier(.22, .68, 0, 1.06) both;
}
/* ⚠ حرکت از لبهٔ **منطقی** می‌آید، نه از راست: در حالتِ چپ‌چین
   کشو باید از چپ بیاید، وگرنه از بیرونِ صفحه می‌پرد تو. */
@keyframes dibaSlideIn { from { transform: translateX(30px); opacity: 0; } }
:root[dir='ltr'] .skin { animation-name: dibaSlideInLtr; }
@keyframes dibaSlideInLtr { from { transform: translateX(-30px); opacity: 0; } }

.skin__head {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-4);
  border-block-end: 1px solid var(--line);
}
.skin__ico {
  inline-size: 32px; block-size: 32px; border-radius: var(--r);
  background: var(--ok-bg); color: var(--accent);
  display: flex; align-items: center; justify-content: center; flex-shrink: 0;
}
.skin__t { font-size: var(--t-sm); font-weight: 600; }
.skin__s { font-size: var(--t-2xs); color: var(--fg-faint); }
.skin__body { flex: 1 1 auto; overflow-y: auto; padding: var(--sp-4); }
.skin__foot {
  padding: var(--sp-3) var(--sp-4);
  border-block-start: 1px solid var(--line);
  display: flex; align-items: center; justify-content: space-between;
  font-size: var(--t-3xs); color: var(--fg-faint);
}
.skin__group { margin-block-end: var(--sp-5); }
.skin__label {
  display: flex; align-items: center; gap: 7px;
  font-size: var(--t-2xs); font-weight: 600; color: var(--fg-muted);
  letter-spacing: .04em; margin-block-end: var(--sp-3);
}
.skin__label svg { color: var(--accent); }
.skin__note { font-size: var(--t-3xs); color: var(--fg-faint); margin-block-start: 7px; line-height: 1.8; }

/* ── تراشه‌های رنگ */
.swatches { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.swatch-btn {
  inline-size: 34px; block-size: 34px; border-radius: var(--r);
  border: 2px solid transparent; cursor: pointer; padding: 0;
  display: flex; align-items: center; justify-content: center;
  color: #fff; position: relative;
}
.swatch-btn::after {
  content: ''; position: absolute; inset: 3px; border-radius: 5px;
  background: var(--sw);
}
.swatch-btn--on { border-color: var(--fg); }
.swatch-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ── کارت‌های انتخابیِ تصویری (حالتِ نمایش، سبکِ نوار، جهت) */
.picks { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-2); }
.picks--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.picks--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }

/* ⚠⚠ نمونهٔ زندهٔ اندازه — همان تکه‌ای که شکایتِ «اصلاً تغییری
   نمی‌کنه» را حل کرد.

   کشوی شخصی‌سازی روی صفحه می‌افتد و متنِ پشتش را می‌پوشاند؛ کاربر
   کلید را می‌زند و جایی را نمی‌بیند که عوض شده باشد. این سطر
   **داخلِ خودِ کشو** و با همان توکن‌ها رندر می‌شود، پس همان لحظه
   بزرگ می‌شود. */
.skin__demo {
  margin-block-start: var(--sp-3);
  padding: var(--sp-3);
  border: 1px dashed var(--line-2); border-radius: var(--r);
  background: var(--surface-2);
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px;
  font-size: var(--t-md);
}
.skin__demo-t { color: var(--fg-muted); }
.pick {
  border: 1px solid var(--line-2); border-radius: var(--r);
  background: var(--surface-2); cursor: pointer; padding: 7px;
  display: flex; flex-direction: column; gap: 6px; align-items: stretch;
  font-family: inherit; color: var(--fg-muted); font-size: var(--t-3xs);
  position: relative;
}
.pick:hover { border-color: var(--line-2); color: var(--fg); }
.pick--on { border-color: var(--accent); color: var(--fg); }
.pick--on::after {
  content: '✓'; position: absolute; inset-block-start: 3px; inset-inline-end: 5px;
  font-size: var(--t-3xs); color: var(--accent); font-weight: 700;
}
/* پیش‌نمایشِ کوچکِ چیدمان — دو ستون، مثلِ خودِ دیبا */
.pick__mini {
  block-size: 38px; border-radius: 5px; overflow: hidden;
  display: flex; border: 1px solid var(--line);
}
.pick__mini i { display: block; block-size: 100%; }
.pick__mini i:first-child { inline-size: 34%; }
.pick__mini i:last-child  { inline-size: 66%; }
.pick__cap { text-align: center; }

/* ── سوییچ‌های ساده‌ی کشو */
.skin__row {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-3); padding-block: 7px;
}
.skin__rowt { font-size: var(--t-xs); color: var(--fg); }
.skin__rows { font-size: var(--t-3xs); color: var(--fg-faint); }

/* «Aa» برای انتخابِ قلم */
.pick__aa { font-size: var(--t-xl); text-align: center; line-height: 1.3; color: var(--fg); }

/* ⚠ تمام‌صفحه و جهت روی موبایل پنهانند: در مرورگرِ موبایل تمام‌صفحه
   یا کار نمی‌کند یا کارِ عجیب می‌کند، و ۶۰ پیکسل در نوارِ تنگ گران
   است. همان درسی که کلیدِ «؟» در جلسه ۱۷ داد — دو کلیدِ اضافه، ۲۲
   پیکسل لولشِ افقی ساخت. */
@media (max-width: 767px) {
  .topbar__wide { display: none; }
}
/* کلیدِ شخصی‌سازی کمی برجسته‌تر از بقیه — تنها کلیدِ نوار که
   «یک چیزِ تازه» است و باید کشف شود. */
.iconbtn--on { background: var(--ok-bg); color: var(--accent); border-color: var(--ok-line); }
.iconbtn--on:hover { color: var(--accent); }

/* ═══════════════════════ متنِ فارسی در چیدمانِ چپ‌چین

   ⚠ عکسِ خودِ صفحه این را نشان داد: با `dir="ltr"` کلِ چیدمان درست
   آینه شد (سودِ قاعده‌ی خصوصیت‌های منطقی)، ولی **جمله‌های فارسی**
   هم چپ‌چین شدند و عددها به سرِ غلط پریدند — «۹ نفر شاغل» شد
   «نفر شاغل ۹».

   چاره `unicode-bidi: plaintext` است: جهتِ هر بلوکِ متن از **اولین
   حرفِ خودش** تعیین می‌شود. پس جمله‌ی فارسی راست‌به‌چپ می‌ماند و
   جمله‌ی آلمانی چپ‌به‌راست — در حالی که جعبه‌ها چپ‌چین‌اند.

   ⚠ فقط روی نگه‌دارنده‌های **بلوکیِ** متن، نه روی هر span: روی
   عنصرهای درون‌خطیِ تودرتو، هر تکه جهتِ خودش را می‌گیرد و جمله
   تکه‌تکه می‌شود.

   ⚠ و `.num` دست‌نخورده می‌ماند: آن عمداً `isolate` است، نه
   `plaintext` — «۰۷:۳۰» همیشه باید چپ‌به‌راست بماند.             */
:root[dir='ltr'] :is(h1, h2, h3, h4, h5, p, td, th, dt, dd, li,
                     summary, label, figcaption, option) {
  unicode-bidi: plaintext;
}
:root[dir='ltr'] :is(
  .phead__title, .phead__sub, .tile__label, .tile__note, .tile__sub,
  .card__head, .card__foot, .note,
  .tsk__title, .wl__name, .rank__name, .dept__name, .setrow__note,
  .empty, .skin__note, .toast__text, .pop__item, .auth__sub, .auth__tag
) {
  unicode-bidi: plaintext;
}
/* ⚠ برچسبِ منو **مستثناست**، و باز هم عکسِ صفحه نشانم داد: با
   `plaintext` متنِ یک‌کلمه‌ایِ برچسب راست‌چین می‌شد و از آیکونش جدا
   می‌افتاد — آیکون در چپِ نوار، برچسب در راستِ نوار، و بینشان یک
   دره. برچسبِ یک‌کلمه‌ای اصلاً مسأله‌ی جهتِ جمله ندارد. */
:root[dir='ltr'] :is(.side__label, .side__head) { unicode-bidi: isolate; }

/* میله‌ی «لغو / تعویق» در نمودارِ جلسات — کم‌رنگ‌تر از بدِ توپر،
   چون لغو یک خطا نیست، یک واقعیت است که باید دیده شود. */
.mbar__b--off { background: var(--bad); opacity: .42; }


/* ═══════════════════════ صندوقِ نامه (مرحله ۴۸)

   ⚠ سه ستون: پوشه‌ها · فهرست · متن. همان الگویی که هر برنامه‌ی
   نامه‌ای دارد و کاربر از قبل بلد است.

   ⚠ ارتفاعِ ثابت با پیمایشِ داخلی، نه پیمایشِ کلِ صفحه: اگر فهرست
   صفحه را بلند کند، ستونِ متن از دید خارج می‌شود و کاربر باید
   بالا و پایین برود — همان چیزی که خواندنِ نامه را آزاردهنده
   می‌کند.
   ──────────────────────────────────────────────────────────── */
.mail {
  display: grid;
  grid-template-columns: 190px minmax(280px, 1fr) minmax(0, 1.35fr);
  gap: var(--sp-4);
  align-items: start;
  min-inline-size: 0;
}
.mail__side { display: flex; flex-direction: column; gap: var(--sp-5); }
.mail__folders { display: flex; flex-direction: column; gap: 2px; }
.mail__folder {
  display: flex; align-items: center; gap: 9px;
  padding: 8px 10px; border-radius: var(--r);
  color: var(--fg-muted); text-decoration: none;
  font-size: var(--t-xs);
}
.mail__folder:hover { background: var(--surface-2); color: var(--fg); text-decoration: none; }
.mail__folder--on { background: var(--ok-bg); color: var(--accent); font-weight: 600; }
.mail__fname { flex: 1; min-inline-size: 0; }
.mail__fn {
  font-size: var(--t-3xs); padding: 1px 6px; border-radius: var(--r-full);
  background: var(--idle); color: var(--fg-muted);
}
.mail__folder--on .mail__fn { background: var(--accent); color: var(--on-accent); }

.mail__labels { display: flex; flex-direction: column; gap: 2px; }
.mail__label {
  display: flex; align-items: center; gap: 9px;
  padding: 6px 10px; border-radius: var(--r);
  color: var(--fg-muted); text-decoration: none; font-size: var(--t-2xs);
}
.mail__label:hover { background: var(--surface-2); color: var(--fg); text-decoration: none; }
.mail__label--on { background: var(--surface-2); color: var(--fg); font-weight: 600; }
/* ⚠ برچسبِ بی‌پیام کم‌رنگ می‌شود ولی **حذف نمی‌شود**: کاربر باید
   بداند چه برچسب‌هایی وجود دارد تا هنگامِ نوشتن انتخابشان کند. */
.mail__label--off { opacity: .5; }
.mail__dot {
  inline-size: 8px; block-size: 8px; border-radius: var(--r-full); flex-shrink: 0;
}
.mail__dot--work       { background: var(--info); }
.mail__dot--finance    { background: var(--accent); }
.mail__dot--membership { background: var(--warn); }
.mail__dot--urgent     { background: var(--bad); }
.mail__dot--personal   { background: var(--fg-faint); }

.mail__list, .mail__read { block-size: min(74vh, 760px); display: flex; flex-direction: column; }
.mail__scroll { overflow-y: auto; flex: 1 1 auto; }
.mail__read .card__body { overflow-y: auto; flex: 1 1 auto; }

/* ── سطرِ نامه */
.mrow {
  display: grid; gap: 3px;
  padding: 11px var(--sp-4);
  border-block-end: 1px solid var(--line);
  text-decoration: none; color: inherit;
  border-inline-start: 3px solid transparent;
}
.mrow:hover { background: var(--surface-2); text-decoration: none; }
.mrow--on { background: var(--ok-bg); border-inline-start-color: var(--accent); }
.mrow__top { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-3); }
.mrow__who { font-size: var(--t-xs); color: var(--fg-muted); }
/* ⚠ نخوانده با **وزنِ قلم** مشخص می‌شود، نه فقط با یک نقطه‌ی رنگی:
   نقطه در فهرستِ بیست‌سطری گم می‌شود، وزن نه. */
.mrow--unread .mrow__who,
.mrow--unread .mrow__subj { color: var(--fg); font-weight: 600; }
.mrow__time { font-size: var(--t-3xs); color: var(--fg-faint); flex-shrink: 0; }
.mrow__subj {
  font-size: var(--t-sm); color: var(--fg);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mrow__star { color: var(--warn); }
.mrow__prev {
  font-size: var(--t-2xs); color: var(--fg-faint);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mrow__tags { display: flex; gap: 5px; margin-block-start: 3px; }

.mtag {
  font-size: var(--t-3xs); padding: 1px 7px; border-radius: var(--r-full);
  border: 1px solid var(--line-2); color: var(--fg-muted);
}
.mtag--work       { background: var(--info-bg); border-color: var(--info-line); color: var(--info); }
.mtag--finance    { background: var(--ok-bg);   border-color: var(--ok-line);   color: var(--accent); }
.mtag--membership { background: var(--warn-bg); border-color: var(--warn-line); color: var(--warn); }
.mtag--urgent     { background: var(--bad-bg);  border-color: var(--bad-line);  color: var(--bad); }

.mail__from { display: flex; align-items: center; gap: 11px; }
.mail__blank {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 7px; color: var(--fg-faint); text-align: center; flex: 1 1 auto;
}
.mail__blankico { color: var(--line-2); }
.mail__blankt { font-size: var(--t-md); color: var(--fg-muted); }

/* ⚠ زیرِ ۱۱۰۰ پیکسل ستونِ متن **زیرِ** فهرست می‌رود، نه اینکه
   باریک شود: سه ستونِ ۲۰۰ پیکسلی از هیچ‌کدام خواندنی نیست. */
@media (max-width: 1100px) {
  .mail { grid-template-columns: 170px minmax(0, 1fr); }
  .mail__read { grid-column: 1 / -1; block-size: auto; }
}
@media (max-width: 767px) {
  .mail { grid-template-columns: minmax(0, 1fr); }
  .mail__side { flex-direction: row; flex-wrap: wrap; gap: var(--sp-3); }
  .mail__folders { flex-direction: row; flex-wrap: wrap; }
  .mail__labels { flex-direction: row; flex-wrap: wrap; }
  .mail__list { block-size: auto; max-block-size: 60vh; }
}


/* ═══════════════════════ گفت‌وگو — بازسازیِ جلسه ۲۰

   ⚠ اجزای تازه‌ی این بخش کنارِ اجزای قبلی می‌نشینند، نه جایشان:
   `.chat`، `.msg` و `.msg__bubble` از جلسه ۱۷ همان‌طور می‌مانند و
   فقط چیزی که واقعاً تازه است اضافه می‌شود. بازنویسیِ چیزی که
   کار می‌کند، فقط راهی برای شکستنِ آن است.
   ──────────────────────────────────────────────────────────── */

/* ارتفاع بیشتر می‌شود چون نوارِ نویسنده حالا چند لایه دارد */
.chat { height: calc(100vh - var(--topbar-h) - 150px); }

/* ── سطرِ گفت‌وگو در فهرست */
.trow {
  display: flex; align-items: center; gap: 9px;
  padding: 8px 9px; border-radius: var(--r);
  color: var(--fg); text-decoration: none;
  animation: dibaListIn .3s ease-out both;
  animation-delay: calc(var(--i, 0) * 25ms);
  transition: background-color .15s ease;
}
.trow:hover { background: var(--surface-2); text-decoration: none; }
.trow--on { background: var(--ok-bg); }
.trow__av { position: relative; flex-shrink: 0; }
.trow__main { min-inline-size: 0; flex: 1; display: flex; flex-direction: column; gap: 2px; }
.trow__top { display: flex; align-items: center; gap: 6px; }
.trow__name {
  font-size: var(--t-sm); font-weight: 600; flex: 1; min-inline-size: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  display: flex; align-items: center; gap: 5px;
}
.trow__name svg { color: var(--fg-faint); flex-shrink: 0; }
.trow__sub { font-size: var(--t-3xs); color: var(--fg-faint); }
.trow__n {
  font-size: var(--t-3xs); background: var(--accent); color: var(--on-accent);
  border-radius: var(--r-full); padding: 1px 6px; font-weight: 700; flex-shrink: 0;
}

/* ── چراغِ آنلاین
   ⚠ روی آواتار می‌نشیند نه کنارِ نام: چشم آواتار را اول می‌بیند، و
   نقطه‌ای که کنارِ متن باشد در فهرستِ ده‌سطری گم می‌شود.

   ⚠ حلقه‌ی هم‌رنگِ زمینه لازم است، وگرنه نقطه‌ی سبز روی آواتارِ سبز
   دیده نمی‌شود. */
/* ⚠ نشانِ «زنده» در جمله، از `.dot` می‌آید و **نه** از `.dotlive`.
   آن یکی `position: absolute` است و نشانِ آنلاین‌بودن روی آواتارِ
   گفت‌وگوست؛ گذاشتنش در یک خطِ متن، نقطه را به گوشه‌ی کارت
   می‌فرستاد — در عکسِ صفحه دیده شد، نه در کد. */
.phead__sub .dot { display: inline-block; vertical-align: middle; }
.dotlive {
  position: absolute; inset-block-end: -1px; inset-inline-end: -1px;
  inline-size: 11px; block-size: 11px; border-radius: var(--r-full);
  background: var(--fg-faint);
  box-shadow: 0 0 0 2px var(--surface);
  transition: background-color .2s ease;
}
.dotlive--on { background: #3ecf8e; }

/* ── نوارِ «دارد می‌نویسد»
   ⚠ جای ثابت دارد و فقط پنهان می‌شود: نوشته‌ای که با آمدنش
   پیام‌ها را چند پیکسل بالا و پایین ببرد، آزاردهنده است — و
   همان لحظه اتفاق می‌افتد که کاربر می‌خواهد بخواند. */
.typing {
  display: flex; align-items: center; gap: 8px;
  padding: 5px var(--sp-4); min-block-size: 26px;
  font-size: var(--t-2xs); color: var(--accent);
  border-block-start: 1px solid var(--line); flex-shrink: 0;
}
.typing__dots { display: inline-flex; gap: 3px; }
.typing__dots i {
  inline-size: 5px; block-size: 5px; border-radius: var(--r-full);
  background: currentColor; animation: dibaPulse 1.1s ease-in-out infinite;
}
.typing__dots i:nth-child(2) { animation-delay: .18s; }
.typing__dots i:nth-child(3) { animation-delay: .36s; }

/* ── نوارِ نویسنده */
.composer {
  border-block-start: 1px solid var(--line); flex-shrink: 0;
  position: relative;
}
.composer__row {
  display: flex; gap: 6px; align-items: flex-end;
  padding: 9px var(--sp-3);
}
.composer__row textarea {
  flex: 1; min-inline-size: 0; resize: none;
  max-block-size: 150px; min-block-size: 34px;
  font-size: var(--t-sm); line-height: 1.7;
}

.reply-bar, .picks-bar, .rec {
  display: flex; align-items: center; gap: 8px;
  padding: 7px var(--sp-3);
  border-block-end: 1px solid var(--line);
  background: var(--surface-2);
  font-size: var(--t-2xs);
}
.reply-bar__bar {
  inline-size: 3px; align-self: stretch; border-radius: var(--r-full);
  background: var(--accent); flex-shrink: 0;
}
.reply-bar__main { min-inline-size: 0; display: flex; flex-direction: column; }
.reply-bar__main b { font-size: var(--t-2xs); color: var(--accent); }
.reply-bar__main span {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.picks-bar { flex-wrap: wrap; }
.pickchip {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--surface); border: 1px solid var(--line-2);
  border-radius: var(--r-full); padding: 3px 4px 3px 10px;
  max-inline-size: 240px;
}
.pickchip__n {
  font-size: var(--t-2xs); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; max-inline-size: 130px;
}
.pickchip__s { font-size: var(--t-3xs); color: var(--fg-faint); }
.pickchip__x {
  border: 0; background: var(--idle); color: var(--fg-muted); cursor: pointer;
  inline-size: 18px; block-size: 18px; border-radius: var(--r-full);
  line-height: 1; font-size: var(--t-sm);
}
.pickchip__x:hover { background: var(--bad-bg); color: var(--bad); }

/* ── ضبطِ ویس */
.rec { color: var(--bad); }
.rec__dot {
  inline-size: 9px; block-size: 9px; border-radius: var(--r-full);
  background: var(--bad); animation: dibaPulse 1s ease-in-out infinite;
}

/* ── انتخاب‌گرِ ایموجی
   ⚠ `position: absolute` روی نوارِ نویسنده، نه در جریانِ صفحه:
   باز شدنش نباید پیام‌ها را جابه‌جا کند. */
.emo {
  position: absolute; inset-block-end: 100%; inset-inline-start: var(--sp-3);
  inline-size: min(320px, calc(100% - var(--sp-4)));
  max-block-size: 240px; overflow-y: auto;
  background: var(--surface); border: 1px solid var(--line-2);
  border-radius: var(--r-lg); box-shadow: var(--shadow);
  padding: 8px; z-index: 30;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(32px, 1fr)); gap: 2px;
  animation: dibaPop .16s ease-out both;
}
.emo__i {
  border: 0; background: none; cursor: pointer;
  /* ⚠ از مقیاس، نه عددِ دلبخواه: تستِ
     `test_font_sizes_come_from_the_scale` همان لحظه ۱۹px را گرفت. */
  font-size: var(--t-xl); line-height: 1; padding: 5px; border-radius: var(--r-sm);
}
.emo__i:hover { background: var(--surface-2); }

/* ── پانویسِ حباب */
.msg { position: relative; }
.msg__foot {
  display: flex; align-items: center; gap: 7px;
  align-self: flex-end; margin-block-start: 1px;
}
.msg__edited { font-size: var(--t-3xs); color: var(--fg-faint); font-style: italic; }
.msg__text { white-space: pre-wrap; }

/* ⚠ دکمه‌ی منو تا وقتی ماوس روی پیام نیامده **دیده نمی‌شود** ولی
   جایش را می‌گیرد: اگر `display: none` بود، ظاهر شدنش حباب را
   جابه‌جا می‌کرد. و در لمس همیشه دیده می‌شود، چون hover نیست. */
.msg__more {
  border: 0; background: none; cursor: pointer;
  color: var(--fg-faint); font-size: var(--t-md); line-height: 1;
  padding: 2px 4px; border-radius: var(--r-sm);
  opacity: 0; transition: opacity .15s ease;
  align-self: center; flex-shrink: 0;
}
.msg:hover .msg__more, .msg__more:focus-visible { opacity: 1; }
.msg__more:hover { background: var(--surface-2); color: var(--fg); }
@media (hover: none) { .msg__more { opacity: .6; } }

.msg__edit {
  font-size: var(--t-sm); line-height: 1.7; resize: none;
  min-inline-size: 220px; inline-size: 100%;
}

/* پیامی که با «پاسخ» پیدا شده، یک لحظه برق می‌زند */
.msg--flash .msg__bubble {
  animation: dibaFlash 1.2s ease-out both;
}
@keyframes dibaFlash {
  0%, 30% { box-shadow: 0 0 0 2px var(--accent); }
  100% { box-shadow: 0 0 0 0 transparent; }
}

.msg--ghost .msg__bubble { opacity: .72; }
.msg--failed .msg__bubble { border-color: var(--bad-line); background: var(--bad-bg); }

/* ── قابِ «پاسخ به» داخلِ حباب */
.quote {
  display: flex; flex-direction: column; gap: 1px;
  border-inline-start: 3px solid var(--accent);
  padding-inline-start: 8px; margin-block-end: 4px;
  cursor: pointer; min-inline-size: 0;
}
.quote__who { font-size: var(--t-3xs); color: var(--accent); }
.quote__body {
  font-size: var(--t-2xs); color: var(--fg-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  max-inline-size: 260px;
}

/* ── واکنش‌ها */
.reacts { display: flex; flex-wrap: wrap; gap: 4px; }
.reacts:empty { display: none; }
.re {
  display: inline-flex; align-items: center; gap: 4px;
  border: 1px solid var(--line-2); background: var(--surface);
  border-radius: var(--r-full); padding: 1px 7px; cursor: pointer;
  font-size: var(--t-2xs); color: var(--fg-muted);
  line-height: 1.6;
}
.re:hover { border-color: var(--accent); }
/* ⚠ واکنشِ **خودم** پررنگ است: بی این نشان، کاربر نمی‌داند کدام را
   خودش زده و کلیکِ دوم برمی‌داردش. */
.re--mine { background: var(--ok-bg); border-color: var(--accent); color: var(--accent); }

/* ── واکنشِ سریع و منوی پیام */
.mmenu {
  position: fixed; z-index: 55;
  inline-size: 216px;
  background: var(--surface); border: 1px solid var(--line-2);
  border-radius: var(--r-lg); box-shadow: var(--shadow);
  padding: 6px; display: flex; flex-direction: column; gap: 1px;
  animation: dibaPop .14s ease-out both;
}
.mmenu__quick {
  display: flex; gap: 2px; padding: 2px 2px 6px;
  border-block-end: 1px solid var(--line); margin-block-end: 4px;
  flex-wrap: wrap;
}
.qre {
  border: 0; background: none; cursor: pointer;
  font-size: var(--t-lg); line-height: 1; padding: 5px; border-radius: var(--r-sm);
}
.qre:hover { background: var(--surface-2); transform: scale(1.15); }
.mmenu__item {
  border: 0; background: none; cursor: pointer; text-align: start;
  font-family: inherit; font-size: var(--t-xs); color: var(--fg);
  padding: 7px 9px; border-radius: var(--r-sm);
}
.mmenu__item:hover { background: var(--surface-2); }
.mmenu__item--bad { color: var(--bad); }
.mmenu__item--bad:hover { background: var(--bad-bg); }

/* ── پیوست در حباب */
.att-img { display: block; border-radius: var(--r); overflow: hidden; max-inline-size: 300px; }
.att-img img { display: block; inline-size: 100%; block-size: auto; }
.att-video { max-inline-size: 320px; border-radius: var(--r); display: block; }
.att-voice { display: flex; align-items: center; gap: 8px; }
.att-voice audio { block-size: 34px; max-inline-size: 240px; }
.att-voice__t { font-size: var(--t-3xs); color: var(--fg-faint); }

.att-doc {
  display: flex; align-items: center; gap: 9px;
  background: var(--surface); border: 1px solid var(--line-2);
  border-radius: var(--r); padding: 8px 10px;
  text-decoration: none; color: var(--fg); max-inline-size: 280px;
}
.att-doc:hover { border-color: var(--accent); text-decoration: none; }
.att-doc__ico {
  inline-size: 30px; block-size: 30px; border-radius: var(--r-sm);
  background: var(--ok-bg); color: var(--accent);
  display: flex; align-items: center; justify-content: center;
  font-size: var(--t-lg); flex-shrink: 0;
}
.att-doc__main { min-inline-size: 0; display: flex; flex-direction: column; }
.att-doc__name {
  font-size: var(--t-2xs); font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  /* ⚠ نامِ فایل تقریباً همیشه **دوجهته** است: «گزارش-مالی.xlsx».
     بی این خط، پسوند به سرِ غلط می‌پرد و در عکسِ خودِ صفحه دیده شد
     که نوشته «xlsx.گزارش-مالی». `plaintext` جهت را از اولین حرفِ
     خودِ نام می‌گیرد — همان چاره‌ی جلسه ۱۹ برای متنِ چپ‌چین. */
  unicode-bidi: plaintext;
}
.att-doc__size { font-size: var(--t-3xs); color: var(--fg-faint); }

/* ── حلقه‌ی درصدِ آپلود
   ⚠ همان چیزی که پرسا خواست: «قابلیت لودینگ چرخشی‌اش را داشته باشد
   که معلوم بشه چه‌قدر داره لود می‌شه». درصدِ **واقعی** است، از
   `xhr.upload.onprogress` — نه یک چرخنده‌ی تزئینی که همیشه
   می‌چرخد و هیچ نمی‌گوید. */
.prog { position: relative; inline-size: 46px; block-size: 46px; }
.prog__ring { inline-size: 46px; block-size: 46px; transform: rotate(-90deg); }
.prog__bg { fill: none; stroke: var(--idle); stroke-width: 4; }
.prog__fg {
  fill: none; stroke: var(--accent); stroke-width: 4; stroke-linecap: round;
  stroke-dasharray: 0 100;
  transition: stroke-dasharray .2s linear;
}
.prog__pct {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: var(--t-3xs); font-weight: 700; color: var(--accent);
}

/* ── کشیدن‌و‌رها */
.chat--drop .chat__room { outline: 2px dashed var(--accent); outline-offset: -4px; }

.chat__loadmore { align-self: center; margin-block-end: 4px; }

/* ⚠ روی موبایل، فهرست و اتاق زیرِ هم؛ و منوی پیام تمام‌عرض می‌شود
   چون ۲۱۶ پیکسل روی ۳۷۵ ممکن است از لبه بزند بیرون. */
@media (max-width: 860px) {
  .chat { height: auto; }
  .chat__list { max-block-size: 260px; }
  .chat__room { min-block-size: 60vh; }
}
@media (max-width: 480px) {
  .mmenu { inline-size: calc(100vw - 24px); }
  .att-img, .att-video, .att-doc { max-inline-size: 100%; }
}

/* نشانِ «همین حالا تازه شد» روی کاشی‌های مالی — عددی که بی‌خبر عوض
   شود، کاربر فکر می‌کند اشتباه خوانده بود. */
.live--pulse .tile { animation: dibaLive 1.4s ease-out both; }
@keyframes dibaLive {
  0% { box-shadow: inset 0 0 0 2px var(--accent); }
  100% { box-shadow: inset 0 0 0 0 transparent; }
}

/* ═══════════════════════════════ پروژه‌ها، اشاره‌ها و پذیرش (جلسه ۲۱) */

.projs {
  display: grid; gap: var(--sp-3);
  grid-template-columns: repeat(auto-fill, minmax(290px, 1fr));
}
.proj {
  display: flex; flex-direction: column; gap: 9px;
  padding: var(--sp-3);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-lg); color: inherit; text-decoration: none;
  transition: border-color .15s ease, transform .15s ease;
}
.proj:hover {
  border-color: var(--accent-line); text-decoration: none;
  transform: translateY(-1px);
}
.proj__top { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.proj__title { font-size: var(--t-sm); line-height: 1.5; }
.proj__detail {
  margin: 0; font-size: var(--t-2xs); line-height: 1.7;
  /* ⚠ شرحِ پروژه در todo گاهی آلمانی است و گاهی فارسی. `plaintext`
     جهت را از خودِ متن می‌گیرد، نه از قالب. */
  unicode-bidi: plaintext;
}
.proj__prog { margin-block-start: auto; }
.proj__progtop {
  display: flex; justify-content: space-between; align-items: baseline;
  margin-block-end: 5px;
}
.proj__foot { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.proj__dates {
  display: flex; gap: 6px; font-size: var(--t-3xs);
  padding-block-start: 7px; border-block-start: 1px solid var(--line);
}

/* نوارِ پیشرفتِ ساده — یک شکل، همه‌جا

   ⚠ `display: block` صریح است، و لازم: نوار روی `<div>` نوشته شده
   بود و کار می‌کرد، ولی وقتی در جلسه ۲۳ روی یک `<span>` گذاشتیم
   عرضش صفر شد و میله‌ی درصدش از قابِ کارت بیرون زد. اندازه‌ی
   بلوکیِ یک عنصرِ درون‌خطی اعمال نمی‌شود. */
.bar {
  display: block;
  block-size: 6px; border-radius: var(--r-full);
  background: var(--surface-2); overflow: hidden;
}
.bar > i {
  display: block; block-size: 100%; background: var(--accent);
  border-radius: var(--r-full);
  transition: inline-size .4s cubic-bezier(.22,1,.36,1);
}
.bar--lg { block-size: 9px; }
/* ⚠ لحنِ میله‌ی تنها. `.bar > i` است نه `.bar__fill`: آن‌یکی در
   `.dept` یک `div` است و `height: 100%`ِ عنصرِ درون‌خطی کار نمی‌کند
   — همین یک بار روی داشبوردِ مدیریتی میله را نامرئی کرد. */
.bar--bad  > i { background: var(--bad); }
.bar--info > i { background: var(--info); }

/* ── نشانِ اشاره روی سطرِ کار */
.men {
  font-size: var(--t-3xs); padding: 2px 6px; border-radius: var(--r-sm);
  background: var(--ok-bg); color: var(--accent); font-weight: 600;
  /* ⚠ نامِ فارسی با `@`ِ لاتین کنارِ هم: بی این، `@` به آخرِ نام
     می‌پرد چون کاراکترِ خنثی است. */
  unicode-bidi: plaintext;
}

/* ── انتخاب‌گرِ اشاره در فرم */
.men-field { position: relative; }
.men-picks { display: flex; gap: 5px; flex-wrap: wrap; }
.men-picks:not(:empty) { margin-block-end: 6px; }
.men-chip {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--t-2xs); padding: 4px 8px; border-radius: var(--r-full);
  background: var(--ok-bg); border: 1px solid var(--accent-line);
  color: var(--accent); unicode-bidi: plaintext;
}
.men-chip button {
  border: 0; background: none; color: inherit; cursor: pointer;
  font-size: var(--t-xs); line-height: 1; padding: 0;
  /* ⚠ ۴۴ پیکسل لمسی اینجا نمی‌شود — تراشه خودش ۲۶ پیکسل است.
     در عوض ناحیه‌ی کلیک با padding منفی کمی بزرگ شده. */
  margin-inline-start: 2px;
}
.men-drop {
  position: absolute; inset-inline: 0; inset-block-start: 100%;
  z-index: 40; margin-block-start: 4px;
  max-block-size: 264px; overflow-y: auto;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r); box-shadow: var(--shadow);
}
.men-opt {
  display: flex; align-items: center; gap: 9px;
  padding: 8px 11px; cursor: pointer; border: 0; inline-size: 100%;
  background: none; color: inherit; text-align: start;
  border-block-end: 1px solid var(--line);
}
.men-opt:last-child { border-block-end: 0; }
.men-opt:hover, .men-opt[aria-selected='true'] { background: var(--surface-2); }
.men-opt__main { min-inline-size: 0; flex-grow: 1; }
.men-opt__main b { display: block; font-size: var(--t-xs); }
.men-opt__main small {
  display: block; font-size: var(--t-3xs); color: var(--fg-faint);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  unicode-bidi: plaintext;
}
.men-empty { padding: 11px; font-size: var(--t-2xs); color: var(--fg-faint); }

/* ── کارتِ زمینه‌ی اشاره روی پرونده‌ی کار */
.mens {
  display: grid; gap: var(--sp-2); margin-block-end: var(--sp-3);
  /* ⚠ `auto-fit` و نه `auto-fill`: با دو کارت در یک شبکه‌ی
     چهارستونی، `auto-fill` دو ستونِ خالی نگه می‌دارد و کارت‌ها به
     گوشه می‌چسبند. `auto-fit` ستون‌های خالی را جمع می‌کند و
     کارت‌ها عرضِ کامل می‌گیرند. */
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}
.men-card {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 11px 13px; border-radius: var(--r-lg);
  background: var(--surface); border: 1px solid var(--line);
}
.men-card__main { min-inline-size: 0; display: flex; flex-direction: column; gap: 3px; }
.men-card__main b { font-size: var(--t-sm); }
.men-card__row { display: flex; gap: 5px; flex-wrap: wrap; margin-block-start: 4px; }

/* ── نوارِ «می‌پذیرم / برمی‌گردانم» */
.claim {
  display: flex; align-items: center; gap: 11px; flex-wrap: wrap;
  padding: 12px 14px; margin-block-end: var(--sp-3);
  border-radius: var(--r-lg);
  background: var(--warn-bg); border: 1px solid var(--warn-line);
}
.claim__ico { color: var(--warn); display: inline-flex; flex-shrink: 0; }
.claim__main {
  display: flex; flex-direction: column; gap: 2px;
  /* ⚠ `min-inline-size: 0` چون در فلکس پیش‌فرضِ `min-width: auto`
     است و متنِ بلند نوار را از ۳۷۵ پیکسل بیرون می‌زند. همان
     خانواده‌ی باگی که در جلسه‌های ۱۷ و ۲۰ خوردیم. */
  min-inline-size: 0; flex-grow: 1;
}
.inp--sm { height: 30px; font-size: var(--t-2xs); max-inline-size: 190px; }

/* ⚠ ردیف‌های «فوری و زیاد» نشانِ چپِ قرمز می‌گیرند — تفاوتِ دو
   دسته باید در یک نگاه دیده شود، نه با خواندنِ عنوانِ کارت. */
.tsk--hot .tsk__row { border-inline-start: 2px solid var(--bad); }

/* ⚠ کارِ بسته‌شده‌ی «امروز انجام دادم» کم‌رنگ‌تر است ولی خط نمی‌خورد:
   خطِ روی متن در فارسی روی نقطه‌ها می‌افتد و ردیف را ناخوانا می‌کند. */
.tsk--done .tsk__row { opacity: .62; }
.tsk--done .tsk__row:hover { opacity: 1; }

/* ── یادآوری‌های من (داشبوردِ من)
   ⚠ ستونِ زمان **ثابت‌عرض** است تا تاریخ‌ها زیرِ هم بنشینند؛ فهرستِ
   یادآوری بدونِ ستونِ منظمِ زمان، فهرستِ کار می‌شود نه تقویم. */
.rem { display: flex; flex-direction: column; }
.rem__row {
  display: flex; align-items: flex-start; gap: var(--sp-3);
  padding: 10px 14px; border-block-start: 1px solid var(--line);
  color: inherit; text-decoration: none;
}
.rem__row:first-child { border-block-start: 0; }
.rem__row:hover { background: var(--surface-2); }
.rem__when {
  flex-shrink: 0; inline-size: 96px;
  display: flex; flex-direction: column; gap: 2px;
  font-size: var(--t-2xs); color: var(--fg-muted);
}
.rem__hm { font-weight: 600; color: var(--fg); font-variant-numeric: tabular-nums; }
.rem__main { min-inline-size: 0; flex-grow: 1; display: flex; flex-direction: column; gap: 3px; }
.rem__title { font-size: var(--t-xs); font-weight: 500; }
.rem__due { font-size: var(--t-3xs); color: var(--fg-faint); }
/* سررسیده‌ای که هنوز فرستاده نشده — نه خطا، ولی باید دیده شود. */
.rem__row--past { background: var(--warn-bg); }

/* ── یادداشتِ شخصی: همان `.nt`ِ یادداشتِ رویداد، با دو افزوده */
.nt:first-child { border-block-start: 0; }
.nt--pin { background: var(--surface-2); }
.nt__pin { display: inline-flex; color: var(--accent); }
/* ⚠ دکمه‌های ویرایش/حذف در ستونِ باریکِ داشبوردِ من زیرِ هم می‌افتادند
   و ردیفِ یادداشت را بلند می‌کردند. `flex-shrink: 0` و `nowrap` نگهشان
   می‌دارد؛ عکسِ صفحه این را نشان داد، نه تست. */
.nt > .inline { flex-shrink: 0; flex-wrap: nowrap; align-self: flex-start; }
.nt__body {
  font-size: var(--t-xs); line-height: 1.85; margin-block-start: 3px;
  /* ⚠ `plaintext`: یادداشت می‌تواند با یک کلمه‌ی لاتین شروع شود و
     آن‌وقت کلِ پاراگراف جهتش برمی‌گردد. */
  unicode-bidi: plaintext;
}

/* ═══════════════════════════════ رویداد و صورت‌جلسه

   ⚠ چهار فهرستِ متفاوت در این بخش هست — رویداد، مسئول، شرکت‌کننده،
   یادداشت، بند — و هر پنج **یک شکلِ پایه** دارند: آواتار یا نشانه
   در آغاز، متنِ چندسطری در میان، و کنش در انتها.

   نسخه‌ی اول هر کدام را جدا چید و روی صفحه پنج ریتمِ عمودیِ
   کمی‌متفاوت ساخت که چشم می‌گرفتش ولی نمی‌توانست بگوید چرا. اینجا
   قدِ آواتار و فاصله‌ی درونی یکی است.
   ─────────────────────────────────────────────────────────── */

/* ── ورودیِ آزاد (بیرونِ .field) */
.inp {
  inline-size: 100%; min-block-size: 34px;
  background: var(--surface); border: 1px solid var(--line-2);
  border-radius: var(--r-sm); color: var(--fg);
  font-family: var(--font); font-size: var(--t-sm);
  padding: 6px 10px;
}
.inp:focus { outline: none; border-color: var(--accent); }
select.sel--sm { min-block-size: 28px; block-size: 28px; font-size: var(--t-2xs); }

/* ⚠ فرمِ «فشرده» برای فرم‌های کوچکِ داخلِ پاصفحه‌ی کارت: فاصله‌ی
   پیش‌فرضِ `.field` برای فرمِ سه‌فیلدیِ ته کارت زیادی گشاد است و
   پاصفحه را بلندتر از خودِ فهرست می‌کند. */
/* ── فرمِ دوستونی
   ⚠ فرمِ رویداد هفده میدان دارد. در یک ستون، صفحه‌اش ۱٫۷۰۰ پیکسل
   بلند می‌شد و «ساعتِ پایان» از «ساعتِ آغاز» یک اسکرول فاصله
   می‌گرفت — دو چیزی که همیشه با هم پر می‌شوند.

   ⚠ و جداکننده‌ها **روی هر میدان** است نه با `+ .field`: در شبکه،
   «میدانِ بعدی» همسایه‌ی بصری نیست و آن قاعده خط‌های وسطِ صفحه
   می‌کشید. آخرین خط با `:last-child` هم برداشته نمی‌شود چون
   ردیفِ آخر می‌تواند دو میدان داشته باشد؛ خطِ پایینیِ همه بودن،
   خودش یک ریتمِ درست است. */
.form--2 {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0 var(--sp-5);
}
.form--2 .field { padding-block: 9px; border-block-end: 1px solid var(--line); }
.form--2 .field + .field { border-block-start: 0; }
.form--2 .field--wide { grid-column: span 2; }
@media (max-width: 700px) {
  .form--2 { grid-template-columns: minmax(0, 1fr); }
  .form--2 .field--wide { grid-column: span 1; }
}

.form--tight { flex-direction: row; flex-wrap: wrap; gap: var(--sp-2); align-items: flex-end; }
.form--tight .field { padding-block: 0; flex: 1 1 160px; min-inline-size: 0; }
.form--tight .field + .field { border-block-start: 0; }

/* ── بندِ فرمِ صورت‌جلسه
   ⚠ `<fieldset>` و `<legend>`ِ واقعی، نه یک `<div>` با متنِ درشت:
   صفحه‌خوان بندها را اعلام می‌کند و کاربرِ صفحه‌کلید می‌فهمد کدام
   میدان به کدام بند تعلق دارد. با شانزده میدان، این تفاوتِ خواندن و
   نخواندن است.

   ⚠ و `min-inline-size: 0` روی `fieldset`: مرورگر به `fieldset` یک
   عرضِ ذاتیِ کمینه می‌دهد که در شبکه سرریز می‌سازد — همان تلهٔ
   پیش‌فرضِ `min-width: auto` که چهار بار خوردیمش. */
/* ⚠⚠ **قابِ بند روی `<fieldset>` نیست، روی `.mblk__body` است.**

   نسخهٔ اولِ جلسه ۲۴ قاب را روی خودِ `fieldset` گذاشت و `<legend>` را
   در شکافِ بالای آن. در مرورگر بد شد و پرسا هم همین را گفت — «خیلی
   بهم چسبیده». سه دلیلِ فنی داشت:

     ۱. `<legend>` در شکافِ قاب می‌نشیند و **فقط یک خط** جا دارد.
        توضیحِ دو‌خطیِ بند روی خطِ قاب می‌افتاد.
     ۲. عنوانِ بند چسبیده به قاب، از عنوانِ بندِ **قبلی** جدا نبود.
     ۳. فاصلهٔ بین بندها `--sp-4` بود؛ با نُه بند، صفحه یک تودهٔ
        پیوسته می‌شد.

   حالا: `fieldset` بی‌قاب و بی‌پدینگ است (پس `<legend>` یک بلوکِ
   معمولیِ بالای قاب می‌شود و هر چند خط که لازم است می‌گیرد)، قاب روی
   بدنه است، و فاصلهٔ بین بندها `--sp-6`.

   ⚠ و `min-inline-size: 0` روی `fieldset` می‌مانَد: مرورگر به
   `fieldset` یک عرضِ ذاتیِ کمینه می‌دهد که در شبکه سرریز می‌سازد —
   همان تلهٔ پیش‌فرضِ `min-width: auto`. */
.mblk {
  border: 0; padding: 0; margin: 0 0 var(--sp-6);
  min-inline-size: 0;
}
.mblk:last-child { margin-block-end: var(--sp-2); }
.mblk__lg {
  display: block; padding: 0; margin: 0 0 var(--sp-2);
  inline-size: 100%; max-inline-size: 100%;
}
.mblk__t {
  display: block; font-size: var(--t-base); font-weight: 600; color: var(--fg);
  margin-block-end: 3px;
}
.mblk__n {
  display: block;
  font-size: var(--t-2xs); color: var(--fg-faint); line-height: 1.75;
  font-weight: 400; max-inline-size: 68ch;
}
.mblk__body {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  /* ⚠ فاصلهٔ **ردیفی** هم لازم است، نه فقط ستونی. نسخهٔ اول
     `gap: 0 var(--sp-5)` داشت و ردیف‌ها را جداکنندهٔ ۱پیکسلی نگه
     می‌داشت — که برای جدول خوب است و برای فرم نه. */
  gap: var(--sp-3) var(--sp-5);
  padding: var(--sp-4);
  border: 1px solid var(--line); border-radius: var(--r);
  background: var(--surface-2);
}
.mblk__body .field { margin: 0; padding-block: 0; border: 0; }
.mblk__body .field--wide { grid-column: span 2; }
@media (max-width: 700px) {
  .mblk__body { grid-template-columns: minmax(0, 1fr); }
  .mblk__body .field--wide { grid-column: span 1; }
}
/* نشانِ «بی این، سند ناقص است» — یک نقطه، نه ستاره:
   ستاره در دیبا یعنی «اجباری»، و این‌ها اجباری نیستند. */
.field__dot { color: var(--warn); margin-inline-start: 4px; }

/* ── تراشه‌های پیشنهاد (دستیار)
   ⚠ `<datalist>` تکمیلِ خودکارِ بومی را می‌دهد ولی تا کاربر تایپ
   نکند خودش را نشان نمی‌دهد — یعنی کاربر نمی‌داند پیشنهادی هست.
   این ردیف همان فهرست را دیدنی می‌کند.

   ⚠ `flex-wrap` و نه اسکرولِ افقی: سه تراشهٔ کوتاه در یک خط جا
   می‌شوند و اسکرولِ افقی در یک ردیفِ چهل‌پیکسلی، روی موبایل با
   اسکرولِ صفحه می‌جنگد. */
.sugg {
  display: flex; align-items: center; gap: 5px; flex-wrap: wrap;
  margin-block-start: 6px;
}
.sugg__lb { font-size: var(--t-3xs); color: var(--fg-faint); }
.sugg__c {
  font-family: var(--font); font-size: var(--t-3xs); cursor: pointer;
  padding: 2px 8px; border-radius: 999px;
  border: 1px solid var(--line-2); background: var(--surface);
  color: var(--fg-muted); max-inline-size: 22ch;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sugg__c:hover { border-color: var(--accent); color: var(--accent); }
.form--tight .field > label { font-size: var(--t-3xs); margin-block-end: 3px; }

/* ── دکمه‌ی فقط‌آیکون */
.btn--icon {
  padding-inline: 7px; color: var(--fg-faint);
}
.btn--icon:hover { color: var(--bad); border-color: var(--bad); }
/* دکمهٔ کنشِ ناممکن — دیده می‌شود ولی کار نمی‌کند، و دلیلش در
   `title` است.
   ⚠ حذفش نمی‌کنیم و فقط خاموشش می‌کنیم: جای خالی به کاربر
     نمی‌گوید «چرا اینجا حذف نیست»، و همان سؤال دوباره می‌آید. */
.btn--off {
  opacity: .42; cursor: not-allowed;
  display: inline-flex; align-items: center; justify-content: center;
}
.btn--off:hover { color: inherit; border-color: var(--line-2); }

/* ── نوارِ فیلترِ بالای فهرست */
.filters {
  display: flex; align-items: flex-end; gap: var(--sp-2); flex-wrap: wrap;
  padding: 10px 12px; margin-block-end: var(--sp-3);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-lg);
}
.filters__f { display: flex; flex-direction: column; gap: 3px; min-inline-size: 0; }
.filters__f > span { font-size: var(--t-3xs); color: var(--fg-muted); }
.filters__f select {
  min-block-size: 30px; background: var(--surface-2);
  border: 1px solid var(--line-2); border-radius: var(--r-sm);
  color: var(--fg); font-family: var(--font); font-size: var(--t-2xs);
  padding: 4px 10px; min-inline-size: 130px;
}
/* جداکنندهٔ «فیلترِ عضویت» از «پروندهٔ ناقص»
   ⚠ یک خطِ نازک و نه یک عنوانِ تازه: نوار باید یک خط بماند، وگرنه
   روی نمایشگرِ کوچک سه ردیف می‌شود و فهرست را پایین می‌راند. */
.filters__sep {
  align-self: stretch; inline-size: 1px; background: var(--line-2);
  margin-inline: var(--sp-2); min-block-size: 30px;
}

/* ── منوی خروجی
   ⚠ `<details>` و نه جاوااسکریپت: بازوبسته‌شدنِ یک منو رفتارِ
   بومیِ مرورگر است — با صفحه‌کلید کار می‌کند، با صفحه‌خوان اعلام
   می‌شود، و اگر app.js نرسید هم باز می‌شود. */
.expo { position: relative; display: inline-block; }
.expo > summary { list-style: none; cursor: pointer; }
.expo > summary::-webkit-details-marker { display: none; }
.expo__menu {
  position: absolute; inset-block-start: calc(100% + 5px); inset-inline-end: 0;
  z-index: 30; min-inline-size: 148px;
  background: var(--surface); border: 1px solid var(--line-2);
  border-radius: var(--r); box-shadow: var(--z-2); overflow: hidden;
}
.expo__menu a {
  display: block; padding: 7px 12px; font-size: var(--t-2xs);
  color: var(--fg); text-decoration: none;
  border-block-end: 1px solid var(--line);
}
.expo__menu a:last-child { border-block-end: 0; }
.expo__menu a:hover { background: var(--surface-2); }

/* ── فهرستِ رویداد */
.evs { display: flex; flex-direction: column; }
.ev {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: 11px 14px; color: var(--fg); text-decoration: none;
  border-block-start: 1px solid var(--line);
  transition: background-color .13s ease;
}
.ev:first-child { border-block-start: 0; }
.ev:hover { background: var(--surface-2); text-decoration: none; }
.ev__badge { flex-shrink: 0; }
/* ⚠ `min-inline-size: 0` — در فلکس پیش‌فرضِ `min-width` مقدارِ
   `auto` است و عنوانِ بلندِ همایش کارت را از صفحه بیرون می‌زند.
   همان خانواده‌ی باگی که در جلسه‌های ۱۷، ۲۰ و ۲۱ خوردیم. */
.ev__main { min-inline-size: 0; flex-grow: 1; display: flex; flex-direction: column; gap: 4px; }
.ev__title { font-size: var(--t-sm); font-weight: 500; }
.ev__meta {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  font-size: var(--t-2xs); color: var(--fg-muted);
}
.ev__side {
  flex-shrink: 0; text-align: center; display: flex;
  flex-direction: column; gap: 1px; min-inline-size: 52px;
}
.ev__side b { font-size: var(--t-lg); font-weight: 600; line-height: 1.2; }
.ev__side small { font-size: var(--t-3xs); color: var(--fg-faint); }

/* ── متنِ روایی
   ⚠ `plaintext` و نه `isolate`: بندی که با «LiveKit» شروع شود
   باید چپ‌به‌راست بچینَد و بندِ فارسی راست‌به‌چپ — و در یک جعبه‌ی
   متنِ کاربر، هر دو با هم می‌آیند. */
.prose {
  white-space: pre-wrap; unicode-bidi: plaintext;
  font-size: var(--t-sm); line-height: 1.9; color: var(--fg);
}
.prose--sm { font-size: var(--t-xs); line-height: 1.85; }
/* ⚠ چکیده‌ی صورت‌جلسه در کارتِ پرونده بریده می‌شود، وگرنه یک متنِ
   دوهزار‌کلمه‌ای کارتِ کنارش را ده برابر بلند می‌کند. */
.prose--clamp {
  display: -webkit-box; -webkit-line-clamp: 6; -webkit-box-orient: vertical;
  overflow: hidden;
}

/* ── مسئولان و پشتیبانی */
.stf { display: flex; flex-direction: column; }
.stf__row {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: 8px 14px; border-block-start: 1px solid var(--line);
}
.stf__row:first-child { border-block-start: 0; }
.stf__main { min-inline-size: 0; flex-grow: 1; display: flex; flex-direction: column; }
.stf__main b { font-size: var(--t-xs); font-weight: 500; }

/* ── شرکت‌کنندگان */
.prts { display: flex; flex-direction: column; }
.prt {
  display: flex; align-items: flex-start; gap: var(--sp-2);
  padding: 11px 14px; border-block-start: 1px solid var(--line);
}
.prt:first-child { border-block-start: 0; }
.prt__main { min-inline-size: 0; flex-grow: 1; display: flex; flex-direction: column; gap: 2px; }
.prt__main > b { font-size: var(--t-sm); font-weight: 500; }
.prt__row { display: flex; gap: 5px; flex-wrap: wrap; align-items: center; margin-block-start: 4px; }
/* ⚠ کشویی باید پهنِ کافی داشته باشد وگرنه فلشِ خودمان (که در
   لبه‌ی درونی می‌نشیند) روی متنِ وضعیت می‌افتد. */
.prt__set { flex-shrink: 0; }
.prt__set select { min-inline-size: 108px; }

/* ── یادداشت‌ها */
.nts { display: flex; flex-direction: column; }
.nt {
  display: flex; align-items: flex-start; gap: var(--sp-2);
  padding: 10px 14px; border-block-start: 1px solid var(--line);
}
.nt__main { min-inline-size: 0; flex-grow: 1; }
.nt__head { display: flex; gap: 7px; align-items: baseline; flex-wrap: wrap; }
.nt__head b { font-size: var(--t-xs); font-weight: 500; }

/* ── بندهای صورت‌جلسه */
.decs { display: flex; flex-direction: column; }
.dec {
  display: flex; align-items: flex-start; gap: var(--sp-3);
  padding: 12px 14px; border-block-start: 1px solid var(--line);
}
.dec:first-child { border-block-start: 0; }
/* ⚠ شماره‌ی بند در یک دایره‌ی هم‌قدِ آواتار می‌نشیند، تا ریتمِ
   عمودیِ این فهرست با فهرستِ شرکت‌کننده و یادداشت یکی باشد. */
.dec__n {
  flex-shrink: 0; inline-size: 26px; block-size: 26px;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--r-full); background: var(--surface-2);
  border: 1px solid var(--line-2);
  font-size: var(--t-2xs); font-weight: 600; color: var(--fg-muted);
}
.dec__main { min-inline-size: 0; flex-grow: 1; display: flex; flex-direction: column; gap: 5px; }
.dec__head { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.dec__row { display: flex; gap: 5px; flex-wrap: wrap; }
.dec__acts { flex-shrink: 0; display: flex; gap: 5px; align-items: center; }

/* ── انتخاب‌گرِ تک‌نفره (شرکت‌کننده)
   ⚠ ظاهرش را از انتخاب‌گرِ اشاره (`.men-*`) قرض می‌گیرد و عمداً
   قاعده‌ی تازه‌ای نمی‌سازد: دو انتخاب‌گر با دو ظاهر، به کاربر
   می‌گوید اینها دو چیزِ متفاوت‌اند — که نیستند. */
.finder { display: flex; flex-direction: column; gap: var(--sp-2); }
/* ⚠ **`flex-basis` روی این ستون نگذار.** یک بار `flex: 1 1 220px`
   نوشتم و چون ظرف `flex-direction: column` است، آن ۲۲۰ پیکسل
   **ارتفاع** شد: زیرِ جعبه‌ی جست‌وجو یک حفره‌ی ۲۲۰ پیکسلی افتاد
   که در کد پیدا نبود و فقط در مرورگر دیده شد. */
.finder .men-field { flex: 0 0 auto; }
.finder .men-drop { inset-block-start: calc(100% + 4px); }

/* ── فرمِ تاشو در پاصفحه‌ی کارت
   ⚠ فرمِ همیشه‌باز در ته هر کارت، صفحه را دو برابر بلند می‌کرد و
   خودِ فهرست را — که چیزِ اصلیِ کارت است — به زیرِ خطِ دید
   می‌فرستاد. `<details>` رفتارِ بومیِ مرورگر است: با صفحه‌کلید
   کار می‌کند و بی جاوااسکریپت هم باز می‌شود. */
.addf > summary {
  list-style: none; cursor: pointer;
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--t-2xs); color: var(--fg-muted);
}
.addf > summary::-webkit-details-marker { display: none; }
.addf > summary:hover { color: var(--fg); }
.addf__body { margin-block-start: 10px; }

/* ⚠ مبلغ در کاشی یک اندازه کوچک‌تر از عددِ شمارشی است.
   «۱۸۰ میلیون ریال» در ۲۰ پیکسل روی دو خط می‌شکست و یکای پول
   تنها در خطِ دوم می‌مانْد — که هم زشت بود هم یک لحظه ناخوانا. */
.tile__value--money { font-size: var(--t-lg); }

/* ⚠ نوارِ کاشیِ **کشسان** — برای صفحه‌هایی که تعدادِ کاشی‌شان
   شرطی است.

   `.tiles` شش ستونِ ثابت دارد و برای میزکار (۶) و تابلو (۸) درست
   است. ولی پرونده‌ی رویداد سه تا پنج کاشی دارد — ظرفیت فقط وقتی
   اعلام شده، بودجه فقط وقتی ثبت شده — و با شبکه‌ی شش‌ستونی، یک
   خانه‌ی خالیِ توخالی ته نوار می‌مانْد که در مرورگر دیده شد و
   شبیهِ کاشیِ گم‌شده بود.

   ⚠ فلکس و نه `repeat(auto-fit, …)`: قاعده‌ی جلسه ۱۶ سرِ جایش
   است — `auto-fit` تعدادِ ستون را به عرضِ پنجره می‌سپارد و کاشیِ
   آخر تنها به خطِ دوم می‌افتد. اینجا خودِ فلکس همه را در یک ردیف
   می‌کشد، هر چند تا که باشند. */
.tiles--fit { display: flex; flex-wrap: wrap; }
.tiles--fit > .tile { flex: 1 1 150px; min-inline-size: 0; }

/* ── پیوستِ کاری */
.atts { display: flex; flex-direction: column; }
.att {
  display: flex; align-items: flex-start; gap: var(--sp-2);
  padding: 10px 14px; border-block-start: 1px solid var(--line);
}
.att:first-child { border-block-start: 0; }
.att__ico { color: var(--fg-faint); display: inline-flex; flex-shrink: 0; margin-block-start: 2px; }
.att__main { min-inline-size: 0; flex-grow: 1; display: flex; flex-direction: column; gap: 3px; }
.att__head { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.att__head a { font-size: var(--t-xs); font-weight: 500; }
.att__read > summary {
  list-style: none; cursor: pointer; font-size: var(--t-2xs);
  color: var(--fg-muted); margin-block-start: 3px;
}
.att__read > summary::-webkit-details-marker { display: none; }
/* ⚠ نشانه‌ی تاشو **مثلثِ یونیکد نیست**: «▾» در Vazirmatn نیست و
   مرورگر آن را از فونتِ جانشین می‌آورد — روی صفحه شبیهِ رقمِ «۵»
   دیده شد. همان مربعِ کوچکِ «+/×»ِ `.addf` اینجا هم به‌کار می‌رود،
   تا دو تاشوی صفحه یک شکل باشند. */
.att__read > summary::before,
.addf > summary::before {
  content: '+'; display: inline-flex; align-items: center;
  justify-content: center; inline-size: 18px; block-size: 18px;
  border-radius: var(--r-sm); background: var(--surface-2);
  border: 1px solid var(--line-2); font-size: var(--t-xs);
  line-height: 1; margin-inline-end: 6px; vertical-align: middle;
}
.att__read[open] > summary::before,
.addf[open] > summary::before { content: '×'; }
.att__read > summary:hover { color: var(--fg); }
/* ⚠ متنِ فایل در جعبه‌ی **اسکرول‌دارِ خودش**، وگرنه یک CSVِ
   هزار‌ردیفی صفحه‌ی کار را چند‌هزار پیکسل بلند می‌کند. و
   `plaintext` چون سلولِ فارسی و انگلیسی با هم می‌آید. */
.att__text {
  margin: 6px 0 0; padding: 9px 11px; max-block-size: 260px;
  overflow: auto; background: var(--surface-2);
  border: 1px solid var(--line); border-radius: var(--r-sm);
  /* ⚠⚠ **فونتِ تک‌عرض نه.** یک بار `var(--mono)` بود و متنِ فارسی
     روی صفحه به حروفِ **جدا** درآمد («ن ا م ش ر ک ت»): آن فونت
     گلیفِ فارسی ندارد و مرورگر هر نویسه را از فونتِ جانشین و در
     شکلِ منفصل می‌آورد.

     هم‌ترازیِ ستون‌ها فدای خوانایی می‌شود، و این معامله‌ی درستی
     است: جدولِ ناهم‌تراز خوانده می‌شود، متنِ حروف‌جدا نه. عددها
     با `tabular-nums` هم‌تراز می‌مانند. */
  font-family: var(--font); font-size: var(--t-2xs); line-height: 1.9;
  font-variant-numeric: tabular-nums;
  white-space: pre; unicode-bidi: plaintext;
}

/* ── نوارِ آپلود */
.upl { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
/* ⚠ ورودیِ فایلِ سیستمی («Choose File» لاتین، دکمه‌ی خاکستریِ
   بیگانه) پنهان می‌شود و برچسب جایش را می‌گیرد. `display: none`
   **نه** — با آن، صفحه‌خوان فیلد را نمی‌بیند و صفحه‌کلید هم به آن
   نمی‌رسد. */
.upl__pick {
  position: relative; overflow: hidden;
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 12px; border-radius: var(--r-sm);
  background: var(--surface-2); border: 1px solid var(--line-2);
  color: var(--fg); font-size: var(--t-2xs); cursor: pointer;
}
.upl__pick:hover { border-color: var(--accent); }
.upl__pick input[type="file"] {
  position: absolute; inset: 0; opacity: 0; cursor: pointer;
  inline-size: 100%; block-size: 100%;
}
.upl__hint { min-inline-size: 0; }

/* ⚠ فهرستِ صدا وقتی کلیدِ صدا خاموش است **کم‌رنگ** می‌شود ولی
   کلیک‌پذیر می‌مانَد: کلیک روی یک صدا خودش صدا را روشن می‌کند، و
   `pointer-events: none` این راه را می‌بست. کم‌رنگی خبر می‌دهد،
   قفل نمی‌کند. */
#sound-picks.is-off .picks { opacity: .5; }

/* ═══════════════════════════════ چک‌لیست، کامنت، تاریخچه (جلسه ۲۳)

   ⚠ همان ریتمِ عمودیِ فهرست‌های جلسه ۲۲: نشانه یا آواتار در آغاز،
   متنِ چندسطری در میان، کنش در انتها — و قدِ آواتار و فاصله‌ی
   درونی یکی.
   ─────────────────────────────────────────────────────────── */

/* ── مراحلِ انجام */
.stps { display: flex; flex-direction: column; }
.stp {
  display: flex; align-items: flex-start; gap: var(--sp-2);
  padding: 9px 14px; border-block-start: 1px solid var(--line);
}
.stp:first-child { border-block-start: 0; }
.stp__box { flex-shrink: 0; display: inline-flex; }
/* ⚠ خودِ تیک یک دکمه است و **۲۶ پیکسل**: کوچک‌ترش روی صفحه‌ی لمسی
   قابلِ زدن نیست، و روزِ برگزاری کسی با گوشی تیک می‌زند. */
.stp__tick {
  inline-size: 22px; block-size: 22px; flex-shrink: 0; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--r-sm); background: var(--surface);
  border: 1.5px solid var(--line-2); color: var(--bg);
  padding: 0; margin-block-start: 1px;
}
.stp__tick:hover { border-color: var(--accent); }
.stp--done .stp__tick { background: var(--accent); border-color: var(--accent); }
.stp__n {
  flex-shrink: 0; font-size: var(--t-3xs); color: var(--fg-faint);
  min-inline-size: 16px; text-align: center; margin-block-start: 4px;
}
.stp__main { min-inline-size: 0; flex-grow: 1; display: flex; flex-direction: column; }
.stp__text { font-size: var(--t-sm); }
/* ⚠ خطِ روی متنِ انجام‌شده لازم است ولی **رنگِ خاکستری تنها کافی
   نیست**: در حالتِ روشن، تفاوتِ خاکستریِ ۶۰٪ و متنِ عادی در یک
   فهرستِ ده‌ردیفی دیده نمی‌شود. */
.stp--done .stp__text { text-decoration: line-through; color: var(--fg-faint); }

/* ── کامنت */
.cmts { display: flex; flex-direction: column; }
.cmt {
  display: flex; align-items: flex-start; gap: var(--sp-2);
  padding: 11px 14px; border-block-start: 1px solid var(--line);
}
/* ⚠ کامنتی که **مخاطب دارد** نشانِ کنارِ خودش را می‌گیرد: در یک
   فهرستِ بیست‌کامنتی، پرسش‌ها باید از یادداشت‌ها جدا دیده شوند. */
.cmt--ask { border-inline-start: 2px solid var(--warn); }
.cmt__main { min-inline-size: 0; flex-grow: 1; display: flex; flex-direction: column; gap: 3px; }
.cmt__head { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.cmt__head b { font-size: var(--t-xs); font-weight: 500; }

/* ── تاریخچه */
.hist { display: flex; flex-direction: column; }
.hist__row {
  display: flex; align-items: baseline; gap: var(--sp-2);
  padding: 7px 14px; border-block-start: 1px solid var(--line);
  font-size: var(--t-2xs);
}
.hist__row:first-child { border-block-start: 0; }
.hist__dot {
  flex-shrink: 0; inline-size: 5px; block-size: 5px; border-radius: var(--r-full);
  background: var(--line-2);
}
.hist__main { min-inline-size: 0; flex-grow: 1; }
.hist__t { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.hist__at { flex-shrink: 0; }

/* ═══════════════════════════════ تقویمِ کارِ زنده (جلسه ۲۳)

   ⚠ «دقیقاً مثل calendar» را جدی گرفتیم: خودِ شبکه، خانه، و
   رویدادها از همان کلاس‌های `.cal*` می‌آیند و هیچ نسخه‌ی دومی
   ساخته نشد. اینجا فقط سه چیزِ تازه اضافه شده که تقویمِ منابعِ
   انسانی لازم نداشت.
   ─────────────────────────────────────────────────────────── */

/* ⚠⚠ قاعده‌های `.cal__ev*` در جلسه ۲۷ **برداشته شدند**.

   پس از یکی‌شدنِ سه تقویم، قالب به `.ev / .ev--<tone>` رفت و
   این کلاس‌ها فقط در `app.js` مانده بودند — که همان باگ را
   می‌ساخت: صفحه درست بالا می‌آمد و پانزده ثانیه بعد بی‌قالب
   می‌شد. حالا هر دو یک نام دارند.

   کلاس‌های مرده اینجا عمداً **دوباره تعریف نمی‌شوند**: اگر
   می‌ماندند، جاوااسکریپتِ کهنه بی‌صدا «کار می‌کرد» و کسی
   نمی‌فهمید دو نشانه‌گذاری در کار است. */
.cal__evs { display: flex; flex-direction: column; gap: 3px; min-inline-size: 0; }

/* ۳. تپشِ «همین حالا عوض شد» روی **خودِ عنصر**.
   ⚠ قاعده‌ی موجود (`.live--pulse .tile`) کلاس را روی **والد**
   می‌خواست، و برای کاشی‌های مالی درست بود. اینجا خودِ خانه‌ی
   تقویم تپش می‌زند، پس یک قاعده‌ی خودش لازم است. */
.cal__cell.live--pulse,
.phead__stat b.live--pulse { animation: dibaLive 1.4s ease-out both; }

/* ⚠ کاربری که «حرکتِ کمتر» را در سیستم‌عاملش خواسته، نباید هر
   پانزده ثانیه سی‌ویک خانهٔ چشمک‌زن ببیند. تغییر باز هم اتفاق
   می‌افتد — فقط بی انیمیشن. */
@media (prefers-reduced-motion: reduce) {
  .cal__cell.live--pulse,
  .phead__stat b.live--pulse { animation: none; }
}

/* ═════════════════════════════════ بنرِ اطلاعیه (جلسه ۲۶)
   خواستهٔ پرسا: «بنر برای اطلاعیهٔ کارمندها، یا موقت باشه یا هشدار
   باشه — مثلِ todo.»

   ⚠ رنگ از همان چهار جفتِ توکنِ موجود می‌آید (`--ok/--warn/--bad/
     --info`) و نه از یک پالتِ تازه: بنرِ «هشدار» باید **دقیقاً**
     همان زردی باشد که کاربر در `.alert--warn` یاد گرفته. رنگِ
     پنجمی یعنی یک زبانِ تازه برای یاد گرفتن.

   ⚠ `border-inline-start` ضخیم و نه `border` دورِ کامل: نوارِ کناری
     در RTL خودش می‌چرخد و چشم را به آغازِ متن می‌بَرد. */
.bnrs { display: grid; gap: var(--sp-2); margin-block-end: var(--sp-3); }

.bnr {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 11px var(--sp-4);
  border-radius: var(--r);
  border: 1px solid var(--line-2);
  border-inline-start: 3px solid var(--line-2);
  background: var(--surface-2);
  font-size: var(--t-xs); line-height: 1.7;
}
.bnr__ico { flex-shrink: 0; margin-block-start: 3px; display: block; }
.bnr__body { flex: 1 1 auto; min-inline-size: 0; }

/* ⚠ `plaintext`: عنوانِ اطلاعیه ممکن است با یک نامِ لاتین شروع شود
   («Outlook از فردا …»)؛ بی این، کلِ خط چپ‌چین می‌افتاد. */
.bnr__title {
  margin: 0; font-weight: 600; color: var(--fg);
  unicode-bidi: plaintext;
}
.bnr__text {
  margin: 2px 0 0; color: var(--fg-2);
  unicode-bidi: plaintext;
}
.bnr__until {
  margin: 4px 0 0; font-size: var(--t-3xs); color: var(--fg-3);
}

.bnr__link {
  flex-shrink: 0; align-self: center;
  padding: 5px 11px; border-radius: var(--r-sm);
  border: 1px solid currentColor;
  font-size: var(--t-3xs); font-weight: 600;
  text-decoration: none; color: inherit;
}
.bnr__link:hover { background: color-mix(in srgb, currentColor 12%, transparent); }

/* ⚠ دکمهٔ بستن `44px` هدفِ لمسی می‌گیرد ولی `×` کوچک می‌مانَد:
   هدفِ کوچک‌تر از این روی موبایل با انگشت زده نمی‌شود. */
.bnr__x { flex-shrink: 0; margin: -4px; }
.bnr__x button {
  inline-size: 34px; block-size: 34px;
  display: grid; place-items: center;
  background: none; border: 0; cursor: pointer;
  color: var(--fg-3); font-size: var(--t-xl); line-height: 1;
  border-radius: var(--r-sm);
}
.bnr__x button:hover { color: var(--fg); background: var(--surface-3); }

.bnr--info    { background: var(--info-bg); border-color: var(--info-line);
                border-inline-start-color: var(--info); color: var(--info); }
.bnr--success { background: var(--ok-bg);   border-color: var(--ok-line);
                border-inline-start-color: var(--accent); color: var(--accent); }
.bnr--warning { background: var(--warn-bg); border-color: var(--warn-line);
                border-inline-start-color: var(--warn); color: var(--warn); }
.bnr--danger  { background: var(--bad-bg);  border-color: var(--bad-line);
                border-inline-start-color: var(--bad); color: var(--bad); }

/* ⚠⚠ عنوانِ بنر رنگِ **متنِ عادی** می‌گیرد و نه رنگِ سطح. متنِ
   زردِ روی زمینهٔ زرد در حالتِ روشن به ۳:۱ هم نمی‌رسید. رنگ فقط
   در آیکون و نوارِ کناری می‌مانَد، که معنا را حمل نمی‌کنند. */
.bnr .bnr__title { color: var(--fg); }
.bnr .bnr__text  { color: var(--fg-2); }

/* ═════════════════════════════════ فهرستِ اطلاعیه‌ها */
.anc__state {
  display: inline-block; padding: 2px 8px; border-radius: 99px;
  font-size: var(--t-3xs); font-weight: 600;
  border: 1px solid var(--line-2); color: var(--fg-3);
}
.anc__state--live { background: var(--ok-bg); border-color: var(--ok-line); color: var(--accent); }
.anc__state--wait { background: var(--info-bg); border-color: var(--info-line); color: var(--info); }
.anc__state--past { color: var(--fg-3); }
.anc__state--off  { background: var(--surface-3); }

/* ⚠ دو کادرِ «تاریخ + ساعت» (`JalaliDateTimeField`) کنارِ هم، نه
   زیرِ هم: `MultiWidget` جنگو دو `<input>`ِ خالی پشتِ سرِ هم
   می‌گذارد و چون `.form input` تمام‌عرض است، ساعت به خطِ بعد
   می‌افتاد و شبیهِ یک میدانِ سومِ بی‌برچسب می‌شد. */
.form .ctl:has(> input + input) { display: flex; gap: 8px; align-items: center; }
.form .ctl:has(> input + input) > input:first-of-type { flex: 1 1 auto; }
.form .ctl:has(> input + input) > input:last-of-type  { flex: 0 0 108px; }

/* ⚠ شمارِ حاضرینِ روز — کمرنگ و ته خانه. خبرِ درجه‌دو است و نباید
   با رویدادها رقابت کند، ولی مدیری که ماه را مرور می‌کند همان یک
   نگاه را می‌خواهد. */
.cal__att {
  margin-block-start: auto; align-self: flex-end;
  font-size: var(--t-3xs); color: var(--fg-faint);
}

/* ═══════════════════════════════ صندوقِ اعلان (جلسه ۲۷)

   ⚠ خبرِ ندیده با **دو** نشانه از دیده جدا می‌شود: نقطهٔ رنگی و
     وزنِ متن. با یک نشانهٔ رنگی تنها، کسی که رنگ را خوب تشخیص
     نمی‌دهد هیچ فرقی نمی‌بیند — و این همان سطرهایی است که باید
     بیشتر از همه به چشم بیایند.                               */
.nlist { list-style: none; margin: 0; padding: 0; }

.nrow { border-block-end: 1px solid var(--line); }
.nrow:last-child { border-block-end: 0; }

.nrow__link {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  color: inherit; text-decoration: none;
}
.nrow__link:hover { background: var(--surface-2); }

/* ⚠ نقطه همیشه در DOM است و فقط رنگش عوض می‌شود، نه اینکه ساخته
   و برداشته شود: وگرنه سطرِ دیده و ندیده دو تورفتگیِ متفاوت
   می‌گیرند و فهرست پلکانی می‌شود. */
.nrow__dot {
  inline-size: 7px; block-size: 7px; border-radius: 50%;
  background: transparent; flex: 0 0 auto;
}
.nrow.is-new .nrow__dot { background: var(--accent); }

.nrow__main { flex: 1 1 auto; min-inline-size: 0; }
.nrow__text { display: block; font-size: var(--t-md); }
.nrow.is-new .nrow__text { font-weight: 600; }
.nrow__meta {
  display: block; margin-block-start: 2px;
  font-size: var(--t-2xs); color: var(--fg-faint);
}
.nrow__at {
  flex: 0 0 auto; font-size: var(--t-xs); color: var(--fg-faint);
  white-space: nowrap;
}

/* ═══════════════════════════════ چیپِ منشن (جلسه ۲۷)

   انتخابِ چندنفره در فرمِ کار — «خبر برود برای».

   ⚠ خودِ `<input>` پنهان است ولی **برداشته نشده**: صفحه‌خوان و
     کلیدِ Tab و فرمِ بی‌جاوااسکریپت همه به همان `<input>` وابسته‌اند.
     `display:none` هم نه — چون آن، عنصر را از ترتیبِ فوکوس هم
     بیرون می‌برد.                                                */
.pchips { display: flex; flex-wrap: wrap; gap: 6px; }

.pchip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 10px 4px 6px;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--surface); cursor: pointer;
  font-size: var(--t-sm); color: var(--fg-2);
  transition: background .12s, border-color .12s;
}
.pchip:hover { background: var(--surface-2); }

.pchip input {
  position: absolute; opacity: 0;
  inline-size: 1px; block-size: 1px; margin: 0;
}

/* ⚠ حلقهٔ فوکوس روی **چیپ** می‌نشیند نه روی ورودیِ پنهان: بی این،
   کاربرِ صفحه‌کلید هیچ نشانه‌ای از اینکه کجاست نمی‌بیند. */
.pchip:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }

.pchip:has(input:checked) {
  background: var(--ok-bg); border-color: var(--ok-line);
  color: var(--accent); font-weight: 600;
}

.pchip__av {
  inline-size: 22px; block-size: 22px; border-radius: 50%;
  display: grid; place-items: center; flex: 0 0 auto;
  font-size: var(--t-3xs); font-weight: 700; color: #fff;
  background: hsl(calc(var(--pch, 0) * 40) 45% 52%);
}
.pchip[data-h="0"] { --pch: 0; } .pchip[data-h="1"] { --pch: 1; }
.pchip[data-h="2"] { --pch: 2; } .pchip[data-h="3"] { --pch: 3; }
.pchip[data-h="4"] { --pch: 4; } .pchip[data-h="5"] { --pch: 5; }
.pchip[data-h="6"] { --pch: 6; } .pchip[data-h="7"] { --pch: 7; }

.pchip__n { white-space: nowrap; }

/* ═══════════════════════════════ رنگِ سالن روی تقویم (جلسه ۲۷)

   خواستهٔ پرسا: «چند تا سالن داریم که بر اساسِ همون — ساعت داره،
   رنگ داره.»

   ⚠⚠ هشت رنگ، و هر کدام **دو** تعریف: یکی برای زمینهٔ روشن و یکی
      برای تیره. رنگی که روی سفید ملایم است، روی مشکی یا محو
      می‌شود یا می‌درخشد — و تقویمی که در یکی از دو حالت خوانده
      نشود، نصفِ روز بی‌فایده است.

   ⚠ `oklch` و نه `hsl`: در `hsl`، هشت رنگ با روشناییِ یکسان،
     روشناییِ **دیده‌شده**ی متفاوتی دارند (زردش می‌درخشد، آبی‌اش
     گم می‌شود). `oklch` روشنایی را همان‌طور می‌بیند که چشم.     */
:root {
  --rm-c1: oklch(60% .13 250);   /* آبی */
  --rm-c2: oklch(60% .15 300);   /* بنفش */
  --rm-c3: oklch(60% .12 155);   /* سبز */
  --rm-c4: oklch(65% .13  75);   /* کهربایی */
  --rm-c5: oklch(62% .13 015);   /* گلی */
  --rm-c6: oklch(62% .10 200);   /* فیروزه‌ای */
  --rm-c7: oklch(64% .15  45);   /* نارنجی */
  --rm-c8: oklch(58% .02 250);   /* خاکستری */
}

.ev--c1, .ev--c2, .ev--c3, .ev--c4,
.ev--c5, .ev--c6, .ev--c7, .ev--c8 {
  /* ⚠ `color-mix` یعنی یک رنگ تعریف می‌شود و زمینه و خط از آن
     مشتق — نه شانزده هِکسِ دستی که روزی یکی‌شان جا می‌مانَد. */
  background: color-mix(in oklab, var(--rm) 14%, var(--surface));
  border-inline-start-color: var(--rm);
}
.ev--c1 { --rm: var(--rm-c1); } .ev--c2 { --rm: var(--rm-c2); }
.ev--c3 { --rm: var(--rm-c3); } .ev--c4 { --rm: var(--rm-c4); }
.ev--c5 { --rm: var(--rm-c5); } .ev--c6 { --rm: var(--rm-c6); }
.ev--c7 { --rm: var(--rm-c7); } .ev--c8 { --rm: var(--rm-c8); }

:root[data-theme='dark'] .ev--c1, :root[data-theme='dark'] .ev--c2,
:root[data-theme='dark'] .ev--c3, :root[data-theme='dark'] .ev--c4,
:root[data-theme='dark'] .ev--c5, :root[data-theme='dark'] .ev--c6,
:root[data-theme='dark'] .ev--c7, :root[data-theme='dark'] .ev--c8 {
  background: color-mix(in oklab, var(--rm) 22%, var(--surface));
}

/* نشانِ رنگِ سالن در افسانه و کشوی ساخت */
.rdot {
  inline-size: 10px; block-size: 10px; border-radius: 3px;
  display: inline-block; flex: 0 0 auto;
  /* ⚠ `--rm` را کلاسِ `.ev--cN`ِ کنارش می‌نشاند — پس نشانِ افسانه و
     خودِ رویداد همیشه یک رنگ‌اند، حتی اگر فردا پالت عوض شود. */
  background: var(--rm, var(--line-2));
}

/* ⚠ کارتِ «تازه» روی تقویم — قابِ رنگی دارد تا معلوم باشد چیزی
   باز شده. کاربر با «+»ِ یک خانهٔ کوچک به اینجا آمده و باید همان
   لحظه ببیند کجا افتاده. */
.card--focus {
  border-color: var(--ok-line);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent) 12%, transparent);
  margin-block-end: var(--sp-4);
  scroll-margin-block-start: 80px;
}

/* افسانهٔ رنگِ سالن‌ها، زیرِ چیپ‌های لایه. */
.rlegend {
  display: flex; flex-wrap: wrap; align-items: center; gap: 4px 14px;
  margin-block-start: var(--sp-3);
  padding-block-start: var(--sp-3);
  border-block-start: 1px solid var(--line);
  font-size: var(--t-2xs); color: var(--fg-muted);
}
.rlegend__t { color: var(--fg-faint); }
.rlegend__i { display: inline-flex; align-items: center; gap: 5px; }
/* ⚠ نشان از همان کلاسِ رویداد رنگ می‌گیرد (`.ev--c3`) و نه از یک
   قاعدهٔ دوم: دو تعریف یعنی روزی افسانه یک رنگ می‌گوید و خودِ
   رویداد رنگِ دیگری دارد — و افسانه‌ای که دروغ بگوید از نبودنش
   بدتر است. */
.rlegend .rdot { border-inline-start: 0; }

/* ⚠ میدانِ «تاشده» — اختیاری، و عمداً کم‌رنگ‌تر و جمع‌تر.
   خواستهٔ پرسا دربارهٔ دسته‌بندیِ نامه: «کسی که می‌خواد نامه بزنه
   گیج نشه». حذفش نمی‌کنیم (لازم است) ولی در جریانِ اصلیِ چشم هم
   نمی‌گذاریمش. */
.field--fold {
  border-block-start: 1px dashed var(--line);
  padding-block-start: var(--sp-3);
  margin-block-start: var(--sp-2);
}
.field--fold > label { color: var(--fg-faint); font-weight: 500; }
.field--fold > label::after { content: " (اختیاری)"; font-size: var(--t-2xs); }

/* چیپی که آدم نیست — یک نشانِ تیکِ کوچک به‌جای دایرهٔ حرفِ اول. */
.pchip__tick {
  inline-size: 14px; block-size: 14px; border-radius: 4px;
  flex: 0 0 auto; border: 1.5px solid var(--line-2);
  background: var(--surface);
}
.pchip:has(input:checked) .pchip__tick {
  background: var(--accent); border-color: var(--accent);
}

/* ═══════════════════════════════ راهنما — دفترِ خواسته‌ها

   ⚠ نقلِ قولِ خودِ پرسا با یک خطِ رنگی از توضیحِ من جدا می‌شود.
     اگر هر دو یک‌شکل بودند، شش ماه بعد کسی نمی‌فهمید کدام حرفِ او
     بوده و کدام تفسیرِ من — و همین تفاوت، کلِ فایدهٔ این صفحه است. */
.wish { border-inline-start: 3px solid var(--line-2); }
.wish--ok   { border-inline-start-color: var(--accent); }
.wish--warn { border-inline-start-color: var(--warn); }
.wish--info { border-inline-start-color: var(--info); }
.wish--bad  { border-inline-start-color: var(--bad); }
.wish--mute { border-inline-start-color: var(--line-2); }

.wish__said {
  margin: 0; padding: var(--sp-3);
  border-inline-start: 2px solid var(--accent);
  background: var(--surface-2); border-radius: var(--r-sm);
  font-size: var(--t-md); line-height: 1.85;
}
.wish__said::before { content: "«"; color: var(--fg-faint); }
.wish__said::after  { content: "»"; color: var(--fg-faint); }

.wish__logic { font-size: var(--t-sm); line-height: 1.9; color: var(--fg-muted); }

.wish__where { display: flex; flex-wrap: wrap; gap: 6px; }
.chip--link { color: var(--accent); text-decoration: none; }
.chip--link:hover { text-decoration: underline; }
.chip--bad { background: var(--bad-bg); color: var(--bad); }

.wish__proof > summary {
  cursor: pointer; font-size: var(--t-xs); color: var(--fg-faint);
}
.wish__proof ul { margin: var(--sp-2) 0 0; padding-inline-start: 18px; }
.wish__proof li { font-size: var(--t-xs); color: var(--fg-muted); line-height: 1.9; }

/* چیپِ خاموشِ حوزه‌ها در «دسترسیِ من» */
.pchip--on { background: var(--ok-bg); border-color: var(--ok-line); color: var(--accent); font-weight: 600; }
.pchip--on .pchip__tick { background: var(--accent); border-color: var(--accent); }

/* ⚠ لحنِ «هشدارِ نرم» — برای پرسشی که منتظرِ جواب است، نه خطا.
   در صفحهٔ راهنما، پرسش‌های بی‌جوابِ من با همین لحن نشان داده
   می‌شوند: قرمز یعنی «خراب است» و اینجا چیزی خراب نیست. */
.note--warn { background: var(--warn-bg); border-color: var(--warn-line); color: var(--warn); }

/* ═══════════════════════════════════════════════════════════════
   عکسِ پروفایل — جلسهٔ ۳۰
   ═══════════════════════════════════════════════════════════════

   ⚠⚠ `object-fit: cover` نه `contain`: عکس‌های یوتاب هر نسبتی
      دارند (۱۵۸×۱۹۲ تا ۶۴۰×۸۶۰). با `contain` هر کارت یک قابِ
      خالی دور صورت می‌گیرد و فهرست به‌هم‌ریخته دیده می‌شود؛ با
      `cover` همه یک‌اندازه‌اند و برش از لبه است، نه از صورت.

   ⚠ `background` روشن می‌مانَد تا اگر تصویر نیامد، دایرهٔ خالی
     دیده شود و نه یک شکستگیِ سیاه. */
.av--photo {
  object-fit: cover;
  border-radius: var(--r-full);
  background: var(--surface-2);
  border: 1px solid var(--line-2);
}
.pcard > .av--photo { margin-inline: auto; display: block; }

/* ═══════════════════════════════════════════════════════════════
   موبایل و تبلت — جلسهٔ ۳۰
   ═══════════════════════════════════════════════════════════════

   خواستهٔ پرسا: «از نظرِ بصری این دیبا باید برای موبایل و تبلت هم
   اوکی باشه — خیلی از اندازه‌ها و المان‌ها و کشویی‌بودن… خیلی
   چیزهاش به‌هم‌ریخته بود.»

   ⚠⚠ هر رفعِ این بخش با **اندازه‌گیریِ واقعی** در کروم روی ۳۹۰×۸۴۴
      و ۷۶۸×۱۰۲۴ پیدا شد، نه با خواندنِ کد. دو تایشان را با خواندنِ
      کد هیچ‌وقت پیدا نمی‌کردم، چون هر دو از قاعده‌ای می‌آمدند که
      **برای حالتِ دسکتاپ درست بود**.                              */

@media (max-width: 767px) {

  /* ── ۱. نوارِ پایین واقعاً پایین نبود ────────────────────────
     ⚠⚠ `.side` در حالتِ عادی `position: sticky` با
        `inset-block-start: var(--topbar-h)` است. قاعدهٔ موبایل آن
        را `fixed` با `inset-block-end: 0` می‌کرد ولی
        `inset-block-start` را **پس نمی‌گرفت** — و در یک جعبهٔ
        `fixed` هر دو با هم یعنی `start` برنده می‌شود.

        نتیجه: نوارِ ناوبری درست زیرِ نوارِ بالا می‌چسبید، وسطِ
        صفحه. در عکسِ صفحه دیده می‌شود: یک ردیفِ آیکون زیرِ لوگو،
        جایی که هیچ‌کس دنبالش نمی‌گردد. */
  .side { inset-block-start: auto; }

  /* ── ۲. حفرهٔ ۲۶۰ پیکسلیِ سرِ هر صفحه ───────────────────────
     ⚠⚠ `.phead__main { flex: 1 1 260px }` برای حالتِ **ردیفی**
        نوشته شده بود و آنجا درست است: ۲۶۰ یعنی «دستِ‌کم ۲۶۰ پیکسل
        عرض بگیر».

        ولی قاعدهٔ موبایل `.phead` را ستونی می‌کند — و در یک ستون،
        `flex-basis` یعنی **ارتفاع**. پس بلوکِ عنوان ۲۶۰ پیکسل
        بلند می‌شد و زیرِ دکمه‌ها یک دشتِ سفید می‌ماند. در **هر**
        صفحهٔ دیبا.

     ⚠ درسش: `flex-basis` جهت‌دار است. هر جا `flex-direction` در
       یک مدیا-کوئری عوض می‌شود، هر `flex: … <basis>`ِ آن ظرف هم
       باید بازبینی شود. */
  .phead__main { flex: 1 1 auto; }

  /* ── ۳. نوارِ بالا از صفحه بیرون می‌زد ──────────────────────
     نُه کنترل در ۳۹۰ پیکسل جا نمی‌شود: ۴۲۰ پیکسل لازم داشت و
     فلکس با فشردنِ جعبهٔ جست‌وجو تا ۲۴ پیکسل «حلش» می‌کرد — یعنی
     یک مربعِ سفیدِ بی‌معنا کنارِ لوگو، و باز هم ۱۸ پیکسل لولشِ
     افقیِ کلِ سامانه.

     ⚠⚠ و لولشِ افقی روی موبایل فقط زشتی نیست: هر اسکرولِ عمودیِ
        انگشت کمی افقی هم می‌شود و صفحه زیرِ دست می‌لرزد. */

  /* کلیدِ جمع/بازکردنِ منو: روی موبایل منو **نوارِ پایین** است و
     جمع نمی‌شود. کلیدی که کاری نمی‌کند، بدتر از نبودنش است. */
  #side-btn { display: none; }
  /* انتخابِ پوستهٔ رنگی: تنظیمی است که یک بار در عمر زده می‌شود.
     جایش `/me/` است، نه نوارِ تنگِ گوشی. */
  #skin-btn { display: none; }
  /* `.topbar__meta` (تاریخ) از قبل پنهان بود و فقط **نقطهٔ** کنارش
     می‌ماند — شش پیکسل نقطهٔ بی‌معنا. */
  .topbar__when { display: none; }

  /* و حالا که جا باز شده، جست‌وجو دوباره یک هدفِ لمسیِ واقعی
     می‌شود. ⚠ `min-inline-size` لازم است چون `min-inline-size: 0`
     در قاعدهٔ پایه یعنی فلکس اجازه دارد تا صفر فشارش دهد. */
  .cmd { min-inline-size: 44px; height: 40px; }

  /* ── ۴. عکس/حروفِ کاربر در نوارِ بالا ───────────────────────
     ۲۷ پیکسل روی ماوس خوب است و زیرِ انگشت نه. */
  .avatar { width: 36px; height: 36px; }
}

/* ── ۵. هدف‌های لمسیِ کوچک، هر جا که هست ───────────────────────
   ⚠ این‌ها به `pointer: coarse` بسته‌اند و نه به عرضِ صفحه: تبلتِ
     ۱۰۲۴ پیکسلی صفحهٔ بزرگ دارد ولی انگشت هم دارد. */
@media (pointer: coarse) {
  /* `.btn--sm` روی ۳۴ بود؛ ۳۴ یعنی سه بار زدن تا بخورد. */
  .btn--sm { height: 40px; }
  /* تراشه‌ها (فیلترها) ۱۹ تا ۳۰ پیکسل بودند — کوچک‌ترین هدف‌های
     کلِ سامانه، و درست همان‌هایی که کاربر پشتِ‌هم می‌زند. */
  .chip, .lchip { min-height: 36px; }
  /* ⚠ ۳۶ و نه ۴۴: این یکی در **نوارِ بالا**ست و آنجا هر پیکسل
     شمرده است. ۴۰ را امتحان کردم و همان ۱۳ پیکسل، نوارِ بالا را
     روی تبلت از صفحه بیرون زد. */
  .avatar { width: 36px; height: 36px; }
  /* ⚠ لینک‌های داخلِ جدول عمداً بزرگ **نمی‌شوند**: ارتفاعِ خودِ
     سطر با `tbody td { padding: 11px }` به ۴۲ پیکسل می‌رسد و
     ناحیهٔ زدنیِ واقعی همان سطر است. بزرگ‌کردنِ تک‌تکِ لینک‌ها
     جدولِ ۲۱ هزار ردیفی را به یک طومار تبدیل می‌کرد. */
}


/* ── ۶. نوارِ بالا روی تبلت ─────────────────────────────────────
   ⚠⚠ تا امروز فقط دو حالت داشتیم: دسکتاپِ کامل، و موبایلِ ≤۷۶۷.
      یعنی یک آی‌پدِ ۷۶۸ پیکسلی **نوارِ بالای دسکتاپ** را می‌گرفت با
      دوازده کنترل — و چون `.iconbtn` کوچک‌شدنی نیست (`flex-shrink: 0`
      و درست هم هست)، فلکس چاره‌ای جز بیرون‌زدن نداشت.

      نتیجه‌اش لولشِ افقی بود، و با اندازه‌گیری روی ۷۶۸×۱۰۲۴ دیده شد
      نه با خواندنِ کد.

   ⚠ آنچه می‌رود، سه کنترلِ **دسکتاپی** است و نه سه قابلیتِ کاری:
     راهنمای میان‌برِ صفحه‌کلید، تمام‌صفحه، و کلیدِ چپ‌چین/راست‌چین.
     هر سه با صفحه‌کلید یا ماوس معنا دارند. هیچ‌کدام کاری نیستند که
     کارمند روی تبلت انجام دهد. */
@media (max-width: 1024px) {
  #keys-btn, .topbar__wide { display: none; }
  /* «اتاق ایران و آلمان» زیرِ «دیبا» — روی نوارِ تنگ، خطِ دومِ
     برند نوار را بلند و شلوغ می‌کند بی آنکه چیزی بگوید. */
  .brand__sub { display: none; }
  /* تاریخِ شمسی: روی تبلت جایش صفحه است نه نوارِ بالا. */
  .topbar__when { display: none; }
}

/* ── ۷. برند در نوارِ بالا: همان تلهٔ `.cmd`، یک سال بعد ────────
   ⚠⚠ `.brand` یک آیتمِ فلکس است و `min-width` پیش‌فرضش `auto` —
      یعنی **کوچک‌تر از محتوایش نمی‌شود**. روی ۳۹۰ پیکسل همین باعث
      می‌شد خطِ فلکسِ نوارِ بالا از ظرف بیرون بزند و کلِ سامانه
      ۳۱ پیکسل افقی بلولد. دقیقاً همان چیزی که در `.cmd` توضیح داده
      شده (خطِ ۴۳۰) و آنجا رفع شده بود — ولی فقط برای `.cmd`.

   ⚠ درسش: در نواری که آیتم‌هایش کوچک‌نشدنی‌اند، **هر** آیتمِ متنی
     باید `min-inline-size: 0` داشته باشد. یکی کافی نیست.

   ⚠ و نشان کوچک نمی‌شود (`flex-shrink: 0`): لوگویی که با فشار
     له شود، بدتر از لوگویی است که نباشد. متن کوتاه می‌شود. */
.brand { min-inline-size: 0; }
.brand__logo { flex-shrink: 0; }
.brand > span { min-inline-size: 0; overflow: hidden; }
.brand__name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ── ۸. و عارضهٔ جانبیِ رفعِ ۷ ─────────────────────────────────
   ⚠⚠ تا `.brand` اجازهٔ کوچک‌شدن گرفت، نامِ «دیبا» کنارِ لوگو
      **ناپدید شد** — در عکسِ صفحه دیده شد، نه در عدد. علتش
      ظریف است و ارزشِ نوشتن دارد:

      `.cmd` پایهٔ فلکسش `auto` بود، یعنی «به اندازهٔ محتوایت» — و
      محتوایش متنِ بلندِ «نام شخص، شماره‌ی سند، یا یک فرمان بنویس…»
      است. پس مجموعِ پایه‌ها از نوار بیشتر می‌شد و فلکس **همه‌ی**
      آیتم‌های کوچک‌شدنی را به نسبت جمع می‌کرد؛ تا دیروز `.brand`
      کوچک‌شدنی نبود و به‌جایش نوار بیرون می‌زد.

   ⚠ درمان این نیست که `.brand` را دوباره سفت کنیم — این است که
     جعبهٔ جست‌وجو **از اول** چیزی مطالبه نکند: `flex-basis: 0`
     یعنی «هرچه ماند مالِ من»، نه «به اندازهٔ متنم جا بده». */
.cmd { flex-basis: 0; }
.cmd__hint {
  min-inline-size: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
}

/* ═══════════════════════════════════════════ تاریخِ دوگانه (جلسهٔ ۳۶)
   دو کادرِ شمسی و میلادی کنار هم. `core/dualdate.py` می‌سازدشان و
   `static/diba/dualdate.js` هم‌گامشان می‌کند.

   ⚠ ویژگی‌های منطقی (`inline`/`block`)، نه `left`/`right` — قاعده‌ی
     RTLِ پروژه، و تستِ نگهبانش. */
.dd {
  display: flex; gap: 8px; align-items: flex-start; flex-wrap: wrap;
}
.dd__box { display: flex; flex-direction: column; gap: 2px; min-inline-size: 150px; flex: 1 1 150px; }
.dd__box input {
  inline-size: 100%; min-block-size: 34px;
  background: var(--surface); border: 1px solid var(--line-2);
  border-radius: var(--r-sm); color: var(--fg);
  font-family: var(--font); font-size: var(--t-sm); padding: 6px 10px;
}
.dd__box input:focus { outline: none; border-color: var(--accent); }
.dd__tag { font-size: var(--t-3xs); color: var(--fg-3); padding-inline-start: 2px; }

/* ═══════════════════════════ فرمِ عضویت، مرتب‌شده (جلسهٔ ۳۹)

   خواستهٔ پرسا: «فرم خیلی بزرگ و نامرتب و زشت هست.»

   ⚠ بخش‌ها `<details>` هستند و نه div + جاوااسکریپت: باز و بسته
     شدن بی هیچ خطِ جاوااسکریپتی کار می‌کند، با صفحه‌کلید هم کار
     می‌کند، و صفحه‌خوان خودش می‌گوید «باز/بسته». همان تصمیمی که
     برای منوی کناری گرفته شد و جواب داد.

   ⚠ ویژگی‌های منطقی (`inline`/`block`) و نه `left`/`right` — قاعدهٔ
     RTLِ پروژه و تستِ نگهبانش. */
.sform { display: flex; flex-direction: column; gap: var(--sp-3); }

.sform__sec {
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-2); overflow: hidden;
}
.sform__head {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: 11px var(--sp-5); cursor: pointer;
  font-size: var(--t-sm); font-weight: 600; color: var(--fg);
  user-select: none; list-style: none;
}
.sform__head::-webkit-details-marker { display: none }
.sform__head:hover { background: var(--surface-2) }
/* ⚠ فلش آخرِ ردیف می‌نشیند و با باز شدن می‌چرخد — نشانهٔ حالت،
   بی آنکه متنی اضافه شود. */
.sform__head svg { margin-inline-start: auto; transition: transform .16s ease }
.sform__sec[open] > .sform__head svg { transform: rotate(180deg) }
.sform__sec[open] > .sform__head { border-block-end: 1px solid var(--line) }
.sform__body { padding: var(--sp-2) var(--sp-5) var(--sp-4) }

/* ⚠⚠ نوارِ چسبانِ پایین: دکمهٔ ذخیره همیشه دیده می‌شود. بی این،
   کاربر برای ذخیره باید تا ته اسکرول کند — و روی فرمی با
   چهل میدان، این یعنی دو بار اسکرولِ بی‌دلیل در هر ذخیره. */
.sform__bar {
  position: sticky; inset-block-end: 0; z-index: 3;
  display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap;
  padding: var(--sp-3) var(--sp-5);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-2); box-shadow: var(--sh-2);
}
.sform__hint {
  font-size: var(--t-2xs); color: var(--fg-faint);
  margin-inline-start: auto; max-inline-size: 38ch;
}

/* آپلودِ فایل — ورودیِ سیستمی با هیچ‌کدام از رنگ‌های دیبا
   هم‌خانواده نیست، مخصوصاً در حالتِ تیره. */
.field input[type="file"] {
  padding: 7px 10px; font-size: var(--t-2xs); cursor: pointer;
}
.field input[type="file"]::file-selector-button {
  margin-inline-end: var(--sp-3); padding: 5px 12px;
  background: var(--surface-2); color: var(--fg);
  border: 1px solid var(--line-2); border-radius: var(--r-sm);
  font-family: var(--font); font-size: var(--t-2xs); cursor: pointer;
}
.field input[type="file"]::file-selector-button:hover {
  border-color: var(--accent);
}

@media (max-width: 700px) {
  .sform__hint { display: none }
}

/* ═══════════════════════════════════════════════════════════════
   ردیف‌های مدرک (جلسهٔ ۴۰)
   ───────────────────────────────────────────────────────────────
   خواستهٔ پرسا: «توی قسمتِ مدارک ممکنه چندین مدرک داشته باشه… یکی
   رو اضافه می‌کنیم یکی دیگه هم اضافه بشه.»

   ⚠ فقط ویژگیِ منطقی (inline-start/end) — قاعدهٔ RTLِ پروژه، و تستِ
     نگهبان هم همین را می‌سنجد.

   ⚠ گرید و نه فلکس: سه ستونِ ردیف‌ها باید **زیرِ هم** بیفتند وگرنه
     ده مدرکِ پشتِ‌سرِ هم یک پلکانِ نامرتب می‌شود. با
     `grid-template-columns` هر ردیف دقیقاً زیرِ ردیفِ بالایی است.
   ═══════════════════════════════════════════════════════════════ */
.docrows { display: flex; flex-direction: column; gap: var(--sp-2); }
.docrows__list { display: flex; flex-direction: column; gap: var(--sp-2); }

.docrow {
  display: grid; gap: var(--sp-3); align-items: end;
  grid-template-columns: minmax(130px, 0.8fr) minmax(0, 1.4fr) minmax(0, 1.2fr) auto;
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--surface-2);
}
/* ⚠ `.field` ته‌فاصله دارد و اینجا لازم نیست: ردیف خودش فاصله‌گذار
   است و با آن، هر ردیف ۱۶ پیکسل بلندتر از لازم می‌شد. */
.docrow .field { margin-bottom: 0; }
.docrow__act { display: flex; align-items: center; padding-bottom: 4px; }
.docrow__act .btn[disabled] { opacity: .35; cursor: default; }

.docrows__foot {
  display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap;
}

@media (max-width: 760px) {
  /* ⚠ در موبایل سه ستون یعنی سه کادرِ باریکِ بی‌استفاده. یک ستون،
     و دکمهٔ برداشتن ته ردیف. */
  .docrow { grid-template-columns: 1fr; align-items: stretch; }
  .docrow__act { justify-content: flex-end; padding-bottom: 0; }
}

/* ═══════════════════════════════════════════════════════════════
   صفحهٔ عمومی (جلسهٔ ۴۰) — فرمِ کاریابی
   ───────────────────────────────────────────────────────────────
   ⚠⚠ تنها صفحه‌هایی از دیبا که بی ورود باز می‌شوند. عمداً نوارِ
      کناریِ داخلی را نمی‌کشند: صفحهٔ عمومی نباید نامِ بخش‌های
      داخلی یا آمارِ اتاق را لو بدهد.

   ⚠ ستونِ محدود (۹۶۰ پیکسل) و نه تمام‌عرض: فرمِ هفت‌بخشیِ کاریابی
     روی یک مانیتورِ ۲۷ اینچی تمام‌عرض، خطِ چشمِ کاربر را در هر
     ردیف نصفِ صفحه می‌بَرد.
   ═══════════════════════════════════════════════════════════════ */
.pub {
  max-inline-size: 960px;
  margin-inline: auto;
  padding: var(--sp-4) var(--sp-4) 64px;
}
.pub__head {
  display: flex; align-items: center; gap: var(--sp-3);
  padding-block: var(--sp-4);
  border-block-end: 1px solid var(--line);
  margin-block-end: var(--sp-4);
}
.pub__title { margin: 0; font-size: var(--t-xl); font-weight: 600; }
.pub__sub   { font-size: var(--t-2xs); color: var(--fg-muted); }
.pub__body  { display: flex; flex-direction: column; gap: var(--sp-3); }
.pub__foot {
  margin-block-start: var(--sp-5);
  padding-block-start: var(--sp-3);
  border-block-start: 1px solid var(--line);
  font-size: var(--t-2xs); color: var(--fg-muted); text-align: center;
}

@media (max-width: 700px) {
  .pub { padding-inline: var(--sp-3); }
}

/* ═══════════════════════════════════════════════════════════════
   صفحهٔ تأییدِ حذف و آیکونِ هشدارِ سرصفحه (جلسهٔ ۴۱)
   ───────────────────────────────────────────────────────────────
   ⚠ دکمهٔ خطرناک **قرمزِ توپر** است و نه یک دکمهٔ معمولیِ دیگر: در
     صفحه‌ای که دو دکمه دارد و یکی‌شان برگشت‌ناپذیرتر است، شکلِ
     یکسان یعنی کاربر آن یکی را می‌زند.
   ═══════════════════════════════════════════════════════════════ */
.btn--danger {
  background: var(--bad); border-color: var(--bad); color: #fff;
}
.btn--danger:hover { filter: brightness(1.08); color: #fff; }
.phead__ico--bad { color: var(--bad); border-color: var(--bad); }

/* ═══════════════════════════════════════════════════════════════
   راهنمای میان‌برها — گروه‌بندی، و پنجرهٔ بازگردانی (جلسهٔ ۴۱)
   ═══════════════════════════════════════════════════════════════ */
.keys__grp {
  padding: 10px var(--sp-4) 5px;
  font-size: var(--t-2xs); font-weight: 700; color: var(--fg-muted);
  background: var(--surface-2);
  border-block-end: 1px solid var(--line);
}
/* «سپس» بینِ دو کلیدِ دنباله‌ای (g سپس h) — کلید نیست، پس شکلِ
   کلید هم ندارد. */
.keys__then {
  font-size: var(--t-2xs); color: var(--fg-muted);
  align-self: center; padding-inline: 2px;
}

/* ⚠ وقتی پیشوندِ دنباله فعال است، کاربر باید بداند سامانه منتظرِ
   کلیدِ دوم است — وگرنه «g» زده و هیچ اتفاقی نیفتاده به نظر
   می‌رسد و دوباره می‌زند. */
html[data-keyseq]::after {
  content: attr(data-keyseq) " …";
  position: fixed; inset-block-end: 16px; inset-inline-start: 16px;
  z-index: 90;
  padding: 5px 12px; border-radius: var(--r-sm);
  background: var(--fg); color: var(--surface);
  font-family: var(--font); font-size: var(--t-2xs);
  box-shadow: var(--z-2); pointer-events: none;
}

.undo__body { padding: var(--sp-4); display: flex; flex-direction: column;
              gap: var(--sp-3); }
.undo__what { font-size: var(--t-md); font-weight: 600; }
.undo__lines {
  margin: 0; padding-inline-start: 18px;
  display: flex; flex-direction: column; gap: 5px;
  font-size: var(--t-xs); line-height: 1.8;
}
.undo__lines li { unicode-bidi: plaintext; }
.undo__foot {
  display: flex; gap: var(--sp-2); justify-content: flex-start;
  padding: var(--sp-3) var(--sp-4);
  border-block-start: 1px solid var(--line); background: var(--surface-2);
}

/* قابِ بازی (جلسهٔ ۴۱) — ارتفاعِ ثابت، چون بازی خودش صفحهٔ کامل است */
.gameframe {
  display: block; inline-size: 100%; block-size: min(78vh, 820px);
  border: 0; background: #0a1620;
}
@media (max-width: 700px) { .gameframe { block-size: 70vh; } }

/* نوارِ «نوعِ پرونده» بالای فرمِ عضویت (جلسهٔ ۴۱) — حقیقی یا حقوقی */
.kindbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-3); flex-wrap: wrap;
  margin-block-end: var(--sp-3);
  padding: 9px var(--sp-3);
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--surface-2);
  font-size: var(--t-xs);
}
.kindbar__now { display: inline-flex; align-items: center; gap: 7px; }
.kindbar__swap { font-size: var(--t-2xs); color: var(--fg-muted); }
.kindbar__swap:hover { color: var(--accent); }

/* ═══════════ انتخابِ طرحِ کارت (جلسهٔ ۴۲) ═══════════════════════
   ⚠⚠ طرح با **تصویر** انتخاب می‌شود و نه با نام. کارمندی که باید
      بگوید «طلایی یا نقره‌ای؟» باید هر دو را ببیند؛ سه رادیوی متنی
      همان تصمیم را به حافظه واگذار می‌کند.
   ⚠ نسبتِ کادر همان ISO 7810 است تا پیش‌نمایش با چیزی که چاپ
     می‌شود یکی باشد. */
.cdesigns { display: flex; flex-wrap: wrap; gap: 10px; }

.cdesign {
  position: relative;
  display: flex; flex-direction: column; gap: 6px;
  padding: 7px; border-radius: 12px;
  border: 1px solid var(--line); background: var(--surface-1);
  cursor: pointer; inline-size: 190px;
  transition: border-color .12s ease, background .12s ease;
}
.cdesign:hover { background: var(--surface-2); }
.cdesign input {
  position: absolute; inset-inline-start: 10px; inset-block-start: 10px;
  inline-size: 16px; block-size: 16px; accent-color: var(--accent);
  z-index: 2;
}
.cdesign:has(input:focus-visible) {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
.cdesign:has(input:checked) {
  border-color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent);
}
.cdesign__art {
  display: block; aspect-ratio: 85.6 / 53.98;
  border-radius: 7px; overflow: hidden; background: var(--surface-2);
  border: 1px solid var(--line);
}
.cdesign__art img {
  inline-size: 100%; block-size: 100%; object-fit: cover; display: block;
}
.cdesign__n { font-size: 12px; font-weight: 600; text-align: center; }

.inp--sm { padding-block: 3px; padding-inline: 6px; font-size: 12px; }

/* ═══════════ پیش‌نمایشِ مدارک (جلسهٔ ۴۲) ═══════════════════════
   ⚠ تصویرِ کوچک کنارِ نامِ فایل، نه به‌جایش: نام هم لازم است، چون
     دو اسکنِ کارتِ ملی از دور شبیه‌اند. */
.docthumb { display: inline-flex; align-items: center; gap: 8px; }
.docthumb img {
  inline-size: 46px; block-size: 34px; object-fit: cover;
  border-radius: 6px; border: 1px solid var(--line);
  background: var(--surface-2); flex: 0 0 auto;
}
.docthumb:hover img { border-color: var(--accent); }

/* عکسِ پرونده، کنارِ نامِ شخص یا شرکت */
.pphoto {
  inline-size: 84px; block-size: 108px; object-fit: cover;
  border-radius: 10px; border: 1px solid var(--line);
  background: var(--surface-2); display: block;
}

/* ═══════════ گالریِ تصویرهای پرونده (جلسهٔ ۴۲) ═══════════════ */
.gal { display: flex; flex-wrap: wrap; gap: 10px; }
.gal__i {
  display: flex; flex-direction: column; gap: 4px;
  inline-size: 132px; text-decoration: none;
}
.gal__i img {
  inline-size: 132px; block-size: 92px; object-fit: cover;
  border-radius: 9px; border: 1px solid var(--line);
  background: var(--surface-2); display: block;
}
/* ⚠ عکسِ خودِ شخص عمداً پرتره است و مدارک منظره: کارتِ ملیِ اسکن‌شده
   افقی است و عکسِ پرسنلی عمودی؛ یک نسبت برای هر دو، یکی را می‌بُرد. */
.gal__i--me img { block-size: 168px; object-fit: cover; }
.gal__i:hover img { border-color: var(--accent); }
.gal__cap {
  font-size: 11.5px; color: var(--muted); text-align: center;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ورودِ جایگزین (کد/رمز) — زیرِ فرمِ ورود (جلسهٔ ۴۴) */
.auth__alt {
  display: flex; align-items: center; justify-content: center;
  gap: 10px; margin-block-start: 14px;
}
.auth__alt > span { color: var(--muted); font-size: 12px; }

/* ═══════════ راهنمای جامع (جلسهٔ ۴۴) ═══════════════════════════ */
.hb__meter { display: flex; align-items: center; gap: 12px; }
.hb__bar {
  flex: 1; block-size: 8px; border-radius: 99px;
  background: var(--surface-2); overflow: hidden;
}
.hb__bar > span {
  display: block; block-size: 100%; background: var(--accent);
  border-radius: 99px;
}
.hb__num { display: flex; align-items: baseline; gap: 8px; white-space: nowrap; }

.hb__what { margin: 0 0 12px; color: var(--ink); }

/* ⚠ عکس کنارِ مراحل و نه زیرشان: در دمو باید هم‌زمان دیده شوند. */
.hb__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; align-items: start; }
@media (max-width: 860px) { .hb__grid { grid-template-columns: 1fr; } }

.hb__steps { margin: 0; padding-inline-start: 20px; display: grid; gap: 9px; }
.hb__steps li { line-height: 1.75; }
.hb__steps b { font-weight: 600; }
.hb__steps .faint { display: block; font-size: 12px; }

.hb__shot { display: block; border-radius: 12px; overflow: hidden;
            border: 1px solid var(--line); background: var(--surface-2); }
.hb__shot img { inline-size: 100%; display: block; }

.hb__notes {
  margin: 14px 0 0; padding: 10px 14px; padding-inline-start: 28px;
  border-radius: 10px; background: var(--surface-2);
  display: grid; gap: 6px; font-size: 12px; color: var(--muted);
}

/* ═══════════════════════════ برنامهٔ سالانه و KPI (جلسهٔ ۴۷)

   ⚠⚠⚠ خانه‌ها **فقط رنگ نیستند.** هر وضعیت یک نشانهٔ شکلی هم دارد:
      پر، نیم‌پر، ضربدر، نقطه‌چین. جدولی که معنایش را فقط با رنگ
      بگوید، برای کسی که رنگ را تشخیص نمی‌دهد یک شبکهٔ خاکستری است —
      و در پرینتِ سیاه‌وسفیدِ گزارشِ مجمع هم همین‌طور.

   ⚠⚠ رنگ‌ها از توکن‌های خودِ دیبا می‌آیند (`--accent`، `--warn`،
      `--bad`) و نه از هِکسِ تازه. یعنی در حالتِ تیره و روشن هر دو
      درست‌اند، بی آنکه جایی دوباره تعریف شوند.

   ⚠ اندازه ۲۲px است و نه کمتر: خانه‌ای که هدفِ کلیک است نباید از
     حدِ لمسی کوچک‌تر شود، و دوازده‌تاشان کنارِ هم روی موبایل. */

.kpicell {
  inline-size: 22px; block-size: 22px;
  padding: 0; margin: 0 auto; display: block;
  border: 1px solid var(--line-2); border-radius: 6px;
  background: transparent; cursor: pointer;
  transition: transform .1s, border-color .12s, background .12s;
}
.kpicell:disabled { cursor: default; }
.kpicell:not(:disabled):hover { transform: scale(1.12); }
.kpicell:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.kpicell__mark { display: block; inline-size: 100%; block-size: 100%; }

.kpicell--none        { border-style: dashed; background: transparent; }
.kpicell--planned     { background: var(--idle); border-color: var(--line-2); }
.kpicell--done_on_time{ background: var(--accent); border-color: var(--accent); }
.kpicell--done_late   { background: var(--warn);   border-color: var(--warn); }
.kpicell--failed      { background: var(--bad);    border-color: var(--bad); }
.kpicell--err         { border-color: var(--bad); border-style: dotted; }

/* نشانهٔ شکلی، برای وقتی که رنگ کافی نیست */
.kpicell--done_late .kpicell__mark {
  background: linear-gradient(135deg, transparent 46%,
              var(--on-accent) 46%, var(--on-accent) 54%, transparent 54%);
}
.kpicell--failed .kpicell__mark {
  background:
    linear-gradient(45deg,  transparent 44%, var(--on-accent) 44%,
                    var(--on-accent) 56%, transparent 56%),
    linear-gradient(135deg, transparent 44%, var(--on-accent) 44%,
                    var(--on-accent) 56%, transparent 56%);
}

.kpigrid__section > td {
  background: var(--surface-2);
  font-size: var(--t-2xs); letter-spacing: .01em;
}

.kpichip {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--t-2xs); color: var(--fg-muted);
}
.kpichip__dot {
  inline-size: 12px; block-size: 12px; border-radius: 4px;
  border: 1px solid var(--line-2); display: inline-block;
}
.kpichip--none .kpichip__dot         { border-style: dashed; }
.kpichip--planned .kpichip__dot      { background: var(--idle); }
.kpichip--done_on_time .kpichip__dot { background: var(--accent); border-color: var(--accent); }
.kpichip--done_late .kpichip__dot    { background: var(--warn);   border-color: var(--warn); }
.kpichip--failed .kpichip__dot       { background: var(--bad);    border-color: var(--bad); }

/* ── کلاس‌هایی که قالب‌ها می‌خواستند و تعریف نشده بودند (جلسهٔ ۴۷)

   ⚠⚠⚠ این‌ها را `core.test_css_classes` پیدا کرد، نه چشمِ آدم. هر
      کدام یعنی یک تکه از یک صفحه **بی‌استایل** رندر می‌شده و هیچ
      تستی قرمز نمی‌شده. بدترین نوعِ خرابی: بی‌صدا، و فقط در صفحه‌ای
      که کمتر باز می‌شود. */

/* نقطهٔ وضعیت کنارِ عنوانِ فهرست — `.dot` فقط رنگِ accent داشت */
.dot--ok   { background: var(--accent); }
.dot--warn { background: var(--warn); }
.dot--bad  { background: var(--bad); }

/* عنوان و زیرعنوانِ داخلِ سرِ کارت */
.card__ico { color: var(--fg-faint); display: flex; flex-shrink: 0; }
.card__t   { font-size: var(--t-sm); font-weight: 600; line-height: 1.4;
             min-inline-size: 0; overflow: hidden; text-overflow: ellipsis; }
.card__s   { font-size: var(--t-2xs); color: var(--fg-muted); margin-top: 2px; }

/* ردیفِ «یک مدرک» در فرمِ پیوست — سه ستون که روی موبایل یکی می‌شوند */
.docrow__kind { flex: 1 1 160px; min-inline-size: 0; }
.docrow__file { flex: 1 1 200px; min-inline-size: 0; }
.docrow__note { flex: 2 1 240px; min-inline-size: 0; }

/* ⚠⚠ ستونِ اولِ جدولِ KPI کفِ عرض دارد.

   در عکسِ موبایل دیده شد: بی این، «برگزاری میزِ مشترکِ تجاری با
   شرکت‌های آلمانی» در پنج خط می‌افتاد — تقریباً یک کلمه در هر خط —
   و ردیف چهار برابرِ بلندی لازم را می‌گرفت.

   جدول خودش افقی اسکرول می‌شود، پس پهن‌ترکردنِ این ستون چیزی را
   نمی‌شکند. */
.kpigrid td:first-child,
.kpigrid th:first-child { min-inline-size: 220px; }

/* ═══════════════════════════ جدول‌های کاری (جلسهٔ ۴۸)

   ⚠⚠ خانهٔ جدول باید **فشرده** باشد: یک جدولِ پانزده‌ستونه با
      ورودی‌های اندازهٔ فرم، در یک صفحه جا نمی‌شود و کاربر مجبور
      می‌شود مدام افقی اسکرول کند.

   ⚠ حاشیهٔ ورودی تا وقتی روی خانه نرفته‌ای **نامرئی** است. پانزده
     کادرِ خاکستری در هر ردیف، جدول را به یک فرمِ غول‌پیکر تبدیل
     می‌کند و چشم دیگر ردیف را دنبال نمی‌کند. */

.wsheet__in {
  inline-size: 100%; min-inline-size: 110px;
  padding: 4px 6px; border-radius: 6px;
  border: 1px solid transparent; background: transparent;
  font: inherit; color: inherit;
}
.wsheet__in:hover { border-color: var(--line); }
.wsheet__in:focus { border-color: var(--accent); background: var(--surface); }
.wsheet__in--bool { inline-size: auto; min-inline-size: 0; }

/* وضعیتِ ذخیره — سه حالت، هر کدام دیدنی بی آنکه مزاحم باشد */
.wsheet__in--busy { opacity: .55; }
.wsheet__in--ok   { border-color: var(--accent); }
.wsheet__in--bad  { border-color: var(--bad); background: transparent; }

/* ⚠⚠ خانه‌ای که همین الان کسِ دیگری عوضش کرده، لحظه‌ای روشن می‌شود.
      بی این نشانه، تغییرِ همکار بی‌صدا زیرِ دستت می‌نشیند و بعداً
      فکر می‌کنی خودت اشتباه نوشته‌ای. */
.wsheet__cell--fresh { background: color-mix(in oklab, var(--accent) 16%, transparent); }

@media (prefers-reduced-motion: no-preference) {
  .wsheet__cell--fresh { transition: background .5s ease-out; }
}

.wsheet td { padding-block: 3px; }
.wsheet th .small { display: block; font-weight: 400; }

/* ═══════════ کلاس‌هایی که سال‌ها در قالب بودند و قاعده نداشتند
   (جلسهٔ ۴۹)

   ⚠⚠⚠ همه را نگهبانِ `core.test_css_classes` پس از بستنِ یک سوراخِ
      خودش پیدا کرد: نسخهٔ قبلی وقتی **کلِ خانواده** غایب بود ساکت
      می‌مانْد، یعنی دقیقاً در بدترین حالت. */

/* ستونِ عددیِ جدول — در ۲۳ قالب استفاده شده و هیچ‌وقت تعریف نشده
   بود. چون `text-align` پیش‌فرضِ `td` در RTL راست است، اثرش دیده
   نمی‌شد؛ ولی `white-space` نداشت و سرستون‌های دوکلمه‌ای می‌شکستند. */
.t td.n, .t th.n { text-align: center; white-space: nowrap; }

/* جعبهٔ ثبتِ ترددِ کارمند (جلسهٔ ۴۴) — کلِ خانواده بی‌قاعده بود، پس
   ساعت در اندازهٔ متنِ معمولی و دکمه چسبیده به آن رندر می‌شد. */
.punchbox {
  display: flex; flex-direction: column; align-items: center;
  gap: 14px; text-align: center; padding-block: 8px;
}
.punchbox__now { display: grid; gap: 2px; }
.punchbox__clock {
  font-size: var(--n-xl); font-weight: 600; line-height: 1.1;
  letter-spacing: .01em;
}
.punchbox__btn { min-inline-size: 190px; min-block-size: 44px; }
.punchbox__hint { max-inline-size: 42ch; }

/* پوستهٔ کارتِ راهنمای جامع */
.hb { display: grid; gap: 4px; }

/* ⚠ ردیفِ ابطال‌شده در دفترِ تردد: کم‌رنگ و خط‌خورده، ولی **حذف
   نشده** — قاعدهٔ فقط-افزودنیِ پروژه یعنی ردِ ابطال باید بماند. */
.row--dim > td { opacity: .5; text-decoration: line-through; }

/* ═══════════════════════════ ذخیرهٔ خودکار و اکسل‌واری (جلسهٔ ۵۰)

   ⚠⚠⚠ نوارِ وضعیتِ ذخیره. ذخیرهٔ خودکاری که **دیده نشود**، از
      ذخیرهٔ دستی بدتر است: کاربر نمی‌داند منتظر بماند یا نه، و
      وقتی شبکه قطع است هم خبردار نمی‌شود — فقط بعداً می‌فهمد یک
      ساعت کارش نرفته.

   ⚠⚠ رنگ تنها حاملِ معنا نیست؛ متنِ هر سه حالت صریح است. و
      `aria-live="polite"` در قالب هست تا صفحه‌خوان هم بشنود. */
.savebar {
  display: inline-flex; align-items: center; gap: 6px;
  min-block-size: 20px;
  font-size: var(--n-sm); color: var(--fg-faint);
}
.savebar--busy { color: var(--fg-muted); }
.savebar--ok   { color: var(--accent); }
.savebar--bad  { color: var(--bad); font-weight: 600; }

/* ⚠ شمارندهٔ «چند ردیف با هم» در پاورقیِ جدول — کادرِ تمام‌عرض
     آنجا کلِ نوار را می‌شکند. */
.inp--mini { inline-size: 5.5rem; min-inline-size: 0; }

/* ⚠⚠ چیپِ نامِ تگ‌شده‌ها زیرِ خانهٔ «شخص». نامِ کاربریِ ناشناخته
      `chip--bad` می‌گیرد — غلطِ املایی همان لحظه دیده می‌شود، نه
      فردا که طرف می‌پرسد «چرا به من خبر ندادی؟» */
.chips {
  display: flex; flex-wrap: wrap; gap: 3px;
  margin-block-start: 3px;
}
.chips:empty { display: none; }
.chips .chip { font-size: var(--n-xs); padding: 1px 6px; }
