/* ============================================================
   tree.css — لوحة الشجرة، بطاقات الأشخاص، الروابط
   The pan/zoom canvas, person cards, connector links, panels.
   ============================================================ */

/* ---------- 1. Canvas ---------- */
.canvas {
  position: relative;
  flex: 1;
  overflow: hidden;
  touch-action: none;            /* نتحكم بالسحب والتكبير يدوياً */
  cursor: grab;
  background-color: var(--bg);
  background-image:
    radial-gradient(circle at 1px 1px, var(--bg-grid) 1.4px, transparent 0);
  background-size: 26px 26px;
  contain: layout paint;
}
.canvas--grabbing { cursor: grabbing; }
.canvas:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* اللوحة نفسها LTR ومثبّتة على الزاوية العليا اليسرى
   حتى تبقى الإحداثيات (x, y) صحيحة مهما كان اتجاه الصفحة. */
.viewport {
  position: absolute;
  top: 0;
  left: 0;
  direction: ltr;
  transform-origin: 0 0;
  will-change: transform;
}

.links {
  position: absolute;
  inset-block-start: 0;
  left: 0;
  overflow: visible;
  pointer-events: none;
}
.link {
  fill: none;
  stroke: var(--g, var(--border-strong));
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: .55;
  transition: opacity .2s, stroke-width .2s;
}
.link--active { opacity: 1; stroke-width: 3; }
.link--dim { opacity: .12; }

.nodes { position: absolute; inset-block-start: 0; left: 0; }

/* ---------- 2. Person card ---------- */
.node {
  position: absolute;
  inset-block-start: 0;
  left: 0;
  width: var(--node-w, 136px);
  direction: rtl;
  will-change: transform;
}

.node__card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 10px 8px 12px;
  border: 1px solid var(--border);
  border-block-start: 3px solid var(--g);
  border-radius: var(--r-lg);
  background: color-mix(in oklab, var(--g) 7%, var(--surface));
  box-shadow: var(--shadow-sm);
  font: inherit;
  color: inherit;
  text-align: center;
  cursor: pointer;
  transition: box-shadow .18s, border-color .18s, transform .12s, opacity .2s;
}
.node__card:hover {
  box-shadow: var(--shadow-md);
  border-color: var(--g);
  transform: translateY(-2px);
}

.node--selected .node__card {
  border-color: var(--g);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--g) 45%, transparent), var(--shadow-md);
}
.node--match .node__card {
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent) 60%, transparent), var(--shadow-md);
}
.node--dim { opacity: .3; }

/* الصورة / photo */
.node__photo {
  position: relative;
  width: 84px;
  height: 84px;
  border-radius: 50%;
  overflow: hidden;
  background: var(--surface-sunk);
  box-shadow: 0 0 0 3px var(--g), 0 0 0 5px color-mix(in oklab, var(--g) 22%, transparent);
  flex: none;
}
.node__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* من لا صورة له: دائرة فارغة بلون جيله، لا حرف — الاسم تحتها كاملاً.
   No photo: an empty circle in the generation's colour, no letter — the
   full name is written right underneath it. */
.node__photo:empty {
  background: color-mix(in oklab, var(--g) 16%, var(--surface-sunk));
}

/* الاسم تحت الصورة / name under the photo */
.node__name {
  font-family: var(--font-head);
  /* قياسات SF Mada أصغر بصرياً من الخطّ الذي سبقه عند نفس الرقم، فرُفعت
     الأربعة معاً — الاسم والإقامة على البطاقتين — لتبقى النسبة بينها.
     SF Mada reads smaller than the face it replaced at the same size, so
     all four sizes moved together and their ratios held. */
  font-size: .95rem;
  font-weight: 700;
  line-height: 1.35;
  color: var(--text);
  /* `pre-line` لا `normal`: الكسرة التي كتبها المحرّر تُحترم، والمسافات
     الزائدة تُطوى كالمعتاد. بدونها يُطبع السطران متلاصقين في سطر.
     `pre-line`, not `normal`: an editor's line break is honoured while
     runs of spaces still collapse. */
  white-space: pre-line;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  overflow-wrap: anywhere;
}

/* البطاقة بلا صورة: الاسم يرث حيّز الدائرة فيكبر ويتوسّط.
   `min-height` يطابق `BARE_H` في layout.js — عليه تُحسب بداية وصلات هذه
   البطاقة إلى أبنائها، فإن غُيّر أحدهما غُيّر الآخر معه.
   A photo-less card: the name inherits the circle's room, grows and
   centres. `min-height` must match `BARE_H` in layout.js. */
.node--bare .node__card {
  min-height: 104px;
  justify-content: center;
}
.node--bare .node__name {
  font-size: 1.4rem;
  line-height: 1.25;
  /* ثلاثة أسطر لا سطران: كسرةٌ مكتوبة قد يلتفّ أحد شطريها، والقصّ عند
     سطرين كان يبتر الاسم بعد أن قرّر المحرّر أين يكسره. */
  -webkit-line-clamp: 3;
  line-clamp: 3;
}

