/* ---- Self-hosted fonts ----
   Google Fonts was an @import at the top of this file: a render-blocking request to a
   third party that is slow or unreachable from Iran, a privacy leak of every visitor's
   IP to Google, and a CSP violation once font-src/style-src became 'self'. These are
   the same woff2 subsets Google serves (variable weight, split by unicode-range so a
   page only fetches the ranges it uses), now on our origin with a one-year cache.

   font-display: optional, measured, not assumed. Lighthouse on simulated slow 4G:
     preload 3 fonts + swap      → performance 73, CLS 0.31 (text re-flows when fonts land)
     no preload + swap           → 93–95, CLS 0.02
     no preload + optional       → 98, LCP 1.8 s, CLS 0
   «optional» means: if the font is not ready within ~100 ms of first paint, this page
   uses the system Persian font and the download finishes in the background for the
   NEXT page (fonts cache for a year). Nothing ever jumps mid-read, which for a reader
   comparing prices matters more than the first screen of a slow first visit. */
@font-face {
  font-family: 'JetBrains Mono';
  font-style: normal;
  font-weight: 400 700;
  font-display: optional;
  src: url('fonts/jetbrainsmono-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Noto Naskh Arabic';
  font-style: normal;
  font-weight: 500 700;
  font-display: optional;
  src: url('fonts/notonaskh-arabic.woff2') format('woff2');
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0897-08E1, U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74, U+FE76-FEFC, U+102E0-102FB, U+10E60-10E7E, U+10EC2-10EC4, U+10EFC-10EFF, U+1EE00-1EE03, U+1EE05-1EE1F, U+1EE21-1EE22, U+1EE24, U+1EE27, U+1EE29-1EE32, U+1EE34-1EE37, U+1EE39, U+1EE3B, U+1EE42, U+1EE47, U+1EE49, U+1EE4B, U+1EE4D-1EE4F, U+1EE51-1EE52, U+1EE54, U+1EE57, U+1EE59, U+1EE5B, U+1EE5D, U+1EE5F, U+1EE61-1EE62, U+1EE64, U+1EE67-1EE6A, U+1EE6C-1EE72, U+1EE74-1EE77, U+1EE79-1EE7C, U+1EE7E, U+1EE80-1EE89, U+1EE8B-1EE9B, U+1EEA1-1EEA3, U+1EEA5-1EEA9, U+1EEAB-1EEBB, U+1EEF0-1EEF1;
}
@font-face {
  font-family: 'Noto Naskh Arabic';
  font-style: normal;
  font-weight: 500 700;
  font-display: optional;
  src: url('fonts/notonaskh-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Vazirmatn';
  font-style: normal;
  font-weight: 400 700;
  font-display: optional;
  src: url('fonts/vazirmatn-arabic.woff2') format('woff2');
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0897-08E1, U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74, U+FE76-FEFC, U+102E0-102FB, U+10E60-10E7E, U+10EC2-10EC4, U+10EFC-10EFF, U+1EE00-1EE03, U+1EE05-1EE1F, U+1EE21-1EE22, U+1EE24, U+1EE27, U+1EE29-1EE32, U+1EE34-1EE37, U+1EE39, U+1EE3B, U+1EE42, U+1EE47, U+1EE49, U+1EE4B, U+1EE4D-1EE4F, U+1EE51-1EE52, U+1EE54, U+1EE57, U+1EE59, U+1EE5B, U+1EE5D, U+1EE5F, U+1EE61-1EE62, U+1EE64, U+1EE67-1EE6A, U+1EE6C-1EE72, U+1EE74-1EE77, U+1EE79-1EE7C, U+1EE7E, U+1EE80-1EE89, U+1EE8B-1EE9B, U+1EEA1-1EEA3, U+1EEA5-1EEA9, U+1EEAB-1EEBB, U+1EEF0-1EEF1;
}
@font-face {
  font-family: 'Vazirmatn';
  font-style: normal;
  font-weight: 400 700;
  font-display: optional;
  src: url('fonts/vazirmatn-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}


/* ============================================================
   STEEL INTELLIGENCE PLATFORM — Design System
   Style: Data-Dense Dashboard | Density 8/10 | Motion 5/10
   ============================================================ */

/* ---------- DARK (default: trading terminal) ---------- */
/* ── DARK ──────────────────────────────────────────────────────────────────
   Neutral slate ground. The client's navy is the BRAND colour here, not the
   floor: it identifies the product on the wordmark, the active nav rule and
   primary actions. Flooding every surface with a saturated navy is what made
   the interface read as decorated rather than professional. */
/* ---- Metric-matched fallbacks ----
   With font-display: optional a first visit renders in a local font and keeps it; the
   web font arrives for the next page. So the site has TWO renderings, and every reserved
   height (scripts/reserve.mjs) must hold for both. These faces give the local fallback
   the web font's vertical metrics (read from the woff2 tables: Vazirmatn 2100/1100 of
   2048, Noto Naskh 1069/634 of 1000, JetBrains Mono 1020/300 of 1000) AND its average
   advance width (size-adjust, measured on the same 300-character Persian sample in
   headless Chrome, regular weight: Segoe UI is 2.5% wider than Vazirmatn, 12% wider
   than Naskh; Noto Sans Arabic matches Vazirmatn to 0.3%; Consolas is 9% narrower than
   JetBrains Mono, Roboto Mono identical). One family per local font, because a
   size-adjust is right for one face only — the stack tries them in order and skips the
   ones this device lacks. The overrides are divided by the size-adjust (Chrome applies
   them to the adjusted size), which keeps the line box exactly the web font's: 25 px =
   25 px at 16 px, measured. */
@font-face {
  font-family: 'Vazirmatn Fallback Segoe';
  src: local('Segoe UI');
  size-adjust: 97.5%; ascent-override: 105.1%; descent-override: 55.1%; line-gap-override: 0%;
}
@font-face {
  font-family: 'Vazirmatn Fallback Noto';
  src: local('Noto Sans Arabic'), local('Noto Sans Arabic UI');
  ascent-override: 102.5%; descent-override: 53.7%; line-gap-override: 0%;
}
@font-face {
  font-family: 'Noto Naskh Arabic Fallback Segoe';
  src: local('Segoe UI');
  size-adjust: 88%; ascent-override: 121.5%; descent-override: 72%; line-gap-override: 0%;
}
@font-face {
  font-family: 'Noto Naskh Arabic Fallback Noto';
  src: local('Noto Sans Arabic'), local('Noto Sans Arabic UI');
  size-adjust: 91%; ascent-override: 117.5%; descent-override: 69.7%; line-gap-override: 0%;
}
@font-face {
  font-family: 'JetBrains Mono Fallback Consolas';
  src: local('Consolas');
  size-adjust: 109.1%; ascent-override: 93.5%; descent-override: 27.5%; line-gap-override: 0%;
}
@font-face {
  font-family: 'JetBrains Mono Fallback Mono';
  src: local('Roboto Mono'), local('Droid Sans Mono'), local('Menlo'), local('Courier New');
  ascent-override: 102%; descent-override: 30%; line-gap-override: 0%;
}

:root[data-theme="dark"] {
  --bg:            #121518;
  --bg-elev:       #171B1F;
  --surface:       #1B1F23;
  --surface-2:     #22272C;
  --surface-3:     #2A3036;
  --border:        #2E343A;
  --border-strong: #414952;

  --fg:            #E9EBED;
  --fg-muted:      #A9B0B8;
  /* Dark twin of the light fix: #868E97 measured 4.31:1 on the up-soft callout ground
     and 4.46:1 on warn-soft — both under the 4.5 AA floor for 11px headings. */
  --fg-subtle:     #8F97A0;

  --primary:       #8FA8D8;
  --primary-hover: #A9BEE6;
  --primary-soft:  #1D2430;
  --on-primary:    #0E1218;

  /* One accent, and it is the brand navy. A second decorative accent is what a
     professional tool does not have. */
  --accent:        #8FA8D8;
  --accent-soft:   #1D2430;
  --on-accent:     #0E1218;

  --up:            #4FBE85;
  --up-soft:       #12301F;
  --down:          #E5787A;
  --down-soft:     #331A1B;
  --warn:          #D4A952;
  --warn-soft:     #2E2718;
  --info:          #7FA8C8;
  --info-soft:     #17242E;

  --ring:          #A9BEE6;
  --shadow: none;
  --grid-line:     #262C32;

  /* Categorical series, found by OKLCH search scored on the MINIMUM separation
     across normal vision, deuteranopia and protanopia at once.
     Worst pair, any view: 12.2. Contrast on --surface: 5.9 / 4.8 / 6.4 / 10.7.
     Cool hues only — every warm candidate collapsed toward the red or amber
     status colours under protanopia (a rust line measured ΔE 1.4 from «down»). */
  --chart-1:#5E99FD;
  --chart-2:#1C989E;
  --chart-3:#BE89F6;
  --chart-4:#E1C6F6;
}

/* ── LIGHT (default) ───────────────────────────────────────────────────────
   Light leads because the readers are industry professionals in their forties
   and fifties reading dense figures in daylight offices, and because Swiss
   minimalism is an ink-on-paper idiom. Dark remains a full peer, not a tint. */
:root,
:root[data-theme="light"] {
  --bg:            #F7F8F9;
  --bg-elev:       #FFFFFF;
  --surface:       #FFFFFF;
  --surface-2:     #F2F4F6;
  --surface-3:     #E8EBEF;
  --border:        #E2E5EA;
  --border-strong: #C6CBD3;

  --fg:            #15181C;
  --fg-muted:      #4C535C;
  /* #6B727C measured 4.29:1 on --surface-2 and 4.41:1 on the card grounds — under the
     4.5 AA floor for text this small, and .eyebrow/.sub/.subtle are used on nearly every
     page. Darkened until it clears 4.5 on white, --bg and --surface-2 alike, while
     staying visibly lighter than --fg-muted so the hierarchy survives. */
  --fg-subtle:     #616872;

  /* The client's navy, unchanged, doing brand work. */
  --primary:       #1B2A5E;
  --primary-hover: #142046;
  --primary-soft:  #EDF0F7;
  --on-primary:    #FFFFFF;

  --accent:        #1B2A5E;
  --accent-soft:   #EDF0F7;
  --on-accent:     #FFFFFF;

  --up:            #1B7A4B;
  --up-soft:       #E4F2EA;
  --down:          #B02A2A;
  --down-soft:     #FAE8E8;
  --warn:          #8A6410;
  --warn-soft:     #F7F0DE;
  --info:          #2D5F8A;
  --info-soft:     #E7EFF6;

  --ring:          #1B2A5E;
  --shadow: none;
  --grid-line:     #EDEFF2;

  /* Same three-vision search, run against the white surface.
     Worst pair, any view: 9.6. Contrast: 8.7 / 5.0 / 5.6 / 8.1 — all AA.
     Four cool hues is what survives here: on white, with red, green and amber
     reserved for direction, every warm or teal candidate I hand-picked scored
     between 4.1 and 5.8, well below the search result. Series also carry a
     dash pattern, which is the channel colour cannot guarantee. */
  --chart-1:#0A46A2;
  --chart-2:#9154B9;
  --chart-3:#1A62D5;
  --chart-4:#702BA9;
}

/* ---------- Density 8/10 : 8 to 32px scale ---------- */
:root {
  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px; --sp-4: 16px;
  --sp-5: 20px; --sp-6: 28px; --sp-7: 40px; --sp-8: 56px;

  --r-sm: 0; --r-md: 0; --r-lg: 0; --r-xl: 0;   /* square by intent */

  /* TYPE SCALE — raised one full step across the board.
     The old scale was 13px base with 10-11px labels: trading-terminal density borrowed
     from Latin interfaces. Persian does not survive it. Arabic-script letterforms carry
     their meaning in the joins and in dot placement below and above the baseline, and
     both disappear first as size drops — at 10px «ت» «ب» «پ» «ث» differ by dots alone,
     rendered a pixel apart. Latin can be scanned at 11px because its skeletons differ;
     Persian at 11px is guesswork. Everything moves up one step. */
  --fs-2xs: 11px; --fs-xs: 12px; --fs-sm: 13px; --fs-base: 15px;
  --fs-md: 17px;  --fs-lg: 21px; --fs-xl: 26px; --fs-2xl: 34px; --fs-3xl: 44px;

  /* Three voices: Naskh carries the publication, Vazirmatn runs the interface,
     JetBrains Mono owns every figure. A price service is read, not operated. */
  --serif: 'Noto Naskh Arabic', 'Noto Naskh Arabic Fallback Segoe', 'Noto Naskh Arabic Fallback Noto', 'Vazirmatn', serif;
  --sans:  'Vazirmatn', 'Vazirmatn Fallback Segoe', 'Vazirmatn Fallback Noto', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --mono:  'JetBrains Mono', 'JetBrains Mono Fallback Consolas', 'JetBrains Mono Fallback Mono', 'SFMono-Regular', Consolas, monospace;

  --nav-h: 56px;
  --sub-h: 40px;
  --side-w: 232px;
  --t-fast: 150ms; --t-mid: 250ms; --t-slow: 400ms;
  --ease: cubic-bezier(.4,0,.2,1);
}

/* ---------- Reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
/* The `hidden` attribute is only a UA rule (`display:none`), and ANY author `display`
   — .grid, .row, .card — silently overrides it. On item.html the empty results grid,
   floored at 70vh, was visible before the script ran and stayed visible in the not-
   found state; Lighthouse read the collapse as a 0.28 layout shift. This makes the
   attribute mean what it says everywhere. */
[hidden] { display: none !important; }
/* Blocks that exist only under ?on=<date> — the archive banners. assets/urlview.js sets
   data-on in <head>, before the body is parsed, so these are present or absent in the
   FIRST paint. Toggling them from the page script instead cost a 0.08 layout shift on
   prices.html: the banner sits above a table whose height is reserved, so unhiding it
   pushed the entire table down. A `hidden` attribute here is still honoured — the two
   states the pages use are «not this view» (CSS) and «not yet decided» (hidden). */
.on-only { display: none; }
:root[data-on="1"] .on-only { display: block; }
/* Reserved heights for script-filled containers (scripts/reserve.mjs). The desktop floor
   is inline min-height; a phone stacks the same content 2–4× taller, so the tablet and
   phone floors ride along as custom properties and take over here. !important because
   nothing else can beat an inline min-height — and that is the point: the measured floor
   for THIS width is the only correct one. Order matters: the phone rule must come last. */
@media (max-width: 1024px) { [style*="--mh-t:"] { min-height: var(--mh-t) !important; } }
@media (max-width: 768px)  { [style*="--mh-m:"] { min-height: var(--mh-m) !important; } }
/* Icon placeholders are replaced with 14 px SVGs by script. An empty span is 0 px wide,
   so every button that carries one widens on load — enough to re-wrap a three-button
   row on a phone. Reserve the box up front. */
[data-i] { display: inline-block; width: 14px; height: 14px; vertical-align: -2px; }
* { margin: 0; padding: 0; }

html {
  font-size: 16px;
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--nav-h) + var(--sub-h) + 12px);
}

body {
  background: var(--bg);
  color: var(--fg);
  font-family: var(--sans);
  font-size: var(--fs-base);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  min-height: 100vh;
  transition: background var(--t-mid) var(--ease), color var(--t-mid) var(--ease);
}

img, svg, video { display: block; max-width: 100%; }
button, input, select, textarea { font: inherit; color: inherit; }
button { background: none; border: none; cursor: pointer; }
a { color: inherit; text-decoration: none; }
table { border-collapse: collapse; width: 100%; }

/* ---------- Focus (never removed) ---------- */
:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}
:focus:not(:focus-visible) { outline: none; }

.skip-link {
  position: absolute; inset-inline-start: var(--sp-4); top: -100px;
  z-index: 999; background: var(--primary); color: var(--on-primary);
  padding: var(--sp-2) var(--sp-4); border-radius: var(--r-md);
  font-weight: 600; transition: top var(--t-fast) var(--ease);
}
.skip-link:focus { top: var(--sp-2); }

/* ---------- Numerals: LTR + tabular ---------- */
.num, td.num, .mono {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  direction: ltr;              /* alignment + fallback direction for digit-only runs */
  unicode-bidi: plaintext;     /* direction comes from the run's first strong char */
}
/* A MIXED run — a number with a Persian word or unit in it («186–193 هزار», «تومان/کیلوگرم»,
   «12 صفحه») — is prose that happens to contain digits. In the mono stack the Persian
   letters fall through to the generic monospace face (Courier New on Windows), which
   sets Arabic in fixed cells: disjointed letters, visibly wrong in every price table's
   unit line. Sans, with tabular digits, keeps the columns aligned and the words joined. */
.num.mixed, .mixed {
  font-family: var(--sans);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}
/* Numeric column HEADERS are prose («تغییر روزانه», «کران پایین») — never mono, never LTR. */
th.num, table.data thead th.num {
  font-family: var(--sans);
  direction: rtl;
  unicode-bidi: isolate;
}
/* Period tag inside a delta chip ('هفتگی') — Persian, and isolated so it cannot
   flip the chip's own direction. */
.delta .per {
  font-family: var(--sans); font-weight: 500; font-size: .92em;
  opacity: .8; margin-inline-start: 4px;
  direction: rtl; unicode-bidi: isolate;
}

/* ---------- Typography ---------- */
h1,h2,h3,h4 { font-family: var(--serif); line-height: 1.35; font-weight: 700; text-wrap: balance; }
h1 { font-size: var(--fs-2xl); letter-spacing: normal; }
h2 { font-size: var(--fs-xl); letter-spacing: normal; }
h3 { font-size: var(--fs-md); }
h4 { font-size: var(--fs-base); }
p  { text-wrap: pretty; }

.eyebrow {
  font-size: var(--fs-2xs); font-weight: 700; letter-spacing: normal;
  text-transform: uppercase; color: var(--fg-subtle);
}
.muted { color: var(--fg-muted); }
.subtle { color: var(--fg-subtle); }
.sm { font-size: var(--fs-sm); }
.xs { font-size: var(--fs-xs); }

/* ============================================================
   LAYOUT SHELL
   ============================================================ */
.app { display: flex; min-height: 100vh; }

/* ---- Top bar ---- */
.topbar {
  position: fixed; inset-block-start: 0; inset-inline: 0; height: var(--nav-h);
  background: var(--bg-elev);
  border-block-end: 2px solid var(--fg);   /* nameplate rule, like a masthead */
  display: flex; align-items: center; gap: var(--sp-3);
  padding-inline: var(--sp-4); z-index: 60;
}
.brand { display: flex; align-items: center; gap: var(--sp-3); min-width: 0; }
.brand-mark {
  width: 30px; height: 30px; flex-shrink: 0;
  border: 1.5px solid var(--fg);
  display: grid; place-items: center; color: var(--fg);
}
.brand-name { font-family: var(--serif); font-weight: 700; font-size: var(--fs-md); white-space: nowrap; }
.brand-sub { font-size: var(--fs-2xs); color: var(--fg-subtle); letter-spacing: normal; text-transform: uppercase; }

.topbar-spacer { flex: 1; }

.icon-btn {
  width: 36px; height: 36px; border-radius: var(--r-md);
  display: grid; place-items: center; color: var(--fg-muted);
  border: 1px solid transparent;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
  position: relative; cursor: pointer;
}
.icon-btn:hover { background: var(--surface-2); color: var(--fg); border-color: var(--border); }
.icon-btn .dot {
  position: absolute; inset-block-start: 6px; inset-inline-end: 6px;
  width: 7px; height: 7px; border-radius: 50%; background: var(--down);
  border: 2px solid var(--bg-elev);
}

/* ---- Sidebar ---- */
.sidebar {
  position: fixed; inset-block-start: var(--nav-h); inset-block-end: 0; inset-inline-start: 0;
  width: var(--side-w); background: var(--bg-elev);
  border-inline-end: 1px solid var(--border);
  overflow-y: auto; overscroll-behavior: contain;
  padding: var(--sp-3) var(--sp-2) var(--sp-6);
  z-index: 50;
  transition: transform var(--t-mid) var(--ease);
}
.nav-group { margin-block-end: var(--sp-4); }
.nav-label {
  font-size: var(--fs-2xs); font-weight: 700; letter-spacing: normal;
  text-transform: uppercase; color: var(--fg-subtle);
  padding: var(--sp-2) var(--sp-3) var(--sp-1);
}
.nav-item {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3); border-radius: var(--r-md);
  color: var(--fg-muted); font-size: var(--fs-base); font-weight: 500;
  min-height: 36px; cursor: pointer;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
  position: relative;
}
.nav-item svg { flex-shrink: 0; opacity: .85; }
.nav-item:hover { background: var(--surface-2); color: var(--fg); }
.nav-item[aria-current="page"] {
  background: var(--primary-soft); color: var(--primary); font-weight: 600;
}
.nav-item[aria-current="page"]::before {
  content: ''; position: absolute; inset-inline-start: 0; inset-block: 8px;
  width: 3px; border-radius: 0 3px 3px 0; background: var(--primary);
}
:root[data-theme="dark"] .nav-item[aria-current="page"] { color: var(--primary-hover); }
.nav-item .badge-mini {
  margin-inline-start: auto; font-size: var(--fs-2xs); font-weight: 700;
  background: var(--down-soft); color: var(--down);
  padding: 0 5px; font-family: var(--mono);
}

