/* ─────────────────────────────────────────────────────────────────────────────
   78trade — the few components the kit does not cover.

   🎨 NO COLORS LIVE IN THIS FILE. The palette comes from the kit's tokens,
   re-pointed by `78-theme.css` (generated by the kit's own theme generator and
   loaded between the kit and this file). Every rule below reads a token, so
   swapping that one file re-skins the whole app — light and dark — without a
   line changing here. If you find yourself typing a hex value in this file,
   the token you actually want already exists.

   If a block below starts looking generally useful, it belongs in the 78 UI Kit
   rather than here — see the Kit feedback notes in HANDOFF.md.
   ───────────────────────────────────────────────────────────────────────────── */

/* ── 1. Page furniture ─────────────────────────────────────────────────────── */
.t78-page-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}

.t78-page-title { font-size: var(--fs-xl); font-weight: 700; line-height: 1.2; }
.t78-page-sub   { color: var(--dim); font-size: var(--fs-sm); margin-top: var(--space-1); }

.t78-section-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: var(--space-6) 0 var(--space-3);
}
.t78-section-head h2 { font-size: var(--fs-lg); font-weight: 600; }

.t78-mode-badge { margin-right: var(--space-1); }

/* An accent link in running text — the wrapping sibling of .t78-acct-link,
   which is nowrap because it sits in a card foot and must not fold. A sentence
   long enough to be a signpost has to be allowed to wrap, or it takes the page
   sideways on a phone. */
.t78-page-link { color: var(--accent); text-decoration: none; }
.t78-page-link:hover { text-decoration: underline; }

/* ── 2. The suggestion card ────────────────────────────────────────────────── */
.t78-suggestion { margin-bottom: var(--space-4); }

/* Placed. The card is its own receipt for the beat before it leaves — the same
   done-then-gone the review list uses, because it is the same idea: a list of
   decisions should hold only the ones still to make. */
.t78-suggestion.is-placed {
  border-left: 3px solid var(--success);
  opacity: .82;
}

/* On its way off the list. Height and margin collapse with the fade so the
   cards below move up rather than a hole opening where this one was. */
.t78-suggestion.is-leaving {
  opacity: 0;
  transform: translateY(-6px);
  max-height: 0;
  margin-bottom: 0;
  padding-top: 0;
  padding-bottom: 0;
  overflow: hidden;
  transition: opacity .28s ease, transform .28s ease,
              max-height .34s ease, margin .34s ease, padding .34s ease;
}

@media (prefers-reduced-motion: reduce) {
  .t78-suggestion.is-leaving { transition: none; }
}

/* The drift notice. Hidden until the server hands back a re-priced plan, and
   the kit is pinned at v0.1.1 — whose components can out-specify a bare
   [hidden] — so this one says so itself. */
.t78-reprice[hidden] { display: none; }

/* The button after a re-price: same button, now offering a different number,
   so it reads as changed rather than as the one you already pressed. */
.t78-repriced { animation: t78-repriced-in .5s ease; }

@keyframes t78-repriced-in {
  from { transform: scale(.97); }
  to   { transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .t78-repriced { animation: none; }
}

.t78-sugg-head {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.t78-ticker {
  font-family: var(--font-mono);
  font-size: var(--fs-xl);
  font-weight: 700;
  letter-spacing: .02em;
}

.t78-company {
  color: var(--dim);
  font-size: var(--fs-sm);
  margin-top: 2px;
}

.t78-headline {
  font-size: var(--fs-lg);
  font-weight: 600;
  margin-top: var(--space-3);
}

/* The "why" is the product — give the prose room to be read. */
.t78-why {
  margin-top: var(--space-2);
  max-width: 68ch;
  line-height: 1.65;
  color: var(--text);
}
.t78-why p       { margin-bottom: var(--space-2); }
.t78-why p:last-child { margin-bottom: 0; }

/* ── 3. The exit plan — buy / take-profit / stop, always together ──────────── */
.t78-plan {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--space-2);
  margin-top: var(--space-4);
}

.t78-plan-cell {
  background: var(--bg3);
  border: 1px solid var(--border);
  border-left: 3px solid var(--dim);
  border-radius: var(--radius);
  padding: var(--space-3);
}

.t78-plan-cell.is-buy    { border-left-color: var(--accent);  }
.t78-plan-cell.is-target { border-left-color: var(--success); }
.t78-plan-cell.is-stop   { border-left-color: var(--danger);  }

.t78-plan-label {
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--dim);
  font-weight: 600;
}

.t78-plan-price {
  font-size: var(--fs-xl);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  margin-top: 2px;
}

.t78-plan-note {
  font-size: var(--fs-xs);
  color: var(--dim);
  margin-top: 2px;
}

/* ── 4. Dollar risk / reward — the thing that keeps greed honest ───────────── */
.t78-rr {
  margin-top: var(--space-4);
  padding: var(--space-3);
  background: var(--bg3);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.t78-rr-line {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.t78-rr-figure { font-variant-numeric: tabular-nums; }
.t78-rr-figure b { font-size: var(--fs-lg); }
.t78-rr-risk   b { color: var(--danger);  }
.t78-rr-reward b { color: var(--success); }

.t78-rr-bar {
  display: flex;
  height: 8px;
  border-radius: var(--radius-pill);
  overflow: hidden;
  margin-top: var(--space-2);
  background: var(--border);
}
.t78-rr-bar span { display: block; height: 100%; }
.t78-rr-bar .is-risk   { background: var(--danger);  }
.t78-rr-bar .is-reward { background: var(--success); }

/* One whole divided into named, proportional segments — the tone-agnostic
   sibling of .t78-rr-bar above, for splits that are not risk vs reward.
   ⚠️ Both of these are placeholders for the kit's ._78-split-bar (v0.2.0); this
   app is still pinned at @v0.1.1, so they stay local until that bump. */
.t78-split-bar {
  display: flex;
  height: 8px;
  border-radius: var(--radius-pill);
  overflow: hidden;
  background: var(--border);
}
.t78-split-bar > span { display: block; height: 100%; }
.t78-split-bar .is-deployed { background: var(--accent); }
.t78-split-bar .is-free     { background: var(--dim);    }

/* ── 5. Approval screen hero ───────────────────────────────────────────────── */
.t78-hero-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--space-4);
  margin: var(--space-4) 0;
}

.t78-hero-num {
  font-size: 34px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}

.t78-confirm-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin-top: var(--space-5);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border);
}

/* ── 6. Under-the-hood detail — collapsed by default, on purpose ───────────── */
.t78-details {
  margin-top: var(--space-4);
  border-top: 1px solid var(--border);
  padding-top: var(--space-3);
}

.t78-details > summary {
  cursor: pointer;
  color: var(--dim);
  font-size: var(--fs-sm);
  list-style: none;
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
}
.t78-details > summary::-webkit-details-marker { display: none; }
.t78-details > summary::before { content: "▸"; transition: transform var(--transition); }
.t78-details[open] > summary::before { transform: rotate(90deg); }
.t78-details > summary:hover { color: var(--accent); }

.t78-detail-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.t78-detail-item {
  font-size: var(--fs-sm);
}
.t78-detail-item dt { color: var(--dim); font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .05em; }
.t78-detail-item dd { font-variant-numeric: tabular-nums; margin-top: 2px; }

/* ── 7. Watchlist chips + the add row ──────────────────────────────────────── */
.t78-chips { display: flex; flex-wrap: wrap; gap: var(--space-2); }