/* بطاقة الزوجة: صندوقٌ كبقيّة البطاقات — يميّزها لونها الخاصّ والحدّ
   المتقطّع وخلوّها من الصورة، لا شكلٌ مختلف. */
.node--wife .node__card {
  border-style: dashed;
  background: color-mix(in oklab, var(--g) 8%, var(--surface));
}
.node--wife .node__name { font-weight: 600; }

/* مكان الإقامة تحت الاسم — سطر واحد يُقصّ بالنقاط.
   nowrap مقصود: لو التفّ لسطرين لطالت البطاقة حتى تلامس الجيل الذي
   تحتها، والفجوة بين الأجيال ٦٢ بكسل لا تحتمل ذلك على كل بطاقة.
   والهامش السالب يُلصقه بالاسم بدل أن يقف في منتصف المسافة بينه وبين
   السنوات، ويستردّ جزءاً من الارتفاع الذي أضافه. */
.node__where {
  max-width: 100%;
  margin-block-start: -3px;
  /* أصغر من الاسم دائماً وأكبر من السنوات: ٠٫٧٢ تحت اسمٍ ٠٫٨ على البطاقة
     المصوّرة، و٠٫٨٥ تحت اسمٍ ١٫٢ على العارية — فالنسبة محفوظة في الحالتين.
     Always smaller than the name and larger than the years: .72 under a
     .8 name on a photo card, .85 under a 1.2 name on a bare one. */
  font-size: .82rem;
  line-height: 1.3;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* البطاقة العارية اسمها ١٫٢rem، فيكبر تابعه معه ويبقى دونه */
.node--bare .node__where { font-size: 1rem; }

/*
   «توثيق : فلان» فوق البطاقة: من يوثّق هذا الفرع ويُسأل عنه. بحبر الصفحة
   نفسه — أسودَ على الورق، وفاتحاً في الوضع الداكن — لا بلونٍ من دورة
   الأجيال يُقرأ كأنّه جيلٌ آخر. ويمتدّ على الجانبين إن طال الاسم: الفرجة
   خالية والبطاقة ١٣٦ بكسلاً لا تسع سطراً كاملاً.
   The vouching line above the card, in the page's own ink — black on
   paper, light in the dark theme — rather than a colour from the
   generation cycle, which would read as another generation. It overflows
   to both sides when the name is long: the gap is empty and a 136px card
   could not hold the line anyway.
*/
.node__keeper {
  position: absolute;
  /* فوق مرفقة الوصلة لا تحتها — والمقدار يحسبه `keeperLift` في
     tree-view.js من فرجة الصفّ نفسها، فالصفوف القصيرة أضيق.
     Above the connector's elbow, by the amount `keeperLift` works out
     from the row's own gap in tree-view.js. */
  inset-block-end: calc(100% + var(--keeper-lift, 4px));
  inset-inline: 0;
  text-align: center;
  font-size: .72rem;
  font-weight: 700;
  line-height: 1.4;
  color: var(--text);
  white-space: nowrap;
  pointer-events: none;      /* لا تحجب زرّ الإضافة تحتها */
}
.node__keeper[hidden] { display: none; }

.node__meta {
  font-size: .68rem;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  direction: ltr;
}

.node__gen {
  position: absolute;
  inset-block-start: 6px;
  inset-inline-start: 8px;
  font-size: .62rem;
  font-weight: 700;
  color: var(--g);
  opacity: .8;
  font-variant-numeric: tabular-nums;
}

/* زر طيّ/فتح الأبناء / collapse toggle */
.node__toggle {
  position: absolute;
  inset-block-end: -13px;
  inset-inline-start: 50%;
  transform: translateX(50%);
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 26px;
  height: 26px;
  padding: 0 7px;
  border: 2px solid var(--g);
  border-radius: 999px;
  background: var(--surface);
  color: var(--g);
  font-family: var(--font-body);
  font-size: .72rem;
  font-weight: 700;
  cursor: pointer;
  box-shadow: var(--shadow-sm);
  z-index: 2;
  transition: background .15s, color .15s;
  font-variant-numeric: tabular-nums;
}
.node__toggle:hover { background: var(--g); color: var(--surface); }
.node__toggle--open { background: var(--g); color: var(--surface); }
.node__toggle--open:hover { opacity: .82; }

/* زر إضافة ابن (لوحة الإدارة) / add-son button, admin only.
   ظاهر دائماً — الإخفاء حتى المرور بالفأرة يجعل الإضافة صعبة الاكتشاف.
   Always visible: hiding it until hover made adding names undiscoverable. */
.node__add {
  position: absolute;
  inset-block-start: -10px;
  inset-inline-end: -10px;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px; height: 30px;
  border: 2px solid var(--surface);
  border-radius: 50%;
  background: var(--success);
  color: #fff;
  font-size: 1.15rem;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
  box-shadow: var(--shadow-md);
  transition: transform .14s, filter .14s;
  z-index: 4;
}
.node__add:hover { transform: scale(1.15); filter: brightness(1.08); }
.node__add:active { transform: scale(.96); }

/* وضع الرؤية البعيدة: عند التصغير الشديد نرسم خرزات ملوّنة فقط
   Low-detail mode: at far zoom we draw coloured beads instead of
   full cards, so a tree with thousands of people stays smooth. */
.node--far { pointer-events: none; }
.node__bead {
  width: 84px;
  height: 84px;
  margin: 10px auto 0;
  border-radius: 50%;
  overflow: hidden;
  background: var(--g);
  box-shadow: 0 0 0 4px color-mix(in oklab, var(--g) 25%, transparent);
}
/* الصورة تملأ الخرزة وتُقصّ دائرةً، وتبقى الحلقة حولها بلون الجيل: يُعرف
   الوجه ويُعرف جيله معاً. ومن لا صورة له تبقى خرزته لونه وحده — الخلفية
   من تحتها، فلا فراغ.
   The photo fills the bead and is clipped to the circle, and the ring
   around it keeps the generation's colour — face and generation read
   together. Whoever has no photo keeps a bead of plain colour: the
   background beneath shows through, so nothing is left blank. */
.node__bead img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* الوجه يستر لون الجيل، فتشتدّ الحلقة تحته: لونٌ صريح كحلقة الصورة على
   البطاقة. ولولاها لضاع الجيل — الخرزة الخالية تقوله بجسمها كلّه.
   A face covers the generation's colour, so the ring under it hardens into
   the solid one the card's photo wears; without it the generation would be
   lost, since an empty bead says it with its whole body. */
.node--face .node__bead {
  box-shadow: 0 0 0 4px var(--g), 0 0 0 7px color-mix(in oklab, var(--g) 22%, transparent);
}
.node--far.node--selected .node__bead { box-shadow: 0 0 0 10px color-mix(in oklab, var(--g) 45%, transparent); }
.node--far.node--match .node__bead { box-shadow: 0 0 0 10px color-mix(in oklab, var(--accent) 70%, transparent); }

/* الاسم عند التصغير: تحت الخرزة، أو عن يسارها حيث يتّسع الصفّ.
   البطاقة كلّها تختفي عند التصغير، فلو صغُر الاسم معها لصار خيطاً. فهو
   هنا مقسومٌ على `--k` — تكبيرُ اللوحة كما تكتبه tree-view.js — فيُبطل
   تصغيرها ويثبت مقاسه على الشاشة، وتثبت معه الفرجة بينه وبين الدائرة.
   وحدُّه `--label-room`: ما يسعه موضعه، يُحسب في tree-view.js، وما زاد
   قُصّ بالنقاط — من يسار السطر، فالكتابة رتلٌ.

   The name at far zoom: under the bead, or to its left where the row
   leaves room. The whole card is gone at that zoom and a name that shrank
   with it would be a thread, so it is divided by `--k` — the canvas zoom,
   written by tree-view.js — which cancels the shrink and holds its size on
   screen, the gap included. `--label-room` is what its place can hold;
   anything longer is clipped with an ellipsis. */
.node__bead-name {
  position: absolute;
  /* تحت الخرزة: أسفلُها ٩٤ (١٠ + ٨٤) وفرجةٌ بستّة بكسل من الشاشة */
  inset-block-start: calc(94px + 6px / var(--k, 1));
  left: 50%;
  transform: translateX(-50%);
  max-width: var(--label-room, 0px);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-align: center;
  font-family: var(--font-head);
  font-size: calc(11px / var(--k, 1));
  font-weight: 700;
  line-height: 1.25;
  color: var(--text);
  /* هالةٌ بلون الخلفية: الاسم يمرّ فوق وصلات الآباء فلا تقطع حروفه.
     A halo in the background colour: the name crosses the parent links. */
  text-shadow: 0 0 calc(5px / var(--k, 1)) var(--bg),
               0 0 calc(5px / var(--k, 1)) var(--bg);
}

/* وحيث يتّسع الفراغ عن اليسار: الاسم في مستوى الدائرة وأكبرُ قليلاً، فلا
   حدَّ له إلا جارُه. و`right` يقيس من حافّة البطاقة إلى حافّة الدائرة:
   نصفُ العرض زائد نصفُ قطر الخرزة (٤٢ = ٨٤ ÷ ٢)، فينمو الاسم يساراً.
   Where the left margin is wide enough: level with the circle and a little
   larger, bounded only by its neighbour. `right` reaches from the card's
   edge to the circle's — half the width plus the bead's 42px radius — and
   the name grows leftwards from there. */
.node--label-side .node__bead-name {
  inset-block-start: 52px;                       /* منتصف الخرزة: ١٠ + ٤٢ */
  left: auto;
  right: calc(50% + 42px + 10px / var(--k, 1));  /* ١٠ = LABEL_GAP هناك */
  transform: translateY(-50%);
  font-size: calc(13px / var(--k, 1));
  text-align: right;
}

/* ---------- 3. Floating toolbar ---------- */
.toolbar {
  position: absolute;
  /* فوق خطّ الإيماء في آيفون: لولا الحاشية لوقع تحته فلا يُضغط.
     Above the home indicator on an iPhone. */
  inset-block-end: calc(16px + env(safe-area-inset-bottom, 0px));
  inset-inline-start: 50%;
  transform: translateX(50%);
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 5px;
  border-radius: 999px;
  background: color-mix(in oklab, var(--surface) 92%, transparent);
  backdrop-filter: blur(10px);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-lg);
  z-index: 30;
}
:dir(ltr) .toolbar { transform: translateX(-50%); }

