/* shell.css — app-shell layout for iranian.fi.
   RTL by default. Grid: [right sidebar 400px | main 1fr | left sidebar 220px].
   Fixed sticky player 80px at bottom. Responsive breakpoints:
     ≥ 1280px  full 3-column
     1024-1279 collapse left to drawer
     < 768     single column + right as modal + mini player */

*{margin:0;padding:0;box-sizing:border-box}
/* every tile/card/row on this site is an <a> (often wrapping an <img> or a
   background-image cover), and browsers make links + images draggable by
   default: a click that has even a tiny bit of mouse movement gets
   reinterpreted as "drag this link out" (native ghost preview + a "+"
   cursor), instead of a click (operator 2026-07-09: "روی هر لینکی کلیک و
   مکث می‌کنم به drag and drop در میاد"). Nothing on the site relies on
   dragging links/images out; the one real drag feature (list.php's
   playlist reorder) uses pointer events, not the native HTML5 drag API,
   so this doesn't touch it. */
a, img { -webkit-user-drag: none; }
:root{
  /* PAPER is the base template now (operator 2026-07-05: «قالب اصلی رو کاغذی کن»).
     These match paper.css's brutal vars, so there is no dark base to forget an
     override for. The skin is also locked to brutal server-side on <html>, so this
     IS the rendered look — flipping the base is a no-op on the live site, it just
     stops new components defaulting to a dark theme that then needs a paper patch. */
  --bg: #fbfaf5;
  --panel: #f1efe6;
  --line: #1f1f1f;
  --muted: rgba(31,31,31,.55);
  --gold: #e7c873;               /* brand token; the monochrome paper rules override to ink where they want it */
  --accent: #1f1f1f;
  --pp-wob-a: 14px 10px 16px 9px / 10px 15px 9px 16px;
  --pp-wob-b: 10px 13px 9px 14px / 13px 9px 14px 10px;
  --pp-shadow: 3px 4px 0 rgba(31,31,31,.14);
  --pp-pencil: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='120'%20height='5'%20viewBox='0%200%20120%205'%3E%3Cpath%20d='M0,2.5%20Q10,1.4%2020,2.4%20T40,2.6%20T60,2.4%20T80,2.6%20T100,2.4%20T120,2.5'%20fill='none'%20stroke='%231f1f1f'%20stroke-width='1.7'%20stroke-linecap='round'/%3E%3C/svg%3E");
  --top-h: 62px;
  --player-h: 80px;
  --right-w: 400px;   /* the context/playing panel — the only sidebar since 2026-07-09 */
}
/* 100% FIRST, 100dvh SECOND (2026-09-01). The shell is a fixed-height column —
   top bar, scrolling middle, player — so its height is decided once and never
   recomputed. On iOS, opening the site from inside another app (a link tapped in
   Gmail) resolves that `100%` against a viewport the page does not end up in,
   and nothing invalidates it afterwards: the whole shell stays SHORTER than the
   screen, with the tab bar mid-display and dead canvas below it.

   `dvh` tracks the viewport the browser is actually showing, which is the fix.

   svh WAS TRIED AND IS WRONG — do not "improve" this back to it. It reintroduces
   the short shell, because Chrome on iOS reports a small viewport that subtracts
   more chrome than is actually on screen. The player sitting slightly under the
   bottom toolbar is handled with padding below, not by shrinking the shell.

   Older engines ignore the second declaration and keep the 100%. */