.t78-add-row {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
  align-items: flex-end;
}
.t78-add-row .t78-add-input { flex: 1 1 180px; max-width: 260px; }

/*
 * ── 7b. The two "your own ideas" controls ──────────────────────────────────
 *
 * One adds a name to the watchlist for good; the other looks at one once. They
 * were a single box, which is exactly why nobody could find the first of them —
 * so the whole job of this block is to make them read as two different things.
 * A rule between them, an icon each, and only the standing one is the primary
 * button.
 */
.t78-idea-split {
  display: grid;
  gap: var(--space-5);
}

/* Stacked by default, with a rule between them. */
.t78-idea + .t78-idea {
  border-top: 1px solid var(--border);
  padding-top: var(--space-5);
}

/*
 * Side by side, and the rule turns vertical with them. 🔴 One breakpoint drives
 * BOTH — an auto-fit track count and a media-query border disagree at the sizes
 * in between, which leaves a vertical rule drawn down the side of nothing.
 * 900px is where this card is wide enough for two readable columns beside the
 * nav rail.
 */
@media (min-width: 900px) {
  .t78-idea-split { grid-template-columns: 1fr 1fr; }

  .t78-idea + .t78-idea {
    border-top: 0;
    padding-top: 0;
    border-left: 1px solid var(--border);
    padding-left: var(--space-5);
  }
}

.t78-idea-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-1);
}

.t78-idea-icon { flex: none; color: var(--dim); }
.t78-idea-keep .t78-idea-icon { color: var(--accent); }

.t78-idea-title {
  font-size: var(--fs-base);
  font-weight: 600;
  margin: 0;
  line-height: 1.3;
}

.t78-idea-blurb {
  color: var(--dim);
  font-size: var(--fs-sm);
  margin: 0 0 var(--space-3);
  max-width: 44ch;
}

/* 🔴 The kit is pinned at v0.1.1, whose [hidden] handling is the one this
   version fixes. Stated here so an empty-state that must disappear actually
   disappears. */
.t78-watch-empty[hidden] { display: none; }

/* ── 8. Busy state for the long research run ───────────────────────────────── */
.t78-busy { position: relative; pointer-events: none; opacity: .6; }

.t78-spinner {
  width: 14px; height: 14px;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  display: inline-block;
  animation: t78-spin .7s linear infinite;
}
@keyframes t78-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .t78-spinner { animation: none; } }

/* ── 9. Refused suggestions — visible, but quiet ──────────────────────────── */
.t78-refusal { font-size: var(--fs-sm); color: var(--dim); }
.t78-refusal strong { color: var(--text); font-family: var(--font-mono); }

/* Collapsed by default: the refusals must stay reachable — a system that
   quietly finds nothing looks like a broken one — without pushing the other
   list off the screen. */
.t78-refused-block { margin-top: var(--space-4); }

.t78-refused-summary {
  cursor: pointer;
  color: var(--dim);
  font-size: var(--fs-sm);
  font-weight: 600;
  list-style: none;
  display: flex;
  align-items: center;
  gap: var(--space-2);
}
.t78-refused-summary::-webkit-details-marker { display: none; }
.t78-refused-summary::before { content: "▸"; transition: transform var(--transition); }
.t78-refused-block[open] > .t78-refused-summary::before { transform: rotate(90deg); }
.t78-refused-summary:hover { color: var(--accent); }

/* ── 10. The ticker modal ──────────────────────────────────────────────────── */
/* Built on the kit's <dialog>; only the parts the kit has no component for
   live here. See the Kit feedback in HANDOFF for the segmented control. */

.t78-tk .t78-tk-head { align-items: flex-start; gap: var(--space-3); }

.t78-tk-id { min-width: 0; }

.t78-tk-symbol {
  font-family: var(--font-mono);
  font-size: var(--fs-xl);
  font-weight: 700;
  letter-spacing: .02em;
  line-height: 1.15;
}

.t78-tk-co {
  color: var(--dim);
  font-size: var(--fs-sm);
  margin-top: 2px;
}

.t78-tk-px { margin-left: auto; text-align: right; }
.t78-tk-now { font-size: var(--fs-xl); font-weight: 700; font-variant-numeric: tabular-nums; }
.t78-tk-chg { font-size: var(--fs-sm); font-weight: 600; font-variant-numeric: tabular-nums; }

.t78-tk-loading { color: var(--dim); padding: var(--space-5) 0; text-align: center; }

/* Toolbar: range on the left, chart type on the right. */
.t78-tk-toolbar {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin-bottom: var(--space-3);
}

/* Segmented control — a joined run of buttons where exactly one is active.
   The kit's ._78-btn-group is a spaced row, and ._78-tabs-segmented is bound to
   tab panels, so neither fits a plain toggle. Kit feedback in HANDOFF. */
.t78-seg {
  display: inline-flex;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

/* Buttons where the choice is client-side (the chart ranges), links where it is
   a different URL (the Activity filter) — a filtered log has to be a page you
   can bookmark and go back from. Same control either way. */
.t78-seg button,
.t78-seg a {
  background: var(--bg3);
  border: 0;
  color: var(--dim);
  padding: 5px 12px;
  font: 600 var(--fs-xs)/1.6 var(--font);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--transition), color var(--transition);
}
.t78-seg > * + * { border-left: 1px solid var(--border); }
.t78-seg button:hover,
.t78-seg a:hover { color: var(--text); }
.t78-seg button.is-on,
.t78-seg a.is-on { background: var(--accent); color: var(--accent-text); }

.t78-soon { font-size: 9px; opacity: .7; margin-left: 4px; text-transform: uppercase; }

.t78-tk-chart { position: relative; height: 300px; margin-bottom: var(--space-4); }

.t78-tk-legend {
  display: flex;
  gap: var(--space-4);
  flex-wrap: wrap;
  margin-bottom: var(--space-4);
  font-size: var(--fs-xs);
}
.t78-tk-legend span { display: inline-flex; align-items: center; gap: 6px; color: var(--dim); }

.t78-tk-dot { width: 18px; height: 0; border-top: 2px dashed var(--dim); }
.t78-tk-dot.is-entry  { border-color: var(--accent); }
.t78-tk-dot.is-target { border-top-style: solid; border-color: var(--success); }
.t78-tk-dot.is-stop   { border-top-style: solid; border-color: var(--danger); }

/* Section heading inside the modal. */
.t78-tk-sec {
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--dim);
  font-weight: 600;
  margin: var(--space-5) 0 var(--space-2);
}

.t78-tk-why {
  background: var(--accent-lo);
  border-radius: var(--radius);
  padding: var(--space-3) var(--space-4);
  line-height: 1.6;
}
.t78-tk-why p { margin-bottom: var(--space-2); }
.t78-tk-why p:last-child { margin-bottom: 0; }

.t78-tk-desc {
  line-height: 1.6;
  font-size: var(--fs-sm);
  margin-bottom: var(--space-4);
  max-width: 68ch;
}

.t78-tk-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--space-2) var(--space-4);
  font-size: var(--fs-sm);
}
.t78-tk-k {
  color: var(--dim);
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: .05em;
}
.t78-tk-v { font-weight: 600; font-variant-numeric: tabular-nums; }

.t78-tk-news a {
  display: block;
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--row-border);
  color: var(--text);
  text-decoration: none;
}
.t78-tk-news a:last-child { border-bottom: none; }
.t78-tk-news a:hover { color: var(--accent); }
.t78-tk-src { color: var(--dim); font-size: var(--fs-xs); margin-top: 2px; }

