/* theyield.co.za - institutional dark theme
 *
 * Refreshed 2026-09-14 from the 2026-09-13 override layer. Still an OVERRIDE
 * LAYER: Dash auto-loads every .css in assets/ after Bootstrap SLATE, so deleting
 * this file reverts the look (the new masthead and Curve-tab markup then render
 * as plain Bootstrap).
 *
 * Direction: a bank markets screen, not a consumer app. Deep ink surfaces with a
 * blue cast; one restrained gold for brand and emphasis; blue for interaction;
 * green/red only as reinforcement of an explicit sign. IBM Plex Sans for UI and
 * figures (tabular lining numerals), IBM Plex Mono for timestamps, codes and
 * provenance - loaded from Google Fonts in the index template, so the droplet
 * serves not one byte of it, with system fallbacks that keep the layout stable
 * if the font never arrives.
 *
 * Every text/background pair and every meaningful boundary is checked against
 * WCAG 2.1 arithmetically by production/FlaskApp/contrast_check.py, which gates
 * commits. Chart colours (--ty-series-*, diverging, brand mark) are validated
 * separately for colour-vision deficiency against --ty-chart-surface with the
 * dataviz palette validator: all eight slots pass, worst adjacent CVD delta-E
 * 8.4. assets/curve.js reads these tokens at render time, so page and charts
 * share one source of truth.
 */

:root {
  /* Surfaces: ink with a blue cast, never pure black (borders vanish on it). */
  --ty-bg: #0a0e14;
  --ty-surface: #10161f;
  --ty-surface-2: #161e2a;
  --ty-surface-3: #1c2533;
  --ty-border: #222c3a;           /* decorative hairline between rows */
  --ty-border-2: #67768d;         /* meaningful boundary: inputs, header rules (>= 3:1) */

  /* Text: hierarchy from contrast, not colour. */
  --ty-text: #e6ebf2;
  --ty-text-muted: #a3afbf;
  --ty-text-dim: #8591a3;

  /* Brand gold for identity and emphasis; blue for anything you can press. */
  --ty-brand: #e2b155;
  --ty-brand-strong: #f0c46e;
  --ty-on-brand: #10161f;         /* text on a gold fill: buttons, selected day */
  --ty-accent: #6eb4ff;
  --ty-accent-strong: #9ccbff;

  /* Rate moves, by the direction of the instrument's PRICE - so a rise in yield
     wears --ty-down. Reinforcement only, and it has to be: measured with the
     dataviz palette validator, these two are 2.7 apart in OKLab for a
     deuteranope (26.4 for normal vision). Every change also carries its sign,
     and every sparkline its slope against a dashed baseline. */
  --ty-up: #4cc38a;
  --ty-down: #f07a7a;
  --ty-live: #4cc38a;             /* the feed's freshness pulse, not a rate move */

  /* Charts. Marks have different requirements from type: these sit in the
     dark-mode lightness band and were validated as a set. */
  --ty-chart-surface: #10161f;
  --ty-chart-grid: #1e2733;
  --ty-chart-axis: #3a4656;
  --ty-chart-brand: #c98500;
  --ty-series-1: #3987e5;
  --ty-series-2: #d95926;
  --ty-series-3: #199e70;
  --ty-series-4: #c98500;
  --ty-series-5: #d55181;
  --ty-series-6: #008300;
  --ty-series-7: #9085e9;
  --ty-series-8: #e66767;
  --ty-div-neg: #3987e5;
  --ty-div-mid: #262d38;
  --ty-div-pos: #e66767;

  --ty-radius: 6px;
  --ty-sans: "IBM Plex Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI",
             Roboto, Arial, sans-serif;
  --ty-mono: "IBM Plex Mono", ui-monospace, "SF Mono", "Cascadia Mono", Menlo,
             Consolas, monospace;
}

/* ------------------------------------------------------------------ base -- */

html { color-scheme: dark; }

body {
  background: var(--ty-bg);
  color: var(--ty-text);
  font-family: var(--ty-sans);
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-feature-settings: "tnum" 1, "lnum" 1;
}

.container, .container-fluid {
  max-width: 1560px;
  /* 16px on a phone, up to 40px on a wide desktop. The 16px floor keeps the
     mobile no-horizontal-scroll guarantee. */
  padding-left: clamp(16px, 3vw, 40px);
  padding-right: clamp(16px, 3vw, 40px);
}

