/*
 * Stalewatch — the dashboard's own components
 * ===========================================
 * Design §06.1: the status strip, the table head with its sort control, the sparkline column, the
 * snapshot line and the subscribe block. Numbered 45 so it lands after the primitives it composes
 * (40) and before the charts (50). See the numbering note in 00-tokens.css.
 */

/* --------------------------------------------------------------- Status strip -------- */
/*
 * DARK GROUND, deliberately. The design's rule is LOUD ABOUT OURSELVES, QUIET ABOUT THE MARKET, and
 * two of these four cells are about us: whether the chain could be traded on, and whether our own
 * pollers are current. `.panel` rebinds the semantic aliases (20-ground.css), so every child here
 * is written once against --fg / --rule and is correct on this ground with no override.
 */
.strip { border-bottom: 1px solid var(--rule); }
.strip__inner {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
}
.strip__cell {
  padding: 13px 24px;
  border-right: 1px solid var(--rule);
}
.strip__cell:first-child { padding-left: 0; }
.strip__cell:last-child { border-right: 0; }
.strip__cell .label { margin-bottom: 9px; }
.strip__cell .datum { font-size: 15px; font-weight: 500; }

/*
 * The ingestion cell is the only one that can read as a FAULT, because it is the only one about us.
 * A widening divergence gets no such treatment: that is the market doing its job.
 */
.strip__cell--fault .datum { color: var(--breach-ink); }

@media (max-width: 720px) {
  .strip__inner { grid-template-columns: repeat(2, 1fr); }
  .strip__cell { border-bottom: 1px solid var(--rule); padding-left: 0; padding-right: 16px; }
  .strip__cell:nth-child(2n) { border-right: 0; }
}

/* --------------------------------------------------------------- Table head -------- */
.table-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--s4);
  flex-wrap: wrap;
  margin-bottom: var(--s4);
}
.table-head__title { font-size: 34px; font-weight: 600; letter-spacing: -0.02em; margin: 0; }
.table-head__sub { margin: 8px 0 0; color: var(--fg-4); }

/* The sort control. Current option is the only one carrying ink at full strength; the rest are
   labels. No underline, because a row of four underlined words reads as a sentence. */
.sort { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.sort__label { margin-right: 4px; color: var(--fg-4); }
.sort__opt {
  font: 500 9.5px/1 var(--font-mono);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 7px 9px;
  color: var(--fg-4);
  border: 1px solid transparent;
  text-decoration: none;
}
.sort__opt:hover { color: var(--fg-2); }
.sort__opt--on { color: var(--accent-ink); border-color: var(--accent); }

/* --------------------------------------------------------------- Instrument table -------- */
.rows--instrument td, .rows--instrument th { padding-top: 9px; padding-bottom: 9px; }
.rows__sub {
  display: block;
  font: 400 11px/1.3 var(--font-sans);
  letter-spacing: 0;
  text-transform: none;
  color: var(--fg-4);
  margin-top: 3px;
}
.rows__spark { width: 118px; text-align: right; }

/*
 * THE SPARKLINE. One per row, all drawn on the SAME fixed ±200 bps scale — that is what makes the
 * column comparable down its length. A row-fitted line would make a 5 bps month and a 200 bps month
 * look identical, which is the opposite of what the column is for.
 *
 * The stroke repeats what the divergence chip already said rather than adding a claim: neutral for
 * a month spent inside the band, breach hue for one that crossed it.
 */
.spark { display: block; width: 110px; height: 24px; margin-left: auto; }
.spark polyline { stroke: var(--fg-4); stroke-width: 1.25; }
.spark--breach polyline { stroke: var(--breach); }

/* --------------------------------------------------------------- Snapshot -------- */
/*
 * The formula, the venues and the instant. A reader who screenshots a row has to be able to
 * reproduce it, and a snapshot with no timestamp is not reproducible.
 */
.snapshot {
  display: flex;
  align-items: center;
  gap: var(--s4);
  flex-wrap: wrap;
  margin-top: var(--s4);
  padding-top: var(--s3);
  border-top: 1px solid var(--rule-soft);
}
/*
 * `max-width: none` overrides the global prose measure on `p` (10-base.css). That measure exists so
 * an argument stays readable at about 68 characters; this is not an argument, it is the formula and
 * the timestamp for the table directly above, and it should run the width of what it describes.
 * Capped, it wrapped to three lines in the left third of the row with the rest of the row empty.
 */
.snapshot__line { margin: 0; flex: 1 1 auto; max-width: none; color: var(--fg-4); line-height: 1.6; }
.snapshot__line time { color: var(--fg-3); }

/* --------------------------------------------------------------- Subscribe -------- */
.subscribe {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s5);
  flex-wrap: wrap;
  border: 1px solid var(--rule-firm);
  padding: var(--s4);
}
.subscribe__title { font-size: 19px; font-weight: 600; letter-spacing: -0.01em; margin: 0; }
.subscribe__sub { margin: 6px 0 0; color: var(--fg-3); font-size: 14px; max-width: 52ch; }
.subscribe__form { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 0; }
.subscribe__input { min-width: 260px; margin: 0; }
.subscribe__more { flex-basis: 100%; margin: 0; color: var(--fg-4); font-size: 12px; }


