/* TradeMade "deck" identity: HUD panels, module tags, telemetry tiles. Loads after styles.css. */

/* panels: cut corners + hairline gradient edge + module tag */
.panel { position: relative; border-radius: 6px; border: 1px solid transparent;
  background: linear-gradient(var(--panel), var(--panel)) padding-box,
              linear-gradient(135deg, color-mix(in srgb, var(--accent) 55%, transparent), color-mix(in srgb, var(--border) 60%, transparent) 40%, color-mix(in srgb, var(--accent2) 45%, transparent)) border-box;
  clip-path: polygon(14px 0, 100% 0, 100% calc(100% - 14px), calc(100% - 14px) 100%, 0 100%, 0 14px); }
.panel::after { content: ""; position: absolute; right: 0; bottom: 0; width: 14px; height: 14px;
  background: linear-gradient(135deg, transparent 50%, color-mix(in srgb, var(--accent) 70%, transparent) 50%); opacity: .8; }
.panel-title { font-family: var(--mono); font-size: .72rem; letter-spacing: .16em; text-transform: uppercase; color: var(--muted);
  margin: -4px 0 12px; padding-bottom: 8px; border-bottom: 1px dashed color-mix(in srgb, var(--border) 140%, transparent); display: flex; gap: 10px; align-items: baseline; }
.panel-title::before { content: "▮"; color: var(--accent); font-size: .6rem; margin-right: 2px; }
.panel-title .muted { text-transform: none; letter-spacing: 0; font-family: var(--font); font-size: .78rem; margin-left: auto; text-align: right; }
/* When a title carries BOTH a subtitle and a legend, two margin-left:auto rules
   compete and flexbox splits the free space between them, so neither lands where
   it was meant to - the subtitle floats mid-title and the legend stops short of
   the right edge. Only the last one pushes. */
.panel-title:has(.legend) .muted { margin-left: 0; text-align: left; }

/* header + session rail */
.top { border-bottom: none; }
.top::after { content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 1px;
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--accent) 70%, transparent), transparent); }
/* The sessions strip had 36px of content in a 40px box wedged against the header
   above and the panels below. It now has real vertical room, a taller row pitch,
   thicker bands and brighter labels so it reads as its own band of the page. */
.session-rail { position: relative; z-index: 1; max-width: 1400px; margin: 18px auto 16px; padding: 0 18px 0 104px; }
.session-rail .rail { position: relative; height: 76px; }
/* Row pitch is set in JS (i * ROW_PITCH) and has to clear the time labels, which
   sit ABOVE their own line and so hang into the row above. At a 16px pitch the
   New York 9:30 label landed on top of the London band. */
.srow-s { position: absolute; left: 0; right: 0; height: 14px; }
.slabel { position: absolute; left: -88px; top: 0; width: 80px; text-align: right; font-family: var(--mono);
  font-size: .66rem; letter-spacing: .1em; opacity: 1; font-weight: 600; }
.sband { position: absolute; top: 4px; height: 6px; border-radius: 3px; background: var(--c); opacity: .3; }
.sband.on { opacity: .85; box-shadow: 0 0 12px var(--c); }
.sline { position: absolute; top: 1px; width: 1px; height: 13px; background: var(--c); opacity: .95; }
/* The label carries the panel's own background so that even at a tight width,
   where two sessions' lines land close together, it is read over a line rather
   than through one. */
.sline span { position: absolute; top: -11px; left: 3px; padding: 0 2px; border-radius: 2px;
  background: var(--bg); font-family: var(--mono); font-size: .58rem;
  color: var(--c); opacity: .95; white-space: nowrap; }
.sline.close span { left: auto; right: 3px; }
.now-line { position: absolute; top: -6px; bottom: 2px; width: 2px; transform: translateX(-50%);
  background: var(--accent); box-shadow: 0 0 12px var(--accent); z-index: 2; }
