/* ==========================================================================
   FinView — component layer
   ==========================================================================
   Depends on finview/tokens.css. Load order is always tokens then components,
   then (during migration only) the legacy stylesheets, which the token bridge
   has already re-coloured.

   Every component here obeys three rules, and they are the rules that make the
   product look like one thing:

     1. No literal values. Colour, space, radius, size and duration all come
        from a token. A hard-coded 13px is how two panels end up 1px apart.
     2. One height per control size. Inputs, selects, buttons and chips of the
        same size token are the same height to the pixel, so a filter row reads
        as a single object.
     3. Logical properties only (inline-start/end, margin-inline). This is a
        Persian-first RTL product with Latin tickers inside it; physical
        left/right would need a mirrored stylesheet to maintain.
   ========================================================================== */

/* ==========================================================================
   SHELL
   ========================================================================== */
.fv-shell {
  display: grid;
  grid-template-columns: var(--fv-sidebar) minmax(0, 1fr);
  min-height: 100vh;
  min-height: 100dvh;
  background: var(--fv-bg);
}

/* --------------------------------------------------------------- sidebar -- */
/* The sidebar stays dark in BOTH themes. It is chrome, not content: keeping it
   constant gives the eye a fixed frame, and means switching theme visibly
   changes the data surface rather than repainting the whole window. */
.fv-sidebar {
  position: sticky;
  top: 0;
  align-self: start;
  height: 100vh;
  height: 100dvh;
  display: flex;
  flex-direction: column;
  gap: var(--fv-space-2);
  padding: var(--fv-space-5) var(--fv-space-4);
  background: var(--fv-chrome);
  border-inline-end: 1px solid var(--fv-chrome-line);
  overflow-y: auto;
  overscroll-behavior: contain;
  z-index: var(--fv-z-header);
}

/* The brand block replaces an element that legacy CSS still styles as
   `.brand-mark`, from three different files:
     app.css        - a fixed 54x54 grid box sized for the old 44px glyph
     hub.css        - a tinted panel with a hover wash
     specialized.css - `body.commodity-page .brand-mark` paints it an amber
                       gradient, which is why the FinView mark sat in a gold
                       card on the commodity and gold boards
   The last of those is specificity (0,2,1) and wins over a plain class, so the
   reset is scoped through `.fv-brand` on any element and marked important.
   This is the one place in the file that uses !important, and it exists only
   for the migration window: when those three rules are deleted, this block
   goes with them. A brand that changes colour depending on which board you are
   looking at is not a brand. */
.fv-brand,
body .fv-brand,
body.commodity-page .fv-brand,
body.gold-page .fv-brand {
  display: flex !important;
  align-items: center;
  gap: var(--fv-space-3);
  width: auto !important;
  height: auto !important;
  padding: var(--fv-space-2) var(--fv-space-3) var(--fv-space-5) !important;
  margin-bottom: 0 !important;
  background: none !important;
  background-image: none !important;
  border: 0 !important;
  border-radius: 0 !important;
  box-shadow: none !important;
  color: var(--fv-chrome-text) !important;
  text-decoration: none;
}
.fv-brand:hover { background: none !important; text-decoration: none; }
.fv-brand__mark {
  flex: 0 0 auto;
  width: 28px !important;
  height: 28px !important;
  display: block;
  /* app.css strokes any svg inside .brand-mark in the accent colour; this mark
     is filled, not stroked, and the F must stay the chrome text colour. */
  stroke: none !important;
  fill: none;
  color: var(--fv-chrome-text);
}
.fv-brand__text { display: flex; flex-direction: column; line-height: 1; min-width: 0; }
.fv-brand__name {
  font-size: var(--fv-text-lg);
  font-weight: var(--fv-weight-bold);
  letter-spacing: 0.01em;
}
.fv-brand__tag {
  margin-top: var(--fv-space-1);
  font-size: var(--fv-text-2xs);
  font-weight: var(--fv-weight-medium);
  letter-spacing: var(--fv-tracking-caps);
  text-transform: uppercase;
  color: var(--fv-chrome-muted);
}

.fv-nav { display: flex; flex-direction: column; gap: var(--fv-space-1); }
.fv-nav__group {
  margin-top: var(--fv-space-5);
  margin-bottom: var(--fv-space-2);
  padding-inline: var(--fv-space-3);
  font-size: var(--fv-text-2xs);
  font-weight: var(--fv-weight-semibold);
  letter-spacing: var(--fv-tracking-caps);
  text-transform: uppercase;
  color: var(--fv-chrome-muted);
}
.fv-nav__item {
  display: flex;
  align-items: center;
  gap: var(--fv-space-3);
  min-height: var(--fv-control-md);
  padding: var(--fv-space-2) var(--fv-space-3);
  border-radius: var(--fv-radius-md);
  color: var(--fv-chrome-text);
  font-size: var(--fv-text-base);
  text-decoration: none;
  transition: background var(--fv-duration) var(--fv-ease),
              color var(--fv-duration) var(--fv-ease);
}
.fv-nav__item:hover { background: var(--fv-chrome-2); color: #fff; text-decoration: none; }
.fv-nav__item[aria-current="page"],
.fv-nav__item.is-active {
  background: var(--fv-chrome-2);
  color: #fff;
  font-weight: var(--fv-weight-semibold);
  /* The accent rail, not a filled pill: it marks position without competing
     with the live-data accents in the content area. */
  box-shadow: inset 2px 0 0 0 var(--fv-accent-bright);
}
html[dir="rtl"] .fv-nav__item[aria-current="page"],
html[dir="rtl"] .fv-nav__item.is-active {
  box-shadow: inset -2px 0 0 0 var(--fv-accent-bright);
}
.fv-nav__icon {
  flex: 0 0 auto;
  width: 18px; height: 18px;
  display: grid; place-items: center;
  opacity: 0.75;
}
.fv-nav__item:hover .fv-nav__icon,
.fv-nav__item.is-active .fv-nav__icon { opacity: 1; }
.fv-nav__label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fv-nav__badge {
  margin-inline-start: auto;
  padding: 0 var(--fv-space-2);
  border-radius: var(--fv-radius-full);
  background: var(--fv-accent-bright);
  color: #04121A;
  font-size: var(--fv-text-2xs);
  font-weight: var(--fv-weight-bold);
  line-height: 16px;
}

/* ---------------------------------------------------------------- topbar -- */
.fv-main { min-width: 0; display: flex; flex-direction: column; }

.fv-topbar {
  position: sticky;
  top: 0;
  z-index: var(--fv-z-header);
  display: flex;
  align-items: center;
  gap: var(--fv-space-5);
  min-height: var(--fv-topbar);
  padding: var(--fv-space-3) var(--fv-space-7);
  background: color-mix(in srgb, var(--fv-surface) 88%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--fv-line);
}
.fv-topbar__titles { min-width: 0; flex: 1 1 auto; }
.fv-topbar__title {
  margin: 0;
  font-size: var(--fv-text-xl);
  font-weight: var(--fv-weight-bold);
  line-height: var(--fv-leading-tight);
  letter-spacing: -0.01em;
}
.fv-topbar__subtitle {
  margin: var(--fv-space-1) 0 0;
  font-size: var(--fv-text-sm);
  color: var(--fv-muted);
  line-height: var(--fv-leading-snug);
}
.fv-topbar__actions {
  display: flex;
  align-items: center;
  gap: var(--fv-space-3);
  flex: 0 0 auto;
}

.fv-content {
  flex: 1 1 auto;
  width: 100%;
  max-width: var(--fv-content-max);
  margin-inline: auto;
  padding: var(--fv-space-7);
  display: flex;
  flex-direction: column;
  gap: var(--fv-space-6);
}

/* ==========================================================================
   PANEL
   ========================================================================== */
.fv-panel {
  background: var(--fv-surface);
  border: 1px solid var(--fv-line);
  border-radius: var(--fv-radius-lg);
  box-shadow: var(--fv-shadow-sm);
  overflow: hidden;
}
.fv-panel__head {
  display: flex;
  align-items: center;
  gap: var(--fv-space-4);
  flex-wrap: wrap;
  padding: var(--fv-space-4) var(--fv-space-5);
  border-bottom: 1px solid var(--fv-line);
  background: var(--fv-surface-2);
}
.fv-panel__title {
  margin: 0;
  font-size: var(--fv-text-md);
  font-weight: var(--fv-weight-semibold);
  line-height: var(--fv-leading-tight);
}
.fv-panel__note {
  margin: var(--fv-space-1) 0 0;
  font-size: var(--fv-text-sm);
  color: var(--fv-muted);
}
.fv-panel__actions { margin-inline-start: auto; display: flex; gap: var(--fv-space-2); align-items: center; }
.fv-panel__body { padding: var(--fv-space-5); }
.fv-panel__body--flush { padding: 0; }

/* ==========================================================================
   KPI
   ==========================================================================
   auto-fit + minmax rather than a fixed column count: the same markup gives
   four across on a desk, two on a tablet and one on a phone with no
   breakpoints to maintain, and no card ever drops below a readable width.
   ========================================================================== */
.fv-kpi-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--fv-space-4);
}
.fv-kpi {
  display: flex;
  flex-direction: column;
  gap: var(--fv-space-2);
  padding: var(--fv-space-4) var(--fv-space-5);
  background: var(--fv-surface);
  border: 1px solid var(--fv-line);
  border-radius: var(--fv-radius-lg);
  min-width: 0;
}
.fv-kpi__label {
  display: flex; align-items: center; gap: var(--fv-space-2);
  font-size: var(--fv-text-sm);
  color: var(--fv-muted);
  font-weight: var(--fv-weight-medium);
}
.fv-kpi__value {
  font-size: var(--fv-text-2xl);
  font-weight: var(--fv-weight-bold);
  line-height: var(--fv-leading-tight);
  letter-spacing: -0.02em;
  /* Long figures shrink rather than wrap: a KPI that wraps to two lines breaks
     the row's rhythm and is the most common cause of a "wonky" dashboard. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.fv-kpi__meta {
  display: flex; align-items: center; gap: var(--fv-space-2);
  font-size: var(--fv-text-sm);
  color: var(--fv-muted);
}

/* Direction is carried by colour AND a glyph, never colour alone -- roughly
   1 in 12 men has a red/green deficiency, and this is a market product where
   that distinction is the whole point. */
.fv-delta { display: inline-flex; align-items: center; gap: var(--fv-space-1); font-weight: var(--fv-weight-semibold); }
.fv-delta--up { color: var(--fv-up); }
.fv-delta--down { color: var(--fv-down); }
.fv-delta--flat { color: var(--fv-flat); }
.fv-delta--up::before { content: "▲"; font-size: 0.75em; }
.fv-delta--down::before { content: "▼"; font-size: 0.75em; }
.fv-delta--flat::before { content: "—"; font-size: 0.75em; }

/* ==========================================================================
   CONTROLS
   ==========================================================================
   One height per size token across every control type. This is the rule that
   makes filter rows look deliberate.
   ========================================================================== */
.fv-field { display: flex; flex-direction: column; gap: var(--fv-space-2); min-width: 0; }
.fv-field__label {
  font-size: var(--fv-text-sm);
  font-weight: var(--fv-weight-medium);
  color: var(--fv-muted);
}

.fv-input,
.fv-select,
.fv-button {
  height: var(--fv-control-md);
  padding-inline: var(--fv-space-4);
  border: 1px solid var(--fv-line-strong);
  border-radius: var(--fv-radius-md);
  background: var(--fv-surface);
  color: var(--fv-text);
  font-family: inherit;
  font-size: var(--fv-text-base);
  line-height: 1;
  transition: border-color var(--fv-duration) var(--fv-ease),
              box-shadow var(--fv-duration) var(--fv-ease),
              background var(--fv-duration) var(--fv-ease);
}
.fv-input::placeholder { color: var(--fv-faint); }
.fv-input:hover, .fv-select:hover { border-color: var(--fv-faint); }
.fv-input:focus, .fv-select:focus {
  outline: none;
  border-color: var(--fv-accent);
  box-shadow: var(--fv-ring);
}
.fv-input:disabled, .fv-select:disabled, .fv-button:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

.fv-select {
  appearance: none;
  /* Chevron drawn in the token colour and flipped for RTL by padding, not by a
     second background image. */
  padding-inline-end: var(--fv-space-8);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%235C6E88' stroke-width='1.6'%3E%3Cpath d='M2.5 4.5 6 8l3.5-3.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-size: 12px;
  background-position: left var(--fv-space-4) center;
}
html[dir="ltr"] .fv-select { background-position: right var(--fv-space-4) center; }
html[data-theme="dark"] .fv-select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='%238DA2C0' stroke-width='1.6'%3E%3Cpath d='M2.5 4.5 6 8l3.5-3.5'/%3E%3C/svg%3E");
}