html { height: 100%; height: 100dvh; }
body {
  height: 100%;
  height: 100dvh;
  font-family: 'Vazirmatn', system-ui, sans-serif;
  background: var(--bg);
  color: #1f1f1f;                /* ink on paper: base text is dark so inheriting components stay legible */
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
a { color: inherit; text-decoration: none; }
button { font: inherit; cursor: pointer; }

/* --- TOP NAV BAR (spans full width) --------------------------------------
   Same background as the body so it visually reads as a single unified surface, not a
   separate band glued on top. Subtle border-bottom for definition, no gradient. */
.app-top {
  height: var(--top-h);
  flex-shrink: 0;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  padding: 0 22px;
  gap: 18px;
  background: var(--bg);
  border-bottom: 1px solid rgba(255,255,255,.06);
  z-index: 50;
}
.tb-left { display: flex; align-items: center; gap: 8px; }
/* the menu moved in here beside the brand (2026-09-10) — see shell.php. The logo
   needs air on its own or the first pill reads as part of it. */
.tb-left .tb-browse:first-of-type { margin-inline-start: 14px; }
.tb-brand { display: flex; align-items: center; padding: 6px 0; }
.tb-brand img { height: 50px; width: auto; display: block; }   /* new owl logo, operator 2026-07-04 */
/* مرور on the topbar — the one nav-column link that needed a desktop home
   after the column retired (2026-07-09). Pill shape matches .tb-lang; sits
   first in .tb-user so it lands right beside the search bar. turbo's
   navActive() toggles .active via its data-ir2-nav attribute. Hidden ≤767
   (bottom tabs + the msrch panel's دسته‌بندی‌ها cover mobile). */
.tb-browse {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: .8rem; font-weight: 800;
  padding: 7px 13px; border-radius: 20px;
  color: rgba(255,255,255,.75);
  background: rgba(255,255,255,.05);
  border: 1px solid transparent;
  transition: background .15s, color .15s;
}
.tb-browse:hover { background: rgba(255,255,255,.1); color: #fff; }
.tb-browse.active { color: var(--gold); }
.tb-browse svg { width: 16px; height: 16px; flex-shrink: 0; }
/* Three pills now sit beside the search bar. Below ~1100px the third one starts
   eating the bar's width, and the search field is the more used control, so
   پادکست drops out there - it still has the podcast button in the search panel,
   which is exactly the surface a narrow window falls back to. */
@media (max-width: 1100px) { .tb-shows { display: none; } }
.tb-searchbtn {
  display: none;   /* mobile-only (shown ≤767); the bar covers desktop/tablet */
  background: none; border: none; color: #fff; cursor: pointer;
  width: 38px; height: 38px; border-radius: 10px;
  align-items: center; justify-content: center;
}
.tb-searchbtn:hover { background: rgba(255,255,255,.06); }
.tb-searchbtn svg { width: 22px; height: 22px; }

.tb-search {
  position: relative;
  display: flex; align-items: center;
  background: rgba(255,255,255,.06);
  border: 1px solid rgba(255,255,255,.08);
  border-radius: 22px;
  padding: 0 14px 0 12px;
  height: 40px;
  max-width: 520px;
  width: 100%;
  justify-self: center;
  transition: background .15s, border-color .15s;
}
.tb-search:focus-within { background: rgba(255,255,255,.1); border-color: rgba(255,255,255,.18); }
.tb-search .tbs-ic { width: 18px; height: 18px; opacity: .55; flex-shrink: 0; margin-inline-end: 8px; }
.tb-search input {
  flex: 1;
  background: none; border: none; outline: none;
  color: #fff; font: inherit; font-size: 16px;
  padding: 0; min-width: 0;
}
.tb-search input::placeholder { color: rgba(255,255,255,.45); font-size: 15px; }
.tb-drop {
  position: absolute; top: calc(100% + 6px); left: 0; right: 0;
  background: rgba(12,12,18,.98);
  backdrop-filter: blur(24px);
  border: 1px solid var(--line);
  border-radius: 12px;
  max-height: 70vh; overflow: auto;
  z-index: 55;
  box-shadow: 0 20px 60px rgba(0,0,0,.7);
  direction: ltr;              /* results are DEVICE vocabulary — cover left (L-03/T-21) */
}
.tb-drop .tbd-t, .tb-drop .tbd-a { unicode-bidi: plaintext; }   /* Persian reads RTL inside the LTR row */

.tb-user { display: flex; align-items: center; gap: 10px; }
/* hidden everywhere for now (operator 2026-07-10: "دگمه Pinglish... اصلا اجرا
   نشد") -- markup + is_pinglish()/?ir_lang= wiring left in place in shell.php,
   just not linked to from the UI, so this is a one-line revert once it's
   actually fixed. */
.tb-lang { display: none; font-size: .74rem; padding: 6px 11px; border-radius: 20px; color: rgba(255,255,255,.7); background: rgba(255,255,255,.05); border: 1px solid transparent; transition: background .15s; }
.tb-lang:hover { background: rgba(255,255,255,.1); color: #fff; }
.tb-ava { display: flex; align-items: center; }
.tb-ava img, .tb-ava-ph { width: 36px; height: 36px; border-radius: 50%; object-fit: cover; }
.tb-ava-ph { background: var(--gold); color: #1a1a1a; display: flex; align-items: center; justify-content: center; font-weight: 900; font-size: .95rem; }
.tb-login {
  background: var(--gold); color: #1a1a1a;
  border: none; border-radius: 20px;
  padding: 8px 16px; font-weight: 800; font-size: .82rem;
  transition: filter .15s;
}
.tb-login:hover { filter: brightness(1.06); }

/* --- APP GRID (below top bar, above player) ------------------------------
   TWO columns since 2026-07-09 (the nav column retired; operator: «سایدبار چپ
   بازنشسته، پنل راست منتقل به چپ»). RTL, so col 1 = the VISUAL RIGHT = main
   (fluid), col 2 = the VISUAL LEFT = the 400px playing-info/context panel.
   The panel keeps its .app-right class + #appRight id — renaming them would
   ripple through ui.js/boot.js/turbo.js for zero behavioral gain; read
   "right" as "the playing panel", not a compass direction. */
.app {
  flex: 1;
  display: grid;
  grid-template-columns: 1fr var(--right-w);
  grid-template-rows: minmax(0, 1fr);   /* SINGLE row: without this, browser was creating implicit rows and stacking the columns vertically like stairs */
  overflow: hidden;
  min-height: 0;
  gap: 0;
}
.app-main  { grid-column: 1; grid-row: 1; min-height: 0; overflow-y: auto; }
.app-right { grid-column: 2; grid-row: 1; min-height: 0; }
/* thin, quiet scrollbars — only visible on hover, Spotify-style */
.app-main, .right-scroll { scrollbar-width: thin; scrollbar-color: transparent transparent; }
.app-main:hover, .right-scroll:hover { scrollbar-color: rgba(255,255,255,.14) transparent; }
.app-main::-webkit-scrollbar, .right-scroll::-webkit-scrollbar { width: 8px; }
.app-main::-webkit-scrollbar-track, .right-scroll::-webkit-scrollbar-track { background: transparent; }
.app-main::-webkit-scrollbar-thumb, .right-scroll::-webkit-scrollbar-thumb { background: transparent; border-radius: 4px; transition: background .15s; }
.app-main:hover::-webkit-scrollbar-thumb, .right-scroll:hover::-webkit-scrollbar-thumb { background: rgba(255,255,255,.14); }

/* (the .app-left/.lnav/.litem nav-column styles lived here — retired 2026-07-09) */

/* --- MAIN CONTENT ------------------------------------------------------ */
.app-main {
  padding: 22px 30px calc(var(--player-h) + 32px);   /* bottom clears the fixed mini-player */
  scroll-behavior: smooth;
}
.app-main > *:first-child { margin-top: 0; }

/* --- THE PLAYING PANEL (.app-right; context-aware playing info) --------
   On the visual LEFT since 2026-07-09. The divider must sit on the edge
   FACING MAIN: panel was visual-right so that was its physical-left edge
   (inline-end in RTL); now it's visual-left, so the facing edge is
   physical-right = inline-START in RTL. Classic silent-looks-wrong detail. */
.app-right {
  background: var(--bg);
  border-inline-start: 1px solid rgba(255,255,255,.05);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
/* min-height:0 = the flex child actually scrolls (not grow past the column). Bottom
   padding clears the fixed mini-player + a little extra, so the last content (the
   programs slot) isn't hidden behind it (operator 2026-07-05: «میره زیر مینی‌پلیر»). */
.right-scroll { flex: 1; min-height: 0; overflow-y: auto; padding: 22px 22px calc(var(--player-h) + 32px); }
.rp-placeholder { text-align: center; padding: 8px 8px 40px; color: var(--muted); }
.rp-ph-cover {
  width: 180px; height: 180px;
  border-radius: 16px;
  background: rgba(255,255,255,.03);
  border: 1px dashed rgba(255,255,255,.1);
  margin: 0 auto 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 2.6rem;
  opacity: .45;
}
.rp-ph-title { font-size: 1.02rem; font-weight: 800; color: #fff; margin-bottom: 5px; }
.rp-ph-sub { font-size: .8rem; color: var(--muted); line-height: 1.6; }

/* right sidebar: live-radio state (filled by shell.js) */
.rp-live { text-align: center; }
.rp-cover { width: 260px; height: 260px; margin: 0 auto 16px; border-radius: 18px; background: #1b1b24 center/cover no-repeat; box-shadow: 0 22px 50px -16px rgba(0,0,0,.7); }
.rp-station { display: inline-flex; align-items: center; gap: 7px; background: linear-gradient(135deg, #ff2d55, #d50f3f); color: #fff; padding: 5px 11px; border-radius: 20px; font-size: .68rem; font-weight: 800; letter-spacing: .04em; margin-bottom: 12px; }
.rp-station i { width: 6px; height: 6px; border-radius: 50%; background: #fff; animation: pulse 1.5s infinite; }
@keyframes pulse { 0%,100% {opacity:1} 50%{opacity:.35} }
.rp-title { font-size: 1.2rem; font-weight: 800; line-height: 1.35; margin-bottom: 6px; }
.rp-artist { font-size: .9rem; color: var(--muted); margin-bottom: 20px; }

.rp-stswitch { display: inline-flex; background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.1); border-radius: 22px; padding: 3px; gap: 0; margin-bottom: 20px; }
.rp-stswitch button { background: none; border: none; color: rgba(255,255,255,.6); font: inherit; font-size: .74rem; font-weight: 800; padding: 6px 14px; border-radius: 18px; letter-spacing: .04em; }
.rp-stswitch button:hover { color: #fff; }
.rp-stswitch button.on { background: linear-gradient(135deg, #ff2d55, #d50f3f); color: #fff; }

.rp-hist { margin-top: 24px; text-align: start; }
.rp-hist h4 { font-size: .78rem; font-weight: 800; color: var(--muted); margin-bottom: 10px; letter-spacing: .04em; }
.rp-hist-row { display: flex; align-items: center; gap: 10px; padding: 8px; border-radius: 8px; }
.rp-hist-row:hover { background: rgba(255,255,255,.04); }
.rp-hist-row .rph-c { width: 38px; height: 38px; border-radius: 6px; background: #1b1b24 center/cover no-repeat; flex-shrink: 0; }
.rp-hist-row .rph-t { font-size: .82rem; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rp-hist-row .rph-a { font-size: .7rem; color: var(--muted); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* --- TABLET: narrower panel. (The nav drawer + burger that lived here died
   with the nav column, 2026-07-09 — and took the RTL translateX sign-bug
   minefield with them.) --- */
@media (max-width: 1279px) {
  :root { --right-w: 320px; }
}

/* --- MOBILE: single column, the panel becomes the swipe-up sheet -------- */
@media (max-width: 767px) {
  :root { --player-h: 62px; --top-h: 54px; --right-w: 0; }
  .app { grid-template-columns: 1fr; }
  /* base grid puts .app-main in col 1 now, so no re-pin needed (the old
     3-col layout had it in col 2, which spawned an implicit dead gutter
     here until 2026-07-05); .app-right un-pins below when it goes fixed. */
  .tb-browse { display: none; }   /* bottom tabs + msrch's دسته‌بندی‌ها cover مرور */
  .tb-search { max-width: none; }
  .tb-search input { font-size: 16px; }
  .tb-search input::placeholder { font-size: 15px; }
  .tb-lang { display: none; }
  .app-right {
    grid-column: unset;
    position: fixed;
    top: var(--top-h); left: 0; right: 0;
    bottom: var(--player-h);
    z-index: 95;
    transform: translateY(105%);
    transition: transform .28s ease;
  }
  .app.right-open .app-right { transform: translateY(0); }
  .app-main { padding: 16px 14px 30px; }
  .app-top { padding: 0 12px; gap: 10px; }
}


/* ==== On-demand Now-Playing panel (right sidebar) ==== */
/* Rendered by shell.js renderRight() when a track is loaded via IR.playTrack().
   RIGHT COLUMN = list-shape context (related tracks). LYRICS stay in the CENTER
   on /song.php where the wide column can breathe. */
.rp-od { text-align: center; direction: rtl; }
.rp-cover-lg { display: block; width: 280px; height: 280px; margin: 0 auto 16px; border-radius: 18px; background: #1b1b24 center/cover no-repeat; box-shadow: 0 22px 50px -16px rgba(0,0,0,.7); cursor: pointer; transition: transform .18s; }
.rp-cover-lg:hover { transform: translateY(-2px); }
.rp-title-lg { font-size: 1.35rem; font-weight: 800; line-height: 1.3; margin-bottom: 4px; padding: 0 6px; }
.rp-artist-lg { font-size: .96rem; color: rgba(255,255,255,.55); margin-bottom: 12px; }
.rp-fapers { margin-bottom: 16px; }
.rp-fapers .rp-fa { font-size: 1rem; font-weight: 700; color: #fff; opacity: .85; margin-bottom: 3px; }
.rp-fapers .rp-fb { font-size: .8rem; color: rgba(255,255,255,.5); }

.rp-od .vtags { display: flex; flex-wrap: wrap; gap: 5px; justify-content: center; margin: 12px 0 4px; }
.rp-od .vtag { font-size: .68rem; padding: 3px 10px; border-radius: 18px; background: rgba(255,255,255,.06); border: 1px solid rgba(255,255,255,.12); color: rgba(255,255,255,.78); text-decoration: none; transition: background .15s; }
.rp-od .vtag:hover { background: rgba(255,255,255,.13); }
.rp-od .vtag.f-mood { color: #ffd87a; border-color: rgba(255,216,122,.3); }
.rp-od .vtag.f-genre { color: #a7c7ff; border-color: rgba(167,199,255,.3); }
.rp-od .vtag.f-theme { color: #ffb0c8; border-color: rgba(255,176,200,.28); }
.rp-od .vtag.f-version { color: #b9f6c8; border-color: rgba(185,246,200,.3); }

/* Related tracks list (right sidebar's raison d'être in on-demand mode) */
.rp-rel-title { margin: 26px 0 10px; font-size: .78rem; font-weight: 800; color: rgba(255,255,255,.55); letter-spacing: .04em; text-align: start; padding: 0 4px; }
.rp-rel { display: flex; flex-direction: column; gap: 2px; }
.rp-rel-row { display: flex; align-items: center; gap: 10px; padding: 7px 6px; border-radius: 10px; color: inherit; text-decoration: none; transition: background .15s; }
.rp-rel-row:hover { background: rgba(255,255,255,.05); }
.rp-rel-cov { width: 42px; height: 42px; border-radius: 7px; background: #1b1b24 center/cover no-repeat; flex-shrink: 0; box-shadow: 0 4px 12px rgba(0,0,0,.35); }
.rp-rel-meta { flex: 1; min-width: 0; text-align: start; }
.rp-rel-t { font-size: .84rem; font-weight: 600; line-height: 1.25; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rp-rel-a { font-size: .7rem; color: rgba(255,255,255,.55); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ==== Mobile Phase M1: bottom tabs + sheet toggle + close ==== */

/* Right sidebar CLOSE button — visible on mobile only (chevron down handle at top of sheet) */
.right-close {
  display: none;
  background: none; border: none; color: #fff;
  padding: 10px; margin: 0 auto;
  width: 100%; height: 42px;
  align-items: center; justify-content: center;
  cursor: pointer; opacity: .6;
}
.right-close svg { width: 26px; height: 26px; }
.right-close::before {
  /* pill "handle" above the chevron, iOS-style bottom-sheet indicator */
  content: "";
  position: absolute;
  top: 6px; left: 50%;
  transform: translateX(-50%);
  width: 40px; height: 4px;
  border-radius: 2px;
  background: rgba(255,255,255,.28);
}

/* Bottom tab bar — mobile only. Fixed above the player row so the tabs are always reachable
   even while a track is playing. Desktop keeps the full left sidebar and hides these tabs. */
.app-tabs {
  display: none;
  position: fixed;
  left: 0; right: 0; bottom: 0;
  height: 58px;
  background: #0a0a10;
  border-top: 1px solid rgba(255,255,255,.06);
  z-index: 105;
  padding-bottom: env(safe-area-inset-bottom, 0);
  /* Was repeat(5, 1fr), hardcoded to the tab count of the day. The day the count
     changed to four, the tabs filled columns 1-4 and column 5 stayed empty — and
     column 1 is the RIGHT one here, so the whole menu bunched against the right
     edge with a dead gap on the left. Auto columns take however many tabs exist,
     so this cannot happen again the next time one is added or removed. */
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  align-items: stretch;
}
.app-tabs .tab {
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 3px;
  color: rgba(255,255,255,.55);
  font-size: .65rem;
  text-decoration: none;
  transition: color .15s;
}
.app-tabs .tab svg { width: 21px; height: 21px; }
.app-tabs .tab.active { color: var(--gold, #e7c873); }
.app-tabs .tab:active { transform: scale(.95); }

/* On mobile, the player floats above the tab bar and the whole app column shortens */
@media (max-width: 767px) {
  /* THE BOTTOM TAB BAR IS NO LONGER RENDERED ON A PHONE (operator 2026-09-11).
     Its four links moved into the header's ☰ menu.

     Thirty percent of a 812px screen was shell furniture: 54 of header, 67 of
     the search bar that had just been opened permanently, 62 of player and 58
     of tabs. The tabs were the row that could go, because everything in them is
     one tap away in the menu and the player is not navigation - it is state,
     and it has to stay visible.

     The MARKUP is untouched and still in the page; only the rule that drew it
     is gone. And --tabs-h going to zero is what actually hands the 58px back:
     every calc() that reserved room below - the app column, the now-playing
     sheet - reads this one variable, so none of them had to be found and
     edited. */
  :root { --tabs-h: 0px; }
  /* the right SHEET sits between top-bar and player (already positioned that way in base). Show close handle. */
  .app-right { top: 0; z-index: 200; }
  .right-close { display: flex; position: relative; padding-top: 20px; }
  .right-scroll { padding: 4px 20px 30px; }

  /* Ensure body scroll accommodates fixed player + tabs (was not accounting for tab bar) */
  body { padding-bottom: 0; }
  /* the room for the player is reserved only while there IS a player: the
     ui layer toggles html.ir2-has-bar with the bar itself (2026-09-12, the
     film section had a white strip where a hidden bar had been) */
  .app { padding-bottom: var(--tabs-h); transition: padding-bottom .55s cubic-bezier(.4,0,.2,1); }
  html.ir2-has-bar .app { padding-bottom: calc(var(--player-h) + var(--tabs-h)); }
  /* the bar slid away (ui.js auto-hide): its room goes too, at the bar's own pace */
  html.ir2-has-bar.ir2-bar-away .app { padding-bottom: var(--tabs-h); }
}

/* On very small (< 400px), tighten tab labels */
@media (max-width: 399px) {
  .app-tabs .tab { font-size: .6rem; }
  .app-tabs .tab svg { width: 19px; height: 19px; }
}

/* ==== Mobile Phase M2: compact mini + progress inside ==== */
/* Progress moves OUT of the top edge into the mini row itself — a thin bar aligned
   under the meta text. Player height shrinks from 62 → 52 to match Spotify's density. */
@media (max-width: 767px) {
  :root { --player-h: 52px; }
}

/* ==== Mobile Phase M3: cleaner top-bar (Spotify iOS style) ==== */
@media (max-width: 767px) {
  /* Hide brand logo on mobile — Safari's URL bar already tells the user which app.
     Freed space goes to a full-width search bar with proper padding. */
  /* operator 2026-07-05 (2nd pass): search icon far-left, logo dead-center.
     3-track grid; logo in the middle track, the icon in the physical-left track
     (grid-column 3 = leftmost in RTL), the right track empty so the logo centers. */
  .app-top { grid-template-columns: 1fr auto 1fr; padding: 0 8px; gap: 6px; height: var(--top-h); }
  .tb-left { grid-column: 2; justify-self: center; gap: 4px; }
  .tb-user { grid-column: 3; justify-self: end; gap: 6px; }
  /* the inline search bar becomes an icon that opens #mSrch. */
  .app-top .tb-search { display: none; }
  .tb-searchbtn { display: inline-flex; }
  .tb-ava { width: 32px; height: 32px; }
  .tb-login { padding: 6px 12px; font-size: .78rem; }
  /* T-17: no avatar in the mobile topbar — profile is the لیست‌ها bottom tab,
     logout now lives on /me. (Logged-out users keep the login button.) */
  .tb-avawrap { display: none; }
  /* the results dropdown was as narrow as the squeezed search box (everything
     truncated to «...adi»). Break it out to near-full width under the topbar
     and give rows breathing room so titles/artists are actually readable. */
  .tb-drop {
    position: fixed;
    top: calc(var(--top-h) + 4px);
    left: 50%; right: auto; transform: translateX(-50%);
    width: calc(100% - 16px); max-width: 560px;
    max-height: calc(100vh - var(--top-h) - 96px);
  }
  .tb-drop > a { padding: 10px; gap: 12px; }
  .tb-drop > a img { width: 48px; height: 48px; }
  .tb-drop .tbd-t { font-size: .96rem; }
  .tb-drop .tbd-a { font-size: .82rem; }
  .tb-drop .tbd-ly { font-size: .78rem; }
}

/* ==== Mobile Phase M4: enforce hidden sheet ==== */
/* Belt-and-braces: a transform alone once let off-screen panels bleed onto the
   page edge (the old drawer's RTL sign-bug); visibility guarantees the sheet
   stays off until opened. (The .app-left drawer half of this block retired
   with the nav column, 2026-07-09.) */
@media (max-width: 767px) {
  .app-right { visibility: hidden; }
  .app.right-open .app-right { visibility: visible; }
  /* brand really must go on mobile (previous rule wasn't specific enough vs the img styles) */
  .app-top .tb-brand img { height: 38px; }   /* J-06: mobile topbar is ~54px — 38px fits with the owl */
}

/* ==== Mobile Phase M5: sheet doesn't cover the mini-player ==== */
/* Now-Playing sheet slides up but leaves the player + tabs visible below (Spotify pattern).
   Was `bottom: var(--player-h)` which used the OLD 62px and didn't account for the tab bar
   below — the sheet extended into the player's fixed area and hid the mini row. */
@media (max-width: 767px) {
  .app-right {
    bottom: calc(var(--player-h) + var(--tabs-h) + env(safe-area-inset-bottom, 0));
  }
}

/* ==== Sheet play-CTA (Now-Playing panel commit button) ==== */
/* Sits below cover+title+tags, above the related list. Big enough to tap without
   thinking. Fills with accent color of the previewed track. */
.rp-play-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin: 18px auto 4px;
  padding: 12px 30px;
  border: none;
  border-radius: 999px;
  background: var(--accent, #e7c873);
  color: #0a0a0a;
  font: inherit;
  font-weight: 800;
  font-size: .96rem;
  cursor: pointer;
  box-shadow: 0 12px 28px -10px rgba(231,200,115,.55);
  transition: transform .12s, filter .15s;
  -webkit-tap-highlight-color: transparent;
}
.rp-play-cta:hover { filter: brightness(1.05); }
.rp-play-cta:active { transform: scale(.96); }
.rp-play-cta svg { width: 20px; height: 20px; }

/* ==== Related row: currently-playing highlight ==== */
/* When user taps a related row in the sheet, audio swaps but the sheet stays put.
   We mark the tapped row with .playing so they have visual confirmation. */
.rp-rel-row.playing { background: rgba(231,200,115,.12); }
.rp-rel-row.playing .rp-rel-t { color: var(--accent, #e7c873); }
.rp-rel-row.playing .rp-rel-cov::after {
  content: "♪";
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0,0,0,.5);
  color: var(--accent, #e7c873);
  font-size: 20px;
  border-radius: 7px;
}

/* ==== B-01 tracklist: currently-playing row (2026-07-09) ==== */
/* Same treatment as .rp-rel-row.playing above, generalized to every ordinary
   .tlist[data-queue-key] .trow sitewide (browse/list/album/etc). Driven by
   ui.js's syncQueueUI() off the real engine state (not set-once-and-forget),
   so it tracks track changes, pause/resume and queue swaps correctly. */
.trow.playing { background: rgba(231,200,115,.12); }
.trow.playing .tt { color: var(--accent, #e7c873); }
.trow.playing .ir2-eq { color: var(--accent, #e7c873); }
.rp-rel-cov { position: relative; }

/* ==== Mobile M6: taller mini + roomier tap targets (was too cramped) ==== */
@media (max-width: 767px) {
  :root { --player-h: 62px; }    /* was 52 — Spotify-ish */
}

/* Quality row inside the Now-Playing sheet (rendered by shell.js) */
.rp-quality-row {
  display: flex; justify-content: center; align-items: center; gap: 10px;
  margin: 18px 0 4px;
}
.rp-quality-btn {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 8px 16px;
  border-radius: 22px;
  background: rgba(255,255,255,.06);
  border: 1px solid rgba(255,255,255,.14);
  color: rgba(255,255,255,.9);
  font: inherit; font-size: .8rem; font-weight: 700;
  cursor: pointer;
  font-variant-numeric: tabular-nums;
}
.rp-quality-btn:hover { background: rgba(255,255,255,.1); }
.rp-quality-btn .rp-q-ico { font-size: 1rem; opacity: .7; }

/* ==== Sheet Now-Playing cover: creative connecting animation ====
   Two-layer effect while the sheet is loading a new track/station:
   (1) a soft golden AURA that grows and shrinks like a breath,
   (2) a thin gold ARC orbiting the cover — masked so only a border-ring shows
       and it doesnt bleed onto the cover art.

/* ==== Sheet Now-Playing cover: creative connecting animation ====
   Two-layer effect while the sheet is loading a new track/station:
   (1) a soft golden AURA that grows and shrinks like a breath,
   (2) a thin gold ARC orbiting the cover — masked so only a border-ring shows
       and it doesn't bleed onto the cover art.
   Both cost 1 GPU-composited transform each — very cheap on mobile. */
.rp-cover-lg { position: relative; }
.rp-cover-lg.connecting {
  animation: irCoverBreath 1.9s ease-in-out infinite;
}
@keyframes irCoverBreath {
  0%, 100% {
    box-shadow: 0 22px 50px -16px rgba(0,0,0,.7), 0 0 0 0 rgba(231,200,115,0);
    transform: scale(1);
  }
  50% {
    box-shadow: 0 22px 50px -16px rgba(0,0,0,.7), 0 0 40px 6px rgba(231,200,115,.32);
    transform: scale(.987);
  }
}
/* Border-only conic-gradient arc using the standard mask trick — only the ring
   is visible, cover art stays untouched. */
.rp-cover-lg.connecting::before {
  content: "";
  position: absolute;
  inset: -6px;
  border-radius: 24px;
  padding: 3px;
  background: conic-gradient(from 0deg, var(--gold, #e7c873) 0deg, transparent 70deg, transparent 360deg);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask-composite: exclude;
  animation: irSpin 1.35s linear infinite;
  pointer-events: none;
  opacity: .95;
}
/* Second slower counter-rotating arc for depth */
.rp-cover-lg.connecting::after {
  content: "";
  position: absolute;
  inset: -6px;
  border-radius: 24px;
  padding: 3px;
  background: conic-gradient(from 180deg, var(--gold, #e7c873) 0deg, transparent 40deg, transparent 360deg);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask-composite: exclude;
  animation: irSpin 2.1s linear infinite reverse;
  pointer-events: none;
  opacity: .55;
  filter: blur(.5px);
}
@keyframes irSpin { to { transform: rotate(360deg); } }

/* Auto-hide (kept per operator, 2026-07-09): collapse the playing panel when
   nothing is playing / previewed — main becomes the single full-width column.
   One rule for desktop AND tablet now that the nav column (whose --left-w
   remap forced two separate blocks) is gone. Mobile stays out: there the
   panel is the swipe-up sheet, and display:none would kill it. */
@media (min-width: 768px) {
  .app.right-hidden { grid-template-columns: 1fr; }
  .app.right-hidden .app-right { display: none; }
}

/* ==== Topbar live-search dropdown ==== */
/* Fills the space under #tbQ with 6 top results, kebab menu on each row,
   and a Telegram-request button when the exact match is missing. */
.tb-drop { padding: 6px; display: none; }
.tb-drop.show { display: block; }

.tb-drop > a {
  display: flex; align-items: center; gap: 11px;
  padding: 8px; border-radius: 10px;
  color: #fff; text-decoration: none;
}
.tb-drop > a:hover, .tb-drop > a.sel { background: rgba(255,255,255,.08); }
.tb-drop > a img {
  width: 42px; height: 42px; border-radius: 8px;
  object-fit: cover; background: #15151c; flex-shrink: 0;
}
.tb-drop .tbd-meta { min-width: 0; flex: 1; }
.tb-drop .tbd-t {
  font-size: .86rem; font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.tb-drop .tbd-a {
  font-size: .74rem; opacity: .55;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.tb-drop .tbd-ly {
  font-size: .72rem; margin-top: 2px;
  color: var(--gold, #e7c873); opacity: .9;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  direction: rtl;
}
.tb-drop .tbd-kebab {
  background: none; border: none; color: rgba(255,255,255,.5);
  cursor: pointer; padding: 6px; flex-shrink: 0;
  display: flex; align-items: center; border-radius: 7px;
}
.tb-drop .tbd-kebab:hover { background: rgba(255,255,255,.12); color: #fff; }
.tb-drop .tbd-kebab svg { width: 18px; height: 18px; fill: currentColor; }

.tb-drop .tbd-all {
  display: block; text-align: center;
  padding: 10px 8px 8px; margin-top: 4px;
  border-top: 1px solid rgba(255,255,255,.06);
  color: var(--gold, #e7c873); font-size: .8rem; font-weight: 700;
  text-decoration: none;
}
.tb-drop .tbd-all:hover { color: #fff; }

.tb-drop .tbd-empty {
  padding: 22px 12px 8px; text-align: center;
  color: rgba(255,255,255,.55); font-size: .85rem;
}
.tb-drop .tbd-reqwrap {
  margin: 10px 2px 2px; padding: 9px; border-radius: 12px; text-align: center;
  background: linear-gradient(90deg, rgba(34,158,217,.18), rgba(34,158,217,.07));
  border: 1px solid rgba(34,158,217,.3);
}
.tb-drop .tbd-req {
  display: inline-flex; align-items: center; gap: 7px;
  background: #229ED9; border: none; color: #fff;
  border-radius: 14px; padding: 7px 14px;
  font: inherit; font-weight: 700; cursor: pointer;
  line-height: 1.35; text-align: start;
  transition: filter .15s;
}
.tb-drop .tbd-req:hover { filter: brightness(1.08); }
.tb-drop .tbd-req svg { width: 15px; height: 15px; fill: currentColor; }
.tb-drop .tbd-req-t { font-size: .78rem; opacity: .95; }
.tb-drop .tbd-req-q { font-size: .82rem; font-weight: 800; }

/* ===== smart-search dropdown: grouped multi-entity (Phase 2, 2026-07-11) =====
   Paper-first: rgba(31,31,31,…) reads on the white dropdown; dimming is via
   opacity (paper.css forces .tb-drop * to inherit the ink color); accent bits
   use a GOLD background (background survives the color override). */
.tb-drop { max-height: min(72vh, 580px); overflow-y: auto; overscroll-behavior: contain; }
.tb-drop .tbd-top {
  display: flex; align-items: center; gap: 12px;
  padding: 12px; margin: 2px 2px 6px; border-radius: 14px; cursor: pointer;
  background: rgba(31,31,31,.05); border: 1px solid rgba(31,31,31,.07);
}
.tb-drop .tbd-top:hover { background: rgba(31,31,31,.09); }
.tb-drop .tbd-top-cov {
  width: 64px; height: 64px; border-radius: 12px; flex-shrink: 0;
  background: #ddd7c8 center/cover no-repeat; box-shadow: 0 6px 16px -8px rgba(0,0,0,.5);
}
.tb-drop .tbd-top-cov.round { border-radius: 50%; }
.tb-drop .tbd-top-meta { min-width: 0; flex: 1; }
.tb-drop .tbd-top-k { font-size: .68rem; font-weight: 800; opacity: .5; letter-spacing: .02em; }
.tb-drop .tbd-top-t { font-size: 1.14rem; font-weight: 800; line-height: 1.2; margin: 2px 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tb-drop .tbd-top-s { font-size: .8rem; opacity: .6; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tb-drop .tbd-top-play {
  flex-shrink: 0; width: 42px; height: 42px; border-radius: 50%; font-size: 1rem;
  display: flex; align-items: center; justify-content: center; background: var(--gold, #e7c873);
}
.tb-drop .tbd-sec { padding: 2px 0; }
.tb-drop .tbd-sec + .tbd-sec { border-top: 1px solid rgba(31,31,31,.06); margin-top: 4px; padding-top: 5px; }
.tb-drop .tbd-sec-h {
  display: flex; align-items: center; justify-content: space-between;
  font-size: .74rem; font-weight: 800; opacity: .5; padding: 4px 8px 5px;
}
.tb-drop .tbd-row {
  display: flex; align-items: center; gap: 11px;
  padding: 7px 8px; border-radius: 10px; cursor: pointer;
  color: inherit; text-decoration: none;
}
.tb-drop .tbd-row:hover, .tb-drop .tbd-row.sel { background: rgba(31,31,31,.07); }
.tb-drop .tbd-cov {
  width: 44px; height: 44px; border-radius: 9px; flex-shrink: 0;
  object-fit: cover; background: #ddd7c8 center/cover no-repeat;
}
.tb-drop .tbd-cov.round { border-radius: 50%; }
.tb-drop .tbd-chips { display: flex; flex-wrap: wrap; gap: 7px; padding: 2px 8px 7px; }
.tb-drop .tbd-chip {
  font-size: .82rem; font-weight: 700; text-decoration: none; color: inherit;
  background: rgba(31,31,31,.06); border: 1px solid rgba(31,31,31,.08);
  border-radius: 999px; padding: 6px 13px;
}
.tb-drop .tbd-chip:hover { background: var(--gold, #e7c873); border-color: transparent; }
.tb-drop .tbd-recent-row { display: flex; align-items: center; }
.tb-drop .tbd-recent-it { flex: 1; min-width: 0; }                 /* the picked item, styled as a result row */
.tb-drop .tbd-recent-it .tbd-cov { width: 48px; height: 48px; }
.tb-drop .tbd-recent-it .tbd-t { font-size: .98rem; font-weight: 700; }
.tb-drop .tbd-recent-it .tbd-a { font-size: .76rem; }
.tb-drop .tbd-cov-q { display: flex; align-items: center; justify-content: center; border-radius: 50%; background: rgba(31,31,31,.06); }
.tb-drop .tbd-cov-q svg { width: 20px; height: 20px; opacity: .5; }
.tb-drop .tbd-recent-del {
  flex-shrink: 0; background: none; border: none; color: inherit; opacity: .4;
  cursor: pointer; padding: 8px; display: flex; border-radius: 8px;
}
.tb-drop .tbd-recent-del:hover { opacity: 1; background: rgba(31,31,31,.09); }
.tb-drop .tbd-recent-del svg { width: 15px; height: 15px; }
.tb-drop .tbd-clear { background: none; border: none; color: inherit; opacity: .5; cursor: pointer; font: inherit; font-size: .74rem; font-weight: 700; }
.tb-drop .tbd-clear:hover { opacity: 1; }
.tb-drop .tbd-browse {
  display: block; text-align: center; padding: 11px 8px; margin-top: 4px;
  border-top: 1px solid rgba(31,31,31,.06);
  font-size: .82rem; font-weight: 700; text-decoration: none; color: inherit; opacity: .85;
}
.tb-drop .tbd-browse:hover { opacity: 1; background: rgba(31,31,31,.05); }

/* Up-next queue block in the right sidebar. Reuses .rp-rel-row visuals; adds a
   subtitle chip on the section title and a subtle numeric badge on the row. */
.rp-queue { display: flex; flex-direction: column; gap: 2px; }
.rp-rel-sub { font-weight: 600; font-size: .68rem; opacity: .55; letter-spacing: 0; }
.rp-q-row .rp-rel-cov { background-color: rgba(255,255,255,.06); background-size: cover; background-position: center; position: relative; display: flex; align-items: center; justify-content: center; }
.rp-q-row .rp-rel-cov::before {
  content: attr(data-qidx-lbl);
  font-family: inherit; font-weight: 800; font-size: .78rem;
  color: rgba(255,255,255,.55);
  font-variant-numeric: tabular-nums;
}

/* On desktop the sheet is context, not control — the sticky player bar owns
   play/pause. Clicking a track anchor auto-commits, so the chip is redundant.
   Keep it on mobile where the sheet is the commit surface (preview → chip). */
@media (min-width: 768px) {
  .rp-play-cta { display: none; }
}

/* Right-sidebar title also becomes a jump-to-detail. Same rationale as mini. */
.app-right .rp-title-lg { transition: color .15s; }
.app-right .rp-title-lg:hover { color: var(--gold, #e7c873); }

/* ==== Sheet transport (mobile-only): prev / big play / next ==== */
.rp-transport {
  display: none;
  align-items: center; justify-content: center;
  gap: 26px;
  margin: 18px 0 4px;
}
.rp-transport .rpt-btn {
  width: 52px; height: 52px;
  background: none; border: none;
  color: rgba(255,255,255,.85);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; border-radius: 50%;
}
.rp-transport .rpt-btn:active { background: rgba(255,255,255,.08); }
.rp-transport .rpt-btn svg { width: 26px; height: 26px; }
.rp-transport .rpt-play {
  width: 72px; height: 72px; border-radius: 50%;
  background: #fff; color: #0a0a0a; border: none;
  display: flex; align-items: center; justify-content: center;
  cursor: pointer;
  box-shadow: 0 14px 30px -10px rgba(0,0,0,.6);
}
.rp-transport .rpt-play svg { width: 30px; height: 30px; }
@media (max-width: 767px) {
  .rp-transport { display: flex; }
  .rp-play-cta { display: none; }   /* transport replaces the single CTA chip */
}

/* ==== Topbar avatar menu (profile + logout) ==== */
.tb-avawrap { position: relative; }
.tb-avamenu {
  /* T-16/L-04: the avatar sits at the screen edge — the menu must open INWARD.
     inline-start grew it toward the physical left in RTL and half of it left
     the monitor; inline-end pins the inner side instead. */
  position: absolute; top: calc(100% + 8px); inset-inline-end: 0;
  max-width: calc(100vw - 12px);
  background: #14141c;
  border: 1px solid rgba(255,255,255,.1);
  border-radius: 12px;
  padding: 6px;
  min-width: 170px;
  z-index: 200;
  box-shadow: 0 18px 44px -12px rgba(0,0,0,.7);
}
.tb-avamenu a, .tb-avamenu button {
  display: flex; align-items: center; gap: 10px;
  width: 100%; padding: 10px 12px;
  background: none; border: none;
  color: #fff; font: inherit; font-size: .86rem;
  text-align: start; cursor: pointer;
  border-radius: 8px; text-decoration: none;
}
.tb-avamenu a:hover, .tb-avamenu button:hover { background: rgba(255,255,255,.06); }
.tb-avamenu .tb-out { color: #ff6b81; }

/* ==== Mobile topbar compaction ====
   The brand PNG is wide (VIP | رادیو ایرانیان lockup) — with burger + search +
   avatar it overflowed the 390px viewport by ~100px, making EVERY page shell
   horizontally scrollable. Shrink + let search flex down; belt-and-braces
   overflow-x guard on the shell root so no future absolutely-positioned or
   transformed child can widen the page again. */
@media (max-width: 767px) {
  .app-top { gap: 8px; padding-inline: 10px; }
  .tb-left { gap: 4px; }
  .tb-brand img { height: 38px !important; }
  .tb-search { min-width: 0; flex: 1 1 auto; padding: 6px 10px; }
  .tb-search input { min-width: 0; }
  .tb-user { gap: 6px; }
}
html, body { overflow-x: hidden; }

/* When the Now-Playing sheet is open on mobile, it IS the player — hide the mini
   bar underneath (its play/next were duplicating the sheet's transport) and let
   the sheet claim that strip of screen too. Slide-down keeps the close animation
   feeling continuous. */
@media (max-width: 767px) {
  .app.right-open .app-right { bottom: 0; }
}

/* ==== mobile search panel (#mSrch) — operator 2026-07-05 ===================
   Opened by the topbar #tbSearchBtn (≤767). Top-anchored overlay: the field sits
   where the topbar was so the rising keyboard never covers it, live results drop
   below, and the «دسته‌بندی‌ها» button just loads /browse. Reuses the .tb-drop
   result styles; #mSrchQ is wired by shell-search.js beside the desktop bar. */
/* Closed = kept in the DOM but visibility:hidden + pointer-events:none, NOT
   display:none — a card can't transition in from display:none (it sticks at the
   start of the slide). visibility delays until the slide-out finishes. */
.msrch {
  position: fixed; inset: 0; z-index: 200;
  visibility: hidden; pointer-events: none;
  transition: visibility 0s .26s;
}
.msrch.open { visibility: visible; pointer-events: auto; transition: visibility 0s 0s; }
.msrch-bd {
  position: absolute; inset: 0; border: none; cursor: default;
  background: rgba(0,0,0,.55); opacity: 0; transition: opacity .22s ease;
}
.msrch.open .msrch-bd { opacity: 1; }
.msrch-card {
  position: absolute; top: 0; inset-inline: 0;
  background: var(--bg, #0c0c12);
  border-bottom: 1px solid var(--line, rgba(255,255,255,.1));
  box-shadow: 0 24px 60px rgba(0,0,0,.6);
  padding: calc(8px + env(safe-area-inset-top, 0px)) 12px 14px;
  transform: translateY(-100%); transition: transform .24s cubic-bezier(.4,0,.2,1);
}
.msrch.open .msrch-card { transform: translateY(0); }
.msrch-row { display: flex; align-items: center; gap: 8px; }
.msrch-field {
  flex: 1; display: flex; align-items: center; min-width: 0;
  background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.1);
  border-radius: 22px; height: 46px; padding: 0 15px;
}
.msrch-field .tbs-ic { width: 19px; height: 19px; opacity: .55; flex-shrink: 0; margin-inline-end: 9px; }
.msrch-field input {
  flex: 1; min-width: 0; padding: 0;
  background: none; border: none; outline: none;
  color: #fff; font: inherit; font-size: 16px;
}
.msrch-field input::placeholder { color: rgba(255,255,255,.45); font-size: 15px; }
.msrch-cats, .msrch-x {
  flex-shrink: 0; width: 46px; height: 46px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 14px; cursor: pointer;
}
.msrch-cats { background: #e7c873; color: #16130a; border: none; }   /* «دسته‌بندی‌ها» shortcut → /browse */
.msrch-cats svg { width: 23px; height: 23px; }
.msrch-x { background: none; border: none; color: rgba(255,255,255,.7); }
.msrch-x:hover { color: #fff; }
.msrch-x svg { width: 22px; height: 22px; }
/* results render inline in the card (not the floating popover the topbar uses) */
.msrch-drop {
  position: static; inset: auto; transform: none;
  margin-top: 12px; width: auto; max-width: none;
  max-height: calc(100vh - 150px);
  max-height: calc(100dvh - 150px);
  overflow: auto;
  background: none; border: none; box-shadow: none; backdrop-filter: none; border-radius: 0;
}
html.msrch-lock, html.msrch-lock body { overflow: hidden; }

/* --- RADIO-CONTEXT STRIP (shared across every .mhero page, 2026-07-09) ------
   "you're currently listening to a closed artist radio (فقط X / بهترین‌های X)":
   a small label + save-to-my-lists + share, sitting near the page's own cover.
   Originated on /song (song-specific styling there, .sng-like); this is the
   same #radioCtx/#radioCtxLabel/#radioCtxSave/#radioCtxShare contract but with
   its own self-contained buttons, since list.php/vip_album.php/mood_station.php
   /program.php/radio.php/browse.php each have their own unrelated action-row
   classes. IR2UI.initRadioCtx() (ui.js) wires it up; paper.css's marker-
   underline on .radioctx .hdlabel already applies here unscoped. */
.radioctx { display: flex; align-items: center; justify-content: center; gap: 10px; margin-bottom: 10px; text-align: center; direction: rtl; }
.radioctx .hdlabel { font-weight: 800; font-size: 1rem; }
.radioctx-acts { display: inline-flex; gap: 4px; }
.radioctx-acts button { background: none; border: none; color: rgba(255,255,255,.65); cursor: pointer; padding: 6px; display: flex; }
.radioctx-acts button svg { width: 19px; height: 19px; }
/* [hidden] needs a real override here: the browser's own [hidden]{display:none}
   UA rule is EQUAL specificity to the plain-class display:flex rules above, and
   at equal specificity author CSS beats the UA stylesheet regardless of source
   order: so IR2UI.initRadioCtx() setting .hidden=true never actually hid
   anything, the strip and both buttons stayed visible always (confirmed live,
   2026-07-09: closed:false yet computed display was still "flex"). This is why
   the operator saw the icons on every browse/list page and clicking them did
   nothing useful (no real closed-radio queue behind them). */
.radioctx[hidden] { display: none; }
.radioctx-acts button[hidden] { display: none; }

/* --- THE PANEL HANDLE (operator 2026-09-10) ------------------------------
   A tablet at 820px gave the playing panel forty percent of the screen, so it
   collapses now — and his condition was that a reader must be able to SEE that
   it opens and closes. Everything below serves that:

   Always visible in both states, because a handle revealed on hover is a handle
   a touch device never finds. Sat on the seam, because a control at the join
   between two panes needs no label. And a vertical PILL, deliberately not the
   round .ir2-hn-btn shape — the rail arrows use that shape AND sit at this very
   edge, so a circle here would have read as "scroll this row sideways".

   Nothing is lost while it is closed: the mini player already carries what is
   playing, which is why the panel collapses to zero rather than to a stub. */
.ir2-panel-toggle {
  position: fixed; z-index: 120;
  top: calc(var(--top-h) + (100vh - var(--top-h) - var(--player-h)) / 2);
  transform: translateY(-50%);
  /* One rule owns this position and panel-toggle.js writes the value inline.

     The commit that made this change said the cascade was misbehaving. It was
     not: the measurements accusing it came from a hidden browser pane, which is
     not rendered, so every computed value read back was stale. The single
     declaration is still the simpler arrangement, but CSS was never the bug. */
  left: var(--panel-seam, var(--right-w));
  width: 22px; height: 62px;
  display: none;                       /* shown from 768 up, below the media query */
  align-items: center; justify-content: center;
  padding: 0; border: 0; cursor: pointer;
  border-radius: 0 8px 8px 0;          /* flat against the panel, rounded toward the page */
  background: var(--bg);
  box-shadow: 1px 0 0 rgba(255,255,255,.10), 3px 0 10px -4px rgba(0,0,0,.5);
  color: var(--muted);
  transition: left .22s ease, color .15s, background .15s;
}
.ir2-panel-toggle:hover { color: var(--gold, #e7c873); background: var(--bg); }
.ir2-panel-toggle svg { width: 15px; height: 15px; }

.app.right-collapsed { grid-template-columns: 1fr 0; }
.app.right-collapsed .app-right { display: none; }
/* .ir2-panel-off is still set - the pill's rounding flips with it - but the
   POSITION comes from --panel-seam, written by panel-toggle.js */
/* the rounding is written by panel-toggle.js now, because it flips with the
   state the same way the chevron does */

@media (min-width: 768px) {
  .ir2-panel-toggle { display: flex; }
  /* nothing is playing, so there is no panel to reveal — offering to open an
     empty column is worse than offering nothing */
  html.ir2-panel-idle .ir2-panel-toggle { display: none; }
}

/* ── the mobile header ‹ › and the always-open search bar (2026-09-11) ───────
   mobile-chrome.js moves the two nodes; these are the clothes they wear once
   they get there. Nothing here exists on a wide screen - the elements are back
   in their original places and their original rules apply untouched. */

.msbar { display: none; }

@media (max-width: 767px) {
  /* ‹ › IN THE HEADER.
     They were sticky at the top of the scrolling column with height:0, floating
     over whatever began there - which, since the banner's play square was made
     flush with its edges, was the play square. In the header they cover
     nothing, and they read as what they are. */
  .app-top .ir2-histnav {
    position: static; height: auto; padding: 0; gap: 5px;
    align-items: center; align-self: center;
  }
  .app-top .ir2-hn-btn { width: 34px; height: 34px; margin-top: 0; }
  .app-top .ir2-hn-btn svg { width: 18px; height: 18px; }

  /* THE SEARCH BAR, OPEN.
     Wider, taller and more solid than the topbar version it used to be: this is
     a field a thumb aims at, not one squeezed between a logo and an avatar. The
     icon that used to summon it has nothing left to summon. */
  .msbar:not([hidden]) {
    display: block; padding: 9px 10px 11px;
    background: var(--bg);
    border-bottom: 1px solid rgba(255,255,255,.07);
    flex-shrink: 0; z-index: 49;
  }
  .msbar .tb-search {
    display: flex; height: 46px; max-width: none; width: 100%;
    border-radius: 14px; border-width: 1.5px;
    padding: 0 14px;
    box-shadow: 0 2px 10px rgba(0,0,0,.10);
  }
  .msbar .tb-search .tbs-ic { width: 20px; height: 20px; opacity: .7; margin-inline-end: 10px; }
  .msbar .tb-search input { font-size: 16px; font-weight: 600; }
  .msbar .tb-search input::placeholder { font-size: 15px; font-weight: 500; }
  html.ir2-msbar .tb-searchbtn { display: none; }

  /* the results hang off the field again. The fixed-position rule above was
     written when the field was inside the topbar and had to escape it; here the
     field is already full width, so anchoring to it is both simpler and right. */
  .msbar .tb-drop {
    position: absolute; top: calc(100% + 6px); left: 0; right: 0;
    transform: none; width: auto; max-width: none;
    max-height: calc(100svh - var(--top-h) - 150px);
  }
}

/* ── the mobile ☰ menu (operator 2026-09-11) ─────────────────────────────────
   Built on the search sheet's shape - backdrop, a card down from under the
   header, ✕ - because that is the dismissal this site settled on, and a second
   pattern for the same gesture is a second thing for a reader to learn. */
.tb-burger { display: none; }

@media (max-width: 767px) {
  .tb-burger {
    display: inline-flex; align-items: center; justify-content: center;
    width: 38px; height: 38px; border-radius: 10px;
    background: none; border: none; color: #fff; cursor: pointer;
  }
  .tb-burger svg { width: 23px; height: 23px; }
}

.mmenu {
  position: fixed; inset: 0; z-index: 200;
  visibility: hidden; pointer-events: none;
  transition: visibility 0s .26s;
}
.mmenu.open { visibility: visible; pointer-events: auto; transition: visibility 0s 0s; }
.mmenu-bd {
  position: absolute; inset: 0; border: none; cursor: default;
  background: rgba(0,0,0,.55); opacity: 0; transition: opacity .22s ease;
}
.mmenu.open .mmenu-bd { opacity: 1; }
.mmenu-card {
  position: absolute; top: 0; inset-inline: 0;
  background: var(--bg, #0c0c12);
  border-bottom: 1px solid var(--line, rgba(255,255,255,.1));
  box-shadow: 0 24px 60px rgba(0,0,0,.6);
  padding: calc(8px + env(safe-area-inset-top, 0px)) 12px 16px;
  transform: translateY(-100%); transition: transform .24s cubic-bezier(.4,0,.2,1);
}
.mmenu.open .mmenu-card { transform: translateY(0); }
.mmenu-head { display: flex; align-items: center; justify-content: space-between; padding: 2px 4px 10px; }
.mmenu-ttl { font-size: .82rem; font-weight: 800; color: rgba(255,255,255,.55); }
.mmenu-x { background: none; border: none; color: rgba(255,255,255,.7); cursor: pointer;
           width: 40px; height: 40px; display: inline-flex; align-items: center; justify-content: center; }
.mmenu-x svg { width: 22px; height: 22px; }

.mmenu-list { display: flex; flex-direction: column; gap: 2px; }
/* Full-width rows rather than a grid of icons. The bottom bar had to fit four
   across a phone; a sheet does not, and a row a thumb cannot miss is worth more
   than the shape being preserved. */
.mmi {
  display: flex; align-items: center; gap: 13px;
  padding: 13px 12px; border-radius: 12px;
  color: #fff; text-decoration: none; font-size: 1rem; font-weight: 600;
  /* one of these rows is a <button> - the classic station is started, not
     opened - and a button brings its own background, border, font and centring
     that would make it the one row shaped differently from the rest */
  width: 100%; box-sizing: border-box; text-align: start;
  background: none; border: none; font-family: inherit; cursor: pointer;
}
.mmi svg { width: 22px; height: 22px; flex-shrink: 0; opacity: .75; }
.mmi:active { background: rgba(255,255,255,.08); }
.mmi.active { color: var(--gold, #e7c873); background: rgba(255,255,255,.06); }
.mmi.active svg { opacity: 1; }
/* the profile row wears the reader's own picture where the others have an icon,
   at the same optical weight: a hair larger, because a circle reads smaller than
   a line drawing of the same box */
.mmi-ava {
  flex-shrink: 0; width: 26px; height: 26px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  overflow: hidden; background: rgba(255,255,255,.12);
}
.mmi-ava img { width: 100%; height: 100%; object-fit: cover; display: block; }
.mmi-ava svg { width: 20px; height: 20px; opacity: .75; }
.mmi-ini { font-size: .82rem; font-weight: 800; }
html.mmenu-lock, html.mmenu-lock body { overflow: hidden; }