.now-line::before { content: ""; position: absolute; top: -6px; left: 50%; width: 10px; height: 10px; border-radius: 50%;
  transform: translateX(-50%); background: radial-gradient(circle at 35% 35%, #fff, var(--accent) 55%, var(--accent2)); }
.sline span { font-size: .58rem; } .slabel { font-size: .62rem; }
/* The "now" tag under the line: phones only (below), so the desktop rail is unchanged. */
.now-tag { display: none; position: absolute; bottom: -4px; transform: translateX(-50%); padding: 0 4px; border-radius: 3px;
  background: var(--bg); color: var(--accent); font-family: var(--mono); font-size: .6rem; font-weight: 600; white-space: nowrap; z-index: 2; }
.now-tag.edge-l { transform: translateX(-6px); }
.now-tag.edge-r { transform: translateX(calc(-100% + 6px)); }
/* Phones: the times stay on (a little smaller), measured to fit a 320px screen
   without touching, and the now tag says what time it is and which session. */
@media (max-width: 700px) { .sline span { font-size: .52rem; padding: 0 1px; } .session-rail { padding-left: 76px; } .slabel { width: 66px; left: -74px; font-size: .6rem; }
  .now-tag { display: block; } }
/* buttons + tabs */
.btn { border-radius: 6px; letter-spacing: .01em; }
.btn.primary { background: linear-gradient(135deg, var(--accent), var(--accent2)); }
.tab { border-radius: 6px; font-family: var(--mono); font-size: .78rem; letter-spacing: .08em; text-transform: uppercase; }
.tab.active { background: linear-gradient(135deg, var(--accent), var(--accent2)); }
.pill { border-radius: 4px; }
.chip { border-radius: 4px; font-family: var(--mono); letter-spacing: .04em; }

/* telemetry tiles */
.card { border-radius: 6px; clip-path: polygon(8px 0, 100% 0, 100% calc(100% - 8px), calc(100% - 8px) 100%, 0 100%, 0 8px);
  background: linear-gradient(180deg, var(--panel2), transparent); }
.card .name { font-family: var(--mono); font-size: .7rem; letter-spacing: .08em; text-transform: uppercase; }
.card .spark { display: block; width: 100%; height: 30px; margin: 2px 0 4px; }
.card .spark path { fill: none; stroke-width: 1.4; stroke-linejoin: round; stroke-linecap: round; }
.card .spark .area { fill-opacity: .15; stroke: none; }
.card .px { font-size: 1.15rem; }
.zbar { height: 3px; }

/* log-style explanation */
.explain { list-style: none; padding-left: 0; font-family: var(--font); }
.explain li { position: relative; padding-left: 22px; margin-bottom: 8px; }
.explain li::before { content: "›"; position: absolute; left: 6px; top: -1px; color: var(--accent); font-weight: 700; }
.explain li:first-child { font-weight: 600; }

/* gauge label */
.gauge-label .big { font-size: 1.7rem; }
#gauge { shape-rendering: geometricPrecision; text-rendering: geometricPrecision; }
.meter-foot .pill { border-color: color-mix(in srgb, var(--accent) 30%, var(--border)); }

/* wire */
.item { border-radius: 4px; }
/* Breaking has to be findable at a glance on both surfaces, because that is
   the whole reason it is marked. A 1px border was not doing it. */
.item.breaking { border-left-width: 3px; border-left-color: var(--off);
  background: linear-gradient(90deg, color-mix(in srgb, var(--off) 9%, transparent), transparent 42%); }
.item.breaking .src::after { content: "BREAKING"; margin-left: 7px; font-family: var(--mono);
  font-size: .54rem; letter-spacing: .12em; color: var(--off);
  border: 1px solid color-mix(in srgb, var(--off) 55%, transparent); border-radius: 3px; padding: 1px 4px; }
.item .src { text-transform: uppercase; letter-spacing: .06em; }

/* verdict */
.verdict { border-radius: 4px; font-family: var(--font); }
.check-val { text-transform: uppercase; letter-spacing: .04em; font-size: .95rem; }

/* modals + chat inherit the cut corners */
.modal-box { border-radius: 6px; clip-path: polygon(14px 0, 100% 0, 100% calc(100% - 14px), calc(100% - 14px) 100%, 0 100%, 0 14px); }
.chat-head b { font-family: var(--mono); letter-spacing: .12em; text-transform: uppercase; font-size: .78rem; }

/* replay banner sits in its own strip at the very top and scrolls away; it never covers the page */
.replay { position: static !important; }


/* ---------- headline analysis table ---------- */
.hl-note { margin: -4px 0 12px; color: var(--muted); font-size: .84rem; max-width: 80ch; }
.hltable-wrap { overflow-x: auto; }
.hltable { min-width: 940px; display: flex; flex-direction: column; gap: 3px; }
.hl-row { display: grid; grid-template-columns: minmax(240px, 2.4fr) 118px repeat(8, minmax(72px, 1fr));
  align-items: center; gap: 6px; padding: 8px 10px; border-radius: 5px; text-decoration: none; color: var(--text); }
.hl-row:not(.hl-head) { background: var(--panel2); border: 1px solid transparent; }
.hl-row:not(.hl-head):hover { border-color: color-mix(in srgb, var(--accent) 45%, transparent); background: color-mix(in srgb, var(--accent) 7%, var(--panel2)); }
.hl-head { position: sticky; top: 0; background: var(--panel); font-family: var(--mono); font-size: .6rem;
  letter-spacing: .12em; text-transform: uppercase; color: var(--muted); padding-bottom: 6px;
  border-bottom: 1px dashed color-mix(in srgb, var(--border) 140%, transparent); border-radius: 0; }
.hl-head span + span { text-align: center; }
.hl-title { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.hl-title b { font-weight: 600; font-size: .9rem; line-height: 1.25; }
.hl-title small { color: var(--muted); font-size: .72rem; }
.hl-sig { display: flex; flex-direction: column; align-items: center; gap: 4px; font-family: var(--mono);
  font-size: .64rem; letter-spacing: .1em; color: var(--muted); }
.hl-sig.s-tailwind { color: var(--on); } .hl-sig.s-headwind { color: var(--off); } .hl-sig.s-neutral { color: var(--mixed); }
.sbar { display: block; width: 42px; height: 3px; border-radius: 2px; background: color-mix(in srgb, var(--border) 160%, transparent); }
.sbar b { display: block; height: 100%; border-radius: 2px; background: currentColor; }
.hl-cell { text-align: center; font-family: var(--mono); font-size: .68rem; letter-spacing: .06em; color: var(--dead); }
.hl-cell.up { color: var(--on); } .hl-cell.down { color: var(--off); }
.hl-cell.none { opacity: .35; }
@media (max-width: 700px) { .hl-row { grid-template-columns: minmax(180px, 2fr) 100px repeat(8, 68px); } }

/* ---------- site-wide alerts ---------- */
.alert-stack { position: fixed; top: 14px; right: 14px; z-index: 40; display: flex; flex-direction: column; gap: 8px; width: min(340px, calc(100vw - 28px)); pointer-events: none; }
.alert-card { position: relative; pointer-events: auto; cursor: pointer; overflow: hidden;
  background: var(--panel); border: 1px solid color-mix(in srgb, var(--accent) 40%, var(--border));
  border-left: 3px solid var(--accent); border-radius: 6px; padding: 10px 30px 12px 12px;
  opacity: 0; transform: translateX(14px); transition: opacity .28s ease, transform .28s ease;
  clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 10px); }
.alert-card.in { opacity: 1; transform: none; }
.alert-card.out { opacity: 0; transform: translateX(14px); }
/* Editor's notes ping in the top LEFT, sliding in from the left edge. Above the
   Trump stack's layer; app.js sets `top` so a note sits under the Trump posts. */
.note-stack { left: 14px; right: auto; z-index: 301; }
.note-stack .alert-card { transform: translateX(-14px); }
.note-stack .alert-card.in { transform: none; }
.note-stack .alert-card.out { transform: translateX(-14px); }   /* after .in: a closing card has both */
.alert-card.k-note { border-left-color: var(--accent2); }
.alert-card.k-note .ak { color: var(--accent2); }
.alert-card.k-note .atick { background: color-mix(in srgb, var(--accent2) 55%, transparent); }
.alert-card.k-breaking { border-left-color: var(--off); }
.alert-card.k-bias { border-left-color: var(--accent2); }
/* A revision is not breaking news and not a market read: it is the number you
   already had changing. Amber, the same colour the site uses for "both sides
   are true at once", so it reads as its own thing at a glance. */
.alert-card.k-revision { border-left-color: var(--mixed); }
.alert-card.k-revision .ak { color: var(--mixed); }
.alert-card.k-revision .atick { background: color-mix(in srgb, var(--mixed) 55%, transparent); }
.alert-card .ak { font-family: var(--mono); font-size: .58rem; letter-spacing: .16em; color: var(--accent); margin-bottom: 4px; }
.alert-card.k-breaking .ak { color: var(--off); }
.alert-card .at { font-size: .88rem; font-weight: 600; line-height: 1.3; }
.alert-card .ab { font-size: .78rem; color: var(--muted); margin-top: 3px; }
.alert-card .aa { display: flex; justify-content: space-between; gap: 8px; margin-top: 7px; font-family: var(--mono); font-size: .62rem; color: var(--muted); }
.alert-card .ax { position: absolute; top: 6px; right: 6px; background: none; border: 0; color: var(--muted); cursor: pointer; font-size: .8rem; line-height: 1; padding: 3px 5px; }
.alert-card .ax:hover { color: var(--text); }
.alert-card .atick { position: absolute; left: 0; bottom: 0; height: 2px; width: 100%; background: color-mix(in srgb, var(--accent) 55%, transparent);
  transform-origin: left; animation: atick 20s linear forwards; }
.alert-card.k-breaking .atick { background: color-mix(in srgb, var(--off) 55%, transparent); }
@keyframes atick { from { transform: scaleX(1); } to { transform: scaleX(0); } }
.btn .badge { position: absolute; top: -5px; right: -5px; min-width: 15px; height: 15px; padding: 0 3px; border-radius: 8px;
  background: var(--off); color: #fff; font-family: var(--mono); font-size: .58rem; line-height: 15px; text-align: center; }
#btn-alerts { position: relative; overflow: visible; }
.alert-list { max-height: 56vh; overflow: auto; display: flex; flex-direction: column; gap: 4px; margin: 10px 0; }
.alert-row { display: grid; grid-template-columns: 92px 1fr auto; gap: 10px; align-items: baseline; padding: 8px 10px; border-radius: 5px; background: var(--panel2); cursor: pointer; }
.alert-row:hover { background: color-mix(in srgb, var(--accent) 8%, var(--panel2)); }
.alert-row.unread { border-left: 2px solid var(--accent); }
.alert-row .ak { font-family: var(--mono); font-size: .58rem; letter-spacing: .12em; color: var(--muted); }
.alert-row .ak.k-breaking { color: var(--off); } .alert-row .ak.k-bias { color: var(--accent2); }
.alert-row .ak.k-revision { color: var(--mixed); }
.alert-row .ar-t { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.alert-row .ar-t b { font-weight: 600; font-size: .86rem; }
.alert-row .ar-t small { color: var(--muted); font-size: .74rem; }
.alert-row .ar-tm { font-family: var(--mono); font-size: .68rem; color: var(--muted); }
/* Named `ring`, not `flash`: styles.css already has a @keyframes flash (a
   background sweep used by a new chat message and a fresh wire item), and
   @keyframes are global - the later definition replaces the earlier one for
   EVERY consumer. This ring was silently playing on new chat messages. */
.flash { animation: ring 2.4s ease; }
@keyframes ring { 0%, 100% { box-shadow: none; } 15%, 60% { box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 70%, transparent); } }

/* ---------- the dial wears one colour, the one it is reading ---------- */
.gauge-panel.is-on, .gauge-panel.is-off, .gauge-panel.is-mixed { border-color: transparent;
  background: linear-gradient(var(--panel), var(--panel)) padding-box,
              linear-gradient(135deg, color-mix(in srgb, var(--gc) 55%, transparent),
                              color-mix(in srgb, var(--border) 60%, transparent) 45%,
                              color-mix(in srgb, var(--gc) 40%, transparent)) border-box; }
.gauge-panel.is-on::after, .gauge-panel.is-off::after, .gauge-panel.is-mixed::after {
  background: linear-gradient(135deg, transparent 50%, color-mix(in srgb, var(--gc) 70%, transparent) 50%); }
.gauge-panel.is-on { --gc: var(--on); }
.gauge-panel.is-off { --gc: var(--off); }
.gauge-panel.is-mixed { --gc: var(--mixed); }
/* One variable drives every coloured part of the dial: arc, halo, hub and ring.
   It may ONLY ever be one of the four signal tokens. --muted was used here for
   "no reading" and that was a bug: the text-colour picker sets --muted, so
   choosing green text painted an idle dial green, which reads as risk-on.
   --dead is fixed in :root and no theme or picker touches it. */
#gauge { --gc: var(--dead); }
#gauge.is-on { --gc: var(--on); }
#gauge.is-off { --gc: var(--off); }
#gauge.is-mixed { --gc: var(--mixed); }
#gauge.is-none { --gc: var(--dead); }
/* The panel behind it needs the same floor, or an unstated panel inherits
   whatever --gc happens to be in scope. */
.gauge-panel { --gc: var(--dead); }

/* ---------- even columns, no dead space on the right ---------- */
.grid-even { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; align-items: stretch; }
.view { display: flex; flex-direction: column; gap: 16px; }
@media (max-width: 1000px) { .grid-even { grid-template-columns: 1fr; } }

/* hovering an alert freezes its countdown so it cannot vanish mid-click */
.alert-card.hold .atick { animation-play-state: paused; }
.alert-card.hold { border-color: color-mix(in srgb, var(--accent) 70%, var(--border)); }
.alert-card.k-wire { border-left-color: var(--muted); }
.alert-card.k-wire .ak { color: var(--muted); }
.alert-card.k-wire .atick { background: color-mix(in srgb, var(--muted) 45%, transparent); }
.alert-row .ak.k-wire { color: var(--muted); }

/* ---------- feed status: one labelled chip, not a row of anonymous dots ---------- */
.feeds-chip { display: inline-flex; align-items: center; gap: 7px; padding: 6px 10px; cursor: pointer;
  background: var(--panel2); border: 1px solid var(--border); border-radius: 6px; color: var(--muted);
  font-family: var(--mono); font-size: .62rem; letter-spacing: .1em; white-space: nowrap; }
.feeds-chip:hover { border-color: color-mix(in srgb, var(--accent) 50%, var(--border)); color: var(--text); }
.feeds-chip i { width: 7px; height: 7px; border-radius: 50%; background: var(--dead); flex: none; }
.feeds-chip i.ok { background: var(--on); box-shadow: 0 0 6px color-mix(in srgb, var(--on) 70%, transparent); }
.feeds-chip i.warn { background: var(--mixed); box-shadow: 0 0 6px color-mix(in srgb, var(--mixed) 70%, transparent); }
.feeds-chip i.bad { background: var(--off); box-shadow: 0 0 6px color-mix(in srgb, var(--off) 70%, transparent); }
.feed-list { max-height: 54vh; overflow: auto; margin: 4px 0 10px; }
.feed-row { display: grid; grid-template-columns: 10px 1fr auto; gap: 10px; align-items: baseline;
  padding: 7px 10px; border-radius: 5px; background: var(--panel2); margin-bottom: 4px; }
.feed-row i { width: 7px; height: 7px; border-radius: 50%; background: var(--dead); align-self: center; }
.feed-row i.ok { background: var(--on); } .feed-row i.bad { background: var(--off); }
.feed-row .fr-n { font-size: .86rem; }
.feed-row .fr-d { font-family: var(--mono); font-size: .68rem; color: var(--muted); text-align: right; }

/* the Alerts button: labelled, badged, and it points at itself when one lands */
.btn .ico.inline { display: inline-block; width: 13px; height: 13px; margin: 0 6px -2px 0; }
#btn-alerts { position: relative; overflow: visible; }
#btn-alerts.pulse { animation: apulse 2.4s ease; }
@keyframes apulse {
  0%, 100% { box-shadow: none; border-color: var(--border); }
  10%, 55% { box-shadow: 0 0 0 2px color-mix(in srgb, var(--off) 55%, transparent); border-color: var(--off); }
}

/* ---------- assets grid: three colours, nothing else to decode ---------- */
.card.c-green  { background: linear-gradient(180deg, color-mix(in srgb, var(--on) 13%, var(--panel2)), transparent 75%);
  border-color: color-mix(in srgb, var(--on) 34%, var(--border)); }
.card.c-red    { background: linear-gradient(180deg, color-mix(in srgb, var(--off) 13%, var(--panel2)), transparent 75%);
  border-color: color-mix(in srgb, var(--off) 34%, var(--border)); }
.card.c-yellow { background: linear-gradient(180deg, color-mix(in srgb, var(--mixed) 12%, var(--panel2)), transparent 75%);
  border-color: color-mix(in srgb, var(--mixed) 30%, var(--border)); }
.card .cword { font-family: var(--mono); font-size: .62rem; letter-spacing: .1em; color: var(--muted); }
.card.c-green .cword  { color: var(--on); }
.card.c-red .cword    { color: var(--off); }
.card.c-yellow .cword { color: var(--mixed); }
.card .temp-tag { display: block; margin-top: 6px; font-family: var(--mono); font-size: .55rem;
  letter-spacing: .08em; text-transform: uppercase; color: var(--mixed); text-align: center;
  border: 1px solid color-mix(in srgb, var(--mixed) 40%, transparent); border-radius: 3px; padding: 2px 4px; }
.card { position: relative; }

/* the count beside the Assets heading */
.tally { font-family: var(--display); font-size: .82rem; letter-spacing: .01em; text-transform: none;
  padding: 2px 9px; border-radius: 4px; border: 1px solid currentColor; }
.tally:empty { display: none; }

/* The count doubles as the key: a dot, its figure, and what that colour means.
   Tabular figures and a fixed figure width so nothing shifts as the counts tick. */
.panel-title { flex-wrap: wrap; }
.legend { margin-left: auto; display: flex; align-items: baseline; gap: 18px;
  font-family: var(--mono); font-size: .64rem; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.legend:empty { display: none; }
.legend .lg { display: inline-flex; align-items: baseline; gap: 7px; }
.legend .lg .d { width: 7px; height: 7px; border-radius: 50%; align-self: center; flex: none; }
.legend .lg b { font-size: .86rem; font-weight: 600; font-variant-numeric: tabular-nums;
  min-width: 1.1em; text-align: right; letter-spacing: 0; }
.legend .l-g .d { background: var(--on); }    .legend .l-g b { color: var(--on); }
.legend .l-r .d { background: var(--off); }   .legend .l-r b { color: var(--off); }
.legend .l-y .d { background: var(--mixed); } .legend .l-y b { color: var(--mixed); }
.assets-hb { margin: -6px 0 12px; font-size: .8rem; color: var(--muted); }
.assets-hb.hidden { display: none; }
@media (max-width: 760px) { .legend { margin-left: 0; width: 100%; gap: 14px; } }


/* ---------- the Calendar button's day view ---------- */
.cal-full { display: flex; flex-direction: column; gap: 3px; margin: 6px 0 4px; }
.calf-row { display: grid; grid-template-columns: 104px 10px 74px 1fr auto; gap: 10px; align-items: baseline;
  padding: 7px 10px; border-radius: 5px; background: var(--panel2); font-size: .88rem; }
.calf-row:has(.sym) { grid-template-columns: 124px 58px 1fr auto; }
.calf-row.past { opacity: .5; }
/* The "worth watching" mark on an earnings row. Deliberately tiny: it sits
   inline after the company name and must never push the figures off the row. */
.ewatch { font-family: var(--mono); font-size: .58rem; letter-spacing: .1em; vertical-align: 1px;
  color: var(--mixed); border: 1px solid color-mix(in srgb, var(--mixed) 45%, transparent);
  border-radius: 3px; padding: 1px 4px; margin-left: 6px; white-space: nowrap; cursor: help; }
.calf-row .t { font-family: var(--mono); font-size: .74rem; color: var(--muted); }
/* expected EPS against the same quarter last year: green up, red down */
.yoy { font-weight: 600; white-space: nowrap; cursor: help; }
.yoy.up { color: var(--on); } .yoy.down { color: var(--off); } .yoy.flat { color: var(--muted); }
/* ---------- folder colours ----------
   These were scoped to `.calf-row`, which is the calendar MODAL's row. The home
   screen draws `.cal-row` with the same `<span class="imp High">`, so on the
   front page every folder dot fell through with no background at all: a red
   folder and an orange folder rendered identically, as nothing. That is the
   "wrong colours / no red folders for Monday" - they were never coloured.

   One rule for both, keyed on the class the markup actually carries, plus a
   visible fallback so an impact value we do not recognise shows up as unknown
   rather than silently disappearing. */
.cal-row .imp, .calf-row .imp {
  width: 9px; height: 10px; border-radius: 1px 2px 2px 2px; align-self: center;
  flex: 0 0 auto; background: var(--dead);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, #000 22%, transparent);
}
/* the little folder tab */
.cal-row .imp::before, .calf-row .imp::before {
  content: ""; display: block; width: 4px; height: 2px; margin: -2px 0 0 0;
  border-radius: 1px 1px 0 0; background: inherit;
}
.cal-row .imp.High, .calf-row .imp.High { background: var(--off); }
.cal-row .imp.Medium, .calf-row .imp.Medium { background: var(--mixed); }
.cal-row .imp.Low, .calf-row .imp.Low { background: color-mix(in srgb, var(--muted) 60%, transparent); }
.calf-row .cty { font-family: var(--mono); font-size: .7rem; letter-spacing: .06em; color: var(--muted); }
.calf-row .sym { font-family: var(--mono); font-size: .8rem; color: var(--accent); }
.calf-row .fc { font-family: var(--mono); font-size: .72rem; color: var(--muted); text-align: right; }
#modal-cal .row-end { display: flex; align-items: center; gap: 10px; justify-content: flex-end; }
#cal-note { margin-right: auto; font-size: .78rem; }

/* ---------- Terminal: the firehose, sortable so it stays readable ---------- */
.terminal { position: fixed; inset: 0; z-index: 35; display: flex; flex-direction: column;
  background: var(--bg); }   /* solid: at 97% the dashboard's own text showed through the headlines */
.terminal.hidden { display: none; }
/* Phones: the filter box was squeezed to two letters. The orb and the TERMINAL label go;
   the Back button and the X both still close it. */
@media (max-width: 560px) { .term-head .term-title, #term-home { display: none; } }
.term-head { display: flex; align-items: center; gap: 12px; padding: 12px 16px;
  border-bottom: 1px solid var(--border); }
.term-head .term-title { font-family: var(--mono); font-size: .8rem; letter-spacing: .18em; color: var(--accent); }
.term-tag { font-size: .78rem; }
.term-search { margin-left: auto; width: min(320px, 40vw); font: inherit; font-size: .86rem; padding: 7px 11px;
  border-radius: 6px; border: 1px solid var(--border); background: var(--panel2); color: var(--text); }
.term-bar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 10px 16px 6px; }
.term-lbl { font-family: var(--mono); font-size: .6rem; letter-spacing: .14em; text-transform: uppercase; color: var(--muted); }
.seg { display: flex; border: 1px solid var(--border); border-radius: 6px; overflow: hidden; }
.seg button { font: inherit; font-size: .78rem; padding: 5px 12px; border: 0; cursor: pointer;
  background: transparent; color: var(--muted); border-right: 1px solid var(--border); }
.seg button:last-child { border-right: 0; }
.seg button:hover { color: var(--text); }
.seg button.on { background: linear-gradient(135deg, var(--accent), var(--accent2)); color: #fff; }
.terminal .chips { padding: 4px 16px 8px; }
.term-list { flex: 1; overflow: auto; padding: 0 10px 8px; }
.trow { display: grid; grid-template-columns: 78px 44px 116px 1fr auto; gap: 12px; align-items: baseline;
  padding: 7px 10px; border-radius: 4px; text-decoration: none; color: var(--text); border-left: 2px solid transparent; }
.trow:nth-child(odd) { background: color-mix(in srgb, var(--panel2) 60%, transparent); }
.trow:hover { background: color-mix(in srgb, var(--accent) 10%, var(--panel2)); }
.trow.breaking { border-left-color: var(--off); border-left-width: 3px;
  background: linear-gradient(90deg, color-mix(in srgb, var(--off) 9%, transparent), transparent 42%); }
.trow.narrow .tsrc { opacity: .6; }
.trow .tt { font-family: var(--mono); font-size: .72rem; color: var(--muted); }
.trow .ta { font-family: var(--mono); font-size: .68rem; }
.trow .tsrc { font-family: var(--mono); font-size: .7rem; letter-spacing: .05em; text-transform: uppercase; color: var(--accent); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.trow .ttl { font-size: .9rem; line-height: 1.35; }
.trow .ttags { display: flex; gap: 4px; }
.trow .ttags i { font-style: normal; font-family: var(--mono); font-size: .58rem; letter-spacing: .06em;
  color: var(--muted); border: 1px solid var(--border); border-radius: 3px; padding: 0 4px; }
.term-foot { display: flex; justify-content: space-between; gap: 12px; padding: 8px 16px;
  border-top: 1px solid var(--border); font-size: .76rem; }
/* Phones: the time and its age share the top line and the headline gets the full width
   under them. With two columns and three visible cells the headline fell into the 64px
   time column on the second row and read one word per line. */
@media (max-width: 800px) { .trow { grid-template-columns: 64px 1fr; row-gap: 2px; } .trow .tsrc, .trow .ttags { display: none; }
  .trow .ttl { grid-column: 1 / -1; } }

/* ---------- Options levels ---------- */
.opts-body { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 12px;
  max-height: 60vh; overflow: auto; margin: 6px 0 10px; }
.opt-card { background: var(--panel2); border: 1px solid var(--border); border-radius: 6px; padding: 12px 14px; }
.opt-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 10px; }
.opt-head b { font-family: var(--display); font-size: .98rem; }
.opt-head .muted { font-size: .7rem; font-family: var(--mono); }
.opt-spot { margin-left: auto; font-family: var(--mono); font-size: 1rem; }
.opt-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(92px, 1fr)); gap: 8px; margin-bottom: 10px; }
.opt-stats > div { display: flex; flex-direction: column; gap: 1px; }
.ol { font-family: var(--mono); font-size: .56rem; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.ov { font-family: var(--mono); font-size: .92rem; }
.ov.up { color: var(--off); } .ov.down { color: var(--on); }
.om { font-size: .66rem; color: var(--muted); }
.opt-ladder { margin: 8px 0; }
.lhead { display: grid; grid-template-columns: 1fr 58px 1fr; font-family: var(--mono); font-size: .54rem;
  letter-spacing: .12em; text-transform: uppercase; color: var(--muted); margin-bottom: 3px; }
.lhead span:first-child { text-align: right; } .lhead span:nth-child(2) { text-align: center; }
.lrow { display: grid; grid-template-columns: 1fr 58px 1fr; align-items: center; gap: 0; height: 13px; }
.lrow .lp { justify-self: end; height: 7px; border-radius: 2px 0 0 2px; background: color-mix(in srgb, var(--on) 65%, transparent); }
.lrow .lc { justify-self: start; height: 7px; border-radius: 0 2px 2px 0; background: color-mix(in srgb, var(--off) 65%, transparent); }
.lrow .lk { text-align: center; font-family: var(--mono); font-size: .62rem; color: var(--muted); }
.lrow.spot .lk { color: var(--accent); font-weight: 600; }
.lrow.spot { background: color-mix(in srgb, var(--accent) 12%, transparent); border-radius: 3px; }
.opt-read { font-size: .84rem; margin: 8px 0 4px; }
.opt-app { font-size: .84rem; margin: 0; color: var(--text); }
.opt-app b { color: var(--accent); }
#modal-opts .row-end { display: flex; align-items: center; gap: 10px; justify-content: flex-end; }
#opts-note { margin-right: auto; font-size: .76rem; }
body.term-open { overflow: hidden; }

/* a paywalled link is marked where it is, rather than explained in a footnote */
.lock { width: 9px; height: 9px; fill: none; stroke: currentColor; stroke-width: 2.1;
  stroke-linecap: round; stroke-linejoin: round; vertical-align: -1px; }
.pay { display: inline-flex; margin-left: 5px; color: var(--muted); opacity: .75; }
.trow:hover .pay, .item:hover .pay, .hl-row:hover .pay { opacity: 1; color: var(--mixed); }
.pay-key { display: inline-flex; align-items: center; gap: 5px; }

/* ---------- FOMC day: the one banner that should stop you trading ---------- */
.fomc-strip { position: relative; z-index: 2; max-width: 1400px; margin: 10px auto -4px; padding: 11px 18px;
  display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap; border-radius: 6px;
  border: 1px solid color-mix(in srgb, var(--off) 55%, transparent);
  background: linear-gradient(90deg, color-mix(in srgb, var(--off) 22%, var(--panel)), var(--panel));
  clip-path: polygon(12px 0, 100% 0, 100% calc(100% - 12px), calc(100% - 12px) 100%, 0 100%, 0 12px); }
.fomc-strip.hidden { display: none; }
.fomc-strip b { font-family: var(--mono); font-size: .82rem; letter-spacing: .18em; color: var(--off); }
.fomc-strip span { font-size: .92rem; }
.fomc-strip em { font-style: normal; font-weight: 700; color: var(--off); }
.fomc-strip.after { border-color: color-mix(in srgb, var(--mixed) 55%, transparent);
  background: linear-gradient(90deg, color-mix(in srgb, var(--mixed) 18%, var(--panel)), var(--panel)); }
.fomc-strip.after b { color: var(--mixed); }
.calf-row.trump { border-left: 2px solid var(--accent2); }

/* ---------- FOMC press conference: top-left, closable ----------
   The iframe streams from YouTube to the viewer; nothing passes through the
   server, so this stays free no matter how many people are watching. */
.fomc-live { position: fixed; top: 96px; left: 16px; z-index: 30; width: min(420px, calc(100vw - 32px));
  background: var(--panel); border: 1px solid color-mix(in srgb, var(--off) 45%, var(--border));
  border-radius: 6px; overflow: hidden;
  clip-path: polygon(12px 0, 100% 0, 100% calc(100% - 12px), calc(100% - 12px) 100%, 0 100%, 0 12px);
  box-shadow: 0 18px 40px rgba(0,0,0,.45); }
.fomc-live.hidden { display: none; }
.fl-head { display: flex; align-items: center; gap: 10px; padding: 9px 12px; border-bottom: 1px solid var(--border); }
.fl-head b { font-family: var(--mono); font-size: .66rem; letter-spacing: .14em; color: var(--off); }
.fl-head .muted { font-family: var(--mono); font-size: .68rem; }
.fl-x { margin-left: auto; background: none; border: 0; color: var(--muted); cursor: pointer;
  font-size: .85rem; line-height: 1; padding: 4px 6px; border-radius: 4px; }
.fl-x:hover { color: var(--text); background: var(--panel2); }
.fl-video { position: relative; width: 100%; aspect-ratio: 16 / 9; background: #000; }
.fl-video iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.fl-links { display: flex; gap: 14px; padding: 9px 12px; }
.fl-links a { font-size: .78rem; color: var(--accent); text-decoration: none; }
.fl-links a:hover { text-decoration: underline; }
@media (max-width: 900px) { .fomc-live { position: static; width: auto; margin: 10px 16px 0; box-shadow: none; } }
.fl-wait { display: flex; align-items: center; gap: 9px; padding: 20px 14px; font-size: .86rem; color: var(--muted); }
.fl-dot { width: 8px; height: 8px; border-radius: 50%; flex: none; background: var(--off); animation: flpulse 2.4s ease-in-out infinite; }
@keyframes flpulse { 0%, 100% { opacity: .35; } 50% { opacity: 1; } }

/* ---------- header clock: each figure says what it is ----------
   Sized to be read at a glance, not squinted at: the units are the thing people
   get wrong, so they are large enough to see and bright enough to notice. */
/* Symmetric padding, and the labels pulled up tight under the numerals. The
   numerals carry the optical weight, so the closer the labels sit the nearer
   the block's apparent centre is to its real one. */
.clock-btn { display: inline-flex; align-items: flex-start; gap: 5px; padding: 2px 9px; }
.cseg { display: inline-flex; flex-direction: column; align-items: center; line-height: 1.02; }
.cseg b { font-family: var(--mono); font-size: 1.22rem; font-weight: 600; font-variant-numeric: tabular-nums;
  color: var(--text); min-width: 1.4em; text-align: center; }
.cseg i { font-style: normal; font-family: var(--mono); font-size: .62rem; font-weight: 600; letter-spacing: .14em;
  text-transform: uppercase; color: color-mix(in srgb, var(--muted) 45%, var(--text)); margin-top: 3px; }
.cseg.meridiem { margin-left: 7px; }
.cseg.meridiem b { color: var(--muted); font-size: .98rem; }
.cseg.meridiem i { color: var(--accent); }
.csep { font-family: var(--mono); font-size: 1.22rem; font-weight: 600; color: var(--muted); line-height: 1.08; }
.top { padding-top: 12px; padding-bottom: 12px; }
/* The left column was being squeezed to 210px by the centre nav, so the brand
   and the clock stacked and the header doubled in height. max-content keeps it
   on one row while 1fr still balances it against the right so the nav stays
   centred on the page. */
@media (min-width: 901px) {
  .top { grid-template-columns: minmax(max-content, 1fr) auto minmax(max-content, 1fr); }
}

/* ---------- background + text colour picker ---------- */
.theme-list .swatch { height: 46px; border-radius: 5px; border: 1px solid var(--border);
  background-size: cover; background-position: center; margin-bottom: 7px; }
.text-list { grid-template-columns: repeat(auto-fill, minmax(118px, 1fr)); }
.pref.tcol { display: flex; align-items: center; gap: 9px; padding: 9px 11px; }
.tsw { width: 15px; height: 15px; border-radius: 50%; flex: none; border: 1px solid rgba(255,255,255,.25); }
.text-note { font-size: .8rem; margin: 8px 0 0; }
.text-note.warn-note { color: var(--mixed); }

/* The Terminal takes over the screen, so it carries the logo and an explicit way
   back. Three of them: the logo, the Dashboard button, and the X. Escape too. */
/* Only neutralise the <button> defaults. The .brand rules keep their own box,
   border and glow, so the logo looks identical to the one on the dashboard. */
.brand-btn { cursor: pointer; font-family: inherit; color: inherit; }
.brand-btn:hover { box-shadow: 0 0 0 1px rgba(255,255,255,.03) inset,
  0 4px 22px color-mix(in srgb, var(--accent) 32%, transparent); }
.term-back { white-space: nowrap; }
.term-div { width: 1px; height: 22px; background: var(--border); flex: none; }
@media (max-width: 800px) { .term-head .term-tag, .term-div { display: none; } }
.meter-foot .pill:empty { display: none; }
/* significance is a 1-10 rating now: the number leads, the lean labels it */
.hl-sig .signum { font-family: var(--mono); font-size: 1.15rem; font-weight: 600; line-height: 1; font-variant-numeric: tabular-nums; }
.hl-sig em { font-style: normal; font-size: .56rem; letter-spacing: .1em; opacity: .9; }
.hl-sig .sbar { width: 46px; }

/* ---------- the week strip in the calendar ---------- */
/* Five equal columns so the row never leaves a gap on the right, whichever day
   is selected and however many red folders each one carries. */
.cal-week { display: grid; grid-template-columns: repeat(5, 1fr); gap: 6px; margin: 10px 0 12px; }
.cal-d { display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 8px 4px 7px; border-radius: 8px; cursor: pointer;
  background: var(--panel2); border: 1px solid var(--border); color: var(--text);
  font-family: var(--font); transition: background .12s, border-color .12s; }
.cal-d:hover { border-color: color-mix(in srgb, var(--accent) 55%, var(--border)); }
.cal-d .dn { font-family: var(--display); font-size: .78rem; letter-spacing: .02em; }
.cal-d .dd { font-family: var(--mono); font-size: .64rem; color: var(--muted); }
.cal-d .dc { font-family: var(--mono); font-size: .58rem; letter-spacing: .08em;
  text-transform: uppercase; color: var(--muted); margin-top: 2px; }
/* A day with red folders on it says so in the signal colour, so the week can be
   read at a glance without opening each day. */
.cal-d .dc.has { color: var(--off); }
.cal-d.gone { opacity: .55; }                 /* already happened, still readable */
.cal-d.gone .dc { color: var(--dead); }
.cal-d.now .dn::after { content: ""; display: inline-block; width: 4px; height: 4px;
  border-radius: 50%; background: var(--accent); margin-left: 5px; vertical-align: middle; }
.cal-d.on { background: color-mix(in srgb, var(--accent) 16%, var(--panel2));
  border-color: color-mix(in srgb, var(--accent) 60%, var(--border)); opacity: 1; }
.cal-d.on .dd { color: var(--text); }
@media (max-width: 620px) { .cal-d .dc { display: none; } }
/* the panel's way into the week view */
#cal-open { cursor: pointer; }
#cal-open:hover { color: var(--accent); }
/* the server's own explanation of why a read did or did not happen */
#hl-state { color: var(--accent); }
#hl-state:empty { display: none; }

/* ---------- the options read: book, flow, and the one opportunity ---------- */
/* The ? sits INSIDE the title row as its first child, not floating over the
   panel. Absolute positioning was the original bug: a floating ? drove straight
   through the title's own right-aligned subtitle. Inline, it cannot overlap
   anything, and it is top-left by construction. 16px, so it reads as a footnote
   marker rather than a control competing with the title. */
.help-btn { flex: 0 0 auto; align-self: center; width: 16px; height: 16px; padding: 0; border-radius: 50%;
  border: 1px solid color-mix(in srgb, var(--border) 130%, transparent); background: transparent;
  color: var(--muted); cursor: pointer; order: -1; margin-right: -3px;
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--display); font-weight: 700; font-size: .62rem; line-height: 1; opacity: .75;
  transition: opacity .12s, color .12s, border-color .12s; }
.help-btn:hover, .help-btn:focus-visible { opacity: 1; color: var(--accent);
  border-color: color-mix(in srgb, var(--accent) 70%, var(--border)); }
/* the panel bullet comes before the ?, so push it back to the front */
.panel-title::before { order: -2; }
/* Settings can switch every one of them off. */
html[data-tips="off"] .help-btn { display: none; }
.of-sec { margin: 10px 0 4px; padding-top: 8px; border-top: 1px dashed color-mix(in srgb, var(--border) 80%, transparent); }
.of-t { display: block; font-family: var(--mono); font-size: .6rem; letter-spacing: .14em; text-transform: uppercase; color: var(--muted); margin-bottom: 6px; }
/* four even columns so nothing is left hanging on the right at any width */
.of-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 7px 10px; }
.of-grid > div { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.of-grid .ov.up { color: var(--on); } .of-grid .ov.down { color: var(--off); }
.of-line { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 6px; }
.of-line .ol { margin-right: 2px; }
.of-chip { font-family: var(--mono); font-size: .66rem; padding: 2px 7px; border-radius: 4px;
  background: var(--panel); border: 1px solid var(--border); color: var(--text); }
.of-chip small { color: var(--muted); font-size: .6rem; margin-left: 3px; }
.of-chip.up { border-color: color-mix(in srgb, var(--on) 50%, var(--border)); color: var(--on); }
.of-chip.down { border-color: color-mix(in srgb, var(--off) 50%, var(--border)); color: var(--off); }
.of-empty { font-size: .8rem; }
.opp { margin-top: 10px; padding: 10px 12px; border-radius: 6px;
  border: 1px solid color-mix(in srgb, var(--accent) 55%, var(--border));
  background: color-mix(in srgb, var(--accent) 9%, var(--panel)); }
.opp.none { border-style: dashed; border-color: var(--border); background: transparent; color: var(--muted); font-size: .84rem; }
.opp-h { display: flex; justify-content: space-between; align-items: baseline; font-family: var(--display); }
.opp .conf { font-family: var(--mono); letter-spacing: .1em; color: var(--accent); font-size: .78rem; }
.opp p { margin: 5px 0 7px; line-height: 1.45; }
.opp-lv { display: flex; gap: 18px; font-family: var(--mono); font-size: .86rem; }
.opp-lv small { display: block; font-size: .58rem; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); }
.opp-by { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 8px; }
.opp-by span { font-family: var(--mono); font-size: .62rem; padding: 2px 7px; border-radius: 4px;
  background: color-mix(in srgb, var(--accent) 18%, transparent); color: var(--text); }
.gloss { margin: 4px 0 6px; display: grid; grid-template-columns: 150px 1fr; gap: 6px 14px; font-size: .84rem; line-height: 1.45; }
.gloss dt { font-family: var(--display); font-size: .8rem; color: var(--text); }
.gloss dd { margin: 0; color: color-mix(in srgb, var(--text) 82%, var(--muted)); }
.gloss em { color: var(--mixed); font-style: normal; }
@media (max-width: 720px) { .of-grid { grid-template-columns: repeat(2, 1fr); } .gloss { grid-template-columns: 1fr; gap: 2px 0; } .gloss dd { margin-bottom: 6px; } }

/* ---------- Today's Editor's Notes ---------- */
.notes-body { margin-top: 4px; }
.notes-h { font-family: var(--display); font-size: 1.05rem; margin: 2px 0 6px; letter-spacing: -.01em; }
.notes-text-view { white-space: pre-wrap; line-height: 1.55; max-width: 90ch; }
.notes-text-view.stale { opacity: .72; }
.notes-edit { margin-top: 12px; padding-top: 10px; border-top: 1px dashed color-mix(in srgb, var(--border) 80%, transparent); display: flex; flex-direction: column; gap: 8px; }
.notes-title, .notes-text { width: 100%; box-sizing: border-box; background: var(--panel2); color: var(--text); border: 1px solid var(--border);
  border-radius: 6px; padding: 8px 10px; font-family: var(--font); font-size: .92rem; }
.notes-text { min-height: 140px; resize: vertical; line-height: 1.5; }
.notes-actions { display: flex; align-items: center; gap: 10px; }
.notes-actions .muted { margin-right: auto; }
.admin-on { font-family: var(--mono); font-size: .62rem; letter-spacing: .1em; text-transform: uppercase; color: var(--mixed); }
.btn.tiny { padding: 2px 8px; font-size: .68rem; }
#admin-err:not(:empty) { color: var(--off); margin: 6px 0; font-size: .84rem; }
#admin-form .row-end { gap: 8px; margin-top: 8px; }

/* ---------- idle states ----------
   A pending check is a ring, not a dash; the label for a closed market is
   calm and dim rather than shouting in the signal colour. */
.check-val.pending { display: flex; justify-content: center; align-items: center; min-height: 1.4em; }
.check-val .ring { width: 15px; height: 15px; border-radius: 50%; border: 2px solid var(--dead); opacity: .55; display: inline-block;
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--dead) 12%, transparent); }
/* The waiting state. A countdown, a track showing how far through the wait we
   are, and one line on what happens at the end of it - so an empty panel still
   tells you something instead of looking broken. */
.verdict.wait { border-inline: 0; }
/* One line, centred: label, time, countdown. The countdown used to be shoved
   to the far right by margin-left:auto, which left the box aligned three
   different ways at once. */
.verdict.wait .vw-top { display: flex; align-items: baseline; justify-content: center; gap: 10px; flex-wrap: wrap; }
.verdict.wait .vw-l { font-family: var(--mono); font-size: .62rem; letter-spacing: .14em; text-transform: uppercase; color: var(--muted); }
.verdict.wait .vw-t { font-family: var(--display); font-weight: 600; }
.verdict.wait .vw-c { color: var(--muted); font-size: .8rem; }
.verdict.wait .vw-c::before { content: "·"; margin-right: 10px; opacity: .5; }
/* The track is bounded and centred under that line. It also has to be clearly
   visible: at the start of a wait the dot sits at 0%, and against an invisible
   track that reads as a stray bullet rather than a progress marker. */
.vw-orbit { position: relative; height: 14px; margin: 12px auto 10px; max-width: 340px; }
.vw-track { position: absolute; left: 0; right: 0; top: 6px; height: 2px; border-radius: 2px;
  background: color-mix(in srgb, var(--text) 16%, transparent); display: block; }
.vw-track i { display: block; height: 100%; border-radius: 2px;
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--accent) 75%, transparent)); }
.vw-dot { position: absolute; top: 3px; width: 8px; height: 8px; margin-left: -4px; border-radius: 50%;
  background: var(--accent); box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 45%, transparent);
  animation: vwPulse 2.6s ease-out infinite; }