.fv-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--fv-space-2);
  font-weight: var(--fv-weight-medium);
  cursor: pointer;
  white-space: nowrap;
}
.fv-button:hover { background: var(--fv-surface-3); text-decoration: none; }
.fv-button--primary {
  background: var(--fv-accent);
  border-color: var(--fv-accent);
  color: #fff;
  font-weight: var(--fv-weight-semibold);
}
.fv-button--primary:hover { background: var(--fv-accent-hover); border-color: var(--fv-accent-hover); }
html[data-theme="dark"] .fv-button--primary { color: #04121A; }
.fv-button--ghost { background: transparent; border-color: transparent; }
.fv-button--ghost:hover { background: var(--fv-surface-3); }
.fv-button--sm { height: var(--fv-control-sm); padding-inline: var(--fv-space-3); font-size: var(--fv-text-sm); }
.fv-button--lg { height: var(--fv-control-lg); padding-inline: var(--fv-space-6); font-size: var(--fv-text-md); }
.fv-button--icon {
  width: var(--fv-control-md);
  padding: 0;
  flex: 0 0 auto;
}

/* --------------------------------------------------------------- filters -- */
.fv-filters {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--fv-space-4);
  align-items: end;
}
.fv-filters__actions {
  display: flex;
  gap: var(--fv-space-2);
  align-items: center;
  /* Sits on the control baseline with the fields beside it, not above them. */
  align-self: end;
}

/* Active filters, shown as removable chips. Without this a user cannot tell
   why a board is showing 12 rows instead of 400 -- the commonest "the data is
   broken" report that is really a forgotten filter. */
.fv-chips { display: flex; flex-wrap: wrap; gap: var(--fv-space-2); }
.fv-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--fv-space-2);
  height: var(--fv-control-sm);
  padding-inline: var(--fv-space-3);
  border-radius: var(--fv-radius-full);
  border: 1px solid var(--fv-accent-line);
  background: var(--fv-accent-soft);
  color: var(--fv-accent);
  font-size: var(--fv-text-sm);
  font-weight: var(--fv-weight-medium);
}
.fv-chip__remove {
  display: grid; place-items: center;
  width: 16px; height: 16px;
  border: 0; padding: 0;
  border-radius: var(--fv-radius-full);
  background: transparent;
  color: inherit;
  cursor: pointer;
  font-size: var(--fv-text-sm);
  line-height: 1;
}
.fv-chip__remove:hover { background: var(--fv-accent-line); }

/* ------------------------------------------------------------- segmented -- */
.fv-segmented {
  display: inline-flex;
  padding: var(--fv-space-1);
  gap: var(--fv-space-1);
  background: var(--fv-surface-inset);
  border: 1px solid var(--fv-line);
  border-radius: var(--fv-radius-md);
}
.fv-segmented__option {
  height: calc(var(--fv-control-md) - var(--fv-space-3));
  padding-inline: var(--fv-space-4);
  border: 0;
  border-radius: var(--fv-radius-sm);
  background: transparent;
  color: var(--fv-muted);
  font-family: inherit;
  font-size: var(--fv-text-sm);
  font-weight: var(--fv-weight-medium);
  cursor: pointer;
  transition: background var(--fv-duration) var(--fv-ease), color var(--fv-duration) var(--fv-ease);
}
.fv-segmented__option:hover { color: var(--fv-text); }
.fv-segmented__option[aria-selected="true"],
.fv-segmented__option.is-active {
  background: var(--fv-surface);
  color: var(--fv-text);
  font-weight: var(--fv-weight-semibold);
  box-shadow: var(--fv-shadow-sm);
}

/* ==========================================================================
   TABLE
   ==========================================================================
   Dense by default. This is a market product: a user comparing forty issues
   wants forty rows on screen, not twelve. Row height comes from the control
   scale so a table and a filter row share a rhythm.
   ========================================================================== */
.fv-table-wrap {
  width: 100%;
  overflow-x: auto;
  overscroll-behavior-x: contain;
}
.fv-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: var(--fv-text-base);
}
.fv-table th,
.fv-table td {
  padding: var(--fv-space-3) var(--fv-space-4);
  text-align: start;
  border-bottom: 1px solid var(--fv-line);
  white-space: nowrap;
}
.fv-table thead th {
  position: sticky;
  top: 0;
  z-index: var(--fv-z-sticky);
  background: var(--fv-surface-2);
  color: var(--fv-muted);
  font-size: var(--fv-text-sm);
  font-weight: var(--fv-weight-semibold);
  border-bottom: 1px solid var(--fv-line-strong);
  /* A sticky header loses its own border when scrolled in some engines; the
     shadow guarantees a visible edge over scrolling rows. */
  box-shadow: 0 1px 0 0 var(--fv-line-strong);
}
.fv-table tbody tr { transition: background var(--fv-duration-fast) var(--fv-ease); }
.fv-table tbody tr:hover { background: var(--fv-surface-2); }
.fv-table tbody tr:last-child td { border-bottom: 0; }

/* Numeric columns are end-aligned so decimal points stack. Combined with the
   tabular figures from tokens.css, a column becomes scannable at a glance. */
.fv-table td.fv-num,
.fv-table th.fv-num { text-align: end; font-variant-numeric: tabular-nums; }

/* First column pinned: on a wide board the symbol must stay visible while the
   user scrolls out to the far metrics, or the numbers lose their subject. */
.fv-table--pinned th:first-child,
.fv-table--pinned td:first-child {
  position: sticky;
  inset-inline-start: 0;
  z-index: var(--fv-z-sticky);
  background: var(--fv-surface);
}
.fv-table--pinned thead th:first-child {
  z-index: calc(var(--fv-z-sticky) + 1);
  background: var(--fv-surface-2);
}
.fv-table--pinned tbody tr:hover td:first-child { background: var(--fv-surface-2); }

.fv-table__sort {
  display: inline-flex; align-items: center; gap: var(--fv-space-1);
  background: none; border: 0; padding: 0;
  color: inherit; font: inherit; cursor: pointer;
}
.fv-table__sort:hover { color: var(--fv-text); }
.fv-table__sort[aria-sort="ascending"]::after { content: "▲"; font-size: 0.7em; color: var(--fv-accent); }
.fv-table__sort[aria-sort="descending"]::after { content: "▼"; font-size: 0.7em; color: var(--fv-accent); }

.fv-symbol { display: inline-flex; flex-direction: column; gap: 1px; min-width: 0; }
.fv-symbol__code { font-weight: var(--fv-weight-semibold); }
.fv-symbol__name {
  font-size: var(--fv-text-sm);
  color: var(--fv-muted);
  overflow: hidden; text-overflow: ellipsis; max-width: 22ch;
}