/* ---- Main ---- */
.main {
  flex: 1; min-width: 0;
  margin-inline-start: var(--side-w);
  margin-block-start: var(--nav-h);
  display: flex; flex-direction: column;
}
.page { padding: var(--sp-5) var(--sp-6) var(--sp-8); flex: 1; }

.page-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--sp-4); flex-wrap: wrap; margin-block-end: var(--sp-5);
}
.page-head h1 { font-size: var(--fs-xl); }
.page-head .lede { color: var(--fg-muted); font-size: var(--fs-base); margin-block-start: 2px; max-width: 62ch; }

/* ---- Ticker tape ---- */
.tape {
  height: var(--sub-h); background: var(--surface);
  border-block-end: 1px solid var(--border);
  display: flex; align-items: center; overflow: hidden;
  position: sticky; inset-block-start: var(--nav-h); z-index: 40;
}
.tape-label {
  flex-shrink: 0; padding-inline: var(--sp-4); height: 100%;
  display: flex; align-items: center; gap: var(--sp-2);
  font-size: var(--fs-2xs); font-weight: 700; letter-spacing: normal;
  text-transform: uppercase; color: var(--fg-subtle);
  border-inline-end: 1px solid var(--border); background: var(--bg-elev);
}
.live-dot {
  width: 6px; height: 6px; border-radius: 50%; background: var(--up);
  animation: pulse 2s var(--ease) infinite;
}
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: .3; } }

.tape-track { display: flex; gap: var(--sp-5); padding-inline: var(--sp-4); animation: marquee 60s linear infinite; }
.tape:hover .tape-track, .tape:focus-within .tape-track { animation-play-state: paused; }
.tape.paused .tape-track { animation-play-state: paused; }
@keyframes marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
.tape-item { display: flex; align-items: center; gap: var(--sp-2); white-space: nowrap; font-size: var(--fs-sm); }
.tape-item .t-sym { font-weight: 600; color: var(--fg-muted); }

/* ============================================================
   COMPONENTS
   ============================================================ */
