/* ═══════════════════════════════════════════════════════════════════
   DISCOVERY HUBS — /consult · /clinic · /clinic/{slug} · /psychology · /diseases
   ═══════════════════════════════════════════════════════════════════
   یک ورقه کوچک برای همه صفحه‌های «انتخاب». توکن‌های پایه از
   /css/css-variables.css می‌آیند که در لایوت سراسری لود می‌شود.

   ── امضای بصریِ این صفحه‌ها: «هاله تصویرسازی» ───────────────────────
   کتابخانه ۲۸۱ تصویرسازیِ سایت همه یک زبان دارند: سوژه مرجانی روی هاله
   نعناییِ محو که تا سفیدِ کناره‌ها پخش می‌شود. تا امروز این تصویرها در یک
   مربعِ ۵۶ پیکسلی می‌نشستند و لبه سفیدشان روی زمینه صفحه یک وصله آشکار
   بود — یعنی دقیقاً همان چیزی که خاصِ این محصول است، دور ریخته می‌شد.

   این ورقه هاله خودِ تصویر را به بیرون ادامه می‌دهد: بشقابی دایره‌ای که
   گرادیانِ شعاعی‌اش از سفیدِ مرکز به همان نعناییِ لبه می‌رسد. لبه سفیدِ
   فایل در بشقاب حل می‌شود و تصویر عضوِ رابطِ کاربری می‌شود، نه مهمانِ آن.

   ☠️ رنگ فقط همین‌جاست. اسمِ تخصص، عدد، خط‌ها و متن‌ها همه خنثی‌اند تا
   بشقاب تنها چیزِ رنگیِ صفحه بماند. اگر رنگِ دومی اضافه کردی، این یکی
   دیگر امضا نیست، تزئین است.
   ═══════════════════════════════════════════════════════════════════ */

.hub-page {
    /* هاله نعنایی — مستقیماً از خودِ تصویرسازی‌ها برداشته شده */
    --hub-aura: #E8F5F2;
    --hub-aura-edge: #CFE7E2;
    --hub-aura-live: #6FC2B4;
    --hub-aura-wash: #F4FBF9;

    /* خط و متن — عمداً نرم‌تر از توکن‌های سراسری: در فهرستی با ۹۸ ردیف،
       #DDD روی سفید به شبکه‌ای از خط‌های پررنگ تبدیل می‌شود. */
    --hub-rule: #EAEDF0;
    --hub-ink: #16202E;

    /* ── سلسله‌مراتبِ متن: سه پله، نه هفت ──────────────────────────────
       ☠️ تا ۲۰۲۶-۰۸-۲۹ هر بلوک خاکستریِ خودش را داشت — #6B7A89 در کارتِ
       پزشک، #7B8A99 در پابرگِ نظر، #8494A4 در آدرس، #98A6B4 در شمارِ
       امتیاز، #4A5A6A در نامِ مرکز. پنج خاکستری در فاصلهٔ ۲۰ درصدِ روشنایی
       یعنی هیچ‌کدام واقعاً «کم‌رنگ‌ترِ» دیگری به‌نظر نمی‌رسند و کارت به
       یک تودهٔ خاکستریِ یکنواخت تبدیل می‌شود. این سه پله جای همه‌شان را
       می‌گیرند و اختلافشان واقعاً دیده می‌شود.
       نسبتِ کنتراست روی سفید: ink 14.8:1، ink-2 8.1:1، ink-3 4.6:1 —
       هر سه بالای کفِ ۴٫۵ برای متنِ بدنه. */
    --hub-ink-2: #45586E;
    --hub-ink-3: #6B7C90;

    /* خطِ پررنگ‌تر برای مرزهایی که واقعاً مرزند (سرِ بخش، پابرگِ کارت). */
    --hub-rule-strong: #DDE4EB;

    /* ☠️ این توکن تا امروز فقط به‌شکلِ fallbackِ `var(--hub-gap-lg, 40px)`
       وجود داشت، یعنی هرگز تعریف نشده بود و همیشه ۴۰ می‌شد. حالا واقعی
       است و در یک جا عوض می‌شود. */
    --hub-gap-lg: 40px;

    /* آفستِ نوارِ چسبانِ پرش — JS با وضعیتِ هدرِ سایت هماهنگش می‌کند. */
    --hub-sticky-top: 0px;

    min-height: 60vh;
    padding: var(--spacing-2xl) 0 var(--spacing-3xl, 48px);
    background: var(--bg-primary);
}

.hub-page .container {
    max-width: var(--container-xl);
    margin: 0 auto;
    padding: 0 var(--spacing-lg);
}

/* ☠️ صفتِ `hidden` روی المانی که کلاسش `display` دارد بی‌اثر است — و
   فیلترِ زنده دقیقاً با همین صفت کاشی و بخش را پنهان می‌کند. بدونِ این بلوک،
   فیلتر بی‌هیچ خطایی هیچ چیزی را پنهان نمی‌کرد؛ فقط عددِ «۳ مورد از ۹۵»
   عوض می‌شد و همه ۹۵ کاشی سرِ جایشان می‌ماندند. */
.hub-tile-cell[hidden],
.hub-tile[hidden],
.hub-section[hidden],
.hub-noresult[hidden],
.hub-filter__clear[hidden] {
    display: none !important;
}

/* ── سطح‌هایی که ما نکشیدیم ────────────────────────────────────────
   انتخابِ متن، اسکرول‌بارِ ریل‌ها و حلقهٔ فوکوس با رنگِ پیش‌فرضِ مرورگر
   می‌آیند و به هیچ سیستمِ طراحی‌ای تعلق ندارند. همین‌ها ارزان‌ترین نشانهٔ
   این‌اند که صفحه ساخته شده، نه سرِ هم شده. */
.hub-page ::selection {
    background: var(--hub-aura);
    color: var(--hub-ink);
}

.hub-sr {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

/* ── مسیر راهنما ─────────────────────────────────────────────────── */
.hub-breadcrumb ol {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--spacing-xs, 4px);
    margin: 0 0 var(--spacing-lg);
    padding: 0;
    list-style: none;
    font-size: var(--font-sm, .875rem);
    /* ☠️ `--text-muted` (#717171) روی زمینهٔ آبی‌روشنِ صفحه ۴٫۱:۱ می‌دهد —
       زیرِ کفِ ۴٫۵. رنگِ خودِ ورقه ۵٫۲:۱ می‌دهد. */
    color: var(--hub-ink-3);
}

/* جداکننده با ::after ساخته می‌شود نه با کاراکتر در HTML — در RTL جهتش
   خودکار درست است و برای screen reader هم خوانده نمی‌شود. */
.hub-breadcrumb li:not(:last-child)::after {
    content: '‹';
    margin: 0 var(--spacing-xs, 4px);
    color: var(--border-light);
}

.hub-breadcrumb a {
    color: var(--hub-ink-3);
    text-decoration: none;
    text-underline-offset: 3px;
}

.hub-breadcrumb a:hover {
    color: var(--primary-color);
    text-decoration: underline;
}

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

   ── چیدمانِ دو ستونیِ سربرگ ─────────────────────────────────────
   ☠️ تیتر `max-width: 58ch` دارد چون خطِ بلندتر خوانده نمی‌شود. تا امروز
   نتیجه‌اش این بود که روی نمایشگرِ ۱۴۴۰، نیمهٔ چپِ سربرگ کاملاً خالی
   می‌ماند و جعبه جست‌وجو — اصلی‌ترین ابزارِ صفحه — یک بلوکِ جدا زیرِ آن
   بود. حالا همان نیمهٔ خالی ابزار را می‌گیرد: پرسش راست، پاسخ‌دهنده چپ.

   ☠️ فقط صفحه‌هایی این را می‌گیرند که فیلترشان به سربرگ چسبیده
   (`.hub-page--tool`). در `/best-doctors-{تخصص}` و `/best-doctors/{شهر}`
   فیلتر مالِ شبکهٔ **پایینِ** صفحه است و بالا آوردنش یعنی کاربر فکر کند
   دارد در فهرستِ پزشکان جست‌وجو می‌کند. */
.hub-hero {
    margin-bottom: var(--spacing-2xl);
}

.hub-head {
    max-width: 58ch;
    margin: 0;
}

@media (min-width: 1024px) {
    .hub-page--tool .hub-hero__grid {
        display: grid;
        grid-template-columns: minmax(0, 1fr) minmax(280px, 400px);
        /* ☠️ `end` نه `center`: جعبه جست‌وجو باید با **خطِ آخرِ** زیرعنوان
           هم‌تراز بنشیند. با center، هر صفحه‌ای که زیرعنوانش یک خط بلندتر
           بود جعبه را جای دیگری می‌انداخت و پنج صفحه پنج چیدمان می‌شدند. */
        align-items: end;
        column-gap: var(--spacing-3xl, 48px);
    }
}

/* ☠️ تیتر با clamp مقیاس می‌گیرد نه با media query: این صفحه بینِ ۳۲۰px و
   نمایشگرِ عریض هیچ نقطه شکستِ طبیعی ندارد و سه‌تا breakpoint فقط سه‌تا
   پرشِ ناگهانی می‌ساخت.

   ☠️ سقف از ۲٫۲۵rem (۳۶px) به ۱٫۸۷۵rem (۳۰px) آمد (تصمیمِ کاربر ۲۰۲۶-۰۸-۳۰).
   عدد از نسبت آمد نه از سلیقه: عنوانِ بخش‌ها ۲۰px است، پس ۳۶px یعنی نسبتِ
   ۱٫۸ برابر — پله‌ای که در صفحه‌ای با نوزده سرِ بخش، تیتر را از بقیهٔ صفحه
   جدا می‌کرد نه اینکه صدرش کند. ۳۰px نسبتِ ۱٫۵ می‌دهد: هنوز آشکارا بزرگ‌ترین
   متنِ صفحه، ولی از همان خانواده.

   ☠️ کفِ موبایل هم متناسب پایین آمد (۲۵٫۶px → ۲۳٫۲px). اگر فقط سقف را کم کنی،
   روی عرض‌های میانی تیتر عملاً ثابت می‌ماند و پله‌ای که clamp قرار بود نرم
   کند دوباره برمی‌گردد. */
.hub-title {
    margin: 0 0 var(--spacing-sm, 8px);
    color: var(--hub-ink);
    font-size: clamp(1.45rem, 1.05rem + 1.7vw, 1.875rem);
    font-weight: var(--font-extrabold, 800);
    line-height: 1.35;
    letter-spacing: -0.01em;
}

/* ☠️ «از نگاه کاربران» یک درجه ریزتر و کم‌رنگ‌تر است، نه یک برچسبِ جدا: ادامهٔ
   همان جمله است و باید همان‌طور خوانده شود. `white-space: nowrap` تا در شکستنِ
   عنوان، «از نگاه» و «کاربران» دو خط نشوند.

   ☠️ نه یک قرص و نه یک رنگِ تازه: تنها رنگِ این صفحه‌ها بشقابِ نعنایی است
   (سرصفحهٔ همین فایل). این متن فقط وزن و اندازه‌اش فرق می‌کند. */
.hub-title__note {
    display: inline-block;
    /* ☠️ نسبت است نه اندازهٔ ثابت، پس با کوچک‌شدنِ تیتر خودش کوچک می‌شود.
       ۰٫۵۵ انتخاب شد تا روی سقفِ ۳۰ پیکسلیِ تیتر به ~۱۶٫۵px برسد — یعنی
       هم‌اندازهٔ زیرعنوانِ زیرش، نه بزرگ‌تر از آن. */
    font-size: .55em;
    font-weight: var(--font-semibold, 600);
    line-height: 1.4;
    letter-spacing: 0;
    color: var(--hub-ink-3);
    white-space: nowrap;
    vertical-align: middle;
}

.hub-doctors__title .hub-title__note,
.hub-section__title .hub-title__note {
    font-size: .72em;
}

.hub-subtitle {
    margin: 0;
    color: var(--hub-ink-2);
    font-size: var(--font-base, 1rem);
    line-height: var(--line-height-relaxed, 1.75);
    /* متنِ توضیحی هرگز از ۶۵ نویسه پهن‌تر نشود، حتی وقتی تیتر کوتاه است. */
    max-width: 62ch;
}