h1, h2, h3, h4, h5, h6 {
  font-family: var(--ty-sans);
  color: var(--ty-text);
  letter-spacing: -0.01em;
  font-weight: 600;
}
h1 { font-size: 1.5rem; }
h2 { font-size: 1.15rem; }
h3 { font-size: 1rem; }

a { color: var(--ty-accent); text-decoration: none; }
a:hover, a:focus-visible { color: var(--ty-accent-strong); text-decoration: underline; }

hr { border-color: var(--ty-border); opacity: 1; }

code, pre, kbd, samp { font-family: var(--ty-mono); }

/* Visible focus, always - removing focus rings is the commonest restyle regression. */
:focus-visible {
  outline: 2px solid var(--ty-accent);
  outline-offset: 2px;
  border-radius: 3px;
}

::selection { background: rgba(110, 180, 255, 0.28); color: var(--ty-text); }

/* -------------------------------------------------------------- masthead -- */

.ty-masthead {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px 20px;
  padding: 18px 0 14px;
}

.ty-brand {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  color: var(--ty-text);
}
.ty-brand:hover, .ty-brand:focus-visible { color: var(--ty-text); text-decoration: none; }

.ty-mark { width: 36px; height: 36px; display: block; flex: none; }

.ty-wordmark {
  font-size: 1.65rem;
  line-height: 1;
  letter-spacing: -0.025em;
  white-space: nowrap;
}
.ty-word-the { font-weight: 400; color: var(--ty-text-muted); }
.ty-word-yield { font-weight: 600; color: var(--ty-text); }

.ty-tagline {
  color: var(--ty-text-dim);
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding-left: 20px;
  border-left: 1px solid var(--ty-border-2);
  line-height: 1.3;
}

.ty-header-link {
  margin-left: auto;
  font-size: 0.82rem;
  color: var(--ty-text-muted);
  border: 1px solid var(--ty-border);
  border-radius: 999px;
  padding: 5px 12px;
  white-space: nowrap;
}
.ty-header-link:hover, .ty-header-link:focus-visible {
  color: var(--ty-text);
  border-color: var(--ty-border-2);
  text-decoration: none;
}

@media (max-width: 640px) {
  .ty-masthead { padding: 14px 0 10px; gap: 8px 12px; }
  .ty-wordmark { font-size: 1.4rem; }
  .ty-mark { width: 30px; height: 30px; }
  .ty-tagline { flex-basis: 100%; order: 3; padding-left: 0; border-left: 0; }
  .ty-header-link { margin-left: auto; }
}

/* --------------------------------------------------------------- tab bar -- */
/* Sticky: the Curve and Namibia tabs are long, and losing the navigation after
   one screen of scrolling is the commonest complaint about tabbed pages. */

.ty-tabs > .nav-tabs {
  position: sticky;
  top: 0;
  z-index: 6;
  gap: 2px;
  margin-bottom: 20px;
  background: var(--ty-bg);
  border-bottom: 1px solid var(--ty-border);
  flex-wrap: nowrap;
  overflow-x: auto;
  scrollbar-width: none;
}
.ty-tabs > .nav-tabs::-webkit-scrollbar { display: none; }

.nav-tabs .nav-link {
  color: var(--ty-text-muted);
  background: transparent;
  border: 0;
  border-bottom: 2px solid transparent;
  border-radius: 0;
  padding: 11px 16px 9px;
  font-size: 0.9rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  white-space: nowrap;
}
.nav-tabs .nav-link:hover { color: var(--ty-text); border-bottom-color: var(--ty-border-2); }
.nav-tabs .nav-link.active {
  background: transparent;
  color: var(--ty-text);
  font-weight: 600;
  border-bottom-color: var(--ty-brand);
}

/* Bootstrap keeps inactive panes mounted and hides them; that is what lets the
   interval callbacks keep working. Nothing may leak visually. */
.tab-content { background: transparent; border: 0; }
.tab-content > .tab-pane { padding-top: 2px; }
.tab-content > .tab-pane:not(.active) { display: none; }
.tab-content > .tab-pane > br { display: none; }   /* <br> was doing spacing's job */
.tab-content > .tab-pane > hr { margin: 20px 0; }

@media (max-width: 768px) {
  .nav-tabs .nav-link { padding: 10px 12px 8px; font-size: 0.86rem; }
  .ty-tabs > .nav-tabs { margin-bottom: 14px; }
}

/* ------------------------------------------------------ section headings -- */

.ty-section-head { margin: 4px 0 18px; }
.ty-section-title {
  font-size: 1.15rem;
  font-weight: 600;
  margin: 0 0 4px;
  color: var(--ty-text);
}
.ty-section-note { color: var(--ty-text-muted); font-size: 0.88rem; margin: 0; max-width: 90ch; }