/* A ticker you can click. Kept quiet until hover so the lists do not turn
   into a wall of links. */
.t78-tickerlink {
  cursor: pointer;
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: inherit;
  letter-spacing: inherit;
  text-align: left;
  border-bottom: 1px dashed transparent;
}
.t78-tickerlink:hover { color: var(--accent); border-bottom-color: var(--accent); }
.t78-tickerlink:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ── 11. [hidden] must actually hide ───────────────────────────────────────── */
/* The kit's ._78-alert sets `display: flex`. A class and an attribute selector
   carry the same specificity, so the later-defined rule wins and a hidden alert
   renders anyway — the ticker modal showed two empty coloured bars because of
   it. `hidden` is a browser-level semantic that nothing should be able to
   out-rank, which is what earns the !important here.
   Reported as Kit feedback in HANDOFF. */
[hidden] { display: none !important; }

/* ── 12. Sign-in ───────────────────────────────────────────────────────────── */
/* The one screen with no shell around it: no topbar, no sidebar, no page body.
   A centered column instead, so the form is the only thing on the page. */
.t78-auth {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  min-height: 100dvh;
  padding: var(--space-5) var(--space-4);
}

.t78-auth-box { width: 100%; max-width: 400px; }

/* The theme toggle floats clear of the column so it cannot push the form
   off-center on a short viewport. */
.t78-auth-top {
  position: absolute;
  top: var(--space-4);
  right: var(--space-4);
  z-index: 1;
}

.t78-auth-brand {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  font-size: var(--fs-xl);
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--accent);
}
.t78-auth-brand svg { width: 24px; height: 24px; }

.t78-auth-sub {
  margin: var(--space-2) 0 var(--space-5);
  color: var(--dim);
  font-size: var(--fs-sm);
  text-align: center;
}

.t78-auth-foot {
  margin-top: var(--space-4);
  color: var(--dim);
  font-size: var(--fs-xs);
  line-height: 1.6;
  text-align: center;
}

/* ── 13. Who is signed in ──────────────────────────────────────────────────── */
.t78-menu-who {
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--border);
  margin-bottom: var(--space-1);
  color: var(--dim);
  font-size: var(--fs-xs);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Sign out is a <button> inside a POST form rather than a link, so it needs the
   button defaults stripped back to what ._78-menu-item expects. */
.t78-menu-btn {
  width: 100%;
  border: 0;
  background: none;
  font: inherit;
  text-align: left;
  cursor: pointer;
}

/* ── 14. The account-value card ────────────────────────────────────────────── */
/* Two baselines behind one toggle: today against the previous close, total
   against where the account started. Both are rendered; the toggle picks. */
.t78-acct-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  margin-bottom: var(--space-1);
}

/* Two questions, stacked: what the ACCOUNT did (cash included) and what the
   TRADES did (on the capital actually deployed). The key column is what keeps
   them from reading as one figure and its footnote — they are different
   numbers about different denominators, and a reader must never take the
   second for a restatement of the first. */
.t78-acct-lines {
  display: grid;
  gap: 2px;
}

.t78-acct-line {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
}

.t78-acct-k {
  flex: none;
  min-width: 4.2em;
  color: var(--dim);
  font-size: var(--fs-xs);
}

.t78-acct-figs { min-height: 1.6em; }

.t78-acct-delta { font-weight: 600; }

.t78-acct-pct {
  margin-left: var(--space-2);
  padding-left: var(--space-2);
  border-left: 1px solid var(--border);
}

.t78-acct-since {
  color: var(--dim);
  font-size: var(--fs-xs);
}

.t78-acct-foot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
  margin-top: var(--space-2);
  color: var(--dim);
  font-size: var(--fs-xs);
}

.t78-acct-link { color: var(--accent); text-decoration: none; white-space: nowrap; }
.t78-acct-link:hover { text-decoration: underline; }

/* The seg buttons sit inside a stat card, where the default padding is too
   generous for the space above a big number. */
.t78-acct-seg button { padding: 3px 9px; }

/* Deployed vs free: how much of the account is actually at work. The bar is
   pushed down to the foot of the card so the headline figure keeps the space
   above it, and the legend carries the dollars — a bar with no numbers on it
   answers "roughly how much" and this card is about being exact. */
.t78-acct-bar { margin-top: var(--space-3); }

.t78-acct-legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-4);
  margin-top: var(--space-2);
  font-size: var(--fs-xs);
}

.t78-acct-leg { display: inline-flex; align-items: baseline; gap: 6px; color: var(--dim); }
.t78-acct-leg b  { color: var(--text); font-variant-numeric: tabular-nums; }
.t78-acct-leg em { font-style: normal; }

.t78-acct-leg i {
  align-self: center;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex: none;
}
.t78-acct-leg i.is-deployed { background: var(--accent); }
.t78-acct-leg i.is-free     { background: var(--dim);    }

/* The account card is the anchor of the Dashboard's KPI row: first, and twice
   the width of its neighbours.

   🔴 Why a container query and not a media query. The kit's ._78-kpi-grid is
   `repeat(auto-fit, minmax(190px, 1fr))`, so its column count follows the GRID's
   width — which is the viewport minus the sidebar, minus the app-body padding,
   and the sidebar itself collapses to a rail or a drawer. A viewport breakpoint
   would be guessing at all three. Worse, guessing wrong is not cosmetic: a
   `span 2` item in a one-column grid invents an implicit second column and the
   row overflows its own page.

   So the grid is asked directly, and only for the one thing worth asking: is
   there a third column to give away. Browsers without container query support
   fall back to the default rule below, which is full-width — the card is never
   wrong, only sometimes not narrow. */
.t78-dash-grid {
  container: t78-dash / inline-size;

  /* Capped at three columns, which is what stops the widened card orphaning a
     neighbour. Five cards where the first counts double is six slots: at three
     across that is two full rows (anchor + P&L / positions + awaiting + risk),
     and at two across it is three. The kit's own auto-fit found five columns on
     a wide screen, which left "Risk per trade" sitting alone on a second row
     with a hole beside it.

     Same auto-fit shape as ._78-kpi-grid, with the minimum track raised to a
     third of the row — so it still collapses on its own on the way down, and
     the 190px floor below is the kit's, kept deliberately identical. */
  grid-template-columns: repeat(auto-fit, minmax(max(190px, (100% - 2 * var(--space-4)) / 3), 1fr));
}

/* 🔴 `1 / -1` rather than `span 2`, and this is the whole safety of it: it means
   "every column there is", so it cannot invent a column that does not exist. A
   `span 2` in a one-column grid adds an implicit second track and the row
   overflows the page — the classic way this change goes wrong on a phone.

   So the card takes the whole row by default (identical to normal behaviour when
   there is only one column, full width when there are two), and only narrows to
   two-of-three once the grid provably HAS three: the template above needs ~602px
   for a third track, and 620px is asked for with margin to spare. */
.t78-dash-grid .t78-acct { grid-column: 1 / -1; }

@container t78-dash (min-width: 620px) {
  .t78-dash-grid .t78-acct { grid-column: span 2; }
}

/* ── 14b. The Dashboard's click-through affordances ────────────────────────── */
/* The little arrow that turns a number into a door. On the KPI cards it is what
   makes "3 awaiting your call" somewhere to go rather than something to read. */
.t78-go {
  display: block;
  margin-top: 2px;
  color: var(--accent);
  font-weight: 600;
}

/* The equity glance: a whole card that is one link to Performance. */
.t78-spark-card {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
  text-decoration: none;
  color: inherit;
  transition: border-color var(--transition);
}
.t78-spark-card:hover { border-color: var(--accent); }

