/* =========================================================================
   بوابة الزبون — Customer portal
   -------------------------------------------------------------------------
   ورقة ثانية فوق ورقة اللوحة، لا بديلة عنها. المقاسات والحواف والحركة كلها من
   :root في style.css، فلا مقاس مرقون هنا.

   A second sheet on top of the console's, not a replacement for it. Spacing,
   radii and easing all come from :root in style.css, so nothing is hard-coded
   here.

   أما اللون فهو الفرق الوحيد بين السطحين: اللوحة أداةُ تشغيل داخلية تبقى
   بالأبيض والأسود، والبوابة واجهةٌ لزبون لا يعرف المنصة — وهي تحتاج ما لا
   يحتاجه المشغّل: هوية يتذكّرها، وإشارة لونية يقرؤها قبل أن يقرأ الكلمة.
   ولذلك تُعاد الرموز نفسها تعريفاً تحت .portal-theme وحدها، فلا يتسرّب لون
   واحد إلى اللوحة.

   Colour is the one difference between the two surfaces. The console is an
   internal operating tool and stays black and white; the portal faces a
   customer who does not know the platform, and needs what an operator does
   not: an identity to remember, and a signal read before the word beside it.
   Which is why the same tokens are redefined under .portal-theme alone — not
   one colour leaks into the console.

   وتبقى بقية القيود: بلا إيموجي، متجاوبة بالكامل، ومبنية على الخصائص المنطقية
   فينقلب التخطيط كاملاً بتبديل dir وحده.

   Every other constraint holds: no emoji, fully responsive, and built on
   logical properties so flipping dir mirrors the whole layout.
   ========================================================================= */

/* ------------------------------------------------------- Brand palette */
/* ستة ألوان لا أكثر، ولكلٍّ منها عملٌ واحد. اللون الذي يعني شيئين لا يعني
   شيئاً: فالأخضر مالٌ وتأكيد دائماً، والذهبي اختيارٌ وهوية دائماً، والأحمر
   خطرٌ أو خروجٌ دائماً — ولا يُستعمل أيٌّ منها للزينة.

   Six colours and no more, each with one job. A colour that means two things
   means nothing: green is always money and confirmation, gold is always
   selection and identity, red is always danger or leaving — and none of them
   is ever used for decoration. */
.portal-theme {
  --gold: #c5992b;      /* الهوية والاختيار — identity and selection */
  --green: #4caf50;     /* المال والتأكيد — money and confirmation */
  --red: #c63131;       /* الخطر والخروج — danger and leaving */
  --sand: #f2e5ca;      /* الأسطح الدافئة — the warm surfaces */
  --olive: #635129;     /* النص الثانوي — secondary text */
  --cocoa: #582415;     /* الحبر والأسطح الداكنة — the ink and dark surfaces */

  --on-gold: #2c1403;
  --on-green: #08210b;
  --on-red: #ffffff;

  /* رموز النظام نفسها، معادٌ تعريفها بالبنّي بدل الأسود — والورقة بيضاء،
     والأرض تحتها درجةٌ واحدة رمادية.

     مرّ هذا السطر بخطأين. أوّلهما كريمٌ يميل إلى الصفرة: الأرض ‎#faf5ea‎ والسطح
     ‎#fffcf5‎، بينهما بكسلان في القناة الزرقاء وحدها — فلا هو فرقُ سطحٍ عن أرض
     يُقرأ، ولا هو بياضٌ نظيف؛ ويظهر صفرةً صريحة إلى جانب أي أبيض حقيقي. وثانيهما
     ردُّ فعلٍ عليه: الأبيضُ نفسه أرضاً وسطحاً، فاستوت البطاقة بما تحتها ولم يبق
     يفصلها إلا حدٌّ شعرة عرضه بكسل — فصارت الصفحة لوحاً مسطّحاً لا يُعرف أين
     تبدأ فيه بطاقة وأين تنتهي.

     والصواب بينهما: السطح أبيضُ خالص كما طُلب، والأرض تحته أخفض منه درجةً —
     ورماديٌّ محايد لا كريم، فالصفرة هي التي شُكيت لا الدرجة. والبطاقة بعد ذلك
     ترتفع بثلاثة أشياء تعمل معاً: أرضٌ أخفض، وحدٌّ ‎--line‎، وظلٌّ ‎--shadow-1‎.

     ودفء الهوية يبقى حيث موضعه: في الذهبي والرملي والبنّي التي تحملها الأسطح
     المصبوغة والوسوم والحدود، لا في لون الورقة.

     The system's own tokens, redefined in brown instead of black — with a white
     paper and a ground one grey step below it.

     This line has been wrong twice. First a cream leaning yellow: the ground
     #faf5ea and the surface #fffcf5, two points of blue apart — neither a
     surface reading against a ground nor a clean white, and frankly yellow
     beside any true white. Then an overcorrection: white itself for both, so
     the card sat level with what it lay on and nothing separated them but a
     hairline one pixel wide — and the page became a flat sheet where a card's
     beginning and end could not be found.

     The truth is between them. The surface is the plain white that was asked
     for, and the ground beneath it sits one step lower — a neutral grey rather
     than a cream, since it was the yellow that was complained of and not the
     step. The card then lifts on three things working together: a lower ground,
     the --line border, and the --shadow-1.

     The identity's warmth stays where it belongs: in the gold, the sand and the
     brown carried by the tinted surfaces, the tags and the borders, rather than
     in the colour of the paper. */
  --fg: var(--cocoa);
  --bg: #ececed;

  --fg-1: #3b1a0d;
  --fg-2: var(--olive);
  --fg-3: #8b7548;
  --fg-4: #bba478;

  --line: rgb(88 36 21 / 0.16);
  --line-strong: rgb(88 36 21 / 0.34);
  --line-soft: rgb(88 36 21 / 0.08);

  --surface: #ffffff;
  --surface-1: rgb(197 153 43 / 0.06);
  --field-face: #fcf9f2;   /* = --surface-1 مسطَّحة فوق --surface — flattened over --surface */
  --surface-2: rgb(197 153 43 / 0.14);
  --surface-3: rgb(197 153 43 / 0.22);
  --surface-inv: var(--cocoa);
  --on-inv: var(--sand);

  /* الزجاج: ما يطفو فوق الخريطة. رمزٌ مستقل لأن الشفافية هنا لون لا قناة.
     Glass: what floats over the map. Its own token, because the translucency
     here is a colour rather than a channel. */
  --glass: rgb(255 255 255 / 0.93);

  /* ما سبق أن فُتح: رماديٌّ دافئ، لا لونٌ سابع في اللوحة بل غيابُ لون.
     What has already been opened: a warm grey — not a seventh colour in
     the palette but the absence of one. */
  --seen: #9a938a;
  --seen-line: #7d7770;
  --seen-ink: #ffffff;

  /* وجه الذهبي حين يُملأ به سطحٌ تُكتب عليه كلمة. في النهار هو الذهبي
     نفسه، وفي الليل أعمق منه — لأن الكتابة عليه ستكون بيضاء، والأبيض على
     ذهبٍ فاتح لا يُقرأ.

     The face of gold where it fills a surface carrying a word. By day it is
     gold itself; by night it is deeper — because the writing on it will be
     white, and white on pale gold cannot be read. */
  --gold-face: var(--gold);
  --brown-face: var(--cocoa);

  --shadow-1: 0 1px 2px rgb(88 36 21 / 0.07);
  --shadow-2: 0 4px 16px rgb(88 36 21 / 0.13);
  --shadow-3: 0 18px 48px rgb(88 36 21 / 0.24);

  /* الارتفاع: ظلٌّ يقول إنّ الشيء مضغوط، لا إطارٌ يقول إنّه مرسوم. الرقاقة
     ترتفع عن الشريط قليلاً، فتُقرأ زرّاً قبل أن تُقرأ حداً.

     Lift: a shadow that says a thing can be pressed, rather than a border
     that says it was drawn. The chip stands a little off the rail, so it
     reads as a button before it reads as an outline. */
  /* ووجه عناصر الشريط لونٌ صريح لا طبقةٌ شفّافة، وإلا عادت الخريطة تُرى من
     خلال ما يعلوها. The face of the rail elements is a stated colour rather
     than a translucent layer — otherwise the map shows through again. */
  --rail-face: #ffffff;
  --rail-face-hover: #f7eed9;

  --lift: 0 1px 1px rgb(88 36 21 / 0.10), 0 2px 5px rgb(88 36 21 / 0.13);
  --lift-hover: 0 2px 3px rgb(88 36 21 / 0.12), 0 6px 14px rgb(88 36 21 / 0.18);
  --lift-press: 0 1px 1px rgb(88 36 21 / 0.16);

  color-scheme: light;
}

.portal-theme[data-theme="dark"] {
  --fg-1: #f6ecd8;
  --fg-2: #d3be94;
  --fg-3: #a38f68;
  --fg-4: #6e5e42;
  --fg: var(--fg-1);

  /* والليل يحتاج الدرجة أكثر من النهار: الأسود على الأسود لا يُرى حدُّه، والحدُّ
     في السمة الداكنة شفافيتُه ‎0.16‎ من رمليٍّ باهت فلا يكاد يبين. فالأرض هنا
     أعمق ممّا كانت والسطح أفتح ممّا كان، والفرق بينهما ثلاثة أضعاف ما كان بين
     ‎#17100a‎ و‎#221710‎ — فتُقرأ البطاقة بطاقةً قبل أن يُقرأ حدُّها.

     Night needs the step more than day does: black on black shows no edge, and
     the dark theme border is a pale sand at 0.16 which barely registers. So the
     ground here is deeper than it was and the surface lighter, three times the
     distance that lay between #17100a and #221710 — and the card reads as a card
     before its border is read at all. */
  --bg: #0f0a06;

  --line: rgb(242 229 202 / 0.16);
  --line-strong: rgb(242 229 202 / 0.34);
  --line-soft: rgb(242 229 202 / 0.08);

  --surface: #291d14;
  --surface-1: rgb(242 229 202 / 0.05);
  --field-face: #33271d;   /* = --surface-1 مسطَّحة فوق --surface — flattened over --surface */
  --surface-2: rgb(242 229 202 / 0.11);
  --surface-3: rgb(242 229 202 / 0.17);
  --surface-inv: #4a2c1c;

  --glass: rgb(34 23 16 / 0.93);

  --seen: #4a453d;
  --seen-line: #6b6459;
  --seen-ink: #ffffff;

  /* في الوضع الليلي لا تُكتب كلمةٌ بالبنّي الغامق. ما كان حبراً داكناً
     على ذهبٍ أو رملٍ يصير أبيض، والسطح تحته يعمّق حتى يُقرأ الأبيض عليه.

     In the dark theme no word is written in dark brown. What was dark ink on
     gold or on sand becomes white, and the surface beneath it deepens until
     white can be read on it. */
  --on-gold: #ffffff;
  --on-inv: #ffffff;
  --gold-face: #7c5d12;
  --brown-face: #4a2c1c;

  --gold: #d9ae45;
  --green: #5fbf63;
  --red: #e05353;

  --shadow-1: none;
  --shadow-2: 0 4px 16px rgb(0 0 0 / 0.6);
  --shadow-3: 0 18px 48px rgb(0 0 0 / 0.8);

  /* الارتفاع في الليل يُقال بالفتحة لا بالظلّ وحده. الظلّ الأسود على أرضٍ
     سوداء لا يُرى، فلو اكتفينا به لبقيت الرقاقة بلا حافّة — وما لا حافّة له
     فوق خريطةٍ داكنة يُقرأ زجاجاً. فوجه الشريط ليلاً أفتح من أرضه بوضوح،
     والظلّ يأتي بعد ذلك ليعمّق ما حوله لا ليصنع الحدّ وحده.

     By night elevation is said with lightness rather than with shadow alone.
     A black shadow on black ground cannot be seen, and had we relied on it
     the chip would have kept no edge at all — and an edgeless thing over a
     dark map reads as glass. So the rail face by night is clearly lighter
     than its ground, and the shadow comes after that to deepen what
     surrounds it rather than to make the edge by itself. */
  --rail-face: #4a2c1c;
  --rail-face-hover: #5c3722;

  --lift: 0 2px 3px rgb(0 0 0 / 0.55), 0 6px 14px rgb(0 0 0 / 0.55);
  --lift-hover: 0 4px 8px rgb(0 0 0 / 0.62), 0 12px 26px rgb(0 0 0 / 0.6);
  --lift-press: 0 1px 2px rgb(0 0 0 / 0.65);

  color-scheme: dark;
}

/* الخريطة تُقرأ، والقراءة تحتاج تبايناً — والتباين هنا مسألة حساب لا ذوق.
   -------------------------------------------------------------------------
   مرّت هذه القاعدة بثلاثة أخطاء. أوّلها صبغةٌ ثقيلة (‎sepia 0.3‎ وتشبّعٌ فوق
   الواحد) أخرجت خريطة فاقعة تنازع دبابيسها على النظر. وثانيها ردُّ فعلٍ عليه:
   لمعانٌ مرفوع وتباينٌ مخفوض. وثالثها — وهو الذي كان هنا — ‎contrast(1.1)‎ ظنّاً
   أنّ رفع التباين يُظهر المعالم. وهو لا يفعل، بل يمحوها.

   وبيانُ ذلك بالقياس لا بالرأي: بلاطةُ ‎voyager_nolabels‎ فوق حيٍّ سكنيّ يقع ضوءُ
   بكسلاتها كلِّه بين ‎221‎ و‎255‎ — أربعٌ وثلاثون درجة من أصل ‎255‎، محشورةٌ كلُّها
   عند البياض. و‎contrast(k)‎ يدور حول المنتصف ‎0.5‎: فما فوقه يدفعه إلى الأبيض، وما
   تحته يدفعه إلى الأسود. وليس في هذه البلاطة بكسلٌ واحد تحت المنتصف — فكلُّ ما
   فعله ‎contrast(1.1)‎ أنّه دفع الأربعَ والثلاثين درجة نحو القصّ حتى صارت إحدى
   وعشرين. أي أنّ الأمر الذي وُضع ليزيد التباين أنقصه ثُلُثاً.

   ثمّ إنّ ‎voyager‎ لا يقول معالمه بالضوء أصلاً بل باللون: الحدائق خضراء والشوارع
   الكبرى ذهبية والماء أزرق والأرض كريميّة والطرق بيضاء — متقاربةٌ في الضوء
   متباعدةٌ في الصبغة. فجاء ‎saturate(0.62)‎ فطرح أربعين في المئة من القناة الوحيدة
   التي تحمل الخبر. فاجتمع على الخريطة محوٌ من جهتين، ولم يبقَ إلا ورقٌ كريميٌّ لا
   معلم فيه — وهو الذي كان يُشتكى منه.

   والصواب أن يُنزَل الضوء أوّلاً إلى حيث يعمل ‎contrast‎ — أي نحو المنتصف — ثمّ
   يُوسَّع، وأن تُردّ الصبغة لا أن تُطرح:

     brightness(0.85)  ينزل بالمدى من [221,255] إلى ما حول المنتصف حيث للتباين أثر
     contrast(1.36)    يوسّعه هناك حقيقةً لا في اتجاه القصّ
     saturate(1.16)    يردّ القناة التي فيها الخبر، وكريميُّ الخريطة وذهبُها هما
                       لونا البوّابة أصلاً فلا غربة بينهما

   والقياس بعدها: طاقةُ الحواف — مقدارُ ما يفرّق بكسلاً عن جاره، وهو المعنى
   الحسابيّ لِـ«أرى شيئاً» — ارتفعت من ‎1492‎ إلى ‎3232‎ على البلاطات نفسها.

   The map is there to be read, and reading needs contrast — which here is a
   matter of arithmetic rather than taste.

   This rule has been wrong three times. First a heavy tint (sepia 0.3, high
   saturation) that produced a garish map fighting its own pins. Then an
   overcorrection with brightness up and contrast down. Then the third — the one
   that stood here — contrast(1.1), on the assumption that raising contrast
   reveals detail. It does not; it erases it.

   Measured rather than judged: a voyager_nolabels tile over a residential
   district has its entire pixel range between 221 and 255 — thirty-four levels
   out of 255, all crowded against white. contrast(k) pivots on 0.5, pushing
   whatever is above it toward white and whatever is below toward black. Not one
   pixel in this tile sits below the pivot, so all contrast(1.1) did was push
   those thirty-four levels into the white clip until twenty-one were left. The
   instruction meant to add contrast removed a third of it.

   And voyager does not state its features in luminance at all, but in hue:
   parks green, arterials gold, water blue, land cream, roads white — near
   neighbours in light, far apart in colour. So saturate(0.62) discarded forty
   percent of the one channel carrying the information. Erased from both sides
   at once, nothing was left but the featureless cream paper that was complained
   of.

   The fix is to bring luminance down to where contrast can act — toward the
   pivot — and then expand, and to give the hue back rather than take it:

     brightness(0.85)  drops [221,255] to around the pivot, where contrast bites
     contrast(1.36)    expands it there in earnest, not into the clip
     saturate(1.16)    restores the channel that carries the news; the map's
                       cream and gold are the portal's own colours anyway

   Measured after: edge energy — how much one pixel differs from its neighbour,
   the arithmetic meaning of "I can see something" — rose from 1492 to 3232 on
   the same tiles.

   والليل معكوسُ ذلك حرفاً بحرف. بعد ‎invert‎ يصير المدى كلُّه بين ‎0‎ و‎34‎ — تحت
   المنتصف كلِّه — فيصير ‎contrast(k>1)‎ هناك قاصّاً إلى السواد لا موسِّعاً، تماماً
   كما كان قاصّاً إلى البياض في النهار. فاللمعان هو الذي يرفع، والتباين يبقى دون
   الواحد قليلاً كي لا يُسحق أدكنُ الدرجات إلى صفر. وطاقةُ الحواف ليلاً: من ‎2305‎
   إلى ‎6161‎.

   Night is the same argument letter for letter, inverted. After invert(1) the
   whole range lies between 0 and 34 — entirely below the pivot — so there
   contrast(k>1) crushes to black exactly as it clipped to white by day. It is
   brightness that lifts, and contrast stays a shade under one so the darkest
   levels are not crushed to zero. Edge energy by night: 2305 -> 6161. */
.portal-theme .mapboxgl-canvas {
  filter: brightness(0.85) contrast(1.36) saturate(1.16);
}
.portal-theme[data-theme="dark"] .mapboxgl-canvas {
  filter: invert(1) hue-rotate(180deg) saturate(0.42) brightness(2.4) contrast(0.92);
}

/* والفراغ خلف البلاطات بلونها نفسه بعد المرشِّح لا قبله.
   -------------------------------------------------------------------------
   وهذا موضعٌ يسهل فيه الغلط: الخلفية ليست داخل ‎.mapboxgl-canvas‎ فلا يمسّها
   المرشِّح، فلو كُتبت بلون البلاطة الخام لظهرت أدكنَ من الخريطة التي فوقها ولومض
   الفرقُ بين بلاطةٍ وأخرى. فالمكتوب هنا أرضُ ‎voyager‎ بعد المرشِّح لا قبله:
   ‎#fbf8f3‎ تصير ‎#f5f1ea‎ نهاراً و‎#1c1914‎ ليلاً.

   The ground behind the tiles is their own colour after the filter, not before.
   An easy place to slip: the backdrop is not inside .mapboxgl-canvas so no
   filter touches it, and writing it in the raw tile's colour would leave it
   visibly darker than the map above it, flashing between one tile and the next.
   What is written here is voyager's land as the filter leaves it — #fbf8f3
   becomes #f5f1ea by day and #1c1914 by night. */
.portal-theme .gm,
.portal-theme .mapboxgl-map { background: #f5f1ea; }
.portal-theme[data-theme="dark"] .gm,
.portal-theme[data-theme="dark"] .mapboxgl-map { background: #1c1914; }

/* صورة القمر الصناعي وحدها بلا مرشّح.
   -------------------------------------------------------------------------
   وهذا استثناء معلَن لا سهو. البلاطات المرسومة تُصبغ لأنها رسمٌ نحن من يقرّر
   ألوانه، أما صورة الأرض فليست رسماً: تجريدها من لونها يمحو ما جاءت من أجله —
   البحر من الرمل، والمزروع من العاري، والمبنيّ من الفضاء. فمن اختار «القمر
   الصناعي» طلب الصورة كما هي، وإعطاؤه إياها رماديةً ردٌّ لطلبه لا تنفيذ له.

   The satellite imagery alone, with no filter.

   A declared exception rather than an oversight. Drawn tiles are tinted because
   a drawing is something whose colours we decide; a photograph of the ground is
   not a drawing, and stripping its colour erases the very thing it was asked
   for — sea from sand, planted from bare, built from empty. Whoever chose
   "satellite" asked for the picture as it is, and handing it to them in grey is
   a refusal of the request rather than an answer to it.

   واللوحة لا تُخترق بذلك: الاستثناء داخل .portal-theme وحدها، وشرطه
   data-base="satellite" — فلوحة الإدارة لا تعرض هذا النوع أصلاً ولا يصلها منه
   شيء.

   The palette is not breached by it: the exception lives inside .portal-theme
   alone under data-base="satellite" — the admin console does not offer this
   tile type at all and nothing of it reaches there. */
.portal-theme .gm[data-base="satellite"] .mapboxgl-canvas,
.portal-theme[data-theme="dark"] .gm[data-base="satellite"] .mapboxgl-canvas {
  filter: none;
}

.portal-theme .gm[data-base="satellite"],
.portal-theme .gm[data-base="satellite"] .mapboxgl-map { background: #223022; }

/* وفوق صورةٍ لا نتحكّم في درجاتها لا تكفي درجةُ لونٍ للنص: تُعطى الأسماء هالةً
   داكنة تحملها فوق الرمل الفاتح والبحر الغامق سواء.

   Over a picture whose values we do not control, a colour alone will not carry
   text: the names are given a dark halo that holds them over pale sand and dark
   sea alike. */
.portal-theme .gm[data-base="satellite"] .gm-label {
  color: #fff;
  text-shadow: 0 0 3px rgb(0 0 0 / 0.95), 0 1px 4px rgb(0 0 0 / 0.85);
}

/* --------------------------------------------------- Shell without scripts */
/* بلا جافاسكربت لا تكون البوابة تطبيقاً بارتفاع الشاشة: تصير مستنداً يتمرّر
   عادةً. لولا ذلك لبقي الدرج وشريط الأدوات خارج ما يُرى، مقصوصَين بارتفاعٍ
   مفروضٍ لا يستطيع الزائر تجاوزه — أي أن نصف الصفحة يختفي لا لشيء إلا لغياب
   سكربت لا يحتاجه شيء منها.

   With no JavaScript the portal is not a viewport-height application but an
   ordinary scrolling document. Otherwise the drawer and the dock would sit
   outside what is visible, clipped by an imposed height the visitor cannot get
   past — half the page gone for want of a script none of it needs. */
:root:not(.js) body { overflow: auto; }
:root:not(.js) .pt { height: auto; overflow: visible; }
:root:not(.js) .pt-main { overflow: visible; }
:root:not(.js) .pt-canvas { position: static; }
:root:not(.js) .pt-canvas .gm-wrap { position: relative; height: 60dvh; }
:root:not(.js) .pt-rail { position: static; flex-wrap: wrap; padding: var(--sp-3) var(--sp-4); }
:root:not(.js) .pt-dock,
:root:not(.js) .pt-canvas .pt-note {
  position: static;
  transform: none;
  margin: var(--sp-3) auto;
  width: fit-content;
}
:root:not(.js) .pt-panel-results {
  position: static;
  width: auto;
  max-height: none;
  margin: var(--sp-3);
}

/* ---------------------------------------------------------------- Shell */
.pt {
  display: flex;
  flex-direction: column;
  height: 100dvh;
  width: 100%;
  overflow: hidden;
  background: var(--bg);
}

.pt-main {
  flex: 1;
  min-height: 0;
  position: relative;
}

.pt[data-mode="map"] .pt-main { overflow: hidden; }

/* ‎scrollbar-gutter‎ يحجز خانة الشريط دائماً، طالت الصفحة أم قصرت.

   وهو نصف المشكلة الثاني: المجرى هو ‎.pt-main‎ لا ‎body‎، فصفحةٌ أطول من الشاشة
   تُظهر شريط تمرير فتفقد خمسة عشر بكسلاً من عرضها، وصفحةٌ أقصر منها لا تُظهره
   فتحتفظ بها. فيتزحزح كل ما هو متوسّطٌ فيها — والتذييل أظهرُها لأنه سطحٌ ممتدّ
   بحدٍّ علويّ — بين صفحةٍ وأخرى، ومع كل تصفية تُقصّر النتائج أو تُطيلها. وحجز
   الخانة يجعل العرض واحداً في الحالتين.

   scrollbar-gutter reserves the bar's slot at all times, whether the page runs
   long or short.

   That is the problem's second half: the stream is .pt-main rather than body,
   so a page taller than the viewport shows a scrollbar and loses fifteen pixels
   of its width, while a shorter one does not and keeps them. Everything centred
   inside shifts between one page and the next — the footer most visibly, being
   a full-width surface with a rule along its top — and with every filter that
   shortens or lengthens the results. Reserving the slot makes the width one and
   the same in both cases. */
.pt[data-mode="page"] .pt-main { overflow: auto; scrollbar-gutter: stable; }

/* -------------------------------------------------------------- Top bar */
.pt-top {
  flex: none;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--sp-3);
  height: 58px;
  padding-inline: var(--sp-4);
  border-block-end: 1px solid var(--line);
  background: var(--surface);
  position: relative;
  z-index: 40;
}

.pt-brand {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  justify-self: start;
}
/* الشعار على الشفافية، وظلٌّ وحده يفصله عن السطح — لا مربّع ذهبيّ ولا لوح أبيض.

   وdrop-shadow لا box-shadow: الثاني يظلّل صندوق الصورة فيرسم مستطيلاً حول
   شعارٍ شفّاف، والأول يقرأ قناة الشفافية فيتبع حرف الـZ وسقف البيت وحدهما.

   وسطح البوابة رمليّ لا أبيض، فالظلّ بنّيّ دافئ لا رماديّ: الظلّ الرماديّ على
   الرمليّ يبدو وسخاً، والدافئ يبدو ظلّاً.

   The logo on transparency, separated from the surface by a shadow alone — no
   gold square and no white plate.

   drop-shadow rather than box-shadow: the latter shadows the image's box and so
   draws a rectangle around a transparent logo, while the former reads the alpha
   channel and follows the Z's stroke and the house's roof and nothing else.

   The portal's surface is sand rather than white, so the shadow is a warm brown
   rather than a grey: a grey shadow on sand reads as dirt, a warm one reads as
   a shadow. */
.pt-brand-logo {
  flex: none;
  display: block;
  width: auto;
  height: 36px;
  object-fit: contain;
  filter: drop-shadow(0 1px 1px rgba(58, 38, 12, 0.3))
          drop-shadow(0 3px 6px rgba(58, 38, 12, 0.22));
}
.portal-theme[data-theme="dark"] .pt-brand-logo {
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.55))
          drop-shadow(0 0 10px rgba(226, 178, 74, 0.28));
}

/* التبويبات لا يحيط بها مربَّع. القسم القائم يُعرَف بخطٍّ تحت كلمته وبلون
   الكلمة نفسها: بنّي محروق في النهار، وذهبٌ فاتح في الليل. وما ليس قائماً
   يُكتب حبراً كاملاً لا باهتاً — فالتبويب الخامل كلمةٌ تُقرأ لا ظلُّ كلمة.

   No box encloses the tabs. The section one stands in is known by a line
   under its word and by the colour of that word: burnt brown by day, pale
   gold by night. What is not current is written in full ink, never faded —
   a resting tab is a word to be read, not the ghost of one. */
.pt-tabs {
  --tab-ink: #201814;         /* حبر التبويب الخامل — the resting tab's ink */
  --tab-live: #8a3c14;        /* البنّي المحروق — the burnt brown */
  --tab-hint: rgb(138 60 20 / 0.34);

  /* المقاس يكبر مع الشاشة: الشريط العلوي في شاشةٍ عريضة يتحمّل كلمةً أكبر
     ورمزاً أوضح. The measure grows with the screen: on a wide display the
     top bar can carry a larger word and a clearer glyph. */
  --tab-size: 14px;
  --tab-h: 34px;
  --tab-pad: 12px;
  --tab-gap: 7px;
  --tab-ico: 16px;
  --tab-rule: 2px;

  display: flex;
  gap: 6px;
  justify-self: center;
  padding: 0;
  border-radius: 0;
  background: none;
}
.pt-tab {
  display: inline-flex;
  align-items: center;
  gap: var(--tab-gap);
  height: var(--tab-h);
  padding-inline: var(--tab-pad);
  border-radius: 0;
  background: none;
  font-size: var(--tab-size);
  font-weight: 600;
  color: var(--tab-ink);
  white-space: nowrap;
  transition: color var(--dur) var(--ease);
}
.pt-tab svg,
.pt-tab .ico { flex: none; width: var(--tab-ico); height: var(--tab-ico); }
.pt-tab span {
  padding-block-end: 3px;
  border-block-end: var(--tab-rule) solid transparent;
  transition: border-color var(--dur) var(--ease);
}
.pt-tab:hover { color: var(--tab-live); background: none; }
.pt-tab:hover span { border-block-end-color: var(--tab-hint); }
.pt-tab[aria-current="page"] { background: none; color: var(--tab-live); font-weight: 700; }
.pt-tab[aria-current="page"] span { border-block-end-color: var(--tab-live); }
.pt-tab:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; border-radius: 4px; }

/* في الليل يصير الحبر رملاً فاتحاً والقائم ذهباً — فالبنّي المحروق لا يُقرأ
   على سطحٍ داكن. In the dark the ink becomes pale sand and the current tab
   gold — burnt brown cannot be read on a dark surface. */
.portal-theme[data-theme="dark"] .pt-tabs {
  --tab-ink: var(--fg-1);
  --tab-live: var(--gold);
  --tab-hint: rgb(217 174 69 / 0.38);
}

@media (min-width: 1280px) {
  .pt-tabs { --tab-size: 15px; --tab-h: 36px; --tab-pad: 14px; --tab-gap: 8px; --tab-ico: 17px; gap: 7px; }
}
@media (min-width: 1600px) {
  .pt-tabs { --tab-size: 16px; --tab-h: 38px; --tab-pad: 15px; --tab-gap: 8px; --tab-ico: 18px; gap: 8px; }
}

.pt-actions {
  display: flex;
  align-items: center;
  gap: 7px;
  justify-self: end;
}

.pt-action,
.pt-rail-btn,
.pt-dock-btn,
.pt-chip-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 32px;
  padding-inline: 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--surface);
  color: var(--fg-1);
  font-size: 12.5px;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.pt-action:hover,
.pt-rail-btn:hover,
.pt-dock-btn:hover,
.pt-chip-btn:hover { background: var(--surface-2); border-color: var(--line-strong); }

.pt-action--ghost { border-color: transparent; background: transparent; color: var(--fg-2); }
/* المساعدُ قبل تشغيله: باهتٌ ويبقى قابلاً للتركيز ليُقرأ ‎title‎ و«قريباً».
   The assistant before it runs: muted, still focusable so its title and "soon" read. */
.pt-assistant[aria-disabled="true"] { opacity: 0.6; cursor: help; }

/* المساعد الذكي — لوحةٌ تحت زرّه في الرأس، عند طرف السطر الأخير.
   ــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــ
   على الشاشة الواسعة لوحةٌ مدمجة تحت الرأس لا تغطّي إلا ما تحتها، وعلى الهاتف
   تملأ ما بين الرأس وشريط الأقسام ولا تغطّي أيّاً منهما. والرسائلُ نصٌّ يلتفّ
   ولا يتجاوز، وسجلُّها يتمرّر وحده.

   The AI assistant — a panel beneath its button in the header, at the line's end.

   On a wide screen a compact panel under the header covering only what lies below
   it; on a phone it fills what is between the header and the section bar, covering
   neither. Messages are text that wraps rather than overflowing, and their log
   scrolls on its own. */
.pt-chat {
  position: fixed;
  z-index: 1300;
  inset-block-start: 66px;
  inset-inline-end: var(--sp-4);
  /* أوسعُ وأطول ممّا كانت (400×600)، ولا تتجاوز الشاشة: محادثةٌ تُقرأ وبطاقاتٌ وخريطة.
     Wider and taller than it was (400×600), never past the screen: a conversation that
     reads, with cards and a map. */
  width: min(560px, calc(100vw - 2 * var(--sp-4)));
  height: min(780px, calc(100dvh - 82px));
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-lg);
  background: var(--bg);
  color: var(--fg-1);
  box-shadow: var(--shadow-3);
  overflow: hidden;
}
.pt-chat[hidden] { display: none; }
.pt-chat-head {
  flex: none; display: flex; align-items: center; gap: var(--sp-2);
  padding: 10px var(--sp-3); border-block-end: 1px solid var(--line); background: var(--surface);
}
.pt-chat-mark {
  flex: none; display: grid; place-items: center; width: 30px; height: 30px;
  border-radius: var(--r-md); background: var(--gold-face); color: var(--on-gold);
}
.pt-chat-titles { flex: 1; min-width: 0; }
.pt-chat-titles h2 { margin: 0; font-size: 14px; font-weight: 700; }
.pt-chat-titles p { margin: 0; font-size: 11.5px; color: var(--fg-3); }
.pt-chat-log {
  flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
  display: flex; flex-direction: column; gap: 8px; padding: var(--sp-3);
}
.pt-chat-empty { margin: auto; text-align: center; font-size: 14px; color: var(--fg-2); }
.pt-chat-msg {
  max-width: 85%; margin: 0; padding: 9px 13px; border-radius: var(--r-md);
  font-size: 14px; line-height: 1.65; white-space: pre-wrap; overflow-wrap: anywhere;
}
/* وكلُّ سطرٍ يأخذ اتجاهَه من أوّل حرفٍ فيه: فرقمُ «‎+971…‎» لا ينقلب إشارتُه إلى آخره في العربية.
   Each line takes its direction from its own first letter: so a "+971…" number does not
   have its sign flipped to its end in Arabic. */
.pt-chat-msg, .pt-chat-card-sub { unicode-bidi: plaintext; text-align: left; }
[dir="rtl"] .pt-chat-msg, [dir="rtl"] .pt-chat-card-sub { text-align: right; }
.pt-chat-msg--you { align-self: flex-end; background: var(--gold-face); color: var(--on-gold); }
.pt-chat-msg--bot { align-self: flex-start; border: 1px solid var(--line); background: var(--surface); }
.pt-chat-msg--thinking { color: var(--fg-3); }
.pt-chat-error {
  flex: none; display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  margin: 0 var(--sp-3) var(--sp-2); padding: 8px 10px;
  border: 1px solid var(--red); border-radius: var(--r-md); font-size: 12.5px; color: var(--red);
}
.pt-chat-error span { flex: 1 1 160px; min-width: 0; }
.pt-chat-error[hidden], .pt-chat-error .btn[hidden] { display: none; }
.pt-chat-form {
  flex: none; display: flex; align-items: flex-end; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3) var(--sp-3); border-block-start: 1px solid var(--line); background: var(--surface);
}
.pt-chat-form textarea {
  flex: 1; min-width: 0; min-height: 40px; max-height: 140px; resize: none;
  padding: 9px 12px; border: 1px solid var(--line-strong); border-radius: var(--r-md);
  background: var(--bg); color: var(--fg-1); font: inherit; font-size: 14px; line-height: 1.5;
}
.pt-chat-form textarea:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; }
.pt-chat-send { flex: none; width: 40px; height: 40px; padding: 0; justify-content: center; }
.pt-chat-send[aria-busy="true"] { opacity: 0.6; cursor: progress; }
/* زرُّ المحادثة الصوتية: لا يُرى بلا سكربت، ويحمرّ وهو يسمع أو يقرأ، وأيقونتُه «إيقاف» حينها.
   The voice button: unseen without a script, red while it listens or reads, its icon
   "stop" meanwhile. */
.pt-chat-form .pt-chat-mic { display: none; flex: none; width: 40px; height: 40px; padding: 0; justify-content: center; align-items: center; }
.js .pt-chat-form .pt-chat-mic { display: inline-flex; }
.pt-chat-mic-on, .pt-chat-mic-off { display: inline-flex; }
.pt-chat-mic-off { display: none; }
.pt-chat-mic[data-state="listening"], .pt-chat-mic[data-state="thinking"], .pt-chat-mic[data-state="speaking"] { border-color: var(--red); color: var(--red); }
.pt-chat-mic[data-state="listening"] .pt-chat-mic-on, .pt-chat-mic[data-state="thinking"] .pt-chat-mic-on, .pt-chat-mic[data-state="speaking"] .pt-chat-mic-on { display: none; }
.pt-chat-mic[data-state="listening"] .pt-chat-mic-off, .pt-chat-mic[data-state="thinking"] .pt-chat-mic-off, .pt-chat-mic[data-state="speaking"] .pt-chat-mic-off { display: inline-flex; }
.pt-chat-mic[data-state="listening"] { animation: pt-chat-listening 1.2s ease-in-out infinite; }
@keyframes pt-chat-listening { 50% { box-shadow: 0 0 0 4px color-mix(in srgb, var(--red) 25%, transparent); } }
@media (prefers-reduced-motion: reduce) { .pt-chat-mic[data-state="listening"] { animation: none; } }
.pt-chat-guestnote {
  flex: none; margin: 0; padding: 8px var(--sp-3);
  border-block-end: 1px solid var(--line-soft, var(--line)); background: var(--surface-1, var(--surface));
  font-size: 12px; line-height: 1.5; color: var(--fg-2);
}
.pt-chat-guestnote a { font-weight: 600; white-space: nowrap; }

/* مصادرُ الجواب القانونيّ: سطرٌ صغيرٌ تحت الجواب بالقانون والمادة، كلٌّ منها رابطٌ إلى نصّه الرسميّ.
   A legal answer's sources: a small line under the answer with the law and the article,
   each a link to its official text. */
.pt-chat-sources { align-self: flex-start; max-width: 100%; min-width: 0; padding-inline-start: 10px; border-inline-start: 2px solid var(--line); font-size: 12px; line-height: 1.6; color: var(--fg-3); }
.pt-chat-sources-title { margin: 0 0 2px; font-weight: 600; color: var(--fg-2); }
.pt-chat-sources ul { margin: 0; padding: 0; list-style: none; }
.pt-chat-sources li { overflow-wrap: anywhere; }
.pt-chat-sources a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }

/* نتائجُ البحث داخل المحادثة: بطاقاتٌ مضغوطة بروابطها، وخريطةٌ دبابيسُها النتائجُ نفسُها.
   The search's results inside the conversation: compact cards with their links, and a
   map whose pins are those very results. */
.pt-chat-results { display: grid; gap: 8px; align-self: stretch; min-width: 0; }
.pt-chat-count { margin: 0; font-size: 12px; color: var(--fg-3); }
.pt-chat-cards { display: grid; gap: 8px; margin: 0; padding: 0; list-style: none; }

/* البطاقة: مكانُ صورةٍ ثابتُ القياس (صورةٌ أو بديلُها)، ثم نوعٌ وعنوانٌ لا يتجاوز سطرين
   ومكان، ثم الحقائقُ رقاعاً، ثم السعرُ والرابطُ في ذيلٍ مفصول. وكلُّها بخواصّ منطقية،
   فتنعكس في العربية وحدها.
   The card: an image place of fixed size (the image or its stand-in), then a kind, a
   title of two lines at most and a place, then the facts as chips, then the price and
   the link in a separated foot. All on logical properties, so Arabic mirrors itself. */
.pt-chat-card {
  display: grid; grid-template-columns: 96px minmax(0, 1fr); gap: 12px; min-width: 0;
  padding: 10px; cursor: pointer;
  border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface);
  transition: border-color var(--dur, .15s) var(--ease, ease), box-shadow var(--dur, .15s) var(--ease, ease);
}
.pt-chat-card:hover { border-color: var(--line-strong); }
.pt-chat-card:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; }
.pt-chat-card.is-selected { border-color: var(--gold); box-shadow: 0 0 0 1px var(--gold); }
.pt-chat-card-media {
  position: relative; display: block; width: 96px; height: 80px; overflow: hidden;
  border-radius: var(--r-sm); background: var(--surface-2);
}
.pt-chat-card-media:empty::after {
  content: ""; position: absolute; inset: 0; margin: auto; width: 26px; height: 26px; opacity: 0.35;
  background: currentColor; color: var(--fg-3);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2'%3E%3Cpath d='M3 10.5 12 3l9 7.5V21H3z'/%3E%3Cpath d='M9 21v-6h6v6'/%3E%3C/svg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2'%3E%3Cpath d='M3 10.5 12 3l9 7.5V21H3z'/%3E%3Cpath d='M9 21v-6h6v6'/%3E%3C/svg%3E") center / contain no-repeat;
}
.pt-chat-card-img { display: block; width: 100%; height: 100%; object-fit: cover; }
.pt-chat-card-info { min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.pt-chat-card-head { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.pt-chat-card-kind {
  padding: 1px 7px; border: 1px solid var(--gold); border-radius: var(--r-pill);
  font-size: 10.5px; font-weight: 600; line-height: 1.6; color: var(--gold); white-space: nowrap;
}
.pt-chat-card-title {
  font-size: 14px; font-weight: 650; line-height: 1.45; overflow-wrap: anywhere;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
}
.pt-chat-card-sub { font-size: 12px; color: var(--fg-3); overflow-wrap: anywhere; }
.pt-chat-card-facts { display: flex; flex-wrap: wrap; gap: 4px; }
.pt-chat-card-fact {
  padding: 1px 7px; border-radius: var(--r-pill); background: var(--surface-2);
  font-size: 11.5px; line-height: 1.6; color: var(--fg-2); white-space: nowrap;
}
.pt-chat-card-foot {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 6px;
  margin-block-start: auto; padding-block-start: 6px; border-block-start: 1px dashed var(--line);
}
.pt-chat-card-price { font-size: 14px; font-weight: 700; color: var(--green); white-space: nowrap; }
.pt-chat-card-price, .pt-chat-pop-price { display: inline-flex; align-items: baseline; gap: 4px; }
.pt-chat-per { font-size: 11px; font-weight: 600; color: var(--fg-3); }
.pt-chat-card-link { font-size: 12.5px; font-weight: 600; white-space: nowrap; }
.pt-chat-card-nopin { font-size: 11px; color: var(--fg-3); }
.pt-chat-map { height: 280px; min-height: 280px; border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden; }
.pt-chat-map[hidden], .pt-chat-map-note[hidden] { display: none; }
.pt-chat-map-note { margin: 0; font-size: 12px; color: var(--fg-3); }
/* دبوسُ المحادثة بطاقةٌ صغيرةٌ برأسٍ مدبَّب تكتب السعر — بحبر هوية البوابة كدبابيس خريطتها،
   والمختارُ مصمَتٌ ويعلو جيرانَه. والتحويلُ على البطاقة الداخلية: Mapbox يكتب موضعَ الزرّ
   نفسه في ‎transform‎.
   The conversation's pin is a small tag with a point beneath it that writes the price —
   in the portal's identity ink like its map's pins, the selected one solid and raised
   above its neighbours. The transform sits on the inner tag: Mapbox writes the button's
   own position into its transform. */
.pt-chat-pin { padding: 0; border: 0; background: none; cursor: pointer; font: inherit; }
.pt-chat-pin:hover, .pt-chat-pin:focus-visible, .pt-chat-pin.is-selected { z-index: 3; }
.pt-chat-pin.is-open { visibility: hidden; }
.pt-chat-pin:focus-visible { outline: none; }
.pt-chat-tag {
  position: relative; display: inline-flex; align-items: center; gap: 3px;
  height: 26px; padding-inline: 8px;
  border: 1px solid var(--cocoa, var(--fg-1)); border-radius: var(--r-sm);
  background: var(--surface); color: var(--fg-1);
  font-size: 12px; font-weight: 700; line-height: 1; white-space: nowrap;
  box-shadow: 0 1px 5px rgb(44 20 3 / 0.28);
  transition: transform 0.16s var(--ease, ease);
}
.pt-chat-tag::after {
  content: ""; position: absolute; inset-block-start: 100%; left: 50%; transform: translateX(-50%);
  border: 4px solid transparent; border-block-start-color: var(--cocoa, var(--fg-1));
}
.pt-chat-tag b { display: inline-flex; align-items: center; gap: 2px; font-weight: 700; font-variant-numeric: tabular-nums; }
.pt-chat-tag small { font-size: 9.5px; font-weight: 600; opacity: 0.8; }
.pt-chat-tag .aed { width: 0.86em; height: 0.74em; }
.pt-chat-pin:hover .pt-chat-tag, .pt-chat-pin:focus-visible .pt-chat-tag { transform: translateY(-2px); }
.pt-chat-pin:focus-visible .pt-chat-tag { outline: 2px solid var(--gold); outline-offset: 2px; }
.pt-chat-pin.is-selected .pt-chat-tag { background: var(--cocoa, var(--fg-1)); color: var(--sand, #fff); }
[data-theme="dark"] .pt-chat-tag { border-color: var(--sand, #fff); color: #fff; }
[data-theme="dark"] .pt-chat-tag::after { border-block-start-color: var(--sand, #fff); }
[data-theme="dark"] .pt-chat-pin.is-selected .pt-chat-tag { background: #4a2c1c; }

/* اسمُ الإمارة: الكتابةُ الوحيدة على الخريطة غيرَ الإعلانات — نصٌّ بهالةٍ لا يُضغط ويبقى تحتها.
   The emirate's name: the only writing on the map besides the listings — haloed text
   that takes no press and stays beneath them. */
.pt-chat-place {
  z-index: 1; pointer-events: none;
  font-size: 13px; font-weight: 700; color: var(--fg-1); white-space: nowrap;
  text-shadow: 0 0 3px var(--bg), 0 0 3px var(--bg), 0 0 6px var(--bg);
}

/* زرُّ الشاشة الكاملة بين أزرار الخريطة: سهمان إلى الخارج، وإلى الداخل حين تكون كاملة.
   The full-screen button among the map's buttons: two arrows outwards, inwards while
   the map is full. */
.pt-chat-full { position: relative; }
.pt-chat-full::before {
  content: ""; position: absolute; inset: 0; margin: auto; width: 16px; height: 16px; background: #333;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7'/%3E%3C/svg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7'/%3E%3C/svg%3E") center / contain no-repeat;
}
.pt-chat-full[aria-pressed="true"]::before {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 14h6v6M20 10h-6V4M14 10l7-7M3 21l7-7'/%3E%3C/svg%3E");
          mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 14h6v6M20 10h-6V4M14 10l7-7M3 21l7-7'/%3E%3C/svg%3E");
}

/* الخريطةُ الكاملة تملأ الشاشة تحت لوحة المحادثة، فتبقى اللوحةُ فوقها تسأل وتجيب، والصفحةُ
   خلفها لا تتمرّر.
   The full map fills the screen beneath the conversation panel, so the panel stays above
   it asking and answering, and the page behind it does not scroll. */
.pt-chat-map.is-full {
  position: fixed; inset: 0; z-index: 1290;
  height: auto; min-height: 0; border: 0; border-radius: 0;
}
html.pt-chat-mapfull, html.pt-chat-mapfull body { overflow: hidden; }
/* بطاقةُ الدبوس على الخريطة: إطارُ النافذة من رموز البوابة لا من Mapbox، والصورةُ بقياسٍ ثابت،
   والعنوانُ سطران على الأكثر، والذيلُ سعرٌ وزرّ. والاتجاهُ اتجاهُ الصفحة.
   The pin's card on the map: the popup's frame from the portal's tokens rather than
   Mapbox's, a fixed-size image, a title of two lines at most, a foot of a price and a
   button. Its direction is the page's. */
.pt-chat-popup .mapboxgl-popup-content {
  padding: 0; overflow: hidden;
  border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--surface); color: var(--fg-1); box-shadow: var(--shadow-3);
  font: inherit;
}
.pt-chat-popup.mapboxgl-popup-anchor-top .mapboxgl-popup-tip,
.pt-chat-popup.mapboxgl-popup-anchor-top-left .mapboxgl-popup-tip,
.pt-chat-popup.mapboxgl-popup-anchor-top-right .mapboxgl-popup-tip { border-bottom-color: var(--surface); }
.pt-chat-popup.mapboxgl-popup-anchor-bottom .mapboxgl-popup-tip,
.pt-chat-popup.mapboxgl-popup-anchor-bottom-left .mapboxgl-popup-tip,
.pt-chat-popup.mapboxgl-popup-anchor-bottom-right .mapboxgl-popup-tip { border-top-color: var(--surface); }
.pt-chat-popup.mapboxgl-popup-anchor-left .mapboxgl-popup-tip { border-right-color: var(--surface); }
.pt-chat-popup.mapboxgl-popup-anchor-right .mapboxgl-popup-tip { border-left-color: var(--surface); }
.pt-chat-popup .mapboxgl-popup-close-button {
  inset-block-start: 6px; inset-inline-end: 6px; inset-inline-start: auto;
  width: 24px; height: 24px; padding: 0; z-index: 1;
  border-radius: var(--r-circle); background: rgb(0 0 0 / 0.45); color: #fff; font-size: 16px; line-height: 24px;
}
.pt-chat-popup .mapboxgl-popup-close-button:hover { background: rgb(0 0 0 / 0.6); }
.pt-chat-pop {
  display: grid; grid-template-columns: 72px minmax(0, 1fr); gap: 10px;
  width: 264px; max-width: 100%; padding: 10px; font-size: 12.5px; line-height: 1.45;
}
.pt-chat-pop-media {
  position: relative; display: block; width: 72px; height: 72px; overflow: hidden;
  border-radius: var(--r-sm); background: var(--surface-2);
}
.pt-chat-pop-media img { display: block; width: 100%; height: 100%; object-fit: cover; }
.pt-chat-pop-media:empty::after {
  content: ""; position: absolute; inset: 0; margin: auto; width: 24px; height: 24px; opacity: 0.35;
  background: currentColor; color: var(--fg-3);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2'%3E%3Cpath d='M3 10.5 12 3l9 7.5V21H3z'/%3E%3Cpath d='M9 21v-6h6v6'/%3E%3C/svg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2'%3E%3Cpath d='M3 10.5 12 3l9 7.5V21H3z'/%3E%3Cpath d='M9 21v-6h6v6'/%3E%3C/svg%3E") center / contain no-repeat;
}
.pt-chat-pop-info { display: flex; flex-direction: column; align-items: flex-start; gap: 3px; min-width: 0; padding-inline-end: 18px; }
.pt-chat-pop-title {
  align-self: stretch; font-size: 13.5px; font-weight: 650; line-height: 1.4; overflow-wrap: anywhere;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
}
.pt-chat-pop-place { font-size: 12px; color: var(--fg-3); unicode-bidi: plaintext; }
.pt-chat-pop-facts { align-self: stretch; font-size: 11.5px; color: var(--fg-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pt-chat-pop-foot {
  grid-column: 1 / -1; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px;
  padding-block-start: 8px; border-block-start: 1px dashed var(--line);
}
.pt-chat-pop-price { font-size: 14px; font-weight: 700; color: var(--green); white-space: nowrap; }
.pt-chat-pop-link { white-space: nowrap; padding: 4px 12px; font-size: 12px; }
.pt-chat-more { justify-self: center; }
.pt-chat-actions { display: flex; flex-wrap: wrap; gap: 6px; align-self: flex-start; }
@media (max-width: 760px) {
  .pt-chat {
    inset-block-start: calc(48px + var(--pt-safe-t, 0px) + 6px);
    inset-block-end: calc(var(--pt-nav, 52px) + var(--pt-safe-b, 0px) + 6px);
    inset-inline: 8px;
    width: auto;
    height: auto;
  }
  .pt-chat-card { grid-template-columns: 80px minmax(0, 1fr); gap: 10px; padding: 8px; }
  .pt-chat-card-media { width: 80px; height: 72px; }
  .pt-chat-map { height: 240px; min-height: 240px; }
  .pt-chat-map.is-full { height: auto; min-height: 0; }

  /* وعلى الهاتف تنزل اللوحةُ إلى أسفل الشاشة ما دامت الخريطةُ كاملة، فيُرى فوقها ما بحثت عنه.
     On a phone the panel drops to the foot of the screen while the map is full, so what
     was searched for shows above it. */
  html.pt-chat-mapfull .pt-chat {
    inset-block-start: auto;
    inset-block-end: calc(var(--pt-safe-b, 0px) + 8px);
    height: min(46dvh, 420px);
  }
}

.pt-avatar {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border: 1px solid var(--line);
  border-radius: var(--r-circle);
  background: var(--surface);
  color: var(--fg-1);
  cursor: pointer;
}
.pt-avatar:hover { background: var(--surface-2); border-color: var(--gold); }

/* --------------------------------------------------------- Map surface */
.pt-canvas { position: absolute; inset: 0; }

/* الخريطة تملأ اللوحة: بلا حافة ولا استدارة ولا ارتفاع مفروض.
   The map fills the canvas: no border, no radius, no imposed height. */
.pt-canvas .gm-wrap {
  position: absolute;
  inset: 0;
  height: 100%;
  border: 0;
  border-radius: 0;
}

/* ضوابط الخريطة تنزل تحت شريط التصنيفات بدل أن تختفي خلفه.
   The map engine's own controls drop below the chip rail rather than hiding
   under it. */
.pt-canvas .mapboxgl-ctrl-top-left, .pt-canvas .mapboxgl-ctrl-top-right { margin-block-start: 54px; }

.pt-rail {
  position: absolute;
  inset-block-start: 10px;
  inset-inline: 10px;
  z-index: 1100;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  pointer-events: none;
}
.pt-rail > * { pointer-events: auto; }

.pt[data-mode="page"] .pt-rail {
  position: static;
  z-index: auto;
  flex-wrap: wrap;
  padding: var(--sp-4) var(--sp-5) 0;
}

.pt-rail-search {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  flex: none;
  max-width: 300px;
  height: 36px;
  padding-inline: 15px;
  border: 0;
  border-radius: var(--r-pill);
  background: var(--rail-face);
  box-shadow: var(--lift);
  cursor: pointer;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease), transform var(--dur) var(--ease);
  font-size: 12.5px;
  font-weight: 600;
  color: var(--fg-2);
}
.pt-rail-search:hover {
  background: var(--rail-face-hover);
  color: var(--fg-1);
  box-shadow: var(--lift-hover);
  transform: translateY(-1px);
}
.pt-rail-search:active { box-shadow: var(--lift-press); transform: translateY(1px); }
.pt-rail-search span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pt-rail-search i { font-style: normal; }
.portal-theme[data-theme="dark"] .pt-rail-search { color: #fff; }
.portal-theme[data-theme="dark"] .pt-rail-search:hover { color: #fff; }

/* والشريطُ نفسه يحمل حقلَ كتابةٍ حيث يكون في القسم بحث: الحبّة هي الحدّ،
   والحقلُ بلا إطارٍ ولا خلفيّةٍ داخلها — وإلّا قُرئ حقلاً فوق زرّ.

   The same pill carries a field to type in where the section has a search: the
   pill is the edge, and the field inside it has no border and no background of
   its own — otherwise it reads as a field on top of a button. */
.pt-rail-search:has(input) { cursor: text; flex: 1; max-width: 420px; }
.pt-rail-search input {
  flex: 1;
  min-width: 0;
  border: 0;
  padding: 0;
  background: none;
  font: inherit;
  color: inherit;
  outline: none;
}
.pt-rail-search input::placeholder { color: var(--fg-3); }

/* ولا زرَّ «امسح» من المتصفّح: يرسمه ويب-كِت داخل الحبّة بلونه هو، فيُقرأ
   شيئاً لا يخصّ الصفحة — والمسحُ يقع بإفراغ الحقل وإرساله.
   No browser clear button: WebKit draws its own inside the pill in its own
   colour, read as something that does not belong to the page — and clearing
   happens by emptying the field and sending it. */
.pt-rail-search input::-webkit-search-cancel-button { display: none; }

/* ------------------------------------------------------- The chip rail */
/* الشريط أعرض من مكانه دائماً، فيُسحَب بالفأرة كما يُسحَب باللمس. والمؤشّر هو
   ما يقول ذلك: كفٌّ مفتوحة، وتنقبض أثناء السحب.

   The rail is always wider than the space it has, so it is dragged with a
   mouse the way it is dragged with a finger. The cursor is what says so: an
   open hand that closes while dragging. */
.pt-chips {
  display: flex;
  gap: 6px;
  flex: 1;
  min-width: 0;
  overflow-x: auto;
  /* والشريط يُقصّ رأسياً لأنه يمرّر أفقياً، فيُترك للظلّ متّسعٌ داخله ثم
     يُسحب مثله من الخارج حتى لا يعلو الشريط. The rail clips vertically
     because it scrolls horizontally, so the shadow is given room inside and
     the same amount is pulled back outside, leaving the rail no taller. */
  padding-block: 10px;
  margin-block: -8px;
  scrollbar-width: none;
  cursor: grab;
  scroll-behavior: smooth;
}
.pt-chips::-webkit-scrollbar { display: none; }
.pt-chips[data-dragging="1"] {
  cursor: grabbing;
  scroll-behavior: auto;
  scroll-snap-type: none;
}

/* أثناء السحب لا يُختار نصّ ولا تُلتقط صورة رابط، وإلا شُدّت الرقاقة بدل
   الشريط. While dragging nothing is selected and no link is picked up as a
   drag image — otherwise the chip would move instead of the rail. */
.pt-chips[data-dragging="1"] * {
  user-select: none;
  pointer-events: none;
}

/* الرقاقة سطحٌ صلب لا زجاج: ما يُنقر يجب أن يكون له وجهٌ ثابت، لا ما وراءه
   ظاهرٌ من خلاله. وحدُّها ظلٌّ تحتها لا خطٌّ حولها — فالخطّ يرسم شكلاً،
   والظلّ يرفع جسماً.

   The chip is a solid surface rather than glass: what is pressed should have
   a face of its own instead of showing what lies behind it. Its edge is a
   shadow beneath it rather than a line around it — a line draws a shape, a
   shadow lifts a body. */
.pt-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: none;
  height: 32px;
  padding-inline: 13px;
  border: 0;
  border-radius: var(--r-pill);
  background: var(--rail-face);
  box-shadow: var(--lift);
  font-size: 12px;
  font-weight: 600;
  color: var(--fg-2);
  white-space: nowrap;
  cursor: pointer;
  -webkit-user-drag: none;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease),
              box-shadow var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.pt-chip:hover {
  background: var(--rail-face-hover);
  color: var(--fg-1);
  box-shadow: var(--lift-hover);
  transform: translateY(-1px);
}
/* وعند الضغط تنزل الرقاقة إلى مكانها ويقصر ظلّها — وهذا وحده ما يقول إنّها
   استُجيب لها. On press the chip settles back and its shadow shortens — which
   by itself says the press was received. */
.pt-chip:active {
  box-shadow: var(--lift-press);
  transform: translateY(1px);
}
.pt-chip[aria-pressed="true"] {
  background: var(--gold);
  color: var(--on-gold);
}
.pt-chip i {
  font-style: normal;
  font-size: 10.5px;
  color: var(--fg-3);
}
.pt-chip[aria-pressed="true"] i { color: rgb(44 20 3 / 0.65); }

.pt-rail-end { display: flex; align-items: center; gap: 6px; flex: none; }
.pt-inline-form { display: contents; }

.pt-select {
  height: 32px;
  padding-inline: 11px;
  border: 0;
  border-radius: var(--r-pill);
  background: var(--rail-face);
  box-shadow: var(--lift);
  color: var(--fg-1);
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  max-width: 160px;
  transition: background var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.pt-select:hover { background: var(--rail-face-hover); box-shadow: var(--lift-hover); }
/* والقائمة تتبع وجه الشريط لا لون النظام — the list follows the rail face,
   not the platform colour. */
.pt-select option,
.pt-select optgroup { background-color: var(--rail-face); color: var(--fg-1); }

/* وزرّ الشريط يتبع رقائقه: لا خطّ، ووجهٌ صلب، وظلٌّ يرفعه. The rail button
   follows its chips: no line, a solid face, a shadow that lifts it. */
.pt-rail .pt-rail-btn,
.pt-rail .pt-chip-btn {
  border: 0;
  background: var(--rail-face);
  box-shadow: var(--lift);
  transition: background var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
              transform var(--dur) var(--ease);
}
.pt-rail .pt-rail-btn:hover,
.pt-rail .pt-chip-btn:hover {
  background: var(--rail-face-hover);
  box-shadow: var(--lift-hover);
  transform: translateY(-1px);
}
.pt-rail .pt-rail-btn:active,
.pt-rail .pt-chip-btn:active { box-shadow: var(--lift-press); transform: translateY(1px); }

/* -------------------------------------------------------------- The dock */
.pt-dock {
  position: absolute;
  inset-block-end: 50px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 1100;
  display: flex;
  gap: 4px;
  padding: 4px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-pill);
  background: var(--glass);
  backdrop-filter: blur(6px);
  box-shadow: var(--shadow-2);
  max-width: calc(100% - 24px);
  overflow-x: auto;
  scrollbar-width: none;
}
.pt-dock::-webkit-scrollbar { display: none; }
.pt-dock-btn { border-color: transparent; background: transparent; height: 30px; }
.pt-dock-btn.is-on {
  background: var(--gold);
  color: var(--on-gold);
  border-color: var(--gold);
}
.pt-dock-btn.is-on:hover { background: var(--gold); filter: brightness(0.94); }
.pt-dock-btn[aria-pressed="true"] { background: var(--gold); color: var(--on-gold); }

/* السطرُ الهادئ داخل البطاقة — a quiet line inside a card.
   ---------------------------------------------------------------------------
   وهو ما تعنيه ‎.pt-note‎ في أحدَ عشرَ موضعاً: «لا وصول هذا الأسبوع»، «الوحدة
   تستقبل»، «يُعاد التسعير». وكانت القاعدةُ الوحيدة تحته هي رقاقةَ الخريطة
   الطافية، فكانت هذه السطورُ كلُّها تُنتزَع من مجرى الصفحة وتتكدّس أسفلها فوق
   بعضها — وتختفي على الهاتف أصلاً. فبطاقةٌ يُقرأ منها سطرٌ واحد كانت تُرسم
   فارغة.

   This is what .pt-note means in eleven places: "no arrivals this week", "the
   unit takes bookings", "it is repriced". The only rule beneath it was the map's
   floating chip, so every one of those lines was pulled out of the page's flow
   and piled at its foot on top of the others — and hidden outright on a
   telephone. A card whose whole content was one line was drawn empty. */
.pt-note {
  margin: 0;
  font-size: 11.5px;
  line-height: 1.7;
  color: var(--fg-3);
}

.pt-note .ico { vertical-align: -2px; margin-inline-end: 4px; color: var(--fg-4); }

/* ورقاقةُ الخريطة تُنسب إلى الخريطة: «٤٢ من ١٢٨ قريبة» تطفو فوق اللوحة وحدها،
   ولا تطفو حيثما كُتب الاسم.
   The map's chip belongs to the map: "42 of 128 nearby" floats over the canvas
   and nowhere else the name happens to be written. */
.pt-canvas .pt-note {
  position: absolute;
  inset-block-end: 12px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 1090;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  max-width: calc(100% - 24px);
  padding: 5px 12px;
  border-radius: var(--r-pill);
  background: var(--cocoa);
  color: var(--sand);
  font-size: 11px;
  font-weight: 600;
  line-height: normal;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ------------------------------------------------ The results, on the map */
/* القائمة تعيش فوق الخريطة لا في صفحة أخرى: يبقى ما يراه الزبون من مواقع
   ودبابيس قائماً وهو يقرأ البطاقات، ويطابق ما في اللوح ما على الخريطة صفاً
   بصف — لأن كليهما يقرأ الصفوف المصفّاة نفسها من الخادم.

   The list lives over the map rather than on another page: the locations and
   pins the customer is looking at stay where they are while the cards are
   read, and what is in the panel matches what is on the map row for row —
   because both read the same filtered rows from the server. */
.pt-panel-results {
  position: absolute;
  inset-block: 56px 12px;
  inset-inline-start: 12px;
  z-index: 1095;
  width: min(392px, calc(100% - 24px));
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--shadow-3);
  overflow: hidden;
}
.pt-panel-results[hidden] { display: none; }

.pt-panel-head {
  flex: none;
  display: flex;
  align-items: baseline;
  gap: 6px;
  padding: 10px 12px;
  border-block-end: 1px solid var(--line);
  font-size: 12.5px;
  font-weight: 650;
}
.pt-panel-head .pt-icon-btn { margin-inline-start: auto; align-self: center; }

/* التمرير محبوس في اللوح: لا يُسلَّم إلى الخريطة تحته حين يبلغ آخره، وإلا
   قفزت الخريطة بينما يقرأ الزبون. The scroll is contained: it is not handed
   to the map beneath when it reaches the end, which would otherwise send the
   map flying while the customer is reading. */
.pt-panel-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--sp-2);
  display: grid;
  gap: var(--sp-2);
  align-content: start;
}

/* -------------------------------------------------------- The result card */
.pt-hit {
  color: inherit;
  display: grid;
  grid-template-columns: 108px minmax(0, 1fr);
  gap: 10px;
  padding: 8px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.pt-hit:hover { border-color: var(--gold); background: var(--surface-1); }

.pt-hit-body { display: grid; gap: 4px; align-content: start; min-width: 0; }

/* وما في البطاقة لا يخرج من البطاقة.
   ----------------------------------------------------------------------
   والحدّ الأدنى التلقائيّ هو ما كان يُخرجه: عنصرٌ في شبكةٍ عرضُ عموده ‎auto‎
   لا ينزل تحت مقاس أضيق ما فيه — و«أضيق ما فيه» عند ‎-webkit-box‎ سطرُه
   كاملاً. فعنوانٌ من ستّ عشرة كلمة يوسّع خليّته إلى عرض السطر، ويقصّه اللوحُ
   عند حافّته — فيقرأ الزبون عنواناً مبتوراً من أوّله، ورقمَ الإعلان في الذيل
   مبتوراً كذلك. والقصّ كان يقع على الحافّة اليسرى لأنّ الصفحة تُقرأ من اليمين،
   فبدا كأنّ الكلام يخرج من البطاقة.

   وصفرٌ في ‎min-width‎ يرفع ذلك الحدّ، فيرجع العنصر إلى عمود لوحه ويقصّه
   ‎line-clamp‎ عند السطر الثاني كما هو مكتوب له. و‎overflow-wrap‎ لكلمةٍ
   واحدةٍ أطول من العمود — عنوانٌ بلا فراغات أو رابط — فتُكسر بدل أن تخرج.

   What is in the card does not leave the card.

   The automatic minimum size is what was pushing it out: a grid item in an
   auto-sized column does not fall below the narrowest its content can be —
   and for a -webkit-box that is its whole line. So a sixteen-word title
   widened its cell to a line's width, and the panel cut it at its edge —
   leaving the reader a title cut off at its head, and the listing's number in
   the foot cut off too. The cut fell on the left edge because the page reads
   from the right, so it looked as though the words were escaping the card.

   A zero min-width lifts that floor, so the item returns to its panel's column
   and line-clamp cuts it at the second line as it was written to. overflow-wrap
   is for a single word longer than the column — a title with no spaces, or a
   pasted address — which is broken rather than let out. */
.pt-hit-body > *,
.pt-row-main > * { min-width: 0; overflow-wrap: anywhere; }

/* والاسم يتقلّص قبل الرقم: ‎flex: 1‎ وحدها لا تُنقص عنصراً تحت مقاس نصّه ما
   دام ‎nowrap‎، فكان الاسمُ الطويل يدفع رقم الإعلان خارج البطاقة بدل أن
   يُختصر هو بثلاث نقاط.
   The name shrinks before the number does: flex: 1 alone never takes an item
   below its text's width while it is nowrap, so a long name pushed the
   listing's number out of the card instead of being cut short itself. */
.pt-card-by { min-width: 0; }

.pt-hit-title {
  font-size: 12.5px;
  font-weight: 650;
  line-height: 1.55;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.pt-hit-desc {
  font-size: 11px;
  color: var(--fg-3);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.pt-hit-foot {
  display: flex;
  align-items: center;
  gap: 6px;
  padding-block-start: 4px;
  border-block-start: 1px solid var(--line-soft);
  font-size: 10px;
  color: var(--fg-4);
}
.pt-hit-foot .pt-card-by { flex: 1; }

/* --------------------------------------------------------- The thumbnail */
/* لا صورة حقيقية في هذه النسخة، فالخانة تُملأ برسمٍ مشتقٍّ من رقم الإعلان
   نفسه: التدرّج ثابت لكل إعلان، ورمز نوعه فوقه. فتتميّز البطاقات عن بعضها
   ولا تُوهِم بصورة ليست موجودة. والحقل photo إن وُجد في البيانات فهو المقدَّم.

   There is no real photograph in this build, so the slot is filled with art
   derived from the listing's own reference: a gradient fixed per listing with
   its type's glyph over it. The cards stay distinguishable without pretending
   to a photograph that does not exist. A photo field in the data, when there
   is one, takes precedence. */
.pt-shot {
  position: relative;
  aspect-ratio: 4 / 3;
  border-radius: var(--r-sm);
  overflow: hidden;
  display: grid;
  place-items: center;
  color: rgb(255 255 255 / 0.82);
  background: linear-gradient(135deg, var(--cocoa), var(--olive));
}
/* والتدرّجات لِما لم يُرفع له صورة. أما المرفوع فيملأ خانته بقاعدة ‎.pt-fit‎
   أسفل هذا القسم، فلا يُرى من التدرّج تحته شيء.

   The gradients are for what has no photograph uploaded. What was uploaded
   fills its slot under the .pt-fit rule below this section, so nothing of the
   gradient beneath it is seen. */
.pt-shot[data-art="1"] { background: linear-gradient(135deg, var(--olive), var(--gold)); }
.pt-shot[data-art="2"] { background: linear-gradient(135deg, var(--gold), var(--sand)); color: rgb(44 20 3 / 0.6); }
.pt-shot[data-art="3"] { background: linear-gradient(135deg, #2f6f33, var(--green)); }
.pt-shot[data-art="4"] { background: linear-gradient(135deg, var(--cocoa), var(--red)); }
.pt-shot[data-art="5"] { background: linear-gradient(135deg, var(--sand), var(--olive)); }

/* وخانةٌ فيها صورةٌ مرفوعة لا يُرسم تحتها التدرّجُ البنّي: لونُها لونُ هيكل
   التحميل، والبريقُ على الصورة نفسها حتى تصل (‎style.css‎).
   A slot holding an uploaded photograph is not painted brown beneath it: its
   colour is the loading skeleton's, and the shine is on the picture itself
   until it arrives (style.css). */
.pt-shot.pt-fit,
.gm-card-shot.pt-fit { background: var(--skel-base); }

.pt-shot-tag {
  position: absolute;
  inset-block-end: 4px;
  inset-inline-start: 4px;
  padding: 1px 6px;
  border-radius: var(--r-sm);
  background: rgb(44 20 3 / 0.66);
  color: #fff;
  font-size: 9px;
  font-weight: 700;
  z-index: 3;
}

/* =========================================================================
   الملء — الخانة تمتلئ بالصورة، وما خرج عنها يُقصّ
   =========================================================================
   المشكلة أن الخانة نسبتُها مكتوبة والصورة نسبتُها ما شاء من رفعها. وللخلاف
   حلّان لا ثالث لهما: أن يبقى في الخانة فراغ، أو أن يُقطع من الصورة.

   وكان الحلّ المكتوب هنا ثالثاً مصطنعاً: ‎contain‎ تُبقي الصورة كاملةً في
   الوسط، ونسخةٌ منها مكبَّرةٌ مضبَّبة تملأ ما بقي. وهو حلٌّ يُخفي الفراغ ولا
   يزيله — يبقى في الخانة موضعٌ ليست فيه الصورة، وكل ما تغيّر أن لونه صار
   لطخةً مضبَّبة بدل السواد. ومن نظر إلى الصفحة رأى ضبابةً حول كل صورة وسأل
   عنها، وهو محقّ: الضبابة ليست محتوى.

   فالمكتوب الآن ‎cover‎: تُكبَّر الصورة حتى تملأ الخانة كلها ويُقطع ما خرج
   عن حوافّها. لا فراغ ولا تمويه — الخانة صورةٌ من حافّة إلى حافّة.

   والقصّ يقلّ كلّما قاربت نسبةُ الخانة نسبةَ الصورة، ولذلك بقيت نسب الخانات
   مضبوطةً على ‎3/2‎ — وهي نسبة ما يُرفع غالباً — وبقي حساب المعرض في
   ‎.pt-gallery‎: هما ما يجعل المقصوص حافّةً رفيعة لا ثلثَ الصورة.

   والمقطع المرئي يُستثنى وحده: قصُّ حواف الفيديو يقطع ما لا يُسترجَع بنقرة،
   وإطارُه ‎16/9‎ وهي نسبةُ ما يُصوَّر أصلاً — فلا فراغ فيه يُذكر.

   FILLING — the slot is filled by the picture, and what falls outside is cut

   The trouble is that the slot's ratio is written and the picture's ratio is
   whatever the person uploading it pleased. The disagreement has two answers
   and no third: either empty space is left in the slot, or something is cut
   from the picture.

   What was written here was a contrived third: contain kept the whole picture
   centred, and an enlarged blurred copy of it filled what was left. That hides
   the empty space rather than removing it — part of the slot still is not the
   picture, and all that changed is that its colour became a blurred smear
   instead of black. Whoever looked at the page saw a haze around every
   photograph and asked about it, rightly: a haze is not content.

   So what is written now is cover: the picture is enlarged until it fills the
   whole slot and whatever falls outside its edges is cut. No empty space and no
   blur — the slot is photograph from edge to edge.

   The cutting grows smaller the closer the slot's ratio comes to the picture's,
   which is why the slot ratios stay pinned at 3/2 — the ratio most of what is
   uploaded arrives in — and why the gallery arithmetic in .pt-gallery stays.
   Those two are what make what is cut a thin edge rather than a third of the
   picture.

   The clip alone is excepted: cutting a video's edges removes what no click
   brings back, and its frame is 16/9, which is the ratio things are filmed in —
   so there is no empty space in it to speak of. */
.pt-fit {
  position: relative;
  overflow: hidden;

  /* والعزل لأن الطبقات الثلاث تترتّب بـ‎z-index‎، ولا يصحّ أن يتسلّق أحدها
     خارج الإطار فيقع فوق ما ليس منه.
     Isolated because the three layers are ordered by z-index, and none of them
     may climb out of the frame to land over what is not its own. */
  isolation: isolate;
}

/* والصورة مطلقةُ الموضع لا في مجرى الإطار.
   -------------------------------------------------------------------------
   ولها سبب دقيق: ارتفاع الإطار مكتوبٌ بـ‎aspect-ratio‎، وصورةٌ في المجرى
   بـ‎height: 100%‎ تسأل عن ارتفاعٍ ليس معلوماً بعد — فتسقط إلى ارتفاعها
   الأصلي، فيتمدّد الإطار إليه، فتُهدر النسبة المكتوبة. أي أن خانةً نسبتُها
   ‎16/10‎ تصير مربّعةً إذا وُضعت فيها صورةٌ مربّعة — وهو عين ما جئنا نصلحه.

   والإطلاق يقطع هذا: ‎inset: 0‎ يعطي الصورة مقاس الإطار بالضبط، ولا تُحسب هي
   في ارتفاعه — فتبقى النسبة للورقة وحدها.

   The picture is absolutely positioned rather than in the frame's flow.

   For a precise reason: the frame's height is written with aspect-ratio, and a
   picture in the flow with height: 100% asks after a height not yet known — so
   it falls back to its own natural height, the frame stretches to it, and the
   written ratio is spent. Which is to say a slot of ratio 16/10 becomes square
   the moment a square photograph is put in it — the very thing we came to fix.

   Taking it out of the flow cuts that: inset: 0 gives the picture exactly the
   frame's size, and it counts for nothing in the frame's height — so the ratio
   stays the stylesheet's alone. */
.pt-fit > img,
.pt-fit > video {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: block;
  width: 100%;
  height: 100%;

  /* وموضعُها الوسط دائماً، ولا يُقلب بالاتجاه: الصورة ليست نصّاً.
     ومعنى الوسط مع ‎cover‎ أن المقصوص يُؤخذ من الحافّتين بالسواء لا من واحدة:
     صورةٌ عريضة يُقطع من يمينها ويسارها بالتساوي، فيبقى موضوعها — وهو في وسطها
     غالباً — كاملاً.

     Centred always, and not flipped by direction: a picture is not text.

     Centred under cover means what is cut is taken from both edges equally
     rather than from one: a wide picture loses as much from its right as from
     its left, so its subject — which is usually in the middle — survives
     whole. */
  object-position: 50% 50%;

  object-fit: cover;
}

/* والمقطع المرئي يُحتوى لا يُقصّ: ما يُقطع من صورةٍ تفتحه نقرةٌ في العارض
   كاملاً، وما يُقطع من فيديو مقطوعٌ طوال تشغيله.
   A clip is contained rather than cut: what is cut from a photograph a click in
   the viewer opens whole, and what is cut from a video stays cut for the length
   of its playing. */
.pt-fit > video { object-fit: contain; }

/* ولا طبقةَ تمويهٍ خلف الصورة: كانت ‎::before‎ نسخةً مكبَّرةً مضبَّبة
   تملأ ما لا تملؤه ‎contain‎. و‎cover‎ لا تترك ما يُملأ، فالطبقة الوسطى حُذفت
   ولم تُعطَّل — قاعدةٌ معطَّلةٌ تعود يوماً بلا قصد.

   No blurred layer behind the picture: ::before was an enlarged blurred copy
   filling what contain did not. cover leaves nothing to fill, so the middle
   layer is deleted rather than disabled — a disabled rule comes back one day
   without anybody meaning it. */

/* -------------------------------------------------------- العلامة المائية */
/* شعارُ الموقع على ما رُفع من صور، أبيضَ شفّافاً خفيفاً.
   -------------------------------------------------------------------------
   وهو قناعٌ لا صورة: ‎mask-image‎ تقرأ من ملف الشعار شكلَه وحده — أين هو وأين
   ليس — ثم يُملأ ذلك الشكل بالأبيض. فلا يظهر ذهبُ الشعار على صورةٍ ذهبية ولا
   تُرى حوافُّه، ويبقى لونُ العلامة واحداً مهما كان لون ما تحتها.

   وشفافيتُه ‎0.14‎: تُقرأ على الصورة الفاتحة وعلى الداكنة، ولا تُقرأ قبل
   الصورة. والظلُّ الرفيع تحته لأن الأبيض على سماءٍ بيضاء لا يُرى بغيره.

   ولا علامة على التدرّج المولَّد: توقيعُ رسمٍ لم يرفعه أحد لا معنى له. ولا
   على المقاطع المرئية: موضعُها هناك موضعُ شريط الضوابط.

   The site's mark over what was uploaded, white and faint.

   A mask rather than a picture: mask-image reads from the logo file its shape
   alone — where it is and where it is not — and that shape is then filled with
   white. So the logo's gold does not show over a golden photograph, its edges
   are not seen, and the mark keeps one colour whatever lies beneath it.

   Its opacity is 0.14: read over a light photograph and over a dark one, and
   not read before the photograph. The thin shadow under it because white on a
   white sky is not seen without one.

   No mark over the generated gradient: signing a drawing nobody uploaded means
   nothing. And none over a clip: its place there is the control bar's place. */
.portal-theme {
  --pt-mark: url("../media/brand/z3-logo.png");
}

.pt-fit[data-photo="1"]::after {
  content: "";
  position: absolute;
  z-index: 2;
  inset-block-end: 5%;
  inset-inline-end: 5%;
  width: clamp(30px, 24%, 116px);
  aspect-ratio: 381 / 333;

  background-color: #fff;
  -webkit-mask-image: var(--pt-mark);
  mask-image: var(--pt-mark);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;

  opacity: 0.14;
  filter: drop-shadow(0 1px 2px rgb(0 0 0 / 0.45));
  pointer-events: none;
}

/* ولا علامة على المقطع المرئي: شريط الضوابط يقف حيث كانت تقف. وتُكتب الصفة في
   المحدِّد لا لزينة: ‎.pt-video::after‎ وحدها أضعف من قاعدة العلامة فوقها، فلا
   تنقضها.

   No mark over a clip: the control bar stands where the mark would have stood.
   The attribute is written into the selector for a reason: .pt-video::after
   alone is weaker than the mark's rule above it, and would not have undone
   it. */
.pt-video[data-photo="1"]::after { content: none; }

/* ------------------------------------------------------- Document pages */
.pt-page {
  max-width: 1180px;
  margin-inline: auto;
  padding: 0 var(--sp-5) var(--sp-7);
}
.pt-page--narrow { max-width: 760px; padding-block-start: var(--sp-6); }

/* شريط ترقيم تحت جدول: خطوةٌ إلى الخلف، ومدى ما يُعرض، وخطوةٌ إلى الأمام.
   ---------------------------------------------------------------------------
   والمدى في الوسط بين الخطوتين لأنه ما يُقرأ: «٢٦–٥٠» يقول أين يقف صاحبُه من
   الجدول. وهو ‎.mono‎ فيبقى الرقم لاتينيّ الاتجاه في صفحةٍ تُقرأ من اليمين،
   والشرطةُ بينهما لا تنقلب.

   A pager beneath a table: a step back, the span on show, a step forward.

   The span sits between the two steps because it is the thing read: "26–50"
   says where its reader stands in the table. It is .mono, so the figure keeps
   its left-to-right run on a page read from the right, and the dash between the
   two numbers does not turn around. */
.pt-pager {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-3);
  padding-block-start: var(--sp-4);
}
.pt-pager-at { font-size: 11.5px; color: var(--fg-3); }

.pt-head { padding-block: var(--sp-6) var(--sp-5); text-align: center; }
.pt-head h1 {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-size: 21px;
  font-weight: 700;
}
.pt-head p { margin-block-start: 5px; font-size: 13px; color: var(--fg-3); }

.pt-subnav {
  display: flex;
  gap: 6px;
  overflow-x: auto;
  padding-block-end: var(--sp-4);
  scrollbar-width: none;
}
.pt-subnav::-webkit-scrollbar { display: none; }

.pt-dim { color: var(--fg-3); font-weight: 500; }

/* ---------------------------------------------------------------- Cards */
.pt-card {
  display: flex;
  flex-direction: column;
  gap: 7px;
  padding: var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--shadow-1);
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.pt-card:hover { border-color: var(--gold); box-shadow: var(--shadow-2); }

.pt-card-head { display: flex; align-items: start; gap: var(--sp-2); }
.pt-card-head h3 { flex: 1; font-size: 14px; font-weight: 650; }

.pt-tagline {
  flex: none;
  padding: 2px 8px;
  border: 1px solid var(--gold);
  border-radius: var(--r-pill);
  font-size: 10px;
  font-weight: 700;
  color: var(--gold);
  white-space: nowrap;
}

.pt-card-place {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 11.5px;
  color: var(--fg-3);
}
.pt-card-place span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* السعر أخضر لأنه مال؛ وهو أول ما تبحث عنه العين في بطاقة عقار.
   The price is green because it is money, and it is the first thing an eye
   looks for on a property card. */
.pt-price { display: flex; align-items: baseline; gap: 5px; }
.pt-price b { font-size: 17px; font-weight: 700; letter-spacing: var(--tr-lg); color: var(--green); }
.pt-price small { font-size: 11px; color: var(--fg-3); }

.pt-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 12px;
  font-size: 11px;
  color: var(--fg-2);
}
.pt-meta li { display: inline-flex; align-items: center; gap: 4px; }

.pt-tags { display: flex; flex-wrap: wrap; gap: 4px; }

.pt-card-foot {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding-block-start: 9px;
  border-block-start: 1px solid var(--line-soft);
}
.pt-card-by { flex: 1; font-size: 11px; color: var(--fg-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pt-card-id { font-size: 10px; color: var(--fg-4); }

/* التوثيق والحجز الفوري تأكيدان، فهما أخضران كالسعر. والكتابة عليهما سوداء
   لا بيضاء: الأخضر ‎#4caf50‎ لونٌ فاتح، ونسبة تباين الأبيض عليه ‎2.8:1‎ —
   تحت الحدّ الأدنى ‎4.5:1‎ — بينما الأسود عليه ‎7.5:1‎. فما يقرؤه المرء
   ليس مسألة ذوق هنا.

   Verification and instant booking are confirmations, so they are green like
   the price. The writing on them is black rather than white: green #4caf50
   is a light colour, and white on it measures 2.8:1 — below the 4.5:1 floor
   — where black measures 7.5:1. What can be read is not a matter of taste
   here. */
.portal-theme .pill.solid {
  background: var(--green);
  border-color: var(--green);
  color: var(--on-green);
}
.portal-theme .pill.solid svg { color: var(--on-green); }

.pt-card--project .pt-kv { display: grid; grid-template-columns: auto 1fr; gap: 3px var(--sp-3); }
.pt-kv dt { font-size: 11px; color: var(--fg-3); }
.pt-kv dd { margin: 0; font-size: 11.5px; text-align: end; font-weight: 600; }

.portal-theme .bar > i { background: var(--gold); }

/* --------------------------------------------------------------- Sheets */
.pt-sheet {
  position: fixed;
  inset: 0;
  z-index: 1600;
  display: grid;
  place-items: center;
  padding: var(--sp-4);
}
.pt-sheet[hidden] { display: none; }

.pt-sheet-veil {
  position: absolute;
  inset: 0;
  background: rgb(44 20 3 / 0.5);
  backdrop-filter: blur(2px);
}

.pt-sheet-panel {
  position: relative;
  width: min(560px, 100%);
  max-height: min(84dvh, 720px);
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--shadow-3);
  overflow: hidden;
  animation: ptRise 0.2s var(--ease);
}
.pt-sheet-panel.is-wide { width: min(760px, 100%); }

@keyframes ptRise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

.pt-sheet-head {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border-block-end: 1px solid var(--line);
}
.pt-sheet-head b { flex: 1; font-size: 14px; font-weight: 650; }

.pt-sheet-body { overflow: auto; padding: var(--sp-4); }

.pt-icon-btn {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  flex: none;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--fg-2);
  cursor: pointer;
}
.pt-icon-btn:hover { background: var(--surface-2); color: var(--fg-1); }
.pt-icon-btn.is-off { opacity: 0.35; cursor: default; }

/* ---------------------------------------------------------------- Forms */
.pt-form { display: grid; gap: var(--sp-4); }

.pt-field { display: grid; gap: 5px; }
.pt-field > span,
.pt-field > label { font-size: 11.5px; font-weight: 600; color: var(--fg-2); }

.pt-field input,
.pt-field select,
.pt-field textarea,
.pt-prefixed {
  width: 100%;
  min-height: 38px;
  padding: 8px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-1);
  color: var(--fg-1);
  font-size: 13px;
}

/* القائمة المنسدلة يرسمها النظام لا الصفحة، فتأخذ ألوانها من خلفية العنصر
   نفسه ولون نصّه. و‎--surface-1‎ شفّافةٌ بخمسةٍ في المئة، ولا شيء خلفها في
   نافذة النظام لتستقرّ عليه إلا الأبيض — فيخرج نصٌّ كريميٌّ على ورقٍ أبيض،
   لا يُقرأ في السمة الداكنة. فيُصرَّح للقائمة وخياراتها لونٌ صلبٌ هو نفسه
   الشفّافةُ مسطَّحةً فوق ‎--surface‎: يبقى الحقل المغلق كما كان بالضبط،
   وتتبع القائمةُ السمةَ أخيراً.

   The dropdown list is painted by the platform, not by the page, so it takes
   its colours from the control's own background and text. --surface-1 is
   translucent at five per cent, and inside the platform's window there is
   nothing behind it to resolve against but white — cream text on white paper,
   unreadable in the dark theme. The list and its options are therefore given
   an opaque colour that is that same translucent one flattened over
   --surface: the closed field looks exactly as it did, and the list finally
   follows the theme. */
.pt-field select {
  background-color: var(--field-face);
}
.pt-field select option,
.pt-field select optgroup {
  background-color: var(--field-face);
  color: var(--fg-1);
}
.pt-field textarea { resize: vertical; min-height: 88px; }
.pt-field input:focus,
.pt-field select:focus,
.pt-field textarea:focus { outline: 2px solid var(--gold); outline-offset: 1px; }

.pt-prefixed { display: flex; align-items: center; gap: 8px; padding-block: 0; }
.pt-prefixed i {
  font-style: normal;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--fg-3);
}
.pt-prefixed input {
  flex: 1;
  border: 0;
  background: transparent;
  padding: 0;
  min-height: 36px;
}
.pt-prefixed input:focus { outline: 0; }

.pt-hint { font-size: 11.5px; color: var(--fg-3); }

.pt-picks, .pt-range { border: 0; padding: 0; margin: 0; display: grid; gap: 9px; }
.pt-picks legend, .pt-range legend {
  padding: 0;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--fg-2);
}

.pt-pick-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 7px;
}

.pt-pick input { position: absolute; opacity: 0; width: 0; height: 0; }

/* مربّعات الاختيار كلها بمقاسٍ واحد.
   -------------------------------------------------------------------------
   كانت خمسة عشر خياراً في شبكةٍ ترتفع صفوفها وتنخفض بطول الكلمة: صفٌّ فيه
   «شقق للإيجار» يلتفّ سطرين فيطول، وصفٌّ فيه «الكل» و«فلل للبيع» فيقصر.
   والعين تقرأ اختلاف المقاس معنى — أنّ هذا الخيار أهمّ من ذاك — ولم يكن إلا
   أنّ اسمه أطول حروفاً. فتُسوَّى الصفوف كلها بأطولها، ويملأ الوجهُ خليّته.

   The pick boxes at one size.

   Fifteen choices sat in a grid whose rows rose and fell with the length of a
   word: a row holding "Apartments to rent" wrapped onto two lines and grew
   tall, while a row of "All" and "Villas for sale" stayed short. The eye reads
   a difference in size as a meaning — that this choice matters more than that
   one — when all it was is a name with more letters in it. So every row is
   levelled to the tallest, and the face fills its cell.

   والثلاثة معاً لا واحد: ‎1fr‎ يسوّي الصفوف، و‎height: 100%‎ على الوسيط ثم على
   الوجه ينقل ذلك الارتفاع إليهما — فبدون الوسيط يبقى الوجه بمقاس محتواه داخل
   خليّةٍ ممدودة.

   All three together and not one: 1fr levels the rows, and height: 100% on the
   label and then on the face carries that height down to them — without the
   label in between, the face keeps its content's size inside a stretched
   cell. */
.pt-pick-grid { grid-auto-rows: 1fr; }
.pt-pick { display: block; height: 100%; }

.pt-pick-face {
  display: grid;
  align-content: center;
  justify-items: center;
  gap: 5px;
  width: 100%;
  height: 100%;
  padding: var(--sp-3) var(--sp-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--fg-2);
  cursor: pointer;
  text-align: center;
  transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.pt-pick-face b { font-size: 11.5px; font-weight: 600; line-height: 1.3; }
.pt-pick-face i { font-style: normal; font-size: 10.5px; color: var(--fg-3); }
.pt-pick-face:hover { border-color: var(--gold); background: var(--surface-1); }
.pt-pick input:checked + .pt-pick-face {
  border-color: var(--gold);
  background: var(--surface-2);
  color: var(--fg-1);
}
.pt-pick input:focus-visible + .pt-pick-face { outline: 2px solid var(--gold); outline-offset: 2px; }

.pt-range-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-3); }

.pt-check { display: flex; align-items: start; gap: 9px; font-size: 11.5px; color: var(--fg-2); }
.pt-check input { width: 16px; height: 16px; flex: none; margin-block-start: 2px; accent-color: var(--green); }

/* صفٌّ من حقلين متجاورين — سعرٌ ومساحة، خط عرضٍ وخط طول. وهما سؤالان يُقرآن
   معاً، فوضعهما تحت بعضهما يطيل النموذج بلا فائدة.

   وينهار إلى عمود واحد على الشاشة الضيّقة: حقلان بعرض النصف على هاتف حقلان لا
   يُكتب فيهما.

   A row of two fields side by side — a price and an area, a latitude and a
   longitude. They are read together, and stacking them lengthens the form for
   nothing.

   It collapses to one column on a narrow screen: two half-width fields on a
   phone are two fields nobody can type in. */
.pt-form-row { display: grid; gap: var(--sp-4); grid-template-columns: 1fr 1fr; }

@media (max-width: 560px) {
  .pt-form-row { grid-template-columns: 1fr; }
}

/* الحقل الاختياري يقول ذلك بجانب اسمه، بخطٍّ أخفت — فلا يُقرأ نموذجٌ نصفُ
   حقوله اختيارية على أنه كلّه مطلوب.

   An optional field says so beside its name in a quieter voice — so a form half
   of whose fields are optional is not read as though all of it were required. */
/* والشرحُ يُفصَل عن الاسم بفراغٍ يُرى: نصفُ درجةٍ في الوزن ودرجةٌ في اللون لا
   تكفيان عند ١١٫٥ نقطة، فيُقرأ «السعر الشهري اختياري» عبارةً واحدة بدل اسمٍ
   وشرحٍ له.
   The hint is parted from the name by a space that can be seen: half a step in
   weight and one in colour do not carry at 11.5px, so "monthly price optional"
   reads as one phrase rather than a name and a note on it. */
.pt-field > span i {
  margin-inline-start: 6px;
  font-style: normal;
  font-weight: 500;
  color: var(--fg-4);
}

/* شرحٌ تحت الحقل: لماذا يُطلب، أو كيف يُملأ. وهو بعد المُدخَل لا قبله، فمن
   عرف ما يُكتب لا يقرؤه ومن تردّد وجده في مكانه.

   A note beneath the field: why it is asked for, or how to fill it. It sits
   after the input rather than before it, so whoever knows what to type skips
   it and whoever hesitates finds it where they look. */
.pt-field > i {
  font-style: normal;
  font-size: 11.5px;
  color: var(--fg-3);
}

/* حصيلة الإرسال فوق النموذج: استُلم، أو ما ينقص.

   وهي في مجرى الصفحة لا معلَّقةً فوقها كـ‎.pt-note‎: تلك رقاقةٌ تطفو فوق الخريطة
   وتذهب، وهذه سطرٌ يبقى ليُقرأ ويُعاد إليه بعد تصحيح حقل.

   The outcome of a submission, above the form: received, or what is missing.

   It sits in the page's flow rather than floating over it like .pt-note: that is
   a chip that hovers over the map and goes, and this is a line that stays to be
   read and returned to after a field is corrected. */
.pt-form-status {
  padding: 9px 12px;
  margin-block-end: var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface-1);
  color: var(--fg-2);
  font-size: 12.5px;
  font-weight: 600;
}

/* شريط المعاينة: هذه الصفحة ليست منشورة، ومن يقرؤها موظّف.

   ويُقال بالحدّ المقطَّع لا بلونٍ سابع: اللوحة ستّة ألوانٍ لكلٍّ منها عمل، وليس
   في المعاينة خطرٌ فتُقال بالأحمر ولا اختيارٌ فتُقال بالذهبي. والمقطَّع في هذه
   المنصة معناه واحد حيثما وقع — ما لم يستقرّ بعد: رقاقةُ الانتظار في الطابور،
   وإطارُ الصورة الغائبة. فهذه صفحةٌ لم تستقرّ بعد.

   The preview bar: this page is not published, and whoever is reading it is
   staff.

   Said with a dashed edge rather than with a seventh colour: the palette is six
   colours each with a job, and a preview is no danger to be said in red and no
   selection to be said in gold. A dash means one thing on this platform wherever
   it falls — what has not settled yet: the waiting pill in the queue, the frame
   of an absent photograph. And this is a page that has not settled yet.

   ولا يُطبع: من طبع الصفحة طبع ما فيها، لا لافتةً تخصّ الشاشة.
   It does not print: whoever prints the page prints what is on it, not a notice
   belonging to the screen. */
.pt-preview {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 12px;
  margin-block-end: var(--sp-4);
  border: 1px dashed var(--olive);
  border-radius: var(--r-sm);
  background: var(--sand);
  color: var(--cocoa);
  font-size: 12.5px;
  line-height: 1.5;
}

.portal-theme[data-theme="dark"] .pt-preview {
  background: var(--brown-face);
  border-color: var(--gold-face);
  color: #fff;
}

.pt-preview > .ico { flex: none; }
.pt-preview b { font-weight: 700; }
.pt-preview b::after { content: " · "; font-weight: 400; }

@media print {
  .pt-preview { display: none; }
}
.pt-form-status--bad {
  border-color: var(--red);
  background: rgb(198 49 49 / 0.08);
  color: var(--red);
}

/* والحصيلة التي معها رمز صفٌّ مرن — the outcome that carries a mark is a flex row.

   و‎img, svg { display: block }‎ في ‎style.css‎ تُصيّر كل رمزٍ في المنصة كتلةً،
   وهو الصحيح حيث يقع الرمز عنصراً في صفٍّ أو شبكة — وهو موضعه في أكثر المنصة.
   أما هنا فكان يقع في مجرى فقرة، فيأخذ سطره لنفسه وينزل الكلام تحته: رمزٌ في
   سطر ونصٌّ في سطر، لا شيء منهما بمحاذاة الآخر.

   img, svg { display: block } in style.css makes every mark on the platform a
   block, which is right where the mark is an item in a row or a grid — its place
   almost everywhere. Here it fell into a paragraph's flow instead, so it took a
   line of its own and the words dropped beneath it: a mark on one line and text
   on another, neither level with the other.

   ويُنتقى بـ‎:has‎ لا بالصنف: أكثر هذه الحصائل بلا رمز — سطرُ نصٍّ، أو قائمةُ
   أخطاء — وصفٌّ مرن يغيّر عرض القائمة فيها بلا داعٍ. فما فيه رمزٌ وحده يصير
   صفّاً، وما يُضاف إليه رمزٌ غداً يصير صفّاً بلا سطرٍ يُكتب.

   It is picked out with :has rather than with a class: most of these outcomes
   carry no mark — a line of text, or a list of errors — and a flex row would
   change the list's width in them for nothing. So only the one with a mark
   becomes a row, and whichever is given a mark tomorrow becomes one with no line
   written for it. */
.pt-form-status:has(> .ico) {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}

/* و‎min-width: 0‎ لأن عنصر الصفّ المرن لا ينكمش دون مقاس محتواه افتراضاً، فمرجعٌ
   طويل بلا فراغٍ يدفع الصندوق خارج عرضه.
   min-width: 0 because a flex item does not shrink below its content's size by
   default, so a long unbroken reference pushes the box past its width. */
.pt-form-status:has(> .ico) > span { min-width: 0; }

.pt-form-status ul { margin: 0; padding-inline-start: 18px; display: grid; gap: 3px; }

/* سطرٌ يقول شيئاً عن النموذج ولا يطلب شيئاً — a line that tells the form's
   reader something and asks nothing of them.

   ويفترق عن ‎.pt-form-status‎ في أنه ليس حالةً حدثت: الحالة تُقرأ مرة وتذهب،
   وهذا يبقى ما بقي النموذج. فحدّه منقّط لا متّصل، كما في تصريح التذييل.

   It differs from .pt-form-status in not being something that happened: a
   status is read once and gone, while this stands as long as the form does. So
   its border is dashed rather than solid, as the footer's declaration is. */
/* ما يكتبه السكربت تحت حقل الوسائط بعد الاختيار: كم اختير، وهل جاوز الحدّ.
   What the script writes under a media field once a choice is made: how many
   were chosen, and whether the ceiling was passed. */
.pt-field-note {
  display: block;
  margin-block-start: 5px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--fg-3);
}
.pt-field-note[data-over] { color: var(--red); }

/* ترويسة نموذج التعديل: مرجع الإعلان وحاله فوقه.
   من فتح صفحة تعديلٍ من جدولٍ فيه ستّة إعلانات يحتاج أن يعرف أيّها فتح.
   The editing form's header: the listing's reference and its standing above it.
   Whoever opened an editing page from a table of six listings needs to know which
   one they opened. */
.pt-edit-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  flex-wrap: wrap;
}

/* والمرجع يُقرأ كما تُقرأ قيمةُ حقل، لا كما يُقرأ رمزٌ في هامش.
   -------------------------------------------------------------------------
   فكان ‎.cell-id‎: ‎11.5px‎ بلون ‎--fg-3‎ — تباينُه ‎4.4:1‎ على الورق الفاتح، تحت
   الحدّ لنصٍّ بهذا الصغر — وبلا اسمٍ فوقه في صفحةٍ كلُّ ما فيها حقلٌ مسمّى.
   فصار بحجم قيم الحقول ولونها، واسمُه فوقه كأسمائها.

   و‎<output>‎ لا ‎<input readonly>‎: المرجع ليس شيئاً يُكتب فيه ثم يُحفظ — تكتبه
   المنصة عند الإنشاء ولا يملك أحدٌ تغييره. وصندوقٌ يُغري بالكتابة يَعِد بما لا
   يقع. والاتجاه يأتيه من ‎.mono‎.

   The reference is read as a field's value is read, rather than as a code in a
   margin.

   It was .cell-id: 11.5px in --fg-3 — a contrast of 4.4:1 on the light sheet,
   under the floor for text that small — with no name above it on a page where
   everything else is a named field. It now carries the size and colour of a
   field's value, with its name above it as theirs have.

   An <output> rather than an <input readonly>: the reference is not something
   typed in and saved — the platform writes it at creation and nobody may change
   it, and a box that invites typing promises what does not happen. Its direction
   comes from .mono. */
.pt-edit-ref > output {
  font-size: 14px;
  font-weight: 650;
  color: var(--fg-1);
  letter-spacing: 0.02em;
}

/* الحفظ والعودة في صفّ واحد آخر النموذج. والعودة زرٌّ شبح لا زرّ ثانٍ: فعلان
   متساويان في المظهر آخر نموذجٍ طويل يجعل من ضغط أحدهما يظنّ أنه ضغط الآخر.
   Saving and going back in one row at the foot of the form. Going back is a ghost
   button rather than a second solid one: two actions of equal weight at the end of
   a long form leave whoever pressed one believing they pressed the other. */
.pt-form-actions {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex-wrap: wrap;
}
/* منتقي الموقع: خريطةٌ ودبوسٌ ورابطٌ يُلصق.
   -------------------------------------------------------------------------
   وكان الموقع حقلين رقميين: خط عرضٍ وخط طول. وهما سؤالٌ لا يعرف جوابه أحد،
   فكانا يُتركان فارغين ويُحفظ الإعلان بلا نقطة — ولا يظهر على الخريطة أبداً.

   The place picker: a map, a pin and a link to paste.

   The location was two numeric fields, a latitude and a longitude. Nobody knows
   the answer to that question, so they were left empty and the listing was saved
   with no point — never appearing on the map. */
.pt-place { display: grid; gap: var(--sp-4); }

/* صفُّ الإمارة والحيّ يبدأ من أعلاه: شرحٌ تحت الإمارة يُطيل عمودها وحده، ولا
   يمطّ عمودَ الحيّ فيُنزل قائمته عن مستوى قائمة الإمارة.
   The emirate and district row starts from its top: a note under the emirate
   lengthens its own column alone, rather than stretching the district column
   and dropping its list below the emirate's. */
.pt-place > .pt-form-row { align-items: start; }

.pt-place-picker { display: grid; gap: 6px; }

.pt-place-map {
  height: 300px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  overflow: hidden;
  background: var(--surface-1);
}

@media (max-width: 640px) {
  .pt-place-map { height: 240px; }
}

/* ومن لا سكربت عنده لا خريطة له: الحاوية مخفيّة بـhidden أصلاً، وهذا السطر
   يمنع ورقةً خارجية من إظهارها فارغة.
   Whoever has no script has no map: the container carries hidden already, and
   this line keeps a third-party sheet from revealing it empty. */
:root:not(.js) .pt-place-picker { display: none; }

/* وزرُّ «إضافة» و«تعديل» في لوحة المشروع يفتح نافذةً لا يفتحها إلا السكربت: بلا
   سكربت زرٌّ لا يفعل شيئاً، فلا يُعرض.
   The Add and Edit buttons on the project board open a dialog only the script
   opens: without it a button that does nothing, so it is not shown. */
:root:not(.js) [data-dialog-open] { display: none; }

/* وزرُّ المساعد كذلك: لوحتُه لا يفتحها إلا السكربت.
   The assistant's button likewise: only the script opens its panel. */
:root:not(.js) [data-chat-open] { display: none; }

/* و‎hidden‎ يُردّ إليها قوّتها: ‎display: grid‎ أعلاه صفةُ مؤلِّفٍ تعلو على
   ‎[hidden]‎ في ورقة المتصفح، فتُعرض الحاوية وإن بقيت الصفة عليها. والصفة لا
   تُرفع إلا حين تُبنى الخريطة فعلاً — فبدون هذا السطر يترك محرّكُ خرائطٍ تعثّر
   صندوقاً فارغاً بحدوده وزرَّ تكبيرٍ فوق لا شيء.

   hidden is given its force back: the display: grid above is an author
   declaration and outranks [hidden] in the browser's own sheet, so the container
   is laid out even while the attribute is still on it. The attribute comes off
   only when a map is actually built — so without this line a map engine that
   stumbled leaves an empty bordered box with an expand button over nothing at
   all. */
.pt-place-picker[hidden] { display: none; }

/* ------------------------------------------------- الخريطة على مِلء الشاشة */
/* ثلاثمئة بكسل داخل نموذجٍ طويل: تكفي لتحريك دبوسٍ عرف صاحبه موضعه، ولا تكفي
   للبحث عنه. ومن لا يعرف إلا اسم حيّه يحتاج أن يرى الشوارع والمباني حتى يعرف
   أيّ مبنىً مبناه، وذلك لا يُرى في نافذةٍ بحجم صورة.

   Three hundred pixels inside a long form: enough to nudge a pin whose owner
   knows where it goes, and not enough to look for one. Whoever knows only their
   district's name needs to see the streets and the buildings to know which
   building is theirs, and that is not seen through a window the size of a
   photograph. */
.pt-place-frame { position: relative; display: grid; min-height: 0; }

/* والزرّ في أعلى اليسار بالبكسل لا بالجهة المنطقية: محرّك الخرائط يضع أدوات
   التقريب في ‎top-right‎ فيزيائياً، لا يقلبها اتجاهُ الصفحة. فزرٌّ يبدأ من جهة
   القراءة يجلس فوقها في الواجهة العربية.

   The button sits top-left in pixels rather than by logical side: the map engine
   puts its zoom controls at a physical top-right, which the page's direction
   does not flip. A button starting from the reading side would sit on top of
   them in the Arabic interface. */
.pt-place-grow {
  position: absolute;
  top: 8px;
  left: 8px;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 6px 9px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--fg-1);
  font-size: 11.5px;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  box-shadow: var(--shadow-2);
}
.pt-place-grow:hover { background: var(--surface-2); }
.pt-place-grow:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

/* الشريط لا يُرسم إلا على مِلء الشاشة، وهو مكتوبٌ في المصدر على كل حال: زرّ
   الإغلاق موجودٌ قبل أن يُفتح شيء.
   The bar is not drawn except at full screen, and is written into the source
   either way: the close button exists before anything opens. */
.pt-place-bar { display: none; }

.pt-place-shut {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex: none;
  padding: 7px 11px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-1);
  color: var(--fg-1);
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
}
.pt-place-shut:hover { background: var(--surface-2); }
.pt-place-shut:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

/* المكبَّرة: شريطٌ فوق، وخريطةٌ تأخذ ما بقي، وحاشيةٌ تحت. وثلاثة صفوفٍ لا
   طبقتان — التنبيه «هذا الدبوس خارج الشارقة» يُقال وقت وضع الدبوس، وأكثر ما
   يوضع الدبوس والخريطة على مِلء الشاشة، فلا يجوز أن يكون تحتها بأربعمئة بكسل
   من النموذج.

   Expanded: a bar above, the map taking what is left, and the note below. Three
   rows rather than two layers — the warning "this pin is outside Sharjah" is
   said at the moment the pin is placed, and the pin is placed most often with
   the map at full screen, so it cannot be four hundred pixels of form
   underneath it. */
.pt-place-picker[data-place-full] {
  position: fixed;
  inset: 0;
  z-index: 2100;
  gap: 0;
  grid-template-rows: auto minmax(0, 1fr) auto;
  background: var(--bg);
}

.pt-place-picker[data-place-full] .pt-place-bar {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: 10px var(--sp-4);
  padding-block-start: calc(10px + var(--pt-safe-t, 0px));
  border-block-end: 1px solid var(--line);
  background: var(--surface);
}
.pt-place-picker[data-place-full] .pt-place-bar > b {
  flex: 1;
  min-width: 0;
  font-size: 13px;
  font-weight: 650;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pt-place-picker[data-place-full] .pt-place-map {
  height: 100%;
  border: 0;
  border-radius: 0;
}

/* ولا زرّ تكبيرٍ فوق ما كبر: الإغلاق في الشريط، ومكانه واحدٌ لا يُبحث عنه.
   No expand button over what is already expanded: closing is in the bar, in one
   place that is not hunted for. */
.pt-place-picker[data-place-full] .pt-place-grow { display: none; }

.pt-place-picker[data-place-full] .pt-place-note {
  padding: 8px var(--sp-4);
  padding-block-end: calc(8px + var(--pt-safe-b, 0px));
  border-block-start: 1px solid var(--line);
  background: var(--surface);
}

/* والصفحة خلفها لا تتمرّر. ومجرى التمرير في البوابة هو ‎.pt-main‎ لا ‎body‎ —
   فقفلُ المستند وحده يترك النموذج ينزلق تحت الخريطة، ويعود صاحبه من مِلء
   الشاشة إلى موضعٍ غير الذي تركه.

   The page behind does not scroll. The portal's scrolling stream is .pt-main
   rather than body — so locking the document alone leaves the form sliding
   beneath the map, and whoever returns from full screen lands somewhere other
   than where they left. */
html.pt-place-open,
html.pt-place-open body { overflow: hidden; }
html.pt-place-open .pt-main { overflow: hidden; }

/* الدبوس: مثلّثٌ فوق نقطة، طرفه عند الإحداثيات بالضبط لا وسطه.
   The pin: a shape over a point, its tip at the coordinates exactly rather than
   its middle. */
.pt-place-pin {
  display: block;
  width: 20px;
  height: 20px;
  margin: 8px auto 0;
  border: 3px solid var(--surface);
  border-radius: var(--r-circle);
  background: var(--gold);
  box-shadow: 0 1px 6px rgb(0 0 0 / 0.35);
  cursor: grab;
}

/* إطار الإمارة: يقول أين تنتهي، فيشرح التنبيه قبل أن يُقرأ.
   -------------------------------------------------------------------------
   وهو الآن طبقةٌ داخل لوحة الخريطة لا مسارُ SVG فوقها، لأنه شكلٌ ملتصقٌ بالأرض
   يتبع حدودها عند كل تقريب. وطبقةُ اللوحة تُصبغ بقيمةٍ في جافاسكربت لا بقاعدة
   CSS — فيبقى اللون هنا متغيّراً يقرؤه السكربت عند الرسم، ولا يصير للّون موضعان
   يفترقان أوّلَ يوم يتغيّر فيه.

   The emirate's frame: it says where the emirate ends, explaining the warning
   before it is read.

   It is now a layer inside the map's canvas rather than an SVG path over it,
   because it is a shape welded to the ground that follows its bounds at every
   zoom. A canvas layer is painted by a value in JavaScript rather than by a CSS
   rule — so the colour stays here as a variable the script reads at draw time,
   and the colour does not get two homes to part ways the first day it changes. */
.pt-place {
  --pt-frame: #EF4444;
  --pt-frame-fill: rgba(239, 68, 68, 0.20);
}

.pt-place-note {
  min-height: 16px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--fg-3);
}
.pt-place-note[data-warn] { color: var(--red); }

/* الإحداثيات مطويّة: هي آخر ما يُسأل عنه، وتملؤها الخريطة والرابط.
   The coordinates folded away: they are the last thing anybody is asked, and the
   map and the link fill them in. */
.pt-place-exact > summary {
  cursor: pointer;
  font-size: 11.5px;
  font-weight: 700;
  color: var(--fg-2);
  padding-block: 4px;
}
.pt-place-exact > summary:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.pt-place-exact[open] > summary { margin-block-end: var(--sp-3); }
.pt-place-exact .pt-hint { margin-block-start: 6px; }
/* رفّ المعاينة: ما اختاره الناشر، مرئيّاً قبل أن يُرسل.
   -------------------------------------------------------------------------
   وكان الحقل يقول «اخترت ٦ ملفات» ولا شيء غير ذلك — فمن رفع ستّ صورٍ من هاتفٍ
   فيه ألفان لا يعرف أيّها اختار، ولا أن واحدةً منها انقلبت على جنبها، إلا بعد
   أن يُنشر إعلانه.

   The preview shelf: what the publisher picked, seen before it is sent.

   The field used to say "you picked 6 files" and no more — so whoever uploaded
   six photographs from a telephone holding two thousand did not know which they
   picked, nor that one of them came out on its side, until after their listing
   was up.

   والشبكة تنكسر بنفسها: عددُ الأعمدة من العرض المتاح لا من رقمٍ مكتوب، فستّ
   صور على شاشةٍ عريضة وثلاثٌ على هاتف. */
/* والصينيّة تحمل السطر والرفّ معاً، وموضعها بعد الوسم ‎<label>‎ لا داخله: كل
   نقرةٍ داخل الوسم تفتح نافذة الاختيار، فزرُّ حذفٍ داخله يحذف الصورة ويفتح
   النافذة في النقرة الواحدة.

   The tray carries the line and the shelf together, and sits after the <label>
   rather than inside it: every press inside that element opens the picker, so a
   remove button within would drop the photograph and open the picker on the one
   press. */
.pt-media-tray { display: grid; }

/* وسطرٌ فارغ لا يأخذ فراغاً: الصينيّة تُبنى مرّةً وتبقى، وسطراها يُملآن ويُفرَّغان
   مع كل اختيار — فلو بقي الفارغ ظاهراً لفتح تحت الحقل بياضاً بلا سبب.
   An empty line takes no room: the tray is built once and stays, its two lines
   filling and emptying with each pick — and were the empty one to keep its box
   it would open a gap under the field for no reason. */
.pt-field-note:empty,
.pt-media-shelf:empty { display: none; }

/* والقاعدة أخفتُ من العدد: العددُ خبرٌ يتغيّر ويُقرأ كل مرّة، والقاعدةُ تُقرأ
   مرّةً وتُعرَف.
   The rule is quieter than the count: the count is news that changes and is read
   each time, the rule is read once and known. */
.pt-field-note--soft { margin-block-start: 2px; font-weight: 500; }

.pt-media-shelf {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
  gap: 8px;
  margin-block-start: 10px;
}

.pt-media-item {
  position: relative;
  margin: 0;
  display: grid;
  gap: 4px;
  min-width: 0;
}

/* وزرُّ الحذف فوق الصورة لا تحتها: الرفّ شبكةٌ تنكسر بنفسها، وزرٌّ في السطر
   يزيح ما بعده كلَّما ظهر. وهو ظاهرٌ دائماً لا عند التمرير وحده — فلا تمرير في
   الهاتف، وهناك تُختار الصور.

   The drop button sits over the photograph rather than beneath it: the shelf is
   a grid that breaks by itself, and a button in the flow shifts everything after
   it each time it appears. And it shows always rather than on hover alone —
   there is no hovering on a telephone, and that is where photographs are
   picked. */
/* وكانت خلفيّته ‎--surface-1‎: صبغةٌ ذهبيّةٌ شفافيّتُها ستّة في المئة. وهي على
   بطاقةٍ بيضاء تُقرأ سطحاً خافتاً، أمّا هنا فتحتها صورةٌ لا لون لها معروف —
   فالزرّ يذوب في الصورة، وعلامةُ الضرب فيه بلونٍ ثانويٍّ باهت فوق ما اتّفق أن
   يكون تحتها: سماءٌ فاتحة فلا تُرى، أو جدارٌ مزخرف فلا تُقرأ.

   وما يطفو فوق صورةٍ لا يستعير لونها: له سطحُه المصمت. فالخلفيّة ‎--surface‎ —
   وهي البياض الخالص نهاراً كما طُلب، والبنّيُّ المعتم ليلاً — والحبر ‎--fg-1‎
   أقوى ما في اللوحة، لا الثانويّ. ثمّ حدٌّ وظلٌّ يفصلانه عمّا تحته: فالأبيض
   على صورةٍ بيضاء بلا حافّةٍ ليس زرّاً.

   Its background was --surface-1: a gold tint at six percent. On a white card
   that reads as a faint surface; here what lies beneath it is a photograph of no
   known colour — so the button dissolves into the picture, and its multiplication
   sign, in a pale secondary ink, falls over whatever happens to be under it: a
   bright sky where it cannot be seen, a patterned wall where it cannot be read.

   What floats over a photograph does not borrow its colour: it keeps its own
   solid surface. So the background is --surface — the plain white by day exactly
   as asked, the opaque brown by night — and the ink is --fg-1, the strongest in
   the palette rather than the secondary one. Then a border and a shadow part it
   from what lies beneath: white on a white photograph with no edge is not a
   button. */
.pt-media-drop {
  position: absolute;
  inset-block-start: 4px;
  inset-inline-end: 4px;
  display: grid;
  place-items: center;
  width: 22px;
  height: 22px;
  padding: 0;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-circle);
  background: var(--surface);
  color: var(--fg-1);
  font-size: 15px;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  box-shadow: 0 1px 3px rgb(0 0 0 / 0.3);
}

/* وفي الليل ينقلب عمل الاثنين: الظلُّ الأسود حول قرصٍ داكنٍ فوق صورةٍ داكنة لا
   يفصل شيئاً، فالحدُّ وحده هو الحافّة — ولذلك يُرفع إلى النصف بدل ثلث.
   By night the two swap their work: a black shadow around a dark disc over a
   dark photograph parts nothing, so the border alone is the edge — and it is
   raised to a half from a third. */
.portal-theme[data-theme="dark"] .pt-media-drop {
  border-color: rgb(242 229 202 / 0.5);
  box-shadow: 0 1px 4px rgb(0 0 0 / 0.6);
}

.pt-media-drop:hover {
  border-color: var(--red);
  background: var(--red);
  color: var(--on-red);
}
.portal-theme[data-theme="dark"] .pt-media-drop:hover { border-color: var(--red); }
.pt-media-drop:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; }

/* والنسبة ثابتة: صورٌ بأبعادٍ مختلفة في رفٍّ واحد تجعل السطر متعرّجاً، و‎cover‎
   يملأ الإطار بلا تشويه.
   A fixed ratio: photographs of differing proportions on one shelf make a jagged
   row, and cover fills the frame without distorting. */
.pt-media-item img,
.pt-media-item video {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  display: block;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-2, rgb(0 0 0 / 0.05));
}

/* والمستند لا صورة له: بطاقةٌ باسمه تُفتح بالنقر.
   A document has no picture: a card bearing its name that opens on a press. */
.pt-media-doc {
  display: grid;
  place-items: center;
  width: 100%;
  aspect-ratio: 1 / 1;
  padding: 6px;
  border: 1px dashed var(--line-strong, var(--line));
  border-radius: var(--r-md);
  background: var(--surface-1, rgb(0 0 0 / 0.03));
  font-size: 10.5px;
  line-height: 1.3;
  text-align: center;
  overflow: hidden;
  word-break: break-all;
}

.pt-media-item figcaption {
  font-size: 10.5px;
  color: var(--fg-3);
  text-align: center;
  direction: ltr;
}

.pt-hint {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: var(--sp-3);
  border: 1px dashed var(--line);
  border-radius: var(--r-md);
  background: var(--surface-1);
  font-size: 12px;
  line-height: 1.7;
  color: var(--fg-2);
}
.pt-hint svg { flex: none; margin-block-start: 3px; color: var(--gold); }
.pt-hint b { letter-spacing: 0.06em; color: var(--fg-1); }

.pt-form-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding-block-start: var(--sp-2);
}

.pt-reset { font-size: 12px; font-weight: 600; color: var(--fg-3); text-decoration: underline; }

/* رقمٌ يفتح واتساب يُرى رابطاً — a number that opens WhatsApp reads as a link. */
.pt-wa { display: inline-flex; align-items: center; gap: 4px; color: var(--gold); text-decoration: underline; text-underline-offset: 3px; }
.pt-wa:hover { text-decoration-thickness: 2px; }
.pt-wa .ico { flex: none; }
.pt-reset:hover { color: var(--red); }

/* الفعل الرئيسي أخضر: هو ما يمضي بالزبون قُدماً — بحثاً أو حفظاً أو إضافة.
   The primary action is green: it is what takes the customer forward — a
   search, a save, an addition. */
/* الارتفاع أدنى لا ثابت. كان height: 38px، فأي نصٍّ يلتفّ إلى سطرين يخرج من
   الزر بدل أن يوسّعه — وهو ما كسر زر التعليق حين كانت لافتته ثلاث كلمات. وأدنى
   ارتفاعٍ يعطي الشكل نفسه تماماً في السطر الواحد (38px كما كان)، ويترك الزر
   ينمو وحده متى طالت اللافتة في لغةٍ أو ترجمةٍ قادمة بدل أن ينكسر.

   A minimum height rather than a fixed one. It was height: 38px, so any label
   wrapping to a second line left the button instead of growing it — which is
   what broke the comment button while its label was three words. A minimum
   gives an identical shape on one line (38px, as before) and lets the button
   grow by itself whenever a label runs long in some language or future
   translation, rather than breaking. */
.pt-submit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  min-height: 38px;
  padding-block: 6px;
  padding-inline: 22px;
  border: 1px solid var(--green);
  border-radius: var(--r-md);
  background: var(--green);
  color: var(--on-green);
  font-size: 13px;
  font-weight: 650;
  cursor: pointer;
}
.pt-submit:hover { filter: brightness(0.93); }
.pt-submit.is-block { width: 100%; }

/* زرُّ الهوية الرقمية — UAE PASS's sign-in button.
   ------------------------------------------------------------------------
   يتبع دليلَ أزرارها (‎UAE PASS Buttons Guide v2‎) لا ألوانَ المنصّة: الأسود
   على الخلفيات الفاتحة، والعنوانُ أبيض، والشعارُ أبيضُ بأخضره وأحمره كما هو
   في ملفّاتها. والارتفاعُ ٤٤ كما في ملفّاتها وفوق الحدّ الأدنى ٣٠، والعرضُ لا
   ينزل عن ١٤٠. وحالاتُه من ملفّاتها أيضاً: المضغوط ‎#383838‎، والتركيز حلقةٌ
   من أخضرها بشفافية ٣٠٪. والزاويةُ زاويةُ أزرار المنصّة، والدليلُ يأذن بذلك.

   It follows UAE PASS's button guide (v2) rather than the platform's colours:
   black on light backgrounds, a white title, and the logo white with its green
   and red as in their files. The height is 44 as in their files and above the
   30 minimum, and the width does not drop below 140. Its states come from their
   files too: pressed is #383838, and focus is a ring of their green at 30%. The
   corner is the platform's own button corner, which the guide allows. */
.pt-uaepass {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  inline-size: 100%;
  min-inline-size: 140px;
  min-block-size: 44px;
  padding-block: 10px;
  padding-inline: 18px;
  border: 1px solid #000;
  border-radius: var(--r-md);
  background: #000;
  color: #fff;
  font-size: 16px;
  line-height: 1.2;
  text-decoration: none;
  box-shadow: 0 2px 2px rgba(0, 0, 0, 0.12), 0 0 2px rgba(0, 0, 0, 0.12);
}
.pt-uaepass:hover { color: #fff; }
.pt-uaepass:active { background: #383838; border-color: #231f20; }
.pt-uaepass:focus-visible { outline: 3px solid rgba(0, 172, 117, 0.3); outline-offset: 2px; }
.pt-uaepass img { flex: none; inline-size: 24px; block-size: 24px; }

/* والهامشُ تحته: الدليل يطلب عُشرَ الارتفاع حدّاً أدنى، وكان الزرُّ يلتصق بسطر
   الشروط تحته. The margin below it: the guide asks a tenth of the height at
   least, and the button sat flush against the terms line below it. */
.pt-uaepass { margin-block-end: var(--sp-4); }

/* وفي السمة الداكنة الأبيض: الدليل يمنع الأسود على الخلفيات الداكنة. والعنوانُ
   أسود، والشعارُ أسودُ بأخضره وأحمره من ملفّاتها، والمضغوط ‎#F6F6F6‎ منها كذلك.
   And in the dark theme the white style: the guide forbids black on dark
   backgrounds. The title is black, the logo black with its green and red from
   their files, and pressed is their #F6F6F6. */
.pt-uaepass .pt-uaepass-on-white { display: none; }
.portal-theme[data-theme="dark"] .pt-uaepass {
  border-color: #231f20;
  background: #fff;
  color: #000;
}
.portal-theme[data-theme="dark"] .pt-uaepass:hover { color: #000; }
.portal-theme[data-theme="dark"] .pt-uaepass:active { background: #f6f6f6; }
.portal-theme[data-theme="dark"] .pt-uaepass .pt-uaepass-on-black { display: none; }
.portal-theme[data-theme="dark"] .pt-uaepass .pt-uaepass-on-white { display: block; }

/* «أو» بين البابين، كما في صفحات الدليل.
   "Or" between the two doors, as on the guide's pages. */
.pt-or {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-block: var(--sp-4);
  color: var(--fg-3);
  font-size: 13px;
}
.pt-or::before,
.pt-or::after { content: ""; flex: 1; border-block-start: 1px solid var(--line); }

.pt-upload {
  display: grid;
  justify-items: center;
  gap: 9px;
  padding: var(--sp-6) var(--sp-4);
  border: 1px dashed var(--line-strong);
  border-radius: var(--r-lg);
  text-align: center;
}
/* والإطار يقصّ ما فيه — the frame clips what it holds.
   ------------------------------------------------------------------------
   وكان دائرةً قطرها ٥٤ بكسل بلا ‎overflow‎، وفيها ‎<img width="72" height="72">‎.
   فالصورة تخرج من الدائرة تسعةَ بكسلات من كل جهة، مربّعةً على دائرة، وتُسحَب
   إلى ٧٢×٧٢ مهما كانت نسبتها — فصورةٌ عرضية تُضغط وصورةُ وجهٍ طولية تُمطّ.
   وليس في الورقة ‎img { max-width }‎ يردّها، فحجمُ ما يُرفع لا يحدّه شيء.

   والعلاج ثلاثة: الإطار يقصّ، والصورة تملؤه لا تحمل مقاسها، و‎cover‎ يقتطع من
   الزائد بدل أن يشوّه. فما يُرفع — أيّاً كانت أبعاده — يُرى دائرةً واحدة.

   It was a fifty-four-pixel circle with no overflow, holding an <img
   width="72" height="72">. The photograph left the circle by nine pixels on
   every side, square over round, and was pulled to 72×72 whatever its
   proportion — a landscape shot squeezed, a portrait face stretched. No
   img { max-width } anywhere on the sheet held it back, so nothing bounded
   the size of what was uploaded.

   Three parts to the remedy: the frame clips, the photograph fills it instead
   of carrying its own measurements, and cover trims the surplus rather than
   distorting. Whatever is uploaded, of whatever dimensions, is seen as one
   circle. */
.pt-upload-face {
  display: grid;
  place-items: center;
  width: 54px;
  height: 54px;
  overflow: hidden;
  border-radius: var(--r-circle);
  background: var(--surface-2);
  color: var(--gold);
}

.pt-upload-face img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* وصورةٌ مرفوعة تستحق أن تُرى: من رفع وجهه للتوثيق يريد أن يتبيّن أيّ صورةٍ
   عنده الآن، و٥٤ بكسل تكفي أيقونةَ آلةِ تصوير ولا تكفي وجهاً. والدائرة الفارغة
   تبقى على قياسها، فلا يتضخّم الإطارُ حول لا شيء.

   An uploaded photograph deserves to be seen: whoever sent their face up for
   verification wants to make out which picture is on file now, and fifty-four
   pixels are enough for a camera glyph but not for a face. The empty circle
   keeps its measure, so the frame does not swell around nothing. */
.pt-upload-face:has(img) {
  width: 96px;
  height: 96px;
  box-shadow: 0 0 0 1px var(--line);
}

.pt-upload b { font-size: 13px; }
.pt-upload p { font-size: 11.5px; color: var(--fg-3); max-width: 340px; }

/* ------------------------------------------------------------- Calendar */
.pt-cal { display: grid; gap: var(--sp-4); }

.pt-cal-nav { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); }
.pt-cal-nav b { font-size: 12.5px; font-weight: 650; }

.pt-cal-month h4 {
  padding-block-end: 7px;
  font-size: 12.5px;
  font-weight: 650;
  text-align: center;
}

.pt-cal-grid { table-layout: fixed; }
.pt-cal-grid th {
  padding-block: 4px;
  font-size: 10px;
  font-weight: 700;
  color: var(--fg-3);
}
.pt-cal-grid td { padding: 1px; text-align: center; }

.pt-day {
  display: grid;
  place-items: center;
  height: 32px;
  border-radius: var(--r-sm);
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: var(--fg-1);
}
a.pt-day:hover { background: var(--surface-2); }
.pt-day.is-blank { visibility: hidden; }
.pt-day.is-past { color: var(--fg-4); text-decoration: line-through; }
.pt-day[data-edge] { background: var(--gold); color: var(--on-gold); font-weight: 700; }
.pt-day[data-span] { background: var(--surface-2); }

/* -------------------------------------------------------- Split settings */
.pt-split { display: grid; grid-template-columns: 220px 1fr; gap: var(--sp-4); align-items: start; }

.pt-side {
  display: grid;
  gap: 2px;
  padding: var(--sp-2);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
}
.pt-side-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 11px;
  border-radius: var(--r-md);
  font-size: 12.5px;
  font-weight: 600;
  color: var(--fg-2);
}
.pt-side-item:hover { background: var(--surface-1); color: var(--fg-1); }
.pt-side-item[aria-current="page"] {
  background: var(--surface-2);
  color: var(--fg-1);
  box-shadow: inset 3px 0 0 var(--gold);
}
[dir="rtl"] .pt-side-item[aria-current="page"] { box-shadow: inset -3px 0 0 var(--gold); }

.pt-panel {
  padding: var(--sp-5);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
}
.pt-panel-title {
  padding-block-end: var(--sp-4);
  font-size: 15px;
  font-weight: 650;
  text-align: center;
}
.pt-panel-title + .pt-panel-title { padding-block-start: var(--sp-5); }

/* ولوحان في صفحةٍ واحدة يفترقان: كانا يتلاصقان حدّاً بحدّ، فتُقرأ الثلاثة
   لوحاً واحداً مقسوماً بخطوط لا ثلاثة أقسام.
   Two panels on one page stand apart: they used to meet edge to edge, so three
   of them read as one panel ruled into strips rather than as three sections. */
.pt-panel + .pt-panel { margin-block-start: var(--sp-4); }

/* ----------------------------------------------------------- The packages */
/* صفحة الباقات — the packages page.
   -------------------------------------------------------------------------
   كانت الباقات تُرسم بصفوف القوائم: ‎.pt-list--boxed‎ وداخلها ‎.pt-row‎. وذلك
   وسمُ سطرٍ يُقرأ ويُضغط — عنوانٌ وسطرٌ صغير تحته وسهمٌ في آخره — لا وسمُ بطاقة
   سعرٍ تُوازَن بأخواتها. فكان ما يُرى: السعر في سطرٍ رماديّ مقصوصٍ بثلاث نقاط،
   وزرُّ الشراء طافياً في أقصى السطر بعيداً عمّا يشتريه، وكلُّ ميزةٍ صفّاً بعرض
   الصفحة بينها خطٌّ منقّط — وعلامةُ الصحّ فوق كلمتها لا بجانبها، لأن
   ‎img, svg { display: block }‎ في ‎style.css‎ تجعل كلّ رمزٍ كتلةً تُنزل ما بعدها
   سطراً.

   The packages used to be drawn with list rows: .pt-list--boxed with .pt-row
   inside it. That is the markup of a line to be read and pressed — a title, a
   small line beneath, an arrow at its end — not of a price card weighed against
   its sisters. What it produced: the price in a grey line clipped with an
   ellipsis, the buy button floating at the far end of the row away from what it
   buys, every feature a full-width strip ruled off with a dotted line — and the
   check mark above its word rather than beside it, because img, svg { display:
   block } in style.css makes every mark a block that pushes what follows onto
   the next line.

   وهذه شبكة بطاقات: البطاقة عمودٌ واحد يُقرأ من أعلى إلى أسفل — الاسم، ثم
   السعر كبيراً بالأخضر لأنه مالٌ وهو أوّل ما يُسأل عنه، ثم لمن هذه الباقة، ثم
   الكمّيتان المبيعتان رقمين، ثم ما تزيده، ثم الزرّ في القاع. والأزرار كلّها في
   سطرٍ واحد مهما اختلفت أطوال البطاقات، لأن ‎margin-block-start: auto‎ تدفعها
   إلى الأسفل.

   This is a grid of cards instead. A card is one column read top to bottom —
   the name, then the price large and in green because it is money and the first
   thing asked, then who the package is for, then the two quantities being sold
   as figures, then what it adds, then the button at the foot. Every button sits
   on one line however unequal the cards, because margin-block-start: auto pushes
   it down. */
/* وصفحة الباقات بعرض المنصة لا بعرض عمود المستند.
   فـ‎.pt-page--narrow‎ سبعمئة وستّون بكسلاً، وهي مقاس صفحةٍ تُقرأ سطراً سطراً:
   نموذجٌ يُملأ، أو نصٌّ نظاميّ. والباقات لا تُقرأ سطراً سطراً بل تُوازن، وأربعُ
   بطاقاتٍ في سبعمئة وستّين تصير مئةً وخمسة وسبعين بكسلاً للواحدة — فتنكسر كلّ
   جملةٍ فيها أربعة أسطر. فالصفحة هنا ‎.pt-page‎ وحدها: ألفٌ ومئةٌ وثمانون.

   The packages page takes the platform's width rather than the document
   column's. .pt-page--narrow is 760px, the measure of a page read line by
   line: a form to fill in, or a piece of regulatory text. Packages are not
   read line by line but weighed against each other, and four cards in 760
   leaves 175px each — every sentence in them breaking over four lines. So the
   page here is .pt-page alone: 1,180. */
.pt-page--plans { padding-block-start: var(--sp-6); }

/* والباقات الأربع في صفٍّ واحد، تُقرأ بنظرةٍ واحدة كما تُقرأ قائمةُ أسعار.

   و‎auto-fit‎ وحدها لا تكفي: بين الشاشتين عرضٌ يتّسع لثلاثة أعمدة لا أربعة،
   فتهبط الرابعة وحدها إلى سطرٍ تحتها — بطاقةٌ يتيمة بعرض الربع تُقرأ خطأً في
   الصفحة قبل أن تُقرأ صفّاً ثانياً. فالعرض الذي يسع ثلاثة يُقسم اثنين اثنين،
   والهاتف واحداً واحداً.

   The four packages stand on one row, read at a glance as a price list is
   read.

   auto-fit alone will not do it: between the two screens lies a width that
   holds three columns and not four, and the fourth card drops alone to a row
   of its own — an orphan a quarter of the page wide, read as a fault before it
   is read as a second row. So a width that holds three is split two and two,
   and the phone one and one. */
.pt-tiers {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--sp-4);
}

.pt-tier {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding: var(--sp-5) var(--sp-4) var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.pt-tier:hover { border-color: var(--gold); box-shadow: var(--shadow-2); }

/* وستّة صفوفٍ تملكها الشبكة لا البطاقة: الاسم بحذاء الاسم، والسعر بحذاء السعر،
   وصندوق الأرقام بحذاء أخيه، والزرّ بحذاء الزرّ.

   وكانت كلُّ بطاقةٍ عموداً يرصف محتواه بنفسه، فطولُ الوصف يزيح صندوقَها وحدها
   وعددُ الميزات يزيح ما تحته — فلا يستوي سطران في بطاقتين. و‎subgrid‎ يجعل
   الصفوف الستّة للصفّ كلّه: ارتفاعُ كلّ صفٍّ أطولُ ما فيه من البطاقات، وتقف
   البطاقات عليه جميعاً.

   Six rows owned by the grid rather than by the card: name level with name,
   price with price, the figures box with its brother, the button with the
   button.

   Each card used to be a column laying out its own content, so the length of a
   description shifted that card's box alone and the number of features shifted
   whatever came under it — and no two lines stood level across two cards.
   subgrid gives the six rows to the whole row of cards: each row's height is
   the tallest card's, and every card stands on it. */
@supports (grid-template-rows: subgrid) {
  .pt-tier {
    display: grid;
    grid-template-rows: subgrid;
    grid-row: span 6;
    row-gap: var(--sp-4);
  }
}

/* والأكثر اشتراكاً يُعلَّم بالذهبي لا بالأحمر: الذهبيّ اختيارٌ وهوية، والأحمر
   خطرٌ أو خروج — و‎.pt-new‎ التي كانت هنا رقاقةٌ حمراء تنادي «جديد».
   The most-subscribed is marked in gold rather than red: gold is selection and
   identity, red is danger or leaving — and .pt-new, which stood here, is the red
   chip that calls "new". */
.pt-tier--popular { border-color: var(--gold); box-shadow: var(--shadow-1); }
.pt-tier--popular::before {
  content: "";
  position: absolute;
  inset-block-start: -1px;
  inset-inline: -1px;
  height: 3px;
  border-start-start-radius: var(--r-lg);
  border-start-end-radius: var(--r-lg);
  background: var(--gold);
}

/* والشارة معلَّقةٌ على حافّة البطاقة لا صفٌّ فوق عنوانها: كانت عنصراً في
   العمود، فتدفع اسمَ الباقة الأكثر اشتراكاً وسعرَها وكلَّ ما تحتهما نزولاً
   بقدر ارتفاعها، فتُقرأ البطاقة المميَّزة مكسورةً بين أخواتها. وخروجُها من
   السياق يردّ عنوانها إلى سطر إخوته، وتبقى هي حيث كانت تُرى.
   The flag hangs on the card's edge rather than standing as a row above its
   title: it used to be an item in the column, pushing the most-subscribed
   package's name, price and everything beneath them down by its own height, so
   the marked card read as broken among its sisters. Taking it out of flow
   returns its title to its siblings' line and leaves the flag where it was
   seen. */
.pt-tier-flag {
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: var(--sp-4);
  transform: translateY(-50%);
  padding: 2px 8px;
  border-radius: var(--r-pill);
  background: var(--gold);
  color: var(--on-gold);
  font-size: 10px;
  font-style: normal;
  font-weight: 700;
  line-height: 1.8;
  letter-spacing: 0.03em;
}

.pt-tier-name { font-size: 15.5px; font-weight: 650; color: var(--fg-1); }

/* والسعر رقمٌ كبير لا سطرٌ في جملة، والدورة بجانبه بخطٍّ خافت: «كم؟» سؤالٌ
   يُجاب قبل أن تُقرأ كلمة.
   The price is a large figure rather than a clause in a sentence, with the
   period quiet beside it: "how much?" is answered before a word is read. */
.pt-tier-price {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 7px;
  margin-block-start: -4px;
}
.pt-tier-price .money {
  font-size: 25px;
  font-weight: 700;
  letter-spacing: var(--tr-lg);
  color: var(--green);
  font-variant-numeric: tabular-nums;
}
.pt-tier-price small { font-size: 11.5px; font-weight: 600; color: var(--fg-3); }

.pt-tier-note { font-size: 12px; line-height: 1.65; color: var(--fg-3); }

/* والكمّيتان من عمودَي الجدول لا من نصٍّ مكتوب باليد: المساحات والمقاعد هما ما
   يُباع، فيُقرآن رقمين كبيرين لا سطراً في وصف.
   The two quantities come from the table's own columns rather than from
   hand-written text: spaces and seats are what is sold, so they are read as two
   figures rather than as a clause in a description. */
.pt-tier-quota {
  display: grid;
  grid-template-columns: 1fr 1fr;
  margin: 0;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-1);
  overflow: hidden;
}
.pt-tier-quota > div {
  display: flex;
  flex-direction: column-reverse;
  gap: 2px;
  padding: 9px var(--sp-1);
  text-align: center;
}
.pt-tier-quota > div + div { border-inline-start: 1px solid var(--line); }
.pt-tier-quota dt { font-size: 10.5px; font-weight: 600; color: var(--fg-3); }
.pt-tier-quota dd {
  margin: 0;
  font-size: 15px;
  font-weight: 700;
  color: var(--fg-1);
  font-variant-numeric: tabular-nums;
}

/* وعلامة الصحّ في عمودٍ خاصّ بها إلى جانب الكلمة لا فوقها: الشبكة تعطيها عرضاً
   ثابتاً فتستوي رؤوس السطور مهما طالت الجملة ولفّت سطرين.
   The check sits in a column of its own beside the word rather than above it:
   the grid gives it a fixed width, so the line heads stay flush however long the
   sentence and however many lines it wraps to. */
.pt-tier-features { display: grid; gap: 8px; }
.pt-tier-features li {
  display: grid;
  grid-template-columns: 14px minmax(0, 1fr);
  align-items: start;
  gap: 8px;
  font-size: 12px;
  line-height: 1.6;
  color: var(--fg-2);
}
.pt-tier-features svg { margin-block-start: 3px; color: var(--green); }

.pt-tier-do { margin-block-start: auto; padding-block-start: var(--sp-2); }
.pt-tier-do .btn { width: 100%; }

/* والباقة القائمة لا زرَّ لها بل علامة: ما لا يُضغط لا يُرسم زرّاً.
   The standing package has a mark rather than a button: what is not pressed is
   not drawn as a button. */
.pt-tier-standing {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: 36px;
  border: 1px dashed var(--line-strong);
  border-radius: var(--r-md);
  background: var(--surface-1);
  font-size: 12.5px;
  font-weight: 650;
  color: var(--fg-2);
}
.pt-tier-standing svg { color: var(--green); }

.pt-tier--mine { border-color: var(--line-strong); background: var(--surface-1); }

/* ------------------------------------------------------- What I have left */
/* والمساحاتُ شريطٌ لا رقم: «٣١٢ من ٤٠٠» رقمان يُقرآن، والشريط يُرى. وكان
   التعليق في القالب يعد بشريطٍ ولا شريط في الصفحة — سطران في قائمةٍ محاطة.
   The spaces are a bar rather than a figure: "312 of 400" is two numbers to be
   read and a bar is seen. The comment in the template promised a bar and the
   page had none — two rows in a boxed list. */
.pt-mine { display: grid; gap: var(--sp-4); }

.pt-mine-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-3);
  padding-block-end: var(--sp-3);
  border-block-end: 1px solid var(--line);
}
.pt-mine-head b { font-size: 15px; font-weight: 650; }
.pt-mine-head i { font-style: normal; font-size: 11.5px; color: var(--fg-3); }

/* وشريط البوابة ذهبيّ لا حبريّ، ويحمرّ حين يمتلئ: الأحمر خطر، وامتلاء المساحات
   هو ما يمنع النشر.
   The portal's bar is gold rather than ink, and turns red when it fills: red is
   danger, and a full allowance is what stops a publication. */
.portal-theme .bar > i { background: var(--gold); }
.portal-theme .bar--full > i { background: var(--red); }

/* والمقياسان جنباً إلى جنب حين يتّسع اللوح: شريطٌ بعرض ألف بكسل لمئةٍ وسبعةٍ
   وثلاثين من أربعمئة يقيس الفراغ أكثر ممّا يقيس المستهلَك.
   The two meters stand side by side where the panel is wide: a bar a thousand
   pixels long for 137 of 400 measures the emptiness more than it measures what
   is spent. */
.pt-meters {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: var(--sp-4) var(--sp-5);
}
.pt-meters .meter-row + .meter-row { margin-block-start: 0; }
.pt-meters .meter-head b { font-size: 12.5px; color: var(--fg-2); }

.pt-mine-do { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3); }
.pt-mine-state { font-style: normal; font-size: 11.5px; color: var(--fg-3); }

/* ---------------------------------------------------------------- Seats */
/* والمقاعد جدولٌ كجداول البوابة: لكلّ حقيقةٍ عمودٌ يُقرأ نزولاً.
   |
   | وكانت صفوفاً محاطة، كلُّ صفٍّ صندوقٌ فيه اسمٌ ورقاقتان وزرّان. وذلك يُقرأ
   | واحداً واحداً: من أراد أن يعرف من في فريقه محاسبٌ ومن مسوّق قرأ الصناديق
   | صندوقاً صندوقاً بدل أن ينزل بعينه في عمود. ومكتبٌ بعشرين مقعداً يقرأ عشرين
   | بطاقة.
   |
   | The seats are a table like the portal's other tables: a column for each fact,
   | read downwards.
   |
   | They used to be boxed rows, each a card holding a name, two chips and two
   | buttons. That reads one at a time: whoever wanted to know which of their team
   | is an accountant and which a marketer read the boxes one by one instead of
   | running their eye down a column. An office with twenty seats reads twenty
   | cards.
   |
   | وهو ‎table.data‎ نفسه الذي في صفحة الأنشطة، بلا ورقة أنماطٍ ثانية: الجدول
   | يلفّ في ‎.table-wrap‎ فيتمرّر عرضاً على الهاتف بدل أن ينكسر.
   |
   | It is the same table.data the activity page uses, with no second stylesheet:
   | the table sits in .table-wrap, so it scrolls sideways on a phone rather than
   | breaking up. */
/* وخمسةُ أعمدةٍ لها أرضيّةُ عرضٍ تخصّها: ‎560‎ التي تكفي جدولاً من ثلاثة تعصر
   الخمسة فيلتفّ الاسم على ثلاثة أسطر والزرّ على سطرين. والأرضية تجعل اللوح
   يتمرّر عرضاً بدل أن يعصر — و‎.table-wrap‎ موضوعٌ لهذا.
   Five columns have a width floor of their own: the 560 that suffices for a
   table of three squeezes five, so the name wraps over three lines and the
   button over two. The floor makes the panel scroll sideways rather than
   squeeze — which is what .table-wrap is there for. */
/* وبين نموذج الدعوة والجدول فاصلٌ يُرى.
   |
   | كان رأسُ الجدول يلتصق بأسفل الحقول، فيُقرأ «الشخص» تسميةً لحقلٍ فوقه لا
   | ترويسةَ عمود. وفراغٌ وحده لا يكفي هنا: فوقه حقولٌ محاطة بإطارات، وتحته
   | صفوفٌ مخطوطة — فلا تعرف العين أين انتهى ما يُكتب وأين ابتدأ ما يُقرأ إلا
   | بخطٍّ يفصلهما.
   |
   | A visible parting between the invitation form and the table.
   |
   | The table's head used to sit against the bottom of the fields, so "person"
   | read as a label for the field above it rather than a column's heading. Space
   | alone does not do it here: above are boxed fields and below are ruled rows, so
   | the eye cannot tell where what is written ends and what is read begins without
   | a line between them. */
.pt-seats-wrap {
  margin-block-start: var(--sp-6);
  padding-block-start: var(--sp-5);
  border-block-start: 1px solid var(--line);
}

.pt-seats { min-width: 820px; }

.pt-seats .pill.good { border-color: var(--green); color: var(--green); }
.pt-seats .pill.wait { border-color: var(--gold); color: var(--gold); }
.pt-seats .pill.dashed { border-style: dashed; }

/* وفاصلٌ بين عمودٍ وعمود.
   |
   | وكانت الأعمدة تفترق بالفراغ وحده: اسمٌ طويل ثم فجوة ثم رقاقة، فلا يُعرف أين
   | انتهى عمودٌ وأين ابتدأ الذي بعده إلا بالتخمين. وجدولٌ خمسةُ أعمدة بلا خطوطٍ
   | بينها يُقرأ سطراً واحداً مبعثراً لا خمسَ حقائق.
   |
   | A rule between one column and the next.
   |
   | The columns used to part on whitespace alone: a long name, then a gap, then a
   | chip — and where one column ended and the next began was a guess. A table of
   | five columns with no lines between them reads as one scattered line rather
   | than five facts.
   |
   | والخطّ خفيف كخطّ الصفوف نفسه، فالشبكة تُقرأ ولا تُرى: جدولٌ محبوسٌ بخطوطٍ
   | سوداء يصير قفصاً.
   |
   | The line is as light as the rows' own, so the grid is read without being
   | looked at: a table caged in black lines becomes a cage. */
.pt-seats thead th + th,
.pt-seats tbody td + td { border-inline-start: 1px solid var(--line-soft); }

/* والرقم عمودٌ قائم بذاته: أرقامٌ جدوليّة تقف خاناتها تحت خاناتها، ولا يلتفّ
   على سطرين.
   The number is a column of its own: tabular figures whose digits stand under
   one another, and it does not wrap over two lines. */
.pt-seat-phone {
  font-variant-numeric: tabular-nums;
  font-size: 12.5px;
  color: var(--fg-2);
  text-align: start;
  white-space: nowrap;
}

.pt-seat-do { display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: var(--sp-2); }
.pt-seat-role { display: flex; align-items: center; gap: 6px; }
.pt-seat-role select {
  min-height: 30px;
  padding: 3px 9px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--field-face);
  color: var(--fg-1);
  font-size: 12px;
}
.pt-seat-role select:focus { outline: 2px solid var(--gold); outline-offset: 1px; }

/* ونموذج الدعوة صفٌّ من ثلاثة حقول وزرّ، لا عمودٌ من أربعة صناديق بعرض اللوح:
   الحقول قصيرة — رقمٌ واسمٌ ودور — وعمودها يمدّ اللوح شاشةً كاملة بلا سبب.
   The invitation form is a row of three fields and a button rather than a column
   of four boxes the width of the panel: the fields are short — a number, a name
   and a role — and a column of them stretches the panel a whole screen for
   nothing. */
.pt-invite {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  align-items: end;
  gap: var(--sp-3);
}
.pt-invite .btn { height: 38px; justify-self: start; min-width: 132px; padding-inline: var(--sp-5); }

.pt-invite-call {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--sp-3);
}
.pt-invite-call p { font-size: 12.5px; color: var(--fg-2); }

/* العرض الذي يسع ثلاثة أعمدة يُقسم اثنين اثنين. والرقم مقيس لا مختار:
   أربعة أعمدة أقلُّها مئتا بكسل مع ثلاث فجوات تحتاج ‎848‎ داخل اللوح، وحشوةُ
   اللوح والصفحة معاً ‎88‎ — فدون ‎936‎ لا يتّسع الصفّ لأربع.
   A width that holds three columns is split two and two. The figure is
   measured rather than chosen: four columns of at least 200px with three gaps
   need 848 inside the panel, and the panel's padding with the page's is 88 —
   so under 936 the row does not hold four. */
@media (max-width: 936px) {
  .pt-tiers { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ومن الهاتف واحدةٌ تحت واحدة. وكلُّ صفٍّ من زرَّين أو حقلٍ وزرّ يصير عموداً:
   زرٌّ بعرض نصف شاشةٍ يُضغط بالإبهام، وزرٌّ بعرض ثلاثة أحرفٍ يُخطأ.
   On the phone one under another. Every row of two buttons, or of a field and
   a button, becomes a column: a button half a screen wide is pressed with the
   thumb, and a button three letters wide is missed. */
@media (max-width: 560px) {
  .pt-tiers { grid-template-columns: 1fr; }
  .pt-tier { padding: var(--sp-4); }
  .pt-tier-price .money { font-size: 22px; }

  .pt-mine-head { gap: 2px; }
  .pt-mine-do { flex-direction: column; align-items: stretch; gap: var(--sp-2); }
  .pt-mine-do .btn { width: 100%; }
  .pt-mine-state { text-align: center; }

  .pt-invite { grid-template-columns: 1fr; }
  .pt-invite .btn { justify-self: stretch; width: 100%; }

  /* ولا يُكسر جدولُ المقاعد على الهاتف: ‎.table-wrap‎ يتمرّر به عرضاً كما
     يتمرّر بجداول الأنشطة، والأعمدة تبقى أعمدة.
     The seats table is not broken up on a phone: .table-wrap scrolls it sideways
     as it scrolls the activity tables, and the columns stay columns. */
  .pt-seats .pt-seat-do { gap: var(--sp-1); }
  /* والفعل الوحيد في صفّه يأخذ الصفّ: زرٌّ صغير وحده في سطرٍ فارغ يُقرأ
     بقيّةَ شيء، ويُخطئه الإبهام.
     The only action on its row takes the row: a small button alone on an empty
     line reads as a leftover, and the thumb misses it. */
  .pt-seat-do > form:not(.pt-seat-role) .btn { flex: 1; }

  .pt-invite-call { align-items: stretch; }
  .pt-invite-call form,
  .pt-invite-call .btn { width: 100%; }
}

/* ------------------------------------------------------------ Add listing */
.pt-roles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: var(--sp-3);
  padding-block-end: var(--sp-5);
}
.pt-role {
  display: grid;
  justify-items: center;
  gap: 9px;
  padding: var(--sp-5) var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  color: var(--fg-2);
  transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.pt-role b { font-size: 13px; font-weight: 600; }
.pt-role:hover { border-color: var(--gold); background: var(--surface-1); }
.pt-role[aria-pressed="true"] {
  border-color: var(--gold);
  border-width: 1.5px;
  background: var(--surface-2);
  color: var(--fg-1);
}

/* --------------------------------------------------------------- Drawer */
/* الدرج بلا جافاسكربت جزءٌ من الصفحة يقف في آخرها وتعمل كل روابطه؛ ولا يصير
   لوحاً منزلقاً إلا حين يوجد سكربت يفتحه ويغلقه. فالحالة الافتراضية هي التي
   تعمل دائماً، والانزلاق تحسينٌ فوقها لا شرطٌ لها.

   With no JavaScript the drawer is simply part of the page, sitting at its foot
   with every link working; it becomes a sliding panel only where a script
   exists to open and close it. The default state is the one that always works,
   and the sliding is a refinement on top of it. */
.pt-drawer {
  display: flex;
  flex-direction: column;
  width: 100%;
  border-block-start: 1px solid var(--line);
  background: var(--surface);
}

.pt-scrim { display: none; }

.js .pt-scrim {
  display: block;
  position: fixed;
  inset: 0;
  z-index: 1400;
  background: rgb(44 20 3 / 0.5);
  backdrop-filter: blur(2px);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur) var(--ease), visibility var(--dur);
}
.js .pt[data-drawer="1"] .pt-scrim { opacity: 1; visibility: visible; }

.js .pt-drawer {
  position: fixed;
  inset-block: 0;
  inset-inline-end: 0;
  z-index: 1500;
  width: min(392px, 100vw);
  border-block-start: 0;
  border-inline-start: 1px solid var(--line);
  box-shadow: var(--shadow-3);
  transform: translateX(100%);
  transition: transform var(--dur) var(--ease);
}
.js[dir="rtl"] .pt-drawer { transform: translateX(-100%); }
.js .pt[data-drawer="1"] .pt-drawer { transform: none; }

.pt-drawer-head {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex: none;
  padding: var(--sp-3) var(--sp-4);
  border-block-end: 1px solid var(--line);
}
.pt-drawer-head b { flex: 1; text-align: center; font-size: 14px; font-weight: 650; }

.pt-drawer-body { flex: 1; overflow: auto; padding: var(--sp-4); display: grid; gap: var(--sp-3); }

.pt-drawer-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  flex: none;
  padding: var(--sp-3) var(--sp-4);
  border-block-start: 1px solid var(--line);
}
.pt-build { font-size: 10.5px; color: var(--fg-4); }

.pt-user {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface-1);
}
.pt-user:hover { border-color: var(--gold); }
.pt-user-avatar {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  flex: none;
  border-radius: var(--r-circle);
  background: var(--gold);
  color: var(--on-gold);
  font-size: 13px;
  font-weight: 700;
}
.pt-user-text { flex: 1; display: grid; gap: 1px; min-width: 0; }
.pt-user-text b { font-size: 13px; font-weight: 650; }
.pt-user-text i { font-style: normal; font-size: 11px; color: var(--fg-3); }

.pt-duo { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-2); }
.pt-duo-item {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 10px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  font-size: 12px;
  font-weight: 600;
  color: var(--fg-2);
}
.pt-duo-item:hover { background: var(--surface-1); color: var(--fg-1); }
.pt-duo-item span { flex: 1; }
.pt-duo-item b { font-size: 11px; color: var(--gold); }

.pt-cta {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  height: 42px;
  border-radius: var(--r-md);
  background: var(--green);
  color: var(--on-green);
  font-size: 13.5px;
  font-weight: 650;
}
.pt-cta:hover { filter: brightness(0.93); }

.pt-group {
  display: flex;
  align-items: center;
  gap: 6px;
  padding-block-start: var(--sp-2);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gold);
}

.pt-list { display: grid; }
.pt-list > li + li { border-block-start: 1px dashed var(--line); }
.pt-list--boxed {
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  overflow: hidden;
}
.pt-list--boxed > li + li { border-block-start: 1px solid var(--line); }

.pt-row {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  padding: 11px 6px;
  border: 0;
  background: transparent;
  text-align: start;
  cursor: pointer;
}
.pt-list--boxed .pt-row { padding-inline: var(--sp-3); }
.pt-row:hover { background: var(--surface-1); }

.pt-row-icon {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  flex: none;
  border-radius: var(--r-md);
  background: var(--surface-1);
  color: var(--fg-2);
}
.pt-row:hover .pt-row-icon { color: var(--gold); }
.pt-row-text { flex: 1; display: grid; gap: 1px; min-width: 0; }
.pt-row-text b { display: flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 600; }
.pt-row-text i {
  font-style: normal;
  font-size: 10.5px;
  color: var(--fg-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pt-row-value { font-size: 12px; font-weight: 700; color: var(--green); }
.pt-row-count {
  min-width: 20px;
  padding-inline: 6px;
  border-radius: var(--r-pill);
  background: var(--surface-2);
  color: var(--fg-2);
  font-size: 10.5px;
  font-weight: 700;
  text-align: center;
}

.pt-go { color: var(--fg-4); flex: none; }
.pt-row:hover .pt-go { color: var(--gold); }

/* «جديد» أحمر لأنه نداء لا حالة — كما هو في التطبيق المرجعي.
   "New" is red because it is a call rather than a state, as it is in the
   reference application. */
.pt-new {
  padding: 1px 6px;
  border-radius: var(--r-pill);
  background: var(--red);
  color: var(--on-red);
  font-size: 9px;
  font-style: normal;
  font-weight: 700;
  letter-spacing: 0.04em;
}

.pt-row--exit .pt-row-text b { color: var(--red); }
.pt-row--exit .pt-row-icon { color: var(--red); }

/* ------------------------------------------------ Map pins for the portal */
/* دبوس نصّي: بطاقة صغيرة برأس مدبَّب أسفلها، فتشير إلى النقطة نفسها لا إلى
   جوارها. A text pin: a small card with a point beneath it, so it marks the
   coordinate itself rather than somewhere beside it. */
.gm-tag {
  position: relative;
  display: inline-flex;
  align-items: baseline;
  gap: 3px;
  height: 24px;
  padding-inline: 8px;
  border: 1px solid var(--fg-1);
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--fg-1);
  font-size: 11.5px;
  font-weight: 700;
  white-space: nowrap;
  box-shadow: 0 1px 4px rgb(44 20 3 / 0.3);
  transition: transform 0.16s var(--ease);
}
.gm-tag b { font-weight: 700; font-variant-numeric: tabular-nums; }
.gm-tag small { font-size: 9px; font-weight: 600; opacity: 0.78; }

.gm-tag::after {
  content: "";
  position: absolute;
  inset-block-start: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 4px solid transparent;
  border-block-start-color: var(--fg-1);
}

.gm-tag--soft { border-style: dashed; }

/* لونٌ واحد لدبابيس البوابة كلها: حبر الهوية.

   كانت العروض خضراء والحجوزات ذهبية، وكان ذلك خطأً في المعنى قبل أن يكون خطأً
   في اللون: الأخضر في هذه اللوحة يعني المال والتأكيد، فإذا صُبغ به كل دبوس لم
   يعد يعني شيئاً — والصفحة تعرف أيّ سطح هي أصلاً، فلا تحتاج أن يقوله لها لون
   ثلاثين دبوساً. فصارت الدبابيس كلها بحبر الهوية، وبقي الأخضر للسعر وحده
   حيث يُقرأ رقماً.

   One colour for all the portal's pins: the identity's ink.

   Offers were green and stays gold, and that was wrong in meaning before it was
   wrong in colour: green in this palette means money and confirmation, and once
   every pin is painted with it, it means nothing. The page already knows which
   surface it is on — it does not need thirty pins to tell it. So the pins now
   carry the identity's ink, and green is left to the price alone, where it is
   read as a figure. */
.portal-theme .gm-tag {
  border-color: var(--cocoa);
  background: var(--surface);
  color: var(--fg-1);
  box-shadow: 0 1px 5px rgb(44 20 3 / 0.26);
}
.portal-theme .gm-tag::after { border-block-start-color: var(--cocoa); }

.portal-theme .gm-tag--solid {
  background: var(--cocoa);
  border-color: var(--cocoa);
  color: var(--sand);
}
.portal-theme .gm-tag--solid::after { border-block-start-color: var(--cocoa); }

.portal-theme[data-theme="dark"] .gm-tag {
  border-color: var(--sand);
  background: var(--surface);
  color: #fff;
}
.portal-theme[data-theme="dark"] .gm-tag::after { border-block-start-color: var(--sand); }
.portal-theme[data-theme="dark"] .gm-tag--solid {
  background: #4a2c1c;
  border-color: var(--sand);
  color: #fff;
}
.portal-theme[data-theme="dark"] .gm-tag--solid::after { border-block-start-color: #4a2c1c; }

/* ما سبق أن فُتح يرمدّ. لا يُخفى ولا يُصغَّر — يُقال عنه فقط إنه مقروء، فمن
   يمسح خريطةً فيها ثلاثون عرضاً يعرف أين وصل بنظرة.

   What has been opened turns grey. It is not hidden and not shrunk — it is
   merely said to have been read, so anyone scanning a map of thirty offers can
   see at a glance how far they got. */
.portal-theme .gm-tag[data-seen="1"],
.portal-theme .gm-tag--solid[data-seen="1"] {
  background: var(--seen);
  border-color: var(--seen-line);
  color: var(--seen-ink);
  box-shadow: none;
  font-weight: 600;
}
.portal-theme .gm-tag[data-seen="1"]::after,
.portal-theme .gm-tag--solid[data-seen="1"]::after { border-block-start-color: var(--seen); }

/* التجمّع بالمدن يرث الحبر نفسه — city clusters inherit the same ink. */
.portal-theme .gm-cluster {
  background: var(--cocoa);
  border-color: var(--sand);
  color: var(--sand);
}
.portal-theme .gm-cluster-name { color: var(--fg-1); }
.portal-theme .gm-marker:hover .gm-cluster { background: var(--gold); color: var(--on-gold); }

/* دائرة الإمارة: اسمُها وحده، وقياسٌ واحد لجميعها.
   -------------------------------------------------------------------------
   لا رقم داخلها ولا على حافّتها: الرقم يقول «سبعة» ولا يقول سبعةُ ماذا، وسبعُ
   شاراتٍ على خريطةٍ تُقرأ من فوق ضجيجٌ يزاحم ما جاءت الدائرة لتقوله — أين. ومن
   أراد العدد فتح الإمارة ورأى دبابيسها.

   والقياس واحد لأن اختلافه يُقرأ مقياساً: دائرةٌ أكبر تعني عند الناظر «أهمّ»،
   وهي هنا لم تكن إلا اسماً أطول حروفاً.

   The emirate's circle: its name alone, at one size for all of them.

   No number inside it and none on its rim: a number says "seven" and does not
   say seven of what, and seven badges over a map read from above are noise
   crowding the one thing the circle exists to say — where. Whoever wants the
   count opens the emirate and sees its pins.

   The size is one because a difference in it reads as a measure: a bigger
   circle means "more important" to a reader, and here it meant nothing but a
   name with more letters in it. */
.gm-place-cluster {
  display: grid;
  place-items: center;
  padding: 5px;
  border-radius: var(--r-circle);
  border: 2px solid var(--sand);
  background: var(--cocoa);
  color: var(--sand);
  box-shadow: 0 2px 9px rgb(44 20 3 / 0.32);
  transition: transform 0.16s var(--ease), background var(--dur) var(--ease);
}
.gm-place-cluster b {
  font-size: 10.5px;
  font-weight: 700;
  line-height: 1.25;
  text-align: center;
  overflow-wrap: anywhere;
}

.gm-marker:hover .gm-place-cluster {
  transform: scale(1.06);
  background: var(--olive);
  z-index: 3;
}

.portal-theme[data-theme="dark"] .gm-place-cluster {
  background: #4a2c1c;
  border-color: var(--sand);
  color: #fff;
}

.gm-marker:hover .gm-tag { transform: translateY(-2px) scale(1.04); z-index: 2; }

/* الدائرة التي رسمها الزبون: صندوقٌ مستدير لا مسارُ SVG — انظر ‎.gm-disc‎ في
   ورقة اللوحة. والمقادير هي هي، وإنما تُقال بـ‎background‎ و‎border‎ بدل ‎fill‎
   و‎stroke‎.

   The circle the customer drew: a rounded box rather than an SVG path — see
   .gm-disc in the console's sheet. The quantities are unchanged and are simply
   said with background and border instead of fill and stroke. */
.portal-theme .gm-area {
  background: rgb(197 153 43 / 0.13);
  border: 1.75px dashed var(--gold);
}
.portal-theme .gm-area--draft { background: rgb(197 153 43 / 0.07); }

.mapboxgl-map.is-drawing,
.mapboxgl-map.is-drawing .mapboxgl-canvas-container { cursor: crosshair; }

/* --------------------------------------------------------- Responsive */
/* اللوح المتوسط: ما بين الحاسوب والهاتف. القشرة تبقى كما هي — شريط علوي واحد
   بثلاثة أقسام في وسطه — ويُصلَح فيه العطل الوحيد: الشبكة التي لا تنكمش.
   فالعمود الأوسط بلا min-width يفرض عرض التبويبات على الشريط كله، فيفيض
   الشريط خارج الشاشة ويخرج معه زرّ الحساب. أما شكل الهاتف نفسه فآخر الورقة.

   The middle board: between desktop and phone. The shell stays as it is — one
   top bar with the three sections in its centre — and its single fault is
   repaired: the grid that will not shrink. Without a min-width the middle
   column forces the tabs' width onto the whole bar, which then overflows the
   screen and takes the account button with it. The phone's own shape is at the
   end of this sheet. */
@media (max-width: 900px) {
  .pt-split { grid-template-columns: 1fr; }
  .pt-side { display: flex; overflow-x: auto; scrollbar-width: none; }
  .pt-side-item { white-space: nowrap; }

  .pt-top { padding-inline: var(--sp-3); gap: var(--sp-2); }
  .pt-tabs { min-width: 0; overflow-x: auto; scrollbar-width: none; }
  .pt-tabs::-webkit-scrollbar { display: none; }
  .pt-hide-sm { display: none; }

  .pt-rail { flex-wrap: wrap; }
  .pt-rail-search { max-width: none; flex: 1; }
  .pt-chips { order: 3; flex-basis: 100%; }

  .pt-range-row { grid-template-columns: 1fr; }
  .pt-page { padding-inline: var(--sp-4); }
}

@media (prefers-reduced-motion: reduce) {
  .pt-drawer, .pt-scrim, .pt-sheet-panel, .gm-tag { transition: none; animation: none; }
  .pt-chips { scroll-behavior: auto; }
}


/* =========================================================================
   العرض العادي — the normal view
   -------------------------------------------------------------------------
   السطح الثاني لكل قسم: لا خريطة، بل قائمةٌ أو شبكةٌ تتمرّر بلا نهاية. وهو
   الافتراضي لأن أغلب من يفتح سوق عقار يتصفّح قبل أن يوقّع مكاناً على خريطة.

   Each section's second surface: no map, but a list or a grid that scrolls
   without end. It is the default because most people opening a property market
   browse before they point at a place on a map.
   ========================================================================= */

.pt-browse {
  max-width: 1280px;
  margin-inline: auto;
  padding: 0 var(--sp-4) var(--sp-7);
}

/* الشريط في العرض العادي يجلس في المجرى ولا يطفو، والحشو من الحاوية لا منه.
   In the normal view the rail sits in the flow rather than floating, and the
   padding comes from its container rather than from itself. */
.pt-browse .pt-rail {
  position: sticky;
  inset-block-start: 0;
  z-index: 30;
  flex-wrap: wrap;
  padding: var(--sp-3) 0;
  background: var(--bg);
  border-block-end: 1px solid var(--line-soft);
}
.pt-rail-search.is-set { color: var(--fg-1); box-shadow: var(--lift), 0 0 0 1.5px var(--gold) inset; }

.pt-rail-clear {
  display: inline-grid;
  place-items: center;
  flex: none;
  width: 30px;
  height: 36px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  background: var(--glass);
  color: var(--fg-3);
}
.pt-rail-clear:hover { color: var(--red); border-color: var(--red); }

/* ------------------------------------------------------------- The feed */
.pt-feed { display: grid; gap: var(--sp-3); padding-block-start: var(--sp-4); }

.pt-feed-count {
  display: flex;
  align-items: baseline;
  gap: 5px;
  font-size: 12.5px;
  font-weight: 650;
}

.pt-feed-body--rows { display: grid; gap: var(--sp-2); }

.pt-feed-body--grid {
  display: grid;
  gap: var(--sp-3);
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  align-items: start;
}

/* حالة الفراغ ليست بطاقةً في الشبكة بل بديلٌ عنها كلها: تمتدّ على الأعمدة
   جميعاً فتستقرّ في وسط الصفحة لا في أوّل عمودٍ منها.

   The empty state is not a card in the grid but a stand-in for the whole of
   it: it spans every column, so it settles in the middle of the page rather
   than in the first column of it. */
.pt-feed-body > .empty-state { grid-column: 1 / -1; }

/* الرابط لا يختفي حين يعمل السكربت: هو نفسه ما يراقبه ويضغطه — فما يراه
   الزائر هو ما يجري فعلاً، لا واجهةٌ ثانية فوق واجهة.

   The link does not disappear when the script runs: it is the very thing the
   script watches and presses — so what the visitor sees is what actually
   happens, rather than a second interface over the first. */
.pt-more {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  justify-self: center;
  height: 36px;
  padding-inline: 22px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-pill);
  background: var(--surface);
  color: var(--fg-1);
  font-size: 12.5px;
  font-weight: 650;
}
.pt-more:hover { border-color: var(--gold); background: var(--surface-1); }
.pt-more[data-busy="1"] { opacity: 0.55; pointer-events: none; }

.pt-feed-end {
  justify-self: center;
  padding-block: var(--sp-4);
  font-size: 11.5px;
  color: var(--fg-4);
}

/* --------------------------------------------------------- The row card */
/* صفٌّ في قائمة: العنوان فالسعر فالحقائق فالوصف، والصورة في الطرف المقابل —
   فيُقرأ العمود من أعلى إلى أسفل بلا اعتراض.

   A row in a list: title, price, facts, description, with the image at the
   opposite edge — so the column reads top to bottom uninterrupted. */
.pt-row-hit {
  display: grid;
  grid-template-columns: minmax(0, 1fr) clamp(132px, 22vw, 236px);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  color: inherit;
  overflow: hidden;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.pt-row-hit:hover { border-color: var(--gold); box-shadow: var(--shadow-1); }

.pt-row-main {
  display: grid;
  gap: 6px;
  align-content: start;
  min-width: 0;
  padding: var(--sp-3);
}

.pt-row-title {
  font-size: 13.5px;
  font-weight: 650;
  line-height: 1.55;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.pt-row-desc {
  font-size: 12px;
  color: var(--fg-3);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.pt-row-place {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 11.5px;
  color: var(--fg-3);
}

/* الحقائق برموزها: مساحةٌ وغرفٌ ودوراتُ مياه وصالات في سطر واحد.
   The facts with their glyphs: area, bedrooms, bathrooms and living rooms on
   one line. */
.pt-facts {
  display: flex;
  flex-wrap: wrap;
  gap: 4px var(--sp-4);
  font-size: 12px;
  color: var(--fg-2);
}
.pt-facts li { display: inline-flex; align-items: center; gap: 4px; }
.pt-facts svg { color: var(--fg-3); flex: none; }

/* -------------------------------------------------------- The grid tile */
.pt-tile {
  display: grid;
  grid-template-rows: auto 1fr;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  color: inherit;
  overflow: hidden;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.pt-tile:hover { border-color: var(--gold); box-shadow: var(--shadow-2); }

.pt-tile-foot {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2);
}

.pt-tile-mark {
  display: grid;
  place-items: center;
  flex: none;
  width: 34px;
  height: 34px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface-1);
  color: var(--fg-2);
}

.pt-tile-text { flex: 1; min-width: 0; display: grid; gap: 2px; }

.pt-tile-line {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-2);
}
.pt-tile-line h3 {
  font-size: 12.5px;
  font-weight: 650;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pt-tile-price {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  white-space: nowrap;
  flex: none;
}
.pt-tile-price small { font-size: 10px; color: var(--fg-3); }
.pt-tile-price b { font-size: 12.5px; font-weight: 700; color: var(--green); }

.pt-tile-text p {
  font-size: 11px;
  color: var(--fg-3);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pt-tile-body { display: grid; gap: 5px; align-content: start; padding: var(--sp-2) var(--sp-3) var(--sp-3); }

.pt-tile-title {
  font-size: 12.5px;
  font-weight: 650;
  line-height: 1.55;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.pt-tile-place { font-size: 11px; color: var(--fg-3); }

/* ما سبق أن فُتح يخفت في القائمة كما يرمدّ على الخريطة — بالذاكرة نفسها.
   What has been opened dims in the list as it greys on the map — out of the
   same memory. */
[data-seen-id][data-seen="1"] { border-color: var(--line-soft); }
[data-seen-id][data-seen="1"] .pt-row-title,
[data-seen-id][data-seen="1"] .pt-tile-title,
[data-seen-id][data-seen="1"] .pt-tile-line h3,
[data-seen-id][data-seen="1"] .pt-hit-title { color: var(--fg-3); }
[data-seen-id][data-seen="1"] .pt-shot { filter: grayscale(0.6); }

/* ------------------------------------------------------ Image variants */
.pt-shot--side { aspect-ratio: auto; height: 100%; min-height: 152px; border-radius: 0; }
/* وخانةُ البطاقة بنسبة الصور كذلك، وللسبب نفسه: الشريطان اللذان يظهران في
   الخانة الكبيرة يظهران هنا أصغرَ لا أقلَّ عدداً.
   The card's slot takes the photographs' ratio too, for the same reason: the two
   bars that show in the large slot show here smaller rather than fewer. */
.pt-shot--wide { aspect-ratio: 3 / 2; border-radius: 0; }
/* والخانة الكبيرة بنسبة الصور لا بنسبةٍ مختارة.
   -------------------------------------------------------------------------
   كانت ‎16/10‎ — أي ‎1.6‎ — وآلات التصوير تُخرج ‎3/2‎، أي ‎1.5‎. والفرق بينهما
   ستّةٌ في المئة من العرض تظهر شريطين قائمين على يمين الصورة ويسارها في أكبر
   خانةٍ في الصفحة. وهما شريطان لا يسدّهما تمويهٌ ولا يبرّرهما شيء: الخانة هي
   التي أخطأت النسبة لا الصورة.

   The large slot takes the photographs' ratio rather than a chosen one.

   It was 16/10 — 1.6 — and cameras produce 3/2, which is 1.5. Six per cent of
   the width between them, showing as two upright bars to the right and left of
   the picture in the largest slot on the page. Two bars no blur covers and
   nothing justifies: it is the slot that has the ratio wrong, not the
   photograph. */
.pt-shot--hero { aspect-ratio: 3 / 2; border-radius: var(--r-sm); }
.pt-shot--thumb { aspect-ratio: 3 / 2; }
.pt-shot--video { aspect-ratio: 16 / 9; border-radius: 0; }

.pt-shot-tags {
  position: absolute;
  z-index: 3;
  inset-block-start: 6px;
  inset-inline-end: 6px;
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  max-width: calc(100% - 12px);
}
.pt-shot-tags span {
  padding: 2px 7px;
  border-radius: var(--r-sm);
  background: rgb(255 252 245 / 0.93);
  color: var(--olive);
  font-size: 9.5px;
  font-weight: 700;
  white-space: nowrap;
}
.portal-theme[data-theme="dark"] .pt-shot-tags span { background: rgb(24 16 10 / 0.86); color: #fff; }

/* =========================================================================
   مفتاح الخريطة في الشريط العلوي — the map switch in the top bar
   ========================================================================= */
.pt-mapswitch { display: inline-flex; align-items: center; gap: 2px; }

.pt-action--on {
  background: var(--gold-face);
  border-color: var(--gold-face);
  color: var(--on-gold);
}
.pt-action--on:hover { background: var(--gold-face); filter: brightness(0.94); }

/* الزرّ الذي يرفع الاختيار. صغيرٌ وبجانب المفتاح مباشرة، فيُقرأ جزءاً منه لا
   فعلاً مستقلاً. The button that lifts the selection. Small and immediately
   beside the switch, so it reads as part of it rather than as its own action. */
.pt-mapswitch-off {
  display: inline-grid;
  place-items: center;
  flex: none;
  width: 28px;
  height: 32px;
  border: 1px solid var(--gold-face);
  border-radius: var(--r-sm);
  background: var(--gold-face);
  color: var(--on-gold);
}
.pt-mapswitch-off:hover { filter: brightness(0.9); }

/* =========================================================================
   نافذة التفاصيل — the detail sheet
   ========================================================================= */
html.pt-locked, html.pt-locked body { overflow: hidden; }

.pt-modal {
  position: fixed;
  inset: 0;
  z-index: 2000;
  display: grid;
  place-items: end center;
}

.pt-modal-veil {
  position: absolute;
  inset: 0;
  background: rgb(44 20 3 / 0.55);
  backdrop-filter: blur(2px);
}

.pt-modal-panel {
  position: relative;
  z-index: 1;
  width: min(1060px, 100%);
  height: min(92dvh, 100%);
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line-strong);
  border-start-start-radius: var(--r-lg);
  border-start-end-radius: var(--r-lg);
  background: var(--bg);
  box-shadow: var(--shadow-3);
  overflow: hidden;
}

.pt-modal-head {
  flex: none;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 10px var(--sp-4);
  border-block-end: 1px solid var(--line);
  background: var(--surface);
}
.pt-modal-head h2 {
  flex: 1;
  min-width: 0;
  font-size: 13.5px;
  font-weight: 650;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pt-modal-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--sp-4);
  display: grid;
  gap: var(--sp-4);
  align-content: start;
}

/* ----------------------------------------------------------- The gallery */
/* الحاوية: الخانات الخمس واللوح المطويّ تحتها شيءٌ واحد، لأن العارض يفتح على
   المعرض لا على خانة. The container: the five slots and the folding panel
   beneath them are one thing, because the viewer opens onto a gallery rather
   than onto a slot. */
.pt-media { display: grid; gap: var(--sp-2); align-content: start; }

/* ونسبة العمودين تصنع نسبة الخانات الصغار، فهي ليست ذوقاً في التخطيط.
   -------------------------------------------------------------------------
   كانت ‎2fr 1fr‎، وأربعُ خاناتٍ في العمود الضيّق تقتسم ارتفاع الكبيرة: عرضُ
   الواحدة نصفُ ثلثِ المعرض وارتفاعُها نصفُ ارتفاع الكبيرة — أي نسبةٌ طوليّة
   مقدارها ‎0.75‎ مهما كانت نسبة الصورة. فصورةٌ عرضيّة — وأكثرُ ما يُرفع
   عرضيّ — تنكمش إلى شريطٍ في وسط الخانة، ويبقى فوقها وتحتها ثلثاها.

   و‎5fr 3fr‎ تجعل الخانة الصغيرة قريبةً من المربّع: تمتلئ الصورةُ العرضية
   خانتَها إلا قليلاً، ويبقى للكبيرة صدارتُها.

   The two columns' proportion makes the small slots' proportion, so it is not a
   matter of taste in layout.

   It was 2fr 1fr, with four slots in the narrow column dividing the large one's
   height: each is half of a third of the gallery wide and half the large slot
   tall — a portrait ratio of 0.75 whatever the photograph's ratio is. So a
   landscape photograph — and most of what is uploaded is landscape — shrinks to
   a strip in the slot's middle with two thirds of it left above and below.

   ــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــ

   والنسبة الصحيحة ليست تقريباً بل معادلة، وهذا اشتقاقها:

     عرض المعرض ‎g‎، والكبيرة ‎a‎، والعمود الضيّق ‎b‎، ونسبة الصور ‎r‎.
     ارتفاع الكبيرة ‎a/r‎، وعمودُ المصغّرات يملؤه بـ‎k‎ صفّاً في ‎c‎ عمود.
     فعرض المصغّرة ‎b/c‎ وارتفاعها ‎(a/r)/k‎.
     وتُملأ المصغّرة تماماً حين ‎(b/c) / ((a/r)/k) = r‎ — أي حين ‎b/a = c/k‎.

   فنسبة العمودين هي نسبة الأعمدة إلى الصفوف، ولا شيء غيرها يُصفّر الفراغ.
   وكانت ‎2fr 1fr‎ مع ‎٢×٢‎: ‎b/a = 0.5‎ و‎c/k = 1‎ — فنصفُ كل مصغّرة فراغ.

   وهذا الملف يحلّها بعمودٍ واحد ‎c = 1‎: تصير المعادلة ‎b/a = 1/k‎، أي أن
   نسبة العمودين تتبع عدد المصغّرات. وعددُها معلومٌ للورقة من أبنائها، فتُقرأ
   بـ‎:has‎ ولا يُسأل عنها الخادم.

   The right proportion is not an approximation but an equation, derived thus:

     the gallery's width g, the large slot a, the narrow column b, and the
     photographs' ratio r. The large slot's height is a/r, and the column of
     small slots fills it with k rows in c columns. So a small slot is b/c wide
     and (a/r)/k tall. It is filled exactly when (b/c) / ((a/r)/k) = r — that is,
     when b/a = c/k.

   The two columns' proportion is the columns-to-rows proportion, and nothing
   else brings the empty space to zero. It was 2fr 1fr against a 2×2: b/a = 0.5
   while c/k = 1 — so half of every small slot was empty.

   This sheet settles it with a single column, c = 1: the equation becomes
   b/a = 1/k, so the two columns' proportion follows the number of small slots.
   That number is known to the sheet from its own children, so :has reads it and
   the server is never asked. */
.pt-gallery {
  position: relative;
  display: grid;

  /* أربع مصغّرات — وهو الحال الغالب، فهو المكتوب هنا.
     Four small slots — the common case, so it is the one written here. */
  grid-template-columns: minmax(0, 4fr) minmax(0, 1fr);
  gap: 6px;
}

/* وما دون الأربع يأخذ نسبته من عدده. و‎:last-child‎ لأن الشرط عددٌ بعينه لا
   «ثلاثةٌ فأكثر»: المعرّفان ‎tools‎ و‎count‎ مطلقا الموضع فلا يُعدّان.

   Fewer than four takes its proportion from its own count. :last-child because
   the condition is an exact number rather than "three or more": the tools and
   the count are absolutely positioned and so are not counted.

   ــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــ

   والقيد على العرض شرطٌ لا زينة: المعادلة كلها مبنيّة على أن العمود الضيّق
   يقف إلى جانب الكبيرة. وتحت ‎860‎ بكسل لا يقف إلى جانبها بل تحتها شريطاً،
   فتسقط المعادلة ولا يبقى لها معنى.

   وأخصُّ من ذلك: ‎:has‎ تأخذ قوّة أقوى ما فيها، فهذه القواعد أقوى من
   ‎.pt-gallery‎ المجرّدة التي تكتبها استعلامات العرض — والاستعلام لا يزيد
   القوّة. فلو تُركت طليقةً لغلبت تخطيطَ الهاتف من فوقه، وعاد العمودان إلى
   شاشةٍ عرضها ثلاثمئة.

   The width bound is a condition rather than a nicety: the whole equation is
   built on the narrow column standing beside the large slot. Below 860 pixels it
   does not stand beside it but beneath it as a strip, so the equation falls away
   and means nothing.

   More particularly: :has takes the specificity of the strongest thing in it, so
   these rules outweigh the bare .pt-gallery that the width queries write — and a
   query adds no specificity. Left unbounded they would beat the phone's layout
   from above, and the two columns would come back on a screen three hundred
   wide. */
@media (min-width: 861px) {
  .pt-gallery:has(.pt-gallery-rest > :nth-child(3):last-child) {
    grid-template-columns: minmax(0, 3fr) minmax(0, 1fr);
  }
  .pt-gallery:has(.pt-gallery-rest > :nth-child(2):last-child) {
    grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  }
  .pt-gallery:has(.pt-gallery-rest > :nth-child(1):last-child) {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }

  /* وصورةٌ واحدة لا عمود بجانبها، فلا يُترك لعمودٍ غائبٍ خُمسُ العرض.
     One photograph has no column beside it, so no fifth of the width is left to
     a column that is not there. */
  .pt-gallery:not(:has(.pt-gallery-rest)) { grid-template-columns: minmax(0, 1fr); }
}

/* وما يُفتح في العارض يقول إنه يُفتح: مؤشّرٌ وحدُّ إبراز عند التركيز.
   وهي صفةٌ يكتبها السكربت حين يشتغل — فبلا سكربتٍ لا مؤشّر يَعِد بما لا يقع.

   What opens in the viewer says that it opens: a cursor and a focus ring. The
   attribute is written by the script when it runs — so with no script there is
   no cursor promising what will not happen. */
.pt-shot[data-open] { cursor: zoom-in; }
.pt-shot[data-open]:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 2px;
}
/* والخانات الصغار تملأ عمودها ولا تقف في رأسه.
   -------------------------------------------------------------------------
   كانت ‎1fr 1fr‎ و‎align-content: start‎: أربعُ خاناتٍ مربّعتين في صفّين تقفان
   في أعلى العمود، وتحتهما فراغٌ أسودُ بثلثي ارتفاع المعرض. وصورتان — وهو حال
   أكثر الإعلانات — تصيران مربّعين صغيرين في السدس الأعلى وتحتهما الفراغ كلّه.

   فصارت الصفوف ‎1fr‎ تتمدّد إلى ارتفاع الخانة الكبيرة إلى جانبها، وعددُ
   الأعمدة يتبع عدد الصور: صورةٌ أو صورتان في عمودٍ واحد فتكونان عريضتين
   مقروءتين، وثلاثٌ فأكثر في عمودين.

   The small slots fill their column rather than standing at its head.

   It was 1fr 1fr with align-content: start: four square slots in two rows
   standing at the top of the column with a black emptiness two thirds of the
   gallery's height beneath them. And two photographs — which is most
   listings — became two small squares in the top sixth with the whole of the
   rest empty.

   So the rows are now 1fr, stretching to the height of the large slot beside
   them, and the number of columns follows the number of photographs: one or two
   in a single column, so they come out wide and legible, and three or more in
   two. */
.pt-gallery-rest {
  display: grid;
  grid-template-columns: 1fr;
  grid-auto-rows: 1fr;
  gap: 6px;
  align-content: stretch;
}
/* وعمودٌ واحد دائماً — وهو الطرف الآخر من المعادلة أعلاه.
   -------------------------------------------------------------------------
   كان يصير عمودين عند الثالثة، فتصير ‎c/k = 1‎ بينما ‎b/a = 0.5‎ فيبقى نصفُ
   الخانة فراغاً. والعمود الواحد يُبقي ‎c = 1‎ فتتكفّل نسبةُ المعرض بالباقي.

   One column always — the other side of the equation above.

   It became two at the third slot, making c/k = 1 while b/a stayed 0.5, leaving
   half of every slot empty. A single column keeps c = 1, and the gallery's own
   proportion settles the rest. */
.pt-gallery-rest > .pt-shot { aspect-ratio: auto; height: 100%; min-height: 0; }

.pt-gallery-tools {
  position: absolute;
  inset-block-start: 8px;
  inset-inline-start: 8px;
  z-index: 2;
  display: flex;
  gap: 6px;
}

.pt-gallery-count {
  position: absolute;
  inset-block-end: 8px;
  inset-inline-start: 8px;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 9px;
  border-radius: var(--r-sm);
  background: rgb(44 20 3 / 0.72);
  color: #fff;
  font-size: 10.5px;
  font-weight: 650;
}

/* بقيّة الصور، في لوحٍ يُطوى.
   -------------------------------------------------------------------------
   ‎<details>‎ لا لوحٌ بسكربت: يفتحه المتصفح ويغلقه، ويعمل قبل أن يصل السكربت
   وبعده سواء — وهي القاعدة نفسها التي تُبدَّل بها التبويبات أعلى الصفحة.

   The rest of the photographs, in a panel that folds.

   <details> rather than a scripted panel: the browser opens and closes it, and
   it works before the script arrives and after it alike — the same rule the tabs
   further down the page switch by. */
.pt-gallery-all { margin-block-start: var(--sp-2); }

.pt-gallery-all > summary {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 11px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  cursor: pointer;
  font-size: 11.5px;
  font-weight: 650;
  color: var(--fg-2);
  list-style: none;
}
.pt-gallery-all > summary::-webkit-details-marker { display: none; }
.pt-gallery-all > summary:hover { background: rgb(197 153 43 / 0.12); }

.pt-gallery-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
  gap: 6px;
  margin-block-start: var(--sp-2);
}

/* =========================================================================
   عارض الصور — the photo viewer
   =========================================================================
   الخانة في المعرض صغيرة ومقصوصةٌ بنسبتها، والصورة المرفوعة قد تكون بعرض
   ألفٍ وخمسمائة. فمن أراد أن ينظر في تفصيلٍ فيها — شقٌّ في جدار، أو ما كُتب
   على لوحة — لم يكن له إلى ذلك سبيل إلا أن يفتح الصورة في لسانٍ ثانٍ إن عرف
   كيف. فصار للمعرض عارضٌ: الصورة بحجم الشاشة، تُقرَّب وتُبعَّد وتُسحب،
   ويُتنقَّل بين ما في المعرض كلِّه لا بين الخمس المعروضة.

   ولا شيء منه في الوسم قبل أن يُفتح: يبنيه السكربت عند أول فتح ويُبقيه. فلا
   يحمل من لا يفتح صورةً وسمَ نافذةٍ لا يراها.

   A slot in the gallery is small and cropped to its ratio, while an uploaded
   photograph may be fifteen hundred across. So whoever wanted to look at some
   detail in it — a crack in a wall, what is written on a sign — had no way to
   but opening the picture in a second tab, if they knew how. The gallery has a
   viewer now: the photograph at the screen's size, zoomed, panned, and browsed
   across the whole gallery rather than the five on show.

   None of it is in the markup before it is opened: the script builds it at the
   first opening and keeps it. So whoever opens no photograph carries no markup
   for a window they never see. */
.pt-viewer {
  position: fixed;
  inset: 0;
  z-index: 3000;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;

  /* والسواد مصمَت تقريباً: عند ‎0.96‎ كان نصُّ الصفحة تحته يُقرأ من خلفه —
     عناوينُ باهتة تسبح فوق صورةٍ يُنظر فيها.
     The black very nearly solid: at 0.96 the page's text was read through it —
     faded headings swimming over a photograph being looked at. */
  background: rgb(14 8 3 / 0.985);
  color: #fff;

  /* واللمس كله للعارض: بلا هذا يتولّى المتصفّح السحبَ تقريباً وتمريراً
     للصفحة تحته، فلا يصل إلى العارض من الإصبع شيء.
     Every touch belongs to the viewer: without this the browser takes the drag
     as its own zoom and as a scroll of the page beneath, and nothing of the
     finger reaches the viewer. */
  touch-action: none;
  user-select: none;
}
.pt-viewer[hidden] { display: none; }

/* ------------------------------------------------------------- الشريط */
.pt-viewer-bar {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 10px var(--sp-4);
  border-block-end: 1px solid rgb(255 255 255 / 0.1);
  background: rgb(0 0 0 / 0.35);
}

.pt-viewer-count {
  flex: 1;
  min-width: 0;
  font-size: 12px;
  font-weight: 650;
  letter-spacing: var(--tr-lg);
  color: rgb(255 255 255 / 0.78);
}

.pt-viewer-tools { display: flex; align-items: center; gap: 6px; }

.pt-viewer-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 5px;
  height: 30px;
  min-width: 30px;
  padding-inline: 8px;
  border: 1px solid rgb(255 255 255 / 0.18);
  border-radius: var(--r-sm);
  background: rgb(255 255 255 / 0.08);
  color: #fff;
  font-size: 11.5px;
  font-weight: 650;
  cursor: pointer;
}
.pt-viewer-btn:hover { background: rgb(255 255 255 / 0.18); }
.pt-viewer-btn:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.pt-viewer-btn[disabled] { opacity: 0.35; cursor: default; }
.pt-viewer-btn--zoom { font-variant-numeric: tabular-nums; min-width: 52px; }

/* -------------------------------------------------------------- المسرح */
.pt-viewer-stage {
  position: relative;
  overflow: hidden;
  display: grid;
  place-items: center;
  cursor: zoom-in;
}
.pt-viewer-stage[data-zoomed="1"] { cursor: grab; }
.pt-viewer-stage[data-drag="1"] { cursor: grabbing; }

/* واللوح بمقاس الصورة معروضةً لا بمقاس المسرح: يحسبه السكربت من أبعادها
   الأصلية وأبعاده. ولذلك ركن اللوح هو ركن الصورة تماماً — فالعلامة تقف على
   الصورة لا في فراغٍ إلى جانبها، وتكبر معها إذا قُرِّبت.

   The plate takes the photograph's displayed size rather than the stage's: the
   script works it out from its natural dimensions and the stage's. Which makes
   the plate's corner the photograph's corner exactly — so the mark stands on
   the picture rather than in the space beside it, and grows with it when it is
   zoomed. */
.pt-viewer-plate {
  position: relative;
  transform-origin: 50% 50%;
  will-change: transform;
}
.pt-viewer-plate img {
  display: block;
  width: 100%;
  height: 100%;
}

/* والعلامة نفسها التي على الخانة، بالقناع نفسه — the same mark as on the slot,
   by the same mask. */
.pt-viewer-plate::after {
  content: "";
  position: absolute;
  inset-block-end: 4%;
  inset-inline-end: 4%;
  width: clamp(46px, 15%, 150px);
  aspect-ratio: 381 / 333;
  background-color: #fff;
  -webkit-mask-image: var(--pt-mark);
  mask-image: var(--pt-mark);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
  opacity: 0.14;
  filter: drop-shadow(0 1px 3px rgb(0 0 0 / 0.5));
  pointer-events: none;
}

/* ------------------------------------------------------ السابق والتالي */
.pt-viewer-nav {
  position: absolute;
  inset-block-start: 50%;
  translate: 0 -50%;
  z-index: 2;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border: 1px solid rgb(255 255 255 / 0.18);
  border-radius: 50%;
  background: rgb(0 0 0 / 0.45);
  color: #fff;
  cursor: pointer;
}
.pt-viewer-nav:hover { background: rgb(0 0 0 / 0.7); }
.pt-viewer-nav:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.pt-viewer-nav--prev { inset-inline-start: 12px; }
.pt-viewer-nav--next { inset-inline-end: 12px; }

/* ‎[hidden]‎ لا تغلب ‎display‎ المكتوبة، فتُكتب لها — hidden does not beat a
   written display, so it is written for it. */
.pt-viewer-nav[hidden],
.pt-viewer-strip[hidden] { display: none; }

/* والسهم يشير إلى جهة السير لا إلى يمينٍ ثابت: في العربية «التالي» يساراً.
   The arrow points the way of travel rather than at a fixed right: in Arabic
   "next" is to the left. */
[dir="rtl"] .pt-viewer-nav svg { scale: -1 1; }

/* ------------------------------------------------------------- الشريط */
.pt-viewer-strip {
  display: flex;
  gap: 6px;
  padding: 8px var(--sp-4);
  overflow-x: auto;
  scrollbar-width: none;
  border-block-start: 1px solid rgb(255 255 255 / 0.1);
  background: rgb(0 0 0 / 0.35);
}
.pt-viewer-strip::-webkit-scrollbar { display: none; }

.pt-viewer-thumb {
  flex: none;
  width: 74px;
  aspect-ratio: 4 / 3;
  padding: 0;
  border: 1px solid rgb(255 255 255 / 0.16);
  border-radius: var(--r-sm);
  background: rgb(255 255 255 / 0.06);
  overflow: hidden;
  cursor: pointer;
  opacity: 0.55;
}
.pt-viewer-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pt-viewer-thumb:hover { opacity: 0.85; }
.pt-viewer-thumb[aria-current="true"] { opacity: 1; border-color: var(--gold); }
.pt-viewer-thumb:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

@media (max-width: 640px) {
  .pt-viewer-bar { padding: 8px var(--sp-3); }
  .pt-viewer-count { font-size: 11px; }
  .pt-viewer-nav { width: 38px; height: 38px; }
  .pt-viewer-nav--prev { inset-inline-start: 6px; }
  .pt-viewer-nav--next { inset-inline-end: 6px; }
  .pt-viewer-strip { padding: 6px var(--sp-3); }
  .pt-viewer-thumb { width: 58px; }
  .pt-viewer-btn--zoom { display: none; }
}

.pt-ghost-btn {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 28px;
  padding-inline: 10px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  background: var(--glass);
  backdrop-filter: blur(6px);
  color: var(--fg-1);
  font-size: 11.5px;
  font-weight: 600;
  cursor: pointer;
}
.pt-ghost-btn:hover { border-color: var(--gold); background: var(--surface); }

/* ------------------------------------------------------------- The body */
.pt-detail-split {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  gap: var(--sp-4);
  align-items: start;
}
.pt-detail-main { display: grid; gap: var(--sp-3); min-width: 0; }
.pt-detail-side { display: grid; gap: var(--sp-3); min-width: 0; }

.pt-detail-price {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px;
}
.pt-detail-price b { font-size: 22px; font-weight: 700; letter-spacing: var(--tr-lg); color: var(--green); }
.pt-detail-price small { font-size: 11.5px; color: var(--fg-3); font-weight: 600; }

.pt-detail-place {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 12px;
  color: var(--fg-3);
}

/* وفقرة الوصف تحفظ أسطر من كتبها: صارت تعرض نصّ الناشر لا جملةً مركّبة، ومن
   كتب ثلاث فقرات في مربّع النصّ قصد ثلاث فقرات. و pre-line يبقي الأسطر ويطوي
   الفراغات الزائدة، فلا يُنقل خللٌ في المسافات من لوحة المفاتيح إلى الصفحة.

   The description keeps the lines of whoever wrote it: it shows the publisher's
   own text now rather than a composed sentence, and somebody who wrote three
   paragraphs into the textarea meant three paragraphs. pre-line keeps the breaks
   and folds the extra spaces, so stray whitespace does not travel from a
   keyboard onto the page. */
.pt-detail-desc {
  font-size: 12.5px;
  line-height: 1.8;
  color: var(--fg-2);
  white-space: pre-line;
  overflow-wrap: anywhere;
}

.pt-detail-h { font-size: 13px; font-weight: 650; padding-block-start: var(--sp-2); }

.pt-facts-grid,
.pt-feature-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: var(--sp-2) var(--sp-4);
  padding: var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
}

/* وعمود الحقائق وحده أوسع، ولا تتبعه المميزات: الحقيقة صارت سطراً واحداً —
   رمزٌ فكلمةٌ فنقطتان فقيمة — فمئةٌ وستّون بكسلاً تكسر «دورات المياه: ٦». أما
   الميزة فكلمةٌ واحدة إلى جانب علامة، وتوسيع عمودها يُنقص ما يُرى منها في
   السطر بلا سبب.

   The facts column alone is wider, and the features do not follow it: a fact is
   now one line — a mark, a word, a colon and a value — and a hundred and sixty
   pixels break "دورات المياه: 6" in two. A feature is one word beside a tick, and
   widening its column shows fewer of them per row for no reason. */
.pt-facts-grid { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }

/* =========================================================================
   سطرُ «عنوانٍ وقيمته» — a label and its value, in a line
   -------------------------------------------------------------------------
   قاعدةٌ واحدة لثلاثة مواضع تكرّرت فيها البنية نفسها حرفاً بحرف: شبكةُ
   الحقائق، ولوحةُ التبويب في نافذة المعروض، وخطّةُ السداد تحتها. وكانت
   ثلاثتها ‎<dl> > <div> > <dt>+<dd>‎ مكتوبةً ثلاث مرّات، فتُصحَّح واحدةٌ
   وتبقى أختاها على حالهما في الصفحة نفسها.

   One rule for three places in which the same structure was repeated letter for
   letter: the facts grid, the tabbed panel in an offer's sheet, and the payment
   plan beneath it. All three were <dl> > <div> > <dt>+<dd> written out three
   times, so one gets corrected and its two sisters stay as they were on the very
   same page.

   وكانت ‎space-between‎ تدفع القيمة إلى الطرف المقابل، فتقرأ العينُ في العربية
   «المساحة» في أقصى اليمين ثم تقطع فراغاً إلى «٨٠٠٠ قدم²» في أقصى اليسار —
   وبينهما لا شيء يربطهما، ولا علامةَ ترقيمٍ تقول إن هذه قيمةُ تلك. فصارت
   تُقرأ من جهة القراءة سطراً واحداً: «المساحة: ٨٠٠٠ قدم²».

   space-between used to push the value to the opposite end, so in Arabic the eye
   reads "المساحة" at the far right, crosses a gap, and finds "8,000 قدم²" at the
   far left — with nothing between them to join them and no punctuation saying
   that this is that one's value. Now it reads from the reading edge as one line:
   "Area: 8,000 ft²".
   ========================================================================= */
/* والمحاذاة على خطّ الكتابة لا على وسط الصندوقين.
   -------------------------------------------------------------------------
   العنوان ‎11.5‎ بكسل والقيمة ‎12‎، فصندوقاهما مختلفا الارتفاع — و‎center‎ يوسّط
   صندوقاً في صندوق فتقع الكلمتان على خطّين متجاورين لا على خطٍّ واحد. وأظهرُ
   منه حين تلتفّ القيمة سطرين: العنوان يطفو في منتصفهما.

   والمقصود سطرٌ يُقرأ «المساحة: ٨٠٠٠ قدم²»، وذلك خطُّ كتابةٍ واحد لا صندوقان
   متوسّطان — فالمحاذاة ‎baseline‎.

   Aligned on the writing line rather than on the middle of the two boxes.

   The label is 11.5 pixels and the value 12, so their boxes differ in height —
   and center centres one box inside the other, putting the two words on two
   neighbouring lines rather than on one. It shows plainest when the value wraps
   to two lines: the label floats in the middle of them.

   What is meant is a line reading "Area: 8,000 ft²", and that is one writing
   line rather than two centred boxes — so the alignment is baseline. */
.pt-facts-grid > div,
.pt-tabbed-panel > div,
.pt-plan > div {
  display: flex;
  align-items: baseline;
  justify-content: flex-start;
  gap: 6px;
}

/* والعنوان لا ينكمش: قيمةٌ طويلة تلتفّ، ولا تأكل من كلمةٍ هي مفتاحُ السطر.
   The label does not shrink: a long value wraps rather than eating into the word
   that is the line's key. */
.pt-facts-grid dt,
.pt-tabbed-panel dt,
.pt-plan dt { flex: none; }

/* والنقطتان من الورقة لا من القالب: هي علامةُ ترقيمٍ تفصل العنوان عن قيمته،
   وليست من البيانات في شيء — فلا تُكتب في عشرين موضعاً ولا تُترجَم.

   وموضعها يتكفّل به المتصفح: النقطتان محايدتان في خوارزمية الاتجاهين، فتقعان
   في آخر النصّ منطقياً — يسارَ الكلمة العربية ويمينَ الإنجليزية — بلا قاعدةٍ
   لكل اتجاه.

   The colon comes from the sheet rather than from the template: it is punctuation
   parting a label from its value and no part of the data — so it is not written
   in twenty places and not translated.

   Its position is the browser's business: a colon is neutral in the bidi
   algorithm and lands at the logical end of the text — to the left of the Arabic
   word and to the right of the English — with no rule per direction. */
.pt-facts-grid dt::after,
.pt-tabbed-panel dt::after,
.pt-plan dt::after { content: ":"; }

.pt-facts-grid dt {
  font-size: 11.5px;
  color: var(--fg-3);
}

/* والرمز داخل العنوان سطريّاً لا عنصراً في صفٍّ مرن: صفٌّ مرن يضع بين الرمز
   والكلمة والنقطتين فجوةً واحدة، فتنفصل النقطتان عن كلمتهما — «المساحة :».
   The mark sits inside the label inline rather than as an item in a flex row: a
   flex row puts one gap between the mark, the word and the colon, which parts the
   colon from its word — "Area :". */
.pt-facts-grid dt > .ico {
  /* و‎inline-block‎ سطرٌ لازم لا زيادة: ‎img, svg { display: block }‎ في
     ‎style.css‎ تُصيّر كل رمزٍ في المنصة كتلةً. وذلك صحيحٌ حيث يقع الرمز عنصراً
     في صفٍّ مرن أو شبكة — وهو موضعه في كل مكانٍ آخر — وهنا وحده يقع داخل نصّ.
     فكان يأخذ سطره لنفسه وتنزل الكلمة تحته، فيصير العنوان سطرين والقيمة إلى
     جانبهما في منتصفهما، ولا يستقيم للنصّ خطٌّ واحد. و‎vertical-align‎ أسفله
     لا أثر لها على كتلة أصلاً، فكانت تُكتب ولا تفعل شيئاً.

     inline-block is a necessary line rather than a spare one: img, svg {
     display: block } in style.css makes every mark on the platform a block. That
     is right where a mark is an item in a flex row or a grid — which is where it
     sits everywhere else — and here alone it sits inside text. So it took a line
     to itself and the word dropped beneath it, making the label two lines with
     the value beside their middle, and no single line for the text to sit on.
     The vertical-align below has no effect on a block at all, so it was written
     and did nothing.

     وهو نظير ما فعلته ‎.aed‎ و‎.type-icon‎ لأنفسهما للسبب نفسه.
     It is the counterpart of what .aed and .type-icon do for themselves, for the
     very same reason. */
  display: inline-block;
  vertical-align: -2px;
  margin-inline-end: 5px;
  color: var(--fg-4);
}

.pt-facts-grid dd {
  margin: 0;
  min-width: 0;
  font-size: 12px;
  font-weight: 650;
  overflow-wrap: anywhere;
}

.pt-feature-grid { font-size: 12px; }
.pt-feature-grid li { display: flex; align-items: center; gap: 7px; }
.pt-feature-mark {
  display: grid;
  place-items: center;
  flex: none;
  width: 18px;
  height: 18px;
  border-radius: var(--r-sm);
  background: rgb(197 153 43 / 0.24);
  color: var(--olive);
}
.portal-theme[data-theme="dark"] .pt-feature-mark { color: var(--gold); }

/* والمرافقُ بعلامةٍ محايدة، فتُقرأ قسماً غيرَ المميزات — facilities carry a neutral
   mark, so they read as a section apart from the features. */
.pt-feature-grid--facilities .pt-feature-mark { background: var(--surface-2); color: var(--fg-2); border: 1px solid var(--line); }

/* أنواعُ وحدات المشروع تحت صوره مباشرة: شاراتٌ تلتفّ ولا تفيض.
   The project's unit types directly beneath its photographs: badges that wrap and
   never overflow. */
.pt-offer-types { display: flex; flex-wrap: wrap; gap: 8px; margin: var(--sp-3) 0 0; padding: 0; list-style: none; }
.pt-offer-types .pill { padding: 5px 12px; font-size: 12.5px; font-weight: 600; border-color: var(--gold); background: rgb(197 153 43 / 0.12); color: var(--olive); }
.portal-theme[data-theme="dark"] .pt-offer-types .pill { color: var(--gold); }

/* إدارةُ الوحدات وميزاتِها ومرافقِها: قوائمُ تُقرأ، وكلُّ كتابةٍ في نافذة.
   The management of units and their features and facilities: lists that are read,
   every write in a modal. */
.pt-amenity-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--sp-2); }
.pt-amenity-head h2 { margin: 0; }
.pt-empty-line {
  padding: var(--sp-3); border: 1px dashed var(--line); border-radius: var(--r-md);
  font-size: 12.5px; color: var(--fg-3);
}
.pt-row-acts { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.pt-unit-thumb {
  display: inline-grid; place-items: center; width: 48px; height: 36px; object-fit: cover;
  border-radius: var(--r-sm); border: 1px solid var(--line); vertical-align: middle;
}
.pt-unit-thumb--none { background: var(--surface-2); color: var(--fg-3); }

.pt-unit-groups { display: grid; gap: var(--sp-3); }
.pt-unit-group { display: grid; gap: var(--sp-2); padding: var(--sp-3); border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface); min-width: 0; }
.pt-unit-group--loose { border-style: dashed; }
.pt-unit-group-title { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px; margin: 0; font-size: 13px; font-weight: 650; overflow-wrap: anywhere; }
.pt-unit-group-title .sub { font-size: 11.5px; font-weight: 500; color: var(--fg-3); }
.pt-amenity-list { display: grid; gap: 6px; margin: 0; padding: 0; list-style: none; }
.pt-amenity-item { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; font-size: 12.5px; min-width: 0; }
.pt-amenity-name { flex: 1 1 160px; min-width: 0; overflow-wrap: anywhere; }
.pt-amenity-name .sub { margin-inline-start: 6px; font-size: 11.5px; color: var(--fg-3); }

/* النافذة: ‎<dialog>‎ أصليةٌ في الطبقة العليا — فلا يُقصّ فيها شيء — بثوب اللوحة.
   The modal: a native <dialog> in the top layer — so nothing in it is clipped — in
   the portal's own dress. */
.pt-dialog {
  width: min(560px, calc(100vw - 24px));
  max-height: min(90dvh, 100%);
  padding: 0;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-lg);
  background: var(--bg);
  color: var(--fg-1);
  box-shadow: var(--shadow-3);
  overflow: hidden;
}
.pt-dialog[open] { display: flex; flex-direction: column; }
.pt-dialog::backdrop { background: rgb(44 20 3 / 0.55); backdrop-filter: blur(2px); }
.pt-dialog > form { display: flex; flex-direction: column; min-height: 0; max-height: inherit; }
.pt-dialog .pt-modal-body { max-height: calc(90dvh - 120px); }
.pt-dialog-foot {
  flex: none; display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--sp-2);
  padding: 10px var(--sp-4); border-block-start: 1px solid var(--line); background: var(--surface);
}
.pt-dialog .btn[aria-busy="true"] { opacity: 0.7; cursor: progress; }
.pt-dialog [aria-invalid="true"] { border-color: var(--red); }
.pt-unit-photo-field { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); }
.pt-unit-photo-field input { flex: 1 1 200px; min-width: 0; }
.pt-unit-photo-field [hidden] { display: none; }

/* «سجّل اهتمامك» على صفحة الوحدة — "register your interest" on the unit's page. */
.pt-unit-interest { display: grid; gap: 6px; justify-items: start; margin-block: var(--sp-3); }
.pt-unit-interest .btn { max-width: 100%; white-space: normal; }
.pt-unit-interest-done { display: inline-flex; align-items: center; gap: 6px; margin: 0; font-size: 13px; font-weight: 600; color: var(--green); }

/* رقمُ الوحدة معزولاً عمّا حوله، واتجاهُه من أوّل حرفٍ قويٍّ فيه لا من لغة الواجهة:
   فـ‎101-A‎ يبقى ‎101-A‎ في الصفحة العربية — وكان اتجاهُ الواجهة يقلبه ‎A-101‎ — و‎أ-101‎
   يُقرأ من اليمين كما كُتب. ولا يتجاوز سطره، والقيمةُ المحفوظة لا تُمسّ.
   The unit number isolated from what surrounds it, its direction taken from its own
   first strong character rather than the interface language: so 101-A stays 101-A
   on the Arabic page — the interface direction turned it into A-101 — and أ-101
   reads from the right as written. It never runs past its line, and the stored
   value is untouched.

   وهو قطعةٌ واحدة ما وسعه السطر — فلا ينكسر ‎101-‎ عن ‎A‎ في عمودٍ ضيّق — ويلتفّ إن طال
   عن السطر كلّه. وحقلُ إدخاله يبقى على محاذاة نموذجه.
   It is one piece while the line holds it — so 101- does not break from A in a
   narrow column — and wraps only when longer than the whole line. Its input keeps
   its form's alignment. */
.pt-unit-no { unicode-bidi: plaintext; overflow-wrap: anywhere; }
span.pt-unit-no { display: inline-block; max-width: 100%; }
[dir="rtl"] input.pt-unit-no { text-align: right; }
/* وفي الفتات يبقى سطرياً: الفتاتُ يُقصّ بنقاطٍ عند ‎42ch‎، والقطعةُ الواحدة (‎inline-block‎) إن
   لم تسعها البقيّة أُخفيت كلُّها لا بعضُها — فكان الرقمُ الطويل يغيب عن المسار. وسطرياً يُقصّ
   حرفاً حرفاً كالعنوان قبله، ويبقى معزولاً.
   In the breadcrumb it stays inline: the crumb is cut with an ellipsis at 42ch, and a
   single piece (inline-block) the room cannot hold is hidden whole rather than in part —
   a long number vanished from the trail. Inline, it is cut letter by letter like the
   label before it, and stays isolated. */
.pt-crumbs .pt-unit-no { display: inline; }

/* الرموزُ في النافذة شبكةٌ في مجراها، لا نافذةٌ فوق نافذة.
   The marks inside the modal are a grid in its flow rather than a popover on a modal. */
.pt-icon-field { margin: 0; padding: 0; border: 0; min-width: 0; }
.pt-icon-field legend { display: flex; align-items: center; gap: 8px; padding: 0; margin-block-end: 5px; font-size: 11.5px; font-weight: 600; color: var(--fg-2); }
.pt-icon-chosen { display: inline-grid; place-items: center; width: 26px; height: 26px; border: 1px solid var(--gold); border-radius: var(--r-sm); background: rgb(197 153 43 / 0.16); color: var(--olive); }
.pt-icon-grid {
  display: grid; grid-template-columns: repeat(auto-fill, 34px); gap: 4px; padding: 6px;
  border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface);
}
.pt-icon-grid label {
  position: relative;
  display: grid; place-items: center; width: 34px; height: 34px; cursor: pointer;
  border: 1px solid transparent; border-radius: var(--r-sm); color: var(--fg-2);
}
.pt-icon-grid label:hover { background: var(--surface-2); }
.pt-icon-grid input { position: absolute; inset: 0; opacity: 0; margin: 0; pointer-events: none; }
.pt-icon-grid label:has(input:checked) { border-color: var(--gold); background: rgb(197 153 43 / 0.16); color: var(--olive); }
.pt-icon-grid label:has(input:focus-visible) { outline: 2px solid var(--gold); outline-offset: 1px; }
.pt-icon-grid--inline { max-height: 190px; overflow-y: auto; overscroll-behavior: contain; }

/* الوحداتُ المعروضة بطاقات: صورةٌ بنسبةٍ ثابتة (‎.pt-shot‎ نفسها) فلا يقفز شيءٌ حين
   تغيب، فرقمٌ ودور، فمواصفات، فسعر. بطاقةٌ في الصفّ على الهاتف، وأكثرُ كلّما اتّسع.
   The units on offer as cards: a photograph at a fixed ratio (the same .pt-shot) so
   nothing jumps when it is missing, then number and floor, specifications, price. One
   card a row on a phone, more as it widens. */
.pt-unit-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 200px), 1fr));
  gap: var(--sp-3);
  margin: 0;
  padding: 0;
  list-style: none;
}
.pt-unit-card {
  display: flex;
  flex-direction: column;
  min-width: 0;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  overflow: hidden;
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.pt-unit-card:hover { border-color: var(--gold); box-shadow: var(--shadow-1); }
.pt-unit-card .pt-shot { border-radius: 0; }
.pt-unit-pick { display: flex; flex-direction: column; flex: 1; min-width: 0; color: inherit; text-decoration: none; }
.pt-unit-pick:focus-visible { outline: 2px solid var(--gold); outline-offset: -2px; }
.pt-unit-card-body { display: grid; gap: 6px; padding: var(--sp-3); min-width: 0; }
.pt-unit-card-title { font-size: 13px; font-weight: 650; overflow-wrap: anywhere; }
.pt-unit-card .pt-meta { flex-wrap: wrap; }
.pt-unit-card .pt-meta > span { display: inline-flex; align-items: center; gap: 4px; }
.pt-unit-card-price { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px; margin-top: 2px; }
.pt-unit-card-price small { font-size: 11px; color: var(--fg-3); font-weight: 600; }
.pt-unit-card-price b { font-size: 15px; font-weight: 700; color: var(--green); overflow-wrap: anywhere; }
.pt-units-empty {
  display: flex; align-items: center; gap: 8px; padding: var(--sp-3);
  border: 1px dashed var(--line); border-radius: var(--r-md); font-size: 12.5px; color: var(--fg-3);
}

/* صفحةُ الوحدة داخل مشروعها: صورتُها بجانب أرقامها، ثم خطّتُها وميزاتُها ومرافقُها.
   وعلى الهاتف تنزل الصورةُ فوق الأرقام.
   A unit's page inside its project: its photograph beside its figures, then its
   plan, features and facilities. On a phone the photograph sits above the figures. */
.pt-unit-detail {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
  gap: var(--sp-4);
  padding: var(--sp-4);
  border: 1px solid var(--gold);
  border-radius: var(--r-md);
  background: var(--surface);
  min-width: 0;
}
.pt-unit-detail-media .pt-shot { border-radius: var(--r-sm); }
.pt-unit-detail-body { display: grid; gap: var(--sp-3); align-content: start; min-width: 0; }
.pt-unit-detail-h { margin: 0; font-size: 12.5px; font-weight: 700; color: var(--fg-2); }
/* بياناتُ الوحدة: لكلّ بيانٍ خانةٌ — اسمُه صغيرٌ خافتٌ فوق قيمته — في شبكةٍ تتّسع
   بعرضها: خانتان على الهاتف، وأكثرُ كلّما اتّسع، والسعرُ بلونه. والمضغوطةُ للإدارة.
   The unit's details: a tile per detail — its label small and muted above its value
   — in a grid that widens with its width: two tiles on a phone, more as it widens,
   the price in its colour. The compact one is the management list's. */
.pt-unit-facts {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 140px), 1fr));
  gap: var(--sp-2); margin: 0;
}
.pt-unit-facts > div {
  display: grid; gap: 4px; align-content: start; min-width: 0;
  padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface);
}
.pt-unit-facts dt { font-size: 11px; font-weight: 600; color: var(--fg-3); }
.pt-unit-facts dd { margin: 0; font-size: 14px; font-weight: 650; color: var(--fg-1); overflow-wrap: anywhere; }
.pt-unit-facts dd b { font-weight: 700; color: var(--green); }
.pt-unit-facts dd .pill { vertical-align: middle; }
.pt-unit-facts--compact { grid-template-columns: repeat(auto-fill, minmax(min(100%, 112px), 1fr)); gap: 6px; }
.pt-unit-facts--compact > div { padding: 7px 10px; gap: 2px; }
.pt-unit-facts--compact dd { font-size: 13px; }

/* الوحداتُ في الإدارة بطاقات: صورتُها بنسبتها بجانب بياناتها، وتنزل فوقها على الهاتف.
   The units on the management page as cards: the photograph at its ratio beside its
   details, sitting above them on a phone. */
.pt-unit-admin-list { display: grid; gap: var(--sp-3); margin: 0; padding: 0; list-style: none; }
.pt-unit-admin {
  display: grid; grid-template-columns: minmax(0, 180px) minmax(0, 1fr); gap: var(--sp-3);
  padding: var(--sp-3); border: 1px solid var(--line); border-radius: var(--r-md); background: var(--bg); min-width: 0;
}
.pt-unit-admin-media .pt-shot { border-radius: var(--r-sm); }
.pt-unit-admin-body { display: grid; gap: var(--sp-2); align-content: start; min-width: 0; }
.pt-unit-admin-title { margin: 0; font-size: 13.5px; font-weight: 700; overflow-wrap: anywhere; }
.pt-unit-card-type { font-size: 11.5px; font-weight: 600; color: var(--olive); }
.pt-unit-page .pt-offer-types { margin: 0; }
.pt-unit-page .pt-offer-ref a { color: var(--gold); font-weight: 600; overflow-wrap: anywhere; }
.pt-unit-page-back { margin: var(--sp-4) 0 0; }

@media (max-width: 560px) {
  .pt-unit-detail { grid-template-columns: minmax(0, 1fr); padding: var(--sp-3); }
  .pt-unit-admin { grid-template-columns: minmax(0, 1fr); }
  .pt-unit-admin-media { max-width: 320px; }
}

/* المقاطع في شبكةٍ تتّسع لا في شريطٍ يُسحب.
   -------------------------------------------------------------------------
   كان الشريط يعطي كل مقطعٍ ‎264‎ بكسل ثابتة مهما اتّسع المكان — فمقطعٌ واحد
   في نافذةٍ عرضها ألفٌ وستّون يقف في زاويتها بربع عرضها، والباقي فراغ. وهي
   شبكةٌ الآن: مقطعٌ واحد يأخذ ما أمامه، وثلاثةٌ تصطفّ، والضيّقة تكدّسها.

   The clips in a grid that takes the room rather than in a strip that is
   dragged. The strip gave every clip a fixed 264 pixels however much room there
   was — so a single clip in a sheet 1060 wide stood in its corner at a quarter
   of its width with the rest left empty. It is a grid now: one clip takes what
   is in front of it, three line up, and a narrow screen stacks them. */
/* و‎auto-fit‎ لا ‎auto-fill‎: الأولى تطوي المسارات الفارغة فيأخذ المقطعُ
   الواحد الصفَّ كلَّه، والثانية تُبقيها فيقف وحيداً في ثلثِ العرض وإلى جانبه
   فراغان بمقاسه. وأكثر الإعلانات مقطعٌ واحد.

   auto-fit rather than auto-fill: the first folds the empty tracks away so one
   clip takes the whole row, and the second keeps them so it stands alone in a
   third of the width with two gaps its own size beside it. And most listings
   have one clip. */
.pt-videos {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
  gap: var(--sp-2);
  padding-block: 2px;
}

/* مشغّلٌ حقيقي لا صورةٌ عليها مثلّث. ضوابطه ضوابط المتصفح، فتعمل باللمس
   وبلوحة المفاتيح وبقارئ الشاشة دون أن نكتب واحدة منها.

   A real player rather than a picture with a triangle on it. Its controls are
   the browser's own, so they work under a finger, from a keyboard and through a
   screen reader without our writing one of them.

   والإطارُ حوله لا عليه: ‎.pt-fit‎ تحتاج عنصراً تُعلَّق عليه طبقاتُها الثلاث،
   و‎<video>‎ ليس منها لأنه هو الطبقة الوسطى. فصار الإطار غلافاً والمشغّل فيه.

   The frame around it rather than on it: .pt-fit needs an element to hang its
   three layers from, and <video> is not that element because it is the middle
   layer itself. So the frame became a wrapper with the player inside it. */
.pt-video {
  display: grid;
  place-items: center;
  aspect-ratio: 16 / 9;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: #0c0805;
}

/* -------------------------------------------------- The tabbed panels */
/* بلا سكربت: أزرار راديو مخفيّة وثلاث لوحات، فيبدّلها المتصفح كما يبدّل أي
   راديو. وحيث لا ‎:has‎ تبقى اللوحة الأولى ظاهرة — فلا يضيع محتوى، إنما تسكن
   التبويبات.

   No script: hidden radios and three panels, switched by the browser as it
   switches any radio. Where :has is unsupported the first panel simply stays
   open — nothing is lost, the tabs merely go quiet. */
.pt-tabbed {
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  overflow: hidden;
}
.pt-tabbed-head { display: flex; border-block-end: 1px solid var(--line); }

.pt-tabbed-tab { position: relative; flex: 1; cursor: pointer; text-align: center; }
.pt-tabbed-tab input { position: absolute; opacity: 0; width: 0; height: 0; }
.pt-tabbed-tab span {
  display: block;
  padding: 9px 8px;
  border-block-end: 2px solid transparent;
  font-size: 12px;
  font-weight: 600;
  color: var(--fg-3);
}
.pt-tabbed-tab input:checked + span { color: var(--fg-1); border-block-end-color: var(--gold); }
.pt-tabbed-tab input:focus-visible + span { outline: 2px solid var(--gold); outline-offset: -2px; }

.pt-tabbed-panel { display: none; gap: 2px; padding: var(--sp-3); }
.pt-tabbed-panel[data-panel="main"] { display: grid; }

.pt-tabbed:has(input[value="extra"]:checked) .pt-tabbed-panel[data-panel="main"],
.pt-tabbed:has(input[value="place"]:checked) .pt-tabbed-panel[data-panel="main"] { display: none; }
.pt-tabbed:has(input[value="extra"]:checked) .pt-tabbed-panel[data-panel="extra"],
.pt-tabbed:has(input[value="place"]:checked) .pt-tabbed-panel[data-panel="place"] { display: grid; }

/* وسطر اللوحة يُقرأ كما تُقرأ الحقيقة: العنوان ثم نقطتان ثم قيمته، لا طرفان
   يفصل بينهما فراغ. انظر ‎.pt-facts-grid > div‎ أعلاه — العلّة واحدة والعلاج
   واحد، ولا يبقى في المنصة سطرا «عنوان وقيمة» يُرسمان على وجهين.

   A panel's line reads as a fact reads: the label, a colon, then its value —
   rather than two ends with a gap between them. See .pt-facts-grid > div above:
   one defect and one remedy, and no two "label and value" lines left on this
   platform drawn two different ways. */
/* والتخطيط من القاعدة المشتركة أعلاه — the layout comes from the shared rule
   above; only the padding is this panel's own. */
.pt-tabbed-panel > div { padding-block: 6px; }
.pt-tabbed-panel > div + div { border-block-start: 1px dashed var(--line-soft); }
.pt-tabbed-panel dt { font-size: 11.5px; color: var(--fg-3); }
.pt-tabbed-panel dd { margin: 0; display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; }

.pt-copy {
  display: inline-grid;
  place-items: center;
  padding: 2px;
  border: 0;
  background: none;
  color: var(--fg-4);
  cursor: pointer;
}
.pt-copy:hover { color: var(--gold); }
.pt-copy[data-copied="1"] { color: var(--green); }

.pt-empty-note {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  padding: var(--sp-5);
  border: 1px dashed var(--line);
  border-radius: var(--r-md);
  color: var(--fg-3);
  font-size: 12px;
}

/* ----------------------------------------------------------- Publisher */
.pt-publisher {
  display: grid;
  justify-items: center;
  gap: var(--sp-2);
  padding: var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  text-align: center;
}
.pt-publisher-mark {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: var(--r-circle);
  background: var(--surface-2);
  color: var(--olive);
}
.portal-theme[data-theme="dark"] .pt-publisher-mark { color: var(--gold); }
.pt-publisher b { font-size: 13px; font-weight: 650; }
.pt-publisher-rating { display: inline-flex; align-items: center; gap: 5px; font-size: 11.5px; color: var(--gold); }

/* الزرّان بمقاسٍ واحد بالضبط، ولا يفترقان إلا في اللون.
   -------------------------------------------------------------------------
   كلٌّ منهما جاء من عائلةٍ أخرى: «اتصال» من ‎.pt-ghost-btn‎ وهو زرٌّ خفيف يطفو
   على الصور، و«واتساب» من ‎.pt-submit‎ وهو زرّ إرسال النماذج. فورث الأول ارتفاع
   اثنين وثلاثين وحرفاً بأحد عشر ونصف، وورث الثاني ثمانيةً وثلاثين وحرفاً
   بثلاثة عشر وحشواً جانبياً بائنتين وعشرين — ووقفا جنباً إلى جنب فبدا أحدهما
   أكبر من الآخر وكأنه أهمّ منه.

   وهما ليسا كذلك: طريقان إلى الشخص نفسه، ومن حقّ القارئ أن يختار أيّهما بلا أن
   يوحي إليه المقاسُ بجواب. فتُكتب مقاييسهما هنا كلها — الارتفاع والحرف ووزنه
   والحشو والحافّة والفراغ الداخلي وقياس الرمز — ولا يُترك للوراثة إلا اللون:
   واحدٌ محدَّد وواحدٌ ممتلئ.

   The two buttons at exactly one size, parting only in colour.

   Each came from a different family: "call" from .pt-ghost-btn, a light button
   that floats over images, and "WhatsApp" from .pt-submit, the button that
   submits forms. So the first inherited a height of thirty-two and a letter of
   eleven and a half, the second a height of thirty-eight, a letter of thirteen
   and side padding of twenty-two — and standing side by side one looked larger
   than the other, as though it mattered more.

   They do not: two roads to the same person, and the reader has the right to
   choose between them without the size whispering an answer. So every measure
   is written here — height, letter and its weight, padding, edge, inner gap
   and glyph size — and nothing is left to inheritance but the colour: one
   outlined and one filled. */
.pt-publisher-acts { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-2); width: 100%; }

/* والرقم المحجوب: سطرٌ يقول لماذا، وزرٌّ واحد حيث يكون له طريق.
   ــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــــ
   وعرضُه عرضُ الزرَّين اللذين يحلّ محلّهما، فلا تقفز بطاقةُ المعلن حين يُفتح
   طلبُ تسويقٍ بعد إعلانٍ عاديّ.
   A withheld number: a line saying why, and one button where there is a road.
   Its width is the width of the two buttons it stands in place of, so the
   advertiser's card does not jump when a marketing request is opened after an
   ordinary listing. */
.pt-publisher-locked { display: grid; gap: var(--sp-2); width: 100%; }

.pt-publisher-locked > .sub {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0;
  line-height: 1.6;
}

.pt-publisher-locked > .sub > svg { flex: none; opacity: .7; }

.pt-publisher-locked > .pt-submit { width: 100%; justify-content: center; }

.pt-publisher-acts > .pt-ghost-btn,
.pt-publisher-acts > .pt-submit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  width: 100%;
  /* ‎min-height‎ يُصفَّر لا يُترك: ‎.pt-submit‎ يحمل حدّاً أدنى قدره ثمانيةٌ
     وثلاثون، وارتفاعٌ مصرَّح به لا ينزل تحت حدٍّ أدنى — فيبقى الزرّان مفترقين
     ببكسلين ولا يُرى لهما سبب. Zeroed rather than left: .pt-submit carries a
     minimum of thirty-eight, and a declared height does not fall below a
     minimum — so the two stay two pixels apart for no visible reason. */
  min-height: 0;
  height: 36px;
  padding-block: 0;
  padding-inline: 10px;
  border-width: 1px;
  border-style: solid;
  border-radius: var(--r-md);
  font-size: 12.5px;
  font-weight: 650;
  line-height: 1;
}

.pt-publisher-acts > * > .ico { width: 15px; height: 15px; }

.pt-ghost-btn[data-copied="1"] { border-color: var(--green); color: var(--green); }

.pt-detail-coords { font-size: 10.5px; color: var(--fg-4); text-align: center; }

/* اسم المعلن رابطاً حيث لصاحبه ملفّ — the advertiser's name as a link where its
   owner has a file. والسهم منطقي لا مرقون: يشير إلى الأمام في الاتجاهين.
   The arrow is logical rather than typed: it points forward in both
   directions. */
.pt-publisher-name {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: var(--fg-1);
}
.pt-publisher-name:hover { color: var(--gold); }
.pt-publisher-name b { font-size: 13px; font-weight: 650; }
.pt-publisher-go { color: var(--fg-4); transform: scaleX(-1); }
[dir="rtl"] .pt-publisher-go { transform: none; }
.pt-publisher-name:hover .pt-publisher-go { color: var(--gold); }

/* خطة السداد في جانب المشروع — the payment plan in a project's side column.
   وهي ما يفصل مشروعاً مرخَّصاً عن وعدٍ على ورق، فلا تُدفن بين ثلاثين سطراً في
   لوحة مبوّبة.
   It is what separates a licensed project from a promise on paper, so it is not
   buried among thirty lines in a tabbed panel. */
.pt-plan {
  display: grid;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  overflow: hidden;
}
/* وكذلك خطّة السداد: تخطيطها من القاعدة المشتركة، وحشوها وحده لها.
   The payment plan likewise: its layout from the shared rule, its padding alone
   its own. */
.pt-plan > div { padding: 8px var(--sp-3); }
.pt-plan > div + div { border-block-start: 1px dashed var(--line-soft); }
.pt-plan dt { font-size: 11.5px; color: var(--fg-3); }
.pt-plan dd { margin: 0; font-size: 12.5px; font-weight: 700; color: var(--gold); }

/* =========================================================================
   صفحة المعروض — an offer's own page
   -------------------------------------------------------------------------
   القشرة وحدها. أما ما فيها فهو قالب التفاصيل نفسه الذي ترسمه النافذة، فلا
   قاعدة هنا تصف سعراً ولا حقيقةً ولا لوحةً مبوّبة — تلك كلها فوق، وتُقرأ في
   الموضعين.

   The shell alone. What is inside it is the very detail template the sheet
   draws, so no rule here describes a price, a fact or a tabbed panel — all of
   those are above, and read in both places.
   ========================================================================= */
.pt-offer { display: grid; gap: var(--sp-4); align-content: start; }

/* وترويسات الأقسام في صفحةٍ أكبر منها في نافذة: النافذة تُمسح بالعين والصفحة
   تُقرأ، ولها من العرض ما يحتمل الفرق.
   Section headings are larger on a page than in a sheet: a sheet is scanned and
   a page is read, and a page has the width to carry the difference. */
.pt-offer .pt-detail-h,
.pt-dev .pt-detail-h { font-size: 14px; padding-block-start: var(--sp-3); }

/* ------------------------------------------------------- The breadcrumb */
/* مسارٌ يتمرّر أفقياً على الشاشة الضيّقة بدل أن ينكسر أربعة أسطر فوق العنوان:
   الفتات دليلٌ يُلمح لا نصٌّ يُقرأ سطراً سطراً.

   A trail that scrolls sideways on a narrow screen rather than breaking over
   four lines above the title: a breadcrumb is a guide glanced at rather than
   text read line by line. */
.pt-crumbs {
  padding-block-start: var(--sp-5);
  overflow-x: auto;
  scrollbar-width: none;
}
.pt-crumbs::-webkit-scrollbar { display: none; }

.pt-crumbs ol {
  display: flex;
  align-items: center;
  gap: 6px;
  white-space: nowrap;
  font-size: 11.5px;
}
.pt-crumbs li { display: inline-flex; align-items: center; gap: 6px; }
.pt-crumbs a { color: var(--fg-3); }
.pt-crumbs a:hover { color: var(--gold); }
.pt-crumbs [aria-current="page"] {
  color: var(--fg-2);
  font-weight: 600;
  max-width: 42ch;
  overflow: hidden;
  text-overflow: ellipsis;
}
.pt-crumb-sep { color: var(--fg-4); transform: scaleX(-1); }
[dir="rtl"] .pt-crumb-sep { transform: none; }

/* ---------------------------------------------------------- The heading */
.pt-offer-head { display: grid; gap: 6px; }
.pt-offer-head h1 {
  font-size: 19px;
  font-weight: 700;
  line-height: 1.5;
}
.pt-offer-ref {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11.5px;
  color: var(--fg-3);
}

/* --------------------------------------------------- The neighbourhood */
/* أربعة أرقام عن الحي لا عن الإعلان، ولذلك تُفصل عنه بحدٍّ لا تُخلط بحقائقه:
   «متوسط سعر الحي ١٨٫٤ مليون» ليست صفةً لهذا العقار، ومن قرأها في شبكة حقائقه
   قرأها عنه.

   Four figures about the district rather than about the listing, which is why a
   border separates them rather than mixing them into its facts: "the district
   averages 18.4 million" is not a property of this property, and read inside its
   own facts grid it would be taken for one. */
.pt-offer-area {
  display: grid;
  gap: var(--sp-3);
  padding-block-start: var(--sp-5);
  border-block-start: 1px solid var(--line);
}

.pt-area-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: var(--sp-2);
}
.pt-area-stats > div {
  display: grid;
  gap: 4px;
  padding: var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
}
.pt-area-stats dt { font-size: 11.5px; color: var(--fg-3); }
.pt-area-stats dd {
  margin: 0;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: var(--tr-lg);
}
.pt-area-stats dd.is-up { color: var(--green); }
.pt-area-stats dd.is-down { color: var(--red); }

/* -------------------------------------------------------- What is like it */
.pt-offer-like {
  display: grid;
  gap: var(--sp-3);
  padding-block-start: var(--sp-5);
  border-block-start: 1px solid var(--line);
}
.pt-offer-like-head {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 14px;
  font-weight: 650;
}
.pt-offer-like-head .ico { color: var(--gold); }

.pt-offer-like-body { display: grid; gap: var(--sp-2); }
.pt-offer-like-body--project,
.pt-offer-like-body--stay {
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: var(--sp-3);
}

/* =========================================================================
   ملفّ المطوّر — a developer's file
   -------------------------------------------------------------------------
   ترويسةٌ تقول من هو، وأربعة أرقام تقول كم بنى، وملفٌّ يقول بأي رخصة، وشبكةٌ
   تعرض ما بناه. ولا شيء منها مخترعٌ لهذه الصفحة: البطاقة بطاقة المشاريع نفسها،
   وشبكة الحقائق شبكةُ التفاصيل نفسها.

   A header saying who they are, four figures saying how much they have built, a
   file saying under which licence, and a grid showing what they built. None of
   it is invented for this page: the card is the projects' own card, and the
   facts grid is the detail's own grid.
   ========================================================================= */
.pt-dev { display: grid; gap: var(--sp-4); align-content: start; }

.pt-dev-head {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex-wrap: wrap;
  padding: var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--shadow-1);
}

.pt-dev-mark {
  flex: none;
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  border-radius: var(--r-md);
  background: var(--surface-2);
  color: var(--gold);
}

.pt-dev-title { flex: 1; min-width: 200px; display: grid; gap: 7px; }
.pt-dev-title h1 { font-size: 19px; font-weight: 700; line-height: 1.4; }
.pt-dev-badges { display: flex; flex-wrap: wrap; gap: 5px; }

.pt-dev-acts { flex: none; display: flex; gap: var(--sp-2); }
.pt-dev-acts > * { height: 32px; }

.pt-dev-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--sp-2);
}
.pt-dev-stats > div {
  display: grid;
  gap: 4px;
  padding: var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
}
.pt-dev-stats dt {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11.5px;
  color: var(--fg-3);
}
.pt-dev-stats dt .ico { color: var(--gold); }
.pt-dev-stats dd { margin: 0; font-size: 19px; font-weight: 700; letter-spacing: var(--tr-lg); }

.pt-dev-file,
.pt-dev-map,
.pt-dev-work { display: grid; gap: var(--sp-3); }

/* والخريطة تأخذ حدَّ الصفحة وحوافها كما تأخذها البطاقة، فلا تطفو مستطيلاً
   بلا إطار بين قسمين مؤطَّرين.
   The map takes the page's border and radius as a card does, so it does not
   float as an unframed rectangle between two framed sections. */
.pt-dev-map .gm-wrap {
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  overflow: hidden;
}

.pt-dev-link { display: inline-flex; align-items: center; gap: 5px; color: var(--gold); }
.pt-dev-link:hover { text-decoration: underline; }

@media (max-width: 720px) {
  .pt-dev-head { flex-direction: column; align-items: stretch; }
  .pt-dev-acts > * { flex: 1; justify-content: center; }
  .pt-offer-head h1 { font-size: 17px; }
  .pt-offer-like-body--project,
  .pt-offer-like-body--stay { grid-template-columns: 1fr; }
}


/* =========================================================================
   بطاقة الخريطة — the card on the map
   -------------------------------------------------------------------------
   ما يُفتح حين يُنقر دبوس. لم تعد قائمةَ مفتاحٍ وقيمة كما كانت، بل البطاقة
   نفسها التي في القائمة — لأن ما يقرّر فتحَ إعلان صورتُه وسعرُه وحقائقُه
   الأربع، لا جدولٌ من سبعة صفوف.

   What opens when a pin is clicked. No longer a key-and-value list but the very
   card that is in the list — because what decides opening a listing is its
   image, its price and its four facts, not a table of seven rows.
   ========================================================================= */
/* عرض البطاقة، وهو موضع العطل الذي كانت به ضيّقة.
   -------------------------------------------------------------------------
   كان Leaflet يكتب العرض في سمة style على العنصر بعد أن يقيس محتواه، وورقةُ
   اللوحة تُلغي ذلك بـ‎width: auto !important‎ وتفرض حدّاً أدنى ‎216‎ بكسل — وهي
   محقّة في ذلك: نوافذ اللوحة قوائمُ مفتاحٍ وقيمة تتّسع لمحتواها. فكانت بطاقة
   البوابة تخرج بعرض ‎216‎ بالضبط مهما رُفعت القيمة في السكربت، ولم يكن إلى
   تجاوزها سبيلٌ إلا ‎!important‎ فوق ‎!important‎.

   The card's width, and the very place the defect lived.

   Leaflet used to write the width into the element's own style attribute after
   measuring its content, and the console's sheet cancelled that with
   width: auto !important and a 216-pixel floor — rightly: the console's popups
   are key-and-value lists that should fit their contents. So the portal's card
   came out at exactly 216 however high the value in the script was raised, and
   there was no way past it but an !important above an !important.

   وMapbox لا يكتب عرضاً في سمة، إنما يكتب ‎max-width‎ على الحاوية من الخيار
   الممرَّر إليه. فسقطت المعركة كلها: عرضٌ مكتوبٌ هنا بمحدِّدٍ أقوى يكفي، ونوافذ
   اللوحة تبقى على حدّها الأدنى كما كانت.

   Mapbox writes no width into an attribute; it writes a max-width onto the
   container from the option handed to it. So the whole fight fell away: a width
   written here under a stronger selector is enough, and the console's popups keep
   their floor as they had it.

   والصندوقان صارا صندوقاً: كان في Leaflet غلافٌ يحمل الخلفية ومحتوىً يحمل العرض،
   وMapbox يجمعهما في واحد.

   And the two boxes became one: Leaflet had a wrapper carrying the background and
   a content node carrying the width, and Mapbox folds them into one. */
.portal-theme .gm-card-popup .mapboxgl-popup-content {
  margin: 0;
  padding: 0;
  width: 412px;
  max-width: calc(100vw - 40px);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--fg-1);
  box-shadow: var(--shadow-3);
  overflow: hidden;
}

/* والذيل يتبع جهة الفتح: مثلّثٌ من الحدود، المصبوغ منه حدُّ الجهة المقابلة.
   The tip follows the direction it opens: a triangle of borders, the coloured one
   being the side opposite. */
.portal-theme .gm-card-popup.mapboxgl-popup-anchor-top .mapboxgl-popup-tip,
.portal-theme .gm-card-popup.mapboxgl-popup-anchor-top-left .mapboxgl-popup-tip,
.portal-theme .gm-card-popup.mapboxgl-popup-anchor-top-right .mapboxgl-popup-tip { border-bottom-color: var(--surface); }
.portal-theme .gm-card-popup.mapboxgl-popup-anchor-bottom .mapboxgl-popup-tip,
.portal-theme .gm-card-popup.mapboxgl-popup-anchor-bottom-left .mapboxgl-popup-tip,
.portal-theme .gm-card-popup.mapboxgl-popup-anchor-bottom-right .mapboxgl-popup-tip { border-top-color: var(--surface); }
.portal-theme .gm-card-popup.mapboxgl-popup-anchor-left .mapboxgl-popup-tip { border-right-color: var(--surface); }
.portal-theme .gm-card-popup.mapboxgl-popup-anchor-right .mapboxgl-popup-tip { border-left-color: var(--surface); }

/* وزرّ الإغلاق ينتقل من اليمين الفيزيائي إلى جهة الصورة.
   -------------------------------------------------------------------------
   ورقة Mapbox تثبّته بـ‎right: 0‎ — يميناً فيزيائياً لا نهايةَ سطر. وفي
   الإنجليزية يقع ذلك فوق المصغَّرة فلا يُرى منه ضرر، وفي العربية ينقلب
   التخطيط ولا ينقلب هو: فيجلس فوق أوّل حرفٍ من العنوان. وبالخاصيّة المنطقية
   يتبع الاتجاهَ فيقع فوق المصغَّرة في اللغتين، وهي وجهٌ مصبوغ يُقرأ الأبيض
   عليه بهالةٍ تحمله فوق فاتحه وغامقه سواء.

   The close button moves from the physical right to the image's side.

   Mapbox's own sheet pins it with right: 0 — a physical right rather than the
   end of a line. In English that lands over the thumbnail and does no harm; in
   Arabic the layout mirrors and it does not, so it comes to rest over the first
   letter of the title. Given the logical property it follows the direction and
   lands over the thumbnail in both languages — a tinted face on which white
   reads, carried over its pale and its dark alike by a halo. */
.portal-theme .gm-card-popup .mapboxgl-popup-close-button {
  z-index: 3;
  inset-block-start: 0;
  inset-inline: auto 0;
  display: grid;
  place-items: center;
  margin: 6px;
  width: 24px;
  height: 24px;
  padding: 0;
  border-radius: var(--r-circle);
  background: rgb(0 0 0 / 0.46);
  color: #fff;
  font-size: 15px;
  line-height: 1;
}
.portal-theme .gm-card-popup .mapboxgl-popup-close-button:hover {
  background: rgb(0 0 0 / 0.68);
  color: #fff;
}

/* البطاقة بمقاس يُقرأ فيه ما عليها.
   -------------------------------------------------------------------------
   كانت في عرض ‎300‎ بكسل — عرض النافذة الافتراضي في Leaflet — فانكسر العنوان
   ثلاثة أسطر، وانضغطت الحقائق الأربع سطرين، وبقي للصورة عمودٌ بعرض ‎106‎ لا
   يبين فيه شيء. والمقاس هنا مقيسٌ على أطول عنوان في البيانات: عمودٌ للنص لا
   يقلّ عن ‎250‎، وعمودٌ للصورة ‎142‎، وارتفاعٌ أدنى يمنع البطاقة القصيرة أن
   تصير شريطاً.

   The card at a size its own contents can be read at.

   It was 300 pixels wide — Leaflet's default popup width — so the title broke
   over three lines, the four facts squeezed onto two, and the image was left a
   106-pixel column in which nothing could be made out. The size here is
   measured against the longest title in the data: no less than 250 for the
   text column, 142 for the image, and a floor on the height so a short card
   does not become a strip. */
.gm-card {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 142px;
  min-height: 168px;
  color: inherit;
}

.gm-card-body {
  display: grid;
  gap: 5px;
  align-content: start;
  min-width: 0;
  padding: 12px 13px;
}

.gm-card-title {
  font-size: 13.5px;
  font-weight: 650;
  line-height: 1.55;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.gm-card-price {
  display: inline-flex;
  align-items: baseline;
  gap: 5px;
  font-size: 17px;
  font-weight: 700;
  letter-spacing: var(--tr-lg);
  color: var(--green);
}
.gm-card-price small { font-size: 11px; font-weight: 600; color: var(--fg-3); letter-spacing: 0; }

.gm-card-facts { display: flex; flex-wrap: wrap; gap: 4px 14px; font-size: 11.5px; color: var(--fg-2); }
.gm-card-fact { display: inline-flex; align-items: center; gap: 4px; }
.gm-card-fact svg { color: var(--fg-3); flex: none; }

.gm-card-desc {
  font-size: 11.5px;
  color: var(--fg-3);
  line-height: 1.6;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.gm-card-place { font-size: 11px; color: var(--fg-4); }

/* أمّا الشاشة الضيّقة فقد كانت تكدّس العمودين عموداً واحداً — الصورة فوق
   والنص تحتها — وهو ما ضاعف ارتفاع البطاقة على الشاشة التي لا ارتفاع فيها.
   وقاعدتها الآن في قسم الهاتف آخرَ الورقة، حيث بقيّةُ ما يطفو على الخريطة.

   The narrow screen used to stack the two columns into one — image above, text
   below — which doubled the card's height on the very screen that has no
   height to spare. Its rule now lives in the phone section at the end of this
   sheet, with everything else that floats on the map. */

.gm-card-shot {
  display: grid;
  place-items: center;
  color: rgb(255 255 255 / 0.82);
  background: linear-gradient(135deg, var(--cocoa), var(--olive));
}
.gm-card-shot[data-art="1"] { background: linear-gradient(135deg, var(--olive), var(--gold)); }
.gm-card-shot[data-art="2"] { background: linear-gradient(135deg, var(--gold), var(--sand)); color: rgb(44 20 3 / 0.6); }
.gm-card-shot[data-art="3"] { background: linear-gradient(135deg, #2f6f33, var(--green)); }
.gm-card-shot[data-art="4"] { background: linear-gradient(135deg, var(--cocoa), var(--red)); }
.gm-card-shot[data-art="5"] { background: linear-gradient(135deg, var(--sand), var(--olive)); }

/* =========================================================================
   الليل: لا كتابة بالبنّي الغامق
   -------------------------------------------------------------------------
   القاعدة واحدة — كل ما كان حبراً داكناً على سطحٍ فاتح يصير أبيض، والسطح
   يعمّق حتى يُقرأ عليه. واستثناءان اثنان لا ثالث لهما:

       الشارة الممتلئة   أسود على أخضر — لأن الأخضر يبقى فاتحاً في الليل
                          كما هو في النهار، والأسود عليه أوضح من الأبيض.
       الرقاقة           أبيض على بنّي.

   One rule — everything that was dark ink on a light surface becomes white, and
   the surface deepens until white can be read on it. With exactly two
   exceptions:

       the filled pill   black on green, because green stays as light by night
                         as by day, and black on it is clearer than white.
       the chip          white on brown.
   ========================================================================= */
/* والأخضر في الليل أفتح لا أغمق، فالأسود عليه يبقى هو المقروء.
   Green is lighter by night rather than darker, so black on it stays the
   legible choice. */
.portal-theme[data-theme="dark"] .pill.solid { color: #000; }
.portal-theme[data-theme="dark"] .pill.solid svg { color: #000; }

/* والوجه نفسه يأتي من --rail-face، فلا يُكتب هنا مرّتين ولا يختلف عن أخواته
   في الشريط. ما يبقى للّيل هو الحبر وحده: أبيض على البنّي.

   The face itself comes from --rail-face, so it is not written twice here and
   does not differ from its siblings on the rail. What is left to the night is
   the ink alone: white on the brown. */
.portal-theme[data-theme="dark"] .pt-chip { color: #fff; }
.portal-theme[data-theme="dark"] .pt-chip i { color: rgb(255 255 255 / 0.7); }
.portal-theme[data-theme="dark"] .pt-chip:hover { color: #fff; }
.portal-theme[data-theme="dark"] .pt-chip[aria-pressed="true"] {
  background: var(--gold-face);
  color: #fff;
}
.portal-theme[data-theme="dark"] .pt-chip[aria-pressed="true"] i { color: rgb(255 255 255 / 0.72); }

.portal-theme[data-theme="dark"] .pt-shot[data-art="2"],
.portal-theme[data-theme="dark"] .gm-card-shot[data-art="2"] { color: #fff; }
.portal-theme[data-theme="dark"] .pt-day[data-edge] { background: var(--gold-face); color: #fff; }
.portal-theme[data-theme="dark"] .pt-canvas .pt-note { background: var(--brown-face); color: #fff; }
.portal-theme[data-theme="dark"] .pt-dock-btn.is-on,
.portal-theme[data-theme="dark"] .pt-dock-btn[aria-pressed="true"] {
  background: var(--gold-face);
  border-color: var(--gold-face);
  color: #fff;
}

/* =========================================================================
   الضيّق — narrow screens
   ========================================================================= */
@media (max-width: 860px) {
  .pt-detail-split { grid-template-columns: 1fr; }

  /* وعمودُ الجانب يصير صفّاً كامل العرض عند هذا الحدّ، فيتّسع للتقويم أكثر
     ممّا يلزمه. ويبقى ضيّقاً بحدٍّ أعلى: شبكةٌ بسبعة أعمدة ممدودةٌ على عرض
     الشاشة خاناتٌ متباعدة لا تُقرأ شهراً.

     The side column becomes a full-width row at this breakpoint, giving the
     calendar more room than it needs. It stays narrow by a ceiling: a
     seven-column grid stretched across the screen is scattered cells rather than
     a month. */
  .pt-book { max-width: 340px; }

  .pt-gallery { grid-template-columns: 1fr; }
  .pt-gallery-rest { grid-template-columns: repeat(4, 1fr); }

  /* والمصغّرات هنا صفٌّ تحت الكبيرة لا عمودٌ إلى جانبها، فلا ارتفاع لها
     تملؤه.
     -----------------------------------------------------------------------
     وكانت ‎height: 100%‎ باقيةً من تخطيط العمود: تسأل عن ارتفاع صفٍّ ضمنيٍّ
     غير محدَّد، فتحسبه صفراً — فتنعدم المصغّرات الأربع بين ‎641‎ و‎860‎ بكسل
     ولا تُرسم أصلاً. وهو ما لا يُرى في فحصٍ على شاشةٍ عريضة ولا على هاتف،
     لأنه بينهما.

     Here the small slots are a row beneath the large one rather than a column
     beside it, so they have no height to fill.

     height: 100% was left over from the column layout: it asks after the height
     of an implicit row that is not definite, and resolves to zero — so the four
     small slots vanish between 641 and 860 pixels and are not drawn at all.
     Which shows in no check on a wide screen and none on a phone, because it
     lies between the two. */
  .pt-gallery-rest > .pt-shot { aspect-ratio: 3 / 2; height: auto; }

  .pt-row-hit { grid-template-columns: minmax(0, 1fr) 132px; }
}

@media (max-width: 560px) {
  .pt-row-hit { grid-template-columns: 1fr; }
  .pt-shot--side { aspect-ratio: 16 / 9; min-height: 0; order: -1; }
  .pt-feed-body--grid { grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); }
  .pt-modal-panel { height: 94dvh; }
}
/* =========================================================================
   التعليقات — the comments
   -------------------------------------------------------------------------
   الشيء الوحيد في البوابة الذي يكتبه الزائر. وشكله يتبع ذلك: خيطٌ مقروء، ثم
   نموذجٌ ظاهر لا زرٌّ يفتح نموذجاً — فمن جاء ليكتب لا يُطلب منه أن يبحث عن
   مكان الكتابة أوّلاً.

   The one thing in the portal a visitor writes. Its shape follows from that: a
   readable thread, then a form in plain sight rather than a button that opens
   one — whoever came to write is not first asked to find where writing happens.
   ========================================================================= */
.pt-thread { display: grid; gap: var(--sp-3); }

.pt-comment {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--sp-2);
  padding: var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
}

/* الردود تُزاح إلى الداخل وتخفت حدودها، فيُقرأ الخيط شجرةً لا قائمة.
   Replies are indented and their borders quieten, so the thread reads as a
   tree rather than a list. */
.pt-thread--replies {
  margin-block-start: var(--sp-2);
  margin-inline-start: var(--sp-3);
  padding-inline-start: var(--sp-3);
  border-inline-start: 2px solid var(--line);
  gap: var(--sp-2);
}
.pt-thread--replies .pt-comment { padding: var(--sp-2); background: var(--surface-1); }

.pt-comment-mark {
  display: grid;
  place-items: center;
  flex: none;
  width: 30px;
  height: 30px;
  border-radius: var(--r-circle);
  background: var(--surface-2);
  color: var(--olive);
}
.portal-theme[data-theme="dark"] .pt-comment-mark { color: var(--gold); }

.pt-comment-body { display: grid; gap: 3px; min-width: 0; }

.pt-comment-head {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
  font-size: 12px;
}
.pt-comment-head b { font-weight: 650; }
.pt-comment-head time { font-size: 10.5px; color: var(--fg-4); }

.pt-comment-text { font-size: 12.5px; line-height: 1.7; color: var(--fg-2); overflow-wrap: anywhere; }

.pt-comment-form {
  display: grid;
  gap: var(--sp-3);
  padding: var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
}

.pt-form-error {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 8px 10px;
  border: 1px solid var(--red);
  border-radius: var(--r-sm);
  background: rgb(198 49 49 / 0.08);
  color: var(--red);
  font-size: 12px;
  font-weight: 600;
}

/* الفخّ يُخفى عن الناس ويبقى للآلات. و‎display: none‎ وحدها تتخطّاها بعض
   الآلات، فيُزاح خارج الشاشة كذلك.

   The trap is hidden from people and left for machines. display: none alone is
   stepped over by some of them, so it is moved off-screen as well. */
.pt-trap {
  position: absolute;
  inset-inline-start: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}


/* =========================================================================
   الهاتف — the phone
   -------------------------------------------------------------------------
   ليست هذه طبقةَ ترقيع فوق شكل الحاسوب، بل شكلٌ ثانٍ للقشرة نفسها. فالفرق بين
   الشاشتين ليس فرقَ عرضٍ فقط: على الحاسوب فأرةٌ تبلغ كل بقعة من اللوح بالقدر
   نفسه من الجهد، وعلى الهاتف إبهامٌ يبلغ أسفلَ الشاشة ولا يبلغ أعلاها. ولذلك
   ينزل التنقّل إلى الأسفل، ويبقى في الأعلى ما لا يُلمس كثيراً: الهوية والحساب.

   This is not a patch over the desktop shape but a second shape for the same
   shell. The difference between the two screens is not only one of width: on a
   desktop a mouse reaches every corner of the board for the same effort, while
   on a phone a thumb reaches the bottom of the screen and not the top. So the
   navigation moves down, and what stays up is what is rarely touched: the
   identity and the account.

   وثلاث قواعد تحكم كل ما تحته:

       الإبهام     ما يُضغط لا يقلّ عن أربعين بكسلاً، وما يُضغط كثيراً يجلس
                   في النصف الأسفل من الشاشة.
       الحافة      لا شيء يُقصّ ولا شيء يفيض: الأشرطة تُمرَّر، والألواح تملأ
                   العرض، والحوافُّ الآمنة للهواتف المحزوزة محسوبة.
       الحرف       حقول الإدخال بستة عشر بكسلاً لا أقل، وإلا كبّر iOS الصفحة
                   عند أول لمسة على حقل — وهي أشهر قفزةٍ في الويب المحمول.

   Three rules govern everything below:

       the thumb   nothing pressable is under forty pixels, and what is pressed
                   often sits in the lower half of the screen.
       the edge    nothing is clipped and nothing overflows: rails scroll,
                   panels fill the width, and the safe insets of notched
                   phones are accounted for.
       the letter  input fields are sixteen pixels and never less, or iOS
                   zooms the page at the first touch on a field — the best
                   known lurch in the mobile web.
   ========================================================================= */
@media (max-width: 760px) {

  /* ------------------------------------------------------------ Metrics */
  /* ارتفاع شريط الأقسام وحوافُّ الجهاز الآمنة رمزان، فيقرأهما كل ما يجلس فوق
     الشريط أو تحته من لوحٍ ودرجٍ ونافذة — ولا يُكرَّر الرقم في عشرة مواضع.

     The height of the section bar and the device's safe insets are tokens, so
     everything that sits above or below it — panel, drawer, sheet — reads the
     same number instead of repeating it in ten places. */
  .portal-theme {
    --pt-nav: 52px;
    /* المصغَّرة تتبع الشاشة لا رقماً ثابتاً: مئةٌ وعشرون بكسلاً ثابتةً تأخذ من
       شاشةٍ عرضها ثلاثمئة وعشرون أربعين بالمئة، فينكسر النصّ إلى جانبها.
       The thumbnail follows the screen rather than a fixed number: a rigid
       hundred and twenty takes forty per cent of a three-hundred-and-twenty
       pixel screen, and the text beside it breaks. */
    --pt-thumb: clamp(96px, 30vw, 128px);
    --pt-safe-b: env(safe-area-inset-bottom, 0px);
    --pt-safe-t: env(safe-area-inset-top, 0px);
    --pt-safe-s: env(safe-area-inset-left, 0px);
    --pt-safe-e: env(safe-area-inset-right, 0px);
  }

  /* اللمس لا يُظلَّل بأزرق النظام — the system's blue tap flash is not ours. */
  .pt a,
  .pt button,
  .pt-modal a,
  .pt-modal button,
  .pt-sheet a,
  .pt-sheet button { -webkit-tap-highlight-color: rgb(197 153 43 / 0.20); }

  /* --------------------------------------------------------- The shell */
  /* المحتوى ينتهي فوق شريط الأقسام لا تحته. الهامش لا الحشو، فيصحّ في الوضعين:
     صفحةٌ تتمرّر داخل main، وخريطةٌ تملأه — فتنتهي الخريطة عند الشريط ولا يختفي
     تحته دبّوس.

     The content ends above the section bar rather than behind it. A margin
     rather than padding, so it holds in both modes: a page that scrolls inside
     main, and a map that fills it — the map ends at the bar and no pin is lost
     beneath it. */
  .pt-main { margin-block-end: calc(var(--pt-nav) + var(--pt-safe-b)); }

  /* وبلا جافاسكربت لا يكون الدرج لوحاً منزلقاً بل ذيلَ الصفحة، وهو آخر ما فيها
     — فيقع تحت الشريط المثبَّت. الهامش على main لا يبلغه لأنه ليس فيه.

     Without JavaScript the drawer is not a sliding panel but the page's tail,
     and it is the last thing on it — so it lands under the pinned bar. The
     margin on main does not reach it, because it is not inside it. */
  :root:not(.js) .pt-drawer { padding-block-end: calc(var(--pt-nav) + var(--pt-safe-b)); }

  /* -------------------------------------------------------- The top bar */
  /* سطرٌ واحد بعد سطرين. الشبكة ذات الأعمدة الثلاثة تصير صفّاً: العلامة في
     أوّله، والأفعال في آخره، والأقسام قد غادرت إلى الأسفل.

     One row after two. The three-column grid becomes a line: the mark at its
     start, the actions at its end, and the sections have left for the bottom.

     وz-index يرتفع لأن شريط الأقسام ابنُ هذا الشريط وإن كان مثبَّتاً في أسفل
     الشاشة: فترتيبه في الطبقات ترتيبُ أبيه. ويبقى دون الدرج والحجاب والنوافذ.

     The z-index rises because the section bar is this bar's child even though
     it is pinned to the bottom of the screen: its place in the stack is its
     parent's. It stays below the drawer, the scrim and the sheets. */
  .pt-top {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    height: auto;
    min-height: 48px;
    padding-block: 0;
    padding-inline: max(var(--sp-3), var(--pt-safe-s)) max(var(--sp-3), var(--pt-safe-e));
    padding-block-start: var(--pt-safe-t);
    z-index: 1200;
  }

  .pt-brand { flex: none; }
  .pt-brand-logo { height: 28px; }
  .pt-actions { margin-inline-start: auto; gap: 0; }

  /* الفعل يصير أيقونةً في مربّع يبلغه الإبهام، وكلمتُه تبقى للقارئ الآلي: فلا
     تُحذف بـdisplay: none — إذ لا اسم لأيقونةٍ مخفيّة عن الوصول.

     An action becomes an icon in a square the thumb can hit, and its word is
     kept for the screen reader: it is not removed with display: none, because
     an icon hidden from accessibility has no name left. */
  /* والحدُّ يُرفع عن الأربعة جميعاً.
     -------------------------------------------------------------------------
     أربعة مربّعات محدودة متجاورة في شريطٍ عرضه ثلاثمئة وستون تُقرأ صفّاً من
     الأزرار الضخمة لا صفّاً من الأدوات. والضخامة يصنعها الحدُّ لا مساحةُ
     اللمس: فيبقى المربّع بمقاسٍ يبلغه الإبهام، ويذهب الوجه والخطّ — تُقرأ
     الأيقونة أيقونةً، ويبقى المملوء وحده مملوءاً وهو القائم.

     The border comes off all four.

     Four bordered squares side by side in a three-hundred-and-sixty-pixel bar
     read as a row of huge buttons rather than a row of tools. It is the border
     that makes the bulk, not the touch area: the square keeps a size the thumb
     reaches, and the face and the line go — the icon reads as an icon, and only
     what is filled stays filled, which is the one that is current. */
  .pt-action,
  .pt-avatar {
    position: relative;
    width: 38px;
    height: 38px;
    padding-inline: 0;
    justify-content: center;
    border-color: transparent;
    background: transparent;
  }
  .pt-action:hover,
  .pt-avatar:hover { background: var(--surface-1); border-color: transparent; }

  /* والقائم يبقى ذهبيّاً: هي الحالة الوحيدة التي يقولها الشريط بلون.
     The current one stays gold: the single state the bar says with colour. */
  .pt-action--on,
  .pt-action--on:hover {
    background: var(--gold-face);
    border-color: var(--gold-face);
    color: var(--on-gold);
  }
  .pt-action > span {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }

  /* زرّ إطفاء الخريطة يذهب: الزرّ الذي بجانبه يفعل فعله نفسه، ومربّعان
     متجاوران يفعلان شيئاً واحداً خطأٌ لا اختصار.

     The map-off button goes: the one beside it does the same thing, and two
     adjacent squares doing one thing is a mistake rather than a shortcut. */
  .pt-mapswitch-off { display: none; }
  .pt-mapswitch { gap: 0; }

  /* ------------------------------------------------- The sections, below */
  /* التبويبات الثلاثة تترك وسط الشريط العلوي وتصير شريطاً مثبَّتاً في أسفل
     الشاشة: ثلاثة أثلاث متساوية، رمزٌ فوق كلمة، وخطٌّ ذهبي على حافّة القسم
     القائم. وهو الموضع الذي يتوقّعه من يمسك هاتفاً بيد واحدة.

     The three tabs leave the centre of the top bar and become a bar pinned to
     the bottom of the screen: three equal thirds, a glyph above a word, and a
     gold line on the edge of the section one stands in. It is the place
     expected by anyone holding a phone in one hand. */
  /* justify-self يبقى من التخطيط العريض حيث كانت التبويبات تتوسّط الشريط.
     وعلى صندوقٍ خارج المجرى لا يعني «توسَّط في مكانك» بل «انكمش إلى مقاس
     محتواك ثم توسَّط» — فيصير الشريط السفلي مئةً وأربعاً وعشرين بكسلاً في وسط
     الشاشة بدل أن يملأ عرضها، رغم أن حافّتيه صفر. ولذلك يُعاد إلى stretch.

     justify-self survives from the wide layout, where the tabs centred
     themselves in the bar. On an out-of-flow box it does not mean "centre
     where you are" but "shrink to your content's size, then centre" — so the
     bottom bar becomes a hundred and twenty-four pixels in the middle of the
     screen instead of filling its width, both insets being zero
     notwithstanding. Hence the return to stretch. */
  .pt-tabs {
    position: fixed;
    inset-block-end: 0;
    inset-inline: 0;
    z-index: 1200;
    justify-self: stretch;
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    gap: 0;
    padding: 0 var(--pt-safe-e) var(--pt-safe-b) var(--pt-safe-s);
    border-radius: 0;
    border-block-start: 1px solid var(--line);
    background: var(--surface);
    box-shadow: 0 -2px 16px rgb(88 36 21 / 0.10);
  }

  .pt-tab {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 3px;
    height: var(--pt-nav);
    padding-inline: 2px;
    border-radius: 0;
    font-size: 10.5px;
    font-weight: 650;
    line-height: 1.2;
  }
  .pt-tab svg { width: 19px; height: 19px; }
  .pt-tab span {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    padding-block-end: 0;
    border-block-end: 0;
  }

  .pt-tab:hover { background: transparent; color: var(--tab-ink); }
  .pt-tab[aria-current="page"] {
    background: transparent;
    color: var(--tab-live);
    box-shadow: inset 0 2px 0 var(--tab-live);
  }

  /* ------------------------------------------------------------ The rail */
  /* سطران: بحثٌ يملأ العرض وزرُّ المرشِّحات بجانبه، ثم شريط التصنيفات تحتهما.
     ومنتقي المدينة يغادر الشريط — فهو مكرَّرٌ حرفاً بحرف داخل نافذة البحث،
     وحقلٌ مكرَّر على شاشةٍ ضيّقة يأخذ مكاناً ولا يضيف فعلاً.

     Two lines: a search that fills the width with the filter button beside it,
     then the category rail beneath them. The city picker leaves the rail — it
     is repeated word for word inside the search sheet, and a repeated field on
     a narrow screen takes room without adding an action. */
  .pt-browse { padding-inline: var(--sp-3); }
  .pt-rail { gap: var(--sp-2); }

  /* الحشو يُكتب بمحدِّدٍ أطول عمداً: القاعدة العامة لوضع الصفحة
     ‎.pt[data-mode="page"] .pt-rail‎ تحمل ثلاث فئات، فتغلب ‎.pt-browse .pt-rail‎
     وتفرض حشواً جانبياً مقداره اثنان وعشرون بكسلاً. ومع حشو الحاوية يصير
     التنازل أربعةً وثلاثين بكسلاً من كل جانب على شاشةٍ عرضها ثلاثمئة وستون —
     خُمسُ العرض ذهب حافّةً فارغة. فيُساوى المحدِّدان طولاً حتى يعود القرار
     لهذه الطبقة.

     The padding is written with a deliberately longer selector: the general
     page-mode rule, .pt[data-mode="page"] .pt-rail, carries three classes and
     so beats .pt-browse .pt-rail, imposing twenty-two pixels of side padding.
     With the container's own padding on top, that concedes thirty-four pixels
     a side on a three-hundred-and-sixty-pixel screen — a fifth of the width
     spent on empty margin. Matching the selector's length returns the decision
     to this layer. */
  .pt[data-mode="page"] .pt-browse .pt-rail { padding: var(--sp-2) 0; }
  .pt-rail-search {
    flex: 1;
    min-width: 0;
    max-width: none;
    height: 44px;
    font-size: 13px;
  }
  .pt-rail-btn,
  .pt-rail-clear {
    width: 44px;
    height: 44px;
    padding-inline: 0;
    justify-content: center;
  }
  .pt-rail-end .pt-select { display: none; }

  /* الشريط يمتدّ إلى حافّتي الشاشة، فما يظهر عند الحافّة نصفُ رقاقةٍ يقول إنّ
     وراءها المزيد — لا كلمةٌ مقطوعة تقول إنّ التصميم انكسر.

     The rail runs to both edges of the screen, so what shows at the edge is
     half a chip saying there is more behind it — not a cut word saying the
     design broke. */
  .pt-chips {
    order: 3;
    flex-basis: 100%;
    margin-inline: calc(var(--sp-3) * -1);
    padding-inline: var(--sp-3);
    scroll-padding-inline: var(--sp-3);
  }
  .pt-chip { height: 40px; padding-inline: 14px; font-size: 12.5px; }
  .pt-chip svg { width: 15px; height: 15px; }

  /* ------------------------------------------ The same rail, over the map */
  /* والمقاسات التي فوقها للعرض العادي وحده.
     -------------------------------------------------------------------------
     الشريط واحد للسطحين، ولكن ما يقف عليه ليس واحداً. في العرض العادي يجلس في
     مجرى صفحةٍ تتمرّر، فالحقلُ العريض والرقاقةُ الواسعة فيه سعةٌ لا ثمن لها.
     وفوق الخريطة يطفو على ما جاء الزبون لينظر إليه، فكلُّ بكسلٍ يأخذه بكسلٌ من
     الخريطة نفسها: أربعةٌ وأربعون للحقل وأربعون للرقاقة تصير — مع الشريط
     العلوي فوقها وشريط الأقسام تحتها — ثلثَ الشاشة قبل أن يُرى دبوسٌ واحد.

     ولذلك يُقاس هنا بمقياسٍ ثانٍ: الحقلُ ثمانيةٌ وثلاثون والرقاقةُ ثلاثون —
     وكلتاهما فوق الأربعة والعشرين التي لا يُخطئها الإصبع، وقد ردّتا إلى
     الخريطة ستّةً وعشرين بكسلاً من ارتفاعها.

     The measurements above are for the normal view alone.

     One rail serves both surfaces, but what it stands on is not one thing. In
     the normal view it sits in the flow of a page that scrolls, where a wide
     field and a roomy chip are a generosity that costs nothing. Over the map it
     floats on the very thing the customer came to look at, and every pixel it
     takes is a pixel of that map: forty-four for the field and forty for the
     chip become — with the top bar above and the section bar below — a third of
     the screen before a single pin is seen.

     So it is measured here by a second measure: thirty-eight for the field and
     thirty for the chip — both above the twenty-four a finger does not miss,
     and together handing twenty-six pixels of height back to the map. */
  .pt[data-mode="map"] .pt-rail {
    inset-block-start: 8px;
    inset-inline: 8px;
    gap: 6px;
  }
  .pt[data-mode="map"] .pt-rail-search {
    height: 38px;
    padding-inline: 12px;
    font-size: 12.5px;
  }
  .pt[data-mode="map"] .pt-rail-btn,
  .pt[data-mode="map"] .pt-rail-clear { width: 38px; height: 38px; }
  .pt[data-mode="map"] .pt-chips {
    gap: 5px;
    margin-inline: -8px;
    padding-inline: 8px;
    scroll-padding-inline: 8px;
  }
  .pt[data-mode="map"] .pt-chip {
    height: 30px;
    padding-inline: 10px;
    gap: 5px;
    font-size: 11.5px;
  }
  .pt[data-mode="map"] .pt-chip svg { width: 13px; height: 13px; }
  .pt[data-mode="map"] .pt-chip i { font-size: 10px; }

  /* -------------------------------------------------------- The document */
  .pt-page { padding-inline: var(--sp-3); padding-block-end: var(--sp-6); }
  .pt-page--narrow { padding-block-start: var(--sp-4); }
  .pt-head { padding-block: var(--sp-5) var(--sp-4); }
  .pt-head h1 { font-size: 18px; }
  .pt-head p { font-size: 12.5px; }
  .pt-panel { padding: var(--sp-4) var(--sp-3); }
  .pt-panel-title { font-size: 14px; }

  /* الصفات الثلاث في عمود: ثلاثةٌ على عمودين تترك واحدةً يتيمةً في صفٍّ لا
     يوازنها فيه شيء، والعمود الواحد يجعل كل صفةٍ زرّاً عريضاً يُقرأ ويُضغط.

     The three roles in one column: three over two columns leaves one orphaned
     in a row with nothing to balance it, while a single column makes each role
     a wide button, read and pressed as one. */
  .pt-roles { grid-template-columns: 1fr; }
  .pt-role { padding: var(--sp-4) var(--sp-3); }

  .pt-subnav,
  .pt-split .pt-side {
    margin-inline: calc(var(--sp-3) * -1);
    padding-inline: var(--sp-3);
    scroll-padding-inline: var(--sp-3);
  }
  .pt-split .pt-side { border-inline: 0; border-radius: 0; }
  .pt-side-item { padding-block: 12px; }

  /* الجدول يبقى جدولاً ويُمرَّر عرضاً، ولكن بحشوٍ أضيق فيدخل منه في الشاشة
     أعمدةٌ أكثر قبل أن يُطلَب التمرير.

     A table stays a table and scrolls sideways, but with tighter padding so
     more of its columns land on the screen before scrolling is asked for. */
  .pt-page table.data { font-size: 12.5px; min-width: 520px; }
  .pt-page table.data.pt-seats { min-width: 720px; }
  .pt-page table.data thead th,
  .pt-page table.data tbody td { padding-inline: var(--sp-3); }

  /* ---------------------------------------------------------- The feed */
  .pt-feed-body--grid { grid-template-columns: 1fr; gap: var(--sp-3); }
  .pt-more { height: 44px; }

  /* بطاقة لكل بوّابة، لا بطاقةٌ لثلاثتها.
     -------------------------------------------------------------------------
     الأقسام الثلاثة تعرض ثلاثة أشياء مختلفة، ولذلك تُقرأ بثلاث طرق:

         الإعلان    يُقرأ مقارنةً. الزبون يمرّ على عشرين إعلاناً يوازن بين
                    أسعارها وغرفها، فيحتاج أن يرى منها ما استطاع في الشاشة
                    الواحدة — ولذلك سطرٌ أفقيّ ومصغَّرةٌ إلى جانبه.

         المشروع    يُقرأ إعجاباً. ما يبيعه المطوّر صورةٌ لحيٍّ لم يُبنَ بعد،
                    والصورة الصغيرة تُفسده — ولذلك صورةٌ عريضة تملأ عرض
                    البطاقة، وتحتها سطران: الاسم والسعر، ثم الموقع.

         الحجز      يُقرأ اشتهاءً. الليلة تُحجز بالغرفة التي تراها، فالصورة
                    أطول من صورة المشروع وأقرب إلى المربّع، وتحتها ما يقرّر
                    الحجز: العنوان، ثم السعر ومدّته، ثم المساحة والأسرّة
                    والحمّامات والضيوف، ثم الحيّ.

     A card for each portal rather than one for the three of them.

     The three sections show three different things, and so are read in three
     ways:

         the listing   is read by comparison. Someone passes over twenty of
                       them weighing prices against rooms, and needs to see as
                       many as a screen will hold — hence a level row with a
                       thumbnail beside it.

         the project   is read by attraction. What a developer sells is a
                       picture of a district not yet built, and a small picture
                       spoils it — hence a wide image filling the card's width,
                       and two lines beneath: the name with the price, then
                       the place.

         the stay      is read by desire. A night is booked on the strength of
                       the room one sees, so its image is taller than the
                       project's and nearer to square, and beneath it stands
                       what decides a booking: the title, the price and its
                       period, then area, beds, baths and guests, then the
                       district. */

  /* ---------------------------------------------------- The listing card */
  /* الإعلان: النصّ ثم الصورة، وقد كان مكدَّساً — the listing: text then image,
     where it had been stacked. */
  .pt-row-hit { grid-template-columns: minmax(0, 1fr) var(--pt-thumb); }
  .pt-row-main { padding: var(--sp-3); gap: 3px; }
  /* العنوان سطران دائماً: مقصوصٌ عند الثاني إن طال، ومحجوزٌ سطرين إن قصر.
     فبدون الحجز ترتفع بطاقةٌ عن جارتها بمقدار سطر، لا لشيء إلا أنّ عنوانها
     أقصر بكلمة — وقائمةٌ تتموّج بهذا المقدار تُقرأ مكسورة.

     The title is always two lines: cut at the second where it runs long, and
     holding two where it falls short. Without that reservation one card stands
     a line taller than its neighbour for no reason but a title shorter by a
     word — and a list rippling by that much reads as broken. */
  .pt-row-title {
    font-size: 13px;
    line-height: 1.55;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    min-height: 3.1em;
  }
  .pt-row-place { font-size: 11px; }

  /* الحقائق الأربع على سطرٍ واحد. وفراغُ الحاسوب بينها — ستة عشر بكسلاً —
     يدفع الرابعةَ إلى سطرٍ ثانٍ على شاشةٍ عرضها ثلاثمئة وعشرون، فترتفع تلك
     البطاقة وحدها عن جاراتها وتتموّج القائمة. والفراغ الأضيق يبقيها سطراً.

     The four facts on one line. The desktop's spacing between them — sixteen
     pixels — pushes the fourth onto a second line on a three-hundred-and-
     twenty-pixel screen, so that one card stands taller than its neighbours
     and the list ripples. The tighter gap keeps them on their line. */
  .pt-facts,
  .pt-meta { gap: 2px var(--sp-2); font-size: 11px; }
  .pt-facts svg,
  .pt-meta svg { width: 13px; height: 13px; }

  /* الوصف يغادر البطاقة الصغيرة: جملةٌ تعيد ما قالته الحقائق فوقها بكلماتٍ
     أطول، وهي على شاشةٍ ضيّقة سطران يمنعان إعلاناً ثالثاً من الظهور.

     The description leaves the small card: a sentence repeating in longer
     words what the facts above it already said, and on a narrow screen two
     lines that keep a third listing off it. */
  .pt-row-desc { display: none; }

  /* وبطاقة لوح الخريطة تستدير معها: هي الإعلانات نفسها في سطحٍ آخر، فلا يصحّ
     أن تكون الصورة فيها في أوّل السطر وفي القائمة في آخره — يقلب الزبونُ
     العرضَ بزرٍّ واحد فينقلب معه اتجاه كل بطاقة أمامه.

     The map panel's card turns with it: it is the same listings on another
     surface, so the image cannot sit at the start of the line here and at its
     end there — one button flips the view, and every card in front of the
     customer would flip with it. */
  .pt-hit { grid-template-columns: minmax(0, 1fr) 96px; gap: var(--sp-2); }
  .pt-hit > .pt-shot { grid-area: 1 / 2; }
  .pt-hit-body { grid-area: 1 / 1; }
  .pt-hit .pt-shot { aspect-ratio: auto; height: 100%; min-height: 84px; }
  .pt-hit-desc { display: none; }   /* كما في القائمة — as in the list */
  .pt-hit-title { min-height: 3.1em; }   /* سطران محجوزان — two lines held */

  .pt-shot--side {
    aspect-ratio: auto;
    height: 100%;
    min-height: var(--pt-thumb);
    border-radius: 0;
    order: 0;
  }
  .pt-shot--side svg { width: 26px; height: 26px; }

  /* ---------------------------------------------------- The project card */
  /* البلاطة تبقى عموديّة وتملأ العرض وحدها. الصورة بنسبة اثنين إلى واحد —
     عريضةٌ كما يُصوَّر المخطّط من فوق — والأوسمة الثلاثة على رأسها تبدأ من جهة
     القراءة، فيقع أوّلها — نوع الوحدات — تحت العين أوّلاً.

     The tile stays upright and fills the width alone. The image at two to one
     — wide, as a masterplan is photographed from above — and its three tags
     sit along its head starting from the reading side, so the first of them,
     the unit type, falls under the eye first. */
  /* عمود البلاطة يُقيَّد بـminmax(0, 1fr).
     -------------------------------------------------------------------------
     عمودها الوحيد ضمنيٌّ مقاسُه auto، وauto يتّسع لأعرض ما فيه. فمشروعٌ اسمه
     «قصور تلال الإمارات» وسعره «تبدأ من 14.5 مليون» — والسعر لا يلتفّ — يجعل
     ذلك العمود مئتين وسبعةً وستين بكسلاً في بطاقةٍ عرضها مئتان وستة وخمسون،
     فتُقصّ الزيادة بـoverflow: hidden ولا يظهر منها شيء... إلا أنّ الصورة في
     الصفّ الأعلى تتبع العمود نفسه، فتُرسم أعرض من البطاقة وتنمو معها في
     الارتفاع سبعة بكسلات. بطاقةٌ واحدة من اثنتي عشرة أطولُ من أخواتها، وسببها
     سعرٌ لا يُرى.

     The tile's column pinned to minmax(0, 1fr).

     Its single implicit column is sized auto, and auto grows to the widest
     thing in it. So a project named "Emirates Hills Palaces" priced "from 14.5
     million" — a price that will not wrap — makes that column two hundred and
     sixty-seven pixels inside a two-hundred-and-fifty-six-pixel card. The
     excess is cut away by overflow: hidden and never seen... except that the
     image in the row above follows the same column, and so is drawn wider than
     the card and grows seven pixels taller with it. One card in twelve stands
     taller than its sisters, and the cause is a price nobody can see. */
  .pt-tile { grid-template-columns: minmax(0, 1fr); }
  .pt-tile-text { min-width: 0; }

  [data-page="projects"] .pt-shot--wide { aspect-ratio: 2 / 1; }

  /* الوسم يبدأ من جهة القراءة في البوّابتين معاً: يفتح الرمليّ الصغير السطر
     حيث تفتح العين الصفحة. The tag starts from the reading side in both
     portals: the little sand pill opens the line where the eye opens the
     page. */
  [data-page="projects"] .pt-shot-tags,
  [data-page="bookings"] .pt-shot-tags { inset-inline: 6px auto; }

  /* والعلامة تنتقل إلى آخر السطر: في التخطيط العريض تسبق النصّ، وعلى الهاتف
     يبدأ السطر باسم المشروع — وهو ما يُبحث عنه — وتقف العلامة في طرفه الآخر.

     The mark moves to the end of the line: in the wide layout it precedes the
     text, while on a phone the line opens with the project's name — the thing
     being looked for — and the mark stands at its far end. */
  [data-page="projects"] .pt-tile-foot { padding: var(--sp-3); gap: var(--sp-3); }
  [data-page="projects"] .pt-tile-mark { order: 1; width: 34px; height: 34px; }
  [data-page="projects"] .pt-tile-line h3 { min-width: 0; font-size: 13.5px; }
  [data-page="projects"] .pt-tile-price b { font-size: 13px; }
  [data-page="projects"] .pt-tile-text p { font-size: 11.5px; }

  /* ------------------------------------------------------- The stay card */
  /* بطاقة الحجز تضطجع على الهاتف كما تضطجع بطاقة الإعلان فوقها.
     -------------------------------------------------------------------------
     كانت وحدها من بين بطاقات البوّابة تبقى مكدَّسة: صورةٌ بنسبة ثلاثة إلى اثنين
     تملأ العرض — مئتان وثلاثة وعشرون بكسلاً على شاشةٍ عرضها ثلاثمئة وستون —
     ثم تحتها عنوانٌ وسعرٌ وأربع حقائق وعنوان مكان. فيصير طولها ثلاثمئة وسبعة
     وستين بكسلاً: أطول من بطاقة المشروع بمئةٍ وثلاثين، وستّة أضعاف سطر
     الإعلان. ولا يظهر منها في الشاشة الواحدة إلا واحدةٌ ونصف، فيصير تصفّح
     عشرين وحدة تمريراً طويلاً لا مسحاً بالعين.

     والصورة هي الثلثان من ذلك الطول، وليست تستحقّه في قائمةٍ تُمسح: ما يفصل
     شاليهاً عن شقّة هو السعر والمدّة وعدد الغرف، والصورة تُدرس في نافذة
     التفاصيل لا في القائمة. فتصير شريطاً جانبياً بعرض ‎--pt-thumb‎ — الرمز نفسه
     الذي تقف عليه مصغَّرة الإعلان — ويقع النصّ إلى جانبه فيحكم هو الطول لا هي.
     والنتيجة نحو مئةٍ وأربعين بكسلاً: أربع بطاقات في الشاشة بدل واحدةٍ ونصف،
     وبنيةٌ واحدة للقائمتين في البوّابة بدل بنيتين.

     The stay card lies down on a phone as the listing card above it does.

     It was the only card in the portal still stacked: a three-to-two image
     filling the width — two hundred and twenty-three pixels on a three-hundred-
     and-sixty-pixel screen — and beneath it a title, a price, four facts and a
     place. Three hundred and sixty-seven pixels tall: a hundred and thirty more
     than a project card and six times a listing row. One and a half of them fit
     on a screen, which turns browsing twenty units into a long scroll rather
     than a glance.

     Two thirds of that height is the image, and it does not earn it in a list
     that is scanned: what separates a chalet from a flat is the price, the
     period and the number of rooms, and the photograph is studied in the detail
     sheet rather than in the list. So it becomes a side strip of --pt-thumb —
     the same token the listing's thumbnail stands on — with the text beside it,
     and the text rules the height instead. The result is about a hundred and
     forty pixels: four cards to a screen instead of one and a half, and one
     structure for both of the portal's lists instead of two. */
  [data-page="bookings"] .pt-tile {
    grid-template-columns: minmax(0, 1fr) var(--pt-thumb);
    grid-template-rows: auto;
  }
  [data-page="bookings"] .pt-tile > .pt-shot {
    grid-area: 1 / 2;
    aspect-ratio: auto;
    height: 100%;
    min-height: var(--pt-thumb);
  }
  [data-page="bookings"] .pt-tile-body {
    grid-area: 1 / 1;
    padding: var(--sp-3);
    gap: 3px;
  }
  [data-page="bookings"] .pt-tile-title { font-size: 13px; line-height: 1.55; min-height: 3.1em; }
  [data-page="bookings"] .pt-tile-body .pt-price b { font-size: 15px; }
  [data-page="bookings"] .pt-tile-place { font-size: 11px; }

  /* ------------------------------------------------------------ The tags */
  /* في المشاريع ثلاثة أوسمة كما هي، وفي الحجوزات واحد: أوّل وسم الحجز نوعُ
     المسكن وهو مكتوبٌ في العنوان تحته حرفاً بحرف، فلا يقول الوسم شيئاً جديداً.
     وحين لا يكون الحجز فوريّاً لم يبق إلا وسمٌ واحد فيصير هو الأخير ويظهر.

     Three tags in projects as they are, and one in stays: a stay's first tag
     is the kind of dwelling, written word for word in the title beneath it, so
     the tag says nothing new. Where a stay is not instant only one tag
     remains, which is then the last, and shows. */
  [data-page="bookings"] .pt-shot-tags span:not(:last-child) { display: none; }
  .pt-shot-tags span {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    font-size: 9.5px;
  }

  /* ------------------------------------------------------------ The map */
  /* الدبابيس تُقرَّب وتُبعَد باللمس، وضوابط التقريب تبقى لمن لا يلمس — تنزل
     تحت الشريط فلا تختفي خلفه، والركن السفلي يُترك لما يخصّه.

     Pins are zoomed by the fingers, and the zoom controls stay for whoever
     does not touch — pushed below the rail so they are not hidden behind it,
     while the bottom corner is left to what belongs there. */
  .pt-canvas .mapboxgl-ctrl-top-left, .pt-canvas .mapboxgl-ctrl-top-right { margin-block-start: 88px; }
  .pt-canvas .mapboxgl-ctrl-bottom-left, .pt-canvas .mapboxgl-ctrl-bottom-right { margin-block-end: 0; }
  .pt-canvas .pt-note { display: none; }

  /* وضوابط التقريب تصغر لا تكبر.
     -------------------------------------------------------------------------
     كانت أربعين في أربعين، فصار العمود الثلاثيّ مئةً وعشرين بكسلاً من ارتفاع
     الخريطة واقفةً في وسط حافّتها — وهي على شاشةٍ ارتفاعها ستّمئة قريبٌ من
     الخُمس. والحجّة التي كبّرتها — أنّ ما يُضغط يكبر — لا تصحّ عليها وحدها:
     التقريب على الهاتف إصبعان لا زرّان، وهذه الأزرار إنما تبقى لمن لا يقرص.
     فتُبقى ولا تُحذف، وتُعطى اثنين وثلاثين: تُبلَغ ولا تحتلّ.

     The zoom controls shrink rather than grow.

     They were forty by forty, which made the stack of three a hundred and
     twenty pixels of map height standing in the middle of its edge — close to a
     fifth of a six-hundred-pixel screen. The argument that enlarged them, that
     what is pressed grows, does not hold for these in particular: zooming on a
     phone is two fingers rather than two buttons, and these remain only for
     whoever cannot pinch. So they are kept rather than removed, and given
     thirty-two: reached without occupying. */
  .pt-canvas .gm-ctrl { margin: 8px !important; }
  .pt-canvas .gm-btn { width: 32px; height: 32px; }
  .pt-canvas .gm-btn svg { width: 15px; height: 15px; }

  /* نسبة الخريطة إلى مصادرها حقٌّ لها، فتُترك لها حافّةُ الشاشة وحدها ويُرفع
     الرصيف فوقها. The map's attribution is its sources' due, so the screen's
     edge is left to it alone and the dock is raised above it. */
  .pt-canvas .mapboxgl-ctrl-attrib { font-size: 9.5px; }

  /* الرصيف ينزل إلى الإبهام. وكلمته تبقى للقارئ الآلي كما في الشريط العلوي.
     The dock comes down to the thumb, its word kept for the screen reader as
     in the top bar. */
  /* والرصيف يعلو نسبةَ الخريطة ولا يمسّها: هي حقُّ مصادرها، وما يغطّيها
     يأخذ حقّاً ليس له. The dock rises clear of the attribution rather than
     touching it: that line is its sources due, and what covers it takes a
     right that is not its own. */
  .pt-dock {
    inset-block-start: auto;
    inset-block-end: 22px;
    gap: 2px;
    padding: 3px;
    max-width: calc(100% - 20px);
  }
  .pt-dock-btn {
    position: relative;
    width: 36px;
    height: 36px;
    padding-inline: 0;
    justify-content: center;
  }
  .pt-dock-btn > span {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }

  /* وحين تُفتح القائمة تأخذ أسفل الشاشة، فيغيب الرصيف: زرّه وزرُّ الإغلاق في
     رأس اللوح شيءٌ واحد، وزرّان متراكبان على شبرٍ واحد أسوأ من واحد.

     When the list opens it takes the bottom of the screen and the dock goes:
     its button and the panel's close button are one thing, and two buttons
     stacked on one handspan are worse than one. */
  .pt-panel-results:not([hidden]) ~ .pt-dock { display: none; }

  /* --------------------------------------------------- The card on the map */
  /* البطاقة كانت تبتلع الخريطة التي فُتحت منها.
     -------------------------------------------------------------------------
     أربعمئةٌ واثنا عشر عرضاً، ومئةٌ وستّةَ عشرَ منها صورةٌ لا صورة فيها — وجهٌ
     متدرّج عليه رمزُ بيت — فوق عنوانٍ وسعرٍ وأربعِ حقائق ووصفٍ في سطرين. فكان
     ما تفتحه نقرةُ دبوسٍ لوحاً يقارب ثلاثمئة بكسل يقف على الخريطة كلّها، ثم
     يدفع نفسه بالتمرير التلقائي حتى يدخل تحت شريط التصنيفات.

     وعلاجه ليس تصغير الأرقام واحداً واحداً، بل ردُّ البطاقة إلى الشكل الذي
     قرّرته هذه الورقة لكل بطاقةٍ فيها على الهاتف: عمودان — النصّ ثم مصغَّرةٌ
     إلى جانبه — كما في ‎.pt-hit‎ و‎.pt-row-hit‎ سواء. ويغادر الوصف كما غادر
     منهما: جملةٌ تعيد بكلماتٍ أطول ما قالته الحقائق فوقها.

     فتصير البطاقة نصف ارتفاعها، ولم تفقد كلمةً واحدة ممّا يُقرَّر به فتحُ
     الإعلان — ولا فقدت شبهها بأختها في لوح النتائج، وهي الإعلان نفسه.

     The card was swallowing the map it opened from.

     Four hundred and twelve wide, a hundred and sixteen of which an image with
     no image in it — a gradient face carrying a house glyph — above a title, a
     price, four facts and two lines of description. So what a tap on a pin
     opened was a panel of nearly three hundred pixels standing on the whole
     map, which then auto-panned itself under the chip rail.

     The cure is not shrinking the numbers one at a time but returning the card
     to the shape this sheet has settled on for every card on a phone: two
     columns — text, then a thumbnail beside it — exactly as .pt-hit and
     .pt-row-hit. And the description leaves as it left those two: a sentence
     repeating in longer words what the facts above it already said.

     The card comes to half its height without losing one word of what decides
     opening a listing — nor its likeness to its sister in the results panel,
     which is that same listing. */
  .portal-theme .gm-card-popup .mapboxgl-popup-content {
    width: 296px !important;
    max-width: calc(100vw - 72px);
  }
  .portal-theme .gm-card-popup .mapboxgl-popup-content { max-width: calc(100vw - 72px); }
  .portal-theme .gm-card-popup .mapboxgl-popup-close-button { margin: 5px; width: 22px; height: 22px; font-size: 14px; }

  .gm-card { grid-template-columns: minmax(0, 1fr) 86px; min-height: 0; }
  .gm-card-body { padding: 10px 11px; gap: 4px; }
  .gm-card-title { font-size: 12.5px; line-height: 1.5; }
  .gm-card-price { font-size: 15px; }
  .gm-card-price small { font-size: 10px; }
  .gm-card-facts { gap: 3px 10px; font-size: 10.5px; }
  .gm-card-fact svg { width: 12px; height: 12px; }
  .gm-card-desc { display: none; }
  .gm-card-place { font-size: 10.5px; }
  .gm-card-shot svg { width: 24px; height: 24px; }

  /* --------------------------------------------------- The results sheet */
  /* ورقةٌ تصعد من أسفل الشاشة وتقف فوق شريط الأقسام. ومقبضٌ في رأسها يقول ما
     تقوله الورقة في كل تطبيق: أنّ ما تحت الإصبع لوحٌ لا صفحة.

     A sheet rising from the bottom of the screen, standing above the section
     bar. A handle at its head says what a sheet says in every application:
     that what is under the finger is a panel and not a page. */
  .pt-panel-results {
    inset-block: auto 0;
    inset-inline: 0;
    width: 100%;
    max-height: 62dvh;
    border-inline: 0;
    border-block-end: 0;
    border-start-start-radius: var(--r-lg);
    border-start-end-radius: var(--r-lg);
    box-shadow: 0 -10px 32px rgb(88 36 21 / 0.22);
  }
  .pt-panel-head {
    position: relative;
    padding-block: 15px 10px;
  }
  .pt-panel-head::before {
    content: "";
    position: absolute;
    inset-block-start: 6px;
    inset-inline: 0;
    margin-inline: auto;
    width: 38px;
    height: 4px;
    border-radius: var(--r-pill);
    background: var(--line-strong);
  }
  .pt-panel-body { padding: var(--sp-2) var(--sp-3) var(--sp-3); }

  /* ---------------------------------------------------------- The sheets */
  /* النافذة تترك وسط الشاشة وتصعد من أسفلها: أقرب إلى الإبهام، وأصدق عن نفسها
     — فما يُفتح بالضغط على شريطٍ سفلي يُتوقَّع أن يأتي من حيث ضُغِط.

     The sheet leaves the centre of the screen and rises from its bottom:
     nearer the thumb, and more honest about itself — what is opened from a
     bar below is expected to come from where it was pressed. */
  .pt-sheet { padding: 0; place-items: end stretch; }
  .pt-sheet-panel,
  .pt-sheet-panel.is-wide {
    width: 100%;
    max-height: 88dvh;
    border-inline: 0;
    border-block-end: 0;
    border-start-start-radius: var(--r-lg);
    border-start-end-radius: var(--r-lg);
    animation: ptSlideUp 0.24s var(--ease);
  }
  .pt-sheet-head { position: relative; padding-block: 16px var(--sp-3); }
  .pt-sheet-head::before {
    content: "";
    position: absolute;
    inset-block-start: 6px;
    inset-inline: 0;
    margin-inline: auto;
    width: 38px;
    height: 4px;
    border-radius: var(--r-pill);
    background: var(--line-strong);
  }
  .pt-sheet-body { padding: var(--sp-4) var(--sp-3) calc(var(--sp-4) + var(--pt-safe-b)); }

  /* ----------------------------------------------------------- The forms */
  /* ستة عشر بكسلاً هي الحدّ الذي لا يكبّر iOS الصفحة عنده. وما دونه يقفز
     التخطيط كلّه عند أول لمسة على حقل، ثم لا يعود.

     Sixteen pixels is the threshold at which iOS does not zoom. Below it the
     whole layout jumps at the first touch on a field, and never comes back. */
  .pt-field input,
  .pt-field select,
  .pt-field textarea,
  .pt-prefixed,
  .pt-prefixed input {
    font-size: 16px;
    min-height: 46px;
  }
  .pt-field textarea { min-height: 104px; }
  .pt-prefixed input { min-height: 44px; }
  .pt-field > span,
  .pt-picks legend,
  .pt-range legend { font-size: 12.5px; }

  /* الفعل الرئيسي يملأ العرض ويجلس فوق ما يُلغيه، فيقع الإبهام عليه لا على
     إعادة الضبط. The main action fills the width and sits above what undoes
     it, so the thumb lands on it rather than on "reset". */
  .pt-form-foot {
    flex-direction: column-reverse;
    align-items: stretch;
    gap: var(--sp-2);
  }
  .pt-submit { width: 100%; height: 48px; font-size: 14px; }
  .pt-reset { padding-block: var(--sp-2); text-align: center; }
  .pt-hint { text-align: center; }
  .pt-pick-grid { grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); }
  .pt-check { font-size: 12.5px; }
  .pt-check input { width: 20px; height: 20px; }

  /* التقويم يُنقر بالإصبع، فيكبر يومه — the calendar is tapped by a finger,
     so its day grows. */
  .pt-day { height: 40px; font-size: 13px; }
  .pt-cal-grid th { font-size: 11px; }

  /* ---------------------------------------------------------- The drawer */
  .js .pt-drawer { width: 100%; }
  .pt-drawer-head {
    padding-block: calc(var(--sp-3) + var(--pt-safe-t)) var(--sp-3);
    padding-inline: var(--sp-3);
  }
  .pt-drawer-body { padding: var(--sp-3); }
  .pt-drawer-foot {
    padding-block: var(--sp-3) calc(var(--sp-3) + var(--pt-safe-b));
    padding-inline: var(--sp-3);
  }
  .pt-icon-btn,
  .pt-chip-btn { min-width: 40px; height: 40px; }
  .pt-row { padding-block: 13px; }
  .pt-duo-item { padding-block: 13px; }
  .pt-cta { height: 48px; }

  /* ----------------------------------------------------- The detail sheet */
  /* التفصيل يملأ الشاشة. النافذة التي تترك شريطاً من الصفحة تحتها تصلح لفأرة
     تضغط خارجها لتغلقها، ولا تصلح لإبهامٍ لا يجد ذلك الشريط.

     The detail fills the screen. A sheet that leaves a strip of the page under
     it suits a mouse that clicks outside to close, and does not suit a thumb
     that cannot find the strip. */
  .pt-modal-panel {
    width: 100%;
    height: 100dvh;
    border: 0;
    border-radius: 0;
  }
  .pt-modal-head {
    padding-block: calc(10px + var(--pt-safe-t)) 10px;
    padding-inline: var(--sp-3);
  }
  .pt-modal-head h2 { font-size: 13px; }

  /* الحشو السفلي يترك مكان شريط الاتصال المثبَّت — the bottom padding leaves
     room for the pinned contact bar. */
  .pt-modal-body {
    padding: var(--sp-3);
    padding-block-end: calc(76px + var(--pt-safe-b));
    gap: var(--sp-3);
  }

  .pt-gallery { grid-template-columns: 1fr; gap: var(--sp-2); }

  /* الصور الباقية شريطٌ يُمرَّر لا شبكةٌ من مربّعاتٍ صغيرة — the remaining
     photos are a strip that scrolls rather than a grid of small squares. */
  .pt-gallery-rest {
    display: flex;
    gap: 6px;
    overflow-x: auto;
    scrollbar-width: none;
    padding-block: 1px;
  }
  .pt-gallery-rest::-webkit-scrollbar { display: none; }
  .pt-gallery-rest > * { flex: none; width: 96px; }

  /* والشريط لا عمود له يُملأ، فتعود النسبة إلى الخانة — the strip has no
     column to fill, so the ratio goes back to the slot. */
  .pt-gallery-rest > .pt-shot { aspect-ratio: 3 / 2; height: auto; }

  /* أفعال الصورة رموزٌ بلا كلمات، فيبقى ركن الصورة الآخر للعدد.
     The image's actions become glyphs without words, which leaves the other
     corner of the image for the count. */
  .pt-gallery-tools { inset-block-start: 6px; inset-inline-start: 6px; gap: 5px; }
  .pt-ghost-btn { height: 34px; }
  .pt-gallery-tools .pt-ghost-btn {
    position: relative;
    width: 34px;
    padding-inline: 0;
    justify-content: center;
  }
  .pt-gallery-tools .pt-ghost-btn > span {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }
  .pt-gallery-count {
    inset-block: 6px auto;
    inset-inline: auto 6px;
  }

  .pt-detail-split { grid-template-columns: 1fr; gap: var(--sp-3); }
  .pt-detail-price b { font-size: 20px; }
  .pt-detail-desc { font-size: 13px; }
  .pt-facts-grid,
  .pt-feature-grid { grid-template-columns: 1fr 1fr; gap: var(--sp-2); padding: var(--sp-3) var(--sp-2); }
  /* ولا يُكدَّس العنوان فوق قيمته على الهاتف: كان ذلك مقبولاً حين كانا طرفين
     بلا رابط، أما وقد صارت بينهما نقطتان فتكديسهما يترك نقطتين معلّقتين في
     آخر سطرٍ وحدهما. والسطر يلتفّ إن ضاق، وهو التفافٌ يُقرأ.

     The label is not stacked over its value on a telephone: that was tolerable
     while they were two ends with nothing joining them, but now that a colon
     stands between them, stacking leaves a colon hanging alone at the end of a
     line. The line wraps if it must, and a wrap reads. */
  .pt-facts-grid > div { flex-wrap: wrap; gap: 5px; }
  .pt-facts-grid dd { font-size: 12.5px; }

  /* والمقاطع عمودٌ واحد: ‎82vw‎ كانت تُخرج المقطع خارج الشاشة بعرض الحشو،
     والشبكة تعطيه ما بقي من العرض لا رقماً مقيساً على الشاشة.
     The clips in a single column: 82vw pushed a clip off the screen by the
     width of the padding, and the grid gives it what is left of the width
     rather than a figure measured against the screen. */
  .pt-videos { grid-template-columns: 1fr; }
  .pt-tabbed-tab span { padding: 12px 6px; font-size: 11.5px; }
  .pt-tabbed-panel > div { padding-block: 9px; }

  /* شريط الاتصال يُثبَّت في أسفل الشاشة — the contact bar is pinned.
     -------------------------------------------------------------------------
     الاتصال بالمعلن هو ما جاء الزبون لأجله، وهو في التخطيط العريض عمودٌ جانبي
     يُقرأ بلمحة. وحين ينهار العمودان إلى عمودٍ واحد يهبط ذلك العمود إلى ما بعد
     التعليقات — فيصير الفعلُ الوحيد الذي تعنيه الصفحة آخرَ ما يُرى فيها.
     ولذلك يُنتزع الزرّان من مكانهما ويُثبَّتان: يبقيان تحت الإبهام مهما بلغ
     التمرير.

     Contacting the advertiser is what the customer came for, and in the wide
     layout it is a side column read at a glance. When the two columns collapse
     into one, that column falls below the comments — and the one action the
     page is about becomes the last thing seen on it. So the two buttons are
     lifted out of their place and pinned: under the thumb however far the page
     has scrolled. */
  .pt-modal .pt-publisher-acts {
    position: fixed;
    inset-block-end: 0;
    inset-inline: 0;
    z-index: 5;
    justify-self: stretch;   /* كما في شريط الأقسام — as in the section bar */
    width: auto;
    gap: var(--sp-2);
    padding: var(--sp-2) var(--sp-3) calc(var(--sp-2) + var(--pt-safe-b));
    border-block-start: 1px solid var(--line);
    background: var(--surface);
    box-shadow: 0 -4px 20px rgb(88 36 21 / 0.18);
  }
  .pt-modal .pt-publisher-acts .pt-ghost-btn,
  .pt-modal .pt-publisher-acts .pt-submit {
    width: auto;
    height: 46px;
    font-size: 13.5px;
  }

  .pt-publisher { padding: var(--sp-3); }
  .pt-comment-form { padding: var(--sp-3); }
  .pt-thread--replies { padding-inline-start: var(--sp-3); }
}

/* الورقة تصعد. يُعرَّف خارج الاستعلام فتراه قاعدة تقليل الحركة وتوقفه.
   The sheet rises. Defined outside the query so the reduced-motion rule can
   see it and stop it. */
@keyframes ptSlideUp {
  from { transform: translateY(100%); }
  to { transform: none; }
}

/* الهاتف مضطجعاً: الارتفاع هو النادر لا العرض، فتنكمش القشرة طولاً وحدها.
   The phone on its side: height is what is scarce rather than width, so the
   shell shrinks in that direction alone. */
@media (max-width: 900px) and (max-height: 480px) {
  .portal-theme { --pt-nav: 46px; }
  .pt-top { min-height: 44px; }
  .pt-brand-logo { height: 24px; }
  .pt-action, .pt-avatar { width: 34px; height: 34px; }
  .pt-tab { gap: 1px; font-size: 9.5px; }
  .pt-tab svg { width: 17px; height: 17px; }
  .pt-head { padding-block: var(--sp-4) var(--sp-3); }
  .pt-sheet-panel, .pt-sheet-panel.is-wide { max-height: 94dvh; }
  .pt-panel-results { max-height: 72dvh; }

  /* ومضطجعاً يشتدّ ضيق الارتفاع، فينكمش ما يطفو على الخريطة مرّةً ثانية:
     الشريط والرقاقة والرصيف وضوابط التقريب — والبطاقة تُقصّ إلى نصف الشاشة
     فلا تعلو الخريطةَ كلَّها.

     On its side the shortage of height sharpens, so what floats on the map
     shrinks a second time: the rail, the chip, the dock and the zoom controls —
     and the card is capped at half the screen so it does not stand over the
     whole map. */
  .pt[data-mode="map"] .pt-rail { inset-block-start: 6px; gap: 5px; }
  .pt[data-mode="map"] .pt-rail-search { height: 34px; }
  .pt[data-mode="map"] .pt-rail-btn,
  .pt[data-mode="map"] .pt-rail-clear { width: 34px; height: 34px; }
  .pt[data-mode="map"] .pt-chip { height: 28px; font-size: 11px; }
  .pt-canvas .mapboxgl-ctrl-top-left, .pt-canvas .mapboxgl-ctrl-top-right { margin-block-start: 78px; }
  .pt-canvas .gm-btn { width: 28px; height: 28px; }
  .pt-dock { inset-block-end: 20px; }
  .pt-dock-btn { width: 32px; height: 32px; }
  .portal-theme .gm-card-popup .mapboxgl-popup-content { max-height: 46dvh; overflow: hidden; }
}

/* وقاعدة تقليل الحركة تُعاد بعد كل ما سبق، وإلا أعادت قاعدةٌ متأخّرة تشغيل ما
   أوقفته. The reduced-motion rule is restated after all of the above, or a
   later rule switches back on what it turned off. */
@media (prefers-reduced-motion: reduce) {
  .pt-sheet-panel,
  .pt-modal-panel,
  .pt-drawer,
  .pt-scrim { animation: none; transition: none; }
}

/* الشاشة الأضيق من ثلاثمئة: شاشة الهاتف المطويّ وأصغر ما يُصنع.
   -------------------------------------------------------------------------
   لا تحتاج شكلاً ثالثاً، إنما تحتاج أن تنكمش الحقائق الأربع — مساحةً وغرفاً
   وحمّاماتٍ وصالات — حتى تبقى على سطر. فإن التفّت الرابعةُ منها ارتفعت البطاقة
   سطراً عن جارتها التي لا رابعةَ لها، وتموّجت القائمة من جديد.

   Narrower than three hundred: the folded phone's cover screen and
   the smallest thing made. It needs no third shape, only that the four facts —
   area, rooms, baths and living rooms — shrink enough to stay on their line.
   Let the fourth of them wrap and that card stands a line above the neighbour
   which has no fourth, and the list ripples again. */
@media (max-width: 300px) {
  .portal-theme { --pt-thumb: 80px; }
  .pt-action, .pt-avatar { width: 34px; height: 34px; }
  .pt[data-mode="map"] .pt-chip { height: 28px; padding-inline: 8px; font-size: 11px; }
  .pt-dock-btn { width: 34px; height: 34px; }
  .gm-card { grid-template-columns: minmax(0, 1fr) 72px; }
  .gm-card-body { padding: 9px 10px; }
  .pt-facts,
  .pt-meta { gap: 2px 5px; font-size: 10px; }
  .pt-facts svg,
  .pt-meta svg { width: 12px; height: 12px; }
  .pt-row-main { padding: var(--sp-2); }
  .pt-row-title { font-size: 12.5px; }
  .pt-price b { font-size: 15px; }
}

/* =========================================================================
   التصفية وهي تجري — a filter in flight
   -------------------------------------------------------------------------
   بين ضغط الرقاقة ووصول النتائج رحلةٌ إلى الخادم. وبلا علامةٍ عليها تبدو
   الضغطة وكأنها لم تقع، فتُضغط ثانيةً وثالثة. فتُخفَّف القائمة القائمة قليلاً
   — تقول «هذه لم تعد نتيجتك» — ويجري خيطٌ ذهبيّ تحت الشريط العلوي يقول إنّ
   الجديدة في الطريق. ولا يُخفى شيء ولا يُستبدل بهيكلٍ رماديّ: ما كان مقروءاً
   يبقى مقروءاً حتى يحلّ محلّه ما هو أصدق منه.

   Between pressing a chip and the results arriving there is a journey to the
   server. With no sign of it the press looks as though it never landed, and is
   made a second and a third time. So the standing list dims a little — saying
   "this is no longer your result" — and a gold thread runs beneath the top bar
   saying the new one is on its way. Nothing is hidden and nothing is replaced
   by a grey skeleton: what was readable stays readable until something truer
   takes its place.
   ========================================================================= */
.pt-main[aria-busy="true"] .pt-feed,
.pt-main[aria-busy="true"] .pt-panel-body,
.pt-main[aria-busy="true"] .pt-panel {
  opacity: 0.5;
  transition: opacity var(--dur) var(--ease);
  pointer-events: none;
}

.pt-main[aria-busy="true"]::before {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: 1300;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--gold), transparent);
  background-size: 40% 100%;
  background-repeat: no-repeat;
  animation: ptThread 0.9s linear infinite;
}

/* الصفحة تتمرّر داخل main، فالخيط يحتاج مرجعاً ثابتاً لا يتمرّر معها.
   The page scrolls inside main, so the thread needs a fixed reference that
   does not scroll away with it. */
.pt[data-mode="page"] .pt-main[aria-busy="true"]::before { position: fixed; }

@keyframes ptThread {
  from { background-position: -40% 0; }
  to { background-position: 140% 0; }
}

@media (prefers-reduced-motion: reduce) {
  .pt-main[aria-busy="true"]::before { animation: none; background-size: 100% 100%; opacity: 0.5; }
  .pt-main[aria-busy="true"] .pt-feed,
  .pt-main[aria-busy="true"] .pt-panel-body,
  .pt-main[aria-busy="true"] .pt-panel { transition: none; }
}

/* =========================================================================
   تذييل الموقع — the site's footer
   -------------------------------------------------------------------------
   آخر ما في الصفحة، ويعيش داخل ‎.pt-main‎ لا تحته: الصفحة تتمرّر في المجرى لا في
   المستند، فتذييلٌ خارجه تذييلٌ لا يبلغه أحد.

   The last thing on the page, living inside .pt-main rather than beneath it:
   the page scrolls in the stream and not in the document, so a footer outside
   it is a footer nobody reaches.

   وهو سطحٌ صريح لا امتدادٌ للأرض: حدٌّ علويّ ووجهٌ ‎--surface‎ يفصلانه عمّا فوقه،
   فيُقرأ نهايةً لا فراغاً بعد آخر بطاقة.

   A stated surface rather than a continuation of the ground: a top border and a
   --surface face separate it from what is above, so it reads as an ending
   rather than as emptiness after the last card.
   ========================================================================= */
.pt-foot {
  /* عمودُ التذييل ثابتٌ يُعلَن هنا، ولا يُقرأ من الصفحة التي فوقه.

     كان يُقرأ منها بـ‎:has()‎ فيتبعُ عرضَها: 1280 في العرض العادي، و1180 في
     الأنشطة، و920 في الوثيقة، و760 في الصفحات الضيّقة. والقصد كان محاذاةَ
     حافّة؛ والحاصل كان تذييلاً يتبدّل بين صفحةٍ وأخرى، بل داخل الصفحة الواحدة:
     رسالةُ ‎session('status')‎ تُحقَن في القشرة داخل ‎.pt-page--narrow‎، فأيُّ
     صفحةٍ حملت رسالةً — ولو كانت صفحةَ تصفّحٍ عرضُها 1280 — انكمش تذييلها إلى
     760 حتى يُعاد تحميلها. تخطيطٌ يتعلّق بما لا علاقة له به.

     والتذييل قشرةُ موقعٍ لا ذيلُ صفحة، شأنه شأن الشريط العلوي: عرضه صفةٌ من
     صفاته هو. فيُعلَن على ‎.pt-foot‎ نفسه لا على سلفٍ من أسلافه — والإعلان على
     العنصر يغلب أي توريثٍ من فوقه — فلا يبقى في المحيط ما يُغيّره.

     والقيمة 1280 لا 1180: هي عمود ‎.pt-browse‎، وهو سطح البوابة الأول —
     الإعلانات والمشاريع والحجوزات — فتنطبق الحافّتان هناك حيث يقف أكثر الناس.
     وما دونه من أعمدةٍ أضيق يقف تحته تذييلٌ أعرض، وذلك شأن تذييل الموقع في كل
     مكان لا عطبٌ فيه.

     The footer's column is a constant declared here, never read off the page
     above it.

     It used to be read with :has(), so it followed the page's width: 1280 on
     the browse view, 1180 on activity, 920 on a document, 760 on the narrow
     pages. The intent was edge alignment; the result was a footer that changed
     between pages and even within one page: the session('status') message is
     injected by the shell inside .pt-page--narrow, so any page carrying a
     message — a 1280-wide browse page included — had its footer shrink to 760
     until the next load. A layout depending on what has nothing to do with it.

     The footer is site chrome rather than a page's tail, as the top bar is: its
     width is a property of itself. So it is declared on .pt-foot rather than on
     any ancestor — a declaration on the element beats any inheritance from
     above it — and nothing is left in the surroundings that can change it.

     1280 rather than 1180: that is .pt-browse's column, and the browse view is
     the portal's first surface — listings, projects, bookings — so the edges
     meet where most people stand. Under the narrower columns a wider footer
     stands, which is what a site footer does everywhere rather than a fault in
     it. */
  --pt-foot-w: 1280px;
  --pt-foot-pad: var(--sp-4);

  margin-block-start: var(--sp-7);
  border-block-start: 1px solid var(--line);
  background: var(--surface);
  color: var(--fg-2);
  font-size: 13px;
}

.pt-foot-inner {
  display: grid;
  grid-template-columns: minmax(260px, 1fr) minmax(0, 1.6fr);
  gap: var(--sp-6);
  max-width: var(--pt-foot-w);
  margin-inline: auto;
  padding: var(--sp-6) var(--pt-foot-pad);
}

.pt-foot-brand { display: grid; gap: var(--sp-3); align-content: start; }

.pt-foot-mark { justify-self: start; }

/* الشعار هنا كما هو في الشريط العلوي: على الشفافية، وظلٌّ دافئ وحده يفصله.
   The logo here as it is in the top bar: on transparency, a warm shadow alone
   separating it. */
.pt-foot-mark img {
  display: block;
  width: auto;
  height: 32px;
  object-fit: contain;
  filter: drop-shadow(0 1px 1px rgb(58 38 12 / 0.3))
          drop-shadow(0 3px 6px rgb(58 38 12 / 0.22));
}
.portal-theme[data-theme="dark"] .pt-foot-mark img {
  filter: drop-shadow(0 1px 2px rgb(0 0 0 / 0.55))
          drop-shadow(0 0 10px rgb(226 178 74 / 0.28));
}

.pt-foot-about { max-width: 46ch; line-height: 1.75; color: var(--fg-2); }

/* التصريح: أصغر من الوصف ولكن لا باهت. ما يقول «المنصة ليست طرفاً في العقد»
   ليس حاشيةً تُقرأ بالمجهر.

   The declaration: smaller than the blurb but never faint. What says "the
   platform is not a party to the contract" is not a footnote read under glass. */
.pt-foot-note {
  display: flex;
  gap: 7px;
  max-width: 52ch;
  padding: var(--sp-3);
  border: 1px dashed var(--line);
  border-radius: var(--r-md);
  background: var(--surface-1);
  font-size: 12px;
  line-height: 1.7;
  color: var(--fg-2);
}
.pt-foot-note svg { flex: none; margin-block-start: 3px; color: var(--gold); }

.pt-foot-nav {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--sp-5);
  align-content: start;
}

.pt-foot-title {
  display: block;
  padding-block-end: var(--sp-3);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: var(--tr-md);
  text-transform: uppercase;
  color: var(--fg-3);
}

.pt-foot-group ul { display: grid; gap: 2px; }

.pt-foot-group a {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  margin-inline-start: -8px;
  border-radius: var(--r-md);
  color: var(--fg-2);
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}
.pt-foot-group a svg { flex: none; color: var(--fg-4); transition: color var(--dur) var(--ease); }
.pt-foot-group a:hover { background: var(--surface-1); color: var(--fg-1); }
.pt-foot-group a:hover svg { color: var(--gold); }

/* الشريط السفلي: حقوقٌ وإصدارٌ ولغة. خطٌّ منقّط يفصله عن الروابط فوقه، لأنه
   ليس مجموعةً رابعة بل ذيلُ الثلاث.

   The bottom bar: rights, build, language. A dotted rule separates it from the
   links above, because it is not a fourth group but the tail of the three. */
.pt-foot-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  max-width: var(--pt-foot-w);
  margin-inline: auto;
  padding: var(--sp-3) var(--pt-foot-pad) var(--sp-5);
  border-block-start: 1px dashed var(--line);
  font-size: 12px;
  color: var(--fg-3);
}

.pt-foot-side { display: flex; align-items: center; gap: var(--sp-4); }

.pt-foot-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--fg-2);
  transition: color var(--dur) var(--ease);
}
.pt-foot-link:hover { color: var(--gold); }

.pt-foot-build { color: var(--fg-4); }

@media (max-width: 900px) {
  .pt-foot-inner { grid-template-columns: 1fr; gap: var(--sp-5); }
}

/* والحشو وحده يتغيّر بالعرض، والعرض الأقصى لا أثر له تحت 1280 أصلاً.

   وعند 760 تنزل الصفحات إلى ‎--sp-3‎ — ‎.pt-browse‎ و‎.pt-page‎ كلتاهما — فينزل
   التذييل معها عند العتبة نفسها لا عند غيرها. ولو بقي عند 560 لانفرجت الحافّتان
   بأربعة بكسلات في الشريط الواقع بين العتبتين، وهو المقاس الذي تُفتح عليه أكثر
   الأجهزة اللوحية.

   The padding alone changes with the width, and the maximum width has no effect
   below 1280 to begin with.

   At 760 the pages drop to --sp-3 — .pt-browse and .pt-page both — so the footer
   drops with them at that same threshold rather than another. Had it stayed at
   560 the edges would have parted by four pixels across the band between the two
   thresholds, which is the size most tablets open at. */
@media (max-width: 760px) {
  .pt-foot { --pt-foot-pad: var(--sp-3); }
}

@media (max-width: 560px) {
  .pt-foot-inner { padding-block: var(--sp-5); }
  .pt-foot-nav { grid-template-columns: repeat(auto-fit, minmax(128px, 1fr)); gap: var(--sp-4); }
}

/* =========================================================================
   الوثيقتان النظاميتان — the two statutory documents
   -------------------------------------------------------------------------
   صفحةٌ تُقرأ لا صفحةٌ تُتصفَّح. وهذا يقرّر كل ما يلي: عرضُ سطرٍ محدود بالمحارف
   لا بالبكسل، وارتفاعُ سطرٍ مرتفع، وفهرسٌ يقفز، وعناوينُ مرقّمة يُشار إليها
   برقمها في مراسلة.

   A page to be read rather than browsed. That decides everything below: a line
   length measured in characters rather than pixels, a generous line height, a
   contents list that jumps, and numbered headings that can be cited by number
   in correspondence.
   ========================================================================= */
.pt-page--doc {
  max-width: 920px;
  padding-block-start: var(--sp-6);
}

/* والوثيقة نفسها لُصْقةٌ أيضاً: زرّ «إلى أعلى» يقصدها. وحاشيةُ التمرير هنا
   بقدر حشوِ الصفحة فوقها، وإلا استقرّت الترويسة ملتصقةً برأس المجرى بلا
   الفسحة التي رُسمت لها.

   The document itself is a fragment too: the back-to-top button points at it.
   Its scroll margin equals the page padding above it, or the header comes to
   rest flush against the top of the stream without the breathing room drawn
   for it. */
.lg { display: grid; gap: var(--sp-6); scroll-margin-block-start: var(--sp-6); }

/* --------------------------------------------------------- The header */
/* الرسم إلى جانب النص لا فوقه: القارئ يبدأ بالكلمة، والرسم يقول له أي وثيقةٍ
   هذه قبل أن يقرأ عنوانها. وعلى الشاشة الضيّقة ينزل تحت النص، لأن رسماً يزيح
   العنوان عن أول ما يُرى مقايضةٌ خاسرة.

   The drawing beside the wording rather than above it: the reader begins with
   the words, and the drawing says which document this is before its title is
   read. On a narrow screen it drops below the text, because a drawing that
   pushes the title out of the first thing seen is a bad trade. */
.lg-hero {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 320px);
  gap: var(--sp-5);
  align-items: center;
  padding: var(--sp-5);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
}

.lg-hero-text { display: grid; gap: var(--sp-3); align-content: start; }

.lg-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  justify-self: start;
  padding: 4px 9px;
  border: 1px solid var(--gold);
  border-radius: var(--r-md);
  background: var(--surface-2);
  font-size: 11px;
  font-weight: 650;
  letter-spacing: var(--tr-md);
  color: var(--fg-1);
}
.lg-badge svg { color: var(--gold); }

.lg-hero h1 { font-size: 26px; font-weight: 700; line-height: 1.35; color: var(--fg-1); }

.lg-lede { max-width: 58ch; font-size: 14px; line-height: 1.85; color: var(--fg-2); }

.lg-hero-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-3);
  padding-block-start: var(--sp-2);
  font-size: 12px;
}

.lg-stamp,
.lg-langswap { display: inline-flex; align-items: center; gap: 6px; }

.lg-stamp { color: var(--fg-3); }
.lg-stamp svg { color: var(--fg-4); }

.lg-langswap {
  padding: 4px 9px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  color: var(--fg-2);
  transition: border-color var(--dur) var(--ease), color var(--dur) var(--ease);
}
.lg-langswap:hover { border-color: var(--gold); color: var(--fg-1); }

.lg-hero-art { justify-self: center; width: 100%; max-width: 320px; }

.lg-art { display: block; width: 100%; height: auto; }

/* ------------------------------------------------------- The contents */
.lg-toc {
  padding: var(--sp-4) var(--sp-5) var(--sp-5);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface-1);
}

.lg-toc-title {
  display: block;
  padding-block-end: var(--sp-3);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: var(--tr-md);
  text-transform: uppercase;
  color: var(--fg-3);
}

/* عمودان على السعة وعمودٌ على الضيق. و‎columns‎ لا ‎grid‎: الترتيب في القائمة
   المرقّمة يجب أن ينزل في العمود الأول ثم ينتقل إلى الثاني، وهو ما تفعله
   الأعمدة وحدها.

   Two columns where there is room and one where there is not. columns rather
   than grid: the order in a numbered list must run down the first column and
   then move to the second, which only columns do. */
.lg-toc-list { columns: 2; column-gap: var(--sp-5); }

.lg-toc-list li { break-inside: avoid; }

.lg-toc-list a {
  display: flex;
  align-items: baseline;
  gap: 9px;
  padding: 5px 0;
  font-size: 13px;
  color: var(--fg-2);
  transition: color var(--dur) var(--ease);
}
.lg-toc-list a:hover { color: var(--gold); }
.lg-toc-list em {
  flex: none;
  min-width: 1.6em;
  font-style: normal;
  font-variant-numeric: tabular-nums;
  font-weight: 650;
  color: var(--fg-4);
}

/* -------------------------------------------------------- A section */
/* ‎scroll-margin‎ لأن الشريط العلوي يطفو فوق المجرى: بغيره يقف العنوان المقصود
   خلفه فيظنّ القارئ أن اللُّصْقة أخطأت موضعها.

   scroll-margin, because the top bar floats over the stream: without it the
   heading jumped to sits behind it and the reader thinks the fragment missed. */
.lg-sec { scroll-margin-block-start: var(--sp-6); }

.lg-sec-head {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding-block-end: var(--sp-3);
  border-block-end: 1px solid var(--line);
}

.lg-num {
  flex: none;
  display: grid;
  place-items: center;
  min-width: 26px;
  height: 26px;
  padding-inline: 5px;
  border-radius: var(--r-md);
  background: var(--gold-face);
  font-size: 12px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--on-gold);
}

.lg-sec-icon { flex: none; display: inline-flex; color: var(--fg-4); }

.lg-sec-title { font-size: 17px; font-weight: 650; line-height: 1.45; color: var(--fg-1); }

.lg-sec-body { display: grid; gap: var(--sp-3); padding-block-start: var(--sp-4); }

.lg-sec-body p { max-width: 74ch; font-size: 14px; line-height: 1.9; color: var(--fg-2); }

/* البنود: علامةٌ ذهبية بدل النقطة الافتراضية، ولا مسافةٌ بادئة من المتصفّح —
   القائمة هنا محاذيةٌ للفقرة قبلها لا مزاحةٌ عنها.

   The points: a gold mark instead of the browser's bullet, and none of its
   indent — the list here lines up with the paragraph before it rather than
   stepping away from it. */
.lg-points { display: grid; gap: var(--sp-2); max-width: 74ch; }

.lg-points li {
  position: relative;
  padding-inline-start: var(--sp-4);
  font-size: 14px;
  line-height: 1.85;
  color: var(--fg-2);
}
.lg-points li::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  inset-block-start: 0.72em;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--gold);
}

/* --------------------------------------------------------- The tables */
.lg-tablewrap {
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
}
.lg-tablewrap:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

.lg-table { width: 100%; border-collapse: collapse; font-size: 13px; }

.lg-table th,
.lg-table td {
  padding: var(--sp-3);
  text-align: start;
  vertical-align: top;
  line-height: 1.75;
  border-block-end: 1px solid var(--line-soft);
}

.lg-table thead th {
  background: var(--surface-1);
  border-block-end: 1px solid var(--line);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: var(--tr-md);
  color: var(--fg-1);
  white-space: nowrap;
}

.lg-table tbody tr:last-child td { border-block-end: 0; }
.lg-table td { color: var(--fg-2); }

/* أول عمود هو مفتاح الصف: يُقرأ عنواناً لا قيمة، فيُكتب بحبرٍ كامل.
   The first column is the row's key: read as a heading rather than a value, so
   it carries the full ink. */
.lg-table tbody td:first-child { font-weight: 600; color: var(--fg-1); min-width: 9rem; }

/* ------------------------------------------------------------ A note */
.lg-note {
  display: flex;
  gap: 9px;
  max-width: 74ch;
  padding: var(--sp-3) var(--sp-4);
  border-inline-start: 3px solid var(--gold);
  border-radius: var(--r-md);
  background: var(--surface-1);
  font-size: 13px;
  line-height: 1.8;
  color: var(--fg-2);
}
.lg-note svg { flex: none; margin-block-start: 4px; color: var(--gold); }

/* -------------------------------------------------------- The contact */
.lg-contact {
  display: grid;
  gap: var(--sp-2);
  max-width: 74ch;
  padding: var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
}
.lg-contact > div { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4); }
.lg-contact dt { min-width: 6rem; font-size: 12px; font-weight: 650; color: var(--fg-3); }
.lg-contact dd { font-size: 14px; color: var(--fg-1); }
.lg-contact a { color: var(--gold); text-decoration: underline; text-underline-offset: 3px; }

.lg-support { justify-self: start; }

/* ------------------------------------------------------------ The end */
.lg-end {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding-block-start: var(--sp-4);
  border-block-start: 1px solid var(--line);
}

.lg-other {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.lg-other:hover { border-color: var(--gold); background: var(--surface-1); }
.lg-other > svg:first-child { flex: none; color: var(--gold); }
.lg-other span { display: grid; gap: 1px; }
.lg-other i { font-style: normal; font-size: 11px; color: var(--fg-3); }
.lg-other b { font-size: 14px; font-weight: 650; color: var(--fg-1); }
.lg-other .pt-go { flex: none; color: var(--fg-4); }

.lg-top {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--fg-3);
  transition: color var(--dur) var(--ease);
}
.lg-top:hover { color: var(--gold); }

@media (max-width: 760px) {
  .lg-hero { grid-template-columns: 1fr; }
  .lg-hero-art { max-width: 260px; }
  .lg-hero h1 { font-size: 21px; }
  .lg-toc-list { columns: 1; }
  .lg-sec-title { font-size: 15px; }
  .lg-sec-body p,
  .lg-points li { font-size: 13.5px; }
}

/* -------------------------------------------------- The printed sheet */
/* وثيقةٌ نظامية تُطبع وتُحفظ ملفاً — فمن حقّ من يفعل ذلك ألّا يطبع معها شريطاً
   علوياً ولا درجَ حساب ولا تذييلاً. والقشرة هنا مبنيّةٌ على ارتفاع الشاشة،
   فالطباعة تحتاج فكّها لا إخفاء بعضها: بغير ذلك تُطبع صفحةٌ واحدة وينقطع ما
   بعدها.

   A statutory document gets printed and filed — and whoever does that is
   entitled not to print a top bar, an account drawer and a footer with it. The
   shell here is built on the viewport's height, so printing needs it undone
   rather than parts of it hidden: without that one page prints and the rest is
   cut off. */
@media print {
  .pt-top,
  .pt-drawer,
  .pt-scrim,
  .pt-foot,
  .lg-langswap,
  .lg-support,
  .lg-end,
  .toasts,
  .skip-link { display: none !important; }

  .pt,
  .pt-main,
  .pt[data-mode="page"] .pt-main { height: auto; overflow: visible; background: #fff; }

  .pt-page--doc { max-width: none; padding: 0; }

  .lg-hero,
  .lg-toc,
  .lg-tablewrap,
  .lg-contact { border-color: #999; background: #fff; }

  .lg-sec { break-inside: avoid-page; }
  .lg-sec-head { break-after: avoid-page; }
  .lg-table { font-size: 11px; }

  /* واللُّصْقة في الورق لا تُنقر: يُكتب عنوان الوثيقة مرةً في أعلاها ويكفي.
     A fragment on paper cannot be clicked: the document's address is written
     once at its head and that is enough. */
  .lg-toc-list a { color: #000; }
}

/* ---------------------------------------- The tables, on a narrow screen */
/* ثلاثة أعمدة في ٣٩٠ بكسل ليست جدولاً بل ثلاثة أعمدة من كلمةٍ واحدة في السطر.
   فتُكدَّس الصفوف على الهاتف: كل صفٍّ بطاقة، وكل خليةٍ ترويسةُ عمودها فوق
   قيمتها — وهي الترويسة نفسها التي في ‎thead‎، محمولةً مع الخلية في ‎data-label‎
   فلا تُكتب مرتين.

   Three columns in 390 pixels is not a table but three columns of one word per
   line. So the rows stack on a phone: each row a card, each cell its column's
   heading above its own value — the very heading that is in the thead, carried
   with the cell in data-label so it is never written twice.

   وترويسة الجدول لا تُحذف بل تُخفى بصرياً وحدها، فتبقى في شجرة الوصول ويبقى
   ‎role‎ المكتوب في الوسم يقول ما لم يعد الشكل يقوله.

   The head row is not removed but hidden visually alone, so it stays in the
   accessibility tree, and the roles written into the markup keep saying what the
   shape no longer says. */
@media (max-width: 640px) {
  .lg-table thead {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  .lg-table,
  .lg-table tbody,
  .lg-table tr,
  .lg-table td { display: block; width: 100%; }

  .lg-table tr {
    padding: var(--sp-3);
    border-block-end: 1px solid var(--line);
  }
  .lg-table tbody tr:last-child { border-block-end: 0; }

  .lg-table td {
    padding: 5px 0;
    border-block-end: 0;
  }

  .lg-table td::before {
    content: attr(data-label);
    display: block;
    padding-block-end: 1px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: var(--tr-md);
    color: var(--fg-3);
  }

  /* أول خلية هي مفتاح الصف، فترويستها زائدة فوقها: البطاقة كلها عنها.
     The first cell is the row's key, so its label is redundant above it: the
     whole card is about it. */
  .lg-table tbody td:first-child {
    min-width: 0;
    padding-block-end: var(--sp-2);
    font-size: 14px;
  }
  .lg-table tbody td:first-child::before { display: none; }
}

/* =========================================================================
   التذييل حين تقصر الصفحة عن الشاشة — the footer when the page is short
   -------------------------------------------------------------------------
   صفحةٌ محتواها أقصر من الشاشة — «أضف إعلاناً» مثلاً — كانت تترك التذييل واقفاً
   في وسطها وتحته شريطٌ من الأرض الرمادية. فيُقرأ كتلةً سقطت في منتصف المستند لا
   نهايةً له، وهو أوضح ما يُقال له «مكسور».

   A page whose content is shorter than the viewport — "add a listing", say —
   used to leave the footer standing in its middle with a band of grey ground
   beneath it. It reads as a block dropped into the middle of the document
   rather than as its end, which is the plainest thing there is to call broken.

   والعلاج أن يصير المجرى عموداً مرناً: ‎margin-block-start: auto‎ يبتلع الفراغ
   الفائض فيدفع التذييل إلى قاع الشاشة، ويصير صفراً حين لا فراغ فيتبع المحتوى.

   The cure is to make the stream a flex column: margin-block-start: auto
   swallows the spare room and pushes the footer to the floor of the viewport,
   and becomes zero when there is none, so it follows the content instead.

   و‎flex: none‎ على كل ابن شرطٌ لا زينة: العمود المرن يضغط أبناءه افتراضياً،
   فصفحةٌ أطول من الشاشة كانت ستُسحق بدل أن تتمرّر.

   flex: none on every child is a condition rather than a flourish: a flex
   column shrinks its children by default, so a page taller than the viewport
   would have been squashed instead of scrolling.

   والفسحة فوق حدّ التذييل تأتي من حشو الصفحة السفلي — ‎--sp-7‎ في ‎.pt-page‎ —
   فلا تحتاج هامشاً ثابتاً ينازع ‎auto‎ على الخانة نفسها.

   The breathing space above the footer's rule comes from the page's own bottom
   padding — --sp-7 on .pt-page — so it needs no fixed margin fighting auto over
   the same slot.

   ومقصورٌ هذا كلّه على ‎[data-mode="page"]‎: صفحة الخريطة تملأ الشاشة ولا تتمرّر
   ولا تذييل لها، ولوحها المطلق لا يحتمل عموداً مرناً فوقه.

   All of it scoped to [data-mode="page"]: the map page fills the viewport, does
   not scroll and carries no footer, and its absolutely positioned canvas will
   not take a flex column over it.
   ========================================================================= */
.pt[data-mode="page"] .pt-main {
  display: flex;
  flex-direction: column;
}

/* ‎flex: none‎ شرطٌ لا زينة: العمود المرن يضغط أبناءه افتراضياً، فصفحةٌ أطول
   من الشاشة كانت ستُسحق بدل أن تتمرّر.

   flex: none is a condition rather than a flourish: a flex column shrinks
   its children by default, so a page taller than the viewport would have
   been squashed instead of scrolling.

   و‎width: 100%‎ هو الأهم، وهو مصيدةٌ لا تُرى: ‎.pt-page‎ يتوسّط بـ‎margin-inline:
   auto‎، والهامش التلقائي في المحور العرضي يُبطل ‎stretch‎ بنصّ المواصفة —
   فيهبط العنصر إلى عرض محتواه. وقد هبط فعلاً: من 760 بكسل إلى 246، فتكدّست
   بطاقات «هل أنت؟» عموداً واحداً وطالت الصفحة حتى فاضت عن الشاشة. والعرض
   الكامل يعيد ‎max-width‎ و‎margin-inline: auto‎ إلى عملهما كما في التخطيط
   الكتلي، فلا يتغيّر شكل أي صفحة بتغيّر نوع حاويتها.

   width: 100% matters more, and it is an invisible trap: .pt-page centres
   itself with margin-inline: auto, and an auto margin on the cross axis
   defeats stretch by the letter of the spec — so the element drops to the
   width of its content. It did: from 760 pixels to 246, stacking the "who
   are you?" cards into a single column and growing the page until it
   overflowed the viewport. A full width returns max-width and margin-inline:
   auto to the work they did under block layout, so no page changes shape
   because the kind of its container changed. */
.pt[data-mode="page"] .pt-main > * { flex: none; width: 100%; min-width: 0; }

.pt[data-mode="page"] .pt-main > .pt-foot { margin-block-start: auto; }


/* ------------------------------------------------------------------ الطيّ */

/* لوحُ الطلب في صفحة الأنشطة — the submission panel on the activity page.

   وكانت الصفحة قراءةً محضة: عشرة جداول لا زرّ فيها. فمن أراد تصريحاً أو حملةً
   أو عرض سعرٍ لم يجد باباً، وبقيت أقسامه فارغةً لا لأنه لم يطلب بل لأنه لا
   يستطيع.

   The page was pure reading: ten tables with no button in them. So whoever
   wanted a permit, a campaign or a quote found no door, and their sections
   stayed empty not because they never asked but because they could not.

   والطيّ بـ‎<details>‎ وحده بلا سكربت، كما تُبدَّل تبويبات صفحة المعروض بأزرار
   راديو: يعمل قبل أن يصل السكربت وبعده سواء. ومطويٌّ ابتداءً لأن الصفحة سجلٌّ
   يُقرأ أولاً، والطلب فعلٌ يُقصد.

   The fold is a bare <details> with no script, exactly as the offer page
   switches its tabs with radios: it works before the script arrives and after it
   alike. Folded to begin with because the page is a record to be read first, and
   submitting is a deliberate act. */
.pt-fold {
  margin-block-end: var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-1);
}

.pt-fold > summary {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 11px 14px;
  cursor: pointer;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--fg-1);
  list-style: none;
  user-select: none;
}

.pt-fold > summary::-webkit-details-marker { display: none; }

.pt-fold > summary:hover { color: var(--gold); }

.pt-fold > summary:focus-visible { outline: 2px solid var(--gold); outline-offset: -2px; }

.pt-fold[open] > summary { border-block-end: 1px solid var(--line); }

.pt-fold > .pt-form { padding: var(--sp-4) 14px; }

/* نموذجٌ داخل خليّة جدول: فعلٌ يكتب لا رابطٌ يُفتح.
   -------------------------------------------------------------------------
   زرٌّ يلغي حجزاً لا يجوز أن يكون رابطاً — يفتحه زاحفٌ أو يسبقه متصفّحٌ يجلب
   الصفحات، فيُلغى حجزٌ لم يطلب أحدٌ إلغاءه. فالفعل نموذجٌ بـPOST، وهذا ما يمنع
   النموذج من كسر ارتفاع الصفّ.

   A form inside a table cell: an action that writes rather than a link that
   opens.

   A button that cancels a booking must not be a link — a crawler opens it, or a
   browser prefetching pages gets there first, and a booking nobody asked to
   cancel is cancelled. So the action is a POST form, and this is what keeps that
   form from breaking the row's height. */
.pt-row-form { display: inline; margin: 0; }

/* وفعلٌ لا رجعة فيه لا يُرسم كفعلٍ عادي: الإلغاء يُقرأ قبل أن يُضغط.
   An action with no way back is not drawn like an ordinary one: cancelling is
   read before it is pressed. */
.btn.danger {
  border-color: var(--red);
  color: var(--red);
}

.btn.danger:hover {
  background: rgb(198 49 49 / 0.08);
}

/* وزرُّ التأكيد في نافذة السؤال يأخذ لون الفعل الذي يؤكّده.
   -------------------------------------------------------------------------
   ورقةُ اللوحة تعطي ‎.swal2-confirm‎ حبرَ اللوحة معكوساً — أسودَ على أبيض —
   وهو الصحيح لزرٍّ يؤكّد حجزاً. أما الذي يعتذر أو يلغي فيقرؤه صاحبه بلون
   الخطر نفسه الذي قرأ به الزرَّ في الصفّ: العين تتبع اللون من الجدول إلى
   النافذة فتعرف أنها في الطريق نفسه.

   والتخصيص يكفي: ‎.console-swal .swal2-confirm.swal-danger‎ صفّان وعنصر،
   وقاعدةُ اللوحة صفٌّ وعنصر — وهذه الورقة تُحمَّل فوق تلك على كلّ حال.

   The dialogue's confirming button takes the colour of the act it confirms.

   The console's sheet gives .swal2-confirm the console's ink inverted — black on
   white — which is right for a button confirming a booking. The one that declines
   or cancels is read in the same danger colour its row button was read in: the eye
   follows the colour from the table into the dialogue and knows it is on the same
   road.

   The specificity carries: .console-swal .swal2-confirm.swal-danger is two classes
   and an element against the console rule's one, and this sheet loads above that
   one in any case. */
/* والبابُ الثالث حين يُفتح يأخذ الأحمر، ويتركه زرُّ التأكيد إلى حبر اللوحة.
   -------------------------------------------------------------------------
   فوحدةٌ عليها حجوزات تُسأل ثلاثة: تراجعٌ، وإيقافُ عرضٍ تبقى معه الحجوزات،
   وإيقافٌ يُنهيها. والأوسطُ هو الفعل المعتاد — إخفاءُ إعلانٍ لا يمسّ ضيفاً —
   والأخيرُ يلغي إقاماتٍ دُفع في بعضها. فلو قُرئا بالأحمر معاً لاستوى عند العين
   ما يُخفي وما يُلغي.

   The third door takes the red when it opens, and the confirming button leaves it
   for the console's ink.

   A unit carrying bookings is asked three things: stand back, stop the display
   with the bookings standing, and stop it ending them. The middle one is the
   ordinary act — hiding a listing touches no guest — and the last cancels stays,
   some of them paid for. Read in red together, what hides and what cancels would
   look the same to the eye. */
.console-swal .swal2-confirm.swal-danger,
.console-swal .swal2-deny.swal-danger {
  background: var(--red);
  border-color: var(--red);
  color: #fff;
}

/* وثلاثةُ أبوابٍ تقف عموداً لا صفّاً.
   -------------------------------------------------------------------------
   فورقةُ النافذة ‎420px‎، وثلاثةُ أزرارٍ تحمل جملاً — «أوقف العرض وأبقِ
   الحجوزات» — لا تقف في صفٍّ إلّا أن يُقصّ اثنان منها أو ينزل الثالث وحده إلى
   سطرٍ تحتهما، فيُقرأ منفيّاً عن أخويه. وعمودٌ بأزرارٍ ممتدّة يُقرأ من أعلاه
   واحداً بعد واحد، وهو ما تفعله الهواتف بالاختيارات الثلاثة منذ عرفتها.

   Three doors stand in a column rather than a row.

   The dialogue's sheet is 420px, and three buttons carrying sentences — "stop
   showing, keep the bookings" — do not stand in a row unless two of them are cut
   short or the third drops onto a line of its own beneath them, where it reads as
   exiled from its brothers. A column of stretched buttons is read from its top one
   after another, which is what telephones have done with three choices for as long
   as they have had them. */
.console-swal-three .swal2-actions {
  flex-direction: column;
  align-items: stretch;
}

/* ------------------------------------------------------------------ الطلب */

/* لوحُ الطلب في صفحة المعروض — the ask panel on an offer's page.

   وهو محدَّدٌ بإطارٍ ومعزولٌ عن الوصف: ما فيه يكتب في القاعدة، وما حوله يُقرأ.
   والفرق بين قراءةٍ وكتابةٍ يُرى قبل أن يُقرأ.

   Framed and set apart from the description: what is inside it writes to the
   database and what is around it is read. The difference between reading and
   writing is seen before it is read. */
.pt-ask {
  margin-block: var(--sp-5);
  padding: var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-1);
  display: grid;
  gap: var(--sp-4);
}

.pt-ask-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  font-size: 13px;
  font-weight: 700;
  color: var(--fg-1);
}

/* وما يقع بعد الإرسال مكتوبٌ تحت الزرّ: الحجز يُسجَّل معلّقاً، والطلب ينتظر
   ردّاً. فمن قرأ ذلك لم يقرأ حجزاً غير مؤكَّد على أنه عطل.
   What happens after sending is written under the button: a booking is recorded
   pending and a request waits on an answer. Whoever read that does not read an
   unconfirmed booking as a fault. */
.pt-ask-note {
  margin: 0;
  font-size: 11.5px;
  line-height: 1.7;
  color: var(--fg-3);
}

/* بطاقةُ الاهتمام صفٌّ واحد مضغوط: النصُّ في جهة، والزرُّ في الطرف الأيسر بكلتا
   اللغتين. وفي العربية يقع آخرُ العناصر يساراً أصلاً، وفي الإنجليزية يُعكس الصفّ.
   The interest card is one compact row: the text on one side, the button at the
   left edge in both languages. In Arabic the last item already falls left; in
   English the row is reversed. */
.pt-ask--interest { padding: var(--sp-3) var(--sp-4); }

.pt-ask-interest {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2) var(--sp-4);
}
[dir="ltr"] .pt-ask-interest { flex-direction: row-reverse; }

.pt-ask-interest .pt-form-status { flex-basis: 100%; margin: 0; }
.pt-ask-copy { flex: 1 1 0; min-width: 0; display: grid; gap: var(--sp-1); }
.pt-ask-interest > .btn { flex: none; white-space: nowrap; }

/* --------------------------------------------------------- تقويم الحجز */

/* التقويم تحت بطاقة المعلن: ورقةٌ بيضاء ضيّقة تنزل بالطول.

   وكان لوحاً عريضاً بعمودين يملأ عرض الصفحة ويحمل سطرَ ملخَّصٍ ورابطَ محوٍ
   وفقرةَ شرح — أربعةُ أشياء تُقرأ قبل أن يُرى الشهر. وموضعه الآن عمودُ الجانب،
   وهو ضيّق بحكم شبكته، فلا يتّسع إلا لما يلزم: شهرٌ فوق حقلٍ فوق زرّ.

   The calendar beneath the advertiser's card: a narrow white sheet running down
   the page.

   It was a wide two-column panel filling the page and carrying a summary line, a
   clearing link and a paragraph of explanation — four things read before the
   month was seen. Its place now is the side column, narrow by its grid, with
   room for nothing but what is needed: a month above a field above a button.

   والورقة بيضاء في السمتين. والرموز تُعاد هنا بقيم النهار بدل صبغ كل عنصرٍ
   على حدة: الحقلُ والحدُّ والحبرُ والسهمان كلّها مبنيّة على هذه الرموز أصلاً،
   فتتبع الورقةَ التي وُضعت عليها. ولو صُبغت الخلفية وحدها لبقي حبرُ الليل
   كريمياً على الأبيض فلا يُقرأ.

   The sheet is white in both themes. The tokens are redefined here with the
   daylight values rather than each element being painted one by one: the field,
   the border, the ink and the two arrows are all built on these tokens already,
   so they follow the paper they are laid on. Painting the background alone would
   leave night's cream ink on white, unreadable. */
.pt-book,
.pt-reviews {
  display: grid;
  gap: 10px;
  padding: 12px;
  border-radius: var(--r-md);
  border: 1px solid rgb(88 36 21 / 0.16);
  background: #ffffff;

  --surface: #ffffff;
  --surface-1: rgb(197 153 43 / 0.06);
  --surface-2: rgb(197 153 43 / 0.14);
  --surface-3: rgb(197 153 43 / 0.22);
  --field-face: #fcf9f2;

  --fg-1: #3b1a0d;
  --fg-2: #635129;
  --fg-3: #8b7548;
  --fg-4: #bba478;

  --line: rgb(88 36 21 / 0.16);
  --line-strong: rgb(88 36 21 / 0.34);
  --line-soft: rgb(88 36 21 / 0.08);

  --gold-face: #c5992b;
  --on-gold: #2c1403;

  color: var(--fg-1);
}

.pt-book .pt-ask-head { font-size: 12.5px; }
.pt-book-cal { gap: 8px; }
.pt-book-form { display: grid; gap: 10px; }

/* والخانة أصغر ممّا هي في نافذة البحث: سبعة أعمدة في عمودٍ ثلثِ العرض.
   The cell is smaller than it is in the search sheet: seven columns inside a
   column a third of the width. */
.pt-book .pt-day { height: 28px; font-size: 11.5px; border-radius: 6px; }
.pt-book .pt-cal-grid th { font-size: 9.5px; padding-block: 2px; }
.pt-book .pt-cal-nav b { font-size: 12px; }
.pt-book .pt-icon-btn { width: 26px; height: 26px; }

/* والمشغول مشطوبٌ ومحزَّز، لا باهتاً وحده: الباهت يُقرأ «خارج الشهر» أو
   «معطّل»، والمحزَّز يُقرأ «مأخوذ».

   A taken night is struck through and hatched rather than merely faded: faded
   reads as "outside this month" or "disabled", while hatched reads as
   "taken". */
.pt-day.is-taken {
  color: var(--fg-4);
  text-decoration: line-through;
  cursor: not-allowed;
  background:
    repeating-linear-gradient(
      45deg,
      transparent 0 3px,
      color-mix(in srgb, var(--fg-4) 32%, transparent) 3px 4px
    );
}

/* مفتاحٌ من كلمتين: ما لا يُفهم من الشبكة وحدها يُقال تحتها، ولا يُقال ما
   يُفهم — «متاحة» هي كل ما عداهما.
   A key of two words: what the grid alone does not say is said beneath it, and
   what it does say is not — "available" is everything else. */
.pt-cal-key {
  display: flex;
  flex-wrap: wrap;
  column-gap: 14px;
  row-gap: 6px;
  margin: 0;
  font-size: 10.5px;
  color: var(--fg-3);
}

.pt-cal-key span { display: inline-flex; align-items: center; gap: 4px; }

/* والجملةُ ليست مفتاحاً: مفتاحُ الألوان كلمتان بمربّعين، وهذه جملةٌ تشرح
   الحقلين. فتأخذ سطرها بدل أن تُلصق بهما فيُقرأ الثلاثةُ سطراً واحداً متّصلاً.
   A sentence is not a key: the colour key is two words with two squares, and
   this explains the two fields. It takes its own line rather than being stuck
   beside them so all three read as one unbroken run. */
.pt-cal-key .pt-cal-said { flex-basis: 100%; line-height: 1.6; }

.pt-key {
  inline-size: 10px;
  block-size: 10px;
  border-radius: 3px;
  border: 1px solid var(--line);
}

.pt-key--chosen { background: var(--gold); border-color: var(--gold); }
.pt-key--taken {
  background:
    repeating-linear-gradient(
      45deg,
      transparent 0 3px,
      color-mix(in srgb, var(--fg-4) 32%, transparent) 3px 4px
    );
}

.pt-book-warn {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0;
  font-size: 11px;
  color: var(--red);
}

/* و‎display‎ على الصنف يغلب ‎display:none‎ التي يحملها ‎[hidden]‎ من ورقة
   المتصفّح، فيبقى المخفيّ ظاهراً.
   A display on the class beats the display:none [hidden] carries from the
   browser's own sheet, so what was hidden stays visible. */
.pt-book-warn[hidden] { display: none; }

/* والزرّ المغلق يُقرأ مغلقاً: لا لونَ دعوةٍ على زرٍّ لا يستجيب.
   A shut button reads shut: no inviting colour on a button that does not
   answer. */
.pt-book .btn[disabled] { opacity: 0.45; cursor: not-allowed; }

/* ---------------------------------------------------------------- Reviews */
/* ما قاله من نزل هنا — the reviews under the calendar.
   -------------------------------------------------------------------------
   لم تكن له قاعدةٌ واحدة: النجوم بلا لون، والتاريخ ‎.pt-note‎ فيطفو رقاقةً،
   والردّ نصٌّ عارٍ ملتصقٌ بالتقييم. فصار ورقةً كورقة الحجز فوقه، وكلُّ تقييم
   بندٌ مفصولٌ بخطٍّ عن الذي يليه.

   It had not one rule: the stars uncoloured, the date a .pt-note and so a
   floating chip, the reply bare text stuck to the review. It is now a sheet like
   the booking sheet above it, and each review an item ruled off from the next. */
.pt-reviews .pt-ask-head { font-size: 12.5px; }
.pt-reviews .pt-ask-head .ico { color: var(--gold-face); }

.pt-review-list {
  display: grid;
  margin: 0;
  padding: 0;
  list-style: none;
}

.pt-review-list > li + li { border-block-start: 1px solid var(--line-soft); }

.pt-review {
  display: grid;
  gap: 6px;
  padding-block: 10px;
}

.pt-review-list > li:first-child .pt-review { padding-block-start: 2px; }

.pt-review-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.pt-stars {
  display: inline-flex;
  gap: 1px;
  color: var(--fg-4);
  line-height: 0;
}

.pt-stars i { display: inline-flex; }
.pt-stars .is-on { color: var(--gold-face); }
.pt-stars .is-on svg { fill: currentColor; }

.pt-review-date {
  font-size: 11px;
  color: var(--fg-3);
}

.pt-review-body {
  margin: 0;
  font-size: 13px;
  line-height: 1.75;
  color: var(--fg-1);
  overflow-wrap: anywhere;
}

/* وردُّ المضيف ورقةٌ داخل التقييم، بخطٍّ ذهبيّ على جانب البداية: يُقرأ
   جواباً على ما فوقه لا تقييماً ثانياً.
   The host's reply is a slip inside the review with a gold rule on its start
   side: it reads as an answer to what is above it, not a second review. */
.pt-review-reply {
  display: grid;
  gap: 2px;
  margin-block-start: 2px;
  padding: 8px 10px;
  border-inline-start: 2px solid var(--gold-face);
  border-radius: var(--r-md);
  background: var(--surface-1);
}

.pt-review-reply b {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 11.5px;
  font-weight: 700;
  color: var(--fg-2);
}

.pt-review-reply b .ico { color: var(--gold-face); }

.pt-review-reply p {
  margin: 0;
  font-size: 12.5px;
  line-height: 1.7;
  color: var(--fg-2);
  overflow-wrap: anywhere;
}

/* والباقي خلف زرٍّ يقول كم: يعمل بلا سكربت لأنه ‎details‎.
   The rest behind a button that says how many: it works without a script
   because it is a details element. */
.pt-review-more { border-block-start: 1px solid var(--line-soft); }

.pt-review-more > summary {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding-block: 9px 0;
  font-size: 12px;
  font-weight: 700;
  color: var(--fg-2);
  cursor: pointer;
  list-style: none;
}

.pt-review-more > summary::-webkit-details-marker { display: none; }
.pt-review-more > summary:hover { color: var(--fg-1); }
.pt-review-more[open] > summary { display: none; }
.pt-review-more > .pt-review-list > li:first-child .pt-review { padding-block-start: 10px; }

/* ------------------------------------------------------------ Nationality */
/* منتقي الجنسية: قائمةٌ أصلية تحتها، وواجهةٌ فوقها.
   -------------------------------------------------------------------------
   والقائمة الأصلية هي الحقل — ‎name="nationality"‎ عليها وحدها — تبقى قائمةً
   مرئيةً تعمل بلا سكربت، وتُزاح بالصنف ‎js‎ إلى مكانها تحت الواجهة بشفافيةٍ
   صفر. ولا تُخفى بـ‎display:none‎: حقلٌ مطلوبٌ مخفيّ بهذه الصفة لا يقبل
   المتصفّح أن يُركّز عليه، فيرفض إرسال النموذج بلا كلمةٍ تُقال ولا فقاعةَ خطأ
   تظهر — والزائر يضغط الزرّ فلا يحدث شيء.

   The nationality picker: a native list underneath, a face over it.

   The native list is the field — name="nationality" is on it alone — it stays a
   visible working list without a script, and the js class moves it to its place
   beneath the face at zero opacity. It is not hidden with display:none: a
   required field hidden that way is one the browser will not focus, so it
   refuses to submit the form with nothing said and no error bubble shown — and
   the visitor presses the button and nothing happens. */
.pt-nat { position: relative; }

.js .pt-nat-native {
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 0;
  inline-size: 100%;
  block-size: 100%;
  min-height: 0;
  padding: 0;
  border: 0;
  opacity: 0;
  pointer-events: none;
}

.pt-nat-ui { display: none; }
.js .pt-nat-ui { display: block; }

/* الزرّ يرث شكل الحقل بالضبط: الحدّ نفسه، والارتفاع نفسه، والوجه نفسه. فلا
   يُقرأ صفٌّ في الاستمارة على أنه من طرازٍ آخر.
   The button inherits the field's shape exactly: the same border, the same
   height, the same face. So no row in the form reads as being of another
   make. */
.pt-nat-button {
  display: flex;
  align-items: center;
  gap: 9px;
  inline-size: 100%;
  min-block-size: 38px;
  padding: 8px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--field-face);
  color: var(--fg-1);
  font: inherit;
  font-size: 13px;
  text-align: start;
  cursor: pointer;
  transition: border-color var(--dur) var(--ease);
}
.pt-nat-button:hover { border-color: var(--line-strong); }
.pt-nat-button:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; }
.pt-nat[data-open="1"] .pt-nat-button { border-color: var(--gold); }

.pt-nat-button::after {
  content: "";
  flex: none;
  margin-inline-start: auto;
  inline-size: 6px;
  block-size: 6px;
  border-inline-end: 1.5px solid var(--fg-3);
  border-block-end: 1.5px solid var(--fg-3);
  transform: translateY(-2px) rotate(45deg);
  transition: transform var(--dur) var(--ease);
}
.pt-nat[data-open="1"] .pt-nat-button::after { transform: translateY(1px) rotate(225deg); }

.pt-nat-name {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.pt-nat-name--empty { color: var(--fg-3); }

/* والحدّ حول العَلَم ليس زينة: علم اليابان أبيضُ حوله، وعلمُ الحياد كذلك،
   فبلا حدٍّ يذوب في ورقة القائمة ويبقى منه القرص وحده.
   The border round a flag is not decoration: Japan's flag is white about it, as
   are others, and with no border it dissolves into the list's paper and nothing
   of it is left but the disc. */
.pt-flag {
  flex: none;
  inline-size: 21px;
  block-size: 16px;
  box-sizing: border-box;
  border: 1px solid var(--line-soft);
  border-radius: 1px;
  object-fit: cover;
  background: var(--surface-2);
}

/* ---------------------------------------------------------- The list itself */
.pt-nat-pop {
  display: none;
  position: absolute;
  inset-block-start: calc(100% + 4px);
  inset-inline: 0;
  z-index: 60;
  flex-direction: column;
  max-block-size: min(322px, 58dvh);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--shadow-2);
  overflow: hidden;
}
.pt-nat[data-open="1"] .pt-nat-pop { display: flex; }

/* وتُفتح إلى أعلى حين لا يبقى تحت الحقل ما يسعها: الصفحة لا تُقفز بالقارئ،
   والقائمة لا تُقصّ عند حافّة النافذة.
   It opens upwards when there is not room enough beneath the field: the page
   does not jump under its reader, and the list is not clipped at the window's
   edge. */
.pt-nat[data-drop="up"] .pt-nat-pop {
  inset-block-start: auto;
  inset-block-end: calc(100% + 4px);
}

.pt-nat-find {
  flex: none;
  padding: var(--sp-2);
  border-block-end: 1px solid var(--line);
}
.pt-nat .pt-nat-search {
  inline-size: 100%;
  min-height: 32px;
  padding: 6px 10px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--field-face);
  color: var(--fg-1);
  font-size: 13px;
}
.pt-nat .pt-nat-search:focus { outline: 2px solid var(--gold); outline-offset: 1px; }
.pt-nat .pt-nat-search::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }

.pt-nat-list {
  flex: 1 1 auto;
  min-block-size: 0;
  margin: 0;
  padding: 4px;
  list-style: none;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.pt-nat-opt {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 8px;
  border-radius: var(--r-md);
  color: var(--fg-1);
  font-size: 13px;
  cursor: pointer;
}
/* والبحثُ يُخفي ما لا يطابق بـ‎hidden‎، و‎display: flex‎ فوق يغلبه — the search hides
   non-matches with hidden, which the display: flex above overrides. */
.pt-nat-opt[hidden] { display: none; }
.pt-nat-opt:hover { background: var(--surface-1); }
.pt-nat-opt[data-active="1"] { background: var(--surface-2); }
.pt-nat-opt[aria-selected="true"] { color: var(--gold); font-weight: 650; }

.pt-nat-opt > span {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* والرمز مكتوبٌ إلى جانب كل دولة لا للزينة: هو ما يُبحث به أيضاً، فمن كتب
   ‎IN‎ ورأى الرمز عرف لماذا وجد الهند.
   The code stands beside each country for a reason: it is searched by too, so
   whoever typed IN and sees the code knows why India was found. */
.pt-nat-code {
  flex: none;
  margin-inline-start: auto;
  color: var(--fg-4);
  font-style: normal;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.06em;
}

.pt-nat-none {
  display: none;
  margin: 0;
  padding: 16px 12px;
  color: var(--fg-3);
  font-size: 12.5px;
  text-align: center;
}
.pt-nat[data-found="0"] .pt-nat-none { display: block; }
.pt-nat[data-found="0"] .pt-nat-list { display: none; }

/* ─────────────────────────────────────────────────────────────────────────────
   أسئلة النوع في نموذج النشر — the type's questions on the publishing form
   ───────────────────────────────────────────────────────────────────────────── */

/* والمجموعة تُخفى بصنف ‎js‎ على الجذر لا بـ‎hidden‎ يرفعه سكربت.

   فمن لم يصله ‎portal.js‎ يرى أسئلة الأنواع كلّها ويملأ ما يخصّ نوعه — نموذجٌ
   طويل يعمل ويُرسَل ويُقبل، لأن التحقّق لا يسأل إلا عن أسئلة النوع المختار.
   ولو كان الإخفاء بـ‎hidden‎ يُنزع بسكربت لرأى ذلك الزائر نموذجاً بلا سؤالٍ
   واحد — وهو ما يبدو عطلاً لا تدرّجاً.

   The group is hidden by the js class on the root rather than by a hidden a
   script lifts.

   Whoever portal.js never reached sees every type's questions and fills in the
   ones for their type — a long form that works, submits and is accepted, since
   the validation asks only after the chosen type's questions. Were the hiding a
   hidden removed by a script, that visitor would see a form with not one
   question on it — which reads as a fault rather than as degradation. */
.pt-type-fields { display: grid; gap: var(--sp-4); }

.js .pt-type-fields[data-off] { display: none; }

/* ─── والحقل يأخذ من العرض بقدر جوابه — a field takes as much width as its answer

   فحقلٌ يُكتب فيه «٣» لا يأخذ عرض الشاشة كما يأخذه الوصف: عشرة أسئلةٍ كلٌّ منها
   بعرضٍ كامل تصير عموداً طوله شاشتان، يُمرَّر فيه ولا يُقرأ. والعين تقرأ ثلاثة
   أرقامٍ في سطرٍ واحد أسرع مما تقرؤها في ثلاثة أسطر.

   والشبكة ستّة أعمدة لا اثنا عشر: الأنصبة ثلاثة — ثلثٌ ونصفٌ وكلّ — فستّةٌ
   تقسمها بلا كسر، واثنا عشر عمودٌ نصفها لا يُستعمل.

   A field that "3" is written into does not take the screen's width the way a
   description does: ten questions each at full width become a column two screens
   tall, scrolled through and not read. And the eye reads three figures on one
   line faster than on three.

   The grid is six columns rather than twelve: the shares are three — a third, a
   half and the whole — and six divides them without a remainder, where twelve is
   a column half of which is never used. */
.pt-tf-grid {
  display: grid;
  gap: var(--sp-4);
  grid-template-columns: repeat(6, 1fr);
  align-items: start;
}

.pt-tf-sm { grid-column: span 2; }   /* رقم — a number */
.pt-tf-md { grid-column: span 3; }   /* قائمة — a list */
.pt-tf-lg { grid-column: span 6; }   /* نصّ — a text */

/* وعلى الشاشة الضيّقة يصير الرقم نصفاً والقائمة سطراً: ثلاثةُ أرقامٍ في عرض
   هاتفٍ حقولٌ لا يُقرأ ما فيها.
   On a narrow screen a number becomes a half and a list a whole line: three
   figures across a phone are fields whose contents cannot be read. */
@media (max-width: 720px) {
  .pt-tf-sm { grid-column: span 3; }
  .pt-tf-md { grid-column: span 6; }
}

@media (max-width: 420px) {
  .pt-tf-sm { grid-column: span 6; }
}

/* واسم الحقل صفٌّ واحد: رمزٌ ثم اسمٌ ثم وحدةٌ ثم «اختياري» في آخر السطر.
   ------------------------------------------------------------------------
   وكان الرمز يقف على سطرٍ فوق الاسم لأنه ‎svg‎ في عنصرٍ من نوع block، فيُقرأ
   شكلاً طافياً لا علامةً على حقل. والصفُّ يجمعهما، و«اختياري» يُدفع إلى الطرف
   فيُقرأ حاشيةً لا جزءاً من الاسم — وكان «المساحة قدم² اختياري» ثلاثَ كلماتٍ
   ملتصقة تُقرأ اسماً واحداً.

   The field's name is one row: a mark, then a name, then a unit, then "optional"
   at the line's end.

   The mark used to stand on a line above the name because it is an svg in a
   block element, so it read as a shape floating rather than as a mark on a
   field. The row gathers the two, and "optional" is pushed to the far end to read
   as a note rather than as part of the name — where "Area ft² optional" was three
   words stuck together and read as one name. */
.pt-tf-label {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 2px 6px;
  min-height: 16px;
}

.pt-tf-label .ico { flex: none; color: var(--fg-3); }

/* والاسم يلتفّ ولا يُقصّ: عمودٌ ثلثُ العرض قد لا يتّسع لـ«التصنيف التخطيطي»،
   وقصُّه بثلاث نقاطٍ يُخفي اسم الحقل عمّن يُطلب منه أن يملأه — والالتفاف يطيل
   السطر ولا يُخفي شيئاً.
   The name wraps rather than being cut: a column a third of the width may not
   hold "التصنيف التخطيطي", and cutting it with an ellipsis hides a field's name
   from whoever is asked to fill it — where wrapping lengthens the line and hides
   nothing. */
.pt-tf-label b { font-weight: 600; }

.pt-tf-label em {
  font-style: normal;
  font-weight: 500;
  color: var(--fg-3);
  flex: none;
}

/* ─── و«نعم/لا» علامةٌ لا حقل — a yes/no is a mark rather than a field

   فمربّع اختيارٍ داخل ‎.pt-field‎ يرث ‎width: 100%‎ و‎min-height: 38px‎، فيصير
   صندوقاً فارغاً بعرض الشاشة واسمُه فوقه بعيداً عنه — فلا يُعرف أنه يُضغط،
   ولا يُعرف أنّ الاسم اسمُه.

   فتُجمع كلّها في صفٍّ من الرقاقات: الاسم إلى جانب المربّع في هدفٍ واحد يُضغط،
   والمؤشَّرة تُعلَم بحدّها لا بلونٍ وحده.

   A checkbox inside .pt-field inherits width: 100% and min-height: 38px, so it
   becomes an empty box the width of the screen with its name far above it — and
   nobody can tell it is to be pressed, nor that the name is its name.

   They are gathered into a row of chips instead: the name beside the box in one
   target that is pressed, and a ticked one marked by its rule rather than by a
   colour alone. */
.pt-tf-checks { display: flex; flex-wrap: wrap; gap: 8px; }

.pt-check {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 9px 14px;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--fg-2);
  background: var(--field-face);
  border: 1px solid var(--line);
  border-radius: 999px;
  cursor: pointer;
  transition: border-color .12s ease, color .12s ease;
}

.pt-check .ico { flex: none; color: var(--fg-3); }

/* والمربّع يعود إلى حجمه: القاعدة العامّة تعطي كل مُدخَلٍ في حقلٍ عرضاً كاملاً
   وارتفاعاً، وهي لا تعني مربّعاً.
   The box returns to its size: the general rule gives every input in a field a
   full width and a height, and it does not mean a checkbox. */
.pt-tf-checks .pt-check input[type="checkbox"] {
  width: 15px;
  height: 15px;
  min-height: 0;
  padding: 0;
  margin: 0;
  flex: none;
  accent-color: var(--gold);
}

.pt-check:hover { border-color: var(--fg-3); }

.pt-check:has(input:checked) {
  border-color: var(--gold);
  color: var(--fg-1);
}

.pt-check:has(input:checked) .ico { color: var(--gold); }

.pt-check:has(input:focus-visible) { outline: 2px solid var(--gold); outline-offset: 1px; }

/* ------------------------------------------------------------ أوراق مشروطة */

/*
| والصندوق المشروط يُطوى حين لا يوافق الجواب — ومع الصنف js وحده.
|
| فمن لا سكربت عنده يرى الصناديق كلها ومعها شرطُ كلٍّ منها مكتوباً تحت اسمه،
| ويرفع ما يخصّه — والخادم يقرأ الجواب ويطلب ما يوافقه على أي حال. والطيُّ
| تحسينٌ فوق ذلك لا شرطٌ له.
|
| A conditional box is folded away when it does not match the answer — and under
| the js class alone.
|
| Whoever has no script sees every box with its condition written beneath its
| name and uploads the one that is theirs — the server reading the answer and
| demanding what matches it regardless. The folding is a refinement over that
| rather than a condition of it.
*/
.js .pt-field--off { display: none; }

/* ─────────────────────────────────────────────────────────────────────────────
   بوابة الرمز حين يرسل الجهاز — the code's gateway when the device sends
   ─────────────────────────────────────────────────────────────────────────────

   خطوتان تعيشان في ورقةٍ واحدة. و‎data-step‎ عليها هو ما يفصلهما، على وزن
   ‎data-drawer‎ و‎data-mode‎ في هذه الورقة نفسها: حالةٌ تُكتب في وسمٍ واحد على
   الحاوية، والورقة تقرّر ما يُرى عندها.

   Two steps living on one sheet. data-step on the container is what separates
   them, in the measure of data-drawer and data-mode on this very sheet: one
   attribute carrying a state, and the stylesheet deciding what is seen at it.

   والخطوة الثانية تُطوى ولا تُرسَم مرفوعةً بـ‎hidden‎: ما يُخفى هنا يُظهره فعلُ
   صاحبه — ضغطةٌ على «أرسل» — لا وصولُ سكربتٍ بعد أن استقرّت الصفحة. */

.pt-otp[data-step="phone"] [data-otp-prove] { display: none; }

/* والطيّ يعمل في الاتجاهين: لوحٌ واحد مفتوح في كل حال.
   -----------------------------------------------------------------------------
   وكان لوح الرقم يبقى مفتوحاً بعد خروج الرمز ليُعاد الطلب منه، فيقف صاحبه أمام
   لوحين: رقمٌ كتبه وزرُّ إرسالٍ إلى جانبه، ورمزٌ يُنتظر منه تحته — ولا شيء يقول
   أيّهما المطلوب الآن. والسؤال الواحد يُسأل في موضعٍ واحد.

   The folding works both ways: one open panel in either state.

   The number's panel used to stay open after the code went out so it could be
   asked for again, leaving its owner at two panels: a number they had typed with
   a send button beside it, and a code awaited beneath — and nothing saying which
   is being asked of them now. One question is asked in one place. */
.pt-otp[data-step="code"] [data-otp-ask] { display: none; }

/* ومخرجا الخطوة الثانية — إعادةُ الرمز وتعديلُ الرقم — يُطويان مع لوحها.
   The second step's two ways out — resending and changing the number — fold with
   its panel. */
.pt-otp-actions { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-block-start: var(--sp-3); }
.pt-otp-actions form { margin: 0; }
.pt-otp[data-step="phone"] .pt-otp-actions { display: none; }

/* وما يفصل اللوحين يقع بينهما وإن دخل بينهما شيء.
   -----------------------------------------------------------------------------
   فـ‎.pt-panel + .pt-panel‎ يشترط الجوار المباشر، وحارسُ رِيكابتشا وسطرُ النتيجة
   يقعان بين اللوحين فينكسر الشرط ويلتصق اللوحان. والشقيق العام ‎~‎ لا يشترطه.

   What separates the two panels falls between them even when something enters
   between them.

   .pt-panel + .pt-panel requires immediate adjacency, and the reCAPTCHA guard
   and the outcome's line fall between the two panels, so the condition breaks
   and they meet flush. The general sibling ~ does not require it. */
.pt-otp > .pt-panel ~ .pt-panel { margin-block-start: var(--sp-4); }

/* الحارس لا يُعطى صندوقاً: رِيكابتشا الخفيّة تعلّق شارتها في زاوية النافذة، ولا
   تشغل من مجرى الصفحة شيئاً.
   The guard is given no box: the invisible reCAPTCHA hangs its badge in the
   window's corner and takes nothing from the page's flow. */
.pt-otp-guard { line-height: 0; }

/* سطر النتيجة، ولا يُرسَم قبل أن يكون له ما يقوله.
   The outcome's line, not drawn before it has something to say. */
.pt-otp-said {
  margin-block-start: var(--sp-4);
  padding: 9px 12px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface-1);
  color: var(--fg-2);
  font-size: 12.5px;
  font-weight: 600;
}

.pt-otp-said:empty { display: none; }

.pt-otp-said.is-bad {
  border-color: var(--red);
  background: rgb(198 49 49 / 0.08);
  color: var(--red);
}

/* ومن لا سكربت عنده يُقال له ذلك — ومرئيّاً افتراضياً يخفيه الصنف ‎js‎، لا
   مخفيّاً يرفعه سكربت.

   وهذه هي الحالة الوحيدة في هذا الملف التي يكون فيها غيابُ السكربت عطلاً لا
   تدرّجاً: الرسالة تخرج من الجهاز، فلا مقابل لها يعمل بدونه. وبقيةُ ما يُخفى
   بـ‎js‎ هنا عكسُه — أشياء تعمل بلا سكربت ويطويها وجوده.

   Whoever has no script is told so — visible by default and hidden by the js
   class, rather than hidden and lifted by a script.

   This is the one case in this file where the script's absence is a fault
   rather than degradation: the message leaves from the device, so it has no
   counterpart that works without it. Everything else hidden by js here is the
   reverse — things that work without a script and are folded away by its
   presence. */
.pt-otp-nojs {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin-block-start: var(--sp-4);
  padding: var(--sp-3);
  border: 1px solid var(--red);
  border-radius: var(--r-md);
  background: rgb(198 49 49 / 0.08);
  color: var(--red);
  font-size: 12px;
  line-height: 1.7;
}

.pt-otp-nojs svg { flex: none; margin-block-start: 3px; }

.js .pt-otp-nojs { display: none; }

/* والانتظار يُرى: الزرّ معطَّل والمؤشّر يقول إن شيئاً يجري.
   The wait is seen: the button is disabled and the cursor says something is
   under way. */
.pt-otp[data-busy="1"] { cursor: progress; }

/* ─────────────────────────────────────────────────────────────────────────────
   حقل الرقم: مفتاحُ الدولة ثم الرقم — the number's field: the country's prefix,
   then the number
   ─────────────────────────────────────────────────────────────────────────────

   صفٌّ واحد من حقلين، لأنهما جوابٌ واحد. والمفتاح لا يأخذ من العرض إلا قدره:
   ‎+971‎ وعَلَمٌ، والباقي كلُّه للرقم — فهو الذي يُكتب ويُقرأ ويُصحَّح.

   One row of two fields, because they are one answer. The prefix takes no more
   width than it needs — a flag and +971 — and all the rest is the number's,
   which is what gets typed, read and corrected. */

.pt-phone { display: flex; align-items: stretch; gap: 8px; }

/* والمفتاح إلى يسار الرقم في اللغتين، لا إلى بدايةِ السطر.
   |
   | فالرقم الدوليّ وحدةٌ واحدة تُقرأ من اليسار إلى اليمين مهما كانت لغة الصفحة:
   | ‎+971 50 123 4567‎. ووضعُ المفتاح في «بداية» السطر العربية — أي يمينه — يجعل
   | العين تقرأ الرقم ثم مفتاحه، فيصير المفتاح لاحقةً لا سابقة. وهذا ما تفعله كلّ
   | واجهةٍ تسأل عن رقمٍ دوليّ بالعربية.
   |
   | و‎row-reverse‎ لا ‎direction: ltr‎: الثاني يقلب اتجاه النصّ في كلّ ما تحته —
   | أسماءُ الدول في القائمة ومحاذاةُ سطورها — والمطلوب ترتيبُ حقلين لا قلبُ لغة.
   |
   | The prefix stands to the left of the number in both languages rather than at
   | the start of the line.
   |
   | An international number is one unit read left to right whatever language the
   | page is in: +971 50 123 4567. Putting the prefix at the Arabic line's start —
   | its right — makes the eye read the number and then its prefix, turning a
   | prefix into a suffix. It is what every interface asking for an international
   | number in Arabic does.
   |
   | row-reverse rather than direction: ltr: the latter flips the text direction of
   | everything beneath it — the countries' names in the list, how their rows align
   | — and what is wanted is two fields ordered, not a language reversed. */
[dir="rtl"] .pt-phone { flex-direction: row-reverse; }

.pt-phone > input {
  flex: 1 1 auto;
  min-width: 0;
  width: auto;
}

.pt-dial { position: relative; flex: none; }

/* ───────────────────────────────────────────────────────────── بلا سكربت

   القائمة الأصلية وحدها، بشكل بقيّة الحقول. وهي تعمل: تُفتح، ويُبحث فيها
   بالكتابة، وتُرسَل. وما ينقصها العَلَم وحده — و‎option‎ لا يحمل صورة.

   The underlying list alone, in the shape of every other field. It works: it
   opens, it is searched by typing, it posts. What it lacks is the flag alone —
   and an option carries no picture. */

.pt-field .pt-dial-native {
  width: auto;
  min-inline-size: 152px;
  max-inline-size: 190px;
  padding-inline: 10px;
}

/* ───────────────────────────────────────────────────────── ومع السكربت

   القائمة تبقى في المستند ويُسحب منها الحبر: هي ما يُرسَل، والوجه فوقها يكتب
   فيها. وهذا نمط ‎.pt-nat‎ نفسه — ولو أُزيلت بـ‎display: none‎ لخرج الحقل من
   الاستمارة ولم يصل الخادمَ مفتاحٌ أصلاً.

   The list stays in the document with the ink drawn out of it: it is what gets
   posted, and the face over it writes into it. This is .pt-nat's own pattern —
   were it removed with display: none the field would leave the form and no
   prefix would reach the server at all. */

.js .pt-field .pt-dial-native {
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 0;
  inline-size: 100%;
  min-inline-size: 0;
  block-size: 100%;
  min-height: 0;
  padding: 0;
  border: 0;
  opacity: 0;
  pointer-events: none;
}

.pt-dial-ui { display: none; }
.js .pt-dial-ui { display: block; }

/* والزرّ يرث شكل الحقل المجاور بالضبط: الحدّ نفسه والارتفاع نفسه والوجه نفسه،
   فلا يُقرأ نصفا الصفّ على أنهما من طرازين.
   The button inherits the neighbouring field's shape exactly: the same border,
   height and face, so the row's two halves do not read as being of two makes. */
.pt-dial-button {
  display: flex;
  align-items: center;
  gap: 7px;
  min-block-size: 38px;
  padding: 8px 10px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface-1);
  color: var(--fg-1);
  font: inherit;
  font-size: 13px;
  text-align: start;
  cursor: pointer;
  transition: border-color var(--dur) var(--ease);

  /* وما في الزرّ وحدةٌ لاتينية: عَلَمٌ ثم ‎+971‎ ثم السهم، من اليسار إلى اليمين
     في اللغتين. فالمفتاح رقمٌ يُقرأ هكذا ولا يُقلَب مع الصفحة.
     What is in the button is a Latin unit: a flag, then +971, then the caret,
     left to right in both languages. The prefix is a number read that way and is
     not flipped with the page. */
  direction: ltr;
}
.pt-dial-button:hover { border-color: var(--line-strong); }
.pt-dial-button:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; }
.pt-dial[data-open="1"] .pt-dial-button { border-color: var(--gold); }

.pt-dial-button i {
  font-style: normal;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--fg-2);
}

/* والسهم يشير إلى أسفل في الاتجاهين، فيُرسَم بحدَّين ماديّين لا منطقيّين.
   |
   | فـ‎border-inline-end‎ ينقلب مع ‎dir‎: يصير الحدّ الأيسر في العربية، فينقلب
   | معه ركن الزاوية ويخرج السهم مشيراً إلى اليسار — أي إلى لا شيء. والقائمة
   | تنفتح إلى أسفل في اللغتين، فالسهم الذي يصفها لا يُعكَس.
   |
   | The caret points downwards in both directions, so it is drawn with physical
   | borders rather than logical ones.
   |
   | border-inline-end flips with dir: it becomes the left border in Arabic, the
   | corner flips with it and the caret comes out pointing left — at nothing. The
   | list opens downwards in both languages, so the arrow describing it is not
   | mirrored. */
.pt-dial-button::after {
  content: "";
  flex: none;
  inline-size: 6px;
  block-size: 6px;
  border-right: 1.5px solid var(--fg-3);
  border-bottom: 1.5px solid var(--fg-3);
  transform: translateY(-2px) rotate(45deg);
  transition: transform var(--dur) var(--ease);
}
.pt-dial[data-open="1"] .pt-dial-button::after { transform: translateY(1px) rotate(225deg); }

/* والقائمة أعرضُ من زرّها: الزرّ يحمل مفتاحاً، والقائمة تحمل أسماء الدول.
   The list is wider than its button: the button carries a prefix, the list
   carries the countries' names. */
.pt-dial-list {
  display: none;
  position: absolute;
  inset-block-start: calc(100% + 4px);

  /* وتتدلّى من حافّة الزرّ اليسرى وتمتدّ يميناً، في اللغتين. والحدّ ماديّ لا
     منطقيّ لأن الزرّ صار في يسار الصفّ بالعربية أيضاً: حدٌّ منطقيّ يعلّقها من
     يمينه فتمتدّ إلى خارج الورقة.
     It hangs from the button's left edge and runs rightwards, in both languages.
     The inset is physical rather than logical because the button now sits at the
     row's left in Arabic too: a logical inset would hang it from the right and
     run it off the sheet. */
  left: 0;
  right: auto;
  z-index: 60;
  min-inline-size: 236px;
  max-block-size: min(296px, 56dvh);
  margin: 0;
  padding: 4px;
  list-style: none;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--shadow-2);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.pt-dial[data-open="1"] .pt-dial-list { display: block; }

/* وتُفتح إلى أعلى حين لا يبقى تحت الزرّ ما يسعها — كما في منتقي الجنسية.
   It opens upwards when there is not room enough beneath the button — as in the
   nationality picker. */
.pt-dial[data-drop="up"] .pt-dial-list {
  inset-block-start: auto;
  inset-block-end: calc(100% + 4px);
}

.pt-dial-opt {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 7px 8px;
  border-radius: var(--r-md);
  color: var(--fg-1);
  font-size: 13px;
  cursor: pointer;
}
.pt-dial-opt:hover { background: var(--surface-1); }
.pt-dial-opt[data-active="1"] { background: var(--surface-2); }
.pt-dial-opt[aria-selected="true"] { color: var(--gold); font-weight: 650; }

/* والاسم يأخذ ما بقي من الصفّ، فيُدفَع المفتاح إلى آخره ويصطفّ المفاتيح
   كلُّها على عمودٍ واحد تقرؤه العين نزولاً.
   The name takes what is left of the row, so the prefix is pushed to its end and
   all the prefixes line up in one column the eye reads downwards. */
.pt-dial-opt > span {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* والمفتاح في آخر الصفّ لا أوّله: الاسم هو ما يُبحث عنه بالعين، والمفتاح ما
   يُتحقَّق منه بعد أن يُوجد الاسم.
   The prefix sits at the row's end rather than its head: the name is what the
   eye looks for, and the prefix what is checked once the name is found. */
.pt-dial-code {
  flex: none;
  margin-inline-start: auto;
  font-style: normal;
  font-size: 12px;
  color: var(--fg-3);
}
.pt-dial-opt[aria-selected="true"] .pt-dial-code { color: inherit; }

/* وفي الشاشة الضيّقة تبقى القائمة داخل الورقة: عرضُها لا يتجاوز عرض الحقل.
   On a narrow screen the list stays inside the sheet: its width does not exceed
   the field's. */
@media (max-width: 420px) {
  .pt-dial-list { min-inline-size: min(236px, 74vw); }
}

/* ------------------------------------------- Managing a stay after publishing

   لوحةُ المضيف على وحدته: تقويمٌ يُكتب لا يُقرأ، ولوحُ وصولٍ ومغادرة.

   The host's panel over their unit: a calendar written rather than read, and a
   board of arrivals and departures.

   وتقويمُ الكتابة غيرُ تقويم القراءة: خليّةُ الضيف رقمٌ يُضغط، وخليّةُ المضيف
   ثلاثةُ حقولٍ فوق بعضها — مربّعٌ ورقمُ اليوم في سطر، ثم سعرُ الليلة، ثم أقلُّ
   مدّتها. فالارتفاعُ يتضاعف، والخطُّ يصغر، والشبكةُ تُمرَّر عرضاً على الهاتف
   بدل أن تنضغط حتى لا يُقرأ فيها شيء.

   A calendar for writing is not a calendar for reading: a guest's cell is a
   number to press, and a host's is three fields stacked — a checkbox and the
   day's number on one line, then the night's price, then its minimum. So the
   height doubles, the type shrinks, and on a telephone the grid scrolls sideways
   rather than being squeezed until nothing in it can be read. */
.pt-stay-cal { display: grid; gap: var(--sp-4); overflow-x: auto; }

.pt-cal-edit { min-inline-size: 520px; }

/* وخليّتان بينهما ٤ نقاط تُقرآن صندوقين، وبينهما نقطتان تُقرآن لوحاً واحداً
   مقسّماً بخطوط. والفرقُ نقطتان.
   Two cells four points apart read as two boxes, and two points apart as one
   slab ruled with lines. The difference is two points. */
.pt-cal-edit td { padding: 3px 4px; vertical-align: top; }

.pt-day-edit {
  display: grid;
  gap: 2px;
  padding: 4px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface);
}

/* والمحجوبةُ محزَّزةٌ كالمأخوذة وبلونٍ آخر: الاثنتان «غير معروضة»، وسببُهما
   مختلف — واحدةٌ حجزها ضيف والأخرى حجبها صاحبُها.
   A blocked night is hatched like a taken one and in another colour: both are
   "not on offer", for different reasons — one a guest took and one its owner
   withheld. */
.pt-day-edit.is-blocked {
  border-color: color-mix(in srgb, var(--red) 40%, var(--line));
  background:
    repeating-linear-gradient(
      45deg,
      transparent 0 3px,
      color-mix(in srgb, var(--red) 18%, transparent) 3px 4px
    );
}

.pt-day-block {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 4px;
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
}

.pt-day-block b { font-weight: 650; color: var(--fg-1); }
.pt-day-block input { margin: 0; cursor: pointer; }

.pt-day-num {
  inline-size: 100%;
  min-inline-size: 0;
  padding: 2px 4px;
  border: 1px solid var(--line);
  border-radius: 4px;
  background: var(--surface-1);
  font-size: 10.5px;
  font-variant-numeric: tabular-nums;
  text-align: center;
  color: var(--fg-1);
}

.pt-day-num::placeholder { color: var(--fg-4); }

/* ومرجعُ الحجز تحت رقم اليوم: الخليّة تقول «هذه مأخوذة» و«بأيّ حجز» معاً،
   فلا يُفتح سجلٌّ لمطابقة تاريخٍ برقم.
   The booking's reference beneath the day's number: the cell says "this one is
   taken" and "by which booking" together, so no record is opened to match a
   date against a number. */
.pt-day.is-taken { block-size: auto; min-block-size: 32px; padding-block: 3px; }
.pt-day-ref { font-size: 8.5px; font-style: normal; letter-spacing: .2px; }

.pt-key--blocked {
  background:
    repeating-linear-gradient(
      45deg,
      transparent 0 3px,
      color-mix(in srgb, var(--red) 34%, transparent) 3px 4px
    );
  border-color: color-mix(in srgb, var(--red) 40%, var(--line));
}

/* والمانعُ في رأس الصفحة بلونٍ يُقرأ قبل نصّه: «وحدتك لا تستقبل» خبرٌ يجب أن
   يُرى قبل أن يُكتب فيها شيء.
   The blocker at the head of the page in a colour read before its words: "your
   unit takes nothing" is news that must be seen before anything is written. */
.pt-blockers { border-color: color-mix(in srgb, var(--red) 38%, var(--line)); }
.pt-blockers .pt-ask-head { color: var(--red); }

/* لوحُ الوصول والمغادرة — the arrivals and departures board. */
.pt-arrivals { display: grid; gap: 2px; margin: 0; padding: 0; list-style: none; }

.pt-arrival {
  display: grid;
  grid-template-columns: 84px 1fr auto auto;
  align-items: center;
  gap: 8px;
  padding: 7px 4px;
  border-block-end: 1px solid var(--line);
  font-size: 11.5px;
}

.pt-arrival:last-child { border-block-end: 0; }

.pt-arrival-when { font-variant-numeric: tabular-nums; color: var(--fg-3); }
.pt-arrival-when .pt-today { color: var(--gold); font-weight: 700; }

.pt-arrival-who { display: grid; gap: 1px; min-inline-size: 0; }
.pt-arrival-who .primary-cell { font-weight: 600; color: var(--fg-1); }
.pt-arrival-who a { font-size: 10.5px; color: var(--fg-3); }

.pt-arrival-money .money { font-weight: 650; }
.pt-arrival-money .sub { font-size: 10.5px; color: var(--fg-3); }

/* وعلى الهاتف يسقط الصفُّ سطرين: أربعةُ أعمدةٍ في عرضٍ ضيّق تجعل الاسم حرفين.
   On a telephone the row falls to two lines: four columns across a narrow width
   leave the name two letters. */
@media (max-width: 560px) {
  .pt-arrival { grid-template-columns: 1fr auto; row-gap: 4px; }
  .pt-arrival-when { grid-column: 1 / -1; }
}

/* معرضُ الإعلان في صفحة تعديله — a listing's gallery on its editing page.
   ---------------------------------------------------------------------------
   وشبكةٌ تملأ عرضها بمربّعاتٍ متساوية: عددُ الصور يتراوح بين واحدةٍ وثلاثين،
   فلا يُكتب عددُ الأعمدة بيد. و‎auto-fill‎ لا ‎auto-fit‎: الأخيرة تمدّ الصورةَ
   الواحدة على عرض الصفحة كلّها، فيقرأ صاحبُ إعلانٍ بصورةٍ واحدة لوحةً بعرض
   شاشته.

   A grid filling its width with equal squares: the number of photographs runs
   between one and thirty, so no column count is written by hand. auto-fill rather
   than auto-fit: the latter stretches a lone photograph across the whole page, so
   the owner of a listing with one photograph reads a billboard the width of their
   screen. */
.pt-media-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: var(--sp-3);
  list-style: none;
  margin: 0 0 var(--sp-4);
  padding: 0;
}

.pt-media-cell {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}

/* والنسبةُ ثابتة والاقتصاصُ من الوسط: صورٌ بأبعادٍ مختلفة في شبكةٍ واحدة تجعل
   الصفَّ يتعرّج، والعينُ تقرأ التعرّج خطأً في الصفحة لا اختلافاً في الصور.
   A fixed ratio and a centre crop: photographs of different proportions in one grid
   make the row zigzag, and the eye reads the zigzag as a fault in the page rather
   than a difference between the photographs. */
.pt-media-cell > img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--r-lg);
  background: var(--surface-2);
  display: block;
}

.pt-media-clip {
  display: grid;
  place-items: center;
  width: 100%;
  aspect-ratio: 4 / 3;
  border-radius: var(--r-lg);
  background: var(--surface-2);
  color: var(--fg-3);
}

/* وشارةُ الغلاف على الصورة نفسها لا تحتها: من ينظر إلى شبكةٍ من عشر صورٍ يبحث
   عن واحدة، والبحثُ في سطرٍ تحت كلّ مربّع أبطأ من علامةٍ على المربّع.
   The cover's badge on the photograph rather than beneath it: whoever looks at a
   grid of ten is hunting for one, and hunting through a line under every square is
   slower than a mark on the square. */
.pt-media-tag {
  position: absolute;
  inset-inline-start: var(--sp-2);
  inset-block-start: var(--sp-2);
  padding: 2px 8px;
  border-radius: var(--r-md);
  background: var(--surface-inv);
  color: var(--surface);
  font-size: 11px;
  font-weight: 600;
}

.pt-media-acts { display: flex; flex-wrap: wrap; gap: var(--sp-1); }

.pt-media-acts > form { margin: 0; }

/* الإبلاغ عن إعلان — لوحٌ يُطوى تحت بطاقة المعلن.
   ---------------------------------------------------------------------------
   والبلاغُ فعلٌ نادر: حقلٌ مفتوحٌ دائماً تحت كلّ إعلان يقول للقارئ إنّ في
   الإعلانات ما يُشكى منه، وهي دعوةٌ لا يريدها سوق. فيُطوى بـ‎details‎ بلا سطر
   جافاسكربت — يفتحه من يريده، ولا يراه من لا يريده إلا سطراً واحداً.
   Reporting a listing — a panel that folds beneath the advertiser's card.
   Reporting is a rare act: a field standing open beneath every listing tells its
   reader that listings are things one complains about, an invitation no market
   wants. So it folds with details and no line of JavaScript — opened by whoever
   wants it, and seen by everybody else as one line. */
.pt-report { margin-top: var(--sp-3); }

.pt-report > summary {
  display: flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  font-size: 12px;
  color: var(--fg-3);
  list-style: none;
  padding: var(--sp-1) 0;
}

.pt-report > summary::-webkit-details-marker { display: none; }

.pt-report > summary:hover { color: var(--fg-2); }

.pt-report[open] > summary { margin-bottom: var(--sp-2); color: var(--fg-2); }

.pt-report > .pt-form { display: grid; gap: var(--sp-2); }

.pt-report textarea { resize: vertical; }

/* منحنى أداء الإعلان — ثلاثون عموداً بلا سكربت ولا مكتبة رسم.
   ---------------------------------------------------------------------------
   والارتفاع متغيّرٌ يُكتب على العنصر نفسه: الخادم يحسب النسبة، والورقة ترسمها.
   فلا يحتاج الرسمُ سطراً واحداً من جافاسكربت، ويُقرأ قبل أن يصل السكربت وبعده
   سواء — وهي قاعدة هذه البوابة في كل ما رُسم فيها.
   The listing's performance curve — thirty bars with no script and no charting
   library. The height is a custom property written onto the element itself: the
   server computes the percentage and the stylesheet draws it. So the drawing needs
   not one line of JavaScript, and reads before the script arrives and after it
   alike — this portal's rule in everything drawn in it. */
/* وللرسم أرضٌ وسقف. كان الشريط بلا هامشٍ ولا حشو: عمودُ اليوم الأعلى يبلغ مئةً
   بالمئة فيصطدم رأسُه بصندوق التنبيه فوقه، والأعمدةُ تُلاصق حافّتي اللوح يميناً
   ويساراً، وتقف على لا شيء. فالثلاثون عموداً تُقرأ ركاماً في فراغٍ أبيض لا رسماً
   في حيّزه.
   والحشو العلويّ سقفٌ لا يمسّه أعلى عمود، والحشو الجانبيّ يُبعد أوّل يومٍ وآخره
   عن الحافّة، والحدُّ السفليّ أرضٌ يقف عليها الجميع — وعليها يُقرأ خطُّ اليوم
   الفارغ «صفراً على الأرض» لا شظيّةً معلّقة في البياض.
   The drawing has a floor and a ceiling. The strip had neither margin nor padding:
   the tallest day reaches a hundred percent and its head strikes the hint box above
   it, the bars touch both edges of the panel, and they stand on nothing. So thirty
   bars read as rubble in white space rather than as a drawing in its own area.
   The top padding is a ceiling the tallest bar never touches, the side padding
   holds the first and last day off the edge, and the bottom border is a floor they
   all stand on: on it the empty day's line reads as "zero, on the ground" rather
   than as a splinter hanging in the white. */
.pt-curve {
  display: flex;
  align-items: flex-end;
  gap: 3px;
  height: 152px;
  margin: var(--sp-4) 0 0;
  padding: var(--sp-4) var(--sp-2) 0;
  border-block-end: 1px solid var(--line);
  list-style: none;
}

.pt-curve > li { flex: 1 1 0; display: flex; align-items: flex-end; height: 100%; min-width: 0; }

/* والعمودُ بلا مشاهدةٍ ارتفاعُه صفر، فيبقى منه خطٌّ رفيعٌ على الأرض يُقرأ «لا
   شيء» ولا يُقرأ «قليل» — والفرق بينهما هو الخبر كلُّه.
   A bar with no views has height zero, leaving a thin line on the floor that reads
   as "nothing" rather than "a little" — and the difference is the whole news. */
.pt-curve-bar {
  display: block;
  width: 100%;
  height: var(--h, 0%);
  min-height: 2px;
  border-radius: 2px 2px 0 0;
  background: var(--surface-3);
}

/* واليومُ الذي وصل فيه استفسار يُعلَّم: المشاهدةُ تقول إنّ أحداً نظر، والاستفسارُ
   يقول إنّ أحداً تحرّك — وهما خبران لا خبر.
   A day an enquiry came in is marked: a view says somebody looked, an enquiry says
   somebody moved — and those are two pieces of news rather than one. */
.pt-curve-bar[data-lead] { background: var(--surface-inv); }

/* وطرفا المدّة تحت طرفي الرسم: بالحشو الجانبيّ نفسه، فيقع التاريخُ تحت عموده لا
   تحت حافّة اللوح.
   The two ends of the period sit under the two ends of the drawing: by the same
   side padding, so each date falls under its own bar rather than under the
   panel's edge. */
.pt-curve-foot {
  display: flex;
  justify-content: space-between;
  margin: var(--sp-2) 0 0;
  padding-inline: var(--sp-2);
  font-size: 11px;
  color: var(--fg-3);
}

/* ومفتاحُ القراءة يفترق عن التاريخين: كان يلتصق بهما، فيُقرأ الصندوقان المنقوطان
   فوق الرسم وتحته حدَّين له لا تنبيهين حوله.
   The key stands apart from the two dates: it used to touch them, so the two dashed
   boxes above and below read as the drawing's own borders rather than as two notes
   around it. */
.pt-curve-key { display: flex; align-items: center; gap: 6px; margin-block-start: var(--sp-4); }

.pt-curve-swatch {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 2px;
  background: var(--surface-inv);
  flex: none;
}

/* وعلى الهاتف يضيق الفاصل ويقصر الرسم: ثلاثون عموداً في عرضِ كفٍّ، والفاصلُ
   الثابت يأكل من عرض العمود أكثر ممّا يتركه له.
   On a telephone the gap narrows and the drawing shortens: thirty bars across the
   width of a palm, where a fixed gap eats more of the bar's width than it leaves
   to it. */
@media (max-width: 560px) {
  .pt-curve { gap: 2px; height: 128px; padding-block-start: var(--sp-3); }
}

/* ورقما المجموع فوق المنحنى: من يفتح الصفحة يسأل «كم» قبل أن يسأل «متى».
   The two totals above the curve: whoever opens the page asks "how many" before
   they ask "when". */
.pt-stat-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-3); margin-bottom: var(--sp-4); }

.pt-stat {
  display: grid;
  gap: var(--sp-1);
  padding: var(--sp-3);
  border: 1px solid var(--surface-3);
  border-radius: var(--r-lg);
}

.pt-stat > span { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--fg-3); }

.pt-stat > b { font-size: 22px; }
