/* ============================================================
   base.css — الأساسيات: الألوان، الخطوط، الأزرار، الحقول
   Design tokens, reset, typography, controls. RTL-first.
   ============================================================ */

/* ---------- 1. Tokens ---------- */
/* ============================================================
   خطّ الموقع — SF Mada Bold من Sultan Fonts
   Site typeface. Licence: http://www.sultanfonts.com

   وزنٌ واحد فقط (٧٠٠)، ولذلك يُعلَن على المدى `100 900`: لو أُعلن
   بـ`font-weight: 700` وحدها لسقط النصّ العادي إلى الخطّ الاحتياطي
   وصار الموقع خطّين. و`font-synthesis` مطفأة فلا يُثخّن المتصفّح
   ثخيناً أصلاً حين يُطلب الوزن ٧٠٠.

   الملفّ woff2 لا otf: ٥٨ كيلوبايت بدل ١٩٨، وكل متصفّح يفهمه منذ ٢٠١٦.
   و`swap` تعرض النصّ بالخطّ الاحتياطي فوراً ثم تبدّله — فلا تبقى الشجرة
   بيضاء حتى ينزل الخطّ.

   One weight only, so it is declared across the whole range: pinned at
   700 it would leave normal-weight text on the fallback and the site
   would read in two typefaces. `font-synthesis` is off so the browser
   never fake-bolds an already-bold face. woff2, not otf: 58KB instead of
   198, understood by every browser since 2016.
   ============================================================ */