.tab-content > .tab-pane > h1 { font-size: 1.3rem; margin: 6px 0 2px; }
.tab-content > .tab-pane > h2 { font-size: 1.15rem; margin: 6px 0 12px; }
.tab-content > .tab-pane > h3 {
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ty-brand);
  margin: 4px 0 0;
}

/* ---------------------------------------------------------------- tables -- */

.table {
  --bs-table-bg: transparent;
  --bs-table-hover-bg: transparent;
  background: var(--ty-surface);
  border: 1px solid var(--ty-border);
  border-radius: var(--ty-radius);
  border-collapse: separate;
  border-spacing: 0;
  overflow: hidden;
  color: var(--ty-text);
  margin-bottom: 24px;
  font-size: 0.9rem;
}

/* SLATE ships a heavy grid; one hairline between rows is all the eye needs. */
.table > :not(caption) > * > * {
  border: 0;
  border-bottom: 1px solid var(--ty-border);
  padding: 0.5rem 0.75rem;
  background: transparent;
  color: inherit;
  vertical-align: middle;
  box-shadow: none;
}
.table > tbody > tr:last-child > * { border-bottom: 0; }

.table > thead > tr > th {
  background: var(--ty-surface-2);
  color: var(--ty-text-muted);
  font-size: 0.69rem;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  white-space: nowrap;
  border-bottom: 1px solid var(--ty-border-2);
}
.table > thead > tr > th:first-child { color: var(--ty-brand); }

/* Numeric columns right-aligned with tabular lining figures, so decimal points
   stack - the single thing that makes a rate table look deliberate. */
.table > thead > tr > th:not(:first-child),
.table > tbody > tr > td:not(:first-child) {
  text-align: right;
  font-variant-numeric: tabular-nums lining-nums;
}
.table > tbody > tr > td:first-child { font-weight: 500; white-space: nowrap; }

.table > tbody > tr:hover > td { background: rgba(110, 180, 255, 0.05); }

.ty-num { text-align: right; font-variant-numeric: tabular-nums lining-nums; }

/* Sparklines (inline SVG drawn by rates_series.py) and the term under a bond code.
   The cell takes only the width its image needs, so the numbers keep theirs - and
   the side padding is trimmed, because the bond table gained a Prior close column
   on 2026-09-26 and at 692px of grid the default 0.75rem put the 3M line past the
   card's edge. Measured, not guessed: 767px of table in a 692px column. */
.ty-spark-cell {
  width: 1%;
  padding: 0.25rem 0.4rem !important;
}
.ty-spark { display: block; width: 64px; height: 22px; margin-left: auto; }
.ty-sub {
  display: block;
  font-size: 0.7rem;
  font-weight: 400;
  line-height: 1.15;
  color: var(--ty-text-dim);
  font-variant-numeric: tabular-nums;
}
/* Rate moves. --ty-up is the colour for a PRICE that rose, --ty-down for one
   that fell - so on a yield, which moves against its price, .ty-up is applied to
   a FALL. rates_series.move_sign() decides; nothing chooses these classes by the
   sign of the printed number alone.
                                                                      2026-09-26
   THE td SELECTOR IS LOAD-BEARING. `.table > :not(caption) > * > *` above sets
   `color: inherit` at specificity (0,1,1); a bare `.ty-up` is (0,1,0) and loses.
   So from the day this theme shipped (2026-09-13) until today, not one change in
   not one table was ever coloured: every cell rendered in plain --ty-text while
   carrying the class that was supposed to colour it. Confirmed against the live
   site by reading the browser's own cascade, after the computed colour of a cell
   marked .ty-up came back as rgb(230,235,242).

   It went unseen because every check - the browser probes, the unit tests, the
   contrast gate - looked at the class NAME on the cell, which was always right.
   The probe now reads getComputedStyle(td).color instead. */
.table td.ty-up, .ty-up { color: var(--ty-up); }
.table td.ty-down, .ty-down { color: var(--ty-down); }
.table td.ty-flat, .ty-flat { color: var(--ty-text-dim); }

/* Two-row table head: the group row names the SOURCE of each column, the row
   below it names the measure. It exists because the source used to live only in
   a title= attribute, and a touch screen has no hover. */