.toolbar .btn--icon {
  width: 36px;
  min-height: 36px;
  border: none;
  border-radius: 50%;
  background: transparent;
}
.toolbar .btn--icon:hover:not(:disabled) { background: var(--surface-sunk); }

.toolbar__sep {
  width: 1px;
  height: 22px;
  background: var(--border);
  margin: 0 3px;
}

.toolbar__zoom {
  min-width: 46px;
  text-align: center;
  font-size: .74rem;
  font-weight: 700;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

/* ---------- 4. Generation legend ---------- */
.legend {
  position: absolute;
  inset-block-start: 12px;
  inset-inline-start: 12px;
  max-width: 190px;
  padding: 8px 10px;
  border-radius: var(--r-md);
  background: color-mix(in oklab, var(--surface) 92%, transparent);
  backdrop-filter: blur(10px);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-md);
  z-index: 25;
  font-size: .74rem;
}
.legend__head {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  background: none;
  border: none;
  padding: 0;
  font-family: var(--font-head);
  font-size: .76rem;
  font-weight: 700;
  color: var(--text);
  cursor: pointer;
}
.legend__caret { margin-inline-start: auto; color: var(--text-faint); transition: transform .2s; }
.legend[data-open="false"] .legend__caret { transform: rotate(-90deg); }
.legend[data-open="false"] .legend__list { display: none; }