.card {
  background: var(--surface); border: 1px solid var(--border);
  overflow: hidden;
}
/* Section head reads like a wire-service rubric: rule above, small-caps label */
.card-head { border-block-end: 1px solid var(--border-strong); }
.card-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-3); padding: var(--sp-3) var(--sp-4);
  border-block-end: 1px solid var(--border);
}
.card-head h2, .card-head h3 { font-family: var(--serif); font-size: var(--fs-md); font-weight: 700; }
.card-head .sub { font-size: var(--fs-xs); color: var(--fg-subtle); margin-block-start: 1px; }
.card-body { padding: var(--sp-4); }
.card-body.flush { padding: 0; }
.card-foot {
  padding: var(--sp-2) var(--sp-4); border-block-start: 1px solid var(--border);
  background: var(--surface-2); font-size: var(--fs-xs); color: var(--fg-subtle);
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
}

/* ---- Grid ---- */
.grid { display: grid; gap: var(--sp-4); }
.g-2 { grid-template-columns: repeat(2, minmax(0,1fr)); }
.g-3 { grid-template-columns: repeat(3, minmax(0,1fr)); }
.g-4 { grid-template-columns: repeat(4, minmax(0,1fr)); }
.g-6 { grid-template-columns: repeat(6, minmax(0,1fr)); }
.g-main { grid-template-columns: minmax(0,2.2fr) minmax(0,1fr); }
.g-side { grid-template-columns: minmax(0,1fr) minmax(0,1.6fr); }
.g-chat { grid-template-columns: minmax(0,1fr) 280px; }
.span-2 { grid-column: span 2; }
.span-3 { grid-column: span 3; }
.span-full { grid-column: 1 / -1; }

/* ---- KPI / Stat tile ---- */
.kpi {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: var(--sp-3) var(--sp-4);
  display: flex; flex-direction: column; gap: var(--sp-1);
  position: relative; overflow: hidden;
  transition: border-color var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
/* Only a real link gets the hover lift — `.kpi.interactive` gave five inert <article>
   tiles the same affordance as a link and is gone with it. */
a.kpi:hover { border-color: var(--border-strong); transform: translateY(-1px); }
.kpi-top { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2); }
.kpi-label { font-size: var(--fs-xs); color: var(--fg-muted); font-weight: 500; }
.kpi-val {
  font-family: var(--mono); font-size: var(--fs-xl); font-weight: 600;
  letter-spacing: normal; font-variant-numeric: tabular-nums;
  direction: ltr; unicode-bidi: isolate;
}
.kpi-meta { display: flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-xs); }
.kpi-unit { color: var(--fg-subtle); font-size: var(--fs-xs); }
.kpi > .kpi-top, .kpi > .kpi-val, .kpi > .kpi-unit { position: relative; z-index: 1; }
.kpi-spark {
  position: absolute; inset-block-end: 0; inset-inline: 0; height: 30px;
  opacity: .42; pointer-events: none; z-index: 0;
  /* soft top edge only — the series itself has to read end to end */
  -webkit-mask-image: linear-gradient(to top, #000 86%, transparent);
  mask-image: linear-gradient(to top, #000 86%, transparent);
}
/* preserveAspectRatio="none" is set on the sparkline, so this stretches it to
   exactly the 30px band at any container width, single- or multi-column. */
.kpi-spark svg { width: 100%; height: 100%; display: block; }

/* ---- Delta (never color alone: arrow glyph + sign) ---- */
.delta {
  display: inline-flex; align-items: center; gap: 3px;
  font-family: var(--mono); font-weight: 600; font-size: var(--fs-xs);
  padding: 1px 6px; border-radius: var(--r-sm);
  direction: ltr; unicode-bidi: isolate;
}
.delta.up   { color: var(--up);   background: var(--up-soft); }
.delta.down { color: var(--down); background: var(--down-soft); }
.delta.flat { color: var(--fg-muted); background: var(--surface-2); }
.delta.bare { background: none; padding: 0; }

/* ---- Spread ----
   A spread is a LEVEL difference (Iranian grade minus its benchmark), not a change over
   a period, so it never takes an arrow — ▼ would claim the spread fell when it is simply
   negative. It must not wear .delta chrome either, or it reads as a period move beside
   the two real delta chips in the same row. The sign is always rendered via signed(),
   so direction survives without colour. */
.spread {
  display: inline-flex; align-items: center;
  font-family: var(--mono); font-weight: 600; font-size: var(--fs-xs);
  direction: ltr; unicode-bidi: isolate;
}
.spread.up   { color: var(--up); }
.spread.down { color: var(--down); }
.spread.flat { color: var(--fg-muted); }

/* ---- Badges ---- */
.badge {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: var(--fs-2xs); font-weight: 700; letter-spacing: normal;
  padding: 1px 7px; white-space: nowrap;
  text-transform: uppercase; border: 1px solid transparent;
}
.badge.b-up    { background: var(--up-soft);      color: var(--up);      border-color: color-mix(in srgb, var(--up) 30%, transparent); }
.badge.b-down  { background: var(--down-soft);    color: var(--down);    border-color: color-mix(in srgb, var(--down) 30%, transparent); }
.badge.b-warn  { background: var(--warn-soft);    color: var(--warn);    border-color: color-mix(in srgb, var(--warn) 30%, transparent); }
.badge.b-info  { background: var(--info-soft);    color: var(--info);    border-color: color-mix(in srgb, var(--info) 30%, transparent); }
.badge.b-pri   { background: var(--primary-soft); color: var(--primary); border-color: color-mix(in srgb, var(--primary) 30%, transparent); }
.badge.b-mute  { background: var(--surface-2);    color: var(--fg-muted);border-color: var(--border); }
:root[data-theme="dark"] .badge.b-pri { color: var(--primary-hover); }

/* ---- Buttons ---- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  min-height: 34px; padding: 0 var(--sp-4); border-radius: var(--r-md);
  font-size: var(--fs-base); font-weight: 600; cursor: pointer;
  border: 1px solid var(--border); background: var(--surface); color: var(--fg);
  transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease),
              color var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
  white-space: nowrap;
}
.btn:hover { background: var(--surface-2); border-color: var(--border-strong); }
.btn:active { transform: translateY(1px); }
.btn-primary { background: var(--primary); border-color: var(--primary); color: var(--on-primary); }
.btn-primary:hover { background: var(--primary-hover); border-color: var(--primary-hover); }
.btn-accent { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.btn-accent:hover { filter: brightness(1.08); }
.btn-ghost { background: transparent; border-color: transparent; color: var(--fg-muted); }
.btn-ghost:hover { background: var(--surface-2); color: var(--fg); }
.btn-sm { min-height: 28px; padding: 0 var(--sp-3); font-size: var(--fs-sm); }
.btn-lg { min-height: 44px; padding: 0 var(--sp-6); font-size: var(--fs-md); }
.btn:disabled { opacity: .5; cursor: not-allowed; }

/* ---- Segmented control ---- */
.seg {
  display: inline-flex; background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--r-md); padding: 2px; gap: 2px;
}
.seg button {
  min-height: 26px; padding: 0 var(--sp-3); border-radius: var(--r-sm);
  font-size: var(--fs-xs); font-weight: 600; color: var(--fg-muted);
  font-family: var(--sans); cursor: pointer;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease);
}
.seg button:hover { color: var(--fg); }
.seg button[aria-pressed="true"] { background: var(--surface); color: var(--fg); box-shadow: var(--shadow); }