/* ── ردِ گام‌ها ─────────────────────────────────────────────────────
   ☠️ جایگزینِ برچسبِ «گام ۲ از ۲». آن نسخه فقط موقعیت را می‌گفت؛ این یکی
   **انتخابِ انجام‌شده** را هم نشان می‌دهد و گامِ تمام‌شده لینک است، پس کاربر
   بدونِ دکمهٔ back مرورگر می‌تواند انتخابش را عوض کند.

   ☠️ خطِ رابط با `::after` روی آیتم کشیده می‌شود نه با یک المانِ جدا، تا
   مارک‌آپ یک `<ol>` تمیز بماند و برای screen reader فقط دو آیتم باشد. */
.hub-steps {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--spacing-sm, 8px);
    margin: 0 0 var(--spacing-lg);
    padding: 0;
    list-style: none;
}

.hub-steps__item {
    display: flex;
    align-items: center;
    gap: var(--spacing-sm, 8px);
    min-width: 0;
}

/* خطِ رابط بینِ دو گام — روی آخری نمی‌آید */
.hub-steps__item:not(:last-child)::after {
    content: '';
    width: 28px;
    height: 1px;
    background: var(--hub-rule);
    flex-shrink: 0;
}

.hub-steps__body {
    display: flex;
    align-items: center;
    gap: var(--spacing-sm, 8px);
    min-width: 0;
    color: var(--hub-ink-2);
    text-decoration: none;
    transition: color var(--transition-base);
}

a.hub-steps__body:hover {
    color: var(--primary-color);
    text-decoration: none;
}

a.hub-steps__body:focus-visible {
    outline: 2px solid var(--primary-color);
    outline-offset: 3px;
    border-radius: var(--radius-sm);
}

/* نشانِ گام: دایرهٔ کوچک با شماره یا تیک */
.hub-steps__mark {
    display: grid;
    place-items: center;
    flex-shrink: 0;
    /* ☠️ ۲۶ نه ۲۲: در ۲۲ پیکسل، رقمِ فارسی در وزنِ bold به لبهٔ دایره
       می‌چسبید و در حالتِ «جاری» زیرِ حلقهٔ ۲ پیکسلی گم می‌شد. */
    width: 26px;
    height: 26px;
    border-radius: var(--radius-full, 9999px);
    background: #EDF1F5;
    color: var(--hub-ink-2);
    font-size: var(--font-xs, .75rem);
    font-weight: var(--font-bold);
    /* ☠️ `line-height: 1` لازم است: ارتفاعِ خطِ ارثیِ ۱٫۵ عدد را در کادرِ
       ۲۲ پیکسلی پایین می‌بَرد و در حالتِ «جاری» لبه‌اش زیرِ حلقهٔ ۲ پیکسلی
       می‌رود. */
    line-height: 1;
    font-variant-numeric: tabular-nums;
}

.hub-steps__mark svg {
    width: 12px;
    height: 12px;
}

.hub-steps__text {
    display: flex;
    align-items: baseline;
    gap: 6px;
}

.hub-steps__label {
    font-size: var(--font-sm, .875rem);
    white-space: nowrap;
    color: var(--hub-ink-2);
}

/* ☠️ مقدارِ انتخاب‌شده پررنگ‌تر از برچسب است چون **همان** چیزی است که کاربر
   دنبالش می‌گردد؛ برچسب فقط می‌گوید این عدد/نام چیست.

   ☠️ این متن **بریده نمی‌شود**. نسخهٔ قبلی `text-overflow: ellipsis` داشت با
   `max-width: 12ch` روی موبایل، و همان متن‌های کوتاه را هم سه‌نقطه می‌کرد:
   واحدِ `ch` عرضِ کاراکترِ «۰» است و حروفِ فارسی از آن پهن‌ترند، پس ۱۲ch حتی
   به ۱۲ حرفِ فارسی نمی‌رسید («دندانپزشک» می‌شد «دندانپز…»).

   این‌جا بریدنِ متن اصلاً کارِ درستی نبود: تمامِ فایدهٔ این نشانگر همین است
   که بگوید کاربر **چه** انتخاب کرده. اگر جا کم بیاید، `<ol>` خودش
   `flex-wrap` دارد و گام به سطرِ بعد می‌رود. */
.hub-steps__value {
    color: var(--hub-ink);
    font-size: var(--font-sm, .875rem);
    font-weight: var(--font-bold);
}

/* گامِ تمام‌شده: تیکِ سفید روی زمینهٔ برند */
.hub-steps__item--done .hub-steps__mark {
    background: var(--primary-color);
    color: var(--text-white);
}

/* گامِ جاری: حلقهٔ برند، توپُر نیست — هنوز تمام نشده */
.hub-steps__item--current .hub-steps__mark {
    background: var(--bg-component);
    color: var(--primary-color);
    box-shadow: inset 0 0 0 2px var(--primary-color);
}

.hub-steps__item--current .hub-steps__body {
    color: var(--hub-ink);
}

.hub-steps__item--current .hub-steps__label {
    font-weight: var(--font-bold);
}

@media (max-width: 480px) {
    .hub-steps__item:not(:last-child)::after {
        width: 16px;
    }

}

/* ── فیلترِ زنده ──────────────────────────────────────────────────
   ☠️ چسبان (sticky) نیست و عمداً: هدرِ سایت `position: fixed` است و با
   اسکرول پنهان/ظاهر می‌شود؛ هر نوارِ چسبانی زیرِ آن گیر می‌افتد. */
.hub-filter {
    margin: 0 0 var(--spacing-2xl);
}

/* داخلِ ستونِ دومِ سربرگ، فاصلهٔ پایین را خودِ سربرگ می‌دهد. */
.hub-hero__tool .hub-filter {
    margin-bottom: 0;
}

@media (max-width: 1023px) {
    /* روی موبایل ابزار زیرِ متن می‌آید و به فاصله نیاز دارد. */
    .hub-hero__tool {
        margin-top: var(--spacing-xl);
    }
}

.hub-filter__field {
    display: flex;
    align-items: center;
    gap: var(--spacing-md, 12px);
    max-width: 520px;
    padding: 0 var(--spacing-lg);
    border: 1px solid var(--hub-rule-strong);
    border-radius: var(--radius-full, 9999px);
    background: var(--bg-component);
    /* ☠️ سایه آفست و بلور دارد، نه هالهٔ صفرآفست: جعبه باید یک لایه بالای
       زمینه باشد، نه یک لکهٔ رنگیِ دورِ خودش. */
    box-shadow: 0 1px 2px rgba(22, 32, 46, .05), 0 6px 16px -12px rgba(22, 32, 46, .35);
    cursor: text;
    transition: border-color var(--transition-base), box-shadow var(--transition-base);
}

/* در ستونِ دومِ سربرگ، جعبه کلِ ستون را می‌گیرد. */
.hub-hero__tool .hub-filter__field {
    max-width: none;
}

.hub-filter__field:hover {
    border-color: #C9D4DE;
}

.hub-filter__field:focus-within {
    border-color: var(--primary-color);
    box-shadow: 0 0 0 3px rgba(37, 60, 137, .14);
}

.hub-filter__icon {
    display: flex;
    flex-shrink: 0;
    color: var(--text-muted);
}

.hub-filter__icon svg {
    width: 18px;
    height: 18px;
}

.hub-filter__input {
    flex: 1;
    min-width: 0;
    height: 50px;
    border: 0;
    background: transparent;
    color: var(--hub-ink);
    font-family: inherit;
    font-size: var(--font-base, 1rem);
    outline: none;
}

/* ☠️ `--text-disabled` (#B0B0B0) روی سفید ۲٫۴:۱ می‌دهد — نصفِ کفِ لازم
   برای متنِ placeholder. این یکی ۴٫۶:۱ است. */
.hub-filter__input::placeholder {
    color: var(--hub-ink-3);
    opacity: 1;
}

/* مکان‌نما هم بخشی از طراحی است، نه پیش‌فرضِ مرورگر. */
.hub-filter__input {
    caret-color: var(--primary-color);
}

/* ☠️ دکمه پاک‌کردنِ خودِ مرورگر روی input[type=search] در RTL سمتِ اشتباه
   می‌نشیند و با دکمه ما دو تا ضربدر کنارِ هم می‌شود. */
.hub-filter__input::-webkit-search-cancel-button {
    -webkit-appearance: none;
    appearance: none;
}

.hub-filter__clear {
    display: flex;
    flex-shrink: 0;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    padding: 0;
    border: 0;
    border-radius: var(--radius-full, 9999px);
    background: var(--bg-hover);
    color: var(--text-muted);
    cursor: pointer;
    transition: background var(--transition-fast), color var(--transition-fast);
}

.hub-filter__clear:hover {
    background: var(--primary-light);
    color: var(--primary-color);
}

.hub-filter__clear svg {
    width: 14px;
    height: 14px;
}

.hub-filter__status {
    margin: var(--spacing-sm, 8px) 0 0;
    padding-right: var(--spacing-lg);
    min-height: 1.25em;
    color: var(--hub-ink-3);
    font-size: var(--font-sm, .875rem);
    font-variant-numeric: tabular-nums;
}

.hub-noresult {
    margin: 0;
    padding: var(--spacing-3xl, 48px) var(--spacing-lg);
    border: 1px dashed var(--hub-rule-strong);
    border-radius: var(--radius-xl, 16px);
    background: var(--bg-component);
    color: var(--hub-ink-2);
    text-align: center;
    line-height: var(--line-height-relaxed, 1.75);
}

/* ═══ نوارِ پرشِ بخش‌ها ═══════════════════════════════════════════════════
   ☠️ چرا این نوار هست: /best-doctors نوزده بخش و ۱۷۵ کاشی دارد و روی موبایل
   ۳۳٬۶۲۴ پیکسل بلند است — چهل صفحهٔ کامل. بدونِ راهِ پرش، «انتخاب بر اساس
   شهر» که آخرین بخش است عملاً دیده نمی‌شود.

   ☠️ چسبندگی با `top: var(--hub-sticky-top)` است نه `top: 0`، و آن متغیر را
   JS می‌نویسد. دلیلش در کامنتِ همان اسکریپت است: هدرِ سایت `position: fixed`
   است و با اسکرول پنهان/ظاهر می‌شود؛ هر عددِ ثابتی یکی از دو حالت را خراب
   می‌کند. تلاشِ قبلیِ «فیلترِ چسبان» دقیقاً به همین دلیل کنار گذاشته شده بود.

   ☠️ z-index زیرِ هدر (۱۰۲۲) است: وقتی کاربر به بالا اسکرول می‌کند و هدر
   برمی‌گردد، هدر باید برنده باشد — نوار خودش هم‌زمان به زیرش سُر می‌خورد. */