/* ==========================================================================
   STATUS
   ========================================================================== */
.fv-badge {
  display: inline-flex; align-items: center; gap: var(--fv-space-2);
  height: 20px;
  padding-inline: var(--fv-space-2);
  border-radius: var(--fv-radius-sm);
  font-size: var(--fv-text-xs);
  font-weight: var(--fv-weight-semibold);
  background: var(--fv-flat-soft);
  color: var(--fv-flat);
}
.fv-badge--up { background: var(--fv-up-soft); color: var(--fv-up); }
.fv-badge--down { background: var(--fv-down-soft); color: var(--fv-down); }
.fv-badge--warn { background: var(--fv-warn-soft); color: var(--fv-warn); }
.fv-badge--info { background: var(--fv-info-soft); color: var(--fv-info); }
.fv-badge--live { background: var(--fv-accent-soft); color: var(--fv-accent); }

/* The live dot pulses only while data is actually arriving; a permanently
   animated indicator stops carrying information. */
.fv-live-dot {
  width: 7px; height: 7px;
  border-radius: var(--fv-radius-full);
  background: var(--fv-accent-bright);
  flex: 0 0 auto;
}
.fv-badge--live .fv-live-dot { animation: fv-pulse 2s var(--fv-ease) infinite; }
.fv-badge--stale { background: var(--fv-warn-soft); color: var(--fv-warn); }
.fv-badge--stale .fv-live-dot { background: var(--fv-warn); animation: none; }
.fv-badge--offline { background: var(--fv-down-soft); color: var(--fv-down); }
.fv-badge--offline .fv-live-dot { background: var(--fv-down); animation: none; }
@keyframes fv-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.45; transform: scale(0.8); }
}

.fv-banner {
  display: flex;
  align-items: flex-start;
  gap: var(--fv-space-3);
  padding: var(--fv-space-4) var(--fv-space-5);
  border: 1px solid var(--fv-line);
  border-inline-start: 3px solid var(--fv-flat);
  border-radius: var(--fv-radius-md);
  background: var(--fv-surface-2);
  font-size: var(--fv-text-base);
}
.fv-banner--warn { border-inline-start-color: var(--fv-warn); background: var(--fv-warn-soft); }
.fv-banner--down { border-inline-start-color: var(--fv-down); background: var(--fv-down-soft); }
.fv-banner--info { border-inline-start-color: var(--fv-info); background: var(--fv-info-soft); }
.fv-banner__title { font-weight: var(--fv-weight-semibold); }

/* ------------------------------------------------------------ empty/load -- */
.fv-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--fv-space-3);
  padding: var(--fv-space-10) var(--fv-space-6);
  text-align: center;
  color: var(--fv-muted);
}
.fv-empty__title { font-size: var(--fv-text-lg); font-weight: var(--fv-weight-semibold); color: var(--fv-text); }
.fv-empty__note { max-width: 46ch; font-size: var(--fv-text-base); line-height: var(--fv-leading-normal); }

/* Skeletons match the real element's height exactly, so content arriving does
   not shift the layout -- the single biggest cause of a page feeling cheap. */
.fv-skeleton {
  position: relative;
  overflow: hidden;
  background: var(--fv-surface-3);
  border-radius: var(--fv-radius-sm);
  color: transparent !important;
}
.fv-skeleton::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, transparent, var(--fv-surface-2), transparent);
  animation: fv-shimmer 1.3s infinite;
}
@keyframes fv-shimmer {
  from { transform: translateX(-100%); }
  to { transform: translateX(100%); }
}
.fv-skeleton--text { height: 1em; }
.fv-skeleton--kpi { height: var(--fv-text-2xl); width: 60%; }

/* ---------------------------------------------------------------- toasts -- */
.fv-toasts {
  position: fixed;
  inset-block-end: var(--fv-space-6);
  inset-inline-end: var(--fv-space-6);
  z-index: var(--fv-z-toast);
  display: flex;
  flex-direction: column;
  gap: var(--fv-space-3);
  pointer-events: none;
}
.fv-toast {
  pointer-events: auto;
  display: flex; align-items: center; gap: var(--fv-space-3);
  min-width: 260px; max-width: 380px;
  padding: var(--fv-space-4) var(--fv-space-5);
  border: 1px solid var(--fv-line);
  border-radius: var(--fv-radius-md);
  background: var(--fv-surface);
  box-shadow: var(--fv-shadow-lg);
  font-size: var(--fv-text-base);
}

/* ==========================================================================
   CHART
   ==========================================================================
   Chart colours live here rather than in JS so both themes are handled by the
   same mechanism as everything else, and a chart cannot end up rendering dark
   ink on a dark panel because a script cached a colour at load time.
   ========================================================================== */
.fv-chart {
  position: relative;
  width: 100%;
  min-height: 240px;
}
.fv-chart svg { display: block; width: 100%; height: 100%; overflow: visible; }
.fv-chart .fv-grid line,
.fv-chart .grid line { stroke: var(--fv-grid-line); stroke-width: 1; }
.fv-chart .fv-axis text,
.fv-chart .axis text { fill: var(--fv-muted); font-size: var(--fv-text-xs); }
.fv-chart .fv-axis path,
.fv-chart .fv-axis line,
.fv-chart .axis path,
.fv-chart .axis line { stroke: var(--fv-line); }
.fv-chart .fv-series-line { fill: none; stroke: var(--fv-accent-bright); stroke-width: 2; }
.fv-chart .fv-series-area { fill: var(--fv-accent-bright); opacity: 0.1; }
.fv-chart .fv-series-up { fill: var(--fv-up); stroke: var(--fv-up); }
.fv-chart .fv-series-down { fill: var(--fv-down); stroke: var(--fv-down); }

.fv-chart__legend {
  display: flex; flex-wrap: wrap; gap: var(--fv-space-4);
  margin-top: var(--fv-space-4);
  font-size: var(--fv-text-sm);
  color: var(--fv-muted);
}
.fv-chart__legend-item { display: inline-flex; align-items: center; gap: var(--fv-space-2); }
.fv-chart__swatch { width: 10px; height: 10px; border-radius: 2px; flex: 0 0 auto; }

.fv-tooltip {
  position: absolute;
  z-index: var(--fv-z-tooltip);
  padding: var(--fv-space-3) var(--fv-space-4);
  border: 1px solid var(--fv-line);
  border-radius: var(--fv-radius-md);
  background: var(--fv-surface);
  box-shadow: var(--fv-shadow-lg);
  font-size: var(--fv-text-sm);
  pointer-events: none;
  white-space: nowrap;
}

/* ==========================================================================
   SPECIAL-INSIGHT PANEL HEADER
   ==========================================================================
   The options trend controls used to sit inside the chart body, on the same
   line as the colour legend. With ten underlyings that legend wraps and pushed
   the controls into a sliver. They now live in the panel header, which is where
   every other panel on the board keeps its controls.
   ========================================================================== */
.special-insight-head {
  display: flex;
  align-items: flex-end;
  gap: var(--fv-space-5);
  flex-wrap: wrap;
}
.special-insight-actions {
  margin-inline-start: auto;
  min-width: 0;
}
.special-insight-actions .option-trend-toolbar {
  display: flex;
  align-items: flex-end;
  gap: var(--fv-space-4);
  flex-wrap: wrap;
  /* No border or padding here: the panel header already frames it, and the
     toolbar's old card styling read as a box inside a box. */
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
}
.special-insight-actions .option-trend-controls {
  display: flex;
  align-items: flex-end;
  gap: var(--fv-space-3);
  flex-wrap: wrap;
}
/* Every control in the row resolves to the same height, so the toggle, the
   selects and the switch read as one object rather than three. */
/* ------------------------------------------------- one segmented control -- */
/* The board had three near-identical implementations of the same widget:
     .segmented              (site.css)  12px / 600
     .option-analysis-switch (hub.css)    9px / 750
     .history-metric-switch  (hub.css)    9px / 700
   so the breakdown toggle -- which only ever matched the generic one -- sat
   next to the metric toggle at a visibly different size. 9px is also below the
   smallest step on the type scale and is genuinely hard to read in Persian.
   All four now resolve to the same spec, sized from the control scale so the
   toggle lines up with the selects beside it in the panel header. */
.segmented,
.option-analysis-switch,
.option-breakdown-switch,
.history-metric-switch {
  display: inline-flex;
  align-items: center;
  gap: var(--fv-space-1);
  height: var(--fv-control-md);
  padding: var(--fv-space-1);
  border: 1px solid var(--fv-line);
  border-radius: var(--fv-radius-md);
  background: var(--fv-surface-inset);
  box-sizing: border-box;
}

.segmented > button,
.option-analysis-switch button,
.option-breakdown-switch button,
.history-metric-switch button {
  display: inline-flex;
  align-items: center;
  min-height: 0;
  height: 100%;
  padding: 0 var(--fv-space-4);
  border: 0;
  border-radius: var(--fv-radius-sm);
  background: transparent;
  color: var(--fv-muted);
  font: inherit;
  font-size: var(--fv-text-sm);
  font-weight: var(--fv-weight-medium);
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--fv-duration) var(--fv-ease),
              color var(--fv-duration) var(--fv-ease);
}
.segmented > button:hover,
.option-analysis-switch button:hover,
.option-breakdown-switch button:hover,
.history-metric-switch button:hover { color: var(--fv-text); }

.segmented > button.active,
.option-analysis-switch button.active,
.option-breakdown-switch button.active,
.history-metric-switch button.active,
.segmented > button[aria-selected="true"] {
  background: var(--fv-surface);
  color: var(--fv-text);
  font-weight: var(--fv-weight-semibold);
  box-shadow: var(--fv-shadow-sm);
}