/* ---- Tabs ---- */
.tabs { display: flex; gap: var(--sp-1); border-block-end: 1px solid var(--border); overflow-x: auto; }
.tabs button {
  padding: var(--sp-3) var(--sp-4); font-size: var(--fs-base); font-weight: 600;
  color: var(--fg-muted); border-block-end: 2px solid transparent; margin-block-end: -1px;
  white-space: nowrap; cursor: pointer;
  transition: color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.tabs button:hover { color: var(--fg); }
.tabs button[aria-selected="true"] { color: var(--primary); border-block-end-color: var(--primary); }
:root[data-theme="dark"] .tabs button[aria-selected="true"] { color: var(--primary-hover); border-block-end-color: var(--primary-hover); }

/* ---- Tables ---- */
.table-wrap { overflow-x: auto; overscroll-behavior-x: contain; }
table.data { font-size: var(--fs-sm); min-width: 640px; }
table.data thead th {
  position: sticky; inset-block-start: 0; z-index: 2;
  background: var(--surface-2); color: var(--fg-subtle);
  font-size: var(--fs-2xs); font-weight: 700; letter-spacing: normal; text-transform: uppercase;
  text-align: start; padding: var(--sp-2) var(--sp-3); white-space: nowrap;
  border-block-end: 1px solid var(--border);
}
table.data thead th.num, table.data td.num { text-align: end; }
table.data th[aria-sort] { cursor: pointer; user-select: none; }
table.data th[aria-sort]:hover { color: var(--fg); }
table.data th .sort-ind { opacity: .4; margin-inline-start: 4px; font-family: var(--mono); }
table.data th[aria-sort="ascending"] .sort-ind,
table.data th[aria-sort="descending"] .sort-ind { opacity: 1; color: var(--primary); }
table.data tbody td { padding: var(--sp-2) var(--sp-3); border-block-end: 1px solid var(--border); vertical-align: middle; }
table.data tbody tr { transition: background var(--t-fast) var(--ease); }
table.data tbody tr:hover { background: var(--surface-2); }
table.data thead th { border-block-end-width: 2px; border-block-end-color: var(--fg-subtle); }
table.data tbody tr:last-child td { border-block-end: none; }
table.data .t-sym { font-weight: 600; }
/* A row name that is a link to the item page: reads as text until hovered, so the
   board keeps its density; underline on hover/focus says «this has its own page». */
table.data a.t-sym { color: inherit; text-decoration: none; }
table.data a.t-sym:hover, table.data a.t-sym:focus-visible { text-decoration: underline; text-underline-offset: 3px; color: var(--primary); }
table.data .t-note { color: var(--fg-subtle); font-size: var(--fs-xs); }

/* ---- Charts ---- */
.chart-box { position: relative; width: 100%; }
.chart-box svg { width: 100%; height: auto; display: block; overflow: hidden; }
.chart-legend { display: flex; flex-wrap: wrap; gap: var(--sp-3); font-size: var(--fs-xs); color: var(--fg-muted); }
.chart-legend span { display: inline-flex; align-items: center; gap: 6px; }
.lg-key { width: 16px; height: 0; border-block-start-width: 2px; border-block-start-style: solid; }
.chart-tip {
  position: absolute; pointer-events: none; z-index: 5; opacity: 0;
  background: var(--surface-3); border: 1px solid var(--border-strong);
  border-radius: var(--r-md); padding: var(--sp-2) var(--sp-3);
  font-size: var(--fs-xs); box-shadow: var(--shadow); white-space: nowrap;
  transition: opacity var(--t-fast) var(--ease);
}
.chart-tip.on { opacity: 1; }
details.data-fallback { margin-block-start: var(--sp-3); font-size: var(--fs-xs); }
details.data-fallback summary { cursor: pointer; color: var(--fg-muted); font-weight: 600; padding: var(--sp-1) 0; }
details.data-fallback summary:hover { color: var(--fg); }
details.data-fallback[open] { padding-block-end: var(--sp-2); }

/* ---- Pipeline (from the sketch) ---- */
.pipeline { display: grid; grid-template-columns: minmax(0,1fr) auto minmax(0,.8fr) auto minmax(0,1fr); gap: var(--sp-3); align-items: center; }
.pipe-arrow { display: grid; place-items: center; color: var(--fg-subtle); }
.pipe-arrow .lbl { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: normal; margin-block-end: 2px; }
.src-list { display: flex; flex-direction: column; gap: var(--sp-2); }
.src-item {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3); border: 1px solid var(--border);
  border-radius: var(--r-md); background: var(--surface); font-size: var(--fs-sm);
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.src-item:hover { border-color: var(--border-strong); background: var(--surface-2); }
.src-item .st { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; }
.st.ok { background: var(--up); } .st.warn { background: var(--warn); } .st.err { background: var(--down); }
.src-item .nm { flex: 1; min-width: 0; font-weight: 500; }
/* The analysis-engine node in the pipeline diagram. Was a circle filled with a radial
   gradient and wrapped in a pulsing halo — in a system whose every other corner is
   square and whose --shadow is none. A glowing orb marked «موتور تحلیل» is the exact
   visual cliché the client objected to. It is now a node like the others: square,
   flat, distinguished by its border weight and its label, not by an aura. */
.ai-core {
  aspect-ratio: 1; display: grid; place-items: center; gap: var(--sp-2);
  background: var(--surface);
  border: 2px solid var(--primary); text-align: center; padding: var(--sp-4);
  position: relative;
}
.out-list { display: flex; flex-direction: column; gap: var(--sp-3); }
.out-card {
  border: 1px solid var(--border); border-radius: var(--r-md);
  padding: var(--sp-3); background: var(--surface);
  transition: border-color var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}
a.out-card:hover { border-color: var(--fg-subtle); background: var(--surface-2); }


/* ---- Below-the-fold rendering is deferred ----
   Lighthouse (mobile, 4× CPU) spent 2.9 s in style & layout on the dashboard: every
   section is built and laid out before the first screen can paint, though only the
   first two are visible. content-visibility:auto lets the browser skip layout of
   sections until they approach the viewport; contain-intrinsic-size reserves a plausible
   height so the scrollbar does not jump. The first three children (page head, reference
   price, forecast hook) stay eager — they ARE the first screen. Charts inside deferred
   sections measure their host on draw, which un-skips that section on demand, so they
   still render at the right width. */
.page > section:nth-of-type(n+3),
.page > a.card {
  content-visibility: auto;
  /* The placeholder is the section's MEASURED height (scripts/reserve.mjs writes
     --cis-d/-t/-m per section; app.js measureMode stamps them). One guess for every
     section — 480px — was 700–2000px short on a phone: the scrollbar kept growing
     while scrolling and axe read the not-yet-rendered sections as overlapping the next
     (Lighthouse mobile a11y 96). `auto` keeps the last rendered size afterwards. */
  contain-intrinsic-size: auto var(--cis-d, 480px);
}
@media (max-width: 1024px) { .page > section[style*="--cis-t:"], .page > a[style*="--cis-t:"] { contain-intrinsic-size: auto var(--cis-t); } }
@media (max-width: 768px)  { .page > section[style*="--cis-m:"], .page > a[style*="--cis-m:"] { contain-intrinsic-size: auto var(--cis-m); } }
@media print { .page > * { content-visibility: visible; } }


/* ---- Boards reserve a screen's worth of height before JS fills them ----
   A price board is 20–30 rows built by script. Before the script runs the table is
   empty, so everything under it sits in the first viewport and is then pushed 1,700 px
   down when the rows arrive — Lighthouse: CLS 0.29 on prices.html. Reserving roughly a
   viewport means the content below starts below the fold, the rows fill the reserved
   area in place, and nothing a reader can see moves. The exact height is unknowable
   before the data renders; a viewport is the honest lower bound.
   Since then scripts/reserve.mjs measures every script-filled table and writes the exact
   floor onto its .table-wrap (inline min-height wins over this rule). This stays as the
   safety net for a table added before the next reserve run. */
[data-board], .table-wrap:has(> table.data > tbody[data-rows]:empty) { min-height: 60vh; }
/* The two-column main+rail layout holds each page's primary content, and on the report
   and news pages that content is script-built. Same reasoning: a viewport's worth. */
.grid.g-main { min-height: 70vh; }

/* ---- Scenario card — the forecast hook on the dashboard ----
   The called scenario is marked with a border, not a fill: at four cards across, a
   filled card reads as «selected» (a control) rather than «this is our call». */
.scen {
  border: 1px solid var(--border); border-radius: var(--r-md);
  padding: var(--sp-3) var(--sp-4); background: var(--surface);
  display: flex; flex-direction: column;
}
.scen.is-called { border-color: var(--primary); box-shadow: inset 0 0 0 1px var(--primary); }
.scen-p { font-size: var(--fs-lg); font-weight: 700; line-height: 1; }
.scen-band {
  display: flex; justify-content: space-between; align-items: baseline; gap: var(--sp-2);
  font-size: var(--fs-xs); color: var(--fg-muted); padding-block: 3px;
}
.scen-band + .scen-band { border-block-start: 1px solid var(--border); }
.scen-band .num { font-size: var(--fs-base); font-weight: 600; color: var(--fg); }
.scen-assume {
  font-size: var(--fs-xs); color: var(--fg-subtle); line-height: 1.65;
  margin-block-start: var(--sp-2); unicode-bidi: plaintext;
}
.scen-flag {
  font-size: var(--fs-2xs); color: var(--primary); font-weight: 600; line-height: 1.6;
  margin-block-start: auto; padding-block-start: var(--sp-2);
}

/* ---- Accuracy ledger — the record that travels with the claim ---- */
.ledger {
  display: grid; gap: var(--sp-3) var(--sp-5);
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--surface-2); padding: var(--sp-4);
}
.ledger-stat { display: flex; flex-direction: column; gap: 3px; align-items: flex-start; min-width: 0; }
.ledger-k { font-size: var(--fs-2xs); color: var(--fg-muted); font-weight: 600; }
.ledger-v { font-size: var(--fs-xl); font-weight: 700; line-height: 1.15; color: var(--fg); }
.ledger-note {
  grid-column: 1 / -1; font-size: var(--fs-xs); color: var(--fg-muted);
  line-height: 1.75; border-block-start: 1px solid var(--border); padding-block-start: var(--sp-3);
}

/* ---- Coverage strip in the dashboard rail ---- */
.news-mini {
  display: flex; gap: var(--sp-3); align-items: baseline;
  padding: var(--sp-2) 0; border-block-start: 1px solid var(--border);
}
.news-mini:hover .news-mini-h { color: var(--primary); }
.news-mini-t { font-size: var(--fs-2xs); color: var(--fg-subtle); flex: none; min-width: 5.5ch; }
.news-mini-h { font-size: var(--fs-sm); color: var(--fg-muted); line-height: 1.6; }

/* ---- Report / prose ---- */
.prose { max-width: 74ch; font-size: var(--fs-md); line-height: 1.8; }
.prose > * + * { margin-block-start: var(--sp-4); }
.prose h2 { font-size: var(--fs-lg); margin-block-start: var(--sp-7); padding-block-end: var(--sp-2); border-block-end: 1px solid var(--border); }
.prose h3 { font-size: var(--fs-md); margin-block-start: var(--sp-5); }
.prose ul, .prose ol { padding-inline-start: var(--sp-5); }
.prose li + li { margin-block-start: var(--sp-2); }
.prose strong { font-weight: 700; color: var(--fg); }
.prose .num { font-weight: 600; }
.callout {
  border: 1px solid var(--border); border-inline-start: 3px solid var(--info);
  background: var(--info-soft); border-radius: var(--r-md);
  padding: var(--sp-3) var(--sp-4); font-size: var(--fs-base); line-height: 1.7;
}
.callout.warn { border-inline-start-color: var(--warn); background: var(--warn-soft); }
.callout.up { border-inline-start-color: var(--up); background: var(--up-soft); }
.callout.down { border-inline-start-color: var(--down); background: var(--down-soft); }
.callout h4, .callout .callout-h { margin: 0 0 var(--sp-1); font-size: var(--fs-base); }

/* ---- Timeline ---- */
.timeline { position: relative; padding-inline-start: var(--sp-5); }
.timeline::before { content: ''; position: absolute; inset-inline-start: 5px; inset-block: 6px; width: 1px; background: var(--border); }
.tl-item { position: relative; padding-block-end: var(--sp-4); }
.tl-item:last-child { padding-block-end: 0; }
.tl-item::before {
  content: ''; position: absolute; inset-inline-start: calc(-1 * var(--sp-5) + 1px); inset-block-start: 6px;
  width: 9px; height: 9px; border-radius: 50%; background: var(--surface);
  border: 2px solid var(--border-strong);
}
.tl-item.hi::before { border-color: var(--accent); background: var(--accent); }
.tl-time { font-family: var(--sans); font-size: var(--fs-2xs); color: var(--fg-subtle); unicode-bidi: plaintext; }
.tl-title { font-weight: 600; font-size: var(--fs-base); margin-block: 2px; }
.tl-body { font-size: var(--fs-sm); color: var(--fg-muted); line-height: 1.65; }

/* ---- Forms ---- */
.field { display: flex; flex-direction: column; gap: var(--sp-1); }
.field label { font-size: var(--fs-sm); font-weight: 600; }
.field .hint { font-size: var(--fs-xs); color: var(--fg-subtle); }
.field .err { font-size: var(--fs-xs); color: var(--down); font-weight: 600; display: flex; align-items: center; gap: 4px; }
/* A rejected field has to be findable without reading the callout — this audience is
   filling nine boxes and needs to be shown which one, not told. */
.input[aria-invalid="true"] { border-color: var(--down); background: var(--down-soft); }
/* Direction colour for a table cell that is not a badge or a delta pill. */
.t-up { color: var(--up); } .t-down { color: var(--down); }
.input, .select, textarea.input {
  min-height: 36px; padding: var(--sp-2) var(--sp-3);
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--r-md); font-size: var(--fs-base); width: 100%;
  transition: border-color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.input:hover, .select:hover { border-color: var(--border-strong); }
.input:focus, .select:focus { border-color: var(--primary); background: var(--surface); }
.input::placeholder { color: var(--fg-subtle); }
textarea.input { min-height: 84px; resize: vertical; line-height: 1.6; }
.search-wrap { position: relative; flex: 1; max-width: 420px; }
/* The icon's placeholder span must sit where the icon will: in flow it took a line of
   its own above the input, and the input jumped up 26 px when the script swapped the
   svg in (prices.html on a phone, caught by the sweep's shift trace). */
.search-wrap svg, .search-wrap > [data-i] { position: absolute; inset-inline-start: var(--sp-3); inset-block-start: 50%; transform: translateY(-50%); color: var(--fg-subtle); pointer-events: none; }
.search-wrap .input { padding-inline-start: 34px; }