@keyframes vwPulse {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 45%, transparent); }
  70%  { box-shadow: 0 0 0 9px color-mix(in srgb, var(--accent) 0%, transparent); }
  100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 0%, transparent); }
}
.vw-what { margin: 0 auto; font-size: .86rem; line-height: 1.5; max-width: 54ch;
  color: color-mix(in srgb, var(--text) 78%, var(--muted)); }
/* the pending rings breathe, so the panel reads as waiting rather than dead */
.check-val .ring { animation: vwRing 3.4s ease-in-out infinite; }
@keyframes vwRing { 0%,100% { opacity: .45; } 50% { opacity: .85; } }
@media (prefers-reduced-motion: reduce) {
  .vw-dot, .check-val .ring { animation: none; }
}
.big.idle-closed, .big.idle-connecting, .big.idle-quiet { color: var(--muted); font-weight: 600; letter-spacing: .01em; }

/* ---------- feed health by class, not by name ---------- */
.feed-row { display: grid; grid-template-columns: 10px 1fr auto auto; gap: 8px 12px; align-items: baseline; padding: 7px 0;
  border-bottom: 1px solid color-mix(in srgb, var(--border) 60%, transparent); }
.feed-row .fr-n { font-family: var(--display); display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.fr-g { font-family: var(--mono); font-size: .56rem; letter-spacing: .12em; text-transform: uppercase;
  padding: 1px 6px; border-radius: 3px; border: 1px solid currentColor; }
.fr-g.g-critical { color: var(--off); }
.fr-g.g-important { color: var(--mixed); }
.fr-g.g-useful { color: var(--muted); }
.feed-row .fr-c { color: var(--muted); font-size: .8rem; }
.feed-row .fr-d { font-size: .8rem; }
.feed-row .fr-h { grid-column: 2 / -1; font-size: .76rem; line-height: 1.35; }

/* A closed market is not an error, so the card says so in readable text rather
   than a grey whisper. Only the price itself dims; the words do not. */
.card.stale .px { color: var(--text); opacity: .92; font-size: .92rem; font-family: var(--display); letter-spacing: .01em; }
.card .age.stale { color: var(--muted); opacity: 1; }

/* the shared help modal's prose */
.help-body p { line-height: 1.55; margin: 0 0 10px; max-width: 78ch; }
.help-body p:last-child { margin-bottom: 4px; }
.help-body b { font-weight: 600; }

/* ---------- headline analysis ----------
   The meter is a trajectory, not a dial: ten waypoints, a craft at today's
   reading, a tail behind it. --gc is set by the band and is one of the four
   signal tokens only, exactly as on the home dial. */
.ar-meter { --gc: var(--dead); display: flex; align-items: center; gap: 18px; flex-wrap: wrap; margin: 2px 0 12px; }
.ar-meter.is-on { --gc: var(--on); } .ar-meter.is-off { --gc: var(--off); }
.ar-meter.is-mixed { --gc: var(--mixed); } .ar-meter.is-none { --gc: var(--dead); }
.ar-svg { flex: 1 1 340px; max-width: 620px; overflow: visible; }
.ar-track { stroke: var(--border); stroke-width: 2; }
.ar-trail { stroke-width: 3; stroke-linecap: round; }
.ar-stop { fill: var(--dead); opacity: .5; }
.ar-stop.lit { fill: var(--gc); opacity: .95; }
.ar-num { fill: var(--dead); font-family: var(--mono); font-size: 8px; text-anchor: middle; }
.ar-num.now { fill: var(--gc); font-weight: 700; font-size: 9.5px; }
.ar-halo { fill: var(--gc); opacity: .16; }
.ar-craft { fill: var(--gc); }
.ar-craft.pending { fill: none; stroke: var(--dead); stroke-width: 2; opacity: .6; }
.ar-ring { fill: none; stroke: var(--gc); stroke-width: 1; opacity: .8; }
.ar-end { fill: var(--dead); font-family: var(--mono); font-size: 7.5px; letter-spacing: 1px; }
.ar-read { display: flex; flex-direction: column; gap: 2px; min-width: 190px; }
.ar-word { font-family: var(--display); font-size: 1.15rem; color: var(--gc); letter-spacing: -.01em; }
.ar-score { color: var(--muted); font-size: .8rem; }
/* even columns, nothing left hanging on the right */
.ar-buttons { display: grid; grid-template-columns: repeat(auto-fit, minmax(104px, 1fr)); gap: 6px; margin-bottom: 12px; }
.ar-btn { display: flex; flex-direction: column; align-items: flex-start; gap: 1px; padding: 7px 9px;
  border-radius: 6px; border: 1px solid var(--border); background: var(--panel2); color: var(--text);
  cursor: pointer; font-family: var(--font); text-align: left; min-width: 0; }
.ar-btn:hover { border-color: color-mix(in srgb, var(--accent) 55%, var(--border)); }
.ar-btn.on { background: color-mix(in srgb, var(--accent) 16%, var(--panel2));
  border-color: color-mix(in srgb, var(--accent) 60%, var(--border)); }
.ar-bn { font-family: var(--display); font-size: .82rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
.ar-bg { font-size: .66rem; color: var(--muted); }
/* The grade's own colour ramp. --grade is a protected token: no theme and no
   text choice may redefine it, so a grade can never read as a signal colour. */
.ar-btn.g-hi .ar-bg, .ar-grade.g-hi em { color: var(--grade); }
.ar-btn.g-mid .ar-bg, .ar-grade.g-mid em { color: var(--muted); }
.ar-btn.g-lo .ar-bg, .ar-grade.g-lo em { color: var(--dead); }
.ar-btn.g-hi { border-left: 2px solid var(--grade); }
.ar-section { border-top: 1px dashed color-mix(in srgb, var(--border) 80%, transparent); padding-top: 12px; }
.ar-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-bottom: 6px; }
.ar-head b { font-family: var(--display); font-size: 1.05rem; }
.ar-grade { display: inline-flex; align-items: baseline; gap: 6px; }
.ar-grade em { font-style: normal; font-family: var(--mono); font-size: .64rem; letter-spacing: .12em; text-transform: uppercase; }
.ar-grade i { font-style: normal; color: var(--muted); font-size: .74rem; }
.ar-mean { font-size: .8rem; }
.ar-text { line-height: 1.55; margin: 0 0 8px; max-width: 84ch; }
.ar-drivers { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 9px; }
.ar-drivers span { font-family: var(--mono); font-size: .64rem; padding: 2px 7px; border-radius: 4px;
  background: var(--panel2); border: 1px solid var(--border); color: var(--muted); }
.ar-opp { padding: 9px 11px; border-radius: 6px; line-height: 1.5;
  border: 1px solid color-mix(in srgb, var(--accent) 50%, var(--border));
  background: color-mix(in srgb, var(--accent) 8%, transparent); }
.ar-opp.none { border-style: dashed; border-color: var(--border); background: transparent; color: var(--muted); font-size: .86rem; }
.ar-watch { margin: 8px 0 0; font-size: .88rem; line-height: 1.5; }
.ar-watch .ol { margin-right: 6px; }
@media (max-width: 620px) { .ar-buttons { grid-template-columns: repeat(auto-fit, minmax(88px, 1fr)); } }

/* ---------- the first-run tour ----------
   The spotlight is a transparent box with an enormous shadow, so the "hole" is
   real rather than four overlay rectangles that never quite line up. */
.tour { position: fixed; inset: 0; z-index: 400; }
.tour.hidden { display: none; }
.tour-veil { position: absolute; inset: 0; }
.tour-hole { position: absolute; border-radius: 10px; pointer-events: none;
  box-shadow: 0 0 0 9999px color-mix(in srgb, #05060f 74%, transparent),
              0 0 0 2px color-mix(in srgb, var(--accent) 70%, transparent);
  transition: left .25s ease, top .25s ease, width .25s ease, height .25s ease, opacity .2s; }
.tour-card { position: absolute; width: min(360px, calc(100vw - 24px)); padding: 14px 16px 12px;
  max-height: calc(100vh - 24px); overflow: auto;
  border-radius: 10px; background: var(--panel); border: 1px solid var(--border);
  box-shadow: 0 18px 50px rgba(0,0,0,.45); transition: left .25s ease, top .25s ease; }
.tour-step { font-size: .6rem; letter-spacing: .16em; text-transform: uppercase; color: var(--muted); }
.tour-card h3 { font-family: var(--display); font-size: 1.02rem; margin: 5px 0 6px; letter-spacing: -.01em; }
.tour-card p { margin: 0 0 12px; line-height: 1.5; font-size: .9rem; }
.tour-foot { display: flex; align-items: center; gap: 8px; }
/* 26 steps = 26 dots: in one fixed row they pushed Back and Next out past the
   card's right edge (1920x855, 2026-10-01). The dots wrap; the buttons keep
   their size. */
.tour-foot .btn { flex: 0 0 auto; }
.tour-dots { display: flex; gap: 4px; margin: 0 auto; flex-wrap: wrap; justify-content: center; flex: 1 1 0; min-width: 0; }
.tour-dots i { width: 5px; height: 5px; border-radius: 50%; background: var(--dead); opacity: .5; }
.tour-dots i.on { background: var(--accent); opacity: 1; }
.tour-card .btn[disabled] { opacity: .45; cursor: default; }
/* the nudge that draws the eye to the clock on a first visit */
@keyframes tmNudge { 0%,100% { transform: none; } 30% { transform: translateY(-3px); } 60% { transform: none; } }
.clock-btn.nudge { animation: tmNudge 1.1s ease 3; border-color: color-mix(in srgb, var(--accent) 65%, transparent);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--accent) 16%, transparent); }
@media (prefers-reduced-motion: reduce) {
  .tour-hole, .tour-card { transition: none; }
  .clock-btn.nudge { animation: none; }
}