.ty-group-head > th {
  background: var(--ty-surface-2);
  color: var(--ty-text-dim) !important;
  font-family: var(--ty-mono);
  font-size: 0.65rem;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  text-align: center;
  white-space: normal;
  padding-bottom: 0.15rem !important;
  border-bottom: 0;
}
.ty-group-head > th:first-child { border-right: 0; }
.ty-group-head + tr > th { padding-top: 0.15rem !important; }

/* The published quote in a row of converted rates: weight, not colour. */
.ty-native strong { font-weight: 700; color: var(--ty-text); }

.ty-next-mpc { color: var(--ty-brand); font-weight: 600; }

/* ------------------------------------------- Rates tab: instrument history -- */

/* A row you can click.
                                                                      2026-09-26
   THE `.table > tbody >` PREFIX IS LOAD-BEARING, for the second time today. The
   site-wide row hover at the top of this file is `.table > tbody > tr:hover > td`
   at specificity (0,2,3); a bare `.ty-pickable:hover > td` is (0,2,1) and loses,
   so the pickable tint rendered as the ordinary 0.05 one and hover could not tell
   a clickable row from any other. Measured in Chromium against this stylesheet,
   after waiting out the 140 ms background transition: pickable row and plain note
   row both computed rgba(110,180,255,0.05). Exactly the `.ty-up` defect found
   this morning, one selector later.

   And because hover and a cursor reach neither touch nor keyboard, the affordance
   cannot rest on them: the first cell carries a permanent accent rule, and the
   chip row under the chart is the reachable control. */
.ty-pickable { cursor: pointer; }
.table > tbody > tr.ty-pickable:hover > td { background: rgba(110, 180, 255, 0.11); }
.table > tbody > tr.ty-pickable:active > td { background: rgba(110, 180, 255, 0.17); }
.table > tbody > tr.ty-pickable > td:first-child {
  box-shadow: inset 2px 0 0 var(--ty-border-2);
}
.table > tbody > tr.ty-pickable:hover > td:first-child {
  box-shadow: inset 2px 0 0 var(--ty-accent);
}

/* The bridge a row click forwards to. Off-screen rather than display:none, so a
   programmatic .click() still reaches React's synthetic handler. */
.ty-hidden-bridge {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  border: 0;
  opacity: 0;
  pointer-events: none;
}

/* rbond's own JSE close, printed under the reference the change is measured
   from. Dimmer than the tag above it: it is context, not the number in play. */
.ty-jse { color: var(--ty-text-dim); font-family: var(--ty-mono); font-size: 0.66rem; }

/* The instrument picker is a RadioItems, not a Checklist: exactly one is chosen.
   The chip's "+" prefix is the ADD-ANOTHER vocabulary of the Curve tab's
   multi-select series chips, and on a single choice it promises something the
   control cannot do. A radio dot says "one of these" instead. */
.ty-chips-one .ty-chip::before {
  content: "\25cb";
  color: var(--ty-text-dim);
  font-weight: 400;
}
.ty-chips-one .ty-chip:has(.ty-chip-input:checked)::before { content: "\25cf"; }

.ty-instrument { margin-top: 24px; }
/* The curve-shape row (2026-11-03) sits between the bond row and the instrument
   panel, on the same 24px section step. The step is on the static container so
   the row inside keeps Bootstrap's own gutter arithmetic. */
.ty-curve-shape { margin-top: 24px; }
.ty-instrument .ty-chart-card { margin-bottom: 10px; }
#rates-history { height: 340px; }

/* The readout under the chart. Wraps to as many rows as it needs; never scrolls. */
.ty-stat-strip {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 32px;
  padding: 12px 2px 2px;
  border-top: 1px solid var(--ty-border);
  margin-top: 10px;
}
.ty-stat { display: flex; flex-direction: column; min-width: 0; }
.ty-stat-label {
  font-size: 0.66rem;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ty-text-dim);
}
.ty-stat-value {
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--ty-text);
  font-variant-numeric: tabular-nums lining-nums;
  line-height: 1.25;
}
.ty-stat-sub { font-size: 0.72rem; color: var(--ty-text-muted); }

@media (max-width: 640px) {
  #rates-history { height: 300px; }
  .ty-stat-strip { gap: 10px 20px; }
  .ty-stat-value { font-size: 0.95rem; }
}

/* Provenance: small, monospaced, present but never competing with the data -
   and it makes staleness visible in public rather than hiding it. */
