/* PelegSoft -סצנת הרקע המונפשת: שמיים, עננים, טירה, אדמה, מים, עצים ואבזרים */

.scene {
    position: fixed;
    /* עם viewport-fit=cover ה-viewport מכסה את כל המסך הפיזי כולל אזורי ה-safe-area,
       לכן inset:0 ממלא מקצה-לקצה גם בלנדסקייפ. (הרחבה עם insets שליליים נחתכה ע"י
       overflow-x:hidden של ה-body והשאירה פס לבן בצד -לכן הוסרה.) */
    inset: 0;
    z-index: 0;
    overflow: hidden;
    pointer-events: none;
    transition: filter 2.5s ease;
    /* מרכז הירח -משמש ל"חור" בהחשכת הלילה (חייב להתאים למיקום .moon) */
    --moon-cx: calc(14% + 3.5vw);
    --moon-cy: calc(10% + 3.5vw);
}

/* ---- שכבת הדמויות הקדמיות, מעל התוכן (בקשת המשתמש) ----
   ‎.scene‎ הוא stacking context (‎fixed‎ + ‎z-index: 0‎), ולכן ‎.folk-layer‎
   שבתוכו לעולם לא יעלה מעל ‎.content‎ שב-z:10 - שום ‎z-index‎ על הילד אינו
   עוזר. השכבה הקדמית נפלטת לכן כאחות של ‎.scene‎, מעל התוכן.

   היא **חייבת להיות זהה ל-‎.scene‎ בכל מה שמשפיע על הדמויות**: אותו
   ‎inset‎, אותו ‎overflow‎ (חותך את הדמות בקצה המסך), אותו ‎transition‎ על
   ה-‎filter‎ ואותם משתני ירח (‎--moon-cx/cy‎ נקראים ע"י מסכת ההחשכה
   שבתוכה). מה שאסור לה הוא ‎z-index: 0‎ - זו כל הנקודה.

   ‎pointer-events: none‎ כמו בסצנה: היא פרושה על כל המסך **מעל התוכן**,
   ובלעדיו היא הייתה בולעת כל לחיצה בעמוד. */
.scene-front {
    position: fixed;
    inset: 0;
    z-index: 11;
    overflow: hidden;
    pointer-events: none;
    /* אין כאן ‎filter‎ ולכן גם לא ‎transition: filter‎ -תאורת השעה של הדמויות
       הקדמיות יושבת על ‎.folk‎/הבועה עצמם (ראה "תאורת הדמויות הקדמיות"):
       פילטר על השכבה הענקית הושמט בשקט בספארי של אייפד, והדמויות נותרו
       בהירות בלילה. */
    --moon-cx: calc(14% + 3.5vw);
    --moon-cy: calc(10% + 3.5vw);
}

/* ---- בתיק העבודות הדמויות עוברות **מאחורי** הכרטיסים ----
   הדיווח: "במחשב עם רזולוציה נמוכה הדמויות עולות על תיק העבודות". השכבה
   הקדמית יושבת ב-z:11 מעל ‎.content‎ (z:10) **בכוונה** -דמות שחוצה את הקלף
   מלפנים היא כל הרעיון שלה- אבל בתיק העבודות הכרטיס **הוא** התוכן, ודמות
   שעומדת עליו מסתירה את הפרויקט עצמו.

   **ערך אחד ולא ‎media query‎ לפי גובה.** נמדד ב-1440 שהחפיפה קיימת בשני
   הגבהים -‎123px‎ ב-760 ו-‎5px‎ (ראש הדמות) ב-950- כלומר אין גובה שממנו
   היא מתחילה, רק כמות; וסף שקופץ בין שני מצבים היה מעיף דמויות קדימה
   ואחורה בשינוי גודל חלון. מסביב לכרטיסים ‎.content‎ שקופה, ולכן הדמויות
   ממשיכות להיראות בדיוק כשהיו בכל מקום שאין בו כרטיס.

   9 ולא 1: מעל ‎.scene‎ (z:0) ומתחת ל-‎.content‎ (z:10), ואין בין השניים
   שום ילד אחר של ה-‎body‎. */
body.page-portfolio .scene-front { z-index: 9; }

.layer { position: absolute; inset: 0; }

/* ---- שמיים והרים -"נשימת" זום איטית ---- */
.layer-sky {
    inset: -3%;
    background: var(--scene-img) center bottom / cover no-repeat, linear-gradient(var(--c-sky-light), var(--c-sky));
    animation: sky-breathe var(--sky-zoom-secs) ease-in-out infinite alternate;
    will-change: scale, transform;
}
/* **גרסה מוקטנת של השמיים במובייל.** ‎sky.png‎ הוא 1672px ונמתח על רוחב
   ה**עולם** (‎--zones‎ × 100vw), כלומר במובייל הוא מצויר על ~1170px בעוד
   שהדפדפן מפענח 1672. ‎--scene-img‎ נפלט ב-‎layout.php‎ ולכן הדריסה כאן,
   ב-media query, ולא שם. (‎image-set()‎ היה נקי יותר אבל אינו נתמך אחיד
   בספארי לבחירה לפי רוחב.) */
@media (max-width: 640px) {
    .scene { --scene-img: url('/images/scene/sky-640w.png'); }
}

/* מונפש על ה-property ‎scale‎ ולא על ‎transform‎ במכוון: כך ‎transform‎ נשאר פנוי
   לפאן המצלמה, ו-‎translate‎ נשאר פנוי לפרלקסת העכבר (js/scene.js). שלושתם
   מצטברים בסדר translate → scale → transform. */
@keyframes sky-breathe {
    from { scale: 1; }
    to   { scale: 1.06; }
}

/* ---- עננים נעים ---- */
/* **השכבה אינה נמתחת ואינה זזה עם המצלמה** (‎--k: 0‎, למטה).
   כל עוד היא נעה בפרלקסה, כל מעבר עמוד הזיז את כל העננים בבת אחת -נמדד
   ‎302px‎ בכל ניווט, מול ‎31px‎ שהם נעים בעצמם ב-2.5 שניות. כלומר עשירית
   שנייה של פאן שווה לכעשרים שניות של תנועה טבעית, וזה נקרא (בצדק) כ"העננים
   משנים מיקום ומתחילים מחדש בכל מעבר".
   מרגע ש-‎--k‎ הוא אפס אין מה למתוח: העננים ממשיכים את מסלולם ברציפות
   מוחלטת לאורך כל האתר, והמסלול ‎106vw‎ מכסה בדיוק את המסך. */
.cloud-layer {
    position: absolute;
    left: 0;
    top: 0; bottom: 0;
    width: 100%;
    pointer-events: none;
}
.cloud {
    position: absolute;
    left: 0; /* עיגון פיזי (לא לוגי!) -ב-RTL עיגון לימין מוציא את מסלול התנועה מהמסך */
    width: var(--cw);
    aspect-ratio: 16 / 9;
    background-size: contain;
    background-repeat: no-repeat;
    background-position: center;
    opacity: var(--op, 0.9);
    animation: cloud-drift var(--secs) linear infinite;
    animation-delay: var(--delay, 0s);
    will-change: transform;
}

/* המסלול חוצה בדיוק את המסך. הוא היה שווה-ערך למתיחת השכבה כשזו נעה
   בפרלקסה (אחרת הפלח הימני שלה נשאר ריק -נמדד ‎7%‎ כיסוי בעמודה הימנית),
   אבל מרגע ש-‎--k‎ הוא אפס השכבה היא בדיוק המסך ואין מה למתוח. */
@keyframes cloud-drift {
    from { transform: translateX(calc(-1 * var(--cw) - 6vw)); }
    to   { transform: translateX(106vw); }
}

/* ---- הטירה -יושבת על הקרקע ---- */
.castle-wrap {
    position: absolute;
    bottom: calc(var(--water-h) + var(--ground-h) - var(--castle-sink)); /* בסיס הסלע שוקע באדמה */
    left: 50%;
    width: var(--castle-width);
    transform: translateX(-50%);
    z-index: 3;
    filter: drop-shadow(0 14px 22px rgba(13, 62, 120, 0.30));
    will-change: transform;
}

/* ‎<picture>‎ הוא ‎inline‎ כברירת מחדל ולכן מוסיף שורת-בסיס מתחת לטירה;
   הוא רק עוטף את ה-‎img‎ ואינו אמור להשפיע על הפריסה. */
.castle-pic { display: block; }
.castle { width: 100%; position: relative; z-index: 1; display: block; }

/* ---- דגלי בד על צריחי הטירה ----
   רצועת 4 פריימים של בד-בלבד (cloth-strip.png), מוצמדת למוט הזהב הקיים בתמונת הטירה.
   המוט לא זז -רק הבד מתנופף. left/top מכוונים למחט הצריח (נמדד בפיקסלים). */
/* ---- הדגל: הרצועה זזה ב-‎transform‎, לא ב-‎background-position‎ ----
   **הדיווח: "הדגלים נעצרים כשאני עובד בממשק הניהול."** ‎background-position‎
   אינה תכונה שהקומפוזיטור יודע להריץ (הוא מכיר ‎opacity‎, ‎transform‎,
   ‎translate/scale/rotate‎ ו-‎filter‎ בלבד), ולכן כל פריים של הדגל הוא
   **ציור מחדש** על החוט הראשי. נמדד: ‎2,426‎ ציורים ב-6 שניות, מהם ‎1,237‎
   ציורי שכבה-מלאה -וארבעת הדגלים הם הצרכן העיקרי. כשהציור עמוס (פאנל
   ניהול פתוח ומצויר מעל הסצנה) הם פשוט קופאים.

   אותו כלל בדיוק כבר תועד כאן על פרישת המגילה, ועל אנימציה שנתקעת
   וקופצת: **מה שאינו בקומפוזיטור אינו רץ כשהחוט עמוס.**

   הרצועה היא ארבעה פריימים זה לצד זה. במקום להזיז את נקודת הרקע,
   ‎.cflag‎ הוא **חלון** של פריים אחד (‎overflow: hidden‎) ובתוכו רצועה
   ברוחב ‎400%‎ שנעה ב-‎translateX‎ בצעדים של ‎25%‎ -שקול בדיוק ל-‎0 /
   33.3334 / 66.6667 / 100%‎ שהיו כאן, ורץ בקומפוזיטור. */
.cflag {
    position: absolute;
    z-index: 2;
    aspect-ratio: 230 / 170;
    overflow: hidden;
    /* היסט אופקי קטן לעטיפת המוט, והרמה אנכית -הבד מתנופף מראש המוט, מעל קצה הצריח */
    transform: translate(0%, -72%);
}
.cflag::before {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    /* ‎left‎ פיזי ולא ‎inset-inline-start‎: המסמך RTL, ושם הלוגי הוא ימין -
       אותה מלכודת שכבר תועדה כאן על עיגון העננים. */
    left: 0;
    width: 400%;
    background-image: url('/images/scene/cloth-strip.png');
    background-size: 100% 100%;
    animation: flag-wave var(--flag-frame) step-end infinite;
    /* ‎animation-delay‎ נפלט כ-‎style‎ על ‎.cflag‎ (פיזור בין ארבעת הדגלים),
       והפסאודו אינו יורש אותו מעצמו -‎inherit‎ מפורש מביא אותו. */
    animation-delay: inherit;
}

/* ‎25%‎ מרצועה שרוחבה ‎400%‎ = פריים אחד, כלומר בדיוק אותם ארבעה מצבים
   שהיו ב-‎background-position‎ -רק שזה רץ בקומפוזיטור. */
@keyframes flag-wave {
    0%   { transform: translateX(0); }
    25%  { transform: translateX(-25%); }
    50%  { transform: translateX(-50%); }
    75%  { transform: translateX(-75%); }
    100% { transform: translateX(0); }
}

/* ---- באנרים אדומים על החומה (מכסים את המצוירים בתמונה) ----
   פריים קבוע (בלי החלפת פריימים שמזיזה את הבד הצידה וחושפת את המצויר) -
   רק תנודת רוח עדינה בהטיה, סביב נקודת התלייה העליונה. */
.cbanner {
    position: absolute;
    z-index: 2;
    aspect-ratio: 202 / 294;
    background-image: url('/images/scene/banner-strip.png');
    background-size: 400% 100%;
    background-position: 0% 0%;
    transform-origin: top center;
    animation: banner-sway 4.5s ease-in-out infinite;
}

@keyframes banner-sway {
    0%, 100% { transform: skewX(0deg); }
    30%      { transform: skewX(2deg) scaleY(0.995); }
    55%      { transform: skewX(-0.5deg); }
    75%      { transform: skewX(1.2deg) scaleY(0.997); }
}