@font-face {
  font-family: "SF Mada";
  src: url("../fonts/sf-mada-bold.88029c11463f.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

/* ============================================================
   SF Article Bold — خطُّ سطرين اثنين: الإدارة في الرئيسية، والنسبة في
   صفحة الدخول. وهما اللذان يبتلع فيهما خطُّ الموقع باءَ «البرمجة»
   و«المبرمج» (انظر `--font-article`).

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

   وخدمتُه من هنا تُسقط ثلاثة كانت لازمةً له وحده: رفعاً يدوياً في لوحة
   الإدارة بعد كل نشرةٍ جديدة، وصفّاً في القاعدة قد لا يُنقل مع النسخ،
   واستثناءً في `PUBLIC_FONT_SLUGS` يفتح مساراً للعامّة. والملفّ ٤٨
   كيلوبايت بصيغة woff2 لا ١٠٨ بصيغة ttf.

   The face for two lines the site font mangles. It used to be served
   from the database because the licence covers use, not publication, and
   the repository was believed public; it is in fact private, and serving
   a webfont from your own site to your own visitors is precisely what a
   webfont licence allows. Serving it here drops three things that existed
   for it alone: a manual upload in the admin after every fresh deploy, a
   database row that a restore might not carry, and a public exception in
   `PUBLIC_FONT_SLUGS`.
   ============================================================ */
@font-face {
  font-family: "SF Article";
  src: url("../fonts/sf-article-bold.3df397f907cc.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

:root {
  color-scheme: light dark;

  /* Surfaces & text — light theme */
  --bg:            #f4f1ea;
  --bg-grid:       #e6e0d4;
  --surface:       #ffffff;
  --surface-2:     #faf8f4;
  --surface-sunk:  #efece4;
  --border:        #ddd6c9;
  --border-strong: #c4baa8;

  --text:          #1c1917;
  --text-muted:    #6b6255;
  --text-faint:    #9a9184;

  --accent:        #8a6d3b;
  --accent-strong: #6b5329;
  --accent-soft:   #f0e7d6;
  --on-accent:     #ffffff;

  --danger:        #b42318;
  --danger-soft:   #fdeceb;
  --success:       #15803d;
  --success-soft:  #eaf6ee;

  /* Shape & depth */
  --r-sm: 6px;
  --r-md: 10px;
  --r-lg: 16px;
  --r-xl: 22px;

  --shadow-sm: 0 1px 2px rgb(28 25 23 / .07), 0 1px 1px rgb(28 25 23 / .04);
  --shadow-md: 0 4px 12px rgb(28 25 23 / .09), 0 1px 3px rgb(28 25 23 / .05);
  --shadow-lg: 0 18px 40px rgb(28 25 23 / .16), 0 4px 12px rgb(28 25 23 / .08);

  /* Type */
  --font-head: "SF Mada", "Noto Kufi Arabic", "SF Arabic", "Geeza Pro", "Segoe UI", Tahoma, sans-serif;
  --font-body: "SF Mada", "Noto Sans Arabic", "SF Arabic", "Geeza Pro", "Segoe UI", Tahoma, sans-serif;

  /* خطُّ السطرين اللذين تُقرأ فيهما «البرمجة» و«المبرمج» — سطرِ الإدارة
     في الرئيسية وسطرِ النسبة في صفحة الدخول.

     وليس فيه SF Mada ولا يعود إليه: هو يبتلع الباء بين ما قبلها وما
     بعدها فتُقرأ «الرمجة» و«المرمج»، وقد جُرّب إبطالُ الرابطات وتكبيرُ
     الحجم وتخفيفُ الوزن فلم يُظهرها شيء. فإن غاب SF Article — ولم يُرفع
     بعدُ إلى القاعدة، أو تعذّر تنزيله — وقع السطر على خطّ النظام
     (Noto Kufi في أندرويد، SF Arabic في آبل) وكلاهما يُظهر النقطة.

     The face for the two lines that carry «البرمجة» and «المبرمج»: the
     stewards line on the hub and the credit line on the sign-in screen.
     SF Mada is deliberately absent from the stack rather than sitting at
     its end — it swallows a medial bā', and disabling ligatures, enlarging
     and lightening were all tried without bringing the dot back. If SF
     Article is missing (never uploaded, or its download failed) the line
     lands on the system Arabic face, which renders the dot. */
  --font-article: "SF Article", "Noto Kufi Arabic", "SF Arabic", "Geeza Pro", Tahoma, sans-serif;

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

     A desert palette, deliberately alternating warm and cool so no
     generation sits beside a neighbour from its own family. The first
     four used to be gold, red, orange and olive — all warm, and
     indistinguishable once softened into a background wash. */
  --gen-0: #a06a12;   /* رمل ذهبي   — dune gold      */
  --gen-1: #37752a;   /* نخيل       — date palm      */
  --gen-2: #a63523;   /* حنّاء وطين  — henna clay     */
  --gen-3: #0e6b80;   /* واحة       — oasis water    */
  --gen-4: #7a3f96;   /* شفق        — desert dusk    */
  --gen-5: #6b6b16;   /* سَمُر       — acacia         */
  --gen-6: #1f5fa8;   /* سماء الظهيرة — noon sky      */
  --gen-7: #9c3660;   /* وردة الصحراء — desert rose   */
  --gen-8: #7a4520;   /* تمر        — dates          */
  --gen-9: #4b5563;   /* بازلت      — basalt         */

  /* لون الزوجات — واحدٌ لهنّ جميعاً، لا يدخل دورة الأجيال.
     الألوان العشرة تملأ الطيف تقريباً، فأي درجة جديدة تجاور واحدة منها
     وتلتبس بها. لذا التمييز هنا بالإشباع لا بالدرجة: هذا اللون الوحيد
     المخفَّف بين عشرة مشبعة، فيُعرف من بعيد ولو ضاع الحدّ المتقطّع.
     One colour for every wife, outside the generation cycle. Ten hues
     already fill the wheel, so a new hue would sit next to one of them
     and be mistaken for it; this one separates by saturation instead —
     the only muted colour among ten saturated ones. */
  --wife: #8d6a6a;    /* وردٌ باهت   — faded rose       */
  /* لون المكلَّف بالفرع — أخضر خارج دورة الأجيال، فلا يلتبس بجيل.
     The steward's colour, a green outside the generation cycle so it
     can never be mistaken for one. */
  --keeper: #0891b2;  /* فيروز       — turquoise        */
  /* أزرقٌ غامق للمشرفين على الصفحة — أوضح من الفيروز على أرضٍ رملية،
     والفيروز يخفت عليها. Deep blue for the page's stewards: the
     turquoise washes out against sand, this does not. */
  --steward: #1e40af; /* نيلة        — deep indigo      */
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg:            #17150f;
    --bg-grid:       #241f16;
    --surface:       #221e17;
    --surface-2:     #2a251c;
    --surface-sunk:  #1b1811;
    --border:        #3b342a;
    --border-strong: #554c3d;

    --text:          #f2ede3;
    --text-muted:    #a9a091;
    --text-faint:    #7b7263;

    --accent:        #d3b076;
    --accent-strong: #e8cb9c;
    --accent-soft:   #33291a;
    --on-accent:     #17150f;

    --danger:        #f2887f;
    --danger-soft:   #3a1f1c;
    --success:       #6cc48a;
    --success-soft:  #1c2f22;

    --shadow-sm: 0 1px 2px rgb(0 0 0 / .5);
    --shadow-md: 0 4px 14px rgb(0 0 0 / .55);
    --shadow-lg: 0 20px 46px rgb(0 0 0 / .65);

    --gen-0: #dfb45c;   /* رمل ذهبي   */
    --gen-1: #7fc06a;   /* نخيل       */
    --gen-2: #ef8a72;   /* حنّاء       */
    --gen-3: #5cc0d6;   /* واحة       */
    --gen-4: #c79ae8;   /* شفق        */
    --gen-5: #cfcf62;   /* سَمُر       */
    --gen-6: #7ab2f0;   /* سماء       */
    --gen-7: #ef8ab0;   /* وردة الصحراء */
    --gen-8: #d1a17a;   /* تمر        */
    --gen-9: #a3b0c0;   /* بازلت      */
    --wife:  #cfa9a9;   /* وردٌ باهت   */
    --keeper: #67e8f9;  /* فيروز       */
    --steward: #93c5fd; /* نيلة        */
  }

  /* الصورة نهارية الإضاءة، فتُخفَّف أكثر تحت الوضع الليلي */
  :root:not([data-theme="light"]) body::before { opacity: .3; }
  :root:not([data-theme="light"]) body::after {
    background: color-mix(in oklab, var(--bg) 80%, transparent);
  }
}

/* التبديل اليدوي للوضع الليلي / manual dark toggle */
:root[data-theme="dark"] {
  --bg:            #17150f;
  --bg-grid:       #241f16;
  --surface:       #221e17;
  --surface-2:     #2a251c;
  --surface-sunk:  #1b1811;
  --border:        #3b342a;
  --border-strong: #554c3d;

  --text:          #f2ede3;
  --text-muted:    #a9a091;
  --text-faint:    #7b7263;

  --accent:        #d3b076;
  --accent-strong: #e8cb9c;
  --accent-soft:   #33291a;
  --on-accent:     #17150f;

  --danger:        #f2887f;
  --danger-soft:   #3a1f1c;
  --success:       #6cc48a;
  --success-soft:  #1c2f22;

  --shadow-sm: 0 1px 2px rgb(0 0 0 / .5);
  --shadow-md: 0 4px 14px rgb(0 0 0 / .55);
  --shadow-lg: 0 20px 46px rgb(0 0 0 / .65);

  --gen-0: #dfb45c;   /* رمل ذهبي   */
  --gen-1: #7fc06a;   /* نخيل     */
  --gen-2: #ef8a72;   /* حنّاء     */
  --gen-3: #5cc0d6;   /* واحة     */
  --gen-4: #c79ae8;   /* شفق    */
  --gen-5: #cfcf62;   /* سَمُر     */
  --gen-6: #7ab2f0;   /* سماء     */
  --gen-7: #ef8ab0;   /* وردة الصحراء */
  --gen-8: #d1a17a;   /* تمر    */
  --gen-9: #a3b0c0;   /* بازلت    */
  --wife:  #cfa9a9;   /* وردٌ باهت */
  --keeper: #67e8f9;  /* فيروز */
  --steward: #93c5fd; /* نيلة */
}
:root[data-theme="dark"] body::before { opacity: .3; }
:root[data-theme="dark"] body::after {
  background: color-mix(in oklab, var(--bg) 80%, transparent);
}

/* ---------- 2. Reset ---------- */
*, *::before, *::after { box-sizing: border-box; }

html, body {
  height: 100%;
  margin: 0;
}

/* ============================================================
   خلفية الموقع — صورة خلف الصفحات
   Site backdrop.

   تُرسم في `::before` مثبَّتة لا في `background` على `body`: الطبقة
   المستقلّة تقبل حجاباً وتعتيماً دون أن يرث النصّ شيئاً منهما، والتثبيت
   يمنع الصورة من التمدّد مع صفحةٍ طويلة فتتشوّه.

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

   Drawn in a fixed `::before` rather than as a `background` on `body`: a
   separate layer takes a scrim without the text inheriting it, and fixing
   it stops a tall page from stretching the picture. The tree canvas keeps
   its own opaque background on purpose — names and links must stay
   readable — so the photo shows around the page cards and on the sign-in
   screen, where nothing competes with it.
   ============================================================ */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  background: url("../img/backdrop.c1212795977e.webp") center / cover no-repeat;
  /* الحجاب: الصورة ٦٦٧ بكسلاً عرضاً، فتُكبَّر على الشاشات العريضة.
     الطمس الخفيف يخفي ذلك ويهدّئها خلف النصّ في آنٍ واحد. */
  filter: blur(2px) saturate(.9);
  transform: scale(1.03);   /* يبتلع حوافّ الطمس الشفّافة */
  opacity: .62;
}
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  background: color-mix(in oklab, var(--bg) 52%, transparent);
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.6;
  -webkit-text-size-adjust: 100%;
  -webkit-font-smoothing: antialiased;
  font-synthesis: none;   /* الخطّ ثخينٌ أصلاً — لا تُثخّنه مرّة ثانية */
  overscroll-behavior: none;
}

h1, h2, h3, h4 {
  font-family: var(--font-head);
  font-weight: 700;
  line-height: 1.3;
  margin: 0;
}

img { max-width: 100%; display: block; }

button, input, select, textarea { font: inherit; color: inherit; }

/* الخاصية hidden يجب أن تغلب على أي display في هذا الملف.
   بدونها يبقى زر مخفيّ ظاهراً، لأن `.btn { display: inline-flex }`
   المكتوبة هنا تتقدّم على قاعدة المتصفّح `[hidden] { display: none }` —
   وهذا ما كان يُظهر زر «جدّ أعلى» للمساعد رغم إخفائه.

   `hidden` must beat every display rule in this file: an author-level
   `.btn { display: inline-flex }` overrides the browser's own
   `[hidden] { display: none }`, which left the "add apex ancestor"
   button visible to assistants even after it was hidden. */
[hidden] { display: none !important; }

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

/* تبديل النصوص المختصرة على الشاشات الضيقة */
.wide-only { display: inline; }
.narrow-only { display: none; }
@media (max-width: 560px) {
  .wide-only { display: none; }
  .narrow-only { display: inline; }
  .hide-narrow { display: none !important; }
}

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---------- 3. App shell ---------- */
.app {
  display: flex;
  flex-direction: column;
  height: 100dvh;
  overflow: hidden;
}
/* لازم: قاعدة display في class تتغلّب على خاصية hidden وحدها */
.app[hidden] { display: none; }

/* منطقة العمل: الشجرة + اللوحة الجانبية */
.workspace {
  position: relative;
  flex: 1;
  display: flex;
  min-height: 0;
  overflow: hidden;
}

.topbar {
  display: flex;
  align-items: center;
  gap: 12px;
  /* يلتفّ على كلّ عرض: شريطُ الشجرات يأخذ سطره تحته، فلا يُقصّ من
     الحافّة ولا يزاحم الأدوات. والالتفافُ هنا لا يعطّل طيّ الأدوات تحت
     «⋯»: `topbar__actions` صفٌّ لا يلتفّ، فيفيض عن حدّه فيُقاس.
     Wraps at any width so the tree strip takes a line of its own rather
     than being clipped. This does not disable the "⋯" fold: the actions
     row does not wrap, so it still spills and is still measured. */
  flex-wrap: wrap;
  row-gap: 6px;
  /* حواشي الأمان: التطبيق المثبَّت على آيفون يملأ الشاشة، فيقع الشريطُ
     تحت النتوء وساعةِ النظام. و`env` صفرٌ حيث لا نتوء — فلا يتغيّر شيءٌ
     على الحاسب ولا على أندرويد.
     Safe-area insets: an installed app fills the screen, so the bar
     would sit under the notch and the system clock. `env` is zero where
     there is none, so nothing moves elsewhere. */
  padding: calc(10px + env(safe-area-inset-top, 0px))
           calc(16px + env(safe-area-inset-right, 0px))
           10px
           calc(16px + env(safe-area-inset-left, 0px));
  background: var(--surface);
  border-block-end: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
  /* الشريط يصنع سياق تكديس، فقائمة نتائج البحث بداخله محبوسة تحت هذا
     الرقم مهما رفعناها. لذلك يعلو الشريط على مفتاح الأجيال (٢٥) وأدوات
     العرض (٣٠)، ويبقى تحت الحجاب (١٠٠) واللوحة (١٢٠).
     The bar creates a stacking context, so the search dropdown inside it
     is capped by this value however high its own z-index. It must sit
     above the legend (25) and toolbar (30), below the veil and panel. */
  z-index: 70;
  flex: none;
}

.topbar__brand {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

/* اسم القبيلة في منتصف الشريط: البحث على جهة والأزرار على الأخرى،
   والاسم يأخذ ما بينهما ويتوسّطه. `order` تضبط الترتيب في الاتجاهين.
   The tribe name sits centred between the search and the buttons; it
   takes the space between them, and `order` keeps the arrangement right
   in both text directions. */
.topbar--centred { justify-content: space-between; }
.topbar--centred .search          { order: 1; }
.topbar--centred .topbar__brand   { order: 2; flex: 1; align-items: center; text-align: center; }
.topbar--centred .topbar__actions { order: 3; }

.topbar__title {
  font-family: var(--font-head);
  font-size: 1.05rem;
  font-weight: 700;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.topbar__subtitle {
  font-size: .76rem;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

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

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

   On a 390px screen the search and the account buttons swallow the bar,
   leaving the site's name ninety pixels to break across five lines — a
   sixth of the screen, and still unreadable. A row of its own holds it in
   two lines and leaves the controls a row beneath.
*/
@media (max-width: 720px) {
  .topbar { flex-wrap: wrap; row-gap: 6px; justify-content: center; }
  .topbar__brand,
  .topbar--centred .topbar__brand {
    flex: 1 0 100%;
    order: -1;
    align-items: center;
    text-align: center;
  }
}

/* ============================================================
   الترحيب بمن دخل / the greeting, once, after signing in
   ============================================================ */
/*
   بطاقةٌ في وسط الشاشة ولا حجاب تحتها: الترحيب لحظةٌ لا نافذةٌ تُغلق،
   فالصفحة تبقى تحته حيّةً تُضغط. ولذلك `pointer-events: none` على
   الغلاف و`auto` على البطاقة وحدها — من ضغط الرسالة طواها، ومن ضغط ما
   وراءها مضى في طريقه.

   A card in the middle of the screen with no veil under it: a greeting is
   a moment, not a dialog to dismiss, so the page stays live beneath it.
   Hence `pointer-events: none` on the wrapper and `auto` on the card
   alone: pressing the message folds it, pressing past it goes through.
*/
.greeting {
  position: fixed;
  inset: 0;
  z-index: 120;
  display: grid;
  place-items: center;
  padding: 20px;
  pointer-events: none;
}

.greeting__card {
  position: relative;
  pointer-events: auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  max-inline-size: min(24rem, 92vw);
  padding: 26px 26px 22px;
  border: 1px solid color-mix(in oklab, var(--accent) 34%, var(--border));
  border-radius: var(--r-xl);
  /* ضوءٌ من أعلى البطاقة: كأنّ عليها مصباحاً، فتبدو مرفوعةً عن الصفحة
     لا ملصقةً بها. A light from the card's head, so it reads as lifted
     off the page rather than pasted on it. */
  background:
    radial-gradient(120% 90% at 50% 0%,
      color-mix(in oklab, var(--accent) 14%, var(--surface)) 0%,
      var(--surface) 62%);
  box-shadow: var(--shadow-lg);
  text-align: center;
  cursor: pointer;
  overflow: hidden;
  animation: greeting-in .55s cubic-bezier(.2, .9, .3, 1) both;
}

.greeting--out .greeting__card { animation: greeting-out .38s ease-in forwards; }

/* فاصلةٌ مزخرفة فوق التحية — نجمةٌ بين خطّين، كصدر الصفحة في الكتب */
.greeting__ornament {
  display: flex;
  align-items: center;
  gap: 10px;
  inline-size: 100%;
  margin-block-end: 4px;
  color: color-mix(in oklab, var(--accent) 70%, transparent);
  font-size: .8rem;
}
.greeting__ornament::before,
.greeting__ornament::after {
  content: "";
  flex: 1;
  block-size: 1px;
  background: linear-gradient(
    to var(--to, left),
    transparent,
    color-mix(in oklab, var(--accent) 45%, transparent)
  );
}
.greeting__ornament::after { --to: right; }

.greeting__hi {
  font-family: var(--font-head);
  font-size: 1.15rem;
  font-weight: 700;
  color: var(--text-muted);
  letter-spacing: .02em;
}

/*
   الاسم هو المقصود بالرسالة، فله وحده الذهب واللمعة: خطٌّ من الضوء يمرّ
   عليه مرّةً واحدة عند الظهور ثم يسكن. لمعةٌ لا تتكرّر: ما يلمع في كل
   ثانيةٍ يصير إزعاجاً لا زينة.
   The name is what the message is about, so the gold and the shimmer are
   its own: a band of light crosses it once on arrival and then rests —
   what glints every second stops being an ornament.
*/
.greeting__name {
  font-family: var(--font-head);
  font-size: 1.7rem;
  font-weight: 700;
  line-height: 1.35;
  background-image: linear-gradient(
    100deg,
    var(--accent-strong) 0%,
    var(--accent) 34%,
    color-mix(in oklab, var(--accent) 45%, #fff) 50%,
    var(--accent) 66%,
    var(--accent-strong) 100%
  );
  background-size: 300% 100%;
  background-position: 100% 0;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  animation: greeting-shimmer 1.9s .45s ease-out both;
}

.greeting__line {
  margin-block-start: 6px;
  font-size: .94rem;
  line-height: 1.75;
  color: var(--text);
}

/* شريطٌ ينحسر في عشر ثوانٍ: يقول متى تنصرف الرسالة قبل أن تنصرف،
   فلا تختفي فجأةً وقد بقي في العين منها شيء.
   A bar that drains over ten seconds, saying when the message will leave
   before it leaves. */
.greeting__bar {
  position: absolute;
  inset-block-end: 0;
  inset-inline: 0;
  block-size: 3px;
  transform-origin: 100% 50%;              /* من اليمين، كالقراءة */
  background: linear-gradient(
    to left,
    var(--accent),
    color-mix(in oklab, var(--accent) 35%, transparent)
  );
  animation: greeting-drain 10s linear forwards;
}
:dir(ltr) .greeting__bar { transform-origin: 0 50%; }

/* بطاقة التنبيه: كالترحيب شكلاً وأدفأ لوناً — فيها عملٌ ينتظر لا تحيّة.
   The notice card: the greeting's shape in a warmer colour — it carries
   work waiting rather than a welcome. */
.greeting__card--alert {
  border-color: color-mix(in oklab, var(--accent) 55%, var(--border));
  background:
    radial-gradient(120% 90% at 50% 0%,
      color-mix(in oklab, var(--accent) 20%, var(--surface)) 0%,
      var(--surface) 62%);
}
.greeting__go { margin-block-start: 12px; }

/* ------------------------------------------------------------
   بطاقةُ الدعوة: تثبيتُ الموقع على الشاشة والتنبيه

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

   ويلتفّان إلى سطرين في الشاشة الضيّقة (`wrap`)، فلا يخرج زرٌّ عن
   حافّة جوّالٍ مطويّ.

   Two answers side by side rather than one button: with only a "yes",
   dismissing becomes the "no" — and they are not the same. They wrap to
   two lines on a narrow screen so neither runs off a folded phone.
   ------------------------------------------------------------ */
.greeting__card--invite {
  border-color: color-mix(in oklab, var(--accent) 45%, var(--border));
}
.invite__steps {
  margin-block-start: 4px;
  opacity: .9;
}
.invite__answer {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
  margin-block-start: 14px;
}

@keyframes greeting-in {
  from { opacity: 0; transform: translateY(14px) scale(.96); }
  to   { opacity: 1; transform: none; }
}
@keyframes greeting-out {
  to { opacity: 0; transform: translateY(-10px) scale(.98); }
}
@keyframes greeting-shimmer {
  from { background-position: 100% 0; }
  to   { background-position: 0 0; }
}
@keyframes greeting-drain {
  from { transform: scaleX(1); }
  to   { transform: scaleX(0); }
}

/* من طلب سكون الحركة: تظهر الرسالة وتنصرف بلا انزلاقٍ ولا لمعان —
   والشريط يبقى ساكناً، والعشر ثوانٍ يحسبها الجافاسكربت لا الحركة.
   For anyone who asked for stillness: no slide and no shimmer, and the
   ten seconds are counted by the script, never by the animation. */
@media (prefers-reduced-motion: reduce) {
  .greeting__card,
  .greeting__name,
  .greeting__bar { animation: none; }
  .greeting__name { color: var(--accent-strong); }
  .greeting--out .greeting__card { opacity: 0; transition: opacity .2s; }
}

.topbar__actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: none;
}

/* ---------- 4. Buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 8px 14px;
  min-height: 38px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--text);
  font-family: var(--font-body);
  font-size: .875rem;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  transition: background .15s, border-color .15s, transform .06s, opacity .15s;
}
.btn:hover:not(:disabled) { background: var(--surface-2); border-color: var(--accent); }
.btn:active:not(:disabled) { transform: translateY(1px); }
.btn:disabled { opacity: .5; cursor: not-allowed; }

.btn--primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}
.btn--primary:hover:not(:disabled) { background: var(--accent-strong); border-color: var(--accent-strong); }

.btn--danger { color: var(--danger); border-color: color-mix(in oklab, var(--danger) 40%, var(--border)); }
.btn--danger:hover:not(:disabled) { background: var(--danger-soft); border-color: var(--danger); }

.btn--ghost { background: transparent; border-color: transparent; }
.btn--ghost:hover:not(:disabled) { background: var(--surface-sunk); border-color: var(--border); }

.btn--icon {
  padding: 0;
  width: 38px;
  min-height: 38px;
  font-size: 1rem;
}

.btn--sm { min-height: 32px; padding: 5px 10px; font-size: .8rem; }
.btn--block { width: 100%; }

/* ---------- 5. Fields ---------- */
.field { display: flex; flex-direction: column; gap: 5px; margin-block-end: 14px; }

.field__label {
  font-size: .8rem;
  font-weight: 600;
  color: var(--text-muted);
}

.input, .textarea, .select {
  width: 100%;
  padding: 9px 12px;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--text);
  font-size: .9rem;
  transition: border-color .15s, box-shadow .15s;
}
.input:focus, .textarea:focus, .select:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent) 22%, transparent);
}