.history-metric-switch[hidden] { display: none !important; }

/* One shape for every control in the trend toolbar: a label above a control,
   all the same height, so the row aligns on both its top and bottom edges.
   The selects come with their own label markup (.history-range span); the
   toggles get .trend-field to match. */
.special-insight-actions .trend-field,
.special-insight-actions .history-range {
  display: grid;
  gap: var(--fv-space-2);
  min-width: 0;
  align-content: end;
}
.special-insight-actions .trend-field__label,
.special-insight-actions .history-range span {
  /* Was 8px -- below the smallest step on the scale and unreadable in Persian. */
  font-size: var(--fv-text-sm);
  font-weight: var(--fv-weight-medium);
  color: var(--fv-muted);
  line-height: 1;
}
/* min-height, not height: hub.css sets `min-height:38px` on these selects,
   which beats a plain `height` and left them 10px taller than the toggles. */
.special-insight-actions .history-range select {
  min-height: var(--fv-control-md);
  height: var(--fv-control-md);
  padding-block: 0;
  border-radius: var(--fv-radius-md);
}

/* The legend moves into the chart body and gets a line of its own. */
.special-insight .option-trend-note {
  margin-bottom: var(--fv-space-4);
  padding-bottom: var(--fv-space-3);
  border-bottom: 1px solid var(--fv-line);
}

@media (max-width: 900px) {
  .special-insight-actions { margin-inline-start: 0; width: 100%; }
  .special-insight-actions .option-trend-toolbar,
  .special-insight-actions .option-trend-controls { width: 100%; }
}

/* ==========================================================================
   SYMBOL SEARCH
   ==========================================================================
   Present on every symbol page so instruments can be reached from one another,
   and the whole page when no symbol is selected.
   ========================================================================== */
.symbol-search-panel {
  padding: var(--fv-space-5);
  margin-bottom: var(--fv-space-5);
  background: var(--fv-surface);
  border: 1px solid var(--fv-line);
  border-radius: var(--fv-radius-lg);
}
/* With no symbol chosen the search is the page, so give it room and centre it
   rather than leaving it as a strip above an empty layout. */
body.symbol-page-empty .symbol-search-panel {
  max-width: 720px;
  margin-inline: auto;
  margin-block-start: var(--fv-space-10);
  padding: var(--fv-space-8);
}

.symbol-search-box { position: relative; display: flex; align-items: center; }
.symbol-search-box input {
  width: 100%;
  height: var(--fv-control-lg);
  padding-inline: var(--fv-space-5) var(--fv-space-9);
  border: 1px solid var(--fv-line-strong);
  border-radius: var(--fv-radius-md);
  background: var(--fv-surface-2);
  color: var(--fv-text);
  font-family: inherit;
  font-size: var(--fv-text-md);
}
.symbol-search-box input:focus {
  outline: none;
  border-color: var(--fv-accent);
  box-shadow: var(--fv-ring);
  background: var(--fv-surface);
}
/* type="search" is right for semantics and mobile keyboards, but WebKit draws
   its own clear button, which sat next to ours as a second ×. Ours stays: it is
   keyboard-reachable, carries a Persian aria-label, and also resets the results
   list, which the native control does not. */
.symbol-search-box input[type="search"]::-webkit-search-cancel-button,
.symbol-search-box input[type="search"]::-webkit-search-decoration {
  -webkit-appearance: none;
  appearance: none;
}
.symbol-search-box .icon-button {
  position: absolute;
  inset-inline-end: var(--fv-space-2);
  width: var(--fv-control-sm);
  height: var(--fv-control-sm);
}
.symbol-search-hint {
  margin: var(--fv-space-3) 0 0;
  font-size: var(--fv-text-sm);
  color: var(--fv-muted);
}