.t78-spark-text { flex: 1 1 200px; min-width: 0; }
.t78-spark-move { font-size: var(--fs-lg); font-weight: 700; font-variant-numeric: tabular-nums; }
.t78-spark-sub  { color: var(--dim); font-size: var(--fs-xs); margin-top: 2px; }
.t78-spark-sub .t78-go { display: inline; margin: 0; }

/* Wide enough to read a trend off, short enough that it stays a glance. */
.t78-spark { flex: 2 1 320px; min-width: 0; }
.t78-spark ._78-sparkline { height: 44px; }

/* ── 14c. Holdings: the switchable Value column ────────────────────────────── */
/* Which of the two the column is showing, said in the header rather than only
   on the control — the number is meaningless if you have to look away to find
   out what it means. */
.t78-val-which {
  display: block;
  color: var(--dim);
  font-size: var(--fs-xs);
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
}

.t78-val-label { margin-right: var(--space-1); }

/* The total that must agree with the account card's split bar. */
.t78-val-total th,
.t78-val-total td {
  border-top: 1px solid var(--border);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* Percentage beside the dollar P&L in the holdings table. Dimmed and lighter:
   the dollars are the figure, the percentage is the context. */
.t78-pl-pct {
  display: block;
  color: var(--dim);
  font-size: var(--fs-xs);
  font-weight: 400;
}

/* ── 15. The equity curve (history.php) ────────────────────────────────────── */
.t78-eq-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
}

/* Two segmented controls now share the card head ($/% and the range), so they
   travel together and wrap as a pair at phone width rather than one of them
   being stranded on its own line. */
.t78-eq-controls {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.t78-eq-chart { position: relative; height: 340px; margin: var(--space-3) 0; }

/* ── The "vs benchmark" card ──────────────────────────────────────────────── */
/* Its Today/Total toggle lives on the label row, so the figure keeps the same
   vertical rhythm as the three stat cards beside it. */
.t78-vs-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
}

/* A seg sized to sit inside a stat-card label without growing the card. */
.t78-seg-xs button { padding: 2px 8px; font-size: 10px; }

.t78-eq-foot {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  font-size: var(--fs-xs);
}

/* ── 16. The market-status pill ────────────────────────────────────────────── */
/* Server-rendered from the venue's clock; the countdown ticks in app.js. */
.t78-mkt {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-2);
  padding: 4px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  font-size: var(--fs-xs);
  white-space: nowrap;
}

.t78-mkt-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--dim);
  flex: none;
  align-self: center;
}

.t78-mkt.is-open .t78-mkt-dot    { background: var(--success); }
.t78-mkt.is-closed .t78-mkt-dot  { background: var(--dim); }
.t78-mkt.is-unknown .t78-mkt-dot { background: var(--warn); }

.t78-mkt-label  { font-weight: 600; }
.t78-mkt-detail { color: var(--dim); }

/* Below the shell's breakpoint the topbar is tight — the label carries it. */
@media (max-width: 720px) {
  .t78-mkt-detail { display: none; }
}

/*
 * On a phone it is tighter than that, and shedding the detail was not enough:
 * at 375px the topbar's contents measured 407px, which put the ACCOUNT MENU
 * about 32px off the right-hand edge — on every page, not just this app's wide
 * one. The topbar is `position: fixed`, so that overflow never showed up as a
 * page scrollbar; the button was simply not there.
 *
 * The pill is the right thing to give up the room. Its colour already carries
 * the state at a glance, and it is ambient information — unlike the Paper badge
 * beside it, which is a safety signal this app does not hide, or the three
 * controls, which you have to be able to tap.
 *
 * 🔴 Hidden to the eye, not to a screen reader: the label keeps its place in the
 * accessibility tree, so "Open"/"Closed" is still announced. And `.t78-mkt` is
 * made a containing block first — an absolutely positioned child of an
 * unpositioned ancestor escapes to the page and plants itself wherever its
 * static position fell, which is exactly the bug this release is fixing.
 */
@media (max-width: 480px) {
  /* The mark keeps the identity and the link home; the wordmark is the one
     thing up here that costs 58px and says nothing the page below does not.
     Dropping it is what gets the account menu on screen at 360px, which is the
     width of an enormous number of Android phones. */
  .t78-brand-text { display: none; }

  .t78-mkt { position: relative; }

  .t78-mkt-label {
    position: absolute;
    top: 0;
    left: 0;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

/* ── 17. Closing a position, and the danger zone ───────────────────────────── */
.t78-close-btn { white-space: nowrap; }

.t78-danger { border-color: var(--danger); }

.t78-danger-list {
  margin: var(--space-2) 0 var(--space-3);
  padding-left: var(--space-5);
  line-height: 1.7;
}

/* Step 1 of Start Over: the account as the broker reports it right now. A
   definition list rather than a table — four or five label/value pairs that
   should wrap onto one line each on a phone and sit in a row on a desktop. */
.t78-preflight {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-5);
  margin: var(--space-3) 0 0;
}

.t78-preflight > div { min-width: 7rem; }

.t78-preflight dt {
  font-size: var(--fs-xs);
  color: var(--dim);
  text-transform: uppercase;
  letter-spacing: .04em;
}

.t78-preflight dd { margin: 0; }

.t78-danger-confirm {
  display: flex;
  align-items: flex-end;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--border);
}

.t78-danger-confirm ._78-field { margin-bottom: 0; min-width: 16rem; }

/* A disabled destructive button should look inert, not merely dimmed. */
.t78-danger-confirm button[disabled] { opacity: .45; cursor: not-allowed; }

/* Visually hidden but read aloud — for the header on the Close column, which
   has no visible text. The kit has no such utility yet (kit feedback).

   🔴 `top: 0; left: 0` is not decoration, it is the fix for a real bug. Without
   them the box sits at its STATIC position, which for the Close header is the
   far right of a ten-column table — roughly 880px into a 375px phone. Being
   absolutely positioned it did not scroll with the table; it planted itself at
   x=878 of the *document*, which stretched the page, which stretched the fixed
   topbar, which pushed the theme toggle, the account menu and the hamburger off
   the side of the screen. An invisible 1px span took the navigation with it.

   Pinned to the origin of whatever contains it, it can never do that again: it
   is 1px at the top-left corner, clipped, and nothing can overflow to the left
   of the origin. See also the ._78-table-wrap rule below. */