/* Overall reads as the home tab, not one of the instruments. */
.ar-btn.ar-overall { border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); }
.ar-btn.ar-overall .ar-bn { font-weight: 600; }
.ar-btn.ar-overall .ar-bg { color: var(--accent); }
.ar-oh { font-family: var(--display); font-size: 1.1rem; letter-spacing: -.01em; }

/* ---------- written opportunities on the home panel ---------- */
.opp-when { font-family: var(--mono); font-size: .62rem; letter-spacing: .1em; text-transform: uppercase; margin-bottom: 8px; }
.opp-w { padding: 9px 11px; border-radius: 6px; margin-bottom: 7px;
  border: 1px solid color-mix(in srgb, var(--accent) 45%, var(--border));
  background: color-mix(in srgb, var(--accent) 7%, transparent); }
.opp-wh { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; font-family: var(--display); }
.opp-wh .conf { font-family: var(--mono); font-size: .72rem; letter-spacing: .1em; color: var(--accent); }
.opp-w p { margin: 4px 0 0; line-height: 1.5; font-size: .9rem; }
.opp-w .opp-lv { display: flex; gap: 18px; margin-top: 7px; font-family: var(--mono); font-size: .82rem; }
.opp-w .opp-lv small { display: block; font-size: .56rem; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); }
.opp-sep { margin: 12px 0 6px; font-family: var(--mono); font-size: .6rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--muted); border-top: 1px dashed color-mix(in srgb, var(--border) 80%, transparent); padding-top: 9px; }
.opp-mover { display: flex; align-items: baseline; gap: 10px; padding: 5px 0; font-size: .88rem; }
.opp-mover .z { margin-left: auto; font-family: var(--mono); font-size: .76rem; color: var(--muted); }

/* ---------- a panel with nothing in it yet ----------
   "At all times we need to look amazing", including at 3am and at weekends. A
   waiting panel now sits a shade back from the live ones with a slow sweep
   across it, so it reads as DOWN rather than broken. No layout shift: it is a
   background and an overlay only. */
.panel.is-waiting { background: color-mix(in srgb, var(--panel) 92%, #000); position: relative; overflow: hidden; }
.panel.is-waiting::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(105deg, transparent 38%,
              color-mix(in srgb, var(--accent) 7%, transparent) 50%, transparent 62%);
  transform: translateX(-100%); animation: panelWait 4.2s ease-in-out infinite;
}
@keyframes panelWait { 0% { transform: translateX(-100%); } 55%, 100% { transform: translateX(100%); } }
.panel.is-waiting > * { position: relative; z-index: 1; }
.panel.is-waiting .panel-title { opacity: .82; }
@media (prefers-reduced-motion: reduce) { .panel.is-waiting::after { animation: none; opacity: .25; } }

/* ---------- the saved format ----------
   The shape of Jason's Discord posts, drawn on the page: an ALL-CAPS title, a
   ═══ rule, TL;DR, another rule, then emoji sections named for the day's news
   and 🎯 SESSION OPPORTUNITIES last. Caps, emoji and spacing do the work, the
   way they do in the original - no bullets, no bold, nothing the plain-text
   version does not have. Colour here is emphasis only: --accent and --grade are
   non-signal tokens, so nothing in this block can be mistaken for the risk-on /
   risk-off reading above it. */
/* A long unbroken run - a URL, a ticker chain, a number the model ran together -
   would otherwise push the panel wider than the screen rather than wrapping. */
.fmt { max-width: 84ch; overflow-wrap: anywhere; }
.fmt-title { font-family: var(--display); font-size: 1.1rem; font-weight: 600;
  letter-spacing: .015em; line-height: 1.38; margin: 0; text-transform: uppercase; }
/* The ═══ divider is part of the format, not decoration, so it has to actually
   read as one. At .76rem and 80% of --dead it came out as three faint specks. */
.fmt-rule { font-family: var(--mono); font-size: .95rem; line-height: 1; letter-spacing: .06em;
  color: var(--dead); margin: 10px 0 11px; user-select: none; }
.fmt-l { display: block; font-family: var(--mono); font-size: .62rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--muted); margin-bottom: 5px; }
.fmt-tldr { margin: 0; line-height: 1.62; }
.fmt-sec { margin-top: 13px; }
.fmt-sh { display: flex; align-items: baseline; gap: 8px; margin-bottom: 3px;
  font-family: var(--display); font-size: .82rem; font-weight: 600; letter-spacing: .07em; }
.fmt-sh i { font-style: normal; font-size: .95rem; line-height: 1; flex: none; }
.fmt-body { margin: 0; line-height: 1.55; }
/* The cross-asset link and the opportunities section are the two a reader came
   for, so they are the two that get lifted off the page. */
.fmt-sec.fmt-link, .fmt-sec.fmt-opp { padding: 10px 12px; border-radius: 8px; background: var(--panel2);
  border: 1px solid var(--border); margin-top: 14px; }
.fmt-sec.fmt-link .fmt-sh { color: var(--accent); }
.fmt-sec.fmt-opp { border-color: color-mix(in srgb, var(--grade) 38%, var(--border)); }
.fmt-sec.fmt-opp .fmt-sh { color: var(--grade); }
@media (max-width: 560px) { .fmt-title { font-size: 1rem; } }

/* ---------- the assets panel reads its own state ----------
   Five readings, five different movements around the panel edge, so which one
   you are in registers before you have read a word.

     strong risk-on   two rails running UP, fast, the panel breathing
     leaning risk-on  ONE rail, up, slow and dim
     mixed            left rail up, right rail down - a tug of war going nowhere
     leaning risk-off ONE rail, down, slow and dim
     strong risk-off  two rails running DOWN, fast, the panel breathing

   Strong versus leaning is told by how MANY rails move, not just how fast, so
   the two are distinguishable at a glance instead of needing a stopwatch.
   Colour comes from the signal tokens and nothing else: this is the risk read,
   so it must be the same green, red and yellow the dial uses. */
.assets-panel { position: relative; overflow: hidden; --rz: var(--dead); }
.assets-panel.rz-on2, .assets-panel.rz-on1   { --rz: var(--on); }
.assets-panel.rz-off2, .assets-panel.rz-off1 { --rz: var(--off); }
.assets-panel.rz-mixed                       { --rz: var(--mixed); }

.assets-panel::before, .assets-panel::after {
  content: ""; position: absolute; top: 0; width: 2px; height: 42%;
  background: linear-gradient(to bottom, transparent, var(--rz), transparent);
  opacity: 0; pointer-events: none; will-change: transform;
}
.assets-panel::before { left: 0; }
.assets-panel::after  { right: 0; }

@keyframes rzUp   { from { transform: translateY(240%); } to { transform: translateY(-110%); } }
@keyframes rzDown { from { transform: translateY(-110%); } to { transform: translateY(240%); } }
@keyframes rzBreathe {
  0%, 100% { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--rz) 6%, transparent); }
  50%      { box-shadow: inset 0 0 30px -14px var(--rz),
                         inset 0 0 0 1px color-mix(in srgb, var(--rz) 30%, transparent); }
}

/* conviction: both edges move, and the panel itself breathes */
.assets-panel.rz-on2::before,  .assets-panel.rz-on2::after  { animation: rzUp 2.6s linear infinite; opacity: .85; }
.assets-panel.rz-off2::before, .assets-panel.rz-off2::after { animation: rzDown 2.6s linear infinite; opacity: .85; }
.assets-panel.rz-on2::after  { animation-delay: -1.3s; }
.assets-panel.rz-off2::after { animation-delay: -1.3s; }
.assets-panel.rz-on2, .assets-panel.rz-off2 { animation: rzBreathe 3.4s ease-in-out infinite; }

/* a lean: one edge only, slower and dimmer - it is a tilt, not a move */
.assets-panel.rz-on1::before  { animation: rzUp 5.4s linear infinite; opacity: .45; }
.assets-panel.rz-off1::after  { animation: rzDown 5.4s linear infinite; opacity: .45; }
.assets-panel.rz-on1, .assets-panel.rz-off1 { animation: rzBreathe 7s ease-in-out infinite; }

/* mixed: the two sides pull against each other and neither wins, which is the
   honest picture of a day with no owner. No breathing - nothing is building. */
.assets-panel.rz-mixed::before { animation: rzUp 4.2s linear infinite; opacity: .55; }
.assets-panel.rz-mixed::after  { animation: rzDown 4.2s linear infinite; opacity: .55; }