/* --------------------------------------------------------------- Nav additions -------- */
.nav__brand { display: flex; align-items: baseline; gap: 9px; }

/*
 * THE MONITOR CHIP. It carries the LIVE hue as text, which is only legible because this bar is
 * always the dark panel — that is the ground the four signal hues were chosen against. On the light
 * page --live as text is about 1.9:1, which is why the mark/ink split exists everywhere else. The
 * chip never appears there.
 */
.nav__mode {
  font: 500 8.5px/1 var(--font-mono);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--live);
  border: 1px solid color-mix(in oklch, var(--live) 50%, transparent);
  padding: 3px 5px;
}

.nav__meta { display: flex; align-items: center; gap: 18px; margin-left: auto; }
.nav__clock {
  font: 500 12px/1 var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--fg-3);
}
.nav__alerts { white-space: nowrap; }

/* The session pill annotates and never states. It carries a mark and a word, and nothing else on
   any page is coloured by it. See app/services/analysis/market_calendar.rb. */
.pill__mark { width: 6px; height: 6px; background: var(--fg-4); flex: 0 0 auto; }
.pill--open .pill__mark { background: var(--inband); }
.pill--closed .pill__mark { background: var(--fg-5); }
.pill--open, .pill--closed { color: var(--fg-2); }

/* .pill is inline-flex, which beats the browser's own [hidden] rule, so hiding it needs saying.
   session_pill_controller.js hides the pill past the end of what config/market_calendar.yml
   covers, rather than asserting a session from a calendar that has never heard of the date. */
.pill[hidden] { display: none; }

/* Inside the nav the pill is a mark and a word with no box: the bar already frames it, and a border
   inside a bordered bar reads as a button somebody forgot to make clickable. */
.nav .pill { border: 0; padding: 0; font-size: 11px; letter-spacing: 0.1em; }

@media (max-width: 860px) {
  .nav__clock, .nav__mode { display: none; }
}

/* --------------------------------------------------------------- Ticker page -------- */
.ticker-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s4);
  flex-wrap: wrap;
  padding-bottom: var(--s4);
  border-bottom: 1px solid var(--rule);
}
.ticker-head__ident { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.ticker-head__ident h1 { margin: 0; font-size: 34px; letter-spacing: -0.02em; }
.ticker-head__name { color: var(--fg-3); font-size: 15px; }
.ticker-head__addrs {
  display: flex; gap: 14px; flex-wrap: wrap; margin: 10px 0 0;
  color: var(--fg-4); word-break: break-all;
}
.ticker-head__controls { display: flex; gap: 8px; flex-wrap: wrap; }
.is-current { border-color: var(--accent); color: var(--accent-ink); }

/*
 * THE HEADLINE STRIP. Five cells, equal width, hairline separated. Not a card grid: a card implies
 * each cell is an independent object, and these five are one reading of one instrument at one
 * moment.
 */
.headline {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  border: 1px solid var(--rule-firm);
  margin-top: var(--s4);
}
.headline__cell { padding: 16px 18px; border-right: 1px solid var(--rule-soft); }
.headline__cell:last-child { border-right: 0; }
.headline__cell .label { margin-bottom: 10px; }
.headline__cell .datum--lg { display: block; margin-bottom: 8px; }
.headline__note { font: 400 11px/1.4 var(--font-mono); color: var(--fg-4); }

@media (max-width: 980px) {
  .headline { grid-template-columns: repeat(2, 1fr); }
  .headline__cell { border-bottom: 1px solid var(--rule-soft); }
  .headline__cell:nth-child(2n) { border-right: 0; }
}

/* The chart's stat header. The two middle figures are the product's claim stated as a contrast, so
   they sit side by side and are never blended into one. */
.chart-head {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: var(--s4); flex-wrap: wrap; margin-bottom: var(--s3);
}
.chart-stats { display: flex; gap: var(--s5); flex-wrap: wrap; }
.chart-stats .label { margin-bottom: 7px; }
.chart-stats .datum { font-size: 17px; font-weight: 500; }
.datum--frozen { color: var(--frozen-ink); }
.datum--breach { color: var(--breach-ink); }

/* A caption states what the chart shows. It is COUNTED, never asserted: see the note in
   app/views/tickers/show.html.erb. */
.caption {
  margin: var(--s3) 0 0;
  color: var(--fg-3);
  font-size: 13px;
  line-height: 1.65;
  max-width: var(--measure);
}
.caption--filter { color: var(--fg-4); }

.split { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s5); }
@media (max-width: 980px) { .split { grid-template-columns: 1fr; } }