.ty-source-row > td, .ty-source {
  color: var(--ty-text-dim);
  font-family: var(--ty-mono);
  font-size: 0.72rem;
  letter-spacing: 0;
  text-align: left !important;       /* beats the numeric right-align rule */
  font-weight: 400 !important;
  white-space: normal !important;
}
.ty-source-row > td { background: rgba(255, 255, 255, 0.012); }
.ty-convention-note { line-height: 1.55; }

.ty-unavailable {
  color: var(--ty-text-muted);
  font-style: italic;
  text-align: center !important;
  padding: 1.1rem !important;
}

/* A dense table scrolls inside its own column whenever it does not fit; the page
   itself never scrolls sideways.
                                                                      2026-09-26
   The scroll used to be `display: block` on the table below 768px only, so
   between 769px and about 1435px - a tablet, a small laptop, a half-screen
   window - a table that did not fit was silently CLIPPED by the card, with no
   scrollbar and nothing to say a column was missing. Measured at 1024px on the
   live site before this change: the bond table wanted 608px of a 493px column,
   the T-bills 596 and the Retail Savings Bonds 497. Three of five tables were
   losing their right-hand column to anyone not on a wide desktop.

   The scroll now lives on a WRAPPER at every width, the same one the Curve tab's
   spreads table already used, and the table itself stays `display: table`. That
   matters: `display: block` on the table stops its rows stretching to the card,
   so a narrow table drew its header tint across only part of its own width and
   left a step down the right-hand side. */
@media (max-width: 768px) {
  body { font-size: 14px; }
  .table { white-space: nowrap; }
  /* Every table on this site is inside a wrapper, so this needs no fallback -
     and the browser probe asserts that invariant at every width, because a table
     added later without one would lose its scroll silently. */
}

/* ---------------------------------------------------- the live feed rows -- */
/* The "Live Pricing" and "UTC Timestamp" rows carry a dbc.Spinner as a
   freshness pulse; keep it small and quiet. */
.table .spinner-grow {
  width: 0.7rem;
  height: 0.7rem;
  vertical-align: middle;
  background-color: var(--ty-live) !important;
}

/* --------------------------------------------------------------- spacing -- */

.row { --bs-gutter-x: 24px; --bs-gutter-y: 20px; }
/* The last card in a grid column carries no trailing gap. The scroll wrapper
   added on 2026-09-26 sits between the column and the table, so both depths
   are matched. */
.tab-content .row > [class^="col"] > div > .table:last-child,
.tab-content .row > [class*=" col"] > div > .table:last-child,
.tab-content .row > [class^="col"] > div > .ty-rates-wrap:last-child,
.tab-content .row > [class*=" col"] > div > .ty-rates-wrap:last-child { margin-bottom: 0; }

@media (max-width: 768px) {
  .row { --bs-gutter-x: 16px; --bs-gutter-y: 16px; }
  .table { margin-bottom: 16px; }
}

/* ------------------------------------------------------ inputs & pickers -- */
/* Dash 2.6 renders dcc.Input as a bare <input>, and DatePickerSingle and
   Dropdown as light widgets. All of them are restyled here, or the calculators
   show white boxes on a dark page. */

.form-control, .form-select,
.tab-pane input[type="number"], .tab-pane input[type="text"] {
  background: var(--ty-surface-2);
  border: 1px solid var(--ty-border-2);
  color: var(--ty-text);
  border-radius: var(--ty-radius);
  padding: 0.42rem 0.65rem;
  font-family: var(--ty-sans);
  font-variant-numeric: tabular-nums;
  min-width: 200px;
}
.form-control:focus, .form-select:focus,
.tab-pane input[type="number"]:focus, .tab-pane input[type="text"]:focus {
  background: var(--ty-surface-2);
  border-color: var(--ty-accent);
  box-shadow: 0 0 0 3px rgba(110, 180, 255, 0.2);
  color: var(--ty-text);
  outline: none;
}
.form-control::placeholder, .tab-pane input::placeholder { color: var(--ty-text-dim); }

/* dcc.Dropdown (react-select v1 in dcc 2.6) */
.Select-control {
  background-color: var(--ty-surface-2) !important;
  border: 1px solid var(--ty-border-2) !important;
  border-radius: var(--ty-radius) !important;
  color: var(--ty-text) !important;
}
.is-focused:not(.is-open) > .Select-control { box-shadow: 0 0 0 3px rgba(110, 180, 255, 0.2) !important; border-color: var(--ty-accent) !important; }
.Select-placeholder, .Select--single > .Select-control .Select-value,
.Select-value-label, .Select-input > input { color: var(--ty-text) !important; }
.Select-arrow { border-top-color: var(--ty-text-muted) !important; }
.Select-menu-outer {
  background: var(--ty-surface-2) !important;
  border: 1px solid var(--ty-border-2) !important;
  border-radius: 0 0 var(--ty-radius) var(--ty-radius) !important;
  z-index: 20 !important;
}
.Select-option, .VirtualizedSelectOption {
  background-color: var(--ty-surface-2) !important;
  color: var(--ty-text) !important;
}
.Select-option.is-focused, .VirtualizedSelectFocusedOption {
  background-color: var(--ty-surface-3) !important;
  color: var(--ty-text) !important;
}