.t78-sr-only {
  position: absolute;
  top: 0;
  left: 0;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* 🔴 The scroll container has to be the containing block for what it scrolls.
   `._78-table-wrap` (kit) is `overflow-x: auto` but not positioned, so it
   scrolls its in-flow content while any absolutely positioned descendant — whose
   containing block is then the page itself — escapes the scroller entirely and
   lands wherever its static position fell inside the un-scrolled table. That is
   how one visually-hidden `<th>` label made the whole Positions page wider than
   a phone.

   `position: relative` with no z-index makes the wrap the containing block and
   creates no stacking context, so nothing about paint order changes. Sticky
   headers (`._78-table-sticky`) are unaffected — sticky resolves against the
   scrolling ancestor, not the containing block.

   ⚠️ Kit feedback: this belongs in the kit's own `._78-table-wrap`. Every
   project that puts a tooltip, a badge or an sr-only label in a wide table has
   this bug waiting for it. */
._78-table-wrap { position: relative; }

/* ── 18. The broker-key form ───────────────────────────────────────────────── */
.t78-keys-form {
  display: flex;
  align-items: flex-end;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.t78-keys-form ._78-field {
  margin-bottom: 0;
  flex: 1 1 18rem;
  min-width: 14rem;
}

.t78-keys-actions {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
}

/* ── 18b. The trading dials ────────────────────────────────────────────────────
   One block per editable setting: the label with its current value on the same
   line, the sentence explaining what it does, then the input and Save.

   The current value sits UP with the label rather than only in the input,
   because the input is a thing you are about to change and the label is what is
   true right now — and after a save those two agree again. Both themes come
   free: every colour below is a kit token. */
.t78-tunable + .t78-tunable {
  margin-top: var(--space-5);
  padding-top: var(--space-5);
  border-top: 1px solid var(--border);
}

.t78-tunable:first-of-type { margin-top: var(--space-4); }

.t78-tunable-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.t78-tunable-head label {
  font-weight: 600;
  font-size: var(--fs-sm);
}

.t78-tunable-now {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  flex-wrap: wrap;
  font-size: var(--fs-sm);
}

.t78-tunable-now b { font-family: var(--font-mono); }

.t78-tunable p { margin: var(--space-1) 0 var(--space-3); }

.t78-tunable-field {
  display: flex;
  align-items: flex-end;
  gap: var(--space-2);
  margin-bottom: var(--space-1);
}

.t78-tunable-field ._78-input {
  /* Wide enough for "1,000.00" and no wider — a money field the width of the
     card invites a number nobody meant to type. */
  width: 9rem;
}

/* The $ or % glued to the field, so the unit is never in doubt. It is a
   decoration on the wrapper rather than text in the input, which keeps the
   input's value a plain number for both the browser's own validation and the
   server's. */
.t78-input-suffix { position: relative; display: inline-block; }

.t78-input-suffix::after {
  content: attr(data-suffix);
  position: absolute;
  top: 50%;
  right: var(--space-3);
  transform: translateY(-50%);
  color: var(--dim);
  font-size: var(--fs-sm);
  pointer-events: none;
}

.t78-input-suffix ._78-input { padding-right: 2.25rem; }

/* Chrome/Safari put spinners over the suffix. The field is a number input for
   the keyboard it summons on a phone, not for the arrows. */
.t78-input-suffix ._78-input::-webkit-outer-spin-button,
.t78-input-suffix ._78-input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.t78-input-suffix ._78-input[type="number"] { -moz-appearance: textfield; appearance: textfield; }

/* A second paragraph inside an alert body. The kit has `._78-mt-*` and
   `._78-mb-*` but no `-0`, so there is no way to say "and no trailing margin"
   — which is what a last paragraph in a box always wants.

   ⚠️ Kit feedback: `._78-mb-0` / `._78-mt-0` are missing from the spacing
   utilities. Every other step exists (4/8/12/16); zero is the one you reach
   for most, because it is how you stop the last child pushing the box open. */
._78-alert-body .t78-alert-p {
  margin-top: var(--space-2);
  margin-bottom: 0;
}

/* ── 19. Documentation ─────────────────────────────────────────────────────── */
/* Three columns on a wide screen: topics · the page · on-this-page. Collapses
   to one, in reading order, on anything narrow. */
.t78-docs {
  display: grid;
  grid-template-columns: 15rem minmax(0, 1fr) 13rem;
  gap: var(--space-5);
  align-items: start;
}

@media (max-width: 1200px) {
  .t78-docs { grid-template-columns: 14rem minmax(0, 1fr); }
  .t78-docs-toc { display: none; }
}

@media (max-width: 860px) {
  .t78-docs { grid-template-columns: minmax(0, 1fr); }
}

.t78-docs-nav,
.t78-docs-toc {
  position: sticky;
  top: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: 2px;
}

@media (max-width: 860px) {
  .t78-docs-nav { position: static; }
}

.t78-docs-nav-head {
  color: var(--dim);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  padding: 0 var(--space-3);
  margin-bottom: var(--space-2);
}

.t78-docs-topic {
  display: block;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius);
  border-left: 2px solid transparent;
  color: var(--text);
  text-decoration: none;
  transition: background var(--transition), border-color var(--transition);
}

.t78-docs-topic:hover { background: var(--bg3); }

.t78-docs-topic.is-on {
  background: var(--bg3);
  border-left-color: var(--accent);
}

.t78-docs-topic-title { display: block; font-size: var(--fs-sm); font-weight: 600; }

.t78-docs-topic-sub {
  display: block;
  color: var(--dim);
  font-size: var(--fs-xs);
  line-height: 1.4;
  margin-top: 2px;
}

.t78-docs-toc-link {
  display: block;
  padding: 3px var(--space-3);
  border-left: 2px solid var(--border);
  color: var(--dim);
  font-size: var(--fs-xs);
  line-height: 1.5;
  text-decoration: none;
}

.t78-docs-toc-link:hover { color: var(--text); border-left-color: var(--accent); }
.t78-docs-toc-link.is-h3 { padding-left: var(--space-5); }

/* ── The rendered page ─────────────────────────────────────────────────────── */
.t78-doc { line-height: 1.75; }

.t78-doc > :first-child { margin-top: 0; }
.t78-doc > :last-child  { margin-bottom: 0; }

.t78-doc h1 { font-size: var(--fs-xl); font-weight: 700; line-height: 1.25; margin: 0 0 var(--space-3); }
.t78-doc h2 {
  font-size: var(--fs-lg);
  font-weight: 600;
  line-height: 1.3;
  margin: var(--space-6) 0 var(--space-3);
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--border);
}
.t78-doc h3 { font-size: var(--fs-base); font-weight: 600; margin: var(--space-5) 0 var(--space-2); }
.t78-doc h4 { font-size: var(--fs-sm); font-weight: 600; margin: var(--space-4) 0 var(--space-2); }

/* The heading anchor: present for linking, quiet until the heading is hovered. */
.t78-doc-anchor {
  margin-left: var(--space-2);
  color: var(--dim);
  text-decoration: none;
  opacity: 0;
  font-weight: 400;
  transition: opacity var(--transition);
}
.t78-doc-h:hover .t78-doc-anchor { opacity: 1; }
.t78-doc-anchor:hover { color: var(--accent); }

.t78-doc p { margin: 0 0 var(--space-3); }

.t78-doc a { color: var(--accent); }

.t78-doc-list { margin: 0 0 var(--space-3); padding-left: var(--space-5); }
.t78-doc-list li { margin-bottom: var(--space-2); }
.t78-doc-list .t78-doc-list { margin: var(--space-2) 0 0; }

.t78-doc-code {
  font-family: var(--font-mono);
  font-size: .9em;
  background: var(--bg3);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 1px 5px;
}

.t78-doc-pre {
  background: var(--bg3);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--space-3);
  overflow-x: auto;
  margin: 0 0 var(--space-4);
  line-height: 1.6;
}

.t78-doc-pre code {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  background: none;
  border: 0;
  padding: 0;
  white-space: pre;
}

/* Callout: the docs use blockquotes for the things worth stopping at. */
.t78-doc-quote {
  margin: 0 0 var(--space-4);
  padding: var(--space-3) var(--space-4);
  border-left: 3px solid var(--accent);
  border-radius: 0 var(--radius) var(--radius) 0;
  background: var(--bg3);
}
.t78-doc-quote > :last-child { margin-bottom: 0; }

.t78-doc-table { margin-bottom: var(--space-4); }
.t78-doc-table td { vertical-align: top; }

.t78-doc-hr { border: 0; border-top: 1px solid var(--border); margin: var(--space-6) 0; }