.legend__list {
  display: flex;
  flex-direction: column;
  gap: 3px;
  margin: 8px 0 0;
  padding: 0;
  list-style: none;
  max-height: 42vh;
  overflow-y: auto;
}
.legend__item {
  display: flex;
  align-items: center;
  gap: 7px;
  color: var(--text-muted);
}
.legend__swatch {
  width: 12px; height: 12px;
  border-radius: 3px;
  background: var(--g);
  flex: none;
}
.legend__count { margin-inline-start: auto; font-variant-numeric: tabular-nums; color: var(--text-faint); }

/* ---------- 5. Search ---------- */
.search { position: relative; flex: none; }

.search__input {
  width: 190px;
  padding-block: 7px;
  padding-inline: 12px 32px;   /* مساحة لأيقونة البحث في نهاية الحقل */
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  background: var(--surface-2);
  font-size: .85rem;
  transition: width .2s, border-color .15s, box-shadow .15s;
}
.search__input:focus {
  outline: none;
  width: 240px;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent) 20%, transparent);
}
.search__icon {
  position: absolute;
  inset-block-start: 50%;
  inset-inline-end: 11px;
  transform: translateY(-50%);
  color: var(--text-faint);
  font-size: .85rem;
  pointer-events: none;
}

.search__results {
  position: absolute;
  inset-block-start: calc(100% + 6px);
  inset-inline-start: 0;
  width: min(300px, 78vw);
  max-height: 320px;
  overflow-y: auto;
  margin: 0;
  padding: 5px;
  list-style: none;
  border-radius: var(--r-md);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  box-shadow: var(--shadow-lg);
  z-index: 60;
}
.search__results:empty { display: none; }

.search__result {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 7px 9px;
  border: none;
  border-radius: var(--r-sm);
  background: none;
  text-align: start;
  cursor: pointer;
}
.search__result:hover, .search__result[aria-selected="true"] { background: var(--surface-sunk); }

.search__avatar {
  width: 30px; height: 30px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--surface-sunk);
  box-shadow: 0 0 0 2px var(--g);
  flex: none;
}
/* الاسم فوق ونسبه تحته — لا بدّ من عمود، فالعنصران span بطبيعتهما سطريان
   فيتدفّقان كنصّ واحد ولا يعمل القصّ بالنقاط عليهما.
   Name above, lineage below: both are spans, so without a column they run
   together as one line and their ellipsis rules never apply. */