.symbol-search-results {
  margin-top: var(--fv-space-4);
  /* Capped and scrollable: a broad query matches hundreds of option contracts
     and an uncapped list would push the rest of the page off-screen. */
  max-height: 60vh;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: var(--fv-space-5);
}
.symbol-search-group h3 {
  margin: 0 0 var(--fv-space-2);
  font-size: var(--fv-text-xs);
  font-weight: var(--fv-weight-semibold);
  letter-spacing: var(--fv-tracking-caps);
  text-transform: uppercase;
  color: var(--fv-muted);
}
.symbol-search-item {
  display: flex;
  align-items: baseline;
  gap: var(--fv-space-3);
  padding: var(--fv-space-3) var(--fv-space-4);
  border-radius: var(--fv-radius-md);
  color: var(--fv-text);
  text-decoration: none;
}
.symbol-search-item:hover,
.symbol-search-item:focus-visible {
  background: var(--fv-surface-3);
  text-decoration: none;
}
.symbol-search-code { font-weight: var(--fv-weight-semibold); flex: 0 0 auto; }
.symbol-search-name {
  color: var(--fv-muted);
  font-size: var(--fv-text-base);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.symbol-search-sub {
  margin-inline-start: auto;
  flex: 0 0 auto;
  font-size: var(--fv-text-sm);
  color: var(--fv-faint);
}

/* ==========================================================================
   SHAREHOLDER TYPEAHEAD
   ==========================================================================
   Anchored to its own label so the panel can overflow the filter row without
   widening it or pushing the controls below it around.
   ========================================================================== */
.sh-name-field { position: relative; }

.sh-suggest {
  position: absolute;
  z-index: var(--fv-z-drawer);
  inset-inline: 0;
  top: calc(100% + var(--fv-space-2));
  max-height: 320px;
  overflow-y: auto;
  padding: var(--fv-space-2);
  background: var(--fv-surface);
  border: 1px solid var(--fv-line-strong);
  border-radius: var(--fv-radius-md);
  box-shadow: var(--fv-shadow-lg);
}

.sh-suggest-item {
  display: flex;
  align-items: baseline;
  gap: var(--fv-space-3);
  width: 100%;
  padding: var(--fv-space-3) var(--fv-space-3);
  border: 0;
  border-radius: var(--fv-radius-sm);
  background: transparent;
  color: var(--fv-text);
  font-family: inherit;
  font-size: var(--fv-text-base);
  text-align: start;
  cursor: pointer;
}
.sh-suggest-item:hover,
.sh-suggest-item:focus-visible { background: var(--fv-surface-3); }

.sh-suggest-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* The position count is why the list is ordered the way it is; showing it
   makes that ordering legible instead of arbitrary. */
.sh-suggest-count {
  margin-inline-start: auto;
  flex: 0 0 auto;
  font-size: var(--fv-text-sm);
  color: var(--fv-faint);
  font-variant-numeric: tabular-nums;
}

.sh-suggest-empty {
  margin: 0;
  padding: var(--fv-space-4);
  font-size: var(--fv-text-sm);
  color: var(--fv-muted);
  text-align: center;
}

/* ==========================================================================
   LAYOUT HELPERS
   ========================================================================== */
.fv-grid { display: grid; gap: var(--fv-space-5); }
.fv-grid--2 { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.fv-grid--3 { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.fv-row { display: flex; align-items: center; gap: var(--fv-space-3); flex-wrap: wrap; }
.fv-row--between { justify-content: space-between; }
.fv-spacer { flex: 1 1 auto; }
.fv-stack { display: flex; flex-direction: column; gap: var(--fv-space-4); }

.fv-visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ==========================================================================
   RESPONSIVE
   ==========================================================================
   Three real breakpoints, chosen by where the layout actually fails rather
   than by device names.
   ========================================================================== */

/* Sidebar becomes an icon rail: the boards need the horizontal space more than
   the labels, and the icons stay in the same order so muscle memory holds. */
@media (max-width: 1180px) {
  .fv-shell { grid-template-columns: var(--fv-sidebar-collapsed) minmax(0, 1fr); }
  .fv-sidebar { padding-inline: var(--fv-space-2); align-items: center; }
  .fv-nav__label,
  .fv-nav__group,
  .fv-brand__text { display: none; }
  .fv-nav__item { justify-content: center; padding-inline: 0; }
  .fv-brand { justify-content: center; padding-inline: 0; }
  .fv-content { padding: var(--fv-space-5); }
}

/* Sidebar off-canvas; it returns as a drawer via the topbar toggle. */
@media (max-width: 860px) {
  .fv-shell { grid-template-columns: minmax(0, 1fr); }
  .fv-sidebar {
    position: fixed;
    inset-block: 0;
    inset-inline-start: 0;
    width: var(--fv-sidebar);
    z-index: var(--fv-z-drawer);
    transform: translateX(-100%);
    transition: transform var(--fv-duration-slow) var(--fv-ease);
    align-items: stretch;
    padding-inline: var(--fv-space-4);
  }
  html[dir="rtl"] .fv-sidebar { inset-inline-start: auto; inset-inline-end: 0; transform: translateX(100%); }
  .fv-sidebar.is-open { transform: translateX(0); }
  .fv-nav__label, .fv-nav__group, .fv-brand__text { display: revert; }
  .fv-nav__item { justify-content: flex-start; padding-inline: var(--fv-space-3); }
  .fv-brand { justify-content: flex-start; }

  .fv-topbar { padding-inline: var(--fv-space-5); gap: var(--fv-space-3); }
  .fv-topbar__subtitle { display: none; }
  .fv-content { padding: var(--fv-space-4); gap: var(--fv-space-5); }
  .fv-scrim {
    position: fixed; inset: 0;
    z-index: calc(var(--fv-z-drawer) - 1);
    background: var(--fv-overlay);
  }
}

/* Phone. Controls grow to comfortable touch targets -- this is the one place
   the product should NOT be dense -- and wide tables scroll horizontally
   inside their own container rather than forcing the page to. */
@media (max-width: 560px) {
  :root {
    --fv-control-sm: 32px;
    --fv-control-md: 40px;
    --fv-control-lg: 46px;
  }
  .fv-content { padding: var(--fv-space-3); }
  .fv-kpi-grid { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: var(--fv-space-3); }
  .fv-kpi { padding: var(--fv-space-3) var(--fv-space-4); }
  .fv-kpi__value { font-size: var(--fv-text-xl); }
  .fv-filters { grid-template-columns: minmax(0, 1fr); }
  .fv-panel__head { padding: var(--fv-space-3) var(--fv-space-4); }
  .fv-panel__body { padding: var(--fv-space-4); }
  .fv-table th, .fv-table td { padding: var(--fv-space-3); }
  .fv-topbar__title { font-size: var(--fv-text-lg); }
  .fv-toasts { inset-inline: var(--fv-space-3); inset-block-end: var(--fv-space-3); }
  .fv-toast { min-width: 0; max-width: none; }
}

/* ==========================================================================
   PRINT
   ==========================================================================
   Traders export boards to PDF for meetings; without this the dark chrome
   prints as a solid black block and wastes the page.
   ========================================================================== */
@media print {
  .fv-sidebar, .fv-topbar__actions, .fv-toasts, .fv-filters__actions { display: none !important; }
  .fv-shell { grid-template-columns: minmax(0, 1fr); }
  .fv-panel { break-inside: avoid; box-shadow: none; border-color: #999; }
  .fv-table thead th { position: static; }
  body { background: #fff; color: #000; }
}

/* ==========================================================================
   Spacing normalisation -- one scale for every page
   ==========================================================================
   The product carries two parallel spacing systems. Board pages set
   `body.platform-v270`, which switches on platform.css's geometry; the
   landing page, /shareholders/ and /trends/ do not, so they fall through to
   the older app.css / site.css values. The two disagree on nearly every
   structural measurement:

     element          boards (platform-v270)   everywhere else
     ---------------  -----------------------  ---------------------------
     .panel-header    15px 18px, gap 14        16px 18px, gap 14
     .main-content    18px 22px 86px           0 24px 30px
     .topbar          14px 0 16px, gap 16      12px 24px / 10px 0, gap 14
     .data-table th   7px 12px                 11px 12px  (td 9px 12px)
     panel stack      16px grid row-gap        12px / 14px / 0px

   Adding `platform-v270` to the remaining pages would harmonise them, but
   that class carries colour and sizing far beyond spacing. So the spacing is
   restated here once, for both systems, in tokens.

   Every value below is a step from the --fv-space-* scale in tokens.css. The
   numbers being replaced -- 7, 9, 10, 11, 14, 15, 18, 22, 30, 86 -- were not
   on it. This file loads last, so these win without !important; the one
   exception is platform.css's !important cell rule, which reads the two
   custom properties redefined immediately below.

   Deliberately NOT normalised: .main-content padding-block-start stays 0 on
   the landing page. Its topbar bleeds edge-to-edge via `margin: 0 -24px`,
   and a top pad would inset the bleed. Boards keep a real top pad.          */

:root {
  /* Consumed by platform.css's `.data-table th/td { ... !important }`, which
     is why redefining them here reaches the board tables too. 7px -> 8px. */
  --platform-cell-x: var(--fv-space-4);  /* 12px, unchanged */
  --platform-cell-y: var(--fv-space-3);  /* 8px  (was 7px) */

  /* Height of the fixed market ticker. app.css already reserves this on
     `body.has-market-ticker`; naming it keeps the two in step. */
  --fv-ticker-h: 42px;
}

/* ---- outer: page frame ------------------------------------------------- */

.main-content,
body.platform-v270 .main-content {
  padding-inline: var(--fv-space-7);      /* 24px (boards were 22px) */
  padding-block-end: var(--fv-space-9);   /* 40px (boards were 86px) */
}

body.platform-v270 .main-content {
  padding-block-start: var(--fv-space-5); /* 16px (was 18px) */
}

/* ---- outer: the top bar ------------------------------------------------ */
/* Only the block axis and the gap are stated. The inline treatment differs
   on purpose -- boards run the bar inside the content column, the landing
   page bleeds it wide with a negative inline margin -- so it is left alone. */

.topbar,
.v2-topbar,
body.platform-v270 .topbar {
  /* !important is required here: .v2-topbar pins `padding-block: 10px 12px`
     with !important of its own, which is why the bar sat 2px shallower on
     every page except the landing one. */
  padding-block: var(--fv-space-4) !important;   /* 12px */
  gap: var(--fv-space-5);                 /* 16px */
  margin-block-end: var(--fv-space-5);    /* 16px */
}

/* ---- outer: rhythm between panels -------------------------------------- */
/* Grid-based stacks ran at three different row gaps (12 / 14 / 16). */

.v2-insight-grid,
.trends-main-grid,
.home-visual-grid,
.home-analysis-grid,
.personal-home-grid,
.market-overview-lower,
.market-overview-section {
  row-gap: var(--fv-space-5);             /* 16px */
}

/* Block-flow stacks had no rhythm at all: on /bonds/ the three panels sat
   flush against each other (0px), while /equity/ leaned on .history-panel's
   own 14px margin. Give every panel that is a direct child of the page the
   same trailing space, and drop it on the last one so the page-bottom pad
   above is the only thing below the fold. */
main.main-content > .panel,
main.main-content > .v2-insight-grid,
main.main-content > .trends-main-grid {
  margin-block-end: var(--fv-space-5);    /* 16px */
}
main.main-content > :last-child {
  margin-block-end: 0;
}

/* ---- inner: panel header ----------------------------------------------- */

.panel-header,
.panel > .panel-header,
body.platform-v270 .panel-header {
  /* Block axis snaps to the scale. The inline 18px is NOT a scale step, but
     three separate rules pin it with !important (hub.css:3620,
     ui-clean.css:772, pro.css:613); it is already uniform on every page, so
     it is stated here as-is rather than fought over for 2px. */
  padding: var(--fv-space-5) 18px;               /* 16px 18px */
  gap: var(--fv-space-4);                        /* 12px */
}

/* ---- inner: table cells ------------------------------------------------ */
/* The standalone /shareholders/ and /trends/ tables sat at 11px (head) and
   9px (body) against every board table's 7px, so the same data read at two
   different densities. Both now take the cell tokens set at the top. */

.data-table thead th {
  padding: var(--platform-cell-y) var(--platform-cell-x);
}
.data-table tbody td {
  padding: var(--platform-cell-y) var(--platform-cell-x);
}

/* ---- inner: filters ---------------------------------------------------- */
/* #filterBody carries id specificity, so this selector has to match it. */

#filterBody .filter-field {
  gap: var(--fv-space-3);                 /* 8px (was 7px) */
}

/* ---- inner: cards ------------------------------------------------------ */
/* 15px pad / 12px gap on the landing page against 16px / 10px on boards. */

.stat-card,
.metric-card,
.kpi-card,
.summary-card {
  padding: var(--fv-space-5);             /* 16px */
  gap: var(--fv-space-4);                 /* 12px */
}

/* .kpi-card is flex on the landing page (gap = space between stacked lines)
   but grid on boards, where platform.css lays it out as `42px minmax(0,1fr)`
   and the gap is the icon-to-content column. Different axis, same token. */
body.platform-v270 .kpi-card,
body.platform-v270 .module-kpi-grid .kpi-card {
  gap: var(--fv-space-4);                 /* 12px (was 10px) */
}

/* ==========================================================================
   Strategy Lab — payoff domain scrubber and scenario summary
   ==========================================================================
   Both borrow the geometry of the existing .strategy-time-scrubber so the two
   controls under the payoff chart read as one pair rather than two designs. */

.strategy-domain-scrubber {
  display: grid;
  gap: var(--fv-space-3);
  padding: var(--fv-space-4) var(--fv-space-5);
  border-top: 1px solid var(--line);
}

.strategy-domain-scrubber > div:first-child {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--fv-space-3);
  font-size: 12px;
  color: var(--muted);
}

.strategy-domain-scrubber > div:first-child b {
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.strategy-domain-scrubber small {
  color: var(--muted);
  font-size: 10px;
  line-height: 1.7;
}


/* ---- scenario summary chips -------------------------------------------- */

.scenario-summary {
  display: flex;
  flex-wrap: wrap;
  gap: var(--fv-space-2);
  justify-content: flex-end;
  max-width: 60%;
}

.scenario-chip {
  display: inline-flex;
  align-items: baseline;
  gap: var(--fv-space-2);
  padding: var(--fv-space-1) var(--fv-space-3);
  border: 1px solid var(--line);
  border-radius: var(--fv-radius-full);
  background: var(--panel-2, var(--panel));
  font-size: 10px;
  white-space: nowrap;
}

.scenario-chip i {
  font-style: normal;
  color: var(--muted);
}

.scenario-chip b {
  font-variant-numeric: tabular-nums;
  font-weight: 700;
}

/* A hand-set domain is worth spotting at a glance: it is the one parameter
   here that silently overrides an automatic behaviour. */
.scenario-chip.manual {
  border-color: color-mix(in srgb, var(--accent, #2f6feb) 45%, var(--line));
}

.scenario-chip.manual b { color: var(--accent, #2f6feb); }
.scenario-chip.muted b { color: var(--muted); }

@media (max-width: 900px) {
  .scenario-summary { max-width: 100%; justify-content: flex-start; }
}

/* ---- Strategy Lab: per-row entry-price basis --------------------------- */
/* Sits beside the price it sets, so a position mixing a marketable leg with a
   resting one can be priced honestly rather than through one global switch. */

/* Input and basis buttons are stacked so both fit: side by side in a dense
   table the buttons were clipped to an ellipsis and could not be clicked. */
.strategy-entry-col {
  white-space: nowrap;
}
.strategy-entry-col .strategy-money-input {
  display: block;
  width: 100%;
}

.leg-basis-group {
  display: inline-flex;
  margin-block-start: 4px;
  border: 1px solid var(--line);
  border-radius: var(--fv-radius-sm);
  overflow: hidden;
  vertical-align: middle;
}

.leg-basis {
  appearance: none;
  border: 0;
  padding: 2px var(--fv-space-2);
  font: inherit;
  font-size: 10px;
  line-height: 1.6;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
}

.leg-basis + .leg-basis { border-inline-start: 1px solid var(--line); }
.leg-basis:hover:not(:disabled) { background: var(--panel-2, var(--panel)); color: var(--text); }
.leg-basis:focus-visible { outline: 2px solid var(--accent, #2f6feb); outline-offset: -2px; }

.leg-basis.active {
  background: color-mix(in srgb, var(--accent, #2f6feb) 14%, transparent);
  color: var(--accent, #2f6feb);
  font-weight: 700;
}

/* A hand-typed price is a deliberate assumption; the control steps aside
   rather than overwriting it on the next market tick. */
.leg-basis:disabled {
  opacity: .45;
  cursor: not-allowed;
}

/* ---- sticky headers: drop the backdrop blur ---------------------------- */
/* A sticky element with backdrop-filter forces the compositor to re-sample
   everything behind it on every scroll frame, across the full header width.
   Measured on the Strategy Lab at 6x CPU throttle, scrolling the assistant:

       as shipped              worst frame 175.1ms, 6 janky frames
       without backdrop-filter worst frame  58.4ms, 3 janky frames
       also without shadows    worst frame  33.3ms, 2 janky frames

   Both bars already sit on a near-opaque background (88%), so making them
   fully opaque costs almost nothing visually and removes the per-frame work.
   The remaining shadow cost is left alone: that is a deliberate design choice,
   not a bug, and is worth a separate decision. */

.v2-topbar,
.strategy-pro-topbar {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  background: var(--bg);
}

/* In «همه دارایی‌ها» mode a row's strategy name is ambiguous on its own, so
   the underlying rides underneath it. */
.ranked-underlying {
  display: block;
  margin-block-start: 2px;
  color: var(--muted);
  font-size: 10px;
  font-weight: 600;
}

/* ---- توازن سفارش‌های بازار: bar and label must point the same way ------- */
/* pro.css:462 forces `direction: ltr` on the meter while its labels
   (pro.css:466) inherit the page's RTL. The two therefore disagreed: the buy
   segment rendered on the LEFT while «خرید» sat on the RIGHT, so at a lopsided
   book the red sliver appeared on the right under the green label. Dropping
   the override makes the meter RTL-native like the labels above and below it:
   buy right, sell left, each under its own colour. */

.order-balance-meter {
  direction: rtl;
}

/* A 2% side is ~3px wide at typical card widths and can vanish entirely.
   A floor keeps it perceivable without inflating the share it represents --
   the width still encodes the true proportion above this threshold. */
.order-balance-meter i {
  min-width: 2px;
}

/* ---- table: rows per page ---------------------------------------------- */

.page-size-field {
  display: inline-flex;
  align-items: center;
  gap: var(--fv-space-2);
  margin-inline-end: var(--fv-space-4);
  font-size: 10px;
  color: var(--muted);
  white-space: nowrap;
}

.page-size-field select {
  height: auto;
  min-height: 0;
  padding: 2px var(--fv-space-3);
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}

/* ---- table: fullscreen -------------------------------------------------- */
/* Applied both by the real Fullscreen API and by the fallback class, so the
   two look identical. requestFullscreen rejects inside an iframe or when the
   permission is denied, and a button that silently does nothing is worse than
   one that expands the panel in place. */

.table-panel.is-fullscreen {
  position: fixed;
  inset: 0;
  z-index: 90;
  margin: 0;
  border-radius: 0;
  border: 0;
  background: var(--panel);
  display: flex;
  flex-direction: column;
}

/* The scroller has to take the leftover height, otherwise the table keeps its
   original max-height and fullscreen just adds empty space below it. */
.table-panel.is-fullscreen .table-scroll {
  flex: 1 1 auto;
  max-height: none;
  height: auto;
}

.table-panel.is-fullscreen .panel-header { flex: 0 0 auto; }
.table-panel.is-fullscreen .panel-footer { flex: 0 0 auto; }

/* ==========================================================================
   KPI cards — density and number integrity
   ==========================================================================
   platform.css sets `min-height: 122px` (rendering 148px tall) and, on the
   value itself, `white-space: normal` with `overflow-wrap: anywhere`. The
   second pair is the serious one: it lets a primary figure break mid-number,
   so ۲۷۶٬۴۳۳٬۶۳۴ can split across two lines at a narrow card. A number that
   wraps is a number that gets misread. */

body.platform-v270 .kpi-card,
body.platform-v270 .module-kpi-grid .kpi-card,
body.platform-v270 .special-kpi,
body.platform-v270 .ops-card,
body.platform-v270 .stats-kpi {
  /* Terminal density: the content needs roughly two thirds of this. */
  min-height: 92px;
  padding: var(--fv-space-4) var(--fv-space-5);
  gap: var(--fv-space-3);
}

body.platform-v270 .kpi-card strong,
body.platform-v270 .special-kpi b {
  /* The value and its unit may still sit on separate lines -- that is the
     intended layout -- but neither may break INSIDE itself. */
  white-space: nowrap;
  overflow-wrap: normal;
  word-break: keep-all;
  /* Digits share a width, so figures line up down a column of cards and a
     changing value does not jitter. */
  font-variant-numeric: tabular-nums;
  line-height: 1.25;
  gap: var(--fv-space-1) var(--fv-space-2);
}

body.platform-v270 .kpi-card strong > *,
body.platform-v270 .special-kpi b > * {
  white-space: nowrap;
  overflow-wrap: normal;
}

/* Long figures shrink rather than wrap or overflow.
   The card has to declare itself a container for these to apply at all --
   without it the queries never match and a twelve-digit value overflows at the
   widest breakpoint, where a six-column grid makes each card narrowest. */
body.platform-v270 .kpi-card,
body.platform-v270 .module-kpi-grid .kpi-card {
  container-type: inline-size;
}

/* Thresholds sit below the width an ordinary card now gets (~200-240px).
   They were 260/210/170, tuned while the KPI row still overflowed and cards
   were wider; against correctly-sized cards those tiers shrank every value,
   including short ones. These trigger where a long figure genuinely cannot
   fit rather than on every card. */
@container (max-width: 190px) {
  body.platform-v270 .kpi-card strong { font-size: 0.92em; }
}
@container (max-width: 158px) {
  body.platform-v270 .kpi-card strong { font-size: 0.82em; }
}
@container (max-width: 132px) {
  body.platform-v270 .kpi-card strong { font-size: 0.7em; }
}

body.platform-v270 .kpi-card small {
  line-height: 1.5;
}

@media (max-width: 760px) {
  body.platform-v270 .kpi-card,
  body.platform-v270 .module-kpi-grid .kpi-card {
    min-height: 78px;
    padding: var(--fv-space-3) var(--fv-space-4);
  }
}

/* ---- option trend: Y axis ---------------------------------------------- */
/* The chart previously had no axis at all: it could say which day was taller
   but never how much. The axis sits OUTSIDE the horizontal scroller so it
   stays put while the bars scroll. */

.option-trend-plot {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: var(--fv-space-3);
  align-items: stretch;
}

.option-trend-yaxis {
  position: relative;
  min-width: 62px;
  padding-block-end: 18px;   /* clears the date labels under the bars */
  font-variant-numeric: tabular-nums;
}

.option-trend-tick {
  position: absolute;
  inset-inline-end: 0;
  transform: translateY(50%);
  font-size: 10px;
  color: var(--muted);
  white-space: nowrap;
}

/* A faint rule across the plot so a bar can be read against its tick rather
   than estimated. */
.option-trend-tick::after {
  content: '';
  position: absolute;
  inset-inline-end: -6px;
  top: 50%;
  width: 4px;
  border-top: 1px solid var(--line);
}

.option-trend-unit {
  display: block;
  margin-block-end: var(--fv-space-2);
  font-size: 10px;
  font-weight: 700;
  color: var(--muted);
  text-align: end;
  white-space: nowrap;
}

@media (max-width: 760px) {
  .option-trend-yaxis { min-width: 48px; }
  .option-trend-tick { font-size: 10px; }
}

/* ---- IV term structure: days-to-expiry beside the expiry --------------- */
/* A term structure is read along the time axis, so the horizon has to be
   visible on the row rather than only in a tooltip. */

.option-term-row .term-dte {
  display: block;
  margin-block-start: 1px;
  font-size: 10px;
  font-weight: 600;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* ---- Unusual Activity table: stop the needless horizontal scrollbar ---- */
/* pro.css:804 pinned `min-width: 620px`, matched to the grid column at the
   time. It desynced from `.data-table td { white-space: nowrap }` (app.css),
   which the علت اصلی column's full-sentence values hit hardest; a 621px
   viewport already forces a scrollbar. Sizing to content instead of a
   guessed pixel value keeps the two from drifting apart again. */

/* pro.css:53 sets `.v2-market-page .data-table { min-width: max-content }`
   platform-wide, so a plain `.option-unusual-table` selector cannot beat it --
   both are one compound selector, and source order alone decided it. Matching
   the `.v2-market-page` prefix brings this to equal specificity, and this file
   loads last, so it wins. */
.v2-market-page .option-unusual-table,
.option-unusual-table {
  min-width: 0 !important;
  width: 100%;
  table-layout: auto;
}

/* Every OTHER column is a short label or a number and stays on one line;
   only the reason column is prose, and prose is what should wrap. */
.data-table tbody td.option-unusual-reason,
.option-unusual-table .option-unusual-reason {
  white-space: normal;
  max-width: 220px;
  font-size: 10px;
  line-height: 1.5;
  color: var(--muted);
}

/* §59 — when IV Term Structure has nothing to plot (e.g. an underlying with
   no eligible-IV contracts), pro.css:792's fixed .82fr/1.5fr split still
   reserves the same width for it, leaving Unusual Activity squeezed next to
   a near-empty card. market.js stamps data-term-empty on the grid so this
   can react instead of holding a rigid 2-column geometry unconditionally. */
.option-intelligence-grid[data-term-empty="1"] {
  grid-template-columns: minmax(220px, .32fr) minmax(0, 1fr);
}

/* §61 — a native <select> sizes its closed box to its widest <option>, not
   the selected one. moduleSort's options are column labels, and some run
   much longer than "نماد" (whichever happens to be selected on load), so the
   closed box was reserving width for a label that was not on screen. The
   dropdown list itself is unaffected -- only the closed control is capped,
   with an ellipsis so a genuinely long selection still reads as truncated
   rather than cut off with no indication. */
#moduleSort {
  max-width: 148px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Gold covered-call board: شرح قرارداد used to be jammed into the symbol
   cell as a <small>, forcing every row to the height of its longest
   description and making نماد impossible to scan. It is now its own
   column; truncate to one line here since the full text is still on the
   cell as a title tooltip. */
.contract-desc {
  display: block;
  max-width: 260px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--sp-muted, var(--muted));
  font-size: 11px;
}

/* Price domain slider: rebuilt as one real dual-handle control (§ gold/lab
   feedback). It used to be two independent range inputs, each with its own
   min-at-0 track, and neither had a direction override -- on this RTL page
   a plain <input type="range"> renders max-at-left, so dragging left
   actually raised the value instead of lowering it. Two natively-stacked
   range inputs can't share one visible thumb+track, so both inputs now
   share the same -50..100 domain, are laid transparently on top of each
   other, and only their thumbs accept pointer events; a separate div
   between them supplies the blue "selected span" fill.
   direction:ltr is deliberate and matches .numeric elsewhere: a price axis
   reads low-to-high left-to-right even inside an RTL page. */
/* 44px, not the 28px this used to declare: app.css applies a
   `min-height: 44px` touch-target floor to .field inputs, and min-height
   always clamps height regardless of specificity -- so the input rendered
   at 44px while the container stayed 28px and the fill was positioned for
   neither. Matching the real height keeps the accessible target and lets
   the fill line up with the native runnable track. */
.domain-range-track {
  position: relative;
  height: 44px;
  direction: ltr;
}
/* The unselected base line. It lives on the container, not on either
   input's ::-webkit-slider-runnable-track: two stacked range inputs each
   painting an opaque track meant the upper one covered both the blue fill
   AND the lower input's thumb, so only one handle was visible and the
   selected span disappeared. The native tracks are transparent below. */
.domain-range-track::before {
  content: "";
  position: absolute;
  inset-inline: 0;
  top: 50%;
  transform: translateY(-50%);
  height: 4px;
  border-radius: 999px;
  background: var(--line);
  pointer-events: none;
}
.domain-range-track input[type="range"] {
  position: absolute;
  inset-inline: 0;
  top: 0;
  width: 100%;
  height: 44px;
  margin: 0;
  background: transparent;
  pointer-events: none;
  -webkit-appearance: none;
  appearance: none;
}
.domain-range-track input[type="range"]::-webkit-slider-runnable-track {
  height: 4px;
  background: transparent;
  border-radius: 999px;
}
.domain-range-track input[type="range"]::-moz-range-track {
  height: 4px;
  background: transparent;
  border-radius: 999px;
}
.domain-range-track input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  pointer-events: auto;
  width: 16px;
  height: 16px;
  margin-top: -6px;
  border-radius: 50%;
  background: var(--accent, var(--primary));
  border: 2px solid var(--panel);
  box-shadow: 0 1px 4px rgba(0,0,0,.25);
  cursor: pointer;
}
.domain-range-track input[type="range"]::-moz-range-thumb {
  pointer-events: auto;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--accent, var(--primary));
  border: 2px solid var(--panel);
  box-shadow: 0 1px 4px rgba(0,0,0,.25);
  cursor: pointer;
}
/* Centred rather than pinned to a hard-coded top: the browser centres the
   native runnable track in the input box, so centring the fill in the same
   box is what actually keeps the two on the same line. */
.domain-range-fill {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  height: 4px;
  background: var(--accent, var(--primary));
  border-radius: 999px;
  pointer-events: none;
}
.domain-range-readout {
  display: flex;
  justify-content: space-between;
  direction: ltr;
  font-size: 11px;
  color: var(--muted);
}
.domain-range-readout b {
  color: var(--text);
  font-variant-numeric: tabular-nums;
  margin-inline-start: 4px;
}

/* Scenario panel: essential controls (vol, mode, the four toggles) stay on
   screen; rates, fees and manual chart bounds move behind this disclosure.
   Reuses .strategy-settings-pro's own grid so the advanced block lines up
   with the fields above it once opened. */
.strategy-advanced-toggle {
  justify-self: start;
  margin-top: 2px;
}
.strategy-settings-advanced {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(4, minmax(130px, 1fr));
  gap: 11px;
  margin-top: 4px;
  padding-top: 11px;
  border-top: 1px dashed var(--line);
}
@media (max-width: 1180px) {
  .strategy-settings-advanced { grid-template-columns: repeat(3, minmax(130px, 1fr)); }
}
@media (max-width: 900px) {
  .strategy-settings-advanced { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 620px) {
  .strategy-settings-advanced { grid-template-columns: 1fr; }
}

/* ریسک و Greeks / ماتریس سناریو share one panel now, switched by this
   toggle -- they are two readings of the same active position, so keeping
   them side by side doubled the row height to show one thing twice. */
.risk-scenario-toggle button {
  min-width: 96px;
  font-size: 11px;
}
.risk-scenario-note {
  margin: 0 0 10px;
  color: var(--muted);
  font-size: 11px;
}
.risk-scenario-view[hidden] { display: none !important; }

/* ---- legs table: contract-derived values ---- */
/* نوع / اعمال / روز belong to the chosen contract. Rendered as values, not
   inputs, so a row can never claim a strike its named contract lacks. */
.leg-derived {
  display: inline-block;
  padding: 4px 8px;
  border-radius: 8px;
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  color: var(--text);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  white-space: nowrap;
}

/* ---- searchable contract picker ---- */
.leg-symbol-field { display: block; min-width: 0; }
.leg-symbol-picker {
  position: absolute;
  z-index: 3000;
  max-height: 320px;
  overflow-y: auto;
  padding: 4px;
  border: 1px solid var(--line);
  border-radius: 12px;
  background: var(--panel);
  box-shadow: 0 18px 46px rgba(8, 26, 59, .22);
  direction: rtl;
}
.leg-symbol-picker[hidden] { display: none !important; }
.leg-symbol-head,
.leg-symbol-option {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) auto minmax(0, 1fr) auto;
  gap: 10px;
  align-items: center;
  padding: 7px 9px;
}
.leg-symbol-head {
  position: sticky;
  top: 0;
  background: var(--panel);
  color: var(--muted);
  font-size: 10px;
  font-weight: 700;
  border-bottom: 1px solid var(--line);
}
.leg-symbol-option {
  border-radius: 9px;
  cursor: pointer;
  font-size: 11px;
}
.leg-symbol-option:hover,
.leg-symbol-option.active {
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}
.leg-symbol-option strong {
  font-size: 11.5px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.leg-symbol-type {
  padding: 2px 7px;
  border-radius: 999px;
  font-size: 10px;
  font-weight: 700;
  background: color-mix(in srgb, var(--green) 16%, transparent);
  color: var(--green);
}
.leg-symbol-type.put {
  background: color-mix(in srgb, var(--red) 16%, transparent);
  color: var(--red);
}
.leg-symbol-empty {
  padding: 16px;
  text-align: center;
  color: var(--muted);
  font-size: 11px;
}

/* پارامترهای سناریو holds the full row now that ریسک/Greeks sits beside the
   P&L chart it describes, so its field grid can breathe instead of being
   squeezed into a 1.55fr column. */
.strategy-workbench-grid.is-single {
  grid-template-columns: minmax(0, 1fr) !important;
}

/* ---- strategy tester: command bar + greeks ---- */
/* قیمت پایه is derived from the chosen contract, so it reads as a value
   beside the chain coverage rather than an input that can contradict it.
   The column it vacated widens زنجیره زنده. */
.strategy-spot-readout { display: grid; gap: 2px; }
.strategy-spot-readout span { color: var(--muted); font-size: 10px; }
.strategy-spot-readout strong { font-variant-numeric: tabular-nums; font-size: 13px; }

.strategy-pro-page .strategy-command-main {
  grid-template-columns: minmax(200px, 1fr) minmax(150px, .7fr) minmax(260px, 1.5fr);
}

/* نما sits with the chart it switches. */
.payoff-mode-select { margin-inline-end: var(--fv-space-4); }

/* The Greeks tiles ran flush to the panel edge. */
.strategy-greeks-pro {
  padding-inline: var(--fv-space-4);
  padding-block-start: var(--fv-space-3);
}

/* ---- per-leg price basis pin (mirrors زنجیره زنده) ---- */
.leg-basis-group {
  border-radius: var(--fv-radius-full);
  overflow: hidden;
}
.leg-basis {
  display: inline-flex;
  align-items: center;
  gap: 3px;
}
.leg-basis.active::before {
  content: "";
  width: 5px; height: 5px;
  border-radius: 50%;
  background: currentColor;
}

/* The time scrubber had the RTL inversion the price-domain slider used to
   have: a range input inherits the page's rtl direction, so ArrowRight/drag
   right moved the value DOWN. A time axis reads left-to-right (today on the
   left, expiry on the right) like the price axis above it. */
.strategy-time-scrubber input[type="range"] {
  direction: ltr;
}

/* ---- leg price-basis pin (replaces the سرخط|آخرین toggle pair) ---- */
/* One trigger showing the active basis, opening a list -- the control
   زنجیره زنده already uses for this decision, and the only shape that has
   room for a third option (میانگین). */
.leg-basis-pin { position: relative; display: block; margin-block-start: 4px; }

.leg-basis-trigger {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 8px;
  border: 1px solid var(--line);
  border-radius: var(--fv-radius-full);
  background: var(--panel);
  color: var(--text);
  font: inherit;
  font-size: 10px;
  cursor: pointer;
}
.leg-basis-trigger:hover { border-color: var(--accent); color: var(--accent); }
.leg-basis-trigger b { font-weight: 700; }
.leg-basis-icon svg { width: 12px; height: 12px; }
.leg-basis-caret {
  width: 0; height: 0;
  border-inline: 3px solid transparent;
  border-block-start: 4px solid currentColor;
}
/* A hand-typed price is a deliberate assumption; the pin says so rather than
   silently continuing to advertise a market basis. */
.leg-basis-trigger.is-manual { opacity: .55; }

.leg-basis-menu {
  /* Positioned from script as a body-level fixed layer. No insets here: the
     old `inset-inline-start: 0` resolves to `right: 0` under RTL and beat the
     inline `left`, pinning the menu to the viewport edge -- on top of the
     sidebar -- instead of under its trigger. */
  position: fixed;
  z-index: 3000;
  min-width: 104px;
  padding: 4px;
  border: 1px solid var(--line);
  border-radius: var(--fv-radius-md);
  background: var(--panel);
  box-shadow: 0 12px 30px rgba(8, 26, 59, .18);
  display: grid;
  gap: 2px;
}
.leg-basis-menu[hidden] { display: none !important; }
.leg-basis-menu button {
  padding: 5px 9px;
  border: 0;
  border-radius: var(--fv-radius-sm);
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: 10.5px;
  text-align: start;
  cursor: pointer;
}
.leg-basis-menu button:hover { background: var(--panel-2); }
.leg-basis-menu button.active { color: var(--accent); font-weight: 700; }

/* ---- leg direction: segmented, not a dropdown ---- */
/* Direction is binary and the most consequential field in the row; a select
   hides the current value behind a control you must open to read it, and
   buy/sell carry colour everywhere else on the platform. */
.leg-side-toggle {
  /* One width for the column whether the row offers one direction or two:
     the UA row has no فروش, and a lone خرید sized to its own text left the
     جهت column ragged against the option rows beside it. */
  --leg-side-w: 52px;
  display: inline-flex;
  justify-content: center;
  width: calc(var(--leg-side-w) * 2 + 3px);
  border: 1px solid var(--line);
  border-radius: var(--fv-radius-md, 9px);
  overflow: hidden;
}
.leg-side-toggle .leg-side {
  appearance: none;
  border: 0;
  /* Equal width so the pair reads as one control and neither side looks
     like the default; the label lengths differ by a character in Persian. */
  flex: 1 1 auto;
  min-width: var(--leg-side-w);
  padding: 5px 10px;
  text-align: center;
  background: var(--panel);
  font: inherit;
  font-size: 11px;
  font-weight: 600;
  line-height: 1.6;
  cursor: pointer;
}
/* Buy is green and sell is red at all times -- the colour identifies the
   side, and only weight/fill changes to show which one is selected. */
.leg-side-toggle .leg-side.buy { color: var(--green); }
.leg-side-toggle .leg-side.sell { color: var(--red); }
.leg-side-toggle .leg-side + .leg-side { border-inline-start: 1px solid var(--line); }
.leg-side-toggle .leg-side:hover:not(.active) { background: var(--panel-2); color: var(--text); }
.leg-side-toggle .leg-side.buy.active {
  background: color-mix(in srgb, var(--green) 18%, var(--panel));
  color: var(--green);
  font-weight: 700;
}
.leg-side-toggle .leg-side.sell.active {
  background: color-mix(in srgb, var(--red) 18%, var(--panel));
  color: var(--red);
  font-weight: 700;
}
.leg-side-toggle .leg-side:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* Pin sits beside the price it qualifies, not under it -- stacking made the
   row taller for a control that is narrower than the box next to it. */
.entry-with-pin {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
/* Every price box is the same size whether or not its row carries a pin --
   a stock row has no basis to pin, and letting its input take the whole cell
   made the two prices in one column different widths. */
.strategy-entry-col .strategy-money-input,
.entry-with-pin .strategy-money-input {
  width: 108px;
  min-width: 108px;
  flex: 0 0 108px;
  text-align: center;
}
.entry-with-pin .leg-basis-pin { margin-block-start: 0; }

/* The popover is a body-level element now; without an explicit width the
   fixed box stretched to most of the viewport. */
/* Above the sidebar (z-index 76-80 in app.css) and the sticky table headers.
   At the popover's original z-index of 40 it rendered UNDER the sidebar, so
   opening a pin near the inline edge looked like the menu had vanished into
   the nav. Matches the symbol picker's layer in this same table. */
#legBasisMenu {
  width: max-content;
  max-width: 180px;
  z-index: 3000;
}

/* The UA row has no فروش at all -- there is no borrow market for the
   underlying, so the row states its one direction rather than offering a
   choice with half of it greyed out. Without the pair, the segmented frame
   would read as a button you can press. */
.leg-side-toggle.is-single {
  border: 0;
  background: transparent;
}
.leg-side-toggle.is-single .leg-side {
  background: transparent;
  cursor: default;
  font-weight: 700;
}

.fv-filter-summary { display:flex; flex-wrap:wrap; align-items:center; gap:8px; font-size:var(--fv-type-helper); }
.fv-filter-summary .filter-chips { display:flex; flex-wrap:wrap; gap:6px; }
.fv-filter-summary button:disabled { opacity:.55; cursor:default; }

/* Current-price tape: one shared component, independent of retired fixed footers. */
.fv-price-tape { display:flex; align-items:center; gap:8px; min-width:0; max-width:100%; width:100%; height:44px; flex:none; overflow:hidden; background:var(--fv-surface); border-block-end:1px solid var(--fv-line); color:var(--fv-text); font-size:12px; }
.fv-price-tape[hidden] { display:none; }
.fv-price-tape__status { flex:none; max-width:220px; padding-inline:12px; font-size:var(--fv-type-timestamp,12px); color:var(--fv-muted); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.fv-price-tape__viewport { container-type:inline-size; min-width:0; flex:1; overflow:hidden; direction:ltr; }
.fv-price-tape__track { display:flex; width:max-content; animation:fv-price-tape-loop 75s linear infinite; }
.fv-price-tape__group { display:flex; flex:none; min-width:100cqw; justify-content:space-around; direction:rtl; }
.fv-price-tape__item { display:grid; grid-template-columns:minmax(60px,1fr) auto; align-content:center; gap:0 10px; flex:0 0 284px; width:284px; min-height:44px; padding:2px 14px; color:inherit; text-decoration:none; border-inline-end:1px solid var(--fv-line); }
a.fv-price-tape__item:hover { background:var(--fv-accent-soft); }
.fv-price-tape__label { grid-row:span 2; align-self:center; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-weight:500; }
.fv-price-tape__value,.fv-price-tape__change { white-space:nowrap; text-align:end; font-variant-numeric:tabular-nums; line-height:1.6; }
.fv-price-tape__change { font-size:12px; color:var(--fv-muted); }
.fv-price-tape__item[data-tone=positive] .fv-price-tape__change { color:var(--fv-up); }
.fv-price-tape__item[data-tone=negative] .fv-price-tape__change { color:var(--fv-down); }
.fv-price-tape__pause { flex:none; min-width:44px; min-height:44px; border:0; background:transparent; color:var(--fv-muted); font:inherit; cursor:pointer; }
.fv-price-tape:hover .fv-price-tape__track,.fv-price-tape[data-paused=true] .fv-price-tape__track { animation-play-state:paused; }
.fv-price-tape:focus-within .fv-price-tape__track,.fv-price-tape[data-reduced-motion=true] .fv-price-tape__track { animation:none; transform:none; }
.fv-price-tape:focus-within .fv-price-tape__viewport,.fv-price-tape[data-reduced-motion=true] .fv-price-tape__viewport { overflow-x:auto; }
.fv-price-tape:focus-within .fv-price-tape__group[aria-hidden],.fv-price-tape[data-reduced-motion=true] .fv-price-tape__group[aria-hidden] { display:none; }
.fv-price-tape :focus-visible { outline:2px solid var(--fv-accent); outline-offset:-2px; }
@keyframes fv-price-tape-loop { to { transform:translateX(-50%); } }
@media(prefers-reduced-motion:reduce) {
  .fv-price-tape__track { animation:none; }
  .fv-price-tape__viewport { overflow-x:auto; }
  .fv-price-tape__group[aria-hidden] { display:none; }
}
@media(max-width:768px) { .fv-price-tape__status { max-width:145px; padding-inline:8px; } }
@media(max-width:480px) { .fv-price-tape__status { max-width:105px; } .fv-price-tape__item { flex-basis:245px; width:245px; padding-inline:10px; } }
@media print { .fv-price-tape { display:none; } }