/* ── 20. The bracket gauge ──────────────────────────────────────────────────
   One position drawn as the bracket it sits inside: a red stretch from the stop
   up to what you paid, a green one from there up to the target, and a marker
   where it is trading now. Compact in a Holdings cell, full-width in the
   Dashboard's overview — the same component, the same geometry from
   Trading\BracketView, two sizes.

   🔴 The split is the ENTRY, and the marker is coloured by which side of it the
   price is on: red below (underwater), green at or above (in profit). That is a
   different question from the row's "near the stop" flag, and the two are kept
   apart on purpose — a position can be slightly underwater and nowhere near its
   stop, or barely in profit with a stop right underneath it.

   🎨 Every colour here is a kit token. The fills are the same --danger and
   --success the P&L figures use. */
.t78-bkt              { --mark: var(--dim);     padding-top: 10px; }
.t78-bkt.zone-loss    { --mark: var(--danger);  }
.t78-bkt.zone-profit  { --mark: var(--success); }

.t78-bkt-track {
  position: relative;
  height: 12px;
  border-radius: 6px;
  background: var(--bg3);
}

/* Clipped, so the two segments take the track's rounded ends and nothing else
   has to know about the radius. The caps, the tick and the marker sit OUTSIDE
   this — they have to be able to stand proud of the bar. */
.t78-bkt-fill {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  overflow: hidden;
}
.t78-bkt-fill > span { position: absolute; top: 0; bottom: 0; }

.t78-bkt-fill .is-loss   { background: var(--danger);  }
.t78-bkt-fill .is-profit { background: var(--success); }
.t78-bkt-fill .is-flat   { background: var(--dim);     }

/* The exits themselves, standing a little proud of the bar at each end. */
.t78-bkt-cap {
  position: absolute;
  top: 50%;
  width: 3px;
  height: 20px;
  margin: -10px 0 0 -1.5px;
  border-radius: 2px;
}
.t78-bkt-cap.is-stop   { background: var(--danger);  }
.t78-bkt-cap.is-target { background: var(--success); }

/* Near the stop, the stop end thickens. The bar's own colours are spoken for by
   profit-and-loss, so the danger reading needs a shape rather than a hue — and
   it is the one signal the compact gauge cannot get from a row flag. */
.t78-bkt.is-danger .t78-bkt-cap.is-stop { width: 5px; margin-left: -2.5px; }

/* The entry tick, which is also the red/green pivot. Cut in the surface colour
   so it reads as a gap in the bar rather than a third colour competing with the
   two that mean something — and so it stays legible in both themes, where
   --danger and --success swap between dark and pastel. */
.t78-bkt-entry {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 3px;
  margin-left: -1.5px;
  background: var(--bg2);
}

/* Where it is trading now: a needle through the bar, and a triangle standing
   above it on the page background so the marker is legible whichever colour it
   is standing on. */
.t78-bkt-now {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 0;
}
.t78-bkt-now::before {
  content: "";
  position: absolute;
  left: -5px;
  bottom: 100%;
  margin-bottom: 3px;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-top: 6px solid var(--mark);
}
.t78-bkt-now::after {
  content: "";
  position: absolute;
  left: -1.5px;
  top: -2px;
  bottom: -2px;
  width: 3px;
  border-radius: 2px;
  background: var(--mark);
  box-shadow: 0 0 0 2px var(--bg2);
}

/* "entry $311.78", under the tick it belongs to. */
.t78-bkt-pivot-row { position: relative; height: 1.2em; margin-top: var(--space-1); }
.t78-bkt-pivot {
  position: absolute;
  transform: translateX(-50%);
  white-space: nowrap;
  color: var(--dim);
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
}

/* The Holdings-cell version: no labels, no caption — every number it could
   print is already in the row next to it. It answers one question. */
.t78-bkt.is-compact                     { padding-top: 7px; }
.t78-bkt.is-compact .t78-bkt-track      { height: 8px; border-radius: 4px; }
.t78-bkt.is-compact .t78-bkt-cap        { height: 14px; margin-top: -7px; }
.t78-bkt.is-compact .t78-bkt-entry      { width: 2px; margin-left: -1px; }
.t78-bkt.is-compact .t78-bkt-now::before {
  left: -4px;
  margin-bottom: 2px;
  border-left-width: 4px;
  border-right-width: 4px;
  border-top-width: 5px;
}
.t78-bkt.is-compact .t78-bkt-now::after { left: -1px; width: 2px; }

.t78-bkt-col      { width: 132px; min-width: 132px; }
th.t78-bkt-col    { text-align: left; }
.t78-bkt-none     { color: var(--dim); }

/* ── 21. The bracket overview panel ─────────────────────────────────────────
   🔴 Mobile-first, always visible, and it lives on the DASHBOARD. The Holdings
   table is ten columns and on a phone it is a sideways scroll; this is the same
   information as a stack that never scrolls sideways and never hides behind a
   click. */
.t78-bkt-intro { margin-bottom: var(--space-4); }

.t78-bkt-row {
  border-left: 2px solid transparent;
  padding-left: var(--space-3);
}
.t78-bkt-row + .t78-bkt-row {
  margin-top: var(--space-5);
  padding-top: var(--space-5);
  border-top: 1px solid var(--border);
}
/* The scanning cue on a phone: the row that needs looking at is marked down its
   whole edge, not just at the marker. */
.t78-bkt-row.is-warn   { border-left-color: var(--warn);   }
.t78-bkt-row.is-danger { border-left-color: var(--danger); }

.t78-bkt-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.t78-bkt-sym {
  font-weight: 600;
  color: var(--text);
  text-decoration: none;
  border-bottom: 1px dotted var(--border);
}
.t78-bkt-sym:hover { color: var(--accent); border-bottom-color: var(--accent); }
.t78-bkt-sym:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.t78-bkt-meta { color: var(--dim); font-size: var(--fs-sm); font-variant-numeric: tabular-nums; }
.t78-bkt-flag { flex: none; }
.t78-bkt-pl   { font-weight: 600; font-variant-numeric: tabular-nums; }

.t78-bkt-ends {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  margin-top: var(--space-3);
  font-size: var(--fs-xs);
}
.t78-bkt-end em {
  font-style: normal;
  color: var(--dim);
  text-transform: uppercase;
  letter-spacing: .05em;
}
.t78-bkt-end b { margin-left: 5px; font-variant-numeric: tabular-nums; }
.t78-bkt-end.is-stop b   { color: var(--danger);  }
.t78-bkt-end.is-target b { color: var(--success); }

/* auto-fit rather than a breakpoint: two across on a phone, three on a desk,
   and the same rule covers the sidebar being open or collapsed. */
.t78-bkt-figs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--space-3);
  margin-top: var(--space-3);
}
.t78-bkt-fig    { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.t78-bkt-fig em {
  font-style: normal;
  color: var(--dim);
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: .05em;
}
.t78-bkt-fig b  { font-size: var(--fs-base); font-variant-numeric: tabular-nums; }
.t78-bkt-fig i  { font-style: normal; color: var(--dim); font-size: var(--fs-xs); }
.t78-bkt-fig.is-risk b   { color: var(--danger);  }
.t78-bkt-fig.is-reward b { color: var(--success); }

/* The caption over each set of figures. Two sets sit under the bar — one
   measured from the price now, one from what you paid — and they are captioned
   because they disagree on purpose: reading a from-here figure as the trade's
   outcome is exactly the confusion these lines exist to prevent. The fixed set
   colours its own numbers by sign (a stop trailed above the entry is a gain),
   so it takes no colour from here. */
.t78-bkt-figset {
  margin-top: var(--space-4);
  color: var(--dim);
}
.t78-bkt-figs-fixed { margin-top: var(--space-2); }

/* No exits resting, or no last price to place on the bar. */
.t78-bkt-bare {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-3);
  color: var(--dim);
  font-size: var(--fs-sm);
}

