:root {
  /* כינויים בלבד. הערכים חיים ב-app.css, ושם בלבד. */
  --tv-surface:        var(--tommy-surface);
  --tv-surface-2:      var(--tommy-surface-2);
  --tv-card:           var(--tommy-panel);
  --tv-border:         var(--tommy-border);
  --tv-border-strong:  var(--tommy-border-strong);
  --tv-primary:        var(--tommy-primary);
  --tv-primary-rgb:    var(--tommy-primary-rgb);
  --tv-warning:        var(--tommy-warning);
  --tv-danger:         var(--tommy-danger);
  --tv-info:           var(--tommy-info);
  --tv-success:        var(--tommy-success);
  --tv-text:           var(--tommy-text);
  --tv-muted:          var(--tommy-text-muted);
  --tv-radius:         var(--tommy-radius);
  --tv-radius-lg:      var(--tommy-radius-lg);
  --tv-shadow-panel:   var(--shadow-panel);
}

/* ══════════════════════════════════════════════════════════════════
   .num — ספרות בתוך עברית

   מספר בעברית RTL נשבר: "25 מ׳" יכול להיראות "מ׳ 25", ו-"TM26004"
   מתהפך. Lovable פותר את זה ב-utility אחד, וטומי לא החזיק כזה בכלל.
   ══════════════════════════════════════════════════════════════════ */
/* `.num` is the incumbent and is already adopted — `qty()` in both the
   BOQ and Equipment renderers emits `<span class="num">`, and the
   browser suite reads quantities back out of it.

   The UI/UX contract names this primitive `tv-num`. Rather than write
   a second implementation of the same three declarations, the two
   names share one rule. Adding a duplicate would be exactly the
   drift this phase exists to remove. */
.num,
.tv-num {
  font-variant-numeric: tabular-nums;
  direction: ltr;
  unicode-bidi: isolate;
}

