:root {
  --bg: #f4f7fb;
  /* Text meant to sit on top of --accent/--primary/--navy fills. Light-mode
     accents are all dark hues (navy/forest-green/brown) so white works; dark
     mode's accents (#22d3ee, #48d49b, #d6a274) were picked to pop against a
     near-black page and are themselves light, so white-on-accent measured
     1.81:1 there -- under half the 4.5:1 minimum for normal text. */
  --on-accent: #fff;
  --panel: #ffffff;
  --panel-2: #f8faff;
  --text: #172033;
  --muted: #65708a;
  --line: #dce3ef;
  --navy: #2458d6;
  --navy-2: #1d49b8;
  --gold: #2458d6;
  --gold-soft: #edf3ff;
  --green: #17a673;
  --green-soft: #e9f8f2;
  --red: #e45b5b;
  --red-soft: #fff0f0;
  --warning: #d99a24;
  --shadow: 0 4px 16px rgba(11, 23, 57, .06);
  --radius: 12px;
  --sidebar: 232px;
  --font: Vazirmatn, IRANSansX, IRANSans, Tahoma, Arial, sans-serif;
  --border: var(--line);
  --surface: var(--panel);
  --surface-2: var(--panel-2);
  --surface-3: #eef3fb;
  --panel-soft: var(--panel-2);
  --hover: var(--gold-soft);
  --accent: var(--navy);
  --primary: var(--navy);
}

html[data-theme="dark"] {
  --bg: #060a12;
  --on-accent: #060a12;
  --panel: #0c1422;
  --panel-2: #111c2d;
  --text: #e7eef7;
  --muted: #8fa2b8;
  --line: #1c2b3d;
  --navy: #22d3ee;
  --navy-2: #39bdf7;
  --gold: #22d3ee;
  --gold-soft: #10253a;
  --green: #34d399;
  --red: #fb7185;
  --warning: #f59e0b;
  --green-soft: #102a24;
  --red-soft: #30171b;
  --surface-3: #16243a;
  --shadow: none;
}