.t78-bkt-foot {
  margin-top: var(--space-5);
  padding-top: var(--space-3);
  border-top: 1px solid var(--border);
  font-size: var(--fs-sm);
}

/* ── 21b. The Dashboard's glance strip ──────────────────────────────────────
   The condensed cousin of the panel above: one slim two-line row per position,
   worst-first, and nothing on it that the full overview is for. Line one reads
   in words, line two is the compact bar with no labels.

   🔴 Mobile-first is the reason it exists. The whole row is a link to
   Positions, so a thumb never has to find a ticker — and it stays a single
   column at every width, because a glance that reflows into two is a glance
   you have to read twice. */
.t78-gl-panel { padding-top: var(--space-2); padding-bottom: var(--space-2); }

.t78-gl-row {
  display: block;
  padding: var(--space-3) var(--space-2) var(--space-4);
  border-left: 2px solid transparent;
  color: var(--text);
  text-decoration: none;
}
.t78-gl-row + .t78-gl-row {
  border-top: 1px solid var(--border);
}
/* The scanning cue: the row that needs looking at is marked down its whole
   edge, the same signal the full overview uses. */
.t78-gl-row.is-warn   { border-left-color: var(--warn);   }
.t78-gl-row.is-danger { border-left-color: var(--danger); }

.t78-gl-row:hover { background: var(--bg3); }
.t78-gl-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* Wraps rather than truncates: on the narrowest screen the flag drops to its
   own line with the P&L still pushed right, and nothing that matters is cut
   off mid-word. */
.t78-gl-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-2);
}
.t78-gl-sym  { font-weight: 600; flex: none; }
.t78-gl-meta {
  min-width: 0;
  color: var(--dim);
  font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums;
}
.t78-gl-flag { flex: none; }

/* Always hard right, on whichever line it lands on. */
.t78-gl-pl {
  margin-left: auto;
  flex: none;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* No exits resting, or no last price to place on the bar — the two cases with
   no bar to draw. They keep their words here; a missing stop is not a detail
   the glance gets to drop. */
.t78-gl-bare {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-2);
  color: var(--dim);
  font-size: var(--fs-sm);
}

.t78-gl-foot {
  margin-top: var(--space-3);
  padding: var(--space-3) var(--space-2) 0;
  border-top: 1px solid var(--border);
  font-size: var(--fs-sm);
}

/* ── 22. The "updated HH:MM" indicator ──────────────────────────────────────
   The auto-refresh's only visible surface. It says when the figures were read
   and, when the market is shut, that it has stopped asking. */
.t78-page-tools {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-3);
}

.t78-live {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--dim);
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
}

.t78-live-dot {
  flex: none;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--success);
}
.t78-live.is-paused .t78-live-dot { background: var(--dim);  }
.t78-live.is-stale  .t78-live-dot { background: var(--warn); }
.t78-live.is-working .t78-live-dot { animation: t78-live-pulse 1s ease-in-out infinite; }

@keyframes t78-live-pulse { 50% { opacity: .2; } }

@media (prefers-reduced-motion: reduce) {
  .t78-live.is-working .t78-live-dot { animation: none; }
}

/* ── 23. The Activity log ───────────────────────────────────────────────────
   🔴 Mobile-first, for the same reason the bracket overview is: the table this
   replaced was nine columns, and on a phone nine columns is a sideways scroll —
   fine for looking one number up, useless for reading what your trading has
   been doing. So it is a stack of full-width rows that never scrolls sideways
   and never hides the result behind a click.

   The stripe down the left edge carries the outcome: green made money, red lost
   it, amber is a bracket that ended without being recorded. Never colour alone —
   every row also says its state in words, in the badge on line one. */
.t78-act-panel { padding-top: var(--space-2); padding-bottom: var(--space-2); }
.t78-act-intro { margin-bottom: var(--space-4); }

.t78-act-row {
  border-left: 2px solid transparent;
  padding: var(--space-4) 0 var(--space-4) var(--space-3);
}
.t78-act-row + .t78-act-row { border-top: 1px solid var(--border); }

.t78-act-row.is-green  { border-left-color: var(--success); }
.t78-act-row.is-red    { border-left-color: var(--danger);  }
.t78-act-row.is-warn   { border-left-color: var(--warn);    }
.t78-act-row.is-accent { border-left-color: var(--accent);  }
.t78-act-row.is-info   { border-left-color: var(--border);  }

/* Wraps rather than truncates: on the narrowest screen the badge drops to its
   own line with the money still pushed right, and nothing is cut mid-word. */
.t78-act-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-2);
}

.t78-act-sym {
  font-weight: 600;
  color: var(--text);
  text-decoration: none;
  border-bottom: 1px dotted var(--border);
}
.t78-act-sym:hover { color: var(--accent); border-bottom-color: var(--accent); }
.t78-act-sym:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.t78-act-meta {
  min-width: 0;
  color: var(--dim);
  font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums;
}
.t78-act-state { flex: none; }

/* Always hard right, on whichever line it lands on — so a column of results
   reads straight down the edge of the page. */
.t78-act-pl {
  margin-left: auto;
  flex: none;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* auto-fit rather than a breakpoint: two across on a phone, four on a desk, and
   the same rule covers the sidebar being open or collapsed. */
.t78-act-figs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: var(--space-3);
  margin-top: var(--space-3);
}
.t78-act-fig    { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.t78-act-fig em {
  font-style: normal;
  color: var(--dim);
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: .05em;
}
.t78-act-fig b  { font-size: var(--fs-base); font-variant-numeric: tabular-nums; }
.t78-act-fig i  { font-style: normal; color: var(--dim); font-size: var(--fs-xs); }
.t78-act-fig.is-target b { color: var(--success); }
.t78-act-fig.is-stop b   { color: var(--danger);  }

/* What happened to it, in words. The one line on the row that is allowed to be
   a sentence — including when the sentence is an admission. */
.t78-act-out {
  color: var(--dim);
  font-size: var(--fs-sm);
  margin-top: var(--space-3);
}
.t78-act-out a { color: var(--accent); text-decoration: none; }
.t78-act-out a:hover { text-decoration: underline; }

/* The section head carries the tally, and gives it its own line on a phone
   rather than squeezing it beside a heading. */
.t78-act-head-row {
  flex-wrap: wrap;
  gap: var(--space-3);
}
.t78-act-tally { font-variant-numeric: tabular-nums; }

/* ── The stream table ───────────────────────────────────────────────────────
   🔴 LAYOUT ONLY. Every color in the table itself comes from the kit's
   Tabulator adapter (css/adapters/tabulator.css), which maps Tabulator's own
   class names onto kit tokens — so the table follows a theme switch for free.
   Nothing below targets a Tabulator class name, deliberately: a private theme
   here would drift the day the kit's adapter changes. */
.t78-act-table-card { padding: var(--space-3); }

.t78-act-filters {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--space-3);
  margin-bottom: var(--space-3);
}
/* ⚠️ Said here because the kit is pinned at v0.1.1, whose components can
   out-specify a bare [hidden] — the same reason .t78-reprice[hidden] exists.
   Without it the "Clear filters" button is visible with nothing to clear. */