/* ---- רצועת האדמה ---- */
.ground {
    position: absolute;
    left: -2vw;
    right: -2vw;
    bottom: var(--water-h);
    height: var(--ground-h);
    z-index: 4;
    /* דשא ירוק בלבד (כמו בתמונה) -כמעט ללא אדמה חומה; רק קצה כהה עדין למטה */
    background:
        linear-gradient(180deg,
            #b9e06b 0%,
            #8ac947 22%,
            #6cb232 50%,
            #55971f 78%,
            #3f7318 92%,
            #315616 100%);
    border-top: 2px solid #d9f18f;
    box-shadow: 0 -6px 18px rgba(13, 62, 120, 0.18);
}

/* שכבת מרקם עדינה על האדמה */
.ground::after {
    content: '';
    position: absolute;
    inset: 0;
    background:
        radial-gradient(14px 5px at 12% 18%, rgba(255,255,255,0.16), transparent 70%),
        radial-gradient(18px 6px at 34% 12%, rgba(255,255,255,0.12), transparent 70%),
        radial-gradient(12px 5px at 58% 20%, rgba(255,255,255,0.14), transparent 70%),
        radial-gradient(16px 6px at 81% 14%, rgba(255,255,255,0.12), transparent 70%),
        radial-gradient(26px 9px at 22% 74%, rgba(30,70,20,0.14), transparent 70%),
        radial-gradient(30px 10px at 66% 82%, rgba(30,70,20,0.14), transparent 70%),
        radial-gradient(22px 8px at 90% 72%, rgba(30,70,20,0.12), transparent 70%);
}

/* ---- מים בתחתית ---- */
.water {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: var(--water-h);
    z-index: 5;
    background: linear-gradient(180deg, #43b5e6 0%, #268fca 45%, #1a6fa7 100%);
}

/* ---- הזרימה היא ‎translate‎ ולא ‎background-position‎ ----
   ‎background-position‎ אינו רץ בקומפוזיטור: כל פריים מצייר מחדש את **כל
   שטח השכבה**, ושלוש שכבות מים כאלה זו מעל זו הן ציור מתמשך. בגלל זה
   הזרימה הייתה **מכובה לגמרי בכל מכשיר מגע** (‎@media (pointer: coarse)‎) -
   נמדד ב-390px ש-‎water-flow‎ כלל אינו ברשימת האנימציות ו-
   ‎background-position-x‎ תקוע על ‎50%‎, כלומר נהר קפוא בטלפון. ומרגע
   שהנהר נמתח על כל העולם (ראה ‎body.pan-on .water‎) אותו ציור התייקר פי
   ארבעה **גם במחשב**, שם הוא כן היה דלוק.

   ‎translate‎ רץ בקומפוזיטור, ולכן עלות הציור שלו לפריים היא **אפס בלי
   קשר לרוחב השכבה** - וזה מה שמאפשר להחזיר את הזרימה לטלפון במקום
   לוותר עליה.

   **הנכס ‎translate‎ ולא ‎transform‎**, כי ‎transform‎ תפוס כאן ע"י
   ‎water-swell‎ (הנשימה האנכית) ו-‎scaleY(1.18)‎ של שכבת האדווה; שני
   הנכסים עצמאיים ומצטברים. אותו תרגיל שכבר משמש כאן את קפיצת הדמויות
   מול ההליכה.

   **הרוחב גדל באריח אחד בדיוק** ‎(100% + --water-tile-w)‎: הגלילה מזיזה
   את האלמנט עצמו, ולכן הקצה הימני חייב עודף בגודל הצעד, אחרת נחשף פס
   ריק בסוף המחזור. הצעד הוא **בדיוק** רוחב אריח, וכיוון שהריצוף מחזורי
   הפריים האחרון זהה לראשון - כלומר הלולאה נסגרת בלי תפר. */
.water-surface {
    position: absolute;
    top: -6px;
    left: 0;
    right: auto;
    width: calc(100% + var(--water-tile-w, 1060px));
    height: 130%;
    /* אריח מראה -ריצוף אופקי ללא תפרים */
    background: url('/images/scene/water-tile.png') repeat-x top / auto 100%;
    opacity: 0.9;
    /* זרימה + "נשימה" אנכית עדינה -חיוּת */
    animation: water-flow 95s linear infinite, water-swell 22s ease-in-out infinite;
    /* ‎--water-tile-w‎: רוחב האריח *בפועל* בפיקסלים, נמדד ונכתב ב-js/scene.js.
       ‎auto 100%‎ סורג את הרוחב לפי גובה המכל (‎--water-h‎, שונה בין נקודות
       שבירה), ולכן קיפריים בפיקסלים קבועים (1060, רוחב הקובץ המקורי) תואם רק
       באקראי - בכל גובה אחר יש קפיצה בכל סיבוב לולאה. ה-fallback הוא הרוחב
       המקורי, לרגע שלפני שה-JS מודד. */
}

/* שכבת גלים שנייה -נעה בכיוון הפוך ובקצב אחר → אדוות/עומק */
.water-surface::before {
    content: '';
    position: absolute;
    inset: 0;
    background: url('/images/scene/water-tile.png') repeat-x top / auto 100%;
    transform: scaleY(1.18);
    transform-origin: top;
    opacity: 0.4;
    animation: water-flow-2 130s linear infinite, water-swell2 28s ease-in-out infinite;
}

/* נצנוצי שמש -פסי אור רכים נעים על פני המים */
.water-surface::after {
    content: '';
    position: absolute;
    inset: 0;
    background-image: linear-gradient(80deg,
        transparent 42%, rgba(255, 255, 255, 0.14) 48%,
        rgba(255, 255, 255, 0.28) 50%, rgba(255, 255, 255, 0.14) 52%, transparent 58%);
    background-size: 340px 100%;
    background-repeat: repeat-x;
    mix-blend-mode: screen;
    opacity: 0.55;
    animation: water-glint 26s linear infinite;
}

/* הכיוונים: שכבת הבסיס נעה שמאלה (‎0 → -אריח‎) והאדווה ימינה. שתיהן
   מתחילות מהקצה שממנו הן זזות, כדי שהעודף שברוחב תמיד יכסה את הכיוון
   שאליו הן הולכות. */
@keyframes water-flow {
    from { translate: 0 0; }
    to   { translate: calc(var(--water-tile-w, 1060px) * -1) 0; }
}
@keyframes water-flow-2 {
    from { translate: calc(var(--water-tile-w, 1060px) * -1) 0; }
    to   { translate: 0 0; } /* כיוון הפוך */
}
@keyframes water-swell {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(2.5px); }
}
@keyframes water-swell2 {
    0%, 100% { transform: scaleY(1.18) translateY(0); }
    50%      { transform: scaleY(1.18) translateY(-2px); }
}
@keyframes water-glint {
    from { translate: -340px 0; }
    to   { translate: 0 0; }
}

/* ============================================================
   פאן מצלמה בין אזורי העולם (body.pan-on)
   ============================================================
   מודל: כל עמוד בתפריט הוא "אזור" בעולם אחד רחב. אזור i יושב במרחק
   i * 100vw מהבית, והמצלמה (--cam) נעה אליו -> תנועה אמיתית בעולם,
   לא החלפת מסך. חזרה לעמוד קודם מחזירה את המצלמה לאותו מקום בדיוק.

   נוסחה אחת לכל השכבות:
       x = (האזור של השכבה - מיקום המצלמה) * צעד * כיוון * מקדם העומק

   --zone נקבע פר-שכבה (רק לשכבות שהתוכן שלהן משתנה בין אזורים);
   --cam נקבע על ה-.scene ומתעדכן בכל ניווט; --k הוא מקדם הפרלקסה:
   קטן = רחוק (כמעט לא זז), גדול מ-1 = קדמת הבמה (זז מהר מהמצלמה).

   RTL: כיוון = -1, כלומר אזור 1 יושב משמאל לאזור 0. מעבר לעמוד הבא
   בתפריט מזיז את המצלמה שמאלה -> העולם מחליק ימינה, בדיוק כמו
   שהחלקת אצבע ימינה מביאה את העמוד הבא.

   מלכודת שנמנעה: כמעט כל שכבה בסצנה כבר משתמשת ב-transform (אנימציית
   sky-breathe, cloud-drift, מרכוז הטירה) או ב-translate (פרלקסת העכבר
   ב-js/scene.js כותבת style.translate על כל [data-depth]). לכן הפאן
   מחלק ביניהם: מי שה-transform שלו תפוס מקבל את הפאן ב-translate
   ולהיפך, ואנימציית השמיים הומרה ל-scale property כדי לפנות transform. */

body.pan-on .scene,
body.pan-on .scene-front {
    --cam: 0;
    --pan-dir: -1;
    --pan-step: 100vw;
    /* --pan-ms-set נפלט inline על ה-.scene מהניהול; ה-media queries משנים את
       --pan-ms הנגזר ממנו, כי inline לא ניתן לדריסה מגיליון הסגנונות. */
    --pan-ms: var(--pan-ms-set, 1400ms);
    /* התאוצה: ease-in-out סימטרי. easing מרוכז-התחלה (ease-out) מסיים 85%
       מהתנועה ברבע הזמן ואז זוחל -נראה כקפיצה, לא כתנועת מצלמה. */
    --pan-ease: cubic-bezier(.45, .02, .25, 1);
}

/* ה-CSS כאן קובע רק את *המצב* (איפה כל שכבה נמצאת כרגע); את *התנועה* בין
   מצבים מריץ ה-JS ב-Web Animations על ערכי transform קונקרטיים. הסיבה נמדדה:
   transition על transform שנגזר מ-var() מחייב את הדפדפן לפתור את ה-calc מחדש
   בכל פריים ב-main-thread, ובסצנה הזאת (אנימציות רבות, blend-modes, drop-shadow)
   זה ירד ל-7fps ולתזוזה ראשונה רק אחרי 800ms. WAAPI על ערך קונקרטי רץ
   ב-compositor -אותו פתרון שכבר עבד כאן לעמעום ה-FADE. */

body.pan-on .ground-back,
body.pan-on .ground-front,
body.pan-on .castle-wrap,
body.pan-on .castle-doors,
body.pan-on .castle-lights {
    --pan-x: calc((var(--zone, 0) - var(--cam, 0)) * var(--pan-step) * var(--pan-dir) * var(--k, 1));
    transform: translateX(var(--pan-x));
}

/* הטירה ואורות החלונות כבר ממורכזים ב--50% -הפאן מתווסף למרכוז ולא דורס אותו */
body.pan-on .castle-wrap,
body.pan-on .castle-doors,
body.pan-on .castle-lights {
    transform: translateX(calc(-50% + var(--pan-x)));
}

/* (כאן ישבה הסתרת האזורים שאינם פעילים -‎zone-hidden‎. היא הוסרה: הסתרה/חשיפה
   היא היפוך מיידי, ולכן העץ השכן היה **קופץ** לתוך התמונה ברגע שהנסיעה יוצאת
   לדרך ונעלם בבת-אחת בסיומה. הדליפה עצמה נפתרה גאומטרית -ראה --k של ground-back
   למטה- ולכן אין עוד מה להסתיר.) */

/* שמיים ועננים: זהים בכל אזור (--zone נשאר 0), רק פרלקסה רחוקה.
   העננים מקבלים את הפאן ב-translate כי ה-transform שלהם תפוס ע"י cloud-drift. */
body.pan-on .layer-sky {
    --k: 0.045;
    transform: translateX(calc((0 - var(--cam, 0)) * var(--pan-step) * var(--pan-dir) * var(--k)));
}
/* ---- העננים אינם נעים עם המצלמה (בקשת המשתמש: המשכיות מלאה) ----
   ‎--k: 0‎, ולכן ‎translate‎ נשאר אפס בכל אזור והשכבה **אינה תלויה
   ב-‎--cam‎ כלל**.

   **למה, ונמדד:** ב-‎--k: 0.18‎ כל מעבר עמוד הזיז את כל העננים ‎302px‎ בבת
   אחת, בעוד שבתנועתם הטבעית הם עוברים ‎31px‎ ב-2.5 שניות. כלומר ניווט אחד
   שווה לכעשרים שניות של תנועה, וזה נקרא כ"העננים משנים מיקום ומתחילים
   מחדש בכל מעבר". אין דרך לרכך את זה בערך ביניים -כל ‎--k‎ שאינו אפס מזיז
   אותם בקפיצה, כי הפאן קצר בהרבה מקצב הענן.

   **תופעת לוואי מכוונת:** אין יותר פרלקסת-עומק לעננים, כלומר הם אינם
   נראים רחוקים יותר מהטירה בזמן הנסיעה. זו ההחלטה -רציפות על פני עומק.
   התמורה: ‎panMove‎ ב-‎js/scene.js‎ מדלג על שכבה שההפרש שלה אפס, ולכן
   השכבה גם יורדת מרשימת האנימציות של כל ניווט. */
body.pan-on .cloud-layer {
    --k: 0;
    translate: 0 0;
}
/* הכוכבים/ניצוצות נעים עם המצלמה כמו השמיים (אותו --k) -כך הם מוצמדים לכיפת
   השמיים ולא נעולים למסך. הפאן על השכבה; הנצנוץ (transform) נשאר על כל ניצוץ. */
body.pan-on .spark-layer {
    --k: 0.045;
    transform: translateX(calc((0 - var(--cam, 0)) * var(--pan-step) * var(--pan-dir) * var(--k)));
}

/* ---- הנהר נמשך על פני העולם ונוסע עם המצלמה (בקשת המשתמש) ----
   עד כה ‎.water‎ הייתה מעוגנת ל**מסך** (‎left:0; right:0‎, בלי ‎--k‎ ובלי
   ‎transform‎), ולכן בזמן שכל העולם נוסע הנהר לבדו עמד -נקרא כפס מים
   מודבק לתחתית הצג ולא כנהר אחד שהמצלמה נוסעת לאורכו. היום זו שכבה
   **רציפה** הפרושה על כל העולם, בדיוק כמו ‎.birds‎ ו-‎.folk-layer‎: אין לה
   ‎--zone‎, אין לה עותק פר-אזור, והיא נעה כשכבת-עולם.

   **‎--k‎ חייב להיות בדיוק 1, וזו גאומטריה ולא טעם.** שכבה שרוחבה
   ‎zones * step‎ מכסה את המסך בקצה הרחוק **בדיוק** (הקצה השמאלי נוחת על
   ‎x=0‎ כש-‎cam‎ מקסימלי). ‎k > 1‎ מזיז אותה מהר מהעולם, ואז היא נגמרת
   **בתוך** התצוגה -נהר שנקטע באמצע המסך. ‎k < 1‎ בטוח מבחינת כיסוי, אבל
   אז המים נגררים עם המצלמה, כלומר בדיוק מה שבאנו לתקן. אותו נימוק שכבר
   תועד כאן על ‎--k: 1‎ של ‎ground-back‎, רק מהצד השני של אי-השוויון.

   ה-‎2vw‎ בכל צד הם שוליים נגד עיגול תת-פיקסלי בקצה בדיוק, כמו
   ‎left/right: -2vw‎ של ‎.ground‎ -כאן פער של פיקסל בודד נראה כפס ריק
   בשולי המסך.

   שלוש שכבות הרקע של ‎.water-surface‎ הן ‎repeat-x‎ עם ‎background-size‎
   קבוע (‎auto 100%‎ לאריח, ‎340px‎ לנצנוצים), ולכן מתיחת האלמנט רק מוסיפה
   חזרות ואינה משנה את המראה; ו-‎--water-tile-w‎ נמדד מה**גובה** בלבד
   (‎js/scene.js‎), אז גם הוא אינו מושפע. */
body.pan-on .water {
    left: calc((var(--zones, 1) - 1) * -1 * var(--pan-step) - 2vw);
    right: auto;
    width: calc(var(--zones, 1) * var(--pan-step) + 4vw);
    --k: 1;
    transform: translateX(calc((0 - var(--cam, 0)) * var(--pan-step) * var(--pan-dir) * var(--k)));
}

/* ---- בזמן התנועה בלבד (.scene.is-panning) ----
   drop-shadow על אלמנט גדול נמדד בפרויקט הזה כחשוד המרכזי לקצב הפריימים
   (ביטולו: פי 3.3), והטירה היא בדיוק המקרה -אלמנט ענק שחוצה את כל המסך.
   מוסר לזמן הנסיעה בלבד ומוחזר בסופה, כך שהמראה במנוחה לא משתנה.

   הדגל יושב על ה-.scene ולא על ה-body במכוון: ניווט ה-SPA מחליף את
   body.className באמצע הנסיעה, וכל מחלקה זמנית שם נמחקת. כשזה קרה, הצל
   של הטירה חזר בדיוק תוך כדי תנועה -וזה נראה כהבזק של טירה כפולה/צללית.
   ה-.scene שורד את הניווט, ולכן הדגל עליו יציב. */
/* **הצל יורד לאפס, אבל הפילטר נשאר בתוקף.** ‎filter: none‎ אינו רק מוותר על
   הצל -הוא **מבטל את הפילטר עצמו**, ולכן האלמנט מבנה מחדש את הרסטור שלו
   פעמיים בכל ניווט: ברגע הלחיצה וברגע שהנסיעה נגמרת. על אלמנט של 135vw
   שני הפריימים האלה נראים, וזו הצורה של "הבהוב" שדווח באייפון.
   ‎drop-shadow(0 0 0 transparent)‎ נראה זהה לחלוטין (אין צל), אבל האלמנט
   ממשיך לשאת פילטר לאורך כל הדרך - אין הדלקה ואין כיבוי.
   **נמדד** (12 מעברים לכל חלופה, CPU x5, סבבים משולבים כדי לבטל סחיפה):
   ‎filter: none‎ נתן **18.3%** פריימים מעל 32ms, והצל השקוף **12.1%** -
   כלומר הוא גם נקי יותר מבחינה חזותית וגם **מהיר יותר**. */
.scene.is-panning .castle-wrap { filter: drop-shadow(0 0 0 transparent); }
/* **‎will-change‎ הוסר -הוא לא הוסיף שכבה אחת, רק קידום וביטול-קידום.**
   כאן ישבה בקשת שכבת-קומפוזיטור לשמונה קבוצות שכבות ענק, שנוספה ברגע הלחיצה
   ונלקחה בסוף הנסיעה. **נמדד ב-CDP (‎LayerTree‎, 390px, נסיעה מלאה):** 106
   שכבות במנוחה ו-110 בשיא - **זהה בדיוק** עם הכלל ובלעדיו. הסיבה זהה לזו
   שכבר תועדה כאן על שקופיות הקרוסלה: אלמנט שרצה עליו אנימציית ‎transform‎
   מקודם ממילא, ולכן הרמז אינו מוסיף דבר.
   מה שהוא כן עשה: הכריח קידום של שמונה שכבות בפריים של הלחיצה, וביטול-קידום
   בפריים של הסיום. שכבה שמקודמת או מבוטלת נצבעת מחדש, ובמכשיר חלש הפריים
   הזה נראה - וזה בדיוק "שני הבהובים קצרים ואז המצלמה זזה" שדווח באייפון. */

/* ---- הטירה נגזרת לחלון האזור שלה ----
   כשהטירה רחבה מהמסך (‎--castle-width‎ גדול מ-100vw; במובייל 135vw בכוונה, ובדסקטופ
   אם מגדילים אותו) היא בולטת ‎(width-100vw)/2‎ מעבר לגבול האזור גם כש---k=1 -ואז
   בזמן פאן היא **חופפת** לטירה של האזור השכן, וזה נקרא כטירה כפולה.

   הגזירה **סטטית**: היא אינה תלויה ב---cam ולכן אינה מהבהבת ואינה צריכה אנימציה.
   כשהטירה צרה מהמסך (ברירת המחדל בדסקטופ) ההיסט מתאפס ולא נגזר דבר, ובאזור הפעיל
   הגבול חופף בדיוק לשולי המסך -כלומר לא יורד שום דבר שהיה נראה ממילא. */
body.pan-on .castle-wrap,
body.pan-on .castle-doors,
body.pan-on .castle-lights {
    --castle-bleed: max(0px, calc((var(--castle-width) - 100vw) / 2));
    clip-path: inset(-100% var(--castle-bleed) -100% var(--castle-bleed));
}

/* מקדמי העומק. הקרקע הרחוקה זזה לאט מהקרובה -> תחושת עומק אמיתית.

   **הקרקע הרחוקה חייבת --k: 1, ולא פחות.** ‎ground-back‎ היא שכבה **פר-אזור**:
   לכל אזור תוכן משלה. מקדם קטן מ-1 (היה 0.85) מזיז אותה רק 85vw לכל צעד-אזור,
   כלומר האזור השכן נעצר 15vw **בתוך** התצוגה הפעילה: עץ שיושב ב-90% של האזור
   השכן נוחת ב-x≈+5vw אצלנו -"עץ כפול" שמופיע ונעלם בתנועה. ההסתרה שנוסתה כאן
   (zone-hidden) רק החליפה את הדליפה בקפיצה: העץ קפץ פנימה כשהנסיעה יצאה לדרך.
   ב---k: 1 האזור השכן מוזז בדיוק ברוחב מסך ולכן נשאר מחוץ לתצוגה, ומה שנראה
   בקצה הוא רק מה ש**באמת** צמוד לו בעולם -כלומר תנועה רציפה בלי קפיצות.
   העומק נשמר בשכבות שאינן פר-אזור (שמיים 0.045, עננים 0.18) וביחס לחזית (1.15),
   שנעה מהר יותר מהטירה. שכבה עם --k>1 אינה דולפת (היא יוצאת במלואה). */
body.pan-on .ground-back   { --k: 1; }
body.pan-on .castle-wrap,
body.pan-on .castle-doors,
body.pan-on .castle-lights { --k: 1; }
body.pan-on .ground-front  { --k: 1.15; }
/* הדמויות "מסתובבות בעולם" ולא שייכות לאזור, אז המצב שלהן תמיד "במרכז".
   בזמן המעבר ה-JS מנפיש אותן החוצה-ובחזרה (עם עמעום שמכסה על המיקום מחדש)
   ומשאיר אותן בסוף בדיוק היכן שה-CSS מעמיד אותן -ללא מצב שצריך לנקות. */
body.pan-on .folk-layer    { --k: 1; }

/* ---- רציפות הליכת הדמויות (body.folk-flow) ----
   במקום שהדמויות ייסחפו החוצה ויתמקמו מחדש בכל מעבר, השכבה שלהן נמתחת על פני
   כל העולם והן ממשיכות ללכת דרכו. השכבה נעה עם המצלמה כשכבת-עולם רגילה (k=1),
   כך שדמות שהולכת נשארת באותו מקום בעולם גם כשהמצלמה עוברת אזור.
   ב-RTL אזור i יושב שמאלה מהבית, ולכן העולם משתרע שמאלה: השכבה מתחילה
   ב-‎-(zones-1)*step‎ ורוחבה zones*step. */
body.folk-flow .folk-layer {
    left: calc((var(--zones, 1) - 1) * -1 * var(--pan-step, 100vw));
    right: auto;
    width: calc(var(--zones, 1) * var(--pan-step, 100vw));
    /* השכבה **מעוגנת לעולם** והמצלמה עוברת עליה: היא נעה בדיוק כמו שכבת-עולם
       (‎--zone‎ 0, k=1), כך שדמות שנמצאת באזור מסוים נשארת שם ויוצאת מהמסך כשעוזבים
       אותו. בלי ה-transform הזה השכבה הייתה מעוגנת ל*מסך* -הדמויות נגררות עם
       המצלמה, ובכל עמוד רואים בדיוק את אותן דמויות. */
    --pan-x: calc((0 - var(--cam, 0)) * var(--pan-step, 100vw) * var(--pan-dir, -1) * var(--k, 1));
    transform: translateX(var(--pan-x));
}
/* מסלול ההליכה נמתח יחד עם העולם -אבל החישוב נעשה ב-PHP (שם ידוע מספר
   הדמויות בפועל) ונפלט על ‎.folk-layer‎, ולכן אין כאן עקיפה. */

/* השמיים נמתחים הצידה כדי שהפרלקסה לא תחשוף את קצה התמונה.
   הרוחב הדרוש נגזר ממספר האזורים ולא קבוע: באזור האחרון השמיים כבר זזו
   (מספר האזורים - 1) * צעד * --k, וערך קבוע חשף פס שמיים ריק בקצה המסך
   ברגע שנוסף עמוד שלישי לתפריט. --zones נפלט מ-PHP לפי התפריט בפועל.
   inset האנכי (-3%) נשמר -הוא מה שמאפשר לזום ה"נשימה" לא לחשוף שוליים. */
body.pan-on .layer-sky {
    inset: -3% calc(-1 * ((max(var(--zones, 1), 1) - 1) * 100% * var(--k) + 3%));
}

/* צמצום תנועה / כיבוי אנימציות: המצלמה מגיעה לאזור מיד, בלי תנועה.
   מבוטל דרך transition-duration ולא דרך --pan-ms, כי --pan-ms נפלט inline
   על ה-.scene (מהניהול) ו-inline גובר על כל כלל גיליון. */
@media (prefers-reduced-motion: reduce) {
    body.pan-on .scene, body.pan-on .scene * { transition-duration: 0ms !important; }
}
body.no-anim .scene, body.no-anim .scene * { transition-duration: 0ms !important; }

/* ---- מובייל וטאבלט ----
   הסצנה כאן מציירת ב-2-3fps כשיש drop-shadow על אלמנטים גדולים, ותנועה של
   מסך שלם היא בדיוק המקרה שחושף את זה. החיסכון מגיע מ**משך** קצר יותר ומפרלקסה
   מרוסנת בשכבות שאינן פר-אזור -לא מהגאומטריה.

   **‎--pan-step‎ חייב להישאר 100vw בכל רוחב מסך. אסור לקצר אותו.**
   הוא לא "מרחק נסיעה" אלא **רוחב אזור בעולם**, ושלושה דברים נשענים על כך
   שאזור = בדיוק מסך אחד:
   1. ‎.folk-layer‎ נמתחת ל-‎zones * pan-step‎, ו-PHP מחשב את מסלול ההליכה ואת
      עוגן מסך-הבית של שומרי הלילה ב-‎(zones-1) * 100vw‎. ‎--pan-step: 80vw‎ הזיז
      את העוגן ב-60vw, ו**נקודת המחזור של השומר נחתה באמצע מסך הטירה**:
      נמדד באייפון (390px) -שומר קופץ מ-x=649 ל-x=212, ושני קופץ מ-x=216 ל-x=653.
      זה בדיוק "דמות שנעלמת ומופיעה פתאום", והוא לא קיים בדסקטופ.
   2. אזור שכן מוזז ב-‎pan-step * k‎. כל ערך קטן מרוחב מסך מותיר את השכן **בתוך**
      התצוגה: ב-80vw דלפו 20vw מהאזור הסמוך גם כש---k=1 -כלומר "עץ שמופיע/נעלם
      כשהמצלמה זזה", שוב במובייל בלבד.
   3. ‎--k‎ של שכבה פר-אזור חייב להיות 1 מאותה סיבה (ראה ההערה אצל ground-back). */
@media (max-width: 1024px) {
    body.pan-on .ground-back  { --k: 1; }     /* היה 0.9 -> 10vw דליפה מהאזור השכן */
    body.pan-on .ground-front { --k: 1.08; }  /* גדול מ-1 -יוצא במלואו, לא דולף */
    body.pan-on .cloud-layer  { --k: 0.12; }  /* שכבה רציפה, לא פר-אזור -מותר */
}
/* **‎(pointer: coarse)‎ ולא רק ‎640px‎ -וזה תיקון, לא הרחבה שרירותית.**
   מה שהכללים כאן מורידים הוא עומס **קומפוזיטור**, וזה תלוי במכשיר ולא ברוחב.
   iPad בלנדסקייפ הוא 1194px - מעל כל סף "טאבלט" בקובץ הזה - ולכן הוא קיבל את
   נסיעת המצלמה המלאה של המחשב עם GPU של נייד. **נמדד:** זיכרון שכבות
   הקומפוזיטור שם ‎148.7MB‎ מול ‎48.5MB‎ באייפון, ותמונות ‎25.3MP‎ מול ‎6.1MP‎.
   הדיווח היה "המצלמה בין העמודים זזה לאט". */
@media (max-width: 640px), (pointer: coarse) {
    body.pan-on .scene,
    body.pan-on .scene-front { --pan-ms: calc(var(--pan-ms-set, 1400ms) * 0.78); }
    /* ---- שתי שכבות מסך-מלא יורדות מהנסיעה במובייל ----
       **‎--k: 0‎ ולא "קצת":** כשהמיקום אינו תלוי ב-‎--cam‎ אין הפרש בין המצב
       הישן לחדש, ולכן ‎panMove‎ מדלג עליהן לגמרי - לא נוצרת אנימציה, ואין גם
       קפיצה בסוף. ערך קטן-אך-לא-אפס היה משאיר אותן בתוך העומס.

       **מה בדיוק מפסידים:** נמדד באייפון 390px לאורך נסיעה שלמה - השמיים זזו
       **9.8px** והניצוצות **17.6px**. זו תנועה שאי אפשר לראות לאורך 1.4 שניות,
       ובתמורה יורדות **שתיים מתוך שבע** השכבות המונפשות - ושתיהן בגודל מסך
       מלא (תמונת השמיים היא הגדולה בסצנה).

       זה נכתב אחרי שהמשתמש בידד את זה: ההבהוב באייפון קורה **רק** כשפאן
       המצלמה דלוק, כלומר העלות היא במספר השכבות שזזות בו-זמנית ולא במה
       שמצויר. העומק נשמר בשכבות שכן נעות (קרקע, טירה, ציפורים). */
    body.pan-on .layer-sky   { --k: 0; }
    body.pan-on .spark-layer { --k: 0; }
}

/* ---- אובייקטים על הקרקע ---- */
.ground-layer {
    position: absolute;
    inset: 0;
    will-change: transform;
}

.ground-back  { z-index: 2; }
.ground-front { z-index: 6; }

.obj {
    position: absolute;
    bottom: calc(var(--water-h) + var(--ground-h) - 2.2vh + var(--dy, 0vh));
    aspect-ratio: 1;
    background-size: contain;
    background-repeat: no-repeat;
    background-position: center bottom;
}

.obj.is-flipped { transform: scaleX(-1); }

/* ---- דגל+עשן מונפשים על גבי בתי הרקע ----
   שתי שכבות-בת שנגזרו מהפיקסלים המקוריים של כל בית (tools/process-house-fx.php)
   וממוקמות **בדיוק** עליהם - לא מחיקה, רק ציור מונפש מעל הסטטי, כמו הבאנרים
   על חומת הטירה. house3/housebig אין להם 'flag' בנתונים (אין דגל מצויר,
   רק כדור-זהב/דרקון) ולכן אין .house-fx-flag בפלט עבורם כלל. */
.house-fx {
    position: absolute;
    background-size: contain;
    background-repeat: no-repeat;
    background-position: center;
    pointer-events: none;
}
/* הספרייט הוא **בד בלבד** (tools/process-house-fx.php גוזר וזורק את המוט/
   הכדור לפי קפיצת צבע + שולי ביטחון) - המוט נשאר סטטי בתמונת הבית עצמה.
   הציר לכן בקצה **השמאלי** של הבד (שם הוא נאחז במוט), ו-skewY (לא skewX)
   מטה את הקצוות העליון/תחתון בזה מזה סביב אותו ציר - זה מה שנקרא כרפרוף,
   בעוד skewX של כל המלבן קורא כנדנוד קשיח (וזה בדיוק היה התסמין שדווח:
   "זז אבל לא כמו דגל" - כי הגרסה הקודמת הטתה גם את שארית המוט שבתוך הגזירה). */
.house-fx-flag {
    transform-origin: 0% 50%;
    animation: house-flag-wave 2.4s ease-in-out infinite;
    /* **היסט קטן שעוטף את המוט** -בדיוק מה שכבר עושה דגל הטירה
       (‎translateX(-3%)‎ שם). נמדד שההרכבה בסיס+ספרייט זהה למקור פיקסל-
       בפיקסל, ושהאנימציה **אינה** פותחת את התפר (המרווח עם אנימציה קטן או
       שווה למרווח בלעדיה בכל שלבי המחזור) -כלומר הרווח הדק שנראה יושב
       בציור עצמו, בין המוט לקצה הבד. אחוז ולא פיקסלים: הוא נפתר מול רוחב
       ‎.obj‎ ולכן נשאר יחסי בכל גודל בית ובכל רוחב מסך.
       ‎margin-left‎ פיזי ולא ‎margin-inline-start‎ -ב-RTL הלוגי היה מזיז ימינה. */
    margin-left: -0.55%;
}
@keyframes house-flag-wave {
    0%, 100% { transform: skewY(0deg) scaleX(1); }
    25%      { transform: skewY(5deg) scaleX(0.95); }
    50%      { transform: skewY(0deg) scaleX(0.9); }
    75%      { transform: skewY(-5deg) scaleX(0.95); }
}
/* העשן הוא **מיכל** ריק (בלי רקע משלו) ובתוכו שני תמרים שעולים לסירוגין.
   ‎overflow: visible‎ במפורש: התמר עולה הרבה מעל גבולות המיכל, שהוא רק
   קופסת העיגון של פתח הארובה. */
.house-fx-smoke { overflow: visible; background: none; }
.house-puff {
    position: absolute; inset: 0;
    background-image: var(--puff);
    background-size: contain;
    background-repeat: no-repeat;
    background-position: center bottom;
    /* מפתח הארובה כלפי מעלה. **הציר האופקי נמדד ואינו 50%**: הענן המצויר
       נסחף הצידה בעלייתו, ולכן מרכז התיבה העוטפת יושב באמצע הסחף ולא על
       הפתח (נמדד 84% ב-‎housebig‎). ‎--puff-ox‎ מגיע פר-בית מ-PHP. */
    transform-origin: var(--puff-ox, 50%) 100%;
    animation: house-smoke-rise var(--puff-dur, 5.2s) linear infinite;
    animation-delay: var(--puff-delay, 0s);
}
/* העותקים בהיסט של שליש מחזור -כך תמיד יש עשן **וגם** תמיד יש תמר שיוצא
   מהארובה: בהיסט של חצי נמדד רגע שבו האחד באמצע העלייה והשני עדיין
   בשקיפות 0, והתמר שבאוויר נראה תלוש מהבית. */
.house-puff-b { animation-delay: calc(var(--puff-delay, 0s) - var(--puff-dur, 5.2s) / 3); }
.house-puff-c { animation-delay: calc(var(--puff-delay, 0s) - var(--puff-dur, 5.2s) * 2 / 3); }
@keyframes house-smoke-rise {
    0%   { transform: translateY(6%)   scale(0.45); opacity: 0; }
    18%  { transform: translateY(0%)   scale(0.62); opacity: 0.85; }
    60%  { transform: translateY(-46%) scale(0.95); opacity: 0.5; }
    100% { transform: translateY(-96%) scale(1.35); opacity: 0; }
}
/* ---- מפל מים מונפש ----
   **המסכה על ההורה, התנועה על הילד.** ‎mask-image‎ חל על כל תת-העץ, ולכן
   הילד שמחליק מטה נגזר בדיוק בגבול המים המצוירים; מלבן עם ‎border-radius‎
   היה משאיר זרימה על הסלע שלצידו - אותו כלל בדיוק שנקנה כאן באורות
   חלונות הטירה ("הצורה נצרבת מהציור, לא מקורבת"). המסכה נוצרת ב-
   ‎tools/process-house-water.php‎ מהפיקסלים של הבית עצמו.

   **‎translate‎ ולא ‎background-position‎:** השני מצייר מחדש את כל השכבה
   בכל פריים, וזה בדיוק מה שהכריח בשעתו לכבות את זרימת הים בטלפון.

   הציור הסטטי **נשאר מתחת**: כאן השכבה מוסיפה תנועה ואינה מחליפה את
   המצויר, ולכן אין צורך למחוק ממנו דבר (בשונה מהדגל והעשן). */
.house-fx-fall {
    background: none;
    overflow: hidden;
    -webkit-mask-size: 100% 100%;
    mask-size: 100% 100%;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    pointer-events: none;
}
/* גובה כפול, ועוגן מעל ההורה: ההחלקה מטה חייבת "מלאי" מלמעלה, אחרת
   נחשף קצה ריק בסוף כל מחזור. */
.house-fall-flow {
    position: absolute;
    left: 0; right: 0;
    top: -100%;
    height: 200%;
    /* מחזור = 12% מגובה הילד, ולכן ‎translate: 0 12%‎ הוא **בדיוק** מחזור
       אחד והלולאה נסגרת בלי תפר. אחוזים ולא פיקסלים -המפל משנה גודל עם
       הבית ועם רוחב המסך. */
    /* **פס לבן על מים לבנים אינו נראה.** מה שקורא כזרימה הוא ה*צל* שבין
       הסילונים, ולכן לכל מחזור יש גם קצה בהיר וגם רצועה כחלחלה שמכהה את
       המים המצוירים מתחת. */
    /* **הקצה מרוכך, וזה מה שקורא כחלק.** התנועה עצמה אחידה לחלוטין -נמדד
       ‎0.117px‎ לפריים בחציון ואפס פריימים מעל ‎20ms‎ ב-240 דגימות- אבל
       קפיצת אטימות מ-‎0‎ ל-‎0.85‎ בתוך ‎2.5%‎ היא קצה חד שנע בתת-פיקסל,
       וזה נקרא כריצוד. הרמפה נפרסת על שמונה עצירות ובשיא נמוך בהרבה. */
    background:
        repeating-linear-gradient(180deg,
            rgba(255, 255, 255, 0.00) 0%,
            rgba(255, 255, 255, 0.30) 1.6%,
            rgba(255, 255, 255, 0.68) 3.2%,
            rgba(214, 238, 250, 0.80) 4.6%,
            rgba(158, 210, 238, 0.62) 6.4%,
            rgba(110, 178, 216, 0.48) 8.2%,
            rgba(140, 200, 232, 0.22) 10%,
            rgba(255, 255, 255, 0.00) 12%);
    animation: house-fall-run var(--fall-dur, 1.7s) linear infinite;
}
/* שכבה שנייה, איטית ורחבה יותר -בלעדיה הזרימה נקראת כפס אחד שנע ולא
   כמים. אותו טעם של שתי שכבות האדוות בים.

   **אחות ולא ‎::after‎, וזה היה ההבהוב.** פסבדו-אלמנט הוא **ילד**, ולכן
   הוא יורש את התנועה של הראשונה **ומוסיף** עליה את שלו: נמדד ‎37px/s‎
   במקום ‎15‎ -כלומר השכבה ה"איטית" סחפה מהר מהמהירה, ושתי רצועות בהירות
   בקצבים קרובים מייצרות פעימה. כאחיות כל אחת נעה בקצב שלה בלבד.

   **וכל שכבה מקבלת קיפריים בתקופת הדפוס שלה** (‎12%‎ מול ‎19%‎): קיפריים
   משותף הזיז את השנייה ‎12%‎ בעוד שהדפוס שלה חוזר כל ‎19%‎, כלומר כל מחזור
   נקטע באמצע וקפץ ‎7%‎ אחורה. **הכלל: מרחק התנועה של שכבה חוזרת חייב
   להיות בדיוק תקופת הדפוס שלה.** */
.house-fall-flow.is-slow {
    background:
        repeating-linear-gradient(180deg,
            rgba(255, 255, 255, 0.00) 0%,
            rgba(255, 255, 255, 0.22) 3%,
            rgba(255, 255, 255, 0.52) 6.5%,
            rgba(168, 214, 240, 0.44) 10%,
            rgba(124, 188, 222, 0.26) 14%,
            rgba(255, 255, 255, 0.00) 19%);
    animation: house-fall-run2 calc(var(--fall-dur, 1.7s) * 2.35) linear infinite;
}

@keyframes house-fall-run {
    from { translate: 0 0; }
    to   { translate: 0 12%; }
}
/* תקופת הדפוס של השכבה השנייה (‎::after‎) היא ‎19%‎ ולא ‎12%‎ -ולכן היא
   חייבת קיפריים משלה. שימוש חוזר בקיפריים של הראשונה הוא בדיוק ההבהוב
   שדווח. */
@keyframes house-fall-run2 {
    from { translate: 0 0; }
    to   { translate: 0 19%; }
}

/* ---- שבשבת הדרקון: הראש מתחלף ימינה ושמאלה ----
   **‎scaleX‎ שעובר דרך אפס ולא ‎rotateY‎.** שבשבת שמסתובבת נראית בהיטל
   דו-ממדי כמראה אופקית, והרגע שבו ‎scaleX‎ הוא 0 הוא בדיוק הרגע שבו
   היא "על הצד" -כלומר הסיבוב מקבל את התחושה הנכונה בלי פרספקטיבה
   ובלי ה-containing block ש-‎perspective‎ היה יוצר לכל ילד ‎fixed‎ (אותה
   מלכודת שכבר תועדה כאן על כרטיס הפרויקט).

   **‎transform-origin‎ הוא מרכז העמוד ולא מרכז הגזירה** (‎--pivot‎ נמדד
   בכלי): מראה סביב מרכז הקופסה מזיזה את הדרקון הצידה בכל היפוך, וזה
   נקרא כדילוג ולא כסיבוב.

   העצירות ארוכות מההיפוך (‎44%‎ לכל כיוון מול ‎6%‎ לתנועה) -שבשבת שמסתובבת
   בלי הפסקה נקראת כמאוורר. */
.house-fx-vane {
    background-size: contain;
    background-repeat: no-repeat;
    background-position: center bottom;
    animation: house-vane-turn 8s ease-in-out infinite;
}
@keyframes house-vane-turn {
    0%, 44%   { transform: scaleX(1); }
    47%       { transform: scaleX(0.06); }
    50%, 94%  { transform: scaleX(-1); }
    97%       { transform: scaleX(0.06); }
    100%      { transform: scaleX(1); }
}

/* ---- נצנוץ על תיבת האוצר ----
   כוכב ארבע-קצוות משני גרדיאנטים מוצלבים ולא תמונה: הוא נשאר חד בכל
   גודל, ואפשר לצבוע ולתזמן אותו במשתנים. */
.house-fx-shine { background: none; overflow: visible; pointer-events: none; }

/* ---- תווי מוזיקה מרחפים ----
   **הנכס ‎translate‎ ולא ‎transform‎**, ו-‎scale‎ כנכס נפרד: כך שניהם רצים
   יחד בלי לדרוס זה את זה, ו-‎transform‎ נשאר פנוי -אותו תרגיל שכבר משמש
   כאן את קפיצת הדמויות מול ההליכה.

   התו **נמחק מהבסיס** (‎tools/process-house-fx.php‎), ולכן ברגע שהוא שקוף
   אין מתחתיו עותק מצויר. הוא מופיע במקום שבו הצייר שם אותו, עולה, נסחף
   ודוהה -וחוזר. העלייה ‎-190%‎ מגובהו ולא ערך בפיקסלים, כדי שתתכווץ עם
   הבית ועם רוחב המסך. */
.house-fx-note {
    animation: house-note var(--note-dur, 4.6s) ease-in-out infinite;
    animation-delay: var(--delay, 0s);
    will-change: translate, opacity;
}
@keyframes house-note {
    0%   { opacity: 0; translate: 0 0;                          scale: 0.72; }
    14%  { opacity: 1; translate: calc(var(--note-dx, 30%) * 0.16) -22%; scale: 1; }
    55%  { opacity: 1; translate: calc(var(--note-dx, 30%) * 0.62) -108%; scale: 1.04; }
    100% { opacity: 0; translate: var(--note-dx, 30%) -190%;    scale: 1.12; }
}
/* ---- ניצוצות בקרן האור של המקרן ----
   **אותו כוכב של תיבת האוצר, בתזמון ובגודל אחרים** -ולא ערכה שלישית.
   שם ההבזק קצר בכוונה (ברק על מתכת); כאן הוא נקרא כאבק שנסחף באור,
   ולכן החלון הבהיר ארוך פי כמה והתנועה איטית.

   ובנוסף הם **גדולים ולבנים יותר**: על קרן צהובה בהירה ניצוץ בגודל של
   תיבת עץ כהה פשוט אינו נראה -נמדד שהוא כמעט בלתי מבחין. */
.house-fx-beam .house-shine {
    width: 40%;
    animation-name: house-beam-shine;
    animation-duration: 4.6s;
    filter: brightness(1.25);
}
@keyframes house-beam-shine {
    0%, 100% { opacity: 0;    scale: calc(var(--shine-scale, 1) * 0.45); rotate: 0deg; }
    22%      { opacity: 0.9;  scale: var(--shine-scale, 1);              rotate: 6deg; }
    50%      { opacity: 1;    scale: calc(var(--shine-scale, 1) * 1.12); rotate: 14deg; }
    78%      { opacity: 0.35; scale: calc(var(--shine-scale, 1) * 0.8);  rotate: 20deg; }
}

/* ---- כוכבי נצנוץ ----
   **במקום, לא בתנועה.** הכוכב נגזר מהשמיים ונמחק משם, ולכן הוא יכול
   להיעלם לגמרי בלי לחשוף עותק מצויר מתחתיו -וזה מה שמאפשר הבהוב אמיתי
   ולא רק שינוי בהירות.

   הסיבוב **עדין** (‎18°‎) ולא סיבוב מלא: כוכב שמסתובב הרבה נקרא כגלגל,
   אותו לקח בדיוק של נצנוצי תיבת האוצר. וההבזק קצר -כוכב שדולק חצי
   מהזמן הוא נורה. */
.house-fx-star {
    animation: house-star var(--star-dur, 3.2s) ease-in-out infinite;
    animation-delay: var(--delay, 0s);
}
@keyframes house-star {
    0%, 100% { opacity: 0.25; scale: 0.72; rotate: 0deg; }
    42%      { opacity: 1;    scale: 1.06; rotate: 10deg; }
    66%      { opacity: 0.72; scale: 0.94; rotate: 18deg; }
}

/* ---- גלי רדיו מהאנטנה ----
   **הציר בקצה שפונה לתורן**, ולכן ההתרחבות היא החוצה מהמשדר ולא סביב
   מרכז הקשת: קשת שגדלה סביב מרכזה זזה גם **פנימה**, אל תוך האנטנה, וזה
   נקרא כרעידה ולא כשידור. אותו עיקרון של ‎transform-origin‎ בדגל ובעשן.

   ‎scale‎ ולא ‎transform‎ -נכס עצמאי, כך שאם תתווסף כאן תנועה נוספת
   (סחיפה, הטיה) השתיים יצטרפו במקום לדרוס זו את זו. */
.house-fx-wave {
    transform-origin: var(--wave-org, 100% 50%);
    animation: house-wave var(--wave-dur, 2s) ease-out infinite;
    animation-delay: var(--delay, 0s);
}
/* ‎--wave-org‎ גובר, ולכן צלחת שמשדרת באלכסון מקבלת **פינה** ולא צלע. */
.house-fx-wave.hfx-wr { transform-origin: var(--wave-org, 0% 50%); }
/* **אינו נכבה לגמרי.** ‎opacity: 0‎ בתחילת המחזור מייצר רגע שבו האנטנה
   דוממת לחלוטין, וזה נקרא כתקלה; שאריות של ‎0.2‎ קוראות כקשת שדעכה. */
@keyframes house-wave {
    0%   { opacity: 0.2;  scale: 0.82; }
    20%  { opacity: 1;    scale: 1.02; }
    50%  { opacity: 0.45; scale: 1.07; }
    100% { opacity: 0.2;  scale: 0.82; }
}

/* ---- שלט מתנדנד ברוח ----
   ‎rotateY‎ ולא ‎rotate‎: סיבוב סביב הציר ה**אנכי** אינו מזיז את הקצה התחתון
   אנכית כלל (הציר יושב עליו), ולכן הוא נשאר מתחת ל-‎-signcap‎ הסטטי בכל
   זווית. וב-‎rotateY‎ הרוחב רק **מתכווץ** אל הציר -גם עם הפרספקטיבה, כי
   הצד שמתקרב לצופה הוא זה שנע פנימה- ולכן אין זווית שבה השלט חורג מהכיסוי.
   **זווית גדולה בכוונה:** ההיסט האופקי הוא ‎d·(1−cos θ)‎, כלומר ‎0.6px‎
   ב-‎±9°‎ -בלתי נראה, וזה נמדד כ"סטטי לגמרי"- מול ‎~8px‎ ב-‎±34°‎. */
.house-fx-sign {
    transform-origin: var(--sign-org, 50% 100%);
    animation: house-sign-swing var(--sign-dur, 4.6s) ease-in-out infinite;
    animation-delay: var(--delay, 0s);
}
/* **הפרספקטיבה בתוך ה-‎transform‎ ולא על ההורה.** על ההורה היא הופכת אותו
   ל-containing block של כל ילד ‎fixed‎ -מלכודת שכבר הפילה כאן את כרטיס
   הפרויקט ואת קלף הצ'אט. */
/* **לא מטרונום.** תנודה סימטרית בשני שלבים נקראת כמכונה; רוח היא משבים,
   ולכן ארבע נקודות בעוצמות שונות ובמרווחים לא-שווים. */
@keyframes house-sign-swing {
    0%, 100% { transform: perspective(1100px) rotateY(-34deg); }
    28%      { transform: perspective(1100px) rotateY( 22deg); }
    52%      { transform: perspective(1100px) rotateY(-13deg); }
    76%      { transform: perspective(1100px) rotateY( 34deg); }
}

@media (prefers-reduced-motion: reduce) {
    /* דוממים אך **גלויים**: בית מוזיקה בלי תווים נקרא כבית שבור, לא
       כאנימציה כבויה -אותו שיקול של העשן בארובה. */
    .house-fx-note { animation: none; opacity: 1; translate: 0 0; scale: 1; }
    .house-fx-star { animation: none; opacity: 1; scale: 1; rotate: 0deg; }
    .house-fx-wave { animation: none; opacity: 1; scale: 1; }
    .house-fx-sign { animation: none; transform: none; }
}
/* **‎28%‎ ולא ‎34%‎, והמרכזים נדחפו פנימה.** הכוכב מסתובב תוך כדי ההבזק,
   ומלבן חוסם של ריבוע מסובב גדל עד ‎√2‎ מצלעו - נמדד ‎58px‎ על אלמנט של
   ‎41.7px‎, וקצה אחד יצא מעל התיבה אל השמיים. */
/* **הזרועות אליפסות ולא קווים.** ‎linear-gradient‎ נותן שני פסים ברוחב
   קבוע מקצה לקצה, וזה נקרא כסימן ‎X‎ מצויר ולא כנצנוץ; ‎radial-gradient‎
   בגודל צר-וארוך מתחדד לקצה מעצמו. שלוש שכבות: ליבה, זרוע אנכית,
   זרוע אופקית. */
.house-shine {
    position: absolute;
    width: 26%;
    aspect-ratio: 1;
    translate: -50% -50%;
    background:
        radial-gradient(closest-side, rgba(255, 255, 255, 0.98), rgba(255, 238, 165, 0.55) 42%, transparent 74%) 50% 50% / 44% 44% no-repeat,
        radial-gradient(closest-side, rgba(255, 253, 228, 0.95), rgba(255, 240, 175, 0.28) 55%, transparent 78%) 50% 50% / 15% 100% no-repeat,
        radial-gradient(closest-side, rgba(255, 253, 228, 0.95), rgba(255, 240, 175, 0.28) 55%, transparent 78%) 50% 50% / 100% 15% no-repeat;
    opacity: 0;
    animation: house-shine-pop 3.6s ease-in-out infinite;
    animation-delay: calc(var(--shine-delay, 0s) * -1);
}
/* ההבזק **קצר** (‎62%..86%‎ מהמחזור): נצנוץ שדולק חצי מהזמן נקרא כנורה
   ולא כברק על מתכת. הסיבוב עדין -‎22°‎ ולא ‎62°‎- כי כוכב שמסתובב הרבה
   נקרא כגלגל. */
@keyframes house-shine-pop {
    0%, 62%, 100% { opacity: 0;    scale: calc(var(--shine-scale, 1) * 0.35); rotate: 0deg; }
    70%           { opacity: 1;    scale: var(--shine-scale, 1);              rotate: 12deg; }
    86%           { opacity: 0;    scale: calc(var(--shine-scale, 1) * 0.55); rotate: 22deg; }
}

@media (prefers-reduced-motion: reduce) {
    .house-fx-flag { animation: none; }
    /* בצמצום תנועה משאירים תמר אחד דומם בפתח הארובה במקום כלום -
       ארובה בלי עשן נקראת כבית נטוש, לא כ"אנימציה כבויה". */
    .house-puff { animation: none; opacity: 0.8; transform: scale(0.7); }
    .house-puff-b, .house-puff-c { display: none; }
    /* המפל והשבשבת נשארים גלויים ודוממים -מפל שנעלם או גג בלי שבשבת
       נראים כתקלה, לא כ"אנימציה כבויה". הנצנוץ יורד לגמרי: הבזק חוזר
       הוא בדיוק סוג התנועה שההעדפה הזאת באה למנוע. */
    .house-fall-flow { animation: none; }
    .house-fx-vane { animation: none; }
    .house-shine { display: none; }
}

/* ---- הפנס: אור אמיתי עם הילה ופעימה עדינה ----
   **המיקום נמדד ולא הוערך.** ‎lamp.png‎ הוא 80x241, והזכוכית המוארת בו
   יושבת סביב ‎(51.2%, 20.1%)‎ מהתמונה. אבל ‎.obj‎ הוא ריבוע (‎aspect-ratio: 1‎)
   עם ‎background-size: contain‎, ולכן התמונה ממולאת לגובה ורוחבה יוצא רק
   ‎80/241 = 33.2%‎ מהקופסה, ממורכז - כך שבמונחי הקופסה מרכז הזכוכית הוא
   ‎(50.4%, 20.1%)‎. הצבע נלקח מהציור עצמו: ממוצע ‎rgb(125,223,219)‎ והבהיר
   ביותר ‎rgb(207,251,252)‎ - אותו תכלת, כבקשת המשתמש. */
.obj[data-sprite="lamp"] { --lamp-glow: 0; }
.obj[data-sprite="lamp"]::after {
    content: '';
    position: absolute;
    left: 50.4%;
    top: 20.1%;
    width: 150%;
    aspect-ratio: 1;
    transform: translate(-50%, -50%);
    border-radius: 50%;
    /* **ההילה יושבת מתחת לשכבות ההחשכה של הלילה** (‎.light-mul‎/‎.light-tint‎
       ב-z8, ואילו אובייקטי הקרקע נמוכים מהן), ולכן היא מוכפלת יחד עם הנוף.
       זו הסיבה שהיא נבנית חזקה: בגרסה החלשה היא נמדדה כאובך תכלת ולא כאור.
       (אורות חלונות הטירה פתרו את זה בשכבה נפרדת ב-z9; כאן זה בלתי אפשרי,
       כי האובייקט חייב לשבת בין העצים ולנוע איתם.) */
    /* **קטנה יותר מהגרסה הקודמת, ובעיקר שקופה במרכז.** הילה רחבה ואטומה
       בלעה את הפנס עצמו - מה שנראה על המסך היה כתם תכלת ולא מנורה. הליבה
       מתחילה כאן חלשה יותר, והדעיכה מהירה, כך שהזכוכית המצוירת נשארת
       הדבר הבהיר ביותר בתמונה. */
    background: radial-gradient(circle at 50% 50%,
        rgba(214, 250, 252, 0.55) 0 6%,
        rgba(160, 235, 245, 0.34) 18%,
        rgba(110, 210, 235, 0.18) 36%,
        rgba(80, 180, 225, 0.07) 58%,
        rgba(80, 180, 225, 0) 80%);
    /* ‎screen‎ מוסיף אור במקום לצבוע מעל -כמו אורות חלונות הטירה */
    mix-blend-mode: screen;
    opacity: var(--lamp-glow);
    pointer-events: none;
    /* **מעבר רך בין מצבי היום** (בקשת המשתמש): הפנס נדלק ונכבה עם השקיעה
       והזריחה במקום להיעלם בקפיצה. ‎opacity‎ שמור למעבר הזה בלבד. */
    transition: opacity 1.6s ease;
    /* הזוהר עצמו: ‎transform‎ (רץ ב-compositor) ו-‎filter: brightness‎ -
       **לא** ‎opacity‎, שאחרת האנימציה הייתה דורסת את מעבר שעות היום. */
    animation: lamp-pulse 3.4s ease-in-out infinite alternate;
}
/* הזכוכית עצמה נושמת יחד עם ההילה -בלי זה ההילה נעה והמנורה קפואה */
@keyframes lamp-pulse {
    from { transform: translate(-50%, -50%) scale(0.92); filter: brightness(0.82); }
    to   { transform: translate(-50%, -50%) scale(1.08); filter: brightness(1.18); }
}
/* הפנס דולק בעיקר כשחשוך. ביום הוא כמעט בלתי נראה, אחרת הילה על שמיים
   בהירים נקראת ככתם ולא כאור. */
/* ביום כבוי לגמרי (בקשת המשתמש) -פנס דולק בשמש נקרא כתקלה */
body[data-daytime="dusk"]  .obj[data-sprite="lamp"] { --lamp-glow: 0.7; }
body[data-daytime="night"] .obj[data-sprite="lamp"] { --lamp-glow: 1; }
body[data-daytime="dawn"]  .obj[data-sprite="lamp"] { --lamp-glow: 0.28; }
@media (prefers-reduced-motion: reduce) {
    .obj[data-sprite="lamp"]::after { animation: none; transform: translate(-50%, -50%); }
}
/* אין כאן תיקון ל-‎.is-flipped‎ **בכוונה**: ההילה עגולה וסימטרית, ומרכזה
   ב-‎50.4%‎ - כלומר ההיפוך מזיז אותה ב-0.8% מרוחב הקופסה. כלל שהיה מתקן את
   זה גם היה **מת**, כי הוא כותב ‎transform‎ שהאנימציה גוברת עליו ממילא. */

/* אובייקטים מאחורי הטירה עומדים גבוה יותר על קו האופק */
.ground-back .obj { bottom: calc(var(--water-h) + var(--ground-h) - 1vh + var(--dy, 0vh)); }

/* ---- ניצוצות ---- */
/* שכבת הניצוצות/כוכבים -עוטפת את כולם כדי שיוכלו לנוע יחד עם המצלמה בפאן
   (המשכיות). הניצוצות עצמם משתמשים ב-transform לנצנוץ, ולכן הפאן חייב לשבת
   על השכבה ולא עליהם -בדיוק כמו .birds מול .bird. */
.spark-layer { position: absolute; inset: 0; z-index: 7; pointer-events: none; }
.spark {
    position: absolute;
    z-index: 7;
    background-size: contain;
    background-repeat: no-repeat;
    background-position: center;
    mix-blend-mode: screen;
    opacity: 0;
    animation: spark-twinkle var(--dur, 3s) ease-in-out infinite;
    animation-delay: var(--delay, 0s);
}

@keyframes spark-twinkle {
    0%, 100% { opacity: 0;    transform: scale(0.5) rotate(0deg); }
    50%      { opacity: 0.95; transform: scale(1.15) rotate(12deg); }
}

/* נצנוצים על הטירה -מעל תמונת הטירה והדגלים */
.spark-castle { z-index: 3; }

/* ---- תאורה לפי שעות היום ----
   שתי שכבות מעל כל הסצנה: multiply (מחשיך) ו-soft-light (צובע).
   המצב נקבע ב-data-daytime על ה-body (PHP בטעינה, JS מתעדכן כל דקה). */
.light {
    position: absolute;
    inset: 0;
    z-index: 8;
    opacity: 0;
    transition: opacity 2.5s ease;
    pointer-events: none;
}

.light-mul  { mix-blend-mode: multiply; }
.light-tint { mix-blend-mode: soft-light; }

/* לילה -החשכה אמיתית: כל הסצנה מוחשכת + שכבת כחול עמוק */
body[data-daytime="night"] .scene {
    filter: brightness(0.52) saturate(0.79);
}

/* ---- תאורת הדמויות הקדמיות: ‎filter‎ על הדמויות עצמן, לא על השכבה ----
   **כאן ישב באג שצבע את כל האתר.** ‎.scene-front‎ נשאה עותק של
   ‎.light-mul‎/‎.light-tint‎ כדי שהדמויות הקדמיות יקבלו את תאורת השעה -אבל
   היא ‎fixed; inset: 0; z-index: 11‎, כלומר **כל המסך ומעל התוכן**, והיא
   stacking context שכמעט כולה שקופה. ‎mix-blend-mode‎ מול backdrop ריק אינו
   ממזג אלא צובע, ולכן בשקיעה/זריחה/לילה כל העמוד קיבל כתם שטוח.
   ‎filter‎ פותר את זה מעצם טבעו: הוא פועל על הפיקסלים של האלמנט בלבד.

   **והפילטר יושב על ‎.folk‎/הבועה, לא על ‎.scene-front‎ כולה.** כשהוא ישב על
   השכבה, דווח מאייפד אמיתי "הגמד מופיע בהיר כאילו זה אור יום" בלילה: הרקע
   כהה (שכבות ה-multiply עובדות) ושומרי הלילה נותרו בהירים -כלומר ספארי
   השמיט **בשקט** את הפילטר של שכבת מסך-מלא עם ילדים מרוכבים שנפרסים על
   רוחב העולם. זה **לא שוחזר** באף מנוע בדיקה (chromium, Edge, WebKit של
   playwright ב-1194 וב-390) -פילטר על שכבה ענקית הוא התנהגות תלוית-מכשיר,
   ולכן הוא יורד אל האלמנטים הקטנים. מתמטית זה זהה: brightness/saturate/
   sepia/hue-rotate הן פעולות פר-פיקסל, והדמויות אינן חופפות (חוק הפיזור).
   הערכים נשארו מכוילים לגוון שכבות המיזוג, כדי שדמות קדמית לא תיראה מוארת
   אחרת מדמות אחורית שלצידה. */
body[data-daytime="dawn"] .scene-front :is(.folk, .folk-say) {
    filter: brightness(1.02) saturate(1.08) sepia(0.14) hue-rotate(-6deg);
}
body[data-daytime="dusk"] .scene-front :is(.folk, .folk-say) {
    filter: brightness(0.95) saturate(1.18) sepia(0.22) hue-rotate(-12deg);
}
/* הלילה של הדמויות הקדמיות = הלילה של ‎.scene‎ **ועוד** ‎#folkNight‎, שהוא
   ה-‎multiply‎ של ‎.light-mul‎ ו-‎.light-tint‎ שהשכבה הזאת אינה מקבלת.

   **‎hue-rotate(6deg)‎ שהיה כאן קודם היה פעולת אפס.** הוא נועד לחקות את
   שכבת ה-tint, אבל ‎hue-rotate‎ ו-‎saturate‎ אינם משנים פיקסל נייטרלי -ולכן
   הדמות הקדמית יצאה **אפורה בהירה** (‎rgb(133,133,133)‎ על לבן) מול
   ‎rgb(48,54,76)‎ בשכבה האחורית: פי 2.45 בהירות, וגוון אחר. זה מה שדווח
   כ"הגמד מואר מדי לעומת הילד הלוחם", ושני הספרייטים כמעט זהים בגיליון.
   36 שילובי ‎brightness/saturate/sepia/hue-rotate‎ נסרקו וכולם החזירו אפור.
   ההגדרה ב-‎templates/scene.php‎, לצד המדידה שממנה נגזרו המקדמים. */
body[data-daytime="night"] .scene-front :is(.folk, .folk-say) {
    filter: brightness(0.52) saturate(0.79) url(#folkNight);
}
/* ---- וכשאי אפשר להשתמש ב-‎url(#folkNight)‎ ----
   דווח שבאייפון הדמות הקדמית נשארת בהירה בלילה. שלושת המדדים שאפשר להריץ
   במחשב יצאו נקיים (סגנון מחושב זהה ב-390 וב-1500 ובוובקיט, ה-CSS וה-SVG
   החיים זהים למקומי, וריבוע לבן נמדד ‎[48,54,76]‎ עם הפילטר) -כלומר WebKit
   של iOS פשוט אינו פותר את ההפניה, וזה אינו ניתן למדידה מכאן.

   **הקירוב אינו ‎sepia‎/‎hue-rotate‎ אלא בהירות ורוויה בלבד.** סריקה ב-canvas
   על 15 צבעי ספרייט הראתה שהגוון כמעט אינו תורם: ‎brightness(0.22)
   saturate(0.48)‎ נותן שגיאה ממוצעת של **10** יחידות RGB מול היעד (מרבית
   ‎21‎), בזמן שמה שמוצג היום שם נמדד **72** (מרבית ‎128‎). התוספת של
   ‎sepia‎+‎hue-rotate‎ שיפרה ל-‎10.0‎ בלבד -כלומר אינה שווה את המורכבות.
   הרישום הישן כאן ("אין ערך שיביא לגוון הכחול") נכון ל**לבן** ומטעה
   לגבי ספרייט צבעוני.

   שני מסלולים, ובכוונה: ‎@supports‎ תופס את iOS בוודאות (‎-webkit-touch-callout‎
   אינו קיים במק ובכרום), והמחלקה תופסת **כל** מנוע שנכשל בפועל -כולל כזה
   שיתגלה מחר. שניהם יורדים מעצמם אם ספארי יתקן.
   ‎@supports‎ יושב **אחרי** הכלל שמעליו כי ה-specificity זהה. */
@supports (-webkit-touch-callout: none) {
    body[data-daytime="night"] .scene-front :is(.folk, .folk-say) {
        filter: brightness(0.22) saturate(0.48);
    }
}
body.no-svgfilter[data-daytime="night"] .scene-front :is(.folk, .folk-say) {
    filter: brightness(0.22) saturate(0.48);
}
/* מעבר התאורה (2.5s) עבר יחד עם הפילטר מהשכבה אל הדמויות */
.scene-front .folk, .scene-front .folk-say { transition: filter 2.5s ease; }

body[data-daytime="night"] .light-mul {
    opacity: 1;
    background: linear-gradient(180deg,
        rgba(20, 32, 82, 0.80) 0%,
        rgba(30, 46, 108, 0.70) 45%,
        rgba(46, 68, 132, 0.60) 100%);
}

body[data-daytime="night"] .light-tint {
    opacity: 1;
    background: linear-gradient(180deg, rgba(60, 85, 190, 0.40), rgba(10, 18, 60, 0.35));
}

/* זריחה -זהב ורוד עדין */
body[data-daytime="dawn"] .light-mul {
    opacity: 1;
    background: linear-gradient(180deg, rgba(200, 170, 210, 0.30), rgba(255, 220, 180, 0.12) 70%);
}

body[data-daytime="dawn"] .light-tint {
    opacity: 1;
    background: linear-gradient(180deg,
        rgba(255, 200, 150, 0.45) 0%,
        rgba(255, 160, 130, 0.40) 55%,
        rgba(255, 210, 150, 0.25) 100%);
}

/* שקיעה -כתום-סגול חם */
body[data-daytime="dusk"] .light-mul {
    opacity: 1;
    background: linear-gradient(180deg, rgba(115, 70, 150, 0.55), rgba(235, 130, 80, 0.35) 65%);
}

body[data-daytime="dusk"] .light-tint {
    opacity: 1;
    background: linear-gradient(180deg,
        rgba(255, 130, 70, 0.75) 0%,
        rgba(255, 100, 85, 0.65) 50%,
        rgba(255, 175, 80, 0.55) 100%);
}

/* בלילה: רק ניצוצות השמיים (הגבוהים) מוצגים, מעל שכבות ההחשכה, וזוהרים כמו כוכבים.
   בלילה בלבד -מעט חזקים יותר (בהירות + זוהר כפול). */
body[data-daytime="night"] .spark-high {
    z-index: 9;
    filter: brightness(4.6)
            drop-shadow(0 0 5px rgba(255, 255, 235, 1))
            drop-shadow(0 0 13px rgba(255, 248, 195, 0.8));
    /* בלילה מהבהבים לאט יותר (שומר על הווריאציה בין הכוכבים) */
    animation-duration: calc(var(--dur, 3s) * 1.8);
}

/* בלילה השכבה כולה עולה ל-z9 (מעל שכבות ההחשכה z8) -חובה על ה**שכבה** ולא על
   הניצוץ: ל-.spark-layer יש stacking context משלה (z-index + ה-transform של הפאן),
   ולכן z:9 על ניצוץ בודד היה נשאר כלוא בתוכה ומתחת להחשכה. אותה מלכודת כמו
   castle-lights. הכוכבים שבתוכה נשארים על z:9 (מעל spark-low) בהקשר המקומי. */
body[data-daytime="night"] .spark-layer { z-index: 9; }

/* **‎visibility: hidden‎ אינו עוצר אנימציות** -הניצוצות המוסתרים המשיכו
   לתקתק סגנון בחוט הראשי בכל פריים, כל הלילה (נמדד: ‎Animation‎ עם
   ‎AnimationHasNoVisibleChange‎ ב-trace). ‎paused‎ שומר את הפאזה, וההשהיה
   נגמרת מעצמה כשה-daytime מתחלף והכלל מפסיק לחול. */
body[data-daytime="night"] .spark-low,
body[data-daytime="night"] .spark-castle { visibility: hidden; animation-play-state: paused; }

/* בלילה: "חור" רך בשכבות ההחשכה סביב הירח -הירח נשאר זוהר למרות שהוא
   מתחת לעננים ולטירה, ועננים שחולפים לידו נראים מוארים באור ירח */
body[data-daytime="night"] .light-mul,
body[data-daytime="night"] .light-tint {
    -webkit-mask-image: radial-gradient(circle 340px at var(--moon-cx) var(--moon-cy),
        rgba(0, 0, 0, 0.30) 0%, rgba(0, 0, 0, 0.55) 22%, rgba(0, 0, 0, 0.78) 46%,
        rgba(0, 0, 0, 0.93) 70%, #000 100%);
    mask-image: radial-gradient(circle 340px at var(--moon-cx) var(--moon-cy),
        rgba(0, 0, 0, 0.30) 0%, rgba(0, 0, 0, 0.55) 22%, rgba(0, 0, 0, 0.78) 46%,
        rgba(0, 0, 0, 0.93) 70%, #000 100%);
}

/* בלילה הים כהה יותר */
body[data-daytime="night"] .water {
    filter: brightness(0.55) saturate(0.85);
}

/* ---- ירח -מופיע רק בלילה, מעל ההחשכה ומתחת לעננים ---- */
.moon {
    position: absolute;
    top: 10%;
    left: 14%;
    width: clamp(70px, 7vw, 110px);
    aspect-ratio: 1;
    border-radius: 50%;
    /* ירח מציאותי: כסוף-לבן עם "ימות" אפורות רכות ורחבות (לא נקודות) והצללת שפה */
    background:
        radial-gradient(ellipse 42% 34% at 62% 36%, rgba(110, 114, 112, 0.20), transparent 72%),
        radial-gradient(ellipse 34% 26% at 34% 58%, rgba(110, 114, 112, 0.16), transparent 72%),
        radial-gradient(ellipse 24% 18% at 56% 72%, rgba(110, 114, 112, 0.13), transparent 70%),
        radial-gradient(circle at 38% 33%, #ffffff 0%, #f2f2ea 42%, #d9d9cd 72%, #aeaea2 100%);
    box-shadow:
        0 0 26px 2px rgba(235, 240, 255, 0.25),
        0 0 90px 10px rgba(190, 205, 255, 0.12);
    opacity: 0;
    transition: opacity 2.5s ease;
    /* מפצה על החשכת הסצנה (filter על .scene + שארית ההחשכה בתוך ה"חור") */
    filter: brightness(2.3);
}

body[data-daytime="night"] .moon { opacity: 1; }

/* ---- כוכב נופל (ציור מקורי) ----
   הקובץ חולץ מציור על רקע כחול ב-‎tools/make-shooting-star.php‎: הצבע בו
   מנורמל לגוון אחד והאלפא נושאת את העוצמה, ולכן הוא מתנהג כאור.

   ‎mix-blend-mode: screen‎ מאותה סיבה שהניצוצות משתמשות בו -הוא מוסיף אור
   לשמיים במקום להניח עליהם מלבן, וכך אין לו קצה נראה.

   ‎filter: brightness‎ הוא **פיצוי ולא קישוט**: בלילה יש ‎filter‎ על ‎.scene‎
   כולה, וכל מה שבתוכה מוחשך. הירח כבר עושה בדיוק את זה מאותו טעם. */
.shooting-star {
    position: absolute;
    top: 0;
    left: 0;
    /* **קטן, ובכוונה.** ב-‎21vw‎ הכוכב יצא ‎315x161‎ ובלע את כל הרצועה שבין
       סרגל הניווט לכותרת עמוד הבית: מה שנראה על המסך היה הזנב בלבד, כי
       הראש -החלק שעושה אותו כוכב -הוסתר מאחורי הכותרת. */
    width: clamp(100px, 14vw, 225px);
    aspect-ratio: 900 / 477;   /* מידות הקובץ שנוצר -כדי שלא יימתח */
    background: url('../images/scene/shooting-star.png') center / contain no-repeat;
    opacity: 0;
    z-index: 9;                /* מעל שכבות ההחשכה כמו הירח, מתחת לעננים (z10) */
    pointer-events: none;
    mix-blend-mode: screen;
    filter: brightness(1.9) saturate(1.05);
}

/* המחלקה מוסרת ומוחזרת ב-JS בכל הופעה, ולכן האנימציה רצה מחדש בכל פעם.
   ‎--sx/--sy‎ (מרחק) ו-‎--star-rot‎ (נטיית המסלול) נכתבים שם פר-הופעה. */
.shooting-star.is-flying {
    animation: star-fly var(--star-ms, 1700ms) cubic-bezier(0.22, 0.61, 0.36, 1) both;
}

@keyframes star-fly {
    0%   { opacity: 0; transform: translate(0, 0) rotate(var(--star-rot, 0deg)) scale(0.72); }
    /* הופעה מהירה ודעיכה ארוכה -כך זה נקרא כהבזק שנמוג ולא כתמונה
       שנדלקת ונכבית. */
    14%  { opacity: 1; }
    62%  { opacity: 1; }
    100% { opacity: 0; transform: translate(var(--sx, 34vw), var(--sy, 9vh)) rotate(var(--star-rot, 0deg)) scale(1.04); }
}

/* חוק המובייל: בטלפון הכוכב תופס חלק גדול מהמסך ובולע את השמיים. */
@media (max-width: 640px) {
    .shooting-star { width: clamp(96px, 40vw, 190px); }
}

@media (prefers-reduced-motion: reduce) {
    .shooting-star,
    .shooting-star.is-flying { animation: none; opacity: 0; }
}

/* ---- שמש -עולה בזריחה, גבוהה ביום, שוקעת בשקיעה, נעלמת בלילה ---- */
.sun {
    position: absolute;
    top: 18%;
    left: 15%; /* צד שמאל -כדי לא להתנגש עם השעון שבימין */
    width: clamp(56px, 7vw, 100px);
    aspect-ratio: 1;
    border-radius: 50%;
    /* קצה רך שנמוג (פחות חזק, לא דיסק מוצק) */
    background: radial-gradient(circle at 50% 45%,
        #FFF3C4 0%, #FFE07A 38%, #FCC24E 62%, rgba(246, 166, 58, 0.7) 84%, rgba(246, 166, 58, 0) 100%);
    /* זוהר עדין */
    box-shadow:
        0 0 18px 4px rgba(255, 205, 90, 0.28),
        0 0 46px 14px rgba(255, 180, 60, 0.14);
    opacity: 0;
    z-index: 3; /* בשמיים, מאחורי העננים והטירה */
    pointer-events: none;
    /* מעבר חלק של עלייה/שקיעה ועמעום. שים לב: אין transition על background/mask -
       אחרת כרום עושה cross-fade בין תמונות השמש ונוצרת חפיפה של שתי שמשות. */
    transition: opacity 2s ease, top 2.5s ease, left 2.5s ease, box-shadow 2s ease;
    animation: sun-pulse 7s ease-in-out infinite;
}

/* בלום זוהר רך סביב השמש (screen -מוסיף אור אמיתי, לא מכסה את הדיסק).
   פועם בעדינות, וזול (שמש בודדת) -עובד יפה גם במובייל. */
.sun::after {
    content: '';
    position: absolute;
    inset: -60%;
    border-radius: 50%;
    pointer-events: none;
    z-index: 1;
    mix-blend-mode: screen;
    background: radial-gradient(circle,
        rgba(255, 240, 165, 0.72) 0%,
        rgba(255, 206, 100, 0.36) 30%,
        rgba(255, 188, 78, 0.12) 52%,
        rgba(255, 184, 74, 0) 70%);
    animation: sun-bloom 5s ease-in-out infinite;
}
body[data-daytime="dusk"] .sun::after {
    background: radial-gradient(circle,
        rgba(255, 176, 96, 0.68) 0%,
        rgba(240, 126, 62, 0.34) 30%,
        rgba(224, 100, 48, 0.12) 52%,
        rgba(220, 96, 46, 0) 70%);
}
body[data-daytime="night"] .sun::after { display: none; }
/* נשימת אור עדינה -הזוהר גדל ומתחזק ואז נרגע, במחזור איטי ורך */
@keyframes sun-bloom {
    0%, 100% { transform: scale(0.94); opacity: 0.62; }
    50%      { transform: scale(1.16); opacity: 1; }
}

/* זריחה -שמש SUN02 (חולצה נקייה מהגיליון), מסתובבת חלק, נמוכה (עולה מהאופק).
   סיבוב אמיתי דרך transform (חלק יותר מ-16 פריימים דיסקרטיים). */
body[data-daytime="dawn"] .sun {
    opacity: 0.95;
    top: min(17%, 10rem);
    left: min(12%, 10rem);
    width: clamp(96px, 12vw, 172px);
    aspect-ratio: 1;
    border-radius: 0;
    box-shadow: none;
    -webkit-mask: none;
    mask: none;
    background: url('/images/scene/dawn-sun.webp') center / contain no-repeat;
    /* זוהר חם + פעימת בהירות עדינה */
    filter: drop-shadow(0 0 20px rgba(255, 205, 95, 0.6))
            drop-shadow(0 0 48px rgba(255, 170, 50, 0.4));
    animation: sun-rise-spin 60s linear infinite, sun-rise-glow 6s ease-in-out infinite;
}
/* יום -שמש נקייה (פריים בודד שחולץ מ-SUN-02), עם זוהר חם ופעימה עדינה */
body[data-daytime="day"] .sun {
    opacity: 0.97;
    top: min(9%, 7rem);
    left: min(13%, 11rem);
    width: clamp(96px, 12vw, 176px);
    aspect-ratio: 1;
    border-radius: 0;
    box-shadow: none;
    -webkit-mask: none;
    mask: none;
    background: url('/images/scene/day-sun.webp') center / contain no-repeat;
    /* זוהר חם שנצמד לצורת השמש (ערך בסיס -האנימציה מנשימה אותו) */
    filter: drop-shadow(0 0 22px rgba(255, 224, 130, 0.85))
            drop-shadow(0 0 60px rgba(255, 190, 70, 0.55))
            drop-shadow(0 0 110px rgba(255, 170, 45, 0.35));
    animation: day-sun-glow 5s ease-in-out infinite;
}

/* סיבוב חלק של שמש-הזריחה + פעימת זוהר עדינה */
@keyframes sun-rise-spin {
    to { transform: rotate(360deg); }
}
@keyframes sun-rise-glow {
    0%, 100% {
        filter: drop-shadow(0 0 18px rgba(255, 205, 95, 0.55))
                drop-shadow(0 0 44px rgba(255, 170, 50, 0.35)) brightness(1);
    }
    50% {
        filter: drop-shadow(0 0 28px rgba(255, 220, 120, 0.8))
                drop-shadow(0 0 66px rgba(255, 180, 60, 0.5)) brightness(1.07);
    }
}
/* נשימת זוהר לשמש-היום -הזוהר עוקב אחרי צורת השמש (drop-shadow) ומתחזק בפעימה */
@keyframes day-sun-glow {
    0%, 100% {
        transform: scale(1);
        filter: drop-shadow(0 0 20px rgba(255, 222, 128, 0.8))
                drop-shadow(0 0 52px rgba(255, 186, 66, 0.5))
                drop-shadow(0 0 100px rgba(255, 168, 44, 0.3))
                brightness(1.04);
    }
    50% {
        transform: scale(1.05);
        filter: drop-shadow(0 0 34px rgba(255, 236, 165, 1))
                drop-shadow(0 0 84px rgba(255, 198, 82, 0.72))
                drop-shadow(0 0 150px rgba(255, 176, 52, 0.45))
                brightness(1.14);
    }
}
/* שקיעה -שמש נמוכה ואדמדמה (שוקעת), חלשה ורכה */
/* שקיעה -אותה שמש נקייה כמו ביום, נמוכה יותר ובגוון חמים-אדמדם */
body[data-daytime="dusk"] .sun {
    opacity: 0.92;
    top: min(26%, 14rem);
    left: min(11%, 9rem);
    width: clamp(92px, 11.5vw, 168px);
    aspect-ratio: 1;
    border-radius: 0;
    box-shadow: none;
    -webkit-mask: none;
    mask: none;
    background: url('/images/scene/day-sun.webp') center / contain no-repeat;
    filter: drop-shadow(0 0 20px rgba(255, 140, 60, 0.6))
            drop-shadow(0 0 52px rgba(230, 90, 40, 0.4))
            saturate(1.15) hue-rotate(-14deg);
    animation: dusk-sun-glow 6s ease-in-out infinite;
}
/* פעימת זוהר חמה לשקיעה (שומרת על גוון השקיעה) */
@keyframes dusk-sun-glow {
    0%, 100% {
        transform: scale(1);
        filter: drop-shadow(0 0 18px rgba(255, 140, 60, 0.55))
                drop-shadow(0 0 44px rgba(230, 90, 40, 0.35))
                saturate(1.13) hue-rotate(-14deg) brightness(1);
    }
    50% {
        transform: scale(1.035);
        filter: drop-shadow(0 0 26px rgba(255, 150, 70, 0.75))
                drop-shadow(0 0 62px rgba(235, 100, 45, 0.5))
                saturate(1.2) hue-rotate(-14deg) brightness(1.05);
    }
}
/* לילה -אותה שמש של השקיעה, שוקעת יפה מתחת לאופק ונעלמת (לא מתחלפת לשמש אחרת!).
   אותו background כמו dusk => אין החלפת תמונה, רק ירידה (top) ועמעום (opacity) חלקים. */
body[data-daytime="night"] .sun {
    opacity: 0;
    top: 96%; /* שוקעת אל מתחת לקו האופק */
    left: min(11%, 9rem);
    width: clamp(92px, 11.5vw, 168px);
    aspect-ratio: 1;
    border-radius: 0;
    box-shadow: none;
    -webkit-mask: none;
    mask: none;
    background: url('/images/scene/day-sun.webp') center / contain no-repeat;
    /* חמה-אדמדמה ומעט מוחשכת ככל ששוקעת */
    filter: drop-shadow(0 0 18px rgba(255, 120, 50, 0.4))
            saturate(1.1) hue-rotate(-16deg) brightness(0.82);
    animation: none; /* בלי פעימה -רק שקיעה נקייה דרך ה-transition */
}

@keyframes sun-pulse {
    0%, 100% { transform: scale(1); }
    50%      { transform: scale(1.04); }
}

/* ---- אורות חלונות הטירה (לילה) ----
   שכבה שמיושרת אחד-לאחד על castle-wrap (אותם מיקום, רוחב ויחס תמונה),
   מעל שכבות ההחשכה -כך האור באמת זוהר מתוך החלונות. */
.castle-lights {
    position: absolute;
    bottom: calc(var(--water-h) + var(--ground-h) - var(--castle-sink));
    left: 50%;
    width: var(--castle-width);
    transform: translateX(-50%);
    aspect-ratio: 1672 / 941;
    z-index: 9;
    opacity: 0;
    transition: opacity 2.5s ease;
    pointer-events: none;
}

/* ---- מתי האורות דולקים (נשלט בניהול, ‎data-wlwhen‎) ----
   ברירת המחדל: משקיעה. מדליקים אור כשמחשיך, לא בחצות; בשמיים שעדיין
   בהירים העוצמה נמוכה יותר, אחרת האור נראה ככתם ולא כחלון.
   **סדר הכללים הוא המימוש**: "לילה בלבד" ו"תמיד" חייבים לבוא **אחרי**
   ברירת המחדל כדי לגבור עליה. */
body[data-daytime="dusk"]  .castle-lights { opacity: 0.55; }
body[data-daytime="night"] .castle-lights { opacity: 1; }
body[data-wlwhen="night"][data-daytime="dusk"] .castle-lights { opacity: 0; }
body[data-wlwhen="always"] .castle-lights { opacity: 1; }
/* מתג ראשי: מכבה את השכבה כולה, ולא רק את התזמון */
body.wl-off .castle-lights { display: none !important; }

/* מצב בדיקה: ?windows=debug -מסגרות ממוספרות על החלונות המועמדים */
.castle-lights.is-debug { opacity: 1; }

.wdebug {
    position: absolute;
    border: 2px solid #ff2d2d;
    background: rgba(255, 45, 45, 0.18);
    border-radius: 40% 40% 20% 20%;
}

.wdebug span {
    position: absolute;
    top: -20px;
    left: 50%;
    transform: translateX(-50%);
    font: bold 13px/1 'Heebo', sans-serif;
    color: #fff;
    background: #ff2d2d;
    padding: 2px 6px;
    border-radius: 8px;
}

/* ---- אור חלון ----
   **האור נשאר בתוך הפתח.** הגרסה הראשונה הוסיפה הילה (‎::after‎) שגלשה
   הרחק אל האבן שמסביב, וזה נקרא כאור ש"זולג החוצה מהחלון" ולא כחלון מואר.
   היום האלמנט **הוא** הפתח -בגבולות שנמדדו פיקסל-מדויק מ-‎castle.png‎ -
   והגרדיאנט מגיע עד דופנותיו ונעצר שם. אין שכבה שנייה, אין ‎blur‎, ואין
   דבר שיוצא מהמלבן. */
.wlight {
    position: absolute;
    /* ---- הצורה נגזרת מהפיקסלים של הטירה, לא מקירוב גיאומטרי ----
       ‎tools/make-window-masks.php‎ צורב את פתח החלון מ-‎castle.png‎ אל אטלס
       מסכות (תא לכל חלון, בטור). התא נמתח בדיוק לקופסה שנמדדה לאותו חלון,
       כלומר המיפוי זהותי - האור מקבל את קווי המתאר של הפתח המצויר ואינו
       יכול לגלוש אל האבן. ‎border-radius‎ נשאר כנפילה לאחור אם המסכה לא
       נטענה. הנוסחה היא סְפרייט רגיל: גובה המסכה ‎N‎ תאים, והתא ה-‎i‎ נבחר
       ב-‎i/(N-1)‎; ‎max(...,1)‎ מגן על המקרה של תא יחיד (חלוקה באפס). */
    -webkit-mask-image: var(--wmask); mask-image: var(--wmask);
    -webkit-mask-repeat: no-repeat;   mask-repeat: no-repeat;
    -webkit-mask-size: 100% calc(var(--wn, 1) * 100%);
    mask-size: 100% calc(var(--wn, 1) * 100%);
    -webkit-mask-position: 0 calc(var(--wi, 0) * 100% / max(var(--wn, 1) - 1, 1));
    mask-position: 0 calc(var(--wi, 0) * 100% / max(var(--wn, 1) - 1, 1));
    border-radius: 46% 46% 12% 12% / 34% 34% 8% 8%;
    /* הצבע נשלט בניהול ונפלט כשלושה מספרים (‎--wl-rgb‎) ולא כ-hex, כדי
       שאפשר יהיה לבנות ממנו את הגרדיאנט בשקיפויות שונות. ברירת המחדל היא
       צהוב חלש (בקשת המשתמש) -לא לבן-לוהט. הליבה שקופה למחצה, כדי שהפתח
       המצויר יישאר קריא מתחת לאור במקום להימחק. */
    background: radial-gradient(ellipse at 50% 46%,
        rgba(var(--wl-rgb, 255, 226, 154), 0.90) 0 40%,
        rgba(var(--wl-rgb, 255, 226, 154), 0.66) 72%,
        rgba(var(--wl-rgb, 255, 226, 154), 0.34) 92%,
        rgba(var(--wl-rgb, 255, 226, 154), 0) 100%);
    mix-blend-mode: screen;
    opacity: 0;
    /* המעבר הוא **אפס כברירת מחדל** (בקשת המשתמש: נדלק ונכבה, כמו מתג),
       ומתג ה-FADE בניהול מזרים לכאן משך אמיתי. ‎transition‎ עם ‎0ms‎ אינו
       עולה דבר, ולכן אין צורך בשני כללים. */
    transition: opacity var(--wl-fade, 0ms) ease-in-out;
    /* **בלי ‎will-change‎.** 17 חלונות = 17 שכבות קומפוזיטור קבועות, ואלמנט
       שרץ עליו מעבר ‎opacity‎ מקודם ממילא לזמן המעבר. אותו לקח שכבר נמדד כאן
       על שקופיות הקרוסלה: הרמז לא הוסיף ולו שכבה אחת, ורק הכריח צביעה מחדש
       בכניסה וביציאה. */
}
/* העוצמה נשלטת בניהול; 100% = האור המלא שנמדד */
.wlight.is-on { opacity: calc(var(--wl-level, 90) / 100); }

/* צמצום תנועה: האורות דולקים בעוצמה נמוכה וקבועה, בלי הדלקה וכיבוי */
@media (prefers-reduced-motion: reduce) {
    .wlight { opacity: 0.55 !important; }
}

/* ---- מתגי תצוגה קבועים (עיצוב האתר -נשמרים לכל המבקרים) ---- */
/* שעון כבוי -הסתרת שעון הטירה בעמוד הראשי */
body.no-clock .tower-clock { display: none !important; }
/* הסתרת השעון במובייל בלבד (מתג ניהול) */
@media (max-width: 640px) {
    body.no-clock-mobile .tower-clock { display: none !important; }
}

/* זמן כבוי -ביטול מערכת התאורה לפי שעה (שמש/ירח/שכבות החשכה/אורות חלונות) */
body.no-daylight .moon,
body.no-daylight .sun,
body.no-daylight .light-mul,
body.no-daylight .light-tint,
body.no-daylight .castle-lights { display: none !important; }
body.no-daylight .scene,
body.no-daylight .scene-front,
body.no-daylight .scene-front :is(.folk, .folk-say) { filter: none !important; }

/* אנימציה כבויה -הקפאת תנועת הסצנה (משאיר את ממשק הניהול פעיל) */
body.no-anim .scene *,
body.no-anim .scene *::before,
body.no-anim .scene *::after,
body.no-anim .tower-clock .tc-hand { animation-play-state: paused !important; }

/* הקפאה זמנית בזמן מעבר הכניסה לעמוד (pt-running, פחות משנייה).
   נמדד ב-WebKit: הסצנה מורידה את קצב הפריימים בטעינה ל-~2fps, ואז אנימציית המעבר
   מצוירת בשני פריימים בלבד ונראית כקפיצה. הקפאה קצרה מפנה פריימים למעבר. */
body.pt-running .scene *,
body.pt-running .scene *::before,
body.pt-running .scene *::after { animation-play-state: paused !important; }
/* אבל הדמויות המהלכות ממשיכות לזוז מיד (בקשת המשתמש) -תנועתן זולה ל-GPU (transform
   בלבד) ולא חונקת את המעבר כמו העננים/המים/הניצוצות. כך ב-F5 הן לא "קופאות" לרגע. */
body.pt-running .folk,
body.pt-running .folk-body { animation-play-state: running !important; }

/* מצב לילה לדמויות: בלילה מוצגים רק יצורי הלילה (הלוחם + הגמד), דלילים -
   "מדי פעם כי זה לילה". יצורי הלילה מרונדרים תמיד ומוסתרים כברירת מחדל. */
.folk-nocturnal { display: none; }
/* דמויות מעבר לכמות הנבחרת -מוסתרות (הכמות נשלטת חי מפאנל הניהול) */
.folk-overcount { display: none !important; }
/* דמות מכובה בבורר הדמויות -מוסתרת */
.folk-charoff { display: none !important; }
/* סוג הדמויות בעמוד -הסתרת סט שלם לפי בחירת העמוד (set-off-<key>).
   **מלכודת specificity שנמדדה:** כללי שעת-היום שלמטה הועלו ל-(0,6,1) בגלל
   שלושת ה-:not() שלהם, והכלל הזה היה (0,4,1) -כלומר סט שהוגדר עם data-time
   כלשהו (לא always) פשוט לא נכבה: "סוג הדמויות בעמוד זה" לא עשה כלום.
   ‎.scene‎ ושני ה-:not() כאן מעלים ל-(0,7,1) וגוברים. ה-:not() אינם קישוט:
   יצורי לילה אינם שייכים לסט, ודמות שכובתה בבורר ממילא מוסתרת. */
body.set-off-legends :is(.scene, .scene-front) .folk-layer .folk[data-set="legends"]:not(.folk-nocturnal):not(.folk-charoff),
body.set-off-tech :is(.scene, .scene-front) .folk-layer .folk[data-set="tech"]:not(.folk-nocturnal):not(.folk-charoff) { display: none !important; }
/* בלילה (מצב שמירה) -רק יצורי הלילה. specificity גבוה (‎.scene .folk-layer .folk.folk-count‎ = 0,7,1)
   כדי לגבור על כללי ה-data-time שהועלו ל-(0,6,1) בגלל ה-:not(). דמויות היום מקבלות folk-count; יצורי הלילה לא. */
body.night-chars[data-daytime="night"] :is(.scene, .scene-front) .folk-layer .folk.folk-count:not(.folk-nocturnal),
body.night-chars[data-daytime="night"] :is(.scene, .scene-front) .folk-layer-back .folk.folk-count:not(.folk-nocturnal) { display: none !important; }
body.night-chars[data-daytime="night"] .folk-nocturnal { display: block !important; }

/* ---- שעת התצוגה של סט דמויות (data-time, נקבע בפאנל הניהול) ----
   always = תמיד; dawn/day/dusk/night = מוצג רק בשלב-היום התואם. */
.folk[data-time="dawn"], .folk[data-time="day"], .folk[data-time="dusk"],
.folk[data-time="night"], .folk[data-time="dusknight"], .folk[data-time="dawnday"] { display: none !important; }
/* הצגה בשעה המתאימה -אך לא לדמויות שהוסתרו ע"י הכמות (folk-overcount) או בורר הדמויות (folk-charoff) */
body[data-daytime="dawn"]  .folk[data-time="dawn"]:not([hidden]):not(.folk-overcount):not(.folk-charoff),
body[data-daytime="day"]   .folk[data-time="day"]:not([hidden]):not(.folk-overcount):not(.folk-charoff),
body[data-daytime="dusk"]  .folk[data-time="dusk"]:not([hidden]):not(.folk-overcount):not(.folk-charoff),
body[data-daytime="night"] .folk[data-time="night"]:not([hidden]):not(.folk-overcount):not(.folk-charoff),
body[data-daytime="dusk"]  .folk[data-time="dusknight"]:not([hidden]):not(.folk-overcount):not(.folk-charoff),
body[data-daytime="night"] .folk[data-time="dusknight"]:not([hidden]):not(.folk-overcount):not(.folk-charoff),
body[data-daytime="dawn"]  .folk[data-time="dawnday"]:not([hidden]):not(.folk-overcount):not(.folk-charoff),
body[data-daytime="day"]   .folk[data-time="dawnday"]:not([hidden]):not(.folk-overcount):not(.folk-charoff) { display: block !important; }

/* ---- מצבי DEBUG (נשלטים מפאנל הניהול) ---- */
body.dbg-freeze *,
body.dbg-freeze *::before,
body.dbg-freeze *::after { animation-play-state: paused !important; }

body.dbg-outline .scene [data-depth],
body.dbg-outline .scene .obj {
    outline: 1px dashed rgba(255, 60, 60, 0.7);
    outline-offset: -1px;
}

body.dbg-grid::after {
    content: '';
    position: fixed;
    inset: 0;
    z-index: 200;
    pointer-events: none;
    background-image:
        linear-gradient(rgba(255, 255, 255, 0.18) 1px, transparent 1px),
        linear-gradient(90deg, rgba(255, 255, 255, 0.18) 1px, transparent 1px);
    background-size: 40px 40px;
}

/* ---- העדפת צמצום תנועה ---- */
/* ---- תושבי העולם: דמויות מהלכות ומדברות ---- */
.folk-layer {
    position: absolute;
    inset: 0;
    z-index: 7;
    pointer-events: none;
    overflow: hidden;
}
/* שכבה אחורית -דמויות שהולכות מאחורי אובייקטי הקדמת (ground-front z6) */
.folk-layer-back { z-index: 5; }
.folk {
    position: absolute;
    left: 0;
    /* --folk-scale מאפשר להגדיל/להקטין סט שלם (נקבע ב-config לכל סט) */
    width: calc(clamp(88px, 9.5vw, 140px) * var(--folk-scale, 1));
    aspect-ratio: 214 / 200; /* יחס התא בגיליון המנורמל (נדרס inline לסטים אחרים) */
    will-change: transform;
}
/* שכבת האחור (folk-layer-back) -דמויות שהולכות מאחורי האובייקטים, גבוה יותר על הדשא.
   הערה: לאלמנט האחורי יש גם את המחלקה folk-layer, לכן חובה להחריג אותו בעקיפה הקדמית. */
.folk-layer-back > .folk {
    /* גבוה יותר מהקדמית -ויחד עם ההקטנה שב-PHP זה הופך את המפגש בין השכבות
       לרמז עומק. הפרש של 1vh בלבד, כפי שהיה, נראה כשתי דמויות שנדחסו זו על זו. */
    bottom: calc(var(--water-h) + 2.8vh + var(--yoff, 0vh));
}
/* שכבת הקדמה בלבד (folk-layer ללא folk-layer-back) -הולכות על קצה הדשא הירוק:
   נמוך, ממש מעל קו המים ולא גבוה מדי. הסטגר (yoff) מוקטן לשמירה על קו-הליכה אחיד. */
.folk-layer:not(.folk-layer-back) > .folk {
    bottom: calc(var(--water-h) + 0.8vh + var(--yoff, 0vh) * 0.4);
}
/* צל קרקע מתחת לדמות -מעגן אותה ויזואלית (שלא תיראה מרחפת) */
.folk::after {
    content: '';
    position: absolute;
    left: 50%;
    bottom: 3%;
    width: 46%;
    height: 7%;
    transform: translateX(-50%);
    background: radial-gradient(ellipse, rgba(0, 0, 0, 0.32), transparent 72%);
    z-index: -1;
    pointer-events: none;
}
/* תנועה חלקה: שכבת GPU עצמאית שלא מושפעת מרענון הספריט */
/* backwards: כשה-delay חיובי (איפוס/כניסה) הדמות נשארת בפריים ההתחלתי -מחוץ למסך- עד כניסתה */
.folk-left  { animation: folk-move-left  var(--dur, 60s) linear var(--delay, 0s) infinite backwards; }
.folk-right { animation: folk-move-right var(--dur, 60s) linear var(--delay, 0s) infinite backwards; }

/* ---- טיפוס על החבית והארגז ----
   אנימציה **שנייה על אותו אלמנט**: ההליכה מנפישה ‎transform‎ והטיפוס מנפיש
   את הנכס ‎translate‎, שהוא נפרד ממנו - ולכן הן אינן דורסות זו את זו. אותו
   משך ואותה השהיה של ההליכה, ומכאן שהן מסונכרנות לחלוטין בלי שום קוד.
   הקיפריימים עצמם נפלטים מ-‎templates/scene.php‎ לפי מיקומי האובייקטים של
   העמוד; ‎has-climb‎ יושב על השכבה רק כשיש על מה לטפס. */
/* **‎:not(.folk-ownwalk)‎ הוא תנאי ולא קישוט.** הקיפריימים ממפים
   אחוז-מהאנימציה למיקום לפי מסלול ה**שכבה**; דמות שדורסת אותו במסלול משלה
   (שומרי הלילה) הייתה מתרוממת במקום אחר לגמרי - נמדד שהם 100% מהמקרים של
   "דמות באוויר בלי אביזר" בעמוד הבית. */
.folk-layer.has-climb:not(.folk-layer-back) > .folk-left:not(.folk-ownwalk) {
    animation: folk-move-left var(--dur, 60s) linear var(--delay, 0s) infinite backwards,
               folk-hop-l     var(--dur, 60s) linear var(--delay, 0s) infinite backwards;
}
.folk-layer.has-climb:not(.folk-layer-back) > .folk-right:not(.folk-ownwalk) {
    animation: folk-move-right var(--dur, 60s) linear var(--delay, 0s) infinite backwards,
               folk-hop-r      var(--dur, 60s) linear var(--delay, 0s) infinite backwards;
}

/* דמות מדברת -נעצרת במקום: מקפיאים את התנועה ואת מחזור ההליכה (עומדת ומדברת) */
.folk.speaking { animation-play-state: paused; }
.folk.speaking .folk-body { animation-play-state: paused; }

/* לחיצה/נגיעה בדמות -> קופצת ונוחתת. ה-clip מקבל את הקפיצה דרך תכונת translate
   (עצמאית מ-transform ה-inline: scaleX/scaleY/translateZ), כדי לא לפגוע בהיפוך/גודל. */
.folk { pointer-events: auto; cursor: pointer; }
.folk-clip.jumping { animation: folk-jump 0.6s cubic-bezier(0.25, 0.7, 0.3, 1); }
@keyframes folk-jump {
    0%   { translate: 0 0; }
    32%  { translate: 0 -30px; }
    48%  { translate: 0 -32px; }
    100% { translate: 0 0; }
}

/* מסגרת-חיתוך: מציגה פריים בודד (overflow hidden), ומחזיקה את ההיפוך+קנה-המידה (inline).
   הבועה (folk-say) אחות ל-clip -ולכן לא נחתכת. */
.folk-clip {
    position: absolute;
    inset: 0;
    overflow: hidden;
    transform-origin: bottom center;
    /* חשוב: אין backface-visibility:hidden כאן! ה-clip מקבל scaleX(-1) (היפוך פנייה),
       וכשהפן האחורי מוסתר, iOS Safari "מהבהב"/הופך את הדמות בזמן ההליכה (הדמות
       נראית פונה לכיוון ההפוך כל שנייה). ה-translateZ ב-transform ה-inline כבר מכריח
       שכבת GPU לחיתוך הפריים -אין צורך ב-will-change שגם גורם לקידום-כפול ולאותו באג. */
}
/* גוף הדמות -רצועת 4 הפריימים (רוחב 400%). מחזור ההליכה ב-transform:translateX (GPU),
   ולא ב-background-position -> בלי repaint פר-פריים -> חלק במובייל (DPR גבוה). */
.folk-body {
    position: absolute;
    left: 0;
    top: 0;
    /* --cols עמודות (פריימים) זו לצד זו. ברירת מחדל 4 (גיליון הדמויות המקורי). */
    width: calc(var(--cols, 4) * 100%);
    height: 100%;
    background-repeat: no-repeat;
    /* התמונה נמתחת לרוחב הרצועה (cols עמודות) ולגובה --rows שורות -> כל תא = גודל הדמות */
    background-size: 100% calc(var(--rows, 8) * 100%);
    background-position: 0 var(--row-y, 0%);
    /* מחזור ההליכה: משך פרופורציונלי למספר הפריימים (~0.15s לפריים) -> קצב רגל אחיד
       בכל סט (4 פריימים=0.6s, 10=1.5s). ה-steps נדרס inline ל-steps(--cols) כששונה מ-4. */
    animation: folk-walk calc(var(--cols, 4) * 0.15s) steps(4) infinite;
    will-change: transform;
    /* בלי backface-visibility:hidden -האלמנט יושב בתוך clip מהופך (scaleX-1), הפן
       האחורי פונה לצופה, ו-hidden היה גורם להיעלמות/היפוך לסירוגין ב-iOS. */
}

/* ---- בועת דיבור ----
   **הבועה חייבת להיות מעל הכול.** כילדה של ‎.folk‎ היא יורשת את מקומו בערימה:
   לכל דמות יש ‎z-index‎ ייחודי משלה (נפלט מ-PHP), ולכן דמות שעברה לידה עם ערך
   גבוה יותר כיסתה את הבועה, וכשהן חוצות זו את זו הבועה "מהבהבת" בין קדימה
   לאחורה עד שהיא נעלמת. גם ‎z-index‎ ענק על הדמות לא היה פותר: שתי שכבות
   הדמויות הן stacking contexts נפרדים, וילד אינו יכול לצאת מהשכבה שלו.
   לכן בזמן דיבור הבועה עוברת ל-‎.say-layer‎ -שכבה **בתוך שכבת הדמויות עצמה**,
   עם ‎z-index‎ שגובר על כל הדמויות שבה.

   **למה דווקא בתוך השכבה ולא שכבה נפרדת מעליה:**
   - על ה-body היא הייתה מעל תוכן האתר (‎.content‎ z10) ומכסה את הטקסט, ובזום של
     הטלפון ‎position: fixed‎ נתלש מהדמות (iOS מעגן fixed ל-viewport החזותי).
   - כשכבה נפרדת בתוך הסצנה היא נשארה במקום כשהמצלמה זזה: אנימציית הפאן רצה
     ב-compositor, ואילו ‎getBoundingClientRect‎ מחזיר את מצב ה-main-thread -
     כלומר הבועה "נסעה עם המצלמה" במקום עם הדמות.
   בתוך השכבה היא יורשת את אותו transform בדיוק, ולכן אין מה לסנכרן. */
.say-layer { position: absolute; inset: 0; z-index: 999; pointer-events: none; }
/* **שכבה ריקה אינה קיימת.** ‎.say-layer‎ פרוסה על **כל רוחב העולם** (‎inset: 0‎
   בתוך ‎.folk-layer‎ שנמתחת ל-‎zones * 100vw‎) ויושבת מעל תריסר שכבות דמויות
   מקודמות, ולכן הקומפוזיטור נאלץ לקדם גם אותה - נמדד: **שתי שכבות של
   1464x1055, יחד 11.8MB**, הפריט היקר ביותר בעמוד כולו, בזמן שהן ריקות.
   והן ריקות כמעט תמיד: הבועה עוברת לכאן רק בשניות שבהן דמות מדברת.
   ‎:empty‎ יורד ברגע שהבועה מוצבת בפנים, כלומר לפני ה-‎rAF‎ שמודד את המלבן. */
.say-layer:empty { display: none; }
.folk-say.is-float { position: absolute; left: 0; top: 0; bottom: auto; transform: none; }
.folk-say {
    position: absolute;
    bottom: 80%;
    left: 50%;
    transform: translateX(-50%);
    background: var(--bubble-bg, #fffdf6);
    color: var(--bubble-text, #2a1c0e);
    border: 2px solid var(--bubble-border, var(--c-gold-deep));
    border-radius: 12px;
    padding: 0.25rem 0.6rem;
    font-family: 'Heebo', sans-serif;
    font-size: 0.72rem;
    line-height: 1.4;
    text-align: center;
    /* משפטים ארוכים נשברים לכמה שורות -בטוח למובייל, בלי לגלוש מהמסך */
    white-space: normal;
    max-width: min(64vw, 230px);
    width: max-content;
    overflow-wrap: break-word;
    box-shadow: 0 3px 9px rgba(13, 62, 120, 0.35);
    opacity: 0;
    transition: opacity 0.3s ease, transform 0.3s ease;
}
.folk-say[hidden] { display: none; }
.folk-say.show { opacity: 1; }
/* סמן ההקלדה -קו מהבהב שנעלם כשהמשפט הושלם */
.folk-caret {
    display: inline-block;
    width: 0.5ch;
    margin-inline-start: 1px;
}
.folk-say.typing .folk-caret::after {
    content: '▌';
    animation: folk-caret-blink 0.6s steps(1) infinite;
}
@keyframes folk-caret-blink { 50% { opacity: 0; } }
.folk-say::after {
    content: '';
    position: absolute;
    top: 100%;
    left: 50%;
    transform: translateX(-50%);
    border: 6px solid transparent;
    border-top-color: var(--bubble-bg, #fffdf6);
}

/* מחזור הליכה: 4 עמודות בספריט עם "החזקה" צמודה -snap פר-פריים, בלי פריים ריק */
@keyframes folk-walk {
    /* מזיז את הרצועה שמאלה בפריים בכל צעד (steps(4)): 0 / -25% / -50% / -75% מרוחב הרצועה */
    from { transform: translateX(0); }
    to   { transform: translateX(-100%); }
}
/* גבולות מסלול ההליכה -ברירת מחדל בלבד. הערכים בפועל נפלטים מ-PHP על
   ‎.folk-layer‎ (נגזרים ממספר הדמויות, כדי שהמרווח ביניהן לא יקטן מרוחב דמות)
   והדמויות יורשות אותם. **חובה שהכלל כאן יהיה על השכבה ולא על ‎.folk‎**:
   הגדרה על ‎.folk‎ עצמו דורסת את הירושה מהשכבה, והמסלול היה נשאר הקבוע הישן. */
.folk-layer { --walk-a: -26vw; --walk-b: 106vw; }
@keyframes folk-move-left  { from { transform: translate3d(var(--walk-b), 0, 0); } to { transform: translate3d(var(--walk-a), 0, 0); } }
@keyframes folk-move-right { from { transform: translate3d(var(--walk-a), 0, 0); } to { transform: translate3d(var(--walk-b), 0, 0); } }

@media (prefers-reduced-motion: reduce) {
    .layer-sky, .cloud, .cflag, .cflag::before, .cbanner, .spark, .water-surface, .wlight, .sun { animation: none; }
    .water-surface::before, .water-surface::after { animation: none; }
    .wlight { opacity: 1; }
    .spark { opacity: 0.7; }
    .folk-layer { display: none; } /* דמויות מהלכות -תנועה, מוסתרות בצמצום תנועה */
}

/* ---- טאבלט ---- */
@media (max-width: 1024px) {
    body {
        --castle-width: 100vw;
        --castle-sink: 11vh;
    }
}
/* **‎--obj-scale‎ = ההגדלה שהאובייקטים מקבלים בפועל.** האביזרים מוגדלים
   ב-1.28 בטאבלט וב-1.85 במובייל (‎transform-origin: bottom center‎), כלומר
   גגם עולה באותו יחס - והטיפוס של הדמויות נגזר מהגובה הזה. שני מספרים
   נפרדים היו מתפצלים בשינוי הבא, ולכן הכול קורא את המשתנה הזה. */
.scene { --obj-scale: 1; }
@media (min-width: 641px) and (max-width: 1024px) { .scene { --obj-scale: 1.28; } }
@media (max-width: 640px) { .scene { --obj-scale: 1.85; } }

/* ---- מובייל: הטירה רחבה מהמסך כדי שתישאר דומיננטית במסך צר וגבוה ---- */
@media (max-width: 640px) {
    body {
        --castle-width: 135vw;
        --water-h: 6vh;
        --ground-h: 4vh;
        --castle-sink: 7vh;
    }

    .castle-wrap, .castle-lights { width: 135vw; }
    .moon { top: 7%; left: 6%; }
    .scene { --moon-cx: calc(6% + 3.5vw); --moon-cy: calc(7% + 3.5vw); }
    .obj { display: none; }
    .obj:nth-child(-n+6) { display: block; } /* במובייל -רק האובייקטים הגדולים */
    /* בתי הרקע נבחרו **במפורש** בניהול לעמוד הזה, ולא נולדו כעיטור מקרי כמו
       פרח או שיח - "רק ששת הראשונים" היה מסתיר אותם בשקט בכל עמוד שכבר נושא
       תבנית עמוסה (התבנית הבסיסית לבדה מונה כ-27 אובייקטים קדמיים, והבתים
       תמיד מתווספים בסופם). באותה specificity בדיוק (0,2,0) כמו הכלל שמעליו,
       ולכן מנצח **בזכות סדר בקובץ** בלי `!important`. */
    .obj[data-sprite^="house"] { display: block; }
    /* האובייקטים ב-vw טהור מתכווצים במובייל בזמן שהדמות "רצפה" ב-88px -מגדילים
       אותם כדי שיהיו פרופורציונליים לדמויות (transform-origin תחתית = נשארים על הקרקע). */
    .obj { transform: scale(1.85); transform-origin: bottom center; }
    .obj.is-flipped { transform: scale(-1.85, 1.85); transform-origin: bottom center; }
    /* העצים כבר גדולים -הגדלה של 1.85 הופכת אותם לענקיים וגורמת לשני עצים סמוכים
       להיתקל (מרכזיהם קבועים, הרוחב גדל). לכן הם מוקטנים למכפיל עדין יותר,
       וכך עץ קדמי ועץ אחורי באותו צד שוב אינם חופפים. (0,2,1) גובר על .obj. */
    .obj[data-sprite^="tree"] { transform: scale(1.28); }
    .obj[data-sprite^="tree"].is-flipped { transform: scale(-1.28, 1.28); }
    /* הבתים תוכננו לגודל-רינדור סופי כבר בדסקטופ (20-31vw, גדולים בהרבה
       מהעצים) - בלי המכפיל הגורף בכלל הכי הם היו כמעט חוצים את המסך במובייל. */
    .obj[data-sprite^="house"] { transform: none; }

    /* ---- אין טיפוס במובייל ----
       ‎.obj‎ מוסתר כאן חוץ מששת הראשונים, והחבית והארגז הם האחרונים בכל
       התבניות - כלומר הם **אינם מצוירים כלל** בטלפון. בלי הכיבוי הזה הדמות
       עולה, צועדת ויורדת מעל אוויר: נמדד ב-390px שהיא מתרוממת 12-16px בזמן
       שרוחב האביזר הוא 0. הסלקטור חייב את אותה ספציפיות (0,3,2) של הכלל
       שלמעלה ולשבת אחריו בקובץ. */
    .folk-layer.has-climb:not(.folk-layer-back) > .folk-left:not(.folk-ownwalk) {
        animation: folk-move-left var(--dur, 60s) linear var(--delay, 0s) infinite backwards;
    }
    .folk-layer.has-climb:not(.folk-layer-back) > .folk-right:not(.folk-ownwalk) {
        animation: folk-move-right var(--dur, 60s) linear var(--delay, 0s) infinite backwards;
    }

    /* ---- אופטימיזציית ביצועים למובייל (iOS Safari) ----
       הקומפוזיטור נחנק מ-screen-blend על עשרות ניצוצות + ציור ספריט של 6 דמויות,
       ואז גם תנועת ה-GPU של הדמויות מקרטעת. מפחיתים את העומס: */
    .spark { mix-blend-mode: normal; }               /* screen blend יקר מאוד ב-iOS */
    /* drop-shadow על הטירה (אלמנט ענק) -נמדד כמחצית מעלות הציור של העמוד ב-WebKit.
       הטירה יושבת על ההר ממילא, ההצללה כמעט לא נראית במסך קטן. */
    .castle-wrap { filter: none; }
    /* **ובנסיעה -אותו ערך בדיוק, ולא "צל שקוף".** במסך הזה אין לטירה פילטר
       במנוחה, ולכן כלל הנסיעה חייב להישאר ‎none‎ גם הוא: כל ערך אחר היה
       **מדליק** פילטר על אלמנט של 135vw ברגע הלחיצה ומכבה אותו בסוף -בדיוק
       שינוי הרסטור שהכלל ההוא בא למנוע, ודווקא במכשיר שבו זה נראה. */
    .scene.is-panning .castle-wrap { filter: none; }
    .spark-castle { display: none; }                 /* נצנוצי הטירה -פחות חיוניים */
    .spark-mobhide { display: none; }                /* כשליש מהניצוצות מוסתרים במובייל */
    .cloud-mobhide { display: none; }                /* מחצית מהעננים מוסתרים -פחות רוחב-פס לקומפוזיטור */
    /* אנימציות מבוססות filter (זוהר drop-shadow של השמש) יקרות מאוד פר-פריים ->
       השמש סטטית במובייל (שומרים סיבוב בזריחה -זה transform זול). */
    body[data-daytime="day"] .sun,
    body[data-daytime="dusk"] .sun { animation: none; }
    body[data-daytime="dawn"] .sun { animation: sun-rise-spin 60s linear infinite; }
    /* הבלום ממשיך לפעום גם במובייל -אנימציית opacity/scale על אלמנט בודד (זולה ל-GPU) */
    /* כוכבי לילה: drop-shadow כפול שמתעדכן בכל הבהוב -> filter פשוט (bright) במובייל */
    body[data-daytime="night"] .spark-high { filter: brightness(3.2); }
    /* (כאן ישבה השבתת זרימת המים. היא הייתה נכונה כל עוד הזרימה הייתה
       ‎background-position‎ -ציור מחדש של שלוש שכבות בכל פריים. היום
       הזרימה היא ‎translate‎ שרץ בקומפוזיטור, כלומר אפס ציור פר-פריים
       בלי קשר לרוחב, ולכן אין מה לכבות. הדיווח שהחזיר את זה: "הנהר לא
       מונפש במובייל".) */
    /* "נשימת" השמיים -שכבה גדולה מאוד במובייל; מכבים אותה */
    .layer-sky { animation: none; }
    /* צמצום קל של דמויות במובייל (2 קדמיות + 1 אחורית = 3), שומר על יצורי הלילה */
    .folk-layer > .folk:not(.folk-nocturnal):nth-of-type(n+3),
    .folk-layer-back > .folk:not(.folk-nocturnal):nth-of-type(n+2) { display: none; }
    .folk::after { display: none; }                  /* צל radial-gradient -פחות ציור */

    /* התנועה מבוססת vw + משך קבוע => במסך צר חוצים פחות פיקסלים לשנייה (נראה איטי
       ומדשדש). מקצרים את המשך כדי לשמור על מהירות-פיקסלים סבירה (ואת ה-delay באותו יחס). */
    .folk-left, .folk-right {
        animation-duration: calc(var(--dur, 60s) * 0.34);
        animation-delay: calc(var(--delay, 0s) * 0.34);
    }
    /* מחזור ההליכה נשאר בקצב הטבעי (0.6s) -רק מהירות הגוף מכווננת לרזולוציה */
}

/* ---- העומס שיורד בכל מכשיר מגע, לא רק בטלפון ----
   **הדיווח: "הטירה איטית בתצוגה, והמצלמה בין העמודים זזה לאט" -באייפד פרו.**
   כל בלוק ה"טאבלט" בקובץ הזה נעצר ב-1024px, ו-iPad בלנדסקייפ הוא **1194** -
   כלומר הוא קיבל את מלוא העומס של המחשב עם GPU של נייד. **נמדד**: זיכרון שכבות
   הקומפוזיטור ‎148.7MB‎ מול ‎48.5MB‎ באייפון, ותמונות ‎25.3MP‎ מול ‎6.1MP‎.

   **הקריטריון הוא ‎pointer: coarse‎ ולא רוחב**, כי מה שנגמר כאן הוא תקציב
   הקומפוזיטור של המכשיר - וזה אינו נגזר מכמה פיקסלים יש לו לרוחב. הכללים
   כפולים במכוון למה שכבר בבלוק של 640: שכפול זהה בטוח לגמרי, ואילו הרחבת
   הבלוק ההוא הייתה גוררת איתה גם את הגאומטריה (‎--castle-width‎, ‎--water-h‎)
   ואת הסתרת האלמנטים - שני דברים שאסור להם לחול על טאבלט.

   **מה שאינו כאן, ובמכוון:** הסתרת עננים, ניצוצות, נצנוצי טירה, דמויות וצל
   הדמות. נמדד שהם **אינם קונים דבר** באייפד (ציורי שכבה-מלאה 432 מול 417,
   בתוך הרעש) - כלומר הם היו גורעים מהסצנה בלי תמורה. */
@media (pointer: coarse) {
    /* screen blend על עשרות ניצוצות -יקר מאוד ב-iOS */
    .spark { mix-blend-mode: normal; }
    /* ‎drop-shadow‎ על אלמנט של 135vw. **ובנסיעה אותו ערך בדיוק ולא "צל שקוף"**:
       כאן אין לטירה פילטר במנוחה, וכל ערך אחר היה **מדליק** פילטר ברגע הלחיצה
       ומכבה אותו בסוף - בדיוק שינוי הרסטור שהכלל ההוא בא למנוע. */
    .castle-wrap { filter: none; }
    .scene.is-panning .castle-wrap { filter: none; }
    /* אנימציות ‎filter‎ יקרות פר-פריים; זריחה נשארת -היא ‎transform‎ וזול */
    body[data-daytime="day"] .sun,
    body[data-daytime="dusk"] .sun { animation: none; }
    body[data-daytime="night"] .spark-high { filter: brightness(3.2); }
    /* (השבתת זרימת המים ירדה גם כאן -ראה ההערה בבלוק של 640px.) */
    /* "נשימת" השמיים -השכבה הגדולה בסצנה */
    .layer-sky { animation: none; }
}

/* טאבלט -תיקון מהירות ביניים (רוחב בינוני) + הגדלת אובייקטים מתונה */
@media (min-width: 641px) and (max-width: 1024px) {
    .folk-left, .folk-right {
        animation-duration: calc(var(--dur, 60s) * 0.62);
        animation-delay: calc(var(--delay, 0s) * 0.62);
    }
    .obj { transform: scale(1.28); transform-origin: bottom center; }
    .obj.is-flipped { transform: scale(-1.28, 1.28); transform-origin: bottom center; }
}

/* ---- ציפורים בשמיים (סילואטות מנפנפות שחוצות את המסך) ---- */
.birds { position: absolute; inset: 0; z-index: 2; pointer-events: none; overflow: hidden; }
/* בפאן הציפורים שייכות **לעולם** ולא למסך: השכבה נמתחת על כל רוחב העולם ונעה
   עם המצלמה ככל שכבת-עולם אחרת, כך שציפור שחוצה את השמיים ממשיכה במעבר בין
   עמודים במקום להיראות תקועה במקומה. --k נמוך (0.3) -הן גבוהות ורחוקות, בין
   העננים (0.18) לקרקע הרחוקה (0.85).
   ה-overflow מוסר: הוא נחתך לפי המסך והיה גוזם את החלק שמעבר לו.
   הרוחב/המסלול נפלטים מ-PHP (--bird-span), כי הם נגזרים ממספר האזורים. */
body.pan-on .birds {
    left: calc((var(--zones, 1) - 1) * -1 * var(--pan-step, 100vw));
    right: auto;
    width: calc(var(--zones, 1) * var(--pan-step, 100vw));
    overflow: visible;
    --k: 0.3;
    transform: translateX(calc((0 - var(--cam, 0)) * var(--pan-step, 100vw) * var(--pan-dir, -1) * var(--k)));
}
.bird {
    position: absolute; left: 0;
    width: calc(24px * var(--s, 1)); height: calc(11px * var(--s, 1));
    /* השקיפות נגזרת מהגודל: ציפור קטנה = רחוקה = חיוורת יותר. ערך אחיד לכולן
       שיטח את השמיים -כל הציפורים נראו באותו מרחק, רק בגדלים שונים.
       הרצפה 0.42 ולא נמוכה מזה: הקטנות דקות ממילא, ומתחת לזה הן נעלמות ברקע. */
    opacity: calc(0.42 + var(--s, 1) * 0.2);
    /* ‎bird-cross-r/-l‎ נפלטים מ-PHP (scene.php) עם ערכים קונקרטיים: ‎var()‎
       בתוך keyframes מוריד את האנימציה לחוט הראשי (המלכודת המתועדת של הפאן),
       ונמדד שכל 30 הציפורים תיקתקו סגנון בכל פריים -משימות ה-raster במנוחה
       ירדו 30% כשהוסר. הכיוון הוא המחלקה ‎bird-rtl‎ ולא ‎scaleX(var(--dir))‎,
       מאותה סיבה בדיוק. */
    animation: bird-cross-r var(--dur, 36s) linear var(--delay, 0s) infinite;
    will-change: transform;
}
.bird.bird-rtl { animation-name: bird-cross-l; }
.bird-w {
    position: absolute; top: 0; width: 52%; height: 100%;
    border-top: 2px solid rgba(40, 52, 74, 0.65);
    border-radius: 65% 65% 0 0;
}
.bird-wl { left: 0;  transform-origin: right center; animation: bird-flap var(--flap, 1.15s) ease-in-out infinite; }
.bird-wr { right: 0; transform-origin: left center;  animation: bird-flap var(--flap, 1.15s) ease-in-out infinite; }
/* **במובייל הכנפיים אינן מתנפנפות.** כל כנף היא אנימציה נפרדת, ולכן שכבת
   compositing נפרדת: **נמדד** שהציפורים לבדן הן 45 מתוך 146 שכבות בעמוד.
   בציפור ברוחב 24px הנפנוף כמעט אינו נראה, והתנועה האופקית -שנשארת -היא מה
   שקורא כ"ציפור עפה". זו החלפה של פרט שאינו נראה בשליש מהשכבות. */
@media (max-width: 640px) {
    .bird-wl, .bird-wr { animation: none; }
}
/* המסלול נגזר מ---bird-span (נפלט מ-PHP לפי מספר האזורים), כדי שהציפור תחצה
   את **העולם** ולא רק את המסך הראשון. המשך גדל באותו יחס -> אותה מהירות מעוף.
   ה-keyframes עצמם (‎bird-cross-r/-l‎) נפלטים מ-scene.php עם הערך הקונקרטי -
   ‎var()‎ בתוכם היה מוריד את כל הציפורים לחוט הראשי. אסור להחזיר אותם לכאן
   בצורת ‎var()‎. */
.birds { --bird-span: 126vw; }
@keyframes bird-flap {
    0%, 100% { transform: rotate(20deg); }
    50%      { transform: rotate(-12deg); }
}
/* בלילה -כהות יותר; בשקיעה מעט חמות */
body[data-daytime="night"] .bird { opacity: 0.4; }
/* ציפור מעבר לכמות שנבחרה בניהול -מוסתרת (הכמות נשלטת חי מפאנל הניהול) */
.bird.bird-off { display: none !important; }
@media (prefers-reduced-motion: reduce) { .birds { display: none; } }
/* פחות ציפורים במובייל -אחת מכל שתיים, כדי שיישארו גם קרובות וגם רחוקות
   (הרשימה מסודרת גדולות ואז קטנות; חיתוך לפי מספר היה משאיר רק סוג אחד). */
@media (max-width: 640px) { .bird:nth-of-type(2n) { display: none; } }

/* ---- הסתרה חיה של רכיבי סצנה (מתגי הניהול) ---- */
/* **‎sky‎ מסתיר את שכבת ההרים בלבד** -‎.layer-sky‎ נושאת את ציור השמיים
   וההרים, וכשהיא יורדת נשאר הצבע שמאחוריה. לכן הוא ניתן לבחירה
   (‎--sky-solid‎), אחרת המסך היה נשאר בצבע קשיח שאיש לא בחר. */
/* **‎sun‎ מכבה גם את השמש וגם את הירח.** הם אותו גרם שמיים בשתי שעות
   ומתחלפים ביניהם לפי ‎data-daytime‎, ולכן מתג שמכבה אחד מהם היה "לא עובד"
   בדיוק בחצי מהשעות -אותה מלכודת שכבר תועדה כאן על נצנוצי הטירה. */
/* ---- הסתרה שהיא דעיכה, לא היעלמות ----
   **בקשת המשתמש: מעבר בין עמודים עם ובלי רכיבי רקע -‎FADE‎ בלבד.**
   ‎display: none‎ אינו מונפש, ולכן כל מתג שנבדל בין שני עמודים היה קופץ
   ברגע שהמצלמה עוצרת. ‎opacity‎ ו-‎visibility‎ שניהם מונפשים ושניהם
   בקומפוזיטור, ו-‎visibility‎ הוא מה שמוציא את השכבה מהיפ-טסט ומסדר
   ה-Tab בסוף (ערך ביניים של פונקציית תזמון ממופה ל-‎visible‎, ולכן היא
   נשארת גלויה לכל אורך הדעיכה ונחבאת רק בסופה).

   **כל שכבות הסצנה ממוקמות מוחלטות**, ולכן ‎visibility‎ במקום ‎display‎
   אינו משנה את הפריסה של דבר.

   ‎zone-off‎ **אינו** משתנה כאן והוא נשאר ‎display: none‎ מיידי -שם זה
   מתועד כתקין: ‎panFillZone‎ ממלא אזור ומסמן אותו באותו טיק, ודעיכה שם
   ציירה תוכן שנכנס למסך ודהה בתוכו ("העץ שנעלם כשהמצלמה זזה"). */
.birds,
.castle-doors,
.castle-lights,
.castle-wrap,
.cbanner,
.cflag,
.folk-layer,
.ground-back,
.ground-front,
.house-fx-flag,
.moon,
.spark-castle,
.spark-high,
.spark-low,
.sun,
.water {
    transition: opacity 0.5s ease, visibility 0.5s;
}
body.scene-off-sun .sun,
body.scene-off-sun .moon,
body.scene-off-castle .castle-wrap,
body.scene-off-castle .castle-lights,
body.scene-off-castle .castle-doors,
body.scene-off-characters .folk-layer,
body.scene-off-birds .birds,
body.scene-off-flags .cflag,
body.scene-off-flags .house-fx-flag,
body.scene-off-banners .cbanner,
body.scene-off-castleSparkles .spark-castle,
body.scene-off-water .water,
body.scene-off-backProps .ground-back,
body.scene-off-frontProps .ground-front,
body.scene-off-sparkles .spark-high,
body.scene-off-sparkles .spark-low { opacity: 0 !important; visibility: hidden !important; }
@media (prefers-reduced-motion: reduce) {
    .birds,
.castle-doors,
.castle-lights,
.castle-wrap,
.cbanner,
.cflag,
.folk-layer,
.ground-back,
.ground-front,
.house-fx-flag,
.moon,
.spark-castle,
.spark-high,
.spark-low,
.sun,
.water { transition: none; }
}
/* **ההרים דוהים ואינם נעלמים** (בקשת המשתמש). ‎display‎ אינו מונפש, ולכן
   הם היו יורדים בבת אחת ברגע שהמצלמה עוצרת -"סתם מופיע". ‎opacity‎
   ו-‎visibility‎ שניהם מונפשים ושניהם בקומפוזיטור, ו-‎visibility‎ הוא מה
   שמוציא את השכבה מהציור בסופו של דבר (ערך ביניים של פונקציית תזמון ממופה
   ל-‎visible‎, ולכן הוא נשאר גלוי לכל אורך הדעיכה ונחבא רק בסוף).
   ‎.layer-sky‎ אינה שכבה פר-אזור אלא רצועה אחת רציפה, ולכן מחלקת ה-body
   מספיקה כאן ואין צורך ב-‎zone-off‎. */
.layer-sky { transition: opacity 0.5s ease, visibility 0.5s; }
body.scene-off-sky .layer-sky { opacity: 0; visibility: hidden; }
/* **גם העננים.** הם חלק מאותו רקע, ובניווט אל עמוד שכיבה אותם הם היו
   נעלמים בבת אחת בסוף הנסיעה בזמן שההרים כבר דהו לידם -כלומר חצי מעבר.
   ‎.cloud‎ מנפיש ‎transform‎ בסחיפה שלו, ולכן כאן ‎opacity‎ בלבד -שתי
   התכונות נפרדות ואינן דורסות זו את זו. */
.cloud { transition: opacity 0.5s ease, visibility 0.5s; }
body.scene-off-clouds .cloud { opacity: 0; visibility: hidden; }
/* הצבע שמתגלה מתחת לשכבת ההרים כשהיא מוסתרת. יושב על ‎.scene‎ ולא על
   ה-‎body‎, כדי שהוא ייחתך יחד איתה ולא יבצבץ מאחורי הפוטר.
   **קיים תמיד ושקוף**, ולא נוצר רק כשהמתג כבוי: פסבדו שנולד ברגע שהתנאי
   מתקיים אינו יכול לדהות -אין לו מצב קודם לעבור ממנו. */
.scene::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    opacity: 0;
    background: var(--sky-solid, linear-gradient(180deg, #1a3c66 0%, #2f6ea8 62%, #4da8e8 100%));
    pointer-events: none;
    transition: opacity 0.5s ease;
}
body.scene-off-sky .scene::before { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
    .layer-sky, .cloud, .scene::before { transition: none; }
}

/* ---- פרופיל הביצועים (טאב "ביצועים" בניהול) ----
   מתגי ההשהיה של הפרופיל. הכמויות ("עד N") נפלטות כ-‎nth-of-type‎ בבלוק
   ‎#perfStyle‎ שב-layout.php -לא כאן, כי המספר דינמי.

   **‎paused‎ ולא ‎display: none‎ למים ולדגלים**: המסך שולט ב"כמה רץ", לא
   ב"מה קיים" -מים קפואים עדיין נראים כמים, ודגל קפוא כדגל. אורות החלונות
   ונצנוצי הטירה כן מוסתרים -אור קפוא באמצע הבהוב נקרא כתקלה.
   ‎!important‎ -אותו נימוק של ‎body.no-anim‎ שמעליו בקובץ. */
body.perf-water-still .water-surface,
body.perf-water-still .water-surface::before,
body.perf-water-still .water-surface::after { animation-play-state: paused !important; }
/* ‎.cflag::before‎ ולא ‎.cflag‎: מאז שהדגל עבר ל-‎transform‎ האנימציה יושבת
   על הפסאודו, וכלל על ההורה אינו מגיע אליה. */
body.perf-flags-still .cflag::before,
body.perf-flags-still .cbanner,
body.perf-flags-still .house-fx-flag,
body.perf-flags-still .house-fx-crown { animation-play-state: paused !important; }
body.perf-wlights-off .castle-lights { display: none !important; }
body.perf-csparks-off .spark-castle { display: none !important; }

/* ---- מתגי הסצנה כשהפאן דלוק ----
   בפאן חיים בסצנה כמה אזורים בו-זמנית, אבל מחלקת ה-body היא של העמוד הנוכחי
   בלבד -היא הייתה מכבה גם את הטירה/האובייקטים של האזור השכן. לכן שכבות-אזור
   ([data-zone]) משוחררות מהמתג הגלובלי, וכל אחת נושאת את המצב שלה ב-zone-off
   (נכתב ב-js מה-body של העמוד שהיא הגיעה ממנו). ה-specificity מכוון: כלל
   ה-zone-off חייב לגבור על השחרור, ולכן שניהם באותה רמה וזה מופיע אחריו. */
body.pan-on.scene-off-castle .castle-wrap[data-zone],
body.pan-on.scene-off-castle .castle-doors[data-zone],
body.pan-on.scene-off-castle .castle-lights[data-zone],
body.pan-on.scene-off-backProps .ground-back[data-zone],
body.pan-on.scene-off-frontProps .ground-front[data-zone] { display: revert !important; }
/* **חייב להיות מיידי, בלי עמעום.** נוסה כאן עמעום 0.35s והוא יצר בדיוק את הבאג
   שנועד למנוע: ‎panFillZone‎ ממלא את מיכל האזור בתוכן **ומסמן zone-off באותו tick**,
   ולכן ב-‎display: none‎ הדפדפן לעולם אינו מצייר תוכן של אזור מכובה. עם עמעום הוא
   כן צויר - ונמדד עץ של אזור היעד שנכנס למסך ודהה בתוכו ב-x=256 מתוך 1280
   ("העץ שנעלם כשהמצלמה זזה"). ההיפוך המיידי כאן הוא היפוך של משהו שמעולם לא נראה. */
body.pan-on .scene [data-zone].zone-off { display: none !important; }

/* ================= לחיצה על השמש/הירח: סיבוב + קונפטי ================= */
/* **‎pointer-events‎ נפתח רק כשהם באמת מוצגים.** שקיפות אינה מסירה מהיפ-טסט
   (חוק מתועד בפרויקט), ושמש שקופה ביום-לילה הייתה בולעת לחיצות על הסצנה. */
body[data-daytime="dawn"] .sun,
body[data-daytime="day"]  .sun,
body[data-daytime="dusk"] .sun,
body[data-daytime="night"] .moon {
    pointer-events: auto;
    cursor: pointer;
    /* מבטל את "הקש-פעמיים-להגדלה" בטלפון: שתי נגיעות מהירות הן שני סיבובים,
       לא זום. אותו כלל שכבר נקנה בכפתורי הקרוסלה. */
    touch-action: manipulation;
}
/* ---- הירח: נדלק לכמה שניות וחוזר (בקשת המשתמש) ----
   בלי סיבוב ובלי קונפטי - בלילה הוא מקור האור של הסצנה, וסיבוב שלו נקרא
   כתקלה. הבהרה ‎filter‎ + הילה מתרחבת, ואז דעיכה איטית חזרה.
   **‎body[data-daytime="night"]‎ בסלקטור, וזו לא קישוט:** הכלל שמציג את
   הירח הוא ‎(0,2,1)‎, ומחלקה לבדה ‎(0,2,0)‎ הפסידה לו - ההבהוב פשוט לא היה
   מופיע. נמדד: הסלקטור שניצח היה ‎day-sun-glow‎, לא שלי. */
body[data-daytime="night"] .moon.is-hit {
    animation: sky-moon-glow 3.4s cubic-bezier(0.2, 0.8, 0.3, 1);
}
@keyframes sky-moon-glow {
    0% {
        filter: brightness(2.3);
        box-shadow: 0 0 26px 2px rgba(235, 240, 255, 0.25), 0 0 90px 10px rgba(190, 205, 255, 0.12);
    }
    14% {
        filter: brightness(3.6);
        box-shadow: 0 0 52px 18px rgba(248, 252, 255, 0.9), 0 0 150px 50px rgba(200, 218, 255, 0.55);
    }
    45% {
        filter: brightness(3.3);
        box-shadow: 0 0 46px 15px rgba(245, 250, 255, 0.72), 0 0 132px 42px rgba(196, 214, 255, 0.42);
    }
    100% {
        filter: brightness(2.3);
        box-shadow: 0 0 26px 2px rgba(235, 240, 255, 0.25), 0 0 90px 10px rgba(190, 205, 255, 0.12);
    }
}
/* **הקונפטי יושב על ה-‎body‎ ולא בסצנה.** הסצנה נושאת ‎filter‎ בלילה
   ו-‎transform‎ בפאן המצלמה - בתוכה הפיסות היו מוחשכות ונגררות עם העולם. */
.sky-confetti {
    position: fixed; inset: 0; z-index: 60;
    pointer-events: none; overflow: hidden;
}
.sky-cf {
    position: absolute; display: block;
    margin: -4px 0 0 -4px;              /* מרכוז הפיסה על נקודת הפיצוץ */
    border-radius: 2px;
    will-change: translate, rotate;     /* חיים קצרים ומוסרות בסיום */
}
.sky-cf.is-round { border-radius: 50%; }
@media (prefers-reduced-motion: reduce) {
    body[data-daytime="night"] .moon.is-hit { animation: none; }
    .sky-confetti { display: none; }
}

/* ==================== דלתות הטירה -הכניסה ליועץ ה-AI ====================
   השכבה מתלבשת אחד-לאחד על ‎.castle-wrap‎ (אותה גאומטריה בדיוק), ולכן
   הדלתות נשארות על מקומן בכל רזולוציה ובכל אזור פאן. היא יושבת **מתחת**
   לשכבות התאורה (z8) כדי לקבל את החשכת הלילה כמו הטירה עצמה. */
.castle-doors {
    position: absolute;
    bottom: calc(var(--water-h) + var(--ground-h) - var(--castle-sink));
    left: 50%;
    width: var(--castle-width);
    transform: translateX(-50%);
    aspect-ratio: 1672 / 941;
    z-index: 4;                 /* מעל הטירה (z3), מתחת לאורות ולתאורה */
    pointer-events: none;       /* השכבה שקופה ללחיצות; הכפתור מחזיר אותן */
}
/* **הפאן: השכבה מצורפת לכללים של ‎.castle-wrap‎ עצמה ואינה משכפלת אותם.**
   הגרסה הראשונה שכפלה שניים מתוך **חמישה** כללים שהטירה מקבלת (‎transform‎
   ו-‎clip-path‎) ופספסה את ‎--k: 1‎ -וכל הפרש בעומק הפאן פירושו שהדלת נעה
   בקצב אחר מהטירה ומתנתקת ממנה בזמן הנסיעה. נמדד: דלת שנייה מרחפת על הדשא.
   צירוף לסלקטור מבטיח זהות **מעצם המבנה**, ולא מסתמך על כך שמישהו יזכור
   לעדכן שני מקומות. ראה הכללים ליד ‎.castle-lights‎ למעלה. */

/* ---- הכפתור ----
   ‎perspective‎ כאן ולא על ההורה: הוא הופך כל ילד ‎fixed‎ ל-containing block,
   וזו מלכודת שכבר הפילה כאן את כרטיס הפרויקט ואת ‎flip‎ של פתיחתו. */
.cdoor {
    position: absolute;
    padding: 0;
    border: 0;
    background: none;
    cursor: pointer;
    pointer-events: auto;       /* חובה -‎.scene‎ כולה none */
    perspective: 340px;
    transform-style: preserve-3d;
    isolation: isolate;   /* מבודד את המיזוג של ‎.cdoor-bloom‎ -ראה שם */
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;  /* בלי "הקש-פעמיים-להגדלה" בטלפון */
}
/* יעד נגיעה: בטלפון הדלת מצוירת ‎21x30‎, והנגיעה שם היא בדיוק מה שהמשתמש
   דיווח עליו כ"לא קורה כלום". ההרחבה ב-‎::before‎ ולא בריפוד -ריפוד היה מותח
   גם את הכנפיים.

   **‎left/top‎ בלבד, ולא ‎inset‎.** ‎inset: 50% 50%‎ קובע את ארבעת הצדדים,
   וביחד עם ‎width/height‎ המשוואה עודפת -הדפדפן מוותר על אחד מהם, וב-RTL
   דווקא על ‎left‎. נמדד: ה-‎::before‎ אכן היה ‎52px‎, אבל שטח הנגיעה בפועל
   נשאר ‎23x32‎ -כלומר ההרחבה לא נגעה במקום הנכון. */
.cdoor::before {
    content: '';
    position: absolute;
    left: 50%;
    top: 50%;
    width: max(100%, 46px);
    height: max(100%, 46px);
    translate: -50% -50%;
}
.cdoor:focus-visible { outline: 3px solid var(--c-gold-light, #ffe7a3); outline-offset: 4px; border-radius: 4px; }

/* ---- כנפי הדלת ----
   כל כנף היא ברוחב **מלא** של הפתח עם צד אחד שקוף, ולכן שתיהן מתלבשות על
   אותה תיבה והציר הוא הקצה החיצוני -בלי חישוב היסט. */
.cdoor-leaf {
    position: absolute;
    inset: 0;
    background-repeat: no-repeat;
    background-size: 100% 100%;
    backface-visibility: hidden;
    transition: transform 1.15s cubic-bezier(0.34, 0.9, 0.3, 1);
}
.cdoor-leaf-l { transform-origin: left center; }
.cdoor-leaf-r { transform-origin: right center; }

/* ---- מה שמאחורי הדלת: עומק, אור, ונגיהה ----
   שלוש שכבות ולא אחת. גרסה אחת בצבע קרם קריאה כ**חור בקיר**: לפתח לא היה
   עומק, ולאור לא הייתה השפעה על מה שסביבו. */

/* 1. עומק הפתח -חשוך, ותמיד שם. נגזר במסכה ולכן אינו יכול לגלוש אל האבן,
      אותו נימוק של מסכות אורות החלונות. */
.cdoor-dark,
.cdoor-glow {
    position: absolute;
    inset: 0;
    -webkit-mask-image: var(--door-mask);
    mask-image: var(--door-mask);
    -webkit-mask-size: 100% 100%;
    mask-size: 100% 100%;
}
.cdoor-dark {
    background: radial-gradient(120% 100% at 50% 78%,
                #4a3520 0%, #2e1d0c 42%, #150c04 74%, #090501 100%);
    opacity: 0;
    transition: opacity 0.35s ease;
}

/* 2. האור עצמו -אולם מואר מעבר לשער.
      **שלוש שכבות ולא גרדיאנט אחד:** גרדיאנט יחיד נותן כתם, ואור אמיתי הוא
      מקור + התפשטות + קרניים. הקרניים הן ‎repeating-conic‎ מנקודת המקור, וזה
      מה שהופך את הפתח מ"חשוך עם כתם" ל"אור שבוקע". */
.cdoor-glow {
    background:
        /* קרני אור מתפצלות מהמקור -עדינות מאוד, הן רמז ולא פסים */
        repeating-conic-gradient(from 200deg at 50% 96%,
            rgba(255, 250, 226, 0.16) 0deg 5deg, rgba(255, 250, 226, 0) 5deg 13deg),
        /* ליבת האור -לבן-חם, קטנה ובוהקת */
        radial-gradient(38% 22% at 50% 88%,
            rgba(255, 255, 252, 1) 0%, rgba(255, 249, 226, 0.92) 40%, rgba(255, 228, 168, 0) 100%),
        /* ההתפשטות אל תוך האולם */
        radial-gradient(78% 62% at 50% 92%,
            rgba(255, 240, 198, 0.72) 0%, rgba(255, 220, 150, 0.34) 42%, rgba(255, 200, 110, 0) 100%);
    opacity: 0;
    transition: opacity 0.55s ease;
}

/* 3. הנגיהה שגולשת החוצה -**בלי מסכה**, וזה מה שהופך אותה לאור.
      ‎screen‎ מוסיף אור על האבן במקום לצבוע אותה, ולכן היא נשארת אבן מוארת.

      **‎isolation: isolate‎ על הכפתור הוא תנאי, לא קישוט.** ‎mix-blend-mode‎
      מתמזג עם ה-backdrop של הקשר-הערימה הקרוב שמבודד; ל-‎.cdoor‎ יש
      ‎perspective‎ **וגם** ‎preserve-3d‎, ובצירוף הזה מנועים משטחים את הקבוצה
      והמיזוג עלול לטפס אל מעל הסצנה כולה -כלומר להבהיר את כל האתר.
      ‎isolation‎ אומר את זה במפורש ואינו תלוי בפרשנות המנוע. */
.cdoor-bloom {
    position: absolute;
    /* מרוכזת בחלק התחתון של הפתח, שם האור באמת נמצא -נגיהה שמרכזה באמצע
       הקשת נראית כערפל ולא כאור שיוצא מדלת. */
    inset: -18% -60% -35%;
    pointer-events: none;
    mix-blend-mode: screen;
    background:
        radial-gradient(closest-side at 50% 76%,
            rgba(255, 250, 232, 0.8) 0%, rgba(255, 238, 190, 0.42) 32%, rgba(255, 222, 152, 0.14) 60%, rgba(255, 220, 140, 0) 100%),
        /* שלולית אור על המדרגות שמתחת -אור שיוצא משער נופל על מה שלפניו */
        radial-gradient(52% 24% at 50% 104%,
            rgba(255, 244, 206, 0.5) 0%, rgba(255, 226, 160, 0.2) 46%, rgba(255, 220, 140, 0) 100%);
    opacity: 0;
    transition: opacity 0.6s ease;
}

/* ---- מצב פתוח ----
   נקבע ב-JS (‎is-open‎), כדי שהרצף פתיחה → אור → זום יהיה מסונכרן במקום אחד
   ולא בשלושה משכי CSS שיתפצלו.

   **72° ולא 82° ולא 105°.** מעבר ל-90° הכנף פונה מהצופה ונעלמת לגמרי, ומה
   שנשאר על המסך הוא הידיות הבולטות -שתי פיסות חומות מרחפות בפתח, וזה מה
   שנראה שבור. ב-72° נשאר פן מקוצר שקוראים אותו כדלת פתוחה.

   **וההחשכה היא מה שמוכר את התנועה:** כנף שנכנסת אל תוך השער נכנסת אל
   הצל, ובלי זה היא נראית כתמונה שמסתובבת ולא כדלת שנפתחת. */
.cdoor.is-open .cdoor-leaf-l { transform: rotateY(72deg); }
.cdoor.is-open .cdoor-leaf-r { transform: rotateY(-72deg); }
.cdoor.is-open .cdoor-leaf   { filter: brightness(0.42) saturate(0.8); }
.cdoor.is-open .cdoor-dark   { opacity: 1; }
.cdoor.is-open .cdoor-glow   { opacity: 1; animation: cdoor-shine 2.6s ease-in-out infinite; }
.cdoor.is-open .cdoor-bloom  { opacity: 1; animation: cdoor-shine 2.6s ease-in-out infinite; }
/* פעימה עדינה -אור סטטי לחלוטין נקרא כמדבקה */
@keyframes cdoor-shine {
    0%, 100% { opacity: 0.92; }
    50%      { opacity: 1; }
}

/* ---- פרץ הכישוף ----
   חמש שכבות ולא הבזק אחד: הבזק לבן לבדו נקרא כמסך שמהבהב, ואילו קרניים
   מסתובבות + טבעות הדף + ניצוצות שעפים החוצה נקראים ככישוף.

   הכול על ה-‎body‎ ולא בסצנה: בתוכה הפרץ היה מוחשך ב-‎filter‎ הלילה ונגרר עם
   פאן המצלמה. z:120 -מעל התוכן (10) ומעל הפוטר (12), מתחת לפאנל (125).
   ‎--fx/--fy‎ נכתבים ב-JS ממיקום הדלת בפועל, כדי שהכל יפרוץ **מהשער**. */
.door-flash {
    position: fixed;
    inset: 0;
    z-index: 120;
    pointer-events: none;
    opacity: 0;
    contain: strict;   /* בולם reflow -זו שכבה חולפת מעל עמוד שלם */
}
.door-flash.is-on { opacity: 1; }
.door-flash > * {
    position: absolute;
    left: var(--fx, 50%);
    top: var(--fy, 55%);
    translate: -50% -50%;
    opacity: 0;
    border-radius: 50%;
}

/* הליבה -הבזק חם שנפתח מהשער */
.df-core {
    width: 46vmax; height: 46vmax;
    background: radial-gradient(closest-side,
        #fff 0%, #fffdf2 16%, rgba(255, 246, 214, 0.9) 30%,
        rgba(255, 226, 150, 0.45) 52%, rgba(255, 210, 120, 0) 78%);
}
.door-flash.is-on .df-core { animation: df-core 1.15s cubic-bezier(0.15, 0.75, 0.3, 1) forwards; }
@keyframes df-core {
    0%   { opacity: 0;    scale: 0.05; }
    16%  { opacity: 1;    scale: 0.55; }
    52%  { opacity: 0.72; scale: 1; }
    100% { opacity: 0;    scale: 1.35; }
}

/* הקרניים -מניפה שמסתובבת ונפתחת. ‎conic‎ מנקודת המקור, ולכן הן מתפצלות
   ממנה ולא נראות כפסים מודבקים */
.df-rays {
    width: 92vmax; height: 92vmax;
    background: repeating-conic-gradient(from 0deg,
        rgba(255, 252, 232, 0.5) 0deg 2.2deg, rgba(255, 252, 232, 0) 2.2deg 11deg);
    -webkit-mask-image: radial-gradient(closest-side, #000 6%, rgba(0,0,0,0.75) 26%, transparent 62%);
    mask-image: radial-gradient(closest-side, #000 6%, rgba(0,0,0,0.75) 26%, transparent 62%);
}
.door-flash.is-on .df-rays { animation: df-rays 1.5s cubic-bezier(0.1, 0.7, 0.25, 1) forwards; }
@keyframes df-rays {
    0%   { opacity: 0;    scale: 0.1;  rotate: -26deg; }
    18%  { opacity: 0.95; }
    100% { opacity: 0;    scale: 1.05; rotate: 22deg; }
}

/* טבעות ההדף -שתיים, בהשהיה, כדי שזה ייקרא כגל ולא כעיגול בודד */
.df-ring {
    width: 12vmax; height: 12vmax;
    border: 0.42vmax solid rgba(255, 246, 210, 0.85);
    box-shadow: 0 0 3vmax rgba(255, 228, 150, 0.6), inset 0 0 2vmax rgba(255, 236, 180, 0.5);
}
.door-flash.is-on .df-ring-1 { animation: df-ring 1.25s cubic-bezier(0.12, 0.7, 0.25, 1) forwards; }
.door-flash.is-on .df-ring-2 { animation: df-ring 1.35s cubic-bezier(0.12, 0.7, 0.25, 1) 0.18s forwards; }
@keyframes df-ring {
    0%   { opacity: 0;    scale: 0.12; }
    12%  { opacity: 0.95; }
    100% { opacity: 0;    scale: 6.5; }
}

/* הניצוצות -נבנים ב-JS (מספר, זווית ומרחק מוגרלים בכל פתיחה). מחזור קבוע
   בקיפריימים נקרא כתבנית אחרי הפעם השנייה, אותו לקח של אורות החלונות. */
.df-motes { width: 0; height: 0; opacity: 1; }
.df-mote {
    position: absolute;
    left: 0; top: 0;
    width: var(--ms, 7px); height: var(--ms, 7px);
    margin: calc(var(--ms, 7px) / -2);
    border-radius: 50%;
    background: radial-gradient(closest-side, #fff 0%, #ffeeb8 45%, rgba(255, 214, 120, 0) 100%);
    opacity: 0;
}
.door-flash.is-on .df-mote {
    animation: df-mote var(--md, 1.1s) cubic-bezier(0.1, 0.7, 0.3, 1) var(--mdelay, 0s) forwards;
}
@keyframes df-mote {
    0%   { opacity: 0;   translate: 0 0;                      scale: 0.4; }
    14%  { opacity: 1; }
    100% { opacity: 0;   translate: var(--mx) var(--my);      scale: 0.15; }
}

@media (prefers-reduced-motion: reduce) {
    .door-flash, .door-flash.is-on { opacity: 0; }
    .door-flash.is-on > * { animation: none; }
}

/* ---- הרמז ---- */
.cdoor-hint {
    position: absolute;
    translate: -50% -100%;
    margin-top: -0.5rem;
    padding: 0.2rem 0.6rem;
    border-radius: 999px;
    background: rgba(13, 40, 78, 0.86);
    border: 1px solid rgba(255, 231, 163, 0.5);
    color: #ffe7a3;
    font-size: 0.72rem;
    font-weight: 700;
    white-space: nowrap;
    opacity: 0;
    transition: opacity 0.25s ease;
    pointer-events: none;
}
.castle-doors:hover .cdoor-hint,
.cdoor:focus-visible ~ .cdoor-hint { opacity: 1; }

/* ---- נשימה עדינה שמסגירה שהדלת לחיצה ----
   בלי שום רמז הדלת נראית כחלק מהציור. ‎filter‎ ולא ‎transform‎: ה-transform
   של הכנפיים תפוס ע"י הפתיחה. */
.cdoor:not(.is-open) .cdoor-leaf { animation: cdoor-breathe 4.5s ease-in-out infinite; }
.cdoor:hover:not(.is-open) .cdoor-leaf { filter: brightness(1.14) drop-shadow(0 0 6px rgba(255, 231, 163, 0.7)); }
@keyframes cdoor-breathe {
    0%, 100% { filter: brightness(1); }
    50%      { filter: brightness(1.07); }
}

@media (prefers-reduced-motion: reduce) {
    .cdoor-leaf { transition: none; animation: none; }
    .door-flash.is-on { animation: none; opacity: 0; }
}

/* ---- מגע: הרמז מוצג תמיד, והמטרה גדלה ----
   **הרמז היה מותנה ב-‎:hover‎ ומוסתר מתחת ל-640px, כלומר במגע הוא לא היה
   קיים כלל** -ובלעדיו הדלת היא חלק מהציור ואין שום דבר שמסגיר שאפשר ללחוץ
   עליה. זה בדיוק הדפוס שכבר תועד כאן על כיתוב גלריית העבודות: כיתוב
   שמתגלה במחווה שאינה קיימת הוא כיתוב שנעלם.

   **‎pointer: coarse‎ ולא רוחב** -בטאבלט (820) אין hover אף שהוא מעל הסף,
   ושם הרמז היה מוגדר ופשוט לא מופיע לעולם. אותו קריטריון שכבר תועד כאן
   על עומס הביצועים באייפד. */
@media (pointer: coarse) {
    /* **הרמז מציג את עצמו ונעלם אחרי 3 שניות** (בקשת המשתמש): הוא הצגה
       חד-פעמית ולא תווית קבועה, ותווית שנשארת תלויה מעל הטירה היא רעש.
       מה שנשאר כדי שהדלת תמשיך להיקרא כלחיצה הוא ה**הילה**, שממשיכה לפעום. */
    .cdoor-hint {
        display: block;
        opacity: 0;
        font-size: 0.62rem;
        padding: 0.14rem 0.5rem;
        animation: cdoor-hint-once 3.5s ease-in-out forwards;
    }
    /* יעד הנגיעה: הדלת מצוירת ‎21x30‎ בטלפון, ומתחת ל-44px אצבע מפספסת */
    .cdoor::before { width: max(100%, 60px); height: max(100%, 60px); }
    /* נשימה נראית. שינוי בהירות של 7% על דלת של 21px אינו נקלט בעין, ולכן
       במגע הקריאה היא **הילה** סביב הפתח.

       ההילה יושבת על ה-‎::before‎ (שממילא קיים בשביל יעד הנגיעה) ו**לא** על
       ‎.cdoor‎ עצמו: ל-‎.cdoor‎ יש ‎transform-style: preserve-3d‎ בשביל סיבוב
       הכנפיים, ו-‎filter‎ עליו היה **משטח** את התלת-ממד ושובר את הפתיחה. */
    .cdoor:not(.is-open)::before {
        border-radius: 50%;
        background: radial-gradient(circle, rgba(255, 231, 163, 0.5) 0%, rgba(255, 231, 163, 0.16) 42%, transparent 70%);
        animation: cdoor-call 3.6s ease-in-out infinite;
    }
}
/* מופיע, נשאר 3 שניות, ונעלם. ‎forwards‎ הוא מה שמשאיר אותו נעלם -בלעדיו
   הוא חוזר לערך הבסיס ברגע שהאנימציה נגמרת. */
@keyframes cdoor-hint-once {
    0%   { opacity: 0; }
    9%   { opacity: 1; }
    86%  { opacity: 1; }
    100% { opacity: 0; }
}
@keyframes cdoor-call {
    0%, 100% { opacity: 0.35; }
    50%      { opacity: 1; }
}
/* בצמצום תנועה הרמז וההילה **נשארים** ורק מפסיקים לפעום: מי שכיבה תנועה
   עדיין צריך לדעת שאפשר ללחוץ. ההילה מקובעת בערך ביניים ולא ב-1, אחרת היא
   נשארת בשיא הבהירות לתמיד. */
/* בצמצום תנועה ההילה מקובעת ומפסיקה לפעום, אבל **הרמז ממשיך להיעלם**:
   שקיפות אינה תנועה, והבקשה היא שהוא לא יישאר על המסך. */
@media (prefers-reduced-motion: reduce) {
    .cdoor::before { animation: none !important; opacity: 0.55; }
}

/* ---- "כתר" הבית: מה שיושב על ראש בית שאין לו דגל ----
   נגזר ונמחק מהבסיס בדיוק כמו הדגל (‎tools/process-house-fx.php‎), אבל
   התנועה שונה **פר-אובייקט**: בד מתנופף, כדור מרחף, וצלחת לוויין סורקת.
   תנועה אחת לשלושתם הייתה נקראת כתקלה על שניים מהם.

   ‎transform-origin‎ בתחתית: כולם יושבים על מוט, וסיבוב סביב המרכז היה
   מנתק אותם ממנו. אין ‎will-change‎ -אלמנט עם אנימציית ‎transform‎ פעילה
   מקודם ממילא, כלל שכבר נמדד כאן על שקופיות הקרוסלה. */
.house-fx-crown {
    /* **הציר בתחתית הספרייט בדיוק (‎100%‎), לא מעליה.** שלוש שורות החפיפה
       יושבות שם, והן מה שמכסה את התפר מול המוט שנשאר בבסיס -ציר גבוה
       יותר היה מזיז דווקא אותן וחושף את החתך. */
    transform-origin: 50% 100%;
    animation-delay: var(--delay, 0s);
    animation-iteration-count: infinite;
    animation-timing-function: ease-in-out;
}
/* כדור עם טבעות -ריחוף עדין וסיבוב קל, כמו מודל שמרחף באוויר */
.hfx-orbit { animation-name: house-crown-orbit; animation-duration: 6.4s; }
@keyframes house-crown-orbit {
    0%, 100% { transform: rotate(-2deg); }
    50%      { transform: rotate(2deg); }
}
/* צלחת לוויין -סריקה איטית ימינה-שמאלה, עם עצירה בקצוות */
.hfx-scan { animation-name: house-crown-scan; animation-duration: 7.8s; }
@keyframes house-crown-scan {
    0%, 8%   { transform: rotate(-4.5deg); }
    46%, 54% { transform: rotate(4.5deg); }
    92%, 100% { transform: rotate(-4.5deg); }
}
/* זרקור -הטיה איטית, כאילו מכוונים אותו */
.hfx-sweep { animation-name: house-crown-sweep; animation-duration: 8.6s; }
@keyframes house-crown-sweep {
    0%, 100% { transform: rotate(-3deg); }
    50%      { transform: rotate(3deg); }
}
@media (prefers-reduced-motion: reduce) {
    .house-fx-crown { animation: none; }
}

/* ---- גרירת בית על האתר (מצב עריכה בלבד) ----
   ‎.scene‎ כולה ‎pointer-events: none‎, ולכן הבתים נעשים יעד ללחיצה **רק**
   כשעורך הבתים גלוי בפאנל -הדלקה קבועה הייתה הופכת כל בית ליעד גם למבקר
   רגיל, וגונבת את הלחיצה מדלת הקלף שיושבת עליו. */
/* ‎[data-oi]‎ = אובייקט של "התאמה אישית", ואותו מצב הזזה משרת גם אותו -
   מנגנון שני היה מתפצל בשינוי הראשון. */
body.house-drag .obj[data-sprite^="house"],
body.house-drag .obj[data-oi] {
    pointer-events: auto;
    cursor: grab;
    touch-action: none;      /* גרירה באצבע, בלי שהעמוד יגלול תחתיה */
}
body.house-drag .obj[data-sprite^="house"]:hover,
body.house-drag .obj[data-oi]:hover {
    outline: 2px dashed rgba(255, 231, 163, 0.85);
    outline-offset: 2px;
}
/* **במצב הזזה הדלת שעל הבית אינה יעד כלל**, ולא רק בזמן הגרירה: היא יושבת
   בדיוק במקום שתופסים בו את הבית, ונמדד שהיא לוכדת את הנגיעה לפני שהגרירה
   מתחילה (‎elementFromPoint‎ החזיר ‎BUTTON.hdoor‎ באייפון). */
body.house-drag .hdoor { pointer-events: none; }
/* וגם הדמויות המהלכות: הן עוברות **לפני** הבתים ולוכדות לחיצה (יש להן
   קפיצה בלחיצה), ונמדד ‎DIV.folk-body‎ על מרכז הבית בדסקטופ. */
body.house-move .folk-layer,
body.house-move .scene-front,
/* **וגם הילדים.** ‎.folk-clip‎ מגדיר ‎pointer-events: auto‎ במפורש (יש לו
   קפיצה בלחיצה), וילד עם ‎auto‎ נשאר יעד גם כשההורה ‎none‎ -חוק שכבר תועד
   כאן על ‎.social-scene-item‎. נמדד: ‎elementFromPoint‎ המשיך להחזיר
   ‎DIV.folk-body‎ על מרכז הבית גם אחרי שהשכבה כובתה. */
body.house-move .folk-layer *,
body.house-move .scene-front * { pointer-events: none !important; }
body.house-dragging .obj[data-sprite^="house"],
body.house-dragging .obj[data-oi] { cursor: grabbing; }
.obj.is-hdrag {
    outline: 2px solid var(--c-gold, #d39a1a);
    outline-offset: 2px;
    /* ‎filter‎ ולא ‎opacity‎: הבית יושב בשכבה עם אחים, ושקיפות הייתה חושפת
       את מה שמאחוריו ונקראת כתקלה. */
    filter: brightness(1.08) drop-shadow(0 0 10px rgba(255, 231, 163, 0.55));
}

/* מיכל הפילטרים של הסצנה. ‎width/height = 0‎ אינו מספיק: SVG בזרימה תופס
   שורה משלו ומזיז את מה שאחריו. ‎absolute‎ מוציא אותו לגמרי.

   **‎visibility: hidden‎ ירד מכאן.** דווח שבטלפון הדמות הקדמית בהירה בלילה
   בעוד שהאחורית כהה -כלומר ‎url(#folkNight)‎ אינו מוחל שם, ומה שנשאר הוא
   ‎brightness(0.52) saturate(0.79)‎ בלבד: בדיוק המצב שהפילטר בא לתקן. ב-WebKit
   הפניה ל-‎<filter>‎ שיושב בתוך תת-עץ **מוסתר** אינה נפתרת בחלק מהגרסאות, וזו
   אותה משפחת באגים שבגללה ‎display: none‎ כבר נפסל כאן.

   ‎<defs>‎ אינו מרונדר לפי המפרט, ו-SVG בגודל 0 עם ‎overflow: hidden‎ אינו
   נראה ואינו תופס מקום -כלומר ההסתרה הייתה מיותרת מלכתחילה. ‎pointer-events‎
   נשאר כרשת ביטחון. **נמדד שאין שינוי מראה בשום מנוע.** */
.scene-defs {
    position: absolute;
    width: 0;
    height: 0;
    overflow: hidden;
    pointer-events: none;
}

/* ================= מצב סייבר =================
   שכבה על הסצנה, פר-עמוד. **‎transform‎/‎opacity‎ בלבד בכל ארבעת הסגנונות** -
   הפרויקט כבר מדד שכל אנימציה שאינה מקומפזת נופלת על החוט הראשי, ושכבה
   חדשה שאינה עומדת בכלל הזה מורגשת מיד בטלפון.

   יושבת מעל הסצנה ומתחת לקלף (‎.content‎ הוא z10): היא אפקט רקע, ואפקט
   שמצויר מעל הטקסט הופך אותו לבלתי קריא. */
/* ---- המארח של מצב "מקדימה" ----
   **שכבה משלו ולא ‎.scene-front‎.** שם הוא ירש התנהגות של הדמויות: המצב
   הסטטי מסתיר את השכבה כולה (והאפקט נעלם), תיק העבודות מוריד אותה ל-‎z:9‎
   (ואז "מקדימה" הוא מתחת לתוכן), והיא נפלטת רק כשיש דמויות קדמיות. הנימוק
   המקורי -"ילד אינו יכול לצאת מה-stacking context של ‎.scene‎"- נשאר נכון,
   והוא בדיוק מה שהמארח הזה נותן.

   ‎z: 11‎ ולא יותר: מעל ‎.content‎ (z10) ומתחת לפוטר ולסרגל הרשתות (12/13),
   שאינם רקע. ‎pointer-events: none‎ -היא פרושה על כל המסך מעל התוכן.
   ואין כאן ‎filter‎ של שעת היום: מעל התוכן האפקט אינו חלק מהעולם, וזה
   כתוב גם בפאנל. */
.cyber-front {
    position: fixed;
    inset: 0;
    z-index: 11;
    pointer-events: none;
}

/* ---- "בעולם": מעל הרקע ומתחת לאובייקטים ----
   ‎z: 8‎ (ברירת המחדל למטה) הוא **מעל כל הנוף** -הטירה ‎3‎, הקרקע ‎4‎, המים
   ‎5‎, אובייקטי החזית ‎6‎, הדמויות ‎7‎- ולכן ההפרש בינו לבין "מקדימה" הוא
   שטח כרטיס התוכן בלבד. זה מה שדווח כ"שתי האפשרויות הן מקדימה".
   ‎1‎ מציב את השכבה מעל השמיים והעננים (שאין להם ‎z-index‎ כלל) ומתחת
   ל-‎.ground-back‎ (‎2‎), כלומר האפקט נמצא **בתוך** העולם: הטירה, העצים
   והדמויות עוברים לפניו. אותו מארח ואותה תאורת לילה. */
.cyber-fx[data-cypos="deep"] { z-index: 1; }
.cyber-fx {
    position: absolute;
    inset: 0;
    z-index: 8;
    overflow: hidden;
    pointer-events: none;
    contain: strict;          /* אין לה ילדים שחורגים, וזה חוסך פריסה */
}
/* ‎--cy-speed‎ הוא **מכפיל משך** (‎100/אחוז‎), ולכן ערך גבוה = איטי יותר.
   מוחל כאן פעם אחת על ‎animation-duration‎ הבסיסי, וכל סגנון ששולח
   ‎animation‎ shorthand מכפיל בעצמו -‎var()‎ בתוך shorthand אינו יורש. */
.cyber-fx .cy-el {
    position: absolute;
    left: var(--cx);
    top: 0;
    display: block;
    animation-duration: calc(var(--cd) * var(--cy-speed, 1));
    animation-delay: var(--cdel);
    animation-iteration-count: infinite;
    animation-timing-function: linear;
    will-change: transform;
}
/* עוצמה = אטימות, לא מספר אלמנטים: מספר שונה בכל דרגה בונה מרקאפ אחר
   ומונע השוואה, ואטימות רצה בקומפוזיטור. */
/* השכבה קיימת תמיד, והמצב הוא data-cy. כבוי = ריק.
   העמעום הוא מה שהופך מעבר בין עמוד עם סייבר לעמוד בלעדיו לרך במקום להיעלמות
   בפריים אחד -opacity רץ בקומפוזיטור, ולכן הוא חינם גם באמצע נסיעת המצלמה. */
.cyber-fx { opacity: 0; transition: opacity .45s ease, visibility .45s; }
/* ‎visibility‎ **כן** מונפש -כל ערך ביניים ממופה ל-‎visible‎, ולכן השכבה
   נשארת גלויה לאורך הדעיכה ויורדת רק בסופה, ובחזרה נדלקת מיד. בלעדיו היא
   נשארת ביפ-טסט ובעץ הציור גם כשהיא שקופה לגמרי. */
.cyber-fx[data-cy=""] { visibility: hidden; }
/* **ושכבה ריקה גם מושהית.** ‎visibility: hidden‎ אינו עוצר אנימציות -חוק
   שכבר נמדד כאן על ניצוצות הלילה ועל טוען העמודים- ומאז שיש ‎CYBER_LAYERS‎
   שכבות, רובן ריקות ברוב העמודים, זה מכפיל את מה שרץ לחינם.
   ‎paused‎ ולא ‎none‎: הפאזה נשמרת, וה-JS אינו צריך לדעת דבר. */
.cyber-fx[data-cy=""],
.cyber-fx[data-cy=""]::before,
.cyber-fx[data-cy=""]::after,
.cyber-fx[data-cy=""] .cy-el,
.cyber-fx[data-cy=""] .cy-el::before,
.cyber-fx[data-cy=""] .cy-el::after { animation-play-state: paused; }
/* מצב ההחלפה של "אקראי" -‎!important‎ כי האטימות נקבעת בכללי ה-‎data-cylvl‎
   שספציפיים ממנו, וכאן צריך לגבור עליהם לרגע אחד. */
.cyber-fx.is-cyswap { opacity: 0 !important; }
/* **האטימות היא משתנה, לא 27 סלקטורים.** קודם ישבו כאן שלושה בלוקים של
   סגנון×דרגה, וכל סגנון או דרגה שנוספו חייבו לגעת בכולם -בדיוק הדפוס
   שהפרויקט מתעד כמקום שבו אחד נשכח. ‎--cy-op‎ נפלט inline מ-‎cyber_opacity‎
   והוא **רציף**, ולכן כל צעד של המחוון נראה. */
.cyber-fx[data-cy]:not([data-cy=""]) { opacity: var(--cy-op, 0.6); }

/* 22 פריטים נפלטים תמיד, והעודף נחתך לפי סגנון ומדרגת צפיפות -כך שאין
   צורך לבנות את השכבה מחדש בכל ניווט (ובנייה בלקוח הייתה בונה שני שהיה
   מתפצל). **כללי החיתוך עצמם נבנים בלולאה** ב-‎cyber_level_css()‎ ונפלטים
   ב-‎layout.php‎: רשימה ידנית של סגנון×דרגה היא המקום שבו אחד נשכח. */

/* ---- גשם מטריקס ---- */
.cyber-fx[data-cy="matrix"] .cy-el {
    width: 2px;
    height: 38vh;
    background: linear-gradient(180deg, transparent, rgba(60, 255, 140, 0.05) 30%,
                                rgba(90, 255, 160, 0.55) 88%, rgba(190, 255, 215, 0.95));
    box-shadow: 0 0 6px rgba(60, 255, 140, 0.5);
    animation-name: cy-fall;
    scale: var(--cs) 1;
}
@keyframes cy-fall {
    from { transform: translateY(-40vh); }
    to   { transform: translateY(105vh); }
}

/* ---- רשת נוירונים ---- */
.cyber-fx[data-cy="neural"] .cy-el {
    top: calc(var(--cs) * 44%);
    width: 7px; height: 7px;
    border-radius: 50%;
    background: radial-gradient(circle, rgba(180, 235, 255, 0.95), rgba(60, 170, 255, 0.15) 70%, transparent);
    box-shadow: 0 0 12px rgba(90, 190, 255, 0.7);
    animation-name: cy-pulse;
    animation-timing-function: ease-in-out;
}
/* הקו היוצא מהנקודה -‎::after‎ ולא אלמנט נוסף: אלמנט אמיתי היה מכפיל את
   מספר שכבות הקומפוזיטור. */
.cyber-fx[data-cy="neural"] .cy-el::after {
    content: "";
    position: absolute;
    left: 50%; top: 50%;
    width: 16vw; height: 1px;
    transform-origin: 0 0;
    transform: rotate(var(--crot));
    background: linear-gradient(90deg, rgba(120, 200, 255, 0.5), transparent);
}
@keyframes cy-pulse {
    0%, 100% { transform: scale(0.6); opacity: 0.25; }
    50%      { transform: scale(1.25); opacity: 1; }
}

/* ---- גליץ׳ ---- */
.cyber-fx[data-cy="glitch"] .cy-el {
    left: 0;
    top: calc(var(--cx) * 0.9);
    width: 100%;
    height: calc(var(--cs) * 9px);
    background: linear-gradient(90deg, transparent, rgba(255, 60, 160, 0.35) 20%,
                                rgba(60, 240, 255, 0.35) 55%, transparent);
    mix-blend-mode: screen;   /* יש כאן backdrop אמיתי -הסצנה שמתחת */
    animation-name: cy-glitch;
    animation-timing-function: steps(1, end);
}
@keyframes cy-glitch {
    0%, 92%, 100% { transform: translateX(0); opacity: 0; }
    93%           { transform: translateX(-2.5%); opacity: 1; }
    95%           { transform: translateX(2%);    opacity: 0.8; }
    97%           { transform: translateX(-1%);   opacity: 1; }
}

/* ---- קווי מעגל חשמלי ---- */
.cyber-fx[data-cy="circuit"] .cy-el {
    top: calc(var(--cs) * 40%);
    width: 22vw; height: 2px;
    transform-origin: 0 50%;
    rotate: var(--crot);
    background: linear-gradient(90deg, rgba(120, 245, 255, 0.12), rgba(120, 245, 255, 0.35));
}
/* הפולס שרץ על הקו -‎::after‎ מונפש, הקו עצמו סטטי */
.cyber-fx[data-cy="circuit"] .cy-el::after {
    content: "";
    position: absolute;
    inset: -2px auto -2px 0;
    width: 3.5vw;
    border-radius: 99px;
    background: linear-gradient(90deg, transparent, rgba(190, 255, 255, 0.95), transparent);
    box-shadow: 0 0 10px rgba(120, 245, 255, 0.8);
    animation: cy-run calc(var(--cd) * var(--cy-speed, 1)) linear var(--cdel) infinite;
}
@keyframes cy-run {
    from { transform: translateX(-4vw); }
    to   { transform: translateX(22vw); }
}

/* **צמצום תנועה עוצר, ואינו מסתיר.** הסגנון הוא חלק מהמראה שנבחר לעמוד;
   מה שמפריע הוא התנועה. */
/* ---- פיקסלים משובשים ----
   בלוק שקופץ בין שני מצבים ונעלם, כמו אריח שהמסך לא הצליח לצייר. שלוש
   שכבות: הבלוק עצמו, הד ציאני שמפגר אחריו (‎::before‎) והד מגנטה מהצד השני
   (‎::after‎) -הפרדת הערוצים היא מה שקורא כשיבוש ולא כריבוע מהבהב.
   **‎steps()‎ ולא ‎ease‎:** תנועה רציפה נקראת כאנימציה, וקפיצה בין מצבים
   בדידים נקראת כתקלה במסך - וזה בדיוק האפקט. הכול ‎transform‎/‎opacity‎,
   כלומר קומפוזיטור בלבד. */
.cyber-fx[data-cy="pixel"] .cy-el {
    /* **רצועות ולא נקודות.** הגרסה הראשונה הייתה ‎9x7px‎ על מסך של 900,
       וכל בלוק נראה כחמישית מהמחזור -נמדד בצילום כשישה ריבועים זעירים,
       כלומר "לא עושה כלום" בצדק. אריח שנפל בשידור הוא **רצועה** ברוחב
       עשרות פיקסלים, וזה גם מה שמבדיל אותו מ-‎neural‎ שהוא נקודות. */
    top: var(--cy);
    left: var(--cx);
    width: calc(74px * var(--cs));
    height: calc(17px * var(--cs));
    background: rgba(120, 255, 214, 0.9);
    animation: cy-pixel calc(var(--cd) * var(--cy-speed, 1)) steps(1, end) var(--cdel) infinite;
}
/* שלוש צורות מתחלפות, כדי שהשיבוש לא ייקרא כדפוס אחד חוזר */
.cyber-fx[data-cy="pixel"] .cy-el:nth-child(3n+2) {
    width: calc(26px * var(--cs));
    height: calc(26px * var(--cs));
}
.cyber-fx[data-cy="pixel"] .cy-el:nth-child(4n+3) {
    width: calc(132px * var(--cs));
    height: calc(9px * var(--cs));
}
.cyber-fx[data-cy="pixel"] .cy-el::before,
.cyber-fx[data-cy="pixel"] .cy-el::after {
    content: '';
    position: absolute;
    inset: 0;
    animation: inherit;
    animation-name: cy-pixel-echo;
}
.cyber-fx[data-cy="pixel"] .cy-el::before {
    background: rgba(80, 220, 255, 0.75);
    transform: translate3d(-42%, -55%, 0);
}
.cyber-fx[data-cy="pixel"] .cy-el::after {
    background: rgba(255, 90, 200, 0.65);
    transform: translate3d(42%, 55%, 0);
    animation-direction: reverse;
}
/* ~40% מהמחזור גלוי: בחמישית שהייתה כאן קודם נמדדו שישה בלוקים על המסך
   כולו בכל רגע נתון. ‎steps(1)‎ ולא ‎ease‎ -תנועה רציפה נקראת כאנימציה,
   וקפיצה בין מצבים בדידים נקראת כתקלה במסך, וזה בדיוק האפקט. */
@keyframes cy-pixel {
    0%, 6%   { opacity: 0; }
    7%, 22%  { opacity: 1; }
    23%, 33% { opacity: 0; }
    34%, 48% { opacity: 1; }
    49%, 62% { opacity: 0; }
    63%, 78% { opacity: 1; }
    79%,100% { opacity: 0; }
}
@keyframes cy-pixel-echo {
    0%, 9%   { opacity: 0; }
    10%, 19% { opacity: 0.8; }
    20%, 36% { opacity: 0; }
    37%, 45% { opacity: 0.8; }
    46%, 65% { opacity: 0; }
    66%, 75% { opacity: 0.8; }
    76%,100% { opacity: 0; }
}

/* ---- "פלג" -שלושה רכיבים בשכבה אחת ----
   רשת משובצת ברקע, ומעליה שני שלישים מהפריטים כעמודות מטריקס ושליש
   כרצועות פיקסלים. **הרשת היא ‎background‎ על ‎::before‎ ולא אלמנטים** -
   רשת של עשרות קווים כאלמנטים היא עשרות שכבות קומפוזיטור, וכגרדיאנט
   חוזר היא שכבה אחת שאינה עולה דבר.
   הפריטים מחולקים ב-‎nth-child‎ ולא במרקאפ נפרד, כדי שהשכבה תישאר אותה
   שכבה בדיוק בכל הסגנונות -וזה מה שמאפשר להחליף ביניהם בהחלפת מאפיין. */
.cyber-fx[data-cy="peleg"]::before,
.cyber-fx[data-cy="peleg"]::after {
    content: '';
    position: absolute;
    inset: -2%;
    pointer-events: none;
}
/* הרשת: קו דק כל 34px ועבה יותר כל חמישה -כמו מחברת חשבון, בציאן */
.cyber-fx[data-cy="peleg"]::before {
    background-image:
        repeating-linear-gradient(90deg, rgba(120,255,214,0.5) 0 1px, transparent 1px 170px),
        repeating-linear-gradient(0deg,  rgba(120,255,214,0.5) 0 1px, transparent 1px 170px),
        repeating-linear-gradient(90deg, rgba(90,200,255,0.22) 0 1px, transparent 1px 34px),
        repeating-linear-gradient(0deg,  rgba(90,200,255,0.22) 0 1px, transparent 1px 34px);
    animation: cy-peleg-grid calc(9s * var(--cy-speed, 1)) linear infinite;
}
/* השיבוש: רצועה אופקית שסורקת ומזיזה את הרשת שמתחתיה בערוץ מגנטה */
.cyber-fx[data-cy="peleg"]::after {
    background-image:
        repeating-linear-gradient(0deg, rgba(255,90,200,0.5) 0 1px, transparent 1px 34px);
    animation: cy-peleg-tear calc(7s * var(--cy-speed, 1)) steps(1, end) infinite;
}
@keyframes cy-peleg-grid {
    0%, 100% { opacity: 0.85; transform: translate3d(0, 0, 0); }
    47%      { opacity: 0.85; transform: translate3d(0, 0, 0); }
    48%      { opacity: 0.5;  transform: translate3d(3px, -2px, 0); }
    50%      { opacity: 0.85; transform: translate3d(0, 0, 0); }
    82%      { opacity: 0.85; transform: translate3d(0, 0, 0); }
    83%      { opacity: 0.45; transform: translate3d(-4px, 1px, 0); }
    85%      { opacity: 0.85; transform: translate3d(0, 0, 0); }
}
@keyframes cy-peleg-tear {
    0%, 21%  { opacity: 0; transform: translate3d(0, 0, 0); }
    22%, 25% { opacity: 1; transform: translate3d(5px, 34px, 0); }
    26%, 63% { opacity: 0; transform: translate3d(0, 0, 0); }
    64%, 66% { opacity: 1; transform: translate3d(-6px, -34px, 0); }
    67%,100% { opacity: 0; transform: translate3d(0, 0, 0); }
}
/* שני שלישים מהפריטים: עמודות מטריקס -אותו מסלול של הסגנון הייעודי */
.cyber-fx[data-cy="peleg"] .cy-el {
    top: -10%;
    left: var(--cx);
    width: 2px;
    height: calc(46% + var(--cs) * 34%);
    background: linear-gradient(180deg, transparent, rgba(120,255,214,0.95));
    animation: cy-fall calc(var(--cd) * var(--cy-speed, 1)) linear var(--cdel) infinite;
}
/* והשליש הנותר: רצועות פיקסלים, בערוצים המפוצלים */
.cyber-fx[data-cy="peleg"] .cy-el[data-k="strip"] {
    top: var(--cy);
    width: calc(64px * var(--cs));
    height: calc(14px * var(--cs));
    background: rgba(120, 255, 214, 0.9);
    animation: cy-pixel calc(var(--cd) * var(--cy-speed, 1)) steps(1, end) var(--cdel) infinite;
}
.cyber-fx[data-cy="peleg"] .cy-el[data-k="strip"]::before,
.cyber-fx[data-cy="peleg"] .cy-el[data-k="strip"]::after {
    content: '';
    position: absolute;
    inset: 0;
    animation: inherit;
    animation-name: cy-pixel-echo;
}
.cyber-fx[data-cy="peleg"] .cy-el[data-k="strip"]::before {
    background: rgba(80, 220, 255, 0.75);
    transform: translate3d(-38%, -60%, 0);
}
.cyber-fx[data-cy="peleg"] .cy-el[data-k="strip"]::after {
    background: rgba(255, 90, 200, 0.65);
    transform: translate3d(38%, 60%, 0);
    animation-direction: reverse;
}

/* ---- "סרט ישן" ו"שיבוש מיקס" ----
   הרכיבים שמרכיבים נזק פילם אמיתי, לפי מה שמסנני פילם מקצועיים מונים
   (Boris FX BCC Film Grunge): **גרעיניות, רעד שער (gate weave), ריצוד
   מנורה, אבק, שערות, שריטות, כתמים, vignette וגוון**. הגרסה הראשונה כאן
   הייתה גרעיניות חלשה ושלושה קווים -שלושה מתשעת הרכיבים, ובגוון **כחול**,
   שהוא ההפך מפילם. מה שהכריע בעין הוא הגוון והרעד.

   שלוש שכבות בלבד לכל אלה: ::before (גרעיניות + רעד), ::after (גוון +
   vignette + ריצוד), והפריטים מחולקים nth-child לארבעה סוגי לכלוך. */
.cyber-fx[data-cy="film"]::before,
.cyber-fx[data-cy="mix"]::before,
.cyber-fx[data-cy="film"]::after,
.cyber-fx[data-cy="mix"]::after {
    content: '';
    position: absolute;
    inset: -3%;               /* חורג מעט, כדי שרעד השער לא יחשוף קצה */
    pointer-events: none;
}
/* גרעיניות + **רעד שער**: הפילם אינו יושב יציב בשער המקרן, והתמונה נעה
   שבר מילימטר בכל פריים. זה הרכיב שהכי חסר בגרסה הראשונה -בלעדיו האפקט
   נקרא כמרקם דומם ולא כהקרנה. steps בקצב פריימים אמיתי. */
.cyber-fx[data-cy="film"]::before,
.cyber-fx[data-cy="mix"]::before {
    background-image:
        url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)' opacity='0.62'/%3E%3C/svg%3E"),
        radial-gradient(ellipse 76% 68% at 50% 47%,
            transparent 0%, rgba(0, 0, 0, 0.28) 58%, rgba(0, 0, 0, 0.82) 100%);
    background-size: 180px 180px, 100% 100%;
    mix-blend-mode: overlay;
    /* ‎--cyfx-grain‎: עוצמת הגרעיניות וה-vignette, מהניהול. ‎1‎ הוא בדיוק
       מה שהיה תמיד, ולכן ברירת המחדל אינה משנה אתר קיים. */
    opacity: calc(0.9 * var(--cyfx-grain, 1));
    /* ‎--cy-speed‎ גם כאן: רעד השער וריצוד המנורה הם **רוב** מה שנראה בסגנון הזה,
       ובלעדיו מחוון המהירות הזיז רק את הלכלוך (שריטות, אבק, שערות, כתמים)
       והאפקט נראה כאילו אינו מגיב לו. ‎var()‎ בתוך ‎animation‎ shorthand אינו יורש
       מהכלל הבסיסי, ולכן כל שכבה ששולחת shorthand חייבת להכפיל בעצמה. */
    animation: cy-film-weave calc(0.5s * var(--cy-speed, 1)) steps(1, end) infinite;
}
/* גוון פילם + vignette + ריצוד מנורה. **הגוון הוא מה שהופך את זה לפילם** -
   סרט ישן הוא חום-ענברי, וכאן הרקע כחול; בלי השכבה הזאת שום כמות של
   שריטות אינה נקראת כהקרנה. filter: sepia אינו יכול לחול על מה שמתחת,
   ולכן זו שכבת צבע במיזוג hard-light. */
.cyber-fx[data-cy="film"]::after,
.cyber-fx[data-cy="mix"]::after {
    /* **mix-blend-mode: color הוא מה שהופך את זה לספיה.** hard-light על
       גרדיאנט חום מעל רקע כחול כהה נמדד כאפור -הוא ממזג בהירות, לא גוון.
       color לוקח hue+saturation מכאן ומשאיר את הבהירות שמתחת, כלומר בדיוק
       מה ש-filter: sepia היה עושה אילו יכול היה לחול על ה-backdrop. */
    /* ‎--cyfx-tint‎ הוא **האלפא של הצבע** ולא ‎opacity‎ של השכבה: האטימות
       תפוסה בקיפריים של הריצוד, ושני הערכים מוכפלים ממילא -כלומר גוון ‎0‎
       מבטל את הצבע גם כשהריצוד רץ. */
    background: rgb(150 104 46 / var(--cyfx-tint, 1));
    mix-blend-mode: color;
    /* **‎linear‎ ולא ‎steps‎** (בקשת המשתמש: "הצבעים שמתחלפים באפקט הסרט
       ישן שיהיה עם FADE מהיר"). העצירות בקיפריים צמודות (‎22%‎→‎23%‎), ולכן
       ‎linear‎ נותן מעבר של ‎~16ms‎ -דעיכה מהירה ולא קפיצה, בלי להאט את
       המחזור. ההערה הישנה כאן ("מנורת מקרן: קפיצות ולא דעיכה") הוחלפה
       בבקשה מפורשת. */
    animation: cy-film-flicker calc(1.6s * var(--cy-speed, 1)) linear infinite;
}
/* אובייקט שמהירותו ‎0‎. **כלל אחד ולא כלל לכל סוג** -‎data-kstop‎ נפלט על
   הפריט, ולכן אין כאן רשימה שאפשר לשכוח בה סוג חדש. הפסבדו-אלמנטים כלולים,
   כי בהם יושבות השריטה השנייה והנקודה השנייה. */
.cy-el[data-kstop],
.cy-el[data-kstop]::before,
.cy-el[data-kstop]::after {
    animation-play-state: paused !important;
}

/* ---- מהירות ‎0‎ = האפקט קופא במקומו ----
   **‎paused‎ ולא ‎none‎**: ‎none‎ מחזיר כל אלמנט לסגנון הבסיס שלו, כלומר
   הפריטים קופצים לנקודת המוצא ונערמים -אותו לקח שכבר נמדד כאן על המצב
   הסטטי. ‎paused‎ מקפיא אותם במקום שבו הם. השכבה עצמה, כל הפריטים ושני
   הפסבדו-אלמנטים. */
.cyber-fx[data-cyspeed="0"],
.cyber-fx[data-cyspeed="0"] *,
.cyber-fx[data-cyspeed="0"]::before,
.cyber-fx[data-cyspeed="0"]::after,
.cyber-fx[data-cyspeed="0"] *::before,
.cyber-fx[data-cyspeed="0"] *::after {
    animation-play-state: paused !important;
}

/* ---- ‎data-cyfx-flicker‎: משרעת הריצוד, ‎0‎ = מנורה יציבה ----
   **מדרגות ולא משתנה**, כי המשרעת יושבת בתוך ה-‎@keyframes‎: ‎var()‎ שם
   מוציא את האנימציה מהקומפוזיטור, ועל שכבה בגודל מסך מלא זה ריצוד אמיתי
   (חוק מתועד כאן על הציפורים). הסלקטור נושא גם את ‎data-cy‎, גם כדי לא
   לחול על סגנון אחר וגם כדי לגבור על כלל הבסיס שמעליו. */
.cyber-fx[data-cy="film"][data-cyfx-flicker="0"]::after,
.cyber-fx[data-cy="mix"][data-cyfx-flicker="0"]::after {
    animation: none;
    opacity: 0.44;            /* אמצע הטווח -גוון יציב, בלי הבהוב */
}
.cyber-fx[data-cy="film"][data-cyfx-flicker="1"]::after,
.cyber-fx[data-cy="mix"][data-cyfx-flicker="1"]::after { animation-name: cy-film-flicker-lo; }
.cyber-fx[data-cy="film"][data-cyfx-flicker="3"]::after,
.cyber-fx[data-cy="mix"][data-cyfx-flicker="3"]::after { animation-name: cy-film-flicker-hi; }
/* ---- ‎data-cyfx-weave‎: משרעת רעד השער, ‎0‎ = פילם יציב ---- */
.cyber-fx[data-cy="film"][data-cyfx-weave="0"]::before,
.cyber-fx[data-cy="mix"][data-cyfx-weave="0"]::before { animation: none; }
.cyber-fx[data-cy="film"][data-cyfx-weave="1"]::before,
.cyber-fx[data-cy="mix"][data-cyfx-weave="1"]::before { animation-name: cy-film-weave-lo; }
.cyber-fx[data-cy="film"][data-cyfx-weave="3"]::before,
.cyber-fx[data-cy="mix"][data-cyfx-weave="3"]::before { animation-name: cy-film-weave-hi; }

@keyframes cy-film-weave {
    0%   { transform: translate3d(0, 0, 0); }
    12%  { transform: translate3d(-0.7%, 0.5%, 0); }
    25%  { transform: translate3d(0.5%, -0.8%, 0); }
    37%  { transform: translate3d(-0.4%, -0.3%, 0); }
    50%  { transform: translate3d(0.8%, 0.6%, 0); }
    62%  { transform: translate3d(-0.6%, -0.6%, 0); }
    75%  { transform: translate3d(0.3%, 0.9%, 0); }
    87%  { transform: translate3d(-0.9%, -0.2%, 0); }
    100% { transform: translate3d(0.2%, 0.4%, 0); }
}
/* אותו מסלול ב-‎0.4‎ וב-‎1.9‎ -‎::before‎ חורג ‎3%‎ מכל צד, ולכן גם החזק
   אינו חושף קצה. */
@keyframes cy-film-weave-lo {
    0%   { transform: translate3d(0, 0, 0); }
    12%  { transform: translate3d(-0.28%, 0.2%, 0); }
    25%  { transform: translate3d(0.2%, -0.32%, 0); }
    37%  { transform: translate3d(-0.16%, -0.12%, 0); }
    50%  { transform: translate3d(0.32%, 0.24%, 0); }
    62%  { transform: translate3d(-0.24%, -0.24%, 0); }
    75%  { transform: translate3d(0.12%, 0.36%, 0); }
    87%  { transform: translate3d(-0.36%, -0.08%, 0); }
    100% { transform: translate3d(0.08%, 0.16%, 0); }
}
@keyframes cy-film-weave-hi {
    0%   { transform: translate3d(0, 0, 0); }
    12%  { transform: translate3d(-1.33%, 0.95%, 0); }
    25%  { transform: translate3d(0.95%, -1.52%, 0); }
    37%  { transform: translate3d(-0.76%, -0.57%, 0); }
    50%  { transform: translate3d(1.52%, 1.14%, 0); }
    62%  { transform: translate3d(-1.14%, -1.14%, 0); }
    75%  { transform: translate3d(0.57%, 1.71%, 0); }
    87%  { transform: translate3d(-1.71%, -0.38%, 0); }
    100% { transform: translate3d(0.38%, 0.76%, 0); }
}
/* מנורת מקרן: קפיצות ולא דעיכה */
/* **הגוון חלקי ולא מלא.** ב-opacity מלא ה-color blend מחליף את הגוון
   לחלוטין ונמדד ככיסוי חום אטום -הסצנה שמתחת נעלמה. ~0.45 משאיר אותה
   מזוהה ורק מטה אותה לענבר, וזה גם מה שמאפשר לריצוד להיראות. */
@keyframes cy-film-flicker {
    0%, 22%  { opacity: 0.42; }
    23%, 27% { opacity: 0.5; }
    28%, 44% { opacity: 0.36; }
    45%, 48% { opacity: 0.48; }
    49%, 66% { opacity: 0.44; }
    67%, 70% { opacity: 0.28; }
    71%, 91% { opacity: 0.46; }
    92%,100% { opacity: 0.52; }
}
/* אותו דפוס בדיוק בשלוש משרעות. **סביב אותו אמצע (‎0.44‎)**, אחרת שינוי
   המשרעת היה משנה גם את עוצמת הגוון הממוצעת -כלומר שני פקדים לאותו דבר. */
@keyframes cy-film-flicker-lo {
    0%, 22%  { opacity: 0.435; }
    23%, 27% { opacity: 0.462; }
    28%, 44% { opacity: 0.415; }
    45%, 48% { opacity: 0.455; }
    49%, 66% { opacity: 0.44; }
    67%, 70% { opacity: 0.388; }
    71%, 91% { opacity: 0.447; }
    92%,100% { opacity: 0.475; }
}
@keyframes cy-film-flicker-hi {
    0%, 22%  { opacity: 0.4; }
    23%, 27% { opacity: 0.58; }
    28%, 44% { opacity: 0.26; }
    45%, 48% { opacity: 0.54; }
    49%, 66% { opacity: 0.44; }
    67%, 70% { opacity: 0.1; }
    71%, 91% { opacity: 0.48; }
    92%,100% { opacity: 0.64; }
}

/* ---- ארבעת סוגי הלכלוך, מחולקים ב-nth-child ----
   **האבק לוקח את כל הזוגיים, ושלושת סוגי הקווים מתחלקים באי-זוגיים**
   (בקשת המשתמש: "קצת יותר נקודות וקצת פחות קווים, שילוב שווה"). בסבב
   הקודם השריטות לקחו את כל האי-זוגיים -11 מתוך 22, כל אחת עם שני קווי
   פסבדו נוספים- והתוצאה נטתה לרשת של קווים במקום ללכלוך של פילם.

   החלוקה היום: אבק ‎2n‎ (11), שריטות ‎6n+1‎ (4), שערות ‎6n+3‎ (4),
   כתמים ‎6n+5‎ (3). זו **חלוקה מלאה** של 22 הפריטים בלי חפיפה. */
/* 1. שריטות אנכיות -קו דק שקופץ למקום אחר ונעלם */
.cyber-fx[data-cy="film"] .cy-el[data-k="scratch"],
.cyber-fx[data-cy="mix"] .cy-el[data-k="scratch"] {
    top: -2%;
    left: var(--cx);
    width: 1px;
    height: 104%;
    background: linear-gradient(180deg,
        transparent, rgba(255, 244, 214, 0.75) 14%,
        rgba(255, 244, 214, 0.2) 52%,
        rgba(120, 80, 30, 0.45) 74%, transparent);
    animation: cy-film-scratch calc(var(--cd) * var(--cy-speed, 1)) steps(1, end) var(--cdel) infinite;
}
/* 2. אבק -נקודות שמהבהבות פריים אחד. הן מה שמוסיף "לכלוך" ולא "אפקט",
      ובגרסה הראשונה לא היו כלל. */
.cyber-fx[data-cy="film"] .cy-el[data-k="dust"] {
    top: var(--cy);
    left: var(--cx);
    width: calc(2px + var(--cs) * 4px);
    height: calc(2px + var(--cs) * 4px);
    border-radius: 50%;
    background: rgba(28, 16, 6, 0.78);
    animation: cy-film-dust calc(0.9s * var(--cy-speed, 1)) steps(1, end) var(--cdel) infinite;
}
/* 3. שערות -קשת דקה שנתקעה בשער, נשארת כמה פריימים וקופצת */
.cyber-fx[data-cy="film"] .cy-el[data-k="hair"] {
    top: var(--cy);
    left: var(--cx);
    width: calc(14px + var(--cs) * 26px);
    height: calc(10px + var(--cs) * 22px);
    border: 1px solid transparent;
    border-top-color: rgba(30, 18, 8, 0.62);
    border-radius: 50%;
    transform: rotate(var(--crot));
    animation: cy-film-hair calc(2.6s * var(--cy-speed, 1)) steps(1, end) var(--cdel) infinite;
}
/* **שתי שריטות נוספות לכל פריט, ולא פריטים נוספים.**
   פסבדו-אלמנט אינו צומת ב-DOM ואינו שכבת קומפוזיטור נוספת -הוא מצויר בתוך
   השכבה של ההורה, שכבר מקודם- ולכן הכפלת מספר השריטות על המסך עולה כלום.
   אותו שיקול שכבר תועד כאן על קו הנוירון ועל רשת ה-‎peleg‎.

   ‎::after‎ הוא שריטה שנייה צמודה (שתי שריטות זו לצד זו הן מה שנראה על
   פילם שנשרט באותו מעבר), ו-‎::before‎ הוא **קטע קצר** שאינו חוצה את כל
   המסך -שריטה שנגמרת באמצע. לכל אחד מחזור באורך שונה, אחרת שלושתן
   מהבהבות יחד ונקראות כפס אחד עבה. */
.cyber-fx[data-cy="film"] .cy-el[data-k="scratch"]::after,
.cyber-fx[data-cy="film"] .cy-el[data-k="scratch"]::before {
    content: "";
    position: absolute;
    top: 0;
    width: 1px;
    pointer-events: none;
}
.cyber-fx[data-cy="film"] .cy-el[data-k="scratch"]::after {
    left: calc(3px + var(--cs) * 7px);
    height: 100%;
    background: linear-gradient(180deg,
        transparent, rgba(255, 240, 205, 0.5) 22%,
        rgba(255, 240, 205, 0.14) 60%, transparent);
    animation: cy-film-scratch2 calc(var(--cd) * 1.31 * var(--cy-speed, 1)) steps(1, end) var(--cdel) infinite;
}
.cyber-fx[data-cy="film"] .cy-el[data-k="scratch"]::before {
    left: calc(-4px - var(--cs) * 9px);
    height: calc(28% + var(--cs) * 26%);
    background: linear-gradient(180deg,
        transparent, rgba(90, 58, 20, 0.55) 30%,
        rgba(255, 236, 198, 0.42) 70%, transparent);
    animation: cy-film-scratch3 calc(var(--cd) * 0.78 * var(--cy-speed, 1)) steps(1, end) var(--cdel) infinite;
}
/* **העובי נגזר מ-‎--cs‎ ולא מ-‎nth-child‎.** על פילם אמיתי יש גם שערות
   דקות וגם שריטה שנחרשה עמוק, וכולן באותו עובי נקראות כרשת. מאז
   שהשריטות אינן יושבות במקומות קבועים ברשימה (הכמות נבחרת בניהול)
   ‎nth-child‎ אינו יכול להבטיח "אחת לכל שמונה"; ‎--cs‎ מוגרל פר-פריט
   בשרת, ולכן הוא נותן את אותה שונות בלי תלות במיקום. */
.cyber-fx[data-cy="film"] .cy-el[data-k="scratch"] {
    width: calc(1px + var(--cs) * 1.1px);
}
@keyframes cy-film-scratch2 {
    0%, 11%  { opacity: 0; }
    12%, 33% { opacity: 0.62; transform: translate3d(0, 0, 0); }
    34%, 51% { opacity: 0; }
    52%, 71% { opacity: 0.44; transform: translate3d(-31px, 0, 0); }
    72%, 86% { opacity: 0; }
    87%, 96% { opacity: 0.55; transform: translate3d(17px, 0, 0); }
    97%,100% { opacity: 0; }
}
@keyframes cy-film-scratch3 {
    0%, 15%  { opacity: 0.5; transform: translate3d(0, 0, 0); }
    16%, 40% { opacity: 0; }
    41%, 58% { opacity: 0.66; transform: translate3d(41px, 12%, 0); }
    59%, 74% { opacity: 0; }
    75%, 88% { opacity: 0.4; transform: translate3d(-22px, -18%, 0); }
    89%,100% { opacity: 0; }
}
@keyframes cy-film-scratch {
    0%, 7%   { opacity: 0; transform: translate3d(0, 0, 0) scaleY(1); }
    8%, 27%  { opacity: 0.85; transform: translate3d(0, 0, 0) scaleY(0.72); }
    28%, 44% { opacity: 0; }
    45%, 62% { opacity: 0.6; transform: translate3d(23px, 0, 0) scaleY(1); }
    63%, 79% { opacity: 0; }
    80%, 91% { opacity: 0.75; transform: translate3d(-14px, 0, 0) scaleY(0.9); }
    92%,100% { opacity: 0; }
}
/* **החלון הורחב מ-‎13%‎ ל-‎32%‎ מהמחזור.** נקודת אבק היא הבהוב של פריים,
   וזה נכון לפילם -אבל עם חלון כה צר נמדד ש-‎1.4‎ נקודות בלבד על המסך בכל
   רגע, כלומר האבק לא נקרא כלל. ארבעה הבזקים במקום שלושה, וכל אחד ארוך
   יותר; עם 11 פריטים זה ‎~3.5‎ נקודות בו-זמנית. */
/* **שתי נקודות לכל פריט אבק, מאותו טעם של השריטות.** נקודה בודדת קטנה
   מכדי להתחרות בקו שחוצה את כל המסך, ולכן ‎11‎ פריטים נקראו כפחות מ-‎4‎
   קווים. פסבדו-אלמנט אינו צומת ב-DOM ואינו שכבת קומפוזיטור, ולכן הכפלת
   מספר הנקודות עולה כלום -ואחרי ההוספה נמדד ‎~8‎ נקודות מול ‎~8‎ קווים,
   כלומר בדיוק השילוב השווה שהתבקש.

   מחזור באורך אחר (‎×1.43‎) והיסט משלה, אחרת שתי הנקודות מהבהבות יחד
   ונקראות ככתם אחד ולא כשתי פיסות לכלוך. */
.cyber-fx[data-cy="film"] .cy-el[data-k="dust"]::after {
    content: "";
    position: absolute;
    top: calc(6px + var(--cs) * 14px);
    left: calc(-5px - var(--cs) * 11px);
    width: calc(1px + var(--cs) * 3px);
    height: calc(1px + var(--cs) * 3px);
    border-radius: 50%;
    background: rgba(22, 12, 4, 0.7);
    pointer-events: none;
    animation: cy-film-dust calc(0.9s * 1.43 * var(--cy-speed, 1)) steps(1, end) var(--cdel) infinite;
}
@keyframes cy-film-dust {
    0%,  9%  { opacity: 0; }
    10%, 17% { opacity: 0.9; }
    18%, 37% { opacity: 0; }
    38%, 45% { opacity: 0.8; transform: translate3d(9px, -14px, 0); }
    46%, 63% { opacity: 0; transform: translate3d(0, 0, 0); }
    64%, 71% { opacity: 0.85; transform: translate3d(-11px, 8px, 0); }
    72%, 89% { opacity: 0; transform: translate3d(0, 0, 0); }
    90%, 97% { opacity: 0.75; transform: translate3d(6px, 11px, 0); }
    98%,100% { opacity: 0; transform: translate3d(0, 0, 0); }
}
@keyframes cy-film-hair {
    0%, 12%  { opacity: 0; }
    13%, 34% { opacity: 0.8; }
    35%, 63% { opacity: 0; }
    64%, 78% { opacity: 0.6; }
    79%,100% { opacity: 0; }
}
/* המחצית השנייה של המיקס: פסי הגליץ */
.cyber-fx[data-cy="mix"] .cy-el[data-k="bar"] {
    left: 0;
    top: calc(var(--cx) * 0.9);
    width: 100%;
    height: calc(var(--cs) * 9px);
    background: linear-gradient(90deg, transparent, rgba(255, 60, 160, 0.4) 20%,
                                rgba(60, 240, 255, 0.4) 55%, transparent);
    mix-blend-mode: screen;
    /* cy-mixbar ולא cy-glitch: שם הפס נדיר בכוונה (8% מהמחזור), וכאן הוא
       חצי מהפריטים -עם אותו חלון המיקס נמדד ריק בשני צילומים. */
    animation: cy-mixbar calc(var(--cd) * var(--cy-speed, 1)) steps(1, end) var(--cdel) infinite;
}
@keyframes cy-mixbar {
    0%, 14%  { opacity: 0; transform: translateX(0); }
    15%, 29% { opacity: 0.95; transform: translateX(-2.5%); }
    30%, 51% { opacity: 0; transform: translateX(0); }
    52%, 63% { opacity: 0.8; transform: translateX(2%); }
    64%, 85% { opacity: 0; transform: translateX(0); }
    86%, 95% { opacity: 1; transform: translateX(-1%); }
    96%,100% { opacity: 0; transform: translateX(0); }
}

/* ---- "אופק ניאון" ----
   רשת שזורמת על הקרקע לעבר הצופה, זוהר אופק מעליה, ועמודי אור שעולים
   ממנו. **הרעיון נשען על משהו שקיים בסצנה הזאת ולא בשום אפקט אחר: קו
   אופק אמיתי** -יש כאן נוף עם קרקע, ולכן רשת בפרספקטיבה נוחתת עליו
   במקום לרחף מעל מלבן.

   שלוש שכבות: ::before (הרשת בפרספקטיבה), ::after (זוהר האופק),
   והפריטים כעמודי אור. */
.cyber-fx[data-cy="neon"]::before,
.cyber-fx[data-cy="neon"]::after {
    content: '';
    position: absolute;
    pointer-events: none;
}
/* ---- מנהרת ניאון: רצפה ותקרה שמתכנסות לנקודת מגוז ----
   **הגרסה הקודמת הייתה דיסק שמש על האופק, והיא דווחה כ"תקוע על המסך סתם
   ככה".** היא צדקה: דיסק הוא אובייקט **אחד** בגודל קבוע שיושב באמצע הנוף
   ואינו קשור אליו, ולכן הוא נקרא כמדבקה. מנהרה, לעומת זאת, **ממלאת את
   המסך מהקצה העליון עד התחתון** ויש לה כיוון -היא נוסעת אל הצופה.

   שני משטחים זהים ומשוקפים: ‎::before‎ הרצפה (מוטה לאחור מלמטה) ו-‎::after‎
   התקרה (מוטה לפנים מלמעלה). שניהם מתכנסים אל אותה נקודה במרכז, וזו
   נקודת המגוז.

   **perspective() בתוך transform ולא על ההורה** -על ההורה הוא היה הופך את
   ‎.cyber-fx‎ ל-containing block של כל ילד ‎fixed‎, מלכודת שכבר הפילה כאן את
   כרטיס הפרויקט ואת קלף הצ'אט. ‎left/right‎ שליליים כי משטח מוטה מתרחב
   לצדדים ככל שהוא מתקרב.

   **הגובה נגזר מההיטל ולא נבחר.** משטח שהוטה ‎63°‎ מוקרן לכ-‎0.45‎ מגובהו,
   ולכן כדי שכל אחד יגיע עד אמצע המסך (‎50%‎) הוא חייב להיות ‎~110%‎. בגרסה
   של ‎54%‎ הרשת הגיעה ל-‎22%‎ בלבד -רצועה דקה בתחתית, וזה מה שנראה כ"לא
   מגיע מלמעלה למטה". */
.cyber-fx[data-cy="neon"]::before,
.cyber-fx[data-cy="neon"]::after {
    left: -45%;
    right: -45%;
    height: 110%;
    background-image:
        repeating-linear-gradient(90deg,
            rgba(255, 90, 205, 0.95) 0 2px, transparent 2px 42px),
        repeating-linear-gradient(0deg,
            rgba(140, 255, 240, 0.75) 0 2px, transparent 2px 42px);
}
.cyber-fx[data-cy="neon"]::before {
    bottom: 0;
    transform: perspective(760px) rotateX(63deg);
    transform-origin: 50% 100%;
    /* דעיכה אל נקודת המגוז, כדי שהרשת לא תיגמר בקו חד */
    -webkit-mask-image: linear-gradient(0deg, #000 0%, #000 26%, transparent 88%);
            mask-image: linear-gradient(0deg, #000 0%, #000 26%, transparent 88%);
    /* התנועה היא תקופת הדפוס **בדיוק** (42px), אחרת המחזור נקטע באמצעו
       וקופץ -אותו כלל שכבר נקנה כאן במפל. */
    animation: cy-neon-run calc(1.6s * var(--cy-speed, 1)) linear infinite;
}
.cyber-fx[data-cy="neon"]::after {
    top: 0;
    transform: perspective(760px) rotateX(-63deg);
    transform-origin: 50% 0;
    -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 26%, transparent 88%);
            mask-image: linear-gradient(180deg, #000 0%, #000 26%, transparent 88%);
    /* **הכיוון הפוך.** המשטח משוקף, ולכן אותו כיוון גלילה היה קורא כתקרה
       שנוסעת אחורה בזמן שהרצפה נוסעת קדימה -ואז אין מנהרה, יש שני משטחים. */
    animation: cy-neon-run-r calc(1.6s * var(--cy-speed, 1)) linear infinite;
}
@keyframes cy-neon-run {
    from { background-position: 0 0, 0 0; }
    to   { background-position: 0 42px, 0 42px; }
}
@keyframes cy-neon-run-r {
    from { background-position: 0 42px, 0 42px; }
    to   { background-position: 0 0, 0 0; }
}
/* ---- נקודת המגוז ----
   הזוהר שאליו שתי הרשתות מתכנסות. **הפריט הראשון בלבד**; 22 "עמודי אור"
   שהיו כאן קודם נקראו בצילום כקווים ורודים מפוזרים על השמיים ולא כאפקט,
   וכל אחד מהם היה שכבת קומפוזיטור משלו. */
.cyber-fx[data-cy="neon"] .cy-el { display: none; }
.cyber-fx[data-cy="neon"] .cy-el:nth-child(1) {
    display: block;
    left: 50%;
    top: 50%;
    width: min(52vw, 40rem);
    height: 3px;
    translate: -50% -50%;
    border-radius: 50%;
    background: rgba(255, 210, 245, 0.9);
    /* **הילה ולא שטח צבוע.** הגרסה הקודמת הייתה מלבן ורוד בגובה ‎22%‎ עם
       ‎mix-blend-mode: screen‎, וזה מה שדווח כ"רקע ורוד מוזר": ‎screen‎ מעל
       שמיים בהירים אינו נקרא כזוהר אלא כשכבת צבע על הנוף כולו. */
    box-shadow:
        0 0 14px 3px rgba(255, 90, 205, 0.8),
        0 0 44px 12px rgba(160, 90, 255, 0.35);
    animation: cy-neon-pulse calc(4.2s * var(--cy-speed, 1)) ease-in-out infinite;
}
@keyframes cy-neon-pulse {
    0%, 100% { opacity: 0.7; transform: scaleX(0.92); }
    50%      { opacity: 1;   transform: scaleX(1); }
}

@media (prefers-reduced-motion: reduce) {
    .cyber-fx .cy-el,
    .cyber-fx .cy-el::before,
    .cyber-fx .cy-el::after,
    .cyber-fx::before,
    .cyber-fx::after { animation-play-state: paused; }
}
/* סעיף "⚡ ביצועים": במצבים החסכוניים השכבה יורדת לגמרי -היא תוספת ולא
   חלק מהסצנה, ולכן היא הראשונה שמוותרים עליה. **וגם המארח של "מקדימה"**:
   בלעדיו נשאר ‎fixed‎ פרוש-מסך בעץ הציור אחרי שכל מה שבתוכו כובה, ודווקא
   כאן ויתרו עליו מטעמי ביצועים. */
body.perf-cyber-off .cyber-fx,
body.perf-cyber-off .cyber-front { display: none; }