/* الحقل المقفل يُقرأ ولا يُكتب — والعين تعرف ذلك قبل أن تحاول اليد.
   A locked field is read, not written, and the eye should know before
   the hand tries. */
.input:disabled, .textarea:disabled, .select:disabled {
  background: var(--surface-sunk);
  color: var(--text-muted);
  cursor: not-allowed;
}
.textarea { resize: vertical; min-height: 76px; line-height: 1.6; }

/* حقل الاسم: `textarea` ليقبل Enter، لكن بمظهر سطرٍ واحد لا صندوق نبذة.
   The name field is a textarea so Enter works, styled as a line not a box. */
.name-input { resize: vertical; min-height: 0; line-height: 1.5; font-family: inherit; }

.field__hint { font-size: .74rem; color: var(--text-faint); }

.field-row { display: flex; gap: 10px; }
.field-row > * { flex: 1; }

/* ---------- 6. Misc ---------- */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 9px;
  border-radius: 999px;
  background: var(--surface-sunk);
  border: 1px solid var(--border);
  font-size: .74rem;
  font-weight: 600;
  color: var(--text-muted);
}

.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  height: 100%;
  padding: 32px;
  text-align: center;
  color: var(--text-muted);
}
.empty-state__icon { font-size: 2.6rem; opacity: .55; }
.empty-state h2 { font-size: 1.15rem; color: var(--text); }
.empty-state p { margin: 0; max-width: 40ch; font-size: .9rem; }