.t78-act-filters [hidden] { display: none; }

/* No vertical padding here on purpose: the kit sizes ._78-input with
   `height: var(--control-h)` and `padding: 0 var(--space-3)`, so adding any
   changes nothing but the internal box — measured, 36px either way. */
.t78-act-dates { min-width: 130px; margin-bottom: 0; }

/* The one filter that is a question rather than a narrowing, so it sits away
   from the dates and carries its own count. */
.t78-act-outside { align-self: center; gap: var(--space-2); font-size: var(--fs-sm); }
.t78-act-outside input:disabled { cursor: default; }

/* Cell contents. The stamp and the sentence, kept from wrapping the row into
   three lines on a narrow table. */
.t78-act-when   { color: var(--dim); font-size: var(--fs-sm); }
.t78-act-detail { color: var(--text); }

/* ── The error log table (errors.php) ───────────────────────────────────────
   It reuses every class above — same card, same filter bar, same cell rules,
   because it is the same table doing the same job on different rows. The one
   thing it adds is the endpoint that sits in front of the message: a path like
   /v2/account/portfolio/history is long, repetitive and worth dimming, but it
   must not be allowed to push the sentence after it out of the row.
   🔴 Layout and a kit token. No color of its own, like everything else here.

   ⚠️ The gap is a margin, not a space in the markup. Tabulator's cell is
   `white-space: nowrap` and the three spans are built by string concatenation,
   which left the endpoint touching the sentence after it (`/v2/ordersHTTP…`).
   A margin cannot be collapsed away by anything. */
.t78-err-endpoint {
  color: var(--dim);
  overflow-wrap: anywhere;
  margin: 0 var(--space-2) 0 var(--space-2);
}

/* The one rule in this file aimed at a Tabulator class, and it sets NO color:
   the fold-out panel on a phone lays its values out in a table cell that does
   not wrap, so the sentence that is the whole reason for tapping ran off the
   edge. Layout only — the panel's colors still come from the kit's adapter. */
.tabulator-responsive-collapse td {
  white-space: normal;
  overflow-wrap: anywhere;
}

/* ── The position review — verdict cards ────────────────────────────────────
   One card per holding that needs a decision. The left edge carries the
   verdict's tone, so the shape of the list is readable before a word of it is:
   red = something is wrong or unprotected, green = a gain to bank or extend,
   amber = protect what is there. */
.t78-review {
  border-left: 4px solid var(--warn);
  margin-bottom: var(--space-3);
}
.t78-review.is-danger { border-left-color: var(--danger); }
.t78-review.is-bank   { border-left-color: var(--success); }
.t78-review.is-run    { border-left-color: var(--success); }
.t78-review.is-warn   { border-left-color: var(--warn); }

.t78-review-head {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
}

/* The verdict in words, under the ticker, in the tone of its own card. */
.t78-review-verdict {
  font-weight: 600;
  margin-top: var(--space-1);
  display: flex;
  align-items: center;
  gap: 6px;
}
.t78-review.is-danger .t78-review-verdict { color: var(--danger);  }
.t78-review.is-bank   .t78-review-verdict { color: var(--success); }
.t78-review.is-run    .t78-review-verdict { color: var(--success); }
.t78-review.is-warn   .t78-review-verdict { color: var(--warn);    }

/* P&L hard right, the percentage under it — the same reading order the
   Activity rows use, so the two pages scan the same way. */
.t78-review-pl {
  flex: none;
  text-align: right;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.t78-review-pl.is-up   { color: var(--success); }
.t78-review-pl.is-down { color: var(--danger);  }
.t78-review-pl i {
  display: block;
  font-style: normal;
  font-weight: 400;
  font-size: var(--fs-xs);
  color: var(--dim);
}

/* The one line on the card allowed to be a sentence: why this verdict. */
.t78-review-why {
  margin: var(--space-3) 0 0;
  color: var(--text);
}

/* The AI's read, collapsed. It is context for the verdict, not the verdict —
   and on a name you already hold it is an opinion, never a buy. */
.t78-review-research {
  margin-top: var(--space-3);
  border-top: 1px solid var(--border);
  padding-top: var(--space-3);
}
.t78-review-research > summary {
  cursor: pointer;
  color: var(--dim);
  font-size: var(--fs-sm);
}
.t78-review-research > summary:hover { color: var(--text); }
.t78-review-research p { margin: var(--space-2) 0 0; }
.t78-review-headline   { font-weight: 600; }

/* ── The review as a WORKLIST ───────────────────────────────────────────────
   The counter above the cards, the "as of" stamp beside the button, and what a
   card looks like once it has been dealt with. The point of all three: you can
   see how much of the list is left, how old the figures are, and which ones you
   have already handled — without the list re-rendering underneath you. */
.t78-review-tools {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-3);
}

.t78-review-count {
  color: var(--dim);
  font-size: var(--fs-sm);
  margin: 0 0 var(--space-3);
}
.t78-review-count b { color: var(--text); font-variant-numeric: tabular-nums; }

/* Nothing left to work: the line stops being a count and reads as a result. */
.t78-review-count.is-clear    { color: var(--success); }
.t78-review-count.is-clear b  { color: var(--success); }

/* Dealt with. Green edge, quieted body — visibly done rather than gone, so the
   card is its own confirmation that the change landed. */
.t78-review.is-done {
  border-left-color: var(--success);
  opacity: .82;
}
.t78-review.is-done .t78-review-verdict { color: var(--success); }

/* A handled card, on its way off the list. It has already said what it became;
   this is the beat between reading that and the list being only what is left.
   Height and margin collapse too, so the cards below move up rather than a hole
   opening where this one was. */
.t78-review.is-leaving {
  opacity: 0;
  transform: translateY(-6px);
  max-height: 0;
  margin-bottom: 0;
  padding-top: 0;
  padding-bottom: 0;
  overflow: hidden;
  transition: opacity .28s ease, transform .28s ease,
              max-height .34s ease, margin .34s ease, padding .34s ease;
}

/* Motion is decoration here — the card still goes, it just goes at once. */
@media (prefers-reduced-motion: reduce) {
  .t78-review.is-leaving { transition: none; }
}

/* The end of the worklist. 🔴 The kit is pinned at v0.1.1, whose components can
   out-specify a bare [hidden], so this one says so itself. */
.t78-review-allhandled[hidden] { display: none; }

/* ── Scorecard on Performance (brief 44) ─────────────────────────────────────
   Layout and opacity only. The tables are themed by the kit's Tabulator adapter
   and the cells by kit text classes; no color is set here. */
.t78-sc-h {
  font: 600 var(--fs-sm)/1.4 var(--font);
  margin: var(--space-5) 0 var(--space-2);
}
.t78-sc-table { margin-top: var(--space-2); }

/* A note beside a figure in a cell ("too few to judge", "n=12"). A margin, not a space: the
   kit's Tabulator adapter lays cells out as flex, which drops whitespace between spans. */
.t78-sc-aside { margin-left: var(--space-1); }

/* "Too few to judge": the whole row greyed, the numbers still readable. */
.t78-sc-thin .tabulator-cell { opacity: .55; }

/* The funnel: one strip that wraps on a phone rather than scrolling. */
.t78-sc-funnel {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2) var(--space-3);
  margin-top: var(--space-2);
}
.t78-sc-stage { white-space: nowrap; }

.t78-sc-more > summary {
  cursor: pointer;
  font: 600 var(--fs-sm)/1.6 var(--font);
}