/* dcc.DatePickerSingle (react-dates) */
.SingleDatePickerInput, .SingleDatePickerInput__withBorder, .DateInput {
  background: var(--ty-surface-2) !important;
  border-color: var(--ty-border-2) !important;
  border-radius: var(--ty-radius);
}
.DateInput_input {
  background: var(--ty-surface-2) !important;
  color: var(--ty-text) !important;
  font-family: var(--ty-sans) !important;
  font-size: 0.95rem !important;
  font-variant-numeric: tabular-nums;
  border-bottom-color: transparent !important;
  padding: 7px 10px !important;
}
.DateInput_input__focused { border-bottom-color: var(--ty-accent) !important; }
.SingleDatePicker_picker, .DayPicker, .DayPicker__horizontal,
.CalendarMonthGrid, .CalendarMonth, .DayPickerKeyboardShortcuts_panel {
  background: var(--ty-surface-2) !important;
}
.SingleDatePicker_picker { border: 1px solid var(--ty-border-2); border-radius: var(--ty-radius); z-index: 30 !important; }
.CalendarMonth_caption, .DayPicker_weekHeader_li { color: var(--ty-text) !important; }
.DayPicker_weekHeader { color: var(--ty-text-muted) !important; }
.CalendarDay__default {
  background: var(--ty-surface-2) !important;
  border-color: var(--ty-border) !important;
  color: var(--ty-text) !important;
}
.CalendarDay__default:hover { background: var(--ty-surface-3) !important; }
.CalendarDay__selected, .CalendarDay__selected:hover {
  background: var(--ty-brand) !important;
  border-color: var(--ty-brand) !important;
  color: var(--ty-on-brand) !important;
}
.CalendarDay__blocked_out_of_range { color: var(--ty-text-dim) !important; }
.DayPickerNavigation_button__default {
  background: var(--ty-surface-3) !important;
  border-color: var(--ty-border-2) !important;
}
.DayPickerNavigation_svg__horizontal { fill: var(--ty-text) !important; }
.DateInput_fang { display: none; }

/* ------------------------------------------------------------ buttons -- */

.btn-primary, .btn-primary:disabled {
  background: var(--ty-brand);
  border-color: var(--ty-brand);
  color: var(--ty-on-brand);
  font-weight: 600;
  letter-spacing: 0.01em;
  border-radius: var(--ty-radius);
  padding: 0.5rem 1rem;
}
.btn-primary:hover, .btn-primary:focus, .btn-primary:active {
  background: var(--ty-brand-strong) !important;
  border-color: var(--ty-brand-strong) !important;
  color: var(--ty-on-brand) !important;
}

/* --------------------------------------------------------- calculators -- */
/* The SA Bonds and Namibia calculators are H4 field labels, H5 ticket
   sections and <span> outputs. Styled as a form and a trade ticket. */

.tab-pane h4 {
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ty-text-muted);
  margin: 14px 0 6px;
}
.tab-pane h5 {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ty-brand);
  margin: 14px 0 6px;
}
.tab-pane h5 + hr { margin: 4px 0 8px; }

.tab-pane span[id$="-output-div"], .tab-pane span[id$="-output"] {
  font-family: var(--ty-mono);
  font-variant-numeric: tabular-nums;
  color: var(--ty-text);
  font-weight: 500;
}

.alert-warning, .ty-ticket-alert {
  background: rgba(226, 177, 85, 0.07) !important;
  border: 1px solid rgba(226, 177, 85, 0.28) !important;
  border-left: 3px solid var(--ty-brand) !important;
  color: var(--ty-text) !important;
  border-radius: var(--ty-radius);
  font-size: 0.92rem !important;
  font-weight: 500 !important;
  white-space: pre-line;
}

/* --------------------------------------------------------------- API tab -- */

