/**
 * chart-desk-page.css — /chart-desk, built to the supplied spec.
 *
 * Palette, spacing, radii and proportions are taken VERBATIM from the spec:
 *   bg #f6f8fb · panel #ffffff · panelAlt #fbfcfe · border #e6ebf2
 *   text #132033 · muted #667085 · green #16a34a · red #dc2626
 *   radius 16px · shadow 0 8px 30px rgba(15,23,42,.06)
 *   rows 64px / 1fr / 170px · main 240px | 1fr | 304px · gaps 14px
 *
 * ONE DELIBERATE DEPARTURE. The spec's generic sample uses blue #2563eb for
 * active chips and tabs; the JagSim mockup screenshots use the brand emerald.
 * Emerald is used here, because every other page on the site is emerald and a
 * blue-accented chart would be the only screen that disagrees. It is one token
 * (--cdp-accent) — say the word and it is blue.
 *
 * LIGHT ONLY, per "Must not include: dark mode". The dark toggle stays on
 * /chart; this page does not inherit it.
 *
 * WHY A SEPARATE PAGE. chart.html is ~4,000 lines built around a sidebar shape.
 * Retrofitting this layout onto it broke the live chart twice. Nothing here can
 * affect /chart.
 */

:root {
  --cdp-bg:         #f6f8fb;
  --cdp-panel:      #ffffff;
  --cdp-panel-2:    #fbfcfe;
  --cdp-border:     #e6ebf2;
  /* #dde4ec, was #eef2f7. The old value is ~1.05:1 against the white panel -
     mathematically present, visually absent. Every row rule in the Positions /
     Open orders / Filled / Cancelled dock uses this token, which is why the owner
     said of the Filled tab: "you can't see any lines of separation. What the fuck
     is going on?" Still a hairline, now an actually visible one. */
  --cdp-line:       #dde4ec;
  --cdp-text:       #132033;
  --cdp-muted:      #667085;
  --cdp-green:      #16a34a;
  --cdp-green-soft: #eaf8ef;
  --cdp-red:        #dc2626;
  --cdp-red-soft:   #fcecec;
  --cdp-accent:     var(--jag-green, var(--jag-green));   /* JagSim emerald, in place of the sample's blue */
  --cdp-accent-soft:var(--jag-green-soft, #E8F7EF);
  --cdp-accent-line:#bfe3d2;
  --cdp-shadow:     0 8px 30px rgba(15, 23, 42, .06);
  /* ── TWO TOKENS THIS FILE READS AND NEVER DEFINED ───────────────────────
     --cdp-surface is referenced by six rules and --cdp-ink by one, and
     neither was declared anywhere in css/. Every one of those rules therefore
     fell through to its hard-coded fallback — #fff and #17211d — in BOTH
     themes, which is why the alert popover, the Time-in-force select, the
     Panels dropdown, the chart-type menu, the watchlist sort menu, the symbol
     typeahead and the saved-layouts modal all opened as white cards no matter
     what the theme block said. A var() fallback is not a definition; it is
     what happens when there isn't one. These values match the fallbacks
     exactly, so light mode is unchanged. */
  --cdp-surface:    #ffffff;
  --cdp-ink:        #17211d;
  --cdp-radius:     16px;
}

* { box-sizing: border-box; }
html, body.cdp {
  margin: 0; height: 100%;
  font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  background: var(--cdp-bg); color: var(--cdp-text);
}
/* The desk used to be overflow:hidden, which meant anything that did not fit was
   simply unreachable - on a short window the dock and the Market Context strip
   were amputated rather than scrolled to. Nothing on this page may be cut off.
   overflow-x stays hidden so a wide panel can never produce a sideways scroll. */
body.cdp { overflow-x: hidden; overflow-y: auto; }
html { scroll-behavior: smooth; }

/* ── Frame: nav / workspace / support strip ────────────────────────────────── */
.cdp-app {
  /* The desk is a fixed-height frame, but .pt-appbar (the site header, 88px and
     position:sticky) sits OUTSIDE it and above it in the document. At a flat
     100vh the two stacked to 100vh+88px inside a 100vh window, so the bottom
     88px - the market-context strip - fell off the screen with body overflow
     hidden to keep it there. Subtract the header instead of clipping it.
     Below 1240px this is overridden to height:auto, where it does not apply. */
  --cdp-appbar-h: 88px;
  /* Both behaviours at once, and the order matters.
     height  - on a normal window the frame is exactly the space under the header,
               so every panel including the Market Context strip fits on screen
               with no scrolling. This is the layout as designed.
     min-height - a floor summing the fixed rows (banner 57 + chart 460 + dock 168
               + context 65 + three 14px gaps + 28px padding). On a SHORT window
               that floor beats the calc, the frame stays legible and the PAGE
               scrolls instead of amputating the bottom panels.
     height:auto alone was wrong: the chart row sized to its content (663px) and
     pushed the market strip off screen even on a tall window. */
  /* min-height, NOT height. There is a footer below this frame now, and a fixed
     height would put it permanently off-screen with nothing able to reach it -
     which is exactly what the owner hit ("I can't scroll"). The desk still fills
     the window on a normal screen; the page grows and scrolls when it must. */
  min-height: calc(100vh - var(--cdp-appbar-h) - 8px);
  padding: 14px; display: grid;
  /* THREE rows, not four. .cdp-topbar is NOT a direct child of .cdp-app, so the
     leading 64px row had no element to hold: the chart (.cdp-main) fell into it
     and was pinned to 64px while the dock took the flexible row. Measured in a
     browser: main 64px / bottom 486px. Correct order is chart, dock, context. */
  /* FOUR ROWS: chart, attribution, dock, footer. Adding the credit strip as a
     grid child without adding its row made it take the DOCK's 168px track and
     shove everything below it down the page - a band of dead space under the
     chart and the dock pushed off. Its row is `auto`, so the strip is only as
     tall as one line of 10.5px text and the dock returns to where it was. */
  /* THE DOCK ROW, and the reason Positions still showed two rows after the last
     attempt. This literal 168px is what actually sizes the dock; raising
     --cdp-dock-h alone only changed the chart's max-height reserve and the
     1101-1280 band, so the panes never grew. Same class of bug as the stale 280
     found earlier: one height, defined in three places, disagreeing. It reads the
     variable now, so there is ONE number. */
  grid-template-rows: minmax(0, 1fr) auto var(--cdp-dock-h) auto; gap: 14px;
}
.cdp-main {
  display: grid; grid-template-columns: 240px minmax(0, 1fr) 304px;
  gap: 14px;
  /* A floor for the chart row. With the frame on min-height the 1fr row would
     otherwise collapse toward its content on a short window and squash the
     candles to nothing; below this the page scrolls instead. */
  min-height: 460px;
}
.cdp-bottom {
  display: grid; grid-template-columns: 1.25fr 1fr 1fr; gap: 14px; min-height: 0;
}

.cdp-panel {
  background: var(--cdp-panel); border: 1px solid var(--cdp-border);
  border-radius: var(--cdp-radius); box-shadow: var(--cdp-shadow);
  min-height: 0; overflow: hidden;
  /* DO NOT ADD position:relative HERE. I did, on 2026-08-19, as a preventive sweep
     after a real containing-block bug on the PORTFOLIO page. It fixed nothing here --
     the only .cdp-sr-only on this page is at chart.html:184, at the top of the body,
     not inside a panel's scrolling body -- and it broke two things at once, because it
     re-anchored every absolutely-positioned descendant of every panel: the Saved
     layouts dialog rendered off the left edge of the screen at negative x, and the
     price axis detached from the plot. A speculative fix on a page with no symptom is
     all downside. */
}
.cdp-panel-header {
  padding: 14px 16px; border-bottom: 1px solid var(--cdp-border);
  display: flex; justify-content: space-between; align-items: center;
  font-weight: 800; font-size: 14px; letter-spacing: -.01em; gap: 10px;
}
.cdp-panel-header .muted { color: var(--cdp-muted); font-weight: 600; font-size: 12px; }

/* Order Ticket header: title on the left, symbol + session pill on the right.
   .cdp-tkt-hdr had NO rule at all, so the pill sat in normal flow and rode over
   the "Order Ticket" title once the panel narrowed. The title is allowed to
   shrink and ellipsis; the pill never shrinks, because a half-clipped
   "Market Clos..." on a page that queues orders is worse than a short title. */
.cdp-panel-header:has(.cdp-tkt-hdr) { gap: 10px; }
.cdp-panel-header > span:first-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cdp-tkt-hdr {
  display: inline-flex; align-items: center; gap: 8px;
  margin-left: auto; flex: 0 0 auto; min-width: 0;
}
.cdp-tkt-hdr #cdpTicketSym { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.cdp-tkt-hdr .cdp-pill { flex: 0 0 auto; white-space: nowrap; }
.cdp-panel-body { padding: 14px 16px; height: calc(100% - 58px); overflow: auto; }

/* ── Top bar ───────────────────────────────────────────────────────────────── */
.cdp-topbar {
  background: var(--cdp-panel); border: 1px solid var(--cdp-border);
  border-radius: var(--cdp-radius); box-shadow: var(--cdp-shadow);
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 16px; gap: 16px; min-height: 64px;
}
.cdp-brand { display: flex; align-items: center; gap: 10px; min-width: 0; }
.cdp-brand img { height: 26px; width: auto; display: block; }
.cdp-tabs { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; overflow: hidden; }
.cdp-tab-link {
  padding: 8px 11px; border-radius: 999px; border: 1px solid transparent;
  background: transparent; color: #334155; font-size: 13px; font-weight: 600;
  line-height: 1; white-space: nowrap; text-decoration: none;
}
.cdp-tab-link:hover { background: var(--cdp-panel-2); }
.cdp-tab-link.active {
  background: var(--cdp-accent-soft); color: var(--cdp-accent); border-color: var(--cdp-accent-line);
}
.cdp-top-actions { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; }
.cdp-search {
  min-width: 220px; max-width: 340px; border: 1px solid var(--cdp-border);
  background: var(--cdp-panel-2); border-radius: 999px; padding: 10px 14px;
  color: var(--cdp-text); font-size: 13px; font-family: inherit; outline: none;
}
.cdp-search::placeholder { color: var(--cdp-muted); }
.cdp-search:focus { border-color: var(--cdp-accent); box-shadow: 0 0 0 3px var(--cdp-accent-soft); }
.cdp-pill {
  border: 1px solid var(--cdp-border); background: var(--cdp-panel-2); border-radius: 999px;
  padding: 8px 12px; font-size: 13px; display: inline-flex; align-items: center; gap: 8px;
  color: var(--cdp-text); white-space: nowrap; text-decoration: none;
}
/* SESSION STATUS IS A STATUS, NOT AN ALARM. "Market Closed" was painted in the
   page's own error red on a pink field — the loudest language the desk has,
   spent on the most ordinary fact there is: it is 8pm. It also failed 4.5:1 in
   BOTH states (red 4.22:1, green 3.01:1), because saturated ink on a tint of
   itself has nowhere left to go.

   Colour moves out of the label and into a 7px dot; the label sits in near-black
   on a quiet surface. Open and closed differ on THREE independent channels — dot
   fill (solid vs hollow), hue (emerald vs slate) and surface tint — so the state
   survives greyscale and every form of colour blindness. No amber: an overnight
   session is not a warning. Red stays reserved for the active SELL side, which is
   what made this read as a fault in the first place. */
.cdp-pill::before {
  content: ""; flex: 0 0 auto; width: 7px; height: 7px; border-radius: 50%;
  background: transparent; box-shadow: inset 0 0 0 2px #74839a;
}
.cdp-pill.green { background: var(--cdp-green-soft); border-color: #c3e4d2; color: #14532d; font-weight: 600; }
.cdp-pill.green::before { background: #15803d; box-shadow: 0 0 0 3px rgba(21,128,61,.14); }
.cdp-pill.red   { background: #f1f4f8; border-color: #d8e0ea; color: #334155; font-weight: 600; }
.cdp-pill.red::before { background: transparent; box-shadow: inset 0 0 0 2px #5b6b82; }

/* ── Watchlist ─────────────────────────────────────────────────────────────── */
.cdp-wl { width: 100%; border-collapse: collapse; font-size: 13px; }
.cdp-wl th, .cdp-wl td {
  text-align: left; padding: 10px 0; border-bottom: 1px solid var(--cdp-line); white-space: nowrap;
}
.cdp-wl th { color: var(--cdp-muted); font-weight: 700; font-size: 12px; }
.cdp-wl td:nth-child(2), .cdp-wl th:nth-child(2),
.cdp-wl td:nth-child(3), .cdp-wl th:nth-child(3) { text-align: right; font-variant-numeric: tabular-nums; }
.cdp-wl tbody tr { cursor: pointer; }
.cdp-wl tbody tr:hover td { background: var(--cdp-panel-2); }
.cdp-wl tbody tr.on td { background: var(--cdp-accent-soft); }
.cdp-wl td:first-child { font-weight: 700; }
.up   { color: var(--cdp-green); font-weight: 700; }
.down { color: var(--cdp-red);   font-weight: 700; }

/* ── Chart panel: 58 / flex / 72, the chart is the hero ────────────────────── */
/* 88px, not 72. The footer cards are label + value (about 40px) inside 20px of
   their own padding, in a strip that also has 24px of padding - 84px of content
   in a 72px box, so every card was cropped along its bottom edge and the rounded
   corners were sliced off. */
/* position:relative so the settings popover has a sane origin. Without it the
   popover's `top` resolved against some far ancestor and it landed at the very
   bottom-right of the page, nowhere near the gear that opens it. */
/* ROW ONE IS auto, NOT 58px. The toolbar's left column stacks THREE lines —
   symbol+price, the submeta run, and the Open / Day range / Volume stats — which
   needs more than 58px. A fixed grid row clipped the third line no matter what
   the toolbar itself declared, so "Volume 15.1M" was sliced through mid-glyph.
   min-height on the toolbar keeps the old floor for the common two-line case. */
.cdp-chart-wrap { position: relative; display: grid; grid-template-rows: auto minmax(0, 1fr) 88px; height: 100%; min-height: 0; }
/* Fullscreen paints its own black backdrop behind the element, so the panel has
   to carry its own surface once it leaves the page flow, or the chart sits on a
   void with its rounded corners cut out. */
.cdp-chart-wrap:fullscreen {
  background: var(--cdp-panel); padding: 12px; border-radius: 0;
}
.cdp-chart-wrap:-webkit-full-screen {
  background: var(--cdp-panel); padding: 12px; border-radius: 0;
}
.cdp-chart-toolbar {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 16px; border-bottom: 1px solid var(--cdp-border); gap: 16px; min-width: 0;
}
.cdp-chart-meta { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.cdp-symbol { font-size: 18px; font-weight: 900; line-height: 1; }
.cdp-submeta { color: var(--cdp-muted); font-size: 12px; line-height: 1.2; }
.cdp-chart-controls { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }
.cdp-chip {
  padding: 7px 10px; border-radius: 10px; background: var(--cdp-panel-2);
  border: 1px solid var(--cdp-border); font-size: 12px; color: var(--cdp-text);
  line-height: 1; white-space: nowrap; cursor: pointer; font-family: inherit;
}
.cdp-chip:hover { border-color: var(--cdp-accent); color: var(--cdp-accent); }
.cdp-chip.active {
  background: var(--cdp-accent-soft); border-color: var(--cdp-accent-line);
  color: var(--cdp-accent); font-weight: 800;
}
#cdpChart { min-height: 0; height: 100%; width: 100%; }
.cdp-chart-footer {
  /* FOUR cards live here - Open, Day Range, Volume, Data - but this was
     repeat(3, 1fr). The fourth wrapped onto an implicit second row inside a strip
     only tall enough for one, so the Data card (the provenance badge and the
     "as of" time) was invisible, and the three above it looked cropped. */
  border-top: 1px solid var(--cdp-border); display: grid; grid-template-columns: repeat(4, 1fr);
  gap: 10px; padding: 12px 16px; background: linear-gradient(180deg, #fff, #fcfdff);
}
.cdp-mini {
  border: 1px solid var(--cdp-border); background: var(--cdp-panel-2);
  border-radius: 14px; padding: 10px 12px; min-width: 0;
}
.cdp-mini-label { font-size: 12px; color: var(--cdp-muted); margin-bottom: 6px; }
.cdp-mini-value { font-size: 15px; font-weight: 900; line-height: 1.2; white-space: nowrap; }

/* ── Order ticket: execution, not analytics ────────────────────────────────── */
.cdp-ticket-tabs { display: flex; gap: 8px; margin-bottom: 14px; }
.cdp-side {
  flex: 1; border: 1px solid var(--cdp-border); background: var(--cdp-panel-2);
  border-radius: 12px; padding: 10px; font-weight: 800; text-align: center;
  cursor: pointer; font-family: inherit; font-size: 14px; color: var(--cdp-muted);
}
.cdp-side.on.buy  { background: var(--cdp-green-soft); color: var(--cdp-green); border-color: #cbeed7; }
.cdp-side.on.sell { background: var(--cdp-red-soft);   color: var(--cdp-red);   border-color: #f5caca; }
.cdp-field { margin-bottom: 12px; }
.cdp-flabel { font-size: 12px; color: var(--cdp-muted); margin-bottom: 6px; display: block; font-weight: 600; }
.cdp-input, .cdp-select {
  width: 100%; border: 1px solid var(--cdp-border); background: #fff; border-radius: 12px;
  padding: 11px 12px; font-size: 14px; color: var(--cdp-text); outline: none; font-family: inherit;
}
.cdp-input:focus, .cdp-select:focus { border-color: var(--cdp-accent); box-shadow: 0 0 0 3px var(--cdp-accent-soft); }
.cdp-row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.cdp-btn-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 10px; }
.cdp-btn {
  border: 0; border-radius: 12px; padding: 12px; font-weight: 900; font-size: 14px;
  cursor: pointer; color: #fff; font-family: inherit;
}
.cdp-btn.buy  { background: var(--cdp-green); }
.cdp-btn.sell { background: var(--cdp-red); }
.cdp-ticket-note { font-size: 12px; color: var(--cdp-muted); line-height: 1.6; margin-top: 12px; }
.cdp-ticket-note strong { color: var(--cdp-text); }

/* ── Bottom strip ──────────────────────────────────────────────────────────── */
.cdp-tbl { width: 100%; border-collapse: collapse; font-size: 13px; }
.cdp-tbl th, .cdp-tbl td {
  text-align: left; padding: 9px 0; border-bottom: 1px solid var(--cdp-line); white-space: nowrap;
}
.cdp-tbl th { color: var(--cdp-muted); font-size: 12px; font-weight: 700; }
.cdp-tbl td:not(:first-child), .cdp-tbl th:not(:first-child) { text-align: right; font-variant-numeric: tabular-nums; }
.cdp-alert-item, .cdp-market-item {
  display: flex; justify-content: space-between; gap: 12px; padding: 10px 0;
  border-bottom: 1px solid var(--cdp-line); font-size: 13px; align-items: flex-start;
}
.cdp-alert-title { font-weight: 800; line-height: 1.2; }
.cdp-small { color: var(--cdp-muted); font-size: 12px; line-height: 1.2; margin-top: 3px; }
.cdp-section-title { font-size: 14px; font-weight: 900; margin: 0 0 10px; }
.cdp-heatmap { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.cdp-heat {
  height: 54px; border-radius: 12px; display: flex; align-items: flex-end; justify-content: flex-start;
  padding: 8px; font-size: 12px; font-weight: 800; color: #0f172a;
}
.cdp-heat.green { background: #ddf7e8; }
.cdp-heat.red   { background: #fde5e5; }
.cdp-heat.gray  { background: #edf2f7; }

/* Anything not yet wired to real data says so instead of implying it works. */
.cdp-stub-note {
  grid-column: 1 / -1; margin: 0 0 0; padding: 9px 14px; border-radius: 999px;
  background: #fff4e6; border: 1px solid #f0c4a0; color: #8a4b16;
  font-size: 12.5px; font-weight: 700; text-align: center;
}

@media (max-width: 1240px) {
  /* overflow:auto reset overflow-X back to auto on every phone and tablet. chart-desk.html
     does not load the shell CSS, so below 1240px that page had no horizontal clamp at any
     level. Keep the vertical scroll; keep the horizontal clamp. */
  body.cdp { overflow-y: auto; overflow-x: clip; }
  .cdp-app { height: auto; grid-template-rows: auto auto auto auto; }
  .cdp-main { grid-template-columns: minmax(0, 1fr); }
  .cdp-bottom { grid-template-columns: minmax(0, 1fr); }
  .cdp-chart-wrap { grid-template-rows: auto 420px auto; }
}

/* ═══ HIERARCHY PASS ═══════════════════════════════════════════════════════
   From the design critique: the chart must read as the hero, the price must be
   the loudest thing in its header, the ticket must feel like execution rather
   than a settings form, and the bottom strip must read as one secondary dock
   instead of three equal boxes competing with the chart. */

/* Every panel header is the SAME height. They were content-sized, so the three
   bottom panels sat a few pixels apart and the whole row read as assembled
   rather than designed. */
.cdp-panel-header { height: 58px; padding: 0 16px; flex: 0 0 auto; }
.cdp-panel-body   { height: calc(100% - 58px); }

/* Price is the loudest thing on the screen after the chart itself. */
.cdp-sym-row { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.cdp-symbol { font-size: 20px; font-weight: 900; line-height: 1; letter-spacing: -.02em; }
.cdp-last {
  font-size: 20px; font-weight: 800; line-height: 1;
  font-variant-numeric: tabular-nums; color: var(--cdp-text);
}
.cdp-chg { font-size: 13px; font-weight: 800; font-variant-numeric: tabular-nums; }
.cdp-chg.up { color: var(--cdp-green); } .cdp-chg.down { color: var(--cdp-red); }
.cdp-submeta { font-variant-numeric: tabular-nums; }

/* The chart body sits tight against its controls; the gap read as slack. */
#cdpChart { border-top: 0; }
.cdp-chart-toolbar { padding: 6px 16px; min-height: 58px; height: auto; }

/* Ticket: one dominant action, not two equal buttons. The BUY/SELL pair above
   chooses the SIDE; this executes it, and its label states what will actually
   happen (queue vs fill) rather than a generic verb. */
.cdp-cta-main {
  width: 100%; padding: 14px; font-size: 15px; letter-spacing: -.01em; margin-top: 2px;
}
/* opacity composites the whole button as a group: white on #96d6ae is 1.68:1,
     so the one control that spends money went near-blank and read as a rendering
     fault rather than as "not yet". Solid grey states the same thing legibly. */
.cdp-cta-main:disabled { opacity: 1; background: #64748b; color: #fff; cursor: not-allowed; }
.cdp-cta-sub { margin: 7px 0 0; font-size: 12px; color: var(--cdp-muted); line-height: 1.45; }
.cdp-side { font-size: 13px; letter-spacing: .04em; }
.cdp-field { margin-bottom: 11px; }
.cdp-ticket-note { border-top: 1px solid var(--cdp-line); margin-top: 14px; padding-top: 12px; padding-bottom: 4px; }
.cdp-kv { display: flex; justify-content: space-between; padding: 6px 0; font-size: 12.5px; }
.cdp-kv:last-child { padding-bottom: 4px; }
/* The ticket column is as tall as the chart. Size the WHITE CARD to that
   column (not a short auto box sitting in grey leftover). Header takes its
   real height; the body fills the rest so Current position is inside the
   card, not clipped by overflow:hidden + a 58px header guess. */
#cdpTicketPanel {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
  overflow: hidden;
  align-self: stretch;
}
#cdpTicketPanel .cdp-panel-header {
  flex: 0 0 auto;
  height: auto;
  min-height: 58px;
}
#cdpTicketPanel .cdp-panel-body {
  flex: 1 1 auto;
  height: auto !important;
  min-height: 0;
  overflow-y: auto;
  padding-bottom: 20px;
  box-sizing: border-box;
}
@media (max-width: 1240px) {
  #cdpTicketPanel { height: auto; overflow: visible; }
  #cdpTicketPanel .cdp-panel-body {
    overflow: visible;
    padding-bottom: 28px;
  }
}
.cdp-kv span { color: var(--cdp-muted); }
.cdp-kv b { color: var(--cdp-text); font-variant-numeric: tabular-nums; }

/* Bottom dock reads as SUPPORT: same header height, same table density, and a
   slightly recessed surface so it sits behind the chart in the hierarchy rather
   than beside it. */
.cdp-bottom .cdp-panel { background: var(--cdp-panel-2); }
.cdp-bottom .cdp-panel-header { background: var(--cdp-panel); }
.cdp-bottom .cdp-panel-body { padding: 10px 16px; }
.cdp-bottom .cdp-tbl th { padding-top: 0; }

/* ═══ TRADING-TOOL LAYER ═══════════════════════════════════════════════════
   The critique's remaining gap: the page had a chart but not the tool surface
   that makes a chart feel like a workspace. Icon buttons rather than word
   buttons -- a real terminal never spells out "Horizontal line" on the toolbar,
   and 22 text labels would wrap into three rows and swamp the chart. Every
   button carries title + aria-label so the meaning is still reachable. */
.cdp-chart-wrap { grid-template-rows: 58px auto minmax(0, 1fr) 88px; }
.cdp-canvasrow { display: grid; grid-template-columns: 44px minmax(0, 1fr); min-height: 0; }

.cdp-tools {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 8px 16px; border-bottom: 1px solid var(--cdp-border); background: #fff;
}
.cdp-toolgrp { display: flex; align-items: center; gap: 3px; }
/* Hairline between groups so drawing / edit / view / market read as clusters
   instead of one undifferentiated strip of 22 icons. */
.cdp-toolgrp + .cdp-toolgrp { padding-left: 14px; border-left: 1px solid var(--cdp-line); }
.cdp-toolgrp-end { margin-left: auto; }

.cdp-tool {
  width: 30px; height: 30px; display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid transparent; background: transparent; color: var(--cdp-muted);
  border-radius: 8px; cursor: pointer; padding: 0;
  transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.cdp-tool svg { width: 16px; height: 16px; }
.cdp-tool:hover { background: var(--cdp-panel-2); color: var(--cdp-text); border-color: var(--cdp-border); }
.cdp-tool.on {
  background: var(--cdp-accent-soft); border-color: var(--cdp-accent-line); color: var(--cdp-accent);
}
.cdp-tool.fired { background: var(--cdp-accent); border-color: var(--cdp-accent); color: #fff; }

/* ── Symbol actions, next to the price where the decision is made ─────────── */
.cdp-symbol-actions { display: inline-flex; gap: 6px; margin-left: 4px; }
.cdp-icon-btn {
  border: 1px solid var(--cdp-border); background: #fff; border-radius: 9px;
  padding: 5px 9px; font: inherit; font-size: 11.5px; font-weight: 700;
  cursor: pointer; color: var(--cdp-muted); line-height: 1;
  box-shadow: 0 1px 2px rgba(15, 23, 42, .04); white-space: nowrap;
}
.cdp-icon-btn:hover { color: var(--cdp-text); border-color: var(--cdp-muted); }
.cdp-icon-btn.favorite.on { color: #b45309; background: #fff7ed; border-color: #f3d3a8; }
.cdp-icon-btn.add-watchlist.on { color: var(--cdp-accent); background: var(--cdp-accent-soft); border-color: var(--cdp-accent-line); }

/* ── Watchlist utilities ───────────────────────────────────────────────────── */
.cdp-wl-actions { display: inline-flex; gap: 4px; }
.cdp-mini-btn {
  width: 24px; height: 24px; border: 1px solid var(--cdp-border); background: #fff;
  border-radius: 7px; cursor: pointer; color: var(--cdp-muted);
  font: inherit; font-size: 13px; font-weight: 800; line-height: 1; padding: 0;
}
.cdp-mini-btn:hover { color: var(--cdp-accent); border-color: var(--cdp-accent-line); background: var(--cdp-accent-soft); }
.cdp-star {
  border: 0; background: transparent; cursor: pointer; padding: 0 6px 0 0;
  color: #cbd5e1; font-size: 13px; line-height: 1;
}
.cdp-star:hover { color: #f59e0b; }
.cdp-star.on { color: #f59e0b; }
.cdp-wl td:first-child { display: flex; align-items: center; gap: 2px; }

/* Ticket spacing tightened per the critique. */
.cdp-ticket-tabs { margin-bottom: 12px; }
.cdp-panel-body { padding: 13px 16px; }

/* ═══ TICKET CONTROLS ══════════════════════════════════════════════════════ */
/* Order type as a segmented row, not a <select>. A dropdown hides two of the
   three choices behind a click and gives no sense of which is fastest; a row
   shows the whole decision at once, which is what an execution panel should do. */
.cdp-seg { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 6px; }
.cdp-seg-btn {
  border: 1px solid var(--cdp-border); background: var(--cdp-panel-2); color: var(--cdp-muted);
  border-radius: 10px; font: inherit; font-size: 12.5px; font-weight: 800;
  padding: 9px 6px; cursor: pointer; line-height: 1;
}
.cdp-seg-btn:hover { color: var(--cdp-text); border-color: var(--cdp-muted); }
.cdp-seg-btn.on {
  background: var(--cdp-accent); border-color: var(--cdp-accent); color: #fff;
  box-shadow: 0 1px 2px rgba(0,110,78,.25);
}

/* Presets beside free entry. Both, deliberately: presets are faster, the input
   is exact, and a trader wants whichever suits the moment. */
.cdp-presets { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; margin-top: 7px; }
.cdp-preset {
  border: 1px solid var(--cdp-border); background: #fff; color: var(--cdp-text);
  border-radius: 9px; font: inherit; font-size: 12px; font-weight: 800;
  padding: 7px 4px; cursor: pointer; line-height: 1;
}
.cdp-preset:hover { border-color: var(--cdp-accent); color: var(--cdp-accent); background: var(--cdp-accent-soft); }
.cdp-preset.on { background: var(--cdp-accent-soft); border-color: var(--cdp-accent-line); color: var(--cdp-accent); }
.cdp-preset-max { color: var(--cdp-accent); border-color: var(--cdp-accent-line); }

/* Price stepper with real up/down affordances, per the reference screenshot. */
.cdp-stepper { position: relative; }
.cdp-stepper .cdp-input { padding-right: 30px; font-variant-numeric: tabular-nums; }
/* Hide the native spinner so there is exactly ONE control, not two stacked. */
.cdp-stepper input::-webkit-outer-spin-button,
.cdp-stepper input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.cdp-stepper input[type=number] { -moz-appearance: textfield; }
.cdp-step-btns {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  display: flex; flex-direction: column; gap: 1px;
}
.cdp-step {
  width: 18px; height: 13px; border: 1px solid var(--cdp-border); background: var(--cdp-panel-2);
  color: var(--cdp-muted); cursor: pointer; padding: 0; line-height: 1; font-size: 7px;
  display: flex; align-items: center; justify-content: center;
}
.cdp-step.up { border-radius: 5px 5px 0 0; } .cdp-step.down { border-radius: 0 0 5px 5px; }
.cdp-step:hover { background: var(--cdp-accent-soft); color: var(--cdp-accent); border-color: var(--cdp-accent-line); }
/* Price is meaningless on a Market order; dim rather than remove so the panel
   does not reflow every time the order type changes. */
/* .45 on the wrapper took the Price label to 1.85:1, its value to 2.80:1 and the
   input border to 1.08:1 — and Market is the shipped default, so on FIRST PAINT
   the field is a washed rectangle with no visible edge. That reads as a field
   that failed to render, not as one that does not apply. */
.cdp-dim { pointer-events: none; }
.cdp-dim .cdp-input { color: #94a3b8; background: var(--jag-card-2, #f7f9fb); }

/* ═══ BOTTOM DOCK POLISH ═══════════════════════════════════════════════════ */
.cdp-hdr-tabs { display: inline-flex; gap: 2px; }
.cdp-htab {
  border: 0; background: transparent; font: inherit; font-size: 13px; font-weight: 800;
  color: var(--cdp-muted); padding: 5px 9px; border-radius: 8px; cursor: pointer;
}
.cdp-htab:hover { color: var(--cdp-text); }
.cdp-htab.on { background: var(--cdp-accent-soft); color: var(--cdp-accent); }
.cdp-hdr-tools { display: inline-flex; gap: 4px; }

.cdp-tbl thead th {
  position: sticky; top: 0; background: var(--cdp-panel-2); z-index: 1;
  border-bottom: 1px solid var(--cdp-border);
}
.cdp-tbl th.sortable { cursor: pointer; color: var(--cdp-text); }
.cdp-tbl th.sortable i { font-style: normal; color: var(--cdp-muted); font-size: 10px; }
.cdp-tbl tbody tr:hover td { background: var(--cdp-accent-soft); }
.cdp-tbl td:first-child { font-weight: 800; }
/* The percentage column carries a soft chip so profit and loss read at a glance
   down the column rather than being another run of plain numerals. */
.cdp-pct {
  font-weight: 800;
}
.cdp-tbl td.cdp-pct.up   { color: var(--cdp-green); }
.cdp-tbl td.cdp-pct.down { color: var(--cdp-red); }
.cdp-badge {
  display: inline-block; padding: 3px 8px; border-radius: 999px;
  font-size: 11px; font-weight: 800; letter-spacing: .02em;
}
.cdp-badge.open   { background: var(--cdp-accent-soft); color: var(--cdp-accent); }
.cdp-badge.filled { background: #eef2f7; color: var(--cdp-muted); }

.cdp-alert-item { align-items: flex-start; gap: 10px; }
.cdp-alert-ico { color: var(--cdp-muted);  /* was amber — an alert ICON is not a hazard */ font-size: 10px; line-height: 1.6; flex: 0 0 auto; }
.cdp-alert-ico.up { color: var(--cdp-green); }
.cdp-alert-main { flex: 1 1 auto; min-width: 0; }
.cdp-alert-val { font-size: 12px; font-weight: 800; color: var(--cdp-muted); white-space: nowrap; }
.cdp-alert-val.up { color: var(--cdp-green); }

/* ═══ MARKET CONTEXT — full-width strip, as in the mockup ══════════════════ */
.cdp-context {
  display: flex; align-items: center; gap: 0;
  background: var(--cdp-panel); border: 1px solid var(--cdp-border);
  border-radius: var(--cdp-radius); box-shadow: var(--cdp-shadow);
  padding: 0 6px 0 0; overflow: hidden; min-height: 54px;
}
.cdp-ctx-label {
  font-size: 12px; font-weight: 800; color: var(--cdp-text); line-height: 1.15;
  padding: 0 16px; flex: 0 0 auto; border-right: 1px solid var(--cdp-border); align-self: stretch;
  display: flex; align-items: center; background: var(--cdp-panel-2);
}
.cdp-ctx-item {
  display: flex; flex-direction: column; gap: 1px; padding: 9px 18px;
  border-right: 1px solid var(--cdp-line); flex: 0 0 auto; min-width: 0;
}
.cdp-ctx-item b { font-size: 11.5px; font-weight: 800; color: var(--cdp-text); letter-spacing: .02em; }
.cdp-ctx-item i {
  font-style: normal; font-size: 12.5px; font-weight: 700;
  color: var(--cdp-text); font-variant-numeric: tabular-nums;
}
.cdp-ctx-item em { font-style: normal; font-size: 11.5px; font-weight: 800; font-variant-numeric: tabular-nums; }
.cdp-ctx-item em.up { color: var(--cdp-green); } .cdp-ctx-item em.down { color: var(--cdp-red); }
/* Sector cells lean on a tinted ground so the heat reads without a separate
   heatmap block eating a whole panel. */
.cdp-ctx-item.heat { justify-content: center; }
.cdp-ctx-sep { width: 1px; align-self: stretch; background: var(--cdp-border); margin: 0 2px; }
.cdp-ctx-more {
  margin-left: auto; border: 1px solid var(--cdp-border); background: #fff; color: var(--cdp-muted);
  width: 28px; height: 28px; border-radius: 8px; cursor: pointer; font-size: 15px; line-height: 1;
  flex: 0 0 auto;
}
.cdp-ctx-more:hover { color: var(--cdp-accent); border-color: var(--cdp-accent-line); background: var(--cdp-accent-soft); }

/* ═══ TIER BAR + LEFT TOOLS RAIL ═══════════════════════════════════════════
   PLACEMENT DECISION, which is what the owner actually asked for. The
   Core/Analysis/Advanced switch sits in a thin strip directly ABOVE the rail it
   filters and hard-left, so it lines up over the rail's own column. Reasons it
   is not anywhere else:

   - In the top toolbar it would be one more chip among nine, disconnected from
     the tools it governs, and competing with the timeframes.
   - Inside the 44px rail it cannot carry real words; "Core / Analysis /
     Advanced" would collapse to C/A/X, which nobody can read cold.
   - In a settings menu it would be invisible, and a control nobody finds is the
     same as no control -- which is precisely this product's existing problem.

   Above the rail, left-aligned, it reads as a heading FOR that column: the
   spatial relationship does the explaining, no tooltip required. It costs 30px
   of height and never scrolls away. The one-line hint next to it says what the
   level means, so a new user is told rather than left to guess. */
.cdp-tierbar {
  display: flex; align-items: center; gap: 12px;
  padding: 6px 16px 6px 8px; border-bottom: 1px solid var(--cdp-border);
  background: var(--cdp-panel-2); min-height: 38px;
}
.cdp-tier { display: inline-flex; gap: 2px; background: #fff; border: 1px solid var(--cdp-border); border-radius: 9px; padding: 2px; }
/* An UNSELECTED tier read as disabled label text: transparent background, no
   border, muted grey. Nothing said "this is a button you may press", so Core and
   Analysis looked like captions next to the one green pill. They carry a surface
   and an edge now, so all three read as controls and only the ACTIVE one is
   filled. */
.cdp-tier-btn {
  border: 1px solid var(--cdp-border); background: var(--cdp-panel);
  font: inherit; font-size: 11.5px; font-weight: 800;
  color: var(--cdp-text); padding: 5px 11px; border-radius: 7px; cursor: pointer; line-height: 1;
}
.cdp-tier-btn:hover { border-color: var(--cdp-accent); color: var(--cdp-accent); }
.cdp-tier-btn.on { background: var(--cdp-accent); border-color: var(--cdp-accent); color: #fff; }
.cdp-tier-btn.on:hover { color: #fff; }
.cdp-tier-hint { font-size: 11.5px; color: var(--cdp-muted); font-weight: 600; }

/* Vertical rail hugging the chart's left edge, as in the reference. */
.cdp-rail {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 6px 0; border-right: 1px solid var(--cdp-border);
  background: var(--cdp-panel-2); overflow-y: auto; overflow-x: hidden;
}
.cdp-rail-sep { width: 22px; height: 1px; background: var(--cdp-border); margin: 5px 0; flex: 0 0 auto; }
.cdp-rtool {
  width: 30px; height: 30px; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid transparent; background: transparent; color: var(--cdp-muted);
  border-radius: 8px; cursor: pointer; padding: 0;
  transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.cdp-rtool[hidden] { display: none; }
.cdp-rtool svg { width: 16px; height: 16px; }
.cdp-rtool:hover { background: #fff; color: var(--cdp-text); border-color: var(--cdp-border); }
.cdp-rtool.on { background: var(--cdp-accent-soft); border-color: var(--cdp-accent-line); color: var(--cdp-accent); }
.cdp-rtool.fired { background: var(--cdp-accent); border-color: var(--cdp-accent); color: #fff; }

/* ═══ RAIL vs TOOLBAR — the split, and why it is not "whatever overflows" ═══
   The owner asked whether Advanced tools could spill onto the top bar to avoid a
   scrollbar. Yes — but split by INTERACTION TYPE, never by overflow pressure.

   Spilling "whatever does not fit" is genuinely bad practice: the same button
   then lives in different places depending on tier and window height, and users
   navigate toolbars by muscle memory and position, not by reading. A tool that
   moves is a tool you hunt for every time.

   The durable rule:
     RAIL    = MODAL tools. They change what your next click DOES, exactly one is
               armed at a time, and you switch between them constantly. A vertical
               column is right: it is scannable, it holds selection state
               visibly, and it sits against the canvas you are about to draw on.
     TOOLBAR = ONE-SHOT actions and toggles. Undo, redo, clear, lock, hide,
               snapshot, fullscreen, scale, and the studies. They fire and are
               done; none of them is ever "armed", so none needs a persistent
               column, and horizontally they read as a group of verbs.

   That removed 11 buttons from the rail. Advanced now shows 10 modal tools,
   which fits without scrolling at any realistic chart height — not by luck, but
   because the rail only ever holds one kind of thing. */
.cdp-tbcluster { display: inline-flex; align-items: center; gap: 10px; }
.cdp-tbgroup { display: inline-flex; align-items: center; gap: 2px; }
.cdp-tbgroup[hidden] { display: none; }
.cdp-tbgroup + .cdp-tbgroup { padding-left: 10px; border-left: 1px solid var(--cdp-border); }

/* TOOLBAR CROWDING.
   The icon row overflowed and painted a full-width native scrollbar straight
   across the icons. Hiding overflow is NOT an option here - the earlier bug
   where .cdp-tierbar sized to content and CLIPPED Save/Share off the panel is
   the cautionary precedent, and a clipped control is worse than an ugly one.
   So the row still scrolls, the scrollbar just stops painting over the icons,
   and the groups are allowed to wrap onto a second line before it comes to
   that. */
.cdp-toolbar, .cdp-tierbar {
  flex-wrap: wrap;
  row-gap: 8px;
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;            /* Firefox */
  -ms-overflow-style: none;         /* legacy Edge */
}
.cdp-toolbar::-webkit-scrollbar, .cdp-tierbar::-webkit-scrollbar { height: 0; width: 0; }
/* Nothing in the toolbar may shrink below its own content, or an icon becomes
   an unlabelled sliver that still takes a click. */
.cdp-toolbar > *, .cdp-tierbar > * { flex: 0 0 auto; }

/* Separator inside the More menu, for the saved-layout group. */
.cdp-menu-sep { height: 1px; margin: 6px 0; background: var(--cdp-border); }
.cdp-tbtn {
  width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid transparent; background: transparent; color: var(--cdp-muted);
  border-radius: 7px; cursor: pointer; padding: 0;
  transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.cdp-tbtn[hidden] { display: none; }
.cdp-tbtn svg { width: 15px; height: 15px; }
.cdp-tbtn:hover { background: #fff; color: var(--cdp-text); border-color: var(--cdp-border); }
.cdp-tbtn.on { background: var(--cdp-accent-soft); border-color: var(--cdp-accent-line); color: var(--cdp-accent); }
.cdp-tbtn.fired { background: var(--cdp-accent); border-color: var(--cdp-accent); color: #fff; }
/* The hint is the first thing to go when the strip gets tight — it is guidance,
   not a control. */
@media (max-width: 1500px) { .cdp-tier-hint { display: none; } }

/* 5 presets now (1/5/10/25/MAX), so the grid widens rather than squeezing four. */
.cdp-presets { grid-template-columns: repeat(5, 1fr) !important; }
.cdp-preset { padding: 7px 2px; font-size: 11.5px; }

/* Chart settings popover. Anchored under the toolbar rather than floating over
   the canvas: it is a short list, and covering the chart to change the chart is
   backwards. */
/* A POPOVER, not a slab. This used to be an in-flow strip occupying its own row
   of .cdp-chart-wrap, so opening three checkboxes SHRANK THE CHART - the setting
   you opened to adjust the chart made the chart smaller, and the only way out was
   an unstyled "Done" that read as a browser default. A settings panel must never
   resize the thing it configures. It floats over the toolbar now, anchored under
   the gear, and the chart does not move at all. */
.cdp-settings {
  /* TOP 64px, NOT calc(100% + 6px). #cdpSettings is a child of .cdp-chart-wrap,
     NOT of .cdp-tierbar - the `.cdp-tierbar{position:relative}` rule below was
     anchoring nothing. So `100%` meant the full height of the chart area and the
     panel appeared 6px BELOW the whole chart, bottom-right, far from the gear.
     The toolbar is grid row one at 58px; 58 + 6 puts this directly under it.
     Owner: "the grid settings should appear right underneath the grid scale and
     axis, and then users just click done and it's minimized." */
  position: absolute; z-index: 60; top: 64px; right: 16px;
  display: flex; gap: 16px; flex-wrap: wrap; align-items: center;
  padding: 12px 14px;
  border: 1px solid var(--cdp-border); border-radius: 12px;
  background: var(--cdp-panel); box-shadow: 0 14px 34px -12px rgba(15, 22, 40, .3);
  font-size: 12.5px; color: var(--cdp-text);
}
.cdp-settings[hidden] { display: none; }
.cdp-settings label { display: inline-flex; align-items: center; gap: 7px; cursor: pointer; font-weight: 700; }
.cdp-settings input { accent-color: var(--cdp-accent); width: 14px; height: 14px; cursor: pointer; }
/* The way out has to look like the way out. */
.cdp-settings-close {
  order: 99; margin-left: 4px;
  padding: 6px 14px; border-radius: 9px; border: 0;
  background: var(--cdp-accent); color: #fff;
  font: inherit; font-size: 12px; font-weight: 800; line-height: 1; cursor: pointer;
}
.cdp-settings-close:hover { background: var(--cdp-green, #005a40); }
/* The popover is positioned against the tier bar, so that bar owns the origin. */
.cdp-tierbar { position: relative; }

/* Hover polish across the supporting panels, per the critique. */
.cdp-alert-item:hover { background: var(--cdp-panel-2); }

/* ── The alert feed rows ───────────────────────────────────────────────────
   These come from the LEGACY dark simulator: css/simulator.css:2421 gives
   .feed-item background:rgba(6,12,24,.9) and color:#d2ddf1. That page was dark;
   this desk is light, so the rows rendered as black slabs stacked inside a white
   panel - the "rogue styling" on the Alerts tab. Restyled to sit in the same
   visual family as every other panel here: soft grey fill, real border, no
   inverted block. Scoped to body.cdp so the pages that are still dark keep the
   treatment they were written for. */
body.cdp .night-feed { display: grid; gap: 8px; align-content: start; }
body.cdp .feed-item {
  background: var(--cdp-panel-2);
  color: var(--cdp-text);
  border: 1px solid var(--cdp-border);
  border-radius: 10px;
  padding: 9px 11px;
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.45;
}
body.cdp .feed-item:hover {
  background: var(--cdp-panel);
  border-color: var(--cdp-accent-line);
}

/* The price-alert button ships with class .adv-dd-trigger, styled by a stylesheet
   this page does not load. It rendered as a raw browser button, and the count span
   sat flush against the label so it read "Alert1" as if that were the word. Given
   the desk's own chip treatment, with the count as a real badge. */
body.cdp #chartAlertBtn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 10px; border-radius: 9px;
  background: var(--cdp-panel-2); border: 1px solid var(--cdp-border);
  font-size: 12px; font-weight: 800; color: var(--cdp-text);
  line-height: 1; cursor: pointer; font-family: inherit;
}
body.cdp #chartAlertBtn:hover { border-color: var(--cdp-accent); color: var(--cdp-accent); }
body.cdp #chartAlertBtn .ca-btn-count {
  display: inline-grid; place-items: center;
  min-width: 16px; height: 16px; padding: 0 4px; border-radius: 999px;
  background: var(--cdp-accent); color: #fff; font-size: 10.5px; font-weight: 800;
}
.cdp-wl tbody tr:hover td { background: var(--cdp-accent-soft); }

/* ═══ STATE SYSTEM ═════════════════════════════════════════════════════════
   The states pass asked for stronger differences between active / hover /
   selected / saved / favorited / hidden / locked. They were not all
   distinguishable before: lock and hide flashed like momentary actions, so
   there was no way to tell whether drawings were CURRENTLY locked -- which is
   the only thing those two buttons exist to communicate. They latch now, and
   latched state gets its own colour so it does not read as merely "selected".

   Four semantic states, each with one meaning:
     .on       chosen / armed        emerald
     .latched  a lasting mode        amber
     .saved    committed             green
     .fired    momentary confirm     solid emerald flash                       */
.cdp-tbtn.latched, .cdp-rtool.latched {
  /* Was amber. LATCHED means "this tool is armed" — a state, not a warning. Amber
     is banned here because it reads as an error on a money screen. */
  background: var(--cdp-accent-soft); border-color: var(--cdp-accent); color: var(--cdp-accent);
}
.cdp-icon-btn.saved, .cdp-preset.saved {
  background: #ecfdf3; border-color: #bbf7d0; color: #166534;
}

/* ═══ DOCK HIERARCHY ═══════════════════════════════════════════════════════
   Dock headers are 48px against the main panels' 58px. Same family, lower rank:
   the size difference is what tells you at a glance that the bottom strip
   SUPPORTS the chart rather than competing with it. Body padding tightens to
   match so the rows stay dense. */
.cdp-bottom .cdp-panel-header { height: 48px; font-size: 13px; }
.cdp-bottom .cdp-panel-body { height: calc(100% - 48px); padding: 10px 16px; }
.cdp-bottom .cdp-htab { font-size: 12.5px; padding: 4px 8px; }

/* Numeric emphasis, applied once rather than per table. */
.cdp-last, .cdp-chg, .cdp-mini-value, .cdp-ctx-item i, .cdp-ctx-item em,
.cdp-tbl td, .cdp-wl td { font-variant-numeric: tabular-nums; }

/* ═══ MULTI-CHART LAYOUTS — single / split / quad ══════════════════════════
   WHERE THE SWITCHER LIVES, and why. The owner's standing question has been
   placement: where does a control go so people actually find it. The rule this
   desk follows is group by WHAT A CONTROL ACTS ON, not by how often it is used:

     left rail    acts on your NEXT CLICK   (modal drawing tools)
     tier bar     acts on the TOOL SET      (Core / Analysis / Advanced)
     toolbar row  acts on WHAT YOU SEE      (timeframe, compare, indicators)
     right end    acts on the WHOLE DESK    (layout, sync, save, share)

   Layout, save and share belong in that last group, and it sits at the RIGHT
   END OF THE TIER BAR, not on the top toolbar row. That is a correction: they
   were on the top row first, and measuring it showed the row overflowing its
   own 58px box by 10px — nine timeframe chips plus a symbol block plus four
   workspace controls do not fit in the 968px that row actually has. Wrapping
   would have pushed the chart down a line on narrower screens.

   The tier bar is the better home anyway, and not only because it fits:
     top row   = WHAT AM I LOOKING AT   (symbol, price, timeframe)
     tier bar  = HOW AM I LOOKING AT IT (tools on the left, workspace on the right)
   It is full width, always visible, sits directly above the chart, and its
   right edge is the strongest position on the screen that is not the primary
   action. Nothing competes with the symbol block, which is what the eye lands
   on first.

   The panels themselves are a plain CSS grid with a 1px gutter that shows the
   panel border through, so the divider between charts costs no extra element
   and never drifts out of alignment with the panel edge. */
.cdp-workspace {
  display: inline-flex; align-items: center; gap: 6px; margin-left: auto;
  padding-left: 12px; border-left: 1px solid var(--cdp-border); flex: 0 0 auto;
}
.cdp-workspace .cdp-chip { padding: 6px 9px; }
.cdp-ctrl-sep { display: none; }   /* the group carries its own left rule now */
/* The tier bar now holds two clusters, so it needs room for the taller of them
   and must never wrap: a workspace control that moves between rows as the
   window changes is a control users hunt for. */
/* WRAP, not nowrap. nowrap sent the overflow into .cdp-tbcluster's horizontal
   scroller, and a scroller crops its last item: the Lock button sat permanently
   half-cut at the right edge, which is exactly how the owner reported it. This
   row lives in an `auto` grid track, so letting it take a second line costs the
   chart a few pixels and keeps every control fully visible and clickable. The
   shrink order below still applies - the hint is still the first thing to give. */
.cdp-tierbar { min-height: 42px; flex-wrap: wrap; row-gap: 6px; }
/* SHRINK ORDER, and it is load-bearing. nowrap alone let the tool cluster
   shrink below its own content: its buttons spilled out of their box and the
   hint, later in the DOM, painted on top of them — a browser test found the
   Bollinger button unclickable because a line of grey helper text was sitting
   over it. Controls never shrink; the hint is the only flexible item, and it
   truncates. That matches the priority already stated below: the hint is
   guidance, not a control, so it is the first thing to go. */
.cdp-tier, .cdp-workspace, .cdp-tierchips, .cdp-tbgroup { flex: 0 0 auto; }
/* The hint yields first (shrink factor 100), then the tool cluster SCROLLS.
   Advanced tier puts 14 icon buttons in this strip; at 1600px that is 1086px of
   content in 998px of panel, and with everything pinned the overflow landed on
   Save and Share — clipped off the panel edge, unclickable, exactly the failure
   mode fixed once already. A horizontally scrolling strip of same-shaped icon
   buttons is the one place scrolling is fine: nothing changes position relative
   to its neighbours, so muscle memory survives. Workspace controls never move. */
.cdp-tbcluster {
  flex: 0 1 auto; min-width: 0; overflow-x: auto; overflow-y: hidden;
  scrollbar-width: thin; scroll-padding: 0 8px;
}
.cdp-tbcluster::-webkit-scrollbar { height: 5px; }
.cdp-tbcluster::-webkit-scrollbar-thumb { background: #d7dfea; border-radius: 4px; }
.cdp-tier-hint {
  flex: 0 100 auto; min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap;
}
.cdp-layout {
  display: inline-flex; gap: 2px; padding: 2px; flex: 0 0 auto;
  background: var(--cdp-panel-2); border: 1px solid var(--cdp-border); border-radius: 10px;
}
.cdp-lay {
  position: relative;
  width: 28px; height: 26px; display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid transparent; background: transparent; color: var(--cdp-muted);
  border-radius: 8px; cursor: pointer; padding: 0;
  transition: background .12s ease, color .12s ease, border-color .12s ease;
}
.cdp-lay svg { width: 16px; height: 16px; }
.cdp-lay-lock {
  position: absolute; right: -1px; bottom: -1px;
  width: 11px !important; height: 11px !important;
  color: #a8501f; background: var(--cdp-panel); border-radius: 3px;
  pointer-events: none;
}
html.is-pro .cdp-lay-lock { display: none; }
.cdp-lay:hover { background: #fff; color: var(--cdp-text); border-color: var(--cdp-border); }
.cdp-lay.on { background: var(--cdp-accent); border-color: var(--cdp-accent); color: #fff; }
.cdp-lay.on .cdp-lay-lock { color: #fff; background: var(--cdp-accent); }
html:not(.is-pro) .cdp-lay.is-pro-opt { color: var(--cdp-muted); }
html:not(.is-pro) .cdp-lay.is-pro-opt:not(.on) { opacity: .72; }
/* Indicators is the one picker. The MA / Bollinger / cloud / volume icons next
   to it were the same studies as a second control — owner 2026-08-23 did not
   realize they were the same thing. TradingView keeps one Indicators button;
   active studies show on the chart, not as extra toolbar icons. */
#cdpTbCluster [data-group="study"] { display: none !important; }
.cdp-chip.cdp-sync[aria-pressed="true"] {
  background: var(--cdp-accent-soft); border-color: var(--cdp-accent-line);
  color: var(--cdp-accent); font-weight: 800;
}
.cdp-chip.saved { background: #ecfdf3; border-color: #bbf7d0; color: #166534; font-weight: 800; }

.cdp-grid { display: grid; min-height: 0; min-width: 0; gap: 1px; background: var(--cdp-border); }
.cdp-grid[data-layout="single"] { grid-template-columns: minmax(0, 1fr); grid-template-rows: minmax(0, 1fr); }
.cdp-grid[data-layout="split"]  { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); grid-template-rows: minmax(0, 1fr); }
.cdp-grid[data-layout="quad"]   { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); grid-template-rows: minmax(0, 1fr) minmax(0, 1fr); }

.cdp-cell {
  position: relative; min-width: 0; min-height: 0; background: #fff;
  display: grid; grid-template-rows: 0 minmax(0, 1fr);
}
.cdp-cell[hidden] { display: none; }
.cdp-cell-canvas { min-width: 0; min-height: 0; }

/* In SINGLE view the per-chart header is suppressed entirely: the toolbar three
   inches above already says NVDA · 195.00 · +1.26%, and repeating it is the kind
   of duplication that makes a screen feel padded. It appears only once there is
   more than one chart, which is the only moment it carries information. */
.cdp-cell-head {
  display: none; align-items: center; gap: 8px; padding: 0 10px;
  border-bottom: 1px solid var(--cdp-line); background: var(--cdp-panel-2);
  font-size: 11.5px; overflow: hidden; white-space: nowrap;
}
.cdp-grid:not([data-layout="single"]) .cdp-cell { grid-template-rows: 30px minmax(0, 1fr); }
.cdp-grid:not([data-layout="single"]) .cdp-cell-head { display: flex; }
.cdp-cell-sym { font-weight: 900; letter-spacing: .01em; }
.cdp-cell-last { font-variant-numeric: tabular-nums; font-weight: 700; }
/* NO margin-left:auto HERE. That pushed the change % to the far side of the pane
   header, so a stat ABOUT the ticker sat nowhere near it — owner: "it looks really
   funny to have it so far away when you're showing a stat about that current
   asset." The % now sits directly after the symbol and price; the SEARCH box takes
   the auto margin and holds the right edge. */
.cdp-cell-chg  { font-variant-numeric: tabular-nums; font-weight: 800; }
.cdp-cell-head .cdp-cell-search { margin-left: auto; }
.cdp-cell-chg.up { color: var(--cdp-green); } .cdp-cell-chg.down { color: var(--cdp-red); }

/* ACTIVE CELL. With four charts on screen the single most important question is
   "which one am I about to trade?" — the order ticket, the studies and the
   alert bell all follow the active cell, so it has to be unmistakable without
   being loud. An inset emerald ring drawn on a pseudo-element keeps it out of
   the layout box, so switching focus never nudges the chart by a pixel. There
   is no ring in single view: with one chart the answer is not in doubt. */
/* THE GREEN ONLY MEANS SOMETHING WHEN A TOOL IS ARMED. Owner, four times:
   "when you select the second chart, it shouldn't be green. It should only be green
   when you wanna put a tool on a certain chart."

   He is right. Focus and targeting are two different states and this painted both the
   same: opening a split view flooded one pane green just for being selected, so the
   loudest colour on the desk marked something the user had not done and did not care
   about. Green is the answer to "where will this tool land", and that question only
   exists while a tool is armed.

   Selected-but-idle now gets a quiet neutral edge -- enough to answer "which pane does
   the search box target" without shouting. The crosshair is excluded from the armed
   test on purpose: it is on by default and belongs to reading, not drawing, so keying
   on it would leave the panel green permanently and change nothing. */
.cdp-grid:not([data-layout="single"]) .cdp-cell.active::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  /* VISIBLE, BUT NOT GREEN. The owner rejected the green highlight, and the neutral
     that replaced it was var(--cdp-border) -- the SAME colour every other cell edge
     already uses, so nothing distinguished the selected pane at all: "it's hard to see
     which chart is highlighted." A selected pane has to answer "where does the search
     box and the drawing tool land" at a glance. Graphite at 2px reads immediately
     against the hairline borders without colouring the pane. */
  border: 2px solid #5b6675; border-radius: 3px;
  box-shadow: inset 0 0 0 1px rgba(91, 102, 117, .18);
}
/* NO FILL, EVER. The owner, again on 2026-08-21: "get rid of this green box, I told
   you to make just an outline to show what chart you're on. That way, when it's
   highlighted, that's the one that's applied. It should be a dark border, dude."

   The armed state used to tint the whole pane AND its head with --cdp-accent-soft.
   Two tinted surfaces plus a green edge is a green box, whatever the edge is doing.
   The BORDER is the entire signal now: graphite when a pane is merely selected,
   heavier and near-black when a tool is armed and will land there. One visual
   language, no colour flooding a pane the user is trying to read a chart in. */
body:has(#cdpRail .cdp-rtool.on:not([data-tool="cross"])) .cdp-grid:not([data-layout="single"]) .cdp-cell.active::after {
  border-width: 3px; border-color: #1d2530;
  box-shadow: inset 0 0 0 1px rgba(29, 37, 48, .28);
}
html[data-chart-theme="dark"] body:has(#cdpRail .cdp-rtool.on:not([data-tool="cross"])) .cdp-grid:not([data-layout="single"]) .cdp-cell.active::after {
  border-color: #dfe6ef; box-shadow: inset 0 0 0 1px rgba(223, 230, 239, .24);
}
.cdp-grid:not([data-layout="single"]) .cdp-cell:not(.active) { cursor: pointer; }

/* Fullscreen presentation mode. :fullscreen removes the page background, so the
   panel has to paint its own or the chart floats on black in some browsers. */
.cdp-chart-wrap:fullscreen { background: var(--cdp-panel); padding: 0; }
.cdp-chart-wrap:fullscreen .cdp-chart-footer { border-bottom: 0; }

/* Below the desk breakpoint quad is unusable, so the grid collapses to a single
   column and stacks — the switcher stays, the charts just no longer sit 4-up in
   380px of width. Stated here rather than left to chance. */
@media (max-width: 1100px) {
  .cdp-grid[data-layout="split"], .cdp-grid[data-layout="quad"] {
    grid-template-columns: minmax(0, 1fr);
    grid-auto-rows: minmax(220px, 1fr); grid-template-rows: none;
  }
}

/* Compare / Indicators moved off the timeframe row. Measured at 1600px, nine
   chips plus the symbol block needed 64px of a 58px toolbar and wrapped — the
   row had been overflowing since before the layout work. The top row is now
   timeframe and nothing else, which is also what it should have been: it
   answers "what am I looking at", while Compare and Indicators change what the
   chart CONTAINS, and that is the tier bar's job. */
.cdp-tierchips { display: inline-flex; gap: 6px; flex: 0 0 auto; }
.cdp-tierchips .cdp-chip { padding: 6px 9px; }
.cdp-tierchips + .cdp-tbcluster { padding-left: 12px; border-left: 1px solid var(--cdp-border); }
.cdp-chip[aria-pressed="true"] { background: var(--cdp-accent-soft); border-color: var(--cdp-accent-line); color: var(--cdp-accent); font-weight: 800; }
@keyframes cdpFlash { 0%,100% { background: transparent; } 40% { background: var(--cdp-accent-soft); } }
.cdp-flashgrp { animation: cdpFlash .9s ease; border-radius: 8px; }

/* Same shrink-order rule as the tier bar, applied to the top row. The timeframe
   chips are discrete controls and must never wrap — measured at 1600px they
   needed 328px and were being handed 322px, so the row silently became two rows
   inside a fixed 58px box and stuck out the top. The meta block is the flexible
   one: the OHLC string truncates, which costs a reader nothing, while a chip
   that jumps to a second row costs them the muscle memory for every chip. */
/* WRAP, DO NOT SCROLL AWAY. This rule set nowrap and overrode the flex-wrap:wrap
   at line ~260. The row then lived inside .cdp-toolbar, which is overflow-x:auto with
   its scrollbar painted to zero height - so once the tier bar and the extra tool groups
   took the width, 1m and 5m scrolled out of view with NO affordance saying they were
   still there. Reported as "on intermediate and advanced the one and five minute
   timeframes disappear". They were never hidden; they were pushed off a silent scroller.
   Wrapping onto a second line is always better than a control you cannot reach.
   (owner 2026-08-21) */
.cdp-chart-controls { flex-wrap: wrap; flex: 0 1 auto; min-width: 0; }
/* overflow:hidden CLIPPED the third line. .cdp-chart-meta is a flex column of
   three rows (symbol+price, submeta, and the Open/Day range/Volume stats); with a
   fixed 58px toolbar the third row sat past the edge and was sliced - the owner
   saw "Volume 15.1M" cut off mid-glyph. visible lets it paint; the row itself
   wraps rather than pushing the toolbar wider. */
.cdp-chart-meta { flex: 1 1 auto; min-width: 0; overflow: visible; }
.cdp-sym-row { min-width: 0; }
.cdp-submeta { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Below the desk breakpoint the page is already a single stacked column, so
   there is no side-by-side row whose muscle memory a wrap would break — and a
   fixed 58px box that its own contents overflow by 6px is worse than an honest
   two-line bar. Height goes auto and the chips are allowed to wrap here, and
   only here. */
@media (max-width: 1100px) {
  .cdp-chart-toolbar { height: auto; min-height: 58px; padding: 10px 16px; align-items: flex-start; }
  .cdp-chart-controls { flex-wrap: wrap; justify-content: flex-start; }
  .cdp-chart-wrap { grid-template-rows: auto auto minmax(0, 1fr) auto; }
  .cdp-tierbar { flex-wrap: wrap; }
  .cdp-workspace { margin-left: 0; }
}

/* ── min-width:0, and it is not boilerplate ────────────────────────────────
   A grid item defaults to min-width:auto, so .cdp-tierbar sized itself to its
   CONTENT rather than to its track: adding the workspace group made the strip
   1229px wide inside a 1000px panel. Because .cdp-panel clips, the result was
   not a visible overlap but something worse — Save and Share were simply cut
   off the right edge and could not be clicked at all. A browser hit-test found
   it; nothing looked wrong in a screenshot.

   With min-width:0 the flex container is finally constrained, so the shrink
   order declared above actually engages and the hint collapses first. */
.cdp-chart-wrap, .cdp-tierbar, .cdp-chart-toolbar, .cdp-canvasrow { min-width: 0; }

/* ═══ ONBOARDING ═══════════════════════════════════════════════════════════
   The app grid gains a row ONLY while the banner is up. A permanently declared
   auto row still costs its 14px gap when empty, so the row is added by a data
   attribute instead — dismissing the banner returns every pixel to the chart. */
/* Banner row is auto, TOPBAR is the 64px one. These two were swapped, so while
   the Core-mode banner was up the banner was pinned to 64px and the toolbar
   collapsed to auto - and dismissing it snapped the whole page upward. Same
   family as the zero-height chart bug: a display:none child stops being a grid
   item, so every row assignment after it shifts. */
.cdp-app[data-onboard] { grid-template-rows: auto minmax(0, 1fr) 168px auto; }

/* The [hidden] attribute alone does NOT hide this banner: .cdp-onboard sets
   display:flex, which outranks the user-agent's [hidden]{display:none}. So
   `banner.hidden = true` left it painted AND still occupying a grid row, which
   is why dismissing it rearranged the page instead of removing it.

   Every other collapsible control on this page already carries the same guard
   (.cdp-rtool[hidden], .cdp-tbgroup[hidden], .cdp-tbtn[hidden],
   .cdp-settings[hidden], .cdp-chip.cdp-sync[hidden]) - this one was simply
   missed. With it, the banner stops being a grid item and the base four-row
   template maps cleanly onto toolbar / chart / dock. */
.cdp-onboard[hidden] { display: none !important; }
.cdp-onboard {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 11px 16px; border: 1px solid var(--cdp-accent-line); border-radius: 14px;
  background: var(--cdp-accent-soft); color: var(--cdp-text); font-size: 12.5px;
}
.cdp-onboard[hidden] { display: none; }
.cdp-onboard-txt strong { display: block; margin-bottom: 2px; font-size: 13px; }
.cdp-onboard-txt span { color: #2f5f4c; }
.cdp-onboard b { font-weight: 800; }

/* ═══ KEYBOARD ═════════════════════════════════════════════════════════════ */
kbd {
  font: inherit; font-size: .92em; font-weight: 700; padding: 1px 5px;
  border-radius: 5px; border: 1px solid #d0d7e2; border-bottom-width: 2px;
  background: #f8fafc; color: var(--cdp-text); line-height: 1.4;
}
.cdp-helper { margin: 12px 0 0; font-size: 11.5px; color: var(--cdp-muted); line-height: 1.7; }
.cdp-sheet {
  position: fixed; inset: 0; z-index: 200; display: grid; place-items: center;
  background: rgba(15, 23, 42, .42); padding: 20px;
}
.cdp-sheet[hidden] { display: none; }
.cdp-sheet-card {
  background: var(--cdp-panel); border: 1px solid var(--cdp-border); border-radius: var(--cdp-radius);
  box-shadow: 0 24px 60px rgba(15, 23, 42, .22); padding: 18px 20px; max-width: 460px; width: 100%;
  max-height: 80vh; overflow: auto;
}
.cdp-sheet-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px; }
.cdp-sheet-head b { font-size: 15px; }
.cdp-keys { display: grid; grid-template-columns: auto 1fr; gap: 9px 14px; margin: 0; align-items: baseline; }
.cdp-keys dt { text-align: right; white-space: nowrap; }
.cdp-keys dd { margin: 0; font-size: 12.5px; color: var(--cdp-text); }
.cdp-sheet-foot { margin: 16px 0 0; font-size: 11.5px; color: var(--cdp-muted); line-height: 1.6; }

/* ═══ PANEL STATES ═════════════════════════════════════════════════════════
   Emerald, not the sample's blue — this page has one accent. The cell-level
   state overlays its chart rather than replacing it, so a reload never collapses
   the pane and bounces the whole grid. */
.cdp-state {
  display: grid; place-items: center; gap: 9px; min-height: 120px; padding: 20px;
  text-align: center; font-size: 12.5px; color: var(--cdp-muted); line-height: 1.6;
}
.cdp-state[hidden] { display: none; }
.cdp-state p { margin: 0; }
.cdp-state.error { color: var(--cdp-red); }
.cdp-cellstate {
  position: absolute; inset: 0; z-index: 4; background: var(--cdp-panel);
  color: var(--cdp-accent); min-height: 0;
}
.cdp-spin {
  width: 20px; height: 20px; border: 2px solid var(--cdp-border);
  border-top-color: var(--cdp-accent); border-radius: 50%;
  animation: cdpSpin .8s linear infinite;
}
@keyframes cdpSpin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .cdp-spin { animation-duration: 2.4s; }
  .cdp-flashgrp { animation: none; }
}

/* ═══ TOOLTIPS ═════════════════════════════════════════════════════════════
   Positioned by JS on a node appended to <body>, NOT a CSS ::after on the
   trigger. .cdp-panel sets overflow:hidden (it has to — it is what keeps the
   chart inside its rounded corners), so a pseudo-element tooltip on a toolbar
   button would be CLIPPED at the panel edge and the top row's tooltips would be
   sliced in half. Escaping the panel means escaping the stacking context. */
.cdp-tip {
  /* Z-INDEX 12060, NOT 300. Escaping .cdp-panel's overflow (see above) was only
     half the problem: the tooltip still had to outrank the SHARED SHELL, which
     this page also loads. paper-trading-shell.css puts .pt-guest-menu-panel and
     .pt-more-menu at 10050, html.is-preview .pt-more.is-open at 10040, and
     #jag-activity-toast at 12050 - so a 300 tooltip lost to all four. The
     watchlist's "Sort the watchlist" tip flips UPWARD from a button near the top
     of the rail, landing squarely in that band, and the owner saw it painted
     behind the page furniture: "it's appearing behind the chart, it can't see
     anything". 12060 clears every shell layer and still sits far below the
     2147482000 report widget, which is meant to beat everything. */
  position: fixed; z-index: 12060; pointer-events: none; opacity: 0;
  transform: translateY(3px); transition: opacity .11s ease, transform .11s ease;
  background: var(--cdp-text); color: #fff; padding: 6px 9px; border-radius: 8px;
  font-size: 11px; font-weight: 700; line-height: 1.35; max-width: 260px;
  box-shadow: 0 8px 24px rgba(15, 23, 42, .22);
}
.cdp-tip.on { opacity: 1; transform: translateY(0); }

/* ═══ ACCESSIBILITY ════════════════════════════════════════════════════════
   :focus-visible only — a persistent ring on every mouse click reads as broken,
   which is why so many sites delete the outline entirely and leave keyboard
   users with nothing. This gives the ring to keyboard users only. */
:focus-visible { outline: 3px solid var(--jag-green, var(--jag-green));   /* was #7aa7ff = 2.39:1; WCAG 1.4.11 wants 3:1 */ outline-offset: 2px; border-radius: 6px; }
.cdp-sr-only {
  left: 0;  /* never let a hidden 1px label sit at a wide static position */
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  border: 0; clip: rect(0, 0, 0, 0); overflow: hidden; white-space: nowrap;
}
.cdp-feed {
  font-size: 10.5px; font-weight: 800; padding: 2px 6px; border-radius: 999px;
  background: #f1f4f8; border: 1px solid #d8e0ea; color: #334155; vertical-align: 1px;  /* was amber — a Delayed badge states a fact */
}
.cdp-feed.live { background: var(--cdp-green-soft); border-color: #bbf7d0; color: #166534; }
.cdp-upd { font-size: 11.5px; font-weight: 600; color: var(--cdp-muted); }
.cdp-mini-value .cdp-feed + .cdp-upd { margin-left: 4px; }

/* GRID LINES were too faint to read as a grid at all (owner). The chart canvas
   draws its own, so this only governs the CSS-drawn rules; the engine-side colour
   is raised alongside it in js/app/chart-layouts.js. */

/* ═══ RESPONSIVE ═══════════════════════════════════════════════════════════
   Three tiers, each losing the least valuable thing first. Nothing is ever
   hidden outright — a control you cannot reach on a laptop is a bug report. */
@media (min-width: 1101px) and (max-width: 1400px) {
  .cdp-main { grid-template-columns: 214px minmax(0, 1fr) 286px; }
}
@media (min-width: 1101px) and (max-width: 1280px) {
  .cdp-main { grid-template-columns: 190px minmax(0, 1fr) 272px; }
  .cdp-bottom { grid-template-columns: 1.2fr 1fr; }
  .cdp-bottom > .cdp-panel:nth-child(3) { grid-column: 1 / -1; }
  /* Tracks --cdp-dock-h above (332px). A literal 280 here left the reserve and
     the actual row disagreeing, which is the same class of bug that made quad
     overflow the fold. */
  .cdp-app { grid-template-rows: minmax(0, 1fr) var(--cdp-dock-h) auto; }
  .cdp-app[data-onboard] { grid-template-rows: auto minmax(0, 1fr) var(--cdp-dock-h) auto; }
}
@media (max-width: 1100px) {
  /* The tier bar's two clusters stack rather than fight for one line, and the
     tool strip scrolls sideways instead of wrapping into an unpredictable
     number of rows. */
  .cdp-tbcluster, .cdp-tierchips { overflow-x: auto; }
  .cdp-bottom { grid-template-columns: minmax(0, 1fr); }
  .cdp-bottom > .cdp-panel:nth-child(3) { grid-column: auto; }
  .cdp-onboard { flex-direction: column; align-items: flex-start; }
}

/* Alert rows gain a dismiss control. Without one the empty state was
   unreachable, which would have made it decoration. */
.cdp-alert-item { position: relative; }
.cdp-alert-x {
  opacity: 0; transition: opacity .12s ease; margin-left: 6px;
  font-size: 15px; line-height: 1;
}
.cdp-alert-item:hover .cdp-alert-x, .cdp-alert-x:focus-visible { opacity: 1; }
.cdp-wl tbody tr[hidden] { display: none; }

/* ═══ AUDIT PASS ═══════════════════════════════════════════════════════════

   1. COLOUR IS NEVER THE ONLY SIGNAL. Up and down were carried by green and
      red text alone, which is invisible to a red-green colourblind reader —
      roughly 1 in 12 men. A triangle carries the same meaning with no colour
      at all (WCAG 1.4.1, Use of Color). Added once here rather than in fifty
      table cells. */
.up::before   { content: '\25B2 '; font-size: .78em; vertical-align: 1px; }
.down::before { content: '\25BC '; font-size: .78em; vertical-align: 1px; }
.cdp-chg.up::before, .cdp-chg.down::before,
.cdp-cell-chg.up::before, .cdp-cell-chg.down::before { font-size: .72em; }
/* Cells whose text is not a direction never get an arrow: the alert timestamp
   ("Now") and the alert icon, which already IS an arrow — that one rendered as
   a doubled glyph until this landed. */
.cdp-alert-ico::before, .cdp-alert-val::before { content: none; }

/* 2. WATCHLIST SPARKLINES. Trend context beside the price, from the same
      series the chart draws — the number and the shape cannot disagree.
      Dropped below 1280px, where the column is too narrow to read anyway. */
/* The sparkline is INLINE in the SYMBOL cell, and the table keeps its NATURAL
   layout. Two wrong turns got here: making the price cell a flex box took it out
   of the column algorithm and every row wrapped to two lines; switching to
   table-layout:fixed with percentage columns then collapsed cell one to 36px for
   65px of content. The browser's own algorithm sizes these three columns
   correctly — star + ticker + 28px chart + price + change is 172px inside 206px.
   nowrap is the only hint it needs. */
.cdp-wl { width: 100%; font-size: 12px; }
.cdp-wl th, .cdp-wl td { white-space: nowrap; }
.cdp-wl th:nth-child(2), .cdp-wl td:nth-child(2),
.cdp-wl th:nth-child(3), .cdp-wl td:nth-child(3) { text-align: right; }
.cdp-wl-sym { margin-right: 5px; }
.cdp-spark { vertical-align: middle; }  /* .75 put a rising stroke at 2.43:1 — fainter than a falling one, so a good day looked like a glitch */
/* The price stepper's arrows carry .up/.down as STYLE hooks and already contain
   a literal glyph, so the generic direction arrow doubled them. Direction
   pseudo-elements are for value cells, not for controls. */
.cdp-step::before { content: none; }
@media (max-width: 1280px) { .cdp-spark { display: none; } }

/* 3. DOCK DENSITY. The audit called the bottom cards cramped against the
      breathing room of the metric cards above. Rows gain vertical rhythm
      without the panel growing: the dock scrolls. */
.cdp-bottom .cdp-tbl td, .cdp-bottom .cdp-tbl th { padding-top: 8px; padding-bottom: 8px; }
.cdp-bottom .cdp-panel-body { padding: 10px 18px; overflow-y: auto; }
.cdp-alert-item { padding: 10px 0; }

/* 4. TICKET TARGETS. Compact inputs are a real hit-area problem in the one
      panel where a mis-click costs money. */
.cdp-input, .cdp-select { padding-top: 12px; padding-bottom: 12px; }
.cdp-seg-btn { padding-top: 10px; padding-bottom: 10px; }

/* 5. MARKET CONTEXT is global, ambient, and NOT part of your portfolio — it
      needed a visible boundary so it stops reading as a fourth dock panel. */
.cdp-context {
  background: linear-gradient(180deg, #f2f6fb, #eef3f9);
  border-top: 2px solid var(--cdp-border);
}
.cdp-ctx-label { color: var(--cdp-muted); }
/* [hidden] is a UA display:none, so ANY author display rule silently beats it.
   .cdp-ctx-item sets display, so hiding the sector chip in JS did nothing and
   SPY kept showing a sector it does not have. */
.cdp-ctx-item[hidden] { display: none; }

/* ═══ STICKY HEADER BLEED ══════════════════════════════════════════════════
   Reported from a screenshot: scrolling the Positions and Orders tables showed
   a row sliding UNDER the column header and staying visible through it. The
   header was sticky but transparent, so the rows simply painted behind the
   text. An opaque background and a stacking order fix it; the hairline keeps
   the boundary readable once rows are moving beneath it. */
.cdp-bottom .cdp-tbl thead th,
.cdp-tbl thead th {
  position: sticky; top: 0; z-index: 3;
  background: var(--cdp-panel-2);
  box-shadow: inset 0 -1px 0 var(--cdp-border);
}
.cdp-bottom .cdp-panel-body { position: relative; }
/* The watchlist sits on the white panel, so it needs the white, not panel-2. */
.cdp-wl thead th { background: var(--cdp-panel); }

/* Popup menu for the symbol "..." action. On <body> for the same reason the
   tooltips are: .cdp-panel clips, and a menu that is half cut off is worse
   than no menu. */
.cdp-menu {
  position: fixed; z-index: 250; min-width: 210px; padding: 6px;
  background: var(--cdp-panel); border: 1px solid var(--cdp-border);
  border-radius: 12px; box-shadow: 0 16px 40px rgba(15, 23, 42, .18);
  display: flex; flex-direction: column; gap: 2px;
}
.cdp-menu[hidden] { display: none; }
.cdp-menu button {
  text-align: left; border: 0; background: transparent; font: inherit;
  font-size: 12.5px; font-weight: 700; color: var(--cdp-text);
  padding: 8px 10px; border-radius: 8px; cursor: pointer;
}
.cdp-menu button:hover { background: var(--cdp-accent-soft); color: var(--cdp-accent); }
.cdp-mini-btn.latched { background: var(--cdp-accent-soft); border-color: var(--cdp-accent); color: var(--cdp-accent); }  /* was amber — this is the watchlist + button when the add row is open */
.cdp-tbl th.sortable { cursor: pointer; user-select: none; }
.cdp-tbl th.sortable:hover { color: var(--cdp-accent); }
.cdp-tbl tbody tr[hidden] { display: none; }

/* ═══ THE CHART HAD ZERO HEIGHT ════════════════════════════════════════════
   `#cdpChart { height: 100% }` was inherited from the original single-chart
   markup. Inside the multi-chart grid that cell is a GRID ITEM in a
   minmax(0,1fr) row, and a percentage height resolved against an indefinite
   row computes to 0 — so klinecharts, which sizes itself from the container at
   init, came up 0px tall. It still painted a 23px time axis, which is why the
   page looked merely cramped rather than broken, but every pointer event
   landed on the cell instead of the canvas: drawing could not start, and the
   snapshot export died on a zero-height canvas.

   Grid items stretch to their row by default, so the fix is to stop asking for
   a percentage and let the row do its job. */
.cdp-cell-canvas, #cdpChart {
  height: auto; min-height: 0; min-width: 0; align-self: stretch;
  position: relative; overflow: hidden;
}
.cdp-cell { min-height: 220px; }
.cdp-grid[data-layout="quad"] .cdp-cell { min-height: 150px; }

/* THE ACTUAL CAUSE of the zero-height chart. In single view the per-cell header
   is suppressed with display:none — and a display:none child is not a grid item
   at all, so the canvas slid up into the header's 0px row while row 2 sat empty
   at 455px. The old engine hid this because it was handed explicit pixel sizes;
   klinecharts measures its container, got 0, and every pointer event then landed
   on the cell instead of the canvas.

   The header stays a grid item and is collapsed by the 0px row instead. Same
   result on screen, and the canvas keeps the row it was always meant to have. */
.cdp-cell-head { display: flex; overflow: hidden; }
.cdp-grid[data-layout="single"] .cdp-cell-head { visibility: hidden; }

/* ═══ THE SCROLL BLEED, PROPERLY ═══════════════════════════════════════════
   Reported twice. The first fix made the header opaque and sticky, and my test
   confirmed both — but that was never the bug. `position:sticky; top:0` pins to
   the scroll container's PADDING box, and these bodies carry padding-top:10px.
   So the header pinned 10px down, and every row scrolled up through the 10px
   strip ABOVE it and painted there in full view. elementFromPoint three pixels
   below the container top returned the SPY row, which is exactly what the
   screenshot showed.

   The strip has to not exist. Top padding comes off the scrolling container and
   moves into the header cell, so the spacing looks identical and there is no
   band above the header for anything to show through. */
.cdp-bottom .cdp-panel-body,
.cdp-panel-body:has(.cdp-wl) { padding-top: 0; }
.cdp-bottom .cdp-tbl thead th { padding-top: 12px; }
.cdp-wl thead th { padding-top: 12px; }
/* The alerts panel has no table and therefore no sticky header, so it keeps its
   breathing room the ordinary way. */
.cdp-bottom .cdp-alert-item:first-child { margin-top: 10px; }

/* ── Symbol search, in the chart header where it belongs ────────────────────
   The desk rebuild moved this into the Watchlist panel as a row filter, leaving
   the trading desk with no way to reach a symbol the user does not already hold.
   Sits between the symbol block and the timeframe chips, per the reference. It
   is the flexible item in that row, so it absorbs slack instead of forcing the
   timeframes to wrap. */
.cdp-search-desk {
  display: flex; align-items: center; gap: 8px;
  flex: 1 1 280px; min-width: 0; max-width: 520px; margin: 0 14px;
}
.cdp-search-desk .cdp-symsearch { flex: 1 1 auto; min-width: 0; max-width: none; margin: 0; }
.cdp-search-desk > #cdpCustomiseBtn { flex: 0 0 auto; }
.cdp-search-desk > #cdpCustomise[hidden] { display: none !important; }
.cdp-symsearch {
  position: relative; display: flex; align-items: center;
  flex: 1 1 220px; min-width: 140px; max-width: 380px; margin: 0 14px;
}
.cdp-symsearch-ico {
  position: absolute; left: 11px; width: 15px; height: 15px;
  color: var(--cdp-muted); pointer-events: none;
}
.cdp-symsearch-input {
  width: 100%; height: 36px; padding: 0 12px 0 33px;
  border: 1px solid var(--cdp-border); border-radius: 999px;
  background: var(--cdp-panel-2); color: var(--cdp-text);
  font: inherit; font-size: 13px; outline: none;
}
.cdp-symsearch-input::placeholder { color: var(--cdp-muted); }
.cdp-symsearch-input:focus {
  background: var(--cdp-panel);
  border-color: var(--cdp-accent);
  box-shadow: 0 0 0 3px var(--cdp-accent-soft);
}
/* Below the desk breakpoint the toolbar already stacks; the search takes its
   own full-width line rather than squeezing the symbol and the chips. */
@media (max-width: 1100px) {
  .cdp-search-desk { flex: 1 1 100%; max-width: none; margin: 8px 0 0; order: 3; }
  .cdp-search-desk .cdp-symsearch { margin: 0; flex: 1 1 auto; max-width: none; }
}

/* ═══ PRICE ALERTS POPOVER ══════════════════════════════════════════════════
   This whole cluster (.chart-alert-pop and its ca-* children) was moved onto the
   desk from a page that loads a different stylesheet, so NONE of its 20-odd
   classes had a single rule here. It rendered as raw text dumped in the document
   flow below the Market Context strip - position:static, transparent, no border,
   with a bare "x" that looked like a typo, and no way to shut it. A live audit
   of the desk found 59 classes in this state; this is the visible family.
   Given a real popover: anchored to its trigger, on top, dismissible. */
.chart-alert-dd { position: relative; display: inline-flex; }
.chart-alert-pop {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 120;
  width: 296px; max-height: 60vh; overflow-y: auto;
  background: var(--cdp-panel); color: var(--cdp-text);
  border: 1px solid var(--cdp-border); border-radius: 14px;
  box-shadow: 0 18px 44px -14px rgba(15, 22, 40, .34);
  font-size: 13px; text-align: left;
}
/* Without this the [hidden] attribute loses to any display the class sets -
   the same trap that kept the Core-mode banner holding a grid row. */
.chart-alert-pop[hidden] { display: none !important; }

.ca-head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 10px;
  padding: 13px 14px; border-bottom: 1px solid var(--cdp-border);
  background: var(--cdp-panel-2); border-radius: 14px 14px 0 0;
}
.ca-kicker { font-size: 10.5px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; color: var(--cdp-muted); }
.ca-head-title { font-size: 14px; font-weight: 800; margin-top: 2px; }
.ca-quota { font-size: 11.5px; color: var(--cdp-muted); font-weight: 600; margin-top: 3px; }
/* The way out has to look like a control, not a stray character. */
.ca-close {
  flex: 0 0 auto; width: 26px; height: 26px; border-radius: 8px;
  border: 1px solid var(--cdp-border); background: var(--cdp-panel);
  color: var(--cdp-muted); font: inherit; font-size: 15px; line-height: 1;
  cursor: pointer; display: grid; place-items: center;
}
.ca-close:hover { color: var(--cdp-text); border-color: var(--cdp-accent); }

.ca-body { padding: 13px 14px; display: grid; gap: 13px; }
.ca-add-label { font-size: 12px; color: var(--cdp-muted); font-weight: 700; margin-bottom: 6px; }
.ca-add-label strong { color: var(--cdp-text); font-weight: 800; }
.ca-row { display: flex; align-items: center; gap: 6px; }
.ca-dollar { color: var(--cdp-muted); font-weight: 700; }
.ca-in {
  flex: 1 1 auto; min-width: 0; height: 34px; padding: 0 10px;
  border: 1px solid var(--cdp-border); border-radius: 9px;
  background: #fff; color: var(--cdp-text); font: inherit; font-size: 13px; outline: none;
}
.ca-in:focus { border-color: var(--cdp-accent); box-shadow: 0 0 0 3px var(--cdp-accent-soft); }
.ca-add {
  flex: 0 0 auto; height: 34px; padding: 0 14px; border: 0; border-radius: 9px;
  background: var(--cdp-accent); color: #fff; font: inherit; font-size: 12.5px;
  font-weight: 800; cursor: pointer;
}
.ca-err { margin: 6px 0 0; font-size: 11.5px; color: var(--cdp-red); min-height: 1px; }

.ca-tabs { display: inline-flex; gap: 4px; padding: 3px; background: var(--cdp-panel-2); border: 1px solid var(--cdp-border); border-radius: 9px; }
.ca-tab {
  border: 0; background: transparent; font: inherit; font-size: 11.5px; font-weight: 800;
  color: var(--cdp-muted); padding: 5px 12px; border-radius: 7px; cursor: pointer; line-height: 1;
}
.ca-tab.on { background: var(--cdp-accent); color: #fff; }

.ca-section-label {
  display: flex; align-items: center; justify-content: space-between;
  font-size: 10.5px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
  color: var(--cdp-muted); margin-bottom: 7px;
}
.ca-list { display: grid; gap: 6px; }
.ca-row-item {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 10px; border: 1px solid var(--cdp-border); border-radius: 9px;
  background: var(--cdp-panel-2);
}
.ca-row-sym { font-weight: 800; font-size: 12.5px; }
.ca-row-px { margin-left: auto; font-weight: 800; font-size: 12.5px; color: var(--cdp-text); }
.ca-row-meta { flex: 1 1 100%; font-size: 11px; color: var(--cdp-muted); font-weight: 600; }
.ca-row-item.is-arch { opacity: .78; flex-wrap: wrap; }
.ca-row-x, .ca-clear-arch {
  border: 1px solid var(--cdp-border); background: var(--cdp-panel);
  color: var(--cdp-muted); border-radius: 7px; cursor: pointer;
  font: inherit; font-size: 12px; line-height: 1; padding: 4px 7px;
}
.ca-row-x:hover, .ca-clear-arch:hover { color: var(--cdp-red); border-color: var(--cdp-red); }
.ca-foot { padding: 11px 14px; border-top: 1px solid var(--cdp-border); font-size: 11.5px; color: var(--cdp-muted); line-height: 1.5; }

/* ── Watchlist bits that also had no rules ─────────────────────────────────── */
.cdp-noquote { color: var(--cdp-muted); }
/* THREE CONTROLS, 182px OF RAIL. The input had no min-width:0, so as a flex item
   it refused to shrink below its intrinsic size and pushed the Add button and the
   dismiss × straight out of the panel — "Add" rendered as "Ad" and the × sat over
   the rail edge. Same root cause as the username bleed on the leaderboard: a flex
   child defaults to min-width:auto and will overflow its parent rather than
   narrow. The input takes the slack; the two buttons never shrink. */
.cdp-wl-add {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center;
  gap: 6px; padding: 0 12px 8px; min-width: 0;
}
/* .cdp-input carries width:100% from its base rule, and as a flex item with the
   default min-width:auto it refuses to shrink below its content — so it pushed
   "Add" out to "Ad" and shoved the x past the panel edge. */
.cdp-wl-add .cdp-input { flex: 1 1 60px; min-width: 0; width: auto; padding: 8px 10px; font-size: 13px; }
/* EVERY button in this row, not just the Add one. My previous pass targeted
   .cdp-wl-x, which does not exist here — the cancel control is
   .cdp-mini-btn[data-act="cancel"] — so that rule matched nothing and the row
   stayed broken while looking fixed. */
.cdp-wl-add .cdp-mini-btn { flex: 0 0 auto; }
/* The status message is a FOURTH flex child. On one line it competed with the
   input for the same 182px; it belongs on its own row. */
.cdp-wl-add .cdp-wl-add-msg { grid-column: 1 / -1; margin: 4px 0 0; }
.cdp-wl-add[hidden] { display: none !important; }
.cdp-wl-add-go {
  /* Sized by its own text, explicitly, so no inherited icon-button geometry can
     clip it again. */
  display: inline-flex; align-items: center; justify-content: center;
  width: auto; min-width: 0; height: 36px; white-space: nowrap;
  border: 1px solid var(--cdp-accent-line); background: var(--cdp-accent-soft);
  color: var(--cdp-accent); border-radius: 9px;
  padding: 0 14px; font: inherit; font-size: 12.5px; font-weight: 800; cursor: pointer;
}
.cdp-wl-add-go:hover { background: var(--cdp-accent); color: #fff; border-color: var(--cdp-accent); }
.cdp-wl-add-msg { margin: 4px 12px 0; font-size: 11.5px; color: var(--cdp-red); }
/* The element was error-only, so every message it carried rendered red - including
   "BTC-USD added.", which is a success. Red is kept as the default (most messages
   here ARE refusals) and success opts in. */
.cdp-wl-add-msg.is-ok { color: var(--cdp-green); }

/* ── Bigger hit targets. The rail and toolbar glyphs were small enough to be
      hard to aim at, which the owner flagged directly. ───────────────────── */
.cdp-rtool svg, .cdp-tbtn svg { width: 19px; height: 19px; }
.cdp-rtool { min-width: 34px; min-height: 34px; }
.cdp-tbtn  { min-width: 32px; min-height: 32px; }
.cdp-group-label {
  font-size: 11px; font-weight: 800; letter-spacing: .02em;
  color: var(--cdp-muted); white-space: nowrap; margin-right: 2px; align-self: center;
}
@media (max-width: 1240px) { .cdp-group-label { display: none; } }

/* The filter box steps aside while the "+" add row is open, so the watchlist
   never shows two near-identical text fields at once. Declared explicitly
   because [hidden] loses to any display a class sets - the same trap that kept
   the Core-mode banner holding a grid row and left the alert popover visible. */
.cdp-search[hidden], .cdp-wl-filter[hidden] { display: none !important; }

/* Inline result for the symbol search. A failed lookup used to report only into
   the Alerts feed on the far side of the screen, which read as a dead box. */
.cdp-symsearch-msg {
  position: absolute; top: calc(100% + 5px); left: 12px; right: 0;
  margin: 0; font-size: 11.5px; font-weight: 700; color: var(--cdp-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; pointer-events: none;
}
.cdp-symsearch-msg.bad { color: var(--cdp-red); }
.cdp-symsearch-msg[hidden] { display: none !important; }
/* A lit star is a saved favourite, not decoration. */
.cdp-star.on { color: var(--cdp-accent); }

/* Typeahead menu for the symbol search. */
.cdp-symmenu {
  position: absolute; top: calc(100% + 5px); left: 0; right: 0; z-index: 130;
  margin: 0; padding: 5px; list-style: none; max-height: 300px; overflow-y: auto;
  background: var(--cdp-panel); border: 1px solid var(--cdp-border);
  border-radius: 12px; box-shadow: 0 16px 40px -14px rgba(15,22,40,.32);
}
.cdp-symmenu[hidden] { display: none !important; }
.cdp-symmenu li {
  display: flex; align-items: baseline; gap: 9px; padding: 8px 10px;
  border-radius: 8px; cursor: pointer; font-size: 13px;
}
.cdp-symmenu li b { font-weight: 800; min-width: 62px; }
.cdp-symmenu li span { color: var(--cdp-muted); font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cdp-symmenu li.on, .cdp-symmenu li:hover { background: var(--cdp-accent-soft); }

/* Data-provider attribution, matching chart-legacy.html's original treatment
   (9px, muted, no underline). Required where the data is shown - the desk had
   dropped it entirely and this page has no footer to fall back on. */
.cdp-data-credit {
  display: block; margin-top: 3px;
  font-size: 9px; color: #aeb8c4; text-decoration: none; white-space: nowrap;
}
.cdp-data-credit:hover { color: var(--cdp-muted); text-decoration: underline; }


/* ── Legal footer ──────────────────────────────────────────────────────────
   Restored from the rest of the site. Outside .cdp-app, so it can never hold a
   grid row of the desk. */
/* ONE LINE. This was two stacked rows (nav, then a wrapping legal paragraph)
   costing ~90px at the bottom of a page whose Positions and Alerts docks are what
   a trader actually reads. Owner: "make the footer one fucking line and make it
   way shorter, that way the positions and alerts can be a bit bigger." Links and
   legal now share a single centred flex row; it only wraps on narrow screens. */
.cdp-footer {
  display: flex; align-items: center; justify-content: center;
  flex-wrap: wrap; gap: 6px 18px;
  padding: 9px 26px; border-top: 1px solid var(--cdp-border);
  background: var(--cdp-panel-2); text-align: center;
}
.cdp-footer nav { display: flex; justify-content: center; flex-wrap: wrap; gap: 18px; margin-bottom: 0; }
.cdp-footer nav a { font-size: 12.5px; font-weight: 700; color: var(--cdp-text); text-decoration: none; }
.cdp-footer nav a:hover { color: var(--cdp-accent); }
.cdp-footer p { margin: 0; font-size: 11.5px; color: var(--cdp-muted); line-height: 1.35; }
.cdp-footer p a { color: var(--cdp-accent); }

/* A search box in every chart cell. In split and quad one shared header box
   could not say which pane it was retargeting. Hidden in single-chart view,
   where the header box is unambiguous and this would just be a duplicate. */
.cdp-cell-search {
  margin-left: auto; width: 96px; height: 24px; padding: 0 8px;
  border: 1px solid var(--cdp-border); border-radius: 7px;
  background: var(--cdp-panel); color: var(--cdp-text);
  font: inherit; font-size: 11.5px; outline: none;
}
.cdp-cell-search::placeholder { color: var(--cdp-muted); }
.cdp-cell-search:focus { border-color: var(--cdp-accent); box-shadow: 0 0 0 2px var(--cdp-accent-soft); }
.cdp-grid[data-layout="single"] .cdp-cell-search { display: none; }

/* Coins vs USD, for crypto only. Restored from the legacy ticket, where it lived
   as .ot-amt-toggle - on the desk it existed only inside the hidden real ticket,
   so it disappeared with the "Full order form" button. */
.cdp-flabel { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.cdp-unit-toggle { display: inline-flex; border: 1px solid var(--cdp-border); border-radius: 7px; overflow: hidden; }
.cdp-unit-toggle[hidden] { display: none !important; }
.cdp-unit-btn {
  border: 0; background: var(--cdp-panel-2); color: var(--cdp-muted);
  font: inherit; font-size: 10.5px; font-weight: 800; padding: 3px 10px; cursor: pointer; line-height: 1.5;
}
.cdp-unit-btn.is-on { background: var(--cdp-accent); color: #fff; }

/* ── Quad polish. The owner: "we just need to fix some of the quad layouts, it
   looks a bit sloppy." Three things were wrong: the active cell's ring was drawn
   INSIDE the 1px grid gap so adjacent cells' rings touched and read as one thick
   line; cell heads had no consistent height, so the four panes' baselines
   disagreed; and the price/change text could collide with the new per-cell search
   box at narrow widths. */
.cdp-grid[data-layout="quad"] .cdp-cell,
.cdp-grid[data-layout="split"] .cdp-cell { background: var(--cdp-panel); }
.cdp-grid:not([data-layout="single"]) .cdp-cell-head {
  height: 30px; padding: 0 10px; gap: 8px;
  display: flex; align-items: center;
  border-bottom: 1px solid var(--cdp-border); background: var(--cdp-panel-2);
}
.cdp-grid:not([data-layout="single"]) .cdp-cell-sym { font-size: 12px; font-weight: 800; flex: 0 0 auto; }
.cdp-grid:not([data-layout="single"]) .cdp-cell-last,
.cdp-grid:not([data-layout="single"]) .cdp-cell-chg { font-size: 11.5px; flex: 0 0 auto; }
.cdp-grid:not([data-layout="single"]) .cdp-cell-search { width: 78px; height: 21px; font-size: 11px; }
/* The ring goes inside the cell, not into the 1px gutter between cells. */
/* Same rule as above: the accent outline is for a pane a TOOL is aimed at, not for a
   pane that merely has focus. This later block was overriding the quiet version. */
/* This later block was re-adding the accent outline and the tinted head that the
   block above deliberately removed - the exact "why do things regress" pattern: two
   copies of the same rule, only one of them fixed. Deleted rather than re-fixed, so
   there is one place that decides how an active pane looks. The ::after border above
   is that place. */

/* An empty chart cell: it waits for its own search box rather than preloading a
   symbol the user never asked for. */
.cdp-cell.is-empty .cdp-cell-canvas { position: relative; }
.cdp-cell.is-empty .cdp-cell-canvas::after {
  content: 'Search a symbol to load this chart';
  position: absolute; inset: 0; display: grid; place-items: center;
  color: var(--cdp-muted); font-size: 12px; font-weight: 600; text-align: center; padding: 12px;
}
.ca-in-sym { text-transform: uppercase; }

/* ── ONE scroller in the dock, not two ─────────────────────────────────────
   .cdp-panel-body scrolls (overflow:auto) and simulator.css:1298 also gives
   .poshist-pane overflow:auto - a scroller inside a scroller, so Positions
   showed two bars side by side. The OUTER one owns it; the pane just grows. */
.cdp-bottom .poshist-pane { overflow: visible !important; height: auto !important; }
.cdp-bottom .positions-panel,
.cdp-bottom .trigger-orders-panel,
.cdp-bottom .orders-panel,
.cdp-bottom .canceled-orders-panel { overflow: visible; border: 0; background: transparent; padding: 0; }
/* The pane's own card chrome is redundant inside the dock panel that already
   draws a card - it was the "container within a container" border. */
.cdp-bottom .panel.poshist-pane { box-shadow: none; border-radius: 0; }

/* ── The Order Ticket title was truncated to "Order Ti..." ─────────────────
   .cdp-panel-header is a flex row and the title had no flex floor, so the
   symbol + "Market Closed" pill squeezed it until it ellipsised. The title is
   the shortest thing in the row and the last thing that should shrink. */
.cdp-panel-header > span:first-child { flex: 0 0 auto; white-space: nowrap; }
.cdp-tkt-hdr { display: flex; align-items: center; gap: 8px; min-width: 0; flex: 0 1 auto; }
.cdp-tkt-hdr .muted { overflow: hidden; text-overflow: ellipsis; }

/* ── OHLC readout: colour the VALUES, label them quietly ───────────────────
   The whole line was one grey run, so O / H / L / C read as prose rather than
   four numbers a trader scans. High is green, Low is red, Close takes the bar's
   own direction; the letters stay muted so the numbers lead. */
.cdp-ohlc-k { color: var(--cdp-muted); font-weight: 700; }
.cdp-ohlc-v { color: var(--cdp-text); font-weight: 800; }
.cdp-ohlc-v.hi { color: var(--cdp-green); }
.cdp-ohlc-v.lo { color: var(--cdp-red); }
.cdp-ohlc-v.up { color: var(--cdp-green); }
.cdp-ohlc-v.dn { color: var(--cdp-red); }
/* Compare is async - the benchmark's bars have to arrive first. Without a
   pending look the button appeared to do nothing for the length of a fetch. */
.cdp-chip.pending { border-color: var(--cdp-accent); color: var(--cdp-accent); opacity: .75; }

/* Saved-layout rows in the Chart settings menu. Named layouts replaced the single
   unnamed slot, where saving a second one silently destroyed the first. */
.cdp-menu-row { display: flex; align-items: stretch; gap: 2px; }
.cdp-menu-row > [data-act="loadlayout"] { flex: 1 1 auto; text-align: left; }
.cdp-menu-x { flex: 0 0 auto; width: 28px; color: var(--cdp-muted); font-size: 15px; line-height: 1; }
.cdp-menu-x:hover { color: var(--cdp-red); }
.cdp-menu-empty { margin: 0; padding: 8px 12px; font-size: 12px; color: var(--cdp-muted); }
/* The button carries a word now, not just an ellipsis. */
#cdpMore { white-space: nowrap; }

/* The Indicators menu. Drives the same toggleStudy() the toolbar icons drive, so
   there is one study system rather than two that can disagree. */
.cdp-indmenu {
  width: 300px; min-width: 300px; max-width: min(300px, calc(100vw - 16px));
  box-sizing: border-box; padding: 6px;
}
/* The Indicators popover's header row: title on the left, a real X on the right.
   Added because the only ways out were click-outside and Escape, neither of which a
   first-time user can see. */
.cdp-menu-headrow { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.cdp-menu-headrow .cdp-menu-head { margin: 0; }
.cdp-menu-x {
  border: 0; background: none; cursor: pointer; color: var(--cdp-muted);
  font-size: 19px; line-height: 1; padding: 2px 6px; margin: -2px -4px 0 0;
  border-radius: 7px; flex: 0 0 auto;
}
.cdp-menu-x:hover { color: var(--cdp-text); background: var(--cdp-control); }
.cdp-menu-x:focus-visible { outline: 2px solid var(--cdp-accent); outline-offset: 1px; }
.cdp-menu-head {
  margin: 2px 4px 6px; font-size: 10.5px; font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; color: var(--cdp-muted);
}
.cdp-indmenu [data-study] {
  display: grid; grid-template-columns: 18px minmax(0, 1fr) auto; align-items: baseline; gap: 8px;
  width: 100%; padding: 8px 8px; border-radius: 8px; text-align: left;
  border: 0; background: none; font: inherit; font-size: 13px; cursor: pointer; color: var(--cdp-text);
}
.cdp-indmenu [data-study]:hover { background: var(--cdp-accent-soft); }
.cdp-ind-tick { color: var(--cdp-accent); font-weight: 800; }
.cdp-ind-name { font-weight: 700; min-width: 0; }
.cdp-ind-note { font-size: 11px; color: var(--cdp-muted); white-space: nowrap; }


/* ══ OWNER FEEDBACK PASS ═══════════════════════════════════════════════════
   Items reported by the owner after clicking through the live desk. Kept in one
   block so the reasoning stays with the rules rather than scattered. */

/* "You have a bad habit of not having separations between a list. A list should
   have a little black line or container to show that it's separate."
   The menu rows sat flush against each other with nothing between them, so
   Copy / Alert / Snapshot / Share / Reset read as one paragraph. */
.cdp-menu button + button { border-top: 1px solid var(--cdp-border); }
.cdp-menu .cdp-menu-row + .cdp-menu-row,
.cdp-menu .cdp-menu-row + button,
.cdp-menu button + .cdp-menu-row { border-top: 1px solid var(--cdp-border); }
.cdp-symmenu li + li { border-top: 1px solid var(--cdp-border); }

/* "The time in force box is not styled, like the price box." The <select> was
   inheriting the browser control instead of the desk's input styling. */
.cdp-ticket select,
select.cdp-select,
#cdpTif {
  width: 100%;
  padding: 9px 30px 9px 11px;
  border: 1px solid var(--cdp-border);
  border-radius: 10px;
  background: var(--cdp-surface, #fff);
  color: var(--cdp-text);
  font: inherit;
  font-weight: 700;
  line-height: 1.2;
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236b7480' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 9px center;
  background-size: 15px 15px;
}
.cdp-ticket select:focus, select.cdp-select:focus, #cdpTif:focus {
  border-color: var(--cdp-accent);
  box-shadow: 0 0 0 3px var(--cdp-accent-soft);
  outline: none;
}

/* "For price alerts, this just needs a border. I hate things that don't have
   borders - you just have these white boxes and it looks funny." */
.ca-pop, .cdp-alertpop, #chartAlertPop {
  border: 1px solid var(--cdp-border);
  border-radius: 14px;
  box-shadow: 0 18px 44px rgba(15, 23, 41, .14);
  background: var(--cdp-surface, #fff);
}
.ca-pop .ca-head, #chartAlertPop .ca-head { border-bottom: 1px solid var(--cdp-border); }
.ca-pop input, #chartAlertPop input {
  border: 1px solid var(--cdp-border);
  border-radius: 10px;
}

/* OPEN / HIGH / LOW / CLOSE ARE COLOURED, ASKED FOR REPEATEDLY.
   I had appended a bare `.cdp-ohlc-v { color: var(--cdp-text) }` BELOW the
   .hi/.lo/.up/.dn rules - same specificity, later in the file, so it won and
   painted every value black. That is why they kept "going back to gray" after I
   said they were fixed: I was overriding my own rule.
   Open is deliberately its own colour rather than green/red - it is a reference
   point, not a direction. */
.cdp-submeta { color: var(--cdp-text); font-weight: 600; }
.cdp-ohlc-k { color: var(--cdp-muted); font-weight: 800; letter-spacing: .02em; }
.cdp-ohlc-v.op { color: #2f6fb0; }

/* The per-cell box said "Search sym" because the field was too narrow for its
   own placeholder. */
.cdp-cell-search { min-width: 104px; }


/* Open / Day Range / Volume moved off the chart and onto the header line. They
   were four cards eating 88px of chart height; inline they cost nothing. */
.cdp-submeta-stats { display: flex; flex-wrap: wrap; gap: 0 16px; margin-top: 2px; font-size: 12.5px; }
.cdp-sm { white-space: nowrap; }
.cdp-sm i { font-style: normal; color: var(--cdp-muted); font-weight: 700; }
.cdp-sm b { color: var(--cdp-text); font-weight: 800; }

/* Only the data-provenance card is left below the chart, so the footer is a
   thin line rather than a row of cards, and the chart keeps the difference.

   FOUR ROWS, NOT THREE: toolbar / tierbar / canvas / footer. Writing three here
   handed the 1fr to the TIER BAR - it ballooned to 283px of empty space while
   the chart collapsed to 220px. The row that must flex is the canvas. */
/* HEADER ROW SIZES TO ITS CONTENT. It was pinned at a hard 58px, and the
   Open / Day range / Volume line (.cdp-submeta-stats, +2px margin, 12.5px text) was
   added underneath the symbol afterwards - so that row overflowed 58px and the stats
   were sliced along their top edge. Owner 2026-08-21: "you did fuck up the open day
   range and volume, those are covered a bit."

   minmax(58px, auto) keeps the old height as a FLOOR so nothing else shifts, and lets
   the row grow the few pixels the stats need. A fixed height is a promise about content
   you do not control; the moment someone adds a line it becomes a clip. The canvas row
   is minmax(0,1fr) and absorbs the difference. */
.cdp-chart-wrap { grid-template-rows: minmax(58px, auto) auto minmax(0, 1fr) auto; }
.cdp-chart-footer {
  grid-template-columns: minmax(0, 1fr);
  padding: 6px 10px;
  min-height: 0;
}
.cdp-chart-footer .cdp-mini { display: flex; align-items: baseline; gap: 8px; padding: 4px 10px; }
/* [hidden] MUST WIN. The line above sets display:flex on this card, and an author
   display rule beats the hidden attribute's UA display:none. stampFeed() sets
   card.hidden = true whenever there is no tick to report -- the attribute landed, the
   card stayed on screen, and it kept showing the last text written to it: "No data",
   parked under a chart drawing 400 real bars. The owner read it as the feed being
   broken. Five other components in this file already carry this guard; the footer
   card was missed. (owner 2026-08-21) */
.cdp-chart-footer .cdp-mini[hidden] { display: none !important; }
.cdp-chart-footer .cdp-mini-label { font-size: 11px; }

/* The locked pane. It replaces the tier-bar banner, which was unstyled and
   pushed the whole desk down - the lock belongs ON the thing that is locked. */
.cdp-cell.is-locked { position: relative; }
.cdp-cell.is-locked .cdp-cell-canvas,
.cdp-cell.is-locked .cdp-cell-head,
.cdp-cell.is-locked .cdp-cellstate { visibility: hidden; }
.cdp-lock {
  position: absolute; inset: 0; z-index: 8;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 4px; padding: 16px; text-align: center;
  background: var(--cdp-panel); border-radius: inherit; color: var(--cdp-muted);
}
.cdp-lock svg { width: 26px; height: 26px; color: var(--cdp-muted); opacity: .75; }
.cdp-lock b { color: var(--cdp-text); font-size: 14px; font-weight: 800; }
.cdp-lock p { margin: 0; font-size: 12.5px; }
.cdp-lock-cta {
  margin-top: 8px; padding: 7px 14px; border-radius: 999px;
  background: var(--cdp-accent); color: #fff !important;
  font-size: 12.5px; font-weight: 800; text-decoration: none;
}


/* Filled and Canceled joined the Positions/Orders strip, so the bottom row is
   two panels rather than three. The trade tables take the freed width - they are
   the ones with columns to show.

   MIN-WIDTH GUARDED. This was a bare rule sitting AFTER the two mobile
   single-column media queries, so at equal specificity it won everywhere and
   both of those blocks were dead code. On a phone the Alerts panel was therefore
   held at 1fr of a 390px viewport and its sentences wrapped one word per line --
   the owner's "the alerts were squished on the side". */
@media (min-width: 1101px) {
  .cdp-bottom { grid-template-columns: 2.25fr 1fr; }
}


/* The chart footer is the Tiingo credit and nothing else now. "Data / Closed /
   8:00 PM" repeated the Market Closed pill in the order ticket and the clock in
   the header, and it cost a band of chart height to do it. Attribution is
   required, so it stays - sitting in the gap directly above the Alerts panel,
   which is where the owner asked for it. */
.cdp-chart-footer {
  display: flex !important;
  justify-content: flex-end;
  align-items: center;
  padding: 4px 12px !important;
  background: transparent;
  border: 0;
}
/* ATTRIBUTION MUST ACTUALLY RENDER. Hiding .cdp-mini also hid the Tiingo credit
   nested inside it - a display:inline child of a display:none parent still does
   not paint, and a DOM probe measured the link at 0x0. The chart page was
   crediting the data provider nowhere, which is a licence obligation, not a
   style choice. Hide the label only, and let the credit through. */
.cdp-chart-footer .cdp-mini-label { display: none; }
.cdp-chart-footer .cdp-mini { display: flex; align-items: baseline; gap: 8px; }
.cdp-chart-footer .cdp-data-credit { display: inline !important; }
.cdp-chart-footer .cdp-data-credit,
.cdp-credit-only {
  display: inline; font-size: 11.5px; color: var(--cdp-muted);
}
.cdp-credit-only a { color: var(--cdp-muted); text-decoration: underline; }

/* "On watchlist" and "Customize chart" were bleeding off the header edge. */
.cdp-head-top { flex-wrap: wrap; row-gap: 6px; min-width: 0; }
.cdp-head-top > * { min-width: 0; }

/* Time in force rendered heavier and larger than every other control in the
   ticket. It is one field among several, not a heading. */
.cdp-ticket select, select.cdp-select, #cdpTif {
  font-size: 13.5px !important;
  font-weight: 600 !important;
}


/* The floating typeahead. It is a child of <body> so no ancestor's overflow can
   clip it; everything positional is set inline by place(). */
.cdp-symmenu--float {
  position: fixed;
  margin: 0;
  max-height: 320px;
  overflow-y: auto;
  background: var(--cdp-surface, #fff);
  border: 1px solid var(--cdp-border);
  border-radius: 12px;
  box-shadow: 0 18px 44px rgba(15, 23, 41, .18);
  list-style: none;
  padding: 4px;
}
.cdp-symmenu--float[hidden] { display: none; }
.cdp-symmenu--float li {
  display: flex; gap: 10px; align-items: baseline;
  padding: 7px 10px; border-radius: 8px; cursor: pointer;
}
.cdp-symmenu--float li.on { background: var(--cdp-accent-soft); }
.cdp-symmenu--float li b { min-width: 62px; font-weight: 800; }
.cdp-symmenu--float li span { color: var(--cdp-muted); font-size: 12.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* "Why can't Go Pro look like a very classy pill?" - it was a square-ish block
   butted against the neighbouring control with a sliver of white between them. */
.nav-gopro, a.nav-gopro, .jag-gopro, a[href="/pro"].btn, .go-pro-btn {
  border-radius: 999px !important;
  padding: 8px 18px !important;
  font-weight: 800 !important;
  letter-spacing: .01em;
  box-shadow: 0 1px 2px rgba(15, 23, 41, .12);
  border: 0 !important;
}


/* The ticker was clipped along its top edge - a 38px line in a row that did not
   reserve room for its own ascenders, so "SPY 776.34" lost its cap height. */
.cdp-head, .cdp-head-top { overflow: visible; }
.cdp-head-top { padding-top: 4px; align-items: baseline; }
.cdp-head-price, .cdp-sym, .cdp-last { line-height: 1.22; }


/* Multi-chart reclaims the watchlist column. 240px beside four charts is 240px
   the charts needed - a quad could not fit on one screen at all. */
body.cdp.cdp-wl-off .cdp-main { grid-template-columns: minmax(0, 1fr) 304px; }
body.cdp.cdp-wl-off #cdpWlPanel { display: none; }
#cdpWlToggle[hidden] { display: none; }


/* Watchlist sort menu. FIXED and body-mounted, positioned by JS against the
   button - NOT absolute inside the panel header.

   It used to be appended to the sort button's parent, which lives inside
   .cdp-panel, and .cdp-panel sets overflow:hidden because that is what keeps the
   chart inside its rounded corners. So the menu was CLIPPED at the panel edge:
   the owner saw a sliver of white poking past the watchlist card and nothing
   else - "when I click watchlist, it hides behind the chart". z-index cannot fix
   a clip; the only cure is leaving the overflow context entirely, which is the
   same thing .cdp-tip already does two hundred lines up.

   12060 matches .cdp-tip so it also clears the shared shell's 10040-12050 band. */
.cdp-wlsort {
  position: fixed; z-index: 12060;
  min-width: 210px; margin: 0; padding: 4px; list-style: none;
  background: var(--cdp-surface, #fff);
  border: 1px solid var(--cdp-border); border-radius: 12px;
  box-shadow: 0 18px 44px rgba(15, 23, 41, .18);
}
.cdp-wlsort[hidden] { display: none; }
.cdp-wlsort li {
  padding: 8px 10px; border-radius: 8px; cursor: pointer;
  font-size: 13px; color: var(--cdp-text); white-space: nowrap;
}
.cdp-wlsort li + li { border-top: 1px solid var(--cdp-border); }
.cdp-wlsort li:hover { background: var(--cdp-accent-soft); }
.cdp-wlsort li.on { font-weight: 800; color: var(--cdp-accent); }
.cdp-wlsort li.on::after { content: ' \2713'; }


/* ══ QUAD HAS TO FIT ON THE SCREEN ═════════════════════════════════════════
   Measured live at a 929px viewport: .cdp-app was 1428 tall and each of the four
   cells was pinned at 526 - the height a SINGLE chart had been given - so the
   bottom row sat below the fold and the dock with it.

   Cause: .cdp-app is on min-height (correct - there is a footer under the frame),
   so every row below it resolves against CONTENT, and `minmax(0,1fr)` had no
   definite height to halve. The grid was dividing a number that was itself
   derived from the cells.

   Giving the chart area a definite ceiling breaks that circle: 1fr now resolves
   against the viewport, so quad genuinely halves. The max() floor means a short
   window still scrolls rather than crushing the candles, and the 168px dock plus
   its gaps are reserved so alerts and open orders stay on screen - which was the
   owner's actual requirement: the multi layout WITHOUT losing them. */
/* The 168px here was a literal copy of ONE of the three dock heights this
   stylesheet defines (168 default, 280 between 1101-1280px, auto below 1240).
   Between 1101 and 1280 the desk therefore overflowed the fold by exactly the
   112px difference - quad rendered correctly and the dock still hung off the
   bottom, which is why "quad still doesn't fit" survived the last fix.
   The dock height is a variable now, so the reserve tracks whatever the active
   query sets. And the cap only applies where .cdp-main is a single row: below
   1101px it stacks, where a max-height crushes the chart to 144px instead. */
/* ONE MORE ROW IN POSITIONS AND ALERTS. Owner, 2026-08-16: "make the positions
   and alerts a tad longer. Maybe we can squeeze in one more row on the positions
   and one more row on the alerts."

   This variable is the whole lever. .cdp-main's max-height below subtracts it, so
   the dock gets exactly what the chart gives up -- raising it here is what makes
   the panes taller; the panes themselves have no fixed height to edit. A dock row
   measures 52px (header 148 -> row1 187 -> row2 239 on his screenshot), so +52
   buys one row in BOTH panes at once, because Positions and Alerts share the row.
   168 -> 220. The chart keeps its 460px floor, so a short window scrolls rather
   than crushing the candles. */
/* 300px: at 220 the owner still counted two rows with empty space under the dock.
   A dock row is ~52px, so this is the original 168 plus two more rows and change. */
/* THREE ROWS, AND THE CHART GETS THE REST. Owner: "I want a little more chart, so
   make it so positions only shows three rows, and make the alerts the equivalent of
   that height." A dock row measures 75px on his screen (DOGE 97 -> TSLA 172 -> QQQ 247)
   and the panel header is 48px, so 3 x 75 + 48 = 273. Alerts needs no separate number:
   both panels sit in the SAME grid row, so one height governs both. */
.cdp-app { --cdp-dock-h: 273px; }
@media (min-width: 1101px) and (max-width: 1280px) { .cdp-app { --cdp-dock-h: 360px; } }
@media (min-width: 1101px) {
  .cdp-main {
    max-height: max(460px, calc(100vh - var(--cdp-appbar-h) - var(--cdp-dock-h) - 56px));
  }
}
@media (max-width: 1100px) { .cdp-main { max-height: none; } }
.cdp-canvasrow, .cdp-grid { min-height: 0; }
.cdp-grid[data-layout="quad"] .cdp-cell,
.cdp-grid[data-layout="split"] .cdp-cell { min-height: 0; }


/* CORE SHOWS THE PLAIN-ENGLISH ROW ONLY. The compact
   "BTC-USD - 1m - Crypto - O 63038.70 H 63047.86 L 63038.69 C 63047.86" line is
   right for someone who already reads charts and noise for someone who does not,
   and it repeats Open twice over when the spelled-out row sits directly beneath
   it. Core is the beginner tier, so Core gets the words and nothing else;
   Analysis and Advanced get both, which is what the owner asked for. */
body.cdp[data-cdp-tier="core"] #cdpSubmeta { display: none; }

/* The header was crowding the top edge - the ticker, the compact line and the
   stats row stacked with no breathing room and the price clipped its ascenders. */
.cdp-head-top { padding-top: 6px; }
.cdp-submeta { margin-top: 2px; }
.cdp-submeta-stats { margin-bottom: 4px; }

/* Confirmation toast, bottom-right. The alert controls announced only through
   the screen-reader live region, so a sighted user pressed Alert four times and
   saw nothing happen. Corner, not centre - the owner was explicit that the
   green thing in the middle of the page is the annoying one. */
.cdp-toast {
  position: fixed; right: 18px; bottom: 18px; z-index: 2147482000;
  display: flex; align-items: center; gap: 10px;
  padding: 11px 16px; border-radius: 12px;
  background: #0f1729; color: #fff;
  font-size: 13.5px; font-weight: 700;
  box-shadow: 0 18px 44px rgba(15, 23, 41, .34);
  opacity: 0; transform: translateY(8px);
  transition: opacity .18s ease, transform .18s ease;
  pointer-events: none;
}
.cdp-toast.on { opacity: 1; transform: translateY(0); }
.cdp-toast .cdp-toast-dot { width: 8px; height: 8px; border-radius: 50%; background: #5ee0a0; flex: 0 0 auto; }
@media (prefers-reduced-motion: reduce) { .cdp-toast { transition: none; } }


/* Chart layout now shares the tier row. Removing the hint sentence freed the
   width, so the workspace controls sit inline instead of wrapping to their own
   band - a row of vertical space the chart gets back, which also helps quad fit
   on one screen. */
.cdp-tierbar { flex-wrap: nowrap; }
.cdp-workspace { margin-left: auto; display: flex; align-items: center; gap: 8px; }
.cdp-tier-hint { display: none; }


/* ══ THE CHART HEADER WAS CLIPPING ITS OWN TICKER ═════════════════════════
   Measured live: .cdp-chart-toolbar sits at y=103 with padding "0px 16px" - no
   vertical padding at all - so the 20px price line began 5px from the panel
   edge and lost its ascenders. Worse, .cdp-chart-meta was 48px tall with
   overflow:hidden while holding three rows (ticker, the compact O/H/L/C line,
   and the Open/Day range/Volume row), so the third row was cut off entirely.

   I styled .cdp-head-top and .cdp-head twice before this. NEITHER CLASS EXISTS
   on the page - which is why the padding never appeared and the owner kept
   reporting the same clipped header. The real elements are .cdp-chart-toolbar
   and .cdp-chart-meta. Measured, not guessed, this time. */
.cdp-chart-toolbar {
  height: auto !important;
  min-height: 58px;
  padding: 10px 16px !important;
  align-items: flex-start;
}
.cdp-chart-meta {
  overflow: visible !important;
  height: auto !important;
  min-height: 0;
}
.cdp-sym-row { align-items: baseline; }


/* ══ TYPE + CONTRAST PASS ══════════════════════════════════════════════════
   Two problems a designer would call first on this header.

   1. TWO TYPE SYSTEMS FOR ONE FACT. The compact row uses single-letter keys with
      coloured tabular values; the row beneath uses grey word-labels with bold
      values. Same information, two visual languages, stacked six pixels apart.
      They share one scale now: label at 11.5px/600 in muted, value at 13px/700
      tabular. Colour still carries direction on High/Low/Close, because that is
      information rather than decoration.

   2. GREY THAT CANNOT BE READ. Several greys sat near 2.8-3.2:1 on white, under
      the 4.5:1 minimum. On a screen showing someone's money that is a defect,
      not a preference. Raised to ~5.4:1, which stays visibly secondary without
      disappearing.

   Also: one 4px spacing rhythm, and tabular figures everywhere a number can
   change - digits that shift width make a live price look unstable. */
.cdp-submeta,
.cdp-submeta-stats {
  font-variant-numeric: tabular-nums;
  line-height: 1.5;
}
.cdp-ohlc-k,
.cdp-sm i {
  font-size: 11.5px;
  font-weight: 600;
  color: var(--cdp-muted);
  letter-spacing: .01em;
}
.cdp-ohlc-v,
.cdp-sm b {
  font-size: 13px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.cdp-submeta-stats { gap: 0 20px; margin-top: 4px; }
.cdp-sm { display: inline-flex; align-items: baseline; gap: 6px; }

/* The fallback qualifier is secondary to the interval it qualifies. */
.cdp-tf-note { font-weight: 600; color: var(--cdp-muted); font-size: 11.5px; }

/* Chip fills carry ONE meaning: filled = currently active. Save was a filled
   green pill sitting beside outline chips, so the eye landed on the least
   important control on the row. */
.cdp-chip { font-variant-numeric: tabular-nums; }
#cdpSave:not(.saved) { background: transparent; color: var(--cdp-text); }

/* Axis labels were the lightest text on the page and sit against a white plot. */
.cdp-axis, .cdp-grid-label { color: var(--cdp-muted); }


/* The sort menu opened ON TOP of the list it sorts, so choosing an order hid the
   result of choosing it. It sits to the RIGHT of the panel now, clear of the
   rows, which is where a context menu belongs when its target is the panel
   beneath it. */
.cdp-wlsort { left: calc(100% + 8px); right: auto; top: 0; }
@media (max-width: 1100px) { .cdp-wlsort { left: auto; right: 0; top: calc(100% + 6px); } }


/* The remove control: quiet until the row is hovered, so a list of eight tickers
   is not eight red x's shouting at you. */
.cdp-wl-x {
  color: transparent; background: none; border: 0; cursor: pointer;
  font-size: 15px; line-height: 1; padding: 0 4px;
}
tr:hover .cdp-wl-x, .cdp-wl-x:focus-visible { color: var(--cdp-muted); }
tr:hover .cdp-wl-x:hover { color: var(--cdp-red); }


/* ══ AN ARMED TOOL SHOULD LOOK ARMED ═══════════════════════════════════════
   The owner on the brush: "as soon as I pick it up it starts drawing, and the
   pointer never changes so I cannot tell what mode I am in." A cursor is the
   only feedback a drawing surface gives before the first click, and this had
   none - the pointer stayed a plain arrow whether you were about to draw a
   trend line or about to pan the chart.

   Crosshair for the geometric tools, which is the convention; a text cursor for
   the annotation tool. The rail button already lights, but the button is not
   where the user is looking - the chart is. */
body.cdp[data-cdp-tool] .cdp-cell-canvas,
body.cdp[data-cdp-tool] .cdp-cell-canvas canvas { cursor: crosshair; }
body.cdp[data-cdp-tool="text"] .cdp-cell-canvas,
body.cdp[data-cdp-tool="text"] .cdp-cell-canvas canvas { cursor: text; }
body.cdp[data-cdp-tool="brush"] .cdp-cell-canvas,
body.cdp[data-cdp-tool="brush"] .cdp-cell-canvas canvas { cursor: cell; }


/* The inline note editor. Sits on the chart at the exact pixel clicked, so the
   user types where the note will live rather than in a dialog covering it. */
.cdp-noteinput {
  position: absolute; z-index: 30; transform: translate(-2px, -50%);
  min-width: 150px; max-width: 240px;
  padding: 5px 9px;
  border: 1px solid var(--cdp-accent); border-radius: 8px;
  background: var(--cdp-surface, #fff); color: var(--cdp-text);
  font: inherit; font-size: 12.5px; font-weight: 600;
  box-shadow: 0 8px 22px rgba(15, 23, 41, .18);
  outline: none;
}
.cdp-cell-canvas { position: relative; }


/* The attribution strip: the grey gap between the chart panel and the dock.
   Small, muted, right-aligned - present for the licence, invisible to anyone
   reading the chart. It must never render on the canvas again. */
.cdp-credit-strip {
  display: none;
  justify-content: flex-end; align-items: center;
  /* Tight to the chart panel above it - the owner wants it directly under the
     Closed / 8:00 PM line, not floating in the middle of the gap. The negative
     top margin pulls it against the panel through the grid gap. */
  margin: -10px 0 0; padding: 0 6px; min-height: 12px; line-height: 1;
}
.cdp-credit-strip .cdp-data-credit {
  font-size: 10.5px; line-height: 1.2;
  color: var(--cdp-muted); text-decoration: none;
}
.cdp-credit-strip .cdp-data-credit:hover { opacity: 1; text-decoration: underline; }
/* Belt and braces: no credit inside the chart panel, whatever else changes. */
.cdp-chart-wrap .cdp-data-credit { display: none !important; }


/* ── Empty-field validation on the order ticket ──────────────────────────────
   2026-08-16. These rules were first written into chart-desk.css — which
   chart.html does not load. Only chart-desk-page.css is linked, so the class was
   applied and nothing turned red. Same failure as the tokens file earlier today:
   a stylesheet nobody imports is a stylesheet that does nothing.
   Placed AFTER the base .cdp-input rule so it wins without !important games. */
.cdp-input.cdp-invalid,
input.cdp-invalid {
  border-color: #C4372F !important;
  background: #FDF0EF;
  box-shadow: 0 0 0 3px rgba(196, 55, 47, .12);
}
.cdp-input.cdp-invalid:focus,
input.cdp-invalid:focus {
  border-color: #C4372F !important;
  box-shadow: 0 0 0 3px rgba(196, 55, 47, .22);
}
.cdp-sub-error {
  color: #C4372F !important;
  font-weight: 700;
}


/* Watchlist rows waiting on their first candle fetch. Em-dashes alone read as
   "this is broken"; a shimmer reads as "this is coming". No data claim either way. */
#cdpWatchlist tr.wl-loading td { color: transparent !important; position: relative; }
#cdpWatchlist tr.wl-loading td:first-child { color: inherit !important; }
#cdpWatchlist tr.wl-loading td:not(:first-child)::after {
  content: ""; position: absolute; right: 12px; top: 50%; transform: translateY(-50%);
  width: 38px; height: 9px; border-radius: 5px;
  background: linear-gradient(90deg, #EDF0F4 25%, #F7F9FB 50%, #EDF0F4 75%);
  background-size: 200% 100%; animation: wlShimmer 1.1s ease-in-out infinite;
}
@keyframes wlShimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
@media (prefers-reduced-motion: reduce) {
  #cdpWatchlist tr.wl-loading td:not(:first-child)::after { animation: none; }
}


/* Order-ticket header: the SYMBOL is the important thing, not the market state.
   It was .muted at body size while a red "Market Closed" pill sat beside it twice the
   visual weight — the owner asked ten times to take the pill off the ticket. The pill
   now lives on the search line where the market state belongs; the symbol is what the
   ticket header carries, and it reads like a heading. */
.cdp-tkt-sym {
  font-size: 17px; font-weight: 800; letter-spacing: -.01em;
  color: var(--cdp-text, #0f172a);
}
.cdp-mkt-inline { margin-left: 10px; flex: 0 0 auto; align-self: center; }


/* A disabled button that just sits there reads as a dead app. When someone presses Buy
   with a field missing, shake it once — the same convention a password box uses. Pairs
   with the red field and the red reason line; this is the part that says "I heard you,
   and I said no", which a grey button alone never does. */
@keyframes cdpNope {
  0%, 100% { transform: translateX(0); }
  15% { transform: translateX(-7px); }
  30% { transform: translateX(6px); }
  45% { transform: translateX(-4px); }
  60% { transform: translateX(3px); }
  80% { transform: translateX(-2px); }
}
.cdp-cta-main.cdp-nope { animation: cdpNope .42s ease-in-out 1; }
@media (prefers-reduced-motion: reduce) {
  .cdp-cta-main.cdp-nope { animation: none; outline: 2px solid #C4372F; outline-offset: 2px; }
}


/* ── The watchlist Add button rendered as "Ad" ─────────────────────────────
   Owner: "make sure the add button on the watchlist is bigger and actually says
   the full ADD." The add row is input + Add + dismiss in a 206px rail, and the
   input had no flex floor, so it took the space and clipped the button's own text.
   The button is the fixed element here -- it has exactly three characters and can
   never need less room -- so it stops shrinking and the input gives way instead. */
/* The row is a grid now, so the old flex sizing here is inert. Kept as a single
   font-size bump; the geometry lives on .cdp-wl-add-go itself. */
.cdp-wl-add .cdp-wl-add-go { font-size: 13px; }
.cdp-wl-add #cdpWlAddInput { min-width: 0; height: 36px; }


/* ── Panels dropdown ───────────────────────────────────────────────────────
   One control for "how much room does the chart get". Checkboxes rather than
   three separate chips: the state is readable without opening anything, and the
   toolbar does not grow by three buttons. */
.cdp-viewdd { position: relative; display: inline-flex; }
.cdp-viewmenu {
  /* fixed + 12060 matches .cdp-wlsort: the only way out of .cdp-panel's
     overflow:hidden, which was slicing this menu off at the panel edge. */
  position: fixed; z-index: 12060;
  background: var(--cdp-surface, #fff); border: 1px solid var(--cdp-border);
  border-radius: 10px; box-shadow: 0 14px 34px rgba(15,23,41,.16);
  padding: 8px; min-width: 178px; display: flex; flex-direction: column; gap: 2px;
}
.cdp-viewmenu[hidden] { display: none; }
.cdp-viewmenu label {
  display: flex; align-items: center; gap: 9px; padding: 7px 8px;
  border-radius: 7px; font-size: 13px; font-weight: 600; cursor: pointer;
}
.cdp-viewmenu label:hover { background: var(--cdp-panel-2); }
.cdp-viewmax {
  margin-top: 4px; font: inherit; font-size: 12.5px; font-weight: 800; cursor: pointer;
  padding: 8px; border-radius: 7px; border: 1px solid var(--cdp-border);
  background: var(--cdp-panel-2); color: var(--cdp-text);
}
.cdp-viewmax:hover { border-color: var(--cdp-accent); color: var(--cdp-accent); }
body.cdp.cdp-pos-off #cdpPosPanel { display: none; }
body.cdp.cdp-alerts-off #cdpAlertsPanel { display: none; }
/* One panel left in the dock should use the full width rather than sit in a third. */
body.cdp.cdp-pos-off .cdp-bottom,
body.cdp.cdp-alerts-off .cdp-bottom { grid-template-columns: minmax(0, 1fr); }
/* Both gone: the dock reserves nothing and the chart takes the frame. */
body.cdp.cdp-dock-off .cdp-bottom { display: none; }
body.cdp.cdp-dock-off .cdp-app { --cdp-dock-h: 0px; }


/* ── Beginner: one order type ──────────────────────────────────────────────
   Owner asked whether to hide Limit and Stop for beginners or let them choose.
   Hide them, for a specific reason: this is not a preference, it is a decision the
   user has no way to make yet. Limit and Stop cannot be chosen sensibly by someone
   who does not yet know what a market order does -- so offering three buttons asks a
   question they cannot answer, and the most common outcome is a limit order that
   never fills and looks like a broken site.

   Market alone is also the honest default: it is what "buy this now" means, which is
   what a first-time user is actually trying to do.

   HIDDEN, NOT REMOVED. Everything still works, the segment stays in the DOM so the
   ticket's own logic is untouched, and one click on Intermediate brings it all back. */

/* With one order type there is nothing to label, and no time-in-force question either:
   a market order placed while the market is shut queues for the open, which the ticket
   already says in words. */



/* ── What each mode shows on the toolbar ───────────────────────────────────
   Owner, looking at the desk: "I'm confused by all these buttons... beginner should
   just have one, intermediate feels like they should have two, advanced everything."

   BUG FIXED HERE TOO: hiding the order-type segment for beginners left its "Order
   type" LABEL behind, so the ticket showed a heading with nothing under it -- worse
   than showing the buttons, because it looks broken rather than simple. The Price
   field goes with it: a market order takes the going price, so there is no price to
   set, and a greyed box a beginner cannot use is a question they cannot answer. */


/* LAYOUTS BY MODE. One chart is all a beginner needs -- a second pane is for
   comparing two instruments, which is not a thing you do on day one. Two at
   Intermediate, everything at Advanced. Hidden, never locked: one click up a mode
   brings them back, and nothing here is sold. */

/* With one layout there is nothing to choose, so the chooser itself goes. */


/* Workspace tools are not beginner tools. Sync links two charts' crosshairs -- it
   cannot do anything with one chart. Save stores a layout, which only matters once
   you have arranged one. Panels hides docks a beginner is still learning to read. */



/* Logarithmic spacing is an Intermediate idea. Owner: "maybe take off the logarithmic
   scale button for beginner and just have time labels and grid lines and tell them to
   toggle and play with them." Two switches a beginner can try beats three where one
   needs a paragraph of explanation first. */



/* ── Mode preview bar ──────────────────────────────────────────────────────
   Bottom of the screen, not the top: the thing being previewed is the toolbar and
   the ticket, so a bar across the top would cover the very controls it is asking
   you to look at. Says plainly that nothing has changed, because the whole point
   is that trying a mode should not cost you the one you are on. */
.cdp-previewbar {
  position: fixed; left: 50%; transform: translateX(-50%); bottom: 18px; z-index: 90;
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 12px 16px; border-radius: 12px;
  background: var(--cdp-text, #0f1729); color: #fff;
  box-shadow: 0 18px 44px rgba(15, 23, 41, .3);
  font-size: 13.5px; font-weight: 600; max-width: calc(100vw - 32px);
}
.cdp-previewbar b { font-weight: 800; }
.cdp-previewbar button {
  font: inherit; font-size: 13px; font-weight: 800; cursor: pointer;
  padding: 8px 14px; border-radius: 9px; white-space: nowrap;
}
.cdp-prev-keep { background: var(--cdp-accent, var(--jag-green)); color: #fff; border: 1px solid var(--cdp-accent, var(--jag-green)); }
.cdp-prev-keep:hover { filter: brightness(1.08); }
.cdp-prev-exit { background: transparent; color: #fff; border: 1px solid rgba(255,255,255,.42); }
.cdp-prev-exit:hover { border-color: #fff; }

/* The reason a Sell will be refused, stated on the ticket instead of only in a toast.
   Red because it is the thing stopping the order -- but only ever in sell mode, since
   holding none of something you are about to buy is completely normal. */
.cdp-kv.cdp-kv-blocked span, .cdp-kv.cdp-kv-blocked b { color: var(--cdp-red, #d64550); }
.cdp-kv.cdp-kv-blocked b::after {
  content: ' — nothing to sell';
  font-weight: 700; font-size: 11.5px; white-space: nowrap;
}

/* The two numbers that disagree, marked so the comparison is the thing you see.
   Reuses the refusal colour from the blocked-sell row so 'red on the ticket' means one
   thing: this order will not go through as it stands. */
.cdp-kv.cdp-kv-short span, .cdp-kv.cdp-kv-short b { color: var(--cdp-red, #d64550); }
.cdp-kv.cdp-kv-short b { font-weight: 800; }

/* Watchlist refresh. Same quiet mini-button family as the add control -- it is a
   recovery affordance, not something to draw the eye. */
.cdp-wl-refresh { font-size: 15px; line-height: 1; padding: 0 8px; min-height: 28px; }
.cdp-wl-refresh:hover { color: var(--cdp-accent); border-color: var(--cdp-accent); }

/* Extended-hours toggle on the ticket. Sits under the CTA sub-line because it modifies
   what that line says -- whether the order fills now or waits for the open. Quiet by
   design: it is a qualifier on the order, not an action. */
.cdp-ext {
  display: flex; align-items: center; gap: 9px; margin: 10px 0 0;
  font-size: 12.5px; font-weight: 600; color: var(--cdp-muted); cursor: pointer;
}
.cdp-ext[hidden] { display: none !important; }
.cdp-ext input { width: 15px; height: 15px; accent-color: var(--cdp-accent); cursor: pointer; flex: 0 0 auto; }
.cdp-ext:hover { color: var(--cdp-text); }

/* THE CHART IS THE PAGE, SO IT GOES FIRST ON ANY STACKED LAYOUT.
   .cdp-main is a three-column grid down to 1100px; below that it collapses to a
   single column and then stacks in DOM ORDER, which put the WATCHLIST above the
   chart -- the first thing a visitor saw was a quote table. That is true on a
   phone AND on a tablet, so the boundary here is 1100px: exactly the width where
   the grid stops holding the columns side by side. Verified live -- .cdp-main
   computes to one 864px column at 900px, and to "240px 832px 304px" at 1440px.

   Order is set rather than moving markup, so the desktop grid and every script
   that queries these ids are untouched. */
@media (max-width: 1100px) {
  .cdp-main > .cdp-panel { order: 5; }
  .cdp-main > .cdp-chartpanel { order: 1; }
  #cdpTicketPanel { order: 2; }
  #cdpWlPanel     { order: 3; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   PHONE LAYOUT (≤ 760px)
   Added 2026-08-17. Until now the smallest breakpoint was 1100px, so a 390px
   phone was being served the tablet layout. Everything below is a specific
   defect the owner photographed on an iPhone, not general tidying.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (max-width: 760px) {

  /* 2. THE HEADER OVERLAPPED ITSELF. Symbol/price and the timeframe pills were
        one nowrap flex row, so "NVDA" was clipped to "N" and the Market pill ran
        off the right edge. On a phone they become two rows: identity first, then
        a horizontally scrollable strip of pills. */
  /* THE TOOLBAR IS A FIXED 58px BOX AND .cdp-chart-meta COLLAPSED TO HEIGHT 0
     inside it, while an earlier rule set overflow:visible -- so the symbol row
     painted outside its own box and the timeframe pills landed ON TOP of
     "NVDA 225.07". Measured live at 390px: meta h=0, sym-row top=78 h=24,
     controls top=86. Both boxes must size to their content here. */
  .cdp-chart-toolbar {
    flex-direction: column; align-items: stretch; gap: 8px; padding: 10px 12px;
    height: auto !important; min-height: 0 !important;
  }
  .cdp-chart-meta {
    width: 100%; overflow: visible;
    height: auto !important; min-height: 0; flex: 0 0 auto;
  }
  .cdp-sym-row { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px; }
  .cdp-chart-controls {
    width: 100%; flex-wrap: wrap; overflow-x: visible; overflow-y: visible;
    -webkit-overflow-scrolling: touch; scrollbar-width: none; padding-bottom: 2px;
    justify-content: flex-start; gap: 4px;
  }
  .cdp-chart-controls::-webkit-scrollbar { display: none; }
  .cdp-chart-controls > * { flex: 0 0 auto; }
  #cdpTf.cdp-chart-controls .cdp-chip { padding: 6px 9px; font-size: 12.5px; }

  /* 3. THE MARKET PILL BLED OFF-SCREEN. It is status, not a control, so it wraps
        onto its own line instead of forcing the row wider than the viewport. */
  .cdp-mkt-inline { white-space: nowrap; align-self: flex-start; }

  /* 4. EVERY PANEL GETS THE FULL WIDTH. A 1fr column of a 390px screen is what
        wrapped the Alerts copy one word per line. */
  .cdp-main, .cdp-bottom { grid-template-columns: minmax(0, 1fr) !important; gap: 10px; }
  .cdp-bottom > .cdp-panel { min-width: 0; }
  .cdp-bottom .cdp-panel-body { height: auto; }
  #cdpAlertsPanel .cdp-panel-body { min-width: 0; }

  /* Sticky Buy/Sell on the order ticket — thumb-reachable without scrolling past the chart. */
  #cdpTicketPanel .cdp-cta-main {
    position: sticky;
    bottom: calc(10px + env(safe-area-inset-bottom, 0px));
    z-index: 40;
    margin-top: 14px;
    box-shadow: 0 4px 22px rgba(15, 23, 20, .16);
  }

  /* 5. Tables must scroll inside their own panel, never widen the page. */
  .cdp-panel-body { overflow-x: auto; }
  .cdp-tbl { min-width: 0; }

  /* THE GRID ROW WAS PINNED TO 58px AND THE TOOLBAR OVERFLOWED IT.
     Line ~1791 sets `.cdp-chart-wrap { grid-template-rows: 58px auto ... }` as a
     BARE rule, after the max-width:1100px block that sets `auto auto ...`, so at
     equal specificity it won at every width -- the same dead-media-query trap as
     .cdp-bottom. Once the phone toolbar became two rows tall (symbol line, then
     the timeframe strip) its content overflowed that fixed 58px row and painted
     straight over the tier bar, the Extended-hours toggle and the chart itself.
     Rows are auto here so each one sizes to what is actually in it. */
  .cdp-chart-wrap {
    grid-template-rows: auto auto minmax(300px, 1fr) auto;
    min-height: 470px;
  }

  /* KEEP THE HEADER A PLAIN COLUMN. A previous attempt added `flex-wrap: wrap`
     and `flex: 1 1 100%` to these children -- but this toolbar is
     flex-direction:column, so a 100% flex-basis resolves against HEIGHT, not
     width. That opened ~280px of dead white space between the price line and the
     tier bar and pushed the timeframe strip off the right edge. Every child is
     simply auto-height and full-width here. */
  .cdp-chart-toolbar { gap: 6px; padding: 8px 12px; flex-wrap: nowrap; }
  .cdp-chart-toolbar > * { flex: 0 0 auto; width: 100%; max-width: 100%; }
  .cdp-symsearch { min-width: 0; }
  .cdp-search-desk { width: 100%; max-width: none; margin: 0; flex-wrap: nowrap; }
  .cdp-search-desk > #cdpCustomiseBtn { width: auto; flex: 0 0 auto; }
  .cdp-chart-toolbar > .cdp-pill { align-self: flex-start; width: auto; }
  /* Nothing in the header may spill outside its own row again. */
  .cdp-chart-toolbar { overflow: visible; height: auto; min-height: 0; }
  /* THE TOOL STRIP WAS CLIPPED IN INTERMEDIATE AND ADVANCED. The tier buttons and
     the tool cluster shared one nowrap row, so on Advanced the layout/split/panel
     icons ran straight off the right edge with no way to reach them.

     What phone trading apps actually do: the chart owns the screen and the tools
     live behind ONE control, because a horizontal strip of icons cannot survive a
     390px screen. Short of that restructure, the honest minimum is that nothing is
     unreachable -- so the bar wraps to a second line instead of clipping, and each
     cluster scrolls sideways under its own finger if it still cannot fit. */
  .cdp-tierbar { min-height: 0; flex-wrap: wrap; row-gap: 6px; padding: 6px 12px; }
  .cdp-tier { flex: 0 0 auto; }
  .cdp-tbcluster {
    flex: 1 1 100%; overflow-x: auto; overflow-y: hidden;
    -webkit-overflow-scrolling: touch; scrollbar-width: none;
    border-left: 0; padding-left: 0;
  }
  .cdp-tbcluster::-webkit-scrollbar { display: none; }
  .cdp-tierchips { overflow-x: auto; flex: 1 1 100%; }
  /* Tier buttons shrink so all three fit one line at 390px. */
  .cdp-tier-btn { padding: 0 10px; font-size: 12.5px; }

  /* THE SAME NUMBERS TWICE. .cdp-submeta prints "VZ · 5D · NASDAQ · O 48.78 H 48.79
     L 48.54 C 48.55" and the line directly under it prints Open / Day range /
     Volume -- the same open, high and low, spelled out. On a 390px screen that
     duplicate costs a band of chart height to say nothing new, and the crosshair
     gives per-bar values on demand anyway. Desktop keeps both. */
  .cdp-submeta { display: none; }

  /* THE TOOL RAIL WAS EATING THE TIMEFRAME CHIPS. Measured live at 390px: the
     vertical .cdp-rail spans x 15-59 and the 1m chip spans x 31-71, both
     position:static -- so the rail, being later in the DOM, painted on top and
     document.elementFromPoint over "1m" returned div.cdp-rail. The chip was
     visible and completely untappable.

     The toolbar gets its own stacking context above the canvas row so its
     controls always win the hit test, and the rail is laid out as a horizontal
     strip under the header instead of a column beside it, which is the only
     shape that fits a 390px screen. */
  .cdp-chart-toolbar { position: relative; z-index: 3; }
  /* THAT RESTACK NEVER HAPPENED. `.cdp-canvasrow { flex-direction: column }` was
     the rule meant to do it, and it is INERT: line 399 declares
     `.cdp-canvasrow { display: grid; grid-template-columns: 44px minmax(0,1fr) }`,
     and flex-direction does nothing to a grid container. Measured at 390px with
     the rule in place: .cdp-canvasrow computes display:grid, .cdp-rail is still
     the 44px LEFT COLUMN (x 15, w 44, h 300) and .cdp-grid gets only 316 of the
     panel's 360px. In Advanced the rail holds 501px of tools inside that 44px
     window - one tool visible at a time - and the chart loses 12% of its width,
     which is what collides the time-axis labels at the bottom of the canvas.
     Stated in the grid's own language it actually applies: one column, and two
     explicit rows. The rows must be explicit - with both rows implicit and `auto`,
     align-content:stretch would hand the rail half the leftover height. */
  .cdp-canvasrow { grid-template-columns: minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr); }
  .cdp-rail {
    flex-direction: row; width: 100%; height: auto;
    border-right: 0; border-bottom: 1px solid var(--cdp-border);
    overflow-x: auto; overflow-y: hidden; padding: 4px 6px; gap: 4px;
  }
  .cdp-rail-sep { width: 1px; height: 22px; margin: 0 4px; }
  /* The rail now costs the canvas ROW 53px (44px tool + 8px padding + 1px rule)
     instead of costing the canvas 44px of WIDTH, and the row's 300px floor was
     written when the row held nothing but the canvas - so without this the chart
     would come out 360x247 where it used to be 316x300, i.e. WIDER but 6% smaller
     in area, which is the opposite of the point. The floor moves onto the canvas
     itself rather than being restated as 353px on the row, so it stays correct if
     the rail's height ever changes. Measured after: canvas 360x300, +13.9% area. */
  .cdp-grid { min-height: 300px; }

  /* TWO MORE DEAD DECLARATIONS IN THIS SAME BLOCK, both lost on specificity.
     1. `.cdp-tbcluster { border-left: 0; padding-left: 0 }` above is beaten by
        `.cdp-tierchips + .cdp-tbcluster { padding-left: 12px; border-left: 1px }`
        (line ~1006, two classes to one). Measured: the cluster still computes
        padding 0 0 0 12px, so the icon row starts 12px in behind a hairline that
        divides it from nothing - .cdp-tierchips is empty on this page.
     2. `.cdp-workspace { margin-left: auto }` (line ~2054, a BARE rule declared
        after the max-width:1100px block that sets margin-left:0) still applies
        here, so on a wrapped phone row the layout/Panels/Save group is shoved to
        the far right behind 107px of dead space. Measured at 390px, Advanced:
        margin-left 107.484px. auto margins are for a row with slack to give; a
        wrapped 390px row has none. */
  .cdp-tierchips + .cdp-tbcluster { border-left: 0; padding-left: 0; }
  .cdp-workspace { margin-left: 0; }

  /* TAP TARGETS. Measured at 390px: MAX 59x28, 1m chip 40x28, Beginner 76x24,
     watchlist refresh 24x28, Open-orders tab 100x23 -- all below a thumb-sized
     target. Height only, so nothing reflows sideways on a narrow screen. */
  .cdp-chip, .cdp-preset, .cdp-tier-btn, .cdp-htab,
  .cdp-mini-btn, .cdp-rtool, .cdp-seg-btn, .cdp-tbtn, .cdp-side {
    min-height: 44px; display: inline-flex; align-items: center; justify-content: center;
  }
  .cdp-mini-btn, .cdp-rtool, .cdp-tbtn { min-width: 44px; }

  /* Order ticket: one panel, no letter-stack, no page-wide scroll. */
  #cdpTicketPanel { min-width: 0; max-width: 100%; overflow-x: hidden; }
  #cdpTicketPanel .cdp-panel-body { overflow-x: hidden; padding-inline: 12px; }
  .cdp-field, .cdp-row2, .cdp-seg, .cdp-presets, .cdp-ticket-tabs { min-width: 0; max-width: 100%; }
  .cdp-row2 { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 8px; }
  .cdp-seg { display: flex; flex-wrap: nowrap; gap: 4px; overflow-x: auto; scrollbar-width: none; }
  .cdp-seg::-webkit-scrollbar { display: none; }
  .cdp-seg-btn { flex: 1 1 0; min-width: 0; padding-inline: 6px; font-size: 12px; }
  .cdp-input, .cdp-select { width: 100%; max-width: 100%; box-sizing: border-box; min-width: 0; }
  .cdp-cta-main { width: 100%; max-width: 100%; box-sizing: border-box; }
  .cdp-ticket-note .cdp-kv span,
  .cdp-ticket-note .cdp-kv b { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
}

/* THE HIDDEN ENGINE TICKET WAS PAINTING AT THE BOTTOM OF THE PAGE.
   #cdpRealTicket carries the `hidden` attribute, but `.panel` / `.order-ticket`
   set an author-level `display`, and an author rule beats the user-agent
   `[hidden] { display: none }`. So the legacy ticket rendered below the footer
   as a second, dead "NVDA / Quantity / MAX / Buy / Sell" form -- the owner's
   "fake order ticket that was misplaced". It must stay in the DOM (it is the
   only path that actually places an order) so it is hidden properly instead. */
#cdpRealTicket[hidden] { display: none !important; }

/* THE OTHER ROGUE TICKET. #mobileTradeDock is legacy furniture from the old
   simulator UI: on the chart desk it renders at the very bottom of the page as bare
   unstyled text -- "NVDA $218.72 BP $5,822.66 / Quantity / MAX / Queue Buy 1 /
   Queue Sell 1" -- because simulator.css carries exactly ONE .mtd- rule and the desk
   styles it nowhere. The owner has reported it three times as an unprofessional
   rogue popup, and I fixed #cdpRealTicket twice thinking it was the same element.

   It is also redundant here: this desk renders a full Order Ticket panel (
   #cdpTicketPanel) on mobile, so hiding the dock removes nothing a user can do.
   It stays in the DOM because js/simulator/order-ticket.js still reads it. */
body.cdp #mobileTradeDock { display: none !important; }


/* A panel that cannot accept a drawing says so for a moment, rather than letting
   the toolbar look broken. Paired with the say() in armDraw. */
.cdp-cell-needsym {
  outline: 2px solid var(--cdp-accent, var(--jag-green));
  outline-offset: -2px;
  transition: outline-color .2s ease;
}

/* ── SAVED LAYOUTS DIALOG ───────────────────────────────────────────────────
   It was built with className 'cdp-lay' -- the class on the three LAYOUT BUTTONS --
   so it inherited their button styling and rendered as a 147x22 static box at
   left:-59px, half off the window, at the bottom of the document. Measured live in
   Chrome on 2026-08-19. It needs to be a real centred modal, and `fixed` so no
   ancestor's overflow can clip it (.cdp-panel sets overflow:hidden, the same clip
   that once sliced the Panels menu). */
.cdp-layouts {
  position: fixed; inset: 0; z-index: 12080;
  display: flex; align-items: center; justify-content: center;
  padding: 20px; background: rgba(15, 23, 20, .38);
}
.cdp-layouts[hidden] { display: none; }

/* RECLAIM TOOLBAR WIDTH SO ALL SIX TIMEFRAMES ALWAYS FIT. (2026-08-22)
   Owner, twice: "all the time frames need to be [visible] all the time ... and right
   there, they're not", and "you need to get rid of one of these words, we have too many
   words up here."

   Both are the same problem. #cdpTf is overflow-x:auto with nowrap, so when the row runs
   out of width the FIRST chips scroll off the left - which is why his screenshot starts
   at 15m with 1m and 5m gone. The width was being spent on four controls and a label for
   ONE decision (how is my screen arranged): "Chart layout" + 3 icons + Panels +
   Customise + Layouts.

   Two removals, no functionality lost:
     .cdp-group-label   the words "Chart layout" next to three self-evident icons. He
                        confirmed the icons themselves read fine; it was the loose label
                        beside them that looked wrong.

   Customise sits next to Search (Coach + owner). The rail + was the other door to
   the same panel and stays hidden in the 2026-08-24 trim below. */
.cdp-tierbar .cdp-group-label { display: none; }

/* Saved layouts now lives INSIDE the Layout menu, so its own toolbar chip is redundant.
   Hidden rather than deleted: chart.html:3714 and :7829 both setAttribute/textContent on
   #cdpSave with no null guard, so removing the element throws. */
#cdpSave { display: none; }

/* ══ THE MOBILE CUT ═══════════════════════════════════════════════════════════
   Owner: "mobile shouldn't have what features then? what do other apps do?"

   The split is not about screen size, it is about what kind of product you are.
   Brokerages drop charting DEPTH on mobile - Fidelity, Schwab and Robinhood all ship
   watchlist + chart + trade + alerts and no multi-pane, no drawing tools, no saved
   layouts. Charting apps keep it: TradingView and thinkorswim ship the lot. JagSim is a
   simulator for learning to trade, which is a brokerage, not TradingView.

   It matters more here than most places: js/app-nav.js records that most of this site's
   traffic is phones, and the mobile job is ONE path - look at a chart, place a practice
   trade. 56 of 76 signups traded, so that path works. Everything cut below was width
   being spent on something else.

   Before this, the chart hid almost nothing under 760px - only scrollbars and
   .cdp-submeta - so a 375px phone was being served the full drawing rail, quad layout,
   the customise panel and saved layouts. */
@media (max-width: 760px) {
  /* Quad on 375px is four ~90px charts. Worse than one, not more than one. */
  .cdp-lay[data-layout="split"],
  .cdp-lay[data-layout="quad"] { display: none !important; }

  /* Compare and Watch are multi-pane presets, so they go with the panes they need.
     Trade and Chart only remain, which is the whole decision on a phone. */
  .cdp-vm-preset[data-preset="compare"],
  .cdp-vm-preset[data-preset="watch"] { display: none !important; }
  .cdp-viewmenu .cdp-vm-presets { grid-template-columns: 1fr 1fr; }

  /* Nobody configures a desk on a phone. They do it once on a laptop and expect it to
     follow them, which is what the server-side prefs are for. */
  /* THE + STAYS. This used to hide #cdpRailAdd too, on the reasoning that "nobody
     configures a desk on a phone." The effect was that a phone user had NO way to add a
     tool at all — not a harder way, none — and the owner hit it himself: "I thought it
     was gone, and I couldn't add anything. It's a pretty big deal." An affordance used
     less often is one you shrink, not one you delete. Saved layouts and the upsell teaser
     still go; those genuinely are laptop work. */
  #cdpVmSaved { display: none !important; }
  /* Phone is always a single chart after enforceMobileLayout — Sync has nothing to sync. */
  #cdpSync { display: none !important; }

  /* Three drawing tools, the three that work with a fingertip. Fibonacci and brush on a
     6-inch screen are bad on every app that ships them, including the ones that do. */
  .cdp-rail [data-group="draw"]:not([data-tool="cross"]):not([data-tool="trend"]):not([data-tool="hline"]) {
    display: none !important;
  }
}

/* ── THE LAYOUT MENU ─────────────────────────────────────────────────────────
   Three toolbar controls answering one question - how is my screen arranged - collapsed
   into one dropdown. Owner on the competitor's version: "this is exactly what I was
   dreaming of ... maybe that's what we should do instead of movable completely free
   range. Like, we have set configurations they can do."
   Set configurations beat free drag here for a reason beyond taste: a preset can be
   tested, a free-range grid has infinite states and none of them are. */
.cdp-viewmenu { min-width: 232px; }
.cdp-viewmenu .cdp-vm-h {
  font-size: 10px; font-weight: 800; letter-spacing: .09em; text-transform: uppercase;
  color: var(--cdp-muted, #6b7480); margin: 2px 0 6px; padding: 0 2px;
}
.cdp-viewmenu .cdp-vm-h + .cdp-vm-h,
.cdp-viewmenu label + .cdp-vm-h { margin-top: 12px; }
.cdp-viewmenu .cdp-vm-presets {
  display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin-bottom: 4px;
}
.cdp-viewmenu .cdp-vm-preset {
  appearance: none; cursor: pointer; font: inherit; font-size: 12.5px; font-weight: 700;
  padding: 8px 10px; border-radius: 8px; text-align: center;
  background: var(--cdp-chip, #f4f7f6); color: var(--cdp-ink, #1a1f29);
  border: 1px solid var(--cdp-border, #e6ebf2);
}
.cdp-viewmenu .cdp-vm-preset:hover { border-color: var(--cdp-accent, #006E4E); }
.cdp-viewmenu .cdp-vm-preset.on {
  background: var(--cdp-accent-soft, #e7f4ef); border-color: var(--cdp-accent, #006E4E);
  color: var(--cdp-accent, #006E4E);
}
.cdp-viewmenu .cdp-vm-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--cdp-border, #e6ebf2);
}
.cdp-viewmenu .cdp-vm-link {
  appearance: none; background: none; border: 0; cursor: pointer; font: inherit;
  font-size: 12.5px; font-weight: 700; color: var(--cdp-accent, #006E4E); padding: 2px 0;
}
.cdp-viewmenu .cdp-vm-link:hover { text-decoration: underline; }

/* DARK MODE: THE SCRIM WAS BUILT FOR A LIGHT PAGE. (2026-08-21)
   Owner: "there's a big glitch in dark mode to save layouts, erases the chart ... it
   completely erases the chart."

   Nothing was erased. `.cdp-layouts` above paints rgba(15,23,20,.38) - a DARK scrim,
   correct over a white desk, invisible as a scrim over a near-black one. In dark mode it
   lands dark-on-dark, the chart behind it goes to mud, and the card is the only thing left
   with any contrast. It reads as "my chart is gone", which is a far worse feeling than
   "there is a dialog open".

   Note what was NOT wrong: --cdp-panel and --cdp-border both already have dark values
   (:2841, :2844), so the CARD was fine. This single hardcoded rgba was the whole defect -
   the one colour in the component that never went through a token.

   Two changes: a lighter scrim so the desk stays legible behind the dialog, and real
   elevation on the card - a hairline highlight plus a deep shadow - so it reads as
   floating ABOVE the chart instead of replacing it. */
html[data-chart-theme="dark"] .cdp-layouts,
html[data-intel-theme="dark"] .cdp-layouts {
  background: rgba(4, 10, 8, .52);
  backdrop-filter: blur(2px);
}
html[data-chart-theme="dark"] .cdp-layouts .cdp-lay-card,
html[data-intel-theme="dark"] .cdp-layouts .cdp-lay-card {
  box-shadow: 0 0 0 1px rgba(255, 255, 255, .10),
              0 28px 64px rgba(0, 0, 0, .72);
}

/* Same failure, same fix, different component. Owner: "I was having a hard time seeing
   the bottom of the builder desk tools because the box is the exact same color as
   background." The customise panel is body-mounted and floats over the desk with no
   scrim at all, so on a dark page its edge simply disappears. */
html[data-chart-theme="dark"] .cdp-customise,
html[data-intel-theme="dark"] .cdp-customise {
  box-shadow: 0 0 0 1px rgba(255, 255, 255, .10),
              0 28px 64px rgba(0, 0, 0, .72);
}
.cdp-layouts .cdp-lay-card {
  width: min(420px, 100%); max-height: min(560px, 86vh); overflow: auto;
  background: var(--cdp-panel, #fff); border: 1px solid var(--cdp-border, #dfe7e1);
  border-radius: 14px; box-shadow: 0 18px 48px rgba(15, 23, 20, .22);
  padding: 16px; text-align: left;
}
.cdp-layouts .cdp-lay-hd {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; margin-bottom: 12px; font-size: 15px;
}
.cdp-layouts .cdp-lay-x {
  border: 0; background: transparent; font-size: 20px; line-height: 1;
  cursor: pointer; color: var(--cdp-muted, #6d7c76); padding: 2px 6px;
}
.cdp-layouts .cdp-lay-new { display: flex; gap: 8px; margin-bottom: 12px; }
.cdp-layouts .cdp-lay-new input { flex: 1 1 auto; min-width: 0; }

/* Saved-layout rows. They had NO styling at all, because the renderer that draws them
   threw on an undefined esc() before any row ever reached the DOM, so nobody saw them. */
.cdp-layouts .cdp-lay-list { display: flex; flex-direction: column; gap: 8px; }
.cdp-layouts .cdp-lay-empty {
  font-size: 13px; line-height: 1.5; color: var(--cdp-muted, #6d7c76); padding: 4px 0 2px;
}
.cdp-layouts .cdp-lay-row {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 10px 12px; border: 1px solid var(--cdp-border, #dfe7e1);
  border-radius: 10px; background: #f8fbf9;
}
.cdp-layouts .cdp-lay-meta { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.cdp-layouts .cdp-lay-name {
  font-weight: 700; font-size: 13.5px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cdp-layouts .cdp-lay-sub { font-size: 11.5px; color: var(--cdp-muted, #6d7c76); }
.cdp-layouts .cdp-lay-act { display: flex; gap: 6px; flex: 0 0 auto; }
.cdp-layouts .cdp-lay-load,
.cdp-layouts .cdp-lay-del {
  font: inherit; font-size: 12px; font-weight: 700; cursor: pointer;
  padding: 6px 11px; border-radius: 8px; border: 1px solid var(--cdp-border, #dfe7e1);
  background: #fff;
}
.cdp-layouts .cdp-lay-load { color: var(--jag-green); border-color: #cfe4d8; }
.cdp-layouts .cdp-lay-load:hover { background: #eef7f0; }
.cdp-layouts .cdp-lay-del { color: #BB2D2D; border-color: #eccfcf; }
.cdp-layouts .cdp-lay-del:hover { background: #fdecec; }
.cdp-layouts .cdp-lay-save {
  font: inherit; font-size: 12.5px; font-weight: 700; cursor: pointer;
  padding: 8px 13px; border-radius: 9px; border: 1px solid #cfe4d8;
  background: #eef7f0; color: var(--jag-green); flex: 0 0 auto;
}
.cdp-layouts .cdp-lay-new input {
  font: inherit; font-size: 13px; padding: 8px 11px; border-radius: 9px;
  border: 1px solid var(--cdp-border, #dfe7e1);
}

/* TIER AS ONE CONTROL. Three tabs cost ~230px of the busiest toolbar on the site for a
   preference you set once. The trigger names the current tier; the three original
   buttons live inside the menu with every handler they already had. */
.cdp-tierbtn { font-weight: 800; white-space: nowrap; }
.cdp-tiermenu {
  /* fixed, set by JS at open time — .cdp-tierbar has overflow:auto and was clipping
     this menu off, which is why it vanished behind the chart. */
  position: fixed; z-index: 12070;
  display: flex; flex-direction: column; gap: 4px; padding: 6px;
  background: var(--cdp-panel, #fff); border: 1px solid var(--cdp-border, #dfe7e1);
  border-radius: 12px; box-shadow: 0 14px 34px rgba(15, 23, 20, .16);
}
.cdp-tiermenu[hidden] { display: none; }
.cdp-tiermenu > button { width: 100%; justify-content: flex-start; text-align: left; }

/* Chart only is the strongest action in the Panels menu — it hides everything at once —
   and it was styled as quietly as the checkboxes above it. Give it a real edge. */
.cdp-viewmax {
  border: 1.5px solid #5b6675 !important;
  font-weight: 800;
}
.cdp-viewmax:hover { background: #f1f4f6 !important; }

/* Chart-type dropdown. Reuses the .cdp-viewmenu shell so it matches Panels exactly —
   one menu style on this toolbar, not a second one invented for a second control. */
.cdp-ctypemenu { z-index: 12075; min-width: 132px; }
.cdp-ctypemenu > button {
  display: block; width: 100%; text-align: left;
  font: inherit; font-size: 12.5px; font-weight: 700; cursor: pointer;
  padding: 8px 12px; border: 0; border-radius: 8px; background: transparent;
  color: var(--cdp-ink, #17211d);
}
.cdp-ctypemenu > button:hover { background: #f1f5f3; }
.cdp-ctypemenu > button.on { background: #eef7f0; color: var(--jag-green); }

/* ══════════════════════════════════════════════════════════════════════════
   DARK MODE  —  body.cdp[data-chart-theme="dark"]
   --------------------------------------------------------------------------
   The header of this file used to read "LIGHT ONLY, per 'Must not include:
   dark mode'." The owner has since asked for it directly: "what is normally
   dark mode for chart and build tonight and have available now". That note is
   superseded, not ignored — this is the one place it changes.

   Two rules held to throughout:

   1. NOT AN INVERTED STYLESHEET. A chart in dark mode has its own conventions —
      a #131722 ground, candles lifted bright enough to clear it, a grid pushed
      further back than it sits on white. Inverting the light greys produces the
      flat charcoal mush that reads as a filter rather than a theme.
   2. TOKENS FIRST, EXCEPTIONS NAMED. Almost the whole desk is already drawn from
      the --cdp-* tokens, so restating them here does most of the work in one
      block. The handful of rules that hard-code #fff cannot be reached that way,
      so each is listed below by name. That list is the honest surface of this
      change: anything not in it is still on tokens and follows automatically.
   ══════════════════════════════════════════════════════════════════════════ */
html[data-chart-theme="dark"],
body.cdp[data-chart-theme="dark"] {
  /* THE OWNER'S PALETTE, VERBATIM — docs/design/DARK-MODE-SPEC-2026-08-19-owner.md.
     Three surface levels, not a dozen near-blacks: "The chart background, sidebar,
     order ticket, toolbar, and bottom panels all use slightly different near-black
     shades ... here it doesn't establish a clear hierarchy."

     WHY THIS BLOCK IS ON <html> AS WELL. Custom properties inherit DOWNWARD. Set
     only on body.cdp, they never reached <html> — and <html>'s background is what
     paints the viewport canvas beyond the body box, so the page kept a pale
     surround around a dark app. Putting them at the root also lifts them ABOVE the
     site nav, which is rendered outside this stylesheet's scope and stayed white. */
  --cdp-bg:         #090D14;   /* page */
  --cdp-panel:      #101620;   /* panels */
  --cdp-panel-2:    #151D29;   /* elevated panel */
  --cdp-control:    #1B2533;   /* elevated control */
  --cdp-border:     #263142;
  --cdp-line:       #263142;
  --cdp-text:       #F1F4F7;
  --cdp-muted:      #A7B0BE;   /* secondary; #737F90 is reserved for true muted */
  --cdp-faint:      #737F90;
  /* P&L reads as a tinted pill, not shouted text: "the -4.45%, +26.2% are
     visually screaming ... a darker red/green with brighter text". */
  --cdp-green:      #43D17A;
  --cdp-green-soft: #123C2B;
  --cdp-red:        #FF6678;
  --cdp-red-soft:   #401C25;
  /* ACTION green, and only action green. "The green is doing way too much ...
     reserve the strongest green for actions." Passive greens draw from
     --cdp-accent-soft, which is a tint, not a fill. */
  --cdp-accent:     #27C96F;
  --cdp-accent-soft:rgba(39, 201, 111, .14);
  --cdp-accent-line:#1E5A41;
  /* "Dark interfaces usually benefit from fewer borders + subtle elevation."
     The shadow does the separating that borders were doing. */
  --cdp-shadow:     0 10px 34px rgba(0, 0, 0, .55);
  color-scheme: dark;          /* native scrollbars, selects and inputs follow */
}
html[data-chart-theme="dark"] { background: var(--cdp-bg); }

/* The named exceptions: every rule in this file that paints #fff directly. */
body.cdp[data-chart-theme="dark"] .cdp-input,
body.cdp[data-chart-theme="dark"] .cdp-select,
body.cdp[data-chart-theme="dark"] .cdp-icon-btn,
body.cdp[data-chart-theme="dark"] .cdp-tier,
body.cdp[data-chart-theme="dark"] .cdp-cell-search,
body.cdp[data-chart-theme="dark"] .cdp-tfbar,
body.cdp[data-chart-theme="dark"] .cdp-cell-canvas { background: var(--cdp-panel-2); }
body.cdp[data-chart-theme="dark"] .cdp-rtool:hover,
body.cdp[data-chart-theme="dark"] .cdp-tbtn:hover,
body.cdp[data-chart-theme="dark"] .cdp-lay:hover { background: var(--cdp-panel-2); color: var(--cdp-text); }
/* The canvas host itself IS the chart ground — it must be the darkest surface
   on the page, not the panel grey, or the candles sit in a box. */
body.cdp[data-chart-theme="dark"] .cdp-cell,
body.cdp[data-chart-theme="dark"] .cdp-cell-canvas { background: #131722; }

/* Snapshot exports the canvas onto a solid ground; in dark mode that ground has
   to be the dark one or the saved PNG comes back white behind dark candles. */

/* ── Live brush stroke ─────────────────────────────────────────────────────
   Drawn as an SVG polyline over the canvas while the pointer is down, then
   discarded the moment the real overlay is created. pointer-events:none is
   load-bearing: this element sits directly under the cursor for the whole
   stroke, and without it the first thing the brush would do is block itself. */
/* One glyph at a time. The button always offers the OTHER mode, which is how
   every platform labels a theme switch. */
.cdp-ico-sun { display: none; }
[data-chart-theme="dark"] .cdp-ico-moon { display: none; }
[data-chart-theme="dark"] .cdp-ico-sun { display: block; }

.cdp-brushlive {
  position: absolute; inset: 0; width: 100%; height: 100%;
  pointer-events: none; z-index: 6;
}

/* ── BUSY IS MOTION, NOT COLOUR ────────────────────────────────────────────
   First attempt at restoring this used the accent for .pending — which is the
   SAME green the selected chip already uses, so measuring it live showed
   selected and busy both at rgb(39,201,111): still indistinguishable. Colour
   cannot separate them here, because the selected chip has already spent the
   accent.

   Every platform signals "fetching" on an already-selected control with motion
   instead. A pulse cannot collide with a palette, it reads identically in both
   themes, and it stops the moment the bars land — which is exactly the question
   the testers were asking ("stuck or loading?"). */
@keyframes cdpChipBusy {
  0%   { opacity: 1;   }
  50%  { opacity: .45; }
  100% { opacity: 1;   }
}
.cdp-chip.pending {
  animation: cdpChipBusy 900ms ease-in-out infinite;
  opacity: 1;                 /* the animation owns opacity now */
}
@media (prefers-reduced-motion: reduce) {
  /* Motion is the signal, so it needs a non-motion equivalent rather than
     simply being switched off. */
  .cdp-chip.pending { animation: none; opacity: .6; text-decoration: underline; text-underline-offset: 3px; }
}

/* ── Blank-pane retry ──────────────────────────────────────────────────────
   Shown by the watchdog in chart.html when a pane still has no bars ~11s after
   a load. An empty grid reads as "this stock has no history", which is never
   what happened — so the pane says what is true and offers the one control
   that has ever helped. */
.cdp-retry {
  margin-top: 10px; padding: 7px 16px; border-radius: 9px; cursor: pointer;
  border: 1px solid var(--cdp-accent-line); background: var(--cdp-accent-soft);
  color: var(--cdp-accent); font: inherit; font-size: 13px; font-weight: 800;
}
.cdp-retry:hover { background: var(--cdp-accent); color: #fff; border-color: var(--cdp-accent); }

/* ══════════════════════════════════════════════════════════════════════════
   DARK MODE, PASS 2 — the teardown fixes
   docs/design/DARK-MODE-TEARDOWN-2026-08-19-owner.md
   --------------------------------------------------------------------------
   His five "fastest improvement" items, in his order, are what this block does:
     1. hierarchy — chart and primary action dominate
     2. reduce border weight — softer dividers
     3. tone down bright whites — buttons and chips
     4. simplify the colour language — one green, one red
     5. text sizing and contrast — tables and side panels

   This is scoped to html[data-chart-theme="dark"] so light mode is untouched.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── 1. THE HEADER. "The top bar and main workspace feel disconnected ... it
      looks like a separate layer pasted on top instead of part of one system."
      The site nav is rendered by js/app-nav.js and styled by
      paper-trading-shell.css, which knows nothing about this theme — that is
      why it stayed fully white. Reachable now only because the theme attribute
      is on <html>. ─────────────────────────────────────────────────────────── */
html[data-chart-theme="dark"] .pt-appbar,
html[data-chart-theme="dark"] .pt-topbar,
html[data-chart-theme="dark"] .jm-secbar {
  background: var(--cdp-panel) !important;
  border-bottom-color: var(--cdp-border) !important;
  color: var(--cdp-text);
}
html[data-chart-theme="dark"] .pt-nav-link,
html[data-chart-theme="dark"] .pt-appbar a:not(.pt-cta):not(.sim-coach-btn),
html[data-chart-theme="dark"] .jm-secbar button { color: var(--cdp-muted); }
html[data-chart-theme="dark"] .pt-nav-link.on,
html[data-chart-theme="dark"] .jm-secbar button.on { color: var(--cdp-text); }

/* ── 2. NO MORE WHITE CHIPS. "White buttons and light pills pop hard against
      the dark background ... those bright elements should be more restrained."
      Every one of these hard-codes #fff in its base rule, so a token swap could
      never reach them. ───────────────────────────────────────────────────────── */
html[data-chart-theme="dark"] .cdp-mini-btn,
html[data-chart-theme="dark"] .cdp-chip,
html[data-chart-theme="dark"] .cdp-pill,
html[data-chart-theme="dark"] .cdp-preset,
html[data-chart-theme="dark"] .cdp-lay,
html[data-chart-theme="dark"] .cdp-tbtn,
html[data-chart-theme="dark"] .cdp-rtool,
html[data-chart-theme="dark"] .cdp-input,
html[data-chart-theme="dark"] .cdp-select,
html[data-chart-theme="dark"] .cdp-search,
html[data-chart-theme="dark"] .cdp-cell-search,
html[data-chart-theme="dark"] .cdp-tier {
  background: var(--cdp-control);
  border-color: transparent;
  color: var(--cdp-muted);
}
html[data-chart-theme="dark"] .cdp-tbtn:hover,
html[data-chart-theme="dark"] .cdp-rtool:hover,
html[data-chart-theme="dark"] .cdp-mini-btn:hover,
html[data-chart-theme="dark"] .cdp-preset:hover,
html[data-chart-theme="dark"] .cdp-lay:hover { background: var(--cdp-panel-2); color: var(--cdp-text); }
/* Selected state earns the accent. Nothing else does. */
html[data-chart-theme="dark"] .cdp-chip.on,
html[data-chart-theme="dark"] .cdp-lay.on,
html[data-chart-theme="dark"] .cdp-rtool.on,
html[data-chart-theme="dark"] .cdp-tbtn.on,
html[data-chart-theme="dark"] .cdp-tbtn.latched {
  background: var(--cdp-accent-soft); color: var(--cdp-accent);
  border-color: var(--cdp-accent-line);
}
/* The market-status pill states what the session is; it is information, not an
   action, so it reads as muted text on a dark neutral — his words exactly:
   "Use a dark neutral pill with muted text instead." */
html[data-chart-theme="dark"] .cdp-mkt-inline,
html[data-chart-theme="dark"] .cdp-mktbadge {
  background: var(--cdp-control); color: var(--cdp-muted);
  border-color: transparent; box-shadow: none;
}

/* ── 3. BORDERS STOP DOING THE WORK. "Many elements rely on borders instead of
      spacing and typography ... borders should be subtler and used more
      selectively." Panels keep ONE hairline; everything inside loses its. ──── */
html[data-chart-theme="dark"] .cdp-panel { border-color: var(--cdp-border); box-shadow: none; }
html[data-chart-theme="dark"] .cdp-panel-header { border-bottom-color: var(--cdp-border); }
html[data-chart-theme="dark"] .cdp-tbgroup + .cdp-tbgroup,
html[data-chart-theme="dark"] .cdp-rail-sep { border-color: var(--cdp-border); }
html[data-chart-theme="dark"] .cdp-wl tbody tr,
html[data-chart-theme="dark"] .cdp-tbl tbody tr,
html[data-chart-theme="dark"] .cdp-alert-item,
html[data-chart-theme="dark"] .cdp-market-item { border-color: rgba(38, 49, 66, .7); }

/* ── 4. THE CHART IS THE HERO. "The chart should be the hero, but it blends
      into the rest of the interface." So the canvas panel sits FORWARD of every
      other surface, and the panels around it recede. One depth rule, applied
      consistently — his "the overall depth system isn't clear". ────────────── */
html[data-chart-theme="dark"] .cdp-cell,
html[data-chart-theme="dark"] .cdp-cell-canvas { background: var(--cdp-panel); }
html[data-chart-theme="dark"] .cdp-main > .cdp-panel:not(:has(.cdp-chart-wrap)),
html[data-chart-theme="dark"] .cdp-bottom > .cdp-panel { background: var(--cdp-bg); }
html[data-chart-theme="dark"] .cdp-panel:has(.cdp-chart-wrap) {
  background: var(--cdp-panel);
  box-shadow: 0 0 0 1px var(--cdp-border), 0 18px 48px rgba(0, 0, 0, .45);
  border-color: transparent;
}

/* ── 5. TEXT HIERARCHY. "There isn't enough separation between labels, values,
      and supporting text ... some text is too faint." Three ranks, and the
      faintest rank is only ever used for genuine metadata. ─────────────────── */
html[data-chart-theme="dark"] .cdp-wl th,
html[data-chart-theme="dark"] .cdp-tbl th,
html[data-chart-theme="dark"] .cdp-flabel,
html[data-chart-theme="dark"] .cdp-mini-label {
  color: var(--cdp-faint); font-size: 11.5px; letter-spacing: .04em;
}
html[data-chart-theme="dark"] .cdp-wl td,
html[data-chart-theme="dark"] .cdp-tbl td { color: var(--cdp-text); }
html[data-chart-theme="dark"] .cdp-small,
html[data-chart-theme="dark"] .cdp-submeta,
html[data-chart-theme="dark"] .cdp-ticket-note { color: var(--cdp-muted); }
/* "In dark UI, dense lists need more spacing because contrast does less of the
   work." */
html[data-chart-theme="dark"] .cdp-wl td { padding-top: 9px; padding-bottom: 9px; }
html[data-chart-theme="dark"] .cdp-alert-item { padding-top: 11px; padding-bottom: 11px; }

/* ── 6. ONE GREEN, ONE RED, AND THE BUY BUTTON OWNS THE GREEN. "Primary actions
      ... don't stand out cleanly from secondary controls." ─────────────────── */
html[data-chart-theme="dark"] .cdp-cta-main {
  background: var(--cdp-accent); color: #06140D; font-weight: 900;
  border: 0; box-shadow: 0 6px 20px rgba(39, 201, 111, .22);
}
html[data-chart-theme="dark"] .cdp-cta-main:hover { background: #34DC7E; }
/* Buy and Sell are two halves of one control, designed as a pair — his "the
   sell side doesn't feel equally designed". Neither shouts until it is chosen. */
html[data-chart-theme="dark"] .cdp-side { background: var(--cdp-control); color: var(--cdp-muted); border-color: transparent; }
html[data-chart-theme="dark"] .cdp-side.on[data-side="buy"]  { background: var(--cdp-green-soft); color: var(--cdp-green); }
html[data-chart-theme="dark"] .cdp-side.on[data-side="sell"] { background: var(--cdp-red-soft);   color: var(--cdp-red); }

/* ══════════════════════════════════════════════════════════════════════════
   DARK MODE, PASS 3 — the light-mode enforcement layer
   --------------------------------------------------------------------------
   MEASURED, NOT GUESSED. Every selector below came from walking the live DOM
   in dark mode and collecting each element whose COMPUTED background was light
   (relative luminance > 0.6). That list is the honest surface of the problem;
   nothing here is speculative.

   The reason a token swap could never fix these: css/simulator.css carries a
   block headed "Paper Trading (invest mode) — kill dark bleed-through" with 99
   body.invest-mode rules, many of them !important, written specifically to
   FORCE light on this page. /chart?mode=invest puts invest-mode on the body, so
   that layer wins over anything not equally armed.

   The bottom dock is the loudest of them — the owner: "if I squint, the top 70%
   says dark trading terminal, while the bottom says light-mode spreadsheet."
   It is #positionsPanel, which is OLD .panel markup, not .cdp-*, so none of the
   desk's own theming reached it.
   ══════════════════════════════════════════════════════════════════════════ */

/* The page ground itself. */
html[data-chart-theme="dark"],
html[data-chart-theme="dark"]:has(body.invest-mode),
html[data-chart-theme="dark"] body.invest-mode,
body.cdp[data-chart-theme="dark"].invest-mode { background: var(--cdp-bg) !important; }

/* THE BOTTOM DOCK. Three surface levels, exactly as specified: the panel sits
   on the panel level, its rows one step forward, its controls one more. */
html[data-chart-theme="dark"] body.invest-mode .panel,
html[data-chart-theme="dark"] body.invest-mode .positions-panel,
html[data-chart-theme="dark"] body.invest-mode .order-ticket,
html[data-chart-theme="dark"] body.invest-mode .chart-arena-panel {
  background: var(--cdp-panel) !important;
  border-color: var(--cdp-border) !important;
  color: var(--cdp-text);
}
html[data-chart-theme="dark"] body.invest-mode .pos-row {
  background: var(--cdp-panel-2) !important;
  border-color: transparent !important;   /* spacing separates rows, not borders */
  color: var(--cdp-text);
}
html[data-chart-theme="dark"] body.invest-mode .pos-row:hover {
  background: var(--cdp-control) !important; border-color: var(--cdp-border) !important;
}
html[data-chart-theme="dark"] body.invest-mode .pos-symbol { color: var(--cdp-text); }
html[data-chart-theme="dark"] body.invest-mode .pos-qty    { color: var(--cdp-muted); }
html[data-chart-theme="dark"] body.invest-mode .pos-avg    { color: var(--cdp-faint); }
html[data-chart-theme="dark"] body.invest-mode .pos-total  { color: var(--cdp-text); border-top-color: var(--cdp-border); }
/* ONE SEMANTIC GREEN, ONE SEMANTIC RED — his "simplify the colour language".
   These were #15803d / #b91c1c, which are the LIGHT-theme values and read as
   mud on a dark ground. */
html[data-chart-theme="dark"] body.invest-mode .pos-pnl.positive { color: var(--cdp-green) !important; }
html[data-chart-theme="dark"] body.invest-mode .pos-pnl.negative { color: var(--cdp-red) !important; }
html[data-chart-theme="dark"] body.invest-mode .pos-sell {
  background: var(--cdp-control) !important;
  border-color: transparent !important; color: var(--cdp-red) !important;
}
html[data-chart-theme="dark"] body.invest-mode .pos-sell:hover {
  background: var(--cdp-red-soft) !important; border-color: var(--cdp-red) !important;
}

/* The remaining measured hot spots. */
html[data-chart-theme="dark"] .sim-help-btn,
html[data-chart-theme="dark"] .sim-settings-btn,
html[data-chart-theme="dark"] .sim-signout-btn,
html[data-chart-theme="dark"] .cdp-feed,
html[data-chart-theme="dark"] kbd {
  background: var(--cdp-control) !important;
  border-color: transparent !important;
  color: var(--cdp-muted) !important;
}
/* A <select> ignores background from a parent rule in some engines unless the
   control's own appearance is addressed, which is why this one stayed white
   while every sibling input went dark. */
html[data-chart-theme="dark"] select.cdp-select {
  background-color: var(--cdp-control) !important;
  color: var(--cdp-text) !important;
  border-color: transparent !important;
}

/* The mobile menu sheet — offscreen on desktop, but it opens white otherwise. */
html[data-chart-theme="dark"] .jm-sheet { background: var(--cdp-panel) !important; color: var(--cdp-text); }
html[data-chart-theme="dark"] .jm-x { background: var(--cdp-control) !important; color: var(--cdp-muted) !important; }
html[data-chart-theme="dark"] .jm-link { color: var(--cdp-muted); }
html[data-chart-theme="dark"] .jm-link.on { background: var(--cdp-accent-soft) !important; color: var(--cdp-accent) !important; }
html[data-chart-theme="dark"] .jm-planchip { background: var(--cdp-control) !important; color: var(--cdp-muted) !important; }

/* PANELS RECEDE, THE CHART COMES FORWARD — "make the chart and primary action
   more dominant". Correcting pass 2, which pushed the side panels down to the
   PAGE ground; that flattened them into the background instead of ranking them.
   Page is the darkest thing on screen, panels sit above it, the chart above
   those. Three levels, one rule. */
html[data-chart-theme="dark"] .cdp-main > .cdp-panel,
html[data-chart-theme="dark"] .cdp-bottom > .cdp-panel { background: var(--cdp-panel); }

/* ══════════════════════════════════════════════════════════════════════════
   DARK MODE, PASS 4 — the header tab strip
   Measured the same way: these were the only light surfaces left in the live
   DOM after pass 3. All three belong to the site header, which is drawn from
   simulator.css and never saw this theme.
   ══════════════════════════════════════════════════════════════════════════ */
html[data-chart-theme="dark"] .pt-view-tabs,
html[data-chart-theme="dark"] .pt-view-tab,
html[data-chart-theme="dark"] .pt-feature-tab {
  background: var(--cdp-control) !important;
  border-color: transparent !important;
  color: var(--cdp-muted) !important;
}
html[data-chart-theme="dark"] .pt-view-tab:hover { background: var(--cdp-panel-2) !important; color: var(--cdp-text) !important; }
/* THE ACTIVE TAB IS THE ONLY ONE THAT SPEAKS. It was pale-green fill with dark
   green text — a light-theme pairing that turns to mud on a dark ground, which
   is the "lack of green font for tabs" he flagged. Tinted fill, green text. */
html[data-chart-theme="dark"] .pt-view-tab.active,
html[data-chart-theme="dark"] .pt-view-tab.on,
html[data-chart-theme="dark"] .pt-view-tab[aria-current="page"] {
  background: var(--cdp-accent-soft) !important;
  color: var(--cdp-accent) !important;
  border-color: var(--cdp-accent-line) !important;
}
html[data-chart-theme="dark"] .sim-hdr-ico,
html[data-chart-theme="dark"] .sim-user-pill {
  background: var(--cdp-control) !important;
  border-color: transparent !important;
  color: var(--cdp-muted) !important;
}
html[data-chart-theme="dark"] .sim-user-pill,
html[data-chart-theme="dark"] .sim-user-name { color: var(--cdp-text) !important; }
/* GO PRO KEEPS ITS WEIGHT. It is a paid conversion control and the one place a
   second accent is earned — but it is a filled button on a dark ground now, not
   a light chip, so it reads as deliberate rather than as a leftover. */
html[data-chart-theme="dark"] .pt-cta,
html[data-chart-theme="dark"] .sim-gopro-btn,
html[data-chart-theme="dark"] a[data-jm="gopro"],
html[data-chart-theme="dark"] a[data-jm="gopro-mobile"] {
  background: var(--cdp-accent) !important;
  color: #06140D !important;
  border-color: transparent !important;
  font-weight: 800;
}
html[data-chart-theme="dark"] .pt-cta:hover { background: #34DC7E !important; }
/* Coach Jag is the other branded control. Orange is its identity, so it keeps
   it — but toned so it is not a third thing competing with Buy and Go Pro. */
html[data-chart-theme="dark"] .sim-coach-btn { box-shadow: none !important; }

/* ══════════════════════════════════════════════════════════════════════════
   DARK MODE, PASS 5 — the tab-strip frame and the orders table
   ══════════════════════════════════════════════════════════════════════════ */
/* The white outline around the whole tab group. It is the CONTAINER's border
   (.pt-view-nav, 1px solid #d6dce4), not the tabs' — which is why restyling the
   tabs left a bright rectangle drawn around them. */
html[data-chart-theme="dark"] .pt-view-nav {
  border-color: var(--cdp-border) !important;
  background: var(--cdp-control) !important;
  overflow: hidden;                  /* so the active tab's tint meets the frame */
}
html[data-chart-theme="dark"] .pt-view-nav .pt-view-tab + .pt-view-tab { border-left-color: var(--cdp-border) !important; }

/* ── THE ORDERS / POSITIONS TABLES ─────────────────────────────────────────
   "hard to see grey wording on black and very tiny too". Two separate faults:
   the header row kept its LIGHT fill, and the secondary text was sitting at the
   muted rank when it is carrying real values (estimated cost, fill terms).
   Column headers stay small because that is their job; VALUES go up a rank in
   both size and contrast. ─────────────────────────────────────────────────── */
html[data-chart-theme="dark"] body.invest-mode table thead,
html[data-chart-theme="dark"] body.invest-mode table thead tr,
html[data-chart-theme="dark"] body.invest-mode table th,
html[data-chart-theme="dark"] .cdp-tbl thead,
html[data-chart-theme="dark"] .cdp-tbl th {
  background: var(--cdp-panel-2) !important;
  color: var(--cdp-muted) !important;
  border-color: var(--cdp-border) !important;
}
html[data-chart-theme="dark"] body.invest-mode table td,
html[data-chart-theme="dark"] .cdp-tbl td {
  color: var(--cdp-text);
  border-color: rgba(38, 49, 66, .8) !important;
  font-size: 13px;                    /* was 12 — the owner could not read it */
  padding-top: 11px; padding-bottom: 11px;
}
/* Supporting text is a rank below the values, not three ranks below. #737F90 on
   #090D14 is under 4.5:1 at 12px; #A7B0BE at 12.5px clears it. */
html[data-chart-theme="dark"] body.invest-mode .oo-note,
html[data-chart-theme="dark"] body.invest-mode .oo-est,
html[data-chart-theme="dark"] body.invest-mode td small,
html[data-chart-theme="dark"] body.invest-mode td .muted {
  color: var(--cdp-muted) !important; font-size: 12.5px;
}
/* Status pills were light-blue-on-white chips. Same shape, dark neutral fill. */
html[data-chart-theme="dark"] body.invest-mode .badge,
html[data-chart-theme="dark"] body.invest-mode .status-pill,
html[data-chart-theme="dark"] body.invest-mode .oo-status,
html[data-chart-theme="dark"] .cdp-badge {
  background: var(--cdp-control) !important;
  color: var(--cdp-muted) !important;
  border-color: transparent !important;
}
/* The row-dismiss X was a white square — one of the brightest things on screen
   for the least important control on the row. */
html[data-chart-theme="dark"] body.invest-mode .oo-cancel,
html[data-chart-theme="dark"] body.invest-mode button.cancel,
html[data-chart-theme="dark"] body.invest-mode td button {
  background: var(--cdp-control) !important;
  border-color: transparent !important;
  color: var(--cdp-muted) !important;
}
html[data-chart-theme="dark"] body.invest-mode td button:hover {
  background: var(--cdp-red-soft) !important; color: var(--cdp-red) !important;
}
/* Symbol links read as generic web links in blue; they are tickers. */
html[data-chart-theme="dark"] body.invest-mode td a { color: var(--cdp-text); }

/* ══════════════════════════════════════════════════════════════════════════
   DARK MODE, PASS 6 — tooltips, selection state, row hover, orders table
   ══════════════════════════════════════════════════════════════════════════ */

/* ── TOOLTIPS WERE WHITE TEXT ON A WHITE CARD ──────────────────────────────
   Measured: .cdp-tip computed to background rgb(241,244,247) with color
   rgb(255,255,255). The card kept its light fill while a broad text rule moved
   the label to the light-theme ink. Every chart tooltip — "Hourly bars",
   "One-minute bars", the order-ticket coach tips — was invisible.
   A tooltip is the one surface that must never inherit; both halves are set. */
html[data-chart-theme="dark"] .cdp-tip,
html[data-chart-theme="dark"] .jag-tip,
html[data-chart-theme="dark"] [role="tooltip"] {
  background: #1B2533 !important;
  color: #F1F4F7 !important;
  border: 1px solid var(--cdp-border) !important;
  box-shadow: 0 10px 28px rgba(0, 0, 0, .55) !important;
}
html[data-chart-theme="dark"] .cdp-tip::after,
html[data-chart-theme="dark"] .cdp-tip::before { border-top-color: #1B2533 !important; background: #1B2533 !important; }

/* ── THE SELECTED TIMEFRAME WAS INDISTINGUISHABLE ──────────────────────────
   The active chip carries `.active`, not `.on` — so the earlier selected-state
   rule matched nothing and all six chips rendered identically. Measured: 1m and
   5D were both rgb(27,37,51) on rgb(167,176,190).
   Selection now reads three ways at once — fill, ink and weight — because on a
   dark ground a single one of those is not enough. */
html[data-chart-theme="dark"] .cdp-chip.active:not(.pending),
html[data-chart-theme="dark"] .cdp-chip.on,
html[data-chart-theme="dark"] .cdp-chip[aria-pressed="true"],
html[data-chart-theme="dark"] .cdp-seg-btn.active,
html[data-chart-theme="dark"] .cdp-tab.active {
  background: var(--cdp-accent-soft) !important;
  color: var(--cdp-accent) !important;
  border: 1px solid var(--cdp-accent) !important;
  font-weight: 800 !important;
}
/* CORRECTION. The comment that stood here — "Light mode had the same defect,
   the rule there also keyed on .on" — was FALSE. Line 267 already carried an
   identical .cdp-chip.active rule with the same four declarations, so this one
   changed nothing about the selected chip. Its only net effect was CASCADE
   ORDER: .cdp-chip.pending (line 1719) sits between them at the same (0,2,0)
   specificity, so moving .active after it made .active win the tie and stripped
   the solid brand-green border that says "fetching".

   A chip carries BOTH classes on every timeframe click (chart.html:4273 adds
   .active, 4289 adds .pending), and that busy border exists because beta
   testers reported the chip as "stuck or loading? no way to tell". I removed
   the answer to that question. :not(.pending) hands it back — the busy state
   outranks the selected state, which is the correct order for the user. */
.cdp-chip.active:not(.pending) { background: var(--cdp-accent-soft); color: var(--cdp-accent); border-color: var(--cdp-accent-line); font-weight: 800; }

/* ── ROW HOVER PAINTED WHITE ───────────────────────────────────────────────
   Hovering an open order turned the whole row white — the single brightest
   thing on the page, triggered by moving the mouse. */
html[data-chart-theme="dark"] body.invest-mode tbody tr:hover,
html[data-chart-theme="dark"] body.invest-mode .oo-row:hover,
html[data-chart-theme="dark"] .cdp-wl tbody tr:hover,
html[data-chart-theme="dark"] .cdp-tbl tbody tr:hover {
  background: var(--cdp-control) !important;
  color: var(--cdp-text) !important;
}
html[data-chart-theme="dark"] .cdp-wl tbody tr.on,
html[data-chart-theme="dark"] .cdp-wl tbody tr[aria-selected="true"] {
  background: var(--cdp-accent-soft) !important;
}

/* ── THE ORDERS TABLE ──────────────────────────────────────────────────────
   Caught by attribute and role as well as class, because this dock is old
   markup and its header cells carry no class of their own. */
html[data-chart-theme="dark"] body.invest-mode thead,
html[data-chart-theme="dark"] body.invest-mode thead tr,
html[data-chart-theme="dark"] body.invest-mode th,
html[data-chart-theme="dark"] .cdp-panel thead,
html[data-chart-theme="dark"] .cdp-panel th {
  background: var(--cdp-panel-2) !important;
  color: var(--cdp-muted) !important;
  border-bottom: 1px solid var(--cdp-border) !important;
}
/* Any small light chip left inside a row — QUEUED, FILLED, STOCK, CRYPTO, CASH. */
html[data-chart-theme="dark"] body.invest-mode td span[class*="badge"],
html[data-chart-theme="dark"] body.invest-mode td span[class*="pill"],
html[data-chart-theme="dark"] body.invest-mode td span[class*="tag"],
html[data-chart-theme="dark"] body.invest-mode td span[class*="status"],
html[data-chart-theme="dark"] .cdp-panel td span[class*="badge"],
html[data-chart-theme="dark"] .cdp-panel td span[class*="pill"] {
  background: var(--cdp-control) !important;
  color: var(--cdp-muted) !important;
  border-color: transparent !important;
}

/* ── ORDER TICKET: ONE PRIMARY ACTION ──────────────────────────────────────
   "Too many tiny controls are packed into a small area ... the eye doesn't know
   where to land first." The quantity presets are the least important row in the
   panel and were competing with the Buy button. They recede; Buy stays loud. */
html[data-chart-theme="dark"] .cdp-preset {
  background: transparent !important;
  border: 1px solid var(--cdp-border) !important;
  color: var(--cdp-faint) !important;
  font-weight: 700;
}
html[data-chart-theme="dark"] .cdp-preset:hover { background: var(--cdp-control) !important; color: var(--cdp-text) !important; }
html[data-chart-theme="dark"] .cdp-preset-max { color: var(--cdp-muted) !important; }
/* Buy / Sell are one control in two states, and neither shouts until chosen. */
html[data-chart-theme="dark"] .cdp-seg-btn { background: var(--cdp-control) !important; color: var(--cdp-muted) !important; border-color: transparent !important; }
html[data-chart-theme="dark"] .cdp-seg-btn.on,
html[data-chart-theme="dark"] .cdp-seg-btn.active { background: var(--cdp-green-soft) !important; color: var(--cdp-green) !important; border: 1px solid var(--cdp-green) !important; }
html[data-chart-theme="dark"] .cdp-seg-btn.on[data-side="sell"],
html[data-chart-theme="dark"] .cdp-seg-btn.active[data-side="sell"] { background: var(--cdp-red-soft) !important; color: var(--cdp-red) !important; border-color: var(--cdp-red) !important; }

/* ── SPACING RHYTHM ────────────────────────────────────────────────────────
   "Some panels have comfortable padding, while others feel tight." One step
   value, applied to the panels that were tightest. */
html[data-chart-theme="dark"] .cdp-panel-body { padding: 16px; }
html[data-chart-theme="dark"] .cdp-field { margin-bottom: 14px; }
html[data-chart-theme="dark"] .cdp-kv { padding: 6px 0; }

/* ══════════════════════════════════════════════════════════════════════════
   DARK MODE, PASS 7 — the undefined tokens, and the last measured leaks
   From the surface audit across all four stylesheets on this page.
   ══════════════════════════════════════════════════════════════════════════ */
html[data-chart-theme="dark"],
body.cdp[data-chart-theme="dark"] {
  /* Defining these two is the single highest-leverage line in the whole theme:
     it darkens six popovers and menus at once, because they were all reading a
     token that did not exist. */
  --cdp-surface: #151D29;
  --cdp-ink:     #F1F4F7;
}

/* THE WHITE "MARKET CLOSED" PILL, named directly by the owner. None of it was
   tokenised — #f1f4f8 fill, #d8e0ea border, #334155 text — so no token swap
   could reach it. It states a fact; it is not an alarm. */
html[data-chart-theme="dark"] .cdp-pill,
html[data-chart-theme="dark"] .cdp-pill.red,
html[data-chart-theme="dark"] .cdp-feed {
  background: var(--cdp-control) !important;
  border-color: transparent !important;
  color: var(--cdp-muted) !important;
}
/* .cdp-feed.live kept a hard-coded #166534 on a now-dark green fill — dark
   green on dark green. */
html[data-chart-theme="dark"] .cdp-feed.live { color: var(--cdp-green) !important; background: var(--cdp-green-soft) !important; }

/* THE SAME INVERSION TRAP AS THE TOOLTIP. .cdp-previewbar sets
   `background: var(--cdp-text)` with white text — fine while --cdp-text is
   near-black, and a white bar with white text on it the moment it is not. */
html[data-chart-theme="dark"] .cdp-previewbar {
  background: #1B2533 !important; color: #F1F4F7 !important;
}

/* Menus: the shells go dark via --cdp-surface above, but their LABELS were on
   --cdp-ink's near-black fallback, so a fixed shell alone would have made them
   read as empty. */
html[data-chart-theme="dark"] .cdp-ctypemenu > button,
html[data-chart-theme="dark"] .cdp-viewmenu label,
html[data-chart-theme="dark"] .cdp-symmenu--float > *,
html[data-chart-theme="dark"] .cdp-wlsort li,
html[data-chart-theme="dark"] .cdp-wlsort button { color: var(--cdp-text) !important; }
html[data-chart-theme="dark"] .cdp-ctypemenu > button:hover,
html[data-chart-theme="dark"] .cdp-wlsort button:hover { background: var(--cdp-control) !important; }
html[data-chart-theme="dark"] .cdp-ctypemenu > button.on,
html[data-chart-theme="dark"] .cdp-wlsort button.on { background: var(--cdp-accent-soft) !important; color: var(--cdp-accent) !important; }

/* The saved-layouts modal was almost entirely light — rows, Load, Delete. */
html[data-chart-theme="dark"] .cdp-layouts,
html[data-chart-theme="dark"] .cdp-lay-row,
html[data-chart-theme="dark"] .cdp-lay-load,
html[data-chart-theme="dark"] .cdp-lay-del,
html[data-chart-theme="dark"] .cdp-lay-save {
  background: var(--cdp-panel-2) !important; color: var(--cdp-text) !important;
  border-color: var(--cdp-border) !important;
}
html[data-chart-theme="dark"] .cdp-lay-load { color: var(--cdp-accent) !important; }
html[data-chart-theme="dark"] .cdp-lay-del  { color: var(--cdp-red) !important; }

/* The price field carries .cdp-dim on FIRST PAINT, because Market is the
   default order type — so a near-white slab was the first thing the ticket
   showed. It reads --jag-card-2, which tokens.css defines as #F7F9FB, so the
   fallback never even engaged. */
html[data-chart-theme="dark"] .cdp-dim .cdp-input,
html[data-chart-theme="dark"] .ca-in {
  background: var(--cdp-control) !important; color: var(--cdp-muted) !important;
  border-color: transparent !important;
}

/* THE LINE THAT SAYS WHY AN ORDER WAS REFUSED. #C4372F on a dark surface is
   about 2:1 — the worst possible place for unreadable text. Both of these
   carry !important in the base rule, so the override must too. */
html[data-chart-theme="dark"] .cdp-sub-error { color: var(--cdp-red) !important; }
html[data-chart-theme="dark"] .cdp-input.cdp-invalid,
html[data-chart-theme="dark"] input.cdp-invalid {
  background: var(--cdp-red-soft) !important; border-color: var(--cdp-red) !important; color: var(--cdp-text) !important;
}

/* Every watchlist row carries .wl-loading until its first fetch returns, so on
   first paint the list was a column of bright white shimmer pills. */
html[data-chart-theme="dark"] #cdpWatchlist tr.wl-loading td:not(:first-child)::after {
  background: linear-gradient(90deg, #151D29, #1B2533, #151D29) !important;
}

/* One ground for the chart, matching the canvas palette in chart.html. The
   earlier pass used #131722 — a fourth near-black in a three-surface system. */
html[data-chart-theme="dark"] .cdp-cell,
html[data-chart-theme="dark"] .cdp-cell-canvas { background: #101620 !important; }

/* Key caps — one lives in the Alerts empty state, which is what that panel
   shows most of the time. */
html[data-chart-theme="dark"] kbd { background: var(--cdp-control) !important; border-color: var(--cdp-border) !important; color: var(--cdp-text) !important; }

/* ── The chart's own status line ───────────────────────────────────────────
   #chartLoadMsg and #chartEmptyState are written ONLY by js/app/main.js, which
   has been reporting every bar failure to them for months while neither existed
   in the markup. They sit over the canvas, centred, and are hidden until the
   data layer has something true to say. */
.cdp-loadmsg {
  position: absolute; left: 50%; top: 16px; transform: translateX(-50%);
  z-index: 8; max-width: 82%; text-align: center;
  padding: 8px 14px; border-radius: 10px;
  background: var(--cdp-panel-2); color: var(--cdp-text);
  border: 1px solid var(--cdp-border);
  box-shadow: 0 6px 20px rgba(15, 23, 42, .14);
  font-size: 13px; font-weight: 700; line-height: 1.4;
  pointer-events: none;                 /* never blocks the chart underneath */
}
.cdp-loadmsg[hidden], .cdp-emptystate[hidden] { display: none !important; }
.cdp-emptystate {
  position: absolute; inset: 0; z-index: 7;
  display: flex; align-items: center; justify-content: center;
  background: var(--cdp-panel); color: var(--cdp-muted);
  font-size: 14px; font-weight: 600;
}
.cdp-emptystate p { margin: 0; }
html[data-chart-theme="dark"] .cdp-loadmsg {
  background: #1B2533; color: #F1F4F7; border-color: var(--cdp-border);
  box-shadow: 0 8px 24px rgba(0, 0, 0, .5);
}

/* ── TIMEFRAMES MOVED ONTO THE TIER ROW ────────────────────────────────────
   Owner: "add the time frames to the same line as the Beginner/Advanced
   labels, to the right of it." They used to sit in the symbol header, a row
   above, which cost a whole row of vertical space on the busiest toolbar on
   the site and separated the interval from every other chart control.

   A hairline before them, because they are a different KIND of thing from the
   tier dropdown — one says how much toolbar you get, the other says what
   window the bars cover. Grouping without a divider would imply they are the
   same control. */
.cdp-tierbar #cdpTf {
  display: flex; align-items: center; gap: 4px;
  /* THE 1m CHIP WAS AMPUTATED, AND NO AMOUNT OF SCROLLING COULD REACH IT.

     .cdp-chart-controls (line 260) sets justify-content:flex-end, and this rule never
     overrode it. When six chips are wider than the strip, flex-end pushes the overflow
     out through the START edge - and content that overflows to the LEFT does not count
     toward scrollWidth, so scrollLeft has nowhere to go. The browser reports a strip
     that fits perfectly while the first chip sits outside it.

     Measured live at 1m before the fix: chip left edge 252px, strip left edge 277px,
     scrollWidth 237 === clientWidth 237, elementFromPoint returned the strip, not the
     chip. After flex-start: chip at 288px, scrollWidth 273 > clientWidth 237, and it
     hit-tests to itself.

     The nowrap + overflow-x:auto below was added to stop the chips stacking vertically
     on tablets. It fixed the stacking and left this: permission to scroll, pointed the
     one way that could never reach the missing chip. */
  justify-content: flex-start;
  margin: 0; padding-left: 10px;
  border-left: 1px solid var(--cdp-border);
  flex: 0 1 auto; min-width: 0;
  overflow-x: auto; scrollbar-width: none;
  /* NOWRAP, and it is load-bearing between 761px and 1100px.
     @media (max-width:1100px) at line 1077 gives .cdp-chart-controls permission to wrap,
     and #cdpTf IS a .cdp-chart-controls. But the PARENT cannot wrap in that band:
     .cdp-tierbar{flex-wrap:nowrap} at line 2120 beats the wrap at line 1079 - identical
     specificity, later in source, and a media query adds none. So the parent stays on one
     line, squeezes this strip to a ~52px sliver, and the six chips wrap INSIDE the sliver
     into a vertical column that punches through the tier bar and paints over the symbol
     sub-line. Measured at 820px: 52px wide by 188px tall, with the 15m chip sliced
     mid-glyph inside a 41px box. The band covers iPad portrait and iPhone Pro landscape.
     Below 760px line 2601 lets .cdp-tierbar wrap, so the chips get their own row and it
     is genuinely fine - which is why this only ever showed up on tablets.
     This element is already built to scroll: overflow-x:auto above, and .cdp-chip is
     flex:0 0 auto below. It was only ever missing permission not to wrap. */
  flex-wrap: nowrap;
}
@media (max-width: 760px) {
  .cdp-tierbar #cdpTf {
    flex-wrap: wrap;
    overflow-x: visible;
    row-gap: 4px;
  }
}
.cdp-tierbar #cdpTf::-webkit-scrollbar { display: none; }
.cdp-tierbar #cdpTf .cdp-chip { flex: 0 0 auto; }
html[data-chart-theme="dark"] .cdp-tierbar #cdpTf { border-left-color: var(--cdp-border); }

@media (max-width: 900px) {
  /* On a narrow toolbar the intervals scroll sideways rather than pushing the
     drawing tools off the row — the interval is the control people reach for
     most, so it keeps its place and gives up width instead. */
  .cdp-tierbar #cdpTf { padding-left: 8px; }
}

/* ── MOBILE: THE X-AXIS DATE LABELS WERE OVERLAPPING ───────────────────────
   Owner: "on mobile layout, the time at the bottom is overlapping each other."
   klinecharts sizes its axis text from the style object, not from the viewport,
   so the same ~11px labels that sit comfortably across 1700px collide at 390px
   — full dates like 2026-08-04 need roughly 78px each and there is not room for
   eight of them.

   Fixed by making the axis text smaller AND telling the engine to space ticks
   further apart on narrow screens, which is done in JS where the chart styles
   live; this rule only handles the surrounding chrome so the footer does not
   crowd the axis it sits under. */
/* REVERTED — THIS WAS STRETCHING THE MOBILE CHART.
   I added "padding-bottom: 2px" to .cdp-cell-canvas to stop the footer
   crowding the axis. That element is the host the ResizeObserver watches, and
   .cdp-cell is a grid with "grid-template-rows: 0 minmax(0, 1fr)". Padding on
   that host feeds back into the measured height: the chart resizes to fill,
   the host grows, the observer fires, it grows again — the page kept
   stretching and mobile trading was unusable.

   The lesson: never add padding to an element that a ResizeObserver measures
   and that a chart engine also sizes itself from. Spacing for the footer has
   to come from the FOOTER, not from the canvas host. */

/* ── iOS ZOOMS THE WHOLE PAGE WHEN YOU TAP A CONTROL UNDER 16px ────────────
   Owner: "when i click 3 horizontal dots page zooms."

   Safari on iPhone auto-zooms to make a focused control legible whenever its
   font-size is below 16px, and it does NOT zoom back out. The ⋯ menu is the
   trigger the owner hit — it takes focus and contains "Save this layout as…",
   whose prompt input is 13px — but every small control on this desk is the
   same landmine, so this is fixed as a class rather than one button.

   16px is not a design choice here; it is the exact threshold Safari uses.
   Applied only at phone widths, so the desk keeps its compact type on desktop
   where no such behaviour exists. */
@media (max-width: 760px) {
  body.cdp input,
  body.cdp select,
  body.cdp textarea,
  body.cdp .cdp-input,
  body.cdp .cdp-select,
  body.cdp .cdp-search,
  body.cdp .cdp-cell-search,
  body.cdp .cdp-noteinput,
  body.cdp .jdp-input { font-size: 16px !important; }

  /* And the menu itself must fit the viewport. A fixed-position card wider
     than the screen is the other way Safari ends up rescaling the page. */
  body.cdp .cdp-moremenu,
  body.cdp .cdp-viewmenu,
  body.cdp .cdp-ctypemenu,
  body.cdp .cdp-tiermenu,
  body.cdp .cdp-layouts,
  body.cdp .cdp-wlsort {
    max-width: calc(100vw - 24px);
    max-height: calc(100vh - 120px);
    overflow-y: auto;
    box-sizing: border-box;
  }
}


/* ══ COACH'S TEACHING LEVEL NO LONGER STRIPS THE CHART ═══════════════════════
   Removed 2026-08-21. Owner, on finding he could not open the layout picker:
   "Why don't I see the layout button? I'm on the chart now. Where's the layouts, man?"

   They were there. He was locked out of them.

   js/coach-jag.js:377 seeds `deskMode: 'beginner'` BEFORE it reads localStorage, and
   deskMode() at :592 returns that seed and never reaches the storage read at :594. So
   html.cj-desk-beginner sits on the root of EVERY account that never explicitly picked a
   mode - which is nearly all of them. The rules that used to live here then
   display:none !important'd:

       #cdpLayout            the entire chart-layout picker
       #cdpViewDD            the Panels dropdown
       #cdpSync, #cdpSave    sync across panes, save a layout
       #cdpTypeSeg           chart type
       #cdpTif               time in force
       .cdp-lay split + quad the multi-chart options

   The chart's OWN mode selector (#cdpTier) was hidden on 2026-08-21 when the three desk
   modes were deleted - so there was no longer any way to leave beginner. The controls
   existed, were mounted, were wired, and were unreachable.

   This also already cost the walkthroughs: the comment further down in this file records
   that 13 tour steps across 4 tours - including "Place your first paper trade" - point at
   controls these rules hid, which is why the owner reported "the demo does not start".

   His decision was "delete the 3 modes, KEEP for Coach Jag". Coach keeps its mode for
   what it is actually for - COPY DEPTH, see the "COPY DEPTH BY MODE" block in
   js/coach-jag.js:597 - and stops hiding the chart. One base desk, which is the whole
   point of having deleted the modes. Tool visibility is the customise panel's job now
   (js/app/chart-customise.js), where the user chooses, rather than a teaching level
   choosing for them silently. */

/* ══ A RUNNING WALKTHROUGH OUTRANKS MODE HIDING ══════════════════════════
   THIS IS WHY THE CHART DEMOS DID NOT START. (2026-08-21)

   js/coach-jag.js seeds deskMode:'beginner' before it reads localStorage, so
   html.cj-desk-beginner is on the root for every account that never picked a
   mode. The rules above then display:none !important exactly the controls that
   13 tour steps point at, across 4 walkthroughs -- including the flagship
   "Place your first paper trade".

   startTour() adds html.cj-tour-on precisely to defeat mode hiding while a walk
   is running. Portfolio implements that (css/portfolio-desk.css, 5 rules). The
   chart never did: grep -c cj-tour-on css/chart-desk-page.css returned 0. So the
   tour started, could not anchor step 0, waited 12 seconds, and opened a card
   pointing at nothing. The owner read that as "the demo does not start".

   Display values match each element's real layout (.cdp-seg is grid,
   .cdp-flabel is flex, the price field is block). Reverts the moment the tour
   ends, because the class is removed then. */
html.cj-tour-on #cdpTypeSeg { display: grid !important; }
html.cj-tour-on #cdpTypeLabel,
html.cj-tour-on label[for="cdpTif"] { display: flex !important; }
html.cj-tour-on #cdpPriceField { display: block !important; }
html.cj-tour-on #cdpTif { display: inline-block !important; }

/* GO PRO IS NOT A TAB ANY MORE (owner approved 2026-08-21).
   423 upsell impressions produced 1 click. An always-on orange button sitting
   next to Chart all day is advertising, and people go blind to it. The PRO tag
   on Intel stays -- that is a label, not an ad. Do NOT hide .pt-nav-pro here:
   that rule deleted the Intel PRO chip on Chart (owner 2026-08-23). Pro is
   still reachable from the account menu and from every in-context lock. */
.pt-gopro, .jm-gopro { display: none !important; }

/* WATCHLIST REMOVE GOES ON THE RIGHT. (2026-08-21)
   Owner: "when have you ever seen a watchlist with x's on the LEFT side, not
   the right side of the item?" He is right - every broker puts remove at the
   trailing edge. The markup has both controls in one cell with the button
   first: <td><button class="cdp-wl-x">x</button><span class="cdp-wl-sym">NVDA
   </span></td>. Reordering with flex fixes the static seed rows AND anything
   the JS rebuilds later, without touching the table structure or the handlers. */
#cdpWatchlist td:first-child {
  display: flex;
  align-items: center;
  gap: 8px;
}
/* order:1 removed. It existed to seat the symbol AFTER the remove button when both
   shared this cell. The button moved to the trailing cell, but this rule stayed - so the
   sparkline (default order 0) rendered to the LEFT of the ticker and the symbols stopped
   lining up under the "Symbol" heading. Half a fix left behind is the same failure as no
   fix: DOM order is now the order. (owner 2026-08-21) */
/* The order/margin-left hack that used to sit here pushed the x to the right edge of
   the SYMBOL cell - i.e. between the symbol and the price, which is the placement the
   owner objected to over and over. A flex child cannot leave its own table cell, so the
   button was moved into the trailing cell in the markup and in the builder instead.
   Rules for its new home are below. (owner 2026-08-21) */


/* #20 THE UPGRADE CTA ON LOCKED PANES WAS 2.17:1. (2026-08-21)
   color:#fff !important sat on --cdp-accent, which flips to #27C96F in dark --
   white on bright green. This is the /plans button shown on every locked pane,
   so the one control meant to take money was the hardest thing to read on it. */
html[data-chart-theme="dark"] .cdp-lock-cta,
html[data-intel-theme="dark"] .cdp-lock-cta {
  background: #0E7A4E !important;
  color: #FFFFFF !important;
  border-color: #0E7A4E !important;
}


/* ── COACH PILL CHROME LIVES HERE, NOT ONLY IN JS ───────────────────────────────
   The owner keeps catching this button as a bare orange rectangle: "coach Jag keeps
   appearing as this rectangle."

   Its base appearance was defined ONLY inside the ~536-line <style> blob that
   js/coach-jag.js builds and appends to <head>. Between first paint and that blob
   landing, .sim-coach-btn renders as an unstyled <button> - square corners, default
   metrics, wrong font. On a slow load that frame is long enough to see and screenshot.

   Chrome that ships above the fold must not wait on a template literal. Duplicated
   here so the pill is right from the first frame; coach-jag.js still appends later and
   would win if it ever needs to differ. Keep the two in sync, or delete the JS copy.
   (owner 2026-08-21) */
.sim-coach-btn {
  height: 40px; box-sizing: border-box; padding: 0 16px; border-radius: 999px;
  /* Label colour is INHERITED from this button, so fixing .cj-fab-label alone never
     worked - no rule matched the label at all. #fff on this orange is 3.20:1. */
  border: 1.5px solid #b8540d; background: #b8540d; color: #ffffff;
  font: 800 13px/1 Inter, ui-sans-serif, system-ui, sans-serif; letter-spacing: .01em;
  cursor: pointer; display: inline-flex; align-items: center; justify-content: center;
  gap: 8px; flex-shrink: 0; box-shadow: 0 1px 3px rgba(232, 107, 31, .35);
}
/* #fff on the Pro orange measured 3.20:1 - under the floor, on the most-clicked
   control on the site, in BOTH themes. Four stylesheets set this label white and
   coach-jag.js re-asserts it with !important, so it is corrected at every source
   rather than out-shouted from one. #ffffff reads 5.52:1. */
.sim-coach-btn .cj-fab-label { color: #ffffff; white-space: nowrap; }
.sim-coach-btn .cj-fab-chev {
  width: 0; height: 0;
  border-left: 4px solid transparent; border-right: 4px solid transparent;
  border-top: 5px solid #fff; margin-left: 1px; flex: 0 0 auto;
}


/* The retired mode bar. Phase 1 (owner 2026-08-21): one base desk, no Beginner /
   Intermediate / Advanced on the chart. Belt and braces with the hidden attribute in
   the markup, because an author display rule beats [hidden] - which is exactly how the
   "No data" pill stayed on screen after being told to hide. */
/* ONLY the mode chips. The first cut of this also matched .cdp-tierbar -- which is the
   WRAPPER holding the timeframes, the whole toolbar cluster AND the Chart layout /
   Panels / Layouts / Customise group (chart.html lines 312-398). Hiding it took the
   entire control row off the desk. A fix applied one level too high. */
.cdp-tier[hidden] { display: none !important; }

/* ── BUILD YOUR DESK panel (Phase 2, owner 2026-08-21) ────────────────────────
   Chrome lives in the stylesheet, NOT in a JS-injected <style>. The Coach pill spent
   weeks rendering as a bare orange rectangle for exactly that reason: its only styling
   sat inside a 536-line template literal that landed after first paint. */
.cdp-customise {
  /* POSITION IS OWNED BY THE JS. The panel is appended to <body> and placed fixed
     against the trigger's rect, because .cdp-tierbar is overflow-x:auto and an
     absolutely-positioned child gets clipped by a scrolling ancestor - which is why the
     Add button looked dead. Declaring position here as well would leave two owners of
     one value, which is how they drift. Only the size cap stays. */
  max-height: min(86vh, 760px);
  display: flex; flex-direction: column;
  background: var(--cdp-panel); border: 1px solid var(--cdp-border);
  border-radius: 14px; box-shadow: 0 18px 48px -18px rgba(0,0,0,.45);
  overflow: hidden; text-align: left;
}
.cdp-customise[hidden] { display: none !important; }
.cdp-customise .cx-head {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 14px 16px 10px; border-bottom: 1px solid var(--cdp-border);
}
.cdp-customise .cx-head h3 { margin: 0; font-size: 15px; font-weight: 900; color: var(--cdp-text); }
.cdp-customise .cx-head p { margin: 3px 0 0; font-size: 12px; color: var(--cdp-muted); }
.cdp-customise .cx-close {
  margin-left: auto; border: 0; background: transparent; cursor: pointer;
  font-size: 22px; line-height: 1; color: var(--cdp-muted); padding: 0 2px;
}
.cdp-customise .cx-close:hover { color: var(--cdp-text); }
.cdp-customise .cx-body { overflow-y: auto; padding: 6px 10px 10px; }
/* A locked chip is DIMMED, never removed. Absent teaches nothing; named-and-greyed
   with one line of what it does is the only advertisement that reaches someone already
   looking for a tool. */
.cdp-customise .cx-locked { cursor: default; }
.cdp-customise .cx-foot {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 10px 16px; border-top: 1px solid var(--cdp-border); background: var(--cdp-panel-2);
}
.cdp-customise .cx-reset {
  font: inherit; font-size: 12px; font-weight: 700; cursor: pointer;
  background: transparent; color: var(--cdp-text);
  border: 1px solid var(--cdp-border); border-radius: 8px; padding: 6px 11px;
}
.cdp-customise .cx-reset:hover { border-color: var(--cdp-accent); color: var(--cdp-accent); }
.cdp-customise .cx-upsell {
  display: inline-flex; margin-left: auto; font: inherit; font-size: 12px; font-weight: 800;
  text-decoration: none; cursor: pointer; border: 0;
  color: #fff; background: var(--cdp-accent); border-radius: 8px; padding: 7px 12px;
}
.cdp-customise .cx-upsell:hover { filter: brightness(1.06); }
/* The trigger anchors the panel. Without this the absolutely positioned panel would
   escape to the nearest positioned ancestor and land somewhere else entirely. */
#cdpCustomiseBtn { position: relative; }
.cdp-toolbar { position: relative; }

/* ── Saved-layout presets + capacity notice (Phase 3, owner 2026-08-21) ─────── */
.cdp-lay-presets { border-top: 1px solid var(--cdp-border); margin-top: 10px; padding-top: 10px; }
.cdp-lay-presets-h {
  font-size: 10.5px; font-weight: 800; letter-spacing: .09em; text-transform: uppercase;
  color: var(--cdp-muted); margin-bottom: 7px;
}
.cdp-lay-presets-row { display: flex; flex-wrap: wrap; gap: 7px; }
.cdp-lay-preset {
  font: inherit; font-size: 12px; font-weight: 700; cursor: pointer;
  padding: 7px 12px; border-radius: 999px;
  border: 1px solid var(--cdp-border); background: var(--cdp-panel-2); color: var(--cdp-text);
}
.cdp-lay-preset:hover { border-color: var(--cdp-accent); color: var(--cdp-accent); background: var(--cdp-accent-soft); }
/* The at-capacity line. Saving used to drop the oldest layout silently; this is the
   sentence that says so. */
.cdp-lay-full {
  margin: 8px 0 0; font-size: 12px; line-height: 1.45;
  color: var(--cdp-text); background: var(--cdp-accent-soft);
  border: 1px solid var(--cdp-accent-line); border-radius: 9px; padding: 8px 10px;
}
.cdp-lay-full[hidden] { display: none !important; }
.cdp-lay-full a { color: var(--cdp-accent); font-weight: 800; }

/* The rail's "add tools" button. Sits where the next tool would go, so it reads as
   "there is more" rather than as another tool. Never hidden by the customise panel —
   it is the way back. */
/* PINNED TO THE BOTTOM, ALWAYS REACHABLE. The rail is overflow-y:auto, so on a short
   window — or once enough tools sit on it — the + scrolled below the fold and looked
   deleted. It is the ONLY route to adding a tool, so it is the one button here that must
   never need scrolling to find. sticky keeps it in flow, after the last tool, while
   pinning it to the visible bottom edge as soon as the rail overflows. */
.cdp-rail-add {
  color: var(--cdp-muted);
  position: sticky; bottom: 0; z-index: 2;
  background: var(--cdp-panel-2);
}
.cdp-rail-add:hover { color: var(--cdp-accent); border-color: var(--cdp-accent); background: var(--cdp-accent-soft); }

/* Watchlist toggle beside the price (owner 2026-08-21). Compact: it sits next to a
   large number, so it must not compete with it. */
.cdp-chip-wl { padding: 5px 9px; font-size: 11.5px; font-weight: 700; }
.cdp-chip-wl.on { color: var(--cdp-accent); border-color: var(--cdp-accent); background: var(--cdp-accent-soft); }


/* ── Watchlist remove, at the END of the row (owner 2026-08-21) ───────────────
   "No one ever has it in the middle between the stock symbol and the price."
   It now sits after the change percent. The panel does NOT get wider: the space is
   reclaimed by tightening the symbol/price gap, exactly as he asked. */
#cdpWatchlist td:first-child { gap: 5px; }
.cdp-wl-sym { margin-right: 2px; }
#cdpWatchlist td:last-child {
  display: flex; align-items: center; justify-content: flex-end; gap: 4px;
  white-space: nowrap;
}
#cdpWatchlist td:last-child .cdp-wl-x {
  order: 2; flex: 0 0 auto; line-height: 1; padding: 0 1px;
  /* ALWAYS VISIBLE. This was hidden until the row was hovered, and the owner reported
     plainly that he could not find the x on any row - on the one symbol he most wanted
     to remove (BTC-USD) there was no way to discover it existed at all. "A control you
     cannot see and could not hit is not a control" is the reasoning already written
     into the coarse-pointer rule below; the same argument holds for a mouse. The width
     was reserved either way, so showing it costs no layout shift. Muted until the row
     is hovered, so a rail of ten symbols does not read as ten buttons. */
  visibility: visible;
  color: var(--cdp-border);
  transition: color .12s ease;
}
#cdpWatchlist tr:hover td:last-child .cdp-wl-x,
#cdpWatchlist td:last-child .cdp-wl-x:focus-visible { color: var(--cdp-muted); }
#cdpWatchlist td:last-child .cdp-wl-x:hover { color: var(--cdp-red); }

/* ── BUILD YOUR DESK, rebuilt (Phase 2b, owner 2026-08-21) ────────────────────
   Chips with an Apple-style badge, a placed strip with insertion slots, and Pro in its
   own section. Chrome lives here, NOT in a JS-injected <style> — the Coach pill spent
   weeks rendering as a bare rectangle because its only styling sat in a template
   literal that landed after first paint. */
.cdp-customise .cx-sec { padding: 12px 14px 4px; border-bottom: 1px solid var(--cdp-border); }
.cdp-customise .cx-sec:last-child { border-bottom: 0; }
.cdp-customise .cx-sec h4 {
  display: flex; align-items: center; gap: 8px; margin: 0 0 8px;
  font-size: 10.5px; font-weight: 800; letter-spacing: .09em; text-transform: uppercase;
  color: var(--cdp-muted);
}
.cdp-customise .cx-count {
  margin-left: auto; font-weight: 700; letter-spacing: 0; text-transform: none;
  font-size: 11px; color: var(--cdp-muted);
}
.cdp-customise .cx-count-full { color: var(--cdp-accent); font-weight: 800; }
.cdp-customise .cx-prosec h4 { color: var(--cdp-accent); }

.cdp-customise .cx-fold {
  margin: 10px 0 4px; padding: 2px 8px 8px;
  border: 1px dashed var(--cdp-border); border-radius: 10px;
}
.cdp-customise .cx-fold > summary {
  display: flex; align-items: center; gap: 8px;
  cursor: pointer; list-style: none;
  font-size: 10.5px; font-weight: 800; letter-spacing: .09em; text-transform: uppercase;
  color: var(--cdp-muted); padding: 8px 2px;
}
.cdp-customise .cx-fold > summary::-webkit-details-marker { display: none; }
.cdp-customise .cx-fold > summary::before {
  content: '\25B8'; font-size: 11px; line-height: 1; color: var(--cdp-faint, var(--cdp-muted));
}
.cdp-customise .cx-fold[open] > summary::before { content: '\25BE'; }
.cdp-customise .cx-fold > summary .cx-count { margin-left: auto; }

.cdp-customise .cx-strip { display: flex; flex-wrap: wrap; gap: 6px; padding-bottom: 8px; }

.cdp-customise .cx-chip {
  position: relative; display: inline-flex; align-items: center; gap: 6px;
  font: inherit; font-size: 12px; font-weight: 700; cursor: pointer;
  padding: 7px 11px 7px 10px; border-radius: 9px;
  border: 1px solid var(--cdp-border); background: var(--cdp-panel-2); color: var(--cdp-text);
  transition: border-color .12s ease, background .12s ease, transform .12s ease;
}
.cdp-customise .cx-chip:hover { border-color: var(--cdp-accent); }
.cdp-customise .cx-chip:active { transform: scale(.97); }

/* The badge. Top-left corner, exactly the iOS remove affordance the owner described. */
.cdp-customise .cx-badge {
  position: absolute; top: -6px; left: -6px;
  min-width: 17px; height: 17px; padding: 0 4px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 999px; font-size: 11px; font-weight: 900; line-height: 1;
  border: 1px solid var(--cdp-panel); color: #fff;
}
.cdp-customise .cx-minus { background: #b3261e; }
.cdp-customise .cx-plus  { background: var(--cdp-accent); }
.cdp-customise .cx-lock  { background: var(--cdp-accent); font-size: 8.5px; letter-spacing: .04em; }

/* A required tool cannot be taken off — say so rather than offering a dead minus. */
.cdp-customise .cx-req { opacity: .72; cursor: default; }
.cdp-customise .cx-req .cx-minus { display: none; }
.cdp-customise .cx-locked { opacity: .78; }

/* Insertion targets. Owner: "they're able to see the highlighted box where the tool will
   go and choose where it will go." */
.cdp-customise .cx-slot {
  width: 12px; align-self: stretch; min-height: 30px; padding: 0;
  border: 1px dashed var(--cdp-accent); border-radius: 6px;
  background: var(--cdp-accent-soft); cursor: pointer;
  animation: cxPulse 1.4s ease-in-out infinite;
}
.cdp-customise .cx-slot:hover { background: var(--cdp-accent); }
@keyframes cxPulse { 0%,100% { opacity: .55 } 50% { opacity: 1 } }
@media (prefers-reduced-motion: reduce) { .cdp-customise .cx-slot { animation: none; opacity: .8 } }

.cdp-customise .cx-slotmsg { font-size: 12px; color: var(--cdp-text); margin: 0 0 8px; }
.cdp-customise .cx-link {
  font: inherit; font-size: 12px; font-weight: 700; padding: 0; border: 0;
  background: none; color: var(--cdp-accent); cursor: pointer; text-decoration: underline;
}
.cdp-customise .cx-note { margin: 0 0 8px; font-size: 11.5px; color: var(--cdp-muted); line-height: 1.45; }
.cdp-customise .cx-warn {
  color: var(--cdp-text); background: var(--cdp-accent-soft);
  border: 1px solid var(--cdp-accent-line); border-radius: 8px; padding: 7px 9px;
}
.cdp-customise .cx-shake { animation: cxShake .5s ease; }
@keyframes cxShake { 0%,100%{transform:translateX(0)} 20%,60%{transform:translateX(-4px)} 40%,80%{transform:translateX(4px)} }
@media (prefers-reduced-motion: reduce) { .cdp-customise .cx-shake { animation: none } }

.cdp-customise .cx-foot .cx-note { margin: 0 0 0 auto; text-align: right; max-width: 55%; }

/* The rail's locked Pro teasers. Dimmed, never armed. Click stays on Chart
   with Unlock with Pro — a lock that dumps you on /plans reads as broken. */
.cdp-rail-teaser { opacity: .42; }
.cdp-rail-teaser:hover { opacity: 1; color: var(--cdp-accent); border-color: var(--cdp-accent); background: var(--cdp-accent-soft); }

/* An empty drawing slot. Same 30px square as a tool, dashed so it reads as a
   hole you can fill — or leave blank. Numbered so Build your desk matches. */
.cdp-rail-empty {
  border: 1.5px dashed var(--cdp-border);
  background: transparent;
  color: var(--cdp-faint, var(--cdp-muted));
  font-size: 10px; font-weight: 800; letter-spacing: .02em;
}
.cdp-rail-empty:hover, .cdp-rail-empty.on {
  border-color: var(--cdp-accent); color: var(--cdp-accent);
  background: var(--cdp-accent-soft);
}

/* Stay-on-Chart Pro toast. Bottom center, same family as the indicator cap. */
.cdp-prostay[hidden] { display: none !important; }
.cdp-prostay {
  position: fixed; z-index: 2147483001; left: 50%; transform: translateX(-50%);
  bottom: 22px; max-width: min(460px, calc(100vw - 24px));
}
.cdp-prostay-card {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px;
  padding: 11px 12px 11px 14px; border-radius: 11px;
  background: var(--cdp-panel); border: 1px solid var(--cdp-accent-line);
  box-shadow: 0 14px 40px -14px rgba(0,0,0,.45);
  color: var(--cdp-text);
}
.cdp-prostay-card svg { width: 22px; height: 22px; flex: 0 0 auto; color: var(--cdp-accent); }
.cdp-prostay-card b { display: block; font-size: 13px; font-weight: 800; line-height: 1.2; }
.cdp-prostay-card p { margin: 2px 0 0; font-size: 12px; color: var(--cdp-muted); line-height: 1.35; }
.cdp-prostay-go {
  flex: 0 0 auto; text-decoration: none; font-size: 12px; font-weight: 800;
  color: #fff; background: var(--cdp-accent); border-radius: 8px; padding: 7px 11px;
  white-space: nowrap;
}
.cdp-prostay-go:hover { filter: brightness(1.06); }
.cdp-prostay-x {
  flex: 0 0 auto; width: 28px; height: 28px; border: 0; background: transparent;
  color: var(--cdp-muted); font-size: 20px; line-height: 1; cursor: pointer; border-radius: 8px;
}
.cdp-prostay-x:hover { color: var(--cdp-text); background: var(--cdp-control); }
/* The reflow. Re-seating the nodes IS the movement; this carries it. */
.cdp-rail .cdp-rtool { transition: background .12s ease, color .12s ease, border-color .12s ease, opacity .18s ease; }

/* TOUCH HAS NO HOVER. The remove X is revealed on tr:hover, which on a phone or tablet
   means it is never revealed at all — the audit measured its tap target at 17x15px and
   invisible. A control you cannot see and could not hit is not a control.
   On coarse pointers it is always visible and gets a real 30px target. (owner 2026-08-21) */
@media (hover: none), (pointer: coarse) {
  #cdpWatchlist td:last-child .cdp-wl-x {
    visibility: visible;
    min-width: 30px; min-height: 30px;
    display: inline-flex; align-items: center; justify-content: center;
    color: var(--cdp-muted);
  }
}

/* ── EDIT MODE ON THE RAIL (owner 2026-08-21) ────────────────────────────────
   "When you click the add button all the widgets should move just in case they wanna
   edit and move stuff — shouldn't just be when the bars are filled." Exactly the iOS
   model: tap the control and everything becomes editable whether or not you are out of
   room. A minus on every removable tool, and a small wobble so it reads as editable
   rather than merely decorated. */
.cdp-rail.cx-editing .cdp-rtool { position: relative; }
.cdp-rail.cx-editing .cdp-rtool:not(.cdp-rail-add):not(.cdp-rail-teaser):not(.cdp-rail-empty):not([data-tool="cross"])::after {
  content: '\2212';
  position: absolute; top: -5px; left: -5px;
  width: 15px; height: 15px; border-radius: 999px;
  background: #b3261e; color: #fff;
  font-size: 11px; font-weight: 900; line-height: 15px; text-align: center;
  border: 1px solid var(--cdp-panel);
  pointer-events: none;      /* the button itself takes the click */
}
/* Crosshair, empty holes, locks, and + never get a minus. A minus that does
   nothing (required tool) is the "red sign doesn't work" bug. */
.cdp-rail.cx-editing .cdp-rtool[data-tool="cross"]::after,
.cdp-rail.cx-editing .cdp-rail-empty::after,
.cdp-rail.cx-editing .cdp-rail-teaser::after,
.cdp-rail.cx-editing .cdp-rail-add::after {
  content: none !important;
  display: none !important;
}
.cdp-rail.cx-editing .cdp-rtool:not(.cdp-rail-add):not(.cdp-rail-teaser):not(.cdp-rail-empty):not([data-tool="cross"]) {
  animation: cxWobble .9s ease-in-out infinite;
}
@keyframes cxWobble {
  0%, 100% { transform: rotate(0deg) }
  25%      { transform: rotate(-2.2deg) }
  75%      { transform: rotate(2.2deg) }
}
@media (prefers-reduced-motion: reduce) {
  .cdp-rail.cx-editing .cdp-rtool { animation: none }
}
/* The add button stays still and stays lit — it is the way out of edit mode. */
.cdp-rail.cx-editing .cdp-rail-add {
  color: var(--cdp-accent); border-color: var(--cdp-accent); background: var(--cdp-accent-soft);
}

/* Toolbar groups inside the panel — the horizontal bar is organised by meaning
   (Timeframe, Study, Overlay, Layout, Chart), so the picker says so rather than
   presenting thirty unlabelled chips. (owner 2026-08-21) */
.cdp-customise .cx-grp { margin-bottom: 6px; }
.cdp-customise .cx-grp-h {
  display: block; margin: 6px 2px 4px;
  font-size: 10.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  color: var(--cdp-muted);
}
.cdp-customise .cx-prosec .cx-chip { border-color: var(--cdp-accent-line); }

/* A Pro indicator in the Indicators menu: named, described, dimmed — never absent. */
.cdp-indmenu [data-study-locked] { opacity: .62; }
.cdp-indmenu [data-study-locked] .cdp-ind-note {
  color: var(--cdp-accent); font-weight: 800; letter-spacing: .05em; text-transform: uppercase; font-size: 9.5px;
}
.cdp-indmenu [data-study-locked]:hover { opacity: 1; background: var(--cdp-accent-soft); }

/* The free indicator cap notice. Appears where the user just clicked, says the number,
   and offers the way out — a refusal that does not explain itself reads as a bug. */
.cdp-studycap {
  position: fixed; z-index: 2147483000; left: 50%; transform: translateX(-50%);
  bottom: 22px; max-width: min(460px, calc(100vw - 24px));
  padding: 11px 15px; border-radius: 11px;
  background: var(--cdp-panel); border: 1px solid var(--cdp-accent-line);
  box-shadow: 0 14px 40px -14px rgba(0,0,0,.45);
  font-size: 13px; font-weight: 600; color: var(--cdp-text); line-height: 1.45;
}
.cdp-studycap[hidden] { display: none !important; }
.cdp-studycap a { color: var(--cdp-accent); font-weight: 800; }


/* ── COACH BUTTON INK: settled with an ID, after five attempts with classes ──────
   White on the Pro orange measures 3.20:1 - under the floor, in BOTH themes, on the
   most-clicked control on the site.

   Five class-based fixes failed, and the diagnosis is worth recording because the
   normal debugging story does not apply. Asking the live page which rules matched
   returned ruleCount: 0 for the label AND ruleCount: 0 for the button - neither element
   has a colour rule that JS can see, yet both compute white. One stylesheet on the page
   cannot be enumerated from script, so the winning rule is somewhere I cannot read.

   Rather than keep guessing at a cascade I cannot inspect, this uses the button's ID
   (specificity 1,0,0) plus !important, which outranks any class-based rule wherever it
   lives. The colour is the lightest ink that clears 4.5:1 on all three button grounds:
     rest #E86B1F 6.20   hover #d45e16 5.12   expanded #c95512 4.54
   `color` covers the label too, which inherits rather than setting its own. */
#sim-coach-btn,
#sim-coach-btn .cj-fab-label,
#sim-coach-btn:hover,
#sim-coach-btn:hover .cj-fab-label,
#sim-coach-btn[aria-expanded="true"],
#sim-coach-btn[aria-expanded="true"] .cj-fab-label {
  color: #ffffff !important;
}


/* ── DARK MODE: the chart's own muted inks ────────────────────────────────────
   Measured on /chart in dark. Same shape as the Intel news failures: these are the
   LIGHT theme's slate greys, correct on white and far too dark on the chart ground.

     h3.panel-title  "Contracts you hold"   #475569 on #101620  2.39:1
     div.jm-who      "Your account PRO"     #48586a on #101620  2.49:1
     div.jm-jump-hd  "Account"              #6a7a72 on #101620  4.01:1
     span.pos-avg    "Avg $... · Last $..."  #737f90 on #151d29  4.17:1

   #9aa8bd clears 4.5 on both grounds (7.52 and 7.03) and still reads as secondary
   text rather than competing with the values beside it. */
html[data-chart-theme="dark"] h3.panel-title,
html[data-chart-theme="dark"] .panel-title,
html[data-chart-theme="dark"] .jm-who,
html[data-chart-theme="dark"] .jm-jump-hd,
html[data-chart-theme="dark"] .pos-avg,
html[data-chart-theme="dark"] .pos-sub {
  color: #9aa8bd;
}


/* .pos-avg resisted the rule above for the same reason the Coach button did: enumerating
   every rule that matches it on the live page returns ruleCount: 0, so its colour comes
   from the one stylesheet on the page that script cannot read. !important is the honest
   tool when you cannot see what you are competing with.
     #737f90 on #151d29 = 4.17  ->  #9aa8bd = 7.03

   NOT touched: .pos-pnl-pct measures 3.56 on a ZERO row. That colour is semantic -
   green for gains, red for losses - and blanket-overriding it would flatten the meaning
   to fix the one row where there is no gain or loss. Logged for the owner instead. */
html[data-chart-theme="dark"] .pos-avg,
html[data-chart-theme="dark"] .positions-panel .pos-avg {
  color: #9aa8bd !important;
}


/* The free-tier indicator cap, shown where the choice is made rather than in a toast at
   the bottom of the page that the owner reported he could not see at all. */
.cdp-indmenu .cdp-ind-cap {
  margin: 0 4px 6px; padding: 7px 9px; border-radius: 8px;
  background: var(--cdp-accent-soft); border: 1px solid var(--cdp-accent-line);
  font-size: 11.5px; line-height: 1.35; color: var(--cdp-text);
  min-height: 2.7em; box-sizing: border-box;
}
.cdp-indmenu .cdp-ind-cap a { color: var(--cdp-accent); font-weight: 800; }
/* Greyed, but still readable and still clickable — clicking says what to do. A row you
   cannot read is a worse answer than a row that explains itself. */
.cdp-indmenu [data-study-capped] { opacity: .55; }
.cdp-indmenu [data-study-capped] .cdp-ind-note { font-weight: 700; color: var(--cdp-muted); }
.cdp-indmenu [data-study-capped]:hover { opacity: .8; background: var(--cdp-accent-soft); }


/* ══ BUILD YOUR DESK — the two-pane picker ═══════════════════════════════════
   Owner asked five times, pointing at Fidelity's Indicator Library: "their indicators
   list has an organized categories favorites... they have a search for the indicators."
   Left category rail, search across the top, Favourites pinned above the groups, and one
   row per tool that says whether it is Free or Pro.

   The badge reads `tier` straight from CHART_FEATURE_CATALOG — the same field the gate
   enforces — so what the label says and what the lock does cannot drift apart. He asked
   for exactly that: "label which tools are pro versus free so I can make sure there's no
   pro bleeding." */

.cx-search { display: flex; gap: 8px; align-items: center; padding: 0 16px 10px; }
.cx-search input {
  flex: 1 1 auto; min-width: 0; height: 36px; border-radius: 10px;
  border: 1px solid var(--cdp-border); background: var(--cdp-control);
  color: var(--cdp-text); font: inherit; font-size: 13.5px; padding: 0 12px;
}
.cx-search input:focus { outline: 2px solid var(--cdp-accent); outline-offset: 1px; }
.cx-clear {
  width: 30px; height: 30px; border-radius: 8px; border: 1px solid var(--cdp-border);
  background: var(--cdp-control); color: var(--cdp-text); font-size: 15px; cursor: pointer;
}

/* The panel is a flex column with its own max-height; the split takes the remaining
   space and the two panes scroll inside it. The old `max-height: 60vh` on each pane was
   a second, disagreeing owner of the same measurement - with the header and search bar
   above them the panes could ask for more room than the panel had. */
.cx-split { display: grid; grid-template-columns: 184px minmax(0, 1fr); gap: 0; flex: 1 1 auto; min-height: 0; }
.cx-nav-rail {
  display: flex; flex-direction: column; gap: 2px; padding: 4px 10px 12px 16px;
  border-right: 1px solid var(--cdp-border); overflow-y: auto; min-height: 0;
}
.cx-nav {
  display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  border: 0; background: none; font: inherit; font-size: 12.5px; font-weight: 600;
  color: var(--cdp-muted); padding: 7px 9px; border-radius: 8px; cursor: pointer;
}
.cx-nav:hover { background: var(--cdp-accent-soft); color: var(--cdp-text); }
.cx-nav.on { background: var(--cdp-accent); color: #fff; font-weight: 700; }
html[data-chart-theme="dark"] .cx-nav.on { color: #04140c; }
.cx-nav-l { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cx-nav-n { font-variant-numeric: tabular-nums; font-size: 11px; opacity: .75; }
.cx-nav-sep { height: 1px; background: var(--cdp-border); margin: 7px 4px; }

.cx-pane { padding: 4px 16px 12px; overflow-y: auto; min-height: 280px; min-width: 0; }
/* COLUMNS, NOT ONE LONG LIST. 35 tools stacked one-per-line means scrolling to find
   anything; at panel width this gives two columns and a whole group is visible at once.
   auto-fill + minmax means it collapses to one column on a phone without a media query. */
.cx-rows { display: grid; grid-template-columns: repeat(auto-fill, minmax(252px, 1fr)); gap: 6px; align-content: start; }
.cx-row {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto auto; gap: 8px;
  align-items: center; padding: 8px 10px; border-radius: 10px;
  border: 1px solid var(--cdp-border); background: var(--cdp-control);
}
.cx-row:hover { border-color: var(--cdp-accent-line); }
.cx-row-lock { opacity: .78; }
.cx-fav {
  border: 0; background: none; cursor: pointer; font-size: 14px; line-height: 1;
  color: var(--cdp-faint); padding: 2px 3px; border-radius: 6px;
}
.cx-fav:hover { color: var(--cdp-accent); }
.cx-fav.on { color: #E8A33D; }   /* a star is a star; not a status colour, so not the banned amber */
.cx-row-main { min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.cx-row-l { font-size: 13px; font-weight: 700; color: var(--cdp-text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cx-row-b { font-size: 11.5px; color: var(--cdp-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cx-tier {
  font-size: 9.5px; font-weight: 800; letter-spacing: .06em; padding: 2px 7px;
  border-radius: 999px; white-space: nowrap;
}
.cx-tier.inc {
  background: var(--cdp-control); color: var(--cdp-muted);
  border: 1px solid var(--cdp-border); font-weight: 700; letter-spacing: .02em;
}
.cx-tier.pro  { background: #fbeee7; color: #a8501f; border: 1px solid #ecd0bb; letter-spacing: .06em; }
html[data-chart-theme="dark"] .cx-tier.inc { background: var(--cdp-panel-2, #1a2230); color: var(--cdp-muted); border-color: var(--cdp-border); }
html[data-chart-theme="dark"] .cx-tier.pro  { background: #2a1a10; color: #f5a870; border-color: #54382a; }
.cx-row-go {
  border: 1px solid var(--cdp-border); background: var(--cdp-control); color: var(--cdp-text);
  border-radius: 8px; padding: 5px 9px; font: inherit; font-size: 11.5px; font-weight: 700;
  cursor: pointer; text-decoration: none; white-space: nowrap;
}
.cx-row-go:hover { border-color: var(--cdp-accent-line); background: var(--cdp-accent-soft); }

/* ── "On your desk" rows ──────────────────────────────────────────────────────
   This pane used to draw cx-chip pills while "All tools" drew these rows, so the
   two halves of one panel looked like they came from different products. Both
   render through toolRow() now; these rules only add what a desk row has extra:
   its position on the rail, and the arrows that change it. */
.cx-row-rail { grid-template-columns: auto auto minmax(0, 1fr) auto auto auto; }
.cx-ord {
  font-size: 10.5px; font-weight: 800; color: var(--cdp-muted);
  font-variant-numeric: tabular-nums; min-width: 15px; text-align: center;
  background: var(--cdp-chip, var(--cdp-control)); border: 1px solid var(--cdp-border);
  border-radius: 6px; padding: 2px 0; line-height: 1.3;
}
.cx-move { display: inline-flex; gap: 2px; }
.cx-mv {
  border: 1px solid var(--cdp-border); background: var(--cdp-control); color: var(--cdp-text);
  border-radius: 6px; width: 22px; height: 22px; padding: 0; line-height: 1;
  font: inherit; font-size: 11px; cursor: pointer;
}
.cx-mv:hover:not(:disabled) { border-color: var(--cdp-accent-line); background: var(--cdp-accent-soft); }
.cx-mv:disabled { opacity: .3; cursor: default; }
.cx-row-off {
  color: #fff;
  background: #c62828;
  border-color: #b71c1c;
}
.cx-row-off:hover { background: #b71c1c; border-color: #8e0000; color: #fff; }
.cx-row-fixed {
  font-size: 10.5px; font-weight: 700; color: var(--cdp-faint);
  white-space: nowrap; padding: 5px 2px;
}
.cx-row-full {
  opacity: .72; cursor: pointer;
  background: var(--cdp-control); color: var(--cdp-muted);
  border-color: var(--cdp-border);
}
.cx-row-empty {
  border-style: dashed; background: transparent; cursor: pointer;
}
.cx-row-empty.on { border-color: var(--cdp-accent); background: var(--cdp-accent-soft); }
.cx-row-on { background: var(--cdp-accent-soft, var(--cdp-control)); }
.cx-row-go.on { background: var(--cdp-accent); border-color: var(--cdp-accent); color: #fff; }
html[data-chart-theme="dark"] .cx-row-go.on { color: #04140c; }
a.cx-row-go { color: #a8501f; border-color: #ecd0bb; background: #fbeee7; }
html[data-chart-theme="dark"] a.cx-row-go { color: #f5a870; border-color: #54382a; background: #2a1a10; }

/* Legacy engine host — never visible. Desk ticket is the only UI. */
#cdpRealTicket,
#cdpRealTicket[hidden],
#cdpFullForm {
  display: none !important;
}
.cdp-rtool[data-tool="ray"],
.cdp-rtool[data-tool="rect"],
.cdp-rtool[data-tool="brush"],
.cdp-rtool[data-tool="fib"],
.cdp-rtool[data-tool="text"],
.cdp-tbtn[data-tool="boll"],
.cdp-tbtn[data-tool="cloud"],
.cdp-tbtn[data-tool="scale"],
.cdp-tbtn[data-tool="lock"],
.cdp-tbtn[data-tool="eye"],
.cdp-tbtn[data-tool="settings"],
#cdpRailAdd,
#cdpLayout,
.cdp-group-label,
#cdpSave {
  display: none !important;
}

@media (max-width: 760px) {
  /* Keep Buying power + Estimated cost on phone — hide only Current position to save height. */
  #cdpTicketPanel { min-width: 0; max-width: 100%; }
  #cdpTicketPanel .cdp-panel-body { overflow-x: hidden; max-width: 100%; }
  .cdp-ticket-note {
    display: grid !important;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 4px 12px;
    margin-top: 8px;
    padding-top: 8px;
    font-size: 11px;
    line-height: 1.35;
  }
  .cdp-ticket-note .cdp-kv {
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    min-width: 0;
    padding: 4px 0;
  }
  .cdp-ticket-note .cdp-kv span,
  .cdp-ticket-note .cdp-kv b {
    min-width: 0;
    max-width: 100%;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .cdp-ticket-note .cdp-kv:nth-child(3) { display: none; }
  .cdp-presets { grid-template-columns: repeat(5, minmax(0, 1fr)) !important; }
  .cdp-preset { min-width: 0; padding: 7px 2px; }
  .cdp-chart-wrap { grid-template-rows: auto auto minmax(280px, 52vh) auto; }
}

@media (max-width: 390px) {
  .cdp-row2 { grid-template-columns: minmax(0, 1fr) !important; }
  .cdp-flabel { flex-wrap: wrap; gap: 4px; }
  .cdp-unit-toggle { flex: 0 0 auto; }
}

/* WINNERS-ONLY FILTER — the rule, not a stamped attribute.
   The desk used to set [hidden] on each .pos-row when the filter was on. That
   held for one paint: js/features/positions-panel.js re-renders those rows on
   every price tick, so losing rows came straight back while the button stayed
   latched and still read "Show all positions". Matching on a class the wrapper
   carries means re-rendered rows arrive already filtered.
   :has() is required because the sign lives on the child .pos-pnl, not the row. */
#poshistWrap.cdp-winners-only .pos-row:has(.pos-pnl.negative) { display: none; }

/* ═══ WATCHLIST FIT (2026-09-01) ═════════════════════════════════════════════
   The rail overflowed its own panel: white-space:nowrap on every cell plus a
   28px sparkline inside the symbol cell pushed the row wider than the column,
   so "Change" was clipped mid-word, the remove x sat half outside, and the
   panel grew a horizontal scrollbar. A watchlist that needs sideways scrolling
   to read a percentage is not a watchlist.

   Fixed by giving the table a real column model instead of letting content
   decide: the symbol column takes what is left, price and change get the width
   they actually need, and the sparkline shrinks rather than pushing. */
.cdp-wl { table-layout: fixed; }
.cdp-wl th:nth-child(1), .cdp-wl td:nth-child(1) { width: auto; min-width: 0; }
/* Re-balanced 2026-09-01 after the sparkline came out of the symbol cell. 78+84 of a
   ~206px panel left about 44px for the ticker, so BTC-USD still rendered as "BTC...".
   The change column does not need 84px once it is only "+0.08%". */
/* MEASURED, not guessed (2026-09-01). The panel table is 206px and the three columns
   were fighting over it: "BTC-USD" needs 72px, "76602.64" needs ~55px, and the change
   cell holds BOTH the text (~58px including the ::before arrow) AND the remove button
   (~16px). That is ~201px of content before any gap, so the change cell - which is
   right-aligned - overflowed LEFT and painted its arrow on top of the price's last
   digit. Slightly tighter numerals buy the room back, and the overflow guards below
   mean a long value can never again spill into the neighbouring column. */
/* Re-cut once more against measured content: at 11.5px "76565.94" needs 62px including
   its padding, so 60px clipped the leading digit by 2px. The change cell shrank with the
   smaller type and no longer needs 74. Budget: 72 (BTC-USD) + 64 + 70 = 206, the table
   width exactly. */
.cdp-wl th:nth-child(2), .cdp-wl td:nth-child(2) { width: 64px; padding-right: 5px; }
.cdp-wl th:nth-child(3), .cdp-wl td:nth-child(3) { width: 70px; }
.cdp-wl th:nth-child(1), .cdp-wl td:nth-child(1) { min-width: 62px; }
.cdp-wl td, .cdp-wl th { overflow: hidden; white-space: nowrap; }
.cdp-wl td:nth-child(3) { display: flex; align-items: center; justify-content: flex-end; gap: 4px; }
/* The price is right-aligned and the change starts at the very next pixel, so with the
   columns this tight "77379.28" and "-1.71%" ran together into one unreadable string.
   Nothing was overflowing - they simply had no gap. Separate them explicitly rather
   than stealing more width from the ticker, which is what caused the truncation. */
.cdp-wl td:nth-child(2), .cdp-wl th:nth-child(2) { padding-right: 9px; }
.cdp-wl td:first-child { gap: 6px; overflow: hidden; }
.cdp-wl .cdp-wl-sym { overflow: hidden; text-overflow: ellipsis; }
.cdp-wl .cdp-spark { flex: 0 1 auto; min-width: 0; }
/* Long crypto prices were the thing actually breaking the column - 78847.03 in a
   78px cell. Shrink the digits rather than the layout. */
.cdp-wl td:nth-child(2) { font-size: 11.5px; letter-spacing: -.02em; }
.cdp-wl td:nth-child(3) { font-size: 11.5px; letter-spacing: -.02em; }
.cdp-wl td:nth-child(3) { padding-right: 2px; }
#cdpWatchlist td:last-child .cdp-wl-x { margin-left: 4px; }
/* Nothing in this rail should ever scroll sideways. */
.cdp-panel .cdp-wl-wrap, .cdp-wl-wrap { overflow-x: hidden; }


/* ── Visible-range chips ───────────────────────────────────────────────────────
   A second, smaller group beside the interval chips. Deliberately quieter than the
   timeframe row: interval is the primary decision and range is a view on top of it, so
   they must not read as one ten-button bar. A range longer than the loaded series is
   disabled rather than hidden - a control that vanishes is as confusing as one that lies,
   and the tooltip says why. (owner 2026-09-02) */
.cdp-rangechips { margin-left: 6px; padding-left: 8px; border-left: 1px solid var(--cdp-line, #e4e8ee); }
.cdp-rangechips .cdp-chip { font-size: 11.5px; padding-inline: 8px; opacity: .92; }
.cdp-rangechips .cdp-chip[disabled] { opacity: .38; cursor: not-allowed; }
.cdp-rangechips .cdp-chip[disabled]:hover { background: none; border-color: var(--cdp-line, #e4e8ee); }
@media (max-width: 760px) {
  /* On a phone the interval row already fills the width; the range group wraps under it
     rather than pushing the toolbar into a horizontal scroll. */
  .cdp-rangechips { margin-left: 0; padding-left: 0; border-left: 0; }
}