.search__text { min-width: 0; display: flex; flex-direction: column; }
.search__name { font-size: .84rem; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.search__lineage { font-size: .7rem; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.search__empty { padding: 12px; text-align: center; color: var(--text-muted); font-size: .82rem; }

/* ---------- 6. Detail / edit panel ---------- */
.panel {
  position: absolute;
  inset-block: 0;
  inset-inline-end: 0;
  width: min(360px, 92vw);
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border-inline-start: 1px solid var(--border);
  box-shadow: var(--shadow-lg);
  z-index: 120;   /* فوق الحجاب (veil = 100) حتى تعمل اللوحة والشجرة فارغة / above the veil so the editor works on an empty tree */
  transform: translateX(-100%);
  transition: transform .26s cubic-bezier(.32, .72, 0, 1);
}
:dir(ltr) .panel { transform: translateX(100%); }
.panel[data-open="true"] { transform: none; }
.panel[hidden] { display: none; }

.panel__head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 12px 14px;
  border-block-end: 1px solid var(--border);
  flex: none;
}
.panel__title { font-size: .95rem; font-weight: 700; margin-inline-end: auto; }

.panel__body { flex: 1; overflow-y: auto; padding: 16px 14px; }
.panel__foot {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 12px 14px;
  border-block-start: 1px solid var(--border);
  background: var(--surface-2);
  flex: none;
}
.panel__foot .btn { flex: 1; }

/* «إضافة أب» يأخذ سطره: نادر الظهور (على الجدّ الأعلى وحده) وحشره
   مع الثلاثة يضيّقها جميعاً. flex-basis لا width حتى يبقى الـ gap سليماً. */
#addFatherBtn, #addWifeBtn { flex-basis: 100%; }

/* رأس البطاقة داخل اللوحة / person hero */
.hero { display: flex; flex-direction: column; align-items: center; gap: 10px; text-align: center; }
.hero__photo {
  width: 128px; height: 128px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--surface-sunk);
  box-shadow: 0 0 0 4px var(--g), 0 0 0 7px color-mix(in oklab, var(--g) 20%, transparent);
}
.hero__photo--blank {
  background: color-mix(in oklab, var(--g) 16%, var(--surface-sunk));
}
/* اسم القبيلة قد يكون سطرين — العناوين التي تعرضه تحترم كسرته.
   والباقي (عنوان اللسان، وصف الصفحة، aria-label) يطوي السطر من نفسه.
   The tribe name may run to two lines, so the headings that show it
   honour the break; titles and labels collapse it by themselves. */
.topbar__title,
.page__title,
.auth__title { white-space: pre-line; }

.hero__name {
  font-family: var(--font-head);
  font-size: 1.4rem;
  font-weight: 700;
  white-space: pre-line;   /* الكسرة المكتوبة تُحترم هنا أيضاً */
}
.hero__badges { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; }
.hero__badge { border-color: color-mix(in oklab, var(--g) 40%, var(--border)); color: var(--g); background: color-mix(in oklab, var(--g) 10%, var(--surface)); }

.detail-block { margin-block-start: 20px; }
.detail-block__label {
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .04em;
  color: var(--text-faint);
  margin-block-end: 6px;
}
.detail-block__value { font-size: .9rem; line-height: 1.75; white-space: pre-wrap; }
/* بلون اللصيقة نفسها، فيُعرف أن الاسمين شيءٌ واحد.
   The tag's own colour, so the two read as one thing. */
.detail-block__value--keeper { color: var(--keeper); font-weight: 700; }

/* «المكلَّف بفرع فلان» — سببٌ للاسم فوقه، لا خبرٌ مستقلّ */
.detail-block__value--soft {
  margin-block-start: 2px;
  font-size: .8rem;
  color: var(--text-muted);
}

/* سلسلة النسب / lineage chain */
.lineage { display: flex; flex-wrap: wrap; gap: 4px 5px; font-size: .88rem; line-height: 1.9; }
.lineage__link {
  border: none;
  background: none;
  padding: 0;
  font: inherit;
  font-weight: 600;
  color: var(--gc, var(--text));
  cursor: pointer;
  border-block-end: 1px dotted currentColor;
}
.lineage__link:hover { opacity: .75; }
.lineage__sep { color: var(--text-faint); }

/* قائمة الأبناء / sons list */
.sons { display: flex; flex-direction: column; gap: 4px; margin: 0; padding: 0; list-style: none; }
.sons__item {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 6px 8px;
  border: none;
  border-radius: var(--r-sm);
  background: none;
  text-align: start;
  cursor: pointer;
  font-size: .86rem;
}
.sons__item:hover { background: var(--surface-sunk); }

/* البنات عددٌ لا اسم: لا يُضغط ولا يتلوّن كأنّه رابطٌ معطوب.
   The daughters row is a count, not a name — it must not look like a
   link that refuses to open. */