.hub-jump {
    position: sticky;
    top: var(--hub-sticky-top, 0px);
    z-index: 1018;
    display: flex;
    align-items: center;
    gap: var(--spacing-sm, 8px);
    /* ☠️ نوار از کانتینر بیرون می‌زند و دوباره همان اندازه padding می‌گیرد.
       بدونِ این، زمینهٔ نوار فقط تا لبهٔ کانتینر می‌رسید و کاشی‌های در حالِ
       اسکرول از دو نوارِ ۱۶ پیکسلیِ کناره از زیرش رد می‌شدند. */
    margin: 0 calc(var(--spacing-lg) * -1) var(--spacing-xl);
    padding: var(--spacing-sm, 8px) var(--spacing-lg);
    /* ☠️ زمینه لازم است و شفاف نمی‌شود: کاشی‌ها زیرِ نوار رد می‌شوند و متنِ
       چیپ‌ها روی تصویرِ متحرک خوانده نمی‌شد. */
    background: linear-gradient(#F4F6FDF2, #F4F6FDF2), var(--bg-primary);
    box-shadow: 0 1px 0 var(--hub-rule-strong), 0 14px 18px -18px rgba(22, 32, 46, .55);
}

@supports (backdrop-filter: blur(8px)) {
    .hub-jump {
        background: rgba(246, 248, 253, .82);
        backdrop-filter: saturate(1.4) blur(10px);
    }
}

/* ☠️ محوشدنِ لبه‌ها تزئین نیست: با نوزده چیپ، همیشه چیپی وسطِ لبه بریده
   می‌شود و برشِ تیز شبیهِ خرابیِ رندر به‌نظر می‌رسید. محوشدگی هم برش را
   عمدی نشان می‌دهد هم می‌گوید فهرست ادامه دارد.

   ☠️ گرادیان `to left` است چون در RTL «شروعِ» اسکرول سمتِ راست است؛ اولین
   توقفِ گرادیان باید همان لبهٔ راست باشد. مقدارها را JS با وضعیتِ واقعیِ
   اسکرول عوض می‌کند تا وقتی به ابتدای فهرست رسیده‌ایم لبهٔ راست محو نباشد. */
.hub-jump__list {
    --hub-fade-start: 0px;
    --hub-fade-end: 0px;
    -webkit-mask-image: linear-gradient(to left, transparent 0, #000 var(--hub-fade-start),
                                        #000 calc(100% - var(--hub-fade-end)), transparent 100%);
    mask-image: linear-gradient(to left, transparent 0, #000 var(--hub-fade-start),
                                #000 calc(100% - var(--hub-fade-end)), transparent 100%);
    display: flex;
    gap: 6px;
    min-width: 0;
    margin: 0;
    padding: 2px 0;
    list-style: none;
    overflow-x: auto;
    scroll-behavior: smooth;
    /* ☠️ `nowrap` واجب است: با wrap، نوارِ چسبانِ نوزده‌چیپی روی موبایل نصفِ
       ارتفاعِ صفحه را می‌گرفت. */
    flex-wrap: nowrap;
    scroll-snap-type: x proximity;
    scrollbar-width: thin;
    scrollbar-color: var(--hub-aura-edge) transparent;
    -webkit-overflow-scrolling: touch;
}

/* ☠️ ست‌کردنِ `scrollbar-width` باعث می‌شود WebKit از ::-webkit-scrollbar
   صرف‌نظر کند؛ هر دو باید با هم بیایند وگرنه سافاری اسکرول‌بارِ سیستمی را
   نگه می‌دارد. */
.hub-jump__list::-webkit-scrollbar {
    height: 4px;
}

.hub-jump__list::-webkit-scrollbar-thumb {
    background: var(--hub-aura-edge);
    border-radius: var(--radius-full, 9999px);
}

.hub-jump__list::-webkit-scrollbar-track {
    background: transparent;
}

.hub-jump__list li {
    flex: 0 0 auto;
    scroll-snap-align: start;
}

.hub-jump__chip {
    display: block;
    padding: 7px 13px;
    border: 1px solid transparent;
    border-radius: var(--radius-full, 9999px);
    background: rgba(255, 255, 255, .75);
    color: var(--hub-ink-2);
    font-size: var(--font-sm, .875rem);
    font-weight: var(--font-medium, 500);
    white-space: nowrap;
    text-decoration: none;
    transition: color var(--transition-fast), background var(--transition-fast), border-color var(--transition-fast);
}

.hub-jump__chip:hover {
    border-color: var(--hub-aura-edge);
    background: #fff;
    color: var(--hub-ink);
    text-decoration: none;
}

/* بخشی که همین حالا در دید است — تنها حالتِ رنگیِ نوار. */
.hub-jump__chip.is-active {
    border-color: var(--hub-aura-live);
    background: var(--hub-aura-wash);
    color: var(--hub-ink);
    font-weight: var(--font-bold, 700);
}

.hub-jump__chip:focus-visible,
.hub-jump__search:focus-visible {
    outline: 2px solid var(--primary-color);
    outline-offset: 2px;
}

.hub-jump__search {
    display: grid;
    place-items: center;
    flex: 0 0 auto;
    width: 36px;
    height: 36px;
    padding: 0;
    border: 1px solid var(--hub-rule-strong);
    border-radius: var(--radius-full, 9999px);
    background: #fff;
    color: var(--hub-ink-2);
    cursor: pointer;
    transition: border-color var(--transition-fast), color var(--transition-fast);
}

.hub-jump__search:hover {
    border-color: var(--hub-aura-live);
    color: var(--hub-ink);
}

.hub-jump__search svg {
    width: 17px;
    height: 17px;
}

/* لنگرِ پرش نباید زیرِ نوارِ چسبان و هدر بیفتد. */
.hub-section[id] {
    scroll-margin-top: calc(var(--hub-sticky-top, 0px) + 68px);
}

/* ── سرِ هر بخش ───────────────────────────────────────────────────
   ☠️ خطِ کشیده بینِ عنوان و عدد تزئین نیست، ساختار است: عنوان را به شمارِ
   خودش وصل می‌کند و مرزِ گروه را در فهرستی که ده بخش پشتِ سرِ هم دارد
   قابلِ دیدن نگه می‌دارد. */
.hub-section {
    margin-top: var(--spacing-3xl, 48px);
}

.hub-section:first-of-type {
    margin-top: 0;
}

.hub-section__head {
    display: flex;
    align-items: center;
    gap: var(--spacing-md, 12px);
    margin-bottom: var(--spacing-lg);
}

@media (max-width: 480px) {
    /* در ۳۹۰ پیکسل، عنوان + خط + قرص در یک سطر جا نمی‌شوند و خط به یک
       رگهٔ ۱۶ پیکسلیِ بی‌معنی تبدیل می‌شود. خط می‌رود، فاصله می‌ماند —
       ولی قرص باید همان‌جایی بماند که خط می‌گذاشتش: انتهای سطر. */
    .hub-section__rule {
        display: none;
    }

    .hub-section__count {
        margin-inline-start: auto;
    }

    .hub-section__head {
        gap: var(--spacing-sm, 8px);
    }
}

/* ☠️ عنوان روی موبایل هم `nowrap` بود و همان‌جا از کارت بیرون می‌زد
   («زیبایی: اقدامات غیرجراحی» در ۳۹۰ پیکسل). حالا می‌شکند و خط فقط وقتی
   کشیده می‌شود که جا باشد. */
.hub-section__title {
    margin: 0;
    color: var(--hub-ink);
    font-size: clamp(1.0625rem, .98rem + .4vw, 1.25rem);
    font-weight: var(--font-extrabold, 800);
    line-height: 1.45;
    letter-spacing: -0.01em;
}

.hub-section__rule {
    flex: 1;
    min-width: var(--spacing-lg);
    height: 1px;
    background: var(--hub-rule-strong);
}

/* ☠️ شمار یک قرصِ کوچک است نه متنِ لخت: در فهرستی که ده سرِ بخش پشتِ سرِ
   هم دارد، عددِ بی‌کادر با عنوانِ بخشِ بعدی قاطی می‌شد. */
.hub-section__count {
    flex-shrink: 0;
    padding: 3px 9px;
    border-radius: var(--radius-full, 9999px);
    background: rgba(255, 255, 255, .8);
    box-shadow: inset 0 0 0 1px var(--hub-rule-strong);
    color: var(--hub-ink-3);
    font-size: var(--font-xs, .75rem);
    font-weight: var(--font-semibold, 600);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* ── شبکه کارت‌ها ─────────────────────────────────────────────────
   یک چیدمان برای **همه** صفحه‌های انتخاب: /consult، /clinic، /psychology،
   /diseases، گامِ شهر و لندینگِ زیبایی.

   ☠️ از ۲۰۲۶-۰۸-۲۹ همهٔ این شبکه‌ها داخلِ `.hub-rail` رندر می‌شوند و آن‌جا
   به ریلِ افقی تبدیل می‌شوند (بلوکِ «ریلِ افقی» پایین‌تر). قواعدِ ستونیِ
   این بلوک عمداً ماندند و حذفشان نکن: هر شبکه‌ای که روزی بیرونِ ریل رندر
   شود — و نتیجهٔ سرچِ سرورسایدِ شهر که فقط `<li>` برمی‌گرداند — باید یک
   چیدمانِ سالم داشته باشد نه یک ردیفِ بی‌انتها.

   ☠️ نسخه قبلی دو چیدمان داشت — کارت برای بعضی صفحه‌ها و فهرستِ افقیِ
   خط‌دار (`hub-grid--rows`) برای صفحه‌های تخصص. آن دومی حذف شد (تصمیمِ کاربر
   ۲۰۲۶-۰۸-۲۳): دو زبانِ بصری برای یک کار یعنی هر تغییرِ آینده باید دو بار زده
   شود، و در عمل صفحه‌ها شبیهِ هم به‌نظر نمی‌رسیدند. اگر دوباره وسوسه شدی
   فهرستِ ردیفی برگردانی، اول این را بخوان.

   ☠️ تعدادِ ستون‌ها صریح است نه `auto-fill`: خواسته محصولی «۲ در موبایل تا
   ۵ در دسکتاپ» است و auto-fill روی نمایشگرِ عریض ۷-۸ ستونِ باریک می‌داد که
   توضیحِ زیرِ نام در آن اصلاً جا نمی‌شد. */
/* ☠️ `minmax(0, 1fr)` نه `1fr` — و این تفاوت واقعاً مهم است.
   `1fr` سرِ کوتاه‌شده‌ی `minmax(auto, 1fr)` است و آن `auto` یعنی «ستون هرگز از
   min-content آیتمش باریک‌تر نشو». هر متنِ `white-space: nowrap` داخلِ کارت،
   min-content را به اندازهٔ کلِ همان رشته بزرگ می‌کند؛ ستون‌ها باد می‌کنند،
   گرید از کانتینر می‌زند بیرون و کلِ صفحه اسکرولِ افقی می‌گیرد.
   `minmax(0, 1fr)` کفِ ستون را صفر می‌کند تا `text-overflow: ellipsis` کارش را
   بکند. */
.hub-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--spacing-md, 12px);
    /* شبکه یک `<ul>` است؛ بولت و تورفتگیِ پیش‌فرضِ فهرست باید برود.
       ☠️ `list-style: none` روی ul در سافاری، لیست را از درختِ دسترس‌پذیری
       بیرون می‌اندازد و همان «فهرست، N مورد» که هدفِ این کار بود از بین
       می‌رود؛ `role="list"` روی .hub-grid آن را برمی‌گرداند. */
    list-style: none;
    margin: 0;
    padding: 0;
}

@media (min-width: 561px) {
    .hub-grid {
        grid-template-columns: repeat(3, minmax(0, 1fr));
        gap: var(--spacing-lg);
    }
}

@media (min-width: 768px) {
    .hub-grid {
        grid-template-columns: repeat(4, minmax(0, 1fr));
    }
}

@media (min-width: 1024px) {
    .hub-grid {
        grid-template-columns: repeat(5, minmax(0, 1fr));
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   ریلِ افقی — یک کامپوننت برای همهٔ فهرست‌های صفحه
   ═══════════════════════════════════════════════════════════════════════
   ☠️ چرا ریل و چرا روی دسکتاپ هم: /best-doctors نوزده بخش و ۱۷۵ کاشی دارد.
   با شبکهٔ ۵ ستونی، خودِ همان صفحه روی دسکتاپ ۱۸٬۰۳۸ پیکسل و روی موبایل
   ۲۴٬۰۹۰ پیکسل بود (اندازه‌گیریِ ۲۰۲۶-۰۸-۲۹). یعنی بخشِ «انتخاب بر اساس شهر»
   عملاً وجود نداشت. ریل هر بخش را به یک نوارِ هم‌ارتفاع تبدیل می‌کند، پس
   سرِ بخش‌ها پشتِ سرِ هم دیده می‌شوند و صفحه یک‌بار قابلِ اسکن است.

   ☠️ همان زبانِ اسلایدرهای هوم‌پیج است و باید بماند: کاربر همین حرکت را در
   صفحهٔ اول یاد گرفته. تفاوت فقط در جزئیاتِ کرافت است (محوِ لبه‌های
   وضعیت‌آگاه، فلشِ خودپنهان‌شونده، درگ با گاردِ کلیک).

   ☠️ لینک‌ها همه در DOM می‌مانند — ریل چیزی را lazy نمی‌کند. برای خزندهٔ
   گوگل این صفحه دقیقاً همان تعداد لینک را دارد که قبل داشت. */
.hub-rail {
    position: relative;
}

/* ☠️ `overflow-x: auto` مقدارِ محاسبه‌شدهٔ `overflow-y` را از `visible` به
   `auto` می‌بَرد — یعنی بالا‌آمدنِ کارت روی hover و سایه‌اش بی‌صدا بریده
   می‌شوند. padding عمودی جای آن حرکت را باز می‌کند و margin منفی همان را
   از چیدمان پس می‌گیرد. بدونِ این جفت، تنها لحظهٔ حرکتِ صفحه از بین می‌رفت. */
.hub-rail [data-rail-track] {
    --hub-fade-start: 0px;
    --hub-fade-end: 0px;
    display: grid;
    grid-auto-flow: column;
    grid-template-rows: auto;
    align-items: stretch;
    overflow-x: auto;
    overflow-y: hidden;
    overscroll-behavior-x: contain;
    /* ☠️ snap عمداً نیست (تصمیمِ کاربر ۲۰۲۶-۰۸-۲۹: رفتار عیناً مثلِ اسلایدرهای
       هوم‌پیج). با snap، درگِ اینرسی‌دار وسطِ راه به نزدیک‌ترین کارت پرت
       می‌شود و حسِ «کشیدن» از بین می‌رود — دقیقاً همان چیزی که در home.css
       هم با کامنتِ «حذف scroll-snap برای روانی درگ» ثبت شده است. */
    cursor: grab;
    margin: -8px 0 0;
    padding: 8px 0 12px;
    /* ☠️ گرادیانِ `to left` است چون در RTL شروعِ اسکرول سمتِ راست است. مقدارها
       را JS با وضعیتِ واقعیِ اسکرول عوض می‌کند تا لبه‌ای که چیزی پشتش نیست
       محو نشود — همان قاعدهٔ نوارِ پرش. */
    -webkit-mask-image: linear-gradient(to left, transparent 0, #000 var(--hub-fade-start),
                                        #000 calc(100% - var(--hub-fade-end)), transparent 100%);
    mask-image: linear-gradient(to left, transparent 0, #000 var(--hub-fade-start),
                                #000 calc(100% - var(--hub-fade-end)), transparent 100%);
    /* ☠️ هر دو خطِ اسکرول‌بار با هم می‌آیند: ست‌کردنِ `scrollbar-width` باعث
       می‌شود WebKit از ::-webkit-scrollbar صرف‌نظر کند و اسکرول‌بارِ سیستمی
       سرِ جایش بماند ([[project_scrollbar_styling]]). */
    /* اسکرول‌بار عیناً همانِ اسلایدرهای هوم‌پیج: ۸ پیکسل، دستهٔ برند روی
       ریلِ خاکستری. کاربر همین را در صفحهٔ اول دیده است. */
    scrollbar-width: thin;
    scrollbar-color: var(--primary-color) var(--gray-200);
    -webkit-overflow-scrolling: touch;
    -webkit-user-select: none;
    user-select: none;
}

.hub-rail [data-rail-track]::-webkit-scrollbar {
    height: 8px;
}

.hub-rail [data-rail-track]::-webkit-scrollbar-thumb {
    background: var(--primary-color);
    border-radius: var(--radius-full, 9999px);
}

.hub-rail [data-rail-track]::-webkit-scrollbar-track {
    background: var(--gray-200);
    border-radius: var(--radius-full, 9999px);
}

/* درگِ تصویر و لینک باید خاموش باشد وگرنه مرورگر به‌جای اسکرول، خودِ عکس را
   می‌کشد — همان قاعده‌ای که home.css برای اسلایدرها دارد. */
.hub-rail [data-rail-track] img,
.hub-rail [data-rail-track] a {
    -webkit-user-drag: none;
    user-drag: none;
}

/* در حالِ درگ فقط شکلِ نشانگر عوض می‌شود؛ جلوگیری از کلیک را JS با همان
   گاردِ `data-dragged` هوم‌پیج انجام می‌دهد نه CSS. */
.hub-rail [data-rail-track].is-dragging {
    cursor: grabbing;
}

/* ── فلش‌ها ────────────────────────────────────────────────────────────
   ☠️ ظاهر و رفتار عیناً از اسلایدرهای هوم‌پیج آمده (تصمیمِ کاربر ۲۰۲۶-۰۸-۲۹):
   دایرهٔ ۴۸ پیکسلی با حاشیهٔ ۲ پیکسل، پرشدن با رنگِ برند روی hover، و در
   دو سرِ نوار **کم‌رنگ‌شدن** به‌جای پنهان‌شدن. نسخهٔ اولِ همین فایل فلشِ
   بی‌کار را `hidden` می‌کرد؛ آن انتخاب فنی درست بود ولی با صفحهٔ اول فرق
   داشت و کاربر همان یکی را یاد گرفته است.

   ☠️ جای فلش‌ها دو حالت دارد و هر دو لازم‌اند: زیرِ ۱۴۰۰ پیکسل روی خودِ لبهٔ
   نوار می‌نشینند و بالای آن بیرونِ کانتینر. کانتینرِ این صفحه ۱۲۸۰ پیکسل
   است؛ بیرون‌بردنِ دکمه در عرض‌های کوچک‌تر یعنی صفحه اسکرولِ افقی بگیرد. */
.hub-rail__nav {
    display: none;
    position: absolute;
    /* ☠️ نصفِ ریل نیست، نصفِ **محتوای** ریل: خودِ نوار ۸ پیکسل margin منفیِ
       بالا و ۱۲ پیکسل padding پایین دارد (جای سایه و اسکرول‌بار)، پس مرکزِ
       هندسی با مرکزِ کارت‌ها شش پیکسل فرق می‌کند. */
    top: calc(50% - 6px);
    z-index: 2;
    place-items: center;
    width: 40px;
    height: 40px;
    margin-top: -20px;
    padding: 0;
    border: 2px solid var(--border-light);
    border-radius: var(--radius-full, 9999px);
    background: var(--bg-component);
    box-shadow: var(--shadow-md);
    cursor: pointer;
    transition: var(--transition-base);
}

.hub-rail__nav svg {
    width: 20px;
    height: 20px;
    fill: var(--text-secondary);
    transition: var(--transition-base);
}

.hub-rail__nav:hover {
    background: var(--primary-color);
    border-color: var(--primary-color);
}

.hub-rail__nav:hover svg {
    fill: #fff;
}

.hub-rail__nav:focus-visible {
    outline: 2px solid var(--primary-color);
    outline-offset: 2px;
}

/* سرِ نوار: دکمه می‌ماند ولی خاموش است — همان `.slider-arrow.disabled`. */
.hub-rail__nav.is-disabled {
    opacity: .3;
    cursor: not-allowed;
    pointer-events: none;
}

.hub-rail__nav--prev { inset-inline-start: 0; }
.hub-rail__nav--next { inset-inline-end: 0; }

/* ☠️ روی دستگاهِ لمسی فلش نه: انگشت خودش ابزارِ اسکرول است و دو دکمهٔ شناور
   فقط روی کارت‌ها می‌افتند. `pointer: fine` کنارِ `hover: hover` لازم است تا
   تبلتی که با قلم کار می‌کند هم درست تشخیص داده شود. */
@media (hover: hover) and (pointer: fine) and (min-width: 768px) {
    .hub-rail__nav {
        display: grid;
    }
}

@media (min-width: 1400px) {
    .hub-rail__nav {
        width: 48px;
        height: 48px;
        margin-top: -24px;
    }

    .hub-rail__nav svg {
        width: 24px;
        height: 24px;
    }

    .hub-rail__nav--prev { inset-inline-start: -56px; }
    .hub-rail__nav--next { inset-inline-end: -56px; }
}

/* ── ریلِ کاشی‌ها ───────────────────────────────────────────────────────
   ☠️ عرضِ ستون ثابت است نه کسری: در یک اسکرولرِ افقی، `1fr` یعنی «به اندازهٔ
   کانتینر» و هر کاشی کلِ عرض را می‌گیرد. عددها طوری‌اند که همیشه تکه‌ای از
   کاشیِ بعدی دیده شود — تنها نشانهٔ صادقانهٔ «فهرست ادامه دارد». */
.hub-rail .hub-grid {
    grid-template-columns: none;
    grid-auto-columns: 158px;
    gap: var(--spacing-md, 12px);
}

/* ☠️ دو ردیف فقط برای بخش‌های پرتعداد و تصمیمش در PHP گرفته می‌شود
   (`hub-grid--rows2`): بخشِ ۴۲ موردیِ «بیماری‌های شایع» در یک ردیف چهل‌ودو
   ستون می‌شد. با دو ردیف، همان بخش نصف طول را می‌گیرد و روی دسکتاپ تقریباً
   همان چیزی را نشان می‌دهد که شبکهٔ قبلی در دو سطرِ اولش نشان می‌داد. */
.hub-rail .hub-grid--rows2 {
    grid-template-rows: repeat(2, auto);
}

@media (min-width: 561px) {
    .hub-rail .hub-grid { grid-auto-columns: 172px; }
}

@media (min-width: 768px) {
    .hub-rail .hub-grid {
        grid-auto-columns: 186px;
        gap: var(--spacing-lg);
    }
}

@media (min-width: 1024px) {
    .hub-rail .hub-grid { grid-auto-columns: 200px; }
}

/* کاشیِ شهر پهن‌تر است چون بشقابش ۱۶:۹ و ۱۲۴ پیکسل عریض است. */
.hub-rail .hub-grid--wide-icon { grid-auto-columns: 168px; }

@media (min-width: 768px) {
    .hub-rail .hub-grid--wide-icon { grid-auto-columns: 196px; }
}

/* خانهٔ کاشی — آیتمِ گرید همین است، نه خودِ لینک. فلکس تا لینک تمامِ
   ارتفاعِ سطر را پر کند (سطرهای گرید پیش‌فرض stretch‌اند). */
.hub-tile-cell {
    display: flex;
}

/* ☠️ ارتفاع دستی تنظیم نمی‌شود: آیتم‌های یک سطرِ grid خودشان هم‌قد می‌شوند
   (`align-items: stretch` پیش‌فرض). `min-height` فقط کفِ سطرهایی است که همه
   کارت‌هایشان کوتاه‌اند — مثل /diseases که فقط تصویر و نام دارد. */
.hub-tile {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--spacing-md, 12px);
    /* ☠️ `min-height` برداشته شد و این تصمیمِ آگاهانه است. سطرهای گرید
       خودشان هم‌قد می‌شوند، پس کفِ دستی هیچ‌وقت لازم نبود — ولی روی
       /diseases و /aesthetic که کاشی‌ها توضیح ندارند، همین کفِ ۱۹۶ پیکسلی
       بینِ نام و عدد یک حفرهٔ ۶۰ پیکسلی باز می‌کرد (`margin-top: auto`
       عدد را به کف می‌چسباند). کارت پر به‌نظر نمی‌رسید، خالی به‌نظر
       می‌رسید. */
    padding: var(--spacing-xl) var(--spacing-lg);
    border: 1px solid var(--hub-rule);
    border-radius: var(--radius-xl, 16px);
    background: var(--bg-component);
    color: var(--hub-ink);
    text-align: center;
    text-decoration: none;
    transition: border-color var(--transition-base), box-shadow var(--transition-base), transform var(--transition-base);
}

/* ☠️ لحظهٔ حرکتِ صفحه همین است و فقط همین: کارت بالا می‌آید، حلقهٔ بشقاب
   پررنگ می‌شود، سایه از هیچ به یک لایهٔ واقعی می‌رسد. سه اثرِ هم‌زمان روی
   یک ژست، نه سه ژستِ پراکنده در صفحه. */
.hub-tile:hover,
.hub-tile:focus-visible {
    border-color: var(--hub-aura-live);
    box-shadow: 0 2px 4px rgba(22, 32, 46, .04), 0 14px 26px -18px rgba(20, 86, 76, .55);
    transform: translateY(-3px);
    color: var(--hub-ink);
    text-decoration: none;
}

.hub-tile:focus-visible {
    outline: 2px solid var(--primary-color);
    outline-offset: 2px;
}

/* ── بشقابِ هاله ──────────────────────────────────────────────────
   ☠️ تصویرها پس‌زمینه **سفیدِ مات** دارند، نه شفاف — و کلِ بشقاب را پر
   می‌کنند. پس هاله نمی‌تواند «زیرِ» تصویر باشد؛ باید **بیرونش** باشد:
   یک حلقه موییِ نعنایی به‌علاوه سایه‌ای با تهِ‌رنگِ همان نعنایی. تصویر هم
   دایره‌ای بریده می‌شود تا گوشه‌های سفیدِ فایل کلاً از بین بروند و آنچه
   می‌ماند خودِ سوژه باشد با هاله داخلیِ خودش.

   ☠️ حلقه `inset` نباشد: سایه inset زیرِ محتوا کشیده می‌شود و تصویری که
   کلِ بشقاب را پر کرده رویش می‌افتد — حلقه بی‌صدا نامرئی می‌شود. */
.hub-tile__plate {
    position: relative;
    display: grid;
    place-items: center;
    flex-shrink: 0;
    width: 96px;
    height: 96px;
    border-radius: var(--radius-full, 9999px);
    background: radial-gradient(circle at 50% 45%, #fff 40%, var(--hub-aura) 100%);
    box-shadow: 0 0 0 1px var(--hub-aura-edge), 0 8px 18px -12px rgba(20, 86, 76, .45);
    overflow: hidden;
    transition: box-shadow var(--transition-base);
}

.hub-tile:hover .hub-tile__plate {
    box-shadow: 0 0 0 1px var(--hub-aura-live), 0 10px 22px -12px rgba(20, 86, 76, .55);
}

.hub-tile__icon {
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: inherit;
}

/* ☠️ تصویر روی hover بزرگ نمی‌شود و این عمدی است: این‌ها فایلِ رستریِ
   ۲۰۰×۲۰۰ هستند و هر مقیاسی بالای ۱ نرم‌شان می‌کند. پاسخِ hover همان
   پررنگ‌شدنِ حلقه است — یک اثر، نه سه تا. */

/* آیتمِ بدونِ تصویر (شهرهای بی‌نماد و آیتم‌های seo): بشقاب می‌ماند، فقط
   محتوایش یک گلیفِ خنثی می‌شود تا ردیف ناقص به‌نظر نرسد. */
.hub-tile__plate--empty {
    background: var(--hub-aura-wash);
}

.hub-tile__glyph {
    width: 26px;
    height: 26px;
    color: var(--hub-aura-live);
}

.hub-tile__text {
    display: flex;
    flex-direction: column;
    gap: var(--spacing-xs, 4px);
    width: 100%;
    min-width: 0;
}

/* ☠️ نامِ تخصص clamp نمی‌شود. بلندترینش «فلوشیپ اتولوژی نورواتولوژی و جراحی
   قاعده جمجمه» است (۴۶ کاراکتر، سه خط) و بریدنِ نامِ یک تخصص یعنی کاربر
   نمی‌فهمد روی چه چیزی کلیک می‌کند. کارت‌های یک سطر خودشان هم‌قد می‌شوند،
   پس سه‌خطی‌شدنِ یک نام شبکه را به هم نمی‌ریزد. */
.hub-tile__name {
    font-size: var(--font-base, 1rem);
    font-weight: var(--font-bold);
    line-height: 1.6;
}

/* توضیحِ تخصص («برای چه مواردی به این تخصص مراجعه کنم»).
   ☠️ سقف **سه** خط است نه دو: بلندترین توضیحِ امروز ۵۸ کاراکتر است و در
   ستونِ ~۲۳۰ پیکسلی سه خط می‌شود. با سقفِ دو خط، نیمی از توضیح‌ها درست وسط
   جمله بریده می‌شدند. clamp خودش لازم است چون متن‌ها در دیتابیس‌اند و ادمین
   می‌تواند بلندترشان کند. */
.hub-tile__desc {
    color: var(--hub-ink-3);
    font-size: var(--font-sm, .875rem);
    font-weight: var(--font-normal, 400);
    line-height: 1.7;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* عدد همیشه به کفِ کارت می‌چسبد (`margin-top: auto`)، پس در یک سطر همه
   عددها هم‌تراز می‌مانند حتی اگر نام‌ها یک تا سه خط باشند. */
.hub-tile__count {
    display: flex;
    align-items: baseline;
    justify-content: center;
    gap: 5px;
    margin-top: auto;
    padding-top: var(--spacing-sm, 8px);
    color: var(--hub-ink-3);
    font-size: var(--font-xs, .75rem);
}

.hub-tile__count-num {
    color: var(--hub-ink);
    font-size: var(--font-sm, .875rem);
    font-weight: var(--font-bold);
    font-variant-numeric: tabular-nums;
}

/* ── حالت خالی و میان‌بر ──────────────────────────────────────────── */
.hub-empty {
    padding: var(--spacing-2xl);
    border: 1px dashed var(--border-light);
    border-radius: var(--radius-lg);
    background: var(--bg-component);
    color: var(--text-muted);
    text-align: center;
}

.hub-skip {
    margin-top: var(--spacing-2xl);
    text-align: center;
}

.hub-skip a {
    color: var(--primary-color);
    font-weight: var(--font-semibold);
    text-decoration: none;
}

.hub-skip a:hover {
    text-decoration: underline;
}

/* ── کاشیِ شهر: بشقابِ عریض (۱۶:۹) ─────────────────────────────────
   ☠️ تصویرهای شهر ۲۴۰×۱۳۵ هستند. در بشقابِ دایره‌ای، `object-fit: cover`
   دو طرفِ نقاشی را می‌بُرد و نمادِ شهر (پل، برج، گنبد) از بین می‌رود.
   این حالت بشقاب را هم‌نسبتِ خودِ تصویر و مستطیل می‌کند. */
.hub-grid--wide-icon .hub-tile__plate {
    width: 124px;
    height: 70px;
    border-radius: var(--radius-md);
    background: radial-gradient(ellipse at 50% 50%, #fff 40%, var(--hub-aura) 100%);
}

/* ── موبایل ───────────────────────────────────────────────────────
   دو ستونِ باریک: بشقاب کوچک‌تر می‌شود ولی توضیح می‌ماند — همان توضیح است
   که به کاربر می‌گوید این تخصص به دردش می‌خورد یا نه. */
@media (max-width: 560px) {
    /* ☠️ اعداد این بلوک از اندازه‌گیری آمده‌اند نه از سلیقه: هر کاشی روی
       موبایل ۲۳۵ تا ۲۵۷ پیکسل بلند بود و /best-doctors با ۱۷۵ کاشی به
       ۳۳٬۶۲۴ پیکسل می‌رسید — چهل صفحهٔ کامل. بشقابِ کوچک‌تر، فاصلهٔ کمتر و
       ارتفاعِ خطِ فشرده‌تر حدودِ یک‌سومِ آن را برمی‌دارد بدونِ اینکه چیزی
       از محتوا کم شود. */
    .hub-tile {
        gap: var(--spacing-sm, 8px);
        padding: var(--spacing-lg) var(--spacing-md, 12px);
    }

    .hub-tile__plate {
        width: 62px;
        height: 62px;
    }

    .hub-tile__name {
        line-height: 1.5;
    }

    .hub-tile__desc {
        line-height: 1.6;
    }

    .hub-tile__name {
        font-size: var(--font-sm, .875rem);
    }

    .hub-tile__desc {
        font-size: var(--font-xs, .75rem);
    }

    .hub-grid--wide-icon .hub-tile__plate {
        width: 108px;
        height: 61px;
    }

    .hub-filter__input {
        height: 46px;
        font-size: var(--font-sm, .875rem);
    }
}

/* ── ترجیحِ حرکتِ کم ──────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
    .hub-tile,
    .hub-tile__plate {
        transition: none;
    }

    .hub-tile:hover {
        transform: none;
    }
}

/* ═══════════════════════════════════════════════════════════════════════════
   فهرستِ پزشک و لینک‌سازیِ متقابل روی هاب — افزودهٔ ۲۰۲۶-۰۸-۲۵
   ═══════════════════════════════════════════════════════════════════════════
   ☠️ کلاس‌ها عمداً `hub-doc*` نام گرفتند نه `top-doctor-card`: آن کلاس‌ها در
   home.css هستند و home.css روی صفحه‌های discovery **لود نمی‌شود** (شرطش در
   layouts/main.php فقط site/index است). قرض‌گرفتنِ آن نام‌ها یعنی کارتِ بی‌استایل.
   ═══════════════════════════════════════════════════════════════════════════ */

.hub-doctors {
    margin-top: var(--hub-gap-lg);
    padding-top: var(--hub-gap-lg);
    border-top: 1px solid var(--hub-rule-strong);
}

/* سرِ بخش همان زبانِ `.hub-section__head` را می‌گیرد — عنوان، خط، قرصِ شمار.
   ☠️ تا امروز فهرستِ پزشک فقط یک `h2` لخت داشت و کنارِ سرِ بخش‌های کاشی روی
   همان صفحه، دو گرامرِ متفاوت برای یک کار بودند. */
.hub-doctors__head {
    display: flex;
    align-items: center;
    gap: var(--spacing-md, 12px);
    margin-bottom: var(--spacing-md, 12px);
}

.hub-doctors__title {
    margin: 0;
    font-size: clamp(1.0625rem, .98rem + .4vw, 1.25rem);
    font-weight: var(--font-extrabold, 800);
    line-height: 1.45;
    letter-spacing: -0.01em;
    color: var(--hub-ink);
}

.hub-doctors__rule {
    flex: 1;
    min-width: var(--spacing-lg);
    height: 1px;
    background: var(--hub-rule-strong);
}

.hub-doctors__count {
    flex-shrink: 0;
    padding: 3px 9px;
    border-radius: var(--radius-full, 9999px);
    background: rgba(255, 255, 255, .8);
    box-shadow: inset 0 0 0 1px var(--hub-rule-strong);
    color: var(--hub-ink-3);
    font-size: var(--font-xs, .75rem);
    font-weight: var(--font-semibold, 600);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

@media (max-width: 480px) {
    .hub-doctors__rule { display: none; }
    .hub-doctors__count { margin-inline-start: auto; }
    .hub-doctors__head { gap: var(--spacing-sm, 8px); }
}

/* ☠️ `minmax(0, 1fr)` واجب است — توضیحِ کاملش بالای `.hub-grid`.
   نمونهٔ واقعیِ خرابی: «فوق تخصص نفرولوژی / متخصص بیماری‌های داخلی» (۴۲ نویسه)
   در `.hub-doc__spec` که `white-space: nowrap` دارد. min-content آن کارت ~۲۵۰px
   می‌شد و با `1fr` ستون تا همان‌جا باد می‌کرد؛ چهار ستونِ بادکرده از کانتینرِ
   ۱۲۰۰ پیکسلی می‌زد بیرون. `overflow: hidden` روی متن جلوی این را **نمی‌گیرد**،
   چون مسئله در محاسبهٔ عرضِ ستون است نه در رندرِ متن. */
/* ☠️ یک پزشک در هر ردیف، در همهٔ عرض‌ها (تصمیمِ کاربر ۲۰۲۶-۰۸-۲۹).
   شبکهٔ چهارستونیِ قبلی کارت را به ۲۸۰ پیکسل می‌رساند و در آن عرض نامِ پزشک
   با ellipsis بریده می‌شد، تخصص دو خط می‌شکست، آدرس نصفه می‌ماند و پیکانِ
   «برو» روی متن می‌افتاد. ردیفِ کامل همان محتوا را بدونِ هیچ برشی جا می‌دهد
   و جا برای چیزی باز می‌کند که کارت اصلاً نداشت: رتبه و دکمهٔ مقصد. */
.hub-doctors__grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 10px;
}

/* ── کارتِ پزشک ────────────────────────────────────────────────────────
   ☠️ `align-items: flex-start` نه `center`. کارت پنج سطرِ متن دارد (نام،
   تخصص، مرکز، آدرس، آمار) و با وسط‌چینی، عکسِ ۵۶ پیکسلی وسطِ آن پنج سطر
   می‌افتاد: کنارِ نامِ پزشک — تنها چیزی که کاربر واقعاً دنبالش است — فضای
   خالی می‌ماند و در ستونی از ۲۴ کارت، لبهٔ عکس‌ها دندانه‌دار می‌شد.

   ☠️ `align-content: start` روی خودِ کارت هم لازم است: در سطرِ گرید، کارتِ
   کوتاه تا قدِ بلندترین کارت کش می‌آید و بدونِ این، محتوایش وسطِ آن ارتفاعِ
   کش‌آمده شناور می‌شود. */
.hub-doc {
    position: relative;
    /* ☠️ گرید است نه فلکس، و دلیلش نواحیِ نام‌دار است: همان مارک‌آپ باید در
       ریل و موبایل «عکس + متنِ زیرِ هم» باشد و روی دسکتاپ «عکس + متن + آمار
       + دکمه» در یک ردیف. با فلکس، جابه‌جاییِ بلوکِ آمار بینِ این دو حالت
       یعنی دو مارک‌آپ — و دو مارک‌آپ یعنی هر تغییرِ آینده دو بار. */
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas:
        "avatar body"
        "avatar meta";
    align-items: start;
    align-content: start;
    column-gap: var(--spacing-md, 12px);
    min-width: 0;
    /* ☠️ منطقی نه فیزیکی: `padding-left` در RTL درست بود و در LTR جای
       پیکان را باز نمی‌کرد. */
    padding: 16px 16px;
    padding-inline-end: 34px;
    border: 1px solid var(--hub-rule);
    border-radius: 14px;
    background: #fff;
    text-decoration: none;
    transition: border-color var(--transition-base), box-shadow var(--transition-base), transform var(--transition-base);
}

/* ستونِ رتبه فقط وقتی هست که فهرست واقعاً مرتب باشد — کلاسِ جدا لازم است
   چون ستونِ خالیِ گرید gap خودش را نگه می‌دارد و کارت را بی‌دلیل تورفته
   می‌کرد. */
.hub-doc--ranked {
    grid-template-columns: auto auto minmax(0, 1fr);
    grid-template-areas:
        "rank avatar body"
        "rank avatar meta";
}

.hub-doc__avatar { grid-area: avatar; }
.hub-doc__body   { grid-area: body; }
.hub-doc__meta   { grid-area: meta; }
.hub-doc__rank   { grid-area: rank; }
.hub-doc__cta    { grid-area: cta; }

.hub-doc:hover,
.hub-doc:focus-visible {
    border-color: var(--hub-aura-live);
    box-shadow: 0 2px 4px rgba(22, 32, 46, .04), 0 14px 26px -18px rgba(20, 86, 76, .55);
    transform: translateY(-2px);
    text-decoration: none;
}

/* ☠️ حلقهٔ فوکوس جدا از حالتِ hover: تا امروز `:focus-visible` فقط همان
   سایهٔ hover را می‌گرفت، یعنی کاربرِ کیبورد هیچ نشانهٔ روشنی نداشت که کجاست. */
.hub-doc:focus-visible {
    outline: 2px solid var(--primary-color);
    outline-offset: 2px;
}

/* ☠️ بشقابِ عکس همان زبانِ بشقابِ کاشی‌هاست: حلقهٔ موییِ نعنایی. بیشترِ
   پزشکان عکس ندارند و placeholderِ خاکستری در شبکه‌ای از ۲۴ کارت به یک
   ردیف لکهٔ بی‌رنگ تبدیل می‌شد. */
.hub-doc__avatar {
    position: relative;
    flex: 0 0 auto;
    display: block;
    width: 64px;
    height: 64px;
    border-radius: var(--radius-full, 9999px);
    background: radial-gradient(circle at 50% 40%, #fff 45%, var(--hub-aura) 100%);
    box-shadow: 0 0 0 1px var(--hub-aura-edge);
    overflow: hidden;
}

.hub-doc__photo {
    display: block;
    width: 100%;
    height: 100%;
    border-radius: inherit;
    object-fit: cover;
}

/* ☠️ `min-width: 0` روی فرزندِ flex لازم است، وگرنه نامِ بلند به‌جای بریده‌شدن
   کارت را پهن می‌کند و گرید را می‌شکند. */
.hub-doc__body {
    min-width: 0;
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

/* ☠️ نامِ پزشک دیگر تک‌خطیِ سه‌نقطه‌دار نیست. روی کارتِ تمام‌عرضِ موبایل،
   «دکتر پریسا حاجی زاده سلمان آبادی» به «دکتر پریسا حاجی زاده سلمان …»
   می‌رسید — یعنی مهم‌ترین متنِ کارت، همان چیزی که کاربر دنبالش است، بریده
   می‌شد. حالا تا دو خط می‌شکند.

   ☠️ بادکردنِ ستون هم برنمی‌گردد: آن خرابی مالِ `white-space: nowrap` بود که
   min-content را به اندازهٔ کلِ رشته بزرگ می‌کرد؛ با شکستنِ عادی min-content
   فقط به اندازهٔ بلندترین **واژه** است (توضیحِ کامل بالای `.hub-grid`). */
.hub-doc__name {
    font-size: .95rem;
    font-weight: var(--font-extrabold, 800);
    line-height: 1.5;
    color: var(--hub-ink);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* ☠️ دیگر `white-space: nowrap` نیست. با آن، «فوق تخصص جراحی پلاستیک ترمیمی
   و سوختگی» روی کارتِ یک‌چهارمِ دسکتاپ به «فوق تخصص جراحی پلاستیک ترمیمی و …»
   می‌رسید — یعنی همان تخصصی که کاربر دنبالش بود بریده می‌شد.

   ☠️ بادکردنِ ستونِ گرید هم برنمی‌گردد: آن خرابی مالِ `nowrap` بود که
   min-content را به اندازهٔ کلِ رشته بزرگ می‌کرد. با شکستنِ عادی، min-content
   فقط به اندازهٔ بلندترین **واژه** است. */
.hub-doc__spec {
    font-size: .78rem;
    line-height: 1.6;
    color: var(--hub-ink-3);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* ☠️ `flex-wrap` لازم است: وقتی هم امتیاز هست و هم «N نوبت موفق»، این دو در
   کارتِ یک‌چهارمِ دسکتاپ جا نمی‌شوند و بدونِ wrap از کارت بیرون می‌زنند.
   ellipsis این‌جا جواب نمی‌داد چون دو المانِ جدا هستند نه یک رشته. */
.hub-doc__meta {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 4px 10px;
    min-width: 0;
    margin-top: 6px;
    font-size: .75rem;
    color: var(--hub-ink-3);
}

.hub-doc__rate,
.hub-doc__done {
    white-space: nowrap;
}

/* ☠️ امتیاز یک واحدِ بصری است نه سه تکه: ستاره، عدد و شمارِ نظر با
   `display: inline-flex` و gapِ صریح کنارِ هم می‌نشینند. با متنِ لخت،
   الگوریتمِ دوجهته «۵.۰» و «(۱۰۹)» را به هم می‌چسباند و روی صفحه
   «۵.۰(۱۰۹)» دیده می‌شد. */
.hub-doc__rate {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    color: #9A6B05;
    font-weight: var(--font-bold, 700);
    font-variant-numeric: tabular-nums;
}

.hub-doc__rate svg {
    width: 13px;
    height: 13px;
    color: var(--star-color, #FFB400);
}

.hub-doc__rate-count {
    color: var(--hub-ink-3);
    font-weight: var(--font-normal, 400);
}

.hub-doc__done {
    font-variant-numeric: tabular-nums;
}

/* ── پیکانِ «برو» ──────────────────────────────────────────────────────
   ☠️ کارت تا امروز هیچ نشانه‌ای نداشت که مقصدی دارد: نه دکمه، نه پیکان،
   فقط یک بلوکِ متنِ کلیک‌پذیر. این پیکان کمترین چیزی است که آن را می‌گوید.
   جایش با `padding-inline-end` روی کارت رزرو شده تا هرگز روی متن نیفتد. */
.hub-doc__go {
    position: absolute;
    inset-inline-end: 10px;
    top: 50%;
    display: grid;
    place-items: center;
    width: 20px;
    height: 20px;
    margin-top: -10px;
    color: #C2CEDA;
    transition: color var(--transition-base), transform var(--transition-base);
}

.hub-doc__go svg {
    width: 15px;
    height: 15px;
}

.hub-doc:hover .hub-doc__go {
    color: var(--hub-aura-live);
    /* در RTL «جلو» یعنی چپ، پس ترجمهٔ منفی. */
    transform: translateX(-3px);
}

/* ── نامِ مرکز درمانی و آدرس روی کارتِ پزشک ───────────────────────────────
   ☠️ این دو خط تزئین نیستند: دادهٔ Search Console (۲۰۲۶-۰۸-۲۹) نشان داد ۶۳٪ از
   کلیک‌های /best-doctors/{شهر} از کوئری‌هایی مثل «لیست پزشکان درمانگاه …» می‌آید
   و متنی که آن رتبه را می‌سازد همین است. با `display: none` هم پنهانشان نکن.

   ☠️ آدرس به دو خط بریده می‌شود نه یک خط: آدرسِ ایرانی تقریباً همیشه بلندتر از
   یک خط است و بریدنِ یک‌خطی نامِ خیابان/بیمارستان را دقیقاً وسط می‌بُرد. */
/* سرصفحهٔ ناحیهٔ کاشی‌ها وقتی فهرستِ پزشک بالای صفحه آمده — بدونِ آن، شبکهٔ
   شهرها/تخصص‌ها ادامهٔ همان فهرست به‌نظر می‌رسد. */
/* ── نظر بیماران ──────────────────────────────────────────────────────── */
.hub-reviews {
    margin-top: var(--hub-gap-lg);
    padding-top: var(--hub-gap-lg);
    /* ☠️ بدونِ این خط، کارت‌های نظر بلافاصله زیرِ کارت‌های پزشک می‌آمدند و
       چون هر دو سفید و هم‌اندازه‌اند، به‌نظر ادامهٔ همان فهرست می‌رسیدند. */
    border-top: 1px solid var(--hub-rule-strong);
}

.hub-reviews__head {
    display: flex;
    align-items: center;
    gap: var(--spacing-md, 12px);
    margin-bottom: var(--spacing-lg);
}

.hub-reviews__title {
    margin: 0;
    font-size: clamp(1.0625rem, .98rem + .4vw, 1.25rem);
    font-weight: var(--font-extrabold, 800);
    line-height: 1.45;
    letter-spacing: -0.01em;
    color: var(--hub-ink);
}

.hub-reviews__rule {
    flex: 1;
    min-width: var(--spacing-lg);
    height: 1px;
    background: var(--hub-rule-strong);
}

.hub-reviews__count {
    flex-shrink: 0;
    padding: 3px 9px;
    margin-inline-start: auto;
    border-radius: var(--radius-full, 9999px);
    background: rgba(255, 255, 255, .8);
    box-shadow: inset 0 0 0 1px var(--hub-rule-strong);
    color: var(--hub-ink-3);
    font-size: var(--font-xs, .75rem);
    font-weight: var(--font-semibold, 600);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* ☠️ ریل در همهٔ عرض‌ها، نه فقط موبایل (تصمیمِ کاربر ۲۰۲۶-۰۸-۲۹). سی نظر در
   شبکهٔ سه‌ستونی ده سطر است — روی دسکتاپ ۲٬۱۰۰ پیکسل — و چون متنِ نظرها
   ناهم‌اندازه است، کفِ کارت‌ها دندانه‌دار می‌شد. در ریل همهٔ کارت‌ها یک
   ردیف‌اند، پس هم‌قد می‌شوند و پابرگشان روی یک خط می‌نشیند.

   ☠️ عرضِ ستون روی موبایل درصد است نه پیکسل: درصد نسبت به عرضِ **دیدِ**
   اسکرولر حساب می‌شود، پس همیشه تکه‌ای از کارتِ بعدی پیداست. */
.hub-reviews__grid {
    grid-auto-columns: 340px;
    gap: var(--spacing-md, 12px);
}

@media (max-width: 767px) {
    .hub-reviews__grid { grid-auto-columns: 86%; }
}

@media (max-width: 480px) {
    .hub-reviews__rule { display: none; }
    .hub-reviews__head { gap: var(--spacing-sm, 8px); }
}

/* ═══ کارتِ نظر ══════════════════════════════════════════════════════════
   ☠️ ترتیبِ کارت از بالا به پایین (تصمیمِ کاربر ۲۰۲۶-۰۸-۲۹): بیمار، ستاره،
   متن، و در **کف** پزشک. نسخهٔ قبلی برعکس بود — ستاره بالا، متن، و پایین یک
   پابرگِ متنی که هم نامِ پزشک بود هم نامِ بیمار. آن ترتیب دو مشکل داشت:
   خواننده تا آخرِ کارت نمی‌دانست این حرفِ چه کسی است، و نامِ پزشک — که
   مقصدِ کلیک است — نه عکس داشت نه عنوان.

   ☠️ کارت ستونِ فلکسی است تا پابرگِ پزشک به کف بچسبد. در نواری که همهٔ
   کارت‌هایش هم‌قدند، همین یک خط باعث می‌شود عکسِ پزشکِ همهٔ کارت‌ها روی یک
   خطِ افقی بنشیند. */
.hub-review {
    display: flex;
    flex-direction: column;
    min-width: 0;
    margin: 0;
    padding: var(--spacing-lg);
    background: #fff;
    border: 1px solid var(--hub-rule);
    border-radius: 14px;
}

/* ── سرِ کارت: بیمار ─────────────────────────────────────────────────── */
.hub-review__patient {
    display: flex;
    align-items: center;
    gap: 10px;
    min-width: 0;
    margin-bottom: var(--spacing-md, 12px);
}

/* ☠️ هیچ بیماری در tbl_review عکس ندارد (شمارشِ کامل در سرصفحهٔ
   `_reviews.php`)، پس این همیشه placeholderِ پیش‌فرضِ سایت است — همان چیزی که
   هر آواتارِ خالیِ دیگری در محصول نشان می‌دهد (تصمیمِ کاربر ۲۰۲۶-۰۸-۲۹).
   نسخهٔ اول حرفِ اولِ نام را می‌گذاشت؛ راست بود ولی یک زبانِ بصریِ تازه بود
   که جای دیگری در سایت وجود نداشت.

   بشقاب همان بشقابِ نعناییِ کارتِ پزشک است تا آواتارِ بیمار و آواتارِ پزشکِ
   کفِ همین کارت از یک خانواده باشند. */
.hub-review__pavatar {
    flex: 0 0 auto;
    display: block;
    width: 38px;
    height: 38px;
    border-radius: var(--radius-full, 9999px);
    background: radial-gradient(circle at 50% 40%, #fff 40%, var(--hub-aura) 100%);
    box-shadow: 0 0 0 1px var(--hub-aura-edge);
    overflow: hidden;
}

.hub-review__pphoto {
    display: block;
    width: 100%;
    height: 100%;
    border-radius: inherit;
    object-fit: cover;
}

.hub-review__who {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.hub-review__pname {
    color: var(--hub-ink);
    font-size: .88rem;
    font-weight: var(--font-bold, 700);
    line-height: 1.5;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* تاریخ فعلاً رندر نمی‌شود (`$showDate` در `_reviews.php`)؛ استایلش می‌ماند
   تا برگرداندنش یک `true` باشد نه یک بازطراحی. */
.hub-review__date {
    color: var(--hub-ink-3);
    font-size: var(--font-xs, .75rem);
    font-weight: var(--font-normal, 400);
    font-variant-numeric: tabular-nums;
}

.hub-review__stars {
    display: flex;
    gap: 2px;
    margin-bottom: var(--spacing-sm, 8px);
    color: #DCE2E9;
}

.hub-review__star {
    width: 15px;
    height: 15px;
}

.hub-review__star.is-on { color: var(--star-color, #FFB400); }

/* ☠️ متن سقفِ خط دارد و باید داشته باشد: کارت‌های یک ردیفِ گرید هم‌قد
   می‌شوند، پس یک نظرِ ۲۲۰ نویسه‌ای قدِ **همهٔ** کارت‌های نوار را تعیین می‌کند.
   ☠️ ارتفاعِ خط ۱٫۷۵ است نه ۱٫۹۵: در ستونِ باریکِ موبایل، با آن عدد هر کارت
   به ۵۰۰ پیکسل می‌رسید. */
.hub-review__text {
    margin: 0 0 var(--spacing-lg);
    font-size: .875rem;
    line-height: 1.75;
    color: var(--hub-ink-2);
    display: -webkit-box;
    -webkit-line-clamp: 5;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* ── پایِ کارت: پزشک ────────────────────────────────────────────────── */
.hub-review__foot {
    display: flex;
    align-items: center;
    gap: 10px;
    min-width: 0;
    /* پابرگ همیشه کف؛ خطِ مویی مرزش را می‌گوید. */
    margin-top: auto;
    padding-top: var(--spacing-md, 12px);
    border-top: 1px solid var(--hub-rule);
}

.hub-review__davatar {
    flex: 0 0 auto;
    display: block;
    width: 44px;
    height: 44px;
    border-radius: var(--radius-full, 9999px);
    background: radial-gradient(circle at 50% 40%, #fff 45%, var(--hub-aura) 100%);
    box-shadow: 0 0 0 1px var(--hub-aura-edge);
    overflow: hidden;
}

.hub-review__dphoto {
    display: block;
    width: 100%;
    height: 100%;
    border-radius: inherit;
    object-fit: cover;
}

.hub-review__dtext {
    display: flex;
    flex-direction: column;
    gap: 1px;
    min-width: 0;
}

.hub-review__doctor {
    color: var(--hub-ink);
    font-size: .88rem;
    font-weight: var(--font-extrabold, 800);
    line-height: 1.5;
    text-decoration: none;
    text-underline-offset: 3px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.hub-review__doctor:hover,
.hub-review__doctor:focus-visible {
    color: var(--primary-color);
    text-decoration: underline;
}

/* ☠️ عنوانِ زیرِ نام از `BrandKitService::resolveSpecialtyName` می‌آید — همان
   منبعی که پروفایل و پوسترِ برندکیت از آن می‌خوانند. متنِ آزادِ دیگری این‌جا
   ننویس، وگرنه یک پزشک روی دو صفحه دو عنوان می‌گیرد
   ([[project_specialty_display_name]]). */
.hub-review__dspec {
    color: var(--hub-ink-3);
    font-size: var(--font-xs, .75rem);
    line-height: 1.6;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* سرصفحهٔ ناحیهٔ کاشی‌ها وقتی فهرستِ پزشک بالای صفحه آمده — بدونِ آن، شبکهٔ
   شهرها/تخصص‌ها ادامهٔ همان فهرست به‌نظر می‌رسد. */
.hub-sections-heading {
    margin: var(--hub-gap-lg) 0 var(--spacing-lg);
    padding-top: var(--hub-gap-lg);
    border-top: 1px solid var(--hub-rule-strong);
    font-size: clamp(1.0625rem, .98rem + .4vw, 1.25rem);
    font-weight: var(--font-extrabold, 800);
    line-height: 1.45;
    letter-spacing: -0.01em;
    color: var(--hub-ink);
}

/* ☠️ وزنِ نامِ مرکز از ۷۰۰ به ۶۰۰ آمد و رنگش یک پله روشن‌تر شد. با ۷۰۰ و
   #4A5A6A، «مطب خیابان شریعتی» دقیقاً هم‌وزنِ نامِ خودِ پزشک دیده می‌شد و
   کارت دو تیتر داشت — چشم نمی‌دانست کدام را اول بخواند. */
/* ☠️ نامِ مرکز و آدرس داخلِ یک بسته‌اند نه دو فرزندِ مستقلِ بدنه: روی
   دسکتاپ باید کنارِ هم روی یک خط بنشینند و روی موبایل زیرِ هم. با دو
   فرزندِ جدا، «کنارِ هم» فقط با تغییرِ چیدمانِ کلِ بدنه ممکن بود که بقیهٔ
   سطرها را هم جابه‌جا می‌کرد. */
.hub-doc__placeline {
    min-width: 0;
    margin-top: 5px;
}

.hub-doc__place {
    display: flex;
    align-items: center;
    gap: 4px;
    font-size: .78rem;
    font-weight: var(--font-semibold, 600);
    color: var(--hub-ink-2);
    min-width: 0;
}

.hub-doc__place svg {
    width: 12px;
    height: 12px;
    flex-shrink: 0;
    color: var(--hub-aura-live);
}

.hub-doc__place-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.hub-doc__addr {
    margin-top: 2px;
    font-size: .74rem;
    line-height: 1.55;
    color: var(--hub-ink-3);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.hub-doctors__intro {
    margin: 0 0 var(--spacing-lg);
    font-size: .88rem;
    line-height: 1.85;
    color: var(--hub-ink-2);
    max-width: 78ch;
}

.hub-doctors__more {
    margin: var(--spacing-lg) 0 0;
    text-align: center;
}

.hub-doctors__more a {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 9px 18px;
    border: 1px solid var(--hub-rule-strong);
    border-radius: var(--radius-full, 9999px);
    background: #fff;
    font-size: .88rem;
    font-weight: var(--font-bold, 700);
    color: var(--primary-color);
    text-decoration: none;
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

.hub-doctors__more a:hover {
    border-color: var(--primary-color);
    box-shadow: 0 8px 18px -14px rgba(37, 60, 137, .8);
    text-decoration: none;
}

.hub-doctors__more a:focus-visible {
    outline: 2px solid var(--primary-color);
    outline-offset: 2px;
}

.hub-doctors__more svg {
    width: 15px;
    height: 15px;
}

/* ═══ ریلِ افقی روی موبایل ══════════════════════════════════════════════
   ☠️ فقط بلوک‌های تکمیلی (`--rail`) این را می‌گیرند. اندازه‌گیریِ
   ۲۰۲۶-۰۸-۲۹ روی /best-doctors: «تازه‌ترین پزشکان» ۳٬۲۱۱ پیکسل از ۳۳ هزار
   پیکسلِ صفحه را می‌خورد — چهار صفحهٔ کامل اسکرول برای فهرستی که خودِ
   کامنتِ hub.php می‌گوید «ضعیف‌ترین کارت‌های صفحه» است.

   ☠️ فهرستِ **اصلی** هرگز ریل نمی‌شود: آن یکی وعدهٔ عنوانِ صفحه است
   («لیست بهترین دکتر …») و باید کامل و قابلِ اسکن بماند، هم برای کاربر هم
   برای خزندهٔ گوگل که ItemList همان‌جاست. */
.hub-doctors--rail .hub-doctors__grid {
    /* ☠️ ریست واجب است: `.hub-doctors__grid` پایه یک ستونِ صریحِ `1fr` دارد و
       در جریانِ ستونی، همان ستونِ صریح اولین کارت را تمام‌عرض می‌کرد. */
    grid-template-columns: none;
    grid-auto-columns: 320px;
    gap: var(--spacing-md, 12px);
}

@media (max-width: 767px) {
    .hub-doctors--rail .hub-doctors__grid { grid-auto-columns: 86%; }
}

/* در ریل، کارت هرگز ردیفی نمی‌شود: عرضِ ۳۲۰ پیکسل جا برای ستونِ آمار و
   دکمه ندارد و همان چیدمانِ فشرده (عکس + متنِ زیرِ هم) درست‌ترین است. */
.hub-doctors--rail .hub-doc {
    padding-inline-end: 30px;
}

/* ━━ لینک‌سازیِ متقابل ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
.hub-crosslinks {
    margin-top: var(--hub-gap-lg);
    padding-top: var(--hub-gap-lg);
    border-top: 1px solid var(--hub-rule-strong);
    display: flex;
    flex-direction: column;
    gap: var(--spacing-xl);
}

.hub-crosslinks__title {
    margin: 0 0 var(--spacing-md, 12px);
    font-size: .92rem;
    font-weight: var(--font-extrabold, 800);
    color: var(--hub-ink);
}

.hub-crosslinks__list {
    display: flex;
    flex-wrap: wrap;
    gap: var(--spacing-sm, 8px);
}

.hub-crosslink {
    display: inline-block;
    padding: 8px 14px;
    border: 1px solid var(--hub-rule-strong);
    border-radius: var(--radius-full, 9999px);
    background: #fff;
    font-size: .8rem;
    line-height: 1.6;
    color: var(--hub-ink-2);
    text-decoration: none;
    transition: border-color var(--transition-fast), background var(--transition-fast), color var(--transition-fast);
}

.hub-crosslink:hover,
.hub-crosslink:focus-visible {
    border-color: var(--hub-aura-live);
    background: var(--hub-aura-wash);
    color: var(--hub-ink);
    text-decoration: none;
}

.hub-crosslink:focus-visible {
    outline: 2px solid var(--primary-color);
    outline-offset: 2px;
}

.hub-crosslink--primary {
    border-color: var(--primary-color);
    background: var(--primary-light, rgb(229, 233, 244));
    color: var(--primary-color);
    font-weight: var(--font-bold, 700);
}

.hub-crosslink--primary:hover,
.hub-crosslink--primary:focus-visible {
    border-color: var(--primary-color);
    background: #fff;
    color: var(--primary-color);
}

/* ── میان‌بر و حالتِ خالی ─────────────────────────────────────────────── */
.hub-skip a {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.hub-skip svg {
    width: 15px;
    height: 15px;
}

/* ═══════════════════════════════════════════════════════════════════════
   کارتِ پزشک — رتبه، آمار و دکمهٔ مقصد
   ═══════════════════════════════════════════════════════════════════════ */

/* ☠️ رتبه تزئین نیست و روی هر فهرستی هم نمی‌آید: فقط فهرستی که خودش
   می‌گوید «بر اساس امتیاز و نظر بیماران، تعداد نوبت‌های انجام‌شده و
   تکمیل‌بودن پروفایل مرتب شده است» عدد می‌گیرد. «تازه‌ترین پزشکان» که
   ترتیبش زمانی است هرگز رتبه نمی‌گیرد — عددِ آن‌جا یک ادعای دروغ بود.

   ☠️ عدد یک نشانِ رنگی نیست: نه دایرهٔ پررنگ، نه مدال. در ستونی از ۲۴ ردیف،
   بیست‌وچهار نشانِ رنگی یعنی هیچ‌کدام دیده نمی‌شوند و چشم جای نامِ پزشک
   روی عددها می‌افتد. */
.hub-doc__rank {
    align-self: center;
    min-width: 20px;
    color: var(--hub-ink-3);
    font-size: .8rem;
    font-weight: var(--font-bold, 700);
    font-variant-numeric: tabular-nums;
    text-align: center;
}

/* ☠️ دکمه یک `<span>` است نه `<a>`: کلِ کارت خودش یک لینک است و لینکِ تودرتو
   HTMLِ نامعتبر می‌سازد که مرورگر بی‌صدا بازش می‌کند و چیدمان را می‌شکند.
   متن هم عمداً «مشاهده پروفایل» است نه «رزرو نوبت»: همهٔ پزشکانِ فهرست
   نوبتِ قابلِ رزرو ندارند و دکمه باید همان کاری را بگوید که واقعاً می‌کند. */
.hub-doc__cta {
    display: none;
    align-items: center;
    gap: 6px;
    flex-shrink: 0;
    align-self: center;
    padding: 9px 16px;
    border: 1px solid var(--hub-aura-edge);
    border-radius: var(--radius-full, 9999px);
    background: var(--hub-aura-wash);
    color: var(--primary-color);
    font-size: .8rem;
    font-weight: var(--font-bold, 700);
    white-space: nowrap;
    transition: background var(--transition-base), border-color var(--transition-base),
                color var(--transition-base);
}

.hub-doc__cta svg {
    width: 14px;
    height: 14px;
    transition: transform var(--transition-base);
}

/* ☠️ یک ژست، نه سه تا: بالا‌آمدنِ کارت، پررنگ‌شدنِ حاشیه و پرشدنِ دکمه با
   هم و در یک زمان اتفاق می‌افتند — همان لحظه‌ای که کاشی‌ها هم دارند. */
.hub-doc:hover .hub-doc__cta,
.hub-doc:focus-visible .hub-doc__cta {
    border-color: var(--primary-color);
    background: var(--primary-color);
    color: #fff;
}

.hub-doc:hover .hub-doc__cta svg {
    transform: translateX(-3px);
}

/* ═══ چیدمانِ ردیفی ═══════════════════════════════════════════════════
   ☠️ سه پله دارد و هر پله از یک خرابیِ دیده‌شده آمده:
     • زیرِ ۷۶۸: کارتِ فشرده (عکس + متنِ زیرِ هم). ردیف در این عرض یعنی
       متنِ آدرس در یک نوارِ ۱۵۰ پیکسلی.
     • ۷۶۸ تا ۱۰۲۳: ردیف بدونِ دکمه. با چیدمانِ فشرده، کارتِ ۷۴۸ پیکسلی
       فقط ۳۰۰ پیکسلِ سمتِ راستش پر بود و بقیه‌اش یک مستطیلِ خالی —
       بدترین حالتِ ممکنِ تبلت.
     • ۱۰۲۴ به بالا: دکمهٔ «مشاهده پروفایل» هم جا می‌شود.

   ☠️ `:not(.hub-doctors--rail)` شرطِ لازم است: ریلِ «تازه‌ترین پزشکان» همان
   کارت را با عرضِ ۳۲۰ پیکسل نشان می‌دهد و چیدمانِ ردیفی آن‌جا نه جا می‌شود
   نه معنا دارد. */
@media (min-width: 768px) {
    .hub-doctors:not(.hub-doctors--rail) .hub-doc {
        grid-template-columns: auto minmax(0, 1fr) auto;
        grid-template-areas: "avatar body meta";
        align-items: center;
        column-gap: var(--spacing-lg);
        /* ☠️ ارتفاعِ کارت عمداً بالا رفت (تصمیمِ کاربر ۲۰۲۶-۰۸-۲۹): با
           padding ۱۵ پیکسل، ردیفِ سه‌سطری ۱۰۴ پیکسل می‌شد و در ستونی از ۲۴
           ردیف، فاصلهٔ بینِ کارت‌ها از فضای داخلشان بیشتر به‌نظر می‌رسید —
           یعنی فهرست به‌جای بیست‌وچهار کارت، یک نوارِ راه‌راه دیده می‌شد. */
        padding: 20px 20px;
        padding-inline-end: 34px;
    }

    .hub-doctors:not(.hub-doctors--rail) .hub-doc--ranked {
        grid-template-columns: auto auto minmax(0, 1fr) auto;
        grid-template-areas: "rank avatar body meta";
        column-gap: var(--spacing-md, 12px);
    }

    .hub-doctors:not(.hub-doctors--rail) .hub-doc--ranked .hub-doc__body {
        margin-inline-start: 4px;
    }

    .hub-doctors:not(.hub-doctors--rail) .hub-doc__avatar {
        width: 82px;
        height: 82px;
    }

    .hub-doctors:not(.hub-doctors--rail) .hub-doc__rank {
        min-width: 26px;
        font-size: .95rem;
    }

    .hub-doctors:not(.hub-doctors--rail) .hub-doc__name {
        font-size: 1.02rem;
    }

    .hub-doctors:not(.hub-doctors--rail) .hub-doc__spec {
        -webkit-line-clamp: 1;
        font-size: .82rem;
    }

    /* بدنه گرید می‌شود تا سطرهایش (نام، تخصص، خطِ مکان) فاصلهٔ یکنواخت
       بگیرند و «خطِ مکان» بتواند خودش یک ردیفِ افقی شود. */
    .hub-doctors:not(.hub-doctors--rail) .hub-doc__body {
        display: grid;
        grid-template-columns: minmax(0, 1fr);
        gap: 1px;
    }

    .hub-doctors:not(.hub-doctors--rail) .hub-doc__placeline {
        display: flex;
        align-items: center;
        min-width: 0;
        margin-top: 6px;
    }

    /* ☠️ در تنگنا، **آدرس** کوتاه می‌شود نه نامِ مرکز. پیش‌فرضِ فلکس هر دو را
       به نسبتِ طولشان می‌چیند و نتیجه‌اش «مطب دکتر امیرحسین پو…» بود — یعنی
       دقیقاً همان رشته‌ای که کوئری‌های «لیست پزشکان درمانگاه …» رویش رتبه
       می‌گیرند، وسط بریده می‌شد. سقفِ ۶۰٪ هم لازم است: نامِ مرکزِ خیلی بلند
       نباید کلِ آدرس را از خط بیرون بیندازد. */
    .hub-doctors:not(.hub-doctors--rail) .hub-doc__place {
        flex: none;
        max-width: 60%;
    }

    .hub-doctors:not(.hub-doctors--rail) .hub-doc__addr {
        flex: 1 1 auto;
        min-width: 0;
    }

    /* ☠️ نامِ مرکز و آدرس روی یک خط، ولی هر دو در DOM: دادهٔ Search Console
       می‌گوید ۶۳٪ کلیکِ /best-doctors/{شهر} از همین متن می‌آید. برشِ بصری
       با ellipsis متن را از صفحه حذف نمی‌کند، فقط از دید. */
    .hub-doctors:not(.hub-doctors--rail) .hub-doc__addr {
        display: block;
        margin-top: 0;
        margin-inline-start: 6px;
        padding-inline-start: 8px;
        border-inline-start: 1px solid var(--hub-rule-strong);
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        font-size: .78rem;
    }

    /* بلوکِ آمار ستونِ خودش می‌شود و به لبهٔ انتهایی چفت می‌شود تا در ستونی
       از ۲۴ ردیف، عددها روی یک محورِ عمودی بنشینند. */
    .hub-doctors:not(.hub-doctors--rail) .hub-doc__meta {
        flex-direction: column;
        align-items: flex-end;
        flex-wrap: nowrap;
        gap: 4px;
        margin-top: 0;
        padding-inline-start: var(--spacing-lg);
        border-inline-start: 1px solid var(--hub-rule);
        text-align: end;
    }
}

@media (min-width: 1024px) {
    .hub-doctors:not(.hub-doctors--rail) .hub-doc {
        grid-template-columns: auto minmax(0, 1fr) auto auto;
        grid-template-areas: "avatar body meta cta";
        padding-inline-end: 20px;
    }

    .hub-doctors:not(.hub-doctors--rail) .hub-doc--ranked {
        grid-template-columns: auto auto minmax(0, 1fr) auto auto;
        grid-template-areas: "rank avatar body meta cta";
    }

    .hub-doctors:not(.hub-doctors--rail) .hub-doc__cta {
        display: inline-flex;
    }

    /* پیکانِ کوچک با وجودِ دکمه حرفِ تکراری است. */
    .hub-doctors:not(.hub-doctors--rail) .hub-doc__go {
        display: none;
    }
}

/* ── ترجیحِ حرکتِ کم، دورِ دوم ─────────────────────────────────────────
   بلوکِ بالای فایل فقط کاشی را می‌گرفت؛ عناصرِ تازه هم باید بی‌حرکت شوند. */
@media (prefers-reduced-motion: reduce) {
    .hub-doc,
    .hub-doc__go,
    .hub-jump__chip,
    .hub-crosslink,
    .hub-doctors__more a {
        transition: none;
    }

    .hub-doc:hover {
        transform: none;
    }

    .hub-doc:hover .hub-doc__go {
        transform: none;
    }

    /* ریل‌ها هم نباید با snapِ نرم بپرند. */
    .hub-reviews__grid,
    .hub-doctors--rail .hub-doctors__grid,
    .hub-jump__list {
        scroll-behavior: auto;
    }

    .hub-tile:hover .hub-tile__plate,
    .aes-door:hover .hub-tile__plate {
        transition: none;
    }
}