.ty-api { margin-top: 4px; }
.ty-api > hr:first-child { display: none; }
.ty-api > h2 { font-size: 1.15rem; margin-bottom: 4px; }
.ty-api-lede { color: var(--ty-text-muted); max-width: 70ch; margin-bottom: 16px; }
.ty-api-desc { color: var(--ty-text-muted); }

code {
  font-size: 0.85em;
  color: var(--ty-accent);
  background: rgba(110, 180, 255, 0.08);
  border: 1px solid rgba(110, 180, 255, 0.16);
  border-radius: 4px;
  padding: 0.08em 0.36em;
  white-space: nowrap;
}

.ty-code {
  font-size: 0.78rem;
  line-height: 1.55;
  color: var(--ty-text);
  background: var(--ty-surface);
  border: 1px solid var(--ty-border);
  border-radius: var(--ty-radius);
  padding: 14px 16px;
  margin: 0;
  overflow-x: auto;
  white-space: pre;
  tab-size: 2;
}
.ty-code code { background: none; border: 0; padding: 0; white-space: pre; color: inherit; }

.ty-api-note {
  color: var(--ty-text-muted);
  border-left: 3px solid var(--ty-brand);
  background: rgba(226, 177, 85, 0.06);
  padding: 10px 14px;
  margin-top: 16px;
  border-radius: 0 var(--ty-radius) var(--ty-radius) 0;
  max-width: 80ch;
}
.ty-api-note strong { color: var(--ty-text); }

/* In the API table the second column is prose; right-aligning a sentence reads
   as a mistake. */
.ty-api .table > thead > tr > th:not(:first-child),
.ty-api .table > tbody > tr > td:not(:first-child),
.ty-api-desc { text-align: left; }
.ty-api .table > tbody > tr > td:first-child { width: 1%; }

/* ------------------------------------------------------------- Curve tab -- */

.ty-curve { padding-bottom: 16px; }

.ty-chart-card {
  background: var(--ty-surface);
  border: 1px solid var(--ty-border);
  border-radius: var(--ty-radius);
  padding: 16px 18px 10px;
  margin-bottom: 20px;
}

.ty-chart-head { margin-bottom: 10px; }
.ty-chart-title {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--ty-text);
  margin: 0 0 2px;
  letter-spacing: 0;
}
.ty-chart-note { color: var(--ty-text-muted); font-size: 0.84rem; margin: 0; max-width: 95ch; }

.ty-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 22px;
  margin: 6px 0 4px;
}
.ty-ctl { display: flex; align-items: center; flex-wrap: wrap; gap: 6px 10px; }
.ty-ctl-label {
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ty-text-dim);
}

/* One range control scopes both time-series charts, so it sits directly above
   them. Not sticky: pinned, it floated over the curve, heatmap and animation
   charts it does not control. */
.ty-range-bar {
  margin: 0 0 12px;
  padding: 2px 0;
}

.ty-reset {
  font: inherit;
  font-size: 0.8rem;
  font-weight: 500;
  color: var(--ty-text-muted);
  background: var(--ty-surface);
  border: 1px solid var(--ty-border-2);
  border-radius: var(--ty-radius);
  padding: 4px 11px;
  cursor: pointer;
}
.ty-reset:hover { color: var(--ty-text); background: var(--ty-surface-2); }

/* Chart heights live here. The graphs are dcc.Graph(responsive=True) and fill
   their containers, and .plot-container must pass the height down or Plotly's
   inner height:100% chain resolves to 0px. A height inside the figure instead was
   wiped by dcc's autosize relayout on resize and collapsed the plots (measured
   2026-09-14; this arrangement held in every run). */
#curve-shape { height: 470px; }
#hist-graph, #spread-graph { height: 400px; }
#moves-graph { height: 360px; }
#anim-graph { height: 480px; }
.ty-graph .js-plotly-plot, .ty-graph .js-plotly-plot .plot-container { height: 100%; }

/* Segmented control (dcc.RadioItems). The native radio stays in the DOM and
   focusable; only its dot is hidden. Selection is weight + fill, not colour. */
.ty-seg {
  display: inline-flex;
  flex-wrap: wrap;
  border: 1px solid var(--ty-border-2);
  border-radius: var(--ty-radius);
  overflow: hidden;
  background: var(--ty-surface);
}
.ty-seg-option {
  position: relative;
  display: inline-flex !important;
  align-items: center;
  margin: 0 !important;
  padding: 4px 11px;
  font-size: 0.8rem;
  font-weight: 500;
  color: var(--ty-text-muted);
  cursor: pointer;
  user-select: none;
  border-left: 1px solid var(--ty-border);
  font-variant-numeric: tabular-nums;
}
.ty-seg-option:first-child { border-left: 0; }
.ty-seg-option:hover { color: var(--ty-text); background: var(--ty-surface-2); }
.ty-seg-input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; }
.ty-seg-option:has(.ty-seg-input:checked) {
  background: var(--ty-surface-3);
  color: var(--ty-text);
  font-weight: 700;
  box-shadow: inset 0 -2px 0 var(--ty-brand);
}
.ty-seg-option:has(.ty-seg-input:focus-visible) { outline: 2px solid var(--ty-accent); outline-offset: -2px; }