.sons__item--count {
  cursor: default;
  color: var(--text-muted);
}
.sons__item--count:hover { background: none; }
.sons__item--count .sons__avatar { box-shadow: 0 0 0 2px var(--border); }
.sons__avatar {
  width: 28px; height: 28px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--surface-sunk);
  box-shadow: 0 0 0 2px var(--g);
  flex: none;
}
.sons__name { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sons__count { font-size: .72rem; color: var(--text-faint); }

/* ---------- 7. Loading veil ---------- */
.veil {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  background: color-mix(in oklab, var(--bg) 78%, transparent);
  backdrop-filter: blur(2px);
  z-index: 100;
  color: var(--text-muted);
  font-size: .88rem;
}
.veil[hidden] { display: none; }

/* ---------- 8. Phones ---------- */
@media (max-width: 720px) {
  .topbar { padding: 8px 12px; gap: 8px; }
  .topbar__title { font-size: .95rem; }
  .topbar__subtitle { display: none; }

  .search__input { width: 130px; }
  .search__input:focus { width: 168px; }

  /* قائمة النتائج مربوطة بحقل بحث ضيّق ومتوسّط، فعرضها الثابت (٣٠٠ بكسل)
     كان يخرج من حافة الشاشة وتُقصّ الأسماء. فتُعلَّق على الشريط نفسه —
     بعرضه إلا هامشين — لا على الحقل.

     وعلى الشريط لا على النافذة برقمٍ ثابت: الشريط على الجوّال سطران —
     الاسم فوق والأدوات تحته — وارتفاعه يتبع طول الاسم، فرقمٌ مكتوب يضع
     القائمة فوق الشريط أو بعيدةً تحته. و`100%` تتبعه كيفما كان.

     Hung off the bar itself rather than the input, and off the bar's own
     height rather than a written number: on a phone the bar is two rows —
     the name above, the controls below — and its height follows the length
     of the name, so any fixed number lands the menu over the bar or well
     below it. */
  .topbar { position: relative; }
  .search { position: static; }
  .search__results {
    inset-block-start: calc(100% + 4px);
    inset-inline: 10px;
    width: auto;
    max-height: 62dvh;
  }

  /* اللوحة تصبح ورقة سفلية على الجوال / panel becomes a bottom sheet */
  .panel {
    inset-block: auto 0;
    inset-inline: 0;
    width: 100%;

    /* السقف من ارتفاع منطقة العمل لا من ارتفاع الشاشة.

       الورقة مرساةٌ إلى أسفل `.workspace`، وفوقها الشريط وأزرار العرض
       وقائمة الفروع — نحو مئتي بكسل. و`82dvh` تُقاس على الشاشة كلّها،
       فمتى طال المحتوى ارتفع أعلى الورقة فوق حافّة الحاوية العليا،
       و`overflow: hidden` يقصّ ما فوقها: يذهب المقبض أوّلاً ثم رأس
       الورقة كلّه — عنوان الجيل وزرّ الإغلاق معاً.

       والمقصوص يزيد كلّما قصرت الشاشة، فيغيب الزرّ في شاشةٍ ويظهر
       ملتصقاً بالحافّة في أطول منها: في ٣٩٣×٨٥٢ تفيض الورقة ٥٢ بكسلاً
       فيختفي الرأس، وفي ٤٣٠×٩٣٢ تفيض ٤٠ فيبقى العنوان بلا فرجةٍ فوقه.

       فالنسبة على الحاوية تمنع الفيضان مهما قصرت الشاشة، و`82dvh` تبقى
       سقفاً على الشاشات الطويلة، والثمانية في المئة تُبقي شريطاً من
       الشجرة ظاهراً فوق الورقة فلا تبدو صفحةً جديدة.

       Capped by the work area, not the screen. The sheet is anchored to
       the bottom of `.workspace`, and the bar, the view buttons and the
       branch picker sit above it — some two hundred pixels. `82dvh`
       counts the whole screen, so a tall card pushes the sheet's top
       past the container's edge and `overflow: hidden` cuts it: first
       the grab handle, then the whole head — the generation title and
       the close button with it. The shorter the screen the more is
       lost, so the button vanished at 393×852 and sat flush against the
       edge at 430×932. The percentage keeps it inside the container at
       any height, `82dvh` still caps tall screens, and the remaining
       eight percent leaves a strip of the tree showing above the sheet
       so it never reads as a new page. */
    max-height: min(82dvh, 92%);
    border-inline-start: none;
    border-block-start: 1px solid var(--border);
    border-start-start-radius: var(--r-xl);
    border-start-end-radius: var(--r-xl);
    transform: translateY(100%);
  }
  :dir(ltr) .panel { transform: translateY(100%); }
  .panel[data-open="true"] { transform: none; }

  .panel::before {
    content: "";
    position: absolute;
    inset-block-start: 5px;
    inset-inline-start: 50%;
    transform: translateX(50%);
    width: 38px; height: 4px;
    border-radius: 999px;
    background: var(--border-strong);
  }
  /* رأس الورقة على الجوّال: حشوٌ ضيّق لا يزيد على ما يلزم.

     ارتفاعه يصنعه زرّ الإغلاق (٣٨ بكسلاً) لا العنوان (خمسة عشر)، وكان
     الحشو يضيف إليه ثلاثين: ثمانية عشر فوق ليخلو المقبضُ مكانَه، واثنا
     عشر تحت — فيبلغ الرأس تسعةً وستين بكسلاً على شاشةٍ ورقتُها بضع مئات.
     فنزل إلى خمسةٍ وخمسين، ومعه رفع المقبض بكسلين ليبقى بين الاثنين فرجة.

     والزرّ نفسه لا يُصغَّر: ٣٨ مقاس الأزرار الأيقونية في الموقع كلّه،
     وهو دون الأربعة والأربعين التي توصي بها آبل للمس أصلاً — فتضييقه
     يشتري بكسلاتٍ بثمن أكثر أزرار الورقة استعمالاً.

     The head's height comes from the close button (38px), not from the
     title (15px), and the padding used to add thirty more. Trimmed to
     55px; the button itself is left alone — 38 is the site's icon size
     and already under Apple's 44pt touch target. */
  .panel__head { padding-block: 10px 6px; }

  .legend { max-width: 150px; inset-block-start: 8px; inset-inline-start: 8px; font-size: .7rem; }

  /* أدوات العرض تلتفّ ولا تخرج من الشاشة: في لوحة الإدارة تسعة عناصر —
     التكبير والطيّ و«＋ جدّ أعلى» — وهي أعرض من شاشة الجوّال، فكانت اللوحة
     المتوسّطة تفيض من الحافّتين معاً ويُقصّ ما على طرفيها.
     The toolbar wraps instead of running off the screen: the editor's has
     nine controls, wider than a phone, and the centred pill used to spill
     past both edges with the outermost buttons clipped. */
  .toolbar {
    inset-block-end: 12px;
    /* التوسيط بهامشين لا بإزاحةٍ نصفية: مع `inset-inline-start: 50%` لا
       يبقى للوحة إلا نصف الشاشة تتّسع فيه، فتلتفّ وهي واسعة. والهامشان
       يتركان لها العرض كلّه فلا تلتفّ إلا إذا ضاق حقاً.
       Centred by two margins rather than a half offset: with a 50% start
       the pill only has half the screen to fit in, so it wraps while there
       is still room. */
    inset-inline: 10px;
    margin-inline: auto;
    inline-size: fit-content;
    max-inline-size: 100%;
    transform: none;
    flex-wrap: wrap;
    justify-content: center;
  }
  :dir(ltr) .toolbar { transform: none; }
}

/* ---------- 9. التلميحة — النبذة عند المرور ---------- */
/*
   خارج `.viewport` فلا تتمدّد مع التكبير، ومكانها يُضبط من الشيفرة
   بـ`transform` لا بـ`inset` — فالمتصفّح ينقلها على بطاقة الرسم وحدها.
   Outside `.viewport` so the zoom never scales it; positioned from JS with
   `transform` rather than `inset`, which keeps the move on the compositor.
*/
.tip {
  position: absolute;
  /* الزاوية العليا اليسرى كـ`.viewport` تماماً: الإحداثيات تُحسب من
     `getBoundingClientRect` وهي فيزيائية، و`inset-inline-start` في صفحة
     من اليمين لليسار تعني اليمين — فتُقذف التلميحة خارج الشاشة.
     Pinned to the physical top-left like `.viewport`: the coordinates come
     from `getBoundingClientRect` and are physical, whereas
     `inset-inline-start` means *right* on an RTL page. */
  top: 0;
  left: 0;
  z-index: 6;
  max-inline-size: min(300px, 78vw);
  padding: 10px 13px 11px;
  border: 1px solid var(--border);
  border-inline-start: 3px solid var(--g, var(--accent));   /* حافّة البدء — يمين الصفحة العربية */
  border-radius: var(--r-md);
  background: var(--surface);
  box-shadow: var(--shadow-lg);
  text-align: start;
  /* لا تعترض المؤشّر: لو اعترضته لخرج عن البطاقة فاختفت هي نفسها.
     Never in the pointer's way — it would count as leaving the card. */
  pointer-events: none;
  opacity: 0;
  transition: opacity .14s ease-out;
}
.tip--in { opacity: 1; }

.tip__who {
  font-family: var(--font-head);
  font-size: .82rem;
  font-weight: 700;
  color: var(--g, var(--accent-strong));
  margin-block-end: 5px;
}

/*
   النبذة قد تطول، فتُحبس في عشرة أسطر وتُقصّ بالنقاط — والكامل في
   بطاقة الشخص. و`pre-line` يحفظ ما كتبه صاحبها من كسرات الأسطر.
   A long note is clamped to ten lines here; the whole of it lives in the
   person's card. `pre-line` keeps the line breaks its author typed.
*/
.tip__note {
  margin: 0;
  font-size: .88rem;
  line-height: 1.7;
  color: var(--text);
  white-space: pre-line;
  overflow: hidden;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 10;
  line-clamp: 10;
}

/* أزرارُ الشجرة المحرَّرة: زرّان متلاصقان في الشريط، المفتوحة منهما
   بلون الموقع — تُقرأ باللون قبل أن تُقرأ بالحرف.
   The edited tree's buttons sit together in the bar, the open one in the
   site's colour so it is read by colour before it is read by letter. */
.bar__trees {
  display: flex;
  align-items: center;
  gap: 6px;
  /* شريطٌ يُسحب لا صفٌّ يفيض: سبعُ شجراتٍ لا تسع الشاشة الخارجية للجهاز
     المطويّ (نحو ٤١٢ بكسلاً)، فكانت تخرج من الحافّتين ويُقصّ أوّلها
     وآخرها. والسحبُ يبقيها كلَّها في متناول الإبهام، ويجرّها القارئ كما
     يجرّ أي شريطِ خياراتٍ على جوّاله.
     A strip that is dragged rather than a row that spills: seven trees do
     not fit the fold's cover screen (about 412px), so the first and last
     were being cut off. Dragging keeps every one of them within a thumb's
     reach. */
  /* وله أرضٌ لا ينزل تحتها: لو تُرك ينكمش بلا حدّ لابتلعت بقيّةُ الشريط
     موضعَه فصار خيطاً لا يُسحب — والأدوات هي التي ينبغي أن تنطوي تحت
     «⋯» حين يضيق المكان، لا الشجرات أن تختفي.
     A floor it cannot go under: left to shrink freely, the rest of the
     bar would swallow it into a thread. When space runs short it is the
     tools that fold under "⋯", not the trees that vanish. */
  /* سطرٌ كامل تحت الشريط: سبعُ شجراتٍ لا تقف بجانب البحث والأدوات على
     شاشةٍ خارجية عرضها ٤١٢، ولا على الداخلية إن طال اسمُ القبيلة. فلها
     سطرها، وفيه تُسحب إن لم تسعه.
     A full line under the bar: seven trees do not stand beside the
     search and the tools on a 412px cover screen, nor on the inner one
     if the tribe's name runs long. They get their own line, and are
     dragged within it when it does not hold them. */
  flex: 1 0 100%;
  order: 9;
  min-inline-size: 0;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  /* ولا شريط تمرير يأكل من ارتفاع الشريط العلويّ */
  scrollbar-width: none;
}
.bar__trees::-webkit-scrollbar { display: none; }
.bar__trees > * { flex: none; scroll-snap-align: center; }

/* ============================================================
   بقيّةُ أدوات الشريط: مطويّةٌ تحت «⋯»، ومنشورةٌ صفّاً حيث يتّسع الشريط
   ============================================================ */

/* والقياس لا العرض: أوّل محاولةٍ طوت الأدوات دون ٥٦٠ بكسل، فما انطوت
   على جوّال صاحب الموقع — لأنّ متصفّحه يعرض الصفحة على عرضٍ منطقيٍّ
   أوسع (طلبُ «موقع سطح المكتب» أو تصغير الصفحة يجعل العرض ٩٨٠ أو
   نحوها)، فلا تنطبق قواعد الجوّال أصلاً بينما الشريط يفيض من الحافّتين.
   فصار الطيّ افتراضاً، ولا يُنشر الصفّ إلا إذا قاس السكربت الشريط
   فوجده يسع — أيّاً كان العرض المكتوب.

   Measured, not guessed: the first attempt folded below 560px and did
   nothing on the owner's phone, whose browser lays the page out at a
   wider logical width (desktop-site mode, or a page-zoom setting, gives
   980px or so), so no phone rule applied while the bar still spilled off
   both edges. Folding is now the default, and the row is unfolded only
   when the script measures the bar and finds it fits — at any width. */
.bar__more { display: block; position: relative; }
.bar__more-btn { display: inline-flex; }

.bar__more-panel {
  display: none;
  position: absolute;
  inset-block-start: calc(100% + 8px);
  inset-inline-end: 0;
  /* داخل سياق الشريط (٧٠) فلا تعلو على الحجاب ولا على لوحة التحرير —
     وهذا ما تفعله قائمةُ البحث نفسها.
     Inside the bar's stacking context (70), so it stays under the veil
     and the editor — exactly as the search dropdown does. */
  z-index: 40;
  flex-direction: column;
  align-items: stretch;
  gap: 8px;
  min-inline-size: 12rem;
  max-block-size: min(70vh, 32rem);
  overflow-y: auto;
  padding: 10px;
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--shadow-lg);
}