/* nothing to report: the panel sits a shade darker, the way an unlit one does */
.assets-panel.rz-none { background: color-mix(in srgb, var(--panel) 88%, #000); }

/* Reduced motion keeps the INFORMATION and drops the movement: the edges still
   say which state it is, and which side they are on still says which way. */
@media (prefers-reduced-motion: reduce) {
  .assets-panel::before, .assets-panel::after { animation: none !important; transform: none; height: 100%; }
  .assets-panel { animation: none !important; }
  .assets-panel.rz-on2::before, .assets-panel.rz-on2::after,
  .assets-panel.rz-off2::before, .assets-panel.rz-off2::after { opacity: .7; }
  .assets-panel.rz-on1::before, .assets-panel.rz-off1::after,
  .assets-panel.rz-mixed::before, .assets-panel.rz-mixed::after { opacity: .4; }
}

/* Light mode deepens the two coloured headings. Measured on the default theme,
   the link heading came out at 2.38:1 against the panel - below even the 3.0
   large-text bar - because the accents are tuned for a dark page. Mixing toward
   --text keeps the hue (so it still reads as accent and as grade, and the two
   stay distinguishable) and buys the contrast back. The divider is part of the
   format rather than decoration, so it gets the same treatment. */
html[data-mode="light"] .fmt-sec.fmt-link .fmt-sh { color: color-mix(in srgb, var(--accent) 55%, var(--text)); }
html[data-mode="light"] .fmt-sec.fmt-opp .fmt-sh  { color: color-mix(in srgb, var(--grade) 55%, var(--text)); }
html[data-mode="light"] .fmt-rule { color: color-mix(in srgb, var(--dead) 55%, var(--text)); }

/* Good / bad for THIS asset. Signal tokens, because that is exactly what this
   is - the same green and red the rest of the site uses for helps/hurts - and a
   word beside the colour so it does not depend on seeing the difference. */
.hit { display: inline-block; margin-left: 8px; padding: 1px 7px; border-radius: 999px;
  font-family: var(--mono); font-size: .6rem; letter-spacing: .08em; text-transform: uppercase;
  vertical-align: middle; border: 1px solid; }
.hit.good { color: var(--on);  border-color: color-mix(in srgb, var(--on) 45%, transparent);
  background: color-mix(in srgb, var(--on) 12%, transparent); }
.hit.bad  { color: var(--off); border-color: color-mix(in srgb, var(--off) 45%, transparent);
  background: color-mix(in srgb, var(--off) 12%, transparent); }

/* The same list under a Headline analysis button, kept tighter than the wire. */
.ar-news { margin-top: 14px; }
.ar-news .fmt-l { margin-bottom: 6px; }
.ar-news .item { padding: 5px 0; }

/* ---------- Money Flow is the featured indicator, so it is sized like one ----
   It was a thin strip on a very long page, reading as one row among many when
   it is the thing the page is built around. Bigger cards, more air, a heavier
   title and a defined edge - weight without a second colour, since colour here
   already means something. */
.assets-panel { padding: 20px 22px; border-width: 2px;
  border-color: color-mix(in srgb, var(--accent) 22%, var(--border)); }
.assets-panel > .panel-title { font-size: 1.05rem; letter-spacing: .06em; margin-bottom: 14px; }
.assets-panel .asset-grid { grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 13px; }
.assets-panel .card { padding: 13px 15px; border-radius: 14px; }
.assets-panel .card .name { font-size: .86rem; }
.assets-panel .card .px { font-size: 1.22rem; margin: 6px 0 5px; }
.assets-panel .card .row { font-size: .86rem; }
/* Compact mode still wins: someone who asked for tighter spacing meant it. */
html.compact .assets-panel { padding: 12px 14px; border-width: 1px; }
html.compact .assets-panel .asset-grid { grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 6px; }
html.compact .assets-panel .card { padding: 7px 9px; }
html.compact .assets-panel .card .px { font-size: 1.05rem; }
@media (max-width: 560px) {
  .assets-panel { padding: 14px 14px; }
  .assets-panel .asset-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 9px; }
}

/* The short-term panel sits under the day one and reads as its companion, not
   as a rival: same card language, one notch quieter, and its own accent edge so
   you always know which of the two you are looking at. */
.st-panel { margin-top: 16px; border-style: dashed; }
.st-panel > .panel-title { font-size: .95rem; }
.st-panel .asset-grid { grid-template-columns: repeat(auto-fill, minmax(170px, 1fr)); gap: 10px; }
.st-panel .card { padding: 9px 11px; }
.st-panel .card .px { font-size: 1rem; }

/* The window each panel reads, said on the heading itself, because the two
   panels are the same fourteen assets and the only difference between them is
   how far back they look. */
.win-tag { font-family: var(--mono); font-size: .58rem; letter-spacing: .1em; text-transform: uppercase;
  padding: 2px 7px; border-radius: 999px; vertical-align: middle; color: var(--muted);
  border: 1px solid var(--border); background: var(--panel2); }


/* ---------- options: stale warning, both clocks, graded signals -------------
   The warning uses --off because a stale chain read as live IS the risk-off
   case for the reader. Bullish/bearish columns use the signal tokens because
   they are a directional call. Levels' holds chips are non-signal: whether a
   level holds is not a market direction. */
.opt-warn { margin: 0 0 12px; padding: 11px 13px; border-radius: 8px; line-height: 1.5; font-size: .9rem;
  border: 1px solid color-mix(in srgb, var(--off) 55%, var(--border));
  background: color-mix(in srgb, var(--off) 10%, var(--panel2)); }
.opt-warn b { color: var(--off); }
.opt-when { font-size: .66rem; color: var(--muted); letter-spacing: .04em; margin: -4px 0 8px; }
.opt-when.old { color: var(--off); }

.sig-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
@media (max-width: 640px) { .sig-cols { grid-template-columns: 1fr; } }
.sig-col { border-radius: 8px; padding: 9px 11px; border: 1px solid var(--border); background: var(--panel2); }
.sig-col.bull { border-color: color-mix(in srgb, var(--on) 40%, var(--border)); }
.sig-col.bear { border-color: color-mix(in srgb, var(--off) 40%, var(--border)); }
.sig-h { font-family: var(--mono); font-size: .62rem; letter-spacing: .14em; text-transform: uppercase; margin-bottom: 6px; }
.sig-col.bull .sig-h { color: var(--on); } .sig-col.bear .sig-h { color: var(--off); }
.sig { padding: 6px 0; border-top: 1px dashed color-mix(in srgb, var(--border) 80%, transparent); }
.sig:first-of-type { border-top: 0; }
.sig-t { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; }
.sig-v { font-size: .78rem; color: var(--muted); white-space: nowrap; }
.sig p { margin: 3px 0 0; font-size: .86rem; line-height: 1.5; }
.sig-none { font-size: .82rem; padding: 4px 0; }

.lv-list { display: flex; flex-direction: column; gap: 5px; }
.lv { display: grid; grid-template-columns: auto auto auto 1fr; gap: 10px; align-items: baseline;
  padding: 6px 9px; border-radius: 6px; background: var(--panel2); font-size: .86rem; }
.lv-k { font-weight: 600; }
.lv-kind { color: var(--muted); font-size: .78rem; }
.lv-hold { font-family: var(--mono); font-size: .6rem; letter-spacing: .1em; text-transform: uppercase;
  padding: 1px 7px; border-radius: 999px; border: 1px solid var(--border); color: var(--muted); }
.lv-hold.h-resistance { border-color: color-mix(in srgb, var(--grade) 55%, var(--border)); color: var(--grade); }
.lv-hold.h-ease { border-style: dashed; }
.lv-hold.h-contested { border-color: color-mix(in srgb, var(--mixed) 55%, var(--border)); color: var(--mixed); }
.lv-why { line-height: 1.45; }
@media (max-width: 640px) { .lv { grid-template-columns: auto auto auto; } .lv-why { grid-column: 1 / -1; } }

.opt-trend { margin: 10px 0 0; font-size: .9rem; line-height: 1.5; }
.of-all { margin-top: 10px; }
.of-all > summary { cursor: pointer; font-family: var(--mono); font-size: .64rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--muted); padding: 4px 0; }
.of-all[open] > summary { margin-bottom: 6px; }


/* Claude's options read: off the page until asked for. `.show-ai` on the modal
   box is the only switch, so every card and the overall line turn together. */
.opt-ai-bar { margin: 0 0 12px; }
#modal-opts .opt-ai { display: none; }
#modal-opts.show-ai .opt-ai { display: block; }
#modal-opts.show-ai span.opt-ai { display: inline; }
#modal-opts.show-ai span.opt-ai.opt-template { display: block; margin-bottom: 8px; padding: 8px 12px; border-radius: 6px;
  background: color-mix(in srgb, var(--mixed) 12%, transparent); border-left: 3px solid var(--mixed); }


/* ---------- the trading journal ----------
   A month grid in the Account tab. A dot marks a day with a note; the selected
   day opens the editor beneath. Colour here is emphasis only. */
.jr-row { border-bottom: 0; padding-bottom: 4px; }
.jr { padding: 0 0 14px; }
.jr-head { display: flex; align-items: center; justify-content: space-between; margin: 4px 0 8px; }
.jr-dow, .jr-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.jr-dow span { text-align: center; font-family: var(--mono); font-size: .6rem; letter-spacing: .1em; color: var(--muted); }
.jr-d { position: relative; height: 34px; border-radius: 6px; border: 1px solid var(--border); background: var(--panel2);
  color: var(--text); font: inherit; font-size: .84rem; cursor: pointer; }
.jr-d:hover { border-color: var(--accent); }
.jr-d.today { border-color: color-mix(in srgb, var(--accent) 60%, var(--border)); font-weight: 600; }
.jr-d.sel { background: color-mix(in srgb, var(--accent) 18%, var(--panel2)); border-color: var(--accent); }
.jr-d.has::after { content: ""; position: absolute; left: 50%; bottom: 4px; width: 5px; height: 5px; margin-left: -2.5px;
  border-radius: 50%; background: var(--grade); }
.jr-pad { height: 34px; }
.jr-edit { margin-top: 10px; }
.jr-when { margin-bottom: 6px; font-size: .9rem; }
.jr-edit textarea { width: 100%; min-height: 110px; resize: vertical; padding: 9px 11px; border-radius: 8px;
  border: 1px solid var(--border); background: var(--panel2); color: var(--text); font: inherit; font-size: .9rem; line-height: 1.5; }
.jr-actions { display: flex; align-items: center; justify-content: flex-end; gap: 8px; margin-top: 8px; }
.jr-actions .mono { margin-right: auto; font-size: .7rem; }


/* ---------- distribution / accumulation days ----------
   Signal tokens on purpose: a distribution day is selling, so it is red the
   way risk-off is red; accumulation is green the way risk-on is green. */
.dist-body { display: flex; flex-direction: column; gap: 12px; }
.dist-row { display: grid; grid-template-columns: 120px 150px 1fr; grid-template-rows: auto auto; gap: 4px 14px; align-items: center;
  padding: 10px 12px; border-radius: 10px; background: var(--panel2); border: 1px solid var(--border); }
.dist-name { display: flex; flex-direction: column; gap: 1px; font-family: var(--display); }
.dist-name .muted { font-size: .72rem; font-family: var(--mono); }
.dist-count { display: flex; align-items: baseline; gap: 8px; --bc: var(--dead); }
.dist-count.b-on { --bc: var(--on); } .dist-count.b-mixed { --bc: var(--mixed); }
.dist-count.b-off, .dist-count.b-off2 { --bc: var(--off); }
.dist-n { font-size: 1.7rem; font-weight: 700; color: var(--bc); line-height: 1; }
.dist-w { font-family: var(--mono); font-size: .62rem; letter-spacing: .12em; text-transform: uppercase; color: var(--bc); }
.dist-strip { display: grid; grid-template-columns: repeat(25, 1fr); gap: 3px; }
/* Scoped. Unscoped, these hit `.cal-d .dc` - the red-folder count under each day
   in the week calendar - which sets only font and colour, so the day counts were
   picking up a grey 14px background bar at 35% opacity from here. */
.dist-strip .dc, .dist-meta i.dc { display: block; height: 14px; border-radius: 3px; background: var(--dead); opacity: .35; }
.dist-strip .dc.k-dist, .dist-meta i.dc.k-dist { background: var(--off); opacity: .95; }
.dist-strip .dc.k-stall, .dist-meta i.dc.k-stall { background: var(--mixed); opacity: .9; }
.dist-strip .dc.k-acc, .dist-meta i.dc.k-acc { background: var(--on); opacity: .85; }
.dist-strip .dc.exp { opacity: .3; background-image: linear-gradient(135deg, transparent 45%, var(--text) 45%, var(--text) 55%, transparent 55%); }
.dist-meta { grid-column: 1 / -1; display: flex; gap: 14px; flex-wrap: wrap; font-size: .74rem; }
.dist-meta i.dc { display: inline-block; width: 10px; height: 10px; margin-right: 5px; vertical-align: -1px; }
@media (max-width: 720px) { .dist-row { grid-template-columns: 1fr 1fr; } .dist-strip { grid-column: 1 / -1; } }

/* a video on the wire is a link to watch, and says so */
.item .vid { font-size: .7em; color: var(--muted); }

/* Light mode: the calendar's country tag measured 4.43:1 on the panel, just
   under the small-text bar. Mixing toward --text keeps it reading as a muted
   label and buys the contrast back, the same treatment the format headings got. */
html[data-mode="light"] .cal-row .cc { color: color-mix(in srgb, var(--muted) 55%, var(--text));
  border-color: color-mix(in srgb, var(--muted) 45%, var(--border)); }
/* The time and the countdown on those rows are the numbers a trader reads, and
   in light mode they sat at the same 4.43:1 the tag did. Same fix. */
html[data-mode="light"] .cal-row .t, html[data-mode="light"] .cal-row .cd { color: color-mix(in srgb, var(--muted) 55%, var(--text)); }


/* The written read under the two counts. --grade is a non-signal accent: the
   colour that carries meaning here is on the counts above it. */
.dist-read { margin-top: 12px; padding: 11px 13px; border-radius: 10px; background: var(--panel2);
  border: 1px solid color-mix(in srgb, var(--grade) 30%, var(--border)); line-height: 1.55; }
.dist-read.none { border-style: dashed; border-color: var(--border); background: transparent; font-size: .86rem; }
.dist-rh { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; margin-bottom: 5px; }
.dist-rh b { font-family: var(--display); font-size: 1rem; color: var(--grade); }
.dist-rh .mono { font-size: .66rem; }
.dist-read p { margin: 0 0 6px; font-size: .9rem; }
.dist-read p:last-child { margin-bottom: 0; }

/* ---------- the hero row, compacted ----------
   The dial's SVG was 286px tall (440px wide on a 200x130 viewBox) and that one
   number set the height of the whole row: the calendar is a grid sibling, so it
   stretched to match and sat 279px empty under three events. Shrinking the dial
   pulls both down together, which is what makes the row read less clunky. */
.hero .panel { padding: 12px 14px; }
#gauge { max-width: 320px; }
/* The dial's drawing overflows its own viewBox at the bottom (overflow is
   visible on purpose, for the comet), so the verdict needs real clearance
   under it - at 2px it sat 4px off the RISK-OFF / RISK-ON end labels. */
.gauge-label { margin-top: 10px; }
.gauge-label .big { font-size: 1.4rem; }
.meter-foot { margin-top: 6px; }
/* The list never fills its old 230px cap; let it use the row and scroll if a
   heavy day ever does fill it. */
.cal { max-height: 100%; }


/* ---------- three findings from the hero review ----------
   1. `.cal { max-height: 100% }` capped nothing: a percentage height against an
      auto-height parent resolves to none, so a heavy calendar day re-inflated
      the hero to 837px with no scrollbar - worse than before the compaction.
      The panel is a flex column now and the list takes what is left and scrolls,
      so the row height stays wherever the dial puts it however many events land.
   2. The dial's halo is a transparent disc that overhangs the pills below it and
      was swallowing the middle pill's tooltip. The dial is presentational; it
      does not need to receive the pointer at all.
   3. There were 20 viewBox units of empty sky above the arc. Cropping them lets
      the dial draw bigger for almost no extra height. */
.cal-panel { display: flex; flex-direction: column; }
.cal-panel > .cal { flex: 1 1 auto; min-height: 0; max-height: none; overflow: auto; }
#gauge { pointer-events: none; max-width: 360px; }

/* ---------- watched, not counted ----------
   NVDA and SPCX still have live cards, they just sit below a rule so the grid
   above is unambiguously the twelve assets the Money Flow reading is made of. */
.stock-watch { margin-top: 10px; padding-top: 9px; border-top: 1px dashed var(--line); }
.sw-head { font-size: 11px; letter-spacing: .04em; text-transform: uppercase;
           color: var(--muted); margin-bottom: 7px; display: flex; flex-wrap: wrap;
           align-items: baseline; gap: 8px; }
.sw-head span { text-transform: none; letter-spacing: 0; font-size: 11.5px; opacity: .8; }

/* ---------- instrument buttons: even rows, and visible ----------
   The column count is decided in JS (arEvenColumns) so the last row is full;
   this is only the hook. Without `.even` the auto-fit rule above still applies,
   which is what a hidden panel gets until it is measured.

   They were also genuinely hard to find: a 1px --border line on --panel2 is
   nearly the panel's own colour, so fourteen buttons read as one grey slab.
   They now sit on their own tone with a brighter edge, and the selected one is
   unmistakable rather than a slightly warmer grey. */
.ar-buttons.even { grid-template-columns: repeat(var(--arcols, 7), minmax(0, 1fr)); }
.ar-btn { background: color-mix(in srgb, var(--text) 7%, var(--panel2));
  border-color: color-mix(in srgb, var(--text) 24%, var(--border)); transition: border-color .12s, background .12s; }
.ar-btn:hover { background: color-mix(in srgb, var(--text) 12%, var(--panel2));
  border-color: color-mix(in srgb, var(--accent) 70%, var(--border)); }
.ar-btn.on { background: color-mix(in srgb, var(--accent) 26%, var(--panel2));
  border-color: var(--accent); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 45%, transparent); }
.ar-btn.on .ar-bn { color: var(--text); }

/* ---------- Editor's Notes on the page ----------
   It was a modal; it is a panel again. The Done button that nearly touched the
   text beside it went with the modal, and the publish row now has room to
   breathe rather than being crushed against a row-end. */
.notes-panel .panel-title { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.notes-panel .panel-title > .btn.sm { margin-left: auto; }
.btn.sm { padding: 3px 10px; font-size: .72rem; line-height: 1.5; }
.notes-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 10px; }
.notes-actions .mono { margin-right: auto; }
.notes-body { margin-top: 6px; }

/* ---------- What's going on today ----------
   It was a column of identical bullets, which is the hardest shape there is to
   scan: every line looked equally important, including the verdict. Now the
   verdict is a card at the top, every other line carries an icon for its
   subject and a rail in the signal colours saying whether that line is good for
   risk, bad for risk, or neither. The colours are the site's protected signal
   tokens, so this panel agrees with the dial and the grid by construction. */
.explain { display: flex; flex-direction: column; gap: 7px; }
.explain li::before { content: none; }
.st-head, .st-row {
  display: grid; grid-template-columns: 26px 1fr; align-items: start; gap: 9px;
  padding: 9px 11px 9px 9px; margin: 0; border-radius: 7px;
  border: 1px solid var(--border); border-left-width: 3px;
  background: color-mix(in srgb, var(--text) 4%, transparent);
}
.st-head { padding: 11px 13px 11px 11px; font-weight: 600; font-size: .95rem;
  background: color-mix(in srgb, var(--text) 8%, transparent); margin-bottom: 3px; }
.st-ic { font-family: var(--display); font-size: .95rem; line-height: 1.45;
  text-align: center; opacity: .95; }
.st-tx { line-height: 1.5; }
.t-on  { border-left-color: var(--on);  }
.t-on  .st-ic { color: var(--on); }
.t-off { border-left-color: var(--off); }
.t-off .st-ic { color: var(--off); }
.t-flat { border-left-color: var(--mixed); }
.t-flat .st-ic { color: var(--mixed); }

/* ---------- the affiliate card (app.js renderAd) ----------
   One small card, bottom-left, for free and signed-out readers. It must never
   sit over the page - not the wire, the dial, Live Chat, the header or the
   phone layout - so its space is reserved, one of two ways:
     WIDE (1360px and up, or 1740px and up with Live Chat open): the content
       moves right just far enough to leave the bottom-left corner free. At
       1920 that is a 48px shift and nothing gets narrower.
     EVERYWHERE ELSE: the card sits in a band across the foot of the window,
       and the page gets the same padding underneath, so the last panel scrolls
       clear of it. The window is simply that much shorter.
   With Live Chat open the band stops at the chat's edge on a desktop; on a
   phone, where chat fills the screen, the card steps aside until chat closes.
   No creative (or a Pro account) = no .pslot-on = none of this applies. */
:root { --ps-w: 300px; --ps-h: 100px; --ps-gap: 12px; }
@media (max-width: 900px) { :root { --ps-w: 250px; --ps-h: 60px; --ps-gap: 8px; } }
:root { --ps-band: calc(var(--ps-h) + 2 * var(--ps-gap));
  --ps-side: calc(var(--ps-w) + 2 * var(--ps-gap) - 16px); }     /* main's own padding is 16px */
.p-dock { position: fixed; left: 0; right: 0; bottom: 0; z-index: 12;
  height: var(--ps-band); padding: var(--ps-gap);
  background: color-mix(in srgb, var(--bg) 94%, transparent); backdrop-filter: blur(10px);
  border-top: 1px solid var(--border); }