/* Chips (dcc.Checklist). A checked chip gains a tick and weight. */
.ty-chips { display: inline-flex; flex-wrap: wrap; gap: 6px; }
.ty-chip {
  position: relative;
  display: inline-flex !important;
  align-items: center;
  gap: 5px;
  margin: 0 !important;
  padding: 3px 10px;
  font-size: 0.8rem;
  color: var(--ty-text-muted);
  border: 1px solid var(--ty-border-2);
  border-radius: 999px;
  cursor: pointer;
  user-select: none;
  background: var(--ty-surface);
}
.ty-chip:hover { color: var(--ty-text); background: var(--ty-surface-2); }
.ty-chip-input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; }
.ty-chip::before { content: "+"; font-weight: 600; color: var(--ty-text-dim); width: 0.7em; text-align: center; }
.ty-chip:has(.ty-chip-input:checked) {
  color: var(--ty-text);
  font-weight: 600;
  background: var(--ty-surface-3);
  border-color: var(--ty-text-muted);
}
.ty-chip:has(.ty-chip-input:checked)::before { content: "\2713"; color: var(--ty-brand); }
.ty-chip:has(.ty-chip-input:focus-visible) { outline: 2px solid var(--ty-accent); outline-offset: 2px; }

.ty-graph { margin: 0 -6px; }

/* The spread statistics table - the table view of the spreads chart. */
/* The one place a table is allowed to scroll. A table is `width: 100%`, so it
   fills this when it fits and overflows it - scrolling, not clipped - when it
   does not. `.ty-rates-wrap` is the same thing around the Rates tab's tables,
   which are returned straight from callbacks. */
.ty-table-wrap, .ty-rates-wrap { overflow-x: auto; }
.ty-table-wrap { margin: 8px 0 6px; }
.ty-rates-wrap { margin-bottom: 24px; }
.ty-rates-wrap > .table { margin-bottom: 0; }

/* A slim, always-visible bar when there IS something to scroll to. Overlay
   scrollbars only appear once you are already scrolling, which is no use as the
   signal that a column is off the right-hand edge. */
.ty-table-wrap, .ty-rates-wrap {
  scrollbar-width: thin;
  scrollbar-color: var(--ty-border-2) transparent;
}
.ty-table-wrap::-webkit-scrollbar, .ty-rates-wrap::-webkit-scrollbar { height: 7px; }
.ty-table-wrap::-webkit-scrollbar-track,
.ty-rates-wrap::-webkit-scrollbar-track { background: transparent; }
.ty-table-wrap::-webkit-scrollbar-thumb, .ty-rates-wrap::-webkit-scrollbar-thumb {
  background: var(--ty-border-2);
  border-radius: 4px;
}
.ty-stats { margin-bottom: 0; font-size: 0.82rem; border-color: var(--ty-border); }
.ty-stats > :not(caption) > * > * { padding: 0.36rem 0.6rem; }
.ty-stats td, .ty-stats th { white-space: nowrap; }
.ty-spread-name { display: flex; align-items: center; gap: 8px; }
.ty-swatch {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 2px;
  flex: none;
}
.ty-swatch-off { border: 1px solid var(--ty-border-2); }

.ty-curve-foot { margin-top: 8px; display: block; }

@media (max-width: 640px) {
  .ty-chart-card { padding: 12px 10px 6px; }
  .ty-controls { gap: 8px 14px; }
  .ty-seg-option { padding: 4px 8px; font-size: 0.76rem; }
  #curve-shape { height: 400px; }
  #hist-graph, #spread-graph { height: 340px; }
  #moves-graph { height: 320px; }
  #anim-graph { height: 420px; }
}

/* ----------------------------------------------------------------- motion -- */

.nav-tabs .nav-link, a, .btn, .ty-seg-option, .ty-chip,
.table > tbody > tr > td { transition: color 140ms ease-out, background-color 140ms ease-out, border-color 140ms ease-out; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}