html[data-accent="green"] { --navy:#16845b; --navy-2:#116b49; --gold:#16845b; --gold-soft:#eaf7f1; --accent:#16845b; --primary:#16845b; }
html[data-accent="brown"] { --navy:#8b5e3c; --navy-2:#70472d; --gold:#8b5e3c; --gold-soft:#f7f0ea; --accent:#8b5e3c; --primary:#8b5e3c; }
html[data-theme="dark"][data-accent="green"] { --navy:#48d49b; --navy-2:#2fc58a; --gold:#48d49b; --gold-soft:#102a22; --accent:#48d49b; --primary:#48d49b; }
html[data-theme="dark"][data-accent="brown"] { --navy:#d6a274; --navy-2:#c98d5e; --gold:#d6a274; --gold-soft:#2b211b; --accent:#d6a274; --primary:#d6a274; }

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  min-width: 320px;
}
button, input, select { font: inherit; }
button { cursor: pointer; }
a { color: inherit; text-decoration: none; }

.app-shell { min-height: 100vh; }
.sidebar {
  position: fixed;
  inset: 0 0 0 auto;
  width: var(--sidebar);
  background: var(--panel);
  border-left: 1px solid var(--line);
  z-index: 30;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 18px 10px;
  direction: rtl;
}
.brand-mark {
  width: 54px;
  height: 54px;
  display: grid;
  place-items: center;
  margin-bottom: 24px;
}
.brand-mark svg { width: 44px; height: 44px; fill: none; stroke: var(--gold); stroke-width: 2; }
.side-nav { display: flex; flex-direction: column; gap: 8px; width: 100%; }
.nav-item {
  min-height: 64px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  border-radius: 14px;
  color: var(--muted);
  font-size: 11px;
  transition: .2s ease;
}
.nav-item:hover { background: var(--panel-2); color: var(--navy); }
.nav-item.active { color: #b67800; background: var(--gold-soft); }
.nav-icon { font-size: 22px; line-height: 1; }
.sidebar-footer { margin-top: auto; }

.main-content { margin-right: var(--sidebar); padding: 20px 24px 28px; direction: rtl; }
.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  margin-bottom: 16px;
}
.title-wrap { display: flex; align-items: center; gap: 18px; }
/* Topbar title block: allow it to shrink. */
/* The inner <div> holding kicker/h1/description is a flex item, so its
   default min-width:auto stops it shrinking below min-content and pushes
   the whole header wider than the dashboard below it. The kicker is the
   widest unbreakable run in there, which is why it is the visible symptom. */
.title-wrap > div { min-width: 0; }
.report-kicker { max-width: 100%; overflow-wrap: anywhere; }
.title-wrap h1 { margin: 0; direction: rtl; text-align: right; font-size: clamp(24px, 2.2vw, 34px); letter-spacing: -.5px; }
.title-wrap p { margin: 4px 0 0; color: var(--muted); font-size: 13px; }
.connection-badge {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  border: 1px solid var(--line);
  background: var(--panel);
  border-radius: 999px;
  padding: 7px 10px;
  color: var(--muted);
  font-size: 12px;
}
.connection-badge i { width: 8px; height: 8px; border-radius: 50%; background: #9da8b9; }
.connection-badge.live i, .connection-badge.online i { background: var(--green); box-shadow: 0 0 0 4px rgba(22,138,85,.12); }
.connection-badge.error i, .connection-badge.offline i { background: var(--red); }
.connection-badge.connecting i { background: var(--gold); animation: pulse 1.4s infinite; }
.topbar-actions { display: flex; align-items: center; justify-content: flex-end; gap: 10px; flex-wrap: wrap; }
.clock-box { text-align: left; direction: rtl; padding-inline: 8px; }
.clock-box strong, .clock-box span { display: block; }
.clock-box strong { font-size: 13px; }
.clock-box span { color: var(--muted); font-size: 12px; margin-top: 3px; }

.icon-button, .primary-button, .secondary-button {
  border: 0;
  border-radius: 11px;
  min-height: 42px;
  padding: 0 15px;
  transition: transform .15s ease, opacity .15s ease, background .15s ease;
}
.icon-button { width: 42px; padding: 0; background: var(--panel); color: var(--navy); border: 1px solid var(--line); font-size: 20px; }
.primary-button { background: var(--navy); color: var(--on-accent); box-shadow: 0 6px 16px rgba(11,43,97,.2); }
.secondary-button { background: var(--panel); color: var(--text); border: 1px solid var(--line); }
.primary-button:hover, .secondary-button:hover, .icon-button:hover { transform: translateY(-1px); }
.primary-button.compact { min-height: 40px; font-size: 12px; }
button:disabled { cursor: not-allowed; opacity: .45; transform: none !important; }

.filter-panel, .panel, .kpi-card {
  background: var(--panel);
  border: 1px solid var(--line);
  box-shadow: var(--shadow);
}
.filter-panel { border-radius: var(--radius); padding: 14px; margin-bottom: 14px; }
.filter-grid {
  display: grid;
  grid-template-columns: 1.05fr 1.8fr .9fr .9fr 1fr 1.5fr auto;
  align-items: end;
  gap: 10px;
}
.field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.field > span, .field legend, .inline-search span, .small-select { color: var(--muted); font-size: 11px; }
.field input, .field select, .inline-search input, .small-select select {
  width: 100%;
  min-height: 40px;
  border: 1px solid var(--line);
  background: var(--panel-2);
  color: var(--text);
  border-radius: 10px;
  padding: 0 11px;
  outline: none;
}
.field input:focus, .field select:focus, .inline-search input:focus, .small-select select:focus { border-color: var(--gold); box-shadow: 0 0 0 3px rgba(242,173,22,.12); }
.block-field { margin: 0; border: 0; padding: 0; flex-direction: row; flex-wrap: wrap; align-items: center; gap: 6px; }
.block-field legend { width: 100%; padding: 0; }
.block-field label { position: relative; }
.block-field input { position: absolute; opacity: 0; pointer-events: none; }
.block-field label span { display: inline-flex; align-items: center; min-height: 40px; padding: 0 12px; border: 1px solid var(--line); border-radius: 10px; background: var(--panel-2); color: var(--muted); font-size: 12px; }
.block-field input:checked + span { border-color: rgba(242,173,22,.55); background: var(--gold-soft); color: #9d6800; font-weight: 700; }
.filter-actions { display: flex; gap: 8px; }

.kpi-grid { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 12px; margin-bottom: 14px; }
.kpi-card { border-radius: 15px; padding: 15px; min-height: 112px; display: flex; gap: 12px; align-items: flex-start; }
.kpi-icon { flex: 0 0 38px; width: 38px; height: 38px; border-radius: 11px; background: var(--gold-soft); color: #d48c00; display: grid; place-items: center; font-size: 20px; }
.kpi-card > div:last-child { min-width: 0; }
.kpi-card span, .kpi-card strong, .kpi-card small { display: block; }
.kpi-card span { color: var(--muted); font-size: 11px; margin-bottom: 7px; }
.kpi-card strong { font-size: clamp(19px, 1.7vw, 26px); direction: ltr; text-align: right; white-space: nowrap; }
.kpi-card small { margin-top: 6px; color: var(--muted); font-size: 10px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.kpi-card small.positive { color: var(--green); }
.kpi-card small.negative { color: var(--red); }
.status-card .kpi-icon { color: var(--green); background: var(--green-soft); }

.dashboard-grid, .upper-grid, .lower-grid { display: grid; gap: 14px; margin-bottom: 14px; }
.upper-grid { grid-template-columns: minmax(620px, 1.35fr) minmax(420px, 1fr); }
.lower-grid { grid-template-columns: minmax(680px, 1.45fr) minmax(380px, .78fr); align-items: start; }
.panel { border-radius: var(--radius); overflow: hidden; min-width: 0; }
.panel-header { min-height: 62px; padding: 14px 16px 10px; display: flex; align-items: center; justify-content: space-between; gap: 12px; border-bottom: 1px solid var(--line); }
.panel-header h2 { margin: 0; font-size: 15px; direction: rtl; text-align: right; }
.panel-header p { margin: 4px 0 0; color: var(--muted); font-size: 10px; }
.panel-actions { display: flex; gap: 8px; }
.small-select { display: flex; align-items: center; gap: 7px; }
/* Sized to its widest option rather than a fixed 145px: the range options
   ("۹۰ روز", "یک سال", "دو سال") are all shorter than that, and the surplus
   rendered as dead space between the value and the chevron. */
.small-select select { min-height: 34px; width: auto; min-width: 0; background: var(--panel); }
.chart-legend { display: flex; gap: 12px; color: var(--muted); font-size: 10px; }
.chart-legend span { display: inline-flex; align-items: center; gap: 5px; }
.dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; background: var(--gold); }
.dot.block { background: var(--navy-2); }

.table-scroll { overflow: auto; max-width: 100%; }
.data-table { border-collapse: collapse; width: 100%; min-width: 920px; font-size: 11px; }
.shareholders-table { min-width: 760px; }
.data-table thead th { position: sticky; top: 0; z-index: 2; background: var(--panel); color: #2d5eaa; font-size: 10px; text-align: right; padding: 11px 12px; border-bottom: 1px solid var(--line); white-space: nowrap; }
.data-table tbody td { padding: 9px 12px; border-bottom: 1px solid var(--line); white-space: nowrap; vertical-align: middle; }
.data-table tbody tr:hover { background: var(--panel-2); }
.data-table tbody tr:last-child td { border-bottom: 0; }
.symbol-cell { color: #1a59a8; font-weight: 700; }
.numeric { direction: ltr; text-align: right; font-variant-numeric: tabular-nums; }
/* A numeric column's HEADER is Persian prose, not a number. Inherited
   direction:ltr resolves the neutral characters around it as ltr, so
   "قیمت اعمال (ریال)" renders with its parentheses on the wrong side. The
   cells keep ltr; only the header is put back. */
th.numeric { direction: rtl; }
.positive { color: var(--green) !important; }
.negative { color: var(--red) !important; }
.change-cell.positive { background: var(--green-soft); }
.change-cell.negative { background: var(--red-soft); }
.block-pill, .type-pill { display: inline-flex; align-items: center; justify-content: center; border-radius: 999px; padding: 3px 8px; font-size: 9px; background: var(--panel-2); color: var(--muted); border: 1px solid var(--line); }
.block-pill.block { background: #eef3ff; color: #315fa8; }
.loading-cell, .empty-cell { text-align: center !important; color: var(--muted); padding: 30px !important; }
.panel-footer { min-height: 50px; border-top: 1px solid var(--line); display: flex; justify-content: space-between; align-items: center; padding: 0 14px; color: var(--muted); font-size: 10px; }
.pagination { display: flex; align-items: center; gap: 8px; }
.pagination button { width: 30px; height: 30px; border: 1px solid var(--line); background: var(--panel); color: var(--text); border-radius: 8px; }
.pagination span { min-width: 26px; text-align: center; color: #a46c00; background: var(--gold-soft); border-radius: 7px; padding: 5px; }

.plot-container { height: 420px; min-height: 320px; }
.mini-plot { height: 220px; }
.inline-search { display: flex; align-items: center; gap: 8px; }
.inline-search input { min-height: 34px; width: 210px; }
.side-widgets { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 14px; }
.compact-panel { min-height: 250px; }
.compact-panel .panel-header { min-height: 56px; }
.compact-panel .panel-header h2 { direction: rtl; }
.event-list { padding: 8px 14px 14px; }
.event-item { display: grid; grid-template-columns: 58px 1fr auto; gap: 8px; align-items: center; padding: 10px 0; border-bottom: 1px solid var(--line); }
.event-item:last-child { border-bottom: 0; }
.event-date { color: #1f5aa3; font-weight: 700; font-size: 11px; }
.event-name { font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.event-amount { direction: ltr; color: var(--muted); font-size: 10px; }
.empty-state { min-height: 150px; display: grid; place-items: center; color: var(--muted); font-size: 11px; }
.movers-columns { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; padding: 12px 14px; }
.movers-columns h3 { margin: 0 0 6px; color: var(--muted); font-size: 10px; }
.mover-item { display: grid; grid-template-columns: 1fr auto; gap: 8px; padding: 7px 0; border-bottom: 1px solid var(--line); font-size: 10px; }
.mover-item strong { font-weight: 600; color: #1f5aa3; }
.breakdown-list { padding: 12px 14px; }
.breakdown-item { display: grid; grid-template-columns: 70px 1fr auto; align-items: center; gap: 8px; margin-bottom: 12px; font-size: 10px; }
.breakdown-track { height: 8px; border-radius: 99px; background: var(--line); overflow: hidden; }
.breakdown-fill { height: 100%; border-radius: inherit; background: linear-gradient(90deg, #f2ad16, #ffd875); }
.breakdown-value { direction: ltr; color: var(--muted); }

.page-footer { display: flex; justify-content: space-between; color: var(--muted); font-size: 10px; padding: 4px 2px 0; }
.toast { position: fixed; z-index: 100; left: 50%; bottom: 24px; transform: translate(-50%, 20px); min-width: 220px; max-width: min(520px, calc(100vw - 30px)); background: #102854; color: #fff; padding: 12px 16px; border-radius: 11px; box-shadow: 0 14px 40px rgba(0,0,0,.25); opacity: 0; pointer-events: none; transition: .2s ease; font-size: 12px; text-align: center; }
.toast.show { opacity: 1; transform: translate(-50%, 0); }
.toast.error { background: #a42d37; }
.loading-overlay { position: fixed; z-index: 80; inset: 0; background: rgba(245,247,251,.58); backdrop-filter: blur(2px); display: flex; align-items: center; justify-content: center; flex-direction: column; gap: 10px; color: var(--navy); font-size: 12px; }
.loading-overlay.hidden { display: none; }
.spinner { width: 34px; height: 34px; border: 3px solid rgba(11,43,97,.15); border-top-color: var(--gold); border-radius: 50%; animation: spin .8s linear infinite; }

@keyframes spin { to { transform: rotate(360deg); } }
@keyframes pulse { 50% { opacity: .35; } }


/* Shared report-shell refinements. */
.brand-mark { color: inherit; }
.brand-mark:focus-visible { border-radius: 14px; }
.report-kicker { display: block; margin-bottom: 5px; color: #a46c00; font-size: 10px; font-weight: 800; letter-spacing: .02em; }
.unit-note { display: block; margin-top: 5px; color: var(--muted); font-size: 9px; direction: rtl; }
.ltr { direction: ltr !important; unicode-bidi: isolate; }

@media (max-width: 760px) {
  .topbar { flex-direction: column; }
  .topbar-actions { width: 100%; justify-content: space-between; }
  .account-chip .who span { display: none; }
  .account-chip { padding-inline: 7px; }
}

@media (max-width: 1500px) {
  .filter-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .wide-search { grid-column: span 2; }
  .filter-actions { justify-content: flex-end; }
  .kpi-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .upper-grid, .lower-grid { grid-template-columns: 1fr; }
  .side-widgets { grid-template-columns: repeat(4, minmax(0,1fr)); }
}

@media (max-width: 1100px) {
  :root { --sidebar: 72px; }
  .main-content { padding: 16px; }
  .side-widgets { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .filter-grid { grid-template-columns: repeat(3, minmax(0,1fr)); }
  .wide-search { grid-column: span 1; }
}

@media (max-width: 760px) {
  .sidebar { position: fixed; inset: auto 0 0 0; width: 100%; height: 68px; padding: 6px 10px; border-right: 0; border-left: 0; border-top: 1px solid var(--line); flex-direction: row; }
  .brand-mark, .sidebar-footer { display: none; }
  .side-nav { flex-direction: row; justify-content: space-around; gap: 2px; }
  .nav-item { min-height: 54px; flex: 1; font-size: 9px; }
  .nav-icon { font-size: 18px; }
  .main-content { margin-right: 0; padding: 12px 10px 88px; }
  .topbar { align-items: flex-start; }
  .title-wrap { align-items: flex-start; flex-direction: column; gap: 8px; }
  .title-wrap h1 { font-size: 23px; }
  .title-wrap p { font-size: 10px; }
  .clock-box { display: none; }
  .topbar-actions .primary-button { display: none; }
  .filter-panel { padding: 10px; }
  .filter-grid { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .block-field, .wide-search, .filter-actions { grid-column: 1 / -1; }
  .filter-actions button { flex: 1; }
  .kpi-grid { grid-template-columns: repeat(2, minmax(0,1fr)); gap: 8px; }
  .kpi-card { min-height: 96px; padding: 12px; }
  .kpi-icon { display: none; }
  .kpi-card strong { font-size: 20px; }
  .panel-header { align-items: flex-start; flex-direction: column; }
  .small-select, .small-select select, .inline-search, .inline-search input { width: 100%; }
  .plot-container { height: 340px; }
  .side-widgets { grid-template-columns: 1fr; }
  .compact-panel { min-height: 220px; }
  .page-footer { flex-direction: column; gap: 5px; }
}

@media (max-width: 420px) {
  .kpi-grid { grid-template-columns: 1fr; }
  .field { grid-column: 1 / -1; }
  .movers-columns { grid-template-columns: 1fr; }
}

/* Plotly is intentionally lazy-loaded. The report table and KPIs become usable
   before the 4.7 MB chart runtime is downloaded and parsed. */
.chart-placeholder,
.chart-loading::before {
  min-height: 180px;
  display: grid;
  place-items: center;
  color: var(--muted);
  font-size: 11px;
  text-align: center;
  padding: 20px;
}
.chart-loading::before { content: 'در حال آماده‌سازی نمودار…'; }
.chart-loading > * { visibility: hidden; }

/* ------------------------------------------------------------- workspace -- */
.workspace-controls {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  padding: 4px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--panel);
}
.workspace-favorite,
.workspace-delete,
.workspace-save {
  min-height: 32px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--text);
  font: inherit;
  cursor: pointer;
}
.workspace-favorite {
  width: 34px;
  color: var(--gold);
  font-size: 21px;
  line-height: 1;
}
.workspace-favorite.active { background: var(--gold-soft); }
.workspace-save { padding: 0 10px; font-size: 11px; font-weight: 700; }
.workspace-save:hover { background: var(--panel-2); color: var(--navy); }
.workspace-delete { width: 30px; color: var(--red); font-size: 19px; }
.workspace-select {
  width: min(160px, 24vw);
  min-height: 32px;
  border: 0;
  border-inline-start: 1px solid var(--line);
  border-inline-end: 1px solid var(--line);
  border-radius: 0;
  background: transparent;
  color: var(--text);
  padding: 0 8px;
  font: inherit;
  font-size: 11px;
}
.workspace-controls button:disabled,
.workspace-controls select:disabled { opacity: .5; cursor: not-allowed; }

@media (max-width: 760px) {
  .workspace-controls { order: 20; width: 100%; }
  .workspace-select { flex: 1; width: auto; }
}

/* ================================================= instrument watchlist === */
.watch-cell { width: 42px; text-align: center !important; }
.watch-button {
  width: 30px;
  height: 30px;
  display: inline-grid;
  place-items: center;
  border: 1px solid var(--line);
  border-radius: 9px;
  background: var(--panel-2);
  color: var(--muted);
  cursor: pointer;
  font-size: 18px;
  line-height: 1;
  transition: transform .15s ease, background .15s ease, color .15s ease, border-color .15s ease;
}
.watch-button:hover { transform: translateY(-1px); color: var(--gold); border-color: var(--gold); }
.watch-button.active { color: #b67800; background: var(--gold-soft); border-color: transparent; }
.watch-button:disabled { opacity: .45; cursor: wait; transform: none; }

/* v1.7 Persian autocomplete and date conventions */
.autocomplete-wrap { position: relative; min-width: 0; }
.autocomplete-wrap > input { width: 100%; }
.autocomplete-list {
  position: absolute; z-index: 1200; inset-inline: 0; top: calc(100% + 6px);
  max-height: 320px; overflow: auto; padding: 6px;
  background: var(--panel); border: 1px solid var(--border); border-radius: 12px;
  box-shadow: 0 18px 46px rgba(9, 24, 48, .22);
}
.autocomplete-list.hidden { display: none; }
.autocomplete-item {
  width: 100%; border: 0; background: transparent; color: var(--text); cursor: pointer;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 9px 10px; border-radius: 8px; text-align: start;
}
.autocomplete-item:hover, .autocomplete-item.active { background: var(--hover); }
.autocomplete-item strong { font-size: 13px; }
.autocomplete-item span { color: var(--muted); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rial-value { direction: rtl; unicode-bidi: plaintext; white-space: nowrap; }
.jalali-input { direction: ltr; text-align: center; font-variant-numeric: tabular-nums; }
.module-status-banner { display:flex; gap:10px; align-items:center; padding:12px 14px; border-radius:12px; background:var(--panel-soft); border:1px solid var(--border); }
.module-status-banner.warn { border-color:#d59d27; }
.module-status-banner.error { border-color:#cf5b5b; }

/* v1.7.0 visual polish: compact watch action and chart explanation */
.sr-only {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  padding: 0 !important;
  margin: -1px !important;
  overflow: hidden !important;
  clip: rect(0, 0, 0, 0) !important;
  white-space: nowrap !important;
  border: 0 !important;
}
.watch-cell { width: 46px; min-width: 46px; max-width: 46px; padding-inline: 6px !important; }
.watch-header { color: #b67800 !important; font-size: 14px !important; text-align: center !important; }
.watch-header > span[aria-hidden="true"] { display: inline-grid; place-items: center; width: 26px; height: 26px; border-radius: 8px; background: var(--gold-soft); }
.chart-footnote { min-height: 34px; padding: 7px 16px 11px; color: var(--muted); font-size: 10px; border-top: 1px dashed var(--line); text-align: right; }


/* v1.7 dependency-free Persian calendar */
.jalali-picker { position: absolute; z-index: 10000; direction: rtl; padding: .75rem; border: 1px solid var(--border); border-radius: 16px; background: var(--surface); box-shadow: 0 18px 50px rgba(5,18,42,.28); color: var(--text); }
.jalali-picker.hidden { display: none; }
.jalali-picker-head, .jalali-picker-foot { display: flex; align-items: center; justify-content: space-between; gap: .5rem; }
.jalali-picker-head { margin-bottom: .65rem; }
.jalali-picker-head button, .jalali-picker-foot button { border: 1px solid var(--border); border-radius: 9px; background: var(--surface-2); color: var(--text); min-width: 34px; min-height: 32px; cursor: pointer; }
.jalali-picker-head strong { font-size: .92rem; }
.jalali-weekdays, .jalali-days { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; text-align: center; }
.jalali-weekdays { color: var(--muted); font-size: .74rem; margin-bottom: 4px; }
.jalali-weekdays span { padding: .2rem 0; }
.jalali-days button, .jalali-empty { aspect-ratio: 1; min-width: 0; border: 0; border-radius: 9px; background: transparent; color: var(--text); font: inherit; cursor: pointer; }
.jalali-days button:hover { background: var(--surface-3); }
.jalali-days button.today { box-shadow: inset 0 0 0 1px var(--accent); }
.jalali-days button.selected { background: var(--primary); color: var(--on-accent); }
.jalali-picker-foot { margin-top: .65rem; justify-content: flex-end; }
.jalali-picker-foot button { padding: 0 .65rem; }

/* ========================================================================
   v1.8 professional UI core — Cobalt Command Center
   A calm, information-dense shell shared by every dashboard.
   ===================================================================== */
body { line-height: 1.55; }
::selection { color: var(--on-accent); background: var(--primary); }

.sidebar {
  align-items: stretch;
  padding: 20px 14px;
  color: #e7eefc;
  background: #0b1739;
  border: 0;
  box-shadow: -10px 0 34px rgba(11, 23, 57, .08);
}
.brand-mark {
  width: 100%;
  height: 52px;
  margin: 0 0 22px;
  border-radius: 10px;
  background: rgba(255, 255, 255, .04);
}
.brand-mark:hover { background: rgba(255, 255, 255, .08); }
.brand-mark svg { width: 38px; height: 38px; stroke: #8fb1ff; }
.side-nav { gap: 5px; }
.nav-item {
  min-height: 46px;
  padding: 0 13px;
  flex-direction: row;
  justify-content: flex-start;
  gap: 11px;
  border-radius: 9px;
  color: #aebbd3;
  font-size: 12px;
  font-weight: 600;
}
.nav-icon { width: 24px; font-size: 19px; text-align: center; }
.nav-item:hover { color: #fff; background: rgba(255, 255, 255, .08); }
.nav-item.active { color: #fff; background: #2458d6; box-shadow: 0 8px 20px rgba(36, 88, 214, .28); }
.sidebar-footer { display: flex; justify-content: center; padding-top: 18px; }
.sidebar-footer .icon-button { color: #dbe7ff; background: rgba(255, 255, 255, .06); border-color: rgba(255, 255, 255, .14); }

.main-content { padding: 0 24px 30px; }
.topbar {
  position: sticky;
  top: 0;
  z-index: 25;
  min-height: 78px;
  margin: 0 -24px 18px;
  padding: 12px 24px;
  background: color-mix(in srgb, var(--bg) 91%, transparent);
  border-bottom: 1px solid color-mix(in srgb, var(--line) 82%, transparent);
  backdrop-filter: blur(14px);
}
.title-wrap { min-width: 0; gap: 14px; }
.title-wrap h1 { font-size: clamp(21px, 1.8vw, 28px); letter-spacing: -.35px; }
.report-kicker { color: var(--primary); letter-spacing: 0; }
.connection-badge { background: var(--panel); box-shadow: 0 2px 10px rgba(11, 23, 57, .05); }
.topbar-actions { min-width: 0; }

.icon-button, .primary-button, .secondary-button { border-radius: 9px; font-weight: 700; }
.primary-button { background: var(--primary); box-shadow: 0 6px 16px rgba(36, 88, 214, .2); }
.primary-button:hover { background: var(--navy-2); }
.secondary-button:hover, .icon-button:hover { color: var(--primary); border-color: color-mix(in srgb, var(--primary) 42%, var(--line)); }

.filter-panel, .panel, .kpi-card { box-shadow: var(--shadow); }
.filter-panel { padding: 16px; }
.field input, .field select, .inline-search input, .small-select select {
  background: var(--panel);
  transition: border-color .15s ease, box-shadow .15s ease, background .15s ease;
}
.field input:hover, .field select:hover, .inline-search input:hover, .small-select select:hover { border-color: #bdc9db; }
.field input:focus, .field select:focus, .inline-search input:focus, .small-select select:focus {
  border-color: var(--primary);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 14%, transparent);
}
.block-field label span { border-radius: 9px; background: var(--panel); }
.block-field input:checked + span { color: var(--primary); border-color: color-mix(in srgb, var(--primary) 44%, var(--line)); background: var(--gold-soft); }

.kpi-grid { grid-template-columns: repeat(6, minmax(155px, 1fr)); }
.kpi-card { min-height: 108px; padding: 15px; border-radius: 12px; }
.kpi-icon { color: var(--primary); background: var(--gold-soft); border-radius: 50%; }
.kpi-card strong { font-size: clamp(18px, 1.55vw, 24px); font-weight: 700; font-variant-numeric: tabular-nums; }

.panel-header { min-height: 62px; padding: 13px 16px; }
.panel-header h2 { font-size: 14px; font-weight: 700; }
.data-table { font-size: 11.5px; }
.data-table thead th { color: var(--muted); background: var(--panel-2); font-weight: 700; }
.data-table tbody tr { transition: background .12s ease; }
.data-table tbody tr:hover { background: var(--hover); }
.symbol-cell, .event-date, .mover-item strong { color: var(--primary); }
.block-pill.block { color: var(--primary); background: var(--gold-soft); }
.pagination span { color: var(--primary); }
.breakdown-fill { background: var(--primary); }
.chart-footnote { font-size: 10.5px; line-height: 1.7; }

.autocomplete-list { padding: 7px; border-radius: 10px; box-shadow: 0 18px 48px rgba(11, 23, 57, .18); }
.autocomplete-item { min-height: 44px; padding: 9px 11px; }
.autocomplete-item:hover, .autocomplete-item.active { color: var(--primary); background: var(--hover); }

:where(a, button, input, select, [tabindex]):focus-visible {
  outline: 3px solid color-mix(in srgb, var(--primary) 35%, transparent);
  outline-offset: 2px;
}

html[data-theme="dark"] .sidebar { background: #080f20; box-shadow: none; }
html[data-theme="dark"] .field input:hover,
html[data-theme="dark"] .field select:hover,
html[data-theme="dark"] .inline-search input:hover,
html[data-theme="dark"] .small-select select:hover { border-color: #304660; }

@media (max-width: 1500px) {
  .kpi-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (max-width: 1100px) and (min-width: 761px) {
  :root { --sidebar: 78px; }
  .sidebar { padding-inline: 10px; }
  .brand-mark { background: transparent; }
  .nav-item { padding: 0; justify-content: center; }
  .nav-item > span:last-child { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0, 0, 0, 0); }
  .nav-icon { width: auto; }
}

@media (max-width: 760px) {
  .sidebar {
    height: 72px;
    padding: 6px 8px max(6px, env(safe-area-inset-bottom));
    color: #e7eefc;
    background: #0b1739;
    border: 0;
  }
  .side-nav { gap: 4px; }
  .nav-item { min-height: 55px; padding: 4px; flex-direction: column; justify-content: center; gap: 2px; border-radius: 8px; }
  .nav-item > span:last-child { position: static; width: auto; height: auto; overflow: visible; clip: auto; }
  .nav-icon { width: auto; font-size: 18px; }
  .main-content { padding: 0 10px 92px; }
  .topbar { position: static; margin: 0 -10px 14px; padding: 14px 10px; backdrop-filter: none; }
  .kpi-card { border-radius: 10px; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
}

/* v2 fixed market ticker; fed from public.current through the Hub adapter. */
.market-ticker{position:fixed;z-index:70;inset-inline:0;bottom:0;min-height:42px;display:flex;align-items:center;overflow:auto hidden;background:color-mix(in srgb,var(--panel) 96%,transparent);border-top:1px solid var(--line);box-shadow:0 -7px 20px rgba(8,20,44,.08);direction:rtl;scrollbar-width:none;padding-bottom:env(safe-area-inset-bottom)}.market-ticker::-webkit-scrollbar{display:none}.ticker-live,.ticker-status,.ticker-item{min-height:42px;display:inline-flex;align-items:center;gap:7px;padding:0 14px;white-space:nowrap;border-inline-end:1px solid var(--line)}.ticker-live{font-weight:800}.ticker-live i{width:8px;height:8px;border-radius:50%;background:var(--positive,#1c9a68);box-shadow:0 0 0 4px color-mix(in srgb,var(--positive,#1c9a68) 18%,transparent)}.ticker-item b{font-size:.78rem;color:var(--muted)}.ticker-item strong{font-size:.82rem}.ticker-item small{font-size:.74rem;font-weight:700}.ticker-status{color:var(--muted)}body.has-market-ticker{padding-bottom:calc(42px + env(safe-area-inset-bottom))}@media(max-width:760px){.ticker-live,.ticker-status,.ticker-item{padding-inline:10px}.ticker-item b{font-size:.72rem}.ticker-item strong{font-size:.76rem}}


/* shared compact sidebar controller — v2.0.2 */
.sidebar-collapse-toggle {
  /* inset-inline-END, not start. The sidebar sits at the inline start of an RTL
     page, i.e. hard against the right edge of the viewport, so pinning the
     toggle to its inline-start hung half of it off-screen (measured right:1458
     against a 1440 viewport) and put a horizontal scrollbar on the page. The
     inner edge -- the one facing the content -- is inline-end. */
  position:absolute; inset-inline-end:-17px; top:74px; z-index:4;
  width:34px; min-width:34px; height:34px; min-height:34px;
  display:grid; place-items:center; padding:0; border-radius:50%;
  background:var(--panel); border:1px solid var(--line); box-shadow:var(--shadow);
}
.sidebar-collapse-toggle span { font-size:22px; line-height:1; transform:translateY(-1px); }
.sidebar, .main-content { transition:width .18s ease, margin-right .18s ease, transform .18s ease; }
html.sidebar-collapsed { --sidebar:76px; }
html.sidebar-collapsed .sidebar .brand-mark { margin-inline:auto; }
html.sidebar-collapsed .sidebar .nav-item { justify-content:center; padding-inline:8px; }
html.sidebar-collapsed .sidebar .nav-item > span:not(.nav-icon),
html.sidebar-collapsed .sidebar .sidebar-footer .identity,
html.sidebar-collapsed .sidebar .brand-mark + .brand-copy { display:none !important; }
html.sidebar-collapsed .sidebar .nav-icon { margin:0; }
@media (max-width:1100px) {
  .sidebar-collapse-toggle { display:none; }
  html.sidebar-collapsed { --sidebar:232px; }
}

/* Universal symbol-detail links used by every analytical table. */
.symbol-link { display:grid; gap:3px; min-width:86px; color:inherit; text-decoration:none; }
.symbol-link strong { color:var(--navy); font-size:11px; }
.symbol-link small { color:var(--muted); font-size:7px; font-weight:400; white-space:nowrap; }
.symbol-link:hover strong { color:#b67800; }

/* ========================================================================
   v2.1 page-by-page visual audit — shared containment for bond/gold/commodity
   ===================================================================== */
:where(.app-shell,.main-content,.topbar,.filter-grid,.kpi-grid,.visual-grid,.panel,.panel-header,.table-scroll) > * {
  min-width: 0;
}
:where(.filter-panel,.panel,.kpi-card,.module-status-banner) {
  box-sizing: border-box;
  width: 100%;
  max-width: 100%;
  overflow-wrap: anywhere;
}
.table-scroll {
  width: 100%;
  max-width: 100%;
  overflow-x: auto;
  overscroll-behavior-inline: contain;
  scrollbar-gutter: stable;
}
.data-table { width: 100%; }
.data-table :where(th,td) {
  box-sizing: border-box;
  padding: 10px 12px;
  line-height: 1.65;
  vertical-align: middle;
}
.data-table th { white-space: nowrap; }
.data-table td.numeric {
  direction: ltr;
  text-align: left;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
:where(.field input:not([type="checkbox"]):not([type="radio"]),.field select,.field textarea,.inline-search) {
  box-sizing: border-box;
  width: 100%;
  min-width: 0;
  min-height: 44px;
}
:where(input[type="checkbox"],input[type="radio"]) {
  inline-size: 16px !important;
  block-size: 16px !important;
  min-inline-size: 16px !important;
  min-block-size: 16px !important;
  max-inline-size: 16px !important;
  max-block-size: 16px !important;
  padding: 0 !important;
}
:where(.kpi-card,.panel-header,.breakdown-row,.mover-item,.event-item) :where(strong,span,small,p) {
  max-width: 100%;
  overflow-wrap: anywhere;
}
@media (max-width: 620px) {
  .data-table :where(th,td) { padding: 9px 10px; }
}

/* =============================================================================
 * Mobile ergonomics.
 *
 * In app.css rather than hub.css because app.css is the one stylesheet every
 * page loads -- site.html loads site.css instead of hub.css, so anything put
 * there silently skipped the market map, the section tabs and the connection
 * pill.
 *
 * A browser audit at 390px found interactive controls down to 23x23 (the
 * connection pill), 30x30 (pagination arrows) and 57x24 ("خواندن همه"). WCAG
 * 2.5.8 asks 24px; Apple HIG and Material both ask 44. Below roughly 32 the
 * miss rate on a phone is obvious, and this is a site people check on a phone
 * during the session.
 * ========================================================================== */

@media (max-width: 720px) {
  /* The sidebar rail collapses to a drawer here, so its edge-mounted collapse
     control has nothing to mount on and escaped the viewport (measured
     left:-52). It is hidden at 1100px already; !important because a later
     .icon-button display rule was winning on equal specificity. */
  .sidebar-collapse-toggle { display: none !important; }

  /* Text-bearing controls: min-height + padding rather than height, so
     anything already large is untouched and labels still centre. */
  .mini-button,
  .text-button,
  .ghost-button,
  .segmented button,
  .pager button,
  .chip-button,
  .sort-head,
  .nav-tab,
  .watch-button {
    min-height: 36px;
    padding-block: 8px;
  }

  /* Icon-only controls keep their painted size; the tap area grows around
     them with a pseudo-element so nothing in the layout shifts. */
  .icon-button,
  .pill-status,
  .watch-button,
  .multiselect-remove,
  button.close,
  [data-action="close"] {
    position: relative;
  }
  .icon-button::after,
  .pill-status::after,
  .watch-button::after,
  .multiselect-remove::after,
  button.close::after,
  [data-action="close"]::after {
    content: '';
    position: absolute;
    inset: 50% 50%;
    width: 44px;
    height: 44px;
    transform: translate(50%, -50%);
  }

  /* 16px checkboxes are genuinely hard to hit; the wrapping label becomes the
     target too, which is far larger and costs nothing. */
  input[type="checkbox"],
  input[type="radio"] { width: 20px; height: 20px; }

  label.switch,
  label.check,
  .check-control {
    min-height: 40px;
    display: flex;
    align-items: center;
    gap: 10px;
  }

  /* iOS zooms any input under 16px, which then breaks the layout it zoomed
     into. 40px min-height keeps the field comfortable to type in. */
  input[type="text"],
  input[type="search"],
  input[type="password"],
  input[type="number"],
  input[type="date"],
  select,
  textarea {
    min-height: 40px;
    font-size: 16px;
  }

  /* Links inside dense metadata rows (breadcrumbs, related lists) were as
     small as 17x14. */
  .crumbs a,
  .breadcrumbs a,
  .related-list a,
  .masthead-brand {
    display: inline-flex;
    align-items: center;
    min-height: 32px;
  }
}

/* Pagination and inline-clear controls carry no class of their own, so the
   ergonomics block above missed them. Targeted by id, which is stable. */
@media (max-width: 720px) {
  #prevPage, #nextPage, #clearSearch { position: relative; }
  #prevPage::after, #nextPage::after, #clearSearch::after {
    content: '';
    position: absolute;
    inset: 50% 50%;
    width: 44px; height: 44px;
    transform: translate(50%, -50%);
  }

  /* Sort headers are as narrow as the shortest column label ("نوع" at 21px).
     A padding floor makes the whole header cell tappable without changing the
     desktop table, which is already comfortable with a mouse. */
  .sort-head { min-width: 44px; padding-inline: 8px; }
}

/* The [hidden] attribute is only `display:none` at user-agent level, so any
   class rule setting `display:grid|flex|block` silently overrides it. That is
   how a panel marked hidden in JS kept rendering. This restores the attribute's
   meaning site-wide; use a class, not [hidden], when you want it ignored. */
[hidden] { display: none !important; }

/* New v2.3 controls need the same mobile floor as everything else. The audit
   caught the saved-filters trigger at 26x16, the inline clear button at 27x27
   and the Jalali date input at 31px. Visually-hidden mirrors are excluded --
   they exist to keep one source of truth for state, not to be tapped. */
@media (max-width: 720px) {
  .saved-trigger,
  .column-trigger,
  .filter-more,
  .filter-reset,
  .column-preset,
  .saved-icon,
  .multi-remove,
  .segmented-filter button {
    min-height: 36px;
  }
  .input-clear,
  .saved-icon,
  .multi-remove { position: relative; }
  .input-clear::after,
  .saved-icon::after,
  .multi-remove::after {
    content: '';
    position: absolute;
    inset: 50% 50%;
    width: 44px; height: 44px;
    transform: translate(50%, -50%);
  }
  .jalali-input,
  .range-num,
  .threshold-num { min-height: 40px; font-size: 16px; }
}


/* ===========================================================================
 * v2.6 visual primitives shared by every page.
 * ======================================================================== */
.ui-icon,.theme-icon{display:block;width:21px!important;height:21px!important;min-width:21px;min-height:21px;max-width:21px;max-height:21px;overflow:visible;fill:none;stroke:currentColor;stroke-width:1.85;stroke-linecap:round;stroke-linejoin:round}
.theme-toggle-button{display:inline-grid!important;place-items:center!important;overflow:hidden;line-height:0}
.theme-toggle-button .theme-icon{grid-area:1/1;display:none!important}
html:not([data-theme="dark"]) .theme-toggle-button .theme-icon-moon{display:block!important}
html[data-theme="dark"] .theme-toggle-button .theme-icon-sun{display:block!important}

body .market-ticker .ticker-live{min-width:94px;justify-content:center;font-weight:900;transition:background-color .2s ease,color .2s ease}
body .market-ticker .ticker-live b{font-size:.72rem;margin-inline-start:2px}
body .market-ticker .ticker-live.market-open{background:#17865d!important;color:#fff!important}
body .market-ticker .ticker-live.market-open i{background:#6ff0b3!important;box-shadow:0 0 0 4px rgba(111,240,179,.18)!important}
body .market-ticker .ticker-live.market-preopen,
body .market-ticker .ticker-live.market-after{background:#d99a13!important;color:#201603!important}
body .market-ticker .ticker-live.market-preopen i,
body .market-ticker .ticker-live.market-after i{background:#fff0a5!important;box-shadow:0 0 0 4px rgba(255,240,165,.22)!important}
body .market-ticker .ticker-live.market-closed{background:#c83f4e!important;color:#fff!important}
body .market-ticker .ticker-live.market-closed i{background:#ffd1d7!important;box-shadow:0 0 0 4px rgba(255,209,215,.2)!important}

/* Warm metal navigation for commodity and gold; silver gets its own cool tone. */
body.commodity-page .sidebar,body.gold-page .sidebar{background:linear-gradient(180deg,#171a24 0%,#292314 48%,#121722 100%);border-inline-start-color:rgba(213,165,38,.25)}
body.commodity-page .brand-mark,body.gold-page .brand-mark{background:linear-gradient(145deg,#fff9e8,#e6c66f);color:#251b05;border-color:rgba(213,165,38,.45)}
body.commodity-page .nav-item.active,body.gold-page .nav-item.active{background:linear-gradient(135deg,#be8611,#e8bd4a);color:#201603;box-shadow:0 8px 24px rgba(190,134,17,.24)}
body.commodity-page[data-active-asset="silver"] .sidebar{background:linear-gradient(180deg,#182131 0%,#26364c 48%,#111a28 100%);border-inline-start-color:rgba(190,205,224,.32)}
body.commodity-page[data-active-asset="silver"] .brand-mark{background:linear-gradient(145deg,#f8fbff,#bdc9d9);color:#172131;border-color:rgba(190,205,224,.55)}
body.commodity-page[data-active-asset="silver"] .nav-item.active{background:linear-gradient(135deg,#aab9cd,#edf3f9);color:#172131;box-shadow:0 8px 24px rgba(170,188,211,.22)}
body.commodity-page[data-active-asset="silver"] .sidebar-toggle{border-color:#b9c7d9;color:#44566f}

/* ---- centre every table's cells -----------------------------------------
   platform.css does this already, but only under body.platform-v270, which
   home, admin, symbol and trends do not have. Same contract, ungated, at lower
   specificity so platform.css still wins where it is loaded. */
.data-table th,
.data-table td,
.special-table th,
.special-table td,
.v2-market-table th,
.v2-market-table td,
.watchlist-table th,
.watchlist-table td {
  vertical-align: middle;
  text-align: center;
}
/* Identity stays right-aligned: these are Persian names and symbols, and a
   column of them centred is far harder to scan down. */
.data-table .symbol-cell,
.data-table td[data-column="symbol"],
.special-table .symbol-sticky,
.watchlist-table .symbol-cell,
.watchlist-table .watch-name,
.data-table .watch-note {
  text-align: right;
}
/* Headers keep their sort control centred with the label. */
.data-table th .sort-head,
.v2-market-table th .sort-head { justify-content: center; width: 100%; }

/* ---- unit to the left of the number --------------------------------------
   platform.css owns this, but only under body.platform-v270. Home, admin,
   symbol and trends do not load it, and .unit-value / .metric-value /
   .special-unit-value have no other rules, so they rendered unit-first-on-the-
   right. Same contract, ungated. */
.money-pair,
.unit-value,
.special-unit-value,
.metric-value {
  display: inline-flex;
  flex-direction: row;
  /* RTL is what puts the unit on the left: the first child sits on the right. */
  direction: rtl;
  unicode-bidi: isolate;
  align-items: baseline;
  gap: 5px;
  max-width: 100%;
  white-space: nowrap;
}
/* The figure itself stays LTR, or digit groups read backwards. */
.money-number,
.unit-number,
.metric-number,
.special-unit-value b {
  direction: ltr;
  unicode-bidi: isolate;
  order: 0;
  font-variant-numeric: tabular-nums;
}
.money-unit,
.unit-label,
.metric-unit,
.special-unit-value small {
  order: 1;
  color: var(--muted);
  font-size: .78em;
  font-weight: 600;
}
/* A count has no unit to show. */
[data-value-kind="count"] .money-unit,
[data-value-kind="count"] .metric-unit { display: none; }

/* ---- centring restated so it actually wins -------------------------------
   The block above is (0,1,1) and loses to two rules declared earlier in this
   same file: `.data-table thead th` (0,1,2) keeps headers right, and
   `.data-table td.numeric` (0,2,1) keeps every figure left. Restated against
   those exact selectors so the cascade settles on centre without !important.
   td.numeric keeps direction: ltr -- that is what stops digit groups reading
   backwards, and it is independent of alignment. */
.data-table thead th,
.special-table thead th,
.v2-market-table thead th,
.watchlist-table thead th { text-align: center; }

.data-table td.numeric,
.special-table td.numeric,
.v2-market-table td.numeric,
.watchlist-table td.numeric { text-align: center; }

/* Identity columns stay right-aligned even as headers: a column of Persian
   names is far harder to scan down when centred. */
.data-table thead th.symbol-column,
.data-table thead th[data-key="symbol"],
.data-table thead th[data-key="name"],
.v2-market-table thead th[data-key="symbol"],
.v2-market-table thead th[data-key="name"] { text-align: right; }
.data-table th[data-key="symbol"] .sort-head,
.data-table th[data-key="name"] .sort-head { justify-content: flex-start; }

/* ---- saved-view control is laid out by content ---------------------------
   This lived in hub.css, which commodity.html and gold.html do not load --
   they take app.css, specialized.css and platform.css. Those two pages
   therefore kept platform.css's fixed four-column grid for a control that has
   seven children, and the rename, default and copy-link buttons wrapped over
   each other. Here it reaches every page. !important is required: on those two
   pages platform.css declares its grid later in the cascade. */
.workspace-controls,
body.platform-v270 .workspace-controls,
body.platform-v270 .topbar-actions .workspace-controls {
  display: flex !important;
  grid-template-columns: none !important;
  align-items: stretch;
  flex-wrap: nowrap;
  width: auto;
  max-width: 100%;
  overflow: hidden;
}
/* The select is the only elastic part; the buttons are sized by their icons.
   Wide enough to read a view's name: these are Persian phrases, and 96px shows
   two or three characters and an ellipsis, which is no more use than a blank.
   The search elsewhere in the row is what gives way to pay for this. */
.workspace-controls .workspace-select {
  flex: 1 1 auto;
  min-width: 150px;
  max-width: 280px;
  overflow: hidden;
  text-overflow: ellipsis;
}
.workspace-controls > :not(.workspace-select) { flex: 0 0 auto; }
.workspace-controls .workspace-favorite,
.workspace-controls .workspace-rename,
.workspace-controls .workspace-default,
.workspace-controls .workspace-link,
.workspace-controls .workspace-delete { min-width: 34px; }
/* A hidden child must take no space: showViews:false hides three of the seven
   rather than removing them, and a flex row still lays out a hidden element
   unless it is told not to. */
.workspace-controls > [hidden] { display: none !important; }
@media (max-width: 760px) {
  /* Copy-link survives a phone least well: it needs a secure context and there
     is rarely anywhere to paste. */
  .workspace-controls .workspace-link { display: none; }
  .workspace-controls .workspace-select { min-width: 72px; max-width: none; }
}

/* ---- dashboard header stays on one row -----------------------------------
   Above 1024 nothing in the header wraps. When the row runs out of width the
   search box gives way first and the title ellipsises after it; the buttons,
   the clock and the saved-view control keep their natural size, because a
   half-width button is no more useful than a missing one.

   Selectors carry the .topbar prefix deliberately. hub.css also styles
   .global-search and loads after app.css on the market pages but is absent
   from commodity and gold, so matching its specificity would give two
   different results on two pages that must look the same. */
@media (min-width: 1024px) {
  .topbar { flex-wrap: nowrap; align-items: center; }

  /* The title gives way before the controls do, but never disappears. */
  .topbar .title-wrap { flex: 0 1 auto; min-width: 0; }
  .topbar .title-wrap > div { min-width: 0; }
  .topbar .title-wrap h1,
  .topbar .title-wrap p {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
  }

  .topbar .topbar-actions {
    flex: 1 1 auto;
    flex-wrap: nowrap;
    min-width: 0;
    justify-content: flex-end;
    /* The search used to carry a margin that separated it from the clock; the
       rules below zero that margin so its elastic width is predictable, so the
       spacing has to come from the row itself. */
    gap: 12px;
  }
  /* Everything except the search is sized by its content. */
  .topbar .topbar-actions > * { flex: 0 0 auto; }
  .topbar .global-search,
  .topbar .v2-search-trigger {
    order: 0;
    flex: 1 1 240px;
    min-width: 150px;
    max-width: 420px;
    margin: 0;
  }
}
/* ---- make the home header fit the row it is now on -----------------------
   Not wrapping is not the same as fitting. Every control except the search is
   sized by its content and refuses to shrink, so a short row overflows rather
   than compressing, and the clock ends up over the search box.

   Below 1500 the cheapest three things go, in order of what they cost:
   the subtitle repeats the page it is on; the connection badge is a dot and a
   word, and panels report their own freshness anyway; the account name is one
   click away inside the chip that stays. */
@media (min-width: 1024px) and (max-width: 1500px) {
  .topbar .title-wrap p { display: none; }
  .topbar .title-wrap .connection-badge { display: none; }
  /* The name stays. Hiding the whole .who left the chip as a role pill and a
     chevron, which is not an account control any more -- it is a button with
     no subject. Only the second line goes: the email/expiry meta is repeated
     inside the panel this chip opens, and the name is not. */
  .topbar .account-chip .who span { display: none; }
  .topbar .global-search,
  .topbar .v2-search-trigger { flex-basis: 200px; min-width: 140px; }
}
/* The title shrinks with the rest but must not shrink away: an ellipsised
   page name is worth less than any of the three dropped above. */
@media (min-width: 1024px) {
  .topbar .title-wrap { min-width: 150px; }
  .topbar .title-wrap h1 { min-width: 0; }
  /* The chip is NOT allowed to shrink. hub.css sets flex-shrink: 0 on it
     deliberately -- it is sized by its text, and a chip narrower than its text
     is a pill with an ellipsis in it. Making it elastic here is what shrank
     اطلاعات حساب to nothing; the search is the control designed to absorb the
     squeeze and it already does. */
  .topbar .account-chip { flex: 0 0 auto; }
  /* Enough for a real name, capped so one long account cannot take the row. */
  .topbar .account-chip .who { min-width: 88px; max-width: 170px; }
  .topbar .account-chip .who strong {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    display: block;
  }
}

/* ---- sidebar chevron points at what the click will do --------------------
   The sidebar is on the right, so collapsing sends it right and opening
   brings it left. The icon's own path points left, which means the expanded
   state -- where the click collapses -- is the one that needs rotating, and
   the collapsed state is the one that does not. It used to be the other way
   round, so each state advertised the other one's action.

   Here rather than in hub.css because commodity and gold never load that
   file, and their toggle consequently never changed at all. */
.sidebar-collapse-toggle .sidebar-toggle-icon {
  transition: transform .18s ease;
  transform: rotate(180deg);
}
html.sidebar-collapsed .sidebar-collapse-toggle .sidebar-toggle-icon {
  transform: none;
}

/* ---- saved-view buttons, on every page -----------------------------------
   Rename, default and copy-link were styled in hub.css, which commodity and
   gold do not load, so on those two they rendered as bare glyphs with default
   button chrome inside the bordered strip platform.css draws. Same rules,
   somewhere every page can see them.

   The grid templates that used to accompany these are deliberately not carried
   over. The control is a flex row now, and a template naming seven columns is
   a second place to remember when an eighth button is added -- which is how
   the columns and the children fell out of step to begin with. */
.workspace-rename,
.workspace-default,
.workspace-link {
  border: 0;
  background: none;
  cursor: pointer;
  padding: 0 8px;
  min-height: var(--platform-control-h, 40px);
  font-size: 13px;
  color: var(--muted);
  line-height: 1;
}
.workspace-rename:hover:not(:disabled),
.workspace-default:hover:not(:disabled),
.workspace-link:hover:not(:disabled) { color: var(--text); background: var(--panel-2); }
.workspace-rename:disabled,
.workspace-default:disabled,
.workspace-link:disabled { opacity: .38; cursor: default; }
/* The one button that reports state rather than offering an action. */
.workspace-default.active { color: var(--gold, #9a6800); }

/* Only the favourite star survives where the board mounts its own saved-view
   panel; without this the control keeps a border around a single icon. */
.workspace-controls.workspace-controls-favorite-only {
  border: 0;
  background: none;
}

/* specialized.css floors the select at 132px at a higher specificity than the
   sizing above, so commodity and gold showed a narrower one than every other
   page. Matched here rather than lowered there: the name needs the room on
   those boards as much as anywhere. */
body.specialized-page .workspace-controls select,
body.specialized-page .workspace-controls .workspace-select {
  min-width: 150px;
  max-width: 280px;
}

/* ---- one gap value for the KPI grids -------------------------------------
   The two axes were already equal, but at 12px on the pages platform.css does
   not reach and 14px on the ones it does -- and 14px between cards 112px tall
   reads tighter than the same 14px between cards 200px wide, which is why the
   rows looked short even where the numbers matched.

   One property, both axes, every page. Written as row-gap and column-gap from
   the same value rather than as the `gap` shorthand, so a later rule touching
   one axis cannot pull them apart without saying so. !important is needed to
   clear platform.css's own !important pair. */
:root { --card-gap: 16px; }
.kpi-grid,
.module-kpi-grid,
.v2-kpi-grid,
.special-kpis,
.symbol-kpi-grid,
body.platform-v270 .kpi-grid,
body.platform-v270 .module-kpi-grid,
body.platform-v270 .special-kpis,
body.platform-v270 .admin-kpi-grid,
body.platform-v270 .ops-grid,
body.platform-v270 .stats-kpi-grid {
  row-gap: var(--card-gap) !important;
  column-gap: var(--card-gap) !important;
}
/* Space below the block, not just inside it.
   hub.css sets .module-kpi-grid to margin: 0, which cancelled the 14px
   .kpi-grid carried and left the next section sitting straight against the
   cards on every board. The separation between two blocks has to be larger
   than the separation between items within one of them, or the grouping reads
   backwards -- so it is derived from the same property rather than being a
   number that can drift away from it. */
.kpi-grid,
.module-kpi-grid,
.v2-kpi-grid,
.special-kpis,
body.platform-v270 .module-kpi-grid,
body.platform-v270 .kpi-grid,
body.platform-v270 .special-kpis {
  margin-bottom: calc(var(--card-gap) * 1.5) !important;
}

/* Phones keep the tighter rhythm: there the grid is one or two columns and the
   cards are already stacked, so a wide row gap is just scrolling. */
@media (max-width: 720px) {
  :root { --card-gap: 10px; }
}

/* ---- the bottom bar and the ticker stop sharing an edge ------------------
   Below 760 the sidebar becomes a fixed bar at `inset: auto 0 0 0` with
   z-index 30, and .market-ticker is fixed to bottom: 0 with z-index 70. Both
   claimed the same edge and the ticker won, so the menu was underneath the
   current-data strip on every page that keeps its sidebar -- the market
   boards, commodity and gold. The hub pages hide the sidebar there and use
   .hub-mobile-nav, which had been given a 42px offset to dodge the ticker, so
   they had the opposite order.

   One order everywhere: menu on the bottom edge, current data directly above
   it. The height is a variable because the ticker offset, the body padding and
   the content padding all have to agree with whichever bar is present, and the
   hub's nav is slightly taller than the sidebar bar. */
@media (max-width: 760px) {
  :root { --bottom-nav-h: 68px; }
  body.hub-root { --bottom-nav-h: 73px; }

  /* Above the ticker's 70, so the menu is never covered by it. */
  .sidebar { z-index: 76; }

  .market-ticker { bottom: calc(var(--bottom-nav-h) + env(safe-area-inset-bottom)); }

  /* Room for both bars, not just one. */
  body.has-market-ticker {
    padding-bottom: calc(42px + var(--bottom-nav-h) + env(safe-area-inset-bottom));
  }
  .main-content {
    padding-bottom: calc(52px + var(--bottom-nav-h) + env(safe-area-inset-bottom));
  }
}

/* ---- a KPI value may wrap on a phone -------------------------------------
   .kpi-card strong and .money-pair are both nowrap, which is right on a wide
   card and wrong on a two-column phone grid: "۱٬۲۳۴٫۵۶ هزار میلیارد ریال" is a
   single unbreakable run several times wider than the card holding it, so it
   overflowed rather than fitting. The one break that keeps both halves
   readable is between the figure and its unit, so that is the only one
   allowed -- the figure itself stays intact. */
/* No breakpoint, and a selector that settles it on specificity rather than on
   load order.

   platform.css declares `body.platform-v270 .money-pair { white-space: nowrap }`
   at the same (0,2,0) as a plain `.kpi-card .money-pair`, and it loads second
   on every board, so the tie went to nowrap and the value kept overflowing.
   Naming both the body class and the card puts these at (0,3,0), which holds
   whichever file loads last.

   Ungated because the cards are too narrow for a scaled figure well before a
   phone width -- the grid is three columns by 1280 and two below that. wrap
   only acts when the content does not fit, so a card with room is unchanged. */
.kpi-card strong,
.special-kpi .value,
body.platform-v270 .kpi-card strong,
body.platform-v270 .special-kpi .value { white-space: normal; overflow-wrap: anywhere; }

.kpi-card .money-pair,
.special-kpi .money-pair,
.special-kpi .special-unit-value,
body.platform-v270 .kpi-card .money-pair,
body.platform-v270 .special-kpi .money-pair,
body.platform-v270 .special-kpi .special-unit-value {
  flex-wrap: wrap;
  white-space: normal;
  max-width: 100%;
}
/* The figure itself never breaks: digit groups split across two lines are
   worse than the overflow this is fixing. */
.kpi-card .money-number,
.special-kpi .money-number,
body.platform-v270 .kpi-card .money-number,
body.platform-v270 .special-kpi .money-number,
body.platform-v270 .special-kpi .special-unit-value b { white-space: nowrap; }

/* ---- the index ticker stops running underneath the menu ------------------
   .market-ticker is fixed at `inset-inline: 0; bottom: 0` with z-index 70 and
   .sidebar is z-index 30, so the شاخص بورس و فرابورس strip was painted across
   the bottom of the menu at every width -- hiding the last nav item on desktop
   and tablet, and the whole bar on a phone.

   ticker.js appends the element to <body> rather than it being in the markup,
   which is why commodity.html and gold.html show no sign of it while still
   having one.

   Two situations, because the sidebar is two different things.

   Where it is a column at the side, the strip goes back to full width and is
   drawn over it. That unbroken band is the intended look; cutting it short at
   the sidebar edge removed the overlap but left a gap that reads as a fault.
   The overlap was never the problem on its own -- the problem was that the
   menu's last item sat inside the band, so the sidebar now reserves that
   height at its foot and nothing in the menu can reach it.

   Where it is a bar along the bottom edge, below 760, there is nothing to draw
   over: the strip sits above the bar instead. That is the order asked for and
   the one that keeps the menu where a thumb expects it. */
.market-ticker { inset-inline: 0; z-index: 90; }
/* No menu item may reach the band the ticker occupies. */
.sidebar { padding-bottom: calc(52px + env(safe-area-inset-bottom)); }

/* ---- the ticker is a desktop-only band -----------------------------------
   Below 1100 there is no room to spend a permanent 42px strip on a
   horizontally scrolling list, and it sat exactly where the menu needs to be.
   Hidden, and everything that existed to accommodate it withdrawn at the same
   width -- the reserved foot on the sidebar, the offset above the bottom bar,
   the extra body padding. Leaving any of those behind would reserve space for
   something no longer on the page. */
@media (max-width: 1100px) {
  .market-ticker { display: none !important; }
  .sidebar { padding-bottom: 6px; }
  body.has-market-ticker { padding-bottom: 0; }
}
@media (max-width: 760px) {
  /* The bar is the foot at this width, so nothing needs reserving. */
  .sidebar { padding-bottom: 6px; z-index: 80; }
  /* Only the bottom bar remains to clear. */
  body.has-market-ticker {
    padding-bottom: calc(var(--bottom-nav-h) + env(safe-area-inset-bottom));
  }
  .main-content {
    padding-bottom: calc(10px + var(--bottom-nav-h) + env(safe-area-inset-bottom));
  }
}

/* ---- a card with no icon is a one-column card ----------------------------
   platform.css lays a KPI card out as `grid-template-columns: 42px
   minmax(0, 1fr)` -- icon, then content. app.css hides .kpi-icon below 760,
   and a display: none grid item is not placed at all, so the content block
   auto-placed into the first column instead: 42px of a 400px card.

   Neither rule is wrong by itself; the fault only exists in the pair, which is
   why it survived several passes over these same cards. Corrected at the width
   where the icon goes away rather than by changing the template, because two
   columns is right whenever there is an icon to put in the first one. */
@media (max-width: 760px) {
  .kpi-card,
  body.platform-v270 .kpi-card,
  body.platform-v270 .module-kpi-grid .kpi-card,
  body.platform-v270 .ops-card,
  body.platform-v270 .stats-kpi {
    grid-template-columns: minmax(0, 1fr) !important;
  }
  /* The content block is the only child left, so it takes the whole card. */
  .kpi-card > div:last-child,
  body.platform-v270 .kpi-card > div:last-child { grid-column: 1 / -1; width: 100%; }
}

/* ---- panel children that pad nothing themselves --------------------------
   platform.css sets `body.platform-v270 .panel { padding-inline: 0 }` at
   (0,2,1), which beats hub.css's `.profile-page .profile-card { padding: 20px }`
   at (0,2,0). Zero is right for panels whose children pad themselves --
   .panel-header and .table-scroll both do -- and wrong for the profile forms,
   which are direct children with no padding of their own and sat flush against
   the card border. Scoped to the page rather than lifted globally, because the
   zero is correct everywhere else. */
body.platform-v270.profile-page .panel > :not(.panel-header):not(.table-scroll):not(.special-table-scroll),
body.hub-root.profile-page .panel > :not(.panel-header):not(.table-scroll):not(.special-table-scroll) {
  padding-inline: 18px;
  padding-bottom: 18px;
}

/* ---- specialized tables centre like every other table --------------------
   specialized.css loads after app.css and states its own alignment on
   .special-table cells, so the centring applied everywhere else stopped at
   commodity and gold. Restated with the page class so it settles on
   specificity rather than on which file loads last. */
body.specialized-page .special-table th,
body.specialized-page .special-table td,
body.specialized-page .special-table thead th { text-align: center; vertical-align: middle; }
/* Identity stays right-aligned: a column of Persian names centred is far
   harder to scan down than one that starts on a common edge. */
body.specialized-page .special-table .symbol-sticky,
body.specialized-page .special-table td:first-child,
body.specialized-page .special-table th:first-child { text-align: right; }

/* ---- how much of the bottom edge is spoken for ---------------------------
   Zero by default: above 760 nothing is fixed to the bottom edge except the
   index strip, and that has its own reservation on .sidebar. The ≤760 block
   above raises this to the bar's height, and every bottom padding on the page
   is expressed against it rather than against a number measured once when the
   strip was still shown there. */
/* Scoped to the widths where no bar exists, not left unconditional: media
   queries add no specificity, so a bare :root here would sit later in the file
   than the <=760 declaration above and win at every width -- zeroing the very
   clearance the bar needs. */
@media (min-width: 761px) { :root { --bottom-nav-h: 0px; } }

/* .main-content is also declared with a bottom padding earlier in this file
   and in platform.css; this keeps the plain (non-platform, non-hub) pages on
   the same expression as the other two. */
@media (max-width: 760px) {
  .main-content {
    padding-bottom: calc(var(--bottom-nav-h) + 16px + env(safe-area-inset-bottom));
  }
}

/* ---- a search field with no caption keeps its neighbours' height ---------
   .special-field is a column flex of caption + control. Dropping the caption
   leaves one child, so the box would stand shorter than the selects beside it
   and the rail would look ragged. Centring the lone control and matching the
   caption's line box keeps the row even. */
body.specialized-page .special-field:not(:has(> span)) {
  justify-content: center;
  min-height: 60px;
}

/* ---- one dark bottom bar for every layout --------------------------------
   Three different elements can be the bar -- .mobile-bottom-nav on the market
   boards, symbol and trends; .hub-mobile-nav on home and admin; .sidebar
   turned into a strip on commodity, gold and profile -- and each looked
   different. The navy some of them showed was not a style at all: below 900
   .sidebar is `var(--panel)`, which in dark theme resolves to #080f20.

   Stated once, with fixed colours rather than theme tokens. The bar is chrome,
   like a phone's own tab bar, and this also removes the light-theme case of a
   white bar on white content separated by a hairline. commodity's warm metal
   and silver's cool gradient are right for a tall rail and wrong for a 58px
   navigation strip, so they are overridden at this width only. */
@media (max-width: 900px) {
  .mobile-bottom-nav,
  .hub-mobile-nav,
  body.specialized-page .sidebar,
  body.commodity-page .sidebar,
  body.gold-page .sidebar,
  body.commodity-page[data-active-asset="silver"] .sidebar,
  body.hub-root .sidebar,
  body.profile-page .sidebar {
    background: linear-gradient(180deg, #101d3a 0%, #0a1226 100%) !important;
    border-top: 1px solid rgba(255, 255, 255, .10) !important;
    border-inline: 0 !important;
    box-shadow: 0 -10px 26px rgba(4, 10, 24, .34);
    backdrop-filter: none;
  }

  /* One row, whatever the item count. */
  .mobile-bottom-nav,
  .hub-mobile-nav {
    display: grid !important;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    grid-template-columns: none !important;
  }
  body.specialized-page .sidebar .side-nav,
  body.hub-root .sidebar .side-nav,
  body.profile-page .sidebar .side-nav { flex-wrap: nowrap; }

  /* Light on navy, in both themes. */
  .mobile-bottom-nav a,
  .mobile-bottom-nav button,
  .hub-mobile-nav a,
  body.specialized-page .sidebar .nav-item,
  body.hub-root .sidebar .nav-item,
  body.profile-page .sidebar .nav-item {
    color: #c6d6f2 !important;
    background: transparent !important;
  }
  .mobile-bottom-nav a.active,
  .mobile-bottom-nav button.active,
  .hub-mobile-nav a.active,
  body.specialized-page .sidebar .nav-item.active,
  body.hub-root .sidebar .nav-item.active,
  body.profile-page .sidebar .nav-item.active {
    color: #ffe6ad !important;
    background: rgba(255, 255, 255, .12) !important;
    border-radius: 10px;
  }

  /* A label that cannot fit its share of the row truncates rather than
     wrapping, which is the other way a bar becomes two rows tall. */
  .mobile-bottom-nav small,
  .hub-mobile-nav small,
  body.specialized-page .sidebar .nav-item > span:not(.nav-icon) {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

/* The bar's own height, for everything that has to clear it. .mobile-bottom-nav
   is 58px plus its safe-area padding; the sidebar strip is 68. */
@media (max-width: 900px) {
  body.v2-market-page,
  body.symbol-page,
  body.trends-page { --bottom-nav-h: 64px; }
}

/* ---- the update prompt is built on every page ---------------------------
   dash.js appends .app-update-prompt wherever installAppUpdateWatcher runs,
   which is all of them. Its rules lived in hub.css, which commodity and gold
   do not load, so on those two the ↻ and both sentences rendered as loose text
   down the page with no box around them. Anything dash.js can put on any page
   has to be styled in the stylesheet every page loads. */
.app-update-prompt {
  position:fixed;
  z-index:10000;
  inset-inline-start:22px;
  top:22px;
  bottom:auto;
  width:min(440px,calc(100vw - 44px));
  display:none;
  grid-template-columns:42px minmax(0,1fr) auto;
  gap:11px;
  align-items:center;
  padding:13px 14px;
  border:2px solid color-mix(in srgb,var(--primary) 48%,var(--line));
  border-radius:16px;
  background:var(--panel);
  box-shadow:0 24px 70px rgba(4,18,43,.32);
}
.app-update-prompt.show { display:grid; animation:optionban-update-in .2s ease-out both; }
@keyframes optionban-update-in {
  from { opacity:0; transform:translateY(-10px) scale(.985); }
  to { opacity:1; transform:translateY(0) scale(1); }
}
.app-update-prompt .update-icon { width:38px;height:38px;display:grid;place-items:center;border-radius:11px;background:var(--gold-soft);color:#996500;font-size:20px; }
.app-update-prompt .update-copy { min-width:0;display:grid;gap:3px; }
.app-update-prompt strong { font-size:11px; }
.app-update-prompt small { color:var(--muted);font-size:8px;line-height:1.7; }
.app-update-prompt .update-actions { display:flex;gap:6px; }
.app-update-prompt button { min-height:34px; }

/* ---- خروج stays reachable on a phone -------------------------------------
   `.topbar-actions .primary-button { display: none }` below 760 hides the CSV
   export and the logout button together. Right for export -- a file download
   on a phone is rarely the intent -- and wrong for logout, which has no other
   route: the account chip opens a profile modal, not a session menu. */
@media (max-width: 760px) {
  .topbar-actions #logoutButton {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 36px;
    padding-inline: 10px;
    font-size: 11px;
  }
  /* Export is the one that stays hidden. */
  .topbar-actions #exportButton { display: none; }
}

/* On a narrow screen the prompt clears whatever is on the bottom edge and
   drops its actions onto their own line. 118px was the old bar-plus-ticker
   measurement; like every other bottom offset it comes from --bottom-nav-h
   now, so it cannot drift from the bar's real height again. */
@media (max-width: 600px) {
  .app-update-prompt {
    inset-inline: 10px;
    top: 10px;
    bottom: auto;
    width: auto;
    grid-template-columns: 36px minmax(0, 1fr);
  }
  .app-update-prompt .update-actions { grid-column: 1 / -1; justify-content: flex-end; }
}