.switch { display: inline-flex; align-items: center; gap: var(--sp-3); cursor: pointer; min-height: 36px; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch .track {
  width: 38px; height: 22px; border-radius: 99px; background: var(--surface-3);
  border: 1px solid var(--border); position: relative; flex-shrink: 0;
  transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.switch .track::after {
  content: ''; position: absolute; inset-block-start: 2px; inset-inline-start: 2px;
  width: 16px; height: 16px; border-radius: 50%; background: var(--fg-muted);
  transition: transform var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.switch input:checked + .track { background: var(--primary); border-color: var(--primary); }
.switch input:checked + .track::after { transform: translateX(16px); background: #fff; }
:root[dir="rtl"] .switch input:checked + .track::after { transform: translateX(-16px); }
.switch input:focus-visible + .track { outline: 2px solid var(--ring); outline-offset: 2px; }

/* ---- Chat / AI analyst ---- */
.chat { display: flex; flex-direction: column; height: calc(100vh - var(--nav-h) - var(--sp-8)); }
.chat-log { flex: 1; overflow-y: auto; padding: var(--sp-4); display: flex; flex-direction: column; gap: var(--sp-4); }
.msg { display: flex; gap: var(--sp-3); max-width: 780px; }
.msg.me { margin-inline-start: auto; flex-direction: row-reverse; }
.msg-av {
  width: 28px; height: 28px; border-radius: var(--r-md); flex-shrink: 0;
  display: grid; place-items: center; font-size: var(--fs-2xs); font-weight: 700;
  background: var(--surface-3); color: var(--fg-muted);
}
.msg.ai .msg-av { background: linear-gradient(135deg, var(--primary), var(--accent)); color: #fff; }
.msg-body {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: var(--sp-3) var(--sp-4); font-size: var(--fs-base); line-height: 1.75;
}
.msg.me .msg-body { background: var(--primary-soft); border-color: color-mix(in srgb, var(--primary) 25%, transparent); }
.msg-body > * + * { margin-block-start: var(--sp-3); }
.msg-body ul { padding-inline-start: var(--sp-5); }
.chat-cite {
  display: inline-flex; align-items: center; gap: 4px; font-size: var(--fs-2xs);
  background: var(--surface-2); border: 1px solid var(--border); color: var(--fg-muted);
  padding: 1px 6px; border-radius: var(--r-sm);
}
.chat-input-bar {
  border-block-start: 1px solid var(--border); padding: var(--sp-3) var(--sp-4);
  background: var(--surface); display: flex; gap: var(--sp-2); align-items: flex-end;
}
.typing { display: inline-flex; gap: 3px; align-items: center; }
.typing i { width: 5px; height: 5px; border-radius: 50%; background: var(--fg-subtle); animation: bounce 1.2s infinite; }
.typing i:nth-child(2) { animation-delay: .15s; } .typing i:nth-child(3) { animation-delay: .3s; }
@keyframes bounce { 0%,60%,100% { transform: translateY(0); opacity: .4; } 30% { transform: translateY(-4px); opacity: 1; } }

.suggest { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.chip {
  display: inline-flex; align-items: center; gap: 6px; white-space: nowrap;
  min-height: 28px; padding: 0 var(--sp-3);
  border: 1px solid var(--border); background: var(--surface-2);
  font-size: var(--fs-sm); color: var(--fg-muted); cursor: pointer;
  transition: background var(--t-fast) var(--ease), color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.chip:hover { background: var(--surface-3); color: var(--fg); border-color: var(--border-strong); }
.chip[aria-pressed="true"] { background: var(--primary-soft); color: var(--primary); border-color: var(--primary); }

/* ---- Report card / archive ---- */
.rep-card {
  display: flex; flex-direction: column; background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--r-lg); overflow: hidden;
  transition: border-color var(--t-fast) var(--ease), transform var(--t-fast) var(--ease), box-shadow var(--t-fast) var(--ease);
}
a.rep-card:hover { border-color: var(--border-strong); transform: translateY(-2px); box-shadow: var(--shadow); }
.rep-cover {
  aspect-ratio: 16/9; display: grid; place-items: center; position: relative;
  background: linear-gradient(135deg, var(--surface-3), var(--surface-2));
  border-block-end: 1px solid var(--border);
}
.rep-cover .issue { font-family: var(--mono); font-size: var(--fs-2xl); font-weight: 700; color: var(--fg-subtle); opacity: .6; }
.rep-meta { padding: var(--sp-3) var(--sp-4); display: flex; flex-direction: column; gap: var(--sp-2); flex: 1; }

/* ---- Alerts ---- */
.alert-row {
  display: flex; gap: var(--sp-3); padding: var(--sp-3) var(--sp-4);
  border-block-end: 1px solid var(--border); align-items: flex-start;
  transition: background var(--t-fast) var(--ease);
}
.alert-row:hover { background: var(--surface-2); }
.alert-row:last-child { border-block-end: none; }
.alert-ico { width: 30px; height: 30px; border-radius: var(--r-md); display: grid; place-items: center; flex-shrink: 0; }
.alert-ico.up { background: var(--up-soft); color: var(--up); }
.alert-ico.down { background: var(--down-soft); color: var(--down); }
.alert-ico.warn { background: var(--warn-soft); color: var(--warn); }
.alert-ico.info { background: var(--info-soft); color: var(--info); }
.alert-row.unread { background: color-mix(in srgb, var(--primary) 5%, transparent); }

/* ---- Heatmap ---- */
.heat { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px,1fr)); gap: 3px; }
.heat-cell {
  aspect-ratio: 1.5; border-radius: var(--r-sm); padding: var(--sp-2);
  display: flex; flex-direction: column; justify-content: space-between;
  font-size: var(--fs-2xs); border: 1px solid transparent;
  transition: border-color var(--t-fast) var(--ease);
}
/* A scanning highlight, not a click promise: the cells carry no activation handler,
   so the pointer cursor and the scale-up were advertising an action that never came. */
.heat-cell:hover { border-color: var(--fg); z-index: 2; }
.heat-cell .hc-sym { font-weight: 700; }
.heat-cell .hc-val { font-family: var(--mono); font-weight: 600; font-size: var(--fs-xs); direction: ltr; }

/* ---- Progress / gauge ---- */
.bar { height: 5px; background: var(--surface-3); overflow: hidden; }
.bar > i { display: block; height: 100%; background: var(--primary); transition: width var(--t-slow) var(--ease); }
.bar > i.up { background: var(--up); } .bar > i.down { background: var(--down); } .bar > i.warn { background: var(--warn); }

/* ---- Misc ---- */
.row { display: flex; align-items: center; gap: var(--sp-3); }
.row.wrap { flex-wrap: wrap; }
.row.between { justify-content: space-between; }
.col { display: flex; flex-direction: column; gap: var(--sp-3); }
.divider { height: 1px; background: var(--border); border: none; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
.stack-sm > * + * { margin-block-start: var(--sp-2); }
.stack   > * + * { margin-block-start: var(--sp-4); }
.stack-lg > * + * { margin-block-start: var(--sp-6); }

.empty { display: flex; flex-direction: column; align-items: center; gap: var(--sp-3); padding: var(--sp-8) var(--sp-4); text-align: center; color: var(--fg-muted); }
.empty svg { color: var(--fg-subtle); }

.skeleton { background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 50%, var(--surface-2) 75%); background-size: 200% 100%; animation: shim 1.4s linear infinite; border-radius: var(--r-sm); }
@keyframes shim { to { background-position: -200% 0; } }

.scrim { position: fixed; inset: 0; background: rgba(2,6,20,.6); z-index: 45; opacity: 0; pointer-events: none; transition: opacity var(--t-mid) var(--ease); }
.scrim.on { opacity: 1; pointer-events: auto; }

/* ---- Disabled-but-visible control ---- */
[data-soon] { opacity: .55; }
[data-soon] input, [data-soon] select, [data-soon] button { pointer-events: none; }
.badge-soon {
  font-size: var(--fs-2xs); font-weight: 600; padding: 0 5px;
  background: var(--surface-3); color: var(--fg-subtle); margin-inline-start: 6px;
}

/* ---- Reveal animation: REMOVED ----
   This was `opacity: 0` until an IntersectionObserver added `.in`. Seven of the ten
   cards on the weekly report were sitting at opacity 0 — the report read as a page of
   black voids, and anything not scrolled past stayed invisible to print, to Ctrl+F and
   to a reader arriving on an anchor. Beyond the bug: fading your own reporting in as
   the reader scrolls is a marketing-page mannerism. A market bulletin is already there
   when you open it. The class is left in the markup as a no-op so the HTML need not
   change; the rule is what mattered. */
.reveal { opacity: 1; transform: none; }

/* ============================================================
   EDITORIAL LAYER
   A price bulletin is read, not operated. The rules below give the
   page a publication's hierarchy: a rubric above each section, the
   figure as the largest thing on screen, and rules doing the work
   that borders and shadows used to do.
   ============================================================ */

/* Section rubric — small caps label sitting on a hairline, like a wire desk */
.card-head {
  align-items: baseline;
  padding-block: var(--sp-3) var(--sp-2);
}
/* Baseline alignment needs every item to HAVE a baseline. A badge the script has not
   filled yet has none, so the browser synthesises one from its bottom edge, the title
   next to it aligns to that, and when the count arrives the title climbs 9 px (prices,
   iran — every card head with a script-filled badge). A zero-width space gives the
   empty badge the same text baseline as the filled one. */
.card-head > :empty::before { content: '\200B'; }
.card-head h2, .card-head h3 { letter-spacing: normal; }
.card-head .sub { font-family: var(--sans); }

/* The price is the loudest thing on the page — it was tied with the page title. */
.kpi-val { font-size: var(--fs-2xl); letter-spacing: normal; font-weight: 500; }
.kpi { padding: var(--sp-4); gap: 2px; }
.kpi-label { font-size: var(--fs-2xs); letter-spacing: normal; text-transform: uppercase;
             color: var(--fg-subtle); font-weight: 600; }
.kpi-unit  { font-size: var(--fs-xs); line-height: 1.5; }

/* Page title steps back so the data can step forward */
.page-head h1 { font-size: var(--fs-lg); letter-spacing: 0; }
.page-head .lede { font-family: var(--serif); font-size: var(--fs-md); line-height: 1.75; }
.eyebrow { letter-spacing: normal; }

/* Standalone section headings get the rubric rule too */
.page > section > h2, .page section > h2 {
  font-size: var(--fs-2xs); font-weight: 700; letter-spacing: normal;
  text-transform: uppercase; color: var(--fg-subtle);
  font-family: var(--sans);
  padding-block-end: var(--sp-2);
  border-block-end: 1px solid var(--fg-subtle);
  margin-block-end: var(--sp-4);
}

/* Tables: the ledger look — tighter rows, rules only where they separate meaning */
table.data tbody td { padding-block: var(--sp-2); }
table.data tbody tr:hover { background: var(--surface-2); }
table.data thead th {
  padding-block: var(--sp-2);
  font-size: var(--fs-2xs); letter-spacing: normal;
}

/* Prose in the weekly report reads as an article, not UI copy */
.prose { font-family: var(--serif); font-size: var(--fs-md); line-height: 1.95; }
.prose h2 {
  font-size: var(--fs-lg); letter-spacing: normal;
  border-block-end: 1px solid var(--border-strong);
}
/* NO DROP CAP. Persian is a cursive script: ::first-letter lifts the first letter out of
   its shaping run, so «بازار» rendered as an isolated «ب» followed by «ازار» — a broken
   word wearing an ornament. Drop caps are a Latin device and have no Arabic-script
   equivalent. The opening paragraph carries the entry instead, which is how Persian
   typography actually marks a beginning. */
.prose > p:first-of-type {
  font-size: 1.08em;
  color: var(--fg);
}

/* Callouts: a rule, not a tinted box with a coloured rail */
.callout {
  background: transparent; border: 0;
  border-block-start: 2px solid var(--info);
  padding: var(--sp-3) 0 0;
}
.callout.warn { border-block-start-color: var(--warn); }
.callout.up   { border-block-start-color: var(--up); }
.callout.down { border-block-start-color: var(--down); }
.callout h4, .callout .callout-h { font-family: var(--sans); font-size: var(--fs-2xs); font-weight: 700;
                letter-spacing: normal; text-transform: uppercase; color: var(--fg-subtle);
                margin-block-end: var(--sp-1); }

/* The ticker reads as a wire feed */
.tape { border-block-end: 1px solid var(--border-strong); }
.tape-label { letter-spacing: normal; font-weight: 700; }

/* Nav: the current item is marked by weight and a rule, not a filled block */
.nav-item[aria-current="page"] { background: transparent; color: var(--fg); font-weight: 700; }
.nav-item[aria-current="page"]::before { width: 2px; background: var(--accent); }
.nav-label { letter-spacing: normal; }

/* ============================================================
   RESPONSIVE — 1440 / 1024 / 768 / 375
   ============================================================ */
@media (max-width: 1280px) {
  .g-6 { grid-template-columns: repeat(3, minmax(0,1fr)); }
  .g-main, .g-side { grid-template-columns: minmax(0,1fr); }
}
@media (max-width: 1024px) {
  :root { --side-w: 0px; }
  .sidebar { transform: translateX(-100%); width: 260px; box-shadow: var(--shadow); }
  :root[dir="rtl"] .sidebar { transform: translateX(100%); }
  .sidebar.open,
  :root[dir="rtl"] .sidebar.open { transform: none; }   /* (0,4,0) must outrank the RTL park rule */
  /* the closed panel keeps its links out of the tab order via [inert], set in app.js */
  .main { margin-inline-start: 0; }
  .menu-btn { display: grid !important; }
  .g-4 { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .g-3 { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .pipeline { grid-template-columns: minmax(0,1fr); }
  .pipe-arrow { transform: rotate(90deg); padding-block: var(--sp-2); }
  :root[dir="rtl"] .pipe-arrow { transform: rotate(-90deg); }
  .ai-core { aspect-ratio: auto; border-radius: var(--r-lg); padding: var(--sp-5); }
}
@media (max-width: 768px) {
  .page { padding: var(--sp-4) var(--sp-4) var(--sp-7); }
  .g-2, .g-3, .g-4, .g-6, .g-chat { grid-template-columns: minmax(0,1fr); }
  .span-2, .span-3 { grid-column: span 1; }
  h1 { font-size: var(--fs-xl); }
  .brand-sub { display: none; }
  .hide-sm { display: none !important; }
  .chat { height: calc(100vh - var(--nav-h) - var(--sp-6)); }
  .prose { font-size: var(--fs-base); }
  /* A filter row (search + chips + count) wraps one way while the chip strip is empty
     and another once the script fills it — on the price board the search box sat 48 px
     wide beside the empty strip, then took a line of its own and everything below
     dropped 98 px (the sweep's shift trace). On a phone each of them owns a full line
     from the first paint, so filling changes nothing. */
  .card .row.wrap > .search-wrap, .card .row.wrap > .suggest { flex-basis: 100%; max-width: none; }
}
@media (max-width: 420px) {
  .page { padding: var(--sp-3) var(--sp-3) var(--sp-6); }
  .kpi-val { font-size: var(--fs-lg); }
  .hide-xs { display: none !important; }
}
.menu-btn { display: none; }

/* ---- Reduced motion ---- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important; scroll-behavior: auto !important;
  }
  .tape-track { animation: none; }
  .reveal { opacity: 1; transform: none; }
}

/* ---- Print ---- */
@media print {
  .topbar, .sidebar, .tape, .no-print { display: none !important; }
  .main { margin: 0; }
  .card { break-inside: avoid; box-shadow: none; }
  body { background: #fff; color: #000; }
}

/* ============================================================
   PERSIAN TYPOGRAPHY CORRECTIONS
   Applied last so they win over every earlier declaration.
   ============================================================ */

/* LETTER-SPACING IS NOT AVAILABLE IN ARABIC SCRIPT.

   Fourteen rules in this file tracked Persian text out (.eyebrow .16em, .nav-label
   .14em, .tape-label .14em, .kpi-label .06em, table headers .08em, the badge and
   rubric styles) and the headings tracked it IN (h1 -.02em, .kpi-val -.03em).

   Latin letters are discrete shapes standing side by side, so tracking moves them
   apart and the words survive. Persian letters JOIN — «مـیـلـگـرد» is one connected
   stroke. Adding space stretches the kashida connections into gaps and the word
   visually falls apart into fragments; removing space collides the joins and buries
   the dots that are the only thing separating «ب» «پ» «ت» «ث». Either direction
   damages legibility, and both read to a Persian eye the way a stretched or squeezed
   font reads to a Latin one: as something no one looked at.

   Hierarchy here comes from size, weight, colour and space — never tracking.

   All twenty declarations were zeroed AT SOURCE rather than overridden here: a blanket
   `body * { letter-spacing: normal }` carries specificity (0,0,1) and would have lost
   to every one of them, since each sits on a class. */

/* The one genuinely Latin string in the interface — the wordmark — keeps its tracking. */
.brand-sub {
  letter-spacing: .1em;
  text-transform: uppercase;
  font-family: var(--sans);
}

/* text-transform: uppercase does nothing to Persian (the script is unicameral) but it
   silently shouts any Latin token embedded in a Persian label — «HRC» inside a
   sentence, a source code, a unit. Off everywhere except the wordmark above. */
.eyebrow, .kpi-label, .nav-label, .tape-label, .badge, .callout h4, .callout .callout-h,
.card-head .sub, table.data thead th, .seg button, .chip, .pipe-arrow .lbl {
  text-transform: none;
}

/* Persian needs a taller line than Latin at the same size: the script hangs
   descenders and stacks dots below the baseline, and ascenders reach high. 1.6 put
   them within a pixel of each other across lines. */
body { line-height: 1.75; }

/* Room around the text, proportional to the larger type. 16px of padding under a
   15px line is a text box with its walls touching the words. */
.card-body { padding: var(--sp-5); }
.card-head { padding: var(--sp-4) var(--sp-5); }
.card-foot { padding: var(--sp-3) var(--sp-5); }
.kpi { padding: var(--sp-5); }

/* Page rhythm. Cards sat 16px apart in a 16px-padded page, so a long report read as one
   undifferentiated stack of rectangles with no pause between sections. The gap between
   two cards must be larger than the gap between elements inside a card, or the boxes
   are the only thing separating them — which is what forced every section into a
   bordered rectangle in the first place. */
.grid { gap: var(--sp-5); }
.page-head { margin-block-end: var(--sp-7); }
.page { padding: var(--sp-6) var(--sp-6) var(--sp-8); }
.page-head .lede { margin-block-start: var(--sp-2); line-height: 1.7; }

/* The Latin wordmark wrapped to two lines on a phone and pushed into the header row.
   It is a mark, not a sentence — it either fits on one line or it is not shown. */
.brand-sub { white-space: nowrap; }
@media (max-width: 480px) { .brand-sub { display: none; } }

/* ============================================================
   MASTHEAD + MINIMAL LAYER
   Replaces the side rail. Appended last so it wins over the older
   .topbar / .sidebar rules without deleting them from history.
   ============================================================ */

:root { --side-w: 0px; --mast-h: auto; }

/* The rail is gone. Its rules stay in the file above but must not lay anything out. */
.sidebar, .scrim, .menu-btn, .topbar { display: none !important; }

.mast {
  position: sticky; inset-block-start: 0; z-index: 60;
  background: var(--bg-elev);
  border-block-end: 1px solid var(--border-strong);
}
.mast-row {
  display: flex; align-items: center; gap: var(--sp-4);
  max-width: 1560px; margin-inline: auto;
  padding-inline: var(--sp-6);
}
/* Row heights are tokens so the pre-JS placeholder (below) and the built rows can never
   disagree — a mismatch of even a few pixels would be a layout shift on every load. */
.mast { --mast-top: 60px; --mast-sections: 51px; --mast-sub: 42px; }
.mast-top      { min-height: var(--mast-top); }
.mast-sections { min-height: var(--mast-sections); }
.mast-sub      { min-height: var(--mast-sub); }
.mast[data-shell="solo"] { min-height: calc(var(--mast-top) + var(--mast-sections)); }
.mast[data-shell="sub"]  { min-height: calc(var(--mast-top) + var(--mast-sections) + var(--mast-sub)); }
.mast-spacer { flex: 1; }

.brand { display: flex; align-items: center; gap: var(--sp-3); }
.brand-mark {
  display: grid; place-items: center; width: 30px; height: 30px;
  background: var(--primary); color: var(--on-primary); flex-shrink: 0;
}
.brand-text { display: flex; flex-direction: column; line-height: 1.25; }
.brand-name { font-family: var(--serif); font-weight: 700; font-size: var(--fs-md); }
.brand-sub  { font-size: var(--fs-2xs); color: var(--fg-subtle); }
.mast-stamp { font-size: var(--fs-xs); color: var(--fg-subtle); }

/* Sections: the four things the product does. Always visible. */
.mast-sections { gap: 0; border-block-start: 1px solid var(--border); overflow-x: auto; scrollbar-width: none; }
.mast-sections::-webkit-scrollbar { display: none; }
.sect {
  position: relative; padding: var(--sp-3) var(--sp-4);
  font-size: var(--fs-base); font-weight: 600; color: var(--fg-muted);
  white-space: nowrap; cursor: pointer;
  transition: color var(--t-fast) var(--ease);
}
.sect:hover { color: var(--fg); }
/* The active marker is a rule that GROWS from the centre — the one piece of motion
   in the navigation, and it encodes something real: which section you are in. */
.sect::after {
  content: ''; position: absolute; inset-inline: 0; inset-block-end: -1px; height: 2px;
  background: var(--primary); transform: scaleX(0);
  transition: transform var(--t-mid) var(--ease);
}
.sect:hover::after { transform: scaleX(.35); }
.sect[aria-current="true"] { color: var(--fg); }
.sect[aria-current="true"]::after { transform: scaleX(1); }

/* Sub-navigation: only the current section's pages. */
.mast-sub {
  gap: 0; border-block-start: 1px solid var(--border);
  background: var(--surface-2); overflow-x: auto; scrollbar-width: none;
}
.mast-sub::-webkit-scrollbar { display: none; }
.subl {
  padding: var(--sp-2) var(--sp-3); font-size: var(--fs-sm); color: var(--fg-muted);
  white-space: nowrap; border-block-end: 2px solid transparent; cursor: pointer;
  transition: color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
  display: inline-flex; align-items: center; gap: 6px;
}
.subl:hover { color: var(--fg); }
.subl[aria-current="page"] { color: var(--fg); font-weight: 600; border-block-end-color: var(--primary); }

/* Main no longer sits beside a rail. */
.main { margin-inline-start: 0; margin-block-start: 0; }
.page { max-width: 1560px; margin-inline: auto; }
.tape { position: static; }

@media (max-width: 720px) {
  .mast-row { padding-inline: var(--sp-4); }
  .brand-sub, .mast-stamp { display: none; }
}

/* ---- MINIMAL: quieter chrome ----
   A professional tool signals with type, weight and space. Filled badges on every
   figure were doing decoration, not communication. */
.badge {
  background: transparent; border: 1px solid var(--border-strong); color: var(--fg-muted);
  font-weight: 600;
}
.badge.b-up   { color: var(--up);   border-color: var(--up); }
.badge.b-down { color: var(--down); border-color: var(--down); }
.badge.b-warn { color: var(--warn); border-color: var(--warn); }
.badge.b-pri  { color: var(--primary); border-color: var(--primary); }
.badge.b-mute { color: var(--fg-subtle); border-color: var(--border); }

/* Cards: a hairline and the surface, nothing else. */
.card { border: 1px solid var(--border); background: var(--surface); }
.card-head { border-block-end: 1px solid var(--border); }
.kpi { border: 1px solid var(--border); background: var(--surface); }

/* ---- MOTION ----
   There was none at all, which reads as unfinished. Everything here is short, tied to
   an interaction the reader initiated, and disabled under reduced-motion. Nothing
   animates on scroll: content is present when the page is. */
a, button, .chip, .btn, .icon-btn, .kpi, .nav-item, tbody tr {
  transition: background-color var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease),
              color var(--t-fast) var(--ease);
}
.btn:active, .icon-btn:active, .chip:active { transform: translateY(1px); }
a.kpi:hover { border-color: var(--border-strong); background: var(--surface-2); }
table.data tbody tr:hover { background: var(--surface-2); }

/* Chart lines wipe in once, on first paint — 420ms, then never again. It traces the
   series left to right, which is the direction the time axis runs, so the motion says
   something true rather than decorating. The clip rect scales; the paths do not. */
@keyframes wipe-in { from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0 0 0 0); } }
.chart-box svg.wipe-in { animation: wipe-in 420ms var(--ease) both; }

@media (prefers-reduced-motion: reduce) {
  .sect::after { transition: none; }
  .chart-box svg.wipe-in { animation: none; clip-path: none; }
  .btn:active, .icon-btn:active, .chip:active { transform: none; }
}

/* `.page` is a flex item of the column flex container `.main`, and on the CROSS axis it
   was sizing to its own content instead of stretching. Its tables carry
   `min-width: 640px`, so on a 375px phone the page became 698px and the whole document
   scrolled sideways — while `.table-wrap`, the scroller that exists precisely to absorb
   a wide table, never triggered, because its ancestor had already widened to fit it.

   Measured: with `width:100%` the document is 375, the page is 375, and the table-wrap
   is 317 and scrolls on its own. `min-width:0` alone did NOT fix this — the constraint
   was the cross-axis size, not the automatic minimum. Stating the width explicitly is
   also the honest declaration: this element is as wide as its column, up to 1560px. */
.main { min-width: 0; }
.page { width: 100%; min-width: 0; }
.card, .card-body, .table-wrap { min-width: 0; }

/* The action cluster in a page head is a flex row that did not wrap, so on a 375px
   phone the dashboard's date badge plus two buttons measured 386px and pushed the
   document 39px sideways. Page-head actions always wrap — there is no layout in which
   holding them on one line is worth a horizontally scrolling page. */
.page-head .row { flex-wrap: wrap; }
/* …and WHERE it wraps must not depend on the script either. The date badge is empty
   until app.js fills it; on a phone the empty badge left room for both buttons on its
   line, the filled one did not, and the second button dropped a line — a 0.085 layout
   shift on the dashboard, measured with CPU throttling after every other shift was
   gone. Inline-level containers therefore get their WIDTH reserved the same way block
   ones get their height: scripts/reserve.mjs writes the measured min-width. */

.brand-compact, .mobile-search { display: none; }

/* ============================================================
   LIQUID GLASS — pearl surfaces, optical edges, quiet blue light.
   Shared by every page. Blur is limited to the navigation layer;
   data surfaces use translucent fills so long tables stay cheap to paint.
   ============================================================ */
@media screen {
  :root {
    --r-sm: 10px; --r-md: 14px; --r-lg: 20px; --r-xl: 28px;
    --glass-blur: 24px;
    --ease: cubic-bezier(.22, .68, 0, 1);
  }

  :root, :root[data-theme="light"] {
    color-scheme: light;
    --bg: #edf2f9;
    --bg-elev: #ffffff;
    --surface: #ffffff;
    --surface-2: #edf2f9;
    --surface-3: #e1e9f4;
    --border: #d7e0ee;
    --border-strong: #a2b3ce;
    --fg: #192742;
    --fg-muted: #435570;
    --fg-subtle: #52637c;
    --primary: #294d94;
    --primary-hover: #1d3c7b;
    --primary-soft: #e7eefb;
    --on-primary: #ffffff;
    --accent: #294d94;
    --accent-soft: #e7eefb;
    --on-accent: #ffffff;
    --ring: #315fb8;
    --grid-line: #e0e7f1;
    --glass: rgba(255, 255, 255, .78);
    --glass-nav: rgba(246, 250, 255, .87);
    --glass-inset: rgba(241, 246, 254, .88);
    --glass-edge: rgba(255, 255, 255, .98);
    --glass-shine: rgba(255, 255, 255, .75);
    --glass-line: rgba(113, 144, 190, .23);
    --glass-tint: rgba(219, 232, 255, .65);
    --glass-lens: rgba(255, 255, 255, .3);
    --shadow: 0 12px 32px -16px rgba(39, 67, 118, .2);
    --glass-shadow: 0 16px 44px -26px rgba(34, 65, 111, .26),
                    inset 0 1px 0 var(--glass-edge), inset 0 -1px 0 var(--glass-shine);
    --action-shadow: 0 6px 16px -7px rgba(35, 72, 142, .6);
    --ambient-blue: rgba(128, 174, 242, .27);
    --ambient-mint: rgba(130, 209, 218, .17);
    --ambient-lilac: rgba(177, 170, 237, .2);
  }

  :root[data-theme="dark"] {
    color-scheme: dark;
    --bg: #0c1422;
    --bg-elev: #172236;
    --surface: #182438;
    --surface-2: #1e2d43;
    --surface-3: #283b55;
    --border: #34465e;
    --border-strong: #637b9e;
    --fg: #edf3ff;
    --fg-muted: #bdcce1;
    --fg-subtle: #acbcd3;
    --primary: #a9c6ff;
    --primary-hover: #c5d8ff;
    --primary-soft: #233755;
    --on-primary: #102444;
    --accent: #a9c6ff;
    --accent-soft: #233755;
    --on-accent: #102444;
    --ring: #bad2ff;
    --grid-line: #30425a;
    --glass: rgba(25, 38, 59, .87);
    --glass-nav: rgba(19, 31, 50, .91);
    --glass-inset: rgba(32, 48, 72, .9);
    --glass-edge: rgba(187, 214, 255, .23);
    --glass-shine: rgba(185, 212, 255, .05);
    --glass-line: rgba(155, 184, 226, .2);
    --glass-tint: rgba(50, 79, 130, .28);
    --glass-lens: rgba(141, 182, 252, .07);
    --shadow: 0 12px 32px -16px rgba(0, 0, 0, .45);
    --glass-shadow: 0 16px 44px -26px rgba(0, 0, 0, .7),
                    inset 0 1px 0 var(--glass-edge), inset 0 -1px 0 var(--glass-shine);
    --action-shadow: 0 6px 22px -8px rgba(93, 149, 245, .3);
    --ambient-blue: rgba(50, 97, 181, .2);
    --ambient-mint: rgba(37, 139, 151, .1);
    --ambient-lilac: rgba(100, 78, 158, .13);
  }

  body {
    display: flow-root;
    background-color: var(--bg);
    background-image:
      radial-gradient(ellipse at 90% 0%, var(--ambient-blue), transparent 52%),
      radial-gradient(ellipse at 5% 40%, var(--ambient-lilac), transparent 50%),
      radial-gradient(ellipse at 70% 90%, var(--ambient-mint), transparent 50%);
    background-attachment: fixed;
  }
  ::selection { background: var(--primary); color: var(--on-primary); }
  html { scroll-padding-top: 204px; }
  :focus-visible { outline-width: 3px; outline-offset: 4px; }

  /* Floating navigation: the highlight follows the selected section. */
  .mast {
    --mast-top: 72px; --mast-sections: 60px; --mast-sub: 52px;
    top: 12px;
    width: calc(100% - 48px);
    max-width: 1504px;
    margin: 12px auto 0;
    background: var(--glass-nav);
    border: 1px solid var(--glass-edge);
    border-radius: 24px;
    box-shadow: var(--glass-shadow);
    -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(150%);
    backdrop-filter: blur(var(--glass-blur)) saturate(150%);
  }
  .mast[data-shell="solo"] { min-height: calc(var(--mast-top) + var(--mast-sections) + 2px); }
  .mast[data-shell="sub"] { min-height: calc(var(--mast-top) + var(--mast-sections) + var(--mast-sub) + 2px); }
  .mast-row { padding-inline: 24px; }
  .mast-top { gap: 14px; }
  .brand-mark {
    width: 40px; height: 40px; border-radius: 14px;
    border: 1px solid var(--glass-edge);
    background: linear-gradient(145deg, var(--primary), var(--primary-hover));
    color: var(--on-primary);
    box-shadow: var(--action-shadow), inset 0 1px 0 var(--glass-shine);
  }
  .brand-name { font-family: var(--sans); font-size: 16px; }
  .brand-sub { margin-top: 4px; font-size: 10px; }
  .mast-stamp { padding-inline: 12px; border-inline-start: 1px solid var(--glass-line); }
  .icon-btn {
    width: 44px; height: 44px; flex-shrink: 0; border-radius: 50%;
    background: var(--glass); border: 1px solid var(--glass-line);
    color: var(--fg-muted); box-shadow: inset 0 1px 0 var(--glass-edge);
  }
  .icon-btn:hover { background: var(--primary-soft); border-color: var(--border-strong); color: var(--primary); }
  .mast-sections { padding-block: 7px; gap: 6px; border-color: var(--glass-line); }
  .sect {
    display: inline-flex; align-items: center; justify-content: center;
    min-height: 44px; padding: 8px 20px; border-radius: 14px;
    border: 1px solid transparent; font-size: 14px;
  }
  .sect::after { display: none; }
  .sect:hover { background: var(--glass-inset); color: var(--primary); }
  .sect[aria-current="true"] {
    background: var(--primary); color: var(--on-primary);
    border-color: var(--glass-edge); box-shadow: var(--action-shadow), inset 0 1px 0 var(--glass-shine);
  }
  .mast-sub {
    gap: 4px; padding-block: 3px; background: transparent;
    border-color: var(--glass-line); border-radius: 0 0 24px 24px;
  }
  .subl { min-height: 44px; border-radius: 12px; border: 1px solid transparent; }
  .subl:hover { background: var(--glass-inset); }
  .subl[aria-current="page"] {
    background: var(--primary-soft); color: var(--primary); border-color: var(--glass-line);
  }

  .tape {
    margin: 14px 28px 0; width: calc(100% - 56px); max-width: 1504px;
    align-self: center; border: 1px solid var(--glass-line); border-radius: 14px;
    background: var(--glass); box-shadow: inset 0 1px 0 var(--glass-edge);
  }
  .tape-label { background: var(--glass-inset); color: var(--fg-muted); border-color: var(--glass-line); }
  .tape-window { flex: 1; min-width: 0; overflow: hidden; }
  .tape-track { width: max-content; padding: 0; gap: 0; animation-name: marquee-rtl; animation-duration: 90s; }
  .tape-group { display: flex; align-items: center; flex-shrink: 0; gap: var(--sp-5); padding-inline: 10px; }
  @keyframes marquee-rtl { from { transform: translateX(0); } to { transform: translateX(50%); } }
  .tape .icon-btn { width: 36px; height: 36px; border: none; background: transparent; }

  .page { padding-top: 24px; }
  .page-head {
    position: relative; isolation: isolate; overflow: hidden;
    align-items: center; gap: 24px; padding: 30px 32px;
    margin-bottom: 24px; border: 1px solid var(--glass-edge); border-radius: var(--r-xl);
    background: linear-gradient(115deg, var(--glass-tint), var(--glass) 68%);
    box-shadow: var(--glass-shadow);
  }
  .page-head::before, .page-head::after {
    content: ''; position: absolute; z-index: -1; pointer-events: none;
    border-radius: 50%; border: 1px solid var(--glass-edge);
    background: linear-gradient(145deg, var(--glass-lens), transparent 70%);
    box-shadow: inset 2px 3px 14px var(--glass-shine), 8px 12px 40px var(--glass-tint);
  }
  .page-head::before { width: 260px; height: 260px; left: 3%; top: -165px; }
  .page-head::after { width: 170px; height: 170px; left: 29%; bottom: -142px; }
  .page-head h1 { font-family: var(--sans); font-size: 30px; color: var(--fg); line-height: 1.55; }
  .page-head .eyebrow { color: var(--primary); font-size: 12px; margin-bottom: 5px; }
  .page-head .lede { font-family: var(--sans); color: var(--fg-muted); font-size: 14px; line-height: 1.95; max-width: 69ch; }
  .page-head > .row { gap: 10px; }
  /* Keep late-filled headings from changing the action row's flex placement. */
  .page-head:has([data-headline]) > :first-child { flex: 1 1 560px; min-width: 0; }
  .page-head:has([data-headline]) > .row { flex: 0 1 auto; }
  .page-head .badge { min-height: 32px; }
  .dashboard .page-head h1 { font-size: 38px; }
  .dashboard-jumps {
    display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
    margin-block: -8px 24px; color: var(--fg-muted); font-size: 13px;
  }
  .jump-label { margin-inline-end: 8px; color: var(--fg-subtle); font-size: 12px; }
  .dashboard-jumps a {
    display: inline-flex; align-items: center; gap: 8px; min-height: 44px;
    padding: 8px 14px; border: 1px solid var(--glass-line); border-radius: 12px;
    background: var(--glass); box-shadow: inset 0 1px 0 var(--glass-edge);
  }
  .dashboard-jumps a:hover { background: var(--primary-soft); color: var(--primary); border-color: var(--border-strong); }
  .source-disclosure > summary { cursor: pointer; list-style: none; min-height: 80px; border-bottom: 0; }
  .source-disclosure > summary::-webkit-details-marker { display: none; }
  .source-disclosure > summary:hover { background: var(--glass-inset); }
  .source-disclosure > summary:focus-visible { outline-offset: -4px; }
  .source-disclosure[open] > summary { border-bottom: 1px solid var(--glass-line); }
  .disclosure-title { display: block; font-size: var(--fs-md); font-weight: 700; }
  .disclosure-label { display: inline-flex; align-items: center; gap: 12px; font-size: 12px; color: var(--primary); }
  .disclosure-label::after {
    content: ''; width: 8px; height: 8px; border-inline-end: 1.5px solid currentColor;
    border-bottom: 1.5px solid currentColor; transform: rotate(45deg); margin-top: -4px;
  }
  .source-disclosure[open] .disclosure-label::after { transform: rotate(225deg); margin-top: 4px; }

  /* Milky content surfaces keep small figures legible over the ambient light. */
  .card {
    background: var(--glass); border: 1px solid var(--glass-edge);
    border-radius: var(--r-xl); box-shadow: var(--glass-shadow);
  }
  .card-head { background: linear-gradient(110deg, var(--glass-shine), transparent); border-color: var(--glass-line); align-items: center; }
  .card-head h2, .card-head h3 { font-family: var(--sans); }
  .card-head .sub { margin-top: 5px; line-height: 1.8; }
  .card-foot { background: var(--glass-inset); border-color: var(--glass-line); }
  .card-foot a { color: var(--primary); font-weight: 600; }
  .kpi, .scen, .report-card {
    background: linear-gradient(145deg, var(--glass), var(--glass-inset));
    border-color: var(--glass-line); box-shadow: inset 0 1px 0 var(--glass-edge);
    border-radius: var(--r-lg);
  }
  .kpi { padding: 18px 20px; gap: 6px; }
  .kpi-label { font-size: 13px; font-weight: 600; }
  .kpi-val { color: var(--fg); }
  a.kpi { transition: background-color var(--t-mid) var(--ease), border-color var(--t-mid) var(--ease), box-shadow var(--t-mid) var(--ease); }
  a.kpi:hover { transform: none; background: var(--primary-soft); border-color: var(--border-strong); box-shadow: var(--shadow), inset 0 1px 0 var(--glass-edge); }
  [data-ref] .kpi { border-top: 3px solid var(--primary); }
  [data-ref] .kpi-val { color: var(--primary); font-size: 32px; }
  .scen.is-called { background: var(--primary-soft); border-color: var(--primary); }
  .ledger { background: var(--glass-inset); border-radius: var(--r-md); overflow: hidden; }
  .callout { border-radius: var(--r-md); border: 1px solid var(--glass-line); background: var(--glass-inset); padding: 14px 18px; }
  .callout-h { color: var(--primary); }
  .card .callout { box-shadow: inset 0 1px 0 var(--glass-edge); }

  .btn {
    min-height: 44px; border-radius: 14px; padding-inline: 16px;
    border-color: var(--glass-line); background: var(--glass); color: var(--fg-muted);
    box-shadow: inset 0 1px 0 var(--glass-edge);
  }
  .btn:hover { border-color: var(--border-strong); background: var(--primary-soft); color: var(--primary); }
  .btn-primary, .btn-accent {
    color: var(--on-primary); border-color: var(--primary);
    background: linear-gradient(145deg, var(--primary), var(--primary-hover));
    box-shadow: var(--action-shadow), inset 0 1px 0 var(--glass-shine);
  }
  .btn-primary:hover, .btn-accent:hover { background: var(--primary-hover); color: var(--on-primary); }
  .btn-ghost { background: transparent; box-shadow: none; border-color: transparent; }
  .btn-sm { min-height: 40px; }
  .btn:disabled { box-shadow: none; }
  .badge, .delta, .chip { border-radius: 9px; }
  .badge.b-mute { background: var(--glass-inset); color: var(--fg-muted); border-color: var(--glass-line); }
  .badge.b-pri { background: var(--primary-soft); }
  .badge.b-up { background: var(--up-soft); }
  .badge.b-down { background: var(--down-soft); }
  .badge.b-warn { background: var(--warn-soft); }
  .seg { background: var(--glass-inset); border-color: var(--glass-line); border-radius: 14px; padding: 4px; }
  .seg button { min-height: 36px; border-radius: 10px; }
  .seg button[aria-pressed="true"] { color: var(--primary); background: var(--surface); box-shadow: var(--shadow), inset 0 1px 0 var(--glass-edge); }
  .input, .select, .textarea {
    border-radius: 12px; min-height: 44px; background: var(--glass-inset);
    border-color: var(--border-strong); box-shadow: inset 0 1px 3px var(--glass-line);
  }
  .input::placeholder, .textarea::placeholder { color: var(--fg-subtle); }
  .input:focus, .select:focus, .textarea:focus { background: var(--surface); }
  .filter-fields { display: grid; grid-template-columns: minmax(180px, 1fr) minmax(200px, 290px) auto; align-items: end; gap: 16px; }
  .filter-fields .search-wrap { max-width: none; width: 100%; }
  .filter-fields .field { gap: 8px; }
  .filter-fields label, .filter-label { font-size: 13px; font-weight: 600; color: var(--fg-muted); }
  .filter-fields > .btn { min-height: 44px; }
  .filter-categories { margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--glass-line); }
  .filter-label { display: block; margin-bottom: 10px; }
  .filter-categories .suggest { gap: 8px; }
  .price-filters .chip { min-height: 40px; padding-inline: 14px; }
  .price-filters .chip[aria-pressed="true"] { background: var(--primary-soft); border-color: var(--primary); color: var(--primary); }
  .filter-count { display: block; margin-top: 12px; }
  .price-table-wrap.is-filtered { min-height: 0 !important; }
  .price-table-wrap:focus-visible { outline-offset: -3px; }
  .table-scroll-hint { display: none; }
  table.data thead th { background: var(--glass-inset); color: var(--fg-muted); border-color: var(--border); }
  table.data thead th.num { color: var(--fg-muted); }
  table.data tbody tr:hover { background: var(--primary-soft); }
  .heat-cell { border-radius: 12px; }
  .bar { border-radius: 99px; overflow: hidden; }
  .chart-tip, .toast { border-radius: 14px; background: var(--bg-elev); box-shadow: var(--shadow); }
  .forecast-chart {
    border-radius: 18px;
    background: linear-gradient(180deg, var(--glass-inset), transparent);
    box-shadow: inset 0 1px 0 var(--glass-edge);
  }
  .forecast-chart .chart-tip { padding: 12px 16px; min-width: 170px; border-color: var(--glass-line); }
  .forecast-caption .chart-legend { gap: 12px 24px; align-items: center; }
  .forecast-caption .lg-band {
    display: inline-block; width: 22px; height: 12px; border-radius: 4px;
    background: var(--primary-soft); border: 1px dashed var(--primary);
  }

  @media (max-width: 1024px) {
    .mast { width: calc(100% - 32px); }
    .mast-row { padding-inline: 18px; }
    .page-head { padding: 24px; }
    .page-head > .row { width: 100%; }
    .sect { padding-inline: 16px; }
  }
  @media (max-width: 768px) {
    html { scroll-padding-top: 144px; }
    .mast { --mast-top: 64px; top: calc(-1 * var(--mast-top) - 1px); margin-top: 8px; width: calc(100% - 20px); border-radius: 20px; }
    .mast:has(:focus-visible) { top: 8px; }
    .mast-row { padding-inline: 12px; }
    .mast-top { gap: 8px; }
    .brand { gap: 8px; }
    .brand-mark { width: 36px; height: 36px; border-radius: 12px; }
    .brand-name { font-size: 13px; }
    .brand-full { display: none; }
    .brand-compact { display: inline; }
    .mobile-search { display: grid; }
    .brand-sub { display: none; }
    .mast-sections { gap: 4px; }
    .sect { padding-inline: 14px; font-size: 13px; flex-shrink: 0; }
    .tape { margin: 12px 16px 0; width: calc(100% - 32px); }
    .page { padding: 18px 16px 40px; }
    .page-head { border-radius: 22px; padding: 24px 20px; gap: 20px; }
    .page-head h1, .dashboard .page-head h1 { font-size: 28px; }
    .page-head .lede { font-size: 13px; }
    .page-head::before { left: -120px; }
    .dashboard .page-head [data-updated] { display: none; }
    .dashboard .page-head { gap: 16px; }
    .dashboard-jumps { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); margin-block: -4px 20px; gap: 6px; }
    .dashboard-jumps a { justify-content: center; padding-inline: 4px; font-size: 12px; }
    .dashboard-jumps a > svg, .jump-label { display: none; }
    .card { border-radius: 22px; }
    .card-head { flex-wrap: wrap; gap: 14px; }
    .card-head > .row { flex-wrap: wrap; }
    .dashboard .card-head:has([data-ref-window], [data-out-window]) > :first-child { flex-basis: 100%; }
    .card-body, .card-head { padding: 18px; }
    .card-body.flush { padding: 0; }
    .card-foot { padding: 14px 18px; flex-wrap: wrap; }
    .filter-fields { grid-template-columns: minmax(0, 1fr); gap: 14px; }
    .filter-fields > .btn { justify-self: start; }
    .price-filters .chip { min-height: 44px; }
    .table-scroll-hint { display: block; padding: 12px 18px; font-size: 12px; color: var(--fg-muted); background: var(--primary-soft); }
    .price-table { table-layout: fixed; width: 640px; min-width: 640px; }
    .price-table :is(th, td):first-child {
      position: sticky; inset-inline-start: 0; min-width: 164px; max-width: 180px;
      background: var(--surface); box-shadow: -1px 0 0 var(--border); z-index: 1;
    }
    .price-table th:first-child { width: 176px; background: var(--surface-2); z-index: 3; }
    .price-table td:first-child { overflow-wrap: anywhere; }
    .price-table tr:hover td:first-child { background: var(--primary-soft); }
    .kpi { padding: 16px 18px; }
    .btn-sm, .seg button, .chip { min-height: 44px; }
    .tape .icon-btn { width: 44px; height: 44px; }
    .tape { height: 46px; }
  }
  @media (max-width: 374px) {
    .brand-mark { display: none; }
    .mast-top { gap: 6px; }
    .page { padding-inline: 12px; }
    .page-head { padding-inline: 16px; }
  }

  @supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .mast { background: var(--bg-elev); }
  }
  @media (prefers-reduced-transparency: reduce) {
    .mast, .card, .page-head, .kpi, .scen, .report-card, .tape { background: var(--surface); }
    .mast { -webkit-backdrop-filter: none; backdrop-filter: none; }
    .page-head::before, .page-head::after { display: none; }
    body { background-image: none; }
  }
  @media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { scroll-behavior: auto !important; transition: none !important; }
  }
  @media (forced-colors: active) {
    .mast, .card, .page-head, .kpi, .scen { border: 1px solid CanvasText; box-shadow: none; }
    .sect[aria-current="true"], .subl[aria-current="page"] { outline: 2px solid Highlight; }
    .page-head::before, .page-head::after { display: none; }
  }
}