.spinner {
  width: 22px; height: 22px;
  border: 2.5px solid var(--border-strong);
  border-block-start-color: var(--accent);
  border-radius: 50%;
  animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* التنبيهات / toasts */
.toasts {
  position: fixed;
  inset-block-end: 18px;
  inset-inline-start: 50%;
  transform: translateX(50%);
  display: flex;
  flex-direction: column;
  gap: 8px;
  z-index: 400;
  pointer-events: none;
  width: min(92vw, 420px);
}
:dir(ltr) .toasts { transform: translateX(-50%); }

.toast {
  padding: 10px 16px;
  border-radius: var(--r-md);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  box-shadow: var(--shadow-lg);
  font-size: .875rem;
  font-weight: 600;
  text-align: center;
  animation: toast-in .22s ease-out;
}
.toast--error   { border-color: var(--danger);  background: var(--danger-soft);  color: var(--danger); }
.toast--success { border-color: var(--success); background: var(--success-soft); color: var(--success); }

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

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    transition-duration: .01ms !important;
  }
}


/* ---------- مفتاح الخلفية الصوتية ---------- */
/* مثبَّت أسفل يسار الشاشة: الأسفل وسطاً فيه شريط أدوات الشجرة، واليمين
   فيه مفتاح الأجيال. وz-index دون لوحة التحرير (١٢٠) وفوق الشريط (٧٠).
   Bottom-left: the tree's toolbar owns bottom-centre and the generation
   legend owns the right. Below the editing panel, above the top bar. */
.music-toggle {
  position: fixed;
  inset-block-end: 18px;
  inset-inline-end: 18px;
  z-index: 90;
  width: 42px;
  height: 42px;
  border-radius: 50%;
  font-size: 1.05rem;
  box-shadow: var(--shadow-md);
  background: var(--surface);
}
.music-toggle:hover { transform: translateY(-2px); }

/* حالته من لونه، فالرمز ♪ واحدٌ في الحالتين. */
.btn--sounding {
  color: var(--accent);
  border-color: color-mix(in oklab, var(--accent) 45%, var(--border));
  background: color-mix(in oklab, var(--accent) 14%, var(--surface));
}


/* زرٌّ حالته مُفعّلة — يُقرأ بلونه لا بنصّه وحده */
.btn--on {
  color: var(--accent);
  border-color: color-mix(in oklab, var(--accent) 45%, var(--border));
  background: color-mix(in oklab, var(--accent) 12%, var(--surface));
}

/* تنبيهٌ تحت حقل: يُقرأ قبل الضغط لا بعده، فيُلوَّن ليُرى.
   A warning under a field: read before the click, so it is coloured. */
.field__hint--warn { color: var(--danger); font-weight: 600; }