.p-dock.hidden { display: none; }
body.pslot-on { padding-bottom: var(--ps-band); }
.p-card { position: relative; display: flex; flex-direction: column; justify-content: center; gap: 3px;
  width: var(--ps-w); height: var(--ps-h); overflow: hidden; padding: 18px 14px 8px;
  border-radius: 8px; text-decoration: none; color: var(--text);
  background: var(--panel); border: 1px solid var(--border); }
.p-card:hover { border-color: color-mix(in srgb, var(--accent) 55%, var(--border)); }
.p-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* the FTC disclosure: always on the card, image or text */
.p-label { position: absolute; top: 5px; left: 8px; z-index: 1; font-size: .58rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--muted); line-height: 1.4; padding: 1px 5px; border-radius: 3px;
  background: color-mix(in srgb, var(--panel) 88%, transparent); }
.p-name { font-size: .95rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.p-alt { font-size: .78rem; color: var(--muted); line-height: 1.35; overflow: hidden;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
.p-img { padding: 0; }
.p-img img { display: block; width: 100%; height: 100%; object-fit: cover; }
@media (max-width: 900px) {
  .p-card { padding: 15px 10px 4px; gap: 1px; }
  .p-name { font-size: .85rem; }
  .p-alt { font-size: .72rem; -webkit-line-clamp: 1; }
  body.chat-open .p-dock { display: none; }
  body.chat-open.pslot-on { padding-bottom: 0; }
}
@media (min-width: 901px) { body.chat-open .p-dock { right: min(380px, 100%); } }
@media (min-width: 1360px) {
  body.pslot-on:not(.chat-open) { padding-bottom: 0; }
  body.pslot-on:not(.chat-open) .p-dock { right: auto; width: calc(var(--ps-w) + 2 * var(--ps-gap));
    background: none; backdrop-filter: none; border-top: 0; }
  body.pslot-on:not(.chat-open) main,
  body.pslot-on:not(.chat-open) .session-rail,
  body.pslot-on:not(.chat-open) .fomc-strip { margin-left: max(var(--ps-side), calc((100% - 1400px) / 2)); margin-right: auto; }
}
@media (min-width: 1740px) {
  body.pslot-on.chat-open { padding-bottom: 0; }
  body.pslot-on.chat-open .p-dock { right: auto; width: calc(var(--ps-w) + 2 * var(--ps-gap));
    background: none; backdrop-filter: none; border-top: 0; }
  body.pslot-on.chat-open main { margin-left: var(--ps-side); }
}
/* TradeMade Pro, Settings > Account (app.js proRowHtml) */
.pro-test { color: var(--accent); font-weight: 600; }
.settings-body .srow .muted a { color: var(--accent); }
/* ---------- corner cards: the Pro popup and the Truth Social reminder (app.js cornerCard) ----------
   Top right, under the header (app.js places it, and moves it left of Live
   Chat on a desktop), one at a time (frontend/cards.js). No dimming and no
   layer behind it, so every click outside it still lands. Under the dialogs
   (z 20), over the page and the affiliate band. Smaller on phones. */
.tm-card { position: fixed; right: 14px; top: 80px; z-index: 18; width: 320px; max-width: calc(100vw - 24px);
  background: var(--panel); border: 1px solid color-mix(in srgb, var(--accent) 45%, var(--border));
  border-left: 3px solid var(--accent); border-radius: 8px; padding: 14px 16px; overflow: hidden;
  box-shadow: 0 10px 30px rgba(0,0,0,.35); display: grid; gap: 8px;
  opacity: 0; transform: translateY(-8px); transition: opacity .3s ease, transform .3s ease; }
.tm-card.in { opacity: 1; transform: none; }
.tm-card.out { opacity: 0; transform: translateY(-8px); pointer-events: none; }
.tm-card.k-truth { border-left-color: var(--off); }
.tm-k { font-family: var(--mono); font-size: .6rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--accent); padding-right: 24px; }
.tm-card.k-truth .tm-k { color: var(--off); }
.tm-t { font-size: .95rem; line-height: 1.35; }
.tm-card p { margin: 0; font-size: .82rem; color: var(--muted); line-height: 1.45; }
.tm-steps { margin: 0; padding-left: 18px; font-size: .8rem; color: var(--muted); line-height: 1.45; }
.tm-steps li + li { margin-top: 2px; }
.tm-row { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 2px; }
.tm-row a.btn { text-decoration: none; }
.tm-x { position: absolute; top: 6px; right: 6px; background: none; border: 0; color: var(--muted);
  cursor: pointer; font-size: .9rem; line-height: 1; padding: 4px 6px; }
.tm-x:hover { color: var(--text); }
.tm-tick { position: absolute; left: 0; bottom: 0; height: 2px; width: 100%; transform-origin: left;
  background: color-mix(in srgb, var(--accent) 55%, transparent); animation: tmtick linear forwards; }
.tm-card.hold .tm-tick { animation-play-state: paused; }
@keyframes tmtick { from { transform: scaleX(1); } to { transform: scaleX(0); } }
@media (max-width: 900px) {
  .tm-card { width: 272px; padding: 11px 12px; gap: 6px; right: 12px; }
  .tm-t { font-size: .86rem; }
  .tm-card p, .tm-steps { font-size: .74rem; }
}
@media (prefers-reduced-motion: reduce) { .tm-card { transition: none; } .tm-tick { animation: none; } }
.tm-card.k-allow { border-left-color: var(--mixed); }
.tm-card.k-allow .tm-k { color: var(--mixed); }
.tm-how { display: grid; gap: 5px; font-size: .78rem; color: var(--muted); line-height: 1.45;
  border-top: 1px dashed var(--border); padding-top: 8px; }
.tm-how b { color: var(--text); font-weight: 600; }
/* the ad-blocker check's bait (index.html): off-screen, 1px, nothing in it */
#bait { position: absolute; left: -10000px; top: 0; width: 1px; height: 1px; overflow: hidden; pointer-events: none; }
/* ---------- the header stays put (Jason, 2026-10-01) ----------
   "Clicking the header's Money Flow button jumps to the panel and the header
   with the top buttons scrolls away." The bar is position: sticky (styles.css
   .top, z-index 10: under the dialogs, toasts, Live Chat and the top-right
   cards). It is now solid, so nothing scrolls through it, and every jump
   target lands just under it: --hdr-h is its height, kept by app.js
   headerFit(). On a phone whose wrapped header would take more than a fifth of
   the screen it is not sticky at all (body.hdr-flow) and --hdr-h is 0. */
:root { --hdr-h: 66px; }
.top { background: var(--bg); backdrop-filter: none; }
body.hdr-flow .top { position: relative; }
main .panel, main section, main .item, main .tab-row { scroll-margin-top: calc(var(--hdr-h) + 12px); }

/* the passing things at the foot of the window sit above it, not over it */
body.pslot-on .toast { bottom: calc(var(--ps-band) + 18px); }
body.pslot-on #vstrip { bottom: calc(var(--ps-band) + 30px); }

/* ---------- LIVE ----------
   The button sits with the other view buttons and is deliberately quiet until
   somebody is actually on air: a control that shouts all day stops meaning
   anything. Dark, it is an ordinary button with a grey dot. Lit, the dot goes
   red and pulses and the button takes the signal colour. */
/* The button is called STREAMS, not LIVE, because "Live Chat" sits on the same
   bar and two controls a word apart is a control nobody trusts. The recording
   dot carries the rest of the meaning: a hollow ring when nothing is running,
   a filled red dot with a halo when something is. */
.btn-live { position: relative; }
.rec-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%;
  margin-right: 7px; vertical-align: middle; background: transparent;
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--muted) 85%, transparent); }
.rec-dot.lit { background: var(--off); box-shadow: inset 0 0 0 1.5px var(--off),
  0 0 0 0 color-mix(in srgb, var(--off) 70%, transparent);
  animation: livepulse 1.9s ease-out infinite; }
@keyframes livepulse {
  0%   { box-shadow: inset 0 0 0 1.5px var(--off), 0 0 0 0 color-mix(in srgb, var(--off) 65%, transparent); }
  70%  { box-shadow: inset 0 0 0 1.5px var(--off), 0 0 0 8px color-mix(in srgb, var(--off) 0%, transparent); }
  100% { box-shadow: inset 0 0 0 1.5px var(--off), 0 0 0 0 color-mix(in srgb, var(--off) 0%, transparent); }
}
@media (prefers-reduced-motion: reduce) { .rec-dot.lit { animation: none; } }
.btn-live.on { border-color: color-mix(in srgb, var(--off) 65%, var(--border));
  background: color-mix(in srgb, var(--off) 15%, var(--panel2)); }
.live-n { font-size: .68rem; margin-left: 6px; opacity: .85; }

.live-body { display: flex; flex-direction: column; gap: 16px; margin-top: 4px; }
/* ---- the merged Video panel: live now / recent videos ---- */
.watch-seg { display: inline-flex; gap: 4px; margin-left: 10px; }
.wtab { font-family: var(--mono); font-size: .68rem; letter-spacing: .08em; text-transform: uppercase;
  background: none; border: 1px solid var(--border); color: var(--muted); border-radius: 6px;
  padding: 4px 10px; cursor: pointer; white-space: nowrap; }
.wtab:hover { color: var(--text); }
.wtab.on { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 55%, transparent);
  background: color-mix(in srgb, var(--accent) 10%, transparent); }
.watch-pane.hidden { display: none; }
.watch-sub { font-family: var(--mono); font-size: .7rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--muted); margin: 2px 0 8px; display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.live-one { border: 1px solid var(--border); border-radius: 9px; overflow: hidden;
  background: var(--panel2); }