/* ولا تُستعمل السمة `hidden` للفتح والإغلاق: في `base.css` قاعدةٌ مقصودة
   `[hidden] { display: none !important }` كُتبت لئلّا يبقى زرٌّ مخفيٌّ
   ظاهراً، وتعليق اللوحة عليها كان يُخفي الصفّ على الحاسب أيضاً.
   The `hidden` attribute is deliberately not used: `base.css` makes it
   `!important` on purpose, and hanging the fold on it hid the desktop
   row too. */
.bar__more-panel.is-open { display: flex; }

/* النموذج داخلها يملأ عرضها، فلا يقف زرّ النسخة الاحتياطية وحده ضيّقاً */
.bar__more-panel .inline-form,
.bar__more-panel .inline-form .btn { display: block; inline-size: 100%; }
.bar__more-panel .btn { text-align: center; }

/* وحيث يتّسع الشريط: صفٌّ فيه كما كان، بلا بطاقةٍ ولا زرّ.
   `--roomy` يضعه السكربت بعد القياس، ويرفعه إذا ضاق الشريط.
   Where the bar has room: the same row as before, no card and no button.
   The script adds `--roomy` after measuring, and takes it back when the
   bar narrows. */
.bar__more--roomy { display: contents; }
.bar__more--roomy .bar__more-btn { display: none; }
.bar__more--roomy .bar__more-panel {
  display: flex;
  position: static;
  flex-direction: row;
  align-items: center;
  flex-wrap: nowrap;
  min-inline-size: 0;
  max-block-size: none;
  overflow: visible;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
}
.bar__more--roomy .bar__more-panel .inline-form,
.bar__more--roomy .bar__more-panel .inline-form .btn { display: inline-flex; inline-size: auto; }
