:root {
  /* v1.0.696 — the focus / menu frames: light blue (was #4A78C4) — tests switch frameBlueOld */
  --ff: #38bdf8;
  --ffg: rgba(56, 189, 248, 0.35);
  --bg: #050506;
  --card: #131319;
  --card2: #1b1c24;
  --border: #2a2b35;
  --text: #f2f3f7;
  --muted: #8d90a0;
  --accent: #7b5cff;
  --accent2: #22c55e;
  --danger: #ef4444;
  /* v1.0.455 — NEW, per explicit request for a yellow "which tab is active"
     frame on the live tab (see .player-list-tab.active's own v1.0.455
     comment below) — no yellow/amber color existed anywhere in the app
     before this. */
  --warning: #eab308;
}

* { box-sizing: border-box; }
/* v1.0.232 — hides the light-gray native scrollbar on every scrollable list
   in the app, per explicit request. Purely visual (scrollbar-width/
   ::-webkit-scrollbar) — every list still scrolls exactly the same as
   before via scrollIntoView()/scrollTop (see focusContent, Nav.focus in
   remote.js, etc.) and the remote's own roll-the-OK-button scroll wheel
   still moves focus up/down the same way; none of that code was touched. */
* { scrollbar-width: none; -ms-overflow-style: none; }
*::-webkit-scrollbar { display: none; width: 0; height: 0; }

/* v1.0.230 — the #debugMarker rule that lived here since v1.0.200 was
   removed per explicit request, together with the div in index.html and
   the marker-text JS block at the top of app.js. */

html, body {
  margin: 0;
  width: 1920px;
  height: 1080px;
  background: var(--bg);
  color: var(--text);
  font-family: "Segoe UI", Arial, sans-serif;
  overflow: hidden;
}

.screen {
  display: none;
  width: 100%;
  height: 100%;
}
.screen.active { display: block; }

/* --- Setup --- */
#screen-setup .setup-box {
  max-width: 900px;
  margin: 200px auto;
  text-align: center;
}
#screen-setup h1 { font-size: 3rem; margin-bottom: 10px; }
#screen-setup .hint { color: var(--muted); font-size: 1.3rem; margin-bottom: 30px; }
#screen-setup input {
  width: 100%;
  font-size: 1.6rem;
  padding: 18px 20px;
  border-radius: 12px;
  border: 2px solid var(--border);
  background: #0b0d12;
  color: var(--text);
  direction: ltr;
  text-align: center;
  margin-bottom: 24px;
  outline: none;
}
/* Driven only by the .focused class (see applyFocus() in app.js), not the
   browser's native :focus — otherwise the address field could still show
   its own blue highlight from real DOM focus at the same time the
   "connect" button lights up via .focused, which is exactly the stuck-
   looking-focused-on-both bug this replaces. */
#screen-setup input.focused { border-color: var(--accent); }
/* Toggle row for Storage.heroVideoDisabled — same checkbox look as the
   rail's "groups" flyout (see .rail-flyout-checkbox) so it reads as the
   same kind of control. Generic (not scoped to #screen-setup) since this
   toggle itself now lives on #screen-restore instead (the screen the
   settings gear actually opens first — see activateRestoreScreen/
   renderRestoreList in app.js); kept here as a shared rule rather than
   duplicated per screen. */
.setup-toggle-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  margin-top: 18px;
}
.setup-toggle-checkbox {
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  border-radius: 6px;
  border: 2px solid var(--border);
  background: var(--card);
  position: relative;
}
.setup-toggle-checkbox.checked {
  border-color: var(--accent2);
  background: var(--accent2);
}
.setup-toggle-checkbox.checked::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 44%;
  width: 12px;
  height: 6px;
  border-left: 3px solid var(--bg);
  border-bottom: 3px solid var(--bg);
  transform: translate(-50%, -50%) rotate(-45deg);
}
.status-msg { font-size: 1.2rem; margin-top: 20px; min-height: 1.5em; }
.status-msg.ok { color: var(--accent2); }
.status-msg.err { color: var(--danger); }

/* Auto-hide-duration row (screen-restore) — replaces the old in-player
   clock-icon picker (see .player-clock-menu-option above, whose own dot
   styling this borrows). This row is a single Nav stop like the two
   checkbox rows above it: Up/Down (the screen's normal Nav) focuses the
   whole row, then Left/Right (handled specially in
   ScreenHandlers['screen-restore'] — Nav itself is vertical-only here)
   moves the selection among the options and applies it immediately, no
   separate Enter needed. Options are laid out in plain DOM/reading order
   (2, 3, 5, 7, 10) which — on this page's dir="rtl" — already renders
   right-to-left as requested, 2 first/rightmost through 10 last/leftmost. */
.setup-autohide-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 18px;
  margin-top: 18px;
  flex-wrap: wrap;
}
.setup-autohide-label { color: var(--muted); font-size: 1.1rem; }
.setup-autohide-options { display: flex; align-items: center; gap: 16px; }
.setup-autohide-option {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--muted);
  font-size: 1.1rem;
}
.setup-autohide-dot {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 2px solid var(--border);
  flex-shrink: 0;
}
.setup-autohide-option.selected { color: var(--text); }
.setup-autohide-option.selected .setup-autohide-dot {
  border-color: var(--accent2);
  background: var(--accent2);
}
/* Nudges the currently-selected dot up slightly while the row itself has
   focus, so Left/Right feels like it's actually moving something even
   though the row (not an individual option) is the real Nav stop. */
.setup-autohide-row.focused .setup-autohide-option.selected .setup-autohide-dot {
  transform: scale(1.15);
}

/* v1.0.492 — NEW: a small centered label above a related toggle+options
   group on #screen-restore (the "בחר אופן צפייה" header) — plain text, not
   focusable/interactive, just a visual sub-heading separating one feature's
   rows from the ones above it inside the same topic body. */
.setup-group-header {
  color: var(--muted);
  font-size: 1.05rem;
  font-weight: 600;
  text-align: center;
  margin-top: 26px;
}

/* --- Restore-deleted-channels screen (opens from the settings gear) ---
   Same visual language as .epg-row/.epg-list (list rows + a topbar back
   button) so it looks native to the rest of the app rather than a
   bolted-on extra screen. */
#restoreEmpty { padding: 0 40px; margin-top: 60px; text-align: center; }
.restore-list {
  padding: 20px 40px;
  height: calc(100% - 220px);
  overflow-y: auto;
}
.restore-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: var(--card);
  border: 2px solid var(--border);
  border-radius: 12px;
  padding: 18px 24px;
  margin-bottom: 14px;
  font-size: 1.2rem;
}
.restore-row.focused {
  border-color: var(--accent);
  box-shadow: 0 0 0 4px rgba(123,92,255,0.35);
}
.restore-row .title { font-weight: 600; }
.restore-row-hint { color: var(--accent2); font-size: 1rem; }
.restore-change-server {
  display: block;
  margin: 10px auto 0;
}

/* v1.0.394 — Settings accordion, per approved mockup: the flat list of
   toggles above is now grouped into 4 topics (מסך הבית / מסך מלא / הגדרות
   חיבור / כללי), each a single .focusable row (same pill look as every
   other button on this screen — .restore-change-server right above is the
   same idea) showing its name at the right edge; Enter/click expands its
   body downward. Every row INSIDE a topic body keeps its own original
   classes/spacing (.setup-toggle-row, .setup-autohide-row, .restore-list,
   .restore-change-server) completely unchanged — only the grouping/
   collapsing chrome here is new. The accordion itself scrolls (unlike the
   old flat screen, which had no scroll of its own) so it stays usable as
   more settings get added to a topic over time. */
.settings-accordion {
  padding: 10px 40px 40px;
  height: calc(100% - 90px);
  overflow-y: auto;
}
.settings-topic { margin-bottom: 16px; }
.settings-topic-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
}
.settings-topic-chevron {
  color: var(--muted);
  font-size: 1rem;
  transition: transform 0.15s ease;
}
.settings-topic.open .settings-topic-chevron { transform: rotate(180deg); }
.settings-topic-body {
  display: none;
  padding: 6px 6px 2px;
}
.settings-topic.open .settings-topic-body { display: block; }
/* The deleted-channels list no longer IS the screen's main content (it used
   to reserve almost the full screen height via the calc() below, back when
   this was the whole screen) — now it only shows at all while the "כללי"
   topic is open, so a small bounded scroll area is enough. */
.settings-topic-body .restore-list {
  height: auto;
  max-height: 420px;
  padding: 4px 0 0;
}
.settings-topic-body #restoreEmpty { margin-top: 20px; padding: 0; }

/* --- Shared focusable --- */
.focusable {
  background: var(--card);
  color: var(--text);
  border: 2px solid var(--border);
  border-radius: 10px;
  font-size: 1.3rem;
  padding: 14px 28px;
  cursor: pointer;
}
.focusable.focused {
  border-color: var(--accent);
  box-shadow: 0 0 0 4px rgba(123,92,255,0.35);
}

/* --- Topbar --- */
.topbar {
  display: flex;
  align-items: center;
  gap: 24px;
  padding: 24px 36px 16px;
}
.topbar h1 { font-size: 1.6rem; margin: 0; }
.back-btn { font-size: 1.2rem; padding: 10px 24px; }

/* --- Grid screen layout: icon rail (right) | hero + rows (main column) --- */
#screen-grid.screen.active { display: flex; }
/* Corner vignette per request — same "black near the content, lighter gray
   near the TV's own edge" principle as the row-channel tile gradient
   above, but here for the screen itself: stays solid black (the page's own
   --bg) almost everywhere, and only brightens to a thin gray right at the
   physical corner, blending outward in both directions at once, so it
   visually melts into the TV's bezel there instead of ending in a hard
   edge. PILOT: top-left corner only for now, to see how it reads before
   adding the others — NOT bottom-right, which is where the settings gear
   icon (.icon-rail-settings, pinned to the bottom of the rightmost rail)
   already sits. */
#screen-grid {
  background: radial-gradient(ellipse 620px 460px at 0% 0%, rgba(88,90,102,0.55) 0%, rgba(88,90,102,0.2) 45%, transparent 75%);
}
/* v1.0.256 — BUG FIX, per live report: checking the new full-screen-gradient
   toggle (see .hero-fullscreen-gradient above #32 further below) also hid
   the icon rail/menu — not just failed to show the gradient image itself.
   Root cause: .hero-fullscreen-gradient uses position:absolute (needed to
   cover the full screen) with z-index:0, but .grid-layout (this element,
   the icon-rail + main-column's shared parent) had NO position set at all
   ("static"). Per the CSS painting-order spec, a non-positioned ("static")
   element ALWAYS paints BEHIND any positioned sibling — even one with
   z-index:0 — regardless of which one comes first in the actual markup.
   So the gradient img, despite being first in the HTML and sitting at the
   lowest normal z-index, was still painting ON TOP of the entire rail/rows
   layout underneath it, not behind it as intended — invisible everywhere
   the image itself was transparent, but opaquely covering the rail exactly
   where the image's own alpha ramp reaches full black (its right edge,
   which is exactly where the rail sits). Fix: position:relative here (no
   visual/layout change on its own — same as static except it now
   participates in normal z-index stacking) moves this element into the
   same comparison as the gradient img, where DOM order decides it — and
   since this element comes AFTER the gradient img in the markup, it now
   correctly paints above it, exactly as originally intended.
   v1.0.258 — briefly added an explicit z-index:10 here, to try to keep
   this element above .hero-fullscreen-gradient once THAT element's own
   z-index moved 0 -> 2 (see its rule further below) — did NOT work as
   intended: giving this positioned element its OWN explicit z-index makes
   it form a new stacking context, which traps its entire subtree
   (.hero-media/.hero-corner-blend included, several levels deeper inside
   it) and compares that whole subtree to .hero-fullscreen-gradient as ONE
   unit at THIS element's z-index (10) — always beating the gradient's
   z-index (2) regardless of value, so the gradient stayed stuck behind the
   video no matter what. v1.0.259 — REVERTED back to the v1.0.256 state
   (no explicit z-index here again): the real fix was moving
   .hero-fullscreen-gradient out of being this element's sibling entirely,
   into <section class="hero"> instead, as a true sibling of .hero-media/
   .hero-corner-blend — see index.html's own updated comment there. With
   that move, this element no longer needs to out-rank the gradient at
   all, since they're no longer being compared to each other in the first
   place. */
.grid-layout { display: flex; width: 100%; height: 100%; position: relative; }

/* --- Icon rail (rightmost in RTL) ---
   Three stacked groups: the app logo/icon on top (decorative, not
   focusable — swap it for the real logo once it's sent over), the actual
   navigation menu (home/archive/favorites/groups/exit) vertically CENTERED
   in the space left over, and the settings icon pinned to the bottom on
   its own, slightly bigger than the rest. The centering is the standard
   flex trick: .icon-rail-menu gets margin-top/bottom:auto, which splits
   all the leftover vertical space (after the logo's own height and the
   settings item's own height) evenly above and below it. */
.icon-rail {
  /* v1.0.166 — explicit request: the rail icons weren't centered between
     "their own margin" (the gap from the icons to the TRUE screen edge)
     and "the start of the home screen" (where the visible tiles/content
     actually begin). Root cause: the visible content starts 56px further
     in than this rail's own inner edge (see .content-rows/.hero-info's own
     56px right padding, added for a different alignment reason) — so the
     old symmetric align-items:center split (15px/15px within this box
     alone) was actually 15px outer vs 15+56=71px inner once that extra
     56px is counted, nowhere near centered.
     Fix: widened this rail from 104px to 130px and switched from
     center-alignment to explicit asymmetric padding so the icons sit
     flush against the LEFT edge (padding-left:0, right next to where the
     content's own 56px begins) with all the added room on the right
     (padding-right:56px, toward the true edge) — 130 - 56 - 0 = 74px
     content box, exactly the icon width, so outer (56) now equals inner
     (0 + 56) exactly. */
  width: 130px;
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  height: 100%;
  padding: 40px 56px 30px 0;
  /* v1.0.260 — NEW, per the same live report as .hero-fullscreen-gradient's
     own v1.0.260 fix above (which switched it to position:fixed so it's no
     longer clipped by .main-column's overflow:hidden, and now genuinely
     spans the full 1920px screen width, including this rail's own 130px
     strip). Without its own stacking context, this rail (and everything
     inside it) is a plain static/z-index:auto descendant of .grid-layout —
     and .hero-fullscreen-gradient's explicit z-index:2 would always beat a
     z-index:auto element regardless of DOM order, painting the (now
     unclipped, full-width) gradient straight over the rail. An explicit
     z-index here, safely above the whole hero stack (highest there is
     #heroCornerBlendDup's 4), keeps the rail on top regardless of what the
     hero stack's own internal z-index values ever are. Scoped to JUST this
     element (not .grid-layout, not .main-column) so it doesn't trap
     .main-column's own subtree into one comparison unit — that exact
     mistake is what v1.0.258's .grid-layout z-index:10 attempt got wrong
     (see .grid-layout's own comment for the full story). */
  position: relative;
  z-index: 10;
}
.icon-rail-logo {
  width: 68px;
  height: 68px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
}
/* Brief row-position flash — appears for about a second whenever focus
   moves to a different row on the home screen (see flashRowIndicator()
   in app.js), then fades away. Per explicit request: the row's position
   among the currently visible rows, shown briefly instead of a permanent
   number sitting on screen all the time. Relocated (was right under the
   app logo) into the rail's own empty gap between the exit icon and the
   settings icon — the exact same slot/size as #digitOverlay's home-screen
   placement below (the two never show at once: this only fires while
   browsing rows, digit entry only while typing a channel number), just
   blue instead of white so the two stay visually distinct. See the
   comment on that rule for how the fixed x=1827/y=872 midpoint is
   derived. Body-level + position:fixed (not nested under the rail
   itself) so it isn't affected by anything the rail's own layout does. */
.row-flash-indicator {
  position: fixed;
  top: 872px;
  left: 1827px;
  transform: translate(-50%, -50%);
  font-size: 1.3rem;
  font-weight: 700;
  color: var(--accent);
  background: var(--card2);
  border: 2px solid var(--accent);
  border-radius: 12px;
  padding: 8px 18px;
  letter-spacing: 3px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease;
  white-space: nowrap;
  z-index: 30;
}
.row-flash-indicator.show { opacity: 1; }
.icon-rail-menu {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 22px;
  margin-top: auto;
  margin-bottom: auto;
}
/* Menu icons (home/archive/favorites/groups/exit) enlarged from their
   original 28px/74px size per request — first roughly 2x (58px/112px),
   too big; dialed back ~25% (44px/84px), still a bit big; dialed back
   another ~12% from that, landing here just a touch bigger than the
   original (see the width/height attributes on each <svg> in index.html,
   now 39). */
.icon-rail-item {
  width: 74px;
  height: 74px;
  border-radius: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  border: 2px solid transparent;
}
/* Settings sits alone at the bottom, a little bigger than the menu icons
   above it so it still reads as its own, separate control. */
.icon-rail-settings {
  width: 76px;
  height: 76px;
}
.icon-rail-item.focused {
  color: var(--text);
  border-color: var(--accent);
  background: var(--card2);
  box-shadow: 0 0 0 4px rgba(123,92,255,0.25);
}

/* --- Rail flyout: the small submenu that opens to the left of whichever
   rail icon (home/archive/favorites/groups) is selected. Anchored flush
   against the rail's own left edge (right: matches .icon-rail's own width)
   and vertically centered on screen, same as the menu it opens from. Opaque
   card background, so no separate dark backdrop is needed behind it. */
.rail-flyout {
  position: fixed;
  top: 0;
  bottom: 0;
  /* v1.0.166 — kept in sync with .icon-rail's width (104 -> 130px). */
  right: 130px;
  display: none;
  align-items: center;
  z-index: 40;
}
.rail-flyout.show { display: flex; }
.rail-flyout-box {
  background: var(--card);
  border: 2px solid var(--border);
  border-radius: 16px;
  padding: 22px;
  min-width: 300px;
  max-width: 380px;
  max-height: 70vh;
  overflow-y: auto;
}
.rail-flyout-title {
  margin: 0 0 14px;
  font-size: 1.15rem;
  color: var(--muted);
  font-weight: 600;
}
.rail-flyout-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.rail-flyout-item {
  background: var(--card2);
  border: 2px solid transparent;
  border-radius: 10px;
  padding: 14px 18px;
  font-size: 1.1rem;
  color: var(--text);
  text-align: right;
  cursor: default;
}
.rail-flyout-item.focused {
  border-color: var(--accent);
  box-shadow: 0 0 0 4px rgba(123,92,255,0.25);
}
/* v1.0.168 — groups flyout: each row gets a checkbox (visible on the home
   screen when checked, hidden when not — see Storage.hiddenGroups/
   isGroupHidden and applyRailFilter in app.js) plus its label, laid out as
   a plain flex row (the item itself keeps text-align:right from the base
   rule above, harmless here since these children don't use it). Picked up
   for reordering (enter-hold, ~2s) gets its own distinct highlight so it
   reads differently from plain keyboard focus while being moved with
   Up/Down — see moveGroupInReorder in app.js. */
.rail-flyout-item-group {
  display: flex;
  align-items: center;
  gap: 14px;
}
.rail-flyout-checkbox {
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  border-radius: 6px;
  border: 2px solid var(--border);
  background: var(--card);
  position: relative;
}
.rail-flyout-checkbox.checked {
  border-color: var(--accent2);
  background: var(--accent2);
}
.rail-flyout-checkbox.checked::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 44%;
  width: 12px;
  height: 6px;
  border-left: 3px solid var(--bg);
  border-bottom: 3px solid var(--bg);
  transform: translate(-50%, -50%) rotate(-45deg);
}
.rail-flyout-label { flex: 1; }
.rail-flyout-item-group.reordering {
  border-color: var(--accent2);
  box-shadow: 0 0 0 4px rgba(34,197,94,0.3);
}
/* "Reset order" row, pinned at the bottom of the groups flyout list (see
   renderRailFlyoutList in app.js) — restores the group order to exactly
   what the M3U/server provides, undoing any custom reordering. Styled
   differently from a plain group row (centered text, accent-colored) so it
   reads as an action, not one more group to toggle/reorder. */
.rail-flyout-reset-item {
  text-align: center;
  color: var(--accent);
  font-weight: 600;
  margin-top: 4px;
}
.rail-flyout-reset-item.focused {
  border-color: var(--accent);
  box-shadow: 0 0 0 4px rgba(123,92,255,0.25);
}

/* --- Main column: hero on top, rows of channels below --- */
.main-column {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* --- Hero: preview of the focused channel, full width. No divider lines —
   the video card is a rounded, floating black panel (old-Netflix style)
   that fades into the info side instead of a hard edge. --- */
.hero {
  flex-shrink: 0;
  /* Down to 0 — this box has no in-flow children left (both .hero-info and
     .hero-media are absolutely positioned out of it, see below), so its own
     height is now purely a spacer for the rows' available room and can be
     shrunk all the way. This is the LAST bit of room this element can free
     — once at 0 there is no more to give from here; more room for the rows
     below this point would have to come from the arrow's reserved gap or
     the rows' own content size instead, not this box. */
  height: 0;
  display: flex;
  /* No top padding — the hero (and the whole grid screen) should start
     flush at the true top edge of the display, not a gap below it. */
  padding: 0 30px 30px 0;
  /* Positioning context for .hero-info below, which is now taken out of
     normal flow so its own height isn't capped at this box's height. */
  position: relative;
}
.hero-info {
  /* Absolutely positioned (not a normal flex child) so its padding-top can
     push the text down past this box's own 350px height and render, layered
     on top (z-index below), over the empty space above the rows panel —
     instead of being clipped, or (worse) silently ending up rendered BEHIND
     the rows panel the way normal-flow overflow would have. Actual push
     amount is still measured and capped in JS (positionHeroInfo) so it can
     never grow into actually overlapping a row tile. */
  position: absolute;
  top: 0;
  /* v1.0.344 — tried -30px here (see git history in the delivered files
     for the full comment) to nudge the whole box right, per his request.
     v1.0.345 — REVERTED back to plain 0, per his own live report ("לא זז
     כלום") — the box's own right edge sits past the visible screen edge
     already (this box intentionally overlaps .hero-media's video
     horizontally, see that rule's own comment), so moving ITS edge further
     right had no visible effect on where the right-aligned text/logo
     actually render — whatever needs to move is the CONTENT's own
     alignment within the box, not the box's position. Deferred until his
     own live check narrows down which. */
  right: 0;
  /* v1.0.274 — raised 5 -> 6, per explicit report that the new
     .hero-fullscreen-gradient (also z-index:5 as of v1.0.272) was painting
     over the top-right info text instead of staying behind it. Text should
     always win regardless of DOM order, so it now sits one level above. */
  z-index: 6;
  box-sizing: border-box;
  /* v1.0.339 — was +50px here ("תגדיל את התיבה שמאלה ב-50 פיקסלים... שיכנס
     בשורה אחת הכל"), for more horizontal room for BOTH #previewNowTitle
     (the plain-text title, see setNowTitleText in app.js) and
     #previewNowLogoImg (the generated clear-logo image) before either has
     to wrap onto a second line.
     v1.0.343 — REVERTED back to plain 40% (no +50px), per explicit request
     ("תחזיר אותה למה שהיה לפני שבקשתי להגדיל") — box back to its original
     size from before v1.0.339. #previewNowTitle's own auto-shrink
     (setNowTitleText in app.js, real DOM measurement, confirmed working
     since v1.0.340) still fits the words onto one line within whatever
     width this box has, just by shrinking the font more when needed —
     no code change required there for this box getting narrower again. */
  width: 40%;
  /* Right padding raised from 14px to 56px to line up exactly with
     #content-rows' own right padding (see that rule) — both now sit the
     same distance from main-column's right edge, so the channel name/title
     text and the first tile's right edge form one straight vertical line. */
  padding: 40px 56px 0 34px;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
}
/* Wraps the station logo + channel name, per explicit follow-up request.
   Round two: a plain flex row with align-items:center (round one) still
   made the *box* 104px tall (the logo's own height — flexbox sizes a row
   to its tallest child regardless of how children are cross-aligned
   within it), which pushed "זמין עד"/the programme title down by the
   logo's extra height compared to before. Fixed per explicit follow-up
   instruction: the logo is taken OUT of normal flow entirely (position:
   absolute) so it no longer affects this row's own height at all — the
   row's rendered height is now just .hero-name's own line height, exactly
   as it was before the logo existed here, so nothing below is pushed down.
   padding-right reserves the horizontal room the logo needs (104px width +
   14px gap) so the name text doesn't render underneath it. */
.hero-name-row {
  position: relative;
  padding-right: 118px;
  margin-bottom: 12px;
}
/* v1.0.330 — per explicit request/toggle (see Storage.getHomeChannelNameHidden
   in storage.js, applyHomeChannelNameHiddenVisual in app.js): hides the
   whole row (station icon + channel name + the quality badge that sits
   next to it — they're one visual line) when this opt-in setting is on.
   Default (no body class) = today's behavior, always shown. */
body.home-channel-name-hidden .hero-name-row {
  display: none;
}
/* Small station logo next to the name — hidden by default (display:none)
   and only switched to block when the focused channel actually has a logo
   (see updatePreview() in app.js). Positioned absolute, bottom:0/right:0
   against .hero-name-row (see that rule's comment for why) — its BOTTOM
   edge lines up with the channel name's own bottom, "one line" with it as
   requested, and being much taller than the text (104px vs. the name's own
   ~34px line height) it grows entirely UPWARD from there, into the empty
   space above within .hero-info's own top padding, rather than downward
   into where the next line ("זמין עד" / the programme title) sits. */
.hero-mini-logo {
  display: none;
  position: absolute;
  bottom: 0;
  right: 0;
  /* Explicit width/height (not max-width) — on the real TV, align-self:
     center did not reliably center this when it was a stacked block above
     the name; kept as a fixed square. Doubled per explicit follow-up
     request — clarified to mean only this logo (next to the channel
     name), not the grid tiles' own logo (see .row-channel img above,
     reverted back to its original size). */
  width: 104px;
  height: 104px;
  object-fit: contain;
}
.hero-name { font-size: 1.75rem; font-weight: 600; color: var(--muted); }
/* Resolution badge (HD/FHD/4K) — sits to the LEFT of the station name (it's
   the second span in the DOM, and in this RTL layout later-in-DOM inline
   content lands further left automatically, no positioning tricks needed).
   dir="ltr" (set on the element itself, see index.html) keeps "4K"/"FHD"
   reading the right way round instead of getting bidi-reordered. Same
   muted color and general look as the name, just smaller — a label, not a
   highlighted pill. Empty (no quality known for this channel) collapses to
   nothing via :empty so it never leaves a stray gap. */
.hero-quality {
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--muted);
  margin-right: 10px;
}
.hero-quality:empty { display: none; }
/* "זמין עד ..." — its own line right under the top name/title line,
   continue-watching tiles only (see updatePreview() in app.js). Used to be
   folded into .hero-now-title together with the channel name, which buried
   it inside a giant 3.1rem headline instead of standing out on its own. */
.hero-available {
  font-size: 1.3rem;
  font-weight: 600;
  /* v1.0.396 — was var(--accent) (the app's purple), briefly tried #2ecc71
     (green) per that version's own request.
     v1.0.397 — REVERTED to plain white, per his immediate follow-up
     ("הירוק של הזמין תחליף ללבן"). */
  color: #fff;
  margin-bottom: 12px;
  display: none;
}
/* v1.0.415 — flex instead of plain block, so the clock icon (see
   updatePreview()'s isContinueWatching branch in app.js) sits inline before
   the countdown text instead of stacking above/below it.
   v1.0.416 — REDESIGNED, per explicit request + approved mockup: the icon
   (now the same archive-list glyph the full-screen player itself uses,
   see buildCwBadgeHtml() in app.js) sits alone, plain white, no
   background of its own, sized up ("בגודל סביר שרואים אותו טוב") — next to
   it a solid-colored pill (.cw-badge-pill below) holds the text. */
.hero-available.show { display: flex; align-items: center; gap: 10px; }
.hero-available .cw-archive-icon { width: 40px; height: 40px; flex-shrink: 0; color: #fff; }
/* v1.0.416 — the "זמין עוד" pill itself: solid color, white text, no
   border/outline, width hugging its own text exactly (fit-content, no
   fixed width) so a short "H:MM" clock string doesn't leave dead space
   where the longer "X שעות ו-Y דקות" text used to reach — per explicit
   request ("שגודל המלבן יהיה תואם... יתאים את עצמו בגודל"). Green while
   more than 24h remain, red at/below it — see CW_LOW_THRESHOLD_MS. */
.cw-badge-pill {
  display: inline-flex;
  align-items: center;
  width: fit-content;
  padding: 4px 14px;
  border-radius: 999px;
  color: #fff;
  font-size: 1rem;
  font-weight: 600;
  white-space: nowrap;
}
.cw-badge-pill.green { background: var(--accent2); }
.cw-badge-pill.red { background: var(--danger); }
.hero-now-title {
  font-size: 3.1rem;
  font-weight: 700;
  margin-bottom: 16px;
  line-height: 1.15;
  /* Was white-space:nowrap + text-overflow:ellipsis, cutting long titles
     off after ~4 words with "..." even though there was plenty of empty
     black space below to show the rest. Now it wraps onto a second line
     instead of truncating — positionHeroInfo() in app.js already measures
     the whole block's real height (scrollHeight) each time and shrinks
     its own push-down amount to fit, so a longer, two-line title just
     nudges the block up a bit rather than overflowing anything. */
  white-space: normal;
  overflow-wrap: break-word;
  word-break: break-word;
}
/* v1.0.240 — NEW, per explicit request ("אם יש כליר לוגו והוא בעברית אז
   נציג אותו במקום שם התכנית"): TMDB's transparent clear-logo artwork,
   shown in place of .hero-now-title's own plain text right above it (see
   #previewNowLogoImg in index.html, toggled from updatePreview()/
   updatePreviewFanart() in app.js). display:none by default and reset
   every channel focus, exactly like every other conditional element in
   this hero box. max-height caps it at roughly the same visual weight
   .hero-now-title's 3.1rem text has — not the box's own full width — so a
   very wide logo image doesn't blow past its normal footprint. */
/* v1.0.338 — max-height reduced 80px -> 64px, per explicit request
   ("תקטין קצת את הכליר לוגו שלנו"). Kept the exact same 2x ratio with the
   home-channel-name-hidden variant below (160px -> 128px) so that toggle's
   own relative "grows into the freed-up space" behavior is unchanged. */
/* v1.0.341 — wrapper around #previewNowLogoImg (see index.html's own
   comment there for why): text-align:right is what actually right-anchors
   the logo now — plain, ancient, reliable inline-box alignment, the same
   mechanism every other right-aligned text element in this box already
   uses, instead of trusting flex-stretch + object-fit/object-position on a
   replaced element (which is what silently failed on the TV's browser). */
.hero-now-logo-wrap {
  text-align: right;
  /* v1.0.395 — REDUCED 16px -> 4px, per his live report ("השורה השנייה
     התרחקה... יש הפרש של 30 פיקס לפחות") — this "second line" turned out
     to be #previewSeasonEpisode right below (a plain DOM text element, see
     index.html), NOT a 2nd line inside the auto-generated logo image
     itself (that file, lib/autoLogo.js, was re-measured directly — its
     own internal line spacing actually got TIGHTER at v1.0.394, not
     farther, and a dash-containing 2-line case doesn't even apply to a
     single-line title like "שיקגו Fire" anyway). The gap he was actually
     seeing was just this wrap's own margin-bottom. */
  margin-bottom: 4px;
}
/* Shared base — sizing/visibility only, applies equally to BOTH the real
   TMDB/Fanart.tv logo (#previewNowLogoImg) and our own auto-generated one
   (#previewNowLogoImgAuto, v1.0.349 — see index.html's own comment on
   .hero-now-logo-wrap for why these are two separate elements now).
   Anything that should affect ONLY one of them (like the margin-right
   experiments below) belongs on .hero-now-logo-real / .hero-now-logo-auto
   specifically, never here. */
.hero-now-logo {
  display: none;
  /* Back to plain natural (aspect-ratio-preserving) sizing, capped only by
     max-height/max-width — no object-fit/object-position needed once the
     wrapper's text-align:right is doing the actual alignment. */
  width: auto;
  height: auto;
  max-height: 64px;
  max-width: 100%;
}
/* v1.0.346 — TEST, per explicit request ("תנסה 70 פיקסלים נראה אם הוא
   זז בכלל"): shifting the whole .hero-info box (tried at v1.0.344, via
   `right`) had NO visible effect — its own right edge already sits past
   the screen's visible edge (intentional overlap with the video, see
   that rule's own comment), so moving IT further didn't move anything
   visible. This instead nudges just the image itself, right on the
   element the wrapper's text-align:right is aligning — a negative
   margin-right pulls the image past that aligned edge, further right,
   regardless of where the wrapper's own edge sits. Only on the logo
   image, not the plain-text title (#previewNowTitle untouched) — he
   asked about "הכליר שלנו" specifically this time.
   v1.0.347 — 70px confirmed it moves ("זה זז"); dialed back to 40px per
   his follow-up ("תחזיר 40 חזרה").
   v1.0.348 — per his explicit follow-up clarifying direction ("שמאלה
   עוד 40" — another 40 to the LEFT, i.e. back toward the natural
   aligned position, not further right): -40px + 40px = 0, so the shift
   is removed entirely and the logo sits back at its plain
   text-align:right-aligned position.
   v1.0.349 — BUG FIX, per his live report ("גם הכליר לוגו של TMDB זז") —
   this was still sitting on the SHARED .hero-now-logo selector, so it
   moved the real TMDB/Fanart.tv logo too, not just ours, whatever this
   value was set to. Moved onto .hero-now-logo-auto specifically — the
   real logo (.hero-now-logo-real) now has no margin-right rule at all, so
   nothing here can ever touch it again.
   v1.0.350 — now that ONLY our own logo can ever be affected here, per
   his explicit direction ("תזיז ב45 פיקסלים ימינה את הכליר שלנו") —
   45px to the right, same negative-margin-right mechanism confirmed
   working back at v1.0.346.
   v1.0.351 — dialed back 20px per his follow-up ("תחזיר עוד שמאלה...
   ב20 פיקס"): -45px + 20px = -25px. */
.hero-now-logo-auto {
  margin-right: -25px;
}
/* v1.0.330 — per explicit request: when the channel icon+name row above
   this is hidden (see body.home-channel-name-hidden .hero-name-row below),
   the space it used to take is free, so the clear logo is allowed to grow
   into it — doubled max-height, same reasoning/pattern as every other
   body-class-driven toggle in this file (e.g.
   body.hero-fullscreen-gradient-on). Only active while that toggle is on;
   default behavior (64px cap, v1.0.338) is completely unchanged otherwise. */
body.home-channel-name-hidden .hero-now-logo {
  max-height: 128px;
}
/* v1.0.385 — NEW, per explicit request (see the index.html comment on
   #previewSeasonEpisode for the full quote): a series episode's season/
   episode text ("עונה 11 פרק 12") as a small line right under the clear
   logo. display:none by default — applySeasonEpisodeText() in app.js adds
   .show only when the currently previewed title actually matched the
   "עונה X פרק Y" pattern (lib/tmdb.js's extractSeasonEpisode). Muted/small,
   same general "secondary info under the main identity" treatment as
   .hero-available just below in the DOM. */
.hero-season-episode {
  display: none;
  /* v1.0.388 — per explicit request ("ממש קטנה צריך להגדיל כפול 3 וצבע
     לבן"): 3x the original 1.2rem, and white instead of the muted-gray
     used for other secondary-info lines nearby.
     v1.0.390 — REDUCED, per live report after seeing it on the TV ("זה
     נראה גדול צריך פי 2"): 3x looked too big live — 2x the original
     1.2rem instead (2.4rem). */
  font-size: 2.4rem;
  color: #fff;
  margin-bottom: 8px;
}
.hero-season-episode.show {
  display: block;
}
/* v1.0.395 — NEW: the season/episode text rendered as our own small
   clear-logo-style PNG (see #previewSeasonEpisodeLogo in index.html,
   applySeasonEpisodeText in app.js). Right-aligned the exact same way as
   the main logo above it (.hero-now-logo-wrap) — wrapped in its own div
   with text-align:right rather than relying on flex stretch, since the
   <img> itself is a replaced element with an intrinsic aspect ratio and
   won't stretch to fill the flex column the way a plain div does (see
   .hero-now-logo-wrap's own comment history for why that distinction
   matters here). Sized visibly smaller — meant to read as a secondary
   line under the main title, matching the same visual weight our own
   logo's internal "line 2" already has. */
.hero-season-episode-logo-wrap {
  text-align: right;
}
.hero-season-episode-logo {
  display: none;
  width: auto;
  height: auto;
  max-height: 32px;
  max-width: 100%;
}
.hero-season-episode-logo.show {
  display: block;
}
/* v1.0.379 — NEW, per explicit request (see the index.html comment on
   #previewMovieMetaRow for the full quotes/spec). display:none by default
   — applyHeroMovieMetaRow() in app.js adds .show only when the currently
   previewed channel actually has a real TMDB MOVIE match (never for a
   plain channel, series, or a movie TMDB couldn't find). RTL flex row,
   same convention as .hero-name-row above — first child = right edge. */
.hero-movie-meta-row {
  display: none;
  align-items: center;
  gap: 18px;
  margin-bottom: 12px;
  font-size: 1.3rem;
  color: var(--muted);
}
.hero-movie-meta-row.show {
  display: flex;
}
/* v1.0.391 — .hero-movie-year NEW, per explicit request ("שכחנו להוסיף שנת
   הוצאה של הסרט", placement: "ראשון מימין, לפני הז'אנר"): straight from
   lib/tmdb.js's data.year (see applyHeroMovieMetaRow in app.js). No color
   of his own was requested — inherits the row's default muted color/size,
   same as runtime/position, just added to their shared ltr rule below. */
.hero-movie-runtime,
.hero-movie-position,
.hero-movie-year {
  direction: ltr; /* "1:45" / "15/120" / "2019" — digits/colon/slash read naturally left-to-right even inside this RTL row */
}
.hero-movie-rating {
  display: flex;
  gap: 2px;
  /* v1.0.380 — per explicit request ("את הכוכבים האדומים... להעביר לצד
     ימין שיהיו ראשונים"): rating is now the FIRST child in index.html
     (right edge in this RTL row), so it no longer needs to be pushed away
     from the others — dropped the old margin-right:auto (that was only
     ever needed to shove it to the far/left edge past the other two
     spans); it now just sits in normal row-gap flow like runtime/position. */
}
.hero-movie-rating-star {
  font-size: 1.3rem;
  line-height: 1;
  color: rgba(255, 255, 255, 0.25); /* empty star — dim outline-ish, matches --muted's general darkness without a new variable */
}
.hero-movie-rating-star.filled {
  color: #e5342b; /* red, per explicit request ("איך נראה בכוכבים אדומים") */
}
/* v1.0.382 — NEW, per explicit request ("ג'אנר הכוונה קטמדיה פעולה
   פשע...", placement: "באותה שורה עם הדירוג/זמן/משך" then "לפני הכוכבים"):
   up to 2 genre names, comma-separated (e.g. "פעולה, פשע"), straight from
   lib/tmdb.js's data.genres (see applyHeroMovieMetaRow in app.js). Empty
   text renders as nothing, same as every other span in this row when the
   underlying field is missing. */
.hero-movie-genres {
  color: #34c759; /* green, per explicit request ("צבע ירוק") */
}
.hero-now-desc {
  font-size: 1.7rem;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  /* v1.0.388 — per explicit request ("תרחיב את תיאור התכנית וסרגל
     ההתקדמות בעוד 50 פיקס שיגדל כלפי שמאלה"): 50px wider, growing only
     toward the LEFT — the right edge (aligned with .hero-info's own right
     padding, where the RTL text starts) must stay put. Overriding this
     flex child's default stretch-to-container width with an explicit
     +50px width, then pulling the now-wider box's left edge left by the
     same 50px via a negative margin, keeps the right edge fixed while the
     box extends past .hero-info's own left inner edge. Same pattern used
     on .hero-progress just below, so the two stay the same width.
     v1.0.390 — WIDENED FURTHER, per explicit follow-up request ("נגדיל
     בעוד 50 פיקס שמאלה"): another +50px to the left on top of the v1.0.388
     amount — total +100px now, same width:+Npx / margin-left:-Npx pattern,
     just N=100 instead of 50. */
  width: calc(100% + 100px);
  margin-left: -100px;
  /* v1.0.342 — BUG FIX, per live report+photo ("התיאור לא מיושר לימין תראה
     את הרווח" — a visible gap before the description text starts): this
     element never set its own direction/text-align, relying entirely on
     inheriting dir="rtl" from <html> — same category of bug as the clear
     logo's own object-fit/object-position failure above (this TV's browser
     not reliably honoring something that's only ever set implicitly).
     display:-webkit-box below (the multi-line-clamp trick) is an old,
     quirky rendering mode in its own right and is the most likely place for
     that inheritance to silently not carry through. Set explicitly instead
     of trusting inheritance, exactly like the logo fix did. */
  direction: rtl;
  text-align: right;
  /* Defense against a long run of text with no spaces (e.g. an un-decoded
     HTML entity glued onto surrounding words) — without this, such a run
     can't wrap and instead overflows straight past the box's right edge
     instead of breaking onto the next line. */
  overflow-wrap: break-word;
  word-break: break-word;
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  /* Was min-height (only a floor) — changed to a hard height because a long
     description was visibly overflowing past 4 lines on the real TV and
     reaching down into the row title below it, meaning -webkit-line-clamp
     alone wasn't reliably capping the rendered height on that device. A
     fixed height + overflow:hidden above guarantees this box can never grow
     past this regardless of the actual description's length or whether
     line-clamp is fully honored. Switched from a hard height to a
     max-height so a SHORT description doesn't reserve the full 4-line
     box — it now shrinks to its real content height (still capped at the
     same 132px ceiling for long ones), so the progress bar added right
     after it in the markup sits snug against however much text there
     actually is, instead of always sitting a fixed distance down. */
  max-height: 132px;
}
/* Non-interactive "how far into this programme are we" indicator, sitting
   directly below the description in normal flow (not absolutely
   positioned) so it naturally follows the description's own height —
   right up against it for a short description, further down for a long
   one. Visually modeled on the full-screen player's progress bar
   (.player-progress-track/.player-progress-fill) but smaller and never
   focusable — see updateHeroProgress() in app.js. */
.hero-progress {
  display: none;
  align-items: center;
  gap: 14px;
  margin-top: 14px;
  /* v1.0.388 — same +50px-to-the-left widening as .hero-now-desc just
     above, same reasoning (see that rule's own comment) — kept in sync so
     the description and the progress bar underneath it are the same
     width.
     v1.0.390 — WIDENED FURTHER, same +50px-to-the-left follow-up as
     .hero-now-desc above — total +100px now. */
  width: calc(100% + 100px);
  margin-left: -100px;
}
.hero-progress.show { display: flex; }
.hero-progress-time { font-size: 1.3rem; color: var(--muted); min-width: 52px; text-align: center; }
.hero-progress-track {
  position: relative;
  flex: 1;
  height: 7px;
  border-radius: 4px;
  background: var(--border);
  overflow: hidden;
}
.hero-progress-fill {
  position: absolute;
  top: 0;
  right: 0;
  height: 100%;
  width: 0%;
  background: var(--accent);
  border-radius: 4px;
}
.hero-media {
  /* Absolutely positioned with FIXED px size (not a % of .hero, and no
     top-offset) — flush against the screen's true top-left corner, exactly
     as requested: not a cm before the edge, the edge itself. Fully
     decoupled from .hero's own height (see .hero above), so it never moves
     or resizes as an accidental side effect of freeing more room for the
     rows below. It's free to render past .hero's bottom edge into where
     the rows sit behind it (#contentRows paints above it — see that rule's
     z-index — so no overlap is ever visible). */
  position: absolute;
  /* Flush against the true screen edge — confirmed correct. A test at
     top:-53px/left:-400px (-4cm/-30cm) pushed the box past the real edge and
     got clipped; reverted back to 0,0. */
  top: 0;
  left: 0;
  width: 1252px;
  height: 473px;
  /* +15% ("increase the resolution") applied as a uniform scale from the
     top-left corner, so that corner stays pinned flush to the screen edge
     and the extra size only ever extends right/down. */
  transform: scale(1.15);
  transform-origin: top left;
  z-index: 1;
  display: flex;
  align-items: flex-start;
  /* flex-start (not center): with object-fit:contain, a video whose aspect
     ratio doesn't exactly match this box gets letterboxed — centering it
     put visible empty space on the LEFT edge too, which read as "the
     picture doesn't reach the edge" even though the box itself did. Left-
     aligning the video within the box means any leftover empty space only
     ever falls on the right/bottom, never between the picture and the
     screen edge. */
  justify-content: flex-start;
  overflow: hidden;
  background: #000;
  /* No rounded corners anywhere on the video panel itself — square edges. */
  /* v1.0.355 — TEST, per explicit request ("גם וגם בשני המצבים תנסה FADE
     IN לשידור בעוד חצי שניה"): this box's own background swaps from
     opaque black to transparent the instant the live video/broadcast
     itself is ready (see .video-live below) — a hard, instant cut, with
     no fade of any kind, in EITHER scenario (fanart-match or the plain
     no-fanart case). 0.5s fade on the background itself now fades the
     black placeholder away gradually in both, reading as the actual
     picture fading in — separate from and in addition to gradient 2's
     own 1.8s overlay fade right above, which is a dimming layer drawn ON
     TOP of the video, not the video/picture itself. Applies symmetrically
     both ways (fade in AND out) since only "the entrance" was asked
     about — flag if an instant exit is wanted instead.
     v1.0.356 — per explicit follow-up ("עוד חצי שניה... הוא בא מהר מידי
     ובמכה" — still comes too fast/abrupt): another +0.5s on top, 0.5s ->
     1.0s. Same rule, both scenarios, still symmetric in/out.
     v1.0.358 — REVERTED to no transition here, per explicit correction
     ("תחנה שאין לה פאנארט השידור יעלה במיידי ללא דיחוי" — a channel with
     no fanart match must appear INSTANTLY, no delay/fade at all). The
     1s fade above was unconditional, so it was also softening the plain
     no-fanart hard-cut, which is exactly the thing that must stay instant.
     The fade now only applies via the new .hero-media-fanart-fade class
     below, added by setHeroMediaVideoLive() in app.js ONLY for the
     fanart-crossfade scenario (see startLiveVideo's viaFanart param) —
     every other path (no-fanart, continue-watching, the returnToGrid
     fast-path resize) has no transition on this element at all any more,
     back to the original instant cut. */
}
/* v1.0.358 — the fanart-crossfade scenario's own +1s fade (same 1s
   duration as before, just now scoped instead of global — see the base
   rule's own comment above for the full story). */
.hero-media.hero-media-fanart-fade {
  transition: background 1s ease;
}
/* v1.0.245 — BUG FIX, per live-device report ("אין תמונה במסך הבית"). Same
   root cause and same fix shape as #screen-player above: this box's own
   background:#000 (right above) used to be safely opaque because the hero
   preview video lived INSIDE it as a child, painted on top of that
   background. Since v1.0.244 moved to one shared, body-level #video element
   painted BEHIND every .screen (including this one's ancestor #screen-grid),
   that same opaque background now sits in front of the video and hides it
   completely. This class is added/removed by setHeroMediaVideoLive() in
   app.js in lockstep with video.style.display itself (see its own comment) —
   transparent exactly while the hero video is actually showing here, back to
   the normal opaque black placeholder (behind the logo/fanart) the rest of
   the time. .hero-corner-blend, .hero-fanart and #previewLogo are all
   separate elements with their own backgrounds/images and their own z-index
   above .hero-media (see each one's own rule) — none of them depend on
   .hero-media's own background, so they're unaffected either way. */
.hero-media.video-live { background: transparent; }
/* v1.0.201 — .hero-blend (the separate CSS-gradient dark overlay that used
   to sit here) was removed entirely per explicit request: element #32 in
   the element map, .hero-corner-blend (the static PNG right below this
   comment's rule in the markup — see index.html), already does this same
   edge-softening job with a real background fill, so the extra element
   was redundant. All its .wide-toggling call sites in app.js were removed
   too (updatePreviewFanart / positionPreviewVideo's two reset spots). If
   this is ever needed again, see git history / the v1.0.200 state for the
   full gradient + scaleX(1)/scaleX(1.3) animation code that lived here. */
/* v1.0.148 — back to ONE unified image again (see index.html for the full
   story): the son's new source has the corner-blend design already mocked
   up 1:1 at the real box size (cropped to exactly 1440x544, confirmed
   transparent at its own top-left, opaque at its own bottom-right).
   Diagnostics along the way (v1.0.145/146) also confirmed the video really
   does fill this box edge-to-edge (no pillarbox margin) and that this
   exact position/size/z-index layer DOES render correctly on this TV — so
   the only things that changed here from the very first v1.0.136 config
   are the source image file and this comment. */
/* v1.0.255 — NEW, per explicit request: a full-SCREEN (1920x1080) gradient
   image he supplied, as an alternative to element #32 (.hero-corner-blend,
   right below) — a settings toggle (restoreHeroFullscreenGradientToggle,
   see applyHeroFullscreenGradient() in app.js) switches between them by
   adding/removing .hero-fullscreen-gradient-on on <body>. #32's own CSS/
   markup is completely untouched — this hides it via a plain selector below
   rather than deleting/modifying it, since he explicitly wants to compare
   both live, not lose the original.
   v1.0.258→262 — this element bounced through several DOM homes and z-index
   values chasing a stacking-context trap, then a clipping bug, then a
   position:fixed-vs-video TV quirk, then a zero-alpha source-image issue —
   see git history for the full blow-by-blow if ever needed; none of it is
   relevant to the current (v1.0.263) architecture below.
   v1.0.263 — REAL fix, after v1.0.262's alpha-floor fix still wasn't
   visible on-device over the video itself. Two independent things changed:
   (1) the <img>'s own src in index.html never had this project's standard
   ?v=X.Y.Z cache-buster that every other asset gets (see the v1.0.206
   comment at the top of index.html, which already warned this exact bug
   shape would recur) — the TV may well have kept showing the STALE
   zero-alpha PNG regardless of what the new build actually shipped. Now
   versioned like everything else.
   (2) moved this element BACK in index.html to be a direct DOM sibling of
   .hero-media/#32/the fanart/the yellow duplicate, inside <section
   class="hero"> — the exact same spot #32 itself lives in, which has
   reliably painted above the live video on this TV for this whole
   project's history (see .hero-media's own v1.0.226 comment: nothing
   inside .hero-media's own scale(1.15) stacking context can ever sit above
   a sibling like #32, which is why #32/the fanart/this element are all
   siblings of it, never children). This removes any doubt about a subtler
   stacking quirk specific to comparing two separate branches of
   .grid-layout (.icon-rail vs .main-column) against the video, which the
   v1.0.261 architecture (this element as a .grid-layout child) could never
   fully rule out. This reintroduces the v1.0.260 clipping bug (.main-column
   is narrower than 1920px and clips overflow) as a side effect — fixed
   below instead, by lifting that clipping only while this toggle is on. */
.hero-fullscreen-gradient {
  display: none;
  position: absolute;
  top: 0;
  left: 0;
  width: 1600px;
  height: 573px;
  /* v1.0.289 — height +15px more (558 -> 573), per explicit request. Both
     #heroFullscreenGradient and #heroFullscreenGradientBridge share this
     class, so the height change applies to both together automatically —
     he treats "the gradient on the TV" as one combined effect.
     v1.0.274 — height +10px per explicit request (544 -> 554).
     v1.0.275 — height +4px more (554 -> 558) and width -40px (1920 ->
     1880), per explicit request. left stays 0, so narrowing the width
     pulls the box's right edge inward/leftward; since the image inside
     stretches to fill the box, its whole gradient pattern (including the
     dark peak near the video's own right edge) shifts left with it.
     v1.0.276 — width -60px more (1880 -> 1820), same left:0 logic as
     above (shifts further left); recolored back to black per explicit
     request (hero-right-dim-black.png, same exact alpha/shape).
     v1.0.278 — width -100px more (1820 -> 1720), same left:0 logic,
     per explicit request.
     v1.0.282 — width -120px more (1720 -> 1600), per explicit request:
     trying to make this box line up with the new .hero-fanart mask-image
     gradient (see that rule's own v1.0.280 comment) so there's no visible
     mismatch when the fanart fades out and the live video's own dim takes
     over. Recolored yellow (hero-right-dim-his-v2-yellow.png) as a visual
     test, same as every other recolor round here. */
  /* v1.0.271 — per his own re-scoped request: NOT a full-screen 1920x1080
     layer any more. He clarified he only wants dimming confined to the
     VIDEO box's own right portion (adjacent to the info panel), same as
     how element #32 already works — not full-screen coverage. Height
     dropped 1080 -> 544 to match #32's own proven-working image height
     (img/hero-corner-blend.png is 973x544) and the new source image
     (hero-right-dim-debug-red.png, 1920x544) — its own alpha is 0 at
     x=0, ramps up peaking around x=1360 (near the video's own right
     edge, .hero-media is left:0/width:1252px), then drops back to 0 by
     x=1400 so it doesn't spill into the info panel. Real smooth alpha
     (not dithered) — at this modest size/area it should composite over
     the live video the same way #32 already does; only our earlier
     FULL-SCREEN (1920x1080) attempts hit the video-blending limitation,
     which may be a size/coverage-area threshold rather than "any partial
     alpha ever fails". This build is a position-check diagnostic (red). -->
  /* v1.0.264 — DIAGNOSTIC, per explicit request/instruction, after v1.0.263
     (same exact DOM spot as #32, still confirmed live via photo: the red
     covers everything around the video's own rectangle — the info panel,
     the rows below — but stops in a clean hard edge exactly at the video's
     own bounds, not one pixel of tint on the picture itself) — strong
     evidence this specific TV composites the live <video> through its own
     hardware layer that normal CSS stacking can't reach, regardless of
     z-index/DOM position. Per his direct instruction ("תביא את התמונה
     האדומה לקדמת המסך שתכסה את כל התפריטים מידע ושורות התחנות וכך נוכיח
     שאפשר לשים תמונה על הטלויזיה"): z-index raised from 2 to 999 — higher
     than every other z-index anywhere in this file (previous max was 100,
     the exit-confirmation dialog) — so this element is now the single
     topmost thing on the whole screen, covering the icon rail and the
     content rows too (not just the hero box), as a clean, unambiguous test
     of whether the video rectangle specifically is unreachable by ANY
     z-index value, however high, or whether it was still losing to
     something else this whole time. If the video is still uncovered at
     z-index:999, that confirms this is a hardware video-plane limitation,
     not a CSS/stacking bug — nothing higher than this is worth trying.
     RESULT (confirmed live, v1.0.264): at z-index:999 this covered
     everything else on screen (rail, rows) but the video stayed
     completely uncovered — "זה מכסה ומעמעם הכל חוץ מהטלויזיה". This is as
     close to definitive proof as this project can get, without native
     debugging tools, that this TV composites the live <video> element
     through its own hardware plane that ordinary CSS stacking simply
     cannot draw above, at ANY z-index. v1.0.265 — reverted back to 2
     (matching #32's own layer) now that the diagnostic has answered the
     question; 999 was only ever meant to visually break the rail/rows for
     this one test, not to ship.
     v1.0.272 — real bug he caught live: focusing a "ערוצי סרטים" channel
     shows .hero-fanart (z-index:3) covering this element (was z-index:2),
     since fanart sits one layer above #32's own z-index and we'd copied
     #32's exact value. Nothing to do with the video-hardware question —
     plain z-index ordering among our own elements. Raised to 5, above
     fanart (3) AND the yellow duplicate (.hero-corner-blend-dup,
     z-index:4), so this stays the topmost of the hero-box overlay stack
     no matter which of #32/fanart/yellow-dup is currently showing. */
  z-index: 5;
  pointer-events: none;
}
body.hero-fullscreen-gradient-on .hero-fullscreen-gradient {
  display: block;
  opacity: 1;
  /* v1.0.297 — per explicit request ("שההחלפות... יהיו מסונכרנות וחפיפה
     חלקה"): the fanart<->video-gradient handoff must be ONE smooth,
     synchronized crossfade, not two separate effects cutting abruptly.
     Switched this whole toggle from display:none/block (an instant cut)
     to an opacity fade, timed to match .hero-fanart's own transition
     (opacity 1s ease, see that rule above) exactly — since both toggle
     at the same instant in app.js (setHeroFullscreenGradientHiddenByFanart
     fires in the same synchronous block as the fanart's own class change,
     see updatePreview()/updatePreviewFanart()), they now fade in perfect
     sync: the fanart fading out and this fading in (or vice versa) at
     once, reading as a single continuous cross-dissolve instead of a
     stutter. pointer-events:none (see base rule) already means the
     fading-out element never blocks input while mid-fade.
     v1.0.380 — per explicit request ("ואת הזמן של הפאנארט עשית?", completing
     the previously-deferred "מטלות נעגל 1.0 שניה בהמשך"): rounded up,
     0.8s -> 1s, kept in sync with .hero-fanart's own transition below and
     .hero-corner-blend-dup's transition, plus the matching setTimeout
     calls in app.js (800 -> 1000). */
  transition: opacity 1s ease;
}
/* v1.0.300 — per explicit request ("תאחר את הופעת השידור גם בחצי שניה"):
   the main element's own fade-IN (this is the one drawn directly on the
   live broadcast/video itself — "השידור" — as opposed to the second copy
   layer, .hero-fullscreen-gradient-bridge, "ההעתק") now starts after the
   fanart begins fading out, instead of at the exact same instant. Scoped
   with :not(.hero-fullscreen-gradient-bridge) so it only touches THIS
   element, not the copy (which has its own, larger, delay right below);
   scoped with :not(.fanart-covering) the same way the bridge's own delay
   rule is (see its comment) so only the fade-IN is delayed, never the
   fade-out.
   v1.0.301 — delay doubled 0.5s -> 1.0s, per explicit report that a flat
   0.5s stagger was too subtle to actually see live on the TV (still
   correct in code — verified — just visually hard to perceive against an
   0.8s fade with everything already overlapping).
   v1.0.302 — per explicit report ("השידור נכנס מהר מידי לא הוארך"): the
   delay change alone only pushed WHEN this element starts fading in —
   once it does start, it was still using the shared 0.8s duration, which
   now reads as too quick/abrupt next to the longer hold+delay around it.
   Added a dedicated transition-duration (0.8s -> 1.3s) on top of the
   existing delay, scoped the same way (fade-IN only, fade-out untouched).
   New timeline from the moment the fanart starts leaving (T=0): fanart
   fades 0->0.8s; this element now fades 1.0->2.3s (starts at 1s like
   before, now takes 1.3s instead of 0.8s to finish) — still overlapping
   the fanart's own fade on purpose (that overlap IS the intended
   crossfade, per the v1.0.297 request). */
/* v1.0.302 — tuned live in the "Crossfade Emulator" preview tool instead of
   guessing on-device: per explicit feedback the copy starting only after
   this element finished read as two separate hits instead of one crossfade
   ("ההעתק צריך להיות ביחד עם הופעת השידור" — the copy needs to be together
   with the broadcast's entrance), so the copy's delay was changed to MATCH
   this element's delay instead of being derived from delay+duration (see
   the bridge rule below) — this "start together" pairing stays as of
   v1.0.303 below.
   v1.0.303 — the shared delay itself moved again: v1.0.302 first tried
   0.4s (midpoint of the fanart's fade), but per explicit feedback after
   seeing it live on the emulator ("עד שהפאנארט לא גמר לדעוך שלא יופיע
   ההעתק והשידור" — until the fanart has finished fading out, neither the
   copy nor the broadcast should appear) any overlap with the still-fading
   fanart was unwanted after all. Delay raised to 0.8s — exactly the
   fanart's own fade duration — so this element and the copy now start the
   instant the fanart is fully gone, not before.
   v1.0.304 — per explicit request, ONE more second of delay added on top
   ("תוסיף עוד שניה עיכוב בהופעה שלהם"): 0.8s -> 1.8s.
   v1.0.307 — that extra second REVERTED per explicit follow-up spec ("רק
   בסוף הFADE OUT יעלם הפאנארט ומיד יראו את הטלויזיה שכבר עלתה... וההעתק
   יופיע בסוף דעיכת הפאנארט"): back to 0.8s — exactly the fanart's own
   fade-out duration.
   v1.0.308 — this transition-delay REMOVED entirely (was 0.8s), per
   repeated live report that this element kept popping into view still
   overlapping the visible fanart regardless of which delay value was
   tried here (0.4s/0.8s/1.8s, v1.0.302-307) — this TV's WebKit does not
   reliably honor a transition-delay applied via a class toggle on this
   element (see the matching app.js v1.0.308 comment for the full
   reasoning and the other known WebKit quirks this same device has hit).
   The wait is now done in JS instead, via a plain setTimeout in app.js
   that delays the actual class-toggle call itself by 800ms — so by the
   time this rule's transition ever engages, the wait has already
   happened and only the duration below still needs to apply. Duration
   (1.3s) unchanged.
   v1.0.352 — per explicit request ("הכניסה של הוידיאו תן לה עוד חצי שניה
   FADE IN"), confirmed scoped to THIS crossfade case specifically (the
   fanart-match scenario, not the plain hard-cut every other channel gets
   — see his own confirmation "2" after being asked which) and to ONLY
   this duration ("בשום דבר אחר לא לגעת שם" — don't touch anything else
   there): 1.3s -> 1.8s. The 800ms JS delay above is untouched.
   v1.0.358 — per explicit request ("תוסיף עוד שניה... גם גריינט 1 וגם
   ב2"), another +1s: 1.8s -> 2.8s. Still dead/inert code (this element
   stays display:none whenever the checkbox is on — see its own override
   below) but applied anyway since gradient 1 was explicitly named. */
body.hero-fullscreen-gradient-on .hero-fullscreen-gradient:not(.hero-fullscreen-gradient-bridge):not(.fanart-covering) {
  transition-duration: 2.8s;
}
/* v1.0.282 — per explicit request: while the movies-group fanart is showing
   (it has its own separate mask-image gradient — see .hero-fanart above),
   this element must hide entirely rather than double up on top of it —
   restored the moment the fanart fades out and the real video takes over.
   See setHeroFullscreenGradientHiddenByFanart() in app.js, which toggles
   this class at exactly those two moments.
   v1.0.297 — changed from display:none (instant cut) to opacity:0, so the
   transition above actually animates instead of snapping; !important
   because the plain hero-fullscreen-gradient-on rule right above already
   sets opacity:1 — this has to win regardless of source order. Stays
   display:block throughout (display can't transition) — opacity alone now
   drives both the show and hide side of this toggle. */
body.hero-fullscreen-gradient-on .hero-fullscreen-gradient.fanart-covering { opacity: 0 !important; }
/* v1.0.263 — companion to moving .hero-fullscreen-gradient back inside
   .hero/.main-column (see that rule's own comment above): .main-column has
   overflow:hidden and is narrower than the full 1920px screen (screen width
   minus .icon-rail's 130px), which would clip this element's rightmost
   strip exactly like it did in v1.0.260. Lifting overflow to visible ONLY
   while the toggle is actually on (this same body class is already used
   right below to hide #32) avoids touching .main-column's normal clipping
   behavior — whatever it's there for — for every other screen/state. */
body.hero-fullscreen-gradient-on .main-column { overflow: visible; }
/* #32 and its animation stand-in (.hero-corner-blend-dup, further below)
   USED TO both be hidden while this toggle was on, so only ONE of the two
   darkening approaches was ever visible at once — per explicit request at
   the time ("לבחור 1 מבניהם").
   v1.0.317 — REMOVED, per live report ("רק בערוצי סרטים... קופץ לו שם
   גריינט שחור קצר ונעלם והשידור מכסה חלק מהמסך" — on movies/fanart
   channels, the video was showing through, uncovered, while the fanart
   was still up): this rule was ALSO firing for the new v1.0.316 static
   red bridge frame (it shares this same toggle), which force-hid #32/its
   duplicate — the entire base corner-darkening system — the moment the
   settings checkbox was turned on to show the red frame, leaving NOTHING
   covering the video's corner while a fanart transitions (the exact same
   failure mode proven in the v1.0.312 diagnostic earlier tonight, now
   reintroduced by this old rule). The red bridge is meant to be a purely
   ADDITIVE decorative frame on top of the normal #32/dup system, never a
   replacement for it — #32/its duplicate now stay fully active regardless
   of this toggle, with the v1.0.313 opacity-transition fix on the
   duplicate now genuinely in effect for the first time (it was always
   force-hidden by this very rule during every earlier test tonight). */
.hero-corner-blend {
  position: absolute;
  top: 0;
  /* v1.0.201 — per explicit request: this element now animates in sync
     with .hero-fanart instead of staying static while the fanart grows/
     shrinks beside it. Reuses .hero-fanart's exact own scaleX values and
     timing (same 0.9s ease, same transform-origin top left, toggled by
     the same .wide class at the exact same three call sites in app.js —
     see updatePreviewFanart / the two reset spots there) so both boxes
     move together, at the same speed, as one unit — this was previously
     the ONE static element in the whole hero box while everything else
     (fanart, the old .hero-blend before it was removed) already animated,
     which is what left a visible seam/edge whenever the fanart was
     enlarged or shrunk without this one following it.
     v1.0.206 — FOUND THE REAL BUG behind every previous round of pixel
     tweaks here: .hero-fanart lives INSIDE .hero-media, which has its own
     transform: scale(1.15) (see .hero-media above) — so fanart's declared
     872px width actually renders on screen at 872*1.15 ≈ 1003px, and its
     scaleX values (0.8968 / 1.0573) apply ON TOP of that. This element
     (.hero-corner-blend) is a SIBLING of .hero-media, not a child — it
     gets NO such 1.15 multiplier, so its declared width was always in a
     different unit than the number it was being visually compared
     against, no matter how many times either one got nudged by a few
     pixels. Real fix: give this element the SAME 872*1.15≈1003px
     reference width and the EXACT SAME scaleX values as .hero-fanart
     (see the base rule's width below and .hero-corner-blend.wide below)
     — both elements now compute their on-screen width as
     reference*scaleX with the SAME reference and SAME scaleX, so they
     land on the exact same right edge at every instant, by construction,
     instead of by manually chasing a match in pixels. */
  transform-origin: top left;
  /* v1.0.207 — TEMPORARILY disabled (transition: none) per explicit
     request, so the size can be inspected without the animation moving
     it — was: transform 0.9s ease. Re-enable once the sizing is
     confirmed (see the matching note on .hero-fanart).
     v1.0.210 — briefly tried setting this base value directly to the
     .wide value (1.0573) to close the one remaining "jump to wide after
     the TMDB fetch resolves" gap. REVERTED in the same round: that
     applies to EVERY channel, not just the movies-group preview being
     tested — regular channels don't run updatePreviewFanart at all, so
     they never get 'wide' added, and were left permanently stuck at the
     oversized scale with no way back, which is what broke element #32's
     normal look on regular channels (reported as it "disappearing"/not
     sitting in its normal place — the darkening's own visible edge no
     longer landed where a regular channel's frame actually needed it).
     Back to the real base value (0.8968); the one-time jump when
     entering a movies channel is left as-is for now (see app.js — no
     good, side-effect-free way to remove it without also always
     rendering oversized on every regular channel).
     v1.0.219 — per explicit follow-up: re-enable the open/close ANIMATION
     for this element specifically (only when a fanart is showing — same
     .wide toggle as always, still only ever added by
     updatePreviewFanart()/removed by the per-channel reset in
     updatePreview(), both movies-group-only — see app.js), instead of the
     instant snap the v1.0.216-218 rounds all used. Explicit request: a
     smooth stretch/return animation "like we did on the full-screen
     player", not a size-only change.
     v1.0.220 — per explicit report (with a photo): even the pure-transform
     animated version still rendered wrong once returned — a hard-edged
     block with no visible gradient at all, "like a different image", not
     the real corner-blend look. Every attempt at ANIMATING this element
     back to base (raw width in v1.0.217, scaleX without transition in
     v1.0.218, scaleX with transition in v1.0.219) produced a different
     visible glitch on this TV specifically around the return. New
     approach per explicit instruction, as an experiment ("ננסה את זה
     רגע"): stop trying to animate/reset THIS element back to base at all
     — no transition here any more (see .wide below for why the stretch
     itself is still wanted, just never reversed on this element). Once
     'wide' is added it now stays forever; the illusion of "returning to
     normal" is instead done by a completely separate new element,
     #heroCornerBlendDup / .hero-corner-blend-dup (see it further below,
     right after .hero-corner-blend.wide) which sits on top of this one,
     always in the base (non-wide) shape, hidden by default and simply
     SHOWN (no transform/position change of its own, ever) at the exact
     moment this element's own 'wide' reset used to fire — see app.js.
     Since that new element never animates a transform either, it should
     be immune to whatever this TV's browser was doing wrong. Given a
     temporary solid yellow fill for now specifically so it's trivially
     visible/distinguishable in a photo while this idea is being tested —
     swap for the real image once confirmed working. */
  transform: scaleX(0.8968);
  /* v1.0.224 — explicit opacity:1 base, since 'wide' now toggles opacity
     (not transform) to hide this element while a fanart is showing — see
     .hero-corner-blend.wide below. */
  opacity: 1;
  /* v1.0.162 — FOUND IT: v1.0.159's left:-467px put part of this element at
     a negative screen coordinate (467px of it hanging off-screen past the
     true left edge). Confirmed by disabling this layer entirely (v1.0.161)
     that the whole home screen's ~7cm rightward shift went away — this TV's
     browser was shifting/re-fitting the ENTIRE rendered canvas because an
     element existed at a negative position, not because of anything wrong
     with the screen or a TV display setting. Fix: instead of positioning
     the full 1440px-wide image off to the left, the PNG itself is now
     pre-cropped to just the 973px slice that was actually visible on
     screen (image-local x=467..1440 from the original — see
     hero-corner-blend-original-1440x544.png in img/ for the untouched
     source, kept as a backup) so this element can sit at left:0 — no
     negative coordinate anywhere — while looking pixel-identical to the
     already-confirmed-correct v1.0.159 result.
     v1.0.380 — per explicit request ("האלמנט השחור... מסביב לשידור אפשר
     לקחת אותו 25 פיקס ימינה"): +25px, 0 -> 25px. Still a POSITIVE
     coordinate, so none of the v1.0.162 negative-position risk above
     applies — no crop-workaround needed this time, a plain left value
     is safe.
     v1.0.381 — per explicit follow-up request ("צריך להזיז אותו חזרה
     שמאלה ב15 פיקס"): -15px back, 25px -> 10px. Still a POSITIVE
     coordinate, so the v1.0.162 negative-position risk still doesn't
     apply.
     v1.0.384 — per explicit follow-up request ("עוד 10 פיקס שמאלה"):
     -10px more, 10px -> 0px. Back to exactly the original v1.0.159
     baseline value — still zero/non-negative, so the v1.0.162 risk still
     doesn't apply. */
  left: 0px;
  /* v1.0.173 — explicit request: width -5px (983->978, pulls the fade
     slightly toward the left/inward), height +5px (544->549, pushes it
     slightly further down so it covers a touch more over the subtitle
     area). Both are plain resizes anchored at top:0/left:0 — no negative
     coordinate introduced (see the v1.0.162 note above for why that
     matters on this TV).
     Follow-up — a visible vertical seam was reported right around this
     element's own right edge, and a since-reverted attempt widened this
     to 1060px to try to push that edge into an already-darker area. That
     made things worse in a more concrete way, confirmed by a marked-up
     photo: it stretched this raster's already-solid-black right portion
     further left, right over the gap between the video and the info
     panel where the fanart (see .hero-fanart/updatePreviewFanart in
     app.js) is supposed to be visible — swallowing it entirely. Reverted
     back to the original 978px, the seam-line question left open at the
     time.
     v1.0.203 — per explicit report, the element had since drifted out of
     sync with the actual broadcast frame's own size (978px no longer
     reaching it) — explicitly NOT a stretch/reposition this time (left
     stays 0, no negative coordinate — see the v1.0.162 note above for why
     that matters on this TV), just a plain +40px width increase so the
     right edge lands exactly on the frame again: 978 -> 1018.
     v1.0.204 — per explicit follow-up, still +20px short: 1018 -> 1038.
     v1.0.205 — per explicit follow-up, -7px back the other way: 1038 ->
     1031.
     v1.0.206 — all of the above (978 through 1031) was chasing the wrong
     number: this element was being compared against .hero-fanart's own
     872px reference width directly, but fanart actually renders at
     872*1.15≈1003px on screen (see the v1.0.206 comment on the base rule
     above for why). Replaced with that real reference instead of another
     pixel guess: 872*1.15 = 1002.8, rounded to 1003. Height left
     untouched (549px) — only the width was ever reported off.
     v1.0.207 — per explicit live-test report, v1.0.206 actually rendered
     this element narrower than before (~45px short on the right edge) —
     so the "reuse fanart's own reference/scaleX" premise from v1.0.206
     didn't hold up in practice (fanart's own numbers were tuned across
     many earlier rounds for a different, unrelated bug — its own overshoot
     past the TV edge — not to define this element's true width). Reverted
     to the same direct-pixel-width convention as every earlier round here
     (978/1018/1038/1031): explicit request was +45px more stretch to the
     right, so 1003 -> 1048. scaleX/transition values below are unchanged —
     they're what keeps this element's open/close animation running at the
     same speed and timing as .hero-fanart's own, which was never the part
     that was wrong.
     v1.0.208 — per explicit follow-up, another +20px stretch: 1048 -> 1068.
     v1.0.209 — per explicit follow-up, another +12px stretch: 1068 -> 1080.
     v1.0.211 — per explicit follow-up, another +10px stretch: 1080 -> 1090.
     v1.0.212 — per explicit follow-up, -5px back the other way: 1090 -> 1085. */
  width: 1085px;
  height: 549px;
  z-index: 2;
  pointer-events: none;
}
/* v1.0.206 — REPLACED the whole "grow further than fanart" idea (v1.0.201
   through v1.0.203's various multipliers, all chasing a mismatch caused
   by the two elements using different-unit reference widths — see the
   base rule's v1.0.206 comment). Now uses the EXACT SAME scaleX as
   .hero-fanart.wide, on the EXACT SAME 872*1.15≈1003px reference width —
   so the two elements' on-screen widths are the identical computation at
   every instant, landing on the same right edge by construction instead
   of by a separately-tuned multiplier. Still toggled at the exact same
   moments as the fanart (see app.js).
   v1.0.216 — per explicit request/diagnosis: a red-alpha-preserving photo
   diagnostic on a movies channel showed this element's TRUE darkening
   shape covering roughly half the picture with a hard edge, right after
   the fanart's own 'wide' stretch kicked in. The regular (non-wide) look
   was confirmed fine as-is — the actual problem is this
   stretch-in-sync-with-the-fanart mechanism itself, not the base width.
   Fix: this element no longer stretches when 'wide' is toggled — matches
   the base scaleX(0.8968) exactly, so app.js can keep adding/removing the
   'wide' class here (harmless now) without this element's own size or
   position ever changing. .hero-fanart.wide itself is UNCHANGED — only
   this element (#32) stops following it.
   v1.0.217 — per explicit follow-up: ONE action only — bring back a
   stretch while a fanart is showing, as a plain +60px width increase.
   Implemented by directly overriding `width: 1145px` (1085 -> 1145)
   instead of scaleX. BROKEN per explicit report: right after returning
   to base (channel switch resetting 'wide'), this element rendered as a
   thin ~45px-wide vertical line instead of its normal shape — a visible
   glitch, not just a wrong number.
   v1.0.218 — root cause: `width` is a layout-affecting property; every
   other toggle in this whole element's history (and .hero-fanart's, see
   below) has ALWAYS varied ONLY `transform: scaleX(...)` between base
   and .wide, with `width`/`height` held constant — precisely so the
   browser only ever does a cheap, compositor-only re-scale of the
   already-rendered box, never a real layout reflow of the <img> itself.
   Switching `width` instead (v1.0.217) forced a genuine reflow on this
   TV's Chromium, which is what produced the broken thin-line frame.
   Fix: back to transform-only, computed to land on the exact same
   +60px-onscreen-wider target as v1.0.217 intended: base renders at
   1085 * 0.8968 ≈ 973px; +60px = 1033px; 1033 / 1085 ≈ 0.9521. `width`
   stays 1085px (same as base) in both states — only scaleX differs.
   v1.0.219 — per explicit follow-up: the target itself changed, +60px ->
   +70px (base 973px -> 1043px on screen), so 1043 / 1085 ≈ 0.9613. The
   transform-only approach from v1.0.218 stays (still the fix for the
   thin-line bug) — this only updates the number. Paired with the new
   `transition: transform 0.9s ease` on the base rule above, so entering
   AND leaving this state now animates smoothly instead of snapping.
   v1.0.220 — per explicit instruction: the stretch itself is BACK (still
   +70px, same 0.9613 as v1.0.219 — the number was never the problem, only
   animating the RETURN was). No transition on this element any more (see
   base rule) — this state is entered as an instant snap, same as
   v1.0.216-218 always did for the wide direction, which was never
   reported broken. What's different this round: nothing ever animates
   this element back out of this state — see .hero-corner-blend-dup below
   for how the "return to normal" is now faked instead.
   v1.0.222 — per explicit follow-up, the target changed again: +70px ->
   +85px (base 973px -> 1058px on screen), so 1058 / 1085 ≈ 0.9751. Same
   instant-snap, never-animated-back mechanism as v1.0.220 — only the
   number changed.
   v1.0.224 — NEW ARCHITECTURE, per explicit request: cancelled the stretch
   on THIS element entirely — transform is now identical to the base rule
   (scaleX 0.8968) in both states, so 'wide' no longer changes this
   element's size/shape at all, ever. Instead 'wide' now only hides this
   element (opacity 0) while a fanart is showing — the yellow duplicate
   (.hero-corner-blend-dup below) is shown in its place instead, now
   carrying its OWN fixed +92px stretch permanently. Since only opacity
   changes here (never transform) there's no more risk of the transform-
   animation glitches this element hit in v1.0.217-219, so the "freeze
   forever" workaround from v1.0.220 is no longer needed either — see
   app.js, this class is now added/removed normally on every transition
   again.
   v1.0.226 — RETIRED, per explicit new architecture (walked through step
   by step in chat before implementing): #32 is now 100% static — no JS
   anywhere touches it, or this class, ever again (see updatePreview /
   updatePreviewFanart in app.js, both had their '.hero-corner-blend'
   classList calls removed entirely). The fanart itself now sits ABOVE #32
   in the stacking order (see .hero-fanart's own z-index, and the
   index.html comment on #previewFanart for why it had to move out of
   .hero-media to do that) and covers it directly while showing — that's
   what replaces the opacity-hide this rule used to do. This selector is
   dead code, kept only so old cached CSS (or a rollback) doesn't leave a
   partially-applied state; safe to delete outright later. */
.hero-corner-blend.wide {
  transform: scaleX(0.8968);
  opacity: 0;
}
/* v1.0.220 — NEW, experimental element ("ננסה את זה רגע" — per explicit
   request): a second, completely separate box in the exact same
   position, always in #32's BASE (non-wide) shape and NEVER itself
   toggled between states — it only ever gets shown or hidden, nothing
   about its own size/position/transform ever changes, so there is no
   transform-transition for this TV's browser to get wrong. Sits ABOVE
   #32 (z-index 3 vs #32's 2), hidden by default. app.js shows it at
   exactly the moment #32's own 'wide' reset used to fire (i.e. "the
   fanart is gone now") — instead of actually resetting #32 itself (which
   stays permanently wide once triggered — see the base rule above) — and
   hides it again the next time 'wide' is added to #32, so the frozen-wide
   #32 becomes visible again underneath.
   v1.0.222 — this was a plain solid-yellow <div> at first (background:
   #ffe600 below), coloring the WHOLE box — per explicit correction, fixed
   the same way the v1.0.215 red diagnostic was: it's a real <img> now
   (see index.html), pointing at a yellow, alpha-preserving debug PNG, so
   only the actual gradient shape shows yellow — no `background` property
   needed any more, the image itself carries the color. Still only shown
   when the fanart is gone (see app.js) — swap the img src for the real,
   non-debug image once this whole experiment is confirmed working.
   v1.0.224 — NEW ARCHITECTURE, per explicit request: roles reversed. This
   element is now the one that stretches (fixed, permanent +92px — base on
   screen 973px, +92 = 1065px, 1065/1085 ≈ 0.9817 — never animated, never
   toggled to any other value) and is shown WHILE a fanart is showing
   (opposite of before), covering/replacing #32 (.hero-corner-blend, which
   itself no longer stretches at all any more — see its own CSS comment)
   for exactly that duration. Hidden again the moment the fanart is gone,
   revealing the base (non-stretched) #32 underneath. See app.js for both
   toggle directions.
   v1.0.226 — z-index bumped 3 -> 4: the fanart itself (.hero-fanart) is
   now ALSO a sibling in this same stacking group (see its own comment),
   sitting at z-index 3 — this duplicate needs to stay above THAT too, not
   just above #32, so it's still the topmost of the three while a fanart
   is up. Nothing else about it changed.
   v1.0.313 — BUG FIX, likely root cause of the long-running "gradient/copy
   pops in over the still-visible fanart" saga (v1.0.282-312): this element
   had NO transition of its own at all — 'show' removed = an instant,
   un-eased opacity 1 -> 0 snap. app.js removes 'show' via a plain
   setTimeout(800ms) tied to when .hero-fanart's OWN 0.8s opacity fade is
   assumed to have just finished (see updatePreview()'s hold-timer branch).
   On this TV, if that 800ms guess doesn't land EXACTLY when the fanart has
   visually finished fading (frame drops, setTimeout scheduling jitter,
   whatever), this element vanishes instantly while the fanart (and
   whatever's really underneath at z-index 1 — the live video) is still
   visibly mid-fade or hasn't fully covered yet — read live as "part of the
   broadcast suddenly appears over the fanart" — repeatedly reported at
   this exact corner/region even across builds where BOTH
   #heroFullscreenGradient and #heroFullscreenGradientBridge were fully
   disabled (v1.0.312), which rules those two elements out and points
   squarely at this one instead. Fix: give it the same 0.8s opacity fade
   .hero-fanart itself already has, so even an imperfect setTimeout no
   longer produces a jarring instant "pop" — any residual timing mismatch
   now blends smoothly instead of snapping. */
.hero-corner-blend-dup {
  position: absolute;
  top: 0;
  /* v1.0.380 — kept in sync with #32's own +25px move just above (same
     round, same reasoning: this element stands in for #32 during the
     fanart transition, so it needs to line up with it exactly).
     v1.0.381 — kept in sync again with #32's own -15px move (25px -> 10px).
     v1.0.384 — kept in sync again with #32's own -10px move (10px -> 0px). */
  left: 0px;
  transform: scaleX(0.9817);
  transform-origin: top left;
  width: 1085px;
  /* v1.0.228 — per explicit follow-up request: +10px fixed/permanent
     height increase (549 -> 559), same "fixed, never changing" spirit as
     the +92px width stretch above — no toggle, no second value, just a
     bigger constant. */
  height: 559px;
  z-index: 4;
  pointer-events: none;
  opacity: 0;
  /* v1.0.313 — see the rule's own big comment above: matches
     .hero-fanart's 1s opacity transition so this fades out gracefully
     instead of snapping instantly.
     v1.0.380 — rounded up in sync with .hero-fanart, 0.8s -> 1s (see that
     rule's own v1.0.380 comment). */
  transition: opacity 1s ease;
}
.hero-corner-blend-dup.show { opacity: 1; }
/* v1.0.362 — per explicit request/investigation: element #32
   (.hero-corner-blend) is its own separate, always-on darkening layer —
   completely independent of gradient 1/2 and NOT hidden by the
   fullscreen-gradient checkbox (see the v1.0.317 comment on the base rule
   above for why that old coupling was removed back then) — so it was
   still showing, unmoved, underneath gradient 2 the whole time, which is
   what looked like "gradient 1 never actually turns off." Per explicit
   request, re-coupling it (and its fanart-transition stand-in, the
   duplicate — same reasoning, so nothing flashes back on mid-transition)
   to this exact checkbox: both hidden the instant it's turned on, both
   automatically back to normal the instant it's turned off — no JS
   changes needed, Storage.getHeroFullscreenGradientEnabled() already
   drives the body class both of these are scoped under.
   v1.0.375 — REVERTED, per explicit request ("תחזיר אותו" — after asking
   "remember the fixed element we hid earlier?"): back to the v1.0.317
   behavior — #32 and its dup are always-on again, completely independent
   of this checkbox, same as before this rule ever existed. Rule kept here
   (commented out) rather than deleted, for the same reason the history
   above was kept — in case this needs re-coupling again later. */
/*
body.hero-fullscreen-gradient-on .hero-corner-blend,
body.hero-fullscreen-gradient-on .hero-corner-blend-dup {
  display: none;
}
*/
/* Diagnostics-only history kept for context (all superseded by the single
   unified image approach above, v1.0.148):
   - v1.0.141-143: two separate image copies (bottom + a 90°-rotated copy
     for the right edge) — the rotation approach worked technically but
     needed careful cropping to avoid an unrelated fade band from the old
     source image.
   - v1.0.144: stretched a single copy to the box's full size — still not
     visibly darkening the right side.
   - v1.0.145: solid red test (in front of the video) — CONFIRMED visible,
     so position/size/z-index were always correct.
   - v1.0.146: same red block moved BEHIND the video (z-index 0) — showed
     NOTHING, proving the video fills this box edge-to-edge with no
     pillarbox margin, laying that whole theory to rest.
   - v1.0.147: hidden entirely, to isolate what .hero-blend's own gradient
     already does on its own.
   Conclusion: everything about the setup was fine — a semi-transparent
   black gradient just doesn't read against a region .hero-blend already
   darkens close to black. v1.0.148 replaces the two-copy setup with the
   son's new single properly-aligned source image instead of chasing this
   further. */
/* Backdrop/fanart shown behind the live preview while it's still loading —
   per explicit request ("למה אני לא רואה פאנארט לפני שהשידור עולה"), fills
   the gap between focusing a channel and the live stream actually having
   pixels to show. Movies-group channels only (see MOVIES_GROUP_NAME/
   isMoviesGroup in app.js) — that's the only group TMDB has any real
   chance of matching; see updatePreviewFanart there.
   v1.0.226 — NEW ARCHITECTURE, per explicit request: used to sit FIRST
   inside .hero-media (behind #previewVideo/#previewLogo in normal DOM
   stacking, automatically covered the moment the real picture had
   something to show). MOVED OUT to be a real DOM sibling of #32
   (.hero-corner-blend) and the yellow duplicate instead — see the
   index.html comment on #previewFanart for the full why (stacking-context
   trap caused by .hero-media's own transform:scale(1.15)). Still only
   ever reset (image + .show class) at the top of updatePreview() for
   whichever channel is focused next — that part is unchanged. Sized
   smaller than the full 1252x473 .hero-media box per explicit follow-up
   request — it was reaching all the way to the right edge, ending up
   directly under the hero-info text panel (which sits on the right, this
   page's dir="rtl" start edge). width -300px (1252 -> 952) pulled its
   right edge back away from that panel, then another -80px (952 -> 872)
   per a further request (still reaching too close to that panel); height
   +10px (473 -> 483) stretches it a bit further down (top stays 0, so the
   extra height only ever grows the bottom edge). Those were the numbers
   while still living inside .hero-media's own 1.15 scale — see the width/
   height rule below for the v1.0.226 recalculation now that it's outside
   that scale. */
.hero-fanart {
  position: absolute;
  top: 0;
  left: 0;
  /* Width/height are now a FIXED reference box, never resized by .wide
     below any more (v1.0.194) — background-size:cover computes its own
     crop/zoom of the image against whatever box size is actually in
     effect, so animating this via width meant .wide (872->942) subtly
     re-cropped the image at each size (reported as looking like a "zoom"
     — part of the backdrop only fully visible at one size, cut off at the
     other). Keeping this box's own size constant means cover always crops
     the same way, and the visual enlargement below is done as a pure
     paint-time stretch instead (transform), which never touches the crop
     at all — see .wide below.
     v1.0.220 — briefly pulled this box's own width in by 70px (872 -> 802,
     deliberately breaking the "fixed reference box" rule above) as a
     stopgap for a seam this could otherwise leave against
     .hero-corner-blend (#32) — REVERTED in the same round: #32 went back
     to actually stretching in sync with this element after all (see its
     own v1.0.220 comment — it just stopped ANIMATING back to base, not
     stopped stretching), so the seam concern this was working around no
     longer applies. Back to the real reference width (872px).
     v1.0.226 — this element moved from being a CHILD of .hero-media (which
     has transform:scale(1.15) — see index.html comment) to a SIBLING of
     it, same trick already used for #32 (see its own v1.0.206 comment for
     the exact same reasoning). To render at the identical on-screen size
     as before, the reference box itself is scaled up by the same ×1.15 to
     compensate for no longer inheriting that scale from the old parent:
     872*1.15 ≈ 1003, 483*1.15 ≈ 555. The scaleX fractions below (0.8968 /
     1.0573) are unchanged — they're pure fractions of this box's own
     width, independent of what that width actually is. Since both
     dimensions scale by the exact same ×1.15 factor, the aspect ratio is
     unchanged, so background-size:cover crops identically to before. */
  width: 1003px;
  height: 555px;
  background-size: cover;
  background-position: center;
  opacity: 0;
  /* v1.0.226 — new z-index, now that this is a sibling of #32/the yellow
     duplicate instead of a child of .hero-media: sits ABOVE #32 (z-index 2)
     so it visually covers it while a fanart is showing, but BELOW the
     yellow duplicate (z-index 4) so that stays on top of both. */
  z-index: 3;
  /* v1.0.199 — per explicit report, even after the double-animation bug
     was fixed (see positionPreviewVideo in app.js), the fanart's own
     CLOSED/baseline size (scaleX(1), i.e. the full 872px reference box)
     still ended up peeking out past the TV's own edge — an additional
     -50px shrink requested on top of that (822/872 ≈ 0.9427).
     v1.0.201 — per explicit follow-up report, a sliver was STILL visible
     at close, so another -40px on top of that: 872-50-40=782,
     782/872 ≈ 0.8968. Kept in sync with the SAME fraction (782/872) used
     as the JS clamp's own non-.wide fanartIntendedScale in
     positionPreviewVideo() — that math compares a video's real rendered
     width against whatever the "no clamp needed" baseline scale actually
     is, so it has to track this number too, not just this rule. */
  /* v1.0.210 — briefly tried setting this base value directly to the
     .wide value (1.0573), same as .hero-corner-blend's own v1.0.210
     attempt — REVERTED in the same round, together with that one (see
     its full comment above for why: it broke element #32's normal look
     on regular channels). Back to the real base value (0.8968). */
  transform: scaleX(0.8968);
  transform-origin: top left;
  /* v1.0.207 — TEMPORARILY disabled (transition: none) per explicit
     request, so the size can be inspected without the animation moving
     it. v1.0.232 — per explicit follow-up report, the fanart was
     disappearing in one abrupt cut (this rule literally read
     "transition: none") right as the live video is about to take over
     (see the classList.remove('show') in app.js) — re-enabling JUST the
     opacity half now that the sizing itself is long confirmed, so it
     closes (and opens) with a light fade. transform is deliberately left
     untouched/instant — that's the part this note was originally guarding,
     the .wide enlarge/shrink math several rounds of earlier bugs were
     fought over (see the matching note on .hero-corner-blend) — no need to
     reopen that can just for a fade on close. */
  /* v1.0.232 — lengthened again per explicit follow-up request (0.3s felt
     too quick). v1.0.236 — lengthened once more, +0.5s (0.6s -> 1.1s), per
     explicit follow-up. #heroCornerBlendDup's own hide is now delayed to
     match this exact duration (see the setTimeout in app.js) instead of
     being removed in the same instant — keep the two numbers in sync if
     this one ever changes again.
     v1.0.259 — shortened per explicit follow-up request (1.1s -> 0.8s);
     the matching setTimeout in app.js was updated to 800ms in the same
     round to stay in sync.
     v1.0.359 — per explicit request/diagnosis (this is the transition
     that actually controls how the live video visibly appears/disappears
     under a fanart — see the big v1.0.358 finding: .hero-media's own
     background fade happens invisibly, underneath the still-opaque
     fanart, so IT was never what read as "the entrance"; this one is):
     +0.7s, 0.8s -> 1.5s, both directions (fade in AND out, same rule).
     The two matching setTimeout(800) calls in app.js were updated to
     1500ms in the same round to stay in sync — see their own comments.
     v1.0.378 — per explicit request ("תקטין ל0.8 שניות ושיהיו מסונכרן בין
     השידור ועיגול בחירה בהגדרות"): back down, 1.5s -> 0.8s. All 3
     matching setTimeout(1500) calls in app.js (the live-video reveal via
     setHeroFullscreenGradientHiddenByFanart, the corner-blend duplicate's
     hide, and the now-disabled gradient-2 bridge swap) were updated to
     800ms in the same round to stay in sync — see their own comments.
     This is independent of the separate fanart-HOLD-duration radio row in
     settings (Storage.getFanartDurationMs, 1-5s) — that controls how long
     the fanart stays up BEFORE this fade starts, not the fade itself;
     both are already correctly sequenced (the hold's own await always
     resolves before this fade starts), so nothing there needed to change
     for this to stay coordinated with "the broadcast" per the request.
     v1.0.380 — per explicit request ("ואת הזמן של הפאנארט עשית?"), finally
     completing the previously-deferred "מטלות נעגל 1.0 שניה בהמשך":
     rounded up, 0.8s -> 1s. The 3 matching setTimeout(800) calls in app.js
     were updated to 1000ms in the same round to stay in sync — see their
     own comments. */
  transition: opacity 1s ease;
  /* v1.0.280 — per explicit request/reference (a screenshot of a different
     streaming app): he wants the fanart backdrop image itself to fade out
     smoothly toward its own right edge (adjacent to the info panel), so the
     photo blends into the dark UI instead of just stopping with a visible
     hard cut. Unlike the live-video hero gradient elsewhere in this file
     (which needed a baked-alpha PNG workaround because this TV's WebKit
     doesn't alpha-blend partial-transparency PNGs against the <video>
     element — see the big finding in that section), .hero-fanart is a
     plain background-image on a static <div>, not video, so a normal CSS
     mask-image gradient works directly here — no PNG needed, no video-plane
     limitation. Percentages here are relative to THIS box's own local
     width, so they still fade at the same relative spot regardless of the
     scaleX(0.8968)/.wide(1.0573) transform above. -webkit- prefix included
     since this TV's Chromium may need it for mask-image.
     v1.0.284 — REVERTED (mask-image removed) as a diagnostic, suspecting
     this property caused a thin bright blue/cyan vertical line reported on
     the real TV near this box's own edge.
     v1.0.285 — RESTORED, per explicit request: the v1.0.284 revert did NOT
     fix the blue/cyan line (confirmed still present on-device with this
     property removed), so mask-image was never the actual cause — this
     effect was fine and wanted, no reason to keep it off. The real cause of
     the line is still unidentified/unfixed; investigate elsewhere (most
     likely candidate: a hard alpha-cutoff edge in one of the baked PNGs
     used by .hero-fullscreen-gradient, not this live CSS mask). */
  -webkit-mask-image: linear-gradient(to right, black 0%, black 70%, transparent 100%);
  mask-image: linear-gradient(to right, black 0%, black 70%, transparent 100%);
}
.hero-fanart.show { opacity: 1; }
/* Enlarged size while the fanart is the only thing in the box (no video
   loaded/loading behind it yet — see the .wide class toggled in
   updatePreviewFanart/updatePreview in app.js), per explicit request —
   was a +70px width change (872->942), now the equivalent stretch done as
   a scaleX so the underlying image is never re-cropped (see the width
   comment on the base rule above). Animates back down to scaleX(1)
   (never smaller — see .hero-blend.wide for why) the moment the live
   video is actually about to take over. Positioning-preview-video in
   app.js can also clamp this scale down further, narrower even than
   scaleX(1) — see its own comment — for a video that renders narrower
   than this box's own 872px baseline.
   v1.0.205 — per explicit request, the stretch itself shrank -20px
   (942->922): 922/872 ≈ 1.0573. Kept in sync with the SAME fraction
   (922/872) used as the JS clamp's own .wide fanartIntendedScale in
   positionPreviewVideo() (app.js). */
.hero-fanart.wide { transform: scaleX(1.0573); }

/* v1.0.287 — a red, -100px-narrower PERMANENT second layer on top of
   #heroFullscreenGradient's own image (a "double frame" for the TV, same
   idea as the fanart's own single-layer mask-image frame), per explicit
   request/correction: NOT a temporary transition bridge any more (the
   v1.0.286 attempt tied this to the fanart's own fade-out only, shown for
   0.8s then hidden — reverted per explicit correction, "גשר למה, אני רוצה
   מסגרת לטלויזיה כמו הפאנארט"). Shares the .hero-fullscreen-gradient class
   itself (see index.html) so it automatically gets the exact same
   show/hide rules as that element — the body.hero-fullscreen-gradient-on
   toggle and the .fanart-covering hide — with zero separate JS logic
   needed; setHeroFullscreenGradientHiddenByFanart() in app.js now just
   toggles 'fanart-covering' on both elements together. Only this width
   override (and the DOM position placing it right after the original, so
   it paints ON TOP at the same z-index — "ההעתק יהיה מעל המקור") differs.
   v1.0.291 — width -10px more (1500 -> 1490), per explicit request.
   v1.0.297 — width reverted 1490 -> 1500: the whole v1.0.290-296 round of
   recoloring/repositioning experiments on this element (red recolors,
   narrower widths, a new image file/remap) was set aside when he rolled
   the installed app back to v1.0.289 ("נגמר הכוח תחזיר לפה") — this source
   tree is brought back in line with that same known-good v1.0.289 state
   (confirmed by extracting that exact ipk: width was 1500px there) before
   layering the new synchronized-crossfade work on top, so this round ships
   ONLY the crossfade fix, not the abandoned experiments too.
   v1.0.291 — ALSO, per explicit request at the time: this element used to
   reveal only once the fanart's fade had actually FINISHED, via its own
   separate opacity:0 + 'bridge-reveal' gate (instant, no transition of its
   own) instead of fading in sync with everything else.
   v1.0.297 — REMOVED that separate delayed-reveal gate, per explicit
   correction ("שההחלפות בין הפאנארט והגריינט שלו לבין הגריינט ההעתק שיהיו
   מסונכרנות וחפיפה חלקה בהחלפה" — the handoff must be synchronized and
   smoothly overlapping, not a delayed/separate reveal). This element
   shares the .hero-fullscreen-gradient class, so it now simply inherits
   that class's own opacity/transition rules (see above) exactly like the
   base element does — both fade together, at the same time, on the same
   0.8s curve, reading as one continuous crossfade instead of two effects
   switching at different moments. Only the width override below remains
   bridge-specific. */
.hero-fullscreen-gradient-bridge {
  /* v1.0.375 — per explicit request ("גריינט 2 שהיה אדום תאריך ימינה בעוד
     100 פיקס"): same exact move as v1.0.366 below, another +100px
     extending the box's own RIGHT edge further right (left still
     anchored at 135 — see below): 1600 -> 1700.
     v1.0.366 — per explicit request ("להאריך אותו בעוד 100 פיקס ימינה"):
     +100px, extending the box's own RIGHT edge further right (left stays
     anchored — see below — so growing the width is what pushes the right
     edge outward): 1500 -> 1600. */
  width: 1700px;
  /* v1.0.354 — TEST, per explicit request ("ננסה להזיז ימינה את גריינט 2
     ב50 פיקס"): the base .hero-fullscreen-gradient class (shared with the
     now permanently-hidden #heroFullscreenGradient, "גריינט 1") sits at
     left:0 — overriding it here, scoped to ONLY the bridge/"גריינט 2", so
     the (inert) main element is never touched.
     v1.0.357 — another +40px right per explicit request ("קח את גריינט 2
     עוד 40 פיקס ימינה"), 50 -> 90.
     v1.0.360 — another +25px right per explicit request, 90 -> 115.
     v1.0.361 — DIAGNOSTIC, per explicit request ("ננסה 50 ימינה ולמטה
     ונראה אם הוא זז בכלל" — a much bigger, more obvious jump, specifically
     to confirm whether this element visibly moves on the real TV at all):
     another +50px right, 115 -> 165.
     v1.0.363 — per explicit request, now that the diagnostic confirmed
     movement AND #32's own darkening was fixed (see the v1.0.362 rule
     further below): back -100px left, 165 -> 65.
     v1.0.364 — another +70px right per explicit request, 65 -> 135.
     v1.0.366 — left untouched this round (only width/height changed —
     see above/below). */
  left: 135px;
  /* v1.0.360 — per explicit request ("תגביה אותו בעוד 25 פיקס"): height
     override, scoped to ONLY the bridge (same reasoning as left above —
     the inert main element's own height, inherited from the shared base
     class, stays untouched). The base class's top:0 is unaffected/still
     inherited, so this fixed top edge stays put and the extra 25px only
     extends the box's BOTTOM edge further down: 573 (inherited) + 25 =
     598.
     v1.0.361 — DIAGNOSTIC, same round as the left +50px above: another
     +50px, 598 -> 648.
     v1.0.363 — per explicit request, same round as the left -100px above:
     -80px, 648 -> 568.
     v1.0.364 — another -70px, same round as the left +70px above:
     568 -> 498.
     v1.0.365 — per explicit request ("הגריינט האדום להגביה בעוד 50 פיקס"):
     +50px, 498 -> 548.
     v1.0.366 — per explicit request, another +20px: 548 -> 568. */
  height: 568px;
}
/* v1.0.364 — NEW, per explicit request: a duplicate of gradient 2 (shares
   .hero-fullscreen-gradient, so it inherits the same base position/
   opacity-transition machinery as gradient 1/2).
   v1.0.365 — METHOD CHANGED, per explicit request ("לשנות שיטה לשכפל כמו
   שהוא ולהזיז אותו שמאלה" — duplicate it AS-IS instead, just moved left):
   no longer a smaller/shrunk copy — an EXACT copy of the bridge's own
   size, only repositioned.
   v1.0.366 — per explicit request ("תשכפל בדיוק אותו הדבר... להזיז אותו
   מהמקום שהאדום יושב 200 שמאלה"): the math (bridge's own left 135, -200)
   lands on left:-65 — a NEGATIVE coordinate. First tried as an actual
   negative left per explicit request, then per his own follow-up
   suggestion ("או שתחתוך לו את הצד השמאלי בהתאם" — or crop its left side
   instead) switched to the SAME safe technique already proven elsewhere
   in this file for the exact same TV-wide-shift bug (see .hero-corner-
   blend's v1.0.162 comment): the source PNG itself is pre-cropped by the
   equivalent 65px slice (see hero-right-dim-his-v2-yellow-cropped65.png,
   cropped 78 native px off a 1920px-wide source — 1920*65/1600=78 — to
   match this box's own 1600px CSS width) instead of positioning the
   element itself off to the left, so this box can stay at left:0 — no
   negative coordinate anywhere — while rendering the visually IDENTICAL
   result (the same 65px-further-left slice of the gradient, just already
   baked into the image instead of achieved via a negative offset).
   Width shrinks to match the now-narrower cropped image: 1600 -> 1535
   (1600-65). */
.hero-fullscreen-gradient-bridge-small {
  width: 1535px;
  height: 568px;
  left: 300px;
  /* v1.0.372 — left 100 -> 300px, per explicit request ("קח את הצהוב
     שניה 200 ימינה ונראה").
     v1.0.371 — left 0 -> 100px, per explicit request ("תיקח אותו 100
     ימינה"). */
  /* v1.0.367 — per explicit report ("הצהוב יותר מידי מלא בעמעום שלו...
     שהשמאל יהיה שקוף"): tried the same CSS mask-image technique already
     proven on .hero-fanart (see its v1.0.280 comment) — opaque on the
     right, fading to transparent on the left, via a linear-gradient mask.
     v1.0.368 — widened the fade zone per follow-up ("עוד שקוף יותר").
     v1.0.369 — REMOVED, per live report ("דעכת אותו מלמעלה למטה" — it
     faded top-to-bottom instead of left-to-right, leaving a solid strip):
     this exact TV's WebKit evidently does NOT handle a keyword-direction
     (`to left`) linear-gradient mask correctly on an <img> element the
     same way it does on .hero-fanart's own <div> — instead of refusing
     it it silently rendered some other (vertical) gradient axis. Rather
     than keep fighting this element's mask-image support specifically,
     switched to the same proven-robust pattern already used elsewhere in
     this file for genuinely stubborn WebKit rendering quirks (see the
     v1.0.162 note on .hero-corner-blend): bake the fade directly into
     the PNG's own alpha channel instead of relying on any CSS effect —
     see hero-right-dim-his-v2-yellow-cropped65-faded.png (src below),
     generated with a horizontal alpha multiplier: solid/unchanged for
     the rightmost 25% of the image, then a plain linear ramp down to
     fully transparent by the left edge, applied on top of the image's
     own existing built-in gradient shape (same source asset already used
     for gradient 1/2 everywhere else in this file). No CSS mask-image
     left on this rule at all now — the image file does 100% of the
     work, so there's nothing left for this TV's browser to get wrong. */
}
/* v1.0.364 — kept hidden by default even while the checkbox is on: the
   shared body.hero-fullscreen-gradient-on .hero-fullscreen-gradient rule
   above sets opacity:1 for every element carrying that class, including
   this new one — these two rules (higher specificity: 3 classes each,
   beats that rule's 2) override it back to 0 until explicitly revealed.
   Revealed via setHeroFullscreenGradientBridgeSwapped(true) in app.js, at
   the exact moment the fanart's own fade-out finishes — same moment the
   bridge itself fades out below — so the two cross-fade into one another
   using the same inherited "opacity 0.8s ease" transition, per explicit
   request for a smooth/blurred handoff with no visible seam ("חיבור
   מעומעם שלא יראו קו"). */
body.hero-fullscreen-gradient-on .hero-fullscreen-gradient-bridge-small {
  opacity: 0;
}
body.hero-fullscreen-gradient-on .hero-fullscreen-gradient-bridge-small.show {
  opacity: 1;
}
/* v1.0.364 — the bridge's own half of the same crossfade: faded out
   (instead of instantly hidden) the moment it's "swapped" for the small
   duplicate above, via the same shared opacity transition. */
body.hero-fullscreen-gradient-on .hero-fullscreen-gradient-bridge.swapped-out {
  opacity: 0;
}
/* v1.0.299 — per explicit request ("תאחר את הופעת ההעתק בחצי שניה"): the
   copy's own APPEARANCE (fading in from hidden to visible, i.e. the moment
   'fanart-covering' is removed and it starts becoming visible again) now
   starts after the main element/fanart begin their fade — no longer
   perfectly simultaneous with them — while its DISAPPEARANCE (fading out
   when 'fanart-covering' is added, fanart about to show) still starts
   immediately, same as the main element, untouched. Scoped with
   :not(.fanart-covering) so this delay only ever applies to the transition
   INTO the visible state: when the class is removed, the element's new
   (target) style is this selector, so the delay applies to that fade-in;
   when the class is added, the new style is the .fanart-covering rule
   instead (no delay there), so fade-out timing is unaffected. Still the
   same 0.8s opacity transition/curve as the base class — only WHEN it
   starts differs, not how long or how it eases.
   v1.0.300 — delay increased 0.5s -> 1.3s, per explicit correction ("הכונה
   להעתק לעקב בחצי שניה את ההופעה שלו אחרת הוא מופיע על הפאנארט" — the
   copy needs to lag its own appearance by half a second AFTER the fanart
   is actually gone, not half a second from the same start point as
   everything else — a flat 0.5s from T=0 still overlapped the fanart's
   own 0.8s fade, so it visibly appeared on top of the still-fading fanart,
   exactly as caught live: "לפני שהוא נגמר נכנס ההעתק").
   v1.0.301 — delay increased 1.3s -> 1.8s, kept in sync with the main
   element's own delay doubling right above (0.5s -> 1s, per explicit
   report the stagger was too subtle to see live) — same formula as
   v1.0.300, just with the bigger 1s lag: 0.8 (fanart's own fade) + 1s
   (requested lag) = 1.8s.
   v1.0.302 — per explicit report ("...גם ההעתק" — the copy has the same
   too-fast problem as the main element, see that rule's own v1.0.302
   comment above): added the same dedicated transition-duration (0.8s ->
   1.3s), and re-derived the delay to keep this element starting exactly
   when the main element now finishes (design goal since v1.0.300 — never
   render on top of a still-fading previous layer): main now finishes at
   1 (its delay) + 1.3 (its new duration) = 2.3s, so this element's delay
   moves 1.8s -> 2.3s to match. New timeline from the moment the fanart
   starts leaving (T=0): fanart fades 0->0.8s; the main element now fades
   1->2.3s (v1.0.302, see its own comment above); this element (the copy)
   now fades 2.3->3.6s — starting right as the main element finishes,
   taking the same longer 1.3s to fade in as everything else. */
/* v1.0.302 — per explicit feedback in the emulator, this element (the
   copy/"ההעתק") no longer waits for the main element to finish; it now
   shares the SAME delay as the main element (see that rule's own comment
   above) so both start together, in sync — this pairing stays as of
   v1.0.303 below.
   v1.0.303 — shared delay raised 0.4s -> 0.8s along with the main element
   (see its own comment): this element now starts together with the main
   element, right as the fanart finishes fading out, with no overlap.
   v1.0.304 — shared delay raised again, 0.8s -> 1.8s, along with the main
   element (see its own v1.0.304 comment).
   v1.0.307 — REVERTED back to 0.8s along with the main element (see its
   own v1.0.307 comment for the full spec/timeline) — this element still
   shares the exact same delay as the main element, in sync, as
   established since v1.0.302.
   v1.0.308 — transition-delay REMOVED entirely, along with the main
   element (see its own v1.0.308 comment for the full reasoning) — this
   element's class toggle happens in the exact same JS call as the main
   element's, so it keeps starting together with it, now via the shared
   800ms setTimeout in app.js instead of a CSS delay. Duration (1.3s)
   unchanged. */
/* v1.0.355 — per explicit follow-up ("גם וגם... תנסה FADE IN לשידור בעוד
   חצי שניה"): the +0.5s originally intended for "the video's entrance"
   (v1.0.352) landed on #heroFullscreenGradient's own rule above, which is
   permanently display:none while this checkbox is on (see its own
   override further below) — completely inert, no visible effect. This is
   the rule that's actually active right now (gradient 2, the bridge/
   "העתק"): 1.3s -> 1.8s, same +0.5s intent, moved to where it's real.
   v1.0.358 — per explicit request ("תוסיף עוד שניה... גם גריינט 1 וגם
   ב2"), another +1s: 1.8s -> 2.8s. This is the rule that's actually
   visible. */
body.hero-fullscreen-gradient-on .hero-fullscreen-gradient-bridge:not(.fanart-covering) {
  transition-duration: 2.8s;
}
/* v1.0.316 — per explicit request/clarification, re-enabling this toggle
   but bringing back ONLY the original simple concept: the narrower red
   bridge/"double frame" (above), always visible while this checkbox is on
   — no synced crossfade with the fanart at all any more (app.js no longer
   toggles 'fanart-covering' on this element — see
   setHeroFullscreenGradientHiddenByFanart's own v1.0.316 comment), so it
   just sits at this toggle-on rule's base opacity:1 permanently once
   enabled. The MAIN element (#heroFullscreenGradient) stays hidden even
   while this checkbox is on — that whole timed system (v1.0.255-314)
   never fully stopped popping in, so only the always-static bridge comes
   back. #heroFullscreenGradient itself keeps its class-shared toggle-on
   rule above (harmless/inert since this override always wins via ID
   specificity), so no other code needs touching if this ever needs
   reverting — just delete this one rule. */
body.hero-fullscreen-gradient-on #heroFullscreenGradient {
  display: none !important;
}

/* v1.0.244 — .hero-video (a class on the old, separate #previewVideo
   element) is retired — there is no dedicated hero video element any
   more, see the .hero-media comment in index.html. Its job (contain-fit
   the picture manually in JS rather than trusting this device's buggy
   object-fit handling, box matches .hero-media's own 1252x473) is now
   #video.video-hero below, positioned by app.js's positionHeroVideo()
   instead of positionPreviewVideo(). Left as a comment, not deleted
   outright, since the reasoning (this device's browser mishandles
   object-fit/object-position) still applies and is worth keeping on
   record. */
.hero-logo {
  /* Switched from relying on the parent .hero-media flex container's
     align-items/justify-content:flex-start to explicit absolute
     positioning — this device's browser has already shown it doesn't
     reliably honor flex cross-axis alignment for an image (same issue seen
     with the small station logo, which needed a margin trick instead of
     align-self). Absolute positioning pinned to .hero-media's own top-left
     corner (.hero-media is itself position:absolute, so it's a valid
     containing block) does not depend on flex alignment at all, and
     guarantees this sits flush at the box's true top-left regardless of
     that bug. This static fallback logo is very likely what was actually
     on screen sitting ~25-30cm off, not the live video frame. */
  position: absolute;
  top: 0;
  left: 0;
  max-width: 80%;
  max-height: 80%;
  object-fit: contain;
  display: none;
}

.content-rows {
  /* Height + margin-top are set in px by JS (positionRowsBlock in app.js):
     height is measured to fit exactly 2 rows, and margin-top pushes that
     block down as far as it safely can within the fixed space left below
     the hero, without ever pushing row 2 out of view. A raw padding-top
     number here previously ran past that available space and made the
     whole block (and everything below it) disappear instead of moving. */
  overflow-y: auto;
  /* Right padding brought down to 56px — matches .hero-info's own right
     padding exactly (see that rule) so the first tile's right edge lines up
     with the channel name/title above it in one straight line, while still
     leaving real clearance from the icon rail. */
  padding: 20px 56px 30px 32px;
  scrollbar-width: none;
  /* Positioned + given a z-index so the row tiles' own (opaque) backgrounds
     always paint above .hero-media, which now intentionally extends past
     .hero's bottom edge behind this block (see .hero-media) — without
     this, a positioned element paints above a plain in-flow block by
     default regardless of source order, which would have let the enlarged
     video show through underneath the tiles. */
  position: relative;
  z-index: 2;
}
.content-rows::-webkit-scrollbar { display: none; }
/* Uniform gap between every row (1-2, 2-3, ...) — no per-row overrides.
   Trimmed from 24px to free real vertical room for pushing the rows block
   further down. */
/* Raised back up from 4px per request — needs real breathing room between
   the row's own tiles and the NEXT row's title below it, so that title
   doesn't look cramped/touching the tiles above it. */
.content-row { margin-bottom: 30px; }

/* Small gray down-chevron shown at the bottom-center of the screen when
   there are more groups below the 2 currently visible rows (i.e. the
   focused row isn't already one of the last two) — lets the user know they
   can go down for more without ever showing a 3rd row half-cut-off. */
.rows-more-indicator {
  /* top AND right are set in px by JS (positionRowsBlock in app.js): top
     measured just below the actual rendered bottom edge of the 2-row
     block, right aligned with the first tile's own right edge (rather than
     centered on screen) so it lines up with it instead of floating
     unrelated to any tile. */
  position: fixed;
  color: var(--muted);
  opacity: 0.85;
  display: none;
  pointer-events: none;
}
.rows-more-indicator.show { display: block; }
.row-title {
  font-size: 2.1rem;
  font-weight: 700;
  /* Trimmed from 18px to free more vertical room, same reason as
     .content-row's margin above. */
  /* Raised back up from 4px per request — the title was sitting too tight
     against its own row's tiles right below it. */
  /* v1.0.407 — 16px -> 6px, per his live follow-up right after the group-logo
     swap (v1.0.406) shipped ("תוריד את הכיתוב ב-10 פיקס") — wants this
     (now sometimes an image, not just text) snugged closer to its own row's
     tiles right below it. */
  margin: 0 0 6px 2px;
  /* v1.0.406 — explicit, rather than relying on dir=rtl inheritance (see
     .hero-now-desc's own v1.0.342 comment for why this app doesn't trust
     that implicitly on this TV's browser) — matters more now that this
     element's content can be an <img> (applyGroupLogo) instead of plain
     text, and image alignment needs a real text-align rule same as text
     does. */
  text-align: right;
}
/* v1.0.406 — NEW: once applyGroupLogo() (app.js) swaps this row's plain
   text for our own auto-generated clear-logo image, size it to roughly the
   same visual weight the 2.1rem text it replaced had — width follows the
   image's own aspect ratio (renderLogoBuffer's trim-to-content, same as
   every other auto-logo in this app), never stretched/cropped. */
.row-title-logo img {
  display: block;
  height: 2.6rem;
  width: auto;
  margin-right: 0;
}
.row-channels {
  display: flex;
  gap: 20px;
  overflow-x: auto;
  padding-bottom: 8px;
  scrollbar-width: none;
}
.row-channels::-webkit-scrollbar { display: none; }

/* Exactly 6 equal tiles visible per row width (gap already counted in), so
   the row never clips a 7th tile halfway at the screen edge — that partial
   last tile was what made rows look uneven/non-uniform tile to tile. A
   fixed pixel width doesn't divide the available width evenly; a
   percentage-of-container width always does, regardless of exact screen
   width. Rows with more channels still scroll — this only fixes how many
   whole tiles fit per screenful. */
.row-channel {
  flex: 0 0 calc((100% - 100px) / 6); /* 100px = 5 gaps × 20px, for 6 tiles */
  /* Without this, a flex item's default min-width is "auto", which lets its
     content's own natural (unwrapped) size override flex-basis — so a tile
     whose "now playing" text happened to be long (nowrap + ellipsis still
     counts toward min-content size) would silently force itself wider than
     the other tiles, exactly the "second tile is rectangular" symptom. */
  min-width: 0;
  /* Vertical gradient per request: solid black behind the text lines at
     the bottom (name + now-playing), fading upward into a lighter
     ("mouse") gray behind the logo near the top — instead of the flat
     --card near-black the whole tile used before. The first ~32% (the
     text zone's own rough share of the tile's height) stays pure black
     with no gradient at all; only the remaining stretch above that
     actually transitions, so the fade visibly starts around where the
     logo begins rather than smearing gray across the text itself. */
  background: linear-gradient(to top, #000000 0%, #000000 32%, #585a66 100%);
  border: 2px solid var(--border);
  border-radius: 16px;
  /* Trimmed further from 14px -> 10px -> 8px, same reason as the row/title
     margins above. v1.0.494 — bottom padding grown well past the other
     three sides (8px -> 22px), per explicit follow-up + reference photo
     (a competing app's tile: a clear gap between the description line and
     the progress bar under it): .tile-progress below is absolutely
     positioned and always sits flush at bottom:0 regardless of this
     padding, so growing ONLY padding-bottom just pushes the tile's own
     bottom edge (and the bar riding on it) further away from the flow
     content above (.now, the last text line) — carving out breathing room
     that didn't exist when it was a flat 8px on every side (the bar's own
     6px was eating almost all of that, leaving ~2px, which read as
     "touching"). Top/left/right stay 8px — unrelated to this request. */
  /* v1.0.505/506/507 grew this to 37px (+15px) for an earlier version of the
     "duplicate frame at the bottom" idea — REVERTED to the plain 22px
     briefly in v1.0.508, then re-grown by +17px for the rebuilt version of
     the same idea (.row-channel::before), then to +12px in v1.0.511.
     v1.0.512 — corrected again to +10px (see that rule's own comment), so
     this goes from 34px to 32px (22 + 10) to match. v1.0.514 briefly grew
     this 5px more (32 -> 37) to move the progress bar down without
     overlapping the strip below it — REVERTED in v1.0.515 (back to 32px,
     accepting the overlap), then RE-REVERTED in v1.0.516 back to 37px, per
     explicit follow-up ("תחזיר כמו שלפני הבניה הזאת" — undo v1.0.515,
     keeping only its width change, see .row-channel.has-progress::after's
     own v1.0.516 comment). */
  /* v1.0.532 — padding-bottom trimmed by 2px (37px -> 35px), per explicit
     follow-up ("תקטין בעוד שתי פיקסלים את הגובה של הקוביות"): this shrinks
     the whole tile's overall height by 2px (border-box height is
     content+padding+border, and this is the only free/empty space in that
     sum — nothing else here changed). The bottom-anchored bars
     (.row-channel::before, .row-channel.has-progress::after) keep their own
     bottom/height values unchanged; since they're positioned against this
     same padding box, shrinking it just moves their shared bottom edge
     (and the border right under it) 2px closer to the content above,
     without altering anything about their own relative position to each
     other. v1.0.533 — same technique, another 2px (35px -> 33px), per the
     same follow-up repeated verbatim.
     v1.0.539 — another 10px (33px -> 23px), per explicit follow-up
     ("המסגרת הלבנה... תקטין אותה בגובה שלה שמלמטה יהיה פחות באיזה 10
     פיקסלים") — applied to every tile the same way (not only the focused
     one), same as every earlier round of this exact shrink: a focused-only
     height difference is a known, previously-fixed bug (see
     .row-channel.focused's own v1.0.502 comment — it made that one tile
     visibly taller than its row neighbors). */
  padding: 8px 8px 23px;
  text-align: center;
  /* v1.0.505 first shipped a uniform box-shadow ring here, 5px out on
     EVERY side — corrected same-day per explicit follow-up ("לא להרחיב
     אותה או לעשות אותה יותר גדולה רק למטה... על אותו גודל בדיוק... רק
     בגובה"): he never wanted the frame bigger all around, only a taller
     "duplicate" of it at the BOTTOM — same width as always, unchanged on
     the sides/top. v1.0.506 tried this via an expanded clip-path
     (inset(0 0 -15px 0 round 16px), letting a ::before strip poke out
     below the tile) — per his own direct, repeated live report ("הפס...
     יוצאת החוצה... לא בדיוק מדוייקת... הפינה") and a verified isolated
     repro: NEGATIVE clip-path inset (any per-side value past a few px, not
     just this one) is a genuine rendering bug on this Chromium-family
     engine — it silently fails to round ONE of the two corners on the
     expanded side and can leave that edge slightly uncontained, regardless
     of top/left/right/bottom being symmetric or not. v1.0.507 — see the
     padding-bottom growth above instead: no more expanded clip-path, so
     this bug class no longer applies at all. */
  /* Needed so .fav-heart (absolute) positions itself relative to this
     tile's own corner, not the whole row-channels strip. */
  position: relative;
  /* v1.0.494 — HAD been overflow:hidden here (clipping every child,
     .tile-progress included, to this box's own 16px corner curve) so the
     bar's bottom corners would follow the exact same curve as the tile's
     border, per a Netflix reference photo. v1.0.498 first tried REMOVING
     this and giving the bar its own small independent radius instead —
     per explicit follow-up ("ביטלת את העיגול שלו... תואם לעיגול של
     הקוביה") that broke the exact thing this was for: the bar no longer
     reads as one seamless piece with the tile. v1.0.499 — reinstates that
     seamless look (see .tile-progress below: back to full-width, 0 radius
     of its own) but via clip-path instead of overflow:hidden. Same visual
     clip, different CSS mechanism — chosen because overflow:hidden here
     was very likely ALSO biting into .row-channel.focused's own outer
     box-shadow glow right at this same corner on this TV's browser engine
     (box-shadow is spec'd to ignore an element's own overflow OR
     clip-path, but this engine didn't honor that for overflow), which is
     what read as the focused blue frame looking "cut" on its right side.
     clip-path clips descendants the same way overflow:hidden does, but is
     a different rendering code path — the bet is that it clips the bar
     exactly as before without also touching the box-shadow. The tile's
     own background still respects border-radius on its own regardless
     (background-clip:border-box needs neither); every other absolutely-
     positioned child (.fav-heart, .tile-number, .cw-dot) sits well inset
     already, so nothing here depended on this clip's curve at all.
     v1.0.505/506 briefly expanded this to inset(0 0 -15px 0 round 16px) to
     make room for a ::before strip poking out below the tile — REVERTED in
     v1.0.507/508. v1.0.528 — expanded again, this time by only -1.5px on
     the bottom, to let the dark strip below grow 1.5px past the plain
     border-box boundary. v1.0.529 — REVERTED back to the plain,
     un-expanded value: the strip below shrank back down (per explicit
     follow-up, "תקטין... 2 פיקס... לא להגדיל") to where it no longer
     reaches past the border-box edge at all, so the expansion is unused
     again and there's no reason to carry the (small, unconfirmed-either-way
     on this TV) corner-rounding risk for nothing. */
  clip-path: inset(0 round 16px);
}
/* v1.0.505/506/507 had an earlier version of this same idea (a "duplicate"
   bottom strip, 15px tall) — reverted, then rebuilt here in v1.0.508 at the
   exact numbers given ("תגביה את המסגרת ב-17... למלא בצד התחתון שלה כולל
   העיגולים... ה-17 הנוספים זה הרחבת הגובה שלה"): 17px tall, corners rounded
   to match the tile's own 16px curve (he confirmed the 17 is only the
   height, not a different corner radius). Same safe technique as before:
   sits fully INSIDE the tile's own taller box (see padding-bottom above),
   left/right at -2px matching the border's own outer edge, so no expanded
   clip-path is needed and the earlier corner-rounding engine bug can't
   recur. */
.row-channel::before {
  content: '';
  position: absolute;
  left: -2px;
  right: -2px;
  /* v1.0.523 shortened this by 1px via height alone (17px -> 16px, bottom
     staying at -2px) — that trims from the TOP edge (nearest the light bar
     above), which shifted how much dark strip shows above the light bar
     and read as if the light bar itself had moved. v1.0.524 — per explicit
     follow-up ("לא רוצה שהאפור [הבהיר] יזוז... מלמטה יהיה חסר לי פיקסל"):
     the missing pixel needs to come off the BOTTOM edge instead, with the
     TOP edge staying exactly where it was (so nothing above visually
     shifts). bottom+height together control both edges — moving bottom
     -2px -> -1px (in by 1) while keeping height at 16px keeps the top edge
     fixed at the same spot as the original 17px/-2px (bottom+height = 15
     either way: -2+17 = -1+16), and the 1px now comes off the bottom
     instead. No change needed to the light gray bar at all. v1.0.525 —
     same method, one more pixel ("קצר אותו בעוד פיקסל באותה שיטה"): bottom
     -1px -> 0px, height 16px -> 15px (bottom+height still = 15, top edge
     still fixed in the exact same place). v1.0.526 — reversed direction,
     GROW by 1.5px ("אם מגדילים אותו... כלפי מטה... לא יזוז מהמיקום שהוא
     נמצא"): bottom 0px -> -1.5px, height 15px -> 16.5px. That fractional
     half-pixel value likely got rounded away by webOS's renderer — he
     reported no visible change even though the CSS itself was correct.
     v1.0.527 — switched to a whole 2px grow instead, per explicit
     follow-up, same top-anchored technique: bottom -1.5px -> -2px, height
     16.5px -> 17px (bottom+height still = 15, top edge unchanged). At this
     point -2px is exactly .row-channel's own clip-path boundary (the
     border-box edge) — going any further down gets silently clipped away,
     which is why the next ask (grow another 1.5px) needed the clip-path
     itself expanded first (see .row-channel's own v1.0.528 comment).
     v1.0.528 — another 1.5px grow, same top-anchored technique, now made
     visible by that clip-path expansion: bottom -2px -> -3.5px, height
     17px -> 18.5px (bottom+height still = 15, top edge still unchanged).
     v1.0.529 — reversed direction again, SHRINK by ~2px, per explicit
     follow-up ("תקטין... 2 פיקס... לא להגדיל"): rounded to a clean whole
     pair rather than the exact fractional 18.5-2=16.5, to avoid a repeat of
     the v1.0.526 issue where a .5px value silently failed to render on the
     TV — bottom -3.5px -> -1px, height 18.5px -> 16px (bottom+height still
     = 15, top edge still unchanged; this also brings it back within the
     plain, un-expanded clip-path boundary, see .row-channel's own v1.0.529
     comment). v1.0.530 — shrink another ~2px, per explicit follow-up
     ("תקטין בעוד 1.5 פיקס" — rounded to a whole 2px again, same reasoning
     as v1.0.529, per his own confirmed choice): bottom -1px -> 1px, height
     16px -> 14px (bottom+height still = 15, top edge still unchanged).
     v1.0.531 — that positive bottom value (1px) turned out to expose a real
     bug in the whole "top-anchored" method: bottom/left/right/top all
     resolve against the PADDING box, so once bottom goes past 0 (positive),
     the strip's own bottom edge sits ABOVE the padding-box's bottom edge —
     leaving that 1px sliver showing the tile's raw background (solid black
     there) between the strip and the real border below, instead of the
     strip reaching all the way down to it like every earlier version did
     (all of v1.0.523-529 kept bottom at 0 or negative, always flush with or
     past that edge — this is the first version to cross into positive
     territory, which is why the gap never showed up before). Per explicit
     report ("נוצר רווח שחור"): fixed by bringing bottom back to 0 (flush,
     no gap) and keeping height at 14px — this necessarily breaks the strict
     bottom+height=15 invariant by 1px (top edge now sits 1px lower than
     before), since shrinking the tile itself (padding-bottom or overall
     height) can't fix this at all — the gap is purely a function of this
     element's own bottom offset, not the tile's size around it. */
  bottom: 0;
  /* v1.0.545 — 14px -> 8px, per explicit follow-up ("האפור עכבר נשאר אותו
     גובה, לא הקטנת אותו... תקטין אותו גם שיהיה כמו הפס האפור הבהיר אותו
     דבר"): this skirt sits BEHIND .row-channel.has-progress::after and is
     the exact same color (var(--border)) as that bar's own watched
     portion, so shrinking the bar's own height (v1.0.544, 10px -> 7px)
     never visually changed the watched/"mouse-gray" side at all — this
     skirt, unchanged at 14px, was still showing through above the bar and
     reading as the full old height. Setting this to 8px puts its own top
     edge (bottom:0 + height:8 = 8) exactly level with the progress bar's
     own top edge (bottom:1px + height:7px = 8) so both the watched and
     unwatched portions now visually match. This does NOT touch
     .row-channel's own padding/height — purely this decorative,
     absolutely-positioned strip, per his explicit "מבלי לפגוע בגובה של
     הקוביות" follow-up.
     v1.0.546 — 8px -> 9px, per direct follow-up live report ("הגובה של
     האפור עכבר קטן יותר מהאפור הבהיר בפיקסל אחד... צריך לגדול בפיקס 1
     כלפי מעלה"): grows this skirt's own top edge 1px higher (bottom stays
     0, so top edge moves from y=8 to y=9) — trusting his direct visual
     read on the real TV over the theoretical calc in the comment above. */
  height: 9px;
  background: var(--border);
  /* v1.0.508 briefly gave the bottom-left corner its own hardcoded 15px
     radius (1px off from the rest) — corrected in v1.0.510, per explicit
     follow-up: he never wanted a made-up number here at all, he wants this
     strip's corners to always just MATCH .row-channel's own existing
     border-radius (16px, set at the top of this rule block) — same angle
     as the tile's real frame, not a separately-picked value. Both corners
     16px again, same as top-left/top-right on this same tile. */
  border-radius: 0 0 16px 16px;
}
.row-channel.focused::before {
  /* v1.0.531 — was var(--accent) (purple/blue), per explicit follow-up
     ("הפוקוס הכחול תחליף בצבע לבן"): this tile's focus color is now white,
     everywhere it shows on this tile (border, this strip, and the watched
     portion of the progress bar below) — var(--accent) itself is untouched
     everywhere else in the app, only this tile's own focused rules were
     switched to a literal #fff.
     v1.0.540 — #fff -> var(--border), per explicit follow-up ("עדיין נותר
     לה התחתית בצבע לבן... הוא לא אפור עכבר כמו השאר"): this strip now
     matches an unfocused tile's own strip color exactly, same as the
     border change right above. Focus is still visible via the progress
     bar's watched-portion color (still #fff on focus, untouched — he named
     this strip specifically, not that).
     v1.0.559 — per explicit follow-up right after v1.0.558 ("אתה צריך לבטל
     גם את העובי התחתון מהאפור עכבר הזה לא רק את האפור"): v1.0.558 only hid
     the progress bar itself (.row-channel.focused.has-progress::after) on
     the focused tile — this separate skirt strip sits BEHIND that bar and
     was untouched, so its own 9px-tall solid band was still showing at the
     bottom of the focused tile even with the bar gone. Hidden here too,
     ONLY on the focused tile — the unfocused .row-channel::before rule
     above (used by every other tile) is untouched, so non-focused tiles
     keep this strip exactly as before. */
  display: none;
  background: var(--border);
}
.row-channel.focused {
  /* v1.0.555 — the entire box-shadow "ring" idea (v1.0.546 through v1.0.554)
     is ABANDONED here, per explicit follow-up ("תמחק גם את המסגרת הלבנה...
     תעשה את הפוקוס שיהיה עשוי מהמסגרת האפורה של הקוביה רק שיהיה בצבע
     כחול... כמו שהיה בהתחלה" — delete the white frame too, make focus just
     this tile's own existing gray border, but in blue, like it was at the
     start). Back to the simple, pre-v1.0.546 mechanism: this tile's normal
     2px border (see .row-channel above), color-only override to blue. No
     box-shadow, no clip-path override, no z-index — none of those are
     needed once there's no shadow bleeding past the border to clip, get
     covered by a neighbor, or need promoting above one. This sidesteps
     every real bug hit along the way (clip-path silently clipping the
     shadow, v1.0.551; 4-stacked-shadows not blending into one smooth
     outline, v1.0.552; the shadow painting behind the next sibling tile,
     v1.0.553) by simply not having a shadow at all — the full history of
     that saga is preserved in this file's git-tracked versions
     (style-1.0.546.css through style-1.0.554.css) if the ring idea ever
     comes back with a different technique (e.g. a real second element). */
  border-color: var(--ff);
  /* v1.0.557 — per explicit follow-up ("תוריד מהקובייה הראשונה [=הממוקדת]
     2 פיקסלים בגובה... בצד התחתון, הגובה יירד כלפי מעלה ב-2 פיקס"): only
     this tile's own padding-bottom shrinks, 23px -> 21px (see .row-channel's
     own base padding above) — border-box height is content+padding+border,
     and padding-bottom is the one dimension here that's pure empty space,
     so trimming it just moves this tile's own bottom edge (and everything
     anchored to it: border, ::before skirt, ::after progress bar — all
     positioned against this same padding box) 2px closer to the content
     above, with the top edge untouched. Note this is the mirror image of
     the known v1.0.502 bug (a THICKER border made the focused tile taller
     than its row neighbors) — here the focused tile becomes 2px SHORTER
     than its neighbors instead, on purpose, per this explicit request. */
  padding-bottom: 21px;
}
/* REMOVED per explicit follow-up request: the small filled circle that used
   to appear on a focused grid tile's bottom-right corner (added earlier,
   modeled on "Televizio") turned out to be confusable with the unrelated
   blue position-thumb circle on the full-screen player's seek bar (see
   .player-progress-fill::after below) — the request was for that thumb
   circle to exist ONLY in the full-screen player, never on the home screen
   grid. The border+glow on .row-channel.focused above is kept as the only
   focus indicator here. */
/* Favorite-marker heart, top-LEFT corner of every tile (see
   renderContentRows() in app.js) — plain white outline normally, filled
   solid red once that channel is marked a favorite (see the 'enter-hold'
   handling in ScreenHandlers['screen-grid'], triggered by holding Enter
   for ~2s on a focused tile). Never itself focusable/clickable — purely a
   status indicator, toggled only via the long-press. Moved from the
   top-right (its original spot, now the channel number instead — see
   .tile-number below) per request. */
.fav-heart {
  position: absolute;
  top: 6px;
  left: 6px;
  width: 24px;
  height: 24px;
  pointer-events: none;
  z-index: 1;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,0.6));
}
.fav-heart path { fill: none; stroke: #fff; stroke-width: 1.8; }
.fav-heart.active path { fill: var(--danger); stroke: var(--danger); }
/* v1.0.416 — a continue-watching tile never has a .fav-heart of its own
   (favoriting doesn't apply to one specific past airing) — this sits in
   that exact same corner instead, per explicit request ("במקום הלב הזה
   שיש בכל תחנה... עיגול אדום קטן... ועיגול ירוק"). Just a plain colored
   dot, nothing else — see cwDotHtml() in app.js. */
.cw-dot {
  position: absolute;
  top: 10px;
  left: 10px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  z-index: 1;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,0.6));
}
.cw-dot.green { background: var(--accent2); }
.cw-dot.red { background: var(--danger); }
/* Channel number, top-right corner of every tile — the heart's old spot.
   Sequential position in the overall channel list (state.channelNumbers in
   app.js), starting at 1, same number the hero preview and the digit-entry
   overlay already use elsewhere. Plain text rather than a badge/circle, with
   a drop shadow for legibility since this corner sits on the gradient's
   lighter-gray end (see .row-channel background above). */
.tile-number {
  position: absolute;
  top: 6px;
  right: 8px;
  font-size: 1.1rem;
  font-weight: 700;
  color: #fff;
  pointer-events: none;
  z-index: 1;
  text-shadow: 0 1px 3px rgba(0,0,0,0.8);
}

/* "Next programme" line, under the progress bar — see updateHeroProgress()
   in app.js. Hidden entirely when the focused channel has no known next
   programme. */
.hero-next {
  display: none;
  font-size: 1.3rem;
  color: var(--muted);
  /* ~2cm gap from the progress bar right above it, per request. */
  margin-top: 27px;
  /* Tried wrapping to a second line (like .hero-now-title) but that
     caused its own problems here, per request — reverted to a single
     line that's simply allowed to run past the box's right-hand... err,
     left-hand edge into the empty space there, rather than either
     wrapping or getting cut off with "...". No overflow:hidden and no
     text-overflow, so the browser never clips it. */
  white-space: nowrap;
  overflow: visible;
}
/* Small yellow star before the "next programme" text, per request — a
   filled 5-point star (not an outline/favorite-style star), sitting
   right before the text it introduces since it comes first in the DOM
   (= rightmost in this RTL row). */
.hero-next-star {
  width: 16px;
  height: 16px;
  fill: #ffc720;
  margin-left: 6px;
  vertical-align: -2px;
}
.hero-next.show { display: block; }
.hero-next b { color: var(--text); font-weight: 600; }

/* Small "נוסף/הוצא ממועדפים" confirmation toast — shown briefly after a
   long-press on Enter toggles a channel's favorite status (see
   toggleChannelFavorite() / showFavToast() in app.js). Purely
   informational, centered near the top so it never covers the tile the
   person is actually looking at. */
.fav-toast {
  position: fixed;
  top: 40px;
  left: 50%;
  transform: translateX(-50%) translateY(-12px);
  background: var(--card2);
  border: 2px solid var(--accent);
  color: var(--text);
  font-size: 1.4rem;
  font-weight: 600;
  padding: 14px 32px;
  border-radius: 14px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease, transform 0.2s ease;
  z-index: 20;
}
.fav-toast.show {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

/* Shown per explicit request when a channel's stream genuinely fails (the
   video element itself errors out — e.g. a channel that's temporarily off
   the air) — centered on screen so it's impossible to miss, unlike the
   old approach of just leaving a stuck black picture behind. See
   Player.onStreamError/showStreamErrorAndReturnHome() in app.js: shown for
   a couple of seconds, then the player stops and the app returns to the
   home screen on its own, no button press needed. */
.stream-error-msg {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: rgba(15,17,21,0.92);
  border: 2px solid var(--danger);
  color: var(--text);
  padding: 26px 46px;
  border-radius: 16px;
  text-align: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease;
  z-index: 35;
}
.stream-error-msg.show { opacity: 1; }
.stream-error-msg-main { font-size: 1.6rem; font-weight: 600; }
/* Per explicit request: a second, smaller line telling the viewer the blue
   remote button removes this channel from the list (temporarily — see
   deleteChannelTemporarily() in app.js) right while this message is up,
   in case the same channel keeps failing. Only shown when that action is
   actually available (see showStreamErrorAndReturnHome) — blank/collapsed
   otherwise. */
.stream-error-msg-hint {
  margin-top: 10px;
  font-size: 1.05rem;
  font-weight: 500;
  color: var(--muted);
}
.stream-error-msg-hint:empty { display: none; }

/* Typed channel-number entry (digit keys on the remote — see
   handleDigitInput() in app.js). Centered, big and simple while digits are
   still being typed; a smaller red "not found" message reuses the same
   box when the typed number doesn't match any channel. */
.digit-overlay {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: rgba(15,17,21,0.9);
  border: 2px solid var(--accent);
  color: var(--text);
  font-size: 3.2rem;
  font-weight: 700;
  padding: 22px 50px;
  border-radius: 16px;
  letter-spacing: 6px;
  display: none;
  z-index: 30;
  white-space: nowrap;
}
.digit-overlay.show { display: block; }
.digit-overlay.error {
  font-size: 1.5rem;
  letter-spacing: normal;
  border-color: var(--danger);
  color: var(--danger);
}
/* Home screen: relocated into the icon rail's own gap between the exit
   icon and the settings icon — same slot/size as .row-flash-indicator
   above, just white instead of blue (see that rule's comment; the two
   never show at once). Position is computed from the rail's own fixed
   layout on the fixed 1920x1080 canvas: a 130px rail flush against the
   right edge (padding 40/56/30/0), a 68px logo, five 74px menu icons
   with 22px gaps (ending with "exit"), and a 76px settings icon pinned
   to the bottom via .icon-rail-menu's auto top/bottom margins — that
   leaves an exactly centered 204px-tall empty gap between exit and
   settings whose midpoint sits at x=1827 / y=872. */
#screen-grid.active ~ .digit-overlay {
  top: 872px;
  left: 1827px;
  transform: translate(-50%, -50%);
  font-size: 1.3rem;
  padding: 8px 18px;
  border-radius: 12px;
  letter-spacing: 3px;
}
#screen-grid.active ~ .digit-overlay.error {
  font-size: 1.1rem;
  letter-spacing: normal;
}
/* Fullscreen player: a rounded card sitting just below the channel-info
   OSD card (.player-osd, top:56px, up to ~210px tall) in the top-right,
   instead of dead-center over the video. */
#screen-player.active ~ .digit-overlay {
  top: 250px;
  right: 0;
  left: auto;
  transform: none;
  width: 400px;
  padding: 20px 30px;
  font-size: 2.1rem;
  letter-spacing: 5px;
  border-radius: 0;
  border-top-left-radius: 18px;
  border-bottom-left-radius: 18px;
}
#screen-player.active ~ .digit-overlay.error {
  font-size: 1.4rem;
  letter-spacing: normal;
}
.row-channel img {
  /* Reverted back to 85px per explicit follow-up correction — the "double
     the logos" request turned out to mean only the mini-logo next to the
     channel name in the home-screen hero preview (see .hero-mini-logo
     below), not these grid-tile logos, which stay exactly as they were.
     Fixed SQUARE box: object-fit:contain + this being an inline element
     inside .row-channel's text-align:center centers it horizontally within
     the (wider) card automatically, and it can never grow past this fixed
     box no matter what the source image's own shape is. */
  display: inline-block;
  width: 85px;
  height: 85px;
  object-fit: contain;
  margin-bottom: 5px;
  /* Was a near-black fill (#0b0d12) — that was fine while the tile's own
     background was flat near-black too (see .row-channel above), but now
     that the tile has its own black-to-gray gradient, a hardcoded near-
     black box here fought against it: a transparent logo's empty margins
     showed this instead of the tile's gradient, which also made a dark-
     colored mark (plenty of official channel logos are just black text)
     nearly invisible against it. Transparent lets the tile's own gradient
     show straight through instead — both fixed at once. */
  background: transparent;
  border-radius: 10px;
}
.row-channel .name {
  font-size: 1.15rem;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.row-channel .now {
  font-size: 0.9rem;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-top: 6px;
}
/* ============================================================
   LEGACY (v1.0.492 – v1.0.502) — the progress bar used to be its own
   pair of absolutely-positioned divs (.tile-progress track +
   .tile-progress-fill) sitting on top of the tile, inset a few px from
   each edge, relying on .row-channel's clip-path for corner-matching.
   That inset was never fully right on this TV's browser engine (a whole
   chain of builds, v1.0.497 through v1.0.502, kept nudging it 1-2.5px
   without it ever reading as truly seamless with the tile's own border).
   v1.0.503 REPLACES this entire mechanism — see the new
   .row-channel.has-progress::after rules right below — with a single
   pseudo-element painted directly on the tile's own bottom edge, so
   there is no separate element to inset/align at all: it's flush
   edge-to-edge like the border itself, clipped to the same rounded
   corner automatically. Kept here, fully inert (commented out), only so
   we can revert instantly if the new approach doesn't work out — do NOT
   delete without asking first.

.tile-progress {
  position: absolute;
  left: 2.5px;
  right: 2.5px;
  bottom: 0;
  height: 6px;
  border-radius: 0;
  background: #A3AABE;
  overflow: hidden;
}
.tile-progress-fill {
  position: absolute;
  top: 0;
  right: 0;
  height: 100%;
  width: 0%;
  background: #585a66;
  border-radius: 0;
}
.row-channel.focused .tile-progress-fill {
  background: #2F80FF;
}
   ============================================================ */

/* v1.0.503 — NEW progress-bar mechanism, per explicit request ("בוא נעשה
   שהמסגרת הזו בצד התחתון שלה תהיה גבוהה קצת... ניקח את המידע מהפס מידע
   ונתחיל להפוך את הפס הזה לאפור הבהיר... לא נוסיף עוד אלמנט"): instead
   of a separate bar element sitting ON the tile, the tile's own bottom
   edge itself IS the bar now — one single ::after pseudo-element (no
   extra HTML, nothing for app.js to size/position independently), flush
   left:0/right:0/bottom:0 so .row-channel's existing
   clip-path: inset(0 round 16px) clips it to the exact same rounded
   corner as the border, automatically, with no inset math at all.
   Height is a uniform 8px on EVERY tile regardless of focus state
   (never conditional on .focused — see .row-channel.focused's own
   comment on the v1.0.501/502 height-mismatch bug that came from a
   focus-only border-width change; not repeating that here).
   Color is a single linear-gradient with a hard stop at the watched
   percentage (fed in as the --w custom property, set in app.js by
   applyTileProgress() — see that function). Per direct follow-up report
   ("הצבע של המסגרת והפס... זה לא אותו אפור עכבר"): the WATCHED portion
   (unfocused) now matches the tile's own border color (var(--border))
   exactly instead of the old #585a66 (that was always the tile's own
   BACKGROUND gradient's top color, never the border's — a coincidence,
   not a deliberate match) — so the part already watched recedes into the
   border/frame itself instead of standing out as its own different gray,
   and the light-gray REMAINING portion (#A3AABE, unchanged) is the one
   thing that visually stands out, same "how much is left" read as always.
   The watched portion switches to var(--accent) (the same purple as the
   tile's own focus border and the hero progress bar above) when focused —
   no blue anywhere on this screen anymore.
   left/right/bottom are inset by NEGATIVE 2px (not 0) and height is 2px
   taller than the visual 8px thickness — per direct follow-up report +a
   verified render (see /tmp render notes): position:absolute's bottom/
   left/right resolve against the PADDING box of .row-channel, which sits
   INSIDE its 2px border — so at 0/0/0 this pseudo-element never actually
   reached the tile's own real border-bottom, leaving that original 2px
   strip (and, worse, its own independently-square corners) fully exposed
   as a separate ring just outside/around this element, most visible right
   at the rounded corners. Extending by exactly the border-width (2px)
   pushes this element's own edges out to the border-BOX edge instead — the
   same edge .row-channel's clip-path (inset(0 round 16px), which clips
   against the border-box by default) already rounds — so it now fully
   covers the border-bottom and correctly fills the rounded corners too,
   with no separate ring left showing. */
.row-channel.has-progress::after {
  content: '';
  position: absolute;
  /* v1.0.512 briefly shifted this 2px right (left:0/right:-4px) as an ad-hoc
     nudge — SUPERSEDED in v1.0.513 by an exact calculation, per explicit
     follow-up ("263 זה אורך הפס... אמור להתחיל מהפנים של המסגרת של הקוביה
     עד הסוף"): the tile's own inner width (padding-box edge to edge, i.e.
     the true distance between the INSIDE faces of the left/right 2px
     border) is exactly 263px at this tile's real rendered size (1702px row
     width, 6 tiles, 20px gaps → 267px outer tile → minus 2px border each
     side). position:absolute here resolves against that same padding box
     by default, so left:0/right:0 (flush, no offset at all) already lands
     exactly on those two inner border faces — 263px, no overlap onto the
     border itself, unlike .row-channel::before below which deliberately
     still reaches the outer border edge via -2px. */
  /* v1.0.515 shortened this by 10px (263px -> 253px, right: 0 -> 10px).
     v1.0.519 reverted that, back to flush left:0/right:0, full 263px width.
     v1.0.520 shortened by 10px again (right: 0 -> 10) then shifted the
     whole bar 4px right (left 0->4, right 10->6). v1.0.521 undid the 4px
     shift, back to left:0/right:10. v1.0.522 — per explicit follow-up
     ("קח אותו שתי פיקס ימינה... רק האפור בהיר"): shifted 2px right again
     (left 0->2, right 10->8). v1.0.534 — per explicit follow-up ("תנסה
     אולי לקחת את האפור הבהיר שלוש פיקסלים ימינה"), shifted another 3px
     right, same method (left +3, right -3): left 2->5, right 8->5.
     v1.0.543 briefly narrowed this another 8px each side (left 5->13,
     right 5->13) — REVERTED same-session in v1.0.544 per immediate
     follow-up ("לא, תחזיר את מה שעשית... אתה הקטנת את האורך, אני התכוונתי
     להקטין את הרוחב... של האפור עכבר והאפור בהיר... רק להקטין את הגובה
     שלהם"): he actually meant the bar's own thickness (height, below),
     not how far left-right it spans — back to 5px/5px. */
  left: 5px;
  right: 5px;
  /* v1.0.507 moved this to 13px (+15) to compensate for .row-channel's
     taller padding-bottom, briefly reverted to -2px in v1.0.508 with that
     growth, then to 15px (+17), then 10px (+12) in v1.0.511, then 8px (+10)
     in v1.0.512. v1.0.515 moved it down another 5px (8px -> 3px) WITHOUT
     growing the box, accepting overlap with the strip below — v1.0.516
     reverted that one part back to 8px. v1.0.518 — moved down 8px more
     (8px -> 0px), per explicit follow-up ("תוריד כלפי מטה את האפור בהיר
     ב-8 פיקס") — this overlaps .row-channel::before below by 8 of its 10px
     height, on purpose, same as the earlier accepted overlap. v1.0.520 —
     pushed UP 5px (0px -> 5px). v1.0.521 — pushed down 4px (5px -> 1px).
     v1.0.522 — pushed UP another 2px (1px -> 3px), per explicit follow-up
     ("תעלה אותו עוד שתי פיקס למעלה"). v1.0.534 — pushed DOWN 2px (3px ->
     1px), per the same follow-up ("ותוריד אותם למטה בשתי פיקסלים"). */
  bottom: 1px;
  /* v1.0.544 — 10px -> 7px, per explicit follow-up ("להקטין את... הגובה
     שלהם" — of the gray/light-gray progress bar itself, NOT the tile's own
     height, which is untouched): bottom stays anchored at 1px, so the bar
     just gets visually thinner, its top edge sitting 3px lower/closer to
     bottom than before. */
  height: 7px;
  /* v1.0.511 — the bottom-LEFT corner got its own border-radius (the other
     three square). v1.0.513 added a conditional (zero-progress class,
     toggled from app.js) to also round the right corner ONLY when nothing
     had been watched yet. v1.0.517 — SIMPLIFIED, per explicit repeated
     follow-up ("ביקשתי ממך לעשות לאפור בצד ימין גם כן עיגול תואם"): both
     corners just round to the tile's own 16px curve unconditionally now,
     always, regardless of progress — no more special-casing pct===0.
     v1.0.545 — 16px -> 7px, per explicit follow-up ("העיגולים בפס האפור
     קיבלו כאילו זווית אחרת... לבדוק שזה יהיה תואם לעיגול של הקוביות"): the
     16px value was calibrated back when this bar was 10px tall (already
     clamped by the browser to its own height on the vertical axis, since a
     radius can never exceed half an element's own height/width); now that
     the bar is only 7px tall (v1.0.544), that same 16px got clamped even
     harder, producing a visibly flatter/more stretched-looking curve than
     before instead of a clean matching round. Set to 7px — a full,
     evenly-proportioned semicircular end for a 7px-tall bar, same on both
     corners (this one shorthand always applies equally to bottom-left and
     bottom-right). Needs his own visual check on the real TV to confirm it
     now reads as matching — unlike .row-channel::before's own 16px (still
     unchanged, see that rule), this bar sits inset 5px from the tile's real
     edges so it can never be pixel-identical to the tile's own corner, only
     proportioned to look consistent with it.
     v1.0.546 — 7px -> 11px, per his own correct diagnosis from a real
     close-up photo ("תראה שהזווית לא מדויקת... זה מערך שונה לגמרי כי הפס
     נכנס פנימה 5 פיקסל"): he's right — this bar is inset 5px from the
     tile's own edges (left/right above), so its corner is geometrically a
     completely separate, smaller arc at a different (x,y) than the tile's
     real 16px corner; no radius value here can make the two curves
     literally coincide. 11px is the standard "nested rounded corner"
     approximation — outer radius (16) minus the inset (5) — which reads as
     visually consistent/concentric with the tile's own corner, closer than
     the previous 7px (chosen only to fit this bar's own 7px height, not
     for concentricity with the tile). Confirmed acceptable as "a better
     attempt, still not perfect" — a truly pixel-identical match would
     require removing the 5px inset entirely (flush to the real edges, like
     .row-channel::before does), which he declined for now: position stays
     as is. */
  border-radius: 0 0 11px 11px;
  /* v1.0.531 — the remaining (light gray) portion darkened slightly, from
     #A3AABE, per explicit follow-up ("האפור בהיר... תחליף לאפור יותר כהה
     קצת") — same change in both this rule and the .focused variant below,
     since both share that exact color for the unwatched portion.
     v1.0.537 — darkened again, #8B91A2 -> #767B89, per explicit follow-up
     ("עוד קצת יותר כהה").
     v1.0.556 briefly tried trimming each color's edges inward with
     transparent gaps (see style-1.0.556.css if this ever needs revisiting)
     — REVERTED same-night, per direct follow-up ("תחזיר את מה שהיה רגע"):
     a plain gradient gap has a flat/square cut where the color ends, not a
     rounded one, so it looked "chopped off" instead of like a rounded pill
     — he wants each color segment to look like its own independently
     rounded capsule (rounded whether it's flush against the bar's real
     corner or floating with a gap before it), which a single gradient
     background fundamentally can't do (gradients only do straight color
     stops, never rounded internal edges) — that needs the bar rebuilt as
     two real separate rounded elements, a bigger change than a CSS tweak,
     intentionally not done yet pending his fuller explanation. Back to the
     plain two-stop gradient, no gaps, exactly as before v1.0.556. */
  background: linear-gradient(to left, var(--border) 0, var(--border) var(--w, 0%), #767B89 var(--w, 0%), #767B89 100%);
}
.row-channel.focused.has-progress::after {
  /* v1.0.531 — watched portion var(--accent) (purple/blue) -> #fff, per the
     same "focus color to white" follow-up as .row-channel.focused::before
     above; remaining portion darkened to #8B91A2, same as the unfocused
     rule above. v1.0.537 — darkened again to #767B89, same as above.
     v1.0.542 — #fff -> var(--border), per explicit follow-up ("פס התקדמות
     ששמנו לו בצד ימין הוא לבן, אני רוצה שזה יהיה כמו כולם אפור עכבר ואפור
     בהיר"): watched portion now matches the unfocused tile's own progress
     bar exactly (identical to .row-channel.has-progress::after above) — no
     white left anywhere on this tile in any state. Focus is now indicated
     instead by .row-channel.focused's own border-color, see that rule.
     v1.0.556 tried, then reverted the same night — see the unfocused rule's
     own v1.0.556 comment above for the full reasoning; kept identical to
     that rule as always.
     v1.0.558 — per explicit follow-up ("תבטל את הפס התקדמות הזה בקוביה
     הראשונה [=הממוקדת]"): hidden entirely, ONLY on the focused tile —
     unfocused tiles keep showing their progress bar as always, this override
     just turns it off for whichever tile currently has focus. The dark
     skirt (.row-channel.focused::before, a separate element sitting behind
     this one) is untouched and still shows. */
  display: none;
  background: linear-gradient(to left, var(--border) 0, var(--border) var(--w, 0%), #767B89 var(--w, 0%), #767B89 100%);
}

/* --- EPG --- */
#epgMsg { padding: 0 40px; margin: 10px 0 0; }
.epg-list {
  padding: 20px 40px;
  height: calc(100% - 100px);
  overflow-y: auto;
}
/* v1.0.421 — row height/text bumped up per explicit request ("ובמקום להגביה
   את השורות בארכיון תגדיל את גודל השורה של השידור החי והשורה של הארכיון
   וגם את הכתב שלהם תגדיל קצת"): padding 18px->24px (taller rows — this is
   the SAME shared class both the "שידור חי" row and every "ארכיון" row use,
   in both the old screen-epg and the new player list panel, so both grow
   together automatically) and font-size 1.2rem->1.35rem/.time's own
   1rem->1.1rem (title/time text both a bit bigger). Chosen instead of the
   earlier "move the whole block up 20px" idea (see .player-list-panel's
   own padding-top, reverted back to 40px). */
.epg-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: var(--card);
  border: 2px solid var(--border);
  border-radius: 12px;
  padding: 24px 24px;
  margin-bottom: 14px;
  font-size: 1.35rem;
  /* v1.0.443 — matches .player-number-tile's own line-height: 26px change:
     pins both elements to the same fixed-px line-height so their total
     height is identical by construction, not by engine-dependent rounding
     of each element's own font-size-relative "normal" default. */
  line-height: 28.5px;
}
/* v1.0.451 — CHANGED, same "blue frame -> light gray" swap — see
   .player-number-tile.focused's own v1.0.451 comment for the full reasoning.
   v1.0.465 — border-color var(--border) -> #fff, per explicit request
   ("ובשורות שים להם גם כן מסגרת לבנה כמו ששמנו במבנה חמש"): matches
   .player-number-tile.focused's own white border exactly.
   v1.0.469 — border-color #fff -> var(--border), per explicit report
   ("המסגרת הלבנה על השורות של הארכיון תמיד נשארת מסגרת לבנה גם אם עוברים
   למבנה חמש גם אם עוברים לשורה שמונה זה לא אמור להיות ככה"): .epg-row is
   shared between the old screen-epg list (#epgList.epg-list) and this
   panel's own rows (#playerListRows.player-list-rows) — .focused itself
   never gets cleared just because focus moves to a DIFFERENT zone within
   the panel (same "class persists, only the white border should react"
   situation .structure5-active/.row8-active above already solve), so the
   white border kept showing here even after leaving 'rows'. Reverted the
   base rule back to the plain gray border and moved the white border to a
   new zone-scoped rule right below (.rows-active, toggled in
   updatePlayerListFocusUI() alongside structure5-active/row8-active) —
   the dim background wash stays unconditional, matching the established
   two-tier pattern. screen-epg's own rows are untouched by this (no
   .rows-active ancestor there), simply reverting to their pre-v1.0.465
   plain-gray-border look, which was never part of this report anyway. */
.epg-row.focused {
  border-color: var(--border);
  background: rgba(255,255,255,0.16);
  box-shadow: none;
}
.player-list-rows.rows-active .epg-row.focused { border-color: #fff; }
.epg-row .title { font-weight: 600; }
.epg-row .time { color: var(--muted); font-size: 1.1rem; }
.epg-row.live .title { color: var(--accent2); }
/* v1.0.232 — the station-name marker on the browse panel's "now airing" row
   (replaces a satellite icon, per explicit request — see
   renderPlayerListRows in app.js). White specifically, so it stands out
   from the rest of the (accent-colored) title text next to it. */
.epg-live-station-name { color: #fff; font-weight: 700; }
/* Swapped per request: archive (already-aired, playable) rows in blue,
   upcoming ("בהמשך", not yet playable) rows in the same soft muted
   gray-white as the station name on the home screen (.hero-name).
   v1.0.436 — CHANGED, per explicit follow-up request: archive row title
   text now plain white instead of the accent purple/blue — .epg-row itself
   is shared app-wide (old screen-epg + the new player-list-rows panel, per
   the comment further below), so this applies everywhere 'past' rows show,
   same as before. */
.epg-row.past .title { color: #fff; }
.epg-row.next .title { color: var(--muted); }
/* v1.0.437 — NEW, per explicit follow-up request ("השעות בשורות הארכיון...
   צריך להגדיל את מה שיש בצד שמאל וגם שיהיה בצבע לבן"): the time text (the
   row's left-side content, per this RTL layout — .time, inside
   .epg-row-meta) in archive/'past' rows specifically, bigger (1.1rem ->
   1.3rem, closer to the row's own 1.35rem base) and white instead of the
   generic muted-gray every other row kind (live/next) still uses via the
   shared .epg-row .time rule above — scoped the same way .epg-row.past
   .title is, right above, rather than touching that shared rule. */
.epg-row.past .time { color: #fff; font-size: 1.3rem; }
/* v1.0.413 — per explicit request: a 'past' row bookmarked to "continue
   watching" (see toggleArchiveBookmark in app.js) gets the whole row tinted
   green, not just its icon — kept as a background tint (not a solid fill)
   so .title/.time stay legible over it, same translucent-tint convention
   the drag-over states elsewhere in this app already use. Combines fine
   with .epg-row.focused's own border/box-shadow (different properties). */
.epg-row.bookmarked { background: rgba(34,197,94,0.18); }
/* Wraps the play-triangle + time together for playable (archive) rows, so
   the icon sits right next to the time instead of at the row's outer
   edge. Replaces a bare .time as the row's second flex child — .epg-row
   .time still applies since it's a descendant selector. */
.epg-row-meta { display: flex; align-items: center; gap: 8px; }
/* v1.0.437 — 16px -> 22px, fill var(--muted) -> #fff, per explicit
   follow-up request ("גם השעות... את הכתב והסימנים שיש שם... בקושי רואים
   אותם... גם להגדיל"): this icon only ever renders on archive/'past' rows
   (see renderPlayerListRows/renderEpgList in app.js — always paired with
   .epg-bookmark-icon, right below), so no extra .epg-row.past scoping is
   needed, unlike .title/.time above which are shared with other row kinds. */
.epg-play-icon { width: 22px; height: 22px; fill: #fff; flex-shrink: 0; }
/* Reminder clock on "בהמשך" (upcoming) rows — dimmed gray, lights up red
   once armed (hold Enter ~2s — see toggleProgramReminder in app.js).
   Untouched by the v1.0.437 change above — that request was specifically
   about archive/'past' rows, and this icon only ever appears on 'next'
   rows (see the same renderPlayerListRows/renderEpgList reference). */
.epg-reminder-icon { width: 16px; height: 16px; color: var(--muted); flex-shrink: 0; }
.epg-reminder-icon.active { color: var(--danger); }
/* v1.0.412 — bookmark icon on "ארכיון" (past) rows — dimmed gray, lights up
   green (same accent2 "available"/success color used elsewhere) once this
   specific airing is added to "continue watching" (hold Enter ~2s — see
   toggleArchiveBookmark in app.js).
   v1.0.437 — 16px -> 22px, color var(--muted) -> #fff (inactive state only
   — .active below, the "already bookmarked" red, is untouched), same
   follow-up request as .epg-play-icon right above — this icon is likewise
   archive-only already, no .epg-row.past scoping needed. */
.epg-bookmark-icon { width: 22px; height: 22px; color: #fff; flex-shrink: 0; }
/* v1.0.413 — changed from green to red (var(--danger)) per explicit
   request, so the icon itself matches the same active-color convention the
   reminder clock icon above already uses — the row's own new green tint
   (.epg-row.bookmarked, right above) is what carries the green now. */
.epg-bookmark-icon.active { color: var(--danger); }
.epg-section-label {
  color: var(--muted);
  font-size: 1rem;
  margin: 20px 0 10px;
}

/* --- Player --- */
/* v1.0.245 — BUG FIX, per live-device report ("הקול נשמע אבל רואים מסך
   שחור"): this used to be a hardcoded opaque background:#000. That was
   harmless back when #video was a normal CHILD of #playerVideoArea (this
   screen's own background just showed through as the letterbox color behind
   it, same stacking context). Since v1.0.244 moved #video to a body-level
   element painted BEHIND every .screen (see the comment at the top of
   <body> in index.html), an opaque background here sits IN FRONT of it in
   paint order and hides it completely — the shared connection kept playing
   fine (audio never stopped) but nothing was ever visible. Transparent now,
   same as .hero-media below for the exact same reason: #video already
   carries its own background:#000 (see #video's own rule further down), so
   the visual result before any picture arrives is identical either way —
   the only thing this change does is stop hiding the picture once it does.
   Nothing else on this screen depended on this rule: .player-list-panel and
   .player-browse-backdrop (the only list-mode siblings that ever need to be
   opaque) already carry their own explicit backgrounds — see their own
   rules below. */
#screen-player { position: relative; }
/* v1.0.231 — the browse-while-watching split panel (Up from plain live
   playback opens this instead of the old full-screen-swap EPG screen — see
   the big index.html comment on #playerVideoArea for why). Outside of
   .list-mode this whole block is a no-op: #playerVideoArea is just a
   full-size transparent wrapper around exactly what used to sit directly in
   #screen-player, and #playerListPanel stays display:none — so ordinary
   full-screen playback is pixel-identical to before this feature existed. */
#screen-player.list-mode { display: flex; flex-direction: row; }
/* v1.0.232 — BUG FIX, per explicit report: #video's own object-fit:fill
   (see the big comment on #video below — chosen for a DIFFERENT, much
   milder squeeze: the height-only shrink when the old controls panel
   slides up) was ALSO applying here, where the box shrinks to half the
   screen's WIDTH instead — stretching the picture edge-to-edge with no
   letterboxing at all, instead of the expected black bars above/below a
   correctly-proportioned picture. Overridden back to `contain` specifically
   in list-mode, so the picture keeps its real aspect ratio (letterboxed)
   the same way it's described everywhere else in this feature. */
#screen-player.list-mode #video { object-fit: contain; }
/* v1.0.425 — NEW, per explicit request ("שים את הטלויזיה... רבע מגודל המסך
   כולו... LEFT 0 TOP 0... חצי מהגובה שלה וחצי מהאורך שלה"): the TV picture
   shrinks from filling its whole half-screen column down to a quarter of
   the SCREEN. #video is a body-level, position:fixed element (see the big
   v1.0.244 comment on it below) entirely outside the flex layout below, so
   it needs its own matching top/left/width/height here. Fixed px (half of
   the app's own fixed 1920x1080 canvas — see html,body above), not vw/vh —
   per the follow-up discussion, this whole app already renders on that one
   fixed canvas which webOS itself scales to the real screen, so px here is
   exactly as resolution-independent as a percentage would be, and stays
   consistent with how every other measurement in this file is written.
   left:0 matches the video column's default (non-swapped) position flush
   against the screen's own left edge; the .swapped override right below
   tracks it to the right edge instead, whichever side togglePlayerListSwap
   has actually put the video column on (see the order-based left/right
   comment further below) — same "top-left of wherever its own column is"
   idea as .player-video-area's own shrink just below. */
/* v1.0.469 — 960x540 -> 1056x594 (+10% both dims) + left:0->15px, tried
   per his original "+10%, without stretching" request — REVERTED in
   v1.0.470, per his explicit, emphatic follow-up report: it pushed
   structure 5 too far right, forced .player-list-panel/row 7 to shrink
   (which he does NOT want touched — "לא לשנות את מה שבנינו כבר"), and
   produced black-gap artifacts around the new TV frame/row 10. Per his own
   words ("תוריד... תקטין... תחזיר למה שהיה"), .player-list-panel/row 7 are
   back to their untouched original values (see their own v1.0.470
   comments) and the TV goes back to being the ONLY thing that changes size
   here.
   v1.0.470 — NEW target, per his explicit follow-up with an exact number
   ("הטלויזיה שתהיה פחות מחצי מסך לפחות ב40 פיקסלים"): less than half the
   screen (960px) by at least 40px -> 920px, height kept at exactly 16:9
   (920 * 9/16 = 517.5) so object-fit:contain still shows the real
   broadcast picture letterbox-free and unstretched — just a SMALLER box
   than the original 960x540, not bigger. left stays 0 (flush against the
   screen's own left edge, exactly as originally) — no left-margin here any
   more, that idea was part of the reverted v1.0.469 growth.
   v1.0.470 — top: 0 -> 112.5px, per explicit follow-up ("הטלוויזיה צריכה
   להתחיל מתחת לשורה 10 [=the clock/status bar over the TV, since renamed
   'row 12' in his own latest sketch to free up 'row 10' for a new planned
   element] באותו רווח כמו שיש לארכיון בין הארכיון לשורה שבע"): the TV must
   start BELOW that bar instead of underneath/behind it (the v1.0.469
   top:25px overlay-on-top-of-the-video placement of that bar is exactly
   what produced the "black gaps / merging" artifact he reported), using
   the SAME reserved-top-space mechanism .player-list-panel/.player-
   number-tiles already use for row 7 above them: both reserve
   padding-top:112.5px (its own bar sits at top:25px/height:82.5px inside
   that reserved zone, ending at 107.5 — a 5px clearance to the real
   content below, see that rule's own comment) — the TV now mirrors that
   exact 112.5px figure so the same visual relationship (bar above, 5px
   clearance, content starts at 112.5) applies to both columns identically,
   per his own "כל ההיגיון הזה צריך להיות מלכתחילה" complaint. Achieved via
   .player-video-area's own new margin-top below (NOT changed here) rather
   than this value directly, since .player-tv-focus-frame centers itself
   on .player-video-area's own box (top:50%) and needs that box's actual
   screen position to move as a whole for the frame to stay aligned with
   the picture — see that rule's own reasoning. width/height unchanged. */
/* v1.0.471 — top: 112.5px -> 122.5px, per explicit report that the 5px
   clearance to row 12 (112.5 - row 12's own 107.5 bottom edge) reads as
   LESS than 5px on the real device ("הרווח פחות מ-5 פיקלים") — +10px more
   clearance (now 15px total), per his own "תקטין את הטלויזה עוד קצת"
   instruction. This value is driven by .player-video-area's own
   margin-top below (see that rule's matching comment) — kept in sync here
   since #video is positioned against #screen-player directly, not against
   that box.
   v1.0.474 — 122.5px -> 82.5px, per explicit direct instruction ("את
   הטלוויזיה... שתרים אותה ב-40 פיקסלים למעלה זה הכל לא ליגוע בכלום שלא
   יזוז לי המסגרת שלא יזוז כלום רק הטלוויזיה"): raises ONLY the picture
   itself 40px up the screen. Safe to change with zero side effects on
   .player-tv-focus-frame or .player-row10 — as of the v1.0.473 round the
   frame was moved OUT of .player-video-area to its own independent
   #screen-player-level position (see its big comment further below), so it
   no longer moves as a side effect of this box's own position the way the
   old v1.0.470/471 comments above describe. Only .player-video-area's own
   matching margin-top (below) and the bottom-controls-cluster's three
   offset rules (further below — recomputed in lockstep so THEY don't
   drift either, per the same "nothing else should move" instruction) also
   changed that round.
   v1.0.475 — 82.5px -> 117.5px, per explicit follow-up instruction/
   clarification across several messages: he first asked to grow the
   picture 3% and raise it another 10px, centered in the gray frame — but
   growing BOTH dimensions to fill the frame's own box exactly (935x517.5)
   would have stretched the real 16:9 picture out of proportion (935/517.5
   = 1.807, not 16/9 = 1.778 — confirmed with him directly, he agreed
   distortion is unacceptable). Final, explicit instruction ("שתשב רק על
   הגבולות של הלמעלה והלמטה, מקסימום שלא תצא מהמסגרת... בצדדים לא מעניין
   שישאר רווח שחור, אבל שלא תצא מהטלוויזיה מהמסגרת בלמעלה ובלמטה"): the
   picture must never exceed the frame's own top/bottom edges, gaps on the
   LEFT/RIGHT are fine (plain black, he may cover them with a transparent-
   center canvas overlay later — not this round). Since height stays
   517.5px (unchanged — matches the frame's own height exactly, so width
   stays a true, undistorted 920px too — see width/height below, neither
   changed), the only way to guarantee top AND bottom both land exactly on
   the frame's own edges is to match this box's top to the frame's own
   absolute top (117.5px — see .player-tv-focus-frame's own top:117.5px)
   exactly, rather than an independent "raise by Npx" figure. This ends up
   LOWER (more onscreen-down) than the v1.0.474 value above (82.5), which
   had drifted the picture's own top edge above the frame's top edge —
   exactly what this round corrects. left also changes (see below) to
   re-center the now-narrower-than-the-frame picture horizontally inside
   it, per the same instruction's earlier "should not shift left/right"
   requirement (935-920=15, split evenly = 7.5px gap each side). */
#screen-player.list-mode #video {
  /* v1.0.476 diagnostic (red background) confirmed: the black area the
     user photographed is NOT part of #video's own CSS box — it stayed
     black even with background:red forced on this whole box, so the gap's
     source is outside this box's CSS entirely (hardware overlay plane or
     another overlapping element — unresolved). Reverting to the normal
     #000 default (removing the override; base rule below already has
     background:#000) — do not ship the red diagnostic permanently.
     v1.0.477 — per explicit request ("תגדיל אותה רגע בשתי אחוז ותמשוך
     אותה תדחוף אותה כלפי מעלה ב-30 פיקסלים בערך"), an EXPLORATORY step
     (not a confirmed fix) while the top-gap cause is still unresolved:
     grow both dimensions 2%, keeping the same center point the v1.0.475
     box had (center_x=487.5, center_y=376.25), then push the whole box up
     an additional 30px. width: 920*1.02=938.4. height: 517.5*1.02=527.85.
     left: 487.5-938.4/2=18.3. top before push: 376.25-527.85/2=112.325,
     minus 30px push = 82.325.
     v1.0.478 — per explicit request ("תגדיל את הטלוויזיה בעוד אחוז...בלי
     להזיז בלי כלום"): grow another 1%, keeping the SAME center point the
     v1.0.477 box had (center_x=18.3+938.4/2=487.5, center_y=82.325+
     527.85/2=346.25) — no additional push this time, only growth. width:
     938.4*1.01=947.784. height: 527.85*1.01=533.129. left: 487.5-947.784/2
     =13.608. top: 346.25-533.129/2=79.686.
     v1.0.479 — a 1.5%-growth attempt was requested then explicitly
     stopped mid-edit ("עצור") before being shipped/built, then replaced
     with a different instruction: raise 40px up and shift 20px right,
     size UNCHANGED ("תעלה אותה... 40 פיקסל למעלה כמו שהיא ותזיז אותה
     ימינה... 20 פיקסל" / "לא להגדיל רק להזיז" — don't grow, just move).
     So width/height stay exactly the v1.0.478 values (947.784/533.129,
     the growth attempt above was reverted, never shipped). top: 79.686-40
     =39.686. left: 13.608+20=33.608.
     v1.0.481 — per explicit request ("אתה גם מגדיל את הטלוויזיה איך שהיא
     בעוד אחוז"): grow another 1%, keeping the SAME center point the
     v1.0.479 box had (center_x=33.608+947.784/2=507.5, center_y=39.686+
     533.129/2=306.2505) — no additional push, only growth. width:
     947.784*1.01=957.262. height: 533.129*1.01=538.460. left: 507.5-
     957.262/2=28.869. top: 306.2505-538.460/2=37.020.
     v1.0.482 — per explicit request: (1) lower the picture 7px down, no
     resize — top: 37.020+7=44.020. (2) re-center horizontally inside the
     gray frame — this undoes the old +20px rightward shift from v1.0.479
     that was never reversed (frame left=20px, width=935px; picture width
     957.262px is wider than the frame, so it should overhang equally on
     both sides): left: 28.869-20=8.869 (exactly the pre-v1.0.479 value,
     now overhanging 11.131px equally on both left/right of the frame:
     20-8.869=11.131 left overhang, (8.869+957.262)-(20+935)=11.131 right
     overhang). width/height unchanged.
     v1.0.484 — per explicit request ("תזיז את הטלוויזיה ימינה 25 פיקס"):
     plain +25px rightward move, no resize, no other change. left:
     8.869+25=33.869. */
  top: 44.020px;
  left: 33.869px;
  width: 957.262px;
  height: 538.460px;
}
/* v1.0.470 — 1920-920=1000 (was 849 in the reverted v1.0.469 growth,
   1920-960=960 originally): swapped mode pins the TV flush to the
   screen's own right edge (no order:0 sibling ahead of it there any more
   — see .player-number-tiles' own order:3 swapped comment), so this left
   offset is always screen-width minus the video's own width — recomputed
   for the new 920px width.
   v1.0.475 — 1000px -> 972.5px: no longer flush against the screen's own
   right edge — now centered inside the swapped frame's own box instead
   (frame swapped: right:20px/width:935px, i.e. frame's own left edge sits
   at 1920-20-935=965; centered 920-wide picture inside that: 965+7.5=
   972.5), same "re-center inside the frame, gaps fine either side" reasons
   as the non-swapped left above.
   v1.0.477 — 972.5px -> 963.3px: recomputed for the 2%-grown 938.4px
   width, keeping it centered on the swapped frame's own center point
   (frame center = 965+935/2=1432.5; 1432.5-938.4/2=963.3).
   v1.0.478 — 963.3px -> 958.608px: recomputed for the 1%-further-grown
   947.784px width, same swapped frame center (1432.5): 1432.5-947.784/2=
   958.608.
   v1.0.479 — 958.608px -> 978.608px: the 1.5%-growth attempt above was
   reverted (never shipped); this round is a plain +20px rightward shift
   instead ("תזיז אותה ימינה... 20 פיקסל", same shift as the non-swapped
   left above), applied on top of the v1.0.478 base: 958.608+20=978.608.
   v1.0.481 — 978.608px -> 953.869px: recomputed for the 1%-further-grown
   957.262px width, same swapped frame center (1432.5): 1432.5-957.262/2=
   953.869.
   v1.0.482 — unchanged: width didn't change this round, and this value
   was never affected by the v1.0.479 +20px shift in the first place (it's
   always freshly centered on the swapped frame's own center, 1432.5), so
   it's already correctly centered — no edit needed.
   v1.0.484 — 953.869px -> 978.869px: same plain +25px rightward move as
   the non-swapped left above ("תזיז את הטלוויזיה ימינה 25 פיקס"), applied
   here too so both modes shift together. */
#screen-player.list-mode.swapped #video { left: 978.869px; }
.player-video-area { position: relative; width: 100%; height: 100%; }
/* Focus indicator for the "tv" stop in the browse loop — a white frame
   around the video, per explicit request, so it's unambiguous the TV
   itself currently has focus (as opposed to the accent-purple color used
   everywhere else for focus, which per follow-up request didn't read
   clearly enough here).
   v1.0.237 — BUG FIX, per explicit report ("לא רואה מסגרת לבנה מסביב
   לשידור של הטלויזיה"): the box-shadow was on .player-video-area itself,
   but #video sits directly inside it at width:100%/height:100% with its own
   #000 background — a child that completely fills its parent's box paints
   OVER the parent's inset box-shadow (box-shadow is part of the parent's
   own background/border layer, painted BEFORE its children). Tried moving
   it onto #video itself — STILL not visible, see v1.0.238 below for why.
   v1.0.238 — REAL FIX, per explicit follow-up report ("אין מסגרת לבנה"
   even after the v1.0.237 attempt): #video renders via its own hardware
   video-overlay plane on this TV, entirely outside normal CSS
   compositing — no box-shadow/border/filter on #video itself can EVER be
   visible there, regardless of stacking order. Moved onto a dedicated
   plain (non-video) sibling div, #playerTvFocusFrame, added right after
   #video in index.html — ordinary DOM content always composites on top of
   the hardware video plane (same reason the OSD/controls/backdrop overlays
   here have always been visible over the picture), so this renders
   reliably. Sized/positioned to exactly cover #video via inset:0, and
   pointer-events:none so it never blocks input. Class still toggled on the
   .player-video-area wrapper by updatePlayerListFocusUI() in app.js — no
   JS change needed, just this descendant selector.
   v1.0.240 — BUG FIX, per explicit report ("יש מסגרת אומנם גדולה... גולשת
   למטה"): inset:0 frames the whole #playerVideoArea BOX, but that box is
   nearly square (~960x1080 in list-mode) while the actual broadcast video
   is 16:9 and (via #video's own object-fit:contain) only occupies a
   smaller, vertically-centered sub-rectangle within it — black letterbox
   bars above/below fill the rest. The frame was hugging the outer box, not
   the real visible picture, so it looked oversized and spilled past the
   picture's own bottom edge. Now sized as its own centered 16:9 box
   instead of inset:0 — matches #video's real visible picture exactly
   (same width, vertically centered the same way object-fit:contain
   centers it) regardless of how tall/square the surrounding container
   actually is. */
/* v1.0.472 — REPOSITIONED, per explicit direct instruction ("את המסגרת
   האפורה של הטלוויזיה תיישר אותה שמאלה שהיא תהיה בקו אחד עם ההתחלה של
   שורה 12 מצד שמאל, ושתהיה באותו אורך של מספר 12 ותגביה אותה כלפי מעלה
   בחמש פיקסלים"): the frame is no longer centered on/sized to #video's
   own exact rectangle (dropping the aspect-ratio/centering mechanism from
   the big comment above) — it now explicitly matches row 12's own box
   instead: same left (20px) and same width (935px) as .player-row10.
   v1.0.473 — BUG FIX, per explicit report ("היא גם מופיעה במסך מלא...
   לקחת אותה ימינה ביקשתי שמאלה"): v1.0.472 kept this element nested INSIDE
   #playerVideoArea, so its left:20px was relative to THAT box's own
   flex-computed position, not to #screen-player the way .player-row10's
   left:20px actually is (.player-row10 is "positioned against #screen-
   player directly", an #screen-player-level sibling — see index.html).
   #playerVideoArea's flex box does NOT sit flush against the screen edge
   (RTL flex order plus the list-panel sibling's own width/margin shift it
   well off zero), so left:20px against it landed far to the right of row
   12's real on-screen position — the "took it right, I asked left" bug.
   Separately, outside .list-mode #playerVideoArea is a full 100%x100% box
   (plain full-screen playback), so this same small fixed-size rect showed
   up as a stray box over the whole picture there too — the "appears in
   full screen too" bug. Fixed by moving the div itself out of
   #playerVideoArea to be an #screen-player-level sibling right next to
   .player-row10 (see index.html) — left:20px now means the exact same
   thing .player-row10's own left:20px means, so the two truly line up —
   and by hiding it outside .list-mode below (see display:none/flex pair),
   since it only ever meant to match row 12's box, and row 12 itself is a
   list-mode-only concept with nothing to line up with otherwise.
   top: was vertically centered on .player-video-area (a 517.5px-tall box
   with its own margin-top:122.5px, i.e. absolute top=122.5, matching
   #video's own top exactly), raised 5px further up per the v1.0.472
   instruction. Now computed directly in #screen-player's own coordinate
   system: 122.5-5=117.5. Height kept at 517.5px (matching #video's own
   height — only position/width moved, height was never part of either
   instruction). Left/width remain independent of the TV's real 920px-wide
   picture underneath — a decorative frame matching the row 7/row 12
   family's own box instead, per the original v1.0.472 explicit choice. */
.player-tv-focus-frame {
  display: none;
  position: absolute;
  top: 117.5px;
  left: 20px;
  width: 935px;
  /* v1.0.480 — 517.5px -> 452.5px, per explicit request ("גם המסגרת של
     הטלויזיה שתסתיים באותו גובה 570px" — the gray TV focus frame's own
     bottom edge should end at the same height as archive row 3's own
     bottom edge). Original v1.0.480 math (490 words) forgot to add each
     row/tab/day-tile's own 2px+2px border on top of its padding+line-height
     — real per-block height is 86.5px (27+27+28.5+2+2), not 82.5px, so the
     real row-step is 96.5px (86.5+10), not 92.5px.
     v1.0.481 — 452.5px -> 472.5px, BUG FIX per explicit follow-up
     ("תבדוק את עצמך בבקשה" / "מה הסוף של הגובה של שורה 3"): corrected row-3
     math with the real 86.5px block height: .player-list-panel margin-top 5
     + padding-top 112.5 + tabs 86.5 + gap 10 + day-tiles 86.5 + gap 10 +
     row1 86.5 + gap 10 + row2 86.5 + gap 10 = row3 starts at 503.5, row3's
     own height 86.5 -> ends at 590. top stays 117.5px (unchanged), so
     height = 590-117.5=472.5.
     v1.0.482 — 472.5px -> 476.5px, per explicit request ("לא להזיז, להגדיל
     את הגודל שלה ב-4 פיקס כלפי מטה" — don't move it, grow it 4px
     downward): top stays 117.5px unchanged, only height grows by 4,
     extending the frame's bottom edge further down (was ending at 590,
     now ends at 594).
     v1.0.483 — 476.5px -> 474.5px, per explicit request ("תקטין חזרה
     בשתי פיקסלים את הגובה" — shrink the height back by 2px): top stays
     117.5px unchanged, height shrinks by 2, bottom edge now ends at 592
     (was 594). Horizontal centering of #video within the frame left
     UNTOUCHED this round — still pending photo confirmation from the
     user before any further left/width change (math says it's already
     centered: 11.131px overhang each side of the frame).
     v1.0.486 — 474.5px -> 473px, per explicit request ("תקטין את הגובה של
     המסגרת האפורה בעוד 1.5 פיקס"): top stays 117.5px unchanged, height
     shrinks by 1.5, bottom edge now ends at 590.5 (was 592). */
  height: 473px;
  pointer-events: none;
  box-sizing: border-box;
  /* v1.0.469 — REWRITTEN from a box-shadow (only ever shown while focused)
     to a real border, always visible, per explicit request ("נהפוך אותה
     למסגרת שתהיה בדיוק על הטלוויזיה היא תלך עם הטלוויזיה לכל מקום... פינות
     עגולות וצבע אפור כהה כמו שאר המבנים... וכשנתביית על זה, הצבע של המסגרת
     יהפוך ללבן"): matches the exact same border/border-radius/color-swap
     idiom .epg-row/.player-number-tile/.player-list-day-tile already use
     (var(--border) at rest, #fff while focused) — "same family" as asked —
     instead of the old focus-only inset glow. box-shadow was originally
     used here (not a plain border) only because v1.0.238 found #video
     itself can never show a border/box-shadow/filter at all (hardware
     video-overlay plane, outside normal CSS compositing) — but this frame
     is a separate ordinary DOM sibling already confirmed to composite ON
     TOP of that plane (that's the whole reason it exists), so a real
     border is equally visible here; nothing about that v1.0.238 limitation
     applies to this element itself, only to #video directly.
     NOTE, stated plainly per this project's own convention of flagging
     unconfirmed-on-real-hardware claims: border-radius on this frame
     clips only what THIS element itself paints — it can visually round
     off the frame's own border/background, but cannot clip #video's own
     picture (same hardware-plane limitation, one level removed), so the
     video's actual last pixel at each corner may still show through as a
     small square poking past the rounded frame corner rather than being
     truly cropped round. Left at a modest 12px radius (matching .epg-row's
     own) for now — if that corner-peek reads as visible/ugly on the real
     device, the fix is a thicker frame or a dedicated corner-mask
     technique, not a bigger radius here. */
  border-radius: 12px;
  /* v1.0.488 — REPLACES the v1.0.487 ::before/::after 45px fill-bar
     pseudo-elements (removed below this rule; that approach is reverted
     per explicit request — "אני תמחק אותם"). Per follow-up sketch review,
     the user wants the frame's OWN border thicker on the left/right sides
     only (top/bottom stay 4px), using border-width directly instead of
     separate filled elements — this is the more correct CSS mechanism
     since border-radius already rounds the OUTER corners of whatever
     border-width is set per side automatically (confirmed intent: "הפינות
     צריכות להיות מעוגלות כלפי חוץ זה מה שייתן לטלוויזה להראות עם פינות
     עגולות" — the rounded-outward corners are what make the TV picture
     itself read as having rounded corners, since this frame composites on
     top of #video's hardware overlay plane and visually masks its square
     corners). Same border color (var(--border) at rest, #fff focused,
     both already set via border-width alone below plus the existing
     border-color rules further down — no color rule needed to change). */
  border-style: solid;
  border-color: var(--border);
  border-top-width: 4px;
  border-bottom-width: 4px;
  /* v1.0.489 — 50px -> 60px, per explicit request ("תגדיל את הרוחב של מה
     שעשינו בצדדים בעוד עשרה פיקסלים כל אחד לצד שלו לפנים שלו"): each side
     grows another 10px inward. box-sizing:border-box above means the
     frame's own outer top/left/width/height stay exactly where they are —
     only the visible window (where the picture shows through) shrinks
     further on the left/right. */
  border-left-width: 60px;
  border-right-width: 60px;
}
/* v1.0.472 — NEW: since the frame now uses an explicit left:20px (see its
   own big v1.0.472 comment above) instead of the old left:0/right:0/
   width:100% that auto-mirrored for free whenever .player-video-area
   itself flipped sides in swapped mode, it needs its own explicit swapped
   override now — same left<->right flip .player-row10 already gets.
   v1.0.473 — display:none/flex pair added: this frame now only ever means
   anything in .list-mode (see the big v1.0.473 comment above), matching
   .player-row10's own display:none/flex pattern exactly. */
#screen-player.list-mode .player-tv-focus-frame { display: block; }
#screen-player.list-mode.swapped .player-tv-focus-frame { left: auto; right: 20px; }
/* v1.0.473 — CHANGED from a descendant selector (.player-video-area.tv-
   focused .player-tv-focus-frame) to a general-sibling selector: this frame
   moved OUT of #playerVideoArea (see index.html) so it's no longer a
   descendant of it, but #playerVideoArea (which still gets the
   .tv-focused class toggled on it in app.js) still comes right before this
   frame in the DOM, so `~` still reaches it. */
.player-video-area.tv-focused ~ .player-tv-focus-frame { border-color: #fff; }
/* v1.0.425 — NEW, shrinks this wrapper to match #video's own new quarter-
   screen size above — every overlay inside it (OSD, controls panel, the
   focus frame right above, the browse controls row/progress bar further
   below) is positioned relative to THIS box (see the big index.html
   comment on #playerVideoArea for why), so shrinking just this one box
   confines all of them to the same smaller rect automatically, with none
   of their own CSS touched — same idea the original half-screen split
   already relied on. Width already comes out to 50% of the SCREEN via
   flex:1 1 50% below (the column itself is half the screen); only height
   needs an explicit shrink — align-self:flex-start overrides the row's
   default stretch-to-fill-height behavior so it hugs the column's own top
   instead of centering/stretching.
   v1.0.431 — BUG FIX, per explicit report ("הטלויזיה והפס מידע עם הכפתורים
   זזו גם הם ימינה... הם חצו את אמצע המסך... גדלו"): flex:1 1 50% meant
   flex-grow:1 was still active, so once v1.0.430 gave .player-list-panel a
   fixed width smaller than its old 50% share, THIS box's flex-grow soaked
   up all the space that freed up and grew past 960px — dragging every
   `right:Npx` overlay inside it (.player-browse-backdrop/-controls-row,
   #playerBrowseProgress) along with it, since they measure from THIS box's
   own right edge, not the real TV rectangle. Fixed at flex:0 0 960px (no
   grow, no shrink, matches #video's real pinned width exactly) so this box
   can never drift from the actual TV rectangle again, with its own
   margin-right:15px supplying the gap to .player-list-panel explicitly
   (see that rule's own v1.0.431 comment) instead of leaving it to
   leftover flex-grow space. */
/* v1.0.469 tried 960px->1056px/50%->55%/+margin-left:15px (matching
   #video's own +10% growth) — REVERTED in v1.0.470 along with #video's own
   matching revert (see its big comment) — the margin-left addition is
   dropped entirely (back to no left margin) and this box shrinks to match
   the new SMALLER TV instead.
   v1.0.470 — 960px -> 920px, 50% -> 517.5px (was 540px = 50% of the real
   1080px screen height; now an explicit px height instead of a percentage,
   simpler than a non-round percentage), matching #video's own new
   920x517.5 size above so this box (and everything measured against it:
   the focus frame, the bottom controls cluster via the matching offset
   recompute on those three rules below) stays in lockstep with the real
   TV rectangle — margin-right stays 15px unchanged, the gap to
   .player-list-panel was never part of this resize either time.
   v1.0.470 — NEW: margin-top: 112.5px added, per #video's own matching
   comment right above — pushes this WHOLE box (and everything positioned
   against it: #video itself is positioned against #screen-player directly
   and gets its own matching top:112.5px change, but .player-tv-focus-
   frame/the bottom controls cluster below ARE children of this box, so
   they move down for free with it) down to clear the "row 12" bar now
   sitting above it, mirroring .player-list-panel/.player-number-tiles'
   own padding-top:112.5px reserved for row 7. Height stays 517.5px
   (unchanged) rather than growing to absorb the new top space, SPECIFI-
   CALLY so .player-tv-focus-frame's existing top:50%/translateY(-50%)
   self-centering (sized via aspect-ratio, matches #video exactly) still
   lands in the right place without its own rule needing any change — the
   box simply moves as a unit, frame and video both still perfectly
   aligned.
   v1.0.471 — 112.5px -> 122.5px, same +10px clearance increase as #video's
   own matching v1.0.471 comment above (real-device report: the gap to row
   12 read as less than the intended 5px). The bottom-cluster's three
   offset rules further below are recomputed in lockstep — see their own
   matching v1.0.471 comments — same reasoning as the v1.0.470 recompute:
   these are measured via `bottom:Npx` against THIS box's own local
   coordinates (unaffected by margin-top itself), so without a matching
   recompute the whole cluster would silently drift 10px closer to the
   true screen bottom instead of staying at its long-established target
   distance.
   v1.0.474 — 122.5px -> 82.5px, same -40px raise as #video's own matching
   v1.0.474 comment above (kept in sync, same reasoning). Height stays
   517.5px (unchanged — only position moves). Bottom-cluster's three offset
   rules recomputed below, same "keep landing at the same true on-screen
   target distance" reasoning as every prior round — per that round's
   instruction specifically nothing except the TV picture itself should
   visually move, so these must NOT be left as-is.
   v1.0.475 — 82.5px -> 117.5px, same change as #video's own matching
   v1.0.475 comment above (kept in sync) — the picture's top now matches
   the frame's own top exactly rather than an independent raise figure.
   Bottom-cluster's three offset rules recomputed below again, same
   reasoning.
   v1.0.477 — 117.5px -> 82.325px, height 517.5px -> 527.85px, same 2%-grow
   + 30px-push change as #video's own matching v1.0.477 comment above (kept
   in sync). Bottom-cluster's three offset rules recomputed below again.
   v1.0.478 — 82.325px -> 79.686px, height 527.85px -> 533.129px, same
   1%-further-grow (no additional push) change as #video's own matching
   v1.0.478 comment above (kept in sync). Bottom-cluster's three offset
   rules recomputed below again.
   v1.0.479 — 79.686px -> 39.686px, same -40px raise (no growth, no width
   change) as #video's own matching v1.0.479 comment above (kept in sync).
   Width/flex-basis and height both stay unchanged (947.784px/533.129px —
   this round only moves the box, per "לא להגדיל רק להזיז"). Bottom-
   cluster's three offset rules recomputed below again.
   v1.0.481 — 39.686px -> 37.020px, height 533.129px -> 538.460px, same
   1%-further-grow (no additional push) change as #video's own matching
   v1.0.481 comment above (kept in sync). Bottom-cluster's three offset
   rules recomputed below again. */
#screen-player.list-mode .player-video-area { flex: 0 0 957.262px; margin-right: 15px; margin-top: 44.020px; min-width: 0; align-self: flex-start; height: 538.460px; }
/* v1.0.477 — flex-basis (938.4px) recomputed to match #video's own new
   2%-grown width above, same "reserved-space box mirrors #video's actual
   box" convention v1.0.470 established (flex:0 0 920px chosen there to
   match #video's width at that time).
   v1.0.478 — flex-basis 938.4px -> 947.784px, same 1%-further-grow, same
   reasoning.
   v1.0.479 — flex-basis unchanged (947.784px) — this round is a pure
   raise+rightward-shift with no size/width change.
   v1.0.481 — flex-basis 947.784px -> 957.262px, same 1%-further-grow, same
   reasoning. */
/* v1.0.427 — NEW, per explicit follow-up report that the bottom control
   cluster (.player-browse-backdrop, #playerBrowseProgress, the actual
   .player-browse-controls-row with pause/restart/favorite/swap) rode up
   along with the v1.0.425 TV shrink above and now sits cramped near the
   BOTTOM OF THE SHRUNK 540px VIDEO BOX instead of the bottom of the real
   screen. Root cause: all three are `position:absolute; bottom:Npx` (see
   their own base rules further below), and their containing block is THIS
   SAME .player-video-area, which the v1.0.425 comment right above
   deliberately shrank to height:50% (540px) so overlays "confine to the
   same smaller rect automatically" — correct for the OSD/focus-frame, but
   NOT what's wanted for this specific bottom cluster, which per explicit
   request ("תוריד למטה עד הסוף") must stay pinned to the bottom of the
   actual 1920x1080 screen regardless of the video picture's own size.
   Fix: push each one's `bottom` further negative by exactly the 540px gap
   between the shrunk box's own bottom edge and the real screen's bottom
   edge (1080 - 540 = 540), landing each at the same true on-screen height
   it had before the TV shrink — position:absolute is not clipped by its
   containing block's height, so this is a plain offset, nothing else about
   any of the three needs to change. */
/* v1.0.464 — -540px -> -440px, per explicit correction: the v1.0.462/463
   moves above only raised the icon row (.player-browse-controls-row) —
   he actually considers the backdrop+progress+controls ONE unit
   ("פס מידע עם התמונה... חשבתי שהוא יחידה אחת") and wants the WHOLE thing
   raised 100px together, not just the icons. Real on-screen distance from
   the true screen bottom goes from 0px to 100px (-440 + 540 = 100), same
   "offset by the 540px shrunk-box gap" math as the v1.0.427 comment above. */
/* v1.0.469 — -440px -> -386px: .player-video-area's own height grew
   540px -> 594px (matching #video's own +10% growth — see its matching
   comment), so the gap between this box's bottom edge and the real
   screen's own bottom edge shrank 1080-540=540 -> 1080-594=486, a 54px
   change — every "offset by the shrunk-box gap" value in this whole
   cluster (this rule + the two right below) moves the same +54px
   (less negative) to land on the exact same true on-screen height as
   before: -440+54=-386. */
/* v1.0.470 — -386px -> -350px: recomputed for the reverted 920x517.5 TV
   AND its new margin-top:112.5px (see .player-video-area's own matching
   comment) — this whole cluster is positioned against .player-video-area
   (bottom:Npx, unaffected by margin-top since that's outside the box), so
   to keep landing at the SAME true on-screen distance from the real
   screen bottom as always (target 100px, unaffected by the TV move), the
   gap constant changes from 1080-517.5=562.5 to 1080-(112.5+517.5)=450
   (the margin-top eats into the space this formula accounts for):
   100-450=-350. Verified: local bottom = 517.5-(-350)=867.5, + this box's
   own new 112.5px screen offset = 980 absolute = 1080-100 ✓, same as
   before the TV move. */
/* v1.0.471 — -350px -> -360px: .player-video-area's own margin-top grew
   112.5->122.5 (+10px, see its own matching v1.0.471 comment), so the gap
   constant shrinks another 10: 1080-(122.5+517.5)=440 (was 450).
   100-440=-360, same "keep landing at the same true on-screen target"
   reasoning as the v1.0.470 recompute. */
/* v1.0.473 — -360px -> -380px: .player-video-area's own margin-top shrank
   122.5->82.5 (-40px, TV raised per explicit instruction), so the gap
   constant grows by 40: 1080-(82.5+517.5)=480 (was 440). 100-480=-380, same
   "keep landing at the same true on-screen target" reasoning as every
   prior recompute — this element must NOT visually move even though the
   TV above it did. */
/* v1.0.475 — -380px -> -345px: .player-video-area's own margin-top grew
   82.5->117.5 (+35px, TV top now matches the frame's own top exactly — see
   #video's own matching v1.0.475 comment), so the gap constant shrinks by
   35: 1080-(117.5+517.5)=445 (was 480). 100-445=-345, same "keep landing
   at the same true on-screen target" reasoning as every prior recompute —
   this element must NOT visually move even though the TV above it did. */
/* v1.0.477 — -345px -> -369.825px: .player-video-area's own margin-top+
   height both changed (2%-grow + 30px-push, see #video's own matching
   v1.0.477 comment), new gap constant: 1080-(82.325+527.85)=469.825 (was
   445). 100-469.825=-369.825, same "keep landing at the same true
   on-screen target" reasoning — this element must NOT visually move. */
/* v1.0.478 — -369.825px -> -367.185px: .player-video-area's own margin-top+
   height both changed again (1%-further-grow, see #video's own matching
   v1.0.478 comment), new gap constant: 1080-(79.686+533.129)=467.185 (was
   469.825). 100-467.185=-367.185, same "keep landing at the same true
   on-screen target" reasoning — this element must NOT visually move. */
/* v1.0.479 — -367.185px -> -407.185px: .player-video-area's own margin-top
   shrank 79.686->39.686 (-40px raise, no growth this round — see #video's
   own matching v1.0.479 comment), height unchanged (533.129), so the gap
   constant grows by 40: 1080-(39.686+533.129)=507.185 (was 467.185).
   100-507.185=-407.185, same "keep landing at the same true on-screen
   target" reasoning — this element must NOT visually move even though the
   TV above it did. */
/* v1.0.481 — -407.185px -> -404.520px: .player-video-area's own margin-top+
   height both changed again (1%-further-grow, see #video's own matching
   v1.0.481 comment), new gap constant: 1080-(37.020+538.460)=504.520 (was
   507.185). 100-504.520=-404.520, same "keep landing at the same true
   on-screen target" reasoning — this element must NOT visually move. */
/* v1.0.560 — "row 13": now a direct #screen-player child (see index.html), so this is in
   true screen coordinates. Vertical position UNCHANGED from before (measured top 759.98px,
   height 220px); horizontal now matches row 10/12: left 20px, width 935px. Top corners
   rounded (12px, same as the other structures), bottom corners square. */
/* v1.0.560 (same round) — height 220 -> 150 (-70px), and moved down so its bottom sits 20px above
   row 8 (row 8 top 992.5 -> bottom 972.5), the same gap structure 1 (#playerListRows, bottom 972.5)
   has above row 8. Top = 972.5 - 150 = 822.5. */
#screen-player.list-mode .player-browse-backdrop { top: 822.5px; bottom: auto; left: 20px; right: auto; width: 935px; height: 150px; border-radius: 12px 12px 0 0; }
#screen-player.list-mode.swapped .player-browse-backdrop { left: auto; right: 20px; }
/* v1.0.464 — -462px -> -362px, same "whole unit, +100px together" move as
   .player-browse-backdrop right above — real on-screen distance goes from
   78px to 178px (-362 + 540 = 178). */
/* v1.0.469 — -362px -> -308px, same +54px cluster-wide shift as
   .player-browse-backdrop's own matching v1.0.469 comment right above. */
/* v1.0.470 — -308px -> -272px, same recompute/reasoning as
   .player-browse-backdrop's own matching v1.0.470 comment right above:
   178-450=-272 (target 178 unchanged, new gap constant 450). */
/* v1.0.471 — -272px -> -282px, same recompute as .player-browse-backdrop's
   own matching v1.0.471 comment right above: 178-440=-282. */
/* v1.0.473 — -282px -> -302px, same gap-constant change (+40, see
   .player-browse-backdrop's own matching v1.0.473 comment) applied here:
   178-480=-302. */
/* v1.0.475 — -302px -> -267px, same gap-constant change (+35, see
   .player-browse-backdrop's own matching v1.0.475 comment) applied here:
   178-445=-267. */
/* v1.0.477 — -267px -> -291.825px, same gap-constant change (new constant
   469.825, see .player-browse-backdrop's own matching v1.0.477 comment)
   applied here: 178-469.825=-291.825. */
/* v1.0.478 — -291.825px -> -289.185px, same gap-constant change (new
   constant 467.185, see .player-browse-backdrop's own matching v1.0.478
   comment) applied here: 178-467.185=-289.185. */
/* v1.0.479 — -289.185px -> -329.185px, same gap-constant change (new
   constant 507.185, see .player-browse-backdrop's own matching v1.0.479
   comment) applied here: 178-507.185=-329.185. */
/* v1.0.481 — -329.185px -> -326.520px, same gap-constant change (new
   constant 504.520, see .player-browse-backdrop's own matching v1.0.481
   comment) applied here: 178-504.520=-326.520. */
/* v1.0.560 — superseded by the "rows 14/15 inside row 13" block (search v1.0.560 row 14). */
/* v1.0.462 — -390px -> -290px, per explicit request ("להעלות את פס המידע
   עם הכפתורים 100 פיקסלים כלפי מעלה" — confirmed to mean this icon row
   specifically: pause/restart/favorite/swap): +100px, same "offset by the
   540px shrunk-box gap" math as the v1.0.427 comment right above —
   real on-screen distance from the true screen bottom goes from 150px to
   250px (-290 + 540 = 250), the backdrop/progress bar above it
   deliberately left untouched (only this row was asked to move).
   v1.0.463 — -290px -> -190px, per explicit follow-up request to raise this
   same row ANOTHER 100px: real on-screen distance goes from 250px to 350px
   (-190 + 540 = 350). Same reasoning, same scope (only this row).
   v1.0.464 — -190px -> -90px, per the same "whole unit, +100px together"
   correction as the backdrop/progress above: real on-screen distance goes
   from 350px to 450px (-90 + 540 = 450) — keeps this row's own gap above
   the backdrop/progress identical to before (all three moved by the exact
   same +100px), just the whole cluster higher on screen together now. */
/* v1.0.469 — -90px -> -36px, same +54px cluster-wide shift as
   .player-browse-backdrop's own matching v1.0.469 comment above. */
/* v1.0.470 — -36px -> -90px: the plain recompute (450-450=0, same formula
   as the two rules above) would put this row's own bottom edge exactly
   flush with #video's new lower edge (both land at the same absolute
   630px screen position — the 112.5px margin-top happens to be close
   enough to this row's own old ~112.5px clearance from the video that the
   recompute alone erases it), so its own ~56-80px height would poke back
   UP into the picture instead of clearing it. Nudged an extra 90px (target
   effectively ~360 instead of 450 — closer to the video, moved up off the
   screen's true bottom by that much) purely to restore a real gap below
   the video — not derived from an exact target the way the other two are,
   so this specific value is the one most worth eyeballing again on the
   real device and nudging further if it still looks tight. */
/* v1.0.471 — -90px -> -80px: same gap-constant shift (+10, see
   .player-browse-backdrop's own matching v1.0.471 comment) applied on top
   of the same "extra buffer below the plain target-450 formula" this rule
   already carries (see its own v1.0.470 comment for why) — plain
   recompute would be 450-440=10, minus the same 90px safety buffer as
   before = -80. */
/* v1.0.473 — -80px -> -120px: same gap-constant change (+40, see
   .player-browse-backdrop's own matching v1.0.473 comment) applied on top
   of this rule's own existing ~90px safety buffer (see its v1.0.470/471
   comments): plain recompute 450-480=-30, minus the same 90px buffer as
   before = -120. */
/* v1.0.475 — -120px -> -85px: same gap-constant change (+35, see
   .player-browse-backdrop's own matching v1.0.475 comment) applied on top
   of this rule's own existing ~90px safety buffer (see its v1.0.470/471
   comments): plain recompute 450-445=5, minus the same 90px buffer as
   before = -85. */
/* v1.0.477 — -85px -> -109.825px: same gap-constant change (new constant
   469.825, see .player-browse-backdrop's own matching v1.0.477 comment)
   applied on top of this rule's own existing ~90px safety buffer: plain
   recompute 450-469.825=-19.825, minus the same 90px buffer as before =
   -109.825. */
/* v1.0.478 — -109.825px -> -107.185px: same gap-constant change (new
   constant 467.185, see .player-browse-backdrop's own matching v1.0.478
   comment) applied on top of this rule's own existing ~90px safety buffer:
   plain recompute 450-467.185=-17.185, minus the same 90px buffer as
   before = -107.185. */
/* v1.0.479 — -107.185px -> -147.185px: same gap-constant change (new
   constant 507.185, see .player-browse-backdrop's own matching v1.0.479
   comment) applied on top of this rule's own existing ~90px safety buffer:
   plain recompute 450-507.185=-57.185, minus the same 90px buffer as
   before = -147.185. */
/* v1.0.481 — -147.185px -> -144.520px: same gap-constant change (new
   constant 504.520, see .player-browse-backdrop's own matching v1.0.481
   comment) applied on top of this rule's own existing ~90px safety buffer:
   plain recompute 450-504.520=-54.520, minus the same 90px buffer as
   before = -144.520. */
/* v1.0.560 — superseded by the "rows 14/15 inside row 13" block (search v1.0.560 row 14). */
/* Visual left/right position is driven by `order`, NOT DOM order — actual
   DOM order here is [video, list] (see index.html), but "which one renders
   on which side" is a pure display concern (the swap button flips it live),
   so `order` keeps that entirely in CSS instead of requiring the DOM to be
   rearranged. Lower `order` renders first along the flex row's start edge —
   on this dir="rtl" page that's the RIGHT edge (same rule already
   established for .player-controls-row's plain DOM-order icons). Default
   (no .swapped): list panel order:1 (right), video order:2 (left) — archive/
   live-list on the right, TV+info on the left, per explicit request.
   .swapped (toggled by the swap button in #playerListPanel — see
   togglePlayerListSwap in app.js) flips the two order values, for other-
   language/LTR use. */
#screen-player.list-mode .player-list-panel { order: 1; }
#screen-player.list-mode .player-video-area { order: 2; }
/* v1.0.430 — NEW, per explicit request ("קטין את כל רוחב המבנה הזה(1) ב-100
   פיקס וקח אותו חזרה מהאמצע לכיוון ימין ב-15 פיקס" — "1" = this whole
   tabs+day-tiles+rows block): was flex:1 1 50% (960px, flush to the screen
   edge, left edge exactly on the screen's middle — see v1.0.429 above).
   v1.0.431 — width cut by another 40px (860 -> 820, per explicit follow-up
   "תקטין עוד רוחב מבנה 1 בעוד 40 פיקס") AND the margin math corrected: the
   target is still "left edge 15px right of the screen's true middle
   (960)", i.e. at 975 — with this panel now 820px wide, the blank strip
   between its right edge and the screen's own right edge (1920) is
   1920-975-820 = 125px. The old 85px margin (v1.0.430) was ALSO carrying
   an implicit assumption that .player-video-area's flex-grow would supply
   the 15px gap on the other side — that's what actually grew/dragged the
   TV and controls bar rightward (see .player-video-area's own v1.0.431
   fix above); this panel's own math wasn't the bug, but the margin value
   still needed updating for the new narrower width regardless. .swapped
   mirrors this onto the left edge instead, since the panel sits on the
   opposite side there. */
/* v1.0.442 — NEW, per explicit report that structure 5 (the number-tiles
   column) sat glued/visually merged against structure 1 with no real gap
   ("צריך שיהיה רווח ריק וברור בין העמודה למבנה 1"), and per his explicit
   priority (asked via a direct choice) that a clear visible gap wins even if
   it doesn't end up matching the top/right gaps exactly: opens a real 20px
   gap between .player-list-panel and .player-number-tiles.
   All three flex children in this row (.player-number-tiles 125px,
   .player-list-panel, .player-video-area 960px+15px margin) are flex:0 0 —
   no grow, no shrink — and were hand-tuned to sum to EXACTLY 1920px with
   zero slack (125+820+15+960=1920). Simply adding a margin here would
   overflow the row by 20px, so this panel's own width drops 820->800 in the
   SAME change to keep the row at exactly 1920 (125+20+800+15+960=1920).
   Combining a 20px margin-right with a 20px narrower width was chosen
   specifically so this panel's LEFT edge (the one .player-number-tile.lead
   — "row 7" — pins itself to, see that rule's own v1.0.441 comment) does
   NOT move: right edge moves 1795->1775 (opening the gap against
   .player-number-tiles, which is unchanged), left edge stays exactly 975
   (975 = 1775 - 800, same as before: 975 = 1795 - 820). Verified with the
   headless-render harness. Because that invariant left edge doesn't move,
   row 7's own CSS needs no matching change here, unlike the 15px shift
   above (which did move that edge and needed a compensating change). */
/* v1.0.444 — 800px/20px margin -> 820px/0 margin, per explicit request +
   on-device report that the real gap reads as ~30px (wider than this
   20px design value — another case of the real TV rendering differently
   than the harness, like the flex-gap bug): closes the gap entirely by
   growing this panel back to its pre-v1.0.442 width (820px), an
   experiment to see if the real device's gap actually comes from
   somewhere else, not from this panel/margin pairing at all. Right edge
   moves 1775 -> 1795 (flush against .player-number-tiles again); LEFT
   edge is untouched either way (975 = 1795-820, same as 975 = 1775-800),
   so row 7 needs no matching change, same invariant as v1.0.442's own
   comment above.
   v1.0.444 — 820px -> 830px, EXPERIMENT, per explicit follow-up request
   ("את מבנה מספר 1 תגדיל בעשרה פיקסלים נוספים ימינה") on top of the same
   real-device "extra room" theory that motivated .player-number-tiles' own
   new margin-right:-5px (see its v1.0.444 comment). NOTE, stated plainly
   rather than silently resolved: this panel's right edge was flush against
   .player-number-tiles' own (nominal, pre-shift) left edge at 1795 — growing
   ONLY this panel by 10px moves this right edge to 1805, which is now 10px
   PAST that old boundary; .player-number-tiles' own left edge also moved (it
   shifted right by margin-right:-5px), so the two structures will overlap by
   some amount unless there truly is real slack absorbing it on the real
   screen — exact resulting numbers verified with the headless-render harness
   before this build ships, reported alongside it.
   v1.0.444 — 830px -> 850px, per explicit request ("תגדיל את מבנה 1 ב-20
   פיקס ימינה"): grows this panel another 20px. Per explicit instruction,
   the day tiles (.player-list-day-tile) must NOT grow with it — see that
   rule's own v1.0.444 comment for how the freed width is redirected into
   the gaps between them instead.
   v1.0.444 — NEW, per explicit report that the above growth showed up on
   this panel's LEFT edge instead ("הגדלת את מבנה 1 לכיוון שמאל ואני
   ביקשתי לימין"): this panel's right edge is flush (margin-right:0)
   against .player-number-tiles per the headless harness (order:0, pinned
   to the screen's own right edge), so any flex-basis growth can only
   extend the FAR edge (left, toward .player-video-area) in that harness
   model. Per explicit follow-up ("פשוט תזיז את מבנה 1 20 פיקס ימינה"),
   first tried margin-right:-20px to physically slide the whole box 20px
   right (left edge back to 965, right edge 1795->1815). REPORTED BROKEN
   on the real device ("לא זז כלום") — with an on-device PHOTO proving why:
   .player-list-rows' own boxes are NOT actually flush against
   .player-number-tiles on the real screen at all, there's a real, clearly
   visible gap between them (rows' right edge vs the tile column's left
   edge) that the headless harness has never shown (harness always reports
   gapPanelToNumTiles:0 here). This is the SAME "real-device extra room"
   mystery already flagged once before, unresolved (see the v1.0.444
   800px/20px-margin -> 820px/0-margin comment above: closing the margin
   to 0 back then did NOT visibly close the on-device gap either, meaning
   this gap does not come from this margin-right value at all, on the real
   device — so the -20px shift above landed entirely inside that same
   invisible-to-the-harness slop and produced no visible change, exactly
   matching the "לא זז כלום" report).
   v1.0.444 — DIAGNOSTIC EXPERIMENT, margin-right -20px -> -60px: on-device
   photo (pixel-measured precisely, not eyeballed — row box's right border
   to the tile column's left border) proved the real gap is a FIXED ~30px
   at margin-right:-20 AND at -60, byte-for-byte the same as it was at the
   original margin-right:0 baseline months ago. CONCLUSION: negative
   margin-right on this flex item (flex:0 0 Npx + margin) is simply not
   respected by this TV's old WebKit — same class of bug as the confirmed
   flex-`gap` issue, just on a different property. Positive margin-right
   DID move things (see the v1.0.442 800px/20px-margin comment above), so
   the bug is specifically about the negative case on this flex-basis item.
   v1.0.444 — NEW, switching mechanism per explicit request ("תזיז אותו
   ימינה ב-10 פיקס נראה"): margin-right reverted to 0 and tried
   position:relative + left:10px instead — a completely different CSS
   mechanism from margin. ALSO REPORTED "לא זז" (no visible change) on the
   real device. Two independent offset mechanisms (negative margin, then
   position:relative+left) have now both failed to move anything visible
   on this TV, while the one thing that HAS reliably moved something
   visible this whole session is a plain SIZE change (flex-basis 830->850
   moved the left edge; this exact panel's own history). Strong signal:
   this old WebKit's flex-item OFFSET handling (margin used for shifting,
   position:relative) is broken/ignored here, independent of which
   property does it — but its SIZE handling (flex-basis) works correctly.
   v1.0.444 — NEW, switching to a size-based lever per the same request:
   position:relative/left removed entirely; flex-basis 850px -> 840px
   instead. Since this panel's right edge is pinned flush against
   .player-number-tiles (confirmed working both in the harness and, via
   the very first growth report this round, on the real device too), a
   10px NARROWER box necessarily moves the LEFT edge 10px to the right —
   using the one lever already proven to actually render on this TV,
   instead of an offset property that's now proven not to. Left edge:
   945 -> 955 (still 10px short of the original 965, i.e. a real 10px
   rightward move of the visible boundary, just achieved by shrinking
   instead of shifting).
   v1.0.444 — NEW, 840px -> 850px again, paired with freeing real room on
   .player-number-tiles' own side this time (see its big v1.0.444 comment
   — container shrunk 125->115, tile's own margin-left dropped 10->0 so
   the TILES don't move, only the container's invisible edge does):
   .player-number-tiles' left edge is now 1805 (was 1795), so this panel's
   right edge (flush, margin-right:0) lands at that same new 1805 — which
   is now also exactly the tiles' own VISIBLE left edge (no more 10px
   invisible-container-vs-visible-tile mismatch). Left edge: 955 -> 955
   (unchanged numerically, since both edges moved by the same 10px), but
   the panel's content should now sit genuinely flush against the tiles'
   real visible edge instead of stopping 10px short of it. .swapped
   mirrors the same 850px (this panel's own width is shared by both
   modes, only which neighbor pins which edge differs — see earlier
   comments for that geometry; the swapped-side room-freeing wasn't
   addressed this round, see .player-number-tiles' own comment). */
/* v1.0.448 — REVERTED all the way back to 830px, per explicit request
   ("תחזיר למה שהיה לפני השינוי שציינתי") after the whole point of this
   segment — growing this panel 830px->850px plus evenly redistributing
   the day-tile gap to match — was, for the FIRST time, actually tested
   with genuinely-confirmed-fresh CSS (the css/style-1.0.444.css stale
   <link> bug this whole segment turned out to be caused by is now fixed
   and independently verified via the on-screen "CSS: 1.0.447" badge) and
   STILL looked like nothing had changed ("אותו הדבר"): so the original
   830->850 growth this whole segment was built on top of apparently never
   produced a visible difference either, once tested for real. Reverting
   the whole thing rather than continuing to build more changes on top of
   a premise that turned out not to hold. .player-list-day-tile's own gap
   math (further below) goes back to a flat 8px to match — see its own
   matching v1.0.448 comment; the flex-gap-doesn't-work-on-this-TV fix
   that same margin-based mechanism also carries is NOT reverted, only the
   redistribution amount is, since that bug is real and unrelated to
   whether this panel is 830 or 850. */
/* v1.0.451 — 830px -> 815px, per explicit request ("רק תקטין את 1 ב-15"):
   a plain, standalone shrink of this panel's own width, unrelated to the
   structure-5 push-left saga above (that one stands as shipped in
   v1.0.450, left as-is per his own decision). Shrinking flex-basis moves
   this panel's own LEFT edge inward by 15px (its right edge stays flush
   against .player-number-tiles' left edge, unaffected by this change). */
/* v1.0.469 — 815px -> 719px, per explicit request ("זה צריך להתאים לחלל
   שנוצר בין מבנה 1 לבין שורה 10 ושיהיה סימטרי בגובה ורוחב שווה בין מבנה 1
   לטלויזיה"): .player-video-area grew +96px (960->1056, see its own
   v1.0.469 comment) — this row's flex items were hand-tuned to sum to
   exactly 1920px with ~0 slack (125 number-tiles + 815 this panel + 15 gap
   + 960 video = 1915) — but per his explicit follow-up on the TV's own
   left-side gap ("שהרווח שלה בצד שמאל יהיה בדיוק אותו רווח כמו שיש לנו
   במבנה מספר חמש בצד ימין"), .player-video-area also gained a NEW 15px
   left-side margin off the screen's own edge, matching
   .player-number-tiles' own confirmed 15px right-side margin (see both
   rules' own matching v1.0.469 comments) — together the +96px growth AND
   this new 15px margin (111px total) are reclaimed here, shrinking this
   panel back down to keep the row from overlapping/overflowing (this
   panel's own right edge stays flush/unmoved against
   .player-number-tiles, margin-right:0 unchanged, so the whole 111px
   shrink comes out of the LEFT edge, which moves 965->1086). Row 7's own
   width shrinks by this identical 111px in the same round to keep its
   long-standing "row 7 left == panel left" invariant intact — see its own
   matching v1.0.469 comment. Verified total: 125 number-tiles content +
   15 its own margin-right + 694 this panel + 15 gap-to-video + 1056 video
   + 15 video's new left margin = 1920, exactly filling the screen. */
/* v1.0.470 — 694px -> 815px, REVERT per explicit, repeated instruction
   ("לא לשנות את מה שבנינו כבר... מבנה אחד... לא לשנות" — "don't touch what
   we already built, structure 1, don't change it"): the v1.0.469 shrink
   (815->694, to make room for the enlarged TV) is fully undone — this
   panel's own width goes back to its long-standing original value,
   untouched by the TV's own resize from here on (see #video's own
   matching v1.0.470 comment — the TV alone absorbs its own size change
   now, never this panel or row 7). margin-right stays 0 (right edge still
   flush against .player-number-tiles, unaffected either way). */
#screen-player.list-mode .player-list-panel { flex: 0 0 815px; margin-right: 0; }
/* v1.0.445 — REVERTED back to 850px, per explicit request ("תחזיר רגע את
   מבנה 1 למה שהיה כולל ביטול שינוי הרווחים בימים, היה סימטרי והיה טוב"):
   the whole 840px experiment (this round) AND the paired
   .player-number-tiles push-left (115px->125px, its own margin-right
   5px->15px, row 7's right/width) were installed as a genuinely fresh
   version (1.0.445, ruling out the earlier version-caching suspicion) and
   STILL reported zero visible change on the real device — so this isn't
   "the fix didn't get picked up", it's "this exact lever didn't do
   anything visible", and repeating it again wasn't going to produce a
   different result. Per explicit request, reverting the whole chain back
   to the last confirmed-good, symmetric state instead of continuing to
   chase it blind: back to 850px here, .player-number-tiles back to its
   own original 125px (see its matching v1.0.445 comment), the plain
   tiles' margin-right back to 5px and row 7's right/width back to
   5px/950px (see their own matching v1.0.445 comments), and
   .player-list-day-tile's own gap math back to its original 11.33px
   (matches this 850px width again, see its own matching v1.0.445
   comment). This is the same geometry as right after the very first
   830px->850px growth earlier this version — the last state actually
   confirmed to render correctly on the real device — used here as the
   clean starting point for whatever's tried next, instead of any of the
   since-abandoned gap-closing attempts.
   v1.0.448 — this rule's own actual declaration was moved up to right
   after this comment block's own start (see the v1.0.448 comment and
   rule right above, now 830px) so it wins the cascade over this one —
   this comment block is kept in place for its history, but no longer
   ends in its own separate declaration. */
#screen-player.list-mode.swapped .player-list-panel { margin-right: 0; margin-left: 0; }
/* v1.0.469 — margin-right: 0 -> 15px, per his explicit follow-up on the
   TV's own screen-edge gap: in swapped mode .player-video-area is the
   FIRST (rightmost) item in this flex row (order:1, nothing ahead of it —
   see .player-number-tiles' own order:3 swapped comment), so its own
   margin-right here is the outer screen-edge inset on that side, mirrored
   from the unswapped mode's margin-left:15px on the same rule's base
   declaration. margin-left (the gap to .player-list-panel, which sits to
   its left in this mode) stays 15px, unchanged. */
/* v1.0.470 — margin-right: 15px -> 0, REVERT: the v1.0.469 screen-edge
   inset for swapped mode is undone along with the rest of that round's TV
   growth (see #video's own big matching comment) — back to flush against
   the screen's right edge in swapped mode, exactly as originally.
   margin-left (the gap to .player-list-panel) stays 15px, unchanged. */
#screen-player.list-mode.swapped .player-video-area { margin-right: 0; margin-left: 15px; }
#screen-player.list-mode.swapped .player-list-panel { order: 2; }
#screen-player.list-mode.swapped .player-video-area { order: 1; }
/* v1.0.434 — NEW, per explicit request: the 125px strip that used to be
   blank margin-right on .player-list-panel (see the v1.0.431 comment
   above) now hosts a real flex child — a column of channel-number tiles
   (#playerNumberTiles, see index.html). order:0 puts it before
   .player-list-panel along this dir="rtl" page's start edge, i.e. it
   renders at the screen's true right edge (1795-1920px), exactly where
   the old blank margin used to be — so .player-list-panel's own
   margin-right above is now 0, this element supplies that space for
   real instead. .swapped mirrors it to the opposite (left) edge with
   order:3 (after both other panels), same mirroring convention as
   everything else in this block. */
#screen-player.list-mode .player-number-tiles { order: 0; }
#screen-player.list-mode.swapped .player-number-tiles { order: 3; }
/* v1.0.444 — the previous margin-right:-5px experiment (and its swapped
   mirror that used to sit here) was REVERTED per explicit request to start
   over ("מתחילים מחדש") — this container carries no margin-right/-left
   override any more, in either mode. */
#screen-player.list-mode .player-number-tiles { display: flex; }
/* v1.0.441 — per explicit request ("מבנה 5 בלבד... ותדחוף את כל המבנה של
   5... ב-15 פיקס שמאלה"): shifts the WHOLE column (structure 5 only —
   .player-list-panel/.player-video-area are untouched) 15px toward the
   screen's center, without disturbing either neighbor's own flex sizing.
   FIRST ATTEMPT put position:relative;right:15px on THIS container
   (#playerNumberTiles) directly — BUG, per explicit report ("יש פס שחור
   שמסתיר את שורה 7 כמעט עד הסוף"): .player-number-tile.lead ("row 7") is
   one of THIS element's own DOM children (see renderPlayerListNumberTiles)
   and relies on being positioned against #screen-player instead (see its
   own long-standing comment) — making this container position:relative
   made IT row 7's new containing block/paint ancestor instead, which broke
   row 7's stacking relative to .player-list-panel's own opaque background
   and left most of it painted over. Reverted: this container stays
   position:static (untouched otherwise), and the 15px shift is applied to
   each PLAIN tile individually instead (see
   `.player-number-tile:not(.lead)` right below) — position:relative on a
   plain tile has nowhere near the same blast radius, since none of them
   have any absolutely-positioned children of their own. Row 7 itself gets
   its own matching 15px shift directly on ITS OWN right/left (back against
   #screen-player, right next to its own rules) instead of inheriting it
   from this container. */
/* v1.0.441 — NEW, see the big comment right above for why this replaced
   shifting the container: each plain (non-lead) tile gets its own 15px
   inward relative shift instead, achieving the same visual column move
   without changing anyone's containing block. */
/* v1.0.444 — BUG FIX, per explicit report + photo evidence ("קוביות בלי
   פינות עגולות בצד שמאל" / straight cut edge instead of a rounded corner,
   specifically on the side facing .player-list-panel): `position:relative;
   right:15px` moves this tile's ENTIRE 125px-wide box (its align-items:
   stretch width, matching .player-number-tiles' own column width) 15px to
   the left — so its LEFT 15px now sits OUTSIDE the container's own bounds
   (container is exactly 125px wide), and .player-number-tiles' own
   `overflow:hidden` (needed for the vertical windowing scroll — see that
   rule's own comment) clips it, cutting straight through the middle of the
   tile's box instead of showing its real (rounded) left border, which is
   now past the clip edge and invisible. Desktop rendering hid this because
   the harness never checked for a HORIZONTALLY clipped tile, only vertical
   positions. Fix: don't move the box at all — just make it 15px NARROWER
   via margin-right instead, so align-items:stretch computes a 110px width
   anchored at the container's own left edge (never overhanging), while the
   RIGHT edge still lands exactly 15px in from the container's right edge —
   same visual result (right edge matching row 7's own right edge, see that
   rule's v1.0.441 comment) with zero overhang, so nothing needs clipping
   and the rounded corners stay intact on both sides. z-index:5 kept (see
   its own v1.0.444 comment); position:relative dropped, no longer needed
   for this. */
/* v1.0.444 — REVISED per explicit follow-up ("מבנה 5 תגדיל רק את השורה שלו
   ב-10 פיקסלים את שאר הקוביות שלו פשוט תזיז ימינה 10 פיקס"): the container
   itself is NOT widened this round (see .player-number-tiles' own reverted
   135px attempt right below) — only row 7 (.player-number-tile.lead, its
   own rule further down) grows by 10px. These plain tiles instead just MOVE
   10px right, same width as before (110px). Deliberately NOT done with
   `position:relative; right:-10px` (a physical box-move) even though
   position:relative is already set here — that exact mechanism (move the
   box instead of resizing it) is what caused the v1.0.443 overhang-clipping
   bug fixed earlier this version (see the big comment above). Done with
   margins instead, same safe pattern as that fix: margin-left:10px pushes
   the box 10px right, margin-right drops from 15 to 5 so the total width
   stays 125-10-5=110 (unchanged) — box now sits comfortably inside the
   container's own 125px width the whole time (10px gap on the left, 5px on
   the right), nothing ever reaches the container's edges, so
   overflow:hidden below has nothing to clip. New right edge: container's
   own right edge (1920, unchanged) - 5 = 1915, matching row 7's own new
   right edge exactly (see its v1.0.444 comment) — same alignment invariant
   this column has kept all along. */
/* v1.0.444 — NEW, per explicit go-ahead ("תנסה מה שאתה יכול") to free real
   room for .player-list-panel to grow right into, after margin/position
   shifts on the PANEL side were both confirmed dead on the real device
   (see its own big v1.0.444 comment): this time using the one lever
   that's actually worked all session — a size change, not an offset —
   and applying it to THIS side of the boundary instead.
   margin-left 10px -> 0px here (the margin facing .player-list-panel,
   NOT the margin-right:5px bezel-safety one facing the screen's own right
   edge, which stays untouched). Paired with .player-number-tiles' own
   flex-basis 125px -> 115px right below (a positive resize, same family
   as the 830->850 growth that DID render): container.left moves from
   1795 to 1805 (10px right, since its right edge is pinned to the screen
   edge), and losing 10px of margin-left exactly cancels that same 10px
   for the tile's own visible position (tile.left = container.left +
   marginLeft = 1795+10 = 1805 before, 1805+0 = 1805 after) — so these
   tiles do not move or resize at all, only the invisible container
   boundary they sit inside does, freeing 10px for the panel. .swapped's
   own margin override (5px/10px) is left untouched: in swapped mode this
   container's pinned edge is the one facing .player-video-area, not
   .player-list-panel, so shrinking it here doesn't free panel-side room
   there the same way — not addressed by this change, flagged rather than
   guessed at. */
/* v1.0.444 — NEW, per explicit follow-up request ("תקטין את רוחב מבנה 1
   ב10 פיקס ותדחוף את מבנה 5 ב10 פיקס שמאלה") after the previous
   container-shrink-only attempt (which deliberately kept these tiles
   visually static) reported "לא זז" again: margin-right grew 5px->15px so
   the tiles themselves would actually move 10px left this time, paired
   with .player-number-tiles' own container growth. Installed as a
   genuinely fresh version (1.0.445) and STILL reported zero visible
   change.
   v1.0.445 — REVERTED both margins back to their original values
   (margin-left 0px->10px, margin-right 15px->5px), per explicit request
   ("תחזיר רגע את מבנה 1 למה שהיה... היה סימטרי והיה טוב") to abandon this
   whole gap-closing chain and go back to the last confirmed-good state:
   tile.left = container.left(1795) + marginLeft(10) = 1805, tile.right =
   1920 - marginRight(5) = 1915 — the exact original geometry from right
   after the 830px->850px panel growth, before any of this segment's
   boundary-gap experiments. */
/* v1.0.449 — TRIED margin-right 5px -> 20px here as the push-left
   mechanism, REVERTED in v1.0.450: he explicitly clarified that ONLY row 7
   should shrink — the plain tiles themselves must stay their original
   size, with the whole structure 5 column translated left as a unit
   instead. Back to 5px. See .player-number-tiles' own new margin-right
   below for the actual push-left mechanism now. */
.player-number-tile:not(.lead) { margin-left: 10px; margin-right: 5px; margin-bottom: 10px; z-index: 5; position: relative; }
#screen-player.list-mode.swapped .player-number-tile:not(.lead) { margin-left: 5px; margin-right: 10px; }
.player-number-tiles {
  display: none;
  /* v1.0.444 — 115px -> 125px, per explicit follow-up request, paired that
     round with .player-number-tile:not(.lead)'s own margin-right growing
     5px->15px so the tiles would visibly shift left. Installed fresh as
     1.0.445 and still reported no visible change.
     v1.0.445 — this container's own 125px happens to already equal its
     true original (pre-saga) value, so no number changes here on this
     revert — but see .player-number-tile:not(.lead)'s own matching
     v1.0.445 comment: its margins DID revert (back to 10px/5px), so the
     actual rendered tile position is back to the original 1805/1915
     geometry, not the 1795/1905 this round's push-left attempt produced
     despite this same 125px width. */
  flex: 0 0 125px;
  min-width: 0;
  flex-direction: column;
  height: 100%;
  box-sizing: border-box;
  /* v1.0.444 — NEW, per explicit request ("תדחוף כלפי מעלה ב-10 פיקס גם את
     5 וגם את 1"): pushes this whole column 10px above the screen's own top
     edge, same safe mechanism as the earlier (reverted) "raise 5px"
     experiment — a negative margin-top on the container itself, never
     position:relative on it (that's the already-fixed v1.0.441 bug, see the
     big comment further below). Row 7 (.player-number-tile.lead) is NOT a
     child of this box positionally (absolute against #screen-player) so it
     needs its own matching -10px nudge directly on its own `top` instead —
     see its v1.0.444 comment.
     v1.0.451 — -10px -> 5px, per explicit request ("את מבנה 1 ואת מבנה 5
     תוריד למטה גם ב-15 פיקס"): moves this whole column DOWN 15px from
     where it currently sits (-10 + 15 = 5), same safe margin mechanism.
     Matches .player-list-panel's own identical v1.0.451 change, and row
     7's own matching +15px `top` nudge (see .player-number-tile.lead's
     own v1.0.451 comment) since it isn't a flex child of this box and
     doesn't move with this margin on its own. */
  margin-top: 5px;
  /* v1.0.450 — NEW, per explicit clarification ("לקצר את שורה 7 בלבד
     ולדחוף את המבנה כולו 15" — shorten ONLY row 7, and push the WHOLE
     structure 5 by 15, as two separate things): v1.0.449's attempt used
     margin-right on the individual plain tiles, which shrank them too —
     wrong, per his correction. This container itself is order:0, pinned
     flush to the screen's own right edge (1920) with nothing after it, so
     a positive margin-right here (the same safe, confirmed-reliable
     mechanism used everywhere else on this TV) translates the WHOLE column
     — box, padding, every tile inside it, all as one unit — 15px left
     without resizing anything inside it. New right edge: 1920-15=1905 (was
     1920); new left edge: 1795-15=1780 (was 1795). Row 7
     (.player-number-tile.lead) is positioned against #screen-player, not a
     child of this flex box, so it does NOT move with this margin — it gets
     its own separate treatment, see its own v1.0.450 comment. */
  margin-right: 15px;
  /* v1.0.444 — briefly widened to 135px as an experiment, REVERTED back to
     125px per explicit follow-up request ("מבנה 5 תגדיל רק את השורה שלו...
     את שאר הקוביות שלו פשוט תזיז ימינה"): the container itself stays its
     original width — only row 7 grows and the plain tiles shift within it,
     see the new comment on `.player-number-tile:not(.lead)` right above. */
  /* v1.0.439 — structure 5 raised back up to the screen's own top edge, per
     explicit request ("נרים את מבנה חמש למעלה לקצה של המסך"): row 6
     (#playerInfoRow) is gone entirely — its whole job (channel/programme
     info line) was folded into the number column's OWN first tile instead
     (see .player-number-tile.lead below), which is now what this
     padding-top clears space for — not a separate freestanding row above
     both structures any more. See .player-number-tile.lead's own comment
     for the exact height this reserves.
     v1.0.440 — 78px -> 90px: .player-number-tile.lead ("row 7", per his own
     new name for it) grew 68px -> 80px so it's exactly as tall as the
     plain tiles beneath it, per explicit request — this still just
     reserves that tile's own height + the same 10px gap as before.
     v1.0.440 — 90px -> 110px, per explicit report ("שורה 7 יושבת ממש על
     הקצה העליון של המסך אין רווח"): row 7 itself moved top:0 -> top:20px
     for its own breathing room from the screen edge (see its own matching
     comment) — this grew by that same 20px so the gap between row 7 and
     the tabs row stays exactly what it was before.
     v1.0.440 — 110px -> 120px, per explicit request ("כל המרווחים...
     שיהיו סימטרים ביחס בינהם לבין... קצה עליון או תחתון של המסך"): the
     20px top margin (screen edge -> row 7) and the old 10px gap (row 7 ->
     this column's own first tile) were two different values — unified to
     the SAME 20px, so every gap in this stack (top margin, row 7 -> tiles,
     and — see padding-bottom right below — tiles -> bottom margin) is
     identical: row 7's own top:20px + height:80px + this 20px gap = 120.
     Verified exactly with the headless-render harness.
     v1.0.444 — 120px -> 110px, per explicit follow-up report that the
     row7->tabs gap still reads as too large next to the 10px row-to-row
     gap: shrinks JUST that gap 20px -> 10px (row 7's own top:20px stays
     unchanged — separate pending 15px discussion): 20 + 80 + 10 = 110.
     Matches .player-list-panel's own identical change.
     v1.0.444 — 110px -> 113px, matching row 7's own height 80->83 change
     (uniform +3px height bump): 20 + 83 + 10 = 113, keeps the row7->tabs
     gap at exactly 10px. Matches .player-list-panel's own identical
     change. */
  padding-top: 112.5px;
  /* v1.0.440 — NEW, same explicit request: a matching 20px margin at the
     BOTTOM edge too, symmetric with the top — confirmed worth it even
     though it shows one less tile than before. Shrinks this column's own
     available height by 20px at the bottom; overflow:hidden (below) means
     anything beyond that simply isn't rendered, no separate JS change
     needed.
     v1.0.462 — 20px -> 112.5px, per explicit request to cut one plain tile
     (PLAYER_LIST_NUMBER_TILE_SLOTS 11 -> 10, see its own matching v1.0.462
     comment in app.js): +92.5px, exactly one tile's own step (82.5px
     content height + 10px margin-bottom). Matches .player-list-panel's
     own identical change below. The freed strip is where the new
     .player-list-row8 sits — see its own comment for the exact math. */
  padding-bottom: 112.5px;
  /* v1.0.443 — BUG FIX, per explicit report + photo evidence ("המרווחים בין
     קוביות הערוצים במבנה 5 קטנות יותר ממרווחי שורות מבנה 1"): this used to
     be `gap: 10px` here on the flex container. On a desktop/modern Chromium
     (this project's own headless-render harness included) that renders
     identically to margin-bottom — which is exactly why the harness kept
     reporting equal 10px gaps and never caught this. But `gap` on a FLEX
     container (as opposed to a CSS Grid container) is a newer addition to
     the spec than flexbox itself, and this TV's own much older embedded
     browser almost certainly silently ignores it — collapsing every tile
     flush against the next with ~0px between them, while .epg-row right
     below keeps working because it uses a plain margin-bottom instead,
     which has been supported forever. Real hardware photo confirmed it:
     row gaps clearly visible, tile gaps nearly touching. Fixed the same way
     .epg-row already does it — see `.player-number-tile:not(.lead)` right
     above, which now carries `margin-bottom: 10px` instead of this `gap`.
     Removing `gap` entirely (not just leaving it alongside the new margin)
     is deliberate: a browser that DOES support flex gap would otherwise
     apply both and double the space to 20px. */
  overflow: hidden; /* windowing is done in JS (renderPlayerListNumberTiles) — sticky-top scroll, not a real scrollbar */
  /* v1.0.444 — TRIED position:relative + z-index here to fix a "structure 5
     cut off / black stripe" report, then REVERTED in the same version:
     .player-number-tile.lead ("row 7") is one of THIS element's own DOM
     children but relies on being positioned against #screen-player instead
     (see its own long-standing comment, and the big v1.0.441 comment right
     above `.player-number-tile:not(.lead)`) — making THIS container
     position:relative hijacks it as row 7's new containing block, which is
     the EXACT bug v1.0.441 already fixed once ("יש פס שחור שמסתיר את שורה 7
     עד הסוף"). Missed that history when adding this. Reverted to
     position:static; z-index moved onto the individual tiles instead (see
     `.player-number-tile`/`.player-number-tile.lead` below), same pattern
     v1.0.441 already established for the 15px shift — position:relative on
     a single plain tile has nowhere near the same blast radius. */
}
.player-number-tile {
  flex: 0 0 auto;
  text-align: center;
  /* v1.0.441 — 25px -> 27px, per explicit request ("תגדיל את גובה השורות גם
     של מבנה 5 וגם 1 במעט..."): kept in lockstep with .player-list-rows
     .epg-row's own identical v1.0.441 change right below, per this rule's
     own long-standing "matches .epg-row's vertical padding" convention. */
  padding: 27px 8px; /* matches .player-list-rows .epg-row's own vertical padding, for rough row alignment */
  border-radius: 12px; /* matches .epg-row/.player-list-day-tile */
  font-size: 1.4rem; /* matches .player-list-day-tile's own bigger-number treatment */
  /* v1.0.443 — NEW, per explicit report that this tile looks a hair taller
     than .epg-row even with identical padding/border: the two use DIFFERENT
     font-sizes (this is 1.4rem, .epg-row is 1.35rem), so with no explicit
     line-height either box's actual line box comes from the browser's own
     "normal" line-height for ITS OWN font-size — which is normally close
     enough to round to the same integer px on a desktop browser (confirmed
     with the render harness: both measured 84px there), but a different
     browser engine (this TV's, much older than the harness's) can round
     that differently, since "normal" is intentionally left engine-defined
     by the CSS spec. Pinning both to the exact same fixed px line-height
     (not em/rem, so it's completely independent of either element's own
     font-size) makes the two heights identical by construction instead of
     by coincidental rounding — see .epg-row's own matching v1.0.443 change. */
  line-height: 28.5px;
  font-weight: 700;
  background: var(--card);
  color: #fff;
  border: 2px solid var(--border);
  direction: ltr; /* plain digits — same reasoning as .player-list-day-tile above */
}
/* v1.0.451 — CHANGED, per explicit request ("את המסגרת הכחולה של הפוקוס
   תבטל, במקומה יהיה הצבע האפור הבהיר של הטאב של הארכיון" — cancel the blue
   focus frame, use the archive tab's own light-gray color instead): drops
   the purple border+glow, replaced with the exact same light-gray
   background .player-list-tab.active already uses — one consistent "this is
   what's focused" visual language across the whole player screen instead of
   a blue frame. Applied identically to .player-list-tab.focused,
   .player-list-day-tile.focused and .epg-row.focused below — NOT to the
   home-screen grid's .row-channel.focused (out of scope, different screen)
   or the small icon-only focus states (progress arrow, restart/pause icons,
   etc.) which were never a border/frame to begin with. */
.player-number-tile.focused { border-color: var(--border); background: rgba(255,255,255,0.16); box-shadow: none; }
/* v1.0.459 — NEW, per explicit request ("כשנכנסים למבנה 5 כל הקוביות שלהם
   ידלקו באפור בהיר גם" — clarified: structure 5's own number-pad tiles,
   not the day-tiles; and, per explicit follow-up, "כולל השורה שבפוקוס" —
   the focused tile lights up too, not excluded). `.structure5-active`
   toggled on `.player-number-tiles` itself (see updatePlayerListFocusUI in
   app.js) whenever focus is actually on structure 5. Applies to every tile
   including `.focused` (higher selector specificity here — 3 classes vs.
   `.focused` alone's 2 — would otherwise silently override it), so the
   second rule below re-applies `.focused`'s own stronger translucent-white
   overlay on top once structure 5 is active: the focused tile still
   visibly stands out brighter than the rest, it just isn't left OUT of
   "all the tiles light up" any more.
   v1.0.461 — the base (non-focused) background switched from a
   translucent white wash to a solid var(--muted) — see this rule's own
   v1.0.461 comment right below for why.
   v1.0.463 — CHANGED BACK, per explicit report that var(--muted) reads as
   "too light gray" ("אני לא רוצה אפור בהיר מדי"): he wants the exact same
   gray used everywhere else in this app for "this has focus" (row 7's own
   .player-number-tile.focused, .epg-row.focused, .player-list-tab.active —
   all rgba(255,255,255,0.16), a translucent white wash over the dark card,
   not a solid mid-gray fill) — "אותו אפור שהפוקוס נמצא עליו בשורה 7 אותו
   הצבע שיהיה בפנים". Base and .focused below now share the literal same
   background value; the focused tile still stands out via its own white
   border only (see the rule right below), not a different fill anymore. */
.player-number-tiles.structure5-active .player-number-tile {
  background: rgba(255,255,255,0.16);
}
.player-number-tiles.structure5-active .player-number-tile.focused {
  background: rgba(255,255,255,0.16);
  /* v1.0.460 — NEW, per explicit follow-up request ("הקוביה הראשונה
     שאנחנו בפוקוס עליה... תהיה עם מסגרת לבנה"): the one tile that actually
     has focus also gets a white border, on top of its own already-brighter
     background, so it reads unambiguously as "this one" against the dimmer
     rest of the column. */
  border-color: #fff;
}
/* v1.0.460 — NEW, per explicit follow-up request ("כשאנחנו לא בפוקוס
   במבנה חמש, כל הספרות יהיו מעומעמות בצבע אפור בהיר כמו בארכיון"): the
   plain digit tiles dim to the same muted gray the archive's day-tile
   numbers/rows already use once focus moves away from them, mirroring
   that exact convention but in reverse — here it's structure 5 itself
   dimming while focus is OUTSIDE it, not while focus is on it.
   `:not(.lead)` excludes row 7, whose content swaps entirely for a
   different reason in this same state (see its own v1.0.460 comment in
   app.js / .lead-arrow below) rather than just dimming in place. */
.player-number-tiles:not(.structure5-active) .player-number-tile:not(.lead) {
  color: var(--muted);
}
/* v1.0.439 — NEW, replaces the old standalone #playerInfoRow ("row 6"),
   cancelled per explicit request ("אני חושב את שורה שש נבטל לגמרי"). That
   row's whole job — a wide info line above structures 1+5 showing
   channel+programme, switching between the highlighted number-tile channel
   and the actually-playing one depending on focus (see the removed
   updatePlayerInfoRow) — now belongs to the number column's OWN first tile
   (i===0 in renderPlayerListNumberTiles): same switching logic, same
   945px combined-structures width and card look row 6 had, but this is a
   REAL, focusable, Enter-able tile now — not a separate inert element —
   since it always represents whichever channel is showing at the top slot
   of the numbers window (playerListNumbersWindowStart), the exact same
   channel Enter already switches to via activatePlayerListNumberTile.
   Positioned absolutely against #screen-player (its own already-established
   positioned ancestor — the same escape row 6 used), NOT a normal flex
   child, so it overlays clear across both structures 1+5 without disturbing
   either one's own column layout — the padding-top on both
   .player-number-tiles/.player-list-panel above/below is what reserves
   this tile's own height so nothing sits underneath it. */
.player-number-tile.lead {
  position: absolute;
  z-index: 5; /* v1.0.444 — matches .player-number-tile:not(.lead)'s own identical addition, same reasoning */
  /* v1.0.440 — 0 -> 20px, per explicit report ("שורה 7 יושבת ממש על הקצה
     העליון של המסך אין רווח"): a little breathing room from the screen's
     own top edge, matching the gap .player-list-panel's own padding-top
     historically left there before row 6/7 ever existed (see its own
     v1.0.421/425 comments). .player-number-tiles/.player-list-panel's
     padding-top grew by this same 20px so the gap to the tabs row stays
     exactly what it was — see their own matching v1.0.440 comments.
     v1.0.443 — kept at 20px (a draft 10px was tried and reverted): the
     20/15/15 top/bottom/right target he set separately is its own pending
     change, not part of this build — left at the last confirmed-correct
     value so this build doesn't ship an unconfirmed number.
     v1.0.444 — briefly tried 20px -> 15px as part of the reverted "raise
     5px" experiment, back to 20 in between, then 20px -> 10px here per
     explicit follow-up request ("תדחוף כלפי מעלה ב-10 פיקס גם את 5 וגם את
     1"): row 7 is positioned against #screen-player directly, not against
     .player-number-tiles, so it does NOT move when that container gets its
     own new margin-top:-10px (see its v1.0.444 comment) — needs this
     identical -10px nudge on its own top here so it stays lined up with the
     plain tiles beneath it, which DO move via the container's margin.
     v1.0.451 — 10px -> 25px, per explicit request ("את מבנה 1 ואת מבנה 5
     תוריד למטה גם ב-15 פיקס"): matches .player-number-tiles' own
     margin-top going -10px -> 5px (a +15px move) — row 7 still isn't a
     flex child of that container, so it needs this identical +15px nudge
     on its own `top` to stay lined up with the plain tiles beneath it. */
  top: 25px;
  /* v1.0.441 — right:0 -> right:15px, width:945 -> 930, per explicit request
     ("מבנה 5 בלבד את שורה 7 תקטין ב-15 פיקס ותדחוף את כל המבנה של 5 אחרי
     ההקטנה ב-15 פיקס שמאלה"): row 7 shrinks by 15px AND its right-side
     anchor moves 15px inward, together — the LEFT edge stays exactly where
     it was (975px, matching .player-list-panel's own left edge, untouched),
     only the right edge moves in by 15px, matching the plain tiles' own
     identical 15px inward shift (see `.player-number-tile:not(.lead)`
     above). Row 7 gets its OWN separate right:15px here instead of
     inheriting a shift from #playerNumberTiles itself — an earlier attempt
     that shifted the container directly broke row 7's stacking against
     .player-list-panel's own background (see that rule's own big v1.0.441
     comment for the full story of that bug and why this was reverted to a
     direct shift here instead). Confirmed with the headless-render harness:
     left edge stays fixed at 975px. See .lead-name's own v1.0.441 comment
     further down for why the name/programme text needs a compensating push
     in the OTHER direction to stay put, while .lead-num is deliberately
     left alone so it keeps tracking the shifted plain tiles below it.
     v1.0.444 — briefly tried 15px -> 10px as part of the reverted "5px
     right" shift experiment, back to 15 in between, then 15px -> 5px here
     per explicit follow-up request ("מבנה 5 תגדיל רק את השורה שלו ב-10
     פיקסלים"): row 7 grows 10px, extending its RIGHT edge outward
     (930->940 width, right:15->5). New right edge: 1920-5=1915, matching the
     plain tiles' own new right edge exactly (see
     `.player-number-tile:not(.lead)`'s own v1.0.444 comment for how they
     get there via a margin-based shift instead of a resize).
     v1.0.444 — BUG FIX, per explicit on-device report ("מבנה 5 זז ימינה אבל
     השורה קטנה בצד שמאל שלה ב-10 פיקס"): growing .player-list-panel by 10px
     (its own v1.0.444 comment) turned out to move ITS left edge 975->965
     (not its right edge — the flex algorithm pinned its right edge flush
     against .player-number-tiles instead, see that comment for the full
     explanation), but this rule's `right:5px` alone still left the LEFT
     edge fixed at 975 (unchanged) — so a new 10px gap opened between the
     panel's new left edge (965) and row 7's own (975), reading as row 7
     being 10px SHORTER than the panel on the left. Fix: width grows another
     10px (940->950) with `right` untouched, so only the LEFT edge moves,
     965 = 1920-5-950, now matching the panel's own new left edge exactly
     again — same "row 7 left == panel left" invariant this rule has kept
     since v1.0.441, just re-anchored to the panel's new position.
     v1.0.444 — right:5px -> 15px, width:950px -> 940px, paired that round
     with the plain tiles' own margin-right growing 5px->15px so row 7
     would keep matching their new right edge. Installed fresh as 1.0.445
     and still reported zero visible change, same as everything else in
     that round.
     v1.0.445 — REVERTED back to right:5px, width:950px, per explicit
     request ("תחזיר רגע את מבנה 1 למה שהיה... היה סימטרי והיה טוב"): the
     plain tiles' own margin-right is back to 5px too (their matching
     v1.0.445 comment), so their right edge is back to 1915 — row 7's own
     right edge needs to match that again: 1920-5=1915. Width back to
     950px keeps the LEFT edge unchanged (965, same as before). Original
     geometry, same as right after the 830px->850px panel growth. */
  /* v1.0.449 — right:5px -> 20px, width:950px -> 935px, per explicit request
     ("תקצר את שורה 7 גם ב-15" — since structure 5 as a whole is being pushed
     15px left, see .player-number-tile:not(.lead)'s own matching v1.0.449
     comment): same "shrink by 15, right anchor moves in by 15, left edge
     stays fixed" pattern as v1.0.441. New left edge: 1920-20-935=965, same
     as before (still matches .player-list-panel's own left edge). New right
     edge: 1920-20=1900, matching the plain tiles' own new right edge.
     v1.0.450 — kept unchanged: per his own explicit clarification, row 7's
     own shrink-by-15 (this rule) is entirely separate from structure 5's
     column-wide 15px push (now done via .player-number-tiles' own new
     margin-right, not by resizing the plain tiles). Row 7 still isn't a
     flex child of that container (positioned against #screen-player), so
     it doesn't move with that margin — but its right edge (1900) still
     lands exactly on the plain tiles' own new right edge after their
     column shifted left, so the two stay matched with no code change
     needed here.
     v1.0.469 — 935px -> 814px, per .player-list-panel's own matching
     v1.0.469 comment: that panel shrank 121px (815->694) to make room for
     the enlarged TV PLUS its new 15px left-edge screen margin, ALL of it
     off its LEFT edge (right edge stayed flush/unmoved) — moving the
     panel's own left edge 965->1086. Row 7's `right` stays 20px unchanged
     (its own right edge, 1900, has nothing to do with this change), so
     shrinking its width by that same 121px moves ONLY its left edge,
     keeping the long-standing "row 7 left == panel left" invariant intact
     at the new 1086 value (1900-814=1086, matching the panel's own new
     left edge exactly).
     v1.0.470 — 814px -> 935px, REVERT per explicit, repeated instruction
     not to touch structure 1 / row 7 (see .player-list-panel's own
     matching v1.0.470 comment): back to the original width, right:20px
     unchanged (right edge, 1900, untouched either way) — left edge goes
     back to 965, matching .player-list-panel's own reverted left edge
     exactly, same invariant as always. */
  right: 20px;
  width: 935px;
  /* v1.0.440 — CHANGED, per explicit request ("שורה 7 צריכה להיות בדיוק
     באותו גובה כמו הקוביות של מבנה 5"): was 68px (a guessed height,
     independent of the plain tiles below it) — now pinned to exactly the
     same 80px the plain .player-number-tile's own padding+font box
     measures out to (verified with the headless-render harness), so row 7
     and every tile beneath it are visually identical in height.
     v1.0.444 — 80px -> 83px, per explicit request to grow every matched
     row/tile/tab height by 3px uniformly (rows, tabs, day-tiles, plain
     tiles — see their own matching v1.0.444 line-height:26->29 changes):
     row 7 has no padding/line-height of its own to bump (fixed height,
     centered flex content), so its height gets the same +3 directly. See
     .player-number-tiles/.player-list-panel's own matching padding-top
     change (110->113) so the row7-to-tabs gap stays 10px, unchanged. */
  height: 82.5px;
  display: flex;
  align-items: center;
  /* v1.0.440 — BUG FIX: this class combines with the base .player-number-tile
     class (which has its own padding:25px 8px, meant for a plain centered
     digit) — without an explicit override here that padding bled through
     and threw off .lead-num's alignment against the tile below it by
     exactly its own 8px side value (caught by the headless-render
     harness). justify-content/text-align dropped too, now meaningless
     with no free space/inline content left in this flex row to apply to —
     see .lead-num's own fixed 125px box below for how alignment is
     actually achieved now. */
  padding: 0;
  box-sizing: border-box;
  direction: rtl;
  font-size: 1.1rem;
}
/* v1.0.441 — left:0 -> left:15px, mirroring the unswapped right:15px change
   right above (same reasoning — row 7 gets its own direct shift here rather
   than inheriting one from the container). */
#screen-player.list-mode.swapped .player-number-tile.lead { right: auto; left: 5px; }
/* v1.0.440 — CHANGED, per explicit request ("הספרה הראשונה תהיה בדיוק מעל
   הספרה שבקוביה שמתחתיה באמצע כמו הקוביות של שאר מספרי הערוצים"): was a
   plain inline span with a margin, sized by its own text — nothing tied its
   horizontal position to the plain number tiles below it, so the two
   digits didn't actually line up. Now a fixed 125px flex box — exactly
   .player-number-tiles' own column width, the same column the tile
   directly below it sits in — center-aligned the same way those tiles are,
   so the digit here and the digit in the tile below land in the same
   horizontal center (confirmed with the headless-render harness). */
.player-number-tile.lead .lead-num {
  flex: 0 0 125px;
  box-sizing: border-box;
  text-align: center;
  font-size: 1.55rem; /* "קצת יותר גדולה" than the plain tiles' own 1.4rem, per explicit request */
  direction: ltr;
  /* v1.0.441 — NEW, BUG FIX caught while re-measuring for this same round's
     other changes: .lead (this element's own parent) inherits the base
     .player-number-tile class's own 2px border — box-sizing:border-box on
     .lead means that border insets ITS content box (where this flex child
     actually sits) by 2px, so this digit was landing 2px left of the plain
     tile's own center below it (a real, if small, violation of "שיהיה
     מדויק לחלוטין"). The plain tiles' own border doesn't cause them this
     same problem (a symmetric border never moves an element's OWN center),
     it only affects a CHILD flowing inside a bordered parent — which only
     .lead-num does. Pulling this element 2px further right/outward with a
     negative margin cancels exactly that inset, confirmed with the
     headless-render harness (both centers now land on the same pixel). */
  margin-right: -2px;
}
/* v1.0.460 — NEW, per explicit request ("במקום הספרה שהייתה בשורה הראשונה
   נעשה חץ עבה כזה לבן שמסמן כלפי מטה לכיוון הקוביות של המספרים"): replaces
   .lead-num in the same fixed 125px column (same alignment convention as
   .lead-num's own v1.0.440 comment — this way the arrow sits centered
   right above the plain tiles' own digit column below it, genuinely
   pointing at them) whenever row 7 is showing the "go to the list" prompt
   instead of a real channel (see renderPlayerListNumberTiles's own
   matching v1.0.460 comment in app.js). A plain CSS border-triangle, not a
   text glyph/icon font — this TV's WebKit has patchy glyph coverage for
   arrow characters, a solid CSS shape renders reliably everywhere. */
.player-number-tile.lead .lead-arrow {
  flex: 0 0 125px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.player-number-tile.lead .lead-arrow::before {
  content: '';
  width: 0;
  height: 0;
  border-left: 14px solid transparent;
  border-right: 14px solid transparent;
  border-top: 18px solid #fff;
}
/* v1.0.440 — NEW, per explicit request ("אפשר להגדיל את כתב שם הערוץ ושם
   המופע והם יתחילו קצת אחרי תחילת קצה הטאב הימני של הארכיון"): text grown
   1.1rem -> 1.25rem, and given its own right padding so it starts a little
   past the archive tab's own right edge (.player-list-tabs' own 20px
   padding puts that edge 20px in from .player-list-panel's right edge —
   this 28px padding lands the text just past it, confirmed with the
   headless-render harness) instead of flush against .lead-num's own box. */
.player-number-tile.lead .lead-name,
.player-number-tile.lead .lead-now {
  font-size: 1.25rem;
}
.player-number-tile.lead .lead-name {
  /* v1.0.441 — 28px -> 13px, per explicit request ("קח בחשבון שרק את שם
     התחנה והמופע תצטרך לדחוף חזרה ימינה ב-15 פיקס"): row 7's own box just
     moved 15px left (see .player-number-tile.lead's own v1.0.441 comment
     above) — .lead-num deliberately moves WITH that shift (so it keeps
     lining up with the plain tiles below, which shifted the same 15px —
     see .player-number-tiles' own v1.0.441 comment), but the name/programme
     text's job is to stay anchored to the archive tab's edge instead (see
     this rule's own original v1.0.440 comment below), which didn't move.
     Reducing this padding by 15px exactly cancels the box's 15px leftward
     shift for this element only, landing the text back at its original
     screen position (confirmed with the headless-render harness). */
  padding-right: 13px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Channel name + programme both plain white/bold, inherited from
   .player-number-tile's own base color/weight — same "everything white"
   look the old #playerInfoRow this replaces already had (no dimming
   between the two, per its own established design). */
.player-list-panel {
  display: none;
  flex-direction: column;
  flex: 1 1 50%;
  min-width: 0;
  height: 100%;
  background: var(--bg, #0f1115);
  /* v1.0.444 — briefly tried margin-top:-5px as part of the "raise 5px"
     experiment, REVERTED per explicit request to start over ("מתחילים
     מחדש"). NEW margin-top:-10px here now, per explicit follow-up request
     ("תדחוף כלפי מעלה ב-10 פיקס גם את 5 וגם את 1") — matches
     .player-number-tiles' own identical new margin-top:-10px (see its
     v1.0.444 comment). Doesn't need its own padding-top change: everything
     inside (tabs, day-tiles, rows) moves up together with this box.
     v1.0.451 — -10px -> 5px, per explicit request ("את מבנה 1 ואת מבנה 5
     תוריד למטה גם ב-15 פיקס"): moves this whole panel DOWN 15px from where
     it currently sits (-10 + 15 = 5), matching .player-number-tiles' own
     identical v1.0.451 change. Everything inside (tabs, day-tiles, rows)
     moves down together with this box, same as before. */
  margin-top: 5px;
  /* Leaves room at the top so the tabs don't sit flush against the screen
     edge — matches the gap visible above the tab row in the reference
     screenshots. v1.0.421 briefly reduced this 40px -> 20px per an earlier
     request to move the tabs+list up — REVERTED back to 40px per his own
     immediate follow-up ("ובמקום להגביה את השורות בארכיון תגדיל את גודל
     השורה... וגם את הכתב"): replaced with growing the rows themselves
     instead (see .epg-row below) rather than shifting the whole block up.
     v1.0.425 — CHANGED BACK to 20px, per explicit new request ("את כל
     המבנה הזה... צריך להעלות אותו איזה 20 פיקסלים למעלה") — this time it's
     the whole block (tabs+days+rows together) he wants moved up, not just
     the rows regrown, so the earlier revert no longer applies. See
     .player-list-rows .epg-row further below for the matching row-height
     trim that keeps the 10th row from being cut off now that everything
     sits 20px higher.
     v1.0.436 — 20px -> 80px, per explicit request ("את מבנה 1 ואת מבנה 5
     נדחוף כלפי מטה ביחד ב-60 פיקס בערך השביל שיתפנה לנו מקום למעלה"): pushed
     down together with .player-number-tiles' own identical change above, to
     free up ~60px of space above both structures.
     v1.0.437 — 80px -> 105px, per explicit follow-up request ("דחוף אותם
     בעוד 25 ויהיה תואם עם השורה החדשה שורה 6"): another 25px, matching
     .player-number-tiles' own identical change above, to line up with
     #playerInfoRow ("row 6").
     v1.0.438 — 105px -> 80px, per explicit follow-up report ("השורה
     האחרונה שוב נחתכת"): reclaims the 25px #playerInfoRow's own height
     gave back (see its removal in v1.0.439 below) — measured with a
     headless render at the real 1920x1080 canvas (tabs 92px + day-tiles
     90px + this padding-top + N row-steps of 90px each must fit within
     1080): at 80px this fits 9 full rows (counting the always-present "now
     playing" row at the top) with no clipping, the 10th mostly (not fully)
     cut off — better than v1.0.437's 105px, which only fit 8 full rows
     clean.
     v1.0.439 — 80px -> 78px, per explicit request ("שורה שש נבטל לגמרי...
     נרים את מבנה חמש למעלה לקצה של המסך"): row 6 (#playerInfoRow) is gone —
     its job now lives inside .player-number-tiles' own first tile (see
     .player-number-tile.lead in that structure's own rules), so this
     padding-top no longer clears space for a separate freestanding row,
     just for that lead tile's height + a small gap — matches
     .player-number-tiles' own identical padding-top exactly, so the tabs
     row and the lead tile line up level, same convention as always.
     v1.0.440 — 78px -> 90px, per .player-number-tile.lead's own v1.0.440
     change (68px -> 80px, so "row 7" — his own new name for this tile —
     is exactly as tall as the plain number tiles below it): 80px + the
     same 10px gap as before = 90px, still matching
     .player-number-tiles' own identical padding-top exactly.
     v1.0.440 — 90px -> 110px, per row 7's own top:0 -> top:20px change
     (breathing room from the screen's top edge, per explicit report) —
     grew by that same 20px, still matching .player-number-tiles' own
     identical padding-top exactly.
     v1.0.440 — 110px -> 120px, matching .player-number-tiles' own
     identical change: the top margin (20px) and the row-7-to-tabs gap
     (was 10px) unified to the same 20px, per explicit request that every
     gap in this stack match.
     v1.0.444 — 120px -> 110px, per explicit follow-up report that this
     row7->tabs gap still reads as too large next to the 10px row-to-row
     gap: shrinks JUST that internal gap 20px -> 10px, top margin (screen
     edge -> row7/tabs) stays 20px unchanged for now (separate pending
     15px discussion) — matches .player-number-tiles' own identical
     change below.
     v1.0.444 — 110px -> 113px, matching row 7's own height 80->83 change
     (uniform +3px height bump across rows/tabs/day-tiles/tiles/row7):
     20 + 83 + 10 = 113. Matches .player-number-tiles' own identical
     change. */
  padding-top: 112.5px;
  /* v1.0.440 — NEW, matching .player-number-tiles' own identical addition:
     a 20px margin at the bottom too, symmetric with the top — per explicit
     request, confirmed worth it even though it shows one less archive row
     than before. .player-list-rows is flex:1 within this column, so it
     naturally stops 20px short of the screen's bottom edge now.
     v1.0.462 — 20px -> 112.5px, per explicit request ("מבנה מספר אחד יש
     לו שמונה שורות... נחתוך אותו על שבע"): +92.5px, exactly one row's own
     step (82.5px content height + 10px margin-bottom, same math as
     .player-number-tiles' own identical change above). .player-list-rows
     is flex:1 within this column, so it simply stops one row-step higher
     now — same "however many whole rows fit" logic as always, just with
     92.5px less room to fit them in. The freed strip is where the new
     .player-list-row8 sits — see its own comment for the exact math. */
  padding-bottom: 112.5px;
  box-sizing: border-box;
}
#screen-player.list-mode .player-list-panel { display: flex; }
/* v1.0.462 — NEW, "row 8": a still-empty full-width placeholder bar, per
   explicit request ("השורה החדשה הזו היא תתחיל מקצה המסך עשרה פיקסלים
   מרווח עד הקצה השני 10 פיקסלים מרווח... ונקרא לה שורה 8"). Positioned
   against #screen-player directly (same escape-the-column trick
   .player-number-tile.lead/"row 7" already uses — see its own big
   comment) so it can span the FULL screen width instead of being confined
   to either .player-list-panel's or .player-number-tiles' own column,
   with a 10px margin off each screen edge as specified.
   Vertically: bottom:20px + height:82.5px lands its top edge exactly
   102.5px above the true screen bottom, leaving precisely the same 10px
   gap above it that separates every other row/tile from its neighbor
   (112.5px reserved by both .player-list-panel's and .player-number-
   tiles' own new padding-bottom, minus this bar's own 82.5px, minus the
   unchanged 20px bottom breathing room = 10px) — this bar simply occupies
   the exact spot the cut 8th row/10th... 11th tile used to sit in, just
   stretched across the whole screen. Content/behavior still TBD — left
   empty and hidden outside list-mode for now, per his own "אני אגיד לך מה
   אנחנו עושים בה עוד מעט".
   v1.0.463 — BUG FIX, per explicit report ("לא רואים את השורה החדשה על כל
   אורך המסך"): the bar itself was positioned correctly all along, but had
   ZERO visual styling (no background/border/content) — a fully transparent
   box is indistinguishable from "not there". Adding a placeholder
   background+border (same visual language as .epg-row/.player-number-tile —
   rounded corners, translucent white fill+border) purely so the bar is
   actually visible on screen and its position can be confirmed; swap this
   out once real content for row 8 is decided.
   v1.0.464 — bottom:20px -> 5px, per explicit follow-up request to lower
   the (now-visible) bar another 15px toward the true screen edge — plenty
   of clearance either way, since .player-list-panel/.player-number-tiles
   still reserve 112.5px above the real bottom (see their own comments),
   10px more than this bar's own 82.5px height needs.
   Also NOW has real content (see renderPlayerListRow8 in app.js) — the
   channel-groups list, per explicit request — so display switches to flex
   (was block) to lay those items out in a row.
   v1.0.464 — background/border CHANGED again, per explicit follow-up
   request ("שורה 8 תשנה את הצבע הפנימי שלה כמו הצבע של מבנה 1 ו מבנה 5
   שיהיה תואם"): the placeholder rgba(255,255,255,...) wash swapped for the
   exact same var(--card)/var(--border) pair .epg-row (structure 1) and
   .player-number-tile (structure 5) both already use — see their own base
   rules above — so all three read as one consistent family instead of row
   8 looking like a separate, different kind of panel.
   v1.0.465 — height 82.5px -> 92.5px, per explicit request ("תגדיל כלפי
   מעלה את שורה 8 בעוד 10 פיקס") — bottom stays 5px so the extra 10px grows
   the bar UPWARD only (top edge moves from 87.5px to 97.5px above the real
   screen bottom), the real bottom edge itself untouched. Still well within
   the 112.5px .player-list-panel/.player-number-tiles reserve above the
   real bottom (see their own comments) — 20px of clearance left over. */
.player-list-row8 {
  display: none;
  position: absolute;
  bottom: 5px;
  /* v1.0.465 — left/right 10px -> 2.5px, per explicit request ("תגדיל
     לאורך את שורה 8 בעוד 15 פיקס ושהרווח יחולק שווה בין הקצה הימני
     והשמאלי"): +15px total length, split evenly off both edges
     (7.5px off each side, taken out of the original 10px margin). */
  left: 2.5px;
  right: 2.5px;
  /* v1.0.465 — 92.5px -> 82.5px, per explicit request ("להקטין את הגובה
     של השורה המספר שמונה בעשרה פיקסלים") — reverts the earlier +10px
     upward growth from this same round; bottom stays 5px, so the top edge
     simply moves back down by 10px. */
  height: 82.5px;
  box-sizing: border-box;
  background: var(--card);
  border: 2px solid var(--border);
  border-radius: 12px;
  align-items: center;
  padding: 0 24px;
  overflow: hidden;
  white-space: nowrap;
}
#screen-player.list-mode .player-list-row8 { display: flex; }
/* v1.0.465 — NEW, per explicit request ("כמגיעים בפוקוס על שורה 8 אז שיהיה
   כל השורה בצבע של האפור הבהיר הזה כמו שאנחנו מתבייתים על המבנה אחד או
   חמש"): the WHOLE bar's own background switches from the resting
   var(--card) to the exact same rgba(255,255,255,0.16) "this has focus"
   wash every other focused row/tile/tab in this panel already uses
   (.epg-row.focused, .player-number-tile.focused, .player-list-tab.focused)
   — while 'row8' actually has focus, not per-item like .row8-item.focused
   above (that's a separate, additional effect on top of this one).
   v1.0.465 — border-color added, per explicit follow-up ("וכשאנחנו מגיעים
   לשורה 8 גם שיהיה לה מסגרת לבנה"): the whole bar also gets a white
   border while focused, same as .epg-row.focused/.player-list-day-tile.focused
   already do. */
.player-list-row8.row8-active { background: rgba(255,255,255,0.16); border-color: #fff; }
/* v1.0.464 — NEW, each group entry inside row 8: the same 4-square grid
   icon the rail nav's own "groups" item uses (index.html — reused verbatim
   in ROW8_GROUP_ICON, app.js) plus the group's name, laid out inline. Base
   color is var(--muted) — the exact same gray the archive's own dimmed
   text/row-7 use elsewhere (per explicit request, "אותו עיקרון") —
   regardless of whether row 8 currently has focus; only the one focused
   item (below) brightens, and only while this zone actually has focus. */
/* v1.0.465 — font-size 1.15rem -> 1.44rem, per explicit request ("להגדיל
   את הכתב והאייקון בשורה שמונה ב-25% בערך") — the icon's own matching
   +25% bump is on .row8-item-icon's width/height below (set directly in
   the ROW8_GROUP_ICON markup, app.js — 20px -> 25px). */
.row8-item {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
  color: var(--muted);
  font-size: 1.44rem;
  font-weight: 600;
}
.row8-item-icon { flex: 0 0 auto; }
/* The literal "|" separator between groups, equally spaced on both sides
   via this same margin on every instance — per explicit request
   ("במרווח שווה").
   v1.0.465 — font-size 1.1rem -> 1.38rem, same +25% as .row8-item above,
   so the separator stays visually proportional to the (now bigger) text.
   v1.0.465 — 24px -> 39px, per explicit request: originally asked for
   +4px, then revised on seeing it ("אני רוצה אפילו יותר ממרווח של ארבע
   פיקסל נעשה לה מרווחים של 15 פיקסלים זה נראה צפוף מידי יותר מידי כתב")
   to +15px instead (24 + 15 = 39) — items were reading as too crowded. */
.row8-separator {
  margin: 0 39px;
  color: var(--muted);
  font-size: 1.38rem;
}
/* v1.0.464 — the currently-focused group brightens to white, but ONLY
   while 'row8' actually has focus (the .row8-active gate, toggled in
   updatePlayerListFocusUI same as .structure5-active) — once focus leaves
   this zone the .focused class may still sit on the DOM element (nothing
   clears it — same "remembers where you left off" convention as the
   number tiles), but without .row8-active on the parent this rule simply
   doesn't match, so it falls back to the plain var(--muted) rule above:
   "כיוצאים מהפוקוס... כיתוב הקבוצות יהיה באפור הכל", exactly as asked. */
.player-list-row8.row8-active .row8-item.focused { color: #fff; }
/* v1.0.467 — NEW, the collapsed "כרגע אתה בקבוצת..." indicator rendered
   instead of the full groups list whenever 'row8' does NOT have focus (see
   renderPlayerListRow8's own big comment in app.js) — mirrors row 7's own
   lead-tile look (.player-number-tile.lead .lead-name/.lead-now further
   below): always plain white/bold, never dimmed to var(--muted), same
   "always readable, not part of the dimmed browsing state" treatment.
   v1.0.469 — margin-right:155.5px added, per explicit report ("הכתב שכתוב
   כרגע אתה בקבוצה כך וכך הוא צריך לזוז שמאלה שיהיה מקביל לכתב של הרשימה של
   הארכיון בדיוק"): with .row8-collapsed-arrow now pulled out of this flex
   flow entirely (see its own v1.0.469 comment below), this div's only
   remaining child is the text — a plain flex item with no justify-content
   override on its own parent (.player-list-row8, this div's containing
   block) sits flush at THAT box's own right content edge by default (row8
   right:2.5px, minus its own 2px border, minus its own 24px padding-right
   = 1917.5-2-24 = 1891.5px). Target: match the archive list's own text
   start (.player-list-rows padding-right:20px + .epg-row's own
   padding-right:24px, off .player-list-panel's own right edge, 1780 —
   flush/margin-right:0 against .player-number-tiles' own confirmed left
   edge, unaffected by that panel's WIDTH — only its width changed in the
   later v1.0.469 TV-resize round, its right edge stayed fixed — so this
   math and the 155.5px result below are unaffected by that later change:
   1780-20-24 = 1736px). Shift needed: 1891.5-1736 = 155.5px, applied as a
   positive margin-right (the one safe, confirmed-reliable shift mechanism
   on this TV's old WebKit — see the project's own established "positive
   margins only" convention). */
.row8-collapsed {
  display: flex;
  align-items: center;
  gap: 8px;
  color: #fff;
  font-size: 1.44rem;
  font-weight: 600;
  margin-right: 155.5px;
}
/* v1.0.469 — REWRITTEN from a flex-flow item to position:absolute directly
   against .player-list-row8 (already position:absolute against
   #screen-player — same "escape the flex flow, position against a fixed
   ancestor" trick .player-number-tile.lead/.lead-arrow already use), per
   explicit report ("החץ בשורה התחתונה לא ממוקם בול מול המספר של מבנה 5 הוא
   יותר מדי ימינה הוא צריך להיות ממוקם בול מול המספר של הקוביות"): the old
   flex-flow position (right after the text, in a gap:8px row) had no
   relationship at all to the digit column's actual x position, just
   whatever the text's own length happened to leave next to it. Reuses
   .player-number-tile.lead .lead-arrow's own EXACT confirmed box — a
   125px-wide column, center-justified — since that arrow already sits
   correctly over the digits (its own v1.0.440 comment). That box's right
   edge sits at global x=1900 (.lead's own right:20px against
   #screen-player, i.e. 1920-20=1900). This element's containing block
   (.player-list-row8) has its own right edge at 1920-2.5=1917.5, so the
   equivalent `right` here is 1917.5-1900=17.5px, giving the identical
   1775–1900 box, dead center on the same digit column row 7's own arrow
   already targets — top:0/bottom:0 stretches it to row8's full height so
   align-items:center still centers the arrow vertically within it, same
   as before. */
.row8-collapsed-arrow {
  position: absolute;
  top: 0;
  bottom: 0;
  right: 17.5px;
  width: 125px;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* Same CSS-triangle technique as .player-number-tile.lead .lead-arrow
   below, just pointing UP (border-bottom instead of border-top) — row 8
   sits BELOW structure 5, not above it, so its own "go here" arrow needs
   to point at the digits from underneath rather than down at tiles from
   above, per explicit request ("חץ... כלפי מעלה"). Same 14/14/18px
   triangle dimensions as that one, for visual consistency between the two. */
.row8-collapsed-arrow::before {
  content: '';
  width: 0;
  height: 0;
  border-left: 14px solid transparent;
  border-right: 14px solid transparent;
  border-bottom: 18px solid #fff;
}
/* v1.0.469 — NEW, "row 10": an info bar over the TV, per explicit request
   ("פס נוסף שימשיך את שורה 7 לצד השמאלי של הטלויזיה... נשים שעון זמן...
   ובצד ימין שלה נכתוב 'כרגע אתה צופה ב...'") — same vertical level as row
   7 (top:25px, height:82.5px, matching its own confirmed-correct values)
   but over the TV's own column instead of structure 5's, positioned
   against #screen-player directly (same escape-the-flex-flow trick row
   7/row 8 already use). Sits ON TOP of the video picture as a plain
   overlay (same idea as the existing OSD channel-switch card, which
   already overlays the video without needing the video itself to shrink
   or reposition) rather than reserving its own dedicated vertical space —
   the lower-risk option given #video's own vertical space budget is
   already tightly tuned against several other hardcoded bottom-cluster
   offsets elsewhere in this file (see their own matching v1.0.469
   comments). Left/width computed off the TV's own new box (left:15px,
   width:1056px, see #video's own v1.0.469 comment) with the same 2.5px
   inset .player-list-row8 already uses: 15+2.5=17.5, 1056-2.5-2.5=1051.
   Same card look as row 8 (background/border/radius) so the two read as
   one consistent family. justify-content:space-between + explicit DOM
   order (status text first, clock second) lands the status text on the
   RIGHT and the clock on the LEFT under RTL's own "first child = start =
   right" convention (same convention .row8-collapsed's own arrow/text
   ordering already relies on) — matching his explicit "שעון... משמאל...
   בצד ימין... כרגע אתה צופה" layout without needing a direction override.
   v1.0.470 — REWORKED. Per his explicit report, this bar "merging"/
   showing black gaps against the TV frame was because it sat ON TOP of
   the video as a plain z-index overlay (the whole idea above) — now
   #video/.player-video-area both moved down 112.5px to start BELOW this
   bar instead (see their own matching v1.0.470 comments), same "reserved
   zone above, small clearance to the real content" relationship
   .player-list-panel/.player-number-tiles already give row 7 via their own
   padding-top:112.5px. top/height unchanged (still matches row 7 exactly,
   still what leaves a 5px clearance: 25+82.5=107.5, 112.5-107.5=5).
   left/width recomputed for the reverted 920px TV (no left-margin any
   more either — see #video's own v1.0.470 comment): same 2.5px inset
   .player-list-row8 uses, directly off the TV's own left:0/width:920 box:
   0+2.5=2.5, 920-2.5-2.5=915. His latest sketch numbers this family
   1(archive)/5+7(structure 5 + its lead row)/10(a NEW, separate,
   not-yet-built full-width bar) and calls THIS bar "12" to avoid the
   collision — no code identifiers renamed here (still .player-row10/
   #playerRow10/#row10Clock/#row10Status throughout), only this comment. */
/* v1.0.471 — left/width CHANGED again, per explicit direct instruction
   ("שורה 12 צריכה להיות שווה לשורה 7 גם באורך וגם במרחק שלה מהקצה
   השמאלי"): row 12 must be a true mirror of row 7 — same width (935px,
   was 915px, matched to the TV's own box instead) AND the same inset from
   its own screen edge (20px from the left, was 2.5px, matched to row 8's
   convention instead) — not derived from the TV's own box width the way
   the v1.0.470 recompute assumed. This means row 12 now extends slightly
   wider than the TV itself (935 vs the TV's own 920px), same as how row 7
   is wider than the plain number-tiles column it sits above — intentional
   per this instruction, mirrors row 7's own relationship to its column
   exactly. */
.player-row10 {
  display: none;
  position: absolute;
  top: 25px;
  left: 20px;
  width: 935px;
  height: 82.5px;
  box-sizing: border-box;
  background: var(--card);
  border: 2px solid var(--border);
  border-radius: 12px;
  align-items: center;
  justify-content: space-between;
  padding: 0 24px;
  overflow: hidden;
  white-space: nowrap;
  color: #fff;
  font-size: 1.44rem;
  font-weight: 600;
  z-index: 5; /* same reasoning as row 7/plain tiles' own z-index:5 — sits above the video's own hardware plane, see .player-number-tile's own v1.0.444 comment */
}
#screen-player.list-mode .player-row10 { display: flex; }
/* v1.0.469 — mirrors row 7's own .swapped override pattern: the TV column
   moves to the screen's right edge in swapped mode (see #video's own
   swapped comment), so this bar's own left inset becomes a right inset
   instead, same value (the swapped TV box is symmetric with the unswapped
   one — see .player-video-area's own swapped v1.0.469 comment).
   v1.0.470 — 17.5px -> 2.5px, same recompute as the base rule's own
   matching v1.0.470 comment above.
   v1.0.471 — 2.5px -> 20px, same "mirror row 7 exactly" change as the base
   rule's own matching v1.0.471 comment above. */
#screen-player.list-mode.swapped .player-row10 { left: auto; right: 20px; }
.row10-clock { flex: 0 0 auto; direction: ltr; }
.row10-status {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-left: 16px; /* keeps a gap from the clock when the status text is long enough to need eliding */
}
/* v1.0.231 — the browse panel's OWN controls row (TV -> Down -> here),
   deliberately separate from the pre-existing .player-controls-row/
   Player.*Focused machinery — see the big index.html comment on
   #playerBrowseControlsRow for why. Sits at the bottom of #playerVideoArea,
   only shown in list-mode. DOM order (pause, restart, favorite, swap) is
   also the visual right-to-left order on this dir="rtl" page, per explicit
   request — pause rightmost, swap last/outermost (leftmost). */
/* v1.0.234 — per explicit follow-up ("שלחת לי רקע גרדיאנט לפס מידע ולפקדים אז
   מה לא בסדר?"): the fullscreen player's OWN controls row sits on a solid
   dark gradient (.player-controls-backdrop below) specifically so it stays
   readable over a bright broadcast picture — the browse panel's controls
   row/progress bar never had an equivalent of their own, which on top of
   the flat low-contrast background (see .player-browse-icon-btn's own
   v1.0.232 comment) is very likely why the whole area still read as
   "nothing there" on the real TV. Same gradient/height as the old system's
   backdrop, just always shown in list-mode (no separate .show toggle of its
   own — same as the row/bar above it). Comes FIRST among these three
   siblings in the DOM (see index.html) specifically so it paints BEHIND
   both #playerBrowseProgress and #playerBrowseControlsRow — none of the
   three set an explicit z-index, so plain DOM order decides stacking. */
.player-browse-backdrop {
  display: none;
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 220px;
  background: linear-gradient(to top, #000000 0%, #000000 32%, #585a66 100%);
  pointer-events: none;
}
#screen-player.list-mode .player-browse-backdrop { display: block; }
/* v1.0.234 — read-only "how far into the current programme" bar for this
   same controls stop, per explicit request. Reuses the shared
   .player-progress/.player-progress-track/.player-progress-fill classes
   (already positioned exactly where this needs to sit — bottom:78px,
   right/left:40px — see .player-progress above) so it looks identical to
   the existing bars; only the fill color needs its own ID override, same
   idea as #playerLiveInfoProgress just below: blue, never red, since this
   is never actually seekable (see showBrowseProgress in app.js — same
   channel.now EPG math as the live-quick bar, no video position). */
#playerBrowseProgress .player-progress-fill { background: var(--accent); }
.player-browse-controls-row {
  display: none;
  position: absolute;
  /* Per explicit request, moved above the progress bar (.player-progress
     sits at bottom:78px, roughly 56px tall including its padding — see
     style.css above) instead of below it. */
  bottom: 150px;
  right: 40px;
  left: 40px;
  align-items: center;
  /* flex-start, NOT flex-end: on this dir="rtl" page "start" is the RIGHT
     edge (see the existing .player-controls-row's own comment on the same
     rule) — this hugs the row to the video area's right side, matching
     pause being the first/rightmost icon. flex-end would (and, in an
     earlier version of this rule, actually did) push the whole row to the
     LEFT edge instead — direction is respected by justify-content, not
     just by item order. */
  justify-content: flex-start;
  /* "לא צמוד אליהם" — spaced apart, not touching. */
  gap: 22px;
}
#screen-player.list-mode .player-browse-controls-row { display: flex; }
/* v1.0.235 — per explicit follow-up request ("תבטל את העיגולים ושיהיו רק
   אייקונים לבנים"): the v1.0.232 circle background/border (below, kept only
   in this comment for context: background rgba(255,255,255,0.16) + a
   rgba(255,255,255,0.3) border) is gone now that the row sits on its own
   dark gradient backdrop (#playerBrowseBackdrop, v1.0.234) — that already
   solves the original contrast problem the circles were added for, so a
   plain icon reads fine without them, same as the fullscreen player's own
   row (.player-pause-icon etc. above) always has. Same drop-shadow outline
   trick as those for legibility over a bright picture, and the same
   color-change + scale(1.15) focus treatment instead of a border (nothing
   left to put a border ON any more). */
.player-browse-icon-btn {
  /* v1.0.236 — BUG FIX, per explicit report/photo: this was the actual
     cause of "no icons, just [dark] shapes" — v1.0.235 removed the custom
     circle background/border but never reset the element's OWN default
     button chrome (a <button> renders with the browser/TV's own gray-ish
     box + border unless told not to), so what showed up on the real TV was
     that plain default button box, not a transparent wrapper around the
     icon like it looked in review. Explicitly stripped now. Bumped to 64px
     (was 52, icons inside were 22 then 32) to actually match the
     fullscreen player's own icons per explicit follow-up request ("תשתמש
     באייקונים שיש במסך המלא") — see the matching width/height bump on each
     inner <svg> in index.html. */
  width: 64px;
  height: 64px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  padding: 0;
  -webkit-appearance: none;
  appearance: none;
  cursor: pointer;
  color: #fff;
  filter: drop-shadow(1.5px 0 0 #000) drop-shadow(-1.5px 0 0 #000)
          drop-shadow(0 1.5px 0 #000) drop-shadow(0 -1.5px 0 #000)
          drop-shadow(0 1px 4px rgba(0,0,0,0.6));
  transition: transform 0.15s ease;
}
/* v1.0.237 — BUG FIX, per explicit report ("תוריד את המסגרת מסביב
   לאייקונים... מסגרת התמקדות באייקונית הריבועים בצבע כחול חזק"): the shared
   .focusable.focused rule (see "--- Shared focusable ---" above) sets its
   own border-color + a 4px purple box-shadow glow — since these buttons
   also carry the generic .focusable class (for the remote-nav plumbing),
   that glow was still bleeding through as a strong blue-purple square frame
   around each icon, even though this row was deliberately designed (see
   this section's own opening comment) to show focus ONLY via color change +
   scale, with no border/frame at all. Explicitly cancelled here — same
   specificity (two classes) and later in the file, so this wins over
   .focusable.focused's box-shadow for every button in this row, favorite
   included (it also carries plain .focused, not just the
   [data-action="favorite"].focused variant). */
.player-browse-icon-btn.focused { color: var(--accent); transform: scale(1.15); box-shadow: none; }
/* Pause/play glyph swap — same .paused-class convention already used by
   the existing #playerPauseIcon (see updatePlayerControlsRowUI in app.js,
   which mirrors Player.el.paused onto this button independently of the
   old #playerPauseIcon/Player.updatePauseIconState). */
.player-browse-icon-btn .icon-play { display: none; }
.player-browse-icon-btn.paused .icon-pause { display: none; }
.player-browse-icon-btn.paused .icon-play { display: block; }
/* Favorite heart — same "filled = active" convention as the existing
   #playerFavHeart, mirrored independently here (see
   updatePlayerControlsRowUI in app.js). */
.player-browse-icon-btn[data-action="favorite"] { color: var(--muted); }
.player-browse-icon-btn[data-action="favorite"].active { color: var(--danger); }
/* v1.0.235 — .focused above would otherwise lose to the two favorite-
   specific color rules just above it (equal specificity, later in the
   file wins) — this pins the focus color back on top whenever the
   favorite button itself has focus, active or not. */
.player-browse-icon-btn[data-action="favorite"].focused { color: var(--accent); }
/* Swap icon — two FIXED arrows (top always right-pointing, bottom always
   left-pointing — neither rotates/moves), only color indicates the current
   side: white = "archive/live are on this side", gray = the other. Default
   (not .swapped): top white, bottom gray — matches #screen-player's own
   default (list panel on the right, see order:1 above). .swapped (toggled
   together with #screen-player's own .swapped by togglePlayerListSwap in
   app.js) flips which arrow is white. */
/* v1.0.236 — BUG FIX: this button never actually stacked its two arrows
   vertically (top/bottom, per the design and this section's own comments)
   — it only ever inherited .player-browse-icon-btn's plain row-direction
   flex, so the two SVGs sat side by side the whole time this row has
   existed. Stacked properly now. */
.player-list-swap-btn { flex-direction: column; gap: 4px; }
.player-list-swap-btn .swap-arrow-top { color: #fff; }
.player-list-swap-btn .swap-arrow-bottom { color: var(--muted); }
.player-list-swap-btn.swapped .swap-arrow-top { color: var(--muted); }
.player-list-swap-btn.swapped .swap-arrow-bottom { color: #fff; }
/* v1.0.425 — horizontal padding made asymmetric (was 40px both sides) per
   explicit request ("ולמשוך אותו גם ימינה 20 פיקסלים"): 20px right / 60px
   left instead of 40/40 — same total horizontal space, just shifted 20px
   toward the screen's own right edge (where this panel sits by default,
   non-swapped) instead of centered in its column. Applied identically to
   .player-list-day-tiles/.player-list-rows right below, so the tabs, day
   tiles and rows all stay aligned with each other.
   v1.0.429 — left padding dropped 60px -> 0, per explicit follow-up
   ("קח את 1 שמאלה שהצד השמאלי שלו יתחיל מאמצע המסך בדיוק" — "1" = this
   whole tabs+day-tiles+rows block): .player-list-panel's own box already
   starts exactly at the screen's true horizontal middle (flex:1 1 50% on a
   two-column row, no gap between columns — see #screen-player.list-mode
   .player-video-area/.player-list-panel further below), but the visible
   tab/tile/row cards themselves only started 60px further in than that,
   leaving a blank strip of the panel's own background between the TV's
   right edge and the first visible card. Zeroing this left padding lets
   the visible content flush up against the panel's own left edge, i.e.
   exactly the screen's middle — right padding (20px) untouched, not part
   of this request. */
/* v1.0.443 — padding-bottom 16px -> 10px, per explicit report: this gap
   (tabs -> day-tiles) was bigger than the day-tiles -> rows gap (12px) and
   the row-to-row/tile-to-tile gap (10px) — three different values with no
   relationship, described as "not symmetric, messes everything up".
   Unified to 10px, matching row-to-row/tile-to-tile exactly, per explicit
   instruction ("סימטרי כמו השורות ושיקביל בדיוק לקוביות של מבנה 5"). See
   .player-list-day-tiles' own matching v1.0.443 comment right below. */
.player-list-tabs { display: flex; gap: 8px; padding: 0 20px 10px 0; }
.player-list-tab {
  flex: 1;
  text-align: center;
  /* v1.0.423 — BUG FIX, per explicit report ("גם לא הגדלת את הגובה של
     הטאבים"): match the same taller row height the day-tiles/.epg-row rows
     just got (padding 24px, ~1.3-1.4rem text), for one consistent height
     across the whole panel — was still the older, smaller 14px/1.15rem.
     v1.0.444 — 24px -> 27px, same real bug as .player-list-day-tile's own
     identical v1.0.444 fix: this comment always SAID "match row height" but
     .player-list-rows .epg-row is actually 27px padding, not 24 — never
     updated when that override was added, so this came out 8px short
     (measured 76px vs rows' 84px, confirmed with the render harness) —
     matches now. */
  padding: 27px 10px;
  border-radius: 12px;
  font-size: 1.3rem;
  /* v1.0.444 — NEW, matching every other v1.0.444 height-match fix: same
     fixed-px line-height so this computes to 84px "by construction". */
  line-height: 28.5px;
  background: rgba(255,255,255,0.05);
  color: var(--muted);
  /* v1.0.435 — CHANGED from transparent, per explicit report/confirmation
     ("מסגרת כחולה גם על 5 וגם על 1... המשתמש לא יידע איפה הוא נימצא"):
     moving focus between this tab (structure 1) and the new number-tile
     column (structure 5, .player-number-tile — see its own comment in
     style.css, which already used this exact gray-resting/blue-focused
     pattern) left this tab's own border invisible while unfocused instead
     of a steady gray, which read as ambiguous about whether a structure
     was still "there" once you'd left it. Now matches .player-number-tile
     exactly: a permanent gray resting border, blue (.focused, var(--accent))
     only on whichever structure currently has focus — confirmed scope is
     structures 1 and 5 specifically, not TV/controls/rows. */
  border: 2px solid var(--border);
}
/* v1.0.451 — REMOVED the permanent light-gray/white-text look this rule used
   to give the "active" tab (whichever tab's CONTENT is currently showing),
   per explicit follow-up correction ("כשעוברים למבנה אחד אז הארכיון לא
   ישאר גם באפור בהיר... רק איפה שאנחנו ממוקדים בזמן אמת שם יהיה האפור
   הבהיר" — light gray must mean ONLY real-time focus, not "active tab";
   once focus leaves the tab, it must look exactly like the inactive one).
   .active still exists as a class (setPlayerListTab still toggles it, and
   JS logic like sign/dataset.tab still reads it) — it's just visually inert
   now; nothing distinguishes "active but not focused" from "inactive"
   any more, which is exactly what was asked for. */
/* v1.0.451 — CHANGED, same "blue frame -> light gray" swap — see
   .player-number-tile.focused's own v1.0.451 comment.
   v1.0.451 (follow-up) — BUG FIX, per explicit report ("כשנכנסים לארכיון
   בפעם הראשונה עדיין יש מסגרת כחולה"): this rule only overrode
   border-color, so the generic `.focusable.focused` rule's own
   `box-shadow: 0 0 0 4px rgba(123,92,255,0.35)` (these tab buttons also
   carry the shared .focusable class — see index.html) was still winning on
   that property and left a blue glow behind even with the border itself
   now gray. Added box-shadow: none here to fully override it — same
   box-shadow:none .player-number-tile.focused/.epg-row.focused/
   .player-list-day-tile.focused already carry, just missed on this one. */
/* v1.0.465 — color: #fff added, per explicit request ("הכתב באפור של הטאב
   של הארכיון תשנה ללבן כשאנחנו בפוקוס עליו"): the tab's own resting text
   color (.player-list-tab above) is var(--muted) always — this rule only
   ever won the background/border, never the text itself, so a focused tab
   still showed gray text on its own light-gray highlight. Brightens to
   white while actually focused, same "focused = brighter" language every
   other focused element in this panel already uses. Applies to both tabs
   (archive/live share this one rule) — he named the archive tab, but
   nothing about the reasoning is archive-specific. */
.player-list-tab.focused { border-color: var(--border); background: rgba(255,255,255,0.16); box-shadow: none; color: #fff; }
/* v1.0.455 — NEW, per explicit request ("צריך שאנחנו על הארכיון מסגרת אדומה
   וכן שידור חי מסגרת צהובה כך נידע היכן אנחנו" + follow-up clarification
   "על הטאב של כל אחד מהם"): a permanent colored frame on whichever tab's
   CONTENT is currently showing — red on the archive tab, yellow on the live
   tab — so that's visible at a glance regardless of where real-time focus
   currently is. This is deliberately NOT the same thing the v1.0.451/452
   "active tab" light-gray removal was about (see the big v1.0.451 comment
   above .focused): that was about the BACKGROUND specifically meaning ONLY
   real-time focus, which still holds — this is a separate visual channel
   (border-color only) carrying a different meaning ("where we are" vs
   "what's focused right now"), same split already used for the day-tiles'
   own current-archive/current-live border indicator further below. Placed
   AFTER .focused in source order (both are 2-class selectors, same
   specificity) so a tab that is BOTH active and focused shows this rule's
   border-color (red/yellow) winning over .focused's gray one, while
   .focused's own background/box-shadow still apply untouched — identical
   coexistence pattern to .player-list-day-tile.current-archive/live vs
   .player-list-day-tile.focused. IDs used (not .active alone) since both
   tabs share the same .active/.player-list-tab classes and only differ by
   which one setPlayerListTab() currently toggles .active onto. */
#playerTabArchive.active { border-color: var(--danger); }
#playerTabLive.active { border-color: var(--warning); }
/* v1.0.456 — NEW, per explicit follow-up ("שהמסגרות האלו יעלמו שאנו בפוקוס
   על מבנה 1 כולל המסגרת הצהובה והאדומה של הימים הארכיון והשידור החי"): both
   this red/yellow tab border AND the day-tile red/green current-day border
   further below (.player-list-day-tile.current-archive/current-live) are
   meant only as a "where were we" reminder once focus has moved AWAY from
   structure 1 to structure 5 ('numbers' zone) or the video area ('tv'/
   'controls') — not while actually browsing inside structure 1 itself
   (tabs/days/rows zones), where the content on screen already makes that
   obvious and the colored frame is just visual noise.
   v1.0.458 — CORRECTED, per explicit follow-up report/clarification
   ("כשאני במצב ארכיון ויורד בשורות אז תישאר המסגרת האדומה כשאני עובר למבנה
   5 ...אז המסגרות האלו צריכות להעלם"): the v1.0.456 logic above had this
   backwards — the colored frame needs to STAY while actually browsing rows
   inside structure 1 (that's the whole point, so you always know which mode
   you're scrolling through), and should disappear ONLY once focus has moved
   to structure 5 ('numbers' zone) specifically — not for the whole of
   structure 1. Renamed `.zone-focused` -> `.structure5-focused` (clearer
   now that it means the opposite of what the old name implied) and the
   toggle condition in app.js flipped to match (see its own matching
   v1.0.458 comment). Also now covers the v1.0.457 day-tile TEXT color
   further below, per the same follow-up ("גם הצבע של הכיתוב בקוביות יעלם") —
   same class, same on/off logic, one more property. */
.player-list-panel.structure5-focused #playerTabArchive.active,
.player-list-panel.structure5-focused #playerTabLive.active {
  border-color: var(--border);
}
/* v1.0.421 — NEW, day-jump tile row (see the index.html comment on
   #playerListDayTiles for the full picture). Empty/collapsed (nothing but
   "today" in the data) just leaves this an empty flex row — zero height
   beyond its own padding, so it costs no visible space when a tab has
   nothing to jump to (e.g. a channel with only today's data).
   v1.0.423 — REDESIGNED, per explicit request ("יתחלקו באורך של שורה
   אחת... בגובה של שורה אחת... שורה אחת תתחלק על שבע... תמיד נראה שבע
   קוביות"): always exactly 7 slots (see PLAYER_LIST_DAY_TILE_SLOTS in
   app.js), each an equal 1/7 share of the same width the rows below use
   (flex:1 here, vs. flex-wrap pills before), sized/styled to match a
   .epg-row's own height (same padding/border-radius/border as .epg-row
   above) instead of a small separate pill look. */
.player-list-day-tiles {
  display: flex;
  /* v1.0.444 — REMOVED `gap: 8px` here, per explicit request that growing
     .player-list-panel 20px wider must NOT grow these tiles — the extra
     width has to go into the space BETWEEN them instead (see
     `.player-list-day-tile`'s own v1.0.444 comment for the actual
     replacement spacing mechanism, and why `gap` itself was dropped rather
     than just bumped to a bigger value): this container is `display:flex`
     and this TV's own much older embedded browser was already CONFIRMED
     this session to silently ignore `gap` on a flex container (see the big
     v1.0.443 comment on `.player-number-tile:not(.lead)` for the full story
     — flex `gap` is a newer spec addition than flexbox itself, and the
     desktop harness can't catch the difference since it always supports
     it). Simply increasing this gap value would have looked right in every
     screenshot from this session's own tooling while doing nothing at all
     on the real screen — the exact same silent failure already diagnosed
     and fixed once this version, so it's replaced here with the
     already-proven-safe margin-based mechanism from the start instead of
     risking a repeat. */
  /* v1.0.428 — bottom padding trimmed 16px -> 6px, per explicit request
     ("צמצם את המרווח שבין השורות והקוביות"): tightens the gap between this
     row and the row list right below it, reclaiming ~10px of extra height
     for the rows list so the 10th row isn't sitting cut off mid-row — none
     of the day-tiles' own size or the rows' own height (.epg-row, restored
     to normal in v1.0.427) is touched, just this one gap.
     v1.0.430 — 6px nudged back up to 12px, per explicit follow-up
     ("השורה הראשונה צמודה מידי צריך להרחיק אותה קצת למטה מקוביות הימים"):
     6px read as too tight against row 1 — still noticeably less than the
     original 16px, just a bit more breathing room than the v1.0.428 trim. */
  /* v1.0.443 — 12px -> 10px, unified with .player-list-tabs' own identical
     change right above — see that rule's own v1.0.443 comment for why. */
  padding: 0 20px 10px 0; /* v1.0.425/429 — see .player-list-tabs's own comment above */
}
.player-list-day-tile {
  /* v1.0.444 — flex:1 1 0 (equal-grow, fills whatever width is available)
     -> flex:0 0 108.86px (fixed), per explicit request ("בקוביות של הימים
     אל תיגע בגודל שלהם"): with .player-list-panel growing 830->850px (see
     that rule's own v1.0.444 comment), equal-grow tiles would have silently
     absorbed that entire 20px into their own width instead of leaving it as
     extra gap — 108.86px is this tile's own measured width from BEFORE this
     change (headless-render harness, 830px-wide panel), pinned so the size
     genuinely never changes now regardless of how wide the panel gets.
     v1.0.454 — 108.86px -> 106.71px, BUG FIX, per explicit report ("הקוביות
     יוצאות החוצה ב15 פיקס ימינה מהמבנה של 1"): this fixed width was
     measured for an 830px-wide .player-list-panel and was never recomputed
     when that panel shrunk 830->815px in v1.0.451 ("רק תקטין את 1 ב-15").
     Available content width for the 7 tiles + 6 gaps is
     panelWidth - 20 (this row's own right padding): was 810px (830-20),
     now 795px (815-20). With the 6 gaps unchanged at a flat 8px
     (.player-list-day-tile:not(:last-child) below, 48px total), the
     per-tile width must shrink to match: (795-48)/7 = 106.7142...px ≈
     106.71px. The stale 108.86px kept demanding 810.02px total (7×108.86 +
     48) inside a box that only has 795px — exactly the reported 15px
     overflow past .player-list-panel's own (now narrower) right edge. */
  flex: 0 0 106.71px;
  text-align: center;
  /* v1.0.444 — 24px -> 27px, per explicit request that everything be the
     same size for real symmetry: the comment here always SAID "matches
     .epg-row" but .epg-row's own padding in this context is actually 27px
     (.player-list-rows .epg-row overrides the base rule's 24px) — this was
     never actually updated when that override was added, so this tile came
     out 6px shorter than a real row/number-tile. Now genuinely matches. */
  padding: 27px 8px; /* matches .player-list-rows .epg-row's own vertical padding, for real row/tile height */
  border-radius: 12px; /* matches .epg-row */
  font-size: 1.4rem; /* NEW — bigger number, per explicit request ("מספר יותר גדול") */
  /* v1.0.444 — NEW, matching .player-number-tile/.epg-row's own identical
     line-height: 26px change: same fixed-px line-height so this tile's
     height computes identically to a row/number-tile "by construction". */
  line-height: 28.5px;
  font-weight: 700;
  background: var(--card); /* matches .epg-row's own background */
  color: #fff; /* NEW — active/reachable tiles read white, per explicit request */
  border: 2px solid var(--border);
  /* v1.0.421 — BUG FIX, per explicit report/photo: "+1"/"+2" showed reversed
     as "1+"/"2+" — same reasoning as the "1:45" duration fix above (line
     ~922), a "+"+digit run has no strong characters of its own, so inside
     this RTL page it got bidi-reordered instead of reading naturally. */
  direction: ltr;
}
/* v1.0.444 — NEW, replaces the removed `gap:8px` on .player-list-day-tiles
   (see that rule's own v1.0.444 comment for why `gap` itself isn't used).
   PLAYER_LIST_DAY_TILE_SLOTS (app.js) is always exactly 7 tiles -> 6 gaps
   between them; the container's whole 20px width increase (from
   .player-list-panel's own v1.0.444 growth) goes entirely into these 6
   gaps, evenly: 20/6 = 3.33px extra per gap, on top of the original 8px =
   11.33px. `:not(:last-child)` skips the last DOM tile (the LEFTMOST one in
   this RTL row — confirmed with the headless-render harness), which needs
   no trailing margin since nothing sits further left of it inside this
   container. margin-LEFT (not right) because in this RTL flex row each
   tile's next sibling in DOM order sits physically to its OWN left, so a
   tile's own trailing (gap-creating) side is its left — confirmed against
   the harness's own physical left/right measurements, not assumed from DOM
   order alone. */
/* v1.0.444 — 11.33px -> 9.66px, per the user's own explicit reminder
   ("כולל החישוב על הכפתורים של הימים זוכר?") to redo this gap math every
   time .player-list-panel's width changes, when the panel briefly shrunk
   850px->840px that round.
   v1.0.445 — REVERTED back to 11.33px, matching the panel's own revert
   back to 850px that round.
   v1.0.448 — REVERTED to a flat 8px, per explicit request ("תחזיר למה
   שהיה לפני השינוי שציינתי") to undo the ENTIRE 830px->850px growth this
   segment was built on (see .player-list-panel's own matching v1.0.448
   comment for why — it turned out, once genuinely tested with fresh CSS,
   to look like no change at all): with the panel back to its true
   original 830px, there's no extra width to redistribute across these 6
   gaps any more, so this goes back to the flat 8px every gap had before
   any of this mechanism existed. Still using the safe margin-based
   mechanism (not the flex `gap` property) — that switch was a genuine,
   separate real-device bug fix (this TV's WebKit silently ignores flex
   `gap`) and stays regardless of the 830-vs-850 question. */
.player-list-day-tile:not(:last-child) { margin-left: 8px; }
/* v1.0.451 — CHANGED, same "blue frame -> light gray" swap — see
   .player-number-tile.focused's own v1.0.451 comment.
   v1.0.465 — border-color var(--border) -> #fff, per explicit request
   ("ושאנחנו מתבייתים על הימים וזזים בתוכם גם שיהיה להם מסגרת לבנה"): same
   white-border treatment as .player-number-tile.focused/.epg-row.focused. */
.player-list-day-tile.focused { border-color: #fff; background: rgba(255,255,255,0.16); box-shadow: none; }
/* v1.0.451 — NEW, per explicit request: marks whichever day-tile matches
   the row currently focused in the rows list — "which day am I looking at
   right now" — red on the archive tab, green on the live tab (reuses the
   app's existing --danger/--accent2 colors). Applied/cleared entirely from
   JS, see updatePlayerListCurrentDayIndicator() in app.js. A border, not a
   background, so it stays visually distinct from — and can coexist with —
   the light-gray .focused background right above (e.g. right after jumping
   in via Enter on the day tile itself, which is briefly both). */
.player-list-day-tile.current-archive { border-color: var(--danger); }
.player-list-day-tile.current-live { border-color: var(--accent2); }
/* v1.0.456 — NEW, same reasoning/mechanism as `.player-list-panel.zone-
   focused #playerTabArchive.active` above (see that rule's own big comment):
   this red/green "current day" border is also meant only as a reminder once
   focus has left structure 1, not while already browsing inside it.
   v1.0.458 — CORRECTED along with that rule's own matching v1.0.458 fix —
   renamed to `.structure5-focused`, condition flipped: this border now
   stays while browsing rows/tabs/days inside structure 1, and disappears
   only once focus has actually moved to structure 5 ('numbers' zone). */
.player-list-panel.structure5-focused .player-list-day-tile.current-archive,
.player-list-panel.structure5-focused .player-list-day-tile.current-live {
  border-color: var(--border);
}
/* v1.0.451 — NEW, the two-line date-mode layout (day-of-month number, small
   weekday name below it) used when Storage.getDayTilesShowDate() is on —
   see playerListDayTileDateHtml()/renderPlayerListDayTilesUI() in app.js.
   Switches the tile from its default single centered line to a small
   vertical stack, still centered as a whole.
   v1.0.453 — BUG FIX, per explicit report ("סימנתי בצ'קבוקס ועכשיו הם דקים
   יותר וכל הסימטריה התקלקלה"): the original version here overrode
   `padding: 27px 8px` -> `10px 4px`, which — since this tile has no explicit
   height, only the base rule's padding+line-height math (27px top/bottom +
   28.5px line-height, the exact same formula every row/tile/tab in this
   panel shares to compute an identical 84px-ish height "by construction",
   see .epg-row's own v1.0.443 comment) — silently shrank this tile's total
   rendered height by 34px (2 x 17px) the moment the checkbox was turned on,
   breaking the height-match with every row/tab/number-tile next to it. Fix:
   DON'T touch padding at all here — only add the flex-column stack, and
   size the two lines' own line-heights to sum to exactly 28.5px (19 + 9.5),
   the same content-height budget the single line used, so the box's total
   height is identical whether the checkbox is on or off. */
/* v1.0.458 — padding: 27px 8px (inherited from the base rule, untouched
   until now) -> 20.75px 8px HERE (two-line mode only), per explicit report
   that the v1.0.457 enlargement made the two lines overlap ("להגדיל עוד את
   הכתב... והם אחד על השני בלי רווח"): v1.0.457's mistake was letting
   font-size exceed line-height by several px with NO extra room reclaimed
   from anywhere, banking entirely on "no overflow:hidden = safe" — true for
   avoiding clipping, but not enough headroom to avoid the two adjacent line
   boxes visually touching. Real fix: reclaim vertical space FROM this
   two-line mode's own top/bottom padding (27 -> 20.75px each side, -12.5px
   combined) and hand every bit of it to the line-heights below (28.5px ->
   41px combined) — the tile's TOTAL content-box height is unchanged
   (20.75+41+20.75 = 27+28.5+27 = 82.5px exactly), so the v1.0.453
   height-match convention still holds, but now there's real breathing room:
   both lines' line-height is set ABOVE its own font-size (not below, unlike
   v1.0.457), so the glyphs sit inside their own box with actual gap between
   them instead of bleeding into each other. Only `.has-date` gets the
   smaller padding — the plain single-line tile (no date checkbox) keeps the
   base rule's untouched 27px 8px. */
.player-list-day-tile.has-date {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  /* v1.0.459 — 20.75px -> 17.75px, per explicit follow-up request to
     enlarge the day-tile text further still: same "reclaim padding, hand it
     to line-height" move as v1.0.458 (see this rule's own v1.0.458 comment
     for the reasoning) — freed 3px each side this round. Total content-box
     height unchanged: 17.75+30+17+17.75 = 82.5px, same as v1.0.458.
     v1.0.461 — 17.75px -> 14.25px, per explicit follow-up ("להגדיל עוד קצת
     את הגודל של הספרות ובמיוחד את הגודל של היום, והשורה השנייה... להוריד
     אותה טיפה למטה"): freed another 3.5px each side, handed to the two
     lines' line-height AND to a new small margin-top on the weekday line
     (see its own v1.0.461 comment) that nudges just that second line down
     a bit, per the same request. Total content-box height still unchanged:
     14.25+32+3+19+14.25 = 82.5px, same as every round since v1.0.453 — see
     that convention's own original comment for why this total can never
     drift (it has to keep matching every other row/tile/tab's own height
     in this panel). */
  padding: 14.25px 8px;
}
/* v1.0.459 — 1.4rem/26px -> 1.6rem/30px, per explicit follow-up request
   ("להגדיל עוד את המספרים"): headroom kept comfortably positive
   (30 - 25.6 = 4.4px), same margin-of-safety logic as v1.0.458's fix for
   the v1.0.457 overlap bug. */
/* v1.0.461 — 1.6rem/30px -> 1.75rem/32px, per explicit follow-up request to
   enlarge the day-tile numbers further still: headroom kept comfortably
   positive (32 - 28 = 4px), same margin-of-safety this rule has used every
   round since the v1.0.457 overlap bug. */
.player-list-day-tile-num { font-size: 1.75rem; font-weight: 700; line-height: 32px; }
/* v1.0.459 — 0.8rem/15px -> 0.88rem/17px, same request ("...ואת השם של
   היום"), same headroom logic (17 - 14.08 ≈ 2.9px).
   v1.0.461 — 0.88rem/17px -> 1.05rem/19px, per explicit follow-up request
   ("במיוחד את הגודל של היום" — a bigger bump than the number got this
   round) plus a new margin-top:3px so this line sits a bit lower than the
   number above it ("השורה השנייה... להוריד אותה טיפה למטה"), per the same
   request. Headroom: 19 - 16.8 = 2.2px, matching this rule's own long-
   standing safe minimum. */
.player-list-day-tile-weekday { font-size: 1.05rem; font-weight: 500; color: var(--muted); line-height: 19px; margin-top: 3px; }
/* v1.0.423 — NEW, the fixed placeholder slots beyond however many days are
   actually reachable (per the existing EPG-window setting) — per explicit
   request ("באפור... אי אפשר להגיע אליהם"): dim/gray, no border, and never
   added to playerListDayTilesNav's item set in app.js, so they can't be
   focused or activated at all — purely there to keep the row always
   reading as a full, symmetric 1..7 sequence. */
.player-list-day-tile.disabled {
  color: var(--muted);
  background: rgba(255,255,255,0.03);
  border-color: transparent;
  opacity: 0.5;
}
/* v1.0.457 — NEW, per explicit request ("שיהיה כתב צהוב בשידור החי וכתב
   אדום בארכיון"): mirrors the v1.0.455 tab border colors onto the day-tile
   text itself — red while browsing archive, yellow while browsing live.
   `.tab-archive`/`.tab-live` toggled on `#playerListDayTiles` by
   setPlayerListTab() (app.js, its own matching v1.0.457 comment) every time
   the tab actually changes. `:not(.disabled)` keeps the unreachable
   placeholder slots their own muted gray regardless of which tab is active
   — matches `.player-list-day-tile.disabled`'s own explicit color above,
   and this rule's extra class (3 vs. that one's 2) would otherwise win and
   override it. Sets color on the TILE itself, not `.player-list-day-tile-
   num` directly, so it's inherited by the big date-mode number (which has
   no color of its own) but NOT by `.player-list-day-tile-weekday`, which
   keeps its own deliberate `var(--muted)` secondary-label color untouched —
   same as the plain relative-offset text (no separate span at all, so it's
   just the tile's own color either way). */
.player-list-day-tiles.tab-archive .player-list-day-tile:not(.disabled) { color: var(--danger); }
.player-list-day-tiles.tab-live .player-list-day-tile:not(.disabled) { color: var(--warning); }
/* v1.0.458 — NEW, per explicit follow-up ("גם הצבע של הכיתוב בקוביות יעלם"):
   this text color follows the exact same show/stay-while-in-structure-1,
   hide-once-on-structure-5 rule as the tab/day-tile borders right above
   (see `.player-list-panel.structure5-focused #playerTabArchive.active`'s
   own big v1.0.458 comment) — reverts to the tile's plain resting white
   once focus is actually on structure 5. `:not(.disabled)` kept for the
   same reason as the base rule (never re-color the unreachable placeholder
   slots even while this override is active). */
.player-list-panel.structure5-focused .player-list-day-tiles.tab-archive .player-list-day-tile:not(.disabled),
.player-list-panel.structure5-focused .player-list-day-tiles.tab-live .player-list-day-tile:not(.disabled) {
  /* v1.0.459 — #fff -> var(--muted), per explicit follow-up request
     ("וכשיוצאים מהם לעשות אותם אפורים גם ואת המספרים גם"): once focus is
     actually on structure 5, the day-number text now reverts to the same
     muted gray .player-list-day-tile-weekday already uses at all times
     (its own color is untouched either way, being set independently right
     below) — one consistent "resting" gray for both lines instead of a
     plain white number over an already-gray weekday label. */
  color: var(--muted);
}
.player-list-rows {
  flex: 1;
  overflow-y: auto;
  padding: 0 20px 30px 0; /* v1.0.425/429 — see .player-list-tabs's own comment above */
}
/* v1.0.437 — NEW, per explicit request ("כשעוברים ממבנה 1 ל-5 הכתב יתחלף
   בשורות בלבד לאפור בהיר"): toggled via JS on every focus-zone change (see
   updatePlayerListFocusUI in app.js) — true while playerListFocusZone is
   'numbers'. Deliberately targets .title/.time with no .epg-row.live/.past/
   .next qualifier, so its 4-class-selector specificity beats every one of
   those 3-class row-kind rules above regardless of which kind a row is —
   the whole row list reads as one dimmed block while structure 5 has focus,
   restored the instant focus returns to structure 1. Icons intentionally
   untouched — only "הכתב" (the text) was asked for. */
.player-list-rows.dimmed .epg-row .title,
.player-list-rows.dimmed .epg-row .time { color: var(--muted); }
/* Rows reuse .epg-row/.epg-row-meta/.epg-play-icon/.epg-reminder-icon as-is
   (see the "--- EPG ---" block above) — same list-row look the rest of the
   app already has, just fed by renderPlayerListRows() in app.js instead of
   renderEpgList(). */
/* v1.0.425 briefly trimmed this panel's own rows (padding 18px/margin 8px,
   scoped via .player-list-rows .epg-row) specifically to force exactly 10
   full rows to fit before the 10th got cut off — REMOVED again in v1.0.427,
   per explicit follow-up ("עשית אותם דקות מידי... אם יהיה 10 שיהיו 10 אם
   נכנס 11 אז 11, לא משנה, העיקר שיהיו רחבות"): squeezing the rows to hit a
   specific count made them too thin/cramped — rows now stay at their
   normal, comfortable shared .epg-row size (24px padding, 14px margin —
   same as everywhere else this class is used), and however many fit at
   that size is simply however many fit, scrolling for the rest exactly
   like any other list here.
   v1.0.430 — small bump back up (padding 24px -> 28px, margin-bottom 14px
   -> 16px), per explicit follow-up ("תגדיל טיפה את גובה השורות שיהיה 9
   שורות ולא תשע וחצי כמו עכשיו"): at the normal shared size, 9 full rows
   plus an awkward sliver of a 10th fit in the available height — this
   isn't chasing a specific row count (still not the goal, per the comment
   right above), just enough of a height increase to push that partial 10th
   row fully below the fold so the list reads as a clean 9, scrolling for
   the rest exactly like before. Scoped to THIS panel only (.epg-row itself,
   used elsewhere in the app, is untouched). */
/* v1.0.432 — small step back down (padding 28px -> 22px, margin-bottom
   16px -> 10px), per explicit follow-up ("תקטין את גובה השורות במעט ואת
   הרווח בינהם במעט... אולי 5 או 10 פיקס... כרגע יש לי 9 ו-3/4 שורות"):
   the v1.0.430 bump overshot a bit — still comfortably above the v1.0.425
   trim that was explicitly rejected as too thin (18px/8px), just a modest
   ~6px-per-side/~6px trim off the v1.0.430 size, in the range he asked
   for.
   v1.0.433 — vertical padding nudged 22px -> 25px, margin-bottom left
   untouched at 10px, per explicit follow-up ("את הרווח בין השורות תשאיר
   רק תגדיל במעט את הגובה שלהם יש לי 10.5 שורות תחשב שיהיה 10 בול"): at
   22px, 10.5 rows fit — a small height-only bump (not touching the gap
   between rows, per his explicit instruction) to land on a clean 10.
   v1.0.441 — 25px -> 27px, per explicit request ("תגדיל את גובה השורות...
   במעט... שיהיו 8 שורות ולא 8.5 כמו עכשיו"): since v1.0.433 this panel
   grew a 120px top gap for row 7 (see .player-list-panel's own v1.0.440
   history), which shrank the available height for this list without this
   row height ever being re-tuned to match — measured with the headless-
   render harness: at 25px only 8 full rows fit plus a ~38px sliver of a
   9th poking through (read as "8.5"). Same margin-bottom-untouched
   approach as v1.0.433 — 27px lands on 8 full rows with only a ~6px
   remainder (no meaningfully visible partial row), same convention as
   every earlier round of this exact tuning. Kept matching
   .player-number-tile's own identical v1.0.441 change, per their
   long-standing "vertical padding matches" convention. */
.player-list-rows .epg-row { padding: 27px 24px; margin-bottom: 10px; }
/* Per explicit request (third revision) — two earlier approaches were
   tried and both rejected: shrinking #video with object-fit:contain
   rescaled the whole picture and added bars on the sides; cropping the
   bottom (object-fit unchanged, a masking wrapper) hid part of the actual
   broadcast picture, which isn't allowed either — everything the station
   sends must stay visible, nothing cropped. So: #video's own height
   animates smaller (matching .player-controls-backdrop's 220px below, same
   as the two earlier attempts), and object-fit is set explicitly to
   "fill" so the whole frame is stretched/compressed to exactly match the
   new box — 100% of the picture stays visible, just vertically squeezed,
   rather than any part of it being cropped or bars appearing.
   NOTE (4th revision): leaving object-fit unset (relying on "fill" being
   the CSS spec default) looked like a zoom-in on the actual TV instead of
   a squish — this device's video element apparently does NOT fall back to
   "fill" on its own when no object-fit is given (see the .hero-video
   comment above for another documented case of this device's browser
   mishandling object-fit defaults/rendering). Setting it explicitly here
   is the fix. Reverses the same way when the controls hide. Toggled
   together with #playerControlsPanel below by
   Player.showProgressBar/hideProgressBar (player.js). */
/* v1.0.244 — #video moved from being a normal child of #playerVideoArea
   (sized via width/height:100% of that box) to a body-level element (see
   the comment at the top of <body> in index.html and on #playerVideoArea
   itself) shared with the home-screen hero preview. position:fixed +
   100vw/100vh replaces the old 100%/100% — same visual result while
   #screen-player is active (still fills the whole screen), but no longer
   dependent on a parent box's own size, since it isn't inside one any
   more. display:none by default — nothing (hero or fullscreen) shows
   until app.js/player.js explicitly turns this on via enterHeroMode()/
   enterFullscreenMode(), so there's never a stray full-screen black frame
   visible behind the setup/grid screens before any channel has ever been
   focused or played. */
#video {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  object-fit: fill;
  background: #000;
  /* Was 0.35s — per explicit feedback the shrink/grow felt too fast and a
     bit stair-steppy rather than smooth on the actual TV; lengthened
     slightly (kept in sync with .player-controls-panel's own transition
     below, which slides at the same time). Deliberately still only
     transitions height (the controls-open shrink, unrelated to hero mode)
     — see the .video-hero comment below for why the hero<->fullscreen
     resize itself is NOT animated, at least for this first cut. */
  /* v1.0.660 — NO transition any more (was height 0.5s): reported critical freezes happen exactly
     when the video gets resized — controls row opening (height 100vh -> 100vh-220px) and the
     archive list screen (video into the small window). Animating the hardware video's size every
     frame is the prime suspect; the resize is now instant. */
  transition: none;
}
#video.controls-open { height: calc(100vh - 220px); }
/* v1.0.244 — the home-screen hero preview's CSS state. top/left/width/
   height are all set as inline styles by positionHeroVideo() in app.js —
   mirrors the retired positionPreviewVideo()'s own contain-fit math
   (see the .hero-video comment above for why that's done manually in JS
   rather than via object-fit/object-position on this device), just
   re-anchored to .hero-media's live getBoundingClientRect() since #video
   is no longer a child of it. object-fit:contain here is a defensive
   fallback for the brief instant before real pixel values are known, not
   the primary sizing mechanism.
   No transition on top/left/width/height on purpose — animating the
   hero<->fullscreen resize was NOT attempted in this first cut: this
   exact TV's browser has a documented history of breaking CSS
   size/transform animations (see .hero-corner-blend's own saga further
   down this file), so the functional fix (one connection, instant
   resize, no reload) ships first and can be tried again separately once
   that's confirmed solid on the real device. */
#video.video-hero {
  object-fit: contain;
}
/* v1.0.338 — BUG FIX, per live report ("קופסת הטלויזיה בשחור מסתירה חלק
   מההגדרות"): #video is a body-level element (see the v1.0.244 comment
   above), positioned by its own .video-hero rule at fixed screen
   coordinates completely independent of whichever .screen div is
   currently shown — Screens.show('screen-restore') never touches it.
   Entering settings now calls Player.stop() (v1.0.337, see
   activateRestoreScreen in app.js), which stops playback but does NOT
   hide the element itself, so it kept sitting there — now a solid black
   box (no more picture, just its own background:#000) — right on top of
   part of the settings screen's own layout. !important beats every
   inline style.display the various play/stop code paths in app.js/
   player.js set on this SAME element, so this reliably wins regardless of
   what state playback was left in when settings opened. See
   body.settings-screen-open in app.js (activateRestoreScreen/
   returnToGrid) for where this class is actually toggled. */
body.settings-screen-open #video {
  display: none !important;
}
/* The whole bottom control cluster (backdrop, progress bar, button row —
   see #playerControlsPanel in index.html) slides up from off-screen as one
   unit, per explicit request, instead of just appearing in place. Each
   element inside keeps its own existing position/show-hide logic
   completely unchanged (this wrapper has no size of its own beyond
   matching the screen exactly) — this only adds the shared slide motion on
   top. 220px = enough to carry the tallest element (.player-controls-
   backdrop, 220px tall, anchored to the very bottom) fully below the
   screen edge when hidden. */
.player-controls-panel {
  position: absolute;
  inset: 0;
  transform: translateY(220px);
  /* Was 0.35s — see the matching comment on #video's own transition. */
  transition: transform 0.5s ease;
}
.player-controls-panel.show { transform: translateY(0); }

/* Full-screen loading cover shown while a stream is buffering after a seek
   (live-rewind / continue-watching resume) — the small .player-osd toast
   below is only a bottom bar, so on its own it left most of the screen
   looking plain black while the new stream buffered (reported as "the
   screen goes black for several seconds"). This sits ABOVE the video
   (z-index 1 > video's stacking-context-less default) so the wait always
   shows a logo + message instead of bare black, and stays up until the
   stream actually starts playing (see player.js showLoadingSplash /
   hideLoadingSplash). */
.player-loading-splash {
  position: absolute;
  inset: 0;
  background: #000;
  display: none;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: 24px;
  z-index: 1;
}
.player-loading-splash.show { display: flex; }
.player-loading-logo {
  max-width: 220px;
  max-height: 220px;
  object-fit: contain;
}
.player-loading-text {
  font-size: 1.4rem;
  color: var(--muted);
  text-shadow: 0 1px 4px rgba(0,0,0,0.6);
}

/* Redesigned per explicit request: was a long bar spanning almost the full
   screen width, anchored to the BOTTOM. Now a small card in the TOP-RIGHT
   corner instead — about a quarter of the screen's width, but taller
   (three text rows instead of one line), flush against the right edge (so
   only its LEFT corners are rounded — the right edge butts against the
   screen edge itself, same idea as a flag/tab attached to the side) and
   sitting a little below the very top rather than flush with it. Slides in
   from off-screen (further right) and fades in together, and reverses the
   same way on exit — kept at a constant `display` so the transition can
   actually run (toggling display:none/flex, the old approach, can't be
   animated). Not too fast, not too slow, per request. */
.player-osd {
  position: absolute;
  top: 56px;
  right: 0;
  /* Went 460px -> 320px (too long) -> 400px (per follow-up: widen/lengthen
     it back out a bit and enlarge the text, now that the "ends in ..." row
     is back below and needs the room). */
  width: 400px;
  background: rgba(15,17,21,0.85);
  border-top-left-radius: 18px;
  border-bottom-left-radius: 18px;
  padding: 30px 26px;
  /* Enlarged again per follow-up request ("להגדיל את הכתב") — this is the
     channel-name line's own size (it has no font-size rule of its own, see
     .player-osd-channel-name below, so it inherits straight from here). */
  font-size: 1.65rem;
  display: flex;
  align-items: center;
  gap: 18px;
  opacity: 0;
  transform: translateX(40px);
  pointer-events: none;
  transition: transform 0.35s ease, opacity 0.35s ease;
}
/* v1.0.330 — per explicit request/toggle (see Storage.getPlayerOsdHidden in
   storage.js, applyPlayerOsdHiddenVisual in app.js). `display` is kept
   constant everywhere above so the slide/fade transition can run (see this
   block's own comment above) — `.show`/`.flash` only ever touch
   opacity/transform, never display — so this is the one rule that actually
   needs `!important`: without it, JS setting `.show` would still win over
   a plain `display:none` here since both rules match the SAME element with
   the same specificity, applied in DOM order. Default (no body class) =
   today's behavior, card still slides in/out normally. */
body.player-osd-hidden .player-osd {
  display: none !important;
}
.player-osd.show {
  opacity: 1;
  transform: translateX(0);
  pointer-events: auto;
}
/* Per explicit request: the card starts RED for its first ~1.5s, then
   fades to its normal dark background — a dark card over a dark video
   frame ("black on black") was easy to miss appearing. Kept as a separate
   class from .show (restarted explicitly via classList remove+reflow+add
   in Player.showOsd, since re-adding a class that's already present
   wouldn't otherwise restart the animation — e.g. switching channels while
   the card is still up from the previous one) so it doesn't interfere with
   .show's own slide-in transition. */
.player-osd.flash {
  /* Per explicit follow-up request: the red hold was too brief to actually
     register on screen — extended by ~1s (0.5s -> 1.5s hold, see the
     keyframe below), keeping the same 1s fade and 0.5s dark hold after it
     (so total went from 2s -> 3s, not just the hold in isolation). */
  animation: osd-bg-flash 3s ease forwards;
}
/* Bordeaux/wine red per explicit request — darker than the app's regular
   --danger red used everywhere else (error text, the digit-overlay error
   state, etc.), so this literal color is scoped to just this animation
   instead of touching the shared --danger variable. */
@keyframes osd-bg-flash {
  0% { background-color: #6d0f22; }
  50% { background-color: #6d0f22; }
  83% { background-color: rgba(15,17,21,0.85); }
  100% { background-color: rgba(15,17,21,0.85); }
}
.player-osd-logo {
  height: 56px;
  max-width: 80px;
  object-fit: contain;
  flex-shrink: 0;
}
/* Text column: channel name, then the airing programme's name, then the
   smaller "ends in ..." line — see the comment on #playerOsd in index.html
   for why the last two are optional. min-width: 0 lets the ellipsis rules
   below actually kick in inside a flex child instead of overflowing. */
.player-osd-info {
  display: flex;
  flex-direction: column;
  gap: 5px;
  min-width: 0;
}
.player-osd-channel-name {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.player-osd-channel-name { font-weight: 700; }
/* Per explicit follow-up request, a program name too long to fit no longer
   truncates with "..." — it scrolls through to its end instead (see
   Player.setupProgramNameMarquee in player.js, which measures the inner
   span against this box and only starts the animation when the text
   actually overflows). This outer element is now just the fixed-width
   masking box: overflow:hidden + no text-overflow/ellipsis, since there's
   no static text of its own anymore (see .player-osd-program-name-inner
   below for the moving part). */
.player-osd-program-name {
  /* Enlarged again per follow-up request, same round as .player-osd-ends-in
     below. */
  font-size: 1.55rem;
  color: var(--muted);
  display: none;
  overflow: hidden;
  white-space: nowrap;
}
.player-osd-program-name.show { display: block; }
.player-osd-program-name-inner {
  display: inline-block;
  white-space: nowrap;
  transform: translateX(0);
}
/* Only added once JS has measured a real overflow — a short title that
   already fits stays completely still, never gets this class, and the
   custom properties below stay at their JS-set values (distance/duration)
   only while marquee is actually running. Pauses briefly at the start
   (see the "+2s" built into the duration calc in setupProgramNameMarquee)
   before scrolling once through, then jumps back to the start and repeats
   — this is a "now playing" card, not something worth a fancier
   back-and-forth animation. */
.player-osd-program-name-inner.marquee {
  animation-name: osd-marquee;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}
@keyframes osd-marquee {
  0% { transform: translateX(0); }
  8% { transform: translateX(0); }
  92% { transform: translateX(var(--marquee-distance, 0)); }
  100% { transform: translateX(var(--marquee-distance, 0)); }
}
/* Re-added per follow-up request (was dropped, then the card was widened
   and given bigger text specifically so there'd be room for this again). */
.player-osd-ends-in {
  /* Enlarged again per follow-up request, same round as
     .player-osd-program-name above. */
  font-size: 1.2rem;
  color: var(--muted);
  display: none;
}
.player-osd-ends-in.show { display: block; }

/* Scrub bar: shown once you've stepped out of pure-live (Enter to restart
   the current programme, or a true archive pick from the EPG). Red fill,
   RTL — it grows from the right edge as the programme progresses, matching
   the app's own reading direction, rather than the more common LTR convention.
   "Time remaining" is the number shown (not elapsed) — total duration sits
   on the far side for context. Left/right still map to rewind/fast-forward
   regardless of fill direction, since that's a remote-control convention,
   not a text-direction one. */
.player-progress {
  position: absolute;
  bottom: 78px;
  right: 40px;
  left: 40px;
  background: rgba(15,17,21,0.85);
  border-radius: 12px;
  padding: 14px 24px;
  display: none;
  align-items: center;
  gap: 16px;
}
.player-progress.show { display: flex; }
.player-time { font-size: 1.05rem; color: var(--muted); min-width: 60px; text-align: center; }

/* Label BELOW the bar ("ארכיון") with a small tail arrow under that,
   pointing back up at the bar — shown/hidden together with the bar itself.
   The bar itself sits higher up (bottom: 78px) specifically to leave room
   for this pair underneath it. */
.player-progress-label {
  position: absolute;
  bottom: 50px;
  right: 40px;
  left: 40px;
  text-align: center;
  color: var(--muted);
  font-size: 0.95rem;
  font-weight: 500;
  text-shadow: 0 1px 4px rgba(0,0,0,0.6);
  display: none;
}
.player-progress-label.show { display: block; }
/* Per explicit request: every player control (pause, the archive marker,
   restart, favorite, and the contextual channel-hop trio / archive
   next-prev pair) now lives in ONE flex row instead of being scattered
   across several hand-positioned absolute spots (see the comment on
   #playerControlsRow in index.html for the full reasoning) — evenly
   spaced, same size, sitting right above the progress bar. Always
   display:flex (not gated by its own 'show' class): with nothing playing
   every child inside is individually display:none via its OWN 'show'
   class exactly as before, so an "empty" row renders nothing and needs no
   extra visibility bookkeeping of its own. Flexbox reflows automatically
   as contextual children's 'show' class toggles, which is what let all
   the old per-icon translateX offset math (and the "paired" class) go
   away entirely. */
.player-controls-row {
  position: absolute;
  bottom: 135px;
  right: 40px;
  left: 40px;
  display: flex;
  align-items: center;
  /* Per explicit follow-up request: was space-between (spread edge to edge
     across the full row width) — now clustered together on the right
     side (this page's dir="rtl" start edge, where the DOM's first child —
     pause — already sits) with a small fixed gap between icons instead. */
  justify-content: flex-start;
  gap: 24px;
}

/* Shared look for every plain round icon in the row (pause, restart,
   favorite, the channel-hop arrows, the archive-next/prev arrows, and the
   archive/EPG marker) — same size, same white-with-black-outline
   treatment so a white icon stays visible against a bright broadcast
   behind it. Each one's own rule below only adds what's actually
   different about it (glyph swapping, active/focused color, etc). */
.player-pause-icon,
.player-restart-icon,
.player-fav-heart,
.player-channel-arrow,
.player-progress-arrow {
  width: 64px;
  height: 64px;
  color: #fff;
  filter: drop-shadow(1.5px 0 0 #000) drop-shadow(-1.5px 0 0 #000)
          drop-shadow(0 1.5px 0 #000) drop-shadow(0 -1.5px 0 #000)
          drop-shadow(0 1px 4px rgba(0,0,0,0.6));
  display: none;
  flex-shrink: 0;
  transition: transform 0.15s ease;
}
.player-pause-icon.show,
.player-restart-icon.show,
.player-fav-heart.show,
.player-channel-arrow.show,
.player-progress-arrow.show { display: block; }

/* Archive marker — per explicit request, redrawn in index.html as an
   EPG-list glyph (a circle with 3 rows) now that it sits in this row
   alongside same-sized round icons, instead of the red triangle it used to
   be below the bar on its own. Turns blue (--accent, same as pause and the
   seek-bar thumb) when focused, not the red the other row icons use. */
.player-progress-arrow.focused { color: var(--accent); transform: scale(1.15); }

/* Restart-to-beginning icon — Enter while selected is what actually
   restarts, so a stray Up press landing here can't jump to the start by
   itself. */
.player-restart-icon.focused { color: var(--danger); transform: scale(1.15); }

/* Favorite toggle — white outline normally, solid red once the channel is
   a favorite (same convention as the grid tiles' .fav-heart). Selected
   (.focused) turns the outline red too, so favoriting it fills it solid
   red on top of that same red outline rather than introducing a different
   color for "selected". */
.player-fav-heart path { fill: none; stroke: #fff; stroke-width: 1.8; }
.player-fav-heart.active path { fill: var(--danger); stroke: var(--danger); }
.player-fav-heart.focused { transform: scale(1.15); }
.player-fav-heart.focused path { stroke: var(--danger); }
/* Live-quick row (Down while genuinely live — see Player.showLiveQuickControls
   in player.js) — per explicit request, every button there shares the same
   blue focus color (pause/the archive marker already use --accent; only the
   heart's own focused color, red above, needs overriding here). Scoped to
   .player-controls-panel.live-quick so archive/live-dvr's own red-heart
   convention elsewhere is untouched. */
.player-controls-panel.live-quick .player-fav-heart.focused path { stroke: var(--accent); }

/* Dedicated play/pause button — shows the pause glyph while playing, swaps
   to the play triangle once paused (.paused, toggled by
   Player.updatePauseIconState) — the triangle itself points left (see
   index.html), matching this RTL app's own direction convention rather
   than the universal left-to-right one. */
.player-pause-icon .icon-play { display: none; }
.player-pause-icon.paused .icon-pause { display: none; }
.player-pause-icon.paused .icon-play { display: block; }
/* v1.0.201 — SIMPLIFIED per explicit request, resolving the whole
   mode-based-color saga (v1.0.136-200): no more live-quick-specific
   override. Not focused = plain white (the shared base rule above,
   unchanged). Focused = red (var(--danger)), always, regardless of mode —
   exactly matching every other button in this row (heart/restart/
   channel-arrows), which already just turns red on focus with no
   mode-dependent exception. If a live-quick-only color is ever wanted
   again, reintroduce a scoped override rule the same way
   .player-controls-panel.live-quick .player-fav-heart.focused does above
   — but per explicit request this is now plain and unconditional. */
.player-pause-icon.focused { color: var(--danger); transform: scale(1.15); }

/* Channel-hop trio — live-dvr only (see Player.focusChannelUp/
   focusReturnLive/focusChannelDown in player.js). The two arrows share the
   other row icons' look; the middle button is a pill with its own "live"
   dot, styled a little differently since it carries text rather than a
   bare glyph. */
.player-channel-arrow.focused { color: var(--danger); transform: scale(1.15); }

.player-return-live-btn {
  display: none;
  align-items: center;
  gap: 10px;
  padding: 10px 22px;
  border-radius: 999px;
  background: rgba(15,17,21,0.85);
  border: 2px solid #fff;
  color: #fff;
  font-size: 1rem;
  font-weight: 600;
  white-space: nowrap;
  filter: drop-shadow(0 1px 4px rgba(0,0,0,0.6));
  transition: transform 0.15s ease, border-color 0.15s ease, color 0.15s ease;
  flex-shrink: 0;
}
.player-return-live-btn.show { display: flex; }
.player-return-live-btn.focused {
  border-color: var(--danger);
  color: var(--danger);
  transform: scale(1.1);
}
.player-return-live-btn .live-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--danger);
  display: inline-block;
}

.player-progress-track {
  position: relative;
  flex: 1;
  height: 8px;
  border-radius: 4px;
  background: var(--border);
  /* was "hidden" — a plain rounded clip is fine for the bar itself, but it
     would also chop off the top/bottom of the position thumb below, which
     is deliberately taller than the 8px track (see .player-progress-fill
     ::after). The fill's own border-radius still keeps its edge looking
     rounded without this. */
  overflow: visible;
}
.player-progress-fill {
  position: absolute;
  top: 0;
  right: 0;
  height: 100%;
  width: 0%;
  background: var(--danger);
  border-radius: 4px;
}
/* v1.0.201 — per explicit request: #playerLiveInfoProgress (element #60 —
   the read-only live-quick info bar, Down while genuinely live, see
   showLiveQuickProgress in app.js) always shows blue, never the real seek
   bar's red — it's never actually seekable (see the "never gets
   bar-focused" note on it in index.html), so it should never look like
   the interactive red bar below it does once loaded. This overrides the
   shared .player-progress-fill rule above by ID, so #playerProgress
   itself (the real seek bar, element #57) keeps its own red default /
   blue-while-.loading / blue-while-.bar-focused behavior untouched. */
#playerLiveInfoProgress .player-progress-fill {
  background: var(--accent);
}
/* Backdrop behind the bottom control cluster (progress bar, archive
   triangle, restart/heart/pause icons) in the fullscreen player — same
   black-to-gray gradient as a home-screen tile (.row-channel), rising up
   from the true bottom edge, sized to just cover that cluster. Sits behind
   everything else in the player (placed right after <video> in the DOM,
   before the loading splash/OSD/icons — see index.html) so it never covers
   any of the actual controls, just gives them a dark surface to sit on
   against a bright broadcast picture. Shown/hidden together with the
   progress bar itself (see Player.showProgressBar/hideProgressBar). */
.player-controls-backdrop {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 220px;
  background: linear-gradient(to top, #000000 0%, #000000 32%, #585a66 100%);
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.2s ease;
}
.player-controls-backdrop.show { opacity: 1; }

/* Poster art for the currently-airing/currently-playing movie (movies-group
   channels only — see MOVIES_GROUP_NAME/isMoviesGroup in app.js), shown
   here in the fullscreen player instead of on the home-screen tile itself
   (that "enlarge the row into tall poster tiles" version was tried and
   reverted per explicit request — the tile moved sideways in a way that
   didn't look good, and coverage/quality were inconsistent; this poster
   only ever lives here now). Sits on the right side (this page's dir="rtl"
   start edge), taller than .player-controls-backdrop's 220px so it pokes
   up past the top edge of that gradient's fade — per explicit follow-up
   request, sized 25% bigger than the original size (170x250 -> 213x313),
   then another 25% on top of THAT per a further request (213x313 ->
   266x391 — same cumulative math as computing 170x250 * 1.5625 directly,
   so the box's own width:height ratio stays the exact same as the very
   first size, nothing skewed), and resting 30px higher (bottom: 30px, was
   0, then 5, then +10, then +15 more — the poster started too close to the
   bottom edge and felt disconnected from the rest of the panel) while
   actually showing. object-fit:cover below
   already means the POSTER IMAGE itself is never stretched to fit this
   box — it fills the box while keeping its own native aspect ratio, only
   cropping whatever little overflow doesn't fit (170:250/213:313/266:391
   are all within a couple percent of a real poster's usual 2:3 ratio, so
   that crop is barely noticeable). Existence (.show, toggled by Player.setPoster
   in player.js, reset to hidden on every Player.play()) and panel-
   visibility (the transform below) are two independent things: with no
   poster match, or nothing playing, .show never gets added at all and the
   controls keep their normal full width (see the .has-poster overrides
   right below), so a missing poster never leaves a gap or placeholder
   box. */
.player-poster-img {
  position: absolute;
  right: 40px;
  bottom: 30px;
  width: 266px;
  height: 391px;
  object-fit: cover;
  border-radius: 10px;
  box-shadow: 0 6px 18px rgba(0,0,0,0.6);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease, transform 0.5s ease;
  /* Extra downward shift ON TOP OF #playerControlsPanel's own shared
     translateY(220px) hide-transform (see that rule above) — this element
     alone (391px tall, resting 30px above the true bottom edge) is taller
     than that shared 220px covers, so without this a sliver of it stayed
     visible even while the panel was otherwise fully "hidden". The math
     (391+30-220=201px, +7px margin = 208px) turned out not to be enough in
     practice — reported still leaving a ~20px sliver on screen (e.g. right
     after Enter on an archive row, before the panel/OSD catches up) — so
     per explicit follow-up request this is now +50px more (208->258px),
     a comfortably bigger margin than the exact on-paper minimum. Reset to
     0 only once the PANEL itself is actually shown (see
     .player-controls-panel.show below) — deliberately not keyed off this
     element's own .show, since that's about whether a poster exists at
     all, not whether the panel is currently expanded. */
  transform: translateY(258px);
}
.player-poster-img.show { opacity: 1; }
.player-controls-panel.show .player-poster-img { transform: translateY(0); }

/* While a poster is showing (.player-controls-panel.has-poster, toggled
   together with .player-poster-img.show — see Player.setPoster), the
   progress bar and control row shrink in from the right to leave room for
   it instead of running underneath/behind it — per explicit request
   ("נצמצם את המסך התקדמות ואת כל הפקדים"). 328px = the poster's own right
   offset (40px) + its current width (266px) + a small gap (22px,
   rounded) — recomputed to match the poster's own resize above. The
   left edge (40px) is untouched, so only the right side compresses. */
.player-controls-panel.has-poster .player-progress,
.player-controls-panel.has-poster .player-controls-row,
.player-controls-panel.has-poster .player-progress-label {
  right: 328px;
}

/* Archive next/prev-programme arrows — archive mode only (see
   Player.hasArchiveNext/hasArchivePrev, set from app.js's EPG cache), a
   flex child of .player-controls-row like everything else in that row now
   (never shown together with the live-dvr trio, since one is archive-only
   and the other live-dvr-only — flexbox just reflows around whichever one
   is actually visible). Left-pointing glyph = move to the next/more-recent
   programme, right-pointing = previous/older — same convention as the
   channel-hop pair. Same shared sizing/outline as the other row icons —
   see .player-channel-arrow above. */
.player-archive-nav-icon {
  width: 64px;
  height: 64px;
  color: #fff;
  filter: drop-shadow(1.5px 0 0 #000) drop-shadow(-1.5px 0 0 #000)
          drop-shadow(0 1.5px 0 #000) drop-shadow(0 -1.5px 0 #000)
          drop-shadow(0 1px 4px rgba(0,0,0,0.6));
  display: none;
  flex-shrink: 0;
  transition: transform 0.15s ease;
}
.player-archive-nav-icon.focused { color: var(--danger); transform: scale(1.15); }
.player-archive-nav-icon.show { display: block; }

/* Auto-hide-duration clock icon — sits to the right of the archive triangle
   marker, same height, right side of the screen (see index.html). Focusing
   it opens the duration-choice row leftward (see .player-clock-menu below);
   Enter/Down while the archive TRIANGLE itself is focused still enters the
   archive as before — this icon is a separate, further stop reached by
   pressing Right from the triangle. */
.player-clock-icon {
  position: absolute;
  bottom: 10px;
  right: 40px;
  width: 34px;
  height: 34px;
  color: #fff;
  filter: drop-shadow(1.5px 0 0 #000) drop-shadow(-1.5px 0 0 #000)
          drop-shadow(0 1.5px 0 #000) drop-shadow(0 -1.5px 0 #000)
          drop-shadow(0 1px 4px rgba(0,0,0,0.6));
  display: none;
  transition: transform 0.15s ease;
}
.player-clock-icon.focused { color: var(--danger); transform: scale(1.2); }
/* Feature removed per explicit request (the navigation around it, and its
   duration menu below, was unreliable and made the whole bottom control row
   feel broken) — kept permanently hidden here rather than deleting the
   underlying JS/HTML, since several other focus methods still defensively
   reference and blur this element for mutual exclusion; leaving it in place
   (just never shown) is the lowest-risk way to retire it. See the
   app.js comment on Player.autoHideMs's restoration in boot(). */
.player-clock-icon.show { display: none; }

/* Duration-choice row, unfolding leftward from the clock icon (see
   Player.focusClockIcon in player.js) — four options (3/5/7/10 seconds,
   closest-to-the-icon = 3s per request), each a small red/outlined
   selection circle plus its label. Only the row's own options move focus
   between themselves and back to the clock icon; the clock icon's own
   'show' class already gates this row's visibility since it's only ever
   shown alongside it. */
.player-clock-menu {
  position: absolute;
  bottom: 4px;
  right: 84px;
  display: none;
  align-items: center;
  gap: 18px;
  background: rgba(15,17,21,0.85);
  border-radius: 999px;
  padding: 10px 20px;
}
/* Same removal as .player-clock-icon.show above — kept hidden, not deleted. */
.player-clock-menu.show { display: none; }
.player-clock-menu-option {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--muted);
  font-size: 0.95rem;
  white-space: nowrap;
}
.player-clock-menu-option-dot {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  border: 2px solid var(--muted);
  flex-shrink: 0;
}
.player-clock-menu-option.selected .player-clock-menu-option-dot {
  border-color: var(--danger);
  background: var(--danger);
}
.player-clock-menu-option.focused { color: var(--text); }
.player-clock-menu-option.focused .player-clock-menu-option-dot { transform: scale(1.15); }
/* Blue "still loading, not interactive yet" state — shown the instant Enter
   starts a live rewind (see Player.enterLiveRewind), before there's any
   real position to reflect, and swapped back to the normal red fill once
   playback has actually resumed (see finishRewindLoad in player.js). Same
   blue as the seek-bar thumb (.player-progress-fill::after) and used the
   same way: blue means "don't touch me yet", per request — the bar (and
   the restart/heart/pause icons alongside it) aren't shown as focusable at
   all while this class is present, see the Player.rewindLoading lock in
   ScreenHandlers['screen-player'] (app.js). */
.player-progress.loading .player-progress-fill {
  background: var(--accent);
}
/* Circular "you are here" thumb at the current position — the fill is
   anchored to the right edge and grows leftward as the programme
   progresses (see the RTL comment above .player-progress), so the fill's
   LEFT edge is exactly the boundary between elapsed/remaining, i.e. the
   playhead. Shown only while the seek bar itself is the focused thing (see
   Player.updateBarThumb) — hidden the moment restart/heart/pause takes
   over, per request, modeled on the same idea as the grid's focus circle. */
.player-progress-fill::after {
  content: '';
  position: absolute;
  left: 0;
  top: 50%;
  transform: translate(-50%, -50%);
  /* Enlarged from 18px per explicit follow-up request (was hard to spot). */
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 4px rgba(123,92,255,0.35), 0 1px 4px rgba(0,0,0,0.6);
  display: none;
}
/* Per explicit request — a small readout of how much the current
   left/right scrub gesture has moved the position, shown right above the
   thumb (.player-progress-fill::after above) while actively scrubbing —
   see Player.seek/showSeekPreview in player.js for when it's filled in and
   shown/hidden. Shares the fill's own left:0 edge (same x-position as the
   thumb, which sits at that same edge) so it always tracks directly above
   it as the fill's width — and so this edge's position — changes; no
   separate positioning math needed. */
.player-seek-offset {
  position: absolute;
  left: 0;
  bottom: 100%;
  transform: translate(-50%, -10px);
  white-space: nowrap;
  padding: 3px 9px;
  border-radius: 6px;
  background: rgba(20, 20, 24, 0.9);
  color: #fff;
  font-size: 20px;
  font-weight: 600;
  display: none;
  pointer-events: none;
}
.player-seek-offset.show { display: block; }
.player-progress.bar-focused .player-progress-fill::after {
  display: block;
}
/* v1.0.202 — SWAPPED per explicit request (was: bar-focused/scrubbing =
   blue, per an earlier request — see git history). Now: while actually in
   the movement/scrub state (bar itself is the focused thing, see
   Player.updateBarThumb) the bar stays its normal red (var(--danger),
   same as the shared default above) — explicit here for clarity even
   though it matches the unstated default. */
.player-progress.bar-focused .player-progress-fill {
  background: var(--danger);
}
/* v1.0.203 — GENERALIZED per explicit follow-up: the v1.0.202 round only
   scoped this to the pause icon specifically ('pause-focused'/
   this.pauseFocused). Per explicit clarification, it was actually meant
   for the WHOLE button group next to the bar (pause, restart, heart, the
   channel-hop trio, archive next/prev, the EPG/archive marker, the clock
   icon) — i.e. any time focus is on some icon in the row INSTEAD OF the
   bar itself, not just when it's specifically on pause. That's exactly
   the negation of bar-focused (see anyIconFocused in
   Player.updateBarThumb, player.js) — renamed the toggled class from
   'pause-focused' to 'icon-focused' to match, still computed in the same
   place. */
.player-progress.icon-focused .player-progress-fill {
  background: var(--accent);
}

/* Real-time clock, bottom-left corner of the screen — per explicit request,
   for doing timing comparisons/tests against the actual current time. Body-
   level (like .fav-toast/.digit-overlay), but per a later explicit request
   only actually shown while the home screen (screen-grid) is active, and
   only when not turned off entirely in settings — see
   updateScreenClockVisibility (app.js), which toggles .hide below. A few
   millimeters in from the true corner (not flush against it) per request —
   ~12px reads as "a few mm" on this TV's 1920x1080 canvas. */
.screen-clock {
  position: fixed;
  left: 12px;
  bottom: 10px;
  font-size: 1rem;
  color: var(--muted);
  text-shadow: 0 1px 3px rgba(0,0,0,0.8);
  z-index: 25;
  pointer-events: none;
  direction: ltr;
}
.screen-clock.hide { display: none; }

/* --- Exit confirmation dialog ---
   Shown from the rail's "יציאה" menu item. Back on the grid's root screen
   no longer force-quits the app directly — it now always jumps to the rail
   itself (see the grid ScreenHandler in app.js), and Exit lives inside
   that rail menu like any other item. */
.exit-dialog {
  position: fixed;
  inset: 0;
  background: rgba(5,5,6,0.75);
  display: none;
  align-items: center;
  justify-content: center;
  z-index: 100;
}
.exit-dialog.show { display: flex; }
.exit-dialog-box {
  background: var(--card);
  border: 2px solid var(--border);
  border-radius: 16px;
  padding: 40px 56px;
  text-align: center;
  min-width: 480px;
}
.exit-dialog-box p {
  font-size: 1.5rem;
  margin: 0 0 28px;
}
.exit-dialog-actions {
  display: flex;
  gap: 20px;
  justify-content: center;
}

/* v1.0.392 — NEW: the small "<channel> — <programme>" info line above the
   actual question in archiveAddHomeDialog — same box/actions styling as
   every other exit-dialog, just a dimmer/smaller first line so the real
   question ("הוסף למסך הבית?") stays the visual focus. */
.exit-dialog-box p.dialog-info-line {
  font-size: 1.1rem;
  color: var(--muted);
  margin-bottom: 12px;
}

/* v1.0.505 — NEW: viewingModeDialog's own title needs a tighter bottom
   margin than every other .exit-dialog-box title (the default 28px, set on
   the bare "p" rule above) now that a second, smaller line (the channel
   name — #viewingModeDialogChannel, .dialog-info-line) sits right under it.
   Every other exit-dialog still has just one line, unaffected. */
#viewingModeDialogTitle {
  margin-bottom: 6px;
}

/* v1.0.493 — NEW: the "בחר אופן צפייה" tile-tap prompt (#viewingModeDialog
   in index.html), per explicit request that it NOT look like every other
   confirm dialog here (.exit-dialog above — a full-viewport dark-backdrop
   modal that blanks out whatever's behind it, which read to him as a
   fullscreen takeover even though it's shown on screen-grid). This is a
   small floating bar instead — no `inset:0`, no backdrop — so the home
   screen grid stays fully visible behind/around it; a fixed position near
   the bottom of the screen, own card background + border only around
   itself. Title and both buttons sit in a single flex row ("בשורה אחת"),
   not stacked like .exit-dialog-box's title-then-button-row layout. */
.tile-tap-bar {
  display: none;
  position: fixed;
  left: 50%;
  bottom: 60px;
  transform: translateX(-50%);
  align-items: center;
  gap: 24px;
  background: var(--card);
  border: 2px solid var(--border);
  border-radius: 14px;
  padding: 16px 28px;
  box-shadow: 0 8px 28px rgba(0,0,0,0.45);
  z-index: 100;
  max-width: 90vw;
}
.tile-tap-bar.show { display: flex; }
.tile-tap-bar-title {
  font-size: 1.15rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tile-tap-bar-actions {
  display: flex;
  gap: 12px;
  flex-shrink: 0;
}

/* ==========================================================================
   v1.0.560 row 14 / row 15 / archive poster — all INSIDE row 13
   (#playerBrowseBackdrop: x 20..955, y 822.5..972.5), 12px inner padding.
   All three are direct #screen-player children now (see index.html), so
   every number here is a real screen coordinate (screen-player = 1920x1080).
   - row 15 (controls): top of row 13  -> top 834.5px, 64px tall.
   - row 14 (progress): bottom of row 13 -> bottom 119.5px (1080-972.5+12).
   - poster: 133x196 (half of the fullscreen 266x391), bottom-aligned with
     rows 14 (bottom 119.5px), right edge 12px inside row 13's right edge
     (1920-943 = 977px from the screen's right... i.e. left 810px). It is
     taller than row 13, so it sticks ~58px up above it — per explicit choice.
   - no poster: rows 14/15 span the whole inner width (x 32..943), controls
     sit on the right (RTL flex-start). With poster: their right edge moves
     to 794 (poster left 810 - 16 gap).
   .swapped mirrors row 13 to the right side of the screen (x 965..1900).
   ========================================================================== */
#screen-player.list-mode #playerBrowseProgress { top: auto; bottom: 119.5px; left: 32px; right: 977px; }
#screen-player.list-mode .player-browse-controls-row { top: 834.5px; bottom: auto; left: 32px; right: 977px; }
#screen-player.list-mode.browse-has-poster #playerBrowseProgress,
#screen-player.list-mode.browse-has-poster .player-browse-controls-row { right: 1126px; }
#screen-player.list-mode.swapped #playerBrowseProgress,
#screen-player.list-mode.swapped .player-browse-controls-row { left: 977px; right: 32px; }
#screen-player.list-mode.swapped.browse-has-poster #playerBrowseProgress,
#screen-player.list-mode.swapped.browse-has-poster .player-browse-controls-row { right: 181px; }
.player-browse-poster-img {
  display: none;
  position: absolute;
  left: 810px;
  bottom: 119.5px;
  width: 133px;
  height: 196px;
  object-fit: cover;
  border-radius: 5px;
  box-shadow: 0 6px 18px rgba(0,0,0,0.6);
  pointer-events: none;
  z-index: 6; /* "in front" of everything else here, incl. row 12 / tiles' z-index:5 */
}
#screen-player.list-mode.browse-has-poster .player-browse-poster-img { display: block; }
#screen-player.list-mode.swapped .player-browse-poster-img { left: auto; right: 32px; }
/* v1.0.560 — the fullscreen poster (#playerPosterImg, inside the fullscreen controls panel) was
   still partly visible, dimmed, behind the archive screen's controls area (its off-screen
   translateY isn't enough inside #playerVideoArea's shorter 538px list-mode box). Replaced here
   by #playerBrowsePosterImg above, so it's hidden in list-mode only; fullscreen is unaffected. */
/* (v1.0.562 — the list-mode "hide fullscreen poster" rule that was here is REMOVED: the poster is now part of the small in-TV panel below.) */

/* ==========================================================================
   v1.0.561 — per explicit request (archive screen, rows 13/14 + poster):
   - row 13 (gradient) grows UPWARD: its top now sits 10px below the TV's gray
     frame (#playerTvFocusFrame bottom = 117.5 + 473 = 590.5 -> top 600.5) —
     10px = the same gap as between archive rows / structure-5 tiles
     (margin-bottom:10px). Bottom unchanged (972.5, 20px above row 8).
     Height = 972.5 - 600.5 = 372px.
   - row 14 (progress) moves to the TOP of row 13, exactly the TV frame's
     width (x 20..955), no longer shrinking for the poster.
   - poster halved again: 133x196 -> 67x98, same bottom/right anchoring
     (bottom 119.5px, right edge x=943 -> left 876px).
   - row 15 (controls) intentionally left exactly as v1.0.560 for now.
   ========================================================================== */
#screen-player.list-mode .player-browse-backdrop { top: 600.5px; height: 372px; }
#screen-player.list-mode #playerBrowseProgress,
#screen-player.list-mode.browse-has-poster #playerBrowseProgress { top: 600.5px; bottom: auto; left: 20px; right: 965px; }
#screen-player.list-mode.swapped #playerBrowseProgress,
#screen-player.list-mode.swapped.browse-has-poster #playerBrowseProgress { left: 965px; right: 20px; }
.player-browse-poster-img { left: 876px; width: 67px; height: 98px; }

/* ==========================================================================
   v1.0.564 — ARCHIVE SCREEN: the fullscreen controls panel, permanently shown
   BELOW the TV (replaces v1.0.562's pop-up-inside-the-TV version, per explicit
   follow-up). #playerControlsPanel keeps its fullscreen 1920x1080 layout and is
   only scaled (--tvp-scale = TV frame width / 1920), so the gradient, buttons,
   progress bars and poster all keep the exact fullscreen proportions. Its bottom
   sits 10px above row 8, its left/right edges match the TV frame — see
   positionListModePanel() in app.js, which sets --tvp-scale/--tvp-left/--tvp-top.
   The TV itself no longer shrinks. Rows 13/14/15 + the browse poster stay removed.
   ========================================================================== */
#screen-player.list-mode .player-browse-backdrop,
#screen-player.list-mode #playerBrowseProgress,
#screen-player.list-mode .player-browse-controls-row,
#screen-player.list-mode .player-browse-poster-img { display: none !important; }
#screen-player.list-mode .player-controls-panel,
#screen-player.list-mode .player-controls-panel.show {
  inset: auto;
  left: var(--tvp-left, 0px);
  top: var(--tvp-top, 0px);
  width: 1920px;
  height: 1080px;
  transform-origin: 0 0;
  transform: scale(var(--tvp-scale, 0.487));
  transition: none;
}
/* v1.0.565 — bigger: gradient exactly 2 archive rows tall (scale = 183/220), panel layout width
   shrunk so it still spans exactly the TV frame; top corners of the gradient rounded (12px real);
   poster capped at 3 rows tall (same aspect). All values come from positionListModePanel(). */
#screen-player.list-mode .player-controls-panel,
#screen-player.list-mode .player-controls-panel.show { width: var(--tvp-width, 1920px); }
#screen-player.list-mode .player-controls-backdrop { border-radius: var(--tvp-radius, 14px) var(--tvp-radius, 14px) 0 0; }
#screen-player.list-mode .player-poster-img { width: var(--tvp-poster-w, 266px); height: var(--tvp-poster-h, 391px); }
#screen-player.list-mode .player-controls-panel.has-poster .player-progress,
#screen-player.list-mode .player-controls-panel.has-poster .player-controls-row,
#screen-player.list-mode .player-controls-panel.has-poster .player-progress-label { right: var(--tvp-poster-offset, 328px); }

/* v1.0.565 — archive screen info block (see index.html / updatePlayerListInfo). */
.player-list-info { display: none; }
#screen-player.list-mode .player-list-info {
  display: flex; flex-direction: column; position: absolute; box-sizing: border-box;
  padding: 0 12px; gap: 8px; overflow: hidden; color: #fff; z-index: 4; pointer-events: none;
}
#screen-player.list-mode.browse-has-poster .player-list-info { padding-right: calc(var(--tvp-poster-real-w, 223px) + 16px); }
#screen-player.list-mode.swapped.browse-has-poster .player-list-info { padding-right: calc(var(--tvp-poster-real-w, 223px) + 16px); }
.pli-status { font-size: 1.15rem; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pli-body { display: flex; flex-direction: row; gap: 20px; min-height: 0; flex: 1; }
.pli-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.pli-side { flex: 0 0 auto; display: none; flex-direction: column; gap: 6px; align-items: flex-end; direction: ltr; }
.player-list-info.has-facts .pli-side { display: flex; }
.pli-logo { display: none; max-height: 48px; max-width: 260px; width: auto; height: auto; align-self: flex-start; }
.player-list-info.has-logo .pli-logo { display: block; }
.player-list-info.has-logo .pli-title { display: none; }
.pli-title { font-size: 1.45rem; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pli-desc { font-size: 1.1rem; color: var(--muted); line-height: 1.35; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; }
.pli-rating { display: flex; gap: 2px; }
.pli-rating .hero-movie-rating-star { font-size: 1.2rem; }
.pli-facts { font-size: 1.05rem; color: var(--muted); white-space: nowrap; }
#screen-player.list-mode .player-controls-panel .player-poster-img { transition: opacity 0.2s ease; }

/* v1.0.563 — the TV's frame (#playerTvFocusFrame), per explicit request:
   - not focused: same color as structure 5's tiles (.player-number-tile background: var(--card))
   - focused (white until now): the same light gray every focused tile/row/tab uses
     (.player-number-tile.focused / .player-list-tab.focused: rgba(255,255,255,0.16)). */
#screen-player.list-mode .player-tv-focus-frame { border-color: var(--card); }
#screen-player.list-mode .player-video-area.tv-focused ~ .player-tv-focus-frame { border-color: rgba(255,255,255,0.16); }

/* v1.0.566 — the info block gets its own card frame, like the other structures: 2 archive rows tall
   (positioned by positionListModePanel), rounded corners, same left edge/width as the TV frame and
   the gradient. The controls panel (with the poster) is lifted above it so the poster stays in front. */
#screen-player.list-mode .player-list-info {
  background: var(--card); border: 2px solid var(--border); border-radius: 12px;
  padding: 12px 16px; z-index: 1;
}
#screen-player.list-mode.browse-has-poster .player-list-info { padding-right: calc(var(--tvp-poster-real-w, 223px) + 16px); }
#screen-player.list-mode .player-controls-panel { z-index: 2; }

/* v1.0.567 — info card, per explicit request:
   - the "currently watching" line moved to row 12 (the clock bar) — hidden here;
   - poster up to 4 archive rows tall: from the card's top to the gradient's bottom;
   - clear logo a bit bigger and moved up into the freed line; description slightly larger and
     centered (symmetric); stars/facts moved up to the same top line, with the channel's own logo
     under them — that left column centered. */
#screen-player.list-mode .player-poster-img { bottom: var(--tvp-poster-bottom, 30px); }
.pli-status { display: none; }
.pli-body { flex: 1; align-items: stretch; }
.pli-main { align-items: center; justify-content: flex-start; text-align: center; }
.pli-logo { max-height: 64px; max-width: 320px; align-self: center; }
.pli-title { font-size: 1.6rem; text-align: center; align-self: stretch; }
.pli-desc { font-size: 1.25rem; text-align: center; align-self: stretch; -webkit-line-clamp: 3; }
.pli-side { align-items: center; justify-content: flex-start; }
.pli-channel-logo { display: none; max-height: 44px; max-width: 150px; width: auto; height: auto; margin-top: 4px; }
.player-list-info.has-channel-logo .pli-channel-logo { display: block; }
.player-list-info.has-channel-logo .pli-side { display: flex; }

/* v1.0.568 — per explicit request: the stars + movie facts ("מותחן · 1:55 · 2022") + the channel's
   logo sit in their own small rounded frame INSIDE the info card, on its left side, as tall as the
   card allows (≈ 2 rows) and just as wide as its content; everything in it centered. */
.pli-side {
  align-self: stretch; justify-content: center; align-items: center; gap: 10px;
  padding: 10px 18px; box-sizing: border-box; min-width: 150px;
  border: 2px solid var(--border); border-radius: 12px; background: rgba(255,255,255,0.05);
}
.pli-body { gap: 16px; }
.pli-channel-logo { margin-top: 0; max-height: 40px; }

/* v1.0.568 — per explicit request: the TV frame gets the same thin 2px outline the archive rows and
   the structure-5 number tiles have (border: 2px solid var(--border)), so its edge is visible.
   Drawn by a pseudo-element over the frame's own thick border (60px sides / 4px top+bottom), so the
   frame's size/position is unchanged. */
#screen-player.list-mode .player-tv-focus-frame::before {
  content: ''; position: absolute; top: -4px; bottom: -4px; left: -60px; right: -60px;
  border: 2px solid var(--border); border-radius: 12px; pointer-events: none;
}

/* v1.0.569 — info card: description may use one more line (the card grew 10px); in the stars box
   the stars sit a bit higher (top of the box) and the channel logo a bit lower (bottom of the box)
   and a little bigger. */
.pli-desc { -webkit-line-clamp: 4; line-height: 1.3; }
#screen-player.list-mode .player-list-info { padding-top: 10px; padding-bottom: 10px; }
.pli-main { gap: 4px; }
.pli-logo { max-height: 56px; }
.pli-side { justify-content: space-between; padding: 14px 18px; }
.pli-rating { margin-top: 0; }
.pli-facts { margin-top: -4px; }
.pli-channel-logo { max-height: 54px; max-width: 180px; }

/* v1.0.570 — stars box: the facts line sits right under the stars, and the channel logo is pushed
   to the bottom of the box and made clearly bigger. */
.pli-side { justify-content: flex-start; gap: 2px; }
.pli-facts { margin-top: 0; }
.pli-channel-logo { margin-top: auto; max-height: 84px; max-width: 220px; }
/* v1.0.570 — availability line above the poster (right side of the info card): yellow star, dash,
   "זמין לצפייה עד ..." — right edge aligned with the poster's right edge. */
.pli-available { display: none; }
#screen-player.list-mode .player-list-info.has-available .pli-available {
  display: block; position: absolute; top: 12px; right: calc(var(--tvp-poster-inset, 33px) - 2px);
  font-size: 1.05rem; color: #fff; white-space: nowrap; direction: rtl;
}
.pli-available-star { color: #f5c518; }
/* v1.0.570 — fixed display height so even small source logos come out clearly bigger */
.pli-channel-logo { height: 76px; width: auto; max-width: 210px; object-fit: contain; }

/* v1.0.571 — clear logo a few px lower, at least 10px between it and the description (which moves
   down with it), and the "זמין לצפייה עד" line on the right vertically centered on the logo's line.
   The logo gets a fixed 56px-tall slot (aspect kept) so that line can be aligned to it exactly. */
.pli-main { padding-top: 4px; gap: 10px; }
.pli-logo { height: 56px; width: auto; max-width: 320px; object-fit: contain; }
.pli-title { height: 56px; line-height: 56px; }
.pli-desc { line-height: 1.22; }
#screen-player.list-mode .player-list-info.has-available .pli-available { top: 30px; line-height: 24px; }

/* v1.0.573 — poster moved right: same gap from the info card's right edge as the stars box has from
   its left edge (see positionListModePanel); the yellow star in the availability line 1.5x bigger. */
#screen-player.list-mode .player-poster-img { right: var(--tvp-poster-right, 40px); }
/* (v1.0.575 — the 1.5x star size from v1.0.573 reverted per explicit request; star stays right-aligned with the poster) */
/* v1.0.573 — availability text in the same color as the description; description a little lower
   (card's bottom padding trimmed so its 4 lines still fit). */
#screen-player.list-mode .player-list-info.has-available .pli-available { color: var(--muted); }
.pli-desc { margin-top: 6px; }
#screen-player.list-mode .player-list-info { padding-bottom: 4px; }

/* ==========================================================================
   v1.0.576 — structure 1 (archive/live rows)
   ========================================================================== */
/* watched ("bookmarked") rows: darker gray instead of the hard-to-see green; a focused one still
   shows the normal focus gray */
.epg-row.bookmarked { background: rgba(255,255,255,0.07); }
.epg-row.bookmarked.focused { background: rgba(255,255,255,0.16); }

/* the info card a focused row opens into: 4 rows tall (4 x 86.5 + 3 x 10 = 376px) */
.player-list-rows .epg-row.expanded {
  display: block; position: relative; height: 376px; padding: 0; overflow: hidden; box-sizing: border-box;
}
.player-list-rows .epg-row.expanded > .title,
.player-list-rows .epg-row.expanded > .epg-row-meta { display: none; }
.rc { position: absolute; inset: 0; }
.rc-info {
  position: absolute; top: 0; left: 0; right: 0; height: 183px; box-sizing: border-box;
  display: flex; flex-direction: row; gap: 16px; padding: 12px 16px;
}
.rc.has-poster .rc-info { padding-right: 224px; }
.rc-side {
  flex: 0 0 auto; display: flex; flex-direction: column; align-items: center; justify-content: flex-start; gap: 2px;
  min-width: 150px; padding: 14px 18px; box-sizing: border-box; direction: ltr;
  border: 2px solid var(--border); border-radius: 12px; background: rgba(255,255,255,0.05);
}
.rc-channel-logo { margin-top: auto; height: 64px; width: auto; max-width: 190px; object-fit: contain; }
.rc-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 10px; padding-top: 4px; }
.rc-logo { display: none; height: 56px; width: auto; max-width: 300px; object-fit: contain; }
.rc.has-logo .rc-logo { display: block; }
.rc.has-logo .rc-title { display: none; }
.rc-title { height: 56px; line-height: 56px; font-size: 1.6rem; font-weight: 600; color: #fff; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; align-self: stretch; text-align: center; }
.rc-desc { font-size: 1.2rem; color: var(--muted); line-height: 1.22; text-align: center; align-self: stretch; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; font-weight: 400; }
.rc-avail { position: absolute; top: 30px; right: 18px; line-height: 24px; font-size: 1.05rem; color: var(--muted); white-space: nowrap; direction: rtl; font-weight: 400; }
.rc-grad {
  position: absolute; left: 0; right: 0; bottom: 0; height: 183px; border-radius: 12px 12px 0 0;
  background: linear-gradient(to top, #000000 0%, #000000 32%, #585a66 100%);
}
.rc-progress { position: absolute; left: 20px; right: 20px; bottom: 40px; display: flex; align-items: center; gap: 14px; direction: rtl; }
.rc.has-poster .rc-progress { right: 230px; }
.rc-time { font-size: 1rem; color: var(--muted); min-width: 52px; text-align: center; font-weight: 400; }
.rc-track { position: relative; flex: 1; height: 8px; border-radius: 4px; background: var(--border); }
.rc-fill { position: absolute; top: 0; right: 0; height: 100%; border-radius: 4px; background: var(--accent); }
.rc-poster { display: none; position: absolute; right: 18px; bottom: 25px; width: 190px; height: 279.5px; object-fit: cover; border-radius: 8px; box-shadow: 0 6px 18px rgba(0,0,0,0.6); }
.rc.has-poster .rc-poster { display: block; }

/* v1.0.579 — red star for "יהיה זמין לצפייה מתאריך" (live / upcoming) */
.pli-available-star.red { color: #e5342b; }

/* v1.0.580 — per explicit request (approved from a mockup): the player's control buttons no longer
   turn red/purple when focused. The focused button gets a white rounded square behind it and its
   icon turns black (a "negative"); unfocused buttons stay plain white. Applies wherever the controls
   panel shows (fullscreen and the archive screen). The favourite heart's red FILL (= channel is a
   favourite) is a state, not focus, so it stays. */
.player-controls-row .focused,
.player-clock-icon.focused {
  background: #fff; border-radius: 12px; color: #000 !important;
  filter: none !important; transform: none !important; box-shadow: 0 0 0 6px #fff;
}
.player-fav-heart.focused path,
.player-controls-panel.live-quick .player-fav-heart.focused path { stroke: #000 !important; }
.player-fav-heart.active.focused path { fill: #000 !important; stroke: #000 !important; }
.player-return-live-btn.focused { background: #fff; color: #000 !important; border-color: #fff !important; }

/* ==========================================================================
   v1.0.582 — controls: every control has a thin white rounded-square frame; the focused one is
   filled white with the icon in black (replaces v1.0.580's frameless version).
   ========================================================================== */
.player-controls-row > svg.show,
.player-clock-icon.show { border-radius: 12px; box-shadow: inset 0 0 0 2px rgba(255,255,255,0.9); }
.player-controls-row .focused,
.player-clock-icon.focused { box-shadow: none; background: #fff; }

/* the row card's own controls — same height/spacing as the player's controls under the TV
   (53px buttons, 18px apart, 17.5px below the gradient's top), first one on the right */
.rc-controls { position: absolute; top: 17.5px; right: 20px; display: flex; flex-direction: row; gap: 18px; direction: rtl; }
.rc.has-poster .rc-controls { right: 230px; }
.rc-btn {
  width: 53px; height: 53px; box-sizing: border-box; display: flex; align-items: center; justify-content: center;
  border-radius: 10px; box-shadow: inset 0 0 0 2px rgba(255,255,255,0.9); color: #fff;
}
.rc-btn.focused { background: #fff; color: #000; box-shadow: none; }

/* descriptions (blue star + what each control does), left side of the gradient, left-aligned */
.help-star { color: #3b82f6; margin-left: 6px; }
.rc-help { position: absolute; top: 14px; left: 20px; display: flex; flex-direction: column; gap: 2px;
  direction: rtl; text-align: left; align-items: flex-start; font-size: 1rem; color: #fff; font-weight: 400; }
.player-controls-help { display: none; }
.player-controls-help.show {
  display: grid; grid-template-columns: auto auto; column-gap: 28px; row-gap: 2px;
  position: absolute; left: 40px; bottom: 138px; direction: rtl; justify-items: start;
  font-size: 1.05rem; color: #fff; text-shadow: 0 1px 3px rgba(0,0,0,0.8); z-index: 1;
}

/* v1.0.583 — per explicit request: the row card (4 rows) no longer keeps the focused-row gray
   (rgba(255,255,255,0.16)) — it is transparent; that gray moves to the info card under the TV. */
.player-list-rows .epg-row.expanded,
.player-list-rows .epg-row.expanded.focused,
.player-list-rows .epg-row.expanded.bookmarked { background: transparent; }
/* (v1.0.585: the permanent gray on the under-TV info card from v1.0.583 is replaced by the
   focus-following rule at the end of this file) */

/* ==========================================================================
   v1.0.584 — row card made 1:1 with the info area + controls panel under the TV (measured
   side by side in the browser; every offset below is relative to the card's OUTER edge, exactly
   like the under-TV info card's): info 193px (border 2 + padding 10/4, as under the TV) + 10px
   gap + gradient 183px = 386px; stars box, channel logo (76px), clear logo, description (20px,
   4 lines), availability line, poster (right 18, bottom 25, 190x279.5), controls (53px, 17.5px
   into the gradient, right edge 226.4px in) and the progress pill (top 78.2px, 39.9px tall,
   6.7px track, 14px times) all at the same positions and sizes.
   ========================================================================== */
.player-list-rows .epg-row.expanded { height: 386px; }
.rc { inset: -2px; line-height: normal; font-size: 16px; }
.rc-info { height: 193px; padding: 12px 18px 6px 18px; }
.rc.has-poster .rc-info { padding-right: 227px; }
.rc-channel-logo { height: 76px; max-width: 210px; }
.rc-desc { font-size: 1.25rem; line-height: 1.22; -webkit-line-clamp: 4; margin-top: 6px; }
.rc-avail { top: 32px; right: 18px; }
.rc-poster { right: 18px; bottom: 25px; }
.rc.has-poster .rc-controls { right: 226.4px; }
.rc-controls { right: 33.3px; }
.rc-progress {
  top: 78.2px; bottom: auto; height: 39.9px; left: 33.3px; right: 33.3px; box-sizing: border-box;
  padding: 0 20px; gap: 13px; background: rgba(15,17,21,0.85); border-radius: 10px;
}
.rc.has-poster .rc-progress { right: 226.4px; }
.rc-track { height: 6.7px; }
.rc-time { font-size: 14px; min-width: 50px; }

/* v1.0.585 — per explicit request, the light gray works like a focus marker:
   - the under-TV info card is gray ONLY while focus is on the TV / its controls (tv, tvpanel —
     #playerVideoArea.tv-focused), otherwise its normal dark card color;
   - the opened row card (structure 1) is gray while it is open (it always holds the focus). */
#screen-player.list-mode .player-list-info { background: var(--card); }
#screen-player.list-mode .player-video-area.tv-focused ~ .player-list-info { background: rgba(255,255,255,0.16); }
.player-list-rows .epg-row.expanded,
.player-list-rows .epg-row.expanded.focused,
.player-list-rows .epg-row.expanded.bookmarked { background: rgba(255,255,255,0.16); }

/* v1.0.586 — the description is now a single line (the focused control only) */
.player-controls-help.show { display: block; }
.rc-help { top: 30px; }

/* v1.0.588 — per explicit clarification: "dimmed" = faded. While a structure-1 row card is open, the
   card is CLEAR (no gray) and the playing side on the left (info card + controls panel under the TV)
   is dimmed. With no card open (or when focus goes back to the TV) nothing is dimmed and the
   under-TV info card has its normal dark color (the v1.0.583/585 gray is gone). */
#screen-player.list-mode .player-list-info,
#screen-player.list-mode .player-video-area.tv-focused ~ .player-list-info { background: var(--card); }
.player-list-rows .epg-row.expanded,
.player-list-rows .epg-row.expanded.focused,
.player-list-rows .epg-row.expanded.bookmarked { background: transparent; }
#screen-player.list-mode .player-list-info,
#screen-player.list-mode .player-controls-panel { transition: opacity 0.2s ease; }
#screen-player.list-mode.row-card-open .player-list-info,
#screen-player.list-mode.row-card-open .player-controls-panel { opacity: 0.35; }

/* v1.0.589 — BUG FIX (reported): opening the card on row 4 pushed the whole list up (the 386px card
   ran ~6px past the list's bottom edge, so the list auto-scrolled). While a card is open the list
   gets 12.5px more room at the bottom (panel bottom padding 112.5 -> 100px) — the card now fits in
   place at row 4 and nothing above it moves. */
#screen-player.list-mode.row-card-open .player-list-panel { padding-bottom: 94px; }

/* v1.0.591 — the rows' "on the home screen" marker is a house: plain house = not on the home screen,
   house with a V = on it. No color either way (was a red bookmark). */
.epg-bookmark-icon, .epg-bookmark-icon.active { color: #fff; }
.epg-bookmark-icon .home-check { display: none; }
.epg-bookmark-icon.active .home-check { display: inline; }

/* v1.0.592 — watched ("on the home screen") rows: light-blue (תכלת) tint instead of the dark gray;
   a focused one still shows the normal focus gray. */
.epg-row.bookmarked { background: rgba(56,189,248,0.22); }
.epg-row.bookmarked.focused { background: rgba(255,255,255,0.16); }

/* v1.0.594 — control descriptions are right-aligned (star first, text running left); their right
   edge is set in JS to the stars/channel-logo box's right edge */
.rc-help, .player-controls-help.show { text-align: right; direction: rtl; align-items: flex-end; }

/* v1.0.597 — square fullscreen button in the controls under the TV (archive screen only), same size and
   frame as the other controls; focused = white fill, black square (shared .player-controls-row .focused).
   And the TV frame, when focused, takes the same light-blue as the bookmarked (home) archive rows. */
.player-fullscreen-icon { display: none !important; }
#screen-player.list-mode .player-controls-panel .player-fullscreen-icon {
  display: block !important; width: 64px; height: 64px; flex-shrink: 0; color: #fff;
  filter: drop-shadow(0 1px 4px rgba(0,0,0,0.6));
}
#screen-player.list-mode .player-video-area.tv-focused ~ .player-tv-focus-frame { border-color: rgba(56,189,248,0.22); }

/* v1.0.599 — live channel arrows (fullscreen + archive screen), same size/frame as the other controls */
.player-live-ch-arrow { display: none; width: 64px; height: 64px; flex-shrink: 0; color: #fff;
  filter: drop-shadow(0 1px 4px rgba(0,0,0,0.6)); }
.player-live-ch-arrow.show { display: block; }
/* v1.0.599 — archive screen TV frame: no light-blue any more. The frame keeps its normal colour and,
   when focused, its thin outline turns white. */
#screen-player.list-mode .player-video-area.tv-focused ~ .player-tv-focus-frame { border-color: var(--card); }
#screen-player.list-mode .player-video-area.tv-focused ~ .player-tv-focus-frame::before { border-color: #fff; }

/* v1.0.599 — "on the home screen" house: red once the programme can be watched (archive), gray while
   it's still live/upcoming. Same in the rows and on the card's button. */
.epg-bookmark-icon.active { color: var(--danger); }
.epg-bookmark-icon.active.pending { color: #8b9099; }
.rc-btn.tone-ready { color: var(--danger); }
.rc-btn.tone-pending { color: #8b9099; }
.rc-btn.tone-ready.focused { color: var(--danger); }
.rc-btn.tone-pending.focused { color: #6b7079; }

/* v1.0.600 — fullscreen channel-number badge: a square on the right, solid light-blue with a white
   frame and white digits; shown ~2s on a channel switch and while typing a number (setting). */
.channel-number-badge { display: none; }
#screen-player.active ~ .channel-number-badge.show {
  display: flex; align-items: center; justify-content: center; box-sizing: border-box;
  position: fixed; right: 60px; top: 50%; transform: translateY(-50%);
  min-width: 200px; height: 200px; padding: 0 24px;
  background: #38bdf8; border: 5px solid #fff; border-radius: 22px;
  color: #fff; font-size: 110px; font-weight: 800; line-height: 1; letter-spacing: 2px;
  text-shadow: 0 2px 6px rgba(0,0,0,0.35); box-shadow: 0 8px 30px rgba(0,0,0,0.45);
  z-index: 31; direction: ltr;
}

/* v1.0.601 — row card: the availability star line moves from the top-right corner to the bottom-right,
   under the controls (right edge on the controls' right edge, same 32px from the edge as it had at
   the top); opposite it, bottom-left: today's date + time | passed/length of the programme. */
.rc-avail { top: auto; bottom: 32px; right: 33.3px; }
.rc.has-poster .rc-avail { right: 226.4px; }
.rc-foot {
  position: absolute; bottom: 32px; left: 33.3px; display: flex; align-items: center; gap: 12px;
  line-height: 24px; font-size: 1.05rem; color: var(--muted); white-space: nowrap; direction: rtl; font-weight: 400;
}
.rc-foot-sep { display: inline-block; width: 2px; height: 18px; background: var(--muted); opacity: 0.6; }
/* v1.0.601 — stars box with no stars/year/genre: the channel logo moves up to the exact middle of
   the box and is a little bigger (row card and the info card under the TV). */
.rc:not(.has-facts) .rc-channel-logo { margin: auto 0; height: 92px; max-width: 230px; }
#screen-player.list-mode .player-list-info:not(.has-facts) .pli-channel-logo { margin: auto 0; height: 92px; max-width: 230px; }
.rc-avail, .rc-foot { z-index: 2; } /* v1.0.601 — above the card's gradient */

/* v1.0.602 — descriptions justified to both sides (row card + info card under the TV) */
.rc-desc, .pli-desc { text-align: justify; }
/* v1.0.603 — (the v1.0.602 bottom-corner star for the info card was reverted: back at the top-right) */
/* v1.0.602 — the dimming of the left side while a row card is open is two steps lighter */
#screen-player.list-mode.row-card-open .player-list-info,
#screen-player.list-mode.row-card-open .player-controls-panel { opacity: 0.6; }

/* v1.0.603 — focus on the TV (archive screen): one white frame around the TV, the info card and the
   controls down to the bottom of the gradient (positioned by positionTvBlockFrame); the TV's own thin
   outline stays its normal colour. */
.player-tv-block-frame { display: none; }
#screen-player.list-mode.tv-block-focused .player-tv-block-frame {
  display: block; position: absolute; box-sizing: border-box; border: 2px solid #fff; border-radius: 12px;
  pointer-events: none; z-index: 50;
}
#screen-player.list-mode .player-video-area.tv-focused ~ .player-tv-focus-frame::before { border-color: var(--border); }

/* v1.0.604 — the progress bar sits lower, away from the buttons:
   - controls under the TV: ~30px lower on screen (36px in the panel's own scaled units), the
     programme label under it moves down with it;
   - the row card: the pill's top edge on the middle of the gradient; the bottom line (date | passed,
     availability star) moves down a little to keep clear of it. */
#screen-player.list-mode .player-controls-panel .player-progress { bottom: 42px; }
#screen-player.list-mode .player-controls-panel .player-progress-label { bottom: 14px; }
.rc-progress { top: 91.5px; }
.rc-avail, .rc-foot { bottom: 22px; }
/* v1.0.604 — archive screen: the focused seek bar is red with a red dot */
#screen-player.list-mode .player-progress.bar-focused .player-progress-fill::after { background: var(--danger); }

/* v1.0.606 — archive: the X "don't add to the home screen" button (same size/frame as the others);
   red once pressed (this programme won't be added) */
.player-no-home-icon { display: none; width: 64px; height: 64px; flex-shrink: 0; color: #fff; filter: drop-shadow(0 1px 4px rgba(0,0,0,0.6)); }
.player-no-home-icon.show { display: block; }
.player-no-home-icon.active { color: var(--danger); }
.player-controls-row .player-no-home-icon.active.focused { color: var(--danger) !important; }

/* v1.0.607/608 — "הופעת הקוביות במסך הבית" = 1 (settings > מסך הבית,
   Storage.getHomeRowsCount, body class set by applyHomeRowsVisual in
   app.js). One row on screen instead of two, same 6 tiles per row.
   v1.0.608 — REDONE per feedback on 607 (everything x2 was far too big,
   texts cut with "...", enlarged group title ate the tile's height):
   - group title above the row: UNCHANGED (same as 2-row mode);
   - tile HEIGHT set by positionRowsBlock() via --home1-tile-h, so the one
     row fills exactly the space the 2 rows used to take;
   - number + heart (top corners), name + programme + progress bar
     (bottom): exact same sizes/positions as 2-row mode;
   - NO poster: the channel logo is the enlarged "poster" — it fills the
     whole middle area between the top corner icons and the texts;
   - movies-row tile WITH a TMDB poster (.has-poster, see applyTilePosters
     in app.js): regular-size logo at the top (centered between number and
     heart), poster fills the middle, texts at the bottom.
   Layout is a flex column where only the middle item stretches — so the
   logo/poster can never overlap the texts or the corner icons.
   NOT touched: border, radius/clip-path, skirt (::before), progress bar
   (::after), side/bottom padding (the focused tile's 2px trim still works).
   Every rule is gated on body.home-rows-1, except .tile-poster being
   hidden in 2-row mode. */
.tile-poster { display: none; }
body.home-rows-1 .row-channel {
  height: var(--home1-tile-h, auto);
  display: flex;
  flex-direction: column;
  /* clears the number (right) / heart or dot (left) in the top corners
     (both sit at top 6-10px, ~24px tall) so the enlarged logo starts
     below them */
  padding-top: 38px;
}
/* keeps the focused tile's existing "2px shorter" trim (v1.0.557) */
body.home-rows-1 .row-channel.focused {
  height: calc(var(--home1-tile-h, 0px) - 2px);
}
/* no poster: logo fills the middle (object-fit: contain keeps its
   proportions, centered). v1.0.610 — 100% -> 75% width, centered, per
   feedback ("a bit too big"). */
body.home-rows-1 .row-channel > img,
body.home-rows-1 .row-channel > div:not([class]) {
  flex: 1 1 0;
  min-height: 0;
  align-self: center;
  width: 75% !important;
  height: auto !important;
  margin: 0 0 10px !important;
  object-fit: contain;
}
body.home-rows-1 .row-channel .name,
body.home-rows-1 .row-channel .now {
  flex: 0 0 auto;
}
/* poster tiles — v1.0.611: NO channel logo; the poster FILLS THE WHOLE
   TILE (inside the border, clipped by the tile's own rounded or square
   corners — clip-path on .row-channel), cropped to fill (cover), and the
   number/heart, name/programme, skirt and progress bar all sit ON TOP of
   it. (v1.0.610 had it as a flex item that touched the top border line.)
   Texts get a dark shadow so they stay readable over the picture. */
body.home-rows-1 .row-channel.has-poster {
  justify-content: flex-end;
}
body.home-rows-1 .row-channel.has-poster > img,
body.home-rows-1 .row-channel.has-poster > div:not([class]) {
  display: none !important;
}
body.home-rows-1 .row-channel.has-poster .tile-poster {
  display: block;
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 0;
}
body.home-rows-1 .row-channel.has-poster .name,
body.home-rows-1 .row-channel.has-poster .now {
  position: relative;
  z-index: 1;
  text-shadow: 0 1px 3px #000, 0 0 6px #000;
}
body.home-rows-1 .row-channel.has-poster .now {
  color: #ddd;
}
body.home-rows-1 .row-channel.has-poster::before,
body.home-rows-1 .row-channel.has-poster.has-progress::after {
  z-index: 2;
}
.row-channel .tile-poster img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  margin: 0;
  border-radius: 0;
  background: transparent;
}

/* v1.0.610 — NEW settings checkboxes (TILE_LOOK_FLAGS in app.js) */
/* "ביטול גרדיאנט בקוביות" — plain black tile background, both modes */
body.tiles-no-gradient .row-channel {
  background: #000;
}
/* "ביטול פינות מעוגלות" — square tiles; -1 = 1-row mode only, -2 = 2-row
   mode only. Also squares the skirt (::before) and progress bar (::after)
   bottoms so nothing sticks out rounded. */
body.home-rows-1.tiles-square-1 .row-channel,
body:not(.home-rows-1).tiles-square-2 .row-channel {
  border-radius: 0;
  clip-path: inset(0);
}
body.home-rows-1.tiles-square-1 .row-channel::before,
body:not(.home-rows-1).tiles-square-2 .row-channel::before,
body.home-rows-1.tiles-square-1 .row-channel.has-progress::after,
body:not(.home-rows-1).tiles-square-2 .row-channel.has-progress::after {
  border-radius: 0;
}

/* "הכפלת גודל פס ההתקדמות" — progress bar (::after 7->14px) and the gray
   skirt behind it (::before 9->18px) twice as tall. Only active where the
   corners are square in the CURRENT mode (tiles-square-1 in 1-row mode,
   tiles-square-2 in 2-row mode) — a straight bar, no rounded-edge issue.
   The focused tile still hides both (v1.0.558/559, untouched). */
body.tiles-bar-x2.home-rows-1.tiles-square-1 .row-channel::before,
body.tiles-bar-x2:not(.home-rows-1).tiles-square-2 .row-channel::before {
  height: 18px;
}
body.tiles-bar-x2.home-rows-1.tiles-square-1 .row-channel.has-progress::after,
body.tiles-bar-x2:not(.home-rows-1).tiles-square-2 .row-channel.has-progress::after {
  height: 14px;
}

/* v1.0.611 — with the doubled bar (skirt 9->18px), the name + programme
   are raised by the same 9px so they don't sit on the taller bar.
   1-row mode: bottom padding 23->32 (focused 21->30); tile height is fixed
   there, so the texts simply move up.
   2-row mode: same +9 at the bottom, taken back from the spacing ABOVE
   the texts (top padding 8->4, logo gap 5->2, name/programme gap 6->4 =
   9px) so the tile keeps exactly the same height and the 2-row layout
   doesn't move. */
body.tiles-bar-x2.home-rows-1.tiles-square-1 .row-channel,
body.tiles-bar-x2:not(.home-rows-1).tiles-square-2 .row-channel {
  padding-bottom: 32px;
}
body.tiles-bar-x2.home-rows-1.tiles-square-1 .row-channel.focused,
body.tiles-bar-x2:not(.home-rows-1).tiles-square-2 .row-channel.focused {
  padding-bottom: 30px;
}
body.tiles-bar-x2:not(.home-rows-1).tiles-square-2 .row-channel {
  padding-top: 4px;
}
body.tiles-bar-x2:not(.home-rows-1).tiles-square-2 .row-channel img {
  margin-bottom: 2px;
}
body.tiles-bar-x2:not(.home-rows-1).tiles-square-2 .row-channel > div:not([class]) {
  margin-bottom: 2px !important;
}
body.tiles-bar-x2:not(.home-rows-1).tiles-square-2 .row-channel .now {
  margin-top: 4px;
}

/* v1.0.612 — halve the gap between tiles (settings checkboxes), per mode:
   20px -> 10px. Tile WIDTH deliberately unchanged (per request) — the
   space saved just lets a bit more of the next tile peek in at the edge. */
body.home-rows-1.tiles-gap-half-1 .row-channels,
body:not(.home-rows-1).tiles-gap-half-2 .row-channels {
  gap: 10px;
}

/* v1.0.612 — home-screen MODE button (live / archive) at the top of the
   rail (replaces the old static TV logo; see renderRailModeBox in app.js).
   Closed: looks like the old logo. Open (Up from the top rail item): the
   current mode grows into a bigger square with its label, the other mode
   drops down right under it; the focused one gets a blue frame. Both float
   over the rail items below (absolute), so nothing else moves. */
.icon-rail-logo { position: relative; }
.rail-mode-box {
  position: relative;
  width: 68px;
  height: 68px;
}
.rail-mode-btn {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 68px;
  height: 68px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  border: 3px solid transparent;
  border-radius: 16px;
  box-sizing: border-box;
}
.rail-mode-btn svg { width: 34px; height: 34px; }
.rail-mode-label {
  display: none;
  margin-top: 6px;
  font-size: 1rem;
  font-weight: 700;
  white-space: nowrap;
}
.rail-mode-other { display: none; }
.rail-mode-box.open .rail-mode-btn {
  width: 116px;
  height: 116px;
  background: var(--card2);
  border-color: var(--border);
  color: var(--text);
  z-index: 50;
}
.rail-mode-box.open .rail-mode-btn svg { width: 50px; height: 50px; }
.rail-mode-box.open .rail-mode-label { display: block; }
.rail-mode-box.open .rail-mode-other {
  display: flex;
  top: 128px;
}
.rail-mode-box.open .rail-mode-btn.focused {
  border-color: var(--ff);
  box-shadow: 0 0 0 4px var(--ffg);
}

/* v1.0.612 — archive mode: row title = archive icon + channel name */
.row-title.row-title-archive {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 12px;
}
.row-title.row-title-archive svg {
  width: 2.1rem;
  height: 2.1rem;
  flex: 0 0 auto;
  color: var(--text);
}
.archive-empty-msg {
  padding: 40px 10px;
  font-size: 1.6rem;
  color: var(--muted);
  text-align: right;
}

/* v1.0.613 — home-screen search dialog + on-screen keyboard. The keyboard
   rows are drawn left-to-right like a real keyboard (direction: ltr), so
   the remote's left/right move the way the keys look. */
.search-dialog {
  position: fixed;
  top: 0; right: 0; bottom: 0; left: 0;
  display: none;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.75);
  z-index: 60;
}
.search-dialog.show { display: flex; }
.search-dialog-box {
  background: var(--card);
  border: 2px solid var(--border);
  border-radius: 20px;
  padding: 30px 36px;
  width: 1180px;
}
.search-dialog-title {
  margin: 0 0 18px;
  font-size: 1.6rem;
  font-weight: 700;
  text-align: right;
}
.search-input {
  min-height: 72px;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  padding: 0 22px;
  margin-bottom: 22px;
  font-size: 2rem;
  background: var(--card2);
  border: 2px solid var(--ff);
  border-radius: 12px;
  direction: rtl;
  unicode-bidi: plaintext;
}
.search-input-placeholder { color: var(--muted); font-size: 1.5rem; }
.search-input-caret {
  display: inline-block;
  width: 3px;
  height: 2.2rem;
  margin: 0 4px;
  background: #4A78C4;
  animation: search-caret 1s steps(1) infinite;
}
@keyframes search-caret { 50% { opacity: 0; } }
.search-keyboard { direction: ltr; display: flex; flex-direction: column; gap: 10px; }
.search-kb-row { display: flex; justify-content: center; gap: 10px; }
.search-kb-key {
  min-width: 78px;
  height: 66px;
  padding: 0 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.7rem;
  font-weight: 600;
  background: var(--card2);
  border: 3px solid transparent;
  border-radius: 12px;
  box-sizing: border-box;
}
.search-kb-controls .search-kb-key { font-size: 1.3rem; min-width: 150px; }
.search-kb-key.search-kb-go { background: #2c4a80; }
.search-kb-key.focused {
  border-color: var(--ff);
  background: #4A78C4;
  color: #fff;
}

/* v1.0.614 — loading dialog (archive switch / search) */
.home-loading {
  position: fixed;
  top: 0; right: 0; bottom: 0; left: 0;
  display: none;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.55);
  z-index: 70;
}
.home-loading.show { display: flex; }
.home-loading-box {
  display: flex;
  align-items: center;
  gap: 24px;
  padding: 34px 50px;
  background: var(--card);
  border: 2px solid var(--ff);
  border-radius: 20px;
}
.home-loading-spinner {
  width: 54px;
  height: 54px;
  border: 6px solid var(--border);
  border-top-color: #4A78C4;
  border-radius: 50%;
  animation: home-loading-spin 0.9s linear infinite;
}
@keyframes home-loading-spin { to { transform: rotate(360deg); } }
.home-loading-text { font-size: 2rem; font-weight: 700; }

/* v1.0.614 — rail menu moved down, so the opened live/archive mode squares
   (which drop down from the TV button, see .rail-mode-box.open) no longer
   cover the first rail items. The menu starts right below where the lower
   square ends instead of being vertically centered. */
.icon-rail-menu {
  margin-top: 230px;
  margin-bottom: auto;
}

/* v1.0.617 — bottom-left toast for background archive group loading */
.archive-toast {
  position: fixed;
  left: 40px;
  bottom: 40px;
  padding: 16px 28px;
  font-size: 1.4rem;
  font-weight: 700;
  color: #fff;
  background: rgba(20, 24, 34, 0.92);
  border: 2px solid var(--ff);
  border-radius: 14px;
  opacity: 0;
  transition: opacity 0.3s ease;
  pointer-events: none;
  z-index: 65;
}
.archive-toast.show { opacity: 1; }

/* v1.0.619 — YouTube trailer box over the hero video area (16:9, same
   top-left spot/height as the hero video box, see HERO_BOX in app.js) */
.hero-trailer {
  position: fixed;
  top: 0;
  left: 0;
  width: 967px;
  height: 544px;
  display: none;
  background: #000;
  z-index: 3;
  pointer-events: none;
}
.hero-trailer.show { display: block; }
.hero-trailer iframe { border: 0; width: 100%; height: 100%; display: block; }

/* v1.0.621 — "loading from the server" message centered over the hero
   video area (same box as the trailer) while an archive preview loads */
.hero-preview-loading {
  position: fixed;
  top: 0;
  left: 0;
  width: 967px;
  height: 544px;
  display: none;
  align-items: center;
  justify-content: center;
  z-index: 4;
  pointer-events: none;
}
.hero-preview-loading.show { display: flex; }
.hero-preview-loading-box {
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 22px 30px;
  background: rgba(15, 17, 21, 0.88);
  border: 2px solid var(--ff);
  border-radius: 16px;
}
.hero-preview-loading-box .home-loading-spinner { width: 40px; height: 40px; border-width: 5px; }
.hero-preview-loading-text { font-size: 1.4rem; font-weight: 700; color: #fff; }

/* v1.0.622 — progress bar x3 (square corners only, per mode like x2):
   skirt 9->27px, bar 7->21px, and the programme title (.tile-prog-title)
   moves ONTO the bar as a one-line label. Remaining text moves up by 18px. */
body.tiles-bar-x3.home-rows-1.tiles-square-1 .row-channel::before,
body.tiles-bar-x3:not(.home-rows-1).tiles-square-2 .row-channel::before {
  height: 27px;
}
body.tiles-bar-x3.home-rows-1.tiles-square-1 .row-channel.has-progress::after,
body.tiles-bar-x3:not(.home-rows-1).tiles-square-2 .row-channel.has-progress::after {
  height: 21px;
}
body.tiles-bar-x3.home-rows-1.tiles-square-1 .row-channel,
body.tiles-bar-x3:not(.home-rows-1).tiles-square-2 .row-channel {
  padding-bottom: 32px;
}
body.tiles-bar-x3.home-rows-1.tiles-square-1 .row-channel.focused,
body.tiles-bar-x3:not(.home-rows-1).tiles-square-2 .row-channel.focused {
  padding-bottom: 30px;
}
body.tiles-bar-x3.home-rows-1.tiles-square-1 .row-channel .tile-prog-title,
body.tiles-bar-x3:not(.home-rows-1).tiles-square-2 .row-channel .tile-prog-title {
  position: absolute;
  left: 8px;
  right: 8px;
  bottom: 1px;
  height: 25px;
  line-height: 25px;
  margin: 0;
  padding: 0 4px;
  font-size: 0.95rem;
  font-weight: 700;
  color: #fff;
  text-align: center;
  text-shadow: 0 1px 2px #000, 0 0 4px #000;
  z-index: 3;
}
/* the focused tile hides its bar+skirt (v1.0.558/559) — keep the title readable there */
body.tiles-bar-x3.home-rows-1.tiles-square-1 .row-channel.focused .tile-prog-title,
body.tiles-bar-x3:not(.home-rows-1).tiles-square-2 .row-channel.focused .tile-prog-title {
  background: rgba(74, 120, 196, 0.85);
}
/* 2-row mode: take the extra height back from above the texts (same idea as x2) */
body.tiles-bar-x3:not(.home-rows-1).tiles-square-2 .row-channel { padding-top: 4px; }
body.tiles-bar-x3:not(.home-rows-1).tiles-square-2 .row-channel img { margin-bottom: 2px; }

/* v1.0.622 — "שודר …" at the end of the hero movie-meta row */
.hero-movie-aired:not(:empty) {
  margin-inline-start: 14px;
  color: #ffd166;
  font-weight: 700;
}

/* v1.0.623 — with the bar at x2 / x3 the FOCUSED tile shows its progress
   bar + skirt too (they're hidden on the focused tile in the normal size,
   v1.0.558/559), in the focus blue. */
body.tiles-bar-x2.home-rows-1.tiles-square-1 .row-channel.focused::before,
body.tiles-bar-x2:not(.home-rows-1).tiles-square-2 .row-channel.focused::before,
body.tiles-bar-x3.home-rows-1.tiles-square-1 .row-channel.focused::before,
body.tiles-bar-x3:not(.home-rows-1).tiles-square-2 .row-channel.focused::before {
  display: block;
}
body.tiles-bar-x2.home-rows-1.tiles-square-1 .row-channel.focused.has-progress::after,
body.tiles-bar-x2:not(.home-rows-1).tiles-square-2 .row-channel.focused.has-progress::after,
body.tiles-bar-x3.home-rows-1.tiles-square-1 .row-channel.focused.has-progress::after,
body.tiles-bar-x3:not(.home-rows-1).tiles-square-2 .row-channel.focused.has-progress::after {
  display: block;
  background: linear-gradient(to left, #4A78C4 0, #4A78C4 var(--w, 0%), #767B89 var(--w, 0%), #767B89 100%);
}
/* x3: the title on the bar no longer needs its own blue box on the focused tile */
body.tiles-bar-x3.home-rows-1.tiles-square-1 .row-channel.focused .tile-prog-title,
body.tiles-bar-x3:not(.home-rows-1).tiles-square-2 .row-channel.focused .tile-prog-title {
  background: transparent;
}

/* v1.0.625 — title on the x3 bar: forced pure white + a solid black outline
   all around (not just a soft shadow) so it reads on both bar colors. */
body.tiles-bar-x3.home-rows-1.tiles-square-1 .row-channel .tile-prog-title,
body.tiles-bar-x3:not(.home-rows-1).tiles-square-2 .row-channel .tile-prog-title {
  color: #fff !important;
  font-weight: 800;
  text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000, 0 0 5px #000;
}

/* v1.0.627 — trailer box now lives right after #video (body level) with NO
   z-index: same stacking spot as the video, under .hero-media and its #32
   corner blend. Size/position = the hero video's 16:9 area. */
.hero-trailer { z-index: auto; }

/* v1.0.628 — per explicit request: the FOCUSED tile never shows a progress
   bar / skirt, in any mode or bar size (undoes v1.0.623's x2/x3 exception),
   and never the programme title on the bar (it's in the hero and on the
   poster) nor an archive tile's day/time line ("אתמול 21:00" — the hero
   already says when it aired). */
body .row-channel.focused::before,
body .row-channel.focused.has-progress::after {
  display: none !important;
}
body.tiles-bar-x3 .row-channel.focused .tile-prog-title,
.row-channel.archive-tile.focused .now {
  display: none !important;
}
/* x3: the text left above the bar sits 3px lower (32 -> 29, focused 30 -> 27) */
body.tiles-bar-x3.home-rows-1.tiles-square-1 .row-channel,
body.tiles-bar-x3:not(.home-rows-1).tiles-square-2 .row-channel {
  padding-bottom: 29px;
}
body.tiles-bar-x3.home-rows-1.tiles-square-1 .row-channel.focused,
body.tiles-bar-x3:not(.home-rows-1).tiles-square-2 .row-channel.focused {
  padding-bottom: 27px;
}

/* v1.0.629 — per explicit report ("the 7th tile is cut ~40px before the end
   of the screen"): the rows' scroll box ended at #contentRows' 32px left
   padding, so the tile peeking in on the left was clipped short of the
   screen edge. The row now reaches the real left edge (-32px), and the
   tile width formula adds those 32px back (100px -> 132px) so every tile
   keeps EXACTLY the same width and position as before. */
.row-channels {
  margin-left: -32px;
}
.row-channel {
  flex: 0 0 calc((100% - 132px) / 6);
}

/* v1.0.630 — "clean" tiles: posters only.
   cw-clean: continue-watching tiles lose station name, programme name and
   the progress bar/skirt. movies-clean: same for the movies-row tiles, the
   focused station's name goes next to the row title (.row-title-station). */
/* (v1.0.630/631 clean rules — replaced by v1.0.635 below) */
.row-title .row-title-station {
  display: inline-block;
  margin-right: 18px;
  padding-right: 18px;
  border-right: 3px solid var(--muted);
  font-size: 1.7rem;
  font-weight: 700;
  color: #fff;
  vertical-align: middle;
}
.row-title.row-title-logo {
  display: flex;
  align-items: center;
  gap: 0;
}

/* v1.0.631 — per report: hiding the texts with display:none made the clean
   tiles SHORTER (tile height follows its content), so the row shrank and
   sank to the bottom. They're now only made invisible (visibility:hidden):
   the tile keeps exactly its original height/width, just without the text. */
/* (v1.0.630/631 clean rules — replaced by v1.0.635 below) */

/* v1.0.632 — archive row title: our clear-logo image of the channel name,
   same height as the live group-title logos (.row-title-logo img) */
.row-title.row-title-archive img.row-title-archive-logo {
  display: block;
  height: 2.6rem;
  width: auto;
}

/* v1.0.633 — archive tiles' invisible placeholder line (see renderContentRows):
   always takes its space, never shown — also on the focused tile. */
.row-channel.archive-tile .now.tile-spacer,
.row-channel.archive-tile.focused .now.tile-spacer {
  display: block !important;
  visibility: hidden !important;
}

/* v1.0.635 — BUG FIX (reported: in 2-row mode the continue-watching tiles
   showed only the channel logo, no names, no bar): "clean" means "poster
   only", so it now applies ONLY to a tile that actually shows a poster —
   1-row mode + .has-poster. Without a poster the tile keeps its normal
   names and progress bar. */
body.home-rows-1.cw-clean:not(.pinfo) .row-channel.cw-tile.has-poster .name,
body.home-rows-1.cw-clean:not(.pinfo) .row-channel.cw-tile.has-poster .now,
body.home-rows-1.movies-clean:not(.pinfo) .movies-row .row-channel.has-poster .name,
body.home-rows-1.movies-clean:not(.pinfo) .movies-row .row-channel.has-poster .now {
  display: block !important;
  visibility: hidden !important;
}
body.home-rows-1.cw-clean:not(.pinfo) .row-channel.cw-tile.has-poster::before,
body.home-rows-1.cw-clean:not(.pinfo) .row-channel.cw-tile.has-poster.has-progress::after,
body.home-rows-1.movies-clean:not(.pinfo) .movies-row .row-channel.has-poster::before,
body.home-rows-1.movies-clean:not(.pinfo) .movies-row .row-channel.has-poster.has-progress::after {
  display: none !important;
}

/* v1.0.636 — per request: a real (TMDB/Fanart) clear logo sat right on top of
   the meta row (year / genres / stars / "שודר …") — 16px of space below it.
   Our auto-generated logo (.hero-now-logo-auto) is unchanged. */
.hero-now-logo.hero-now-logo-real {
  margin-bottom: 16px;
}

/* v1.0.637 — per request: STRETCH the YouTube trailer vertically (not move
   it): 40px past the top and 60px past the bottom of its box, which clips
   them off — hides YouTube's bottom strip ("more videos" etc.) and eats into
   the letterbox bars of wide-format trailers. 544px box -> 644px tall
   picture: scaleY(644/544), anchored at the top, shifted up 40px. */
.hero-trailer {
  overflow: hidden;
}
.hero-trailer iframe {
  transform-origin: top center;
  transform: translateY(-40px) scaleY(1.1838);
}

/* v1.0.641 — per request: the channel-number badge (zapping) moves UP to the
   top-right corner, 60px from the top and 60px from the right edge — so it
   covers the channel's own on-screen logo while it's up. */
#screen-player.active ~ .channel-number-badge.show {
  top: 60px; right: 60px; transform: none;
}

/* v1.0.641 — per request: fullscreen player — the blue-star control description
   sat too close to the info (progress) bar; raised 18px so its middle lines up
   with the middle of the controls row (the middle of the gap above the bar). */
#screen-player:not(.list-mode) .player-controls-help.show { bottom: 156px; }

/* v1.0.641 — fullscreen channel list buttons (right of pause), same size/frame as the other controls */
.player-chlist-btn { display: none; width: 64px; height: 64px; flex-shrink: 0; color: #fff;
  filter: drop-shadow(0 1px 4px rgba(0,0,0,0.6)); }
.player-chlist-btn.show { display: block; }
#screen-player.list-mode .player-chlist-btn { display: none !important; }

/* v1.0.641 — the list itself: a column of squares on the right, 60px from the edges.
   --fs-chlist-count (4/6/8) squares fill the height; corners round/square (settings). */
.fs-chlist { display: none; }
#screen-player.active .fs-chlist.show {
  display: block; position: absolute; top: 60px; bottom: 60px; right: 60px; width: 470px; z-index: 32;
  --fs-chlist-gap: 14px;
}
.fs-chlist-inner {
  height: 100%; overflow: hidden; display: flex; flex-direction: column; gap: var(--fs-chlist-gap);
  direction: rtl;
}
.fs-chlist-item {
  flex: 0 0 calc((100% - (var(--fs-chlist-count) - 1) * var(--fs-chlist-gap)) / var(--fs-chlist-count));
  box-sizing: border-box; display: flex; align-items: center; gap: 16px; padding: 0 18px;
  background: rgba(20, 22, 28, 0.93); border: 3px solid transparent; border-radius: 14px;
  box-shadow: 0 6px 20px rgba(0,0,0,0.45); color: #fff; overflow: hidden;
}
.fs-chlist.square .fs-chlist-item { border-radius: 0; }
.fs-chlist-item.focused { border-color: var(--ff); background: rgba(40, 46, 60, 0.95); }
.fs-chlist-item.playing .fs-chlist-num { color: #38bdf8; }
.fs-chlist-num { flex: 0 0 auto; min-width: 56px; font-size: 34px; font-weight: 800; text-align: center; direction: ltr; }
.fs-chlist-logo { flex: 0 0 96px; height: 70%; max-height: 90px; display: flex; align-items: center; justify-content: center; }
.fs-chlist-logo img { max-width: 100%; max-height: 100%; object-fit: contain; }
.fs-chlist-text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 4px; text-align: right; }
.fs-chlist-name { font-size: 22px; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.fs-chlist-title { font-size: 19px; color: rgba(255,255,255,0.75); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.fs-chlist[style*="--fs-chlist-count: 8"] .fs-chlist-title { -webkit-line-clamp: 1; white-space: nowrap; text-overflow: ellipsis; display: block; }
#screen-player.active .fs-chlist.show { position: fixed; }
/* while the list is open: no channel card / big number badge on top of it (the list shows both) */
body.fs-chlist-open #playerOsd, body.fs-chlist-open .channel-number-badge { display: none !important; }

/* v1.0.641 — one line under the fullscreen info bar: red star + programme | channel | ends at.
   Vertically in the middle of the black strip below the bar (bar bottom ~79px above the screen
   bottom -> centre at 40px); right edge under the buttons, and moves left with them when a
   poster is showing (same 328px as the bar/row). Only while a bar is showing. */
.player-now-line { display: none; }
.player-controls-panel .player-progress.show ~ .player-now-line:not(:empty) {
  display: flex; align-items: center; gap: 14px; position: absolute; right: 40px; left: 40px; bottom: 40px;
  transform: translateY(50%); direction: rtl; white-space: nowrap; overflow: hidden;
  font-size: 26px; color: #fff; text-shadow: 0 1px 3px rgba(0,0,0,0.8);
}
.player-controls-panel.has-poster .player-progress.show ~ .player-now-line { right: 328px; }
#screen-player.list-mode .player-now-line { display: none !important; }
.player-now-line .pnl-star { color: #e5342b; font-size: 38px; line-height: 1; }
.player-now-line .pnl-title { font-weight: 700; overflow: hidden; text-overflow: ellipsis; }
.player-now-line .pnl-channel { color: rgba(255,255,255,0.85); }
.player-now-line .pnl-ends { color: rgba(255,255,255,0.7); }
.player-now-line .pnl-sep { color: rgba(255,255,255,0.35); }
.player-now-line .pnl-group { color: rgba(255,255,255,0.85); }
.player-now-line .pnl-group.browsing { color: #38bdf8; font-weight: 700; }

/* v1.0.641 — per request: while the fullscreen channel list is open the live picture is squeezed
   to the left of it (full height, narrower): 60px list margin + 40px gap + the list's width
   (560/470/400px for 4/6/8 squares). Animated both ways. */
#screen-player.active .fs-chlist.show { width: var(--fs-chlist-w, 470px); }
body.fs-chlist-open #video:not(.video-hero) {
  width: calc(100vw - var(--fs-chlist-w, 470px) - 100px);
}
/* v1.0.660 — no width animation on the video any more: animating the hardware video's size every
   frame is a suspect in the reported fullscreen freezes (this TV has a history of breaking on
   animated video/size changes — see #video.video-hero's own comment). Instant squeeze. */

/* v1.0.643 — the groups list: one square per group — our group logo (or its name) + channel count */
.fs-chlist-item.fs-chlist-group { justify-content: center; }
.fs-chlist-group .fs-chlist-text { align-items: center; text-align: center; }
.fs-chlist-group .fs-chlist-name { font-size: 28px; }
.fs-chlist-grouplogo { max-width: 90%; max-height: 64px; object-fit: contain; }
.fs-chlist-group.playing .fs-chlist-title { color: #38bdf8; }

/* v1.0.644 — a small extra gap between the three list buttons and pause (a separate family) */
#playerGroupListBtn.show { margin-left: 24px; }

/* v1.0.654 — per request: the continue-watching badge (archive icon + "זמין עוד…") moved from its
   own line to the END of the hero's 2nd line (the meta row); the pill is light blue, red in the last 24h. */
#previewAvailableUntil, #previewAvailableUntil.show { display: none !important; }
.hero-movie-available:empty { display: none; }
.hero-movie-available { display: inline-flex; align-items: center; gap: 8px; }
.hero-movie-available .cw-archive-icon { width: 30px; height: 30px; flex-shrink: 0; color: #fff; }
.cw-badge-pill.green { background: #38bdf8; } /* v1.0.654 — light blue (תכלת); red stays for the last 24h */
/* v1.0.654 — empty meta-row items no longer leave their 18px gaps (the badge/"שודר" start at the edge) */
.hero-movie-meta-row > :empty { display: none; }

/* v1.0.661 — settings test switch "אנימציה בשינוי גודל הווידאו": brings back the pre-v1.0.660
   video resize animations (controls row / archive list screen / fullscreen list squeeze) */
body.video-resize-anim #video { transition: height 0.5s ease; }
body.video-resize-anim.fs-chlist-open #video:not(.video-hero),
body.video-resize-anim.fs-chlist-closing #video:not(.video-hero) { transition: width 0.35s ease, height 0.5s ease; }

/* v1.0.661 — fullscreen lists: a small title on top saying which list is open */
#screen-player.active .fs-chlist.show { display: flex; flex-direction: column; }
.fs-chlist-head {
  flex: 0 0 auto; margin-bottom: 12px; font-size: 26px; font-weight: 700; color: #fff;
  text-shadow: 0 1px 3px #000, 0 0 8px #000; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  direction: rtl;
}
.fs-chlist-head .fs-head-star { color: #e5342b; margin-left: 8px; }
#screen-player.active .fs-chlist.show .fs-chlist-inner { flex: 1 1 0; min-height: 0; height: auto; }

/* v1.0.661 — 2-row home layout: a row that has posters shows them the way the 1-row layout does
   (one tall tile = exactly two normal rows high, --home1-tile-h set by positionRowsBlock); the
   other rows stay normal. Gated on body.poster-rows-on (2 rows + posters on) + .poster-row. */
body.poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel {
  height: var(--prow-h, auto);
  display: flex;
  flex-direction: column;
  padding-top: 38px;
}
body.poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.focused {
  height: calc(var(--prow-h, 0px) - 2px);
}
body.poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel > img,
body.poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel > div:not([class]) {
  flex: 1 1 0;
  min-height: 0;
  align-self: center;
  width: 75% !important;
  height: auto !important;
  margin: 0 0 10px !important;
  object-fit: contain;
}
body.poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel .name,
body.poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel .now { flex: 0 0 auto; }
body.poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster { justify-content: flex-end; }
body.poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster > img,
body.poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster > div:not([class]) { display: none !important; }
body.poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster .tile-poster {
  display: block; position: absolute; top: 0; right: 0; bottom: 0; left: 0; z-index: 0;
}
body.poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster .name,
body.poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster .now {
  position: relative; z-index: 1; text-shadow: 0 1px 3px #000, 0 0 6px #000;
}
body.poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster .now { color: #ddd; }
body.poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster::before,
body.poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster.has-progress::after { z-index: 2; }
body.poster-rows-on.cw-clean:not(.home-rows-1):not(.pinfo) .content-row.poster-row .row-channel.cw-tile.has-poster .name,
body.poster-rows-on.cw-clean:not(.home-rows-1):not(.pinfo) .content-row.poster-row .row-channel.cw-tile.has-poster .now,
body.poster-rows-on.movies-clean:not(.home-rows-1):not(.pinfo) .movies-row.poster-row .row-channel.has-poster .name,
body.poster-rows-on.movies-clean:not(.home-rows-1):not(.pinfo) .movies-row.poster-row .row-channel.has-poster .now {
  display: block !important;
  visibility: hidden !important;
}
body.poster-rows-on.cw-clean:not(.home-rows-1):not(.pinfo) .content-row.poster-row .row-channel.cw-tile.has-poster::before,
body.poster-rows-on.cw-clean:not(.home-rows-1):not(.pinfo) .content-row.poster-row .row-channel.cw-tile.has-poster.has-progress::after,
body.poster-rows-on.movies-clean:not(.home-rows-1):not(.pinfo) .movies-row.poster-row .row-channel.has-poster::before,
body.poster-rows-on.movies-clean:not(.home-rows-1):not(.pinfo) .movies-row.poster-row .row-channel.has-poster.has-progress::after {
  display: none !important;
}

/* v1.0.662 — fullscreen options button (end of the row) + its menu + the description window */
.player-options-btn { display: none; width: 64px; height: 64px; flex-shrink: 0; color: #fff;
  filter: drop-shadow(0 1px 4px rgba(0,0,0,0.6)); }
.player-options-btn.show { display: block; }
#screen-player.list-mode .player-options-btn { display: none !important; }
.player-options-box { min-width: 760px; text-align: right; }
.po-row { display: flex; align-items: center; gap: 24px; padding: 18px 22px; border-radius: 12px;
  border: 3px solid transparent; font-size: 28px; color: #fff; }
.po-row + .po-row { margin-top: 10px; }
.po-row.focused { border-color: var(--accent, #6c8cff); background: rgba(255,255,255,0.06); }
.po-row-label { white-space: nowrap; }
.po-row .setup-autohide-options { flex-wrap: wrap; }
.player-desc-box { max-width: 1100px; text-align: right; }
.player-desc-title { font-size: 36px; font-weight: 700; margin: 0 0 8px; }
.player-desc-meta { font-size: 24px; color: #aab; margin: 0 0 18px; }
.player-desc-text { font-size: 28px; line-height: 1.5; margin: 0; max-height: 60vh; overflow: hidden; }

/* v1.0.662 — picture format (fullscreen only: not the home preview, not the archive list screen).
   Done with object-fit / padding so the video's own box never moves (see #video). */
body.aspect-auto:not(.player-list-mode) #video:not(.video-hero) { object-fit: contain; }
body.aspect-zoom:not(.player-list-mode) #video:not(.video-hero) { object-fit: cover; }
body.aspect-4x3:not(.player-list-mode) #video:not(.video-hero) {
  box-sizing: border-box; padding: 0 calc((100vw - 133.333vh) / 2); object-fit: fill;
}
body.aspect-21x9:not(.player-list-mode) #video:not(.video-hero) {
  box-sizing: border-box; padding: calc((100vh - 42.857vw) / 2) 0; object-fit: fill;
}

/* v1.0.663 — the settings gear the same size as the other rail icons (was 76px box / 42px glyph) —
   part of the tests-tab switch below (body.rail-edge) */
body.rail-edge .icon-rail-settings { width: 74px; height: 74px; }
body.rail-edge .icon-rail-settings svg { width: 35px; height: 35px; }

/* v1.0.663 — "rail to the edge" (tests tab switch, on by default: body.rail-edge): while browsing the
   tiles the rail sits 30px from the screen edge (was 56px), and the hero text + the rows move over
   by the same 26px (the layout itself is the edge one, so they really gain the room). With the rail
   focused (Back) everything slides back to the old place (body.rail-expanded) — transforms only,
   so the animation costs nothing. The TV / its frame / the fanart are not touched. */
/* v1.0.664 — tighter, per request: the rail icons ~25px from the screen edge (box 6px), and the
   hero text + rows ~25px from the icons (was 56px paddings). Back = everything slides to the old spot. */
body.rail-edge .icon-rail { width: 80px; padding-right: 6px; }
body.rail-edge .hero-info { padding-right: 28px; } /* v1.0.666 — +23px gap from the rail */
body.rail-edge .content-rows { padding-right: 28px; }
body.rail-edge .icon-rail,
body.rail-edge .hero-info,
body.rail-edge .content-rows { transition: transform 0.3s ease; }
body.rail-edge .icon-rail { transform: translateX(-12px); } /* v1.0.667 — icons centered between the rows' edge and the screen edge (~36px each side) */
body.rail-edge.rail-expanded .icon-rail { transform: translateX(var(--rail-sx, -50px)); } /* v1.0.752 — measured (railHug) */
body.rail-edge.rail-expanded .hero-info,
body.rail-edge.rail-expanded .content-rows { transform: translateX(var(--rail-cx, -78px)); }
body.rail-edge .rail-flyout { right: 92px; }
body.rail-edge.rail-expanded .rail-flyout { right: calc(180px + var(--rail-sx, -50px)); }
body.rail-edge .row-flash-indicator { left: 1865px; }
body.rail-edge.rail-expanded .row-flash-indicator { left: calc(1905px + var(--rail-cx, -78px)); }
body.rail-edge #screen-grid.active ~ .digit-overlay { left: 1865px; }
body.rail-edge.rail-expanded #screen-grid.active ~ .digit-overlay { left: calc(1905px + var(--rail-cx, -78px)); }

/* v1.0.665 — continue-watching tile dot: light blue (תכלת) instead of green, like the "זמין עוד" pill;
   red stays for the last 24h. Tests-tab switch (body.cw-dot-green = the old green). */
body:not(.cw-dot-green) .cw-dot.green { background: #38bdf8; }

/* v1.0.666 — the "more rows" arrow slides with the rows (placed for the not-slid rows in app.js) */
body.rail-edge .rows-more-indicator { transition: margin 0.3s ease; }
body.rail-edge.rail-expanded .rows-more-indicator { margin-right: calc(-1 * var(--rail-cx, -78px)); }

/* v1.0.666 — per request: with the rail at the edge the rows are 78px wider — the regular tiles
   keep EXACTLY their old width (the 78px are taken out of the 1/6 formula, see .row-channel above). */
body.rail-edge .row-channel { flex: 0 0 calc((100% - 132px - 78px) / 6); }
/* v1.0.666 — poster tiles get the poster's own 2:3 proportion (width = tall tile height x 2/3), so
   the poster shows whole — no crop, no stretch (object-fit: cover never stretches; at the old width
   it cut ~13px off the sides). Tests-tab switch: body.poster-exact-off = the old width. */
body:not(.poster-exact-off).home-rows-1 .row-channel.has-poster {
  flex: 0 0 calc((var(--home1-tile-h, 420px) - 4px) * 2 / 3 + 4px); /* v1.0.670 — exact: 2:3 INSIDE the 2px border */
}
body:not(.poster-exact-off).poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster {
  flex: 0 0 calc((var(--prow-h, 420px) - 4px) * 2 / 3 + 4px);
}

/* v1.0.668 — "1.5" rows: the rows WITHOUT posters get taller tiles (--home15-tile-h, set by
   positionRowsBlock) so the block shows one full row + half of the next. Same inside layout as the
   1-row mode (logo fills the middle, texts at the bottom). Tile WIDTH unchanged -> the tile box is
   taller than before; logos keep their proportions (object-fit: contain), nothing is stretched.
   Poster rows are untouched (they keep the full-block height, see .poster-row). */
body.home-rows-15:not(.home-rows-1) .content-row .row-channel {
  height: var(--home15-tile-h, auto);
  display: flex;
  flex-direction: column;
  padding-top: 38px;
}
body.home-rows-15:not(.home-rows-1) .content-row .row-channel.focused {
  height: calc(var(--home15-tile-h, 0px) - 2px);
}
body.home-rows-15:not(.home-rows-1) .content-row .row-channel > img,
body.home-rows-15:not(.home-rows-1) .content-row .row-channel > div:not([class]) {
  flex: 1 1 0;
  min-height: 0;
  align-self: center;
  width: 75% !important;
  height: auto !important;
  margin: 0 0 10px !important;
  object-fit: contain;
}
body.home-rows-15:not(.home-rows-1) .content-row .row-channel .name,
body.home-rows-15:not(.home-rows-1) .content-row .row-channel .now { flex: 0 0 auto; }
/* v1.0.668 — in 1.5 mode the half row reaches the bottom edge: keep the "more rows" arrow drawn on top of it */
body.home-rows-15 .rows-more-indicator { z-index: 20; }

/* v1.0.669 — row-number flash / typed channel number: vertically in the real middle of the gap between
   the last menu icon and settings (--rail-mid-y measured in app.js; 872px was the pre-magnifier spot) */
.row-flash-indicator { top: var(--rail-mid-y, 872px); }
#screen-grid.active ~ .digit-overlay { top: var(--rail-mid-y, 872px); }

/* v1.0.669 — "שורת תמונות: שתי שורות" (body.poster-small): posters inside NORMAL-height rows —
   the poster tile is exactly as tall as the other tiles (--home2-tile-h, or the 1.5 height) and 2:3
   wide, the poster fills it whole (no crop, no stretch). Narrower than the other tiles on purpose. */
body.poster-small:not(.home-rows-1) .row-channel.has-poster {
  --poster-h: var(--home2-tile-h, 169px);
  height: var(--poster-h);
  flex: 0 0 calc(var(--poster-h) * 2 / 3);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}
body.poster-small.home-rows-15:not(.home-rows-1) .row-channel.has-poster { --poster-h: var(--home15-tile-h, 252px); }
body.poster-small:not(.home-rows-1) .row-channel.has-poster.focused { height: calc(var(--poster-h) - 2px); }
body.poster-small:not(.home-rows-1) .row-channel.has-poster > img,
body.poster-small:not(.home-rows-1) .row-channel.has-poster > div:not([class]) { display: none !important; }
body.poster-small:not(.home-rows-1) .row-channel.has-poster .tile-poster {
  display: block; position: absolute; top: 0; right: 0; bottom: 0; left: 0; z-index: 0;
}
body.poster-small:not(.home-rows-1) .row-channel.has-poster .name,
body.poster-small:not(.home-rows-1) .row-channel.has-poster .now {
  position: relative; z-index: 1; text-shadow: 0 1px 3px #000, 0 0 6px #000;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}
body.poster-small:not(.home-rows-1) .row-channel.has-poster::before,
body.poster-small:not(.home-rows-1) .row-channel.has-poster.has-progress::after { z-index: 2; }

/* v1.0.669 — the info above the tiles (hero text) 60px wider, growing to the LEFT (right edge fixed).
   Text lines get longer; a clear logo that is limited by WIDTH may show a little bigger (its own
   proportions kept — never stretched). Tests-tab switch: heroWideOff. */
body.hero-wide .hero-info { width: calc(40% + 60px); }

/* ===================== v1.0.670 — POSTER TILES: info strip (body.pinfo) =====================
   Replaces the old "clean" checkboxes. A poster tile (1-row layout, or a poster row) is CLEAN by
   default: only the poster (+ number / heart on top). Settings can add, at the BOTTOM of the tile, on
   a dark strip that grows only as much as needed: the channel name (pinfo-ch), the programme name
   (pinfo-prog) and the progress bar (pinfo-bar). The tile never gets taller: the poster gets shorter
   by the strip's height and stays exactly 2:3, so the tile gets narrower to the poster's width —
   the texts never sit on the picture. */
body.pinfo { --pi-ch: 0px; --pi-pr: 0px; --pi-bar: 0px; --pi-pad: 0px; }
body.pinfo.pinfo-ch { --pi-ch: 26px; --pi-pad: 6px; }
body.pinfo.pinfo-prog { --pi-pr: 22px; --pi-pad: 6px; }
body.pinfo.pinfo-bar { --pi-bar: 10px; }
body.pinfo { --pi-s: calc(var(--pi-pad) + var(--pi-ch) + var(--pi-pr) + var(--pi-bar)); }
body.pinfo.home-rows-1 .row-channel.has-poster {
  flex: 0 0 calc((var(--home1-tile-h, 420px) - 4px - var(--pi-s)) * 2 / 3 + 4px);
}
body.pinfo.poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster {
  flex: 0 0 calc((var(--prow-h, 420px) - 4px - var(--pi-s)) * 2 / 3 + 4px);
}
body.pinfo.poster-exact-off.home-rows-1 .row-channel.has-poster,
body.pinfo.poster-exact-off.poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster {
  flex: 0 0 calc((100% - 132px - 78px) / 6);
}
body.pinfo.home-rows-1 .row-channel.has-poster .tile-poster,
body.pinfo.poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster .tile-poster {
  bottom: var(--pi-s);
}
body.pinfo.home-rows-1 .row-channel.has-poster,
body.pinfo.poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster {
  padding-bottom: var(--pi-bar);
  background: #000;
}
body.pinfo.home-rows-1 .row-channel.has-poster .name,
body.pinfo.poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster .name {
  display: none; height: 26px; line-height: 26px; margin: 0; text-shadow: none;
}
body.pinfo.home-rows-1 .row-channel.has-poster .now,
body.pinfo.poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster .now {
  display: none; height: 22px; line-height: 22px; margin: 0; text-shadow: none;
}
body.pinfo.pinfo-ch.home-rows-1 .row-channel.has-poster .name,
body.pinfo.pinfo-ch.poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster .name,
body.pinfo.pinfo-prog.home-rows-1 .row-channel.has-poster .now,
body.pinfo.pinfo-prog.poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster .now { display: block; }
body.pinfo.home-rows-1 .row-channel.has-poster::before,
body.pinfo.poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster::before { display: none !important; }
body.pinfo:not(.pinfo-bar).home-rows-1 .row-channel.has-poster.has-progress::after,
body.pinfo:not(.pinfo-bar).poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster.has-progress::after { display: none !important; }
body.pinfo.pinfo-bar.home-rows-1 .row-channel.has-poster.has-progress::after,
body.pinfo.pinfo-bar.poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster.has-progress::after { height: 7px !important; bottom: 1px !important; }
/* the programme line is also .tile-prog-title (at bar x3 it sits ON the bar) — in the strip it is a plain line */
body.pinfo.home-rows-1 .row-channel.has-poster .tile-prog-title,
body.pinfo.poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster .tile-prog-title {
  position: static !important; transform: none !important; background: none !important; padding: 0 !important;
  left: auto !important; right: auto !important; bottom: auto !important; font-size: 0.9rem !important; color: var(--muted) !important;
}

/* ===================== v1.0.673 — POSTER INFO STRIP, take 3 (per request) =====================
   NO crop and NO stretch — the poster is always shown WHOLE at its exact 2:3.
   Strip bands, tighter: progress bar 12px, programme name 22px (light blue), channel name 22px
   (beige), texts right under the picture; a small 8px black gradient joins them.
   1.5-high poster rows: the row grows DOWN by 45px when the strip is on (35 + 10 per request), so the
   poster keeps (almost) its clean size and the tile gets WIDER as fewer items are shown.
   1-row layout: no room to grow — the poster gets shorter by the strip and the tile a bit narrower.
   Tests-tab switch pinfoV672 = the v1.0.672 look (clean width, poster enlarged + cropped). */
body.pinfo:not(.pinfo-v672) { --pi-x: 22px; --pi-ch: 0px; --pi-pr: 0px; --pi-bar: 0px; --pi-extra: 0px; }
body.pinfo:not(.pinfo-v672).pinfo-ch { --pi-ch: 22px; }
body.pinfo:not(.pinfo-v672).pinfo-prog { --pi-pr: 22px; }
body.pinfo:not(.pinfo-v672).pinfo-bar { --pi-bar: 12px; }
body.pinfo:not(.pinfo-v672) { --pi-s: calc(var(--pi-ch) + var(--pi-pr) + var(--pi-bar)); }
body.pinfo:not(.pinfo-v672).pinfo-any.prow-15 { --pi-extra: 45px; }
/* 1.5 poster rows: every tile of the row grows by the extra height */
body.pinfo:not(.pinfo-v672).poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel {
  height: calc(var(--prow-h, 252px) + var(--pi-extra));
}
body.pinfo:not(.pinfo-v672).poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.focused {
  height: calc(var(--prow-h, 252px) + var(--pi-extra) - 2px);
}
body.pinfo:not(.pinfo-v672).poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster {
  flex: 0 0 calc((var(--prow-h, 252px) + var(--pi-extra) - 4px - var(--pi-s)) * 2 / 3 + 4px);
}
body.pinfo:not(.pinfo-v672).home-rows-1 .row-channel.has-poster {
  flex: 0 0 calc((var(--home1-tile-h, 420px) - 4px - var(--pi-s)) * 2 / 3 + 4px);
}
body.pinfo:not(.pinfo-v672).home-rows-1 .row-channel.has-poster .tile-poster,
body.pinfo:not(.pinfo-v672).poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster .tile-poster {
  bottom: var(--pi-s);
}
body.pinfo:not(.pinfo-v672).pinfo-any .row-channel.has-poster .tile-poster::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 8px;
  background: linear-gradient(to bottom, rgba(0,0,0,0), rgba(0,0,0,0.85)); pointer-events: none;
}
body.pinfo:not(.pinfo-v672).home-rows-1 .row-channel.has-poster,
body.pinfo:not(.pinfo-v672).poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster {
  padding-bottom: var(--pi-bar); background: #000;
}
body.pinfo:not(.pinfo-v672).home-rows-1 .row-channel.has-poster .now,
body.pinfo:not(.pinfo-v672).poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster .now {
  order: 1; height: 22px; line-height: 22px; font-size: 1rem !important; font-weight: 700;
  color: #38bdf8 !important; text-align: center;
}
body.pinfo:not(.pinfo-v672).home-rows-1 .row-channel.has-poster .name,
body.pinfo:not(.pinfo-v672).poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster .name {
  order: 2; height: 22px; line-height: 22px; font-size: 0.95rem; font-weight: 600;
  color: #d6c7a1; text-align: center;
}
body.pinfo.pinfo-bar:not(.pinfo-v672).home-rows-1 .row-channel.has-poster.has-progress::after,
body.pinfo.pinfo-bar:not(.pinfo-v672).poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster.has-progress::after {
  bottom: 3px !important;
}

/* ---- the v1.0.672 look, kept behind the tests-tab switch pinfoV672 ---- */
body.pinfo.pinfo-v672 { --pi-x: 28px; --pi-ch: 0px; --pi-pr: 0px; --pi-bar: 0px; }
body.pinfo.pinfo-v672.pinfo-ch { --pi-ch: 28px; }
body.pinfo.pinfo-v672.pinfo-prog { --pi-pr: 28px; }
body.pinfo.pinfo-v672.pinfo-bar { --pi-bar: 28px; }
body.pinfo.pinfo-v672 { --pi-s: calc(var(--pi-ch) + var(--pi-pr) + var(--pi-bar)); }
body.pinfo.pinfo-v672.home-rows-1 .row-channel.has-poster { flex: 0 0 calc((var(--home1-tile-h, 420px) - 4px) * 2 / 3 + 4px); }
body.pinfo.pinfo-v672.poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster { flex: 0 0 calc((var(--prow-h, 420px) - 4px) * 2 / 3 + 4px); }
body.pinfo.pinfo-v672 .row-channel.has-poster .tile-poster::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 18px;
  background: linear-gradient(to bottom, rgba(0,0,0,0), #000); pointer-events: none;
}
body.pinfo.pinfo-v672.home-rows-1 .row-channel.has-poster,
body.pinfo.pinfo-v672.poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster { padding-bottom: var(--pi-bar); }
body.pinfo.pinfo-v672.home-rows-1 .row-channel.has-poster .now,
body.pinfo.pinfo-v672.poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster .now {
  order: 1; height: 28px; line-height: 28px; font-size: 1rem !important; font-weight: 700; color: #38bdf8 !important; text-align: center;
}
body.pinfo.pinfo-v672.home-rows-1 .row-channel.has-poster .name,
body.pinfo.pinfo-v672.poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster .name {
  order: 2; height: 28px; line-height: 28px; font-size: 0.95rem; font-weight: 600; color: #d6c7a1; text-align: center;
}
body.pinfo.pinfo-bar.pinfo-v672.home-rows-1 .row-channel.has-poster.has-progress::after,
body.pinfo.pinfo-bar.pinfo-v672.poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster.has-progress::after { bottom: 10px !important; }

/* v1.0.673 — when the next row is shown CUT (1.5 layouts), the "more rows" arrow is not shown */
body.rows-cut .rows-more-indicator { display: none !important; }

/* v1.0.674 — poster tile + info strip with the channel name: the channel number sits after the name
   ("שם - 12", number light blue) instead of the top corner. Tests-tab switch: nameNumOff. */
.row-channel .name-num { display: none; }
body.pinfo.pinfo-ch:not(.name-num-off).home-rows-1 .row-channel.has-poster .name-num,
body.pinfo.pinfo-ch:not(.name-num-off).poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster .name-num { display: inline; }
.row-channel .name-num b { color: #38bdf8; font-weight: 700; }
body.pinfo.pinfo-ch:not(.name-num-off).home-rows-1 .row-channel.has-poster .tile-number,
body.pinfo.pinfo-ch:not(.name-num-off).poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster .tile-number { display: none; }

/* v1.0.675 — with the number after the name, the name drops a trailing "HD/FHD/UHD/4K" (fits better).
   Tests-tab switch: nameHdOff. */
.row-channel .name-short { display: none; }
body.pinfo.pinfo-ch:not(.name-num-off):not(.name-hd-off).home-rows-1 .row-channel.has-poster .name-orig,
body.pinfo.pinfo-ch:not(.name-num-off):not(.name-hd-off).poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster .name-orig { display: none; }
body.pinfo.pinfo-ch:not(.name-num-off):not(.name-hd-off).home-rows-1 .row-channel.has-poster .name-short,
body.pinfo.pinfo-ch:not(.name-num-off):not(.name-hd-off).poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel.has-poster .name-short { display: inline; }

/* v1.0.676/677 — poster tile info in the row title: "<group> - <programme> - <channel>".
   677: one straight line — the title becomes a centered flex row (the smaller texts sit on the
   title's middle, not its baseline) and every " - " has exactly the same spacing. */
.row-title.has-rti { display: flex; align-items: center; flex-wrap: nowrap; }
.row-title .row-title-info { display: inline-flex; align-items: center; font-size: 1.7rem; font-weight: 700; line-height: 1; white-space: nowrap; }
.row-title .row-title-info .rti-sep { color: var(--muted); margin: 0 12px; }
.row-title .row-title-info .rti-prog { color: #38bdf8; }
.row-title .row-title-info .rti-ch { color: #d6c7a1; }
/* v1.0.831 - the station part (its " - " + the name) is one box: it has its own move lines (stX / stY) */
.row-title .row-title-info .rti-st { display: inline-flex; align-items: center; }

/* v1.0.678 — the fixed focus frame (see smoothRowScrollTo in app.js): drawn on top of the focused tile;
   the tile's own focus border is hidden. Moves (only when needed) with a short transition. */
.focus-frame {
  position: fixed; left: 0; top: 0; width: 0; height: 0; z-index: 25; pointer-events: none;
  border: 3px solid var(--ff); border-radius: 16px; box-sizing: border-box; display: none;
  transition: transform 0.22s ease-out, width 0.22s ease-out, height 0.22s ease-out;
}
.focus-frame.show { display: block; }
body:not(.fframe) .focus-frame { display: none !important; }
#screen-grid:not(.active) ~ .focus-frame { display: none !important; }
body.fframe #screen-grid .row-channel.focused { border-color: var(--border); }

/* v1.0.678 — movie rows, a tile WITHOUT a picture (tall layouts): the channel logo was huge — now
   smaller (55% of the width) and centred in the middle area, equal space around it. Logo proportions
   kept (object-fit: contain). Tests-tab switch bigLogoOn = the old big logo. */
body:not(.big-logo).home-rows-1 .content-row.movies-row .row-channel:not(.has-poster) > img,
body:not(.big-logo).home-rows-1 .content-row.poster-row .row-channel:not(.has-poster) > img,
body:not(.big-logo).poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel:not(.has-poster) > img,
body:not(.big-logo).home-rows-15:not(.home-rows-1) .content-row.movies-row .row-channel:not(.has-poster) > img,
body:not(.big-logo).home-rows-1 .content-row.movies-row .row-channel:not(.has-poster) > div:not([class]),
body:not(.big-logo).home-rows-1 .content-row.poster-row .row-channel:not(.has-poster) > div:not([class]),
body:not(.big-logo).poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel:not(.has-poster) > div:not([class]),
body:not(.big-logo).home-rows-15:not(.home-rows-1) .content-row.movies-row .row-channel:not(.has-poster) > div:not([class]) {
  width: 55% !important;
  max-height: 55%;
  margin: auto !important;
  flex: 0 1 auto;
}

/* v1.0.679 — new rail (tests tab switch railV2Off -> no body.rail-v2): the focused icon grows to the
   open live/archive squares' size (116px) with its label under it, white, blue frame; negative margins
   keep the other icons exactly where they are. The menu column sits right above settings with equal
   20px gaps, freeing room under the live/archive squares for the add-on square. */
.rail-item-label { display: none; }
body:not(.rail-v2) .icon-rail-item[data-rail-item=addon] { display: none; }
body.rail-v2 .icon-rail-menu { margin-top: auto; margin-bottom: 0; gap: 20px; }
body.rail-v2 #railSettings { margin-top: 20px; }
body.rail-v2 .icon-rail-item { box-sizing: border-box; flex-shrink: 0; }
body.rail-v2 .icon-rail-item.focused {
  width: 116px;
  height: 116px;
  margin: -21px;
  flex-direction: column;
  background: var(--card2);
  border: 3px solid var(--ff);
  box-shadow: 0 0 0 4px var(--ffg);
  color: #fff;
  position: relative;
  z-index: 50;
}
body.rail-v2:not(.rail-edge) .icon-rail-settings.focused { margin: -20px; }
body.rail-v2 .icon-rail-item.focused svg { width: 50px; height: 50px; }
body.rail-v2 .icon-rail-item.focused .rail-item-label {
  display: block;
  margin-top: 6px;
  font-size: 1rem;
  font-weight: 700;
  white-space: nowrap;
}
/* add-on square: white when an add-on is set, gray when not (also when focused) */
body.rail-v2 .rail-addon { color: #fff; }
body.rail-v2 .rail-addon.addon-off { color: #555a63; }
body.rail-v2 .rail-addon.addon-off.focused { color: #8a8f99; }

/* v1.0.679 — message in the middle of the screen (fades after a few seconds) */
.center-toast {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) scale(0.96);
  background: rgba(20, 24, 34, 0.95);
  border: 2px solid var(--ff);
  border-radius: 16px;
  color: #fff;
  font-size: 1.6rem;
  font-weight: 700;
  padding: 22px 40px;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease, transform 0.2s ease;
  z-index: 200;
}
.center-toast.show { opacity: 1; transform: translate(-50%, -50%) scale(1); }
/* the settings gear keeps its own 20px gap above it (id rule), so its grow margins include that gap */
body.rail-v2 #railSettings.focused { margin: -1px -21px -21px; }
body.rail-v2:not(.rail-edge) #railSettings.focused { margin: 0 -20px -20px; }

/* v1.0.680 — the add-on square moved into the live/archive family: a third square under them when the
   TV button opens (new rail only). White = an add-on is set, gray = none. */
.rail-mode-addon { display: none; }
body.rail-v2 .rail-mode-box.open .rail-mode-addon { display: flex; top: 256px; }
body.rail-v2 .rail-mode-box.open .rail-mode-addon.addon-off { color: #555a63; }
body.rail-v2 .rail-mode-box.open .rail-mode-addon.addon-off.focused { color: #8a8f99; }

/* v1.0.682 — soft transition between the tiles and the rail (tests tab switch railAnimOff -> no
   body.rail-anim): Back from a tile = the rail icon grows smoothly together with the rows sliding over
   (0.3s); Left back to the tiles = it shrinks smoothly. Moving between the rail icons themselves stays
   instant, as before (body.rail-zone-anim only lives for the moment of the zone change). */
@keyframes rail-label-in { from { opacity: 0; } to { opacity: 1; } }
body.rail-anim.rail-zone-anim.rail-v2 .icon-rail-item {
  transition: width 0.3s ease, height 0.3s ease, margin 0.3s ease,
              background-color 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease, color 0.3s ease;
}
body.rail-anim.rail-zone-anim.rail-v2 .icon-rail-item svg { transition: width 0.3s ease, height 0.3s ease; }
body.rail-anim.rail-zone-anim.rail-v2 .icon-rail-item.focused .rail-item-label { animation: rail-label-in 0.3s ease; }

/* v1.0.683 — add-ons tab (tests tab switch addonsTabOff hides it) */
body.addons-tab-off #topicAddons { display: none; }
.addons-hint { padding: 10px 18px 14px; color: var(--muted); font-size: 1.05rem; }
.addons-hint b { color: var(--text); margin-right: 6px; }
.addons-empty { padding: 10px 18px 18px; color: var(--muted); font-size: 1.15rem; }
.addon-toggle-row { align-items: center; }
.addon-toggle-row .addon-logo { width: 48px; height: 48px; object-fit: contain; border-radius: 8px; flex-shrink: 0; }
.addon-toggle-row .addon-text { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.addon-toggle-row .addon-sub { color: var(--muted); font-size: 0.95rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 1100px; }
.addon-toggle-row { justify-content: flex-start; text-align: right; gap: 16px; }
.addon-toggle-row .addon-text { align-items: flex-start; }

/* ===================== v1.0.684 — add-on (VOD) screen ===================== */
body.vod-screen-open #video { display: none !important; }
#screen-vod { position: fixed; inset: 0; background: var(--bg, #07080b); overflow: hidden; }
.vod-hero { position: absolute; left: 0; top: 0; right: 0; height: 560px; }
/* the picture sits in the same 16:9 box as the home screen's TV — a 16:9 backdrop fills it exactly, a
   poster (when there's no backdrop) is shown whole in the middle: never cropped or stretched */
.vod-hero-media { position: absolute; left: 0; top: 0; width: 960px; height: 540px; background: #000; overflow: hidden; }
.vod-hero-media img { width: 100%; height: 100%; object-fit: contain; opacity: 0; transition: opacity 0.3s ease; display: block; }
.vod-hero-media img.show { opacity: 1; }
.vod-hero-info { position: absolute; left: 1000px; right: 56px; top: 30px; bottom: 10px; display: flex; flex-direction: column; gap: 14px; text-align: right; }
.vod-filters { display: flex; gap: 14px; justify-content: flex-start; margin-bottom: 18px; }
.vod-filter { padding: 10px 22px; border-radius: 30px; background: var(--card2); border: 3px solid transparent; font-size: 1.25rem; color: var(--muted); display: flex; gap: 10px; align-items: center; }
.vod-filter b { color: var(--text); font-weight: 700; }
.vod-filter.active { color: #38bdf8; }
.vod-filter.focused { border-color: var(--ff); color: #fff; box-shadow: 0 0 0 4px var(--ffg); }
.vod-title { font-size: 2.6rem; font-weight: 800; color: #fff; line-height: 1.15; }
.vod-meta { font-size: 1.35rem; color: #cbd5e1; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.vod-dot { width: 6px; height: 6px; border-radius: 50%; background: #64748b; display: inline-block; }
.vod-desc { font-size: 1.3rem; color: #cbd5e1; line-height: 1.45; display: -webkit-box; -webkit-line-clamp: 6; -webkit-box-orient: vertical; overflow: hidden; }
.vod-rows { position: absolute; left: 0; right: 0; top: 570px; bottom: 0; overflow: hidden; }
.vod-row { padding: 0 56px 22px 0; }
.vod-row-title { font-size: 1.6rem; font-weight: 700; color: #fff; margin: 0 0 12px; }
/* 2:3 posters, shown whole (object-fit: contain) */
.vod-row-tiles { display: flex; gap: 20px; overflow: hidden; padding: 4px 4px 4px 56px; }
.vod-tile { flex: 0 0 180px; width: 180px; height: 270px; border-radius: 12px; border: 3px solid transparent; background: #111827; overflow: hidden; box-sizing: border-box; display: flex; align-items: center; justify-content: center; }
.vod-tile img { width: 100%; height: 100%; object-fit: contain; display: block; }
.vod-tile-name { padding: 12px; font-size: 1.1rem; text-align: center; color: #e2e8f0; }
.vod-tile.focused { border-color: var(--ff); box-shadow: 0 0 0 4px var(--ffg); }
.vod-empty { position: absolute; top: 700px; left: 0; right: 0; text-align: center; font-size: 1.5rem; color: var(--muted); }
/* list popup (genre / year / streams) */
.vod-picker { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.6); display: none; align-items: center; justify-content: center; z-index: 150; }
.vod-picker.show { display: flex; }
.vod-picker-box { width: 1000px; max-height: 860px; background: #111827; border: 2px solid #334155; border-radius: 18px; padding: 24px; display: flex; flex-direction: column; }
.vod-picker-title { font-size: 1.8rem; font-weight: 800; color: #fff; margin-bottom: 16px; text-align: right; }
.vod-picker-list { overflow: hidden; display: flex; flex-direction: column; gap: 8px; }
.vod-pick { padding: 12px 18px; border-radius: 12px; border: 3px solid transparent; font-size: 1.35rem; color: #e2e8f0; display: flex; align-items: center; gap: 16px; background: rgba(255, 255, 255, 0.04); flex-shrink: 0; text-align: right; }
.vod-pick.focused { border-color: var(--ff); background: var(--card2); color: #fff; }
.vod-q { flex: 0 0 90px; text-align: center; font-weight: 800; padding: 6px 0; border-radius: 8px; background: #1e293b; color: #fff; }
.vod-pick.cached .vod-q { background: #0e7490; }
.vod-pick-text { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.vod-pick-text b { font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; direction: ltr; text-align: right; }
.vod-pick-text small { color: #94a3b8; font-size: 1.05rem; }
/* the VOD player */
#screen-vodplay { position: fixed; inset: 0; pointer-events: none; z-index: 5; }
.vod-osd { position: absolute; left: 0; right: 0; bottom: 0; padding: 60px 70px 50px; background: linear-gradient(to top, rgba(0,0,0,0.85), rgba(0,0,0,0)); opacity: 0; transition: opacity 0.25s ease; }
.vod-osd.show { opacity: 1; }
.vod-osd-title { font-size: 2rem; font-weight: 800; color: #fff; margin-bottom: 18px; text-align: right; }
.vod-osd-row { display: flex; align-items: center; gap: 20px; }
.vod-time { direction: ltr; }
.vod-time { font-size: 1.3rem; color: #e2e8f0; min-width: 90px; text-align: center; }
.vod-bar { flex: 1; height: 8px; border-radius: 4px; background: rgba(255,255,255,0.25); overflow: hidden; }
.vod-bar-fill { height: 100%; width: 0; background: #4A78C4; }
.vod-osd-state { margin-top: 12px; font-size: 1.4rem; color: #fff; text-align: center; min-height: 1.4em; }

/* ===================== v1.0.685 — the home screen's add-on (VOD) mode ===================== */
/* the genre / year bar: just above the rows (top set in app.js placeVodBar), aligned with the rows' start edge */
.vod-home-bar { position: fixed; right: 84px; top: 520px; display: none; gap: 14px; z-index: 20; }
body.vod-bar-on #screen-grid.active ~ .vod-home-bar { display: flex; }
body.rail-edge .vod-home-bar { right: 56px; }
.vod-home-bar .vod-filter { padding: 8px 20px; font-size: 1.15rem; }
/* the mode squares: whichever square holds the add-on mode is gray while no add-on is on */
body.rail-v2 .rail-mode-box.open .rail-mode-btn.addon-off { color: #555a63; }
body.rail-v2 .rail-mode-box.open .rail-mode-btn.addon-off.focused { color: #8a8f99; }

/* ===================== v1.0.686 — add-on: library, series, player ===================== */
/* library icon in the rail (new rail only; tests-tab switch libraryIconOff) — 7 icons now, so the
   column's gaps shrink from 20px to 6px to keep it under the open live/archive/add-on squares */
body:not(.rail-v2) .rail-library, body:not(.lib-icon) .rail-library { display: none; }
body.rail-v2.lib-icon .icon-rail-menu { gap: 6px; }
body.rail-v2.lib-icon #railSettings { margin-top: 6px; }
body.rail-v2.lib-icon #railSettings.focused { margin: -15px -21px -21px; }
body.rail-v2.lib-icon:not(.rail-edge) #railSettings.focused { margin: -14px -20px -20px; }
/* the bar: movies / series button stands out a bit */
.vod-home-bar .vod-type b { color: #38bdf8; }
/* continue watching: progress on the add-on tile */
.vod-cw-bar { position: absolute; left: 10px; right: 10px; bottom: 8px; height: 6px; border-radius: 3px; background: rgba(255,255,255,0.25); overflow: hidden; z-index: 3; }
.vod-cw-bar i { display: block; height: 100%; background: #38bdf8; }
.row-channel.vod-item { position: relative; }
/* list popup: icons */
.vod-pick-icon { flex: 0 0 44px; height: 44px; display: flex; align-items: center; justify-content: center; }
.vod-pick-icon svg { width: 30px; height: 30px; color: #cbd5e1; }
.vod-pick.focused .vod-pick-icon svg { color: #fff; }
/* the player */
.vod-sub-text { position: fixed; left: 8%; right: 8%; bottom: 7%; text-align: center; font-size: 2.3rem; font-weight: 700; line-height: 1.3; color: #fff; white-space: pre-line;
  text-shadow: 0 0 6px #000, 0 0 3px #000, 2px 2px 2px #000; z-index: 6; pointer-events: none; transition: bottom 0.2s ease; }
body.vod-osd-on.vod-player-v2 .vod-sub-text { bottom: 330px; }
body.vod-osd-on:not(.vod-player-v2) .vod-sub-text { bottom: 230px; }
.vod-osd { z-index: 7; }
.vod-bar.focused { outline: 3px solid var(--ff); outline-offset: 4px; }
.vod-btns { display: none; margin-top: 26px; gap: 18px; justify-content: center; }
body.vod-player-v2 .vod-btns { display: flex; }
.vod-btn { display: flex; flex-direction: column; align-items: center; gap: 8px; min-width: 120px; padding: 12px 14px; border-radius: 14px; border: 3px solid transparent; color: #cbd5e1; font-size: 1.05rem; background: rgba(255,255,255,0.06); }
.vod-btn svg { width: 38px; height: 38px; }
.vod-btn.focused { border-color: var(--ff); background: var(--card2); color: #fff; box-shadow: 0 0 0 4px var(--ffg); }
.vod-btn .i-play { display: none; }
.vod-btn.paused .i-play { display: block; }
.vod-btn.paused .i-pause { display: none; }
.vod-delay-panel { position: fixed; top: 60px; left: 50%; transform: translateX(-50%); display: none; flex-direction: column; align-items: center; gap: 10px; padding: 18px 34px;
  background: rgba(17,24,39,0.95); border: 2px solid var(--ff); border-radius: 18px; z-index: 160; color: #fff; }
.vod-delay-panel.show { display: flex; }
.vod-delay-title { font-size: 1.3rem; color: #cbd5e1; }
.vod-delay-ctl { display: flex; align-items: center; gap: 26px; font-size: 2rem; font-weight: 800; }
.vod-delay-ctl b { width: 54px; height: 54px; border-radius: 50%; background: #1e293b; display: flex; align-items: center; justify-content: center; }
.vod-delay-value { min-width: 110px; text-align: center; }
.vod-delay-play { font-size: 1.5rem; color: #38bdf8; }

/* v1.0.687 — settings: the open tab's header stays marked in gray (the blue frame is still the focus) */
body:not(.settings-open-plain) .settings-topic.open > .settings-topic-header { background: #3a4150; color: #fff; }
/* library view: the bar's "library" chip */
.vod-home-bar .vod-filter[data-filter="lib"] b { color: #fca5a5; }

/* v1.0.689 — the rail in the add-on mode (like Stremio): search, home, movies, series, library, exit,
   settings — no archive / favorites / groups there. 6 icons -> the 20px gaps come back. */
body:not(.home-mode-vod) .rail-vod-only, body:not(.rail-v2) .rail-vod-only { display: none; }
body.rail-v2.home-mode-vod .icon-rail-item[data-rail-item="archive"],
body.rail-v2.home-mode-vod .icon-rail-item[data-rail-item="favorites"] { display: none; }
/* v1.0.691 — "groups" stays in the add-on mode, as genres */
.lbl-vod { display: none; }
body.home-mode-vod .lbl-vod { display: inline; }
body.home-mode-vod .lbl-std { display: none; }
/* v1.0.689 — the genre / year bar slides with the rows when the rail opens (Back) */
body.rail-edge .vod-home-bar { transition: transform 0.3s ease; }
body.rail-edge.rail-expanded .vod-home-bar { transform: translateX(var(--rail-cx, -78px)); }

/* v1.0.692 — the add-on "options" button + panel. The button's corners follow the tiles' (square when
   the tiles are square in the current row layout). */
.vod-home-bar .vod-opt-btn { border-radius: 0; padding: 12px 34px; min-width: 360px; justify-content: space-between; font-size: 1.25rem; }
body.vod-opt-round .vod-home-bar .vod-opt-btn { border-radius: 14px; } /* settings checkbox (add-ons tab) */
.vod-home-bar .vod-opt-btn b { color: #38bdf8; }
.vod-opts { position: fixed; top: 0; bottom: 0; right: 130px; display: none; align-items: center; z-index: 60; }
body.rail-edge .vod-opts { right: 92px; }
.vod-opts.show { display: flex; }
.vod-opts-box { min-width: 380px; max-width: 460px; display: flex; flex-direction: column; }
.vod-opts-tabs { display: flex; gap: 10px; margin-bottom: 14px; }
.vod-opts-tab { flex: 1; text-align: center; padding: 12px 10px; border-radius: 10px; background: var(--card2); border: 2px solid transparent; color: var(--muted); font-size: 1.15rem; font-weight: 700; }
.vod-opts-tab.selected { color: #fff; background: #334155; }
.vod-opts-tab.focused { border-color: var(--accent); box-shadow: 0 0 0 4px rgba(123,92,255,0.25); }
.vod-opts-list { display: flex; flex-direction: column; gap: 8px; max-height: 52vh; overflow: hidden; }
.vod-opts-list .rail-flyout-item { display: flex; justify-content: space-between; gap: 16px; flex-shrink: 0; }
.vod-opts-k { color: var(--muted); }
.vod-opts-list .rail-flyout-item b { color: #38bdf8; }
/* v1.0.842 - the line "כוונון הדיאלוג המאוזן" while its tuning is on (the arrows change the window, not the list) */
.vod-opts-list .rail-flyout-item.hp-adj-on { border-color: #facc15 !important; box-shadow: 0 0 0 4px rgba(250, 204, 21, 0.3) !important; }
.vod-opts-list .rail-flyout-item.hp-adj-on b { color: #facc15; }
.vod-opts-hint { margin-top: 12px; color: var(--muted); font-size: 0.95rem; text-align: center; }

/* v1.0.693 — seasons as tabs above the episode list (one screen) */
.vod-picker-tabs { display: flex; gap: 10px; margin-bottom: 14px; overflow: hidden; flex-shrink: 0; }
.vod-ptab { flex: 0 0 auto; padding: 10px 20px; border-radius: 10px; background: #1e293b; color: #94a3b8; font-size: 1.2rem; font-weight: 700; border: 2px solid transparent; }
.vod-ptab.selected { background: #334155; color: #fff; border-color: var(--ff); }

/* v1.0.694 — add-on mode: the description gets 5 lines (was 4), and the director / actors line under it
   in light blue (tests-tab switch vodHeroMoreOff) */
.hero-cast { display: none; }
body.vod-hero-more.home-mode-vod .hero-cast { display: block; width: calc(100% + 100px); margin-left: -100px; font-size: 1.35rem; color: #38bdf8; margin-top: 10px; line-height: 1.35;
  /* v1.0.695 — BUG FIX (the TV cut the line's start, only "...קנים" showed): wraps onto up to 2 lines from the
     start edge instead of one unbreakable line */
  white-space: normal; direction: rtl; text-align: right; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; max-height: 3.8em; }
body.vod-hero-more.home-mode-vod .hero-now-desc { -webkit-line-clamp: 5; max-height: 165px; }

/* v1.0.695 — add-on series: seasons / episodes count under the clear logo, light blue (same colour as the cast line) */
.hero-series-count { display: none; font-size: 1.6rem; color: #38bdf8; margin: 4px 0 8px; text-align: right; line-height: 1.3; }
.hero-series-count.show { display: block; }

/* v1.0.695 — series episode list: wider, the episode picture on the left (16:9 box, the picture is
   shown whole — contain, never cropped or stretched), and the air date of episodes not aired yet */
.vod-picker.vod-picker-wide .vod-picker-box { width: 1500px; max-height: 940px; }
.vod-pick.vod-ep { align-items: center; padding-top: 8px; padding-bottom: 8px; }
.vod-pick.vod-ep .vod-pick-text { flex: 1 1 auto; }
.vod-pick.vod-ep .vod-pick-text small { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; line-height: 1.35; }
.vod-ep-img { flex: 0 0 192px; width: 192px; height: 108px; border-radius: 8px; overflow: hidden; background: #0b1220; display: flex; align-items: center; justify-content: center; margin-right: auto; }
.vod-ep-img img { width: 100%; height: 100%; object-fit: contain; display: block; }
.vod-ep-air { color: #38bdf8; font-size: 1.1rem; font-weight: 700; }
.vod-pick.vod-ep-future { opacity: 0.75; }
.vod-pick.vod-ep-future.focused { opacity: 1; }

/* v1.0.695 — add-on mode, a description longer than 5 lines: 6 lines, and the whole hero block goes up one
   line (33px = one description line) so its bottom stays in the same place (tests switch vodDesc6Off) */
body.vod-hero-more.home-mode-vod.vod-desc6 .hero-now-desc { -webkit-line-clamp: 6; max-height: 198px; }
/* v1.0.700 — a full 5-line description + stars row + cast: 30px up (tests switch vodDesc5LiftOff) */
body.vod-hero-more.home-mode-vod.vod-desc5full .hero-info { margin-top: -30px; }
body.vod-hero-more.home-mode-vod.vod-desc6 .hero-info { margin-top: -63px; } /* v1.0.698 — +30px more (was -33px = only the 6th line) */
body.vod-hero-more.home-mode-vod.vod-desc6.vod-cast2 .hero-info { margin-top: -78px; } /* v1.0.698 — 6 lines + a 2-line cast: 45px more (instead of 30) */

/* v1.0.695 — the actors list: the actor's photo keeps its own 2:3 shape (TMDB profile photos are 2:3) —
   contain, never cropped or stretched */
.vod-pick.vod-actor { padding-top: 6px; padding-bottom: 6px; }
.vod-actor-img { flex: 0 0 60px; width: 60px; height: 90px; border-radius: 6px; overflow: hidden; background: #0b1220; display: flex; align-items: center; justify-content: center; }
.vod-actor-img img { width: 100%; height: 100%; object-fit: contain; display: block; }

/* v1.0.695 — add-on mode: "סרט" / "סדרה" above the clear logo — the same look as our own clear logo
   (Arial bold, tight letters, dark drop shadow), small, light blue (tests switch vodTypeTagOff) */
.hero-type-tag { display: none; font-family: Arial, 'Segoe UI', Tahoma, sans-serif; font-weight: 700; letter-spacing: -0.04em;
  font-size: 1.4rem; line-height: 1.1; color: #38bdf8; text-shadow: 0 2px 5px rgba(0, 0, 0, 0.85); text-align: right; margin-bottom: 4px; } /* v1.0.698 — smaller (was 1.9rem) */
.hero-type-tag.show { display: block; }

/* v1.0.696 — add-on series: the seasons / episodes count sits on the title's line, light blue */
.hero-movie-series-count { color: #38bdf8; }
/* v1.0.696 — and that line hugs the clear logo (was ~20px below it, now ~8px) */
body.home-mode-vod.vod-count-inrow .hero-movie-meta-row { margin-top: -12px; }

/* v1.0.696 — tests switch: the old blue frames */
body.frame-blue-old { --ff: #4A78C4; --ffg: rgba(74, 120, 196, 0.35); }

/* v1.0.697 — the cast line: 200px wider, growing toward the picture (the right edge stays) — tests switch vodCastWideOff */
body.vod-cast-wide.vod-hero-more.home-mode-vod .hero-cast { width: calc(100% + 300px); margin-left: -300px; }

/* v1.0.698 — settings: no "→ חזרה" button (Back on the remote), the title moves to the edge — tests switch settingsBackBtnShow */
body.settings-no-back #btnRestoreBack { display: none; }

/* v1.0.699 — tests switch vodEnterMenuOff: no "בחירת תפריט" choice */
body.vod-enter-menu-off .vod-enter-menu-opt { display: none; }

/* v1.0.699 — series: the "next episode" card (bottom right). A rounded rectangle in dark gray; a light-gray fill
   grows from the right until the chosen time runs out, then the next episode plays. OK = now, Back = cancel. */
.vod-next { position: absolute; right: 70px; bottom: 110px; z-index: 9; display: none; transition: bottom 0.25s ease; }
body.vod-osd-on .vod-next { bottom: 400px; } /* above the player's bar / buttons while they're shown */
.vod-next.show { display: block; }
.vod-next-go { position: relative; overflow: hidden; min-width: 380px; padding: 18px 26px; border-radius: 14px; background: rgba(48, 50, 56, 0.94); border: 3px solid transparent; color: #fff; display: flex; flex-direction: column; align-items: flex-start; gap: 4px; }
.vod-next-fill { position: absolute; top: 0; right: 0; bottom: 0; width: 0; background: rgba(176, 180, 190, 0.7); transition: width 0.25s linear; }
.vod-next-label, .vod-next-name { position: relative; text-shadow: 0 1px 4px rgba(0, 0, 0, 0.9); }
.vod-next-label { font-size: 1.6rem; font-weight: 800; }
.vod-next-name { font-size: 1.15rem; color: #fff; max-width: 520px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.vod-next-go.focused { border-color: var(--ff); box-shadow: 0 0 0 4px var(--ffg); }
body.vod-auto-next-off [data-ls-opt="vodNextAt"] { display: none; }

/* v1.0.699 — player: next episode / episodes only on a series episode (next: only when there is one); tests switch vodPlayerEpBtnsOff */
.vod-btn-ep { display: none; }
body.vod-is-ep .vod-btn-ep[data-act="episodes"], body.vod-is-ep.vod-has-next .vod-btn-ep[data-act="next"] { display: flex; }
body.vod-ep-btns-off .vod-btn-ep2 { display: none !important; }

/* v1.0.698 — add-on tiles: in my library (top right) / watched (top left), light blue on a dark round badge — tests switch vodBadgesOff */
.vod-badges { position: absolute; inset: 0; pointer-events: none; z-index: 6; }
.vod-badge { position: absolute; top: 10px; width: 38px; height: 38px; border-radius: 50%; background: rgba(0, 0, 0, 0.65); color: #38bdf8; display: none; align-items: center; justify-content: center; box-shadow: 0 1px 4px rgba(0, 0, 0, 0.6); }
.vod-badge.on { display: flex; }
.vod-badge svg { width: 22px; height: 22px; }
.vod-badge-lib { right: 10px; }
.vod-badge-seen { left: 10px; }

/* v1.0.699 — the cast line: ONE line only, whatever doesn't fit is simply cut (whole words, no "…") — tests switch vodCastOneLineOff */
body.vod-hero-more.home-mode-vod.vod-cast-1line .hero-cast { display: block; -webkit-line-clamp: unset; max-height: 1.35em; overflow: hidden; white-space: normal; }

/* v1.0.700 — add-on mode: the year / genres / stars / title / seasons row is always ONE line — if it's still too long,
   the title in it shrinks with "…" (everything else keeps its size) */
body.home-mode-vod .hero-movie-meta-row.show { flex-wrap: nowrap; min-width: 0; overflow: hidden; }
body.home-mode-vod .hero-movie-meta-row > * { flex-shrink: 0; white-space: nowrap; }
body.home-mode-vod .hero-movie-meta-row > .hero-movie-channel { flex-shrink: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }

/* v1.0.701 — the focus frame jumps (no animation) when it appears again or the tile's shape changed */
.focus-frame.snap { transition: none !important; }

/* v1.0.701 — add-on player subtitles: size (--sub-scale) and position (--sub-x right / --sub-y up), set in the subtitles menu */
.vod-sub-text { font-size: calc(2.3rem * var(--sub-scale, 1)); transform: translate(var(--sub-x, 0px), calc(-1 * var(--sub-y, 0px))); }
.vod-subpos-help { font-size: 1.1rem; color: #cbd5e1; }

/* v1.0.701 — tests switch searchScopeOff: no Search tab */
body.search-scope-off #topicSearch { display: none; }

/* v1.0.702 — tests switch vodMultiYearOff: no genre / year limit rows */
body.vod-multiyear-off .vod-limit-row { display: none; }

/* v1.0.703 — the "+" tile at the end of a capped add-on row */
.row-channel.vod-more-tile { display: flex !important; flex-direction: column; align-items: center; justify-content: center; background: rgba(255, 255, 255, 0.06); }
.vod-more-plus { font-size: 7rem; font-weight: 300; line-height: 1; color: #38bdf8; }
.vod-more-label { margin-top: 10px; font-size: 1.4rem; color: #cbd5e1; }
body.rowmax-off .rowmax-row { display: none; }

/* v1.0.706 — the row under the logo (year / genres / stars / channel): 45px wider, toward the picture (the start edge stays) —
   tests switch heroRowWideOff */
body.hero-row-wide .hero-movie-meta-row { width: calc(100% + 45px); margin-left: -45px; box-sizing: border-box; }

/* v1.0.706 — live / archive rail (no library icon there): a bit more room between the icons, the column grows upward
   (it's anchored at the bottom) — tests switch railGapMoreOff */
body.rail-v2.rail-gap-more:not(.lib-icon) .icon-rail-menu { gap: 28px; }

/* v1.0.708 — a poster row with wide channel tiles under it: both rows 10px lower (tests switch posterWideDownOff) */
body.rows-poster-wide .content-rows { top: 10px; }
/* v1.0.712 — on the LG TV the second row's channel name was still cut: there both rows went 10px UP instead.
   v1.0.715 — per request: on the TV the rows stay in their place (0px) — the 2nd row's title then lines up with
   the rail's exit button, exactly as in Chrome; the channel name is kept whole by applyPosterWideHeight (app.js). */
body.rows-poster-wide:not(.web-build) .content-rows { top: 0; }

/* v1.0.709 — per request: the three mode squares (live / archive / add-on) 15px higher (tests switch railModeUpOff) */
body.rail-mode-up .rail-mode-box.open .rail-mode-btn { transform: translate(-50%, -25px); } /* v1.0.711: 15 -> 25 */

/* v1.0.716 — per request: the gap above "★ המשך הצפייה הבא" = the gap between the description and the progress bar
   above it (14px, the progress row's own margin-top). Tests switch heroNextGapOff (-> the old 27px). */
body.hero-next-gap .hero-next { margin-top: 14px; }

/* v1.0.720 — NEW (per request): the title and the clear logo share ONE fixed-height slot, so the description under
   them stays in place whether a logo or the plain title shows (it only grows for an extra-tall logo). Capped (the
   description never goes lower than this): 64px normally, 100px when the logo may be 128px tall. Logo and text sit
   at the slot's bottom, right above the description. Tests switch heroSlotOff. Nothing is stretched or cropped. */
body.hero-slot { --hero-slot-h: 64px; }
body.hero-slot.home-channel-name-hidden { --hero-slot-h: 100px; }
body.hero-slot.hero-logo-shown .hero-now-logo-wrap { min-height: var(--hero-slot-h); display: flex; align-items: flex-end; justify-content: flex-start; }
body.hero-slot:not(.hero-logo-shown) .hero-now-title:not(:empty) { min-height: var(--hero-slot-h); margin-bottom: 4px; display: flex; align-items: flex-end; }
/* v1.0.720 — NEW (per request): a soft fade (0.2s) as the clear logo / the title appear. Tests switch heroFadeOff. */
@keyframes heroFadeIn { from { opacity: 0; } to { opacity: 1; } }
body.hero-fade .hero-now-logo { animation: heroFadeIn 0.2s ease-out; }
body.hero-fade .hero-now-title { transition: opacity 0.2s ease-out; }

/* v1.0.721 — NEW (per request): while the menu column is entered, every button that isn't focused sits in a gray
   frame on the card background, like the live / archive / add-on squares (same colors; its own 74px size, so the
   column's spacing stays). Tests switch railFramesOff. */
body.rail-v2.rail-frames.rail-expanded .icon-rail-item:not(.focused) {
  background: var(--card2);
  border: 3px solid var(--border);
}
/* v1.0.721 — the heart (favorites) out of the menu column (now under "בית"). Tests switch railHeartKeep. */
body.rail-no-heart .icon-rail-item[data-rail-item="favorites"] { display: none; }

/* v1.0.721 — per request: the framed menu buttons as big as the squares (116 wide) with their name under the icon,
   as tall as fits the buttons shown in this mode (--rail-btn-h, max 100). The focused one keeps its 116x116 look.
   Tests switch railBigFramesOff (-> the small 74px frames). Icons keep their proportions. */
body.rail-v2.rail-frames.rail-big.rail-expanded .icon-rail-item:not(.focused) {
  width: 116px;
  height: var(--rail-btn-h, 100px);
  flex-direction: column;
}
body.rail-v2.rail-frames.rail-big.rail-big-labels.rail-expanded .icon-rail-item:not(.focused) .rail-item-label {
  display: block;
  margin-top: 4px;
  font-size: 1rem;
  font-weight: 700;
  white-space: nowrap;
}
body.rail-v2.rail-frames.rail-big.rail-expanded .icon-rail-item.focused {
  margin: calc((var(--rail-btn-h, 100px) - 116px) / 2) 0;
}
body.rail-v2.rail-frames.rail-big.rail-expanded #railSettings.focused {
  margin: calc(var(--rail-btn-gap, 12px) + (var(--rail-btn-h, 100px) - 116px) / 2) 0 calc((var(--rail-btn-h, 100px) - 116px) / 2);
}
body.rail-v2.rail-frames.rail-big.rail-expanded .icon-rail-menu { gap: var(--rail-btn-gap, 12px); }
body.rail-v2.rail-frames.rail-big.rail-expanded #railSettings { margin-top: var(--rail-btn-gap, 12px); }

/* v1.0.723 — the "Cast" settings tab exists only in the Chrome version */
body:not(.web-build) .web-only-topic { display: none; }
/* v1.0.723 — the Cast button (web/web-shim.js) fades away in full screen after the chosen seconds */
#webCastBtn { transition: opacity 0.4s ease; }
body.cast-btn-hidden #webCastBtn { opacity: 0; pointer-events: none; }
body.rail-v2.rail-frames.rail-big.rail-sym.rail-expanded .icon-rail { padding-bottom: var(--rail-bottom-pad, 15px); } /* v1.0.723 (tests switch railSymmetryOff) */

/* v1.0.724 — date / time (+ version) of each change in the tests tab */
.test-added { margin-right: auto; padding-right: 16px; font-size: 0.95rem; color: var(--muted); white-space: nowrap; }

/* ===== v1.0.726 — "גופנים" tab: bundled fonts (only downloaded when chosen). Default = unchanged. ===== */
@font-face { font-family: 'AppHeebo'; src: url('../fonts/Heebo-Light.ttf') format('truetype'); font-weight: 300; font-style: normal; }
@font-face { font-family: 'AppHeebo'; src: url('../fonts/Heebo-Regular.ttf') format('truetype'); font-weight: 400; font-style: normal; }
@font-face { font-family: 'AppHeebo'; src: url('../fonts/Heebo-Medium.ttf') format('truetype'); font-weight: 500; font-style: normal; }
@font-face { font-family: 'AppHeebo'; src: url('../fonts/Heebo-SemiBold.ttf') format('truetype'); font-weight: 600; font-style: normal; }
@font-face { font-family: 'AppHeebo'; src: url('../fonts/Heebo-Bold.ttf') format('truetype'); font-weight: 700; font-style: normal; }
@font-face { font-family: 'AppHeebo'; src: url('../fonts/Heebo-ExtraBold.ttf') format('truetype'); font-weight: 800; font-style: normal; }
@font-face { font-family: 'AppRubik'; src: url('../fonts/Rubik-Light.ttf') format('truetype'); font-weight: 300; font-style: normal; }
@font-face { font-family: 'AppRubik'; src: url('../fonts/Rubik-Regular.ttf') format('truetype'); font-weight: 400; font-style: normal; }
@font-face { font-family: 'AppRubik'; src: url('../fonts/Rubik-Medium.ttf') format('truetype'); font-weight: 500; font-style: normal; }
@font-face { font-family: 'AppRubik'; src: url('../fonts/Rubik-SemiBold.ttf') format('truetype'); font-weight: 600; font-style: normal; }
@font-face { font-family: 'AppRubik'; src: url('../fonts/Rubik-Bold.ttf') format('truetype'); font-weight: 700; font-style: normal; }
@font-face { font-family: 'AppRubik'; src: url('../fonts/Rubik-ExtraBold.ttf') format('truetype'); font-weight: 800; font-style: normal; }
@font-face { font-family: 'AppAssistant'; src: url('../fonts/Assistant-Light.ttf') format('truetype'); font-weight: 300; font-style: normal; }
@font-face { font-family: 'AppAssistant'; src: url('../fonts/Assistant-Regular.ttf') format('truetype'); font-weight: 400; font-style: normal; }
@font-face { font-family: 'AppAssistant'; src: url('../fonts/Assistant-Medium.ttf') format('truetype'); font-weight: 500; font-style: normal; }
@font-face { font-family: 'AppAssistant'; src: url('../fonts/Assistant-SemiBold.ttf') format('truetype'); font-weight: 600; font-style: normal; }
@font-face { font-family: 'AppAssistant'; src: url('../fonts/Assistant-Bold.ttf') format('truetype'); font-weight: 700; font-style: normal; }
@font-face { font-family: 'AppAssistant'; src: url('../fonts/Assistant-ExtraBold.ttf') format('truetype'); font-weight: 800; font-style: normal; }
@font-face { font-family: 'AppArial'; src: url('../fonts/LiberationSans-Regular.ttf') format('truetype'); font-weight: 400; font-style: normal; }
@font-face { font-family: 'AppArial'; src: url('../fonts/LiberationSans-Bold.ttf') format('truetype'); font-weight: 700; font-style: normal; }
@font-face { font-family: 'AppSuez'; src: url('../fonts/SuezOne-Regular.ttf') format('truetype'); font-weight: 400; font-style: normal; }
body.app-font-arial, body.app-font-arial button, body.app-font-arial input, body.app-font-arial select, body.app-font-arial textarea { font-family: 'AppArial', "Segoe UI", Arial, sans-serif; }
body.app-font-heebo, body.app-font-heebo button, body.app-font-heebo input, body.app-font-heebo select, body.app-font-heebo textarea { font-family: 'AppHeebo', "Segoe UI", Arial, sans-serif; }
body.app-font-rubik, body.app-font-rubik button, body.app-font-rubik input, body.app-font-rubik select, body.app-font-rubik textarea { font-family: 'AppRubik', "Segoe UI", Arial, sans-serif; }
body.app-font-assistant, body.app-font-assistant button, body.app-font-assistant input, body.app-font-assistant select, body.app-font-assistant textarea { font-family: 'AppAssistant', "Segoe UI", Arial, sans-serif; }
body.app-font-suez, body.app-font-suez button, body.app-font-suez input, body.app-font-suez select, body.app-font-suez textarea { font-family: 'AppSuez', "Segoe UI", Arial, sans-serif; }
.fonts-note { font-size: 1rem; opacity: .75; margin: 10px 20px; }
/* ===== v1.0.726 — "סמלי ערוצים" tab ===== */
.logos-subhead { font-size: 1.1rem; font-weight: 700; opacity: .8; margin: 18px 20px 6px; }
/* "large" logo = a bigger box (proportions kept by object-fit: contain). "small" and equal-area are done in
   app.js with padding inside the same box, so nothing around the logo moves. */
body.logo-sz2-l:not(.home-rows-1):not(.home-rows-15) .row-channel > img { width: 100px; height: 100px; margin: -8px 0 -2px; } /* same 90px of height as before (85 + 5) -> the name doesn't move */
body.logo-sz1-l.home-rows-1 .row-channel > img { width: 90% !important; }
body.logo-sz15-l.home-rows-15:not(.home-rows-1) .content-row .row-channel > img,
body.logo-sz15-l.poster-rows-on:not(.home-rows-1) .content-row.poster-row .row-channel > img { width: 90% !important; }

/* ===== v1.0.728 — one-row layout (rule 1): tiles 15px above the bottom edge (translateY in app.js), the info
   starts 15px under the top edge and only grows downward; the "more rows" arrow flashes under the settings
   icon. Tests switches row1BottomOff / row1HeroTopOff. ===== */
body.row1-hero-top .hero-info { padding-top: var(--row1-pt, 15px) !important; margin-top: 0 !important; }
body.row1-arrow-rail .rows-more-indicator { display: none !important; margin: 0 !important; }
body.row1-arrow-rail .rows-more-indicator.show.arrow-flash { display: block !important; }
/* v1.0.728 — live / archive: "ערוץ" / "ארכיון" is the first line of the info (tests switch liveTypeTagOff) */
body.live-type-tag:not(.home-mode-vod) #previewTypeTag { order: -1; }
body.live-type-tag:not(.home-mode-vod):not(.home-channel-name-hidden) #previewTypeTag { padding-right: 118px; } /* beside the channel logo, like the name */
/* v1.0.728 — the "כללים" tab: read-only text rows */
.rules-row { padding: 14px 20px; margin: 6px 0; border-radius: 10px; font-size: 1.15rem; line-height: 1.5; }
.rules-row b { color: #38bdf8; }
.rules-row.focused { background: rgba(255, 255, 255, 0.08); }
/* v1.0.728 — the one-row shift, combined with the rail's own slide (body.rail-edge.rail-expanded) */
body.row1-arrow-rail .content-rows { transform: translateY(var(--row1-dy, 0px)); }
body.row1-arrow-rail.rail-edge.rail-expanded .content-rows { transform: translateX(var(--rail-cx, -78px)) translateY(var(--row1-dy, 0px)); }
/* v1.0.728 — "הראה את החץ התחתון": the one-row arrow under the tiles shows in the add-on mode too, even when the
   next row peeks in (that used to hide it there) */
body.row1-arrow-bottom.rows-cut .rows-more-indicator.show { display: block !important; }
/* v1.0.730 — "סרט" / "סדרה" / "ערוץ" / "ארכיון": a soft fade, only after the clear logo (or the title) is shown
   (tests switch tagFadeOff). The word keeps its place meanwhile (opacity only), so nothing moves. */
body.hero-tag-fade #previewTypeTag { opacity: 0; transition: opacity 0.6s ease; }
body.hero-tag-fade.hero-tag-ready #previewTypeTag { opacity: 1; }
/* v1.0.734 — one row: the menu icons from "search" down follow the focused row's title (see alignRailToRow in
   app.js; the top mode square stays). Only while the rail is closed. Tests switch row1RailAlignOff. */
body.row1-arrow-rail:not(.rail-expanded) .icon-rail-item,
body.row1-arrow-bottom:not(.rail-expanded) .icon-rail-item { transform: translateY(var(--rail-dy, 0px)); }
/* v1.0.737 — TEMPORARY measuring grid + its rail button (out of the flow, placed by placeGridTool in app.js) */
.icon-rail-menu { position: relative; }
.icon-rail-item.rail-grid-tool { position: absolute; left: 0; right: 0; margin-left: auto; margin-right: auto; }
body.grid-tool-off .icon-rail-item.rail-grid-tool { display: none; }
.grid-overlay { position: fixed; left: 0; top: 0; width: 1920px; height: 1080px; z-index: 2147483000; pointer-events: none; display: none; }
.grid-overlay.show { display: block; }
.grid-overlay.no-nums .grid-nums { display: none; }
/* v1.0.743 — the bar's button as a section selector (v1.0.744: arrows drawn in CSS, one on each side of the name, bigger) */
.vod-filter .hbar-sec-arr { display: none; }
.vod-filter .hbar-arr { display: none; width: 0; height: 0; border-top: 9px solid transparent; border-bottom: 9px solid transparent; flex-shrink: 0; }
.vod-filter .hbar-arr.a1 { border-left: 12px solid #fff; }
.vod-filter .hbar-arr.a2 { border-right: 12px solid #fff; }
.vod-filter.focused .hbar-arr { display: inline-block; }
/* v1.0.837 - the vertical menu hidden: toward its side the arrow key opens it, so only the other arrow is drawn (its room is kept: the button does not change size) */
body.rail-hide-on:not(.hbar-right-rail-off) .vod-filter .hbar-arr.a1 { visibility: hidden; }
.vod-filter.hbar-sec-pending b { color: #38bdf8; }
/* v1.0.744 — the archive groups ticks list (home: where the options panel is; player: centred, above everything) */
.arc-pick { z-index: 9000; }
.arc-pick.arc-pick-center { right: 0; left: 0; justify-content: center; }
.arc-pick .arc-pick-limit b { display: flex; align-items: center; gap: 12px; }
.arc-pick .arc-pick-arr { display: inline-block; width: 0; height: 0; border-top: 8px solid transparent; border-bottom: 8px solid transparent; }
.arc-pick .arc-pick-arr.a1 { border-left: 11px solid #38bdf8; }
.arc-pick .arc-pick-arr.a2 { border-right: 11px solid #38bdf8; }
/* v1.0.744 — the channel name in the info's stars row: light blue (tests switch heroChCyanOff) */
body.hero-ch-cyan .hero-movie-meta-row > .hero-movie-channel { color: #38bdf8; }
/* v1.0.744 — tiles' progress bar colour (Settings; gray = as before; tests switch tileProgColorOff) */
body.tprog-cyan .row-channel.has-progress::after { background: linear-gradient(to left, #38bdf8 0, #38bdf8 var(--w, 0%), #767B89 var(--w, 0%), #767B89 100%) !important; }
body.tprog-red .row-channel.has-progress::after { background: linear-gradient(to left, #ef4444 0, #ef4444 var(--w, 0%), #767B89 var(--w, 0%), #767B89 100%) !important; }
body.tprog-green .row-channel.has-progress::after { background: linear-gradient(to left, #22c55e 0, #22c55e var(--w, 0%), #767B89 var(--w, 0%), #767B89 100%) !important; }
body.tprog-cyan .tile-progress-fill { background: #38bdf8 !important; }
body.tprog-red .tile-progress-fill { background: #ef4444 !important; }
body.tprog-green .tile-progress-fill { background: #22c55e !important; }
/* v1.0.744 — archive tiles: the day + time, small and light blue, between the logo and the programme name — it takes the
   invisible spacer line's height, so the tile stays exactly as tall (tests switch arcWhenOff) */
.row-channel .arc-when { display: none; }
body.arc-when-on .row-channel.archive-tile:not(.has-poster) .arc-when { display: block !important; visibility: visible !important; color: #38bdf8; font-size: 0.85rem; margin-top: 0; margin-bottom: 6px; line-height: 16px; height: 16px; }
body.arc-when-on .row-channel.archive-tile:not(.has-poster) .now.tile-spacer { display: none !important; } /* beats the v1.0.633 always-there rule */
/* v1.0.744 — archive "2.5 rows": only the height gets lower (the logo box 85 -> 45px, never stretched: contain);
   the rows go on downward */
body.arc-rows-25 .row-channel > img { width: 45px; height: 45px; } /* v1.0.749 — every screen; only the tile's own logo (a direct child), never a poster picture */
body.arc-rows-25 .row-channel > div[style] { width: 45px !important; height: 45px !important; }
/* v1.0.745 — a coloured progress bar runs over the frame's own mouse-gray (var(--border)), not the light gray (tests switch tprogBgLight) */
body.tprog-cyan:not(.tprog-bg-light) .row-channel.has-progress::after { background: linear-gradient(to left, #38bdf8 0, #38bdf8 var(--w, 0%), var(--border) var(--w, 0%), var(--border) 100%) !important; }
body.tprog-red:not(.tprog-bg-light) .row-channel.has-progress::after { background: linear-gradient(to left, #ef4444 0, #ef4444 var(--w, 0%), var(--border) var(--w, 0%), var(--border) 100%) !important; }
body.tprog-green:not(.tprog-bg-light) .row-channel.has-progress::after { background: linear-gradient(to left, #22c55e 0, #22c55e var(--w, 0%), var(--border) var(--w, 0%), var(--border) 100%) !important; }
/* v1.0.746 — row 8's first chip: "בחירת קבוצות" (opens the archive groups ticks list) */
.row8-item.row8-pick .row8-item-name { color: #38bdf8; font-weight: 700; }
/* v1.0.747 — list modes: the "מצב" line; the name editor (TV: a keyboard at the bottom; site: a text box) */
.list-mode-row b { display: flex; align-items: center; gap: 12px; color: #38bdf8; }
.name-ed { position: fixed; left: 0; right: 0; bottom: 0; display: none; justify-content: center; z-index: 9500; padding-bottom: 30px; }
.name-ed.show { display: flex; }
.name-ed-box { background: rgba(17, 18, 24, 0.97); border: 2px solid #334155; border-radius: 18px; padding: 22px 30px; display: flex; flex-direction: column; gap: 14px; min-width: 900px; box-shadow: 0 10px 40px rgba(0, 0, 0, 0.6); }
.name-ed-title { font-size: 1.3rem; color: var(--muted); }
.name-ed-line { min-height: 56px; display: flex; align-items: center; justify-content: center; font-size: 2rem; font-weight: 700; background: var(--card2); border-radius: 12px; padding: 0 20px; }
.name-ed-kb { direction: ltr; display: flex; flex-direction: column; gap: 10px; }
.name-ed-kb .search-kb-key { height: 58px; font-size: 1.5rem; }
.name-ed-hint { text-align: center; color: var(--muted); font-size: 0.95rem; }
.name-ed-input { width: 100%; font-size: 1.8rem; font-weight: 700; background: transparent; border: none; outline: none; color: #fff; text-align: center; }
/* v1.0.747 — the archive's groups icon in the menu column: hidden (its work is in the horizontal menu now);
   Settings > תפריט אנכי shows it */
body.home-mode-archive.rail-arc-groups-off .icon-rail-item[data-rail-item="groups"] { display: none; }
.list-mode-row .arc-pick-arr { display: inline-block; width: 0; height: 0; border-top: 8px solid transparent; border-bottom: 8px solid transparent; }
.list-mode-row .arc-pick-arr.a1 { border-left: 11px solid #38bdf8; }
.list-mode-row .arc-pick-arr.a2 { border-right: 11px solid #38bdf8; }
/* v1.0.749 — row titles get smaller (and thinner) as more rows show: 1 = as before, 1.5, 2, 2.5 (tests switch rowTitleScaleOff) */
body.rt-scale:not(.home-rows-1) .row-title { font-size: 1.8rem; font-weight: 600; }
body.rt-scale.home-rows-15 .row-title { font-size: 1.95rem; font-weight: 650; }
body.rt-scale.arc-rows-25 .row-title { font-size: 1.6rem; font-weight: 500; }
/* v1.0.749 — the archive icon and the title logos get smaller with the title (they were fixed sizes) */
body.rt-scale .row-title.row-title-archive svg { width: 1em; height: 1em; }
body.rt-scale .row-title-logo img,
body.rt-scale .row-title.row-title-archive img.row-title-archive-logo { height: 1.238em; }
/* v1.0.749 — what sits inside the row title (the station name / the poster info continuation) shrinks with it too */
body.rt-scale .row-title .row-title-station,
body.rt-scale .row-title .row-title-info { font-size: 0.81em; }
/* v1.0.749 — title -> tiles gap = the description's line gap (set by applyRowTitleGap; tests switch rtGapOff) */
body.rt-gap:not(.home-rows-1) .row-title { margin-bottom: var(--rt-gap, 6px); } /* one row keeps its tuned 6px */
/* v1.0.750 — the gap between rows gets smaller with the titles (the same ratio: 1 = 38px as before, 1.5 = 35, 2 = 33,
   2.5 = 29 — row margin + the tiles' 8px padding). Tests switch rtRowGapOff */
body.rt-rowgap:not(.home-rows-1) .content-row { margin-bottom: 25px; }
body.rt-rowgap.home-rows-15 .content-row { margin-bottom: 27px; }
body.rt-rowgap.arc-rows-25 .content-row { margin-bottom: 21px; }
/* v1.0.753 — the menu column by measured gaps (see measureRailHug in app.js; tests switch railHugOff) */
body.rail-hug .icon-rail,
body.rail-hug .hero-info,
body.rail-hug .content-rows,
body.rail-hug .vod-home-bar { transition: transform 0.3s ease; }
body.rail-hug .icon-rail { transform: translateX(var(--hug-s0, 0px)); }
body.rail-hug.rail-expanded .icon-rail { transform: translateX(var(--hug-s1, 0px)); }
body.rail-hug.rail-expanded .hero-info,
body.rail-hug.rail-expanded .content-rows,
body.rail-hug.rail-expanded .vod-home-bar { transform: translateX(var(--hug-c1, 0px)); }
body.rail-hug.row1-arrow-rail.rail-expanded .content-rows { transform: translateX(var(--hug-c1, 0px)) translateY(var(--row1-dy, 0px)); }
/* v1.0.831 - the bottom info bar moves aside with the info / the rows when the vertical menu opens (it used to stay).
   Tests switch bbarRailOff (app.js: applyRailHugVars sets body.bbar-rail). */
body.bbar-rail .bottom-info-bar { transition: transform 0.3s ease; }
body.bbar-rail.rail-edge.rail-expanded .bottom-info-bar { transform: translateX(var(--rail-cx, -78px)); }
body.bbar-rail.rail-hug.rail-expanded .bottom-info-bar { transform: translateX(var(--hug-c1, 0px)); }
/* v1.0.832 - "the vertical menu hidden" (app.js: railHideApply). While the focus is not in the menu it is not drawn, and
   the info / the horizontal menu / the rows / the arrow / the bottom bar are pulled over its place. */
body.rail-hide-on .icon-rail { transition: transform 0.3s ease, opacity 0.25s ease; }
body.rail-hidden .icon-rail { opacity: 0; pointer-events: none; }
body.rail-hidden .main-column { overflow: visible; } /* (it cuts at the menu's edge; the screen itself still cuts) */
body.rail-hide-on .hero-info,
body.rail-hide-on .content-rows,
body.rail-hide-on .vod-home-bar,
body.rail-hide-on .bottom-info-bar { transition: transform 0.3s ease; }
body.rail-hidden .hero-info,
body.rail-hidden .content-rows { transform: translateX(var(--rail-hide-x, 46px)) translateY(var(--rail-hide-y, 0px)); }
body.rail-hidden .vod-home-bar { transform: translateX(var(--rail-hide-x, 46px)); } /* (v1.0.834: the page's own up / down reaches the bar through its placing from the rows - app.js placeVodBar) */
body.rail-hidden.row1-arrow-rail .content-rows { transform: translateX(var(--rail-hide-x, 46px)) translateY(calc(var(--row1-dy, 0px) + var(--rail-hide-y, 0px))); }
body.rail-hide-on .rows-more-indicator { transition: margin 0.3s ease; }
body.rail-hidden .rows-more-indicator { margin-top: var(--rail-hide-y, 0px); }
body.rail-hidden .rows-more-indicator { margin-right: calc(-1 * var(--rail-hide-x, 46px)); }
body.rail-hide-on.rail-hide-noarrow #rowsMoreIndicator.rows-more-indicator { display: none !important; } /* v1.0.833: by id - "arrow-keep" has a stronger display rule */
/* v1.0.833 - the rows' box keeps its far edge where it was (it only grows toward the hidden menu): no empty strip at the
   far end, more of the last tile shows */
body.rail-hidden .content-rows { margin-left: calc(-1 * var(--rail-hide-x, 46px) - 120px); padding-left: calc(var(--rail-hide-pad0, 32px) + var(--rail-hide-x, 46px) + 120px); } /* (the padding takes the added width: the tiles keep their exact size) */
/* v1.0.836 - the strip of tiles itself is what cuts them: it reaches the rows box's far edge again (same trick, sizes unchanged) */
body.rail-hidden #contentRows .row-channels { margin-left: calc(var(--rail-hide-rcm0, -32px) - var(--rail-hide-x, 46px) - 120px); padding-left: calc(var(--rail-hide-rcp0, 0px) + var(--rail-hide-x, 46px) + 120px); }
/* v1.0.841 - reported from the TV: ~30px were still left dark at the far edge. Both boxes now reach 120px PAST where they
   used to end (same trick: margin out, padding in, sizes unchanged), so only the screen's own edge cuts the last tile. */
/* v1.0.753 — archive with 2.5 rows: the rows a bit closer (gap 29 -> 21px) so more of the third row shows
   (tests switch arc25TightOff) */
body.rt-rowgap.home-mode-archive.arc-rows-25:not(.arc25-tight-off) .content-row { margin-bottom: 13px; }
/* v1.0.755 — the focused menu button (ת.א) smaller: 116 -> 100px (icon 50 -> 44px), the glow 4 -> 3px — it looked too
   big. The squares (live / archive / add-on) are not changed. Tests switch railFocusSmallOff */
body.rail-focus-sm.rail-v2 .icon-rail-item.focused { width: 100px; height: 100px; margin: -13px; box-shadow: 0 0 0 3px var(--ffg); }
body.rail-focus-sm.rail-v2:not(.rail-edge) .icon-rail-settings.focused { margin: -12px; }
body.rail-focus-sm.rail-v2 .icon-rail-item.focused svg { width: 44px; height: 44px; }
body.rail-focus-sm.rail-v2 #railSettings.focused { margin: 7px -13px -13px; }
body.rail-focus-sm.rail-v2:not(.rail-edge) #railSettings.focused { margin: 8px -12px -12px; }
body.rail-focus-sm.rail-v2.lib-icon #railSettings.focused { margin: -7px -13px -13px; }
body.rail-focus-sm.rail-v2.lib-icon:not(.rail-edge) #railSettings.focused { margin: -6px -12px -12px; }
body.rail-focus-sm.rail-v2.rail-frames.rail-big.rail-expanded .icon-rail-item.focused { margin: calc((var(--rail-btn-h, 100px) - 100px) / 2) 8px; }
body.rail-focus-sm.rail-v2.rail-frames.rail-big.rail-expanded #railSettings.focused { margin: calc(var(--rail-btn-gap, 12px) + (var(--rail-btn-h, 100px) - 100px) / 2) 8px calc((var(--rail-btn-h, 100px) - 100px) / 2); }
/* v1.0.756 — the three squares (live / archive / add-on) in the same frame size as the other buttons (116 -> 100px,
   icon 44, glow 3px); their group keeps its bottom edge (the gap to the button under them stays), so it starts 48px
   lower. Tests switch railSquaresSmallOff */
body.rail-sq-sm .rail-mode-box.open .rail-mode-btn { width: 100px; height: 100px; transform: translate(-50%, 48px); }
body.rail-sq-sm.rail-mode-up .rail-mode-box.open .rail-mode-btn { transform: translate(-50%, 23px); }
body.rail-sq-sm .rail-mode-box.open .rail-mode-btn svg { width: 44px; height: 44px; }
body.rail-sq-sm .rail-mode-box.open .rail-mode-other { top: 112px; }
body.rail-sq-sm.rail-v2 .rail-mode-box.open .rail-mode-addon { top: 224px; }
body.rail-sq-sm .rail-mode-box.open .rail-mode-btn.focused { box-shadow: 0 0 0 3px var(--ffg); }
/* v1.0.758 — an upcoming programme saved for later: a live countdown (and the reminder bell) on its tile / row */
.row-channel .cw-countdown { position: absolute; top: 8px; left: 10px; z-index: 3; font-size: 0.95rem; font-weight: 700; color: #38bdf8; background: rgba(0, 0, 0, 0.65); padding: 2px 8px; border-radius: 8px; unicode-bidi: isolate; }
.epg-row-meta .cw-countdown { margin-left: 12px; font-weight: 700; color: #38bdf8; unicode-bidi: isolate; }
.row-channel .future-bell { position: absolute; top: 8px; right: 10px; width: 26px; height: 26px; color: #fbbf24; z-index: 3; }

/* v1.0.778 — the add-on's rows rebuilt: the frame waits hidden until the tile is still; the rows fade in (rowsFadeOff = none) */
.focus-frame.settle-hide { opacity: 0 !important; transition: none !important; }
#contentRows.rows-settle .content-row { opacity: 0; transition: none; }
#contentRows.rows-settle-in .content-row { opacity: 1; transition: opacity 0.2s ease-out; }
.focus-frame.settle-in { transition: opacity 0.2s ease-out !important; }

/* ===== v1.0.779 — "הרחבת אריח": the focused tile widens to 3 tiles (others pushed, 0.3s); left 2/3 = a rounded window
   (trailer / the title's start / fanart, filling it without stretching — the sides are cut), right 1/3 = its menu ===== */
.row-channel.tile-exp-anim { transition: flex-basis 0.3s ease, width 0.3s ease, min-width 0.3s ease, max-width 0.3s ease !important; }
.row-channel.tile-exp { flex: 0 0 var(--tile-exp-w) !important; width: var(--tile-exp-w) !important; min-width: var(--tile-exp-w) !important; max-width: none !important; }
.row-channel.tile-exp-now { transition: none !important; }
.row-channel.tile-exp > *:not(.tile-exp-box) { visibility: hidden; }
.tile-exp-box { position: absolute; inset: 0; display: flex; flex-direction: row; direction: ltr; gap: 10px; padding: 8px; box-sizing: border-box; border-radius: inherit; overflow: hidden; background: #0b0f19; z-index: 4; }
.tile-exp-media { flex: 0 0 calc(66.666% - 5px); height: 100%; position: relative; overflow: hidden; border-radius: inherit; background: #000 center / cover no-repeat; }
.tile-exp-media iframe, .tile-exp-media video { position: absolute; top: 0; height: 100%; border: 0; pointer-events: none; }
.tile-exp-media video { left: 0; width: 100%; object-fit: cover; }
.tile-exp-menu { flex: 1 1 auto; min-width: 0; direction: rtl; overflow: hidden; display: flex; flex-direction: column; gap: 4px; font-size: 1rem; color: #e2e8f0; }
.tile-exp-title { font-weight: 700; font-size: 1.05rem; margin-bottom: 6px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tile-exp-item { display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: 8px; opacity: 0.8; border: 2px solid transparent; white-space: nowrap; }
.tile-exp-item .vod-pick-icon { display: inline-flex; width: 22px; height: 22px; flex: 0 0 22px; }
.tile-exp-item .vod-pick-icon svg { width: 22px; height: 22px; }
.tile-exp-item .vod-pick-text b { font-weight: 600; font-size: 0.95rem; }
.tile-exp-menu.active .tile-exp-item.focused { opacity: 1; background: rgba(56, 189, 248, 0.18); border-color: #38bdf8; }
body.tile-exp-hidefan #previewFanart { opacity: 0 !important; }
/* v1.0.780 — הרחבת אריח: hidden preload, the poster layout (2 tiles), lines filling the height, the title in the corner.
   The box is RTL with row-reverse: Hebrew = media left / menu right; the English sheet turns it LTR = mirrored. */
.tile-exp-box { direction: rtl; flex-direction: row-reverse; }
.tile-exp-box.tile-exp-pre { opacity: 0; pointer-events: none; }
.tile-exp-box.poster { padding: 0; gap: 0; }
.tile-exp-box.poster .tile-exp-media { flex: 0 0 var(--tile-exp-pw); background: transparent; border-radius: inherit; }
.tile-exp-box.poster .tile-exp-menu { padding: 8px 10px; }
.row-channel.tile-exp-poster { position: absolute; inset: 0; width: 100% !important; height: 100% !important; min-width: 0 !important; max-width: none !important; margin: 0 !important; transform: none !important; visibility: visible !important; }
.row-channel.tile-exp-poster * { visibility: visible !important; }
.tile-exp-menu .tile-exp-items { display: flex; flex-direction: column; gap: 4px; }
.tile-exp-box.fill .tile-exp-menu .tile-exp-items { flex: 1 1 auto; min-height: 0; }
.tile-exp-box.fill .tile-exp-item { flex: 1 1 0; min-height: 0; display: flex; align-items: center; font-size: var(--te-fs, 1rem); }
.tile-exp-box.fill .tile-exp-item .vod-pick-icon, .tile-exp-box.fill .tile-exp-item svg { width: 1.2em; height: 1.2em; flex: 0 0 auto; }
.tile-exp-box.tcorner .tile-exp-title { text-align: right; flex: 0 0 auto; margin: 0 0 4px; }
.tile-exp-box.tcorner .tile-exp-menu { text-align: right; }
.tile-exp-box.tcorner.fill .tile-exp-title { font-size: calc(var(--te-fs, 1rem) * 1.05); }
.tile-exp-box.fill .tile-exp-item .vod-pick-icon { flex: 0 0 1.2em; }
.tile-exp-box.fill .tile-exp-item .vod-pick-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.tile-exp-box.fill .tile-exp-item .vod-pick-text b { font-size: inherit; }
.tile-exp-vtitle { position: absolute; top: 10px; right: 14px; max-width: 80%; z-index: 2; color: #fff; font-weight: 700; font-size: calc(var(--te-fs, 1rem) * 1.1); text-align: right; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-shadow: 0 1px 3px rgba(0,0,0,0.9), 0 0 8px rgba(0,0,0,0.6); pointer-events: none; }
.tile-exp-box:not(.fill) .tile-exp-vtitle { font-size: 1.15rem; }
/* v1.0.781 — the title over the trailer: 75% of the menu lines (tests switch tileExpVtSmallOff = as 1.0.780) */
.tile-exp-box.vtsmall .tile-exp-vtitle { font-size: calc(var(--te-fs, 1rem) * 0.75); }
.tile-exp-box.vtsmall:not(.fill) .tile-exp-vtitle { font-size: 0.85rem; }
/* v1.0.782 — the hero's pause / play button (Up from the horizontal menu): the same round sign as the site's mouse one */
#heroPauseBtn { position: absolute; width: 120px; height: 120px; margin: -60px 0 0 -60px; border-radius: 50%; border: 4px solid #fff; box-sizing: border-box; background: rgba(0, 0, 0, 0.5); z-index: 99997; display: none; align-items: center; justify-content: center; pointer-events: none; box-shadow: 0 0 0 4px #38bdf8, 0 0 24px rgba(0, 0, 0, 0.5); }
#heroPauseBtn.show { display: flex; }
#heroPauseBtn svg { width: 54px; height: 54px; }
body.hero-pause-focus #webPauseSign { display: none !important; }
/* v1.0.783 — the video waits (loaded, stopped, invisible) behind the poster until the tile widens */
.tile-exp-media .te-wait { opacity: 0; }
.tile-exp-vtitle { transition: top 0.4s ease; }
/* v1.0.783 — two steps, smooth: the poster stays its own size, anchored by the menu, and fades out while the video
   (anchored the same way, already at its final size) fades in; the window / padding widen together with the tile. */
.tile-exp-box.two { transition: padding 0.3s ease, gap 0.3s ease; }
.tile-exp-box.two .tile-exp-media { transition: flex-basis 0.3s ease; }
.tile-exp-box.two.poster .tile-exp-menu { padding: 8px; }
.tile-exp-box.two .row-channel.tile-exp-poster { top: 0; bottom: 0; right: 0; left: auto; width: var(--tile-exp-pw) !important; transition: opacity 0.45s ease; }
.tile-exp-box.two .row-channel.tile-exp-poster.te-out { opacity: 0; }
.tile-exp-box.two .tile-exp-media iframe, .tile-exp-box.two .tile-exp-media video { transition: opacity 0.45s ease; }
/* v1.0.783 — "מסך מלא" on the tile's trailer: the same player grows over the whole screen (no reload, same point) */
body.tile-exp-fs .content-rows, body.tile-exp-fs-out .content-rows { transform: none !important; transition: none !important; }
body.tile-exp-fs .content-rows { z-index: 2147482000 !important; position: relative; }
body.tile-exp-fs .row-channel.tile-exp, body.tile-exp-fs .tile-exp-box, body.tile-exp-fs .tile-exp-media { z-index: 2147482000 !important; overflow: visible !important; clip-path: none !important; -webkit-mask-image: none !important; mask-image: none !important; }
body.tile-exp-fs .tile-exp-media iframe.te-fs { position: fixed !important; left: 0 !important; right: auto !important; top: 0 !important; width: 1920px !important; height: 1080px !important; z-index: 2147483000 !important; opacity: 1 !important; border-radius: 0 !important; transition: none !important; }
body.tile-exp-fs #focusFrame, body.tile-exp-fs .tile-exp-vtitle { display: none !important; }
/* v1.0.784 — "גודל התצוגה בצד שמאל": the hero's picture layers grow together (one factor, no stretching), anchored at the
   screen's top-left (top-right in English — zoom scales their offsets too). Only with body.hero-grow (size > 0). */
body.hero-grow .hero-media, body.hero-grow .hero-corner-blend, body.hero-grow .hero-fanart, body.hero-grow .hero-corner-blend-dup,
body.hero-grow .hero-fullscreen-gradient, body.hero-grow #heroTrailer { zoom: var(--hero-zoom, 1); }
.hero-grow-bar { display: inline-flex; align-items: center; gap: 4px; direction: ltr; }
.hero-grow-bar i { display: inline-block; width: 14px; height: 18px; border-radius: 3px; background: rgba(255, 255, 255, 0.18); }
.hero-grow-bar i.on { background: #facc15; }
.hero-grow-px { margin-left: 8px; font-size: 0.85em; opacity: 0.8; min-width: 3.2em; }
/* v1.0.784 — enlarged: the layers bottom-up = TV · fanart · #32 · the tiles' rows (the rows above all the picture) */
body.hero-grow #contentRows { z-index: 7; }
/* v1.0.784 — #32's bigger copy (the one the add-on keeps, since its fanart stays) stays also over the live picture
   (tests switch dupKeepOff = it goes away with the fanart, as before) */
body.dup-keep .hero-corner-blend-dup { opacity: 1 !important; }
/* v1.0.785 — "עונה X · פרק Y" on the episode's picture (the expanded tile, series) — like the title over a trailer */
.tile-exp-eplabel { position: absolute; top: 10px; right: 14px; max-width: 80%; z-index: 2; color: #fff; font-weight: 700; font-size: calc(var(--te-fs, 1rem) * 0.85); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-shadow: 0 1px 3px rgba(0,0,0,0.9), 0 0 8px rgba(0,0,0,0.6); pointer-events: none; }
/* v1.0.786 — the hero's trailer whole: no stretch, nothing cut (YouTube's title and round icon stay) — tests switch
   heroTrailerWholeOff = moved up 40px and stretched x1.18 as in 1.0.637 */
body.hero-trailer-whole .hero-trailer iframe { transform: none !important; }
/* v1.0.786 — the hero's trailer full screen (the same player: no reload, same point) */
body.hero-trailer-fs #heroTrailer { top: 0 !important; left: 0 !important; right: auto !important; width: 1920px !important; height: 1080px !important; zoom: 1 !important; z-index: 2147483000 !important; }
body.hero-trailer-fs #heroTrailer iframe { transform: none !important; }
/* v1.0.786 — two round buttons over the hero (pause / full screen): the ring on the chosen one */
#heroFullBtn { position: absolute; width: 120px; height: 120px; margin: -60px 0 0 -60px; border-radius: 50%; border: 4px solid #fff; box-sizing: border-box; background: rgba(0, 0, 0, 0.5); z-index: 99997; display: none; align-items: center; justify-content: center; pointer-events: none; box-shadow: 0 0 24px rgba(0, 0, 0, 0.5); }
#heroFullBtn.show { display: flex; }
#heroFullBtn svg { width: 54px; height: 54px; }
#heroPauseBtn:not(.sel) { box-shadow: 0 0 24px rgba(0, 0, 0, 0.5); }
#heroFullBtn.sel { box-shadow: 0 0 0 4px #38bdf8, 0 0 24px rgba(0, 0, 0, 0.5); }
/* v1.0.786 — the round buttons grow with the picture ("גודל התצוגה בצד שמאל"), same factor, around their own centre */
body.hero-grow #heroPauseBtn, body.hero-grow #heroFullBtn { transform: scale(var(--hero-zoom, 1)); }
/* v1.0.787 — כ.צ: the title on its own line, the 12 cells across the whole line under it */
.rail-flyout-item.hero-grow-line { display: block; }
.hero-grow-line .hero-grow-title { margin-bottom: 8px; }
.hero-grow-line .hero-grow-bar { display: flex; align-items: center; gap: 5px; width: 100%; }
.hero-grow-line .hero-grow-bar i { flex: 1 1 0; width: auto; height: 20px; }
.hero-grow-line .hero-grow-px { flex: 0 0 auto; margin-left: 10px; }

/* v1.0.793 — live / archive: the channel name is always in the row under the logo; the row stays ONE line inside the
   block — a name too long for what is left ends with "…" (same as the add-on mode's row). Tests switch heroChAlwaysOff. */
body.hero-ch-always:not(.home-mode-vod) .hero-movie-meta-row.show { flex-wrap: nowrap; min-width: 0; overflow: hidden; }
body.hero-ch-always:not(.home-mode-vod) .hero-movie-meta-row > * { flex-shrink: 0; white-space: nowrap; }
body.hero-ch-always:not(.home-mode-vod) .hero-movie-meta-row > .hero-movie-channel { flex-shrink: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
/* v1.0.793 — "כוונון תצוגה": its own window at the bottom of the screen (the list that opened it hides meanwhile).
   The squares follow the page's direction: filled from the right in Hebrew / Arabic, from the left in English. */
.tune-win { position: fixed; left: 50%; bottom: 20px; transform: translateX(-50%); display: none; z-index: 70; width: 940px; box-sizing: border-box; background: var(--card); border: 2px solid var(--border); border-radius: 16px; padding: 12px 18px; }
.tune-win.show { display: block; }
.tune-win-title { font-size: 1.15rem; color: var(--muted); font-weight: 600; margin-bottom: 8px; }
.tune-win-list { display: flex; flex-direction: column; gap: 4px; }
.tune-row { display: flex; align-items: center; gap: 14px; background: var(--card2); border: 2px solid transparent; border-radius: 8px; padding: 3px 12px; font-size: 1.05rem; color: var(--text); }
.tune-row.focused { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(123, 92, 255, 0.25); }
.tune-label { flex: 0 0 330px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tune-bar { flex: 1 1 auto; display: flex; align-items: center; gap: 4px; }
.tune-bar i { flex: 1 1 0; height: 18px; border-radius: 3px; background: rgba(255, 255, 255, 0.18); }
.tune-bar i.on { background: #facc15; }
.tune-bar i.neg { background: #38bdf8; }
.tune-px { flex: 0 0 130px; color: #38bdf8; white-space: nowrap; text-align: center; }
.tune-win-hint { margin-top: 8px; color: var(--muted); font-size: 0.95rem; text-align: center; }
body.tune-open .vod-opts { visibility: hidden; }

/* v1.0.796 — the small window of "אופן הרחבת האריח" in the live / archive lists (same look as the tuning window) */
.tune-win.te-win { width: 620px; }
.te-win .tune-row { padding: 8px 14px; font-size: 1.1rem; }
.te-win .tune-label { flex: 0 0 200px; }
.te-val { flex: 1 1 auto; text-align: end; color: #38bdf8; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* v1.0.797 — the description panel in an open live / archive tile (menu | description | the tile itself) */
.te-descp { position: absolute; top: 0; bottom: 0; box-sizing: border-box; background: #000; padding: 10px 14px; display: flex; flex-direction: column; gap: 6px; color: #e2e8f0; opacity: 0; transition: opacity 0.35s ease; overflow: hidden; text-align: start; }
.te-descp.show { opacity: 1; }
.te-descp-title { flex: 0 0 auto; font-weight: 700; font-size: 1.3rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.te-descp-time { flex: 0 0 auto; color: #38bdf8; font-size: 1.05rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.te-descp-text { flex: 1 1 auto; min-height: 0; overflow: hidden; font-size: 1.1rem; line-height: 1.4; color: #cbd5e1; display: -webkit-box; -webkit-box-orient: vertical; }
.te-descp-prog { flex: 0 0 6px; border-radius: 3px; background: rgba(255, 255, 255, 0.18); overflow: hidden; }
.te-descp-prog i { display: block; height: 100%; background: #38bdf8; }
.tile-exp-box.te-desc .tile-exp-media { background: #000; }

/* v1.0.798 / v1.0.799 — "תיאור תוכנית באריחים בלי תמונה": a live tile with no poster, not in focus, one-row layout.
   Top line: the channel's name between the number and the heart (same size as always). A line. Then ONE line: a cyan
   star + the programme's name on the start side, its hours on the other side. Then the description (justified, bigger),
   down to the bottom. On the tile's own gradient; the tile keeps its width. No logo — unless Settings › "הראה לוגו בשורה
   אחת שלא בפוקוס" (body.td-logo): then a small logo on top and the name under it.
   Sizes: plain values (the builder adds to them) + var(--td-*-d) from "כוונון תצוגה". Tests switch tileDescOff. */
.row-channel .td { display: none; }
body.tile-desc-on.home-rows-1 .row-channel.td-able:not(.has-poster):not(.focused):not(.tile-exp):not(.tile-exp-poster) { padding-top: calc(6px + var(--td-y-d, 0px)); }
body.tile-desc-on.home-rows-1 .row-channel.td-able:not(.has-poster):not(.focused):not(.tile-exp):not(.tile-exp-poster) > img,
body.tile-desc-on.home-rows-1 .row-channel.td-able:not(.has-poster):not(.focused):not(.tile-exp):not(.tile-exp-poster) > div:not([class]) { display: none !important; }
body.td-logo.tile-desc-on.home-rows-1 .row-channel.td-able:not(.has-poster):not(.focused):not(.tile-exp):not(.tile-exp-poster) > img { display: block !important; flex: 0 0 auto !important; align-self: center; width: calc(64px + var(--td-logo-d, 0px)) !important; height: calc(64px + var(--td-logo-d, 0px)) !important; margin: 20px 0 4px !important; padding: 0 !important; }
body.tile-desc-on.home-rows-1 .row-channel.td-able:not(.has-poster):not(.focused):not(.tile-exp):not(.tile-exp-poster) .name { font-size: calc(1.15rem + var(--td-name-d, 0px)); line-height: 28px; padding: 0 36px; }
body.td-logo.tile-desc-on.home-rows-1 .row-channel.td-able:not(.has-poster):not(.focused):not(.tile-exp):not(.tile-exp-poster) .name { padding: 0 6px; }
body.tile-desc-on.home-rows-1 .row-channel.td-able:not(.has-poster):not(.focused):not(.tile-exp):not(.tile-exp-poster) .now { display: none; }
body.tile-desc-on.home-rows-1 .row-channel.td-able:not(.has-poster):not(.focused):not(.tile-exp):not(.tile-exp-poster) .td { display: block; flex: 0 0 auto; }
body.tile-desc-on.home-rows-1 .row-channel.td-able:not(.has-poster):not(.focused):not(.tile-exp):not(.tile-exp-poster) .td-line { height: 2px; width: 70%; align-self: center; margin: 8px 0 9px; background: rgba(56, 189, 248, 0.55); border-radius: 1px; }
body.tile-desc-on.home-rows-1 .row-channel.td-able:not(.has-poster):not(.focused):not(.tile-exp):not(.tile-exp-poster) .td-row { display: flex; align-items: baseline; justify-content: space-between; padding: 0 14px; }
body.tile-desc-on.home-rows-1 .row-channel.td-able:not(.has-poster):not(.focused):not(.tile-exp):not(.tile-exp-poster) .td-title { flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-align: start; font-weight: 700; font-size: calc(1.25rem + var(--td-title-d, 0px)); }
body.tile-desc-on.home-rows-1 .row-channel.td-able:not(.has-poster):not(.focused):not(.tile-exp):not(.tile-exp-poster) .td-time { flex: 0 0 auto; white-space: nowrap; color: var(--muted); font-size: calc(0.85rem + var(--td-time-d, 0px)); }
body.tile-desc-on.home-rows-1 .row-channel.td-able:not(.has-poster):not(.focused):not(.tile-exp):not(.tile-exp-poster) .td-text { display: -webkit-box; -webkit-box-orient: vertical; flex: 1 1 0; min-height: 0; overflow: hidden; margin-top: 8px; padding: 0 12px; font-size: calc(1.15rem + var(--td-text-d, 0px)); line-height: 1.38; color: #cbd5e1; text-align: justify; text-align-last: center; white-space: normal; }
.td-star { color: #38bdf8; }
/* v1.0.799 — "כוונון תצוגה": tabs by subject, side by side, on the title line */
.tune-win-title { display: flex; align-items: center; justify-content: space-between; }
.tune-tabs { display: inline-flex; }
.tune-tab { margin: 0 4px; padding: 4px 14px; border-radius: 8px; border: 2px solid transparent; background: var(--card2); color: var(--muted); font-size: 1.05rem; font-weight: 600; white-space: nowrap; }
.tune-tab.sel { color: #38bdf8; }
.tune-tab.focused { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(123, 92, 255, 0.25); color: var(--text); }
.tune-win-list { min-height: 250px; }
.tune-win .tune-label { flex: 0 0 400px; }
.tune-win { width: 1040px; }
.tune-win.tune-top { bottom: auto; top: 20px; } /* the tiles tab: up, so the tiles being tuned stay visible */

/* ===== v1.0.801 — one edge for the info block (tests switch heroEdgeOff) =====
   The styled logo's picture has no transparent side strip any more (the server trims it to the letters / their glow), so the
   fixed -25px that used to take that strip back is off for it; "שודר …" has no start gap when it opens its row. */
body.hero-edge .hero-now-logo-auto[src*="-sty"] { margin-left: 0; margin-right: 0; }
body.hero-edge .hero-movie-aired.aired-first:not(:empty) { margin-inline-start: 0; }
/* v1.0.801 — "גודל התצוגה בצד שמאל": the "+N" on the title's line, 16 squares across the whole line (tests switch heroGrow16Off) */
.hero-grow-16 .hero-grow-title { display: flex; align-items: baseline; justify-content: space-between; }
.hero-grow-16 .hero-grow-title .hero-grow-px { margin: 0; min-width: 0; }
.hero-grow-16 .hero-grow-bar { gap: 4px; }
/* v1.0.803 — the tuning window's title (page + layout) on one line beside the five tabs */
.tune-win-name { white-space: nowrap; font-size: 0.92em; }
.tune-tab { padding: 4px 10px; margin: 0 3px; font-size: 1rem; }
/* v1.0.808 — the hero trailer loading behind the fanart (not seen yet), then the fanart's small fade over the moving picture */
.hero-trailer.pre { display: block; opacity: 0; }
.hero-fanart.tr-fade { transition: opacity 0.6s ease !important; }
/* v1.0.808 — "פס המידע התחתון" (placed and sized in app.js: bottomBarPlace). The outer box clips; the inner one is skewed 45°
   so its side runs along the small arrow, with a tile's rounded corner on top. Colour / corner: plain values for the builder. */
.bottom-info-bar { position: fixed; display: none; overflow: hidden; z-index: 1; pointer-events: none; }
.bottom-info-bar > i { position: absolute; top: 0; height: 100%; display: block; background: #38bdf8; }
.tune-win { width: 1280px; }
.tune-bar i.col { border: 2px solid transparent; box-sizing: border-box; height: 22px; }
.tune-bar i.col.sel { border-color: #fff; box-shadow: 0 0 0 2px #7b5cff; }
/* v1.0.809 — the small arrow kept in the one-row look (set by the arrow's watcher once it had to bring it back) */
body.arrow-keep.rows-cut:not(.row1-arrow-rail) .rows-more-indicator.show { display: block !important; }
.bottom-info-bar { z-index: 3; }
.rows-more-indicator { z-index: 5; }
/* v1.0.810 — the info block's lines coming in one after the other (app.js: infoRevealRun sets each line's delay) */
@keyframes infoIn { from { opacity: 0; top: var(--info-in-rise, 0px); } to { opacity: 1; top: 0; } }
.info-in { position: relative; animation: infoIn var(--info-in-ms, 250ms) ease-out backwards; }
.bottom-info-bar > svg { display: block; }
@keyframes teMenuIn { from { opacity: 0; } to { opacity: 1; } }
.tune-win { width: 1800px; }
.tune-win .tune-label { flex: 0 0 520px; }
/* v1.0.819 — rule 13: the play triangle points LEFT in Hebrew / Arabic and RIGHT in English, everywhere.
   .play-ico = drawn pointing right, .play-ico-l = drawn pointing left. */
.play-ico, .play-ico-l { transform-box: fill-box; transform-origin: 50% 50%; }
html:not([dir="ltr"]) .play-ico { transform: scaleX(-1); }
html[dir="ltr"] .play-ico-l { transform: scaleX(-1); }
span.play-ico { display: inline-block; }
/* v1.0.819 — a locked tab of the tuning window: its lines are dimmed (only the lock's own line stays lit) */
.tune-win.tune-locked .tune-win-list .tune-row:not(:first-child) { opacity: 0.4; }
/* v1.0.819 — the name of the station that plays, on the video's corner ("התחנה נשארת") */
.stay-badge { position: fixed; display: none; z-index: 6; pointer-events: none; padding: 5px 14px; border-radius: 10px; background: rgba(8, 12, 22, 0.72); color: #fff; font-size: var(--stay-badge-fs, 1.15rem); font-weight: 600; white-space: nowrap; }
.stay-badge.on-bar { background: transparent; color: #06121f; padding: 0; border-radius: 0; font-size: calc(var(--stay-badge-fs, 1.15rem) * 0.88); font-weight: 700; }
.row-title .row-title-playing { font-size: 1.7rem; font-weight: 700; line-height: 1; white-space: nowrap; color: #d6c7a1; }
/* v1.0.820 — the tuning window: one place on every tab, 20px higher than before (its own line moves it: app.js tuneWinPlace) */
.tune-win { bottom: 40px; }

/* v1.0.822 — the measuring line of the tuning window (first tile's edge, up to the top of the screen) */
.tune-guide { position: fixed; top: 0; width: calc(var(--te-vline, 2px) + var(--te-vline-d, 0px)); background: #38bdf8; z-index: 69; pointer-events: none; display: none; }
/* v1.0.822 — archive, one-row look: the rows stay unseen until their pictures are in (the "loading" message is up meanwhile) */
body.arc-pics-wait #contentRows, body.arc-pics-wait #rowsMoreIndicator, body.arc-pics-wait #bottomInfoBar, body.arc-pics-wait .focus-frame, body.arc-pics-wait #rowFlashIndicator { visibility: hidden !important; }

/* v1.0.823 — the tuning window: more tabs than one line holds -> they wrap to a second line (and the English sheet gets the same width as the Hebrew one) */
.tune-win { width: 1800px; }
.tune-win .tune-label { flex: 0 0 520px; }
.tune-win-title { flex-wrap: nowrap; }
.tune-tabs { flex: 1 1 auto; min-width: 0; overflow: hidden; flex-wrap: nowrap; margin: 0 6px; padding: 4px 0; }
.tune-tab { flex: 0 0 auto; }
.tune-more { flex: 0 0 auto; width: 16px; text-align: center; color: #38bdf8; font-size: 1.5rem; font-weight: 700; line-height: 1; visibility: hidden; }
.tune-rail-guide { position: fixed; left: 0; top: 0; width: 0; height: 0; z-index: 69; pointer-events: none; display: none; }
.tune-rail-guide > i { position: fixed; display: block; }
.tune-rail-guide > i.ln { background: #38bdf8; }

/* v1.0.826 — the vertical menu's icons keep their place (the alignment to the row's title) also while the menu is open:
   they no longer go up / down on entering and leaving it. Tests switch railDyFixedOff. */
body.rail-dy-fixed.row1-arrow-rail.rail-expanded .icon-rail-item,
body.rail-dy-fixed.row1-arrow-bottom.rail-expanded .icon-rail-item { transform: translateY(var(--rail-dy, 0px)); }

/* ===== v1.0.827 — the actors window (x1 / x2 / x3) ===== */
.cast-win { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.6); display: none; align-items: center; justify-content: center; z-index: 140; }
.cast-win.show { display: flex; }
.cast-win-box { width: 1000px; max-height: 900px; background: #111827; border: 2px solid #334155; border-radius: 18px; padding: 22px 24px; display: flex; flex-direction: column; box-sizing: border-box; }
.cast-win-box.cast-m2, .cast-win-box.cast-m3 { width: 1700px; max-height: 980px; }
.cast-win-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; }
.cast-win-title { font-size: 1.8rem; font-weight: 800; color: #fff; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cast-win-modes { display: flex; gap: 14px; flex: 0 0 auto; }
.cast-mode { display: inline-flex; align-items: center; gap: 8px; padding: 6px 14px; border-radius: 22px; border: 3px solid transparent; font-size: 1.2rem; font-weight: 700; color: var(--muted); background: rgba(255, 255, 255, 0.05); }
.cast-mode > i { width: 16px; height: 16px; border-radius: 50%; border: 2px solid var(--muted); box-sizing: border-box; display: inline-block; }
.cast-mode.sel { color: #38bdf8; }
.cast-mode.sel > i { border-color: #38bdf8; background: #38bdf8; box-shadow: inset 0 0 0 3px #111827; }
.cast-mode.focused { border-color: var(--ff); color: #fff; }
.cast-win-now { height: 30px; line-height: 30px; font-size: 1.15rem; color: #38bdf8; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-bottom: 6px; }
.cast-win-list { overflow: hidden; display: flex; flex-direction: column; gap: 8px; min-height: 0; }
.cast-row { flex: 0 0 auto; display: flex; align-items: center; gap: 14px; box-sizing: border-box; padding: 0 8px; border-radius: 12px; background: rgba(255, 255, 255, 0.04); overflow: hidden; }
.cast-actor { flex: 0 0 auto; display: flex; align-items: center; gap: 14px; border: 3px solid transparent; border-radius: 10px; box-sizing: content-box; padding: 0 8px 0 0; }
.cast-win-box.cast-m1 .cast-actor { flex: 1 1 auto; }
.cast-win-box.cast-m2 .cast-actor, .cast-win-box.cast-m3 .cast-actor { width: 400px; }
.cast-photo, .cast-film { flex: 0 0 auto; border-radius: 6px; overflow: hidden; background: #0b1220; display: flex; align-items: center; justify-content: center; box-sizing: border-box; }
.cast-photo img, .cast-film img { width: 100%; height: 100%; object-fit: contain; display: block; }
.cast-film { border: 3px solid transparent; }
.cast-film > i { font-style: normal; font-size: 0.85rem; color: #cbd5e1; text-align: center; padding: 4px; }
.cast-name { display: flex; flex-direction: column; gap: 4px; min-width: 0; color: #e2e8f0; }
.cast-name b { font-size: 1.35rem; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cast-name small { font-size: 1rem; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cast-win-box.cast-m3 .cast-name b { font-size: 1.6rem; }
.cast-sep { flex: 0 0 auto; width: 3px; height: 60%; background: #38bdf8; border-radius: 2px; }
.cast-strip { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 10px; overflow: hidden; height: 100%; }
.cast-none { color: var(--muted); font-size: 1.1rem; }
.cast-actor.focused-cell { border-color: var(--ff); background: var(--card2); }
.cast-film.focused-cell { border-color: var(--ff); }
.cast-win-head, .cast-win-now { flex: 0 0 auto; }

/* v1.0.828 — the actors window: x1 = the old list's sizes exactly; x2 / x3 = the name on the photo's bottom (small black fade) */
.cast-win-box.cast-m1 { width: 1000px; max-height: 860px; padding: 24px; }
.cast-win-box.cast-m1 .cast-win-head { margin-bottom: 16px; }
.cast-win-box.cast-m1 .cast-win-now { display: none; }
.cast-row.vod-pick { height: auto; background: rgba(255, 255, 255, 0.04); overflow: visible; }
.cast-row.vod-pick.focused { background: var(--card2); }
.cast-rowx { padding: 0 8px; }
.cast-win-box.cast-m2 .cast-actor, .cast-win-box.cast-m3 .cast-actor { width: auto; padding: 0; gap: 0; }
.cast-photo { position: relative; }
.cast-cap { position: absolute; left: 0; right: 0; bottom: 0; padding: 22px 6px 5px; display: flex; flex-direction: column; align-items: center; text-align: center; background: linear-gradient(to top, rgba(0, 0, 0, 0.9) 0%, rgba(0, 0, 0, 0.75) 55%, rgba(0, 0, 0, 0) 100%); color: #fff; }
.cast-cap b { max-width: 100%; font-size: 1rem; font-weight: 700; line-height: 1.15; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cast-cap small { max-width: 100%; font-size: 0.8rem; color: #cbd5e1; line-height: 1.15; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cast-win-box.cast-m3 .cast-cap b { font-size: 1.2rem; }
.cast-win-box.cast-m3 .cast-cap small { font-size: 0.95rem; }
.cast-row.vod-pick { padding: 6px 18px; gap: 16px; box-sizing: content-box; }

/* v1.0.843 — the small reminder arrow in the middle of the screen's width (the "full home page": the vertical menu
   hidden, no arrow under the tiles, no bottom info bar). app.js (fullArrowPlace) writes its place and size; the drawing
   is the same arrow made smaller, proportions kept. Tests switch fullArrowOff. */
.rows-more-indicator.arrow-full { left: var(--farrow-l, 50%) !important; right: auto !important; top: var(--farrow-t, auto) !important; margin: 0 !important; transition: none !important; }
.rows-more-indicator.arrow-full svg { display: block; width: var(--farrow, 24px); height: var(--farrow, 24px); }
body.rail-hide-on.rail-hide-noarrow #rowsMoreIndicator.rows-more-indicator.arrow-full.arrow-flash { display: block !important; }
/* v1.0.844 — the fullscreen players' position bar runs LEFT -> RIGHT in Hebrew / Arabic too (body.pbar-ltr, app.js
   pbarLtr): the time gone by on the left, the whole length on the right, the bar fills from its left edge and the round
   mark sits on the fill's right end. Only the bars themselves; the buttons row stays as it was. Tests switch
   playerBarLtrOff. (Not in the English stylesheet: there the bar already runs this way.) */
body.pbar-ltr .player-progress { direction: ltr; }
body.pbar-ltr .player-progress-fill { right: auto; left: 0; }
body.pbar-ltr .player-progress-fill::after { left: auto; right: 0; transform: translate(50%, -50%); }
body.pbar-ltr .player-seek-offset { left: auto; right: 0; transform: translate(50%, -10px); }
body.pbar-ltr .vod-osd-row { direction: ltr; }

/* v1.0.844 — the arrow under the settings icon stays on screen while its lines are tuned (app.js arrowHoldSet) */
body.row1-arrow-rail #rowsMoreIndicator.rows-more-indicator.arrow-hold.arrow-flash { display: block !important; }
/* v1.0.845 — the tiles' strip reaches the screen's far edge with the vertical menu SHOWN too (app.js rowsFarApply writes
   the measured distance): margin outward + the same padding inward, the tiles keep their exact place and size. */
body.rows-far:not(.rail-hide-on) .content-rows { margin-left: calc(-1 * var(--rows-far, 0px)); padding-left: calc(var(--rows-far-pad0, 32px) + var(--rows-far, 0px)); }
body.rows-far:not(.rail-hide-on) #contentRows .row-channels { margin-left: calc(var(--rows-far-rcm0, -32px) - var(--rows-far, 0px)); padding-left: calc(var(--rows-far-rcp0, 0px) + var(--rows-far, 0px)); }

/* v1.0.845 — the reminder arrow in the middle shows whenever its own code lights it (the vertical menu shown or hidden) */
body #rowsMoreIndicator.rows-more-indicator.arrow-full.arrow-flash { display: block !important; }
/* v1.0.846 — the horizontal menu: the arrow toward the vertical menu goes to that menu (shown or hidden), so only the
   other one (the page change) is drawn; its place is kept. app.js hbarRightToRail / body.hbar-right-rail. */
body.hbar-right-rail .vod-filter .hbar-arr.a1 { visibility: hidden; }
/* v1.0.847 — the programme guide ("מדריך", stage 1). app.js renderGuide writes every box's place (mirrored by itself in
   English), so these rules are the same in both stylesheets. Sizes: 7 rows of 68px, 6px apart; 150 minutes across. */
#screen-guide { position: relative; overflow: hidden; background: var(--bg); }
body.guide-open #focusFrame, body.guide-open .bottom-info-bar, body.guide-open #rowsMoreIndicator { display: none !important; }
.guide-info { position: absolute; top: 44px; width: 900px; }
.guide-tag { color: #22b8e6; font-size: 22px; font-weight: 700; }
.guide-title { font-size: 54px; font-weight: 700; line-height: 1.15; margin: 10px 0 6px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.guide-meta { font-size: 24px; color: var(--muted); margin-bottom: 10px; }
.guide-meta b { color: var(--text); font-weight: 500; }
.guide-desc { font-size: 26px; line-height: 1.3; color: #c9cbd6; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; max-height: 102px; }
.guide-prog { display: flex; align-items: center; gap: 14px; margin-top: 16px; font-size: 22px; color: var(--muted); }
.guide-bar { flex: 1; height: 6px; border-radius: 3px; background: var(--border); position: relative; }
.guide-bar u { position: absolute; top: 0; height: 100%; background: var(--accent); border-radius: 3px; }
.guide-vid { position: absolute; top: 44px; width: 704px; height: 396px; border-radius: 16px; border: 2px solid var(--border); background: linear-gradient(135deg, #1b1c24, #0b0b10 70%); }
.guide-head { position: absolute; top: 478px; font-size: 22px; font-weight: 700; color: var(--text); white-space: nowrap; }
.guide-head small { display: block; color: var(--muted); font-weight: 400; font-size: 18px; }
.guide-tick { position: absolute; top: 486px; height: 584px; width: 0; border-left: 1px solid #1f2029; }
.guide-tick span { position: absolute; top: 0; font-size: 20px; color: var(--muted); white-space: nowrap; }
.guide-ch { position: absolute; height: 68px; border-radius: 12px; background: var(--card); border: 2px solid var(--border); display: flex; align-items: center; gap: 10px; padding: 0 14px; font-size: 20px; font-weight: 500; white-space: nowrap; overflow: hidden; box-sizing: border-box; }
.guide-ch .num { color: var(--muted); font-size: 19px; min-width: 28px; flex: none; }
.guide-ch .nm { overflow: hidden; text-overflow: ellipsis; flex: 1; direction: ltr; text-align: right; }
.guide-ch.on { border-color: #22b8e6; color: #22b8e6; }
.guide-cell { position: absolute; height: 68px; border-radius: 12px; background: var(--card); border: 2px solid var(--border); padding: 7px 14px; overflow: hidden; white-space: nowrap; box-sizing: border-box; }
.guide-cell b { display: block; font-size: 23px; font-weight: 500; overflow: hidden; text-overflow: ellipsis; }
.guide-cell i { font-style: normal; font-size: 17px; color: var(--muted); direction: ltr; display: block; }
.guide-cell.live { background: var(--card2); }
.guide-cell.past { opacity: 0.45; }
.guide-cell.arc b { color: #c9cbd6; } /* ended, still in the channel's archive: can be played */
.guide-cell.empty b { color: var(--muted); line-height: 50px; }
.guide-cell.foc { border-color: var(--ff); box-shadow: 0 0 0 3px var(--ffg); background: #20222d; opacity: 1; }
.guide-cell.foc b { font-weight: 700; }
.guide-now { position: absolute; top: 512px; height: 558px; width: 2px; background: var(--danger); }
.guide-now span { position: absolute; top: -28px; left: -34px; width: 70px; z-index: 2; text-align: center; background: var(--danger); color: #fff; font-size: 18px; font-weight: 700; border-radius: 6px; direction: ltr; }
/* v1.0.849 — the guide: the small window plays (the video element itself sits in it), groups in colours, the groups line */
#screen-guide { background: transparent; }
body.guide-open.guide-vid-on .guide-vid { background: transparent; }
.guide-ghead { position: absolute; height: 30px; line-height: 30px; font-size: 20px; font-weight: 700; white-space: nowrap; overflow: hidden; box-sizing: border-box; padding: 0 12px; }
.guide-ghead u { position: absolute; left: 0; right: 0; bottom: 2px; height: 2px; opacity: 0.45; }
.guide-ch.playing .num { color: var(--danger); font-weight: 700; }
.guide-cell.rem::after { content: ''; position: absolute; top: 8px; left: 8px; width: 10px; height: 10px; border-radius: 50%; background: var(--warning); }
.guide-groups { position: absolute; top: 386px; height: 54px; display: flex; align-items: center; gap: 10px; overflow: hidden; white-space: nowrap; box-sizing: border-box; padding: 3px; }
.guide-gchip { flex: none; height: 44px; line-height: 40px; padding: 0 16px; border: 2px solid; border-radius: 10px; font-size: 20px; font-weight: 500; background: var(--card); box-sizing: border-box; opacity: 0.6; }
.guide-gchip.cur { opacity: 1; background: var(--card2); }
.guide-groups.active .guide-gchip.foc { opacity: 1; box-shadow: 0 0 0 3px var(--ffg); border-color: var(--ff) !important; color: var(--text) !important; }
.guide-desc { -webkit-line-clamp: 2; max-height: 68px; }
/* v1.0.850 — the add-on's player: the title in its OWN proportions (black bars when it is wider than the screen), never
   stretched — whatever "תצורת צפייה" is set for the channels' player (16:9 / stretch fill the whole screen, which is right
   for a 16:9 broadcast and stretches a cinema-wide film). Zoom / 4:3 / 21:9 stay as chosen. Tests switch vodFitOff. */
body.vod-playing.vod-fit:not(.aspect-zoom):not(.aspect-4x3):not(.aspect-21x9) #video:not(.video-hero) { object-fit: contain; }
/* ==========================================================================
   v1.0.854 — NEW (per request): the shape of the player's buttons, chosen in the settings (מסך מלא).
   body.pbs-N = the channels' player (live / archive / favourites / guide); body.vbs-N = the add-on's player.
   No class = each one's own look as it was (1 = the channels' rounded frame, 3 = the add-on's filled square).
   Only the frame around an icon changes — no icon is stretched or changes its size.
   ========================================================================== */
/* --- the channels' player --- */
body.pbs-2 .player-controls-row > svg.show, body.pbs-2 .player-clock-icon.show { border-radius: 50%; box-shadow: 0 0 0 2px rgba(255,255,255,0.9); }
body.pbs-2 .player-controls-row .focused, body.pbs-2 .player-controls-row > svg.show.focused, body.pbs-2 .player-clock-icon.focused { border-radius: 50%; box-shadow: 0 0 0 2px #fff; }
body.pbs-3 .player-controls-row > svg.show, body.pbs-3 .player-clock-icon.show { border-radius: 14px; box-shadow: none; background: rgba(255,255,255,0.12); }
body.pbs-3 .player-controls-row .focused, body.pbs-3 .player-controls-row > svg.show.focused, body.pbs-3 .player-clock-icon.focused { border-radius: 14px; background: #1e293b; color: #fff !important; box-shadow: 0 0 0 3px #38bdf8, 0 0 0 7px rgba(56,189,248,0.3); }
body.pbs-4 .player-controls-row > svg.show, body.pbs-4 .player-clock-icon.show { border-radius: 50%; background: rgba(255,255,255,0.14); box-shadow: 0 0 0 4px rgba(255,255,255,0.14); }
body.pbs-4 .player-controls-row .focused, body.pbs-4 .player-controls-row > svg.show.focused, body.pbs-4 .player-clock-icon.focused { border-radius: 50%; background: #38bdf8; box-shadow: 0 0 0 4px #38bdf8; }
body.pbs-5 .player-controls-row > svg.show, body.pbs-5 .player-clock-icon.show { width: 96px; padding: 0 16px; box-sizing: border-box; border-radius: 32px; }
body.pbs-5 .player-controls-row .focused, body.pbs-5 .player-controls-row > svg.show.focused, body.pbs-5 .player-clock-icon.focused { border-radius: 32px; }
body.pbs-6 .player-controls-row > svg.show, body.pbs-6 .player-clock-icon.show,
body.pbs-6 .player-controls-row .focused, body.pbs-6 .player-controls-row > svg.show.focused, body.pbs-6 .player-clock-icon.focused { border-radius: 0; }
body.pbs-7 .player-controls-row > svg.show, body.pbs-7 .player-clock-icon.show,
body.pbs-8 .player-controls-row > svg.show, body.pbs-8 .player-clock-icon.show { box-shadow: none; }
body.pbs-7 .player-controls-row .focused, body.pbs-7 .player-controls-row > svg.show.focused, body.pbs-7 .player-clock-icon.focused { background: none; box-shadow: none; color: #38bdf8 !important; transform: scale(1.25) !important; }
body.pbs-8 .player-controls-row .focused, body.pbs-8 .player-controls-row > svg.show.focused, body.pbs-8 .player-clock-icon.focused { background: none; color: #38bdf8 !important; border-radius: 0 0 12px 12px; box-shadow: inset 0 -5px 0 #38bdf8; }
body.pbs-3 .player-fav-heart.focused path, body.pbs-3 .player-controls-panel.live-quick .player-fav-heart.focused path { stroke: #fff !important; }
body.pbs-3 .player-fav-heart.active.focused path { fill: #ef4444 !important; stroke: #ef4444 !important; }
body.pbs-7 .player-fav-heart.focused path, body.pbs-7 .player-controls-panel.live-quick .player-fav-heart.focused path,
body.pbs-8 .player-fav-heart.focused path, body.pbs-8 .player-controls-panel.live-quick .player-fav-heart.focused path { stroke: #38bdf8 !important; }
body.pbs-7 .player-fav-heart.active.focused path, body.pbs-8 .player-fav-heart.active.focused path { fill: #ef4444 !important; stroke: #38bdf8 !important; }
/* --- the add-on's player: the frame goes around the icon (64px box, the icon itself stays 38px), the caption under it --- */
body.vbs-on .vod-btn, body.vbs-on .vod-btn.focused { background: none; border-color: transparent; box-shadow: none; }
body.vbs-on .vod-btn.focused { color: #fff; }
body.vbs-on .vod-btn svg { width: 64px; height: 64px; padding: 13px; box-sizing: border-box; }
body.vbs-1 .vod-btn svg { border-radius: 12px; box-shadow: inset 0 0 0 2px rgba(255,255,255,0.9); }
body.vbs-2 .vod-btn svg { border-radius: 50%; box-shadow: inset 0 0 0 2px rgba(255,255,255,0.9); }
body.vbs-4 .vod-btn svg { border-radius: 50%; background: rgba(255,255,255,0.14); }
body.vbs-5 .vod-btn svg { width: 96px; padding: 13px 29px; border-radius: 32px; box-shadow: inset 0 0 0 2px rgba(255,255,255,0.9); }
body.vbs-6 .vod-btn svg { border-radius: 0; box-shadow: inset 0 0 0 2px rgba(255,255,255,0.9); }
body.vbs-1 .vod-btn.focused svg, body.vbs-2 .vod-btn.focused svg, body.vbs-5 .vod-btn.focused svg, body.vbs-6 .vod-btn.focused svg { background: #fff; color: #000; box-shadow: none; }
body.vbs-4 .vod-btn.focused svg { background: #38bdf8; color: #000; }
body.vbs-7 .vod-btn.focused svg { color: #38bdf8; transform: scale(1.25); }
body.vbs-8 .vod-btn.focused svg { color: #38bdf8; border-radius: 0 0 12px 12px; box-shadow: inset 0 -5px 0 #38bdf8; }
/* the settings rows with many choices: the choices go under the row's name, and wrap */
.setup-autohide-row.opt-wrap { flex-wrap: wrap; }
.setup-autohide-row.opt-wrap .setup-autohide-options { width: 100%; flex-wrap: wrap; margin-top: 10px; }

/* v1.0.856 — settings › הרחבות: the links-order row only when an add-on is installed */
body.no-addons .vod-link-order-row { display: none; }

/* v1.0.843 — the stylesheet's own version, read by the small "CSS:" badge (the file name no longer carries it) */
/* v1.0.860 — settings › מסך מלא › צורת הלחצנים: every choice shows 3 small sample buttons in its shape (the middle one
   = focused), so it's clear what is picked. Drawn in CSS, nothing stretched. */
.pb-sample { display: inline-flex; align-items: center; gap: 6px; margin: 0 8px; vertical-align: middle; }
.pb-sample i { display: inline-flex; align-items: center; justify-content: center; width: 30px; height: 30px; box-sizing: border-box; color: #e2e8f0; border-radius: 8px; box-shadow: 0 0 0 2px rgba(255,255,255,0.35); }
.pb-sample i svg { width: 16px; height: 16px; }
.pb-sample i.f { color: #fff; box-shadow: 0 0 0 2px #fff; }
.pb-sample.ps2 i { border-radius: 50%; box-shadow: 0 0 0 2px rgba(255,255,255,0.6); }
.pb-sample.ps2 i.f { box-shadow: 0 0 0 2px #fff; }
.pb-sample.ps3 i { box-shadow: none; background: rgba(255,255,255,0.14); }
.pb-sample.ps3 i.f { background: #1e293b; box-shadow: 0 0 0 2px #38bdf8; }
.pb-sample.ps4 i { border-radius: 50%; box-shadow: none; background: rgba(255,255,255,0.16); }
.pb-sample.ps4 i.f { background: #38bdf8; }
.pb-sample.ps5 i { width: 42px; border-radius: 15px; }
.pb-sample.ps6 i { border-radius: 0; }
.pb-sample.ps7 i { box-shadow: none; }
.pb-sample.ps7 i.f { box-shadow: none; color: #38bdf8; transform: scale(1.2); }
.pb-sample.ps8 i { box-shadow: none; border-radius: 0; }
.pb-sample.ps8 i.f { color: #38bdf8; box-shadow: inset 0 -3px 0 #38bdf8; }
/* v1.0.861 — the settings' search: its row at the top, and the rows that don't match hidden while it's on */
.settings-search-row { margin-bottom: 10px; }
#settingsAccordion .ss-hide { display: none !important; }
/* v1.0.862 — straight keyboard */
.search-kb-row.kb-grid .search-kb-key { min-width: 0; padding: 0 4px; }
/* v1.0.862 — the archive screen's own tuning window (a5Tune) + what it adjusts. All vars default to "no change". */
#screen-player.list-mode .player-tv-focus-frame::before { border-width: var(--a5-fw, 2px); }
#screen-player.list-mode .pli-side {
  padding: calc(14px + var(--a5-sp, 0px)) calc(18px + var(--a5-sw, 0px) + var(--a5-sp, 0px));
  min-width: calc(150px + 2 * var(--a5-sw, 0px));
  margin-top: var(--a5-sm, 0px); margin-bottom: var(--a5-sm, 0px);
}
#screen-player.list-mode .pli-channel-logo { transform: translate(var(--a5-lx, 0px), var(--a5-ly, 0px)) scale(var(--a5-logo, 1)); }
/* v1.0.863 — logo box move; the TV frame's gray strips follow its gap */
#screen-player.list-mode .pli-side { transform: translate(var(--a5-sx, 0px), var(--a5-sy, 0px)); }
#screen-player.list-mode .player-tv-focus-frame { border-left-width: var(--a5-fbx, 60px); border-right-width: var(--a5-fbx, 60px); border-top-width: var(--a5-fby, 4px); border-bottom-width: var(--a5-fby, 4px); }
#screen-player.list-mode .player-tv-focus-frame::before { top: calc(-1 * var(--a5-fby, 4px)); bottom: calc(-1 * var(--a5-fby, 4px)); left: calc(-1 * var(--a5-fbx, 60px)); right: calc(-1 * var(--a5-fbx, 60px)); }
#screen-player.list-mode .player-number-tile:not(.lead) { padding-top: calc(27px + var(--a5-s5, 0px)); padding-bottom: calc(27px + var(--a5-s5, 0px)); }
#screen-player.list-mode .player-list-row8 {
  left: calc(2.5px + var(--a5-r8l, 0px)); right: calc(2.5px + var(--a5-r8r, 0px));
  bottom: calc(5px + var(--a5-r8b, 0px)); height: calc(82.5px + var(--a5-r8h, 0px));
}
.a5-tune { display: none; position: fixed; top: 16px; max-height: 1048px; overflow: hidden; left: 1440px; width: 440px; z-index: 10050; box-sizing: border-box;
  background: #0c0e12; border: 2px solid var(--border); border-radius: 12px; padding: 10px; direction: rtl; color: #fff; }
.a5-tune.show { display: block; }
.a5-title { font-size: 1.05rem; font-weight: 700; text-align: center; margin-bottom: 4px; }
.a5-row { display: flex; justify-content: space-between; align-items: center; gap: 8px; padding: 3px 10px; margin: 1px 0;
  border: 2px solid transparent; border-radius: 8px; background: rgba(255,255,255,0.06); font-size: 0.92rem; }
.a5-row.focused { border-color: #fff; background: rgba(255,255,255,0.16); }
.a5-val { color: #38bdf8; white-space: nowrap; font-weight: 700; }
.a5-hint { font-size: 0.8rem; color: var(--muted); text-align: center; margin-top: 4px; }
/* v1.0.863 — structure 1 (#playerListPanel) tuning */
#screen-player.list-mode .player-list-panel { flex-basis: calc(815px + var(--a5-p1w, 0px)); margin-left: calc(var(--a5-p1x, 0px) - var(--a5-p1w, 0px)); margin-right: calc(-1 * var(--a5-p1x, 0px)); margin-top: calc(5px + var(--a5-p1y, 0px)); padding-bottom: calc(112.5px - var(--a5-p1l, 0px)); }
#screen-player.list-mode .player-list-rows .epg-row:not(.expanded) { padding-top: calc(27px + var(--a5-p1r, 0px)); padding-bottom: calc(27px + var(--a5-p1r, 0px)); }
#screen-player.list-mode.swapped .player-list-panel { margin-right: calc(-1 * var(--a5-p1x, 0px) - var(--a5-p1w, 0px)); margin-left: var(--a5-p1x, 0px); }
#screen-player.list-mode.row-card-open .player-list-panel { padding-bottom: calc(94px - var(--a5-p1l, 0px)); }
/* v1.0.864 — the day tiles widen together with structure 1 (7 tiles, evenly) */
#screen-player.list-mode .player-list-day-tile { flex-basis: calc(106.71px + var(--a5-p1w, 0px) / 7); }
/* v1.0.864 — row 7 (structure 5's wide top tile) tuning; width grows to the left */
#screen-player.list-mode .player-number-tile.lead { top: calc(25px + var(--a5-l7y, 0px)); width: calc(935px + var(--a5-l7w, 0px)); height: calc(82.5px + var(--a5-l7h, 0px)); right: calc(20px - var(--a5-l7x, 0px)); }
#screen-player.list-mode.swapped .player-number-tile.lead { right: auto; left: calc(5px + var(--a5-l7x, 0px) - var(--a5-l7w, 0px)); }
/* v1.0.864 — light-blue guide lines exactly through the screen's middle (archive screen only) */
.a5-guide { display: none; position: fixed; left: 0; top: 0; width: 1920px; height: 1080px; pointer-events: none; z-index: 10040; }
body.player-list-mode .a5-guide.show { display: block; }
.a5-guide i { display: none; position: absolute; background: #7dd3fc; }
.a5-guide i.h { left: 0; width: 1920px; top: 539.5px; height: 1px; }
.a5-guide i.v { top: 0; height: 1080px; left: 959.5px; width: 1px; }
.a5-guide.gh i.h, .a5-guide.gv i.v { display: block; }
/* v1.0.865 — home tuning window: tools line (move / size circles + middle guide), bigger tabs; archive: buttons strip background height */
.tune-tools { display: flex; align-items: center; gap: 14px; margin-bottom: 8px; font-size: 1.05rem; color: var(--text); flex-wrap: nowrap; }
.tune-tool { display: inline-flex; align-items: center; gap: 8px; padding: 4px 12px; border: 2px solid transparent; border-radius: 8px; background: var(--card2); white-space: nowrap; }
.tune-tool.focused { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(123, 92, 255, 0.25); }
.tune-radio { display: inline-block; width: 16px; height: 16px; border-radius: 50%; border: 2px solid #38bdf8; box-sizing: border-box; }
.tune-radio.on { background: #38bdf8; box-shadow: inset 0 0 0 3px var(--card2); }
.tune-tool-val { color: #38bdf8; font-weight: 700; }
.tune-tool-hint { color: var(--muted); font-size: 0.9rem; white-space: nowrap; }
.tune-win.tabs-big .tune-tab { font-size: 1.26rem; padding: 5px 14px; }
body.tune-open .a5-guide.show.home { display: block; }
#screen-player.list-mode .player-controls-backdrop { height: calc(220px + var(--a5-cth, 0px)); }
:root { --css-ver: "1.0.866"; }