.live-head { display: flex; align-items: center; gap: 9px; padding: 9px 12px; }
.live-head b { font-family: var(--display); }
.live-badge { font-size: .6rem; letter-spacing: .1em; font-weight: 700; padding: 2px 6px;
  border-radius: 3px; background: var(--off); color: #fff; }
.live-yt { margin-left: auto; font-size: .76rem; color: var(--accent); text-decoration: none; }
.live-yt:hover { text-decoration: underline; }
.live-frame { position: relative; width: 100%; aspect-ratio: 16 / 9; background: #000; }
.live-frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.live-t { padding: 9px 12px; font-size: .84rem; color: var(--muted); }
.live-empty { padding: 26px 18px; text-align: center; }
.live-empty-h { font-family: var(--display); font-size: 1.02rem; margin-bottom: 8px; }
.live-empty p { max-width: 54ch; margin: 0 auto; line-height: 1.55; }

/* The top distribution band used to share a colour with the one below it, which
   was harmless while it fired on 29% of days and meant nothing in particular.
   Now that it is rare - about 4% of sessions - it should not be mistaken for
   the band below. Same hue, because it is more of the same thing rather than a
   different signal; a solid fill instead of a second colour. */
.dist-count.b-off2 { background: color-mix(in srgb, var(--off) 22%, transparent);
  border-radius: 6px; padding: 2px 8px; box-shadow: inset 0 0 0 1px var(--off); }

/* ---------- VIDEOS ----------
   A grid of thumbnails that become players where they stand. Nothing here is an
   iframe until it is clicked: forty players built up front would be forty
   videos loading at once on somebody's connection. */
.videos-body { display: grid; gap: 12px; margin-top: 10px;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  max-height: min(66vh, 620px); overflow: auto; padding-right: 4px; }
.vcard { display: flex; flex-direction: column; gap: 0; text-align: left; padding: 0;
  border: 1px solid var(--border); border-radius: 9px; overflow: hidden;
  background: var(--panel2); color: var(--text); cursor: pointer; font-family: var(--font);
  transition: border-color .12s, transform .12s; }
button.vcard:hover { border-color: color-mix(in srgb, var(--accent) 60%, var(--border));
  transform: translateY(-1px); }
.vthumb { position: relative; display: block; aspect-ratio: 16 / 9; background: #000; }
.vthumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.vplay { position: absolute; inset: 0; margin: auto; width: 42px; height: 42px; border-radius: 50%;
  background: color-mix(in srgb, #000 55%, transparent); box-shadow: 0 0 0 1px rgba(255,255,255,.35); }
.vplay::after { content: ""; position: absolute; inset: 0; margin: auto; width: 0; height: 0;
  border-left: 13px solid #fff; border-top: 8px solid transparent; border-bottom: 8px solid transparent;
  transform: translateX(2px); }
.vlen { position: absolute; right: 6px; bottom: 6px; font-style: normal; font-size: .68rem;
  padding: 1px 5px; border-radius: 3px; background: rgba(0,0,0,.78); color: #fff; font-family: var(--mono); }
.vmeta { display: flex; flex-direction: column; gap: 3px; padding: 9px 10px 11px; min-width: 0; }
.vtitle { font-size: .84rem; line-height: 1.32; display: -webkit-box; -webkit-line-clamp: 2;
  -webkit-box-orient: vertical; overflow: hidden; }
.vsub { font-size: .72rem; color: var(--muted); }
.vyt { font-size: .74rem; color: var(--accent); text-decoration: none; margin-top: 3px; }
.vyt:hover { text-decoration: underline; }
/* the one that is playing takes the full width of the grid */
.vcard.playing { grid-column: 1 / -1; cursor: default; border-color: var(--accent); }
.vframe { position: relative; width: 100%; aspect-ratio: 16 / 9; background: #000; }
.vframe iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.vchip { font-size: .72rem; padding: 3px 9px; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--border); background: var(--panel2); color: var(--muted);
  font-family: var(--font); margin-left: 6px; }
.vchip.on { background: color-mix(in srgb, var(--accent) 22%, var(--panel2));
  border-color: var(--accent); color: var(--text); }
.vchip b { margin-left: 5px; font-family: var(--mono); font-size: .68rem; opacity: .8; }
.vid-n { font-size: .68rem; margin-left: 6px; opacity: .8; }

/* ---------- New York is closed ----------
   Sits above the day's events. Deliberately calm rather than alarming: a closed
   market is normal, and the point of the notice is to stop a quiet dashboard
   reading as a broken one. */
.cal-closed { display: flex; flex-direction: column; gap: 3px; margin: 0 0 9px;
  padding: 9px 11px; border-radius: 7px; border: 1px solid var(--border);
  border-left: 3px solid var(--mixed);
  background: color-mix(in srgb, var(--mixed) 9%, transparent); }
.cal-closed.hidden { display: none; }
.cal-closed b { font-family: var(--display); font-size: .86rem; }
.cal-closed span { font-size: .78rem; color: var(--muted); line-height: 1.45; }

/* ===========================================================================
   THE HEADER IS ONE LINE OF BUTTONS
   ---------------------------------------------------------------------------
   Jason: "this button layout is not our main its supposed to be generally just
   a line of buttons not multiple under".

   WHAT IT ACTUALLY TAKES. Measured in the browser, with nothing allowed to
   squash: .left 436 + nav 896 + .right 415 + 32 of gaps + 32 of padding = 1811
   CSS px for a single line. So the header did not merely wrap at 1440 - it
   wrapped at 1600, and at 1920 it cleared by about a hundred pixels. Browser
   zoom divides the CSS viewport, so 125% on a 1920 screen is a 1536px page and
   wraps too. That is the "depending on how zoomed in you are" part.

   WHY IT WRAPPED, WHICH IS NOT WHERE IT LOOKED. The centre column is `auto`
   between two side columns of minmax(max-content, 1fr) (the rule just above,
   added to stop the brand and clock stacking). max-content sides cannot give up
   a single pixel, so 100% of any shortfall is taken out of the nav alone: at
   1600 it got 661 of the 896 it needed, at 1024 it got 104 and stacked nine
   rows deep - a 397px sticky header, 44% of the viewport. And because
   flex-wrap:wrap makes a flex container's min-content just its widest child,
   the nav volunteered to be starved. Wrapping was not the symptom of the
   squeeze; it was what permitted it.

   THE FIX IS TWO HALVES.
   1. Structure: sides sized to their content, the nav takes everything left and
      is allowed to shrink (minmax(0,1fr)), and nothing wraps or squashes.
   2. A ladder: as the page narrows the header gives things up in a deliberate
      order - spacing first, then the wordmark, then labels on the buttons whose
      icons carry them, then the feeds wording. Each step's breakpoint is set
      from the measured width the step before it needs, not guessed:
        full     1811   everything, as designed
        L1       1695   tighter spacing and type
        L2       1561   the TRADE MADE wordmark goes, the orb stays
        L3       1420   Alerts / Calendar / Terminal become their icons
        L4       1304   the feeds chip becomes its status dot
        L5       1215   Options / Videos become their icons

      THE BREAKPOINTS SIT ABOVE THOSE FIGURES, not on them. The requirements
      were measured by cloning the header into an iframe, and an iframe has no
      page scrollbar - so every measurement was some 15-17px wider than the real
      viewport and every breakpoint landed that much too late. The last button
      in the row (the light/dark moon) was being sliced in half in three narrow
      bands - around 1700, 1565 and 1425 - and because the nav scrolls with a
      hidden scrollbar, it was cut off silently rather than visibly overflowing.
      Each step now fires ~30px early, which costs nothing and leaves margin for
      a longer signed-in name or a wider feeds label.

      Below the last step the nav scrolls sideways rather than wrapping: still
      one line.
      Below 901 the existing single-column rule takes over, which is the phone
      layout and is meant to stack.
   =========================================================================== */

@media (min-width: 901px) {
  /* Sides take what they need; the nav takes the rest and MAY shrink. This is
     the line that stops the nav being the only thing that pays. */
  .top { grid-template-columns: auto minmax(0, 1fr) auto; }
}

.top .left, .top .right, .actions { flex-wrap: nowrap; }
/* Nothing in the header squashes below its own content. Without this the row
   "fits" by crushing the buttons, which measures as one line and reads as a
   mistake. */
.top .left > *, .top .right > *, .actions > * { flex: 0 0 auto; }
.top .btn, .top .feeds-chip, .top .pill, .top .tab { white-space: nowrap; }

/* `safe` so that when the nav is wider than its column the overflow goes off
   the END rather than off both sides - centred overflow clips the first button
   out of reach, and the first button is Preferences. */
/* The unread-count bubble on Alerts sits at top:-5px, i.e. OUTSIDE its button,
   and `overflow-y: hidden` here was slicing the top half of it clean off. The
   hidden axis has to stay - it is what stops the horizontal scroller growing a
   vertical one - so the clip box is grown upwards by the bubble's overhang
   instead, and the negative margin puts the row back where it was. Nothing
   moves on screen; there is simply room for the bubble now. */
.actions { justify-content: safe center; overflow-x: auto; overflow-y: hidden;
  padding-top: 8px; margin-top: -8px;
  scrollbar-width: none; -ms-overflow-style: none; }
.actions::-webkit-scrollbar { width: 0; height: 0; display: none; }

/* ---- the featured pair: Money Flow and its Visualizer lead the nav ----
   Jason, 2026-09-29: "thats our featured indicators". The same green the
   Visualizer button in the tab row wears, held still: the header is not the
   place for a moving border. The Visualizer is a link, so it is given a
   button's box. A little air after it separates the pair from the tools. */
.btn.feat { border-color: color-mix(in srgb, var(--on) 50%, var(--border));
  background: color-mix(in srgb, var(--on) 9%, var(--panel2)); }
.btn.feat .ico { stroke: var(--on); stroke-width: 1.8; }
.btn.feat:hover, .btn.feat:focus-visible { border-color: var(--on);
  background: color-mix(in srgb, var(--on) 16%, var(--panel2)); }
a.btn.feat { display: inline-block; text-decoration: none; color: var(--text); }
#btn-mfvis { margin-right: 8px; }
/* The Money Flow button lands on the grid; the header is sticky, so the grid
   stops below it rather than under it, title showing. --hdr-h is the header's
   height while it is sticky and 0 on a phone where it scrolls away. */
#assets-panel { scroll-margin-top: calc(var(--hdr-h) + 18px); }

/* ---- L1: spacing and type, before anything is given up ---- */
@media (max-width: 1955px) {
  .top { gap: 10px; padding-left: 10px; padding-right: 10px; }
  .actions { gap: 4px; }
  .actions .btn { padding-left: 9px; padding-right: 9px; font-size: .8rem; }
  .top .right { gap: 7px; }
}
/* ---- L2: the wordmark. The orb stays, and it is still the link home. ---- */
@media (max-width: 1827px) { .brand .wm { display: none; } .brand { padding: 7px 10px; } }
/* ---- L3: the three whose icons say it - a bell, a calendar, a console ---- */
@media (max-width: 1688px) {
  #btn-alerts, #btn-cal, #btn-term { font-size: 0; padding-left: 10px; padding-right: 10px; }
  #btn-alerts .ico.inline, #btn-cal .ico.inline, #btn-term .ico.inline { margin: 0; width: 15px; height: 15px; }
  #btn-alerts .badge { font-size: .62rem; }
}
/* ---- L4: the feeds chip keeps its dot, which is what carries the status ---- */
@media (max-width: 1541px) { #feeds-label { display: none; } .feeds-chip { padding: 6px 8px; } }
/* ---- L5: the last two that have an icon to fall back on ---- */
@media (max-width: 1412px) {
  #btn-opts, #btn-watch { font-size: 0; padding-left: 10px; padding-right: 10px; }
  #btn-opts .ico.inline, #btn-watch .ico.inline { margin: 0; width: 15px; height: 15px; }
  #btn-watch .vid-n { font-size: .62rem; margin-left: 4px; }
}
/* ---- L6: the session pill. The rail directly under the header already says
   which of Asia / London / New York is open, so this is the one thing left in
   the header that is said twice.

   The ladder used to stop at L5, which left a 400px-wide hole: below about
   1212px the nav overflowed and, because .actions scrolls with a hidden
   scrollbar, the buttons past the edge were unreachable rather than visibly
   broken. The guard never saw it because its width list floored at 1200 and
   caught only the first 12 pixels of it.

   Note the state that fails here is signed OUT, not signed in - the opposite of
   every step above. Below 1330 the name pill is capped at 68px, so a signed-in
   header is 33px NARROWER than one showing the full "Sign in" button. ---- */
@media (max-width: 1323px) { #session { display: none; } }

/* The FOMC card hung off a hard-coded 96px, which was the height of a header
   that had already wrapped. One line is 66. */
.fomc-live { top: 82px; }

/* ===========================================================================
   THE SAME COMPLAINT, THREE MORE PLACES
   ---------------------------------------------------------------------------
   Auditing the header turned up other rows of controls with the same fault, and
   two of them are worse than wrapping - they OVERFLOW, which means the controls
   past the edge cannot be reached at all:

   .settings-tabs  six tabs (Display, Meter, Alerts, Notifications, Wire,
                   Account) with no flex-wrap at all. On a narrow window they
                   run past the modal's edge and are clipped by its clip-path,
                   so Wire and Account are simply unreachable.
   .term-head      the terminal's top bar does the same on a phone.
   .tabs           the asset strip directly under the logo. With a dozen or more
                   assets pinned it is on two rows at EVERY desktop width - the
                   most visible instance of exactly what Jason pointed at, and
                   the Preferences copy sends people to it by name.

   All three get the same treatment as the header: one line, and if the line is
   longer than the box it scrolls sideways rather than stacking or hiding.
   =========================================================================== */

.tabs, .settings-tabs {
  flex-wrap: nowrap;
  overflow-x: auto; overflow-y: hidden;
  scrollbar-width: none; -ms-overflow-style: none;
  /* A flex item defaults to min-width:auto, which refuses to shrink below its
     content - that refusal is what turns a long row into an overflow instead of
     a scroll. */
  min-width: 0;
}
.tabs::-webkit-scrollbar, .settings-tabs::-webkit-scrollbar { width: 0; height: 0; display: none; }
.tabs > *, .settings-tabs > * { flex: 0 0 auto; }
.tab { white-space: nowrap; }

/* The terminal's top bar: let the search field give up width so the close
   button and the back link stay on the bar instead of off the end of it. */
.term-head { min-width: 0; }
.term-head > * { flex: 0 0 auto; }
.term-head input, .term-head .term-search { flex: 1 1 60px; min-width: 0; }

/* A panel subtitle sits far right by default (margin-left:auto above). The
   calendar's day is not a footnote about the panel, it is what the rows in it
   are FOR - a column of bare times means nothing without it - so this one sits
   immediately beside the title instead of across the panel from it. */
.panel-title .muted.beside { margin-left: 0; text-align: left; }

/* ---------- a calendar figure: actual, forecast, previous ---------- */
.nums { display: inline-flex; gap: 11px; font-family: var(--mono); font-size: .72rem;
  white-space: nowrap; vertical-align: baseline; }
.nums .n { color: var(--muted); }
.nums .n i { font-style: normal; opacity: .55; margin-right: 3px; font-size: .64rem;
  letter-spacing: .08em; text-transform: uppercase; }
/* The actual is the one you came for, so it is the one that is legible. */
.nums .act { color: var(--text); font-weight: 600; }
/* Direction only. NOT green/red: whether beating a forecast is good depends on
   the indicator, and guessing that is exactly the kind of call this site does
   not make. */
.nums .act.over::after  { content: " \25B2"; font-size: .68em; opacity: .75; }
.nums .act.under::after { content: " \25BC"; font-size: .68em; opacity: .75; }
.cal-row .nums { font-size: .68rem; gap: 8px; }

/* The calendar modal's row is a fixed five-column grid, and adding the
   act/forecast/previous group pushed its content to ~480px inside a 341px box
   on a phone - the figures rendered off the right edge, reachable only by
   scrolling sideways. Narrow screens get the numbers on their own line under
   the event instead, which is the one thing that cannot overflow. */
@media (max-width: 620px) {
  .calf-row { grid-template-columns: 78px 10px 1fr; row-gap: 3px; }
  .calf-row .ttl { grid-column: 3; }
  .calf-row .cty { grid-column: 3; font-size: .78rem; }
  .calf-row .fc  { grid-column: 3; justify-self: start; }
  .calf-row .nums { font-size: .68rem; gap: 9px; }
}

/* ---------- a long name must not eat the nav ----------
   .top .right is an auto grid track whose children are flex:0 0 auto and
   nowrap, so it takes exactly as much as its content wants. The nav is the
   only flexible track, so it pays for all of it: a 20-character username
   (the cap in auth.py) plus a " . owner" badge is ~130px more than the
   "Sign in" button it replaces, and the chip falls back to the EMAIL when
   somebody has no username yet, which has no length limit worth relying on.
   The header stayed one row throughout - it just quietly pushed Backtest and
   the icon buttons past the edge of a nav that scrolls with a hidden
   scrollbar, so nothing looked wrong; the buttons were simply gone.

   Capped with an ellipsis instead. The full name is in the title attribute
   and spelled out in full under Settings -> Account. */
/* 88, not 128. At 1920 no ladder step has fired yet, so every other element is
   at full size and there is only ~109px of slack in the whole row - a 128px
   name pill ate it and left the nav 32px short. 88px is the widest the name can
   be without the row needing more than 1920 offers, with a little margin. */
.top .user-chip .pill { max-width: 88px; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; vertical-align: middle; }
@media (max-width: 1688px) { .top .user-chip .pill { max-width: 104px; } }
@media (max-width: 1541px) { .top .user-chip .pill { max-width: 84px; } }
@media (max-width: 1412px) { .top .user-chip .pill { max-width: 68px; } }

/* AND LET THE NAME BE THE THING THAT GIVES WAY. Capping the pill at a fixed
   width only moves the problem: at 1920 signed in, the full-size right column
   is 522px and the row still ran 32px over, so the nav was still the one
   paying. The right column is a rigid `auto` track whose children are all
   flex:0 0 auto - it takes what it wants and there is no negotiation.
   Now the track may shrink, and within it the ONE item that is allowed to
   shrink is the name, which is the least important thing in the header and the
   only one that degrades gracefully: it ellipses, it keeps its hover title, and
   it is spelled out in full under Settings -> Account. Everything else in the
   row keeps its size. */
@media (min-width: 901px) { .top { grid-template-columns: auto minmax(0, 1fr) minmax(0, auto); } }
.top .right { min-width: 0; }
.top .right > .user-chip { flex: 0 1 auto; min-width: 0; }
.top .user-chip { min-width: 0; }
.top .user-chip .pill { flex: 0 1 auto; }

/* ---------- the same ladder, ~70px earlier, for a signed-in header ----------
   Signing in costs the row about 62px (a name pill and a sign-out icon in place
   of one "Sign in" button). Each step therefore fires that much sooner rather
   than the whole site being tightened for a visitor who is not signed in. */
@media (max-width: 2025px) { body.signed-in .top { gap: 10px; padding-left: 10px; padding-right: 10px; }
  body.signed-in .actions { gap: 4px; }
  body.signed-in .actions .btn { padding-left: 9px; padding-right: 9px; font-size: .8rem; }
  body.signed-in .top .right { gap: 7px; } }
@media (max-width: 1897px) { body.signed-in .brand .wm { display: none; } body.signed-in .brand { padding: 7px 10px; } }
@media (max-width: 1758px) {
  body.signed-in #btn-alerts, body.signed-in #btn-cal, body.signed-in #btn-term { font-size: 0; padding-left: 10px; padding-right: 10px; }
  body.signed-in #btn-alerts .ico.inline, body.signed-in #btn-cal .ico.inline, body.signed-in #btn-term .ico.inline { margin: 0; width: 15px; height: 15px; }
  body.signed-in #btn-alerts .badge { font-size: .62rem; } }
@media (max-width: 1611px) { body.signed-in #feeds-label { display: none; } body.signed-in .feeds-chip { padding: 6px 8px; } }
@media (max-width: 1482px) {
  body.signed-in #btn-opts, body.signed-in #btn-watch { font-size: 0; padding-left: 10px; padding-right: 10px; }
  body.signed-in #btn-opts .ico.inline, body.signed-in #btn-watch .ico.inline { margin: 0; width: 15px; height: 15px; }
  body.signed-in #btn-watch .vid-n { font-size: .62rem; } }
/* Last resort below the ladder: the name goes entirely. It is the least
   important thing in the header, the sign-out control stays, and the name is
   still under Settings -> Account. */
@media (max-width: 1383px) { body.signed-in .top .user-chip .pill { display: none; } }

/* ---------- the volatility row: two facts, each labelled ----------
   `.z` is how far it MOVED in 24 hours; `.rng` is how much ground today's
   high-to-low covers. They used to sit side by side as a bare number and a bare
   letter grade, which read as a contradiction whenever they disagreed - an up
   arrow, a red D and "2.7x its own normal" all on one line, with nothing saying
   the D and the 2.7 were measuring different things.
   The colour ramp is the same one the letter used, so the at-a-glance signal is
   unchanged; only the words are new. */
.opp-mover .z { margin-left: auto; }
.opp-mover .rng { font-family: var(--mono); font-size: .72rem; padding: 1px 7px;
  border-radius: 6px; white-space: nowrap; }
.opp-mover .z b, .opp-mover .rng b { font-weight: 700; }
@media (max-width: 560px) {
  /* Three facts and a name do not fit a phone in one line. */
  .opp-mover { flex-wrap: wrap; row-gap: 2px; }
  .opp-mover .z { margin-left: 0; }
}

/* ---------- an opportunity card says which way, and on what ----------
   The card used to be a title and one free sentence, and the sentence was free
   to mention neither. "Crude Oil - Crude has the only catalyst on the board
   independent of the rates story" named no catalyst and no direction, which is
   not an opportunity, it is a note to self. The direction and the catalyst are
   their own fields now, so a card that lacks one is dropped before it is drawn
   rather than rendered as a shrug. */
.opp-way { font-family: var(--mono); font-size: .68rem; letter-spacing: .08em;
  text-transform: uppercase; padding: 1px 7px; border-radius: 999px; }
.opp-way.up { background: color-mix(in srgb, var(--on) 22%, transparent); color: var(--on); }
.opp-way.down { background: color-mix(in srgb, var(--off) 22%, transparent); color: var(--off); }
/* Amber, the site's colour for "both sides are true at once". A card that has
   named the force pushing the other way is a range to trade, not a direction
   to take, and it must not wear a green or a red arrow. */
.opp-way.two-way { background: color-mix(in srgb, var(--mixed) 22%, transparent); color: var(--mixed); }
.opp-cat { font-size: .8rem; color: var(--muted); margin: 2px 0 4px; }
.opp-cat small { font-family: var(--mono); font-size: .64rem; letter-spacing: .12em;
  text-transform: uppercase; opacity: .6; margin-right: 5px; }

/* ---- L7: the last three labels ----------------------------------------
   The 901-1135px band could not be closed before this, because Preferences,
   STREAMS, Backtest and Live Chat were the only labels left and none had an
   icon to fall back on - collapsing them would have left blank buttons. They
   have icons now (STREAMS keeps its recording dot, which already reads as
   "live"), so the last ~180px is available and the band closes.

   This is the floor. Below it the nav scrolls sideways, and below 901 the
   single-column phone layout takes over. */
@media (max-width: 1246px) {
  #btn-prefs, #btn-watch, #btn-chat {
    font-size: 0; padding-left: 10px; padding-right: 10px;
  }
  #btn-prefs .ico.inline, #btn-chat .ico.inline {
    margin: 0; width: 15px; height: 15px;
  }
  #btn-watch .rec-dot { margin: 0; }
  #btn-watch .live-n { font-size: .62rem; margin-left: 4px; }
}

/* The last three pixels. At 910px - one step above the 901px phone layout -
   the row still overshot by 3px. Not worth another ladder step; the gap and
   the outer padding give it back with room to spare. */
@media (max-width: 1098px) {
  .actions { gap: 3px; }
  .top { padding-left: 8px; padding-right: 8px; gap: 8px; }
}

/* ---- 2026-09-29: the featured pair joined the nav and cost it ~225px ----
   Every step above moved up by what it measured it needed plus 30px, and two
   steps were added at the bottom, where the row had only 3px to spare before.
   L9: the background and light/dark buttons go. Both are also in Settings
       (Background, Light / dark), so nothing is lost; headercheck.js knows
       they may be hidden (data-in-settings).
   L10: the featured pair keeps its icons and loses its words - last, because
       they are the site's own indicators and everything else went first.
   Desktop widths only: below 901 the phone layout stacks and has room. */
@media (min-width: 901px) and (max-width: 1081px) {
  #btn-theme, #btn-mode { display: none; }
}
@media (min-width: 901px) and (max-width: 1001px) {
  #btn-mf, #btn-mfvis { font-size: 0; padding-left: 10px; padding-right: 10px; }
  #btn-mf .ico.inline, #btn-mfvis .ico.inline { margin: 0; width: 15px; height: 15px; }
  #btn-mfvis { margin-right: 4px; }
}

/* The options quick-reference table. Three columns - what you see, what it
   means in ordinary words, what to do about it - because Jason's note was that
   a veteran FUTURES trader still could not follow options prose. Scrolls
   sideways rather than squashing on a narrow window. */
.gloss-tbl { width: 100%; border-collapse: collapse; margin: 6px 0 10px; font-size: .84rem; }
.gloss-tbl th { text-align: left; font-family: var(--mono); font-size: .62rem; letter-spacing: .12em;
  text-transform: uppercase; color: var(--muted); font-weight: 600;
  padding: 6px 10px; border-bottom: 1px solid var(--border); }
.gloss-tbl td { padding: 8px 10px; border-bottom: 1px solid color-mix(in srgb, var(--border) 55%, transparent);
  vertical-align: top; line-height: 1.45; }
.gloss-tbl tbody tr:hover { background: color-mix(in srgb, var(--accent) 6%, transparent); }
.gloss-tbl td:first-child { white-space: normal; color: var(--text); width: 22%; }
.gloss-tbl td:last-child { color: var(--muted); width: 32%; }
@media (max-width: 700px) { .gloss-tbl { font-size: .78rem; } .gloss-tbl th, .gloss-tbl td { padding: 6px 7px; } }

/* "This is Friday's read, not today's." Amber, the site's colour for something
   that is true but not the whole truth. It sits above the read rather than
   replacing it: the old analysis is still worth reading, it just must never be
   mistaken for this morning's. */
.ar-stale { margin: 2px 0 10px; padding: 8px 12px; border-radius: 6px; font-size: .82rem; line-height: 1.45;
  color: var(--text); background: color-mix(in srgb, var(--mixed) 12%, transparent);
  border-left: 3px solid var(--mixed); }
.ar-stale.hidden { display: none; }

/* The Money Flow Visualizer button, the way into /flow. It used to be a small
   "Watch it flow" tag in the Money Flow panel title. Jason, 2026-09-23, asked
   for it next to OVERVIEW, right of any asset tabs Preferences adds, "very
   visable" and with "a animation for the button so it catches the eye but
   nothing too crazy so it doesnt annoy people".
   So two slow, quiet motions: the green-to-accent border drifts round like the
   streams on the map, and a soft sheen crosses the face once every 8 s. Neither
   moves the button or its text. Both stop for anyone who asks the OS for less
   motion. */
.tab-row { display: flex; align-items: center; gap: 6px; min-width: 0; }
/* the tab strip gives up width and scrolls sideways, the button never hides */
.tab-row > .tabs { flex: 0 1 auto; min-width: 0; }
.flow-btn {
  flex: 0 0 auto; position: relative; overflow: hidden; isolation: isolate;
  display: inline-flex; align-items: center; gap: 7px; padding: 7.5px 14px; border-radius: 6px;   /* 7.5 + the 1.5 border = a tab's height */
  font-family: var(--mono); font-size: .78rem; font-weight: 600; letter-spacing: .08em; text-transform: uppercase;
  color: var(--text); text-decoration: none; white-space: nowrap;
  border: 1.5px solid transparent;
  background:
    linear-gradient(color-mix(in srgb, var(--on) 10%, var(--panel)), color-mix(in srgb, var(--on) 10%, var(--panel))) padding-box,
    linear-gradient(90deg, var(--on), var(--accent), var(--on), var(--accent), var(--on)) border-box;
  background-size: 100% 100%, 300% 100%;
  box-shadow: 0 0 14px color-mix(in srgb, var(--on) 25%, transparent);
  animation: flow-btn-border 9s linear infinite;
}
.flow-btn .ico { width: 15px; height: 15px; flex: 0 0 auto; fill: none; stroke: var(--on); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.flow-btn::after {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: linear-gradient(105deg, transparent 35%, color-mix(in srgb, var(--on) 30%, transparent) 50%, transparent 65%);
  transform: translateX(-110%);
  animation: flow-btn-sheen 8s ease-in-out infinite;
}
.flow-btn:hover, .flow-btn:focus-visible {
  box-shadow: 0 0 20px color-mix(in srgb, var(--on) 45%, transparent);
  background:
    linear-gradient(color-mix(in srgb, var(--on) 20%, var(--panel)), color-mix(in srgb, var(--on) 20%, var(--panel))) padding-box,
    linear-gradient(90deg, var(--on), var(--accent), var(--on), var(--accent), var(--on)) border-box;
  background-size: 100% 100%, 300% 100%;
}
@keyframes flow-btn-border { from { background-position: 0 0, 0 0; } to { background-position: 0 0, 150% 0; } }
@keyframes flow-btn-sheen { 0%, 78% { transform: translateX(-110%); } 96%, 100% { transform: translateX(110%); } }
@media (prefers-reduced-motion: reduce) {
  .flow-btn { animation: none; }
  .flow-btn::after { animation: none; display: none; }
}

/* The Money Flow window switch: 24hr (default) | 30m, styled as the window tags. */
.mf-win { display: inline-flex; gap: 2px; vertical-align: middle; }
.mf-w { cursor: pointer; font: inherit; }
.mf-w.active { color: var(--text); border-color: color-mix(in srgb, var(--accent) 60%, transparent);
  background: color-mix(in srgb, var(--accent) 22%, transparent); }
.mf-w:not(.active):hover { color: var(--text); }

/* The "keep it live" reminder above the streams. */
.live-tip { margin: 6px 0 10px; padding: 8px 12px; border-radius: 6px; font-size: .84rem; line-height: 1.45;
  color: var(--text); background: color-mix(in srgb, var(--off) 10%, transparent); border-left: 3px solid var(--off); }
.live-tip b { color: var(--off); }

/* The Video panel's title stays pinned while its long list scrolls, so its X is
   always one click away. (The box has 18px of padding, hence top: -18px.) */
#modal-watch .modal-box > .watch-title { position: sticky; top: -18px; z-index: 3; margin: -18px -18px 10px;
  padding: 14px 60px 12px 18px; background: var(--bg); border-bottom: 1px solid var(--border); }
.modal-x { position: absolute; top: 10px; right: 12px; width: 34px; height: 34px; border-radius: 50%;
  border: 1px solid var(--border); background: var(--panel); color: var(--text); font-size: 1rem; line-height: 1;
  cursor: pointer; text-transform: none; letter-spacing: 0; }
.modal-x:hover, .modal-x:focus-visible { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 16%, var(--panel)); }

/* "Featured indicator · by TradeMade" on the Money Flow title. Jason, 2026-09-23:
   "make sure its clear money flow and the money flow visualizer is our featured
   indicator made by TradeMade". Lit in the accent so it reads as a badge, not
   another window tag like 24hr beside it. */
.win-tag.feat-tag { color: var(--accent); border-color: color-mix(in srgb, var(--accent) 50%, transparent);
  background: color-mix(in srgb, var(--accent) 12%, transparent); box-shadow: 0 0 12px color-mix(in srgb, var(--accent) 22%, transparent); }

/* Trump's market posts: the posts themselves, STACKED in the top-left corner,
   newest on top, each until its own X. Not a modal - no backdrop, the page
   stays usable. Above the modals (20) and the toast (30), below the tour (400).
   The stack scrolls on its own once it is taller than the screen. */
.truth-stack { position: fixed; top: 12px; left: 12px; z-index: 300; width: min(400px, calc(100vw - 24px));
  max-height: calc(100vh - 24px); overflow-y: auto; display: flex; flex-direction: column; gap: 10px;
  overscroll-behavior: contain; scrollbar-width: thin; scrollbar-color: var(--border) transparent; }
.truth-stack.hidden, .truth-bar.hidden { display: none; }
#truth-cards { display: flex; flex-direction: column; gap: 10px; }
.truth-bar { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 6px 8px 6px 12px;
  border-radius: 10px; background: var(--bg); border: 1px solid var(--border); font-size: .8rem; color: var(--muted);
  box-shadow: 0 8px 24px rgba(0, 0, 0, .45); }
.truth-card { position: relative; flex: 0 0 auto;
  background: var(--bg); color: var(--text); border: 1px solid color-mix(in srgb, var(--off) 55%, var(--border));
  border-left: 3px solid var(--off);
  border-radius: 12px; padding: 14px 16px 12px; box-shadow: 0 14px 40px rgba(0, 0, 0, .55), 0 0 0 1px rgba(255, 255, 255, .03);
  animation: truth-in .2s ease-out; }
@keyframes truth-in { from { transform: translateX(-14px); opacity: 0; } to { transform: none; opacity: 1; } }
.truth-x { position: absolute; top: 8px; right: 8px; width: 32px; height: 32px; border-radius: 50%;
  border: 1px solid var(--border); background: var(--panel); color: var(--text); font-size: 1.05rem; cursor: pointer; }
.truth-x:hover, .truth-x:focus-visible { border-color: var(--off); background: color-mix(in srgb, var(--off) 18%, var(--panel)); }
.truth-head { display: flex; align-items: center; gap: 10px; padding-right: 38px; }
.truth-av { flex: 0 0 auto; width: 38px; height: 38px; border-radius: 50%; display: grid; place-items: center;
  font: 700 .82rem var(--mono); letter-spacing: .04em; color: #fff; background: linear-gradient(135deg, #b22234, #3c3b6e); }
.truth-who { display: flex; flex-direction: column; min-width: 0; }
.truth-who b { font-size: 1rem; }
.truth-who span { font-size: .8rem; color: var(--muted); }
.truth-flag { display: inline-block; margin: 10px 0 0; padding: 3px 9px; border-radius: 4px; font: 600 .7rem var(--mono);
  letter-spacing: .08em; text-transform: uppercase; color: var(--off); background: color-mix(in srgb, var(--off) 14%, transparent); }
.truth-text { margin: 8px 0 10px; font-size: .95rem; line-height: 1.45; white-space: pre-wrap; overflow-wrap: anywhere;
  max-height: 16em; overflow-y: auto; }   /* a very long post scrolls inside its card, not the stack */
.truth-img { display: block; width: 100%; max-height: 220px; object-fit: contain; border-radius: 10px; margin: 0 0 12px;
  background: rgba(255, 255, 255, .03); }
.truth-img.hidden { display: none; }
.truth-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; font-size: .8rem; color: var(--muted);
  border-top: 1px solid var(--border); padding-top: 10px; }
.truth-foot a { margin-left: auto; color: var(--accent); text-decoration: none; }
.truth-foot a:hover { text-decoration: underline; }
@media (prefers-reduced-motion: reduce) { .truth-card { animation: none; } }

/* ---------- visual alerts (the Visual alerts buttons; Settings > Notifications) ----------
   For multi-monitor setups: see visualNotify() in app.js. A glowing FRAME round
   the screen edges, never a full-screen flash; two slow pulses (about one a
   second, well under the three-a-second photosensitivity limit), then a steady
   frame and a banner until you look. Under the Trump cards (z 300), over
   everything else, and never catches the pointer. */
#vflash { position: fixed; inset: 0; pointer-events: none; z-index: 290; opacity: 0; --vc: var(--accent);
  box-shadow: inset 0 0 0 5px var(--vc), inset 0 0 80px 16px color-mix(in srgb, var(--vc) 55%, transparent);
  transition: opacity .35s ease; }
#vflash.soft { box-shadow: inset 0 0 0 3px var(--vc), inset 0 0 40px 6px color-mix(in srgb, var(--vc) 35%, transparent); }
#vflash.hold { opacity: .8; }
#vflash.pulse { animation: vpulse 1.9s ease-in-out 1; }
@keyframes vpulse { 0% { opacity: 0; } 22% { opacity: 1; } 46% { opacity: .15; } 70% { opacity: 1; } 100% { opacity: 0; } }
/* when it stays lit, the second pulse settles on the steady frame instead of
   dropping to nothing and snapping back, which would read as a third flash */
#vflash.hold.pulse { animation-name: vpulse-hold; }
@keyframes vpulse-hold { 0% { opacity: 0; } 22% { opacity: 1; } 46% { opacity: .15; } 70% { opacity: 1; } 100% { opacity: .8; } }
#vflash.soft.pulse { animation-name: vpulse-soft; }
@keyframes vpulse-soft { 0% { opacity: 0; } 30% { opacity: .9; } 100% { opacity: 0; } }
/* bottom centre: the Trump cards own the top left and the alert cards the top right */
#vstrip { position: fixed; bottom: 64px; left: 50%; transform: translateX(-50%); z-index: 291; --vc: var(--accent);
  max-width: min(640px, calc(100vw - 32px)); display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px;
  padding: 12px 18px; border-radius: 12px; cursor: pointer; color: var(--text);
  background: color-mix(in srgb, var(--bg) 86%, var(--vc)); border: 2px solid var(--vc);
  box-shadow: 0 0 30px color-mix(in srgb, var(--vc) 55%, transparent); }
#vstrip b { font: 700 .78rem var(--mono); letter-spacing: .14em; color: var(--vc); }
#vstrip span { font-weight: 600; line-height: 1.35; }
#vstrip small { flex-basis: 100%; color: var(--muted); font-size: .72rem; }
/* the last minute's headlines, on the wire and in the Terminal: a steady tint,
   not an animation, because both lists redraw every few seconds */
.item.vnew, .trow.vnew { box-shadow: inset 3px 0 0 var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent); }
.vis-btn.on { color: var(--text); border-color: var(--accent); background: color-mix(in srgb, var(--accent) 22%, transparent);
  box-shadow: 0 0 12px color-mix(in srgb, var(--accent) 40%, transparent); }
.panel-title .vis-btn { margin-left: 8px; padding: 3px 10px; border-radius: 999px; cursor: pointer; flex: 0 0 auto;
  font: 500 .62rem var(--mono); letter-spacing: .08em; text-transform: uppercase; color: var(--muted);
  border: 1px solid var(--border); background: var(--panel2); }
.panel-title .vis-btn.on { color: var(--text); border-color: var(--accent); background: color-mix(in srgb, var(--accent) 22%, transparent); }
@media (prefers-reduced-motion: reduce) {
  #vflash.pulse { animation: none; opacity: .85; }
  #vflash.soft.pulse { animation: none; opacity: .5; }
}

/* ---- asking for desktop alerts (app.js notifyAsk) ----
   Bottom left: the alert cards own the top right, Trump's posts and the
   editor's notes the top left, the toast the bottom centre. The affiliate card
   shares the bottom left, so while it is up this sits above it. */
.notify-ask { position: fixed; left: 16px; bottom: 16px; z-index: 120; width: min(340px, calc(100vw - 32px));
  background: var(--panel); border: 1px solid color-mix(in srgb, var(--accent) 45%, var(--border));
  border-left: 3px solid var(--accent); border-radius: 8px; padding: 14px 16px 14px;
  box-shadow: 0 10px 30px rgba(0,0,0,.35); display: grid; gap: 8px;
  opacity: 0; transform: translateY(10px); transition: opacity .3s ease, transform .3s ease; }
.notify-ask.in { opacity: 1; transform: none; }
.notify-ask b { font-size: .95rem; }
.notify-ask p { margin: 0; font-size: .82rem; color: var(--muted); line-height: 1.45; }
.notify-ask .na-row { display: flex; gap: 8px; }
@media (prefers-reduced-motion: reduce) { .notify-ask { transition: none; } }
/* when the affiliate card is up, sit above it rather than over it */
body.pslot-on .notify-ask { bottom: calc(var(--ps-band) + 16px); }

/* ---------- phones: every header button on screen, upright or sideways ----------
   Jason, 2026-10-01: "lots of buttons cut off" on mobile. Below 901 the header
   stacks into rows, but the nav kept its desktop behaviour - one line that
   scrolls sideways with a hidden scrollbar - so on a 390px phone Terminal,
   Options, Video and the two theme buttons sat past the edge with nothing to
   say they were there. On the phone layout the rows WRAP instead: taller, but
   every button can be seen and tapped. Desktop (901+) is untouched; its ladder
   is guarded by tools/headercheck.js. */
@media (max-width: 900px) {
  .top { gap: 8px; padding-left: 10px; padding-right: 10px; }
  .top .left, .top .right, .actions { flex-wrap: wrap; row-gap: 6px; }
  .actions { overflow: visible; justify-content: flex-start; gap: 5px; padding-top: 0; margin-top: 0; }
  #btn-mfvis { margin-right: 0; }
}
/* ---------- Gold (Jason, 2026-10-01) ----------
   "once they are pro there is a golden glow behind the trademade button top left".
   body.pro-on is set by app.js goldMarks() from the server's is_pro (ME.pro, the
   owner included) and follows the live "account" push, so it comes and goes
   without a reload. body.gold-hint is the heart's hint that Gold exists: free
   readers only, and only while Gold is on sale to them (pro.checkout).
   Both are drawn with box-shadow on a pseudo-element: nothing here takes up
   space, so the header's width and its one-row ladder are untouched. Where the
   wordmark hides (L2 and the phone layout) the brand is just the orb, so the
   glow is just round the orb. Neutral names only - no ad-like class names. */
:root { --gold: #f2c14e; --gold-deep: #e0a526; }
html[data-mode="light"] { --gold: #e3a514; --gold-deep: #c58a06; }
body.pro-on .brand { position: relative; border-color: color-mix(in srgb, var(--gold) 60%, var(--border)); }
body.pro-on .brand b { color: var(--gold); }
body.pro-on .brand .orb { background: radial-gradient(circle at 35% 35%, #fff, var(--gold) 45%, var(--gold-deep)); box-shadow: 0 0 14px var(--gold); }
body.pro-on .brand::before { content: ""; position: absolute; inset: -1px; border-radius: inherit; pointer-events: none;
  box-shadow: 0 0 12px 2px color-mix(in srgb, var(--gold) 50%, transparent), 0 0 30px 8px color-mix(in srgb, var(--gold) 20%, transparent);
  animation: gold-breathe 6s ease-in-out infinite; }
body.gold-hint .donate-icon { position: relative; color: var(--gold); border-color: color-mix(in srgb, var(--gold) 45%, var(--border)); }
body.gold-hint .donate-icon::before { content: ""; position: absolute; inset: -1px; border-radius: inherit; pointer-events: none;
  box-shadow: 0 0 10px 1px color-mix(in srgb, var(--gold) 35%, transparent);
  animation: gold-breathe 6s ease-in-out infinite; }
@keyframes gold-breathe { 0%, 100% { opacity: .55; } 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  body.pro-on .brand::before, body.gold-hint .donate-icon::before { animation: none; opacity: .85; }
}