/* ─────────── Panel ─────────── */
.tv-panel {
  background: var(--tv-card);
  border: 1px solid var(--tv-border);
  border-radius: var(--tv-radius-lg);
  box-shadow: var(--tv-shadow-panel);
  overflow: hidden;
}
.tv-panel-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px;
  border-bottom: 1px solid var(--tv-border);
  padding: 14px 20px;
}
.tv-panel-head h2, .tv-panel-head h3 {
  margin: 0; font-size: 15px; font-weight: 600; color: var(--tv-text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tv-panel-head .tv-sub {
  margin: 2px 0 0; font-size: 12px; color: var(--tv-muted);
}
.tv-panel-body { padding: 16px 20px; }
.tv-panel-foot {
  display: flex; flex-wrap: wrap; align-items: center;
  justify-content: flex-end; gap: 8px;
  border-top: 1px solid var(--tv-border);
  background: var(--tv-surface);
  padding: 12px 20px;
}

/* ─────────── Stat / KPI ─────────── */
.tv-stats {
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  margin: 0 0 20px;
}
.tv-stat {
  background: var(--tv-card);
  border: 1px solid var(--tv-border);
  border-radius: var(--tv-radius-lg);
  box-shadow: var(--tv-shadow-panel);
  padding: 14px 16px;
}
.tv-stat-label { font-size: 13px; color: var(--tv-muted); }
/* Build 9226 §OwnerVisualCorrection ("MOBILE DASHBOARD — FAIL/MAJOR")
   — `overflow-wrap: anywhere` let the browser break a currency value
   literally anywhere it ran out of room, including mid-digit-group
   ("2,538,50" / "0 ₪") at the larger mobile font-size below. A KPI
   number is exactly the one place a line break must never land
   inside the value itself — `white-space: nowrap` is the standard,
   correct rule for that (every real value in this system is short
   enough to fit its card; if a future value somehow were not, an
   overflow is a far smaller visual defect than a split number). */
.tv-stat-value {
  margin-top: 4px; font-size: 24px; font-weight: 600;
  letter-spacing: -.01em; text-align: left;
  font-variant-numeric: tabular-nums; direction: ltr; unicode-bidi: isolate;
  color: var(--tv-text); white-space: nowrap;
}
.tv-stat-sub { margin-top: 2px; font-size: 12px; color: var(--tv-muted); }
.tv-stat.is-warning .tv-stat-value { color: var(--tv-warning); }
.tv-stat.is-danger  .tv-stat-value { color: var(--tv-danger); }

/* ─────────── Badge ─────────── */
.tv-badge {
  display: inline-flex; align-items: center; gap: 6px;
  border-radius: 999px; border: 1px solid;
  padding: 2px 10px; font-size: 12px; font-weight: 500; line-height: 20px;
  white-space: nowrap; vertical-align: middle;
}
.tv-badge .tv-dot {
  width: 6px; height: 6px; border-radius: 999px; background: currentColor;
}
.tv-badge-ok      { border-color: rgba(var(--tommy-primary-rgb),.30);  background: rgba(var(--tommy-primary-rgb),.12);  color: var(--tv-primary); }
.tv-badge-success { border-color: rgba(var(--tommy-success-rgb),.30); background: rgba(var(--tommy-success-rgb),.12); color: var(--tv-success); }
.tv-badge-warn    { border-color: rgba(var(--tommy-warning-rgb),.30); background: rgba(var(--tommy-warning-rgb),.12); color: var(--tv-warning); }
.tv-badge-danger  { border-color: rgba(var(--tommy-danger-rgb),.35);background: rgba(var(--tommy-danger-rgb),.12);color: var(--tv-danger); }
.tv-badge-info    { border-color: rgba(var(--tommy-info-rgb),.30);background: rgba(var(--tommy-info-rgb),.12);color: var(--tv-info); }
/* UI-1c: הייחוס כותב `bg-secondary` = oklch(.29), וטומי צבע כאן
   `surface-2` = oklch(.26). חמש הטונים האחרים כבר תואמים לייחוס
   בשקיפויות (30%/35% גבול, 12% רקע); זו הייתה החריגה היחידה. */
.tv-badge-neutral { border-color: var(--tv-border-strong); background: var(--tommy-secondary-surface); color: var(--tv-muted); }

/* ══════════════════════════════════════════════════════════════════
   Build 9224 — TommyProjectShell: לשוניות/נתיב טומי, לא כפתורי
   דפדפן ═══════════════════════════════════════════════════════════
   `.tps-link` / `.tps-tab` / `.tps-back` (tommy-projectshell.js) לא
   קיבלו אף פעם CSS משלהם. בלי `background`/`border`/`appearance`
   מוצהרים והם ללא global button-reset, הדפדפן מצייר אותם עם
   ה-UA-chrome המובנה שלו — רקע אפור בהיר, מסגרת 3D בולטת,
   `appearance:auto` — בדיוק "הקוביות עם הרקע הלבן" שדווחו על
   ממשק כהה. זה תיקון ממוקד (רק בתוך `[data-project-shell]`), לא
   reset גלובלי שעלול לגעת בכפתורים אחרים שלא נבדקו. */
[data-project-shell] button {
  appearance: none;
  -webkit-appearance: none;
  background: transparent;
  border: 0;
  color: inherit;
  font: inherit;
  cursor: pointer;
}

[data-project-shell] .tps-link {
  padding: 2px;
  border-radius: var(--tommy-radius-sm);
}
[data-project-shell] .tps-link:hover { color: var(--tommy-text); }

[data-project-shell] .tps-back {
  border: 1px solid var(--tommy-border-strong);
  background: var(--tommy-secondary-surface);
  border-radius: var(--tommy-radius-md);
}
[data-project-shell] .tps-back:hover {
  background: color-mix(in srgb, var(--tommy-secondary-surface) 55%, var(--tommy-primary) 10%);
}

[data-project-shell] .tps-tab {
  border-radius: var(--tommy-radius-sm) var(--tommy-radius-sm) 0 0;
  border-bottom: 2px solid transparent;
  padding: 7px 12px;
}
[data-project-shell] .tps-tab:hover {
  background: var(--tommy-secondary-surface);
}
/* ה-`after:` שבתבנית המקור הוא syntax של Tailwind JIT אמיתי —
   אין לו מימוש ב-utils.css הידני הזה, והוא no-op שקט. ההדגשה
   הירוקה של הלשונית הפעילה נכתבת כאן במקום, על `aria-selected`
   שהתבנית כבר פולטת ממילא — לא markup חדש. */
[data-project-shell] .tps-tab[aria-selected="true"] {
  border-bottom-color: var(--tommy-primary);
  background: color-mix(in srgb, var(--tommy-primary) 9%, transparent);
}

/* ── פחות גובה, פחות שטח מת ── ("must reduce ProjectShell vertical
   height, reduce dead space"). הריפוד/מרווחים המקוריים (Tailwind
   מועתק: px-6 pt-4, mt-3, mt-4) היו קצב של עמוד שיווקי, לא סרגל
   כלים הנדסי צפוף. */
[data-project-shell] .px-6.pt-4 { padding: 10px 20px 0; }
[data-project-shell] h1 { font-size: 17px; }
[data-project-shell] .mt-3 { margin-top: 8px; }
[data-project-shell] .mt-4 { margin-top: 8px; }

/* ── "חזרה לתפריט הראשי" הישן ו-hero הענק של Overview הופכים
   מבלוק צף מנותק להמשך ישיר של הכותרת של ProjectShell — לא
   מוסתרים, לא נהרסים, רק לא-כותרת-שנייה. הניווט של שני הכפתורים
   (data-nav-back / data-main) נשאר זהה לחלוטין; רק המראה משתנה,
   וממוקד רק במסכים שבהם ProjectShell מותקן (מזוהה דרך ~ , בלי
   markup/JS חדש). */
[data-project-shell] + .screen-back {
  display: inline-flex;
  margin: 0 22px;
  padding: 3px 2px;
  font-size: 12.5px;
  font-weight: 500;
  background: transparent;
  border: 0;
  border-radius: 0;
  color: var(--tommy-text-muted);
}
[data-project-shell] + .screen-back:hover { color: var(--tommy-text); border-color: transparent; }
/* Build 9226 §OwnerVisualConvergence — the old Overview `.hero`
   (title/paragraph/underline) that used to sit right after
   `.screen-back` is gone from index.html entirely now (removed, not
   hidden — see the header comment there): it duplicated the crumb/
   active-tab title ProjectShell already shows, and the reference has
   nothing at all in that position. The rules that used to demote it
   (`#overviewScreen [data-project-shell] + .screen-back + .hero ...`)
   are removed with it rather than left as dead CSS targeting a
   selector nothing matches. `.screen-back` itself is unchanged.

/* ══════════════════════════════════════════════════════════════════
   Build 9224 — Item 4: תוכניות חשמל, בלי "חלון בתוך חלון"
   ══════════════════════════════════════════════════════════════════
   הארכיטקטורה נשארת iframe (evpanel/index.html) — במפורש מותר,
   שכתוב מסוכן אסור. זה תיקון בצד טומי בלבד: מסירים את מסגרת ה-.box
   (רקע/גבול/צל/פינות עגולות) ואת שורת הכלים הכפולה שהייתה כותרת
   אפליקציה שנייה בפני עצמה, ומחליפים בסרגל פעולה דק אחד שמכיל רק
   את הפעולה האמיתית (שמירה לפרויקט). גובה ה-iframe עצמו הוגדל
   ונקשר ל-viewport כדי שהוא ימלא את אזור התוכן בפועל במקום קופסה
   קבועה של 900px שיוצרת "אי לבן" קטן באמצע עמוד ריק.

   Build 9225 §29 - גלילה כפולה (עמוד חיצוני + iframe פנימי, ביחד)
   הייתה עדיין מגבלה ידועה כאן, כי פתרון מלא נראה כמו גשר
   postMessage - שכתוב ארכיטקטורה מחוץ לתחום. בפועל evpanel/index.html
   נטען מאותו מקור (נתיב יחסי), אז אין צורך בגשר: `evpAutoSizeFrame`
   (assets/js/app.js) קורא ישירות ל-contentDocument.scrollHeight -
   בדיוק כמו evpApplyBrandLogo/evpApplyTommyFooter שכבר עשו זאת - וקובע
   את גובה ה-iframe לגובה התוכן האמיתי שלו, כך שרק העמוד החיצוני גולל. */
.evp-frame-host {
  background: transparent;
  border: 0;
  box-shadow: none;
  border-radius: 0;
  padding: 0;
}
.evp-frame-toolbar {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 14px;
  padding: 0 2px 10px;
  border-bottom: 1px solid var(--tommy-border);
  margin-bottom: 10px;
}
.evp-save-hint { color: var(--tommy-text-muted); font-size: 12.5px; }

/* ─────────── פס התקדמות ───────────
   UI-2A. פרימיטיב משותף ולא רכיב של הדשבורד: אותו פס מופיע בייחוס
   גם בסיור, גם ברשימת הסיורים וגם בשלבי הפרויקט. הוא נכתב פעם אחת.

   `direction: ltr` על המסלול הוא מכוון. המילוי מייצג אחוז, והאחוז
   גדל שמאלה→ימינה בכל שפה; פס שמתמלא מימין בעברית נקרא כמו פס
   שמתרוקן. הטקסט סביבו נשאר RTL. */
.tv-prog {
  height: 6px; border-radius: 999px;
  background: var(--tv-surface-2);
  overflow: hidden; direction: ltr;
}
.tv-prog > i {
  display: block; height: 100%;
  background: var(--tv-primary);
  border-radius: inherit;
  transition: width .2s;
}
.tv-prog.is-warn > i   { background: var(--tv-warning); }
.tv-prog.is-danger > i { background: var(--tv-danger); }

/* ─────────── Segmented ─────────── */
.tv-seg {
  display: inline-flex; gap: 2px; padding: 2px;
  border: 1px solid var(--tv-border-strong);
  border-radius: var(--tv-radius);
  background: var(--tv-surface);
}
.tv-seg button {
  border: 0; background: transparent; cursor: pointer;
  border-radius: 5px; padding: 6px 12px;
  font: inherit; font-size: 13px; font-weight: 500;
  color: var(--tv-muted); transition: color .15s, background .15s;
}
.tv-seg button:hover { color: var(--tv-text); }
.tv-seg button.is-active {
  background: var(--tv-surface-2); color: var(--tv-text);
  box-shadow: var(--tv-shadow-panel);
}

/* ─────────── שדה חיפוש עם אייקון בימין (RTL) ─────────── */
.tv-search { position: relative; flex: 1 1 224px; min-width: 200px; }
.tv-search input {
  width: 100%; height: 38px;
  border: 1px solid var(--tv-border-strong);
  border-radius: var(--tv-radius);
  background: var(--tv-surface);
  color: var(--tv-text); font: inherit; font-size: 14px;
  padding: 0 36px 0 12px;
}
.tv-search input::placeholder { color: var(--tv-muted); }
.tv-search input:focus {
  outline: none; border-color: rgba(var(--tommy-primary-rgb),.5);
  box-shadow: 0 0 0 2px rgba(var(--tommy-primary-rgb),.25);
}
.tv-search::before {
  content: '⌕'; position: absolute; inset-inline-end: 12px; top: 50%;
  transform: translateY(-50%); color: var(--tv-muted);
  font-size: 18px; pointer-events: none;
}
.tv-select {
  height: 38px; border: 1px solid var(--tv-border-strong);
  border-radius: var(--tv-radius); background: var(--tv-surface);
  color: var(--tv-text); font: inherit; font-size: 14px; padding: 0 12px;
}

.tv-toolbar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 12px;
  background: var(--tv-card);
  border: 1px solid var(--tv-border);
  border-radius: var(--tv-radius-lg);
  padding: 12px 16px; margin: 0 0 20px;
}

/* ─────────── Split view ───────────
   ב-RTL הילד הראשון יושב מימין. הרשימה ראשונה בקוד, ולכן היא
   מימין; הפרטים שניים, ולכן משמאל — בדיוק כמו ב-Lovable. */
.tv-split { display: grid; gap: 20px; align-items: start; }
@media (min-width: 1180px) {
  .tv-split { grid-template-columns: minmax(0, 1fr) 400px; }
  .tv-split-detail { position: sticky; top: 24px; align-self: start; }
}
.tv-split-list { min-width: 0; display: flex; flex-direction: column; gap: 16px; }

/* ─────────── שורת רשימה נבחרת ─────────── */
.tv-rows > * + * { border-top: 1px solid var(--tv-border); }
.tv-row {
  display: grid; gap: 12px; width: 100%;
  padding: 14px 20px; text-align: start;
  background: transparent; border: 0; cursor: pointer;
  color: var(--tv-text); font: inherit;
  transition: background .15s, box-shadow .15s;
}
@media (min-width: 900px) {
  .tv-row { grid-template-columns: minmax(0,1fr) 180px 190px; align-items: start; }
}
.tv-row:hover { background: var(--tv-surface); }
.tv-row.is-selected {
  background: rgba(var(--tommy-primary-rgb),.08);
  /* הפס הנבחר בצד ההתחלה — בימין בעברית. */
  box-shadow: inset -2px 0 0 0 var(--tv-primary);
}
/* UI-2A: הכותרת והשורה שמתחתיה נחתכות ולא נשברות.
   בייחוס שתיהן `truncate`. בלי זה שם פרויקט ארוך דוחף את התג ואת
   הסכום מהשורה — וזו בדיוק התקלה ש-`.dr-name` נכתבה כדי למנוע.
   `.tv-row-note` דווקא כן נשברת: היא נושאת את *הסיבה*, וסיבה חתוכה
   באמצע היא מידע שאבד. */
.tv-row-title { font-size: 14px; font-weight: 500; margin: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tv-row-meta  { margin: 2px 0 0; font-size: 12.5px; color: var(--tv-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tv-row-note  { margin: 4px 0 0; font-size: 12px; color: var(--tv-muted); }

/* ─────────── בלוקים: הנדסה מול מסחר ───────────
   ההפרדה היא ויזואלית ומכוונת: הדרישה ההנדסית על משטח נייטרלי,
   המידע המסחרי בגוון המותג. אי אפשר לבלבל ביניהם במבט. */
.tv-block {
  border: 1px solid var(--tv-border);
  background: var(--tv-surface);
  border-radius: var(--tv-radius);
  padding: 12px 14px;
}
.tv-block + .tv-block { margin-top: 14px; }
.tv-block-commercial {
  border-color: rgba(var(--tommy-primary-rgb),.30);
  background: rgba(var(--tommy-primary-rgb),.08);
}
.tv-eyebrow {
  margin: 0; font-size: 12px; letter-spacing: .04em;
  text-transform: uppercase; color: var(--tv-muted);
}
.tv-block-commercial .tv-eyebrow { color: var(--tv-primary); }

/* שורת נתון: תווית מימין, ערך משמאל */
.tv-dl { margin: 8px 0 0; display: grid; gap: 6px; font-size: 13px; }
.tv-dl-row { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.tv-dl-row dt { color: var(--tv-muted); }
.tv-dl-row dd {
  margin: 0; font-weight: 500;
  font-variant-numeric: tabular-nums; direction: ltr; unicode-bidi: isolate;
}
.tv-dl-row dd.is-danger { color: var(--tv-danger); }

/* ─────────── הערות מוטבעות ─────────── */
.tv-note {
  display: block; border-radius: var(--tv-radius);
  border: 1px solid var(--tv-border); background: var(--tv-surface);
  padding: 10px 12px; font-size: 12.5px; line-height: 1.7;
  color: var(--tv-muted);
}
.tv-note-warn   { border-color: rgba(var(--tommy-warning-rgb),.30); background: rgba(var(--tommy-warning-rgb),.10); color: var(--tv-warning); }
.tv-note-danger { border-color: rgba(var(--tommy-danger-rgb),.30);background: rgba(var(--tommy-danger-rgb),.10);color: var(--tv-danger); }
.tv-note-ok     { border-color: rgba(var(--tommy-success-rgb),.30); background: rgba(var(--tommy-success-rgb),.10); color: var(--tv-success); }

/* ─────────── מצב ריק ─────────── */
.tv-empty {
  padding: 40px 20px; text-align: center;
  font-size: 14px; color: var(--tv-muted);
}

/* ─────────── כותרת מסך ─────────── */
.tv-head { margin: 0 0 20px; }
.tv-head .tv-crumb { margin: 0; font-size: 12.5px; color: var(--tv-muted); }
/* UI-2B — `h1` היה חסר כאן לגמרי.
   הדשבורד מרנדר `<h1>` (הברכה היא זהות העמוד), והכלל הזה כיסה רק
   `h2`. התוצאה: הכותרת נפלה לברירת המחדל של הדפדפן — `2em` עם
   `margin:.67em 0` — כלומר גודל שרירותי ומרווח עליון שלא ביקש אותו
   אף אחד. זה חלק מהסיבה שההיררכיה בראש העמוד נראתה רופפת. */
.tv-head h1,
.tv-head h2 { margin: 2px 0 0; font-size: 24px; font-weight: 600; letter-spacing: -.01em; }
.tv-head .tv-lede { margin: 4px 0 0; font-size: 14px; color: var(--tv-muted); }
.tv-head-row {
  display: flex; flex-wrap: wrap; align-items: flex-end;
  justify-content: space-between; gap: 16px;
}
.tv-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }

/* ─────────── טבלה קומפקטית ─────────── */
.tv-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.tv-table th, .tv-table td {
  padding: 8px 10px; text-align: start; vertical-align: top;
  border-bottom: 1px solid var(--tv-border);
  overflow-wrap: anywhere;
}
.tv-table th { color: var(--tv-muted); font-weight: 500; }
.tv-table tr:last-child td { border-bottom: 0; }

/* ─────────── מובייל: רשימה ואז פרטים, לא שתי עמודות ─────────── */
@media (max-width: 1179px) {
  .tv-split-detail { margin-top: 4px; }
  .tv-row { grid-template-columns: 1fr; }
  /* חזרה לרשימה מוצגת רק כשאין מקום לשתי העמודות. */
  .tv-back-to-list { display: inline-flex; }
}
.tv-back-to-list { display: none; }

/* ─────────── כותרת עמוד: crumb עצמאי ─────────── */
.tv-crumb { margin: 0; font-size: 12.5px; color: var(--tv-muted); }

/* ─────────── פאנל עם ריפוד פנימי ─────────── */
.tv-panel-pad { padding: 16px 20px; }
.tv-panel-pad > h4:first-child { margin-top: 0; }

/* ─────────── היררכיה: אתר ← אזור ───────────
   משמש את מסך כתב הכמויות. הכותרת המתקפלת היא אותו רכיב שהציוד
   משתמש בו לקטגוריה, ולכן היא כאן ולא בקובץ מסך. */
.tv-boq-site { margin-bottom: 14px; padding: 16px 20px; }
.tv-boq-site > h4 {
  margin: 0 0 10px; font-size: 15px; font-weight: 600;
  display: flex; align-items: center; gap: 8px;
}
.tv-boq-site > h4::before {
  content: '\1F3E2'; /* 🏢 — purely decorative, matches Lovable's group icon */
  font-size: 15px; line-height: 1;
}
.tv-boq-area { margin: 8px 0; }
.tv-area-head {
  display: flex; justify-content: space-between; align-items: center;
  width: 100%; gap: 10px;
  background: rgba(var(--tommy-primary-rgb), .05);
  border: 1px solid var(--tv-border);
  border-radius: var(--tv-radius);
  padding: 10px 12px; margin: 0 0 8px;
  color: var(--tv-text); font: inherit; font-weight: 600;
  cursor: pointer; text-align: start;
}
.tv-area-head:hover { background: rgba(var(--tommy-primary-rgb), .09); }
.tv-area-head-meta { display: flex; align-items: center; gap: 8px; font-weight: 500; }
.tv-area-head-total {
  font-weight: 700; font-variant-numeric: tabular-nums;
  font-family: var(--tommy-font-mono, ui-monospace, monospace);
}

/* ─────────── שורת פריט: מחיר בולט, כמו בלוח הפניה ─────────── */
.tv-table td:nth-child(4),
.tv-table td:nth-child(5) {
  font-weight: 700; font-variant-numeric: tabular-nums;
}
.tv-table tbody tr:hover { background: rgba(var(--tommy-primary-rgb), .04); }

/* ─────────── שורת טבלה מוחרגת ─────────── */
.tv-table tr.is-excluded > td { opacity: .55; text-decoration: line-through; }
.tv-table tr.is-excluded > td:last-child { text-decoration: none; }

/* ─────────── שלבי חישוב ─────────── */
.tv-steps {
  margin: 6px 0 0; padding-inline-start: 18px;
  font-size: 13px; line-height: 1.7; color: var(--tv-muted);
}

/* ─────────── מובייל: טבלה גוללת בתוך עצמה ─────────── */
@media (max-width: 780px) {
  .tv-table { display: block; overflow-x: auto; white-space: nowrap; }
  .tv-stats { grid-template-columns: repeat(2, 1fr); }
}


/* ══════════════════════════════════════════════════════════════════
   VISUAL PARITY V1 — סיור ומסמכים (Build 9.168)
   ------------------------------------------------------------------
   מקור: `src/routes/projects.$id.survey.tsx`
         `src/routes/projects.$id.documents.tsx`

   כאן לא שונתה שורת HTML אחת ולא שורת לוגיקה אחת. הסיור הוא מנוע
   של אלפי שורות עם תנאים הנדסיים ומחזור חיי SurveyInstance; שכתוב
   שלו בשביל מראה היה מסכן בדיוק את מה שאין סיבה לסכן. מה שהשתנה
   הוא הדקדוק הוויזואלי של המחלקות שכבר קיימות.
   ══════════════════════════════════════════════════════════════════ */

/* ── ראש הסיור ──
   Lovable: פאנל עם סיכום התקדמות, לא כותרת עם פס ירוק תחתון. */
.wizard {
  background: var(--tv-card);
  border: 1px solid var(--tv-border);
  border-radius: var(--tv-radius-lg);
  box-shadow: var(--tv-shadow-panel);
}
.wizard-head {
  background: var(--tv-surface);
  border-bottom: 1px solid var(--tv-border);   /* היה פס ירוק 4px */
  padding: 18px 20px;
}
.wizard-head h1 { font-size: 22px; font-weight: 600; letter-spacing: -.01em; }
.wizard-head p { font-size: 14px; margin-top: 6px; }
.progress { height: 6px; background: var(--tv-surface-2); }
.progress i { box-shadow: none; }   /* הזוהר הוחלף במילוי נקי */
.progress-box span { font-weight: 600; }

/* ── סרגל הפעולות ── */
.toolbar button {
  border-radius: var(--tv-radius);
  border-color: var(--tv-border-strong);
  background: var(--tv-surface-2);
  font-weight: 500; font-size: 13px; padding: 8px 12px;
}
.toolbar button:hover { background: var(--tv-surface); border-color: var(--tv-primary); }
.toolbar .green-action { color: var(--tommy-primary-contrast); font-weight: 600; }

/* ── כרטיס מקטע ── */
.wizard-body { padding: 20px; }
.step h2 { font-size: 20px; font-weight: 600; letter-spacing: -.01em; }
.step h3 { font-size: 16px; font-weight: 600; margin: 20px 0 10px; }
.step-help { font-size: 13.5px; line-height: 1.7; margin-bottom: 16px; }
.step-count {
  background: rgba(var(--tommy-primary-rgb), .12);
  border-color: rgba(var(--tommy-primary-rgb), .30);
  color: var(--tv-primary);
  font-size: 12px; font-weight: 600; padding: 3px 10px;
}

/* ── שדות ──
   Lovable: תווית 13px בינונית מעל שדה 38px. טומי היה 15px/900
   ושדה 18px — כבד מדי ביחס לשאר המערכת אחרי המעבר. */
.field span { font-size: 13px; font-weight: 500; }
.field input, .field select, .field textarea {
  border-radius: var(--tv-radius);
  border-color: var(--tv-border-strong);
  background: var(--tommy-input);
  font-size: 14px; padding: 9px 12px;
}
.field input:focus, .field select:focus, .field textarea:focus {
  border-color: rgba(var(--tommy-primary-rgb), .5);
  box-shadow: 0 0 0 2px rgba(var(--tommy-primary-rgb), .25);
}
.field-grid { gap: 16px; }

/* ── שדה מותנה ──
   נשאר בלוק משלו, כך שברור שהוא נפתח בעקבות תשובה. */
.conditional {
  border-radius: var(--tv-radius-lg);
  background: var(--tv-surface);
  padding: 16px;
}

/* ── Segmented ── */
.seg button {
  border-radius: var(--tv-radius);
  border-color: var(--tv-border-strong);
  background: var(--tv-surface);
  font-weight: 500; font-size: 13px;
}
.seg button.active {
  background: rgba(var(--tommy-primary-rgb), .12);
  border-color: rgba(var(--tommy-primary-rgb), .35);
  color: var(--tv-primary);
}

/* ── ניווט המקטעים ──
   ב-Lovable זו עמודה דביקה של 240px; בטומי הניווט כבר בימין.

   UI-1c: כאן ישבה הגדרה שנייה של `.nav-item.active` עם רקע ירוק
   בשקיפות .12, והיא זו שגברה בפועל על ההגדרה שב-app.css. שתי
   הגדרות מתחרות לאותו מצב הן בדיוק הסחיפה שהשלב הזה קיים כדי
   להסיר, ולכן היא הוסרה — הניווט הפעיל מוגדר במקום אחד בלבד. */
.nav-sub-dot { background: var(--tv-primary); }

/* ── משבצות העלאה ── */
.upload-tile {
  border-radius: var(--tv-radius-lg);
  background: var(--tv-surface);
  font-weight: 500; font-size: 13px;
}
.upload-tile:hover { border-color: rgba(var(--tommy-primary-rgb), .5); }

/* ── הערה מוטבעת ──
   הייתה ענבר קשיח. עכשיו טוקן, ובאותה שפה של `.tv-note-warn`. */
.pill-note {
  background: rgba(var(--tommy-warning-rgb), .10);
  border-color: rgba(var(--tommy-warning-rgb), .30);
  color: var(--tv-warning);
  border-radius: var(--tv-radius);
  font-size: 13px; line-height: 1.7;
}

/* ── כפתורי הניווט בתחתית ── */
.wizard-actions { padding: 16px 20px; }
.btn { border-radius: var(--tv-radius); font-size: 14px; font-weight: 600; padding: 10px 18px; }
.btn.primary { color: var(--tommy-primary-contrast); }
.btn.secondary { background: var(--tv-surface-2); border: 1px solid var(--tv-border-strong); }

/* ── כרטיסי סיכום ── */
.summary-card, .circuit-card, .breaker-box, .check-row label {
  border-radius: var(--tv-radius-lg);
  border-color: var(--tv-border);
  background: var(--tv-surface);
}
.check-row label { font-weight: 500; font-size: 14px; }

/* ══════════════════════════════════════════════════════════════════
   מסמכים — פריסת שתי עמודות
   Lovable: `xl:[minmax(0,380px) minmax(0,1fr)]` — הרשימה צרה
   וראשונה, כלומר בימין ב-RTL; הסקירה רחבה ומשמאל.
   ══════════════════════════════════════════════════════════════════ */
.tv-docs { display: grid; gap: 16px; align-items: start; }
@media (min-width: 1180px) {
  .tv-docs { grid-template-columns: minmax(0, 380px) minmax(0, 1fr); }
  .tv-docs-list { position: sticky; top: 24px; align-self: start; max-height: calc(100vh - 48px); }
  .tv-docs-list .tv-rows { overflow-y: auto; }
}

/* ── מודלים: אותה שפה כמו הפאנלים ── */
.modal {
  border-radius: var(--tv-radius-lg);
  background: var(--tv-card);
  border-color: var(--tv-border);
}
.modal-actions button {
  border-radius: var(--tv-radius);
  border-color: var(--tv-border-strong);
  background: var(--tv-surface-2);
  font-weight: 500; height: 44px;
}
.modal-actions button:hover {
  border-color: var(--tv-primary);
  background: rgba(var(--tommy-primary-rgb), .10);
}
.toast {
  border-radius: var(--tv-radius);
  border-color: rgba(var(--tommy-primary-rgb), .5);
  background: var(--tv-card);
  font-weight: 500;
}

/* ── מובייל: הסיור נשאר שמיש בשטח ── */
@media (max-width: 780px) {
  .field-grid { grid-template-columns: 1fr; }
  .check-row { grid-template-columns: 1fr; }
  .photo-grid { grid-template-columns: repeat(2, 1fr); }
  .wizard-body { padding: 16px; }
  .wizard-head { padding: 16px; }
  .tv-docs { grid-template-columns: 1fr; }
}

/* ══════════════════════════════════════════════════════════════════
   CORRECTION PASS (Build 9.169)
   תיקונים שנמצאו בבדיקה ידנית ב-Vercel — לא בבדיקות אוטומטיות.
   ══════════════════════════════════════════════════════════════════ */

/* ── בורר כהה משותף ──
   הדפקט: בורר סטטוס הפרויקט נפתח לבן עם טקסט בהיר. `<option>`
   מרונדר על ידי מערכת ההפעלה, ויורש רק אם צובעים אותו במפורש.
   זה חל על **כל** בורר במערכת, ולכן זה נכתב פעם אחת ולא פר מסך. */
select,
.tv-select,
.field select,
.boq-controls select,
select.soft-select {
  background-color: var(--tommy-input);
  color: var(--tommy-text);
  border: 1px solid var(--tommy-border-strong);
  border-radius: var(--tv-radius);
  font: inherit;
  font-size: 14px;
  padding: 8px 12px;
  min-height: 38px;
  /* החץ המובנה מרונדר בהיר במערכות מסוימות. */
  appearance: none; -webkit-appearance: none;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--tommy-text-muted) 50%),
    linear-gradient(135deg, var(--tommy-text-muted) 50%, transparent 50%);
  background-position: left 14px center, left 9px center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-inline-start: 32px;
}
select:focus, .tv-select:focus {
  outline: none;
  border-color: rgba(var(--tommy-primary-rgb), .5);
  box-shadow: 0 0 0 2px rgba(var(--tommy-primary-rgb), .25);
}
/* התפריט עצמו. בלי שתי השורות האלה הוא נפתח לבן. */
select option,
select optgroup {
  background-color: var(--tommy-surface-2);
  color: var(--tommy-text);
}
select option:checked,
select option:hover {
  background-color: var(--tommy-accent-surface);
  color: var(--tommy-text);
}
/* אותו טיפול לכל קלט, כדי שלא יישאר שדה לבן אחד. */
input, textarea {
  color-scheme: dark;
}

/* ── כפתורים משותפים ── */
.tv-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  border: 1px solid var(--tv-border-strong);
  background: var(--tv-surface-2);
  color: var(--tv-text);
  border-radius: var(--tv-radius);
  font: inherit; font-size: 14px; font-weight: 500;
  min-height: 38px; padding: 8px 16px; cursor: pointer;
  transition: background .15s, border-color .15s;
}
.tv-btn:hover { border-color: rgba(var(--tommy-primary-rgb), .5); }

/* ── UI-1: the five variants from the reference (§7) ──
   primary reserved for the main action · secondary/outline for normal
   supporting actions · ghost for low-emphasis utilities · danger only
   for destructive ones. Sizes 38px / 32px.

   `.tv-btn` alone is the SECONDARY appearance — that is what it
   already looked like, and three call sites depend on it. Rather than
   redefine it and move those, `.tv-btn-secondary` is an explicit alias
   so new code can name what it means. */
.tv-btn-primary {
  background: var(--tv-primary);
  border-color: var(--tv-primary);
  color: var(--tommy-primary-contrast);
  font-weight: 600;
}
.tv-btn-primary:hover { filter: brightness(1.08); }

.tv-btn-secondary { background: var(--tv-surface-2); border-color: var(--tv-border-strong); }
.tv-btn-secondary:hover { border-color: rgba(var(--tommy-primary-rgb), .5); }

.tv-btn-outline { background: transparent; border-color: var(--tv-border-strong); }
.tv-btn-outline:hover { background: var(--tv-surface-2); border-color: rgba(var(--tommy-primary-rgb), .5); }

.tv-btn-ghost { background: transparent; border-color: transparent; color: var(--tv-muted); }
.tv-btn-ghost:hover { background: var(--tv-surface-2); color: var(--tv-text); border-color: transparent; }

/* UI-1c — מילוי מלא, כמו בייחוס:
   `border-transparent bg-destructive text-destructive-foreground
    hover:bg-destructive/90`.

   עד עכשיו זה היה מילוי בשקיפות .12 עם מלל אדום — כלומר כפתור
   הרסני שנראה כמו תג. המילוי המלא הוא מה שמבדיל אותו מכל כפתור
   אחר במסך, וזו כל מטרתו.

   המלל כהה ולא לבן. הייחוס כותב לבן, אבל מניח אדום כהה משלו; על
   האדום של טומי לבן נותן 2.88:1. ההנמקה המלאה יושבת ליד
   `--tommy-danger-contrast` ב-app.css. */
.tv-btn-danger {
  background: var(--tv-danger);
  border-color: transparent;
  color: var(--tommy-danger-contrast);
}
.tv-btn-danger:hover {
  /* `/90` בייחוס: הצבע מדולל אל הרקע, כלומר מעט כהה יותר. */
  background: color-mix(in srgb, var(--tv-danger) 90%, transparent);
  border-color: transparent;
}
@supports not (background: color-mix(in srgb, red 50%, blue)) {
  .tv-btn-danger:hover { background: rgba(var(--tommy-danger-rgb), .9); }
}

/* sizes */
.tv-btn-sm { min-height: 32px; padding: 5px 12px; font-size: 13px; }
.tv-btn-lg { min-height: 46px; padding: 12px 22px; font-size: 15px; }

/* ── focus, disabled, icons ──
   `:focus-visible` only, so a mouse click does not leave a ring behind
   while the keyboard still gets one. The accessibility journey asserts
   that a disabled control neither takes focus nor acts, so `disabled`
   must LOOK disabled as well as be it. */
.tv-btn:focus-visible {
  outline: 2px solid rgba(var(--tommy-primary-rgb), .60);
  outline-offset: 2px;
}
.tv-btn[disabled],
.tv-btn[aria-disabled="true"] {
  opacity: .45;
  cursor: not-allowed;
  pointer-events: none;
}
/* An icon inside a button must not flip with the RTL text flow, and
   must not shrink when the label is long. */
.tv-btn svg,
.tv-btn .tv-btn-ico { width: 16px; height: 16px; flex: 0 0 auto; }
.tv-btn-sm svg,
.tv-btn-sm .tv-btn-ico { width: 14px; height: 14px; }

/* ══════════════════════════════════════════════════════════════════
   פנקס הפרויקט — ידית וטיפוגרפיה
   ══════════════════════════════════════════════════════════════════ */

/* הידית הסגורה: מפעיל אופקי וקריא, לא מילה מסובבת.
   שקוף למחצה כדי שלא יחסום תוכן. */
.tnb:not(.tnb-open) .tnb-tab {
  /* 9223 — היה `inset-inline-start:0`, שבעמוד RTL (כל האפליקציה)
     מתורגם בפועל ל-`right:0` הפיזי — מתנגש עם ה-`left-0` הפיזי
     שה-JS (tommy-notebook.js) כבר שם על אותו אלמנט (Tailwind).
     שתי התכונות חלות בו-זמנית (הן לא אותה תכונת CSS, אין כאן
     "ניצחון מפרט/סדר" רגיל), וכש-left:0 וגם right:0 מוגדרים יחד
     על position:fixed עם width:auto — הקופסה נמתחת על כל רוחב
     המסך. זו הייתה הסיבה האמיתית, שאומתה ברינדור בדפדפן אמיתי,
     לכך שכפתור "הערה מהירה" הופיע במרכז המסך: `align-self:center`
     שלו (למטה) מרכז אותו בתוך קופסה שגויה שרוחבה כל המסך. הדרישה
     כאן היא פיזית (קצה שמאל אמיתי), לא לוגית/כיוונית — `left:0`
     במפורש, כמו ב-JS, מסיר את הסתירה ומחזיר את הקופסה לרוחב-תוכן
     טבעי מוצמד לשמאל האמיתי. */
  position: fixed; left: 0; top: 50%;
  transform: translateY(-50%);
  display: flex; flex-direction: column; gap: 8px;
  padding: 10px; z-index: 60;
}
.tnb-handle {
  display: flex; align-items: center; gap: 10px;
  /* אופקי. `writing-mode` הוסר לחלוטין. */
  writing-mode: horizontal-tb;
  background: color-mix(in srgb, var(--tommy-panel) 88%, transparent);
  border: 1px solid var(--tv-border-strong);
  border-radius: 999px;
  box-shadow: 0 8px 24px -12px rgba(0,0,0,.8);
  color: var(--tv-text); font: inherit; font-size: 15px; font-weight: 600;
  padding: 12px 18px; min-height: 48px; cursor: pointer;
  transition: border-color .15s, background .15s;
}
@supports not (background: color-mix(in srgb, red 50%, blue)) {
  .tnb-handle { background: var(--tommy-panel); }
}
.tnb-handle:hover, .tnb-handle:focus-visible {
  border-color: rgba(var(--tommy-primary-rgb), .55);
  background: var(--tommy-panel);
}
.tnb-handle-ico { font-size: 17px; color: var(--tv-primary); line-height: 1; }
.tnb-handle-label { white-space: nowrap; }
.tnb-handle-count {
  background: rgba(var(--tommy-primary-rgb), .16);
  color: var(--tv-primary);
  border-radius: 999px; padding: 1px 9px; font-size: 13px; font-weight: 700;
}
.tnb-quick {
  align-self: center;
  width: 40px; height: 40px; border-radius: 999px;
  background: color-mix(in srgb, var(--tommy-panel) 88%, transparent);
  border: 1px solid var(--tv-border-strong);
  color: var(--tv-primary); font-size: 22px; line-height: 1; cursor: pointer;
}
.tnb-quick:hover { border-color: rgba(var(--tommy-primary-rgb), .55); }

/* Build 9224 — Item 3: אשכול-קצה קומפקטי אחד, לא שתי צורות צפות
   בגדלים שונים באמצע גובה המסך. 9223 תיקן את המיקום הפיזי (שמאל
   אמיתי, לא מרכז) — זה כאן לא נוגע בו (position:fixed;left:0
   נשארים כפי שהם, ראה הכלל למעלה), רק בגודל, בקצב הפנימי ובעיגון
   האנכי: פינה תחתונה-שמאלית קומפקטית במקום ריחוף באמצע המסך.
   הפאנל הבודק ב-responsive-quicknote-sidebar-9223.spec.js בודק רק
   ציר X של הטריגר/פאנל — לא נוגע בשינוי הזה. */
.tnb:not(.tnb-open) .tnb-tab {
  top: auto;
  bottom: 20px;
  transform: none;
  flex-direction: row;
  align-items: center;
  gap: 0;
  padding: 4px;
  background: color-mix(in srgb, var(--tommy-panel) 92%, transparent);
  border: 1px solid var(--tv-border-strong);
  border-left: none;
  border-radius: 0 16px 16px 0;
  box-shadow: 2px 6px 20px -8px rgba(0,0,0,.55);
}
/* מעל `.tnb-tab` (לא `.tnb-handle`/`.tnb-quick` בבדידות): המרקאפ
   המקורי (tommy-notebook.js) נושא גם מחלקות-utility מ-Tailwind
   (`text-[13px]`, `px-3`, `bg-card`, `border-border-strong`...)
   שחיות ב-tommy-lovable-utils.css, שנטען אחרי tommy-lovable.css —
   כלל יחיד-מחלקה כאן היה מפסיד להן בספציפיות שווה+סדר-טעינה
   מאוחר יותר, בדיוק כמו שקרה ל-`.tps-*` (Item 2). ההיקף הזה נותן
   שתי מחלקות, שמנצח בוודאות בלי !important. */
.tnb-tab .tnb-handle {
  min-height: 30px;
  padding: 5px 12px;
  font-size: 12px;
  font-weight: 600;
  gap: 6px;
  background: transparent;
  border: 0;
  box-shadow: none;
  border-radius: 999px;
}
.tnb-tab .tnb-handle:hover, .tnb-tab .tnb-handle:focus-visible {
  background: color-mix(in srgb, var(--tommy-primary) 12%, transparent);
  border-color: transparent;
}
.tnb-tab .tnb-handle-ico { font-size: 13px; }
.tnb-tab .tnb-handle-count { padding: 0 7px; font-size: 12px; }
/* .tnb-quick's own text node is "+ הערה מהירה" (tommy-notebook.js) —
   at a 26px icon-only box that wraps into an unreadable two-line
   mess ("הע/מהי"), exactly the "awkward text wrapping" the ticket
   calls out. The label stays for accessibility (aria-label/title
   already on the button, read by screen readers and shown as a
   native tooltip on hover) but is visually collapsed to font-size:0
   and replaced on screen by a single "+" glyph — true icon-only,
   no JS/markup change. Padding is zeroed explicitly (`px-3 py-2`
   utilities on the element would otherwise still apply inside the
   fixed 26px box). */
.tnb-tab .tnb-quick {
  align-self: center;
  width: 26px; height: 26px;
  display: flex; align-items: center; justify-content: center;
  padding: 0;
  font-size: 0;
  background: transparent;
  border: 0;
  border-inline-start: 1px solid var(--tv-border-strong);
  border-radius: 0;
  box-shadow: none;
}
.tnb-tab .tnb-quick::before {
  content: "+";
  font-size: 17px;
  line-height: 1;
  color: var(--tv-primary);
}
.tnb-tab .tnb-quick:hover {
  background: color-mix(in srgb, var(--tommy-primary) 12%, transparent);
  border-color: var(--tv-border-strong);
}

/* המגירה הפתוחה: טיפוגרפיה קריאה. */
.tnb-open .tnb-panel { width: 420px; }
.tnb-head h4 { font-size: 19px; font-weight: 600; }
.tnb-sub { font-size: 13px; line-height: 1.7; }
.tnb-note { padding: 14px 0; }
.tnb-meta { font-size: 12.5px; }
.tnb-body { font-size: 15px; line-height: 1.75; }
.tnb-acts .soft-btn { font-size: 13px; }
.tnb-acts .soft-btn.is-on {
  border-color: rgba(var(--tommy-primary-rgb), .5);
  color: var(--tv-primary);
}
.tnb-tools input, .tnb-compose textarea { font-size: 14px; }
.tnb-compose textarea { min-height: 110px; }
.tnb-pinbox {
  display: flex; align-items: center; gap: 8px;
  margin: 8px 0; font-size: 13.5px; color: var(--tv-text); cursor: pointer;
}
.tnb-pinbox input { width: 17px; height: 17px; accent-color: var(--tv-primary); }
.tnb .seg button { font-size: 13px; }

/* ── הערות מסומנות בסקירה ── */
.ov-pinned { margin: 0 0 20px; }
.ov-pinned-row { grid-template-columns: minmax(0,1fr) auto; }
.ov-pinned-text {
  margin: 0; font-size: 15px; line-height: 1.7; color: var(--tv-text);
  white-space: pre-wrap; overflow-wrap: anywhere;
}

/* ── הערות ומשימות (Build 9225 §9) ──────────────────────────────
   אותה שפה חזותית של שאר הכרטיסים בסקירה (tv-panel/tv-row/tv-sub),
   בלי משטחים/צבעים פרטיים - ר' Build 9225 §8. */
.ov-notes-tasks .tv-panel-head { align-items: flex-start; }
.ov-nt-type {
  /* Build 9225 — found during a §17-19 regression sweep: 11px violated
     the system-wide 12px typography floor locked by build-9168.test.js.
     This was a latent bug from §9's original implementation, never
     caught because the Node-regression ledger only re-runs files not
     already recorded — a stale cached PASS hid it all session. */
  display: inline-block; font-size: 12px; font-weight: 600;
  padding: 1px 7px; border-radius: 999px;
  background: var(--tv-surface); color: var(--tv-muted);
  border: 1px solid var(--tv-border);
}
.ov-nt-type.is-task {
  color: var(--tommy-primary);
  border-color: rgba(var(--tommy-primary-rgb), .4);
  background: rgba(var(--tommy-primary-rgb), .1);
}
.ov-nt-row .ov-pinned-text { font-size: 14px; margin-top: 4px; }
.ov-nt-status-row {
  margin-top: 6px; display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
}
.ov-nt-status-row select, .ov-nt-status-row input {
  background: var(--tv-surface); color: var(--tv-text);
  border: 1px solid var(--tv-border-strong); border-radius: var(--tommy-radius-md);
  padding: 5px 8px; font: inherit; font-size: 12.5px;
}
.ov-nt-empty { color: var(--tv-muted); font-size: 13px; padding: 4px 0; }
.ov-nt-compose {
  margin: 14px 20px 18px; padding-top: 14px; border-top: 1px solid var(--tv-border);
  display: flex; flex-direction: column; gap: 9px;
}
.ov-nt-compose .seg { display: flex; gap: 6px; }
.ov-nt-compose .seg button {
  padding: 5px 12px; border-radius: var(--tommy-radius-md);
  border: 1px solid var(--tv-border-strong); background: var(--tv-surface);
  color: var(--tv-muted); font: inherit; font-size: 13px; cursor: pointer;
}
.ov-nt-compose .seg button.is-on {
  background: rgba(var(--tommy-primary-rgb), .12); color: var(--tommy-primary);
  border-color: rgba(var(--tommy-primary-rgb), .4);
}
.ov-nt-compose textarea {
  width: 100%; min-height: 60px; resize: vertical;
  background: var(--tv-surface); color: var(--tv-text);
  border: 1px solid var(--tv-border-strong); border-radius: var(--tommy-radius-md);
  padding: 8px 10px; font: inherit; font-size: 13.5px;
}

/* ══════════════════════════════════════════════════════════════════
   מסך הכניסה לסיורים
   ══════════════════════════════════════════════════════════════════ */
#svhBody { padding: 24px; max-width: 1180px; }
.svh-types-row { margin: 0 0 20px; }
.svh-types { flex-wrap: wrap; }
.svh-primary { margin: 0 0 20px; }
.svh-card { margin: 0 0 20px; }
.svh-card.is-continue { border-color: rgba(var(--tommy-primary-rgb), .35); }
.svh-card.is-muted .svh-lead { color: var(--tv-muted); }
.svh-lead { margin: 8px 0 4px; font-size: 15px; }
.svh-title { margin: 6px 0 8px; font-size: 18px; font-weight: 600; }
.svh-meta { margin: 0 0 14px; display: flex; align-items: center;
            gap: 10px; flex-wrap: wrap; }
.svh-list .tv-row { grid-template-columns: minmax(0,1fr) auto auto; }
.svh-badges { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.svh-nudge { margin: 0 0 20px; }
/* Build 9227 §FieldSurveysArchive (item 10) — the "show archived"
   toggle above a per-project survey list; same spacing rhythm as
   .svh-nudge/.svh-primary right above it. */
.svh-archive-toggle { margin: 0 0 14px; display: flex; justify-content: flex-end; }
.svh-kpis { margin: 0 0 20px; }
.svh-cross-panel { margin: 0 0 20px; }
.svh-cross-toolbar { flex-wrap: wrap; margin: 0 0 14px; }
.svh-cross-list .tv-row { grid-template-columns: minmax(0,1fr) auto auto; }
@media (max-width: 900px) {
  .svh-cross-list .tv-row { grid-template-columns: 1fr; }
}

/* Build 9226 §OwnerVisualCorrection — list(רחבה)+detail(צרה) עבור
   מרחב הסיורים חוצה-הפרויקטים, לפי composition הפניה (surveys.tsx). */
.svh-cross-grid { display: grid; grid-template-columns: minmax(0,1fr); gap: 16px; align-items: start; }
@media (min-width: 1150px) {
  .svh-cross-grid { grid-template-columns: minmax(0,1.15fr) minmax(0,1fr); }
  .svh-cross-aside { position: sticky; top: 16px; }
}
.svh-cross-main { min-width: 0; }
.svh-cross-aside { display: flex; flex-direction: column; gap: 14px; }
.svh-cross-list .tv-row.is-selectable,
.svh-cross-list .tv-row { cursor: pointer; }
.svh-cross-list .tv-row.is-selected { background: rgba(var(--tommy-primary-rgb), .08);
  box-shadow: inset 2px 0 0 0 var(--tv-primary, var(--tommy-primary)); }
.svh-cross-detail { margin: 0 0 14px; }
.svh-cross-detail .tv-panel-head { align-items: flex-start; gap: 12px; }
.svh-cross-detail-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.svh-blocker-row { display: flex; align-items: flex-start; gap: 10px; grid-template-columns: none !important; }
.svh-blocker-row .tv-badge { flex-shrink: 0; font-size: 12px; padding: 2px 6px; line-height: 1.3; margin-top: 3px; }
.svh-blockers-clear { color: var(--tv-success); }

/* Build 9226 §OwnerVisualCorrection ("SURVEY — PARTIAL") — all four
   survey wizards (municipal/chargingProject/private/retrofit) grew a
   real Lovable-style summary rail above the wizard
   (#municipalSummaryRail/#cpSummaryRail/#piSummaryRail/#reSummaryRail
   — tvSurveySummaryRail()/reSummaryRailHtml(), assets/js/app.js /
   tommy-retrofit.js) without ever removing the ORIGINAL wizard-head's
   own `.progress-box` (#cpProgressText/#cpProgressBar etc.) — so the
   same percentage was shown twice, stacked, which the reference never
   does. The old elements stay in the DOM exactly as before (every
   updater still finds and updates them — updateCpProgress() and its
   three siblings read the summary-rail data from the very same call,
   so nothing about that data flow changes) — only their old visual
   duplicate is hidden, consolidating progress display into the one
   real KPI card, like the reference. */
.wizard-head .progress-box { display: none; }

/* Build 9226 §OwnerVisualCorrection — "REPORTS & DOCUMENTS — FAIL/
   MAJOR": cross-project generated-documents library
   (#docsOpenCrossLibrary, assets/js/app.js crossLibraryHtml()),
   matching Lovable's reports.tsx list(1.25fr)+detail(1fr) grid.
   Same structural pattern as .svh-cross-grid above, reused rather
   than duplicated with different numbers for no reason. */
.docs-cross-grid { display: grid; grid-template-columns: minmax(0,1fr); gap: 16px; align-items: start; }
@media (min-width: 1150px) {
  .docs-cross-grid { grid-template-columns: minmax(0,1.25fr) minmax(0,1fr); }
  .docs-cross-aside { position: sticky; top: 16px; }
}
.docs-cross-main { min-width: 0; }
.docs-cross-aside { display: flex; flex-direction: column; gap: 14px; }
.docs-cross-toolbar { margin: 10px 0 12px; }
.docs-cross-list .tv-row { cursor: pointer; }
.docs-cross-list .tv-row.is-selected { background: rgba(var(--tommy-primary-rgb), .08);
  box-shadow: inset 2px 0 0 0 var(--tv-primary, var(--tommy-primary)); }
.docs-cross-panel .tv-panel-head { align-items: flex-start; gap: 12px; }

/* Build 9226 §OwnerVisualCorrection — Settings IA (left rail + tab
   body), matching Lovable's settings.tsx two-column layout: a
   220px-ish rail on the right (RTL) and a flexible content column. */
.settings-shell {
  display: grid;
  grid-template-columns: minmax(0,1fr) 220px;
  gap: 16px;
  align-items: start;
}
.settings-rail {
  grid-column: 2;
  background: var(--tv-card);
  border: 1px solid var(--tv-border);
  border-radius: var(--tv-radius-lg);
  box-shadow: var(--tv-shadow-panel);
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.settings-rail-btn {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 12px;
  border: 0;
  background: transparent;
  border-radius: var(--tv-radius);
  color: var(--tv-muted);
  font-size: 13.5px;
  font-weight: 500;
  text-align: right;
  cursor: pointer;
}
.settings-rail-btn:hover { background: var(--tv-surface); color: var(--tv-text); }
.settings-rail-btn.is-active { background: var(--tv-surface-2); color: var(--tv-text); }
.settings-rail-btn.is-active .settings-tab-icon { color: var(--tv-primary); }
.settings-tab-icon { width: 18px; height: 18px; flex: none; }
.settings-body {
  grid-column: 1;
  grid-row: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 16px;
}
@media (max-width: 900px) {
  .settings-shell { grid-template-columns: 1fr; }
  .settings-rail { grid-column: 1; flex-direction: row; overflow-x: auto; }
  .settings-body { grid-column: 1; }
}

@media (max-width: 900px) {
  #svhBody { padding: 16px; }
  .svh-list .tv-row { grid-template-columns: 1fr; }
}

/* ══════════════════════════════════════════════════════════════════
   קריאוּת דסקטופ — סקאלה, לא כיווץ
   הבדיקה הידנית הראתה טקסט קטן מדי בכמה אזורים. אלה תיקוני
   היררכיה, לא שינוי zoom או transform.
   ══════════════════════════════════════════════════════════════════ */
/* UI-2B — סקאלה, אחרי בדיקה במסך אמיתי.

   הייחוס צויר בחלון של 1600 CSS px (נמדד: סרגל צד 358px בתמונה של
   2400 → יחס 1.5, כלומר 240px; כותרת עליונה 83px → 56px). המסך של
   הבעלים רחב בהרבה, ובאותו גודל גופן טקסט נקרא קטן יותר ביחס למסך.
   לכן הסקאלה עלתה — אבל לא באחוז אחיד: היחסים בין הרמות נשמרו, ומה
   שקיבל את העלייה הגדולה הוא מה שנמדד בייחוס כדומיננטי — המספר
   ב-KPI והברכה בראש העמוד. */
.tv-row-title  { font-size: 15.5px; }
.tv-row-meta   { font-size: 13.5px; }
.tv-row-note   { font-size: 13.5px; }
.tv-stat-label { font-size: 14px; letter-spacing: .005em; }
/* המספר הוא הדבר שהעין תופסת ראשונה בייחוס. 26px לא נשא את המשקל.

   `text-align` תוקן ל-`right`. הכלל הבסיסי יישר שמאלה, וזו הייתה
   תקלה גלויה: נמדד בייחוס שכל שלוש השורות בכרטיס — התווית, המספר
   והמלל שמתחת — מיושרות לאותו קו בצד ההתחלה (26px מהקצה הימני).
   מספר שמיושר שמאלה בכרטיס רחב מתנתק מהתווית שלו ונראה כמו תקלה.
   `direction: ltr` נשאר, כי הוא זה ששומר על סדר הספרות. */
.tv-stat-value { font-size: 32px; margin-top: 6px; text-align: right; }
.tv-stat-sub   { font-size: 13px; margin-top: 4px; }
.tv-stat       { padding: 18px 20px; }
.tv-table      { font-size: 14px; }
.tv-table th, .tv-table td { padding: 10px 12px; }
.tv-panel-head h2, .tv-panel-head h3 { font-size: 17px; }
.tv-panel-head .tv-sub { font-size: 13.5px; }
.tv-panel-head { padding: 16px 20px; }
.tv-badge      { font-size: 12.5px; padding: 3px 11px; }
.tv-eyebrow    { font-size: 12.5px; }
.tv-note       { font-size: 13.5px; }
.tv-empty      { font-size: 15px; }
.tv-head h1,
.tv-head h2    { font-size: 30px; }
.tv-head .tv-lede { font-size: 15px; margin-top: 6px; }
.tv-head       { margin-bottom: 24px; }
.tv-crumb      { font-size: 13px; }
/* השורות: מעט יותר אוויר. בייחוס שורה נושמת, ואצלנו היא הייתה צפופה. */
.tv-row        { padding: 15px 20px; }
.tv-seg button { font-size: 13.5px; padding: 8px 14px; }
.tv-search input, .tv-select { height: 40px; font-size: 14px; }
.soft-btn      { font-size: 14px; min-height: 36px; }

/* ══════════════════════════════════════════════════════════════════
   PHASE 14 — "מוצרים ותוצרים רלוונטיים לפרויקט"

   Built entirely from primitives that already exist: `.tv-rows`,
   `.tv-row`, `.tv-badge-*`, `.soft-btn`. What is added here is layout
   and one state affordance — not a new visual language, and not one
   literal colour.

   The panel answers "what does this project need?" at a glance, so it
   sits above the deliverables card and stays compact: one line per
   output, the decision on the right, the reason underneath in muted
   text. It is a summary, not a form.
   ══════════════════════════════════════════════════════════════════ */
.appl-panel {
  background: var(--tommy-panel);
  border: 1px solid var(--tommy-border);
  border-radius: var(--tommy-radius-lg);
  padding: 16px 18px;
  margin: 0 0 16px;
}
.appl-panel > h4 { margin: 0 0 2px; }
.appl-panel > .step-help { margin: 0 0 12px; }

/* Three columns: name, state, actions. On a narrow screen they stack,
   because a three-across row of Hebrew buttons wraps into nonsense
   below roughly 720px. */
.appl-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  gap: 10px 16px;
  align-items: center;
}
.appl-state { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; }
.appl-state .tv-row-note { margin: 0; white-space: nowrap; }

/* The recommendation, when it disagrees with the decision. Muted, not
   alarming: it is information, and the decision still stands. */
.appl-rec-differs { font-style: italic; }

.appl-actions { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
.appl-pick { white-space: nowrap; }

/* The chosen state. A pressed button, not a new component — the same
   accent the rest of Tommy uses for "this is the active choice". */
.appl-pick.is-on {
  border-color: rgba(var(--tommy-primary-rgb), .45);
  background: rgba(var(--tommy-primary-rgb), .14);
  color: var(--tommy-primary);
  font-weight: 600;
}

/* Drift: a survey change that disagrees with a standing decision.
   `.tv-note-warn` already carries the warning treatment. */
.appl-drift { margin: 0 0 12px; }
.appl-drift-row + .appl-drift-row {
  margin-top: 10px; padding-top: 10px;
  border-top: 1px solid var(--tommy-border);
}
.appl-drift-row p { margin: 0 0 2px; }
.appl-drift-row .add-line { margin-top: 6px; }

/* Modules whose engineering rule is not defined yet. Stated plainly
   rather than dressed up as a recommendation. */
.appl-pending { margin-top: 10px; }

@media (max-width: 720px) {
  .appl-row { grid-template-columns: 1fr; }
  .appl-state { align-items: flex-start; }
  .appl-actions { justify-content: flex-start; }
}

/* ══════════════════════════════════════════════════════════════════
   סקירת פרויקט — ראש המסך  (Build 9.204)
   ==================================================================
   שני רכיבים: שורת כרטיסי הסיכום וסרגל השלבים. שניהם החליפו את
   `pill-note` + `topSummary`, שהיו רשת של תשעה זוגות label/value
   באותו משקל ויזואלי — כלומר טבלה שאי אפשר לסרוק.

   הכרטיס בנוי על אותם טוקנים של `.tv-panel`, ולא על משטח חדש: אם
   רדיוס או גבול הפאנל ישתנו במקום אחד, הכרטיסים ילכו אחריהם.
   ══════════════════════════════════════════════════════════════════ */
.ov-sum {
  display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
}
.ov-card {
  background: var(--tv-card);
  border: 1px solid var(--tv-border);
  border-radius: var(--tv-radius-lg);
  box-shadow: var(--tv-shadow-panel);
  padding: 13px 16px 14px;
  min-width: 0;
}
/* הכרטיס משנה גוון רק כשיש מה לומר. חומרה קריטית צובעת את הגבול
   ואת הפס העליון; אזהרה רגילה מסתפקת בפס. כרטיס ניטרלי נשאר
   ניטרלי — אחרת ששת הכרטיסים צועקים במקביל ואף אחד לא נשמע. */
.ov-card.is-warn  { border-color: rgba(var(--tommy-warning-rgb), .45); }
.ov-card.is-danger{ border-color: rgba(var(--tommy-danger-rgb), .5); }
.ov-card-label {
  font-size: 12.5px; color: var(--tv-muted); font-weight: 500;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ov-card-value {
  margin-top: 6px; font-size: 17px; font-weight: 600; color: var(--tv-text);
  line-height: 1.35; min-width: 0;
}
/* הערך יורד לגודל טקסט רגיל כשהוא משפט ולא מספר. `approvalLine`
   מחזיר "מאושר במלואו · 4" — 17px על משפט כזה נראה ככותרת. */
.ov-card-value b { font-weight: 600; }
.ov-card-value .muted { font-size: 14px; font-weight: 500; }
.ov-card-value .num { font-family: var(--tommy-font-mono, ui-monospace, monospace); }
.ov-card-sep { color: var(--tv-muted); font-weight: 400; }
.ov-card-stage { font-size: 15px; font-weight: 500; }
/* 9.211 — 11.5px היה מתחת לרצפת ה-12px שהמערכת אוכפת. זו שורת משנה
   שקוראים בה נתוני פרויקט אמיתיים, ולא סימן טיפוגרפי או טקסט בתוך
   viewBox — כלומר אין לה עילה לפטור, ולכן היא עלתה לרצפה עצמה. */
.ov-card-sub {
  margin-top: 3px; font-size: 12px; color: var(--tv-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ov-card.is-danger .ov-card-sub b { color: var(--tommy-danger); }
/* בורר הסטטוס יושב בתוך כרטיס, ולכן הוא חייב להתנהג ככרטיס: רוחב
   מלא, בלי לגלוש, ובגובה שלא דוחף את הכרטיס מעל שאר השורה. */
.ov-card-value select {
  width: 100%; max-width: 100%;
  background: var(--tv-surface); color: var(--tv-text);
  border: 1px solid var(--tv-border-strong);
  border-radius: var(--tommy-radius-md);
  padding: 6px 9px; font: inherit; font-size: 14px; font-weight: 500;
  outline: none;
}
.ov-card-value select:focus {
  border-color: var(--tv-primary);
  box-shadow: 0 0 0 3px rgba(var(--tommy-primary-rgb), .18);
}

/* ─────────── זרימת הפרויקט ─────────── */
.ov-flow { margin-top: 16px; }
/* ── הצעד הבא   (Build 9.211) ─────────────────────────────────────
   שורת הפעולות שנשמטה כשהראש הישן הוחלף. הכפתורים גולשים לשורה
   שנייה במסך צר — בניגוד לסרגל השלבים, שגלישה שלו הורסת את הרצף,
   כאן כל כפתור עומד בפני עצמו ואין משמעות לסדר שנשבר. */
.ov-next { margin-top: 16px; }
.ov-next-acts {
  display: flex; flex-wrap: wrap; gap: 8px;
  padding: 14px 20px;
}
.ov-next-acts .soft-btn { margin: 0; }
/* גלילה אופקית ולא גלישה לשורה שנייה: סרגל שלבים שנשבר באמצע
   מאבד את המשמעות של "רצף". במסך צר גוללים אותו. */
.ov-steps {
  display: flex; align-items: center; gap: 4px;
  padding: 14px 20px 15px;
  overflow-x: auto; overscroll-behavior-x: contain;
}
.ov-step {
  display: inline-flex; align-items: center; gap: 7px;
  white-space: nowrap; flex: 0 0 auto;
  border: 1px solid var(--tv-border);
  background: var(--tommy-secondary-surface);
  color: var(--tv-muted);
  border-radius: var(--tommy-radius-md);
  padding: 7px 13px; font-size: 13px;
}
.ov-step.is-done {
  border-color: rgba(var(--tommy-primary-rgb), .25);
  background: rgba(var(--tommy-primary-rgb), .08);
  color: var(--tv-primary);
}
.ov-step.is-current {
  border-color: var(--tv-primary);
  background: rgba(var(--tommy-primary-rgb), .15);
  color: var(--tv-primary);
  font-weight: 600;
}
/* הוי של שלב שהושלם מצויר ב-CSS ולא כ-SVG: הוא חוזר על עצמו בכל
   שלב, ו-SVG מוטבע היה משכפל את אותה גאומטריה שש פעמים בכל רינדור. */
.ov-step-tick {
  width: 12px; height: 7px; flex: 0 0 auto;
  border: 1.6px solid currentColor; border-top: 0; border-inline-end: 0;
  transform: rotate(-45deg) translateY(-2px);
}
/* המפריד הוא חץ לכיוון הקריאה — בעברית שמאלה. */
.ov-step-sep {
  width: 7px; height: 7px; flex: 0 0 auto; margin: 0 3px;
  border: 1.4px solid var(--tv-muted); border-top: 0; border-inline-start: 0;
  transform: rotate(45deg);
  opacity: .7;
}
@media (max-width: 720px) {
  .ov-sum { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; }
  .ov-card { padding: 11px 13px 12px; }
  .ov-card-value { font-size: 16px; }
}

/* ══════════════════════════════════════════════════════════════════
   Build 9226 §OwnerCompositionCorrection (2026-09-13) — screen 03
   "too vertically stacked / sparse".
   ==================================================================
   Two real, already-computed panels (sitesHtml/attentionHtml, app.js)
   moved from the generic dashed `.sub-card` (used for every collapsed
   accordion further down this same page) onto `.tv-panel`, the same
   solid card language as the KPI row and stage-flow panel above them
   — they are the dominant 2:1 region in the Lovable reference, not a
   secondary "more info" strip.

   `.ov-sev-card` replaces the CRITICAL/IMPORTANT/INFO row's old
   `.field` markup (a plain form label/value pair — inert form chrome,
   not a stat). Same real counts, no new data; a compact colored tile
   using the same tone tokens as `.ov-card.is-warn`/`.is-danger` above.
   ══════════════════════════════════════════════════════════════════ */
.ov-sites-panel, .ov-attn-panel { min-width: 0; }
.ov-sites-panel .mini-table, .ov-attn-panel .mini-table { margin-top: 4px; }
.ov-sev-row {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px; margin-bottom: 12px;
}
.ov-sev-card {
  background: var(--tv-surface); border: 1px solid var(--tv-border);
  border-radius: var(--tommy-radius-md);
  padding: 10px 8px; text-align: center; min-width: 0;
}
.ov-sev-card-value {
  font-size: 20px; font-weight: 700; color: var(--tv-text);
  font-family: var(--tommy-font-mono, ui-monospace, monospace);
}
.ov-sev-card-label {
  margin-top: 2px; font-size: 12px; color: var(--tv-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ov-sev-card.is-danger { border-color: rgba(var(--tommy-danger-rgb), .5); }
.ov-sev-card.is-danger .ov-sev-card-value { color: var(--tv-danger); }
.ov-sev-card.is-warn { border-color: rgba(var(--tommy-warning-rgb), .45); }
.ov-sev-card.is-warn .ov-sev-card-value { color: var(--tv-warning); }
.ov-sev-card.is-info { border-color: rgba(var(--tommy-info-rgb), .4); }
.ov-sev-card.is-info .ov-sev-card-value { color: var(--tv-info); }

/* Build 9226 §OwnerCompositionCorrection (2026-09-13) — screen 05
   (project documents). "the preview/detail must visually dominate as
   in Lovable." `.fold-detail-preview` frames the real preview host
   (app.js/tommy-folder.js builds it from the document's own real
   html(), the exact markup its "PDF" button already prints) with the
   same solid surface as the rest of the detail pane, and a fixed
   height tall enough to read as a genuine preview rather than a
   sliver — the document scrolls inside it rather than being cropped
   or fabricated at a different size.

   Build 9226 §PreviewFrameFix (2026-09-14) — `.fold-preview-frame` is
   the Shadow DOM host div now, not an iframe (see tommy-folder.js for
   why: an iframe/srcdoc opens a new document, and this suite's
   page.addInitScript re-seeds tommyProjectsRegistry into *every* new
   document including one inside the page itself, wiping projects
   added at runtime). The `zoom` shrink that turns the full print page
   into a whole-page thumbnail now happens *inside* the shadow root
   (on `.fold-preview-scale`, same 900×1270 @ .33/.23 as before) - this
   host div is sized to the already-scaled result so the surrounding
   layout is unchanged. */
.fold-detail-preview {
  border: 1px solid var(--tv-border); border-radius: var(--tommy-radius-md);
  overflow: hidden; background: #fff; flex: 0 0 auto;
  height: 420px; display: flex; align-items: flex-start; justify-content: center;
}
.fold-preview-frame {
  display: block; width: 297px; height: 419px; border: 0; flex: none;
}
@media (max-width: 720px) {
  .fold-detail-preview { height: 300px; }
  .fold-preview-frame { width: 207px; height: 292px; }
}

/* ══════════════════════════════════════════════════════════════════
   מצב כספי ורווחיות  (Build 9225 §25/§26/§27/§28)
   ==================================================================
   הכרטיס בנוי על אותו `.tv-panel`/`.ov-card`/`.seg` שכל שאר כרטיסי
   הסקירה בנויים עליהם - אין כאן משטח עיצובי חדש, בדיוק כמו הכרטיסים
   שמעל. סרגלי הבחירה (נטו/ברוטו, עמודות/עוגה) חוזרים על אותה מתכונת
   שכבר קיימת לסרגל סוג הרשומה בהערות ומשימות. */
.ov-profitability { margin-top: 16px; }
.ov-fin-body { padding: 16px 20px; display: flex; flex-direction: column; gap: 16px; }
/* `.ov-fin-kpis`/`.ov-fin-card` are a deliberate, near-identical copy of
   `.ov-sum`/`.ov-card` above (the same grid, the same dark-card recipe) -
   not a reuse of that literal selector. `tests/project-overview-lovable.test.js`
   asserts there are exactly six `.ov-card` elements on Project Overview
   (the top summary row); giving these four KPI tiles their own class keeps
   that count true while still matching the approved card design exactly. */
.ov-fin-kpis {
  display: grid; gap: 12px; margin: 0;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.ov-fin-card {
  background: var(--tv-card); border: 1px solid var(--tv-border);
  border-radius: var(--tv-radius-lg); box-shadow: var(--tv-shadow-panel);
  padding: 13px 16px 14px; min-width: 0;
}
.ov-fin-card.is-danger { border-color: rgba(var(--tommy-danger-rgb), .5); }
.ov-fin-card-label {
  font-size: 12.5px; color: var(--tv-muted); font-weight: 500;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ov-fin-card-value {
  margin-top: 6px; font-size: 17px; font-weight: 600; color: var(--tv-text);
  line-height: 1.35; min-width: 0;
}
.ov-fin-card-value .num { font-family: var(--tommy-font-mono, ui-monospace, monospace); }
.ov-fin-card-value .muted { font-size: 14px; font-weight: 500; }
.ov-fin-card.is-danger .ov-fin-card-value { color: var(--tv-danger); }
.ov-fin-warn { margin: 0; }
.ov-fin-toggle { display: flex; gap: 6px; grid-template-columns: none; }
.ov-fin-toggle button {
  padding: 5px 12px; border-radius: var(--tommy-radius-md);
  border: 1px solid var(--tv-border-strong); background: var(--tv-surface);
  color: var(--tv-muted); font: inherit; font-size: 13px; cursor: pointer;
}
.ov-fin-toggle button.is-on {
  background: rgba(var(--tommy-primary-rgb), .12); color: var(--tommy-primary);
  border-color: rgba(var(--tommy-primary-rgb), .4);
}
.ov-fin-chart-area { display: flex; flex-direction: column; gap: 10px; }
.ov-fin-chart-head { display: flex; justify-content: flex-end; }
.ov-fin-chart-note { margin: 0; font-size: 13px; }
.ov-fin-chart-svg { width: 100%; max-width: 360px; height: auto; }
.ov-fin-bar.is-income { fill: var(--tv-primary); }
.ov-fin-bar.is-expense { fill: var(--tv-danger); }
.ov-fin-bar.is-profit { fill: var(--tv-success); }
.ov-fin-chart-val { font-size: 12px; fill: var(--tv-text); font-weight: 600; }
.ov-fin-chart-lbl { font-size: 12px; fill: var(--tv-muted); }
.ov-fin-donut-wrap {
  display: flex; align-items: center; gap: 20px; flex-wrap: wrap;
}
.ov-fin-donut-total { display: flex; flex-direction: column; gap: 2px; }
.ov-fin-donut-total-label { font-size: 12.5px; color: var(--tv-muted); }
.ov-fin-donut-total-val { font-size: 19px; font-weight: 700; color: var(--tv-text); }
.ov-fin-donut { width: 136px; height: 136px; flex: 0 0 auto; }
.ov-fin-donut-track { fill: none; stroke: var(--tv-border); stroke-width: 18; }
.ov-fin-donut-seg { fill: none; stroke-width: 18; }
.ov-fin-donut-seg.is-expense { stroke: var(--tv-danger); }
.ov-fin-donut-seg.is-profit { stroke: var(--tv-success); }
.ov-fin-donut-pct { font-size: 20px; font-weight: 700; fill: var(--tv-text); }
.ov-fin-donut-pct-lbl { font-size: 12px; fill: var(--tv-muted); }
.ov-fin-donut-legend { display: flex; flex-direction: column; gap: 6px; }
.ov-fin-legend-item { display: flex; align-items: center; gap: 7px; font-size: 13.5px; color: var(--tv-text); }
.ov-fin-dot { width: 10px; height: 10px; border-radius: 999px; display: inline-block; flex: 0 0 auto; }
.ov-fin-dot.is-expense { background: var(--tv-danger); }
.ov-fin-dot.is-profit { background: var(--tv-success); }
.fin-ledger-title { margin: 14px 0 4px; color: var(--tommy-primary); }
.fin-ledger-table { margin-bottom: 10px; }
@media (max-width: 720px) {
  .ov-fin-donut-wrap { gap: 14px; }
}

/* ══════════════════════════════════════════════════════════════════
   עורך הסיור — שפת הייחוס  (Build 9.204)
   ==================================================================
   למה זה שכבת CSS ולא כתיבה מחדש של ה-DOM
   ---------------------------------------
   שלושת עורכי הסיור — מוניציפלי, פרויקט טעינה, עמדה בודדת — הם
   HTML סטטי בתוך index.html: 384 פקדים ב-41 צעדים, כ-105KB.
   נראוּת מותנית קשורה אליהם בארבעה מנגנונים שונים:

     data-pif              129 שדות
     data-pi-show-if        67 תנאים
     data-cp-show-if / data-fact-show-if
     data-toggle + data-toggle-value
     ושמונה קריאות toggle('#…') מקודדות קשיח ב-app.js:
       wifiSsidWrap, wifiPasswordWrap, parkingPaintQtyWrap,
       signQtyWrap, signTypeWrap, municipalLogoRequiredWrap,
       acSameInstallConfigWrap, dcSameInstallConfigWrap

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

   הסימן המזל: מבנה השדה כבר אחיד לחלוטין —
     <label class="field"><span>תווית</span><control></label>
   ולכן כל מה ש-§4 מבקש — כותרת, ניווט סעיפים, התקדמות, כרטיסים
   קומפקטיים, היררכיה, מצבי חובה, פקדים עקביים, פקדי צילום,
   פעולות נגישות, מרווח RTL — מושג דרך הסגנון, בלי לגעת באף
   מזהה שדה ובאף קשר תנאי.

   `.hidden` ב-app.css נושא `!important`, ולכן שום כלל כאן לא יכול
   לחשוף שדה מוסתר. הבדיקה `survey-chrome` אוכפת את זה.
   ══════════════════════════════════════════════════════════════════ */

/* ─────────── פריסה ─────────── */
.survey-layout { grid-template-columns: 264px minmax(0, 1fr); gap: 20px; }

/* ─────────── ניווט הסעיפים ─────────── */
.section-nav {
  background: var(--tv-card); border: 1px solid var(--tv-border);
  border-radius: var(--tv-radius-lg); box-shadow: var(--tv-shadow-panel);
  padding: 12px; top: 16px;
}
.section-nav h3 {
  margin: 2px 4px 10px; font-size: 13px; font-weight: 600;
  color: var(--tv-muted); letter-spacing: .01em;
}
.section-link {
  border: 1px solid transparent; background: transparent;
  border-radius: var(--tommy-radius-md); padding: 8px 10px; margin-bottom: 2px;
  gap: 9px; font-size: 13.5px; color: var(--tv-muted); font-weight: 500;
  transition: background .15s, color .15s;
}
.section-link:hover { background: var(--tv-surface); color: var(--tv-text); }
/* 9.211 — עיגול מספר הסעיף היה 11.5px, מתחת לרצפת ה-12px. זהו מספר
   שמשמש לניווט בסקר, לא קישוט, ולכן הוא כפוף לרצפה. העיגול גדל
   ל-24px כדי שספרה בת שתי ספרות תמשיך לשבת במרכזו בנוחות. */
.section-link span {
  width: 24px; height: 24px; font-size: 12px; font-weight: 600;
  background: var(--tommy-secondary-surface); color: var(--tv-muted);
}
.section-link b { font-size: 13.5px; font-weight: 500; }
/* הסעיף הפעיל מסומן ברקע ובצבע, לא בפס פנימי של 4px שנקרא כשגיאה. */
.section-link.active {
  background: var(--tommy-sidebar-accent); border-color: transparent;
  color: var(--tv-text); box-shadow: none;
}
.section-link.active b { font-weight: 600; }
.section-link.active span { background: rgba(var(--tommy-primary-rgb), .16); color: var(--tv-primary); }
.section-link.done span    { background: rgba(var(--tommy-success-rgb), .16); color: var(--tv-success); }
.section-link.partial span { background: rgba(var(--tommy-warning-rgb), .16); color: var(--tv-warning); }

/* ─────────── מעטפת האשף ─────────── */
/* 9.207 — `overflow` שהשבית את ההידבקות

   app.css מגדיר `.wizard{overflow:hidden}` כדי לגזום את הפינות
   המעוגלות. `overflow:hidden` הופך את האלמנט ל-scroll container,
   ולכן כל צאצא עם `position:sticky` נדבק ביחס אליו — ולא ביחס
   ל-`.main`, שהוא הגולל האמיתי (`.app-shell{overflow:hidden}` +
   `.main{overflow:auto}`). מכיוון ש-`.wizard` עצמו אינו נגלל,
   ההידבקות פשוט לא מופעלת אף פעם.

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

   `overflow:clip` גוזם בדיוק כמו `hidden` אבל **אינו** יוצר
   scroll container, ולכן ה-sticky חוזר להיקבע מול `.main`. אם
   דפדפן כלשהו לא יכיר את הערך, ההצהרה תיפסל והתנהגות היום
   תישמר — נסיגה בטוחה. */
.wizard { overflow: clip; }
.wizard {
  border: 1px solid var(--tv-border); border-radius: var(--tv-radius-lg);
  box-shadow: var(--tv-shadow-panel); background: var(--tv-card);
}
/* הכותרת נדבקת: בסיור בן 12 צעדים ההתקדמות ושמירה היו נגללות
   מהמסך אחרי הסעיף הראשון, ואז אין דרך לדעת איפה אתה בלי לגלול
   חזרה למעלה. */
.wizard-head {
  position: sticky; top: 0; z-index: 5;
  background: var(--tv-card); color: var(--tv-text);
  padding: 15px 20px 13px;
  border-bottom: 1px solid var(--tv-border);
}
.wizard-head h1 { font-size: 18px; font-weight: 600; margin: 0; letter-spacing: -.01em; }
.wizard-head p  { margin: 4px 0 0; font-size: 13px; color: var(--tv-muted); line-height: 1.5; max-width: 68ch; }
.head-row { gap: 18px; }
.progress-box { min-width: 156px; }
.progress-box span { font-weight: 600; font-size: 13px; color: var(--tv-primary); }
.progress { height: 6px; background: rgba(255, 255, 255, .07); margin-top: 6px; }
.progress i { box-shadow: none; }

/* ─────────── סרגל הכלים ─────────── */
.toolbar { gap: 7px; margin-top: 13px; }
.toolbar button {
  border: 1px solid var(--tv-border-strong); background: var(--tv-surface);
  color: var(--tv-text); border-radius: var(--tommy-radius-md);
  padding: 7px 11px; font-weight: 500; font-size: 13px; gap: 7px;
}
.toolbar button:hover { border-color: var(--tv-primary); background: var(--tommy-secondary-surface); }
.toolbar .green-action {
  background: var(--tv-primary); border-color: var(--tv-primary);
  color: var(--tommy-primary-contrast); font-weight: 600;
}
.toolbar .green-action:hover { background: var(--tv-primary); filter: brightness(1.06); }
.toolbar .ico-small svg { width: 15px; height: 15px; }

/* ─────────── גוף הצעד ─────────── */
.wizard-body { padding: 20px; }
.step-count {
  background: rgba(var(--tommy-primary-rgb), .10); color: var(--tv-primary);
  border: 1px solid rgba(var(--tommy-primary-rgb), .3);
  padding: 4px 10px; font-weight: 600; font-size: 12px; margin-bottom: 10px;
}
.step h2 { font-size: 17px; font-weight: 600; margin: 0 0 6px; letter-spacing: -.01em; }
.step h3 {
  font-size: 14px; font-weight: 600; margin: 22px 0 10px; color: var(--tv-text);
  padding-bottom: 7px; border-bottom: 1px solid var(--tv-border);
}
.step-help { margin: 0 0 16px; font-size: 13px; color: var(--tv-muted); line-height: 1.55; }

/* ─────────── שדות ─────────── */
/* אין כאן `display` בכוונה. `.field` ו-`.field.hidden` נושאות אותה
   ספציפיות, והגיליון הזה נטען אחרי app.css — כלל display כאן היה
   מנצח ומציג כל שדה מוסתר. `!important` על `.hidden` מגן, אבל אין
   סיבה להישען על זה. */
.field-grid { gap: 13px 14px; }
.field { gap: 6px; }
.field > span {
  font-size: 12.5px; font-weight: 500; color: var(--tv-muted);
  letter-spacing: .005em;
}
.field input, .field select, .field textarea {
  border: 1px solid var(--tv-border-strong); border-radius: var(--tommy-radius-md);
  padding: 8px 11px; font-size: 14px; background: var(--tv-surface); color: var(--tv-text);
  transition: border-color .15s, box-shadow .15s;
}
.field input:hover, .field select:hover, .field textarea:hover { border-color: var(--tv-muted); }
.field input:focus, .field select:focus, .field textarea:focus {
  border-color: var(--tv-primary); box-shadow: 0 0 0 3px rgba(var(--tommy-primary-rgb), .18);
}
.field input::placeholder, .field textarea::placeholder { color: var(--tv-muted); opacity: .75; }
.field textarea { min-height: 76px; }
/* ערך מספרי/טכני נקרא כמספר. `direction:ltr` שומר על סדר הספרות
   בתוך שדה RTL — בלעדיו "22.5" נקרא הפוך בחלק מהדפדפנים. */
.field input[type="number"] { direction: ltr; text-align: right; font-variant-numeric: tabular-nums; }

/* ─────────── בלוקים בתוך צעד ─────────── */
/* בלוק תנאי מסומן בפס בצד ההתחלה: הוא נפתח כתגובה לתשובה קודמת,
   והפס הוא מה שאומר "זה נובע ממה שענית מעל". */
.conditional {
  border: 1px solid var(--tv-border); background: var(--tv-surface);
  border-radius: var(--tv-radius); padding: 14px 16px; margin-top: 14px;
  box-shadow: inset -2px 0 0 0 rgba(var(--tommy-primary-rgb), .45);
}
.check-row { gap: 10px; }
.check-row label {
  border: 1px solid var(--tv-border); border-radius: var(--tommy-radius-md);
  padding: 10px 12px; background: var(--tv-surface); font-weight: 500; font-size: 13.5px;
}
.breaker-box { border-color: var(--tv-border); background: var(--tv-surface); border-radius: var(--tv-radius); }
.seg button {
  border: 1px solid var(--tv-border); background: var(--tv-card);
  border-radius: var(--tommy-radius-md); padding: 8px; font-weight: 500; font-size: 13.5px;
}
.seg button.active {
  background: rgba(var(--tommy-primary-rgb), .14); border-color: var(--tv-primary);
  color: var(--tv-primary); font-weight: 600;
}
.breaker-result {
  background: var(--tv-card); border: 1px solid var(--tv-border-strong); border-style: solid;
  border-radius: var(--tv-radius); font-size: 19px; font-weight: 600; padding: 12px;
  font-variant-numeric: tabular-nums;
}
.circuit-card { border-color: var(--tv-border); background: var(--tv-surface); border-radius: var(--tv-radius); }
.summary-card { background: var(--tv-surface); border-color: var(--tv-border); border-radius: var(--tv-radius); padding: 13px 15px; }
.summary-card b { font-size: 12.5px; color: var(--tv-muted); font-weight: 500; }

/* ─────────── צילומים וקבצים ─────────── */
.upload-tile {
  min-height: 96px; border: 1px dashed var(--tv-border-strong);
  border-radius: var(--tv-radius); background: var(--tv-surface);
  font-weight: 500; font-size: 13px; gap: 7px; padding: 14px;
  transition: border-color .15s, background .15s;
}
.upload-tile:hover { border-color: var(--tv-primary); background: var(--tommy-secondary-surface); }
.upload-tile .ico-small svg { width: 21px; height: 21px; }
/* 9.211 — שם הקובץ שהועלה, מתחת לאריח. 11.5px היה מתחת לרצפה. */
.upload-tile small { font-size: 12px; }
.photo-grid { gap: 12px; }

/* ─────────── פעולות ─────────── */
/* דביק בתחתית: "הבא" ו"הקודם" הם הפעולה הנפוצה ביותר בסיור, והם
   היו בקצה התחתון של צעד שיכול להיות ארוך ממסך שלם. */
.wizard-actions {
  position: sticky; bottom: 0; z-index: 4;
  padding: 12px 20px; border-top: 1px solid var(--tv-border);
  background: var(--tv-card);
}
.btn { border-radius: var(--tommy-radius-md); padding: 9px 18px; font-weight: 600; font-size: 14px; }
.btn.primary { background: var(--tv-primary); color: var(--tommy-primary-contrast); }
.btn.secondary { background: var(--tv-surface); color: var(--tv-text); border: 1px solid var(--tv-border-strong); }

/* ─────────── הערות ואזהרות ─────────── */
.pill-note {
  background: rgba(var(--tommy-warning-rgb), .09);
  border: 1px solid rgba(var(--tommy-warning-rgb), .35);
  color: var(--tv-text); border-radius: var(--tv-radius);
  padding: 12px 14px; margin: 14px 0; font-size: 13px; line-height: 1.55;
}
.review-box {
  background: rgba(var(--tommy-warning-rgb), .09);
  border-color: rgba(var(--tommy-warning-rgb), .35);
  border-radius: var(--tv-radius); font-size: 13px;
}
.review-box h3 { font-size: 14px; font-weight: 600; margin: 0 0 7px; border: 0; padding: 0; }
.equip-row { border-color: var(--tv-border); border-radius: var(--tv-radius); font-size: 13.5px; }
.equip-row div { border-inline-start-color: var(--tv-border); }
.equip-row.header { background: var(--tv-surface); font-weight: 600; }
.complex-panel { border-color: var(--tv-border); background: var(--tv-surface); border-radius: var(--tv-radius); }
/* ── `.box` = פאנל   (Build 9.212) ────────────────────────────────
   `.box` הוא עטיפת הפאנל הוותיקה, ועדיין נושאת 13 מקטעים בכתב
   הכמויות ועוד ברטרופיט ובתוכניות החשמל. היא כבר משתמשת באותם
   טוקנים, אבל דרך האליאסים הישנים — ובעיקר דרך `--shadow`, שהוא
   צל נפילה כבד (18px/40px) שנועד לכרטיסי הדשבורד הגדולים ולא
   לפאנל תוכן. לצד `.tv-panel` זה נראה כמו שתי שכבות שונות של
   ממשק על אותו מסך.

   מיושר לפאנל. אותה משפחה, לא מחלקה חדשה. */
.box {
  background: var(--tv-card);
  border: 1px solid var(--tv-border);
  border-radius: var(--tv-radius-lg);
  box-shadow: var(--tv-shadow-panel);
  padding: 20px;
}

/* ── משפחת רשימת הציוד   (Build 9.212) ────────────────────────────
   מדידת המטריצה (`qa/ui-matrix.js`) סיווגה את מסך רשימת הציוד
   כ-CUSTOM: הוא בנוי כולו ממחלקות `eql-*` משלו ואינו חולק את
   מערכת הפרימיטיבים. המשמעות המעשית היא שכל שיפור במערכת —
   הפרדת ראש הפאנל, שפת הרשימה, יישור ספרות — פשוט לא מגיע אליו.

   `.eql-table` היא בדיוק הדפוס שממנו `.mini-table` נגמלה ב-9.209:
   רשת תאים מלאה, כותרות במשקל 800, 15px. לצד כתב הכמויות והקטלוג
   זה נקרא כמו מסך מדור אחר.

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

   מה שנשמר במכוון: הרקע הכתום של שורה שלא אותרה בקטלוג. זו אינה
   קישוט אלא **מצב מסחרי** — פריט בלי מחיר — והוא צריך להישאר
   בולט. הוא רק עבר לטוקן האזהרה. */
.eql-table-wrap {
  background: var(--tv-card);
  border: 1px solid var(--tv-border);
  border-radius: var(--tv-radius-lg);
  box-shadow: var(--tv-shadow-panel);
}
.eql-table { font-size: 13.5px; }
.eql-table th {
  background: transparent;
  color: var(--tv-muted);
  font-weight: 500;
  font-size: 12px;
  letter-spacing: .01em;
  padding: 10px;
  border-bottom: 1px solid var(--tv-border);
}
.eql-table td {
  padding: 9px 10px;
  border-bottom: 1px solid var(--tv-border);
  color: var(--tv-text);
}
.eql-table tbody tr:hover td { background: var(--tv-surface); }
.eql-table .eql-mono, .eql-table b { font-variant-numeric: tabular-nums; }
/* פריט בלי מחיר — מצב, לא עיטור. */
.eql-table tr.eql-unmatched { background: rgba(var(--tommy-warning-rgb), .08); }
.eql-item {
  background: var(--tv-card);
  border: 1px solid var(--tv-border);
  border-radius: var(--tv-radius-lg);
  box-shadow: var(--tv-shadow-panel);
}
.eql-item:hover { border-color: var(--tv-border-strong); }
.eql-add {
  background: var(--tv-card);
  border: 1px solid var(--tv-border);
  border-radius: var(--tv-radius-lg);
  box-shadow: var(--tv-shadow-panel);
  padding: 16px 20px;
}

/* ══════════════════════════════════════════════════════════════════
   ארכיטקטורת הגלילה — מודל נעול   (Build 9.217)
   ══════════════════════════════════════════════════════════════════
   חוסם RC: הבעלים לא הצליח לגלול בתוך מודולים. הסיבה אינה כלל אחד
   אלא **שלושה אזורי גלילה שמתחרים** על אותו גלגל עכבר.

   ── מה נמצא בפועל ──
   שרשרת ה-DOM היא:

       html / body          ← בלי גובה ובלי overflow: המסמך גולל
       .app-shell           ← height:100vh; overflow:hidden  (רק ≥901px)
         header.topbar      ← shrink-0
         .app-row           ← flex, min-h-0
           aside.sidebar    ← overflow-hidden
             nav            ← overflow-y-auto **overscroll-contain**
           main.main        ← overflow:auto

   שלושה ממצאים, לפי סדר החומרה:

   **1. `overscroll-contain` על ניווט הסרגל.** זה האשם המרכזי בתחושה
      ש"הגלגל נתפס באזור אחר". `overscroll-behavior: contain` מבטל
      **שרשור גלילה**: כשהמצביע מעל הסרגל והניווט הגיע לקצהו, האירוע
      אינו עובר הלאה ל-main או לעמוד — הוא פשוט נבלע. הסרגל הוא
      240px של שטח מטרה, וגלגול מעליו לא עושה כלום.

   **2. חמישה מודולים אינם גלויים ברציפות.** `costcatalog`,
      `equiplist`, `projectfolder`, `surveys`, `rules` הם
      `.nav-item.nav-sub` עם `max-height:0; overflow:hidden`,
      ונפתחים רק ב-`.sidebar:hover` או כשהם פעילים. זה בדיוק
      "כיווץ כדי להתאים" שהבעלים אסר.

   **3. המסמך גולל במקביל ל-main.** ל-`html`/`body` אין גובה ואין
      overflow. מעל 901px `.app-shell` מהדק ל-`100vh; overflow:hidden`,
      ולכן מה שגולש **נחתך במקום להיגלל** — וזה מסביר "לא ניתן
      להגיע לתחתית המודול". מתחת ל-901px אין הידוק בכלל, והמסמך
      גולל בנוסף ל-main: שני פסי גלילה.

   ── המודל הנעול ──
   כותרת קבועה · סרגל קבוע בלי גלילה פנימית · **main הוא אזור
   הגלילה האנכי היחיד** · המסמך אינו גולל, לא אנכית ולא אופקית.

   ── למה זה לא טלאי עיוור ──
   `overflow-x:hidden` כאן אינו הסתרה של תסמין: `main` הוא המכולה
   היחידה שמחזיקה תוכן רחב, והמסמך מהודק כי הוא **אמור** להיות
   מהודק במעטפת אפליקציה. רכיבים שבאמת צריכים גלילה משלהם — טבלה
   רחבה, רשימת הפנקס, דיאלוג — שומרים עליה, והן נשארות מפורטות
   למטה.

   ── מה שנשאר בכוונה אזור גלילה משלו ──
   `.tnb-list` (רשימת הפנקס במגירה) · `.boq-catalog-modal` (דיאלוג
   עם max-height) · `.eql-table-wrap` ו-`.ov-steps` (גלילה **אופקית**
   בלבד, לטבלה רחבה ולסרגל שלבים שאסור לו להישבר) · `.tv-docs-list`.
   כל אחת מהן היא רכיב תחום שגלילתו היא חלק מהתפקוד שלו, ולא מכולה
   שמתחרה על העמוד.

   ── סדר גיליונות ──
   `tommy-lovable-utils.css` נטען **אחרי** הקובץ הזה, ולכן
   `.overflow-y-auto` שעל אלמנט ה-nav מנצח סלקטור בעל אותה
   ספציפיות. `.sidebar nav` הוא (0,1,1) מול (0,1,0) — מנצח בלי
   `!important`.
   ══════════════════════════════════════════════════════════════════ */
@media (min-width: 901px) {
  /* Build 9225 §2 — הקטע הזה (html/body, .app-shell, .app-row, .main)
     הוסר מכאן במכוון, לא רק "נדרס" ע"י כלל מאוחר יותר. הבעלים ביטל
     את המודל הזה (ראו הבלוק "Build 9225 §2" בהמשך הקובץ), וכלל CSS
     שסותר את המודל הנוכחי אבל עדיין יושב במקור — גם אם הוא בפועל
     "מנוצח" ע"י כלל מאוחר יותר עם אותה ספציפיות — הוא מקור טעייה:
     בדיקת מקור (Node, לא דפדפן) שקוראת "את בלוק ה-@media הראשון"
     הייתה מוצאת את הכללים הישנים האלה ומדווחת PASS על מודל שכבר לא
     קיים בפועל. זו בדיוק הסיבה ש-`tests/scroll-architecture.test.js`
     הועבר לארכיון (`tests/archive/`) יחד עם שני הקבצים המקבילים לו.
     המודל הנוכחי מוגדר במלואו ובאופן בלעדי בבלוק "Build 9225 §2"
     למטה — שם, ורק שם, יש עכשיו html/body/.app-shell/.app-row/.main.

     מה שכן נשאר כאן, כי הוא עדיין נכון תחת שני המודלים: ה-nav עצמו
     אינו אזור גלילה עצמאי (`.sidebar nav`), וכל תתי-המודולים גלויים
     ברציפות בלי כיווץ (`.nav-item.nav-sub`) — ראו את שני הכללים
     שנשארו מיד מתחת. */
  .sidebar nav {
    overflow: visible;
    overscroll-behavior: auto;
    flex: 0 1 auto;
    min-height: 0;
  }

  /* Build 9226 §OwnerVisualCorrection — היפוך שני של אותה החלטה.
     השורה הבאה תיעדה עד כה "כל המודולים גלויים ברציפות. הכיווץ
     בוטל" (Build 9225 §2, שביטל את הכיווץ-על-hover של Build 9.72).
     הבעלים דיווח כעת שהתוצאה ההפוכה הפכה לבעיה משלה: "הסרגל/
     תתי-המודולים למעשה תמיד פתוחים וקבועים ויזואלית... זה בדיוק
     מה שהבעלים אסר" (ציטוט מהבריף) - use the Lovable hierarchy:
     שמונת המודולים הראשיים תמיד גלויים, תתי-המודולים מכווצים
     כברירת מחדל, ופתיחת קבוצה אחת מכווצת את הקבוצה הלא-קשורה
     הקודמת. זו לא חזרה ל-Build 9.72 (שם הפתיחה הייתה על :hover של
     כל הסרגל, בלי מושג "קבוצה" - ראו assets/css/app.css, שם המנגנון
     ההוא נוטרל עם הערה מפנה לכאן): כאן הפתיחה/סגירה נשלטת ב-JS
     (tommyNavSyncGroups, app.js) לפי המסך הפעיל, דרך מחלקת
     `.nav-group.group-open` על עטיפת ה-<div> של כל קבוצה (ראו
     index.html) - לא hover, ולא תלוי-עכבר בכלל, ולכן עובד זהה
     בדסקטופ ובמגע. tests/owner-approved-locks.test.js §2 מתעד את
     ההיפוך בבדיקה שנעלה את המודל הקודם. */
  .sidebar .nav-item.nav-sub {
    max-height: 0;
    overflow: hidden;
    opacity: 0;
    padding-top: 0;
    padding-bottom: 0;
    pointer-events: none;
    transition: max-height .18s ease, opacity .15s ease, padding .18s ease;
  }
  .sidebar .nav-group.group-open .nav-item.nav-sub {
    max-height: 48px;
    overflow: visible;
    opacity: 1;
    padding-top: 7px;
    padding-bottom: 7px;
    pointer-events: auto;
  }

  /* Build 9.219 — צפיפות דסקטופ נעולה (בוטלה ב-Build 9225 §3, ראו
     למטה). ב-9.217 הסרגל כבר הפסיק לגלול, אבל ב-1760×864 חסרו 54px
     וב-1600×900 חסרו 18px, ולכן 9.219/9223 הידקו את הגיאומטריה עד
     שכל 19 הכפתורים נכנסו בלי גלילה. */
  .sidebar nav { gap: 3px; }

  /* Build 9223 — צפיפות ויזואלית (בוטלה ב-Build 9225 §3, ראו למטה).
     9.219 כבר פתר את הגלילה, אבל הבעלים דיווח שוב, ויזואלית,
     שהאייקונים והתוויות עדיין גדולים ועמוסים מדי. */

  /* Build 9225 §3 — היפוך: "מתאים למסך אחד בלי גלילה" בוטל לגמרי
     ב-§2 (העמוד גולל כרגיל עכשיו). זו הייתה הסיבה שדחפה את 9.219
     ו-9223 להידוק אגרסיבי — ועכשיו שהיא לא קיימת, ההידוק עצמו הוא
     הבעיה: הבעלים דיווח על 9223/9224 בדיוק כפי ש-Build 9224 עצמו
     תיעד ("צפוף ולא מעוצב בכוונה"). הנעילה שהחזיקה החלטה שבוטלה
     גרועה מהיעדרה — לכן היא מעודכנת כאן ולא רק "נדרסת" ע"י בלוק
     חדש (אותו נימוק כמו ב-owner-approved-locks.test.js §2 ובבלוק
     "Build 9225 §2" למעלה בקובץ הזה).

     היעד אינו לחזור ל-33px/26px של טרום-9.219 (§3 אוסר "סרגל גדול
     מדי בחזרה"), אלא לנקודת-אמצע קריאה ומקצועית:

       אייקון:  18px → 21px   (+3px — עדיין קומפקטי, פחות "צפוף")
       ריפוד:   4px  → 7px    (לכל שורה, מלמעלה ומלמטה)
       מרווח:   8px  → 10px   (בין האייקון לתווית)
       גופן:    12.5px → 13.5px (התווית הראשית — הערך המקורי של
                                  הייחוס, מ-`text-[13.5px]` שכבר
                                  קיים ב-HTML; ה-override שהקטין
                                  אותו פשוט מוסר)
       תת-גופן: 12px → 13px   (תת-פריטים — נשאר קטן/קליל יחסית
                                 לראשי אבל לא צמוד לרצפת ה-12px)

     שום מודול לא הוסתר, לא כווץ ולא קיבל scale — רק המידות. אומת
     ברינדור אמיתי (Chromium) שאין חפיפה/חיתוך/גלישת-שתי-שורות
     ב-1760×864, 1600×900, 1600×1000 — ראו
     qa/browser/specs/responsive-quicknote-sidebar-9223.spec.js חלק
     C, שהתקרות שלו עודכנו בהתאם (ולא נמחקו — התקרה הישנה תיעדה
     "משמעותית קטן מ-26px טרום-9223"; החדשה מתעדת "משמעותית קטן
     מ-26/33px טרום-9.219", אותו כלל, יעד מספרי שונה). */
  .sidebar .nav-item {
    padding-top: 7px; padding-bottom: 7px;
    gap: 10px; font-size: 13.5px;
  }
  .sidebar .nav-item .ico svg { width: 21px; height: 21px; }
  /* תת-הגופן (13px) מוגדר במקום אחד בלבד — בכלל ה-nav-sub הקיים
     למטה בקובץ (ליד opacity:.68), לא כאן, כדי שלא יהיו שני מקורות
     אמת סותרים לאותה תכונה. */

  /* תחתית הסרגל — "כרטיס משתמש", אותו תוכן, ריפוד נינוח יותר עכשיו
     שאין תקציב-גובה נוקשה. */
  #sidebarUserBlock { padding-top: 8px; padding-bottom: 8px; }
  #sidebarUserBlock .tommy-logout-btn { margin-top: 0; }

  /* Build 9224 — קצב ומדרג ויזואלי, לא רק צפיפות.
     9223 כיווץ את הגובה עד שהוא נכנס בלי גלילה; הבעלים דיווח שוב
     שהתוצאה נראית "צפוף ולא מעוצב בכוונה" — 19 פריטים ברצף שטוח
     אחד, בלי שום סימן קבוצתי, בלי היררכיה בין ראשי למשני, ומצב
     פעיל שקורא "כבד" (מילוי שטוח מלא) על סרגל כהה וצר כל כך.
     זה לא נוגע בגובה/רוחב/גלילה של הסרגל (הבדיקות ב-
     scroll-architecture-9219.spec.js נשארות שריר וקיים) — רק בקצב
     האנכי בין קבוצות, בעוצמת מצב פעיל/משני, ובהפרדת הפוטר.
     תוספת גובה כוללת: 6×10px (מרווחי קבוצה) + ~11px (פוטר) ≈ 71px,
     בתוך ה-197px רזרבה שנמדדה בפועל ב-1760×864 — הצפיפות הגיאומטרית
     של 9.219/9223 לא נפגעת. */

  /* מרווח לפני תחילת כל קבוצה לוגית — data-main קיים כבר בכל
     כפתור; אין כאן markup חדש, שינוי HTML או מיון מחדש.
     Build 9226 §C — שמונה המודולים הראשיים (AppShell.tsx), ולכן שבע
     נקודות התחלה (הראשון, dashboard, לא צריך מרווח מעליו — הוא כבר
     בתחילת הניווט): פרויקטים (projects), סיורי שטח (surveyHub),
     תכנון והנדסה (evpanel), כתב כמויות (boq), ציוד (equiplist),
     דוחות ומסמכים (reports), הגדרות (settings). */
  /* Build 9225 §3 — 10px → 14px: קצת יותר אוויר בין קבוצות עכשיו
     שאין תקציב-גובה נוקשה (אותו נימוק כמו שאר §3 בקובץ הזה). */
  .sidebar .nav-item[data-main="projects"],
  .sidebar .nav-item[data-main="surveyHub"],
  .sidebar .nav-item[data-main="evpanel"],
  .sidebar .nav-item[data-main="boq"],
  .sidebar .nav-item[data-main="equiplist"],
  .sidebar .nav-item[data-main="reports"],
  .sidebar .nav-item[data-main="settings"] {
    margin-top: 14px;
  }

  /* מצב פעיל מאופק: פס דק בצבע ההדגשה + מילוי קליל, במקום מילוי
     שטוח שקורא כבד על משטח כהה וצר. padding-inline-start מתקזז
     כנגד רוחב הפס כדי שאין קפיצה אופקית בין פעיל ללא-פעיל. */
  .sidebar .nav-item {
    border-inline-start: 2px solid transparent;
    padding-inline-start: calc(0.75rem - 2px);
  }
  .sidebar .nav-item.active {
    background: color-mix(in srgb, var(--tommy-sidebar-accent) 65%, transparent);
    border-inline-start-color: var(--tommy-primary);
  }

  /* היררכיה ראשי/משני ברורה: משני קטן וקליל יותר, לא אותו משקל
     חזותי כמו פריט ראשי — מקבל בחזרה ניגודיות מלאה במעבר עכבר
     ובמצב פעיל, כדי שלא ייקרא "מושבת".
     Build 9225 §3 — 12px (רצפת הטיפוגרפיה הנעולה, build-9168.test.js)
     → 13px, אותו נימוק כמו הפריט הראשי ממש למעלה בקובץ: לא צריך
     להיצמד לרצפה עכשיו שאין תקציב-גובה נוקשה. */
  .sidebar .nav-item.nav-sub {
    font-size: 13px;
    opacity: .68;
  }
  .sidebar .nav-item.nav-sub b { font-weight: 500; }
  .sidebar .nav-item.nav-sub:hover,
  .sidebar .nav-item.nav-sub.active {
    opacity: 1;
  }

  /* הפרדת הפוטר מהניווט: לא רק קו דק — ריפוד עליון גדול יותר ורקע
     כהה מעט יותר, כדי שהאזור ייקרא בלוק נפרד ולא המשך דחוס של
     הרשימה. */
  #sidebarUserBlock {
    margin-top: 6px;
    padding-top: 7px;
    background: color-mix(in srgb, var(--tommy-sidebar-accent) 35%, transparent);
  }
}

/* ══════════════════════════════════════════════════════════════════
   תתי-מודולים באקורדיון גם מתחת ל-901px   (Build 9226 §OwnerVisualCorrection)
   ══════════════════════════════════════════════════════════════════
   עד כה הבלוק הזה הרחיב את החלטת Build 9225 §2 ("כל תתי-המודולים
   גלויים ברציפות") גם לתפריט הניידת, כדי שלא יהיה פער בין הרוחבים.
   הבעלים ביטל את ההחלטה ההיא (ראו ההערה המלאה בבלוק
   "@media (min-width:901px)" למעלה בקובץ הזה) — ומאותו נימוק בדיוק
   ("מרחיבים את אותו כלל, לא ממציאים כלל שני"), ההיפוך גם הוא מורחב
   לכאן: אותו מנגנון אקורדיון מבוסס-`.group-open` (נשלט ב-JS דרך
   tommyNavSyncGroups, לא hover — ולכן זהה בין מגע לעכבר מלכתחילה,
   בלי צורך באבחנה בין הרוחבים). */
@media (max-width: 900px) {
  .sidebar .nav-item.nav-sub {
    max-height: 0;
    overflow: hidden;
    opacity: 0;
    padding-top: 0;
    padding-bottom: 0;
    pointer-events: none;
    transition: max-height .18s ease, opacity .15s ease, padding .18s ease;
  }
  .sidebar .nav-group.group-open .nav-item.nav-sub {
    max-height: 48px;
    overflow: visible;
    opacity: .85;
    padding-top: 9px;
    padding-bottom: 9px;
    pointer-events: auto;
  }
}

/* ══════════════════════════════════════════════════════════════════
   ארכיטקטורת הגלילה — מודל חדש, מחליף את הנעילה של 9.217/9.219
   (Build 9225 §2)
   ══════════════════════════════════════════════════════════════════
   הבעלים ביטל את הדרישה הקודמת ("הסרגל כולו נכנס למסך אחד") —
   התוצאה נראתה צפופה מדי. הדרישה החדשה: **גלילת עמוד רגילה אחת**.
   הסרגל וה-main זזים למעלה/מטה יחד, כחלק מאותו עמוד — בלי פס גלילה
   עצמאי לסרגל, בלי פס גלילה עצמאי ל-main, בלי גלישה אופקית.

   מה זה מחליף, לפי הבלוק שממש מעל (9.217, שורות ~1606-1620):
     html,body            { height:100%;  overflow:hidden }  → בוטל
     .app-shell            { height:100vh; overflow:hidden }  → בוטל
     .app-row               { overflow:hidden }                → בוטל
     .main                    { overflow-y:auto; overflow-x:hidden } → בוטל

   `scroll-architecture-9219.spec.js` ו-`build-9219-scroll.test.js`
   בדקו את המודל הישן (סרגל+main נעולים ב-100vh, main כאזור הגלילה
   היחיד, גלגול-מעל-הסרגל משורשר ל-main.scrollTop). זו לא רגרסיה —
   זו הוראת בעלים מפורשת שמחליפה נעילה קודמת. הבדיקות הישנות הועברו
   ל-non-gating (`tests/build-9219-scroll.test.js` ו-
   `qa/browser/specs/scroll-architecture-9219.spec.js` מתועדים כארכיון
   בהערה בראש כל קובץ); `tests/build-9225-scroll.test.js` ו-
   `qa/browser/specs/scroll-architecture-9225.spec.js` נועלים את המודל
   הנוכחי.

   מה לא זז: `.app-shell{min-height:100vh}` הבסיסי (כל הרוחבים, לא
   בבלוק הזה) נשאר — הוא מבטיח שהעמוד לפחות גובה מסך אחד גם כשיש בו
   מעט תוכן; הוא לא סותר גלילה, רק קובע רצפה.

   מה עדיין שומר גלילה פנימית משלו בכוונה (בלוק Build 9.217 מעל
   מפרט את הרשימה המלאה: `.tnb-list`, `.boq-catalog-modal`,
   `.eql-table-wrap`/`.ov-steps` (אופקי בלבד), `.tv-docs-list`) — אלה
   רכיבים תחומים, לא מכולות-עמוד, והדרישה החדשה לא נוגעת בהם. */
@media (min-width: 901px) {
  /* המסמך גולל כרגיל — זה עמוד, לא מעטפת אפליקציה עם viewport נעול.
     overflow-x:hidden מונע גלישה אופקית בלי ליצור פס גלילה אנכי שני
     (overflow-y נשאר "visible", וה-auto המחושב שנובע מכך על ציר X
     הוא בדיוק גלילת-העמוד הטבעית שרוצים). */
  html, body { height: auto; overflow-x: hidden; overflow-y: visible; }

  .app-shell { height: auto; min-height: 100vh; overflow: visible; }
  .app-row   { min-height: 0; overflow: visible; }

  /* main כבר אינו אזור גלילה עצמאי — הוא זז עם העמוד, לא בתוכו. */
  .main {
    min-height: 0;
    overflow: visible;
  }

  /* `aside.sidebar` (תג+מחלקה) גובר בספציפיות על `.h-full`
     ו-`.overflow-hidden` — שתי מחלקות Tailwind-ported בלי
     `!important` שנטענות אחרי הקובץ הזה ב-tommy-lovable-utils.css
     (אותה תבנית ספציפיות שתועדה למעלה, ב-9.217, בשביל `.sidebar
     nav`). בלי הגברה כאן, `.h-full{height:100%}` היה נועל את הסרגל
     לגובה ה-app-row (שכבר אינו 100vh קבוע) במקום לתת לו לזרום עם
     תוכנו כפריט flex רגיל. */
  aside.sidebar {
    height: auto;
    overflow: visible;
  }

  /* התאמה: 9.217 כיבתה את הגלילה העצמאית של ה-nav (`overflow:visible`,
     `overscroll-behavior:auto`) כדי לפתור בעיית שרשור-גלגל. הכלל הזה
     עדיין תקף במודל החדש בלי שינוי — nav הוא עדיין לא אזור גלילה
     עצמאי, רק שהעמוד כולו גולל אותו כחלק מהזרימה, לא main. */
}

/* ══ המסכים שנשארו עם משפחה משלהם   (Build 9.215) ═══════════════════
   מדידת המטריצה סיווגה חמישה מסכים כ-CUSTOM: הם עובדים, אבל בנויים
   ממחלקות משלהם ולכן שיפור במערכת המשותפת לא מגיע אליהם. שניים מהם —
   שני אשפי הסיור — הם משפחה נפרדת בכוונה ולא נוגעים בהם. שלושת
   האחרים הם מסכי עבודה יומיומיים, וכאן הם מצטרפים למשפחה.

   אותה שיטה כמו `.sub-card` ו-`.box` ב-9.212: מגדירים את הפרימיטיב,
   לא כותבים מחדש את הרנדרר. אלה שרשורי מחרוזות ארוכים, ו-9.84 הוא
   מקרה מתועד שבו עריכה כזו רוקנה מסך בשקט בזמן ש-`node --check` עבר.

   `--tv-shadow-panel` על משטח, `--tv-border` על קו, `--tv-radius-lg`
   על פינה. אין מחלקה חדשה ואין מערכת שלישית. */
.docs-usage, .docs-folder, .docs-proj, .docs-file {
  background: var(--tv-card);
  border: 1px solid var(--tv-border);
  border-radius: var(--tv-radius);
}
.docs-usage, .docs-proj { box-shadow: var(--tv-shadow-panel); }
.docs-folder:hover, .docs-file:hover { border-color: var(--tv-border-strong); }
.cost-staging {
  border: 1px solid var(--tv-border-strong);
  background: var(--tv-surface);
  border-radius: var(--tv-radius);
}
.cost-category {
  background: var(--tv-surface);
  border: 1px solid var(--tv-border);
  border-radius: var(--tv-radius);
}
/* התגיות של המשפחות האלה מתיישרות לתגית המערכת: אותו גובה, אותה
   פינה, אותו משקל. הצבע נשאר שלהן — הוא נושא משמעות. */
.cost-dup-badge, .docs-builtin, .rep-status {
  border-radius: var(--tv-radius);
  font-size: 12px;
  font-weight: 500;
  padding: 3px 8px;
}
/* כפתורי ה-BOQ וההגדרות היו שניהם 52px — גובה של כפתור ראשי במסך
   נחיתה, לא של פעולה בסרגל. שניהם מיושרים לגובה הפעולות של המערכת. */
.boq-btn, .settings-btn {
  height: 40px;
  padding: 0 16px;
  border-radius: var(--tv-radius);
  border: 1px solid var(--tv-border);
  background: var(--tv-card);
  color: var(--tv-text);
  font-size: 13.5px;
}
.boq-btn:hover, .settings-btn:hover { border-color: var(--tv-border-strong); }

/* ══ מסך ההגדרות   (Build 9.216) ═══════════════════════════════════
   ההגדרות היו המסך היחיד שנשאר בלי אף משטח משותף: לא פאנל, לא
   כרטיס — רק כפתורים בגובה 52px על רקע העמוד. הוא נקרא כמו מסך
   מדור אחר, וזו הסיבה היחידה שהוא סווג CUSTOM.

   `.complex-panel` הוא המשטח שכבר קיים במסך הזה (שלוש קבוצות
   פעולות יושבות בו), והוא מקבל את צורת הפאנל — אותה החלטה כמו
   `.sub-card` ו-`.box` ב-9.212. אין מחלקה חדשה. */
.complex-panel {
  background: var(--tv-card);
  border: 1px solid var(--tv-border);
  border-radius: var(--tv-radius-lg);
  box-shadow: var(--tv-shadow-panel);
  padding: 18px 20px;
}
.complex-panel h3 {
  font-size: 14px; font-weight: 600; color: var(--tv-text);
  margin: 0 -20px 16px; padding: 0 20px 14px;
  border-bottom: 1px solid var(--tv-border);
}
.complex-actions { gap: 8px; margin: 0 0 12px; }

/* ══ מסמכים ודוחות   (Build 9.216) ═════════════════════════════════
   כרטיסי הדוחות היו המשטח האחרון במסך הזה בלי כלל משלו. */
.rep-card, .docs-usage, .docs-proj {
  background: var(--tv-card);
  border: 1px solid var(--tv-border);
  border-radius: var(--tv-radius-lg);
  box-shadow: var(--tv-shadow-panel);
}
.rep-card:hover { border-color: var(--tv-border-strong); }
/* ══ .sub-card = פאנל   (Build 9.212) ═══════════════════════════════
   ב-9.20x המחלקה כבר גויסה חלקית: המסגרת המקווקוות הפכה לקו מלא
   והרקע לטוקן. אבל ההרכב נשאר של "כרטיס משנה" — משטח שטוח, כותרת
   בתוך אותו ריפוד כמו הגוף, בלי קו הפרדה.

   התוצאה נראתה במדידה: בסקירת הפרויקט ישבו שבעה `sub-card` לצד שני
   `tv-panel`, זה מעל זה. אותה היררכיה, שתי צורות. זה מה שגרם למסך
   להיקרא "לא מהגר", לא הצבעים.

   ── למה CSS ולא שכתוב של אתרי הקריאה ──
   ל-`.sub-card` יש 34 אתרי קריאה שכולם בנויים משרשור מחרוזות ארוך.
   בקובץ הזה כבר תועד מקרה (9.84) שבו הכנסת ביטוי לאמצע שרשור כזה
   קטעה את ההשמה, התחביר נשאר תקין, `node --check` עבר — והמסך פשוט
   נפתח ריק. שכתוב של 34 אתרים כדי לשנות *מראה* הוא סיכון שאין לו
   תמורה.

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

   ── הכותרת בולטת החוצה ──
   הריפוד יושב על הכרטיס כדי שהגוף — שהוא תוכן שרירותי — יקבל אותו
   בלי שאיש יידע מה יש בו. הכותרת מבטלת אותו במרג'ין שלילי כדי
   שקו ההפרדה יגיע לקצוות, כמו ב-`.tv-panel-head`. זה גם מה
   שמאפשר לכפתור ה"הצג/פירוט" שכבר יושב ב-h4 להישאר במקומו.
   ═══════════════════════════════════════════════════════════════ */
.sub-card {
  border: 1px solid var(--tv-border);
  background: var(--tv-card);
  border-radius: var(--tv-radius-lg);
  box-shadow: var(--tv-shadow-panel);
  padding: 0 20px 16px;
  margin: 14px 0;
}
.sub-card > h4 {
  font-size: 14px; font-weight: 600; color: var(--tv-text);
  margin: 0 -20px 16px;
  padding: 14px 20px;
  border-bottom: 1px solid var(--tv-border);
}
/* h4 מקונן (למשל "יקלטו כהושלמו" בתוך גוף הכרטיס) אינו כותרת של
   פאנל ולא מקבל את הקו. */
.sub-card h4 { font-size: 14px; font-weight: 600; color: var(--tv-text); margin: 0 0 10px; }
/* כרטיס מקופל הוא שורה, לא פאנל: אין גוף, ולכן אין קו הפרדה. */
.sub-card.is-collapsed { padding: 0; }
.sub-card.is-collapsed > h4 { margin: 0; border-bottom: 0; }
@media (max-width: 780px) {
  .sub-card { padding: 0 14px 14px; }
  .sub-card > h4 { margin: 0 -14px 14px; padding: 12px 14px; }
  .sub-card.is-collapsed { padding: 0; }
  .sub-card.is-collapsed > h4 { margin: 0; }
}

@media (max-width: 900px) {
  /* במסך צר הכותרת והפעולות מפסיקות להידבק: שתי רצועות דביקות על
     מסך נמוך משאירות לתוכן חלון של כמה שורות. */
  .wizard-head, .wizard-actions { position: static; }
  .wizard-body { padding: 16px 14px; }
}

/* ─────────── רשימת הסיורים  (Build 9.204) ───────────
   ארבע עמודות: זהות, מצב, השפעה הנדסית, פעולה. "אינו מזין" הוא
   טקסט מושתק ולא תג — שני תגים ניטרליים זה ליד זה נקראים כשני
   מצבים מתחרים, בזמן שרק אחד מהם הוא מצב. */
.tv-row.sv-row {
  grid-template-columns: minmax(0, 1fr) max-content max-content max-content;
  align-items: center; gap: 12px;
}
.sv-not-governing { font-size: 12px; color: var(--tv-muted); white-space: nowrap; }
.sv-types { display: flex; flex-wrap: wrap; gap: 8px; }
.sv-new { margin-bottom: 16px; }
/* `.sv-list` היא העוגן של פאנל הרשימה. היא נושאת כלל אמיתי ולא
   רק hook: שער `ui-standard` מפיל מחלקה שמופיעה בסימון בלי כלל,
   ובצדק — מחלקה כזו היא בדרך כלל שריד של עיצוב שהוסר. */
.sv-list .tv-empty { margin: 0; }
@media (max-width: 900px) {
  .tv-row.sv-row {
    grid-template-columns: minmax(0, 1fr) max-content;
    gap: 8px 10px; align-items: start;
  }
}

/* ══════════════════════════════════════════════════════════════════
   הערה מהירה — פאנל בקצה, לא דיאלוג במרכז   (Build 9.208)
   ==================================================================
   רגרסיה שהבעלים איתר: "הערה מהירה" נפתחה כמודאל ממורכז. השורש לא
   היה כלל CSS מאוחר אלא הקוד עצמו — `quickNote()` קרא ל-`TommyForm`,
   שהוא דיאלוג ממורכז גנרי. התיקון הוא בקוד; כאן רק השפה הוויזואלית.

   הפאנל יורש את מראה המגירה (`.tnb-panel`) ונשאר באותו קצה שמאלי,
   אבל אינו בגובה מלא: הערה אחת אינה מצדיקה מסך שלם. הוא צר,
   קומפקטי, וממוקם באותו מקום שבו הידית הייתה — כך שהמעבר מהכפתור
   לפאנל נקרא כהמשך ולא כקפיצה.
   ══════════════════════════════════════════════════════════════════ */
.tnb-quick-open { align-items: center; }
.tnb-quickpanel {
  width: 360px; max-width: 92vw;
  display: flex; flex-direction: column;
  background: var(--tv-card);
  border: 1px solid var(--tv-border-strong);
  border-inline-start: none;
  border-radius: 0 var(--tv-radius-lg) var(--tv-radius-lg) 0;
  box-shadow: 6px 0 28px rgba(0, 0, 0, .38);
  overflow: hidden;
}
.tnb-quickpanel .tnb-head h4 { font-size: 15px; font-weight: 600; margin: 0; }
.tnb-quick-body { padding: 14px 18px 4px; display: flex; flex-direction: column; gap: 10px; }
.tnb-quick-body textarea {
  width: 100%; box-sizing: border-box; resize: vertical; min-height: 108px;
}
.tnb-quick-check {
  display: flex; align-items: center; gap: 8px;
  font-size: 13px; color: var(--tv-text); cursor: pointer;
}
.tnb-quick-check input { width: 15px; height: 15px; accent-color: var(--tv-primary); }
/* 9.211 — רמז המקלדת בפאנל ההערה המהירה. הועלה לרצפת ה-12px. */
.tnb-quick-hint { margin: 0; font-size: 12px; color: var(--tv-muted); line-height: 1.5; }
.tnb-quick-actions {
  display: flex; gap: 8px; padding: 14px 18px 16px;
  border-top: 1px solid var(--tv-border); margin-top: 12px;
}
.tnb-quick-actions .btn { padding: 8px 16px; font-size: 13.5px; }
@media (max-width: 900px) {
  .tnb-quickpanel { width: 92vw; border-radius: 0; }
}

/* ══════════════════════════════════════════════════════════════════
   הפרימיטיבים הישנים — הרמה לשפה אחת   (Build 9.209)
   ==================================================================
   `.mini-table`, `.add-line` ו-`.num` מופיעים בעשרות מקומות שנכתבו
   לפני ההגירה: סקירת הפרויקט, תיק הפרויקט, מסמכים, כללים, כתב
   כמויות. כל עוד הם נראים כמו טומי הישן, כל מסך שמשתמש בהם נקרא
   כאי ישן — גם אחרי שהמסך עצמו הועבר.

   ההרמה נעשית כאן ולא בשכתוב DOM. `.mini-table` לבדה מופיעה
   ב-24 מקומות; שכתוב של כל אחד מהם הוא 24 הזדמנויות לשבור טבלה
   שמציגה נתונים אמיתיים, בלי שום רווח מעבר למה ששינוי סגנון אחד
   נותן. זה בדיוק העיקרון של Path A.

   הכלל הישן צייר גריד מלא: מסגרת סביב כל תא, כותרת ב-font-weight
   900. בשפת הייחוס טבלה היא רשימה עם מפרידי שורה — הגבולות
   האנכיים מיותרים כי ההיישור כבר מפריד את העמודות.
   ══════════════════════════════════════════════════════════════════ */
.mini-table {
  width: 100%; border-collapse: collapse;
  margin: 10px 0 14px; font-size: 13.5px;
}
.mini-table th, .mini-table td {
  border: 0; border-bottom: 1px solid var(--tv-border);
  padding: 9px 10px; text-align: start; vertical-align: top;
}
.mini-table th {
  background: transparent; color: var(--tv-muted);
  font-weight: 500; font-size: 12px; letter-spacing: .01em;
  border-bottom: 1px solid var(--tv-border-strong);
  white-space: nowrap;
}
.mini-table tbody tr:last-child td { border-bottom: 0; }
.mini-table tbody tr:hover td { background: var(--tv-surface); }
/* מספרים בטבלה נקראים כעמודה רק כשהספרות מיושרות. */
.mini-table .num, .mini-table td b { font-variant-numeric: tabular-nums; }

/* שורת הפעולות שמתחת לכרטיס. */
.add-line { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }

/* ── ערכים טכניים ─────────────────────────────────────────────────
   `.num` נושא מספרי פרויקט, זרמים, הספקים, אורכים וסכומים. שתי
   דרישות שונות:

     ספרות באותו רוחב — כדי שעמודה של מספרים תיקרא כעמודה
     בידוד דו-כיווני — כדי ש-RTL לא יסדר מחדש ערך טכני

   בלי `isolate`, מחרוזת כמו `3×400A` או `TM26001` יכולה להיקרא
   הפוך כשהיא יושבת בתוך משפט עברי. זו אינה בעיה אסתטית: מספר
   פרויקט שהתהפך הוא מספר אחר. */
.num {
  font-variant-numeric: tabular-nums;
  unicode-bidi: isolate;
}

/* ── מצב טעינה בדשבורד   (Build 9.209) ────────────────────────────
   שלוש שורות שלד מרוסנות במקום הצהרה עסקית שגויה. לא ספינר מסתובב:
   המסך הזה נטען במאיות שנייה כשאין ענן, ואנימציה שמושכת עין על
   משהו שכבר נגמר גרועה ממצב סטטי שקט. */
.dash-hydrating { display: flex; flex-direction: column; gap: 10px; padding: 4px 2px 2px; }
.dash-skel-line {
  height: 14px; border-radius: 6px;
  background: linear-gradient(90deg,
    var(--tv-surface) 0%, var(--tommy-secondary-surface) 50%, var(--tv-surface) 100%);
  background-size: 200% 100%;
  animation: tommy-skel 1.4s ease-in-out infinite;
}
.dash-skel-line.short { width: 45%; }
.dash-hydrating .step-help { margin: 2px 0 0; }
@keyframes tommy-skel { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
@media (prefers-reduced-motion: reduce) { .dash-skel-line { animation: none; } }

/* ══════════════════════════════════════════════════════════════════
   Build 9226 §B — פרימיטיבים חדשים מהרכיבייה שהבעלים הוסיף
   ══════════════════════════════════════════════════════════════════
   מקור: LOVABLE_RETROFIT_IMPLEMENTATION_KIT/tommy-lovable-additions.css
   בחבילת ה-handoff של Build 9226. נבדק לפני המיזוג: אף אחת מחמש
   המחלקות (`.tv-compare`, `.tv-rail`, `.tv-modal`, `.tv-drawer`,
   `.tv-fab`) לא הייתה קיימת עדיין בקובץ הזה — אין כפילות/התנגשות
   למזג איתה. כל שמות המשתנים (`--tv-border`, `--tv-surface-2`,
   `--tv-primary`, `--tv-warning`, `--tv-success`, `--tv-text`,
   `--tv-muted`, `--tv-card`, `--tv-radius`, `--tv-radius-lg`) כבר
   מוגדרים ב-`:root` בראש הקובץ הזה — שכבת הכינויים הקיימת שמצביעה
   על `--tommy-*` ב-app.css. תצוגה בלבד: אין כאן שינוי ל-ID, ל-
   data-attribute או ללוגיקה כלשהי — עדיין יש לחבר את המחלקות האלה
   בפועל למרקאפ הקיים (הרכיבייה עצמה, לא רק ה-CSS שמזין אותה,
   מטופלת בשלבי המיגרציה הבאים: Notebook/Quick-Note ל-`.tv-drawer`/
   `.tv-fab`/`.tv-modal`, סרגל הסקר ל-`.tv-rail`, אישור/השפעה ל-
   `.tv-compare`). */

/* Before / After — שפה ויזואלית אחידה לאישור ולניתוח השפעה */
.tv-compare { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.tv-compare > * { min-width: 0; }
.tv-compare-col {
  border: 1px solid var(--tv-border);
  border-radius: var(--tv-radius);
  background: var(--tv-surface-2);
  overflow: hidden;
}
.tv-compare-col > header {
  padding: 8px 12px;
  font-size: 12px; font-weight: 600; letter-spacing: .02em;
  color: var(--tv-muted);
  border-bottom: 1px solid var(--tv-border);
  background: color-mix(in oklab, var(--tv-card) 70%, transparent);
}
.tv-compare-col.is-after > header { color: var(--tv-primary); }
.tv-compare-col .tv-dl-row { padding: 9px 12px; }
.tv-compare-col.is-after .tv-dl-row.is-changed {
  background: color-mix(in oklab, var(--tv-primary) 10%, transparent);
}
.tv-compare-col.is-before .tv-dl-row.is-changed { opacity: .62; }
.tv-delta { font-size: 12px; font-weight: 600; }
.tv-delta.up   { color: var(--tv-warning); }
.tv-delta.down { color: var(--tv-success); }
@media (max-width: 900px) { .tv-compare { grid-template-columns: 1fr; } }

/* Rail — נווט מקטעים לסקר שטח.
   שם נפרד מ-.tv-steps הקיים (שהוא רשימה ממוספרת) כדי לא לדרוס אותו. */
.tv-rail { display: flex; flex-wrap: wrap; gap: 8px; }
.tv-rail button {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 8px 12px; font: inherit; font-size: 13px; font-weight: 500;
  color: var(--tv-muted); cursor: pointer;
  background: var(--tv-surface-2);
  border: 1px solid var(--tv-border); border-radius: 999px;
  transition: background .15s, border-color .15s, color .15s;
}
.tv-rail button b {
  display: grid; place-items: center; width: 20px; height: 20px;
  border-radius: 999px; font-size: 12px;
  background: var(--tv-surface); color: var(--tv-muted);
  font-variant-numeric: tabular-nums;
}
.tv-rail button:hover { color: var(--tv-text); border-color: var(--tv-border-strong); }
.tv-rail button.is-active {
  color: var(--tv-text); border-color: var(--tv-primary);
  background: color-mix(in oklab, var(--tv-primary) 14%, transparent);
}
.tv-rail button.is-active b { background: var(--tv-primary); color: var(--tommy-primary-contrast); }
.tv-rail button.is-done  { color: var(--tv-success); }
.tv-rail button.is-done b { background: color-mix(in oklab, var(--tv-success) 22%, transparent); color: var(--tv-success); }
.tv-rail button.is-warn  { color: var(--tv-warning); border-color: color-mix(in oklab, var(--tv-warning) 45%, var(--tv-border)); }
.tv-rail button.is-warn b { background: color-mix(in oklab, var(--tv-warning) 22%, transparent); color: var(--tv-warning); }

/* בתוך המגירה השורה צרה — עמודה אחת */
.tv-drawer .tv-row { grid-template-columns: minmax(0,1fr); }

/* Modal — Quick Note / דיאלוגים */
.tv-modal-backdrop {
  position: fixed; inset: 0; z-index: 60;
  background: rgba(2,6,10,.66); backdrop-filter: blur(3px);
  display: grid; place-items: center; padding: 24px;
}
.tv-modal {
  width: min(680px, 100%);
  background: var(--tv-card);
  border: 1px solid var(--tv-border-strong);
  border-radius: var(--tv-radius-lg);
  box-shadow: 0 24px 64px rgba(0,0,0,.5);
  overflow: hidden;
}
.tv-modal textarea {
  width: 100%; min-height: 150px; resize: vertical;
  background: var(--tv-surface-2); color: var(--tv-text);
  border: 1px solid var(--tv-border); border-radius: var(--tv-radius);
  padding: 10px 12px; font: inherit; font-size: 14px;
}
.tv-modal textarea:focus { outline: none; border-color: var(--tv-primary); }

/* Drawer — פנקס הפרויקט (נפתח מצד שמאל ב-RTL) */
.tv-drawer {
  position: fixed; top: 0; bottom: 0; left: 0; z-index: 55;
  width: min(420px, 92vw);
  background: var(--tv-card);
  border-left: 0; border-right: 1px solid var(--tv-border-strong);
  box-shadow: 12px 0 40px rgba(0,0,0,.45);
  display: flex; flex-direction: column;
}
.tv-drawer-body { flex: 1; overflow: auto; padding: 14px 16px; }
/* כשהמגירה פתוחה — כפתור ההערה הצף מוסתר */
body.tv-drawer-open .tv-fab { display: none; }
.tv-fab {
  position: fixed; bottom: 24px; left: 24px; z-index: 50;
  border-radius: 999px; padding: 12px 18px;
  box-shadow: 0 10px 30px rgba(0,0,0,.45);
}

/* ══════════════════════════════════════════════════════════════
   Build 9226 §OwnerVisualCorrection — חיפוש גלובלי בכותרת העליונה
   ══════════════════════════════════════════════════════════════
   הייחוס (01-dashboard.png) מציג שדה חיפוש במרכז הכותרת. הכותרת
   הרוחבית עצמה (ולא הפריסה: כותרת-בתוך-עמודת-תוכן/סרגל-עמודה-
   מלאה של הייחוס) לא שונתה בפעימה הזו — ניסיון קודם (9.201, ראו
   ההערה שלו ב-index.html מעל ה-<main>) לשחזר בדיוק את מבנה ה-DOM
   של הייחוס יצר `mobileMenuBtn` כפול (אחד בכותרת החדשה בתוך
   העמודה, אחד שנשאר בכותרת הרוחבית), ומזהה כפול "גנב" כל פעולה
   שתלויה ב-getElementById מהעותק הראשון. שחזור נכון של הפריסה
   המלאה (סרגל כעמודה מלאה בגובה המסך + כותרת רק מעל main) הוא
   שינוי ארכיטקטוני רוחב-מסך שדורש בדיקה ייעודית משלו, כולל אימות
   מחדש מלא של חוזה הגלילה הנעול ב-scroll-architecture-9225.spec.js
   — ולכן לא מבוצע בפעימה הזו. הוספת שדה החיפוש בתוך הכותרת
   הקיימת היא שיפור בטוח, תוסף, שלא נוגע במבנה הזה. */
.header-search { position: relative; }
.header-search input {
  width: 100%; height: 34px;
  padding: 0 12px 0 34px;
  border-radius: var(--tv-radius);
  border: 1px solid var(--tv-border);
  background: var(--tv-surface-2);
  color: var(--tv-text);
  font: inherit; font-size: 13.5px;
}
.header-search input::placeholder { color: var(--tv-muted); }
.header-search input:focus { outline: none; border-color: var(--tv-primary); }
.header-search::before {
  content: ""; position: absolute; top: 50%; left: 12px;
  width: 15px; height: 15px; transform: translateY(-50%);
  background: var(--tv-muted);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'/%3E%3C/svg%3E") center/contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'/%3E%3C/svg%3E") center/contain no-repeat;
  pointer-events: none;
}
@media (max-width: 900px) { .header-search { display: none; } }

/* Build 9226 §OwnerVisualCorrection - .sr-only: תווית נגישות עבור
   #headerSearchInput (label שקושר ל-input חדש) שתהיה נסתרת חזותית
   אבל נגישה לקורא מסך - התקן הרגיל, לא מומצא, זהה לכל ספריית
   utility (Tailwind/Bootstrap וכו'). נדרש כי ui-standard.test.js
   אוסר class חדש בסימון בלי חוק style שמגדיר אותו. */
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* Build 9226 — "תכנון והנדסה" (Engineering root) cross-project matrix,
   assets/js/app.js evpanelRenderRoot(). Spacing/layout only, reusing
   the existing .tv-stats/.tv-stat/.tv-seg/.tv-panel/.data-table
   primitives for everything else - no new component. */
.evp-root-kpis { margin: 0 0 16px; }
.evp-root-toolbar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin: 0 0 14px;
}
.evp-root-tabs { flex-wrap: wrap; }
.evp-root-search {
  height: 36px; min-width: 220px; padding: 0 12px; border-radius: 8px;
  border: 1px solid var(--tv-border); background: var(--tv-surface);
  color: var(--tv-text); font: inherit;
}
.evp-root-panel { margin-bottom: 24px; }
/* Build 9226 §OwnerVisualConvergence (2026-09-13) — "matrix density,
   row height" (screen 12). `.data-table` is a shared primitive (also
   used by the real Equipment List / Cost Catalog tables), so its base
   padding/font-size stay generic-table sized (11px/15px) and are not
   touched here — that would ripple into screens that were never
   flagged as different. Scoped two-class overrides, only for this
   matrix, bring the row down to the reference's tighter density
   without a new component and without touching any other table on
   the site. */
.evp-root-panel .data-table th,
.evp-root-panel .data-table td { padding: 7px 10px; font-size: 13px; }
.evp-root-panel .data-table td { vertical-align: middle; }
.evp-root-panel .data-table tbody tr[data-evp-root-select] { cursor: pointer; }
.evp-root-detail { margin-top: 16px; }
.evp-new-plan-head { margin: 0 0 12px; font-size: 16px; font-weight: 600; }
@media (max-width: 900px) {
  .evp-root-toolbar { flex-direction: column; align-items: stretch; }
  .evp-root-search { min-width: 0; width: 100%; }
}

/* Build 9226 §OwnerVisualConvergence (2026-09-13) — screen 16 (mobile
   dashboard), "סטטוס פרויקטים לפי שלב". The desktop reference (screen
   01) keeps the amount/count/progress-bar/label row exactly as built
   (`.dash-stage-row`, app.css) — that composition is already correct
   and stays untouched at desktop widths. The MOBILE reference draws
   the *same real per-stage numbers* (byStatus/sumByStatus, computed
   once in the shared dashboard renderer) as a flat divided list with
   no bar: amount, count, label — nothing invented, just the bar
   dropped and the row simplified, matching Lovable's own phone
   composition for this panel. Scoped to the phone breakpoint already
   used elsewhere in this file (720px) so tablet widths keep the
   existing 900px 2-column/bar-below treatment (app.css) untouched. */
@media (max-width: 720px) {
  .dash-stage { gap: 0; }
  .dash-stage-row {
    grid-template-columns: minmax(0, 1fr) max-content max-content;
    gap: 10px; padding: 12px 0; font-size: 13.5px;
  }
  .dash-stage-row + .dash-stage-row { border-top: 1px solid var(--tv-border); }
  .dash-stage-row .tv-prog { display: none; }
  .dash-stage-n { min-width: 20px; text-align: center; }
  .dash-stage-sum { min-width: 0; }
}

/* Build 9226 §OwnerVisualConvergence (2026-09-13) — screen 17 (mobile
   projects). The shared row (.proj-item-wrap, app.js
   projectsRenderScreen) was already accepted for desktop (screen 02)
   and stays byte-for-byte the same there — nothing here fires above
   the phone breakpoint already used for screen 16 above (720px).

   Two real, data-backed additions, mobile-only:
   - .proj-attn: "one clear attention reason where real data exists" —
     plain colored text, not another card/chip, so it reads as one
     line, not a second badge competing with the status one.
   - .proj-actions-toggle / .is-actions-open: the four action icons
     (◎ ✎ ↗ ✕) collapse behind one "⋮" toggle instead of standing as
     four dominant icons of equal weight to everything else on the
     card. Same buttons, same data-attributes — nothing removed, just
     hidden until the toggle opens .proj-item-actions inline below the
     row (no absolute positioning, so nothing can clip or overlay a
     neighboring card). */
.proj-attn { display: none; }
@media (max-width: 720px) {
  .proj-attn {
    display: flex; align-items: center; gap: 6px;
    margin-top: 2px; font-size: 12.5px; line-height: 1.5;
  }
  .proj-attn .tv-dot {
    width: 6px; height: 6px; min-width: 6px; border-radius: 999px;
    background: currentColor;
  }
  .proj-attn-warn   { color: var(--tv-warning); }
  .proj-attn-danger { color: var(--tv-danger); }

  .proj-actions-toggle {
    display: flex; align-items: center; justify-content: center;
    width: 34px; height: 34px; margin-inline-start: auto;
    background: var(--tommy-panel); color: var(--tv-text);
    border: 1px solid var(--tommy-surface-2); border-radius: 9px;
    font-size: 18px; line-height: 1; cursor: pointer;
  }
  .proj-actions-toggle:hover { border-color: var(--cyan); }
  .proj-item-actions-wrap {
    display: flex; flex-wrap: wrap; justify-content: flex-end;
    flex-basis: 100%;
  }
  /* `.proj-item-actions` also carries the utility classes `flex
     items-center gap-1` (tommy-lovable-utils.css, loaded after this
     file) — `.flex{display:flex}` is a same-specificity single-class
     rule that would win on source order alone, so the hide rule here
     needs the extra `.proj-item-actions-wrap` ancestor class to
     actually beat it. */
  .proj-item-actions-wrap .proj-item-actions { display: none; width: 100%; justify-content: flex-end; margin-top: 8px; }
  .proj-item-wrap.is-actions-open .proj-item-actions { display: flex; }
}
@media (min-width: 721px) {
  .proj-actions-toggle { display: none; }
}