/*
 * THE SESSION CLOCK. Two states, derived from config/market_calendar.yml. The design draws five
 * segments; only the regular session is configured, and inventing pre- and post-market boundaries
 * would be a claim about the market nothing here has verified.
 */
.sessions { display: flex; height: 28px; border: 1px solid var(--rule-firm); margin-top: var(--s2); }
.sessions__seg { flex-basis: 0; }
.sessions__seg--open { background: color-mix(in oklch, var(--inband) 22%, transparent); }
.sessions__seg--closed { background: var(--panel-deep); }

/* --------------------------------------------------------------- State blocks -------- */
/*
 * LOUD ABOUT OURSELVES, QUIET ABOUT THE MARKET — the design's rule, expressed as GROUND.
 *
 * DEGRADED inverts to the dark panel because it is our fault, and it is the one thing on the page
 * a reader cannot verify against the chain themselves.
 *
 * PAUSED gets a light wash and no inversion. A paused token is the issuer doing something they are
 * supposed to do, noticed and handled. Treating it as an alarm would train readers to ignore the
 * alarm that matters.
 */
.state-block {
  display: flex;
  gap: 14px;
  align-items: flex-start;
  padding: 16px 18px;
  margin-bottom: var(--s4);
  border: 1px solid var(--rule-firm);
}
.state-block__mark { width: 6px; align-self: stretch; flex: 0 0 auto; }
.state-block__title { margin: 0 0 6px; font-size: 15px; font-weight: 600; }
.state-block__body { margin: 0 0 4px; font-size: 13px; line-height: 1.6; color: var(--fg-2); max-width: var(--measure); }
.state-block__body:last-child { margin-bottom: 0; }

.state-block--degraded { border-color: var(--panel-rule); }
.state-block--degraded .state-block__mark { background: var(--breach); }

.state-block--paused {
  background: color-mix(in oklch, var(--frozen) 10%, transparent);
  border-color: color-mix(in oklch, var(--frozen) 40%, transparent);
}
.state-block--paused .state-block__mark { background: var(--frozen); }

/* EMPTY is neither our fault nor the market's: it is the beginning. Quiet, and it carries a date
   rather than a promise, because "coming soon" is what a reader has learned to ignore. */
.state-block--empty { border-color: var(--rule); }
.state-block--empty .state-block__mark { background: var(--fg-5); }

/* The dollar register. A GLOSS on the basis-point figure beside it, never a headline of its own:
   dollars do not compare across tickers, and a reader who sees only the dollar figure cannot tell
   whether $5.82 on a $310 share is normal. PRD §10 requires both, always, with no toggle. */
.gloss { font: 400 11px/1.4 var(--font-mono); font-variant-numeric: tabular-nums; color: var(--fg-4); }

/*
 * THE CONFIDENCE GRADE. Renders only when it is NOT high: a flag on every row trains the eye to
 * skip the column, and the rows worth flagging are the ones that are not high.
 *
 * UNMEASURED is not a low grade. A feed with no pool has no divergence to be confident about, and
 * treating its absent number as a bad measurement would imply we tried and did badly.
 */
.conf {
  font: 500 8.5px/1 var(--font-mono);
  letter-spacing: 0.11em;
  padding: 4px 6px;
  margin-left: 6px;
  vertical-align: middle;
}
.conf--medium { background: color-mix(in oklch, var(--frozen) 22%, transparent); color: var(--frozen-ink); }
.conf--low { background: color-mix(in oklch, var(--breach) 18%, transparent); color: var(--breach-ink); }
.conf--unmeasured { background: color-mix(in oklch, var(--fg-4) 12%, transparent); color: var(--neutral-ink); }
