@import url('tokens.css');

/* ============================================================================
   RESET
   ========================================================================== */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; padding: 0; }

html { -webkit-text-size-adjust: 100%; }

body {
  min-height: 100vh;
  background: var(--bg-0);
  color: var(--txt-1);
  font-family: var(--font-ui);
  font-size: var(--fs-base);
  line-height: var(--lh-base);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

img, svg, canvas { display: block; max-width: 100%; }
/* The hidden attribute must win over any component's own display rule. Without this,
   `.field { display: block }` silently re-shows an element the JS just hid. */
[hidden] { display: none !important; }
button, input, select, textarea { font: inherit; color: inherit; }
a { color: inherit; text-decoration: none; }
a[href]:not(.btn):not(.nav-item):not(.entry-card):not(.news-item):not(.feed-row):hover { color: var(--accent-hi); }
table { border-collapse: collapse; width: 100%; }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

::selection { background: var(--accent-soft); color: var(--txt-1); }

::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: var(--bg-0); }
::-webkit-scrollbar-thumb {
  background: var(--line-2);
  border-radius: var(--r-full);
  border: 3px solid var(--bg-0);
}
::-webkit-scrollbar-thumb:hover { background: var(--line-3); }

/* ============================================================================
   CANDLE MOTIF
   The app's visual signature. A repeating candlestick figure — body plus wick,
   one green, one red — rendered as a tiling SVG so it costs no requests. Used at
   low opacity behind headers and empty states. It is texture, not decoration for
   its own sake: it makes the surface read as a chart without competing with the
   real data drawn on top of it.
   ========================================================================== */
.candle-field {
  position: relative;
  isolation: isolate;
}
.candle-field::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  opacity: 0.5;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='80' viewBox='0 0 120 80'%3E%3Cg stroke-width='1.5' fill='none'%3E%3Cg stroke='%232fbf71'%3E%3Cpath d='M12 14v52'/%3E%3Crect x='7' y='26' width='10' height='28' fill='%232fbf71' fill-opacity='.16'/%3E%3C/g%3E%3Cg stroke='%23e5484d'%3E%3Cpath d='M42 22v46'/%3E%3Crect x='37' y='34' width='10' height='22' fill='%23e5484d' fill-opacity='.16'/%3E%3C/g%3E%3Cg stroke='%232fbf71'%3E%3Cpath d='M72 8v58'/%3E%3Crect x='67' y='18' width='10' height='34' fill='%232fbf71' fill-opacity='.16'/%3E%3C/g%3E%3Cg stroke='%23e5484d'%3E%3Cpath d='M102 30v42'/%3E%3Crect x='97' y='40' width='10' height='20' fill='%23e5484d' fill-opacity='.16'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
  background-size: 120px 80px;
  -webkit-mask-image: linear-gradient(180deg, rgba(0,0,0,.5), transparent 85%);
  mask-image: linear-gradient(180deg, rgba(0,0,0,.5), transparent 85%);
  pointer-events: none;
}

/* Ticker tape rule — a horizontal divider that reads as market data. */
.tape-rule {
  height: 3px;
  border: 0;
  background-image: repeating-linear-gradient(
    90deg,
    var(--line-2) 0 14px,
    transparent 14px 20px
  );
  opacity: 0.7;
}

/* ============================================================================
   APP SHELL
   ========================================================================== */
.app {
  display: grid;
  grid-template-columns: var(--nav-w) 1fr;
  min-height: 100vh;
}

.sidebar {
  position: sticky;
  top: 0;
  align-self: start;
  height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--bg-1);
  border-right: 1px solid var(--line-1);
  overflow-y: auto;
}

.brand {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-5) var(--sp-5) var(--sp-4);
  border-bottom: 1px solid var(--line-1);
}
.brand-mark {
  flex: none;
  width: 34px;
  height: 34px;
  border-radius: var(--r-md);
  background: linear-gradient(150deg, var(--bg-3), var(--bg-2));
  border: 1px solid var(--line-2);
  display: grid;
  place-items: center;
}
.brand-name {
  font-size: var(--fs-md);
  font-weight: var(--fw-semi);
  letter-spacing: 0.01em;
  line-height: 1.2;
}
.brand-sub {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  color: var(--txt-3);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wider);
}

.nav { padding: var(--sp-4) var(--sp-3); flex: 1; }
.nav-group + .nav-group { margin-top: var(--sp-5); }
.nav-label {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wider);
  color: var(--txt-4);
  padding: 0 var(--sp-3) var(--sp-2);
}

.nav-item {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-md);
  color: var(--txt-2);
  font-size: var(--fs-md);
  font-weight: var(--fw-med);
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
  position: relative;
}
.nav-item + .nav-item { margin-top: 2px; }
.nav-item:hover { background: var(--bg-2); color: var(--txt-1); }
.nav-item.is-active { background: var(--accent-soft); color: var(--accent-hi); }
/* The active marker is an amber candle body sitting in the gutter. */
.nav-item.is-active::before {
  content: '';
  position: absolute;
  left: 0;
  top: 50%;
  transform: translate(-9px, -50%);
  width: 3px;
  height: 18px;
  border-radius: var(--r-full);
  background: var(--accent);
}
.nav-item svg { flex: none; width: 16px; height: 16px; opacity: 0.85; }
.nav-item.is-active svg { opacity: 1; color: var(--accent-hi); }

.nav-badge {
  margin-left: auto;
  min-width: 19px;
  height: 19px;
  padding: 0 6px;
  border-radius: var(--r-full);
  background: var(--loss);
  color: #fff;
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-bold);
  display: grid;
  place-items: center;
}
.nav-badge:empty, .nav-badge[hidden] { display: none; }

.sidebar-foot {
  padding: var(--sp-4);
  border-top: 1px solid var(--line-1);
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  color: var(--txt-4);
  line-height: 1.7;
}
.conn-dot {
  display: inline-block;
  width: 6px; height: 6px;
  border-radius: var(--r-full);
  background: var(--txt-4);
  margin-right: 6px;
  vertical-align: middle;
}
.conn-dot.is-live { background: var(--gain); box-shadow: 0 0 8px var(--gain); }

/* An import is done, not live. It confirms rather than pulses: a tick in the same
   green, with none of the glow that says "this is streaming right now". */
.conn-dot.is-done {
  width: 12px; height: 12px;
  background: none;
  box-shadow: none;
  color: var(--gain);
}
.conn-dot.is-done svg {
  width: 12px; height: 12px;
  display: block;
  stroke-width: 2.6;
}

.main { min-width: 0; display: flex; flex-direction: column; }

.topbar {
  position: sticky;
  top: 0;
  z-index: 20;
  height: var(--hdr-h);
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  padding: 0 var(--sp-6);
  /* Near-opaque on purpose. At 0.86 the chart labels and table rows scrolling
     underneath ghosted through the header and read as overlapping text. The blur
     keeps the depth; the opacity keeps it legible. */
  background: rgba(13, 18, 25, 0.97);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--line-1);
}
.topbar h1 {
  font-size: var(--fs-lg);
  font-weight: var(--fw-semi);
  letter-spacing: -0.01em;
}
.topbar-spacer { flex: 1; }

.page {
  flex: 1;
  padding: var(--sp-6);
  max-width: var(--max-w);
  width: 100%;
}
.page > * + * { margin-top: var(--sp-6); }

/* ============================================================================
   PRIMITIVES
   ========================================================================== */
.eyebrow {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wider);
  color: var(--txt-3);
}

.card {
  position: relative;
  background: var(--bg-1);
  border: 1px solid var(--border-1);
  border-radius: var(--r-xl);
  box-shadow: var(--sh-1), var(--sh-2);
  isolation: isolate;
  overflow: hidden;
}
.card-head {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5);
  border-bottom: 1px solid var(--line-1);
}
.card-head h2, .card-head h3 {
  font-size: var(--fs-md);
  font-weight: var(--fw-semi);
  letter-spacing: 0.01em;
}
.card-head .spacer { flex: 1; }
.card-body { padding: var(--sp-5); }
.card-body.flush { padding: 0; }

.grid { display: grid; gap: var(--sp-4); }
.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.grid-2-1 { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
.grid-1-2 { grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); }

.row { display: flex; align-items: center; gap: var(--sp-3); }
.row.wrap { flex-wrap: wrap; }
.spacer { flex: 1; }

/* -- Numbers ------------------------------------------------------------- */
.num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
}
.gain { color: var(--gain); }
.loss { color: var(--loss); }
.flat { color: var(--flat); }
.muted { color: var(--txt-3); }
.dim { color: var(--txt-2); }

/* -- Stat tile ------------------------------------------------------------ */
.stat {
  background: var(--bg-1);
  border: 1px solid var(--line-2);
  border-radius: var(--r-lg);
  padding: var(--sp-4) var(--sp-5);
  position: relative;
  overflow: hidden;
}
/* A hairline of the tile's own semantic colour along the top edge — the same idea
   as a candle body, compressed to a single rule. */
.stat::after {
  content: '';
  position: absolute;
  inset: 0 0 auto 0;
  height: 2px;
  background: var(--line-2);
}
.stat.is-gain::after { background: var(--gain); }
.stat.is-loss::after { background: var(--loss); }
.stat.is-accent::after { background: var(--accent); }

.stat-label {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--txt-3);
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}
.stat-value {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-2xl);
  font-weight: var(--fw-semi);
  line-height: var(--lh-tight);
  letter-spacing: -0.02em;
  margin-top: var(--sp-2);
}
.stat-value.sm { font-size: var(--fs-xl); }
.stat-meta {
  font-size: var(--fs-xs);
  color: var(--txt-3);
  margin-top: var(--sp-1);
}

/* -- Buttons -------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  height: 36px;
  padding: 0 var(--sp-4);
  border: 1px solid var(--border-2);
  border-radius: var(--r-md);
  background: var(--bg-2);
  color: var(--txt-1);
  font-size: var(--fs-md);
  font-weight: var(--fw-med);
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--dur-fast) var(--ease),
              border-color var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease);
}
.btn:hover:not(:disabled) { background: var(--bg-3); border-color: var(--line-3); }
.btn:disabled { opacity: 0.4; cursor: not-allowed; box-shadow: none; transform: none; }
.btn svg { width: 15px; height: 15px; }

/* The primary action is lit at rest, not only on hover — on a dark ground a flat
   fill reads as just another surface. Hover steps to the lighter accent. */
.btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
  font-weight: var(--fw-semi);
  box-shadow: 0 0 16px var(--accent-glow);
}
.btn-primary:hover:not(:disabled) {
  background: var(--accent-hi);
  border-color: var(--accent-hi);
}
.btn-primary:hover:not(:disabled) { background: var(--accent-hi); border-color: var(--accent-hi); }

.btn-ghost { background: transparent; border-color: transparent; color: var(--txt-2); }
.btn-ghost:hover:not(:disabled) { background: var(--bg-2); color: var(--txt-1); }

.btn-danger { color: var(--loss); border-color: var(--loss-line); background: var(--loss-soft); }
.btn-danger:hover:not(:disabled) { background: rgba(229, 72, 77, 0.2); }

.btn-sm { height: 28px; padding: 0 var(--sp-3); font-size: var(--fs-sm); }
.btn-icon { width: 36px; padding: 0; }
.btn-icon.btn-sm { width: 28px; height: 28px; }

/* -- Form controls -------------------------------------------------------- */
.field { display: block; }
.field + .field { margin-top: var(--sp-4); }
.field-label {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--txt-3);
  margin-bottom: var(--sp-2);
}
.field-hint { font-size: var(--fs-xs); color: var(--txt-3); margin-top: var(--sp-2); }

.input, .select, .textarea {
  width: 100%;
  padding: 8px var(--sp-3);
  background: var(--bg-inset);
  border: 1px solid var(--line-2);
  border-radius: var(--r-md);
  color: var(--txt-1);
  font-size: var(--fs-md);
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.input:hover, .select:hover, .textarea:hover { border-color: var(--line-3); }
.input:focus, .select:focus, .textarea:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--bg-1);
  /* A soft ring rather than a hard outline — reads as focus without the jump in
     visual weight that a 2px outline causes on a dense form. */
  box-shadow: 0 0 0 3px var(--accent-soft);
}
.input::placeholder, .textarea::placeholder { color: var(--txt-4); }
.input.num, .input[type="number"] { font-family: var(--font-mono); }

.textarea { resize: vertical; min-height: 92px; line-height: var(--lh-base); }
.textarea.tall { min-height: 132px; }

.select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='M2.5 4.5L6 8l3.5-3.5' stroke='%235f6d7e' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--sp-3) center;
  padding-right: var(--sp-8);
}

/* -- Chip / multi-select -------------------------------------------------- */
.chips { display: flex; flex-wrap: wrap; gap: var(--sp-2); }

.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px var(--sp-3);
  border: 1px solid var(--line-2);
  border-radius: var(--r-full);
  background: var(--bg-2);
  color: var(--txt-2);
  font-size: var(--fs-sm);
  cursor: pointer;
  user-select: none;
  transition: all var(--dur-fast) var(--ease);
}
.chip:hover { border-color: var(--line-3); color: var(--txt-1); }
.chip.is-on {
  background: var(--accent-soft);
  border-color: var(--accent-line);
  color: var(--accent-hi);
}
.chip.is-on.tone-bad { background: var(--loss-soft); border-color: var(--loss-line); color: var(--loss-hi); }
.chip.is-on.tone-good { background: var(--gain-soft); border-color: var(--gain-line); color: var(--gain-hi); }
.chip input { position: absolute; opacity: 0; pointer-events: none; }

/* Static, non-interactive label */
.tag {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 22px;
  padding: 0 var(--sp-2);
  border-radius: var(--r-sm);
  background: var(--bg-2);
  border: 1px solid var(--border-1);
  color: var(--txt-2);
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  white-space: nowrap;
}
.tag.accent { background: var(--accent-soft); border-color: var(--accent-line); color: var(--accent-hi); }
.tag.gain { background: var(--gain-soft); border-color: var(--gain-line); color: var(--gain-hi); }
.tag.loss { background: var(--loss-soft); border-color: var(--loss-line); color: var(--loss-hi); }

/* Direction badge — a miniature candle. Buy sits above its wick, sell below. */
.dir {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}
.dir::before {
  content: '';
  width: 3px;
  height: 13px;
  border-radius: 1px;
}
.dir.buy { color: var(--gain); }
.dir.buy::before { background: var(--gain); box-shadow: 0 -3px 0 -1px var(--gain), 0 3px 0 -1px var(--gain); }
.dir.sell { color: var(--loss); }
.dir.sell::before { background: var(--loss); box-shadow: 0 -3px 0 -1px var(--loss), 0 3px 0 -1px var(--loss); }

/* Grade pill */
/* A bare letter grade: a 22px square, which is all it needs. */
.grade {
  display: inline-grid;
  place-items: center;
  width: 22px; height: 22px;
  border-radius: var(--r-sm);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  border: 1px solid;
}

/* A grade that says whose it is needs room for the words. The square above was
   built for one character, so putting "Your grade" inside it crushed the label
   against the letter. Carrying an owner label turns the pill into a flex chip
   that sizes to its own content — the colours, border and radius are unchanged,
   so it still reads as the same object, just wide enough to be legible. */
.grade:has(.grade-who) {
  display: inline-flex;
  align-items: center;
  gap: 0;
  width: auto; height: auto;
  min-height: 22px;
  padding: 2px var(--sp-2);
  white-space: nowrap;
}
/* Older engines without :has still get a readable pill rather than a crushed one. */
@supports not selector(:has(*)) {
  .grade { width: auto; height: auto; min-height: 22px; padding: 2px var(--sp-2); }
}
.grade-A { color: var(--gain-hi); border-color: var(--gain-line); background: var(--gain-soft); }
.grade-B { color: var(--gain); border-color: var(--gain-line); background: var(--gain-soft); }
.grade-C { color: var(--accent); border-color: var(--accent-line); background: var(--accent-soft); }
.grade-D { color: var(--loss-hi); border-color: var(--loss-line); background: var(--loss-soft); }
.grade-F { color: var(--loss); border-color: var(--loss-line); background: var(--loss-soft); }

/* Impact dot — Forex Factory's folder colours */
.impact {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--font-mono); font-size: var(--fs-2xs);
  text-transform: uppercase; letter-spacing: var(--tracking-wide);
}
.impact::before {
  content: ''; width: 8px; height: 10px; border-radius: 2px; flex: none;
}
.impact.high { color: var(--impact-high); }
.impact.high::before { background: var(--impact-high); }
.impact.medium { color: var(--warn); }
.impact.medium::before { background: var(--impact-med); }
.impact.low { color: var(--impact-low); }
.impact.low::before { background: var(--impact-low); }
.impact.holiday { color: var(--impact-holiday); }
.impact.holiday::before { background: var(--impact-holiday); }

/* ============================================================================
   TABLE
   ========================================================================== */
.table-wrap { overflow-x: auto; }

.table { font-size: var(--fs-md); }
.table thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--bg-2);
  padding: var(--sp-3) var(--sp-4);
  text-align: left;
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-semi);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--txt-3);
  white-space: nowrap;
  border-bottom: 1px solid var(--line-2);
}
.table tbody td {
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--line-1);
  white-space: nowrap;
}
.table tbody tr { transition: background var(--dur-fast) var(--ease); }
.table tbody tr:hover { background: var(--bg-2); }
.table tbody tr.clickable { cursor: pointer; }
.table .r { text-align: right; }
.table .c { text-align: center; }
.table td.wrap { white-space: normal; min-width: 220px; }

/* ============================================================================
   CHARTS
   ========================================================================== */
.chart-wrap { position: relative; width: 100%; }
.chart-wrap canvas { width: 100%; display: block; }

.legend {
  display: flex; flex-wrap: wrap; gap: var(--sp-4);
  font-size: var(--fs-xs); color: var(--txt-2);
}
.legend-item { display: inline-flex; align-items: center; gap: 6px; }
.legend-swatch { width: 9px; height: 9px; border-radius: 2px; flex: none; }

.chart-tip {
  position: absolute;
  z-index: 40;
  min-width: 130px;
  padding: var(--sp-2) var(--sp-3);
  background: var(--bg-3);
  border: 1px solid var(--line-3);
  border-radius: var(--r-md);
  box-shadow: var(--sh-2);
  font-size: var(--fs-xs);
  pointer-events: none;
  opacity: 0;
  transform: translate(-50%, -100%);
  transition: opacity var(--dur-fast) var(--ease);
}
.chart-tip.is-on { opacity: 1; }
.chart-tip .tip-title {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--txt-3);
  margin-bottom: 3px;
}
.chart-tip .tip-row { display: flex; justify-content: space-between; gap: var(--sp-4); }
.chart-tip .tip-row span:last-child { font-family: var(--font-mono); }

/* ============================================================================
   INSIGHTS
   ========================================================================== */
.insight {
  display: flex;
  gap: var(--sp-4);
  padding: var(--sp-4);
  border-radius: var(--r-lg);
  background: var(--bg-2);
  border: 1px solid var(--line-1);
}
.insight + .insight { margin-top: var(--sp-3); }
.insight.good { border-color: var(--gain-line); }
.insight.bad { border-color: var(--loss-line); }
.insight.neutral { border-color: var(--accent-line); }
.insight-title { font-size: var(--fs-md); font-weight: var(--fw-semi); margin-bottom: 3px; }
.insight-body { font-size: var(--fs-sm); color: var(--txt-2); line-height: var(--lh-snug); }
/* The tone lives in a filled chip rather than an edge rail — one signal, one place. */
.insight-icon {
  flex: none;
  width: 30px; height: 30px;
  border-radius: var(--r-md);
  display: grid; place-items: center;
  border: 1px solid transparent;
}
.insight-icon svg { width: 15px; height: 15px; }
.insight.good .insight-icon    { color: var(--gain-hi); background: var(--gain-soft); border-color: var(--gain-line); }
.insight.bad .insight-icon     { color: var(--loss-hi); background: var(--loss-soft); border-color: var(--loss-line); }
.insight.neutral .insight-icon { color: var(--accent-hi); background: var(--accent-soft); border-color: var(--accent-line); }

/* ============================================================================
   JOURNAL
   ========================================================================== */
.entry-card {
  display: block;
  background: var(--bg-1);
  border: 1px solid var(--line-2);
  border-radius: var(--r-lg);
  padding: var(--sp-4) var(--sp-5);
  transition: border-color var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
  position: relative;
  overflow: hidden;
}
.entry-card:hover { border-color: var(--line-3); transform: translateY(-1px); }
/* Left edge encodes the outcome, so a losing week is visible before reading a word. */
.entry-card::before {
  content: '';
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 3px;
  background: var(--line-2);
}
.entry-card.is-win::before { background: var(--gain); }
.entry-card.is-loss::before { background: var(--loss); }
.entry-card.is-note::before { background: var(--accent); }

.entry-head { display: flex; align-items: baseline; gap: var(--sp-3); flex-wrap: wrap; }
.entry-title { font-size: var(--fs-lg); font-weight: var(--fw-semi); letter-spacing: -0.01em; }
.entry-date {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--txt-3);
}
.entry-excerpt {
  margin-top: var(--sp-2);
  font-size: var(--fs-md);
  color: var(--txt-2);
  /* A unitless line-height and a max-height of exactly two of them. Clamping with
     overflow:hidden against an arbitrary box height slices the second line through
     the middle of the glyphs, which reads as a hairline drawn across the text. */
  line-height: 1.45;
  max-height: 2.9em;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.entry-meta { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-3); }

.prose { font-size: var(--fs-base); line-height: var(--lh-base); color: var(--txt-1); }
.prose p + p { margin-top: var(--sp-3); }
.prose-block + .prose-block { margin-top: var(--sp-5); }
.prose-block h4 {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wider);
  color: var(--txt-3);
  margin-bottom: var(--sp-2);
}
.prose-block .body { white-space: pre-wrap; color: var(--txt-1); }
.prose-block .body:empty::after {
  content: 'Not written yet.';
  color: var(--txt-4);
  font-style: italic;
}

/* Definition list used on the entry detail page */
.dl { display: grid; grid-template-columns: auto 1fr; gap: var(--sp-2) var(--sp-4); font-size: var(--fs-md); }
.dl dt {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--txt-3);
  align-self: center;
}
.dl dd { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }

/* Confidence / rating selector */
.rating { display: inline-flex; gap: var(--sp-1); }
.rating button {
  width: 32px; height: 30px;
  border: 1px solid var(--line-2);
  border-radius: var(--r-sm);
  background: var(--bg-2);
  color: var(--txt-3);
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  cursor: pointer;
  transition: all var(--dur-fast) var(--ease);
}
.rating button:hover { border-color: var(--line-3); color: var(--txt-1); }
.rating button.is-on { background: var(--accent-soft); border-color: var(--accent-line); color: var(--accent-hi); }

/* Switch */
.switch { display: inline-flex; align-items: center; gap: var(--sp-3); cursor: pointer; }
.switch input { position: absolute; opacity: 0; pointer-events: none; }
.switch-track {
  width: 38px; height: 22px;
  border-radius: var(--r-full);
  background: var(--bg-3);
  border: 1px solid var(--line-2);
  position: relative;
  transition: all var(--dur-base) var(--ease);
  flex: none;
}
.switch-track::after {
  content: '';
  position: absolute;
  top: 2px; left: 2px;
  width: 16px; height: 16px;
  border-radius: var(--r-full);
  background: var(--txt-3);
  transition: all var(--dur-base) var(--ease);
}
.switch input:checked + .switch-track { background: var(--gain-soft); border-color: var(--gain-line); }
.switch input:checked + .switch-track::after { transform: translateX(16px); background: var(--gain); }
.switch input:focus-visible + .switch-track { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ============================================================================
   NEWS + CALENDAR
   ========================================================================== */
.news-item {
  display: block;
  padding: var(--sp-4) var(--sp-5);
  border-bottom: 1px solid var(--line-1);
  transition: background var(--dur-fast) var(--ease);
}
.news-item:hover { background: var(--bg-2); }
.news-item:last-child { border-bottom: 0; }
.news-src {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--accent);
}
.news-time { font-family: var(--font-mono); font-size: var(--fs-2xs); color: var(--txt-4); }
.news-title { font-size: var(--fs-md); font-weight: var(--fw-med); margin-top: 3px; line-height: var(--lh-snug); }
.news-item:hover .news-title { color: var(--accent-hi); }
.news-sum {
  font-size: var(--fs-sm);
  color: var(--txt-3);
  margin-top: var(--sp-2);
  line-height: 1.45;
  max-height: 2.9em;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.cal-row {
  display: grid;
  grid-template-columns: 92px 52px 1fr auto;
  gap: var(--sp-3);
  align-items: center;
  padding: var(--sp-3) var(--sp-5);
  border-bottom: 1px solid var(--line-1);
}
.cal-row:last-child { border-bottom: 0; }
.cal-row.is-imminent { background: var(--loss-soft); }
.cal-row.is-past { opacity: 0.45; }
.cal-time { font-family: var(--font-mono); font-size: var(--fs-md); font-variant-numeric: tabular-nums; }
.cal-ccy {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  color: var(--txt-1);
  background: var(--bg-3);
  border: 1px solid var(--line-2);
  border-radius: var(--r-sm);
  padding: 2px 0;
  text-align: center;
}
.cal-title { font-size: var(--fs-md); }
.cal-figs { display: flex; gap: var(--sp-4); font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--txt-3); }

.day-head {
  padding: var(--sp-3) var(--sp-5);
  background: var(--bg-2);
  border-bottom: 1px solid var(--line-2);
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wider);
  color: var(--txt-2);
  position: sticky;
  top: 0;
  z-index: 1;
}

/* Countdown to the next red folder event */
.countdown {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-3xl);
  font-weight: var(--fw-bold);
  letter-spacing: -0.02em;
  line-height: 1;
}
.countdown.is-close { color: var(--loss); }

/* ============================================================================
   ALERTS
   ========================================================================== */
.bell { position: relative; }
.bell-dot {
  position: absolute;
  top: 3px; right: 3px;
  width: 7px; height: 7px;
  border-radius: var(--r-full);
  background: var(--loss);
  border: 1.5px solid var(--bg-1);
}

.drawer {
  position: fixed;
  top: 0; right: 0; bottom: 0;
  width: min(420px, 100vw);
  z-index: 60;
  background: var(--bg-1);
  border-left: 1px solid var(--line-2);
  box-shadow: var(--sh-3);
  display: flex;
  flex-direction: column;
  transform: translateX(100%);
  transition: transform var(--dur-slow) var(--ease);
}
.drawer.is-open { transform: none; }
.drawer-head {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5);
  border-bottom: 1px solid var(--line-1);
}
.drawer-body { flex: 1; overflow-y: auto; }
.scrim {
  position: fixed; inset: 0; z-index: 50;
  background: rgba(0, 0, 0, 0.6);
  opacity: 0; pointer-events: none;
  transition: opacity var(--dur-base) var(--ease);
}
.scrim.is-on { opacity: 1; pointer-events: auto; }

.alert-item {
  padding: var(--sp-4) var(--sp-5);
  border-bottom: 1px solid var(--line-1);
  border-left: 3px solid transparent;
}
.alert-item.is-unread { border-left-color: var(--loss); background: var(--bg-2); }
.alert-title { font-size: var(--fs-md); font-weight: var(--fw-semi); }
.alert-body {
  font-size: var(--fs-sm);
  color: var(--txt-2);
  margin-top: var(--sp-2);
  white-space: pre-wrap;
  line-height: var(--lh-snug);
  font-family: var(--font-mono);
}
.alert-time { font-family: var(--font-mono); font-size: var(--fs-2xs); color: var(--txt-4); margin-top: var(--sp-2); }

/* ============================================================================
   MISC
   ========================================================================== */
.empty {
  padding: var(--sp-12) var(--sp-6);
  text-align: center;
  color: var(--txt-3);
}
.empty-title { font-size: var(--fs-lg); font-weight: var(--fw-semi); color: var(--txt-2); margin-bottom: var(--sp-2); }
.empty-body { font-size: var(--fs-md); max-width: 48ch; margin: 0 auto var(--sp-5); line-height: var(--lh-snug); }

.toast-stack {
  position: fixed;
  bottom: var(--sp-5); right: var(--sp-5);
  z-index: 80;
  display: flex; flex-direction: column; gap: var(--sp-2);
  pointer-events: none;
}
.toast {
  padding: var(--sp-3) var(--sp-4);
  background: var(--bg-3);
  border: 1px solid var(--line-3);
  border-left: 3px solid var(--accent);
  border-radius: var(--r-md);
  box-shadow: var(--sh-2);
  font-size: var(--fs-md);
  max-width: 380px;
  animation: toast-in var(--dur-base) var(--ease);
}
.toast.ok { border-left-color: var(--gain); }
.toast.err { border-left-color: var(--loss); }
@keyframes toast-in {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: none; }
}

.skeleton {
  background: linear-gradient(90deg, var(--bg-2) 25%, var(--bg-3) 50%, var(--bg-2) 75%);
  background-size: 200% 100%;
  animation: shimmer 1.4s infinite;
  border-radius: var(--r-sm);
}
@keyframes shimmer { to { background-position: -200% 0; } }

.seg {
  display: inline-flex;
  background: var(--bg-2);
  border: 1px solid var(--line-2);
  border-radius: var(--r-md);
  padding: 2px;
  gap: 2px;
}
.seg button {
  padding: 4px var(--sp-3);
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--txt-3);
  font-size: var(--fs-sm);
  font-weight: var(--fw-med);
  cursor: pointer;
  transition: all var(--dur-fast) var(--ease);
}
.seg button:hover { color: var(--txt-1); }
.seg button.is-on { background: var(--bg-3); color: var(--accent); }

.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* ============================================================================
   RESPONSIVE
   ========================================================================== */
@media (max-width: 1180px) {
  .grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .grid-2-1, .grid-1-2, .grid-3 { grid-template-columns: minmax(0, 1fr); }
}

@media (max-width: 860px) {
  :root { --nav-w: 0px; }
  .app { grid-template-columns: 1fr; }
  .sidebar {
    position: fixed;
    left: 0; top: 0; bottom: 0;
    width: 232px;
    z-index: 70;
    transform: translateX(-100%);
    transition: transform var(--dur-slow) var(--ease);
  }
  .sidebar.is-open { transform: none; }
  .page { padding: var(--sp-4); }
  .grid-2, .grid-4 { grid-template-columns: minmax(0, 1fr); }
  .cal-row { grid-template-columns: 76px 44px 1fr; }
  .cal-figs { grid-column: 1 / -1; padding-left: 128px; }
}

@media (min-width: 861px) { .menu-btn { display: none; } }

@media print {
  .sidebar, .topbar, .toast-stack, .drawer, .scrim { display: none !important; }
  body { background: #fff; color: #000; }
  .app { grid-template-columns: 1fr; }
  .card { border-color: #ccc; break-inside: avoid; }
}

/* ============================================================================
   MOTION
   Added so the dashboard feels alive rather than printed. Everything here is
   entrance or feedback motion — nothing loops indefinitely except the ticker,
   and the whole layer collapses to nothing under prefers-reduced-motion via
   the global rule in tokens.css.
   ========================================================================== */

/* -- Staggered entrance ---------------------------------------------------- */
@keyframes rise-in {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}

.reveal {
  opacity: 0;
  animation: rise-in var(--dur-slow) var(--ease) forwards;
}
/* Index-based delay keeps the cascade in DOM order without per-element JS. */
.reveal:nth-child(1)  { animation-delay: 0ms; }
.reveal:nth-child(2)  { animation-delay: 45ms; }
.reveal:nth-child(3)  { animation-delay: 90ms; }
.reveal:nth-child(4)  { animation-delay: 135ms; }
.reveal:nth-child(5)  { animation-delay: 180ms; }
.reveal:nth-child(6)  { animation-delay: 225ms; }
.reveal:nth-child(7)  { animation-delay: 270ms; }
.reveal:nth-child(8)  { animation-delay: 315ms; }
.reveal:nth-child(n+9) { animation-delay: 360ms; }

@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; animation: none; }
}

/* -- Card feedback --------------------------------------------------------- */
.card {
  transition: border-color var(--dur-base) var(--ease),
              transform var(--dur-base) var(--ease),
              box-shadow var(--dur-base) var(--ease);
}
.card.interactive:hover {
  border-color: var(--border-2);
  transform: translateY(-2px);
  box-shadow: var(--sh-lift);
}

/* Stat tiles lift and light their accent rule on hover. */
.stat {
  transition: transform var(--dur-base) var(--ease),
              border-color var(--dur-base) var(--ease),
              box-shadow var(--dur-base) var(--ease);
}
.stat:hover {
  transform: translateY(-2px);
  border-color: var(--border-2);
  box-shadow: var(--sh-lift);
}
.stat::after { transform-origin: top; transition: transform var(--dur-base) var(--ease); }
.stat:hover::after { transform: scaleY(1.5); }

/* The number itself gets tabular-locked width so counting up doesn't reflow. */
.stat-value { font-variant-numeric: tabular-nums; }

/* -- Pulse for live/imminent state ----------------------------------------- */
@keyframes pulse-ring {
  0%   { box-shadow: 0 0 0 0 rgba(229, 72, 77, 0.45); }
  70%  { box-shadow: 0 0 0 10px rgba(229, 72, 77, 0); }
  100% { box-shadow: 0 0 0 0 rgba(229, 72, 77, 0); }
}
.pulse { animation: pulse-ring 2.2s infinite; border-radius: var(--r-full); }

@keyframes breathe {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.45; }
}
.live-dot {
  display: inline-block;
  width: 7px; height: 7px;
  border-radius: var(--r-full);
  background: var(--gain);
  animation: breathe 2s var(--ease) infinite;
}
.countdown.is-close { animation: breathe 1.6s var(--ease) infinite; }

/* -- Instrument ticker tape ------------------------------------------------ */
/* A marquee of the instruments actually traded, showing real P/L from the
   journal rather than decorative fake prices. */
.ticker {
  position: relative;
  overflow: hidden;
  background: var(--bg-1);
  border: 1px solid var(--line-2);
  border-radius: var(--r-lg);
  height: 44px;
  display: flex;
  align-items: center;
}
/* The badge sits in its own column rather than floating over the track. Overlaying it
   meant symbols slid underneath and became unreadable as they passed. */
.ticker { display: flex; align-items: stretch; gap: 0; }
.ticker-status {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0 var(--sp-4);
  border-right: 1px solid var(--border-2);
  background: var(--bg-2);
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  white-space: nowrap;
  z-index: 2;
}
.ticker-status.is-live { color: var(--gain-hi); }
.ticker-status.is-degraded { color: var(--warn-hi); }

.ticker-viewport {
  position: relative;
  flex: 1;
  min-width: 0;
  overflow: hidden;
  display: flex;
  align-items: center;
}
/* Fades belong to the scrolling viewport, not the whole bar, so they never sit on
   top of the badge. */
.ticker-viewport::before,
.ticker-viewport::after {
  content: '';
  position: absolute;
  top: 0; bottom: 0;
  width: 40px;
  z-index: 2;
  pointer-events: none;
}
.ticker-viewport::before { left: 0;  background: linear-gradient(90deg, var(--bg-1), transparent); }
.ticker-viewport::after  { right: 0; background: linear-gradient(270deg, var(--bg-1), transparent); }

.ticker-track {
  display: flex;
  gap: var(--sp-8);
  padding-left: var(--sp-6);
  white-space: nowrap;
  animation: ticker-scroll 42s linear infinite;
  will-change: transform;
}
.ticker:hover .ticker-track { animation-play-state: paused; }

@keyframes ticker-scroll {
  from { transform: translateX(0); }
  /* -50% because the track renders its contents twice for a seamless loop. */
  to   { transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce) {
  .ticker-track { animation: none; }
  .ticker { overflow-x: auto; }
}

.tick {
  display: inline-flex;
  align-items: baseline;
  gap: var(--sp-2);
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
}
.tick-sym {
  font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-wide);
  color: var(--txt-1);
}
.tick-val { font-variant-numeric: tabular-nums; }
.tick-meta { font-size: var(--fs-2xs); color: var(--txt-4); }

/* -- Clickable chart affordance -------------------------------------------- */
.chart-wrap.clickable canvas { cursor: pointer; }
.chart-hint {
  font-size: var(--fs-xs);
  color: var(--txt-4);
  margin-top: var(--sp-2);
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease);
}
.chart-wrap:hover + .chart-hint,
.card:hover .chart-hint { opacity: 1; }

/* -- Buttons and rows ------------------------------------------------------ */
.btn { transition: background var(--dur-fast) var(--ease),
                   border-color var(--dur-fast) var(--ease),
                   color var(--dur-fast) var(--ease),
                   transform var(--dur-fast) var(--ease); }
.btn:active:not(:disabled) { transform: translateY(1px); }

.btn-primary:hover:not(:disabled) { box-shadow: 0 0 20px var(--accent-glow); }

.table tbody tr.clickable { transition: background var(--dur-fast) var(--ease); }
.table tbody tr.clickable:hover td:first-child { box-shadow: inset 3px 0 0 var(--accent); }

.entry-card { transition: border-color var(--dur-base) var(--ease),
                          transform var(--dur-base) var(--ease),
                          box-shadow var(--dur-base) var(--ease); }
.entry-card:hover { transform: translateY(-2px); box-shadow: var(--sh-lift); }
.entry-card::before { transform-origin: left; transition: transform var(--dur-base) var(--ease); }
.entry-card:hover::before { transform: scaleX(1.7); }

.news-item { transition: background var(--dur-fast) var(--ease),
                         box-shadow var(--dur-fast) var(--ease); }
.news-item:hover { box-shadow: inset 3px 0 0 var(--accent); }

.chip, .tag { transition: all var(--dur-fast) var(--ease); }
.chip:active { transform: translateY(1px); }

/* -- Spin for in-flight refresh -------------------------------------------- */
@keyframes spin { to { transform: rotate(360deg); } }
.btn.is-busy svg { animation: spin 900ms linear infinite; }

/* -- Segmented control slide ----------------------------------------------- */
.seg button { transition: background var(--dur-base) var(--ease),
                          color var(--dur-fast) var(--ease); }

/* -- Drawer + toast easing ------------------------------------------------- */
.drawer { transition: transform var(--dur-slow) cubic-bezier(0.32, 0.72, 0, 1); }

/* ============================================================================
   HERO / COMMAND PANEL
   The headline figure, timeframe pills and market status. Modelled on the way a
   portfolio dashboard leads with one number and lets you rescale the window
   beneath it, rather than opening with a grid of equally-weighted tiles.
   ========================================================================== */
.hero {
  position: relative;
  overflow: hidden;
  background:
    radial-gradient(120% 140% at 0% 0%, rgba(74, 158, 255, 0.10), transparent 58%),
    var(--bg-1);
  border: 1px solid var(--line-2);
  border-radius: var(--r-xl);
}
.hero-top {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-5);
  flex-wrap: wrap;
  padding: var(--sp-5) var(--sp-6) 0;
}
.hero-figure { min-width: 0; }
.hero-value {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-4xl);
  font-weight: var(--fw-bold);
  letter-spacing: -0.03em;
  line-height: 1;
  margin-top: var(--sp-2);
}
.hero-delta {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  margin-top: var(--sp-3);
  font-family: var(--font-mono);
  font-size: var(--fs-md);
}
.hero-delta .sep { color: var(--txt-4); }

/* Timeframe pills — 1W / 1M / 3M / 1Y / ALL */
.pills {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  background: var(--bg-2);
  border: 1px solid var(--line-2);
  border-radius: var(--r-full);
}
.pills button {
  padding: 5px var(--sp-4);
  border: 0;
  border-radius: var(--r-full);
  background: transparent;
  color: var(--txt-3);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semi);
  letter-spacing: var(--tracking-wide);
  cursor: pointer;
  transition: background var(--dur-base) var(--ease), color var(--dur-fast) var(--ease);
}
.pills button:hover { color: var(--txt-1); }
.pills button.is-on {
  background: var(--accent-grad);
  color: var(--on-accent);
  box-shadow: 0 0 16px var(--accent-glow);
}

/* Market status — which FX sessions are open right now. */
.status-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 6px var(--sp-3);
  background: var(--bg-2);
  border: 1px solid var(--line-2);
  border-radius: var(--r-md);
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--txt-2);
}
.status-chip.is-open { border-color: var(--gain-line); color: var(--gain-hi); }
.status-chip.is-closed { color: var(--txt-3); }

.session-bar { display: flex; gap: var(--sp-2); flex-wrap: wrap; }
.session {
  flex: 1 1 0;
  min-width: 92px;
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-md);
  background: var(--bg-2);
  border: 1px solid var(--line-1);
  transition: all var(--dur-base) var(--ease);
}
.session.is-live {
  background: var(--gain-soft);
  border-color: var(--gain-line);
}
.session-name {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--txt-3);
}
.session.is-live .session-name { color: var(--gain-hi); }
.session-meta { font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--txt-4); margin-top: 2px; }

/* -- Stat tile with sparkline --------------------------------------------- */
.stat-spark { margin-top: var(--sp-3); height: 30px; }
.stat-row { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--sp-3); }

.delta {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-weight: var(--fw-semi);
}
.delta svg { width: 11px; height: 11px; }

/* -- Gauge ----------------------------------------------------------------- */
.gauge-wrap { position: relative; display: grid; place-items: center; }
.gauge-label {
  position: absolute;
  text-align: center;
  pointer-events: none;
}
.gauge-value {
  font-family: var(--font-mono);
  font-size: var(--fs-2xl);
  font-weight: var(--fw-bold);
  letter-spacing: -0.02em;
  line-height: 1;
}
.gauge-caption {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--txt-3);
  margin-top: var(--sp-1);
}

/* -- Live feed panel ------------------------------------------------------- */
.live-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px var(--sp-2);
  border-radius: var(--r-sm);
  background: var(--loss-soft);
  border: 1px solid var(--loss-line);
  color: var(--loss-hi);
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-wide);
}
.live-badge::before {
  content: '';
  width: 5px; height: 5px;
  border-radius: var(--r-full);
  background: var(--loss);
  animation: breathe 1.6s var(--ease) infinite;
}

.feed-row {
  display: grid;
  grid-template-columns: 52px 88px 1fr auto;
  gap: var(--sp-3);
  align-items: baseline;
  padding: var(--sp-3) var(--sp-5);
  border-bottom: 1px solid var(--line-1);
  transition: background var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
.feed-row:last-child { border-bottom: 0; }
.feed-row:hover { background: var(--bg-2); box-shadow: inset 3px 0 0 var(--accent); }
.feed-time { font-family: var(--font-mono); font-size: var(--fs-2xs); color: var(--txt-4); }
.feed-src {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--accent);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.feed-title {
  font-size: var(--fs-md);
  line-height: var(--lh-snug);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.feed-row:hover .feed-title { color: var(--accent-hi); }
.feed-tags { display: flex; gap: 4px; }

/* Instrument badge — marks a story as touching something actually traded. */
.instr {
  display: inline-flex;
  align-items: center;
  padding: 1px 6px;
  border-radius: var(--r-sm);
  background: var(--accent-soft);
  border: 1px solid var(--accent-line);
  color: var(--accent-hi);
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-bold);
  letter-spacing: 0.04em;
  white-space: nowrap;
}

/* -- Quote bar ------------------------------------------------------------- */
.quote-bar {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-5) var(--sp-6);
  border-radius: var(--r-xl);
  background:
    radial-gradient(100% 200% at 100% 50%, rgba(74, 158, 255, 0.08), transparent 60%),
    var(--bg-1);
  border: 1px solid var(--line-2);
}
.quote-mark {
  font-size: 40px;
  line-height: 1;
  color: var(--accent);
  opacity: 0.5;
  font-family: Georgia, serif;
}
.quote-text { font-size: var(--fs-lg); font-style: italic; color: var(--txt-1); }
.quote-attr {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wider);
  color: var(--txt-3);
  margin-top: var(--sp-2);
}

/* -- Glass panel ----------------------------------------------------------- */
.glass {
  background: rgba(19, 26, 36, 0.62);
  backdrop-filter: blur(16px) saturate(140%);
  -webkit-backdrop-filter: blur(16px) saturate(140%);
  border: 1px solid rgba(255, 255, 255, 0.06);
}

@media (max-width: 860px) {
  .hero-value { font-size: var(--fs-3xl); }
  .feed-row { grid-template-columns: 52px 1fr; }
  .feed-src, .feed-tags { display: none; }
}

/* ============================================================================
   DEPTH PASS
   The first build read as flat boxes on a flat ground. Reference trading dashboards
   get their richness from three things, all applied here: surfaces that catch light
   at the top edge, an accent that glows rather than just colours, and KPI tiles that
   carry a trend and a comparison instead of a lone number.
   ========================================================================== */

/* Panels sit on a subtly lit ground rather than pure flat black. */
body {
  background:
    radial-gradient(1300px 800px at 10% -10%, rgba(122, 184, 255, 0.16), transparent 62%),
    radial-gradient(1000px 700px at 95% 5%, rgba(122, 184, 255, 0.10), transparent 58%),
    radial-gradient(900px 900px at 50% 110%, rgba(74, 158, 255, 0.07), transparent 60%),
    var(--bg-0);
  background-attachment: fixed;
}

/* Every card gets a top-edge highlight plus a blue wash — the highlight says the
   surface is raised, the wash keeps it from reading as a grey box on a dark page. */
.card, .stat, .kpi, .hero, .quote-bar, .ticker, .mc-tile {
  position: relative;
  background-image:
    linear-gradient(180deg, rgba(255, 255, 255, 0.05), transparent 44%),
    radial-gradient(120% 90% at 0% 0%, var(--glow-blue), transparent 62%);
}
.card::after, .stat::before, .hero::after {
  content: '';
  position: absolute;
  inset: 0 0 auto 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.10), transparent);
  pointer-events: none;
}
.stat::before { z-index: 1; }

.card { border-radius: var(--r-xl); }
.card-head { padding: var(--sp-4) var(--sp-5) var(--sp-3); }

/* -- KPI tile with trend and comparison ----------------------------------- */
.kpi {
  position: relative;
  overflow: hidden;
  padding: var(--sp-4) var(--sp-5) 0;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.035), transparent 45%),
    var(--bg-1);
  border: 1px solid var(--line-2);
  border-radius: var(--r-xl);
  transition: transform var(--dur-base) var(--ease),
              border-color var(--dur-base) var(--ease),
              box-shadow var(--dur-base) var(--ease);
}
.kpi:hover {
  transform: translateY(-2px);
  border-color: var(--border-2);
  box-shadow: var(--sh-lift);
}
/* A coloured wash bleeding up from the sparkline, tinted by the metric's own tone. */
.kpi::after {
  content: '';
  position: absolute;
  inset: auto 0 0 0;
  height: 60%;
  pointer-events: none;
  background: linear-gradient(0deg, var(--kpi-wash, transparent), transparent);
}
.kpi.tone-gain   { --kpi-wash: rgba(47, 191, 113, 0.10); }
.kpi.tone-loss   { --kpi-wash: rgba(229, 72, 77, 0.10); }
.kpi.tone-accent { --kpi-wash: rgba(74, 158, 255, 0.10); }

.kpi-label {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--txt-3);
}
.kpi-value {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-2xl);
  font-weight: var(--fw-semi);
  letter-spacing: -0.02em;
  line-height: 1.1;
  margin-top: var(--sp-2);
}
.kpi-foot {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex-wrap: wrap;
  margin-top: var(--sp-2);
  font-size: var(--fs-xs);
  color: var(--txt-3);
}
.kpi-spark { margin: var(--sp-3) calc(var(--sp-5) * -1) 0; height: 42px; position: relative; z-index: 1; }

/* Delta chip — direction is carried by colour AND arrow, so it survives greyscale. */
.delta-chip {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 1px 6px;
  border-radius: var(--r-full);
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-bold);
  border: 1px solid transparent;
}
.delta-chip.up   { color: var(--gain-hi); background: var(--gain-soft); border-color: var(--gain-line); }
.delta-chip.down { color: var(--loss-hi); background: var(--loss-soft); border-color: var(--loss-line); }
.delta-chip.flat { color: var(--txt-3); background: var(--bg-3); border-color: var(--line-2); }
.delta-chip::before { font-size: 9px; line-height: 1; }
.delta-chip.up::before   { content: '▲'; }
.delta-chip.down::before { content: '▼'; }
.delta-chip.flat::before { content: '–'; }

/* -- Instrument badge ------------------------------------------------------ */
/* Colour is derived from the symbol itself, so an instrument keeps the same badge
   everywhere it appears without maintaining a lookup table. */
.instr-badge {
  display: inline-grid;
  place-items: center;
  width: 30px; height: 30px;
  flex: none;
  border-radius: var(--r-full);
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-bold);
  letter-spacing: -0.02em;
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.14);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.18);
}
.instr-badge.sm { width: 22px; height: 22px; font-size: 8px; }

.asset-row {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
}
.asset-name { font-weight: var(--fw-semi); font-size: var(--fs-md); line-height: 1.2; }
.asset-sub { font-size: var(--fs-2xs); color: var(--txt-3); font-family: var(--font-mono); }

/* -- Allocation bars ------------------------------------------------------- */
.alloc-row {
  display: grid;
  grid-template-columns: 84px 1fr 54px;
  gap: var(--sp-3);
  align-items: center;
  padding: var(--sp-2) 0;
}
.alloc-name { font-family: var(--font-mono); font-size: var(--fs-sm); color: var(--txt-2); }
.alloc-track {
  height: 7px;
  border-radius: var(--r-full);
  background: var(--bg-3);
  overflow: hidden;
}
.alloc-fill {
  /* A span is inline by default, and an inline element ignores width/height —
     which rendered these bars as empty gray tracks with numbers at the end. */
  display: block;
  height: 100%;
  border-radius: var(--r-full);
  background: var(--accent-grad);
  box-shadow: 0 0 12px var(--accent-glow);
  transform-origin: left;
  animation: alloc-grow 900ms var(--ease) both;
}
.alloc-fill.is-gain { background: linear-gradient(90deg, var(--gain), #1e9a58); box-shadow: 0 0 12px rgba(47,191,113,.35); }
.alloc-fill.is-loss { background: linear-gradient(90deg, var(--loss), #b8353a); box-shadow: 0 0 12px rgba(229,72,77,.35); }
@keyframes alloc-grow { from { transform: scaleX(0); } to { transform: scaleX(1); } }
.alloc-pct {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-sm);
  text-align: right;
  color: var(--txt-2);
}

/* -- Weekday x session matrix ---------------------------------------------- */
.matrix { display: grid; gap: 4px; }
.matrix-head, .matrix-row { display: grid; grid-template-columns: 46px repeat(4, 1fr); gap: 4px; }
.matrix-corner, .matrix-label {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--txt-3);
  display: flex;
  align-items: center;
}
.matrix-colhead {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--txt-3);
  text-align: center;
  padding-bottom: 2px;
}
.matrix-cell {
  aspect-ratio: 2.2 / 1;
  border-radius: var(--r-md);
  border: 1px solid var(--line-1);
  display: grid;
  place-items: center;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-xs);
  font-weight: var(--fw-semi);
  cursor: default;
  transition: transform var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.matrix-cell:hover { transform: scale(1.06); border-color: var(--line-3); z-index: 1; }
.matrix-cell.is-empty { color: var(--txt-4); background: var(--bg-2); }

/* -- Section header with tagline ------------------------------------------- */
.section-head {
  display: flex;
  align-items: baseline;
  gap: var(--sp-3);
  flex-wrap: wrap;
  padding-bottom: var(--sp-1);
}
.section-head h2 {
  font-size: var(--fs-lg);
  font-weight: var(--fw-semi);
  letter-spacing: -0.01em;
}
.section-tag {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wider);
  color: var(--accent);
}

/* -- Hero refinements ------------------------------------------------------ */
.hero {
  border-radius: var(--r-hero);
  background:
    radial-gradient(140% 120% at 0% 0%, rgba(74, 158, 255, 0.13), transparent 55%),
    linear-gradient(180deg, rgba(255, 255, 255, 0.04), transparent 40%),
    var(--bg-1);
  box-shadow: var(--sh-2);
}
.hero-value { text-shadow: 0 0 40px currentColor; }
.hero-value.gain { text-shadow: 0 0 44px rgba(47, 191, 113, 0.35); }
.hero-value.loss { text-shadow: 0 0 44px rgba(229, 72, 77, 0.35); }

.countdown { text-shadow: 0 0 30px currentColor; }

/* Live badge / status pills gain a soft halo so "live" reads instantly. */
.live-badge { box-shadow: 0 0 16px rgba(229, 72, 77, 0.22); }
.status-chip.is-open { box-shadow: 0 0 16px rgba(47, 191, 113, 0.18); }
.session.is-live { box-shadow: inset 0 0 22px rgba(47, 191, 113, 0.10); }

/* -- Mini stat strip inside a card ----------------------------------------- */
.ministat { display: flex; flex-direction: column; gap: 2px; }
.ministat-label {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--txt-3);
}
.ministat-value {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-lg);
  font-weight: var(--fw-semi);
}

@media (max-width: 860px) {
  .matrix-head, .matrix-row { grid-template-columns: 40px repeat(4, 1fr); }
  .alloc-row { grid-template-columns: 70px 1fr 46px; }
}

/* -- Live price ticker ------------------------------------------------------ */
.ticker { padding-left: 76px; }
.ticker-status {
  position: absolute;
  left: var(--sp-4);
  z-index: 3;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px var(--sp-2);
  border-radius: var(--r-sm);
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-wide);
}
.ticker-status.is-live {
  background: var(--gain-soft); border: 1px solid var(--gain-line); color: var(--gain-hi);
}
.ticker-status.is-degraded {
  background: var(--warn-soft); border: 1px solid var(--warn-line); color: var(--warn);
}
.tick.is-degraded { opacity: 0.82; }
.tick.is-out { opacity: 0.45; }
.tick-note {
  font-family: var(--font-mono);
  font-size: 9px;
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--warn);
  border: 1px solid var(--warn-line);
  border-radius: 3px;
  padding: 0 4px;
}
/* A brief flash when a price changes, so movement is noticed without watching. */
@keyframes tick-flash-up   { from { background: rgba(47,191,113,.30); } to { background: transparent; } }
@keyframes tick-flash-down { from { background: rgba(229,72,77,.30); } to { background: transparent; } }
.tick-val.flash-up   { animation: tick-flash-up 700ms var(--ease); border-radius: 3px; }
.tick-val.flash-down { animation: tick-flash-down 700ms var(--ease); border-radius: 3px; }

/* -- Spotlight -------------------------------------------------------------
   The glow is a gradient positioned by two custom properties that ambient.js
   writes on mousemove. Nothing is created or animated in JS, so this costs one
   composited layer per hovered card and nothing at rest. */
.card, .kpi, .hero {
  --spot-x: 50%;
  --spot-y: 50%;
}
.card::before, .kpi::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--dur-slow) var(--ease);
  background: radial-gradient(
    280px circle at var(--spot-x) var(--spot-y),
    rgba(74, 158, 255, 0.09),
    transparent 72%
  );
}
.card.has-spot::before, .kpi.has-spot::before { opacity: 1; }
/* Card content must sit above the glow layer. */
.card > *, .kpi > * { position: relative; z-index: 1; }

/* The stat tile already uses ::before for its accent rule, so it opts out. */
.stat::before { z-index: 2; }

@media (prefers-reduced-motion: reduce) {
  .card::before, .kpi::before { display: none; }
}

/* Legend rows become hover targets for series isolation. */
.alloc-row[data-series] { cursor: default; border-radius: var(--r-sm); }
.alloc-row[data-series]:hover { background: var(--bg-2); }
.alloc-row[data-series]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ============================================================================
   AMBIENT BACKGROUND LAYER
   A fixed, full-viewport node field behind the entire app. It only ever shows
   through the gutters between panels, because every card surface is opaque —
   which is what lets it read as atmosphere rather than compete with the data.

   Fixed positioning keeps it out of document flow entirely, so it cannot affect
   the height of anything.
   ========================================================================== */
.tj-bgcanvas {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  z-index: 0;
  display: block;
  pointer-events: none;
}

/* The shell floats above the field and drops its own opaque page background so
   the field is visible between panels. Individual cards keep theirs. */
.app {
  position: relative;
  z-index: 1;
  background: transparent;
}

/* The body gradients stay — they tint the field rather than hide it. */
@media (prefers-reduced-motion: reduce) {
  .tj-bgcanvas { display: none; }
}

/* ============================================================================
   SECTIONS FROM THE DESIGN HANDOFF
   ========================================================================== */

/* -- Watchlist ------------------------------------------------------------- */
.wl-row {
  display: grid;
  grid-template-columns: 30px 1fr auto auto;
  gap: var(--sp-3);
  align-items: center;
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-md);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
.wl-row:hover { background: var(--bg-2); }
.wl-row.is-active { background: var(--accent-soft); box-shadow: inset 2px 0 0 var(--accent); }
.wl-sym { font-family: var(--font-mono); font-weight: var(--fw-semi); font-size: var(--fs-md); }
.wl-name {
  font-family: var(--font-mono); font-size: var(--fs-2xs);
  color: var(--txt-4); text-transform: uppercase; letter-spacing: var(--tracking-wide);
}
.wl-price { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: var(--fs-md); }
.wl-chg {
  font-family: var(--font-mono); font-size: var(--fs-xs);
  padding: 2px 6px; border-radius: var(--r-sm); min-width: 62px; text-align: right;
}
.wl-chg.gain { background: var(--gain-soft); color: var(--gain-hi); }
.wl-chg.loss { background: var(--loss-soft); color: var(--loss-hi); }
.wl-chg.flat { background: var(--bg-3); color: var(--txt-3); }

/* -- Global session map ---------------------------------------------------- */
/* A dot grid rather than real geography: the point is which desk is awake, and a
   coastline would add detail that carries no information. */
.map-wrap {
  position: relative;
  overflow: hidden;
  border-radius: var(--r-lg);
  background: var(--bg-inset);
  border: 1px solid var(--border-1);
  /* Both children are position:absolute, so the wrapper has no content to size
     itself from and collapses to nothing without an explicit width. */
  width: 100%;
  height: 190px;
}
/* Two stacked layers — the dot-matrix landmass and the markers — sharing one
   transform, so a city marker can never drift away from the coast beneath it. */
.map-layer {
  position: absolute;
  inset: 0;
  transform-origin: center;
  transition: transform var(--dur-slow) var(--ease);
}
.map-wrap { height: 220px; }
.map-marker {
  position: absolute;
  width: 10px; height: 10px;
  border-radius: var(--r-full);
  transform: translate(-50%, -50%);
  transition: transform var(--dur-slow) var(--ease), box-shadow var(--dur-base) var(--ease);
  cursor: pointer;
}
.map-marker.is-open { background: var(--gain); box-shadow: 0 0 0 4px var(--gain-soft), 0 0 16px var(--gain); }
.map-marker.is-closed { background: var(--txt-4); }
.map-marker.is-active { transform: translate(-50%, -50%) scale(1.5); }
.map-foot {
  display: flex; gap: var(--sp-2); flex-wrap: wrap; margin-top: var(--sp-3);
}
.sess-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px var(--sp-3); border-radius: var(--r-md);
  background: var(--bg-2); border: 1px solid var(--border-1);
  font-family: var(--font-mono); font-size: var(--fs-2xs);
  text-transform: uppercase; letter-spacing: var(--tracking-wide);
  color: var(--txt-3); cursor: pointer;
  transition: all var(--dur-fast) var(--ease);
}
.sess-chip:hover { border-color: var(--border-2); }
.sess-chip.is-active { border-color: var(--accent-line); color: var(--accent-hi); background: var(--accent-soft); }
.sess-chip::before { content: ''; width: 6px; height: 6px; border-radius: var(--r-full); background: var(--txt-4); }
.sess-chip.is-open::before { background: var(--gain); box-shadow: 0 0 8px var(--gain); }

/* -- Live activity feed ---------------------------------------------------- */
.act-row {
  display: grid;
  grid-template-columns: 52px 1fr;
  gap: var(--sp-3);
  align-items: baseline;
  padding: var(--sp-2) 0;
  border-bottom: 1px solid var(--border-1);
  font-size: var(--fs-sm);
}
.act-row:last-child { border-bottom: 0; }
.act-time { font-family: var(--font-mono); font-size: var(--fs-2xs); color: var(--txt-4); }
.act-dot { display: inline-block; width: 5px; height: 5px; border-radius: var(--r-full); margin-right: 6px; }

/* -- Mission control ------------------------------------------------------- */
.mc-tile {
  padding: var(--sp-4) var(--sp-5);
  background: var(--bg-1);
  border: 1px solid var(--border-1);
  border-radius: var(--r-xl);
  box-shadow: var(--sh-1), var(--sh-2);
}
.mc-value {
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  font-size: var(--fs-2xl); font-weight: var(--fw-semi); margin-top: var(--sp-2);
}

/* -- Symbol chart controls ------------------------------------------------- */
.sym-head { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
.sym-foot { display: flex; align-items: baseline; gap: var(--sp-6); margin-top: var(--sp-4); }

/* -- Insight callout — one line, mid page ---------------------------------- */
.callout {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5);
  border-radius: var(--r-xl);
  background: linear-gradient(90deg, var(--accent-soft), transparent 70%), var(--bg-1);
  border: 1px solid var(--border-1);
  font-size: var(--fs-md);
}
.callout::before {
  content: ''; flex: none;
  width: 6px; height: 6px; border-radius: var(--r-full);
  background: var(--accent); box-shadow: 0 0 10px var(--accent-glow);
}

/* ============================================================================
   PAGE CHARACTER
   Each page gets its own accent weighting rather than all of them reading the
   same. Analytics leans blue because it is about reading, not transacting.
   ========================================================================== */

/* -- Analytics: blue-weighted ---------------------------------------------- */
.page-analytics .card-head h2 { color: var(--txt-1); }
.page-analytics .eyebrow { color: var(--accent-dim); }
.page-analytics .card {
  background:
    linear-gradient(180deg, rgba(122, 184, 255, 0.035), transparent 46%),
    var(--bg-1);
}
.page-analytics .stat::after,
.page-analytics .kpi { border-color: var(--accent-line); }
.page-analytics .section-tag { color: var(--accent-hi); }
.page-analytics .table thead th { color: var(--accent-dim); }
/* The insight rail reads blue unless it is specifically a money finding. */
.page-analytics .insight.neutral { border-left-color: var(--accent); background: linear-gradient(90deg, var(--accent-soft), var(--bg-2) 60%); }

/* -- Journal: card grid with more life ------------------------------------- */
.journal-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: var(--sp-3); }

.entry-card {
  display: block;
  position: relative;
  overflow: hidden;
}
/* A quiet accent wash that only appears on hover — the grid stays calm at rest. */
.entry-card::after {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(420px circle at var(--spot-x, 50%) var(--spot-y, 50%),
              rgba(122, 184, 255, 0.06), transparent 65%);
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease);
  pointer-events: none;
}
.entry-card:hover::after { opacity: 1; }

.entry-grade-ring {
  position: absolute;
  top: var(--sp-4); right: var(--sp-4);
  width: 34px; height: 34px;
}

/* Emotion pips — a compact read of the psychology on the card face. */
.emo-strip { display: inline-flex; gap: 3px; align-items: center; }
.emo-pip { width: 7px; height: 7px; border-radius: var(--r-full); }
.emo-pip.good { background: var(--gain); }
.emo-pip.bad { background: var(--loss); }
.emo-pip.neutral { background: var(--flat); }

/* -- Red Folder: countdown urgency ----------------------------------------- */
@keyframes impact-sweep {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(100%); }
}
.cal-row.is-imminent { position: relative; overflow: hidden; }
.cal-row.is-imminent::after {
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, rgba(229, 72, 77, 0.14), transparent);
  animation: impact-sweep 2.6s var(--ease) infinite;
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) { .cal-row.is-imminent::after { animation: none; } }

/* A thin progress rail showing how much of the countdown has elapsed. */
.impact-rail {
  height: 2px;
  border-radius: var(--r-full);
  background: var(--bg-3);
  overflow: hidden;
  margin-top: 6px;
}
.impact-rail span {
  display: block; height: 100%; width: 100%;
  background: linear-gradient(90deg, var(--warn), var(--loss));
  /* Fill level is a scaleX, not a width — same look, no layout work on each tick. */
  transform-origin: left;
  transition: transform 1s linear;
}

.cal-row { transition: background var(--dur-fast) var(--ease); }
.cal-row:hover { background: var(--bg-2); }

/* -- News: a clearer header block ------------------------------------------ */
.news-head {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: var(--sp-3);
}
.news-explain {
  font-size: var(--fs-sm);
  color: var(--txt-3);
  line-height: var(--lh-snug);
  margin-top: var(--sp-2);
}

/* ============================================================================
   BLUE LIFT
   The interface was reading as black-and-grey. These raise the blue presence
   without touching the money colours, which stay exactly as they were.
   ========================================================================== */

/* Panel borders pick up a faint blue rather than pure white alpha. */
.card, .kpi, .stat, .mc-tile { border-color: rgba(122, 184, 255, 0.14); }
.card:hover, .kpi:hover, .stat:hover { border-color: rgba(122, 184, 255, 0.26); }

/* A soft outer glow so panels sit in light rather than on a void. */
.card, .kpi, .mc-tile {
  box-shadow: var(--sh-1), var(--sh-2), 0 0 40px rgba(122, 184, 255, 0.05);
}
.card:hover, .kpi:hover {
  box-shadow: var(--sh-1), var(--sh-2), 0 0 56px rgba(122, 184, 255, 0.11);
}

.hero {
  background:
    radial-gradient(150% 130% at 0% 0%, rgba(122, 184, 255, 0.20), transparent 58%),
    linear-gradient(180deg, rgba(255, 255, 255, 0.05), transparent 42%),
    var(--bg-1);
  box-shadow: var(--sh-2), 0 0 70px rgba(122, 184, 255, 0.09);
}

.sidebar {
  background:
    linear-gradient(180deg, rgba(122, 184, 255, 0.06), transparent 40%),
    var(--bg-1);
}
.topbar { background: rgba(16, 26, 38, 0.80); }

/* Table headers and hairlines carry the tint too, so dense tables don't
   collapse back to grey. */
.table thead th { background: rgba(122, 184, 255, 0.05); }
.table tbody tr:hover { background: rgba(122, 184, 255, 0.055); }

.seg, .pills { background: rgba(122, 184, 255, 0.06); }
.input, .select, .textarea { background: rgba(7, 13, 20, 0.75); }

/* Chart plot areas get the faintest wash so they aren't holes in the page. */
.chart-wrap { border-radius: var(--r-md); }

/* -- In-app video ---------------------------------------------------------- */
.video-modal {
  position: fixed;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  z-index: 70;
  width: min(880px, calc(100vw - var(--sp-8)));
  padding: var(--sp-5);
  background: var(--bg-1);
  border: 1px solid rgba(122, 184, 255, 0.22);
  border-radius: var(--r-xl);
  box-shadow: var(--sh-3), 0 0 80px rgba(122, 184, 255, 0.12);
  animation: rise-in var(--dur-slow) var(--ease);
}
.news-open {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  color: var(--txt-4);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
}
.news-item:hover .news-open { color: var(--accent-hi); }
.news-sum { -webkit-line-clamp: 3; max-height: 4.35em; }

/* -- Trade detail: blue-weighted like analytics ----------------------------- */
.page-trade .card {
  background:
    linear-gradient(180deg, rgba(122, 184, 255, 0.05), transparent 46%),
    var(--bg-1);
}
.page-trade .dl dt { color: var(--accent-dim); }
.page-trade .card-head h2 { letter-spacing: -0.01em; }
.page-trade .hero-value,
.page-trade .stat-value { text-shadow: 0 0 32px currentColor; }

/* -- Allocation bars: readable fill, hover reveals the remainder ------------- */
.alloc-track { position: relative; }
/* A faint track showing the unfilled remainder, so a 12% bar reads as 12 of 100
   rather than as a short bar floating in a dark gutter. */
.alloc-ghost {
  position: absolute;
  inset: 0;
  border-radius: var(--r-full);
  border: 1px solid rgba(122, 184, 255, 0.16);
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease);
}
.alloc-row:hover .alloc-ghost,
.alloc-row:focus-visible .alloc-ghost { opacity: 1; }
.alloc-row:hover .alloc-fill { filter: brightness(1.25); }
.alloc-track { height: 8px; }
.alloc-fill { transition: filter var(--dur-fast) var(--ease); }

/* -- Volume strip beneath the win/loss ring -------------------------------- */
.vol-strip { margin-top: var(--sp-2); }
.vol-legend {
  display: flex; justify-content: space-between;
  font-family: var(--font-mono); font-size: var(--fs-2xs);
  color: var(--txt-4); text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  margin-top: 2px;
}

/* -- TradingView widgets ---------------------------------------------------- */
.tv-panel { min-height: 120px; border-radius: var(--r-lg); overflow: hidden; }
.tv-off {
  padding: var(--sp-5);
  border: 1px dashed rgba(122, 184, 255, 0.24);
  border-radius: var(--r-lg);
  background: rgba(122, 184, 255, 0.04);
  text-align: center;
}
.tv-off-title { font-size: var(--fs-md); font-weight: var(--fw-semi); color: var(--txt-1); }
.tv-off-body {
  font-size: var(--fs-sm); color: var(--txt-3);
  line-height: var(--lh-snug); max-width: 52ch;
  margin: var(--sp-2) auto var(--sp-4);
}
.tradingview-widget-copyright { font-size: var(--fs-2xs) !important; opacity: .5; }

/* ============================================================================
   CYBERPUNK LAYER
   Reserved for the journal and analytics — the two screens you read rather than
   scan. Scanlines, corner brackets and a scan sweep, all built from gradients so
   they cost nothing and never obscure a number.
   ========================================================================== */

/* Corner brackets — a terminal frame rather than a rounded card. */
.cyber { position: relative; }
/* The brackets grow from 14px to 20px on hover. They are drawn at full size and
   clipped back, rather than having their width and height animated: clip-path is a
   paint-level change, where animating the box would ask the browser to re-lay-out
   on every frame. Scaling was the other option and is not equivalent here — it
   would thicken the 2px stroke along with the bracket. */
.cyber::before,
.cyber::after {
  content: '';
  position: absolute;
  width: 20px; height: 20px;
  pointer-events: none;
  border-color: var(--accent);
  opacity: 0.55;
  transition: opacity var(--dur-base) var(--ease), clip-path var(--dur-base) var(--ease);
}
.cyber::before {
  top: -1px; left: -1px;
  border-top: 2px solid; border-left: 2px solid; border-top-left-radius: 4px;
  clip-path: inset(0 6px 6px 0);
}
.cyber::after {
  bottom: -1px; right: -1px;
  border-bottom: 2px solid; border-right: 2px solid; border-bottom-right-radius: 4px;
  clip-path: inset(6px 0 0 6px);
}
.cyber:hover::before,
.cyber:hover::after { opacity: 1; clip-path: inset(0); }

/* Faint scanlines. 3px period keeps them invisible as texture but present as feel. */
.scanlines { position: relative; }
.scanlines > * { position: relative; z-index: 1; }
.scanlines::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: repeating-linear-gradient(
    180deg,
    rgba(122, 184, 255, 0.035) 0 1px,
    transparent 1px 3px
  );
  border-radius: inherit;
}

/* A single sweep on reveal, not a loop — a permanently scanning panel is a
   distraction on a screen you are trying to read. */
@keyframes cyber-sweep {
  0%   { transform: translateY(-100%); opacity: 0; }
  35%  { opacity: 1; }
  100% { transform: translateY(100%); opacity: 0; }
}
.sweep-once { position: relative; overflow: hidden; }
.sweep-once::before {
  content: '';
  position: absolute;
  inset: 0 0 auto 0;
  height: 45%;
  pointer-events: none;
  background: linear-gradient(180deg, transparent, rgba(122, 184, 255, 0.10), transparent);
  animation: cyber-sweep 1.4s var(--ease) 1;
}
@media (prefers-reduced-motion: reduce) { .sweep-once::before { animation: none; display: none; } }

/* -- Collapsible group ------------------------------------------------------ */
.group {
  border: 1px solid rgba(122, 184, 255, 0.16);
  border-radius: var(--r-xl);
  background:
    linear-gradient(180deg, rgba(122, 184, 255, 0.05), transparent 40%),
    var(--bg-1);
  overflow: hidden;
}
.group + .group { margin-top: var(--sp-3); }

.group-head {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  padding: var(--sp-3) var(--sp-4);
  background: transparent;
  border: 0;
  border-bottom: 1px solid transparent;
  color: var(--txt-1);
  font: inherit;
  cursor: pointer;
  text-align: left;
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.group-head:hover { background: rgba(122, 184, 255, 0.06); }
.group.is-open .group-head { border-bottom-color: rgba(122, 184, 255, 0.14); }

.group-caret {
  width: 16px; height: 16px; flex: none;
  color: var(--accent);
  transition: transform var(--dur-base) var(--ease);
}
.group.is-open .group-caret { transform: rotate(90deg); }

.group-title {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wider);
  color: var(--txt-2);
}
.group.is-open .group-title { color: var(--accent-hi); }

.group-stats { display: flex; gap: var(--sp-4); align-items: baseline; }
.group-stat {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-sm);
}
.group-stat span { color: var(--txt-4); font-size: var(--fs-2xs); margin-left: 4px; }

/* grid-template-rows animates without the layout thrash of animating height. */
.group-body {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--dur-slow) var(--ease);
}
.group.is-open .group-body { grid-template-rows: 1fr; }
.group-inner { overflow: hidden; }
.group-pad { padding: var(--sp-4); display: grid; gap: var(--sp-3); }

/* A month bar showing that month's net, so a collapsed group still says something. */
.group-spark { height: 4px; border-radius: var(--r-full); background: var(--bg-3); overflow: hidden; flex: 1; max-width: 120px; }
.group-spark span { display: block; height: 100%; border-radius: inherit; }
.group-spark span.gain { background: linear-gradient(90deg, var(--gain), var(--gain-hi)); }
.group-spark span.loss { background: linear-gradient(90deg, var(--loss), var(--loss-hi)); }

@media (max-width: 640px) {
  .group-stats { gap: var(--sp-2); }
  .group-spark { display: none; }
}

/* -- Session map caption + chip countdowns ---------------------------------- */
.map-caption {
  margin-top: var(--sp-3);
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--txt-3);
  text-align: center;
  min-height: 14px;
}
.sess-cd {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-2xs);
  opacity: 0.75;
  margin-left: 5px;
}
.sess-chip.is-open .sess-cd { color: var(--gain-hi); opacity: 1; }

/* Notice shown when a third-party market widget is cancelled by the browser
   (almost always an ad blocker). Centred over the dead frame it explains. */
.tv-blocked {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-3);
  text-align: center;
  padding: var(--sp-6);
  background: var(--bg-1);
  border-radius: var(--r-lg);
}
.tv-blocked .tv-off-body { max-width: 44ch; }

/* -- Watchlist v2: TradingView-style grouped, sortable list ----------------- */
.wl2-cols {
  display: grid;
  grid-template-columns: 28px minmax(0,1fr) 88px 76px 72px;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-4);
  border-bottom: 1px solid var(--line-1);
  background: var(--bg-1);
}
.wl2-cols button {
  background: none; border: 0; cursor: pointer;
  font-family: var(--font-mono); font-size: var(--fs-2xs);
  letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--txt-4); text-align: left; padding: 2px 0;
  transition: color var(--dur-fast) var(--ease);
}
.wl2-cols button.r { text-align: right; }
.wl2-cols button:hover { color: var(--txt-2); }
.wl2-cols button.is-on { color: var(--accent-hi); }
.wl2-cols button.is-on[data-dir="asc"]::after { content: " ▲"; font-size: 8px; }
.wl2-cols button.is-on[data-dir="desc"]::after { content: " ▼"; font-size: 8px; }

/* Sticky section headers with a solid ground, so scrolling rows pass BENEATH
   them instead of colliding with the text. */
.wl2-group {
  position: sticky; top: 0; z-index: 2;
  display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-4);
  background: var(--bg-2);
  border-top: 1px solid var(--line-1);
  border-bottom: 1px solid var(--line-1);
  font-family: var(--font-mono); font-size: var(--fs-2xs);
  letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--txt-3);
}
.wl2-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--kind-c, var(--txt-3));
  box-shadow: 0 0 6px var(--kind-c, transparent);
}
.wl2-row {
  display: grid;
  grid-template-columns: 28px minmax(0,1fr) 88px 76px 72px;
  gap: var(--sp-2);
  align-items: center;
  padding: var(--sp-2) var(--sp-4);
  cursor: pointer;
  font-size: var(--fs-md);
  transition: background var(--dur-fast) var(--ease);
}
.wl2-row:hover { background: var(--bg-2); }
.wl2-row.is-active { background: var(--accent-soft); box-shadow: inset 2px 0 0 var(--accent); }
.wl2-row .r { text-align: right; }
.wl2-live {
  display: inline-block; width: 6px; height: 6px; border-radius: 50%;
  background: var(--gain); margin-left: 6px; vertical-align: middle;
  animation: pulse-dot 2s ease-in-out infinite;
}

/* -- Collapsible dashboard cards -------------------------------------------- */
.collapse-btn {
  background: none; border: 0; cursor: pointer;
  color: var(--txt-4); padding: 2px;
  display: inline-flex; align-items: center;
  transition: color var(--dur-fast) var(--ease), transform var(--dur-base) var(--ease);
}
.collapse-btn:hover { color: var(--txt-1); }
.collapse-btn svg { width: 14px; height: 14px; }
.card.is-collapsed .collapse-btn { transform: rotate(-90deg); }
.card.is-collapsed .card-body { display: none; }

@keyframes pulse-dot {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}

/* Inline collapsible subsections (emotion phases and friends). */
.collapse-head {
  display: flex; align-items: center; justify-content: space-between;
  width: 100%; background: none; border: 0; cursor: pointer;
  padding: 0 0 var(--sp-3); text-align: left;
  color: var(--txt-3);
  transition: color var(--dur-fast) var(--ease);
}
.collapse-head:hover { color: var(--txt-1); }
.collapse-head svg { width: 12px; height: 12px; transition: transform var(--dur-base) var(--ease); }
[data-collapse].is-collapsed .collapse-head svg { transform: rotate(-90deg); }
[data-collapse].is-collapsed .collapse-body { display: none; }

/* The month's net figure was ending flush against the group's right border and
   the cyber corner bracket. Extra breathing room keeps the number inside. */
.group-head { padding-right: var(--sp-6); }
.group-stats { min-width: 0; flex-wrap: wrap; justify-content: flex-end; row-gap: var(--sp-1); }

/* Stat tiles: the value must FIT, never kiss or clip at the box edge. The tile
   measures itself and steps the number down as its own width shrinks — grid
   column count and viewport can both change, so the container is the truth. */
.stat { container-type: inline-size; min-width: 0; }
.stat-value { max-width: 100%; }
@container (max-width: 230px) { .stat-value { font-size: var(--fs-xl); } }
@container (max-width: 170px) { .stat-value { font-size: var(--fs-lg); letter-spacing: -0.01em; } }

/* The month header's net bar: flex:1 inside a content-sized flex-end parent
   resolves to zero width. A fixed basis keeps the bar visible at every width. */
.group-spark { flex: 0 0 90px; width: 90px; }

/* One-line explanation under a chart card's title: what the marks MEAN. */
.card-sub {
  /* No negative margin. Pulling this up by sp-2 tucked the first line under the
     card header's divider — which read exactly like overlapping text, because it
     was. Sits below the rule now, with its own breathing room. */
  padding: var(--sp-3) var(--sp-5) 0;
  margin: 0;
  font-size: var(--fs-sm);
  line-height: 1.55;
  color: var(--txt-3);
}
.card-sub p { margin: 0 0 var(--sp-2); }
.card-sub p:last-child { margin-bottom: 0; }
.card .card-sub + .card-body { padding-top: var(--sp-3); }

/* -- News grouped by publication ------------------------------------------- */
.src-group + .src-group { border-top: 1px solid var(--line-1); }
.src-head {
  display: flex; align-items: center; gap: var(--sp-3);
  width: 100%; padding: var(--sp-3) var(--sp-4);
  background: var(--bg-1); border: 0; cursor: pointer;
  color: var(--txt-2); font: inherit; text-align: left;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.src-head:hover { background: var(--bg-2); color: var(--txt-1); }
.src-caret { display: inline-flex; transition: transform var(--dur-base) var(--ease); }
.src-caret svg { width: 13px; height: 13px; }
.src-group.is-closed .src-caret { transform: rotate(-90deg); }
.src-group.is-closed .src-body { display: none; }
.src-name {
  font-family: var(--font-mono); font-size: var(--fs-2xs);
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--txt-1);
}
.src-count {
  font-family: var(--font-mono); font-size: var(--fs-2xs); color: var(--txt-3);
}

/* -- Panel grid ------------------------------------------------------------
   Rearrangeable pages unwrap their row containers onto one twelve-column grid,
   with each card carrying the span its old wrapper gave it. The result renders
   identically to the wrapped version — the point is only that the cards are now
   individually movable instead of welded together three to a row. */
.panel-grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: var(--sp-4);
  row-gap: var(--sp-6);
}
/* The page's own between-section spacing would stack on top of the row gap. */
.panel-grid > * + * { margin-top: 0; }
/* The span rides in on a custom property rather than an inline grid-column, so
   the responsive rules below can still override it — an inline style could not
   be beaten by a media query. */
.panel-grid > * { grid-column: span var(--span, 12); min-width: 0; }

@media (max-width: 1180px) {
  /* Mirrors what the wrapped layout did at this width, span for span: four across
     became two, thirds and the 2:1 split went full width, and halves stayed put. */
  .panel-grid > [data-span="3"] { grid-column: span 6; }
  .panel-grid > [data-span="4"],
  .panel-grid > [data-span="8"] { grid-column: span 12; }
}
@media (max-width: 860px) {
  .panel-grid > * { grid-column: span 12 !important; }
  .panel-grid { row-gap: var(--sp-5); }
}

/* -- Rearrange mode --------------------------------------------------------
   Only active while the Rearrange button is on. Outside that mode not one of
   these rules applies, so the pages look and behave exactly as before. */
.is-rearranging > [data-panel-id] {
  position: relative;
  cursor: grab;
  outline: 1px dashed var(--accent-line);
  outline-offset: 3px;
  border-radius: var(--r-xl);
  transition: outline-color var(--dur-fast) var(--ease), opacity var(--dur-fast) var(--ease);
}
.is-rearranging > [data-panel-id]:hover { outline-color: var(--accent); }
.is-rearranging > [data-panel-id].is-dragging { opacity: 0.45; cursor: grabbing; }

.panel-grip {
  position: absolute;
  top: -11px;
  left: var(--sp-5);
  z-index: 5;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 2px 10px;
  border-radius: var(--r-full);
  background: var(--accent);
  color: #06111f;
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.4);
}
.panel-grip .grip-dots { font-size: 13px; letter-spacing: 0; }
.panel-grip .grip-label,
.panel-grip .grip-dots { pointer-events: none; }

/* Width stepper. Lives on the handle so a panel can be widened where it sits,
   rather than in a separate mode. */
.panel-grip .grip-w {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 17px; height: 17px;
  padding: 0;
  border: 0;
  border-radius: var(--r-full);
  background: rgba(6, 17, 31, 0.18);
  color: inherit;
  font: inherit;
  font-size: 12px;
  line-height: 1;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease);
}
.panel-grip .grip-w:hover { background: rgba(6, 17, 31, 0.38); }
.panel-grip .grip-size {
  min-width: 30px;
  text-align: center;
  letter-spacing: 0;
  pointer-events: none;
}

/* The preset picker, shown only while rearrange mode is on. */
.rearrange-tools { display: inline-flex; align-items: center; gap: var(--sp-2); }
.rearrange-tools[hidden] { display: none; }
.rearrange-tools .input-sm {
  width: auto;
  min-width: 132px;
  padding: 5px var(--sp-3);
  font-size: var(--fs-xs);
}

/* -- Journal coaching ------------------------------------------------------
   Reads what the trader wrote and answers it. Built from the same tokens as the
   rest of the site — the evidence is what should stand out here, not the panel. */
.coach-headline {
  margin-top: var(--sp-5);
  padding: var(--sp-4) var(--sp-5);
  border-radius: var(--r-lg);
  border: 1px solid var(--loss-line);
  background: linear-gradient(90deg, rgba(233, 84, 84, 0.09), var(--bg-2) 62%);
}
.coach-headline-label {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wider);
  color: var(--loss-hi);
}
.coach-headline-value {
  font-size: var(--fs-xl);
  font-weight: var(--fw-semi);
  color: var(--txt-1);
  margin-top: 2px;
}
.coach-headline-meta {
  font-size: var(--fs-sm);
  color: var(--txt-2);
  margin-top: var(--sp-2);
}

.coach-list { margin-top: var(--sp-5); display: flex; flex-direction: column; gap: var(--sp-3); }

.coach-pattern {
  position: relative;
  padding: var(--sp-4) var(--sp-5);
  border-radius: var(--r-lg);
  border: 1px solid var(--line-1);
  background: var(--bg-2);
}
.coach-pattern.is-focus { border-color: var(--loss-line); }
.coach-pattern.is-good { border-color: var(--gain-line); }
.coach-focus-tag {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wider);
  color: var(--loss-hi);
  margin-bottom: var(--sp-2);
}
.coach-reading {
  font-size: var(--fs-md);
  color: var(--txt-1);
  line-height: var(--lh-snug);
  max-width: 76ch;
}

/* The trader's own sentences, set apart so it is obvious they were not written
   by this page. */
.coach-quotes { margin-top: var(--sp-4); }
.coach-quotes-head,
.coach-source-head,
.coach-plan-head {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wider);
  color: var(--txt-3);
  margin-bottom: var(--sp-2);
}
/* The trader's own sentence, quoted back. The left rule is the standard
   typographic mark for quoted material and is what separates their words from the
   app's voice — it is a quotation rule, not an accent tab on a card. Neutral
   --line-3 on purpose: the words carry the emphasis, not the border. */
.coach-quote {
  margin: 0 0 var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  border-left: 2px solid var(--line-3);
  background: var(--bg-1);
  border-radius: 0 var(--r-md) var(--r-md) 0;
}
.coach-quote p {
  font-size: var(--fs-sm);
  color: var(--txt-2);
  line-height: 1.55;
  font-style: italic;
}
.coach-quote a {
  display: inline-block;
  margin-top: var(--sp-2);
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  color: var(--txt-3);
  text-decoration: none;
}
.coach-quote a:hover { color: var(--accent); }

.coach-money {
  margin-top: var(--sp-4);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-md);
  border: 1px solid var(--line-1);
  background: var(--bg-1);
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: var(--fs-sm);
  color: var(--txt-2);
  line-height: var(--lh-snug);
}
.coach-money.is-worse { border-color: var(--loss-line); }
.coach-money.is-better { border-color: var(--gain-line); }
.coach-money-note { font-size: var(--fs-2xs); color: var(--txt-3); }

/* Sources sit together under one heading so a pattern reads as one argument
   rather than a stack of unrelated boxes. */
.coach-sources { margin-top: var(--sp-4); }
.coach-source + .coach-source { margin-top: var(--sp-2); }

.coach-source {
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-md);
  background: var(--bg-1);
  border: 1px solid var(--line-1);
}
.coach-source-body {
  font-size: var(--fs-sm);
  color: var(--txt-2);
  line-height: var(--lh-snug);
  max-width: 76ch;
}
.coach-source-cite {
  display: inline-block;
  margin-top: var(--sp-3);
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  color: var(--accent-dim);
  line-height: 1.5;
  text-decoration: none;
  border-bottom: 1px solid transparent;
}
.coach-source-cite:hover { color: var(--accent); border-bottom-color: var(--accent-line); }

.coach-action {
  margin-top: var(--sp-4);
  font-size: var(--fs-sm);
  color: var(--txt-2);
  line-height: var(--lh-base);
  max-width: 76ch;
}

/* The one line to carry into the next session. */
.coach-plan {
  margin-top: var(--sp-4);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-md);
  border: 1px solid var(--accent-line);
  background: var(--accent-soft);
}
.coach-plan p {
  font-size: var(--fs-sm);
  color: var(--txt-1);
  line-height: var(--lh-snug);
  max-width: 76ch;
}

/* -- Style coaching --------------------------------------------------------
   Sits under the hold-time verdict in "How you actually trade". The verdict says
   what the numbers show; this says what to do about it, drawn from the trader's
   own journal. Built from the same tokens as the insight blocks above it so it
   reads as the end of that thought rather than a new panel. */
.style-coach {
  margin-top: var(--sp-4);
  padding: var(--sp-4) var(--sp-5);
  border-radius: var(--r-lg);
  border: 1px solid var(--line-1);
  background: var(--bg-2);
}
.style-coach.is-from-journal { border-color: var(--accent-line); }
.style-coach-tag {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wider);
  color: var(--accent-dim);
}
.style-coach-head {
  font-size: var(--fs-md);
  font-weight: var(--fw-semi);
  color: var(--txt-1);
  margin-top: 2px;
}
/* Same object as the journal's quotation, so it uses the same rule rather than a
   second copy of it. Only the spacing differs here, because this one leads the
   card instead of following a heading. */
.style-coach .coach-quote { margin: var(--sp-3) 0 0; }
.style-coach-reading,
.style-coach-action {
  margin-top: var(--sp-3);
  font-size: var(--fs-sm);
  color: var(--txt-2);
  line-height: var(--lh-base);
  max-width: 76ch;
}
.style-coach-prompt {
  margin-top: var(--sp-3);
  font-size: var(--fs-xs);
  color: var(--txt-3);
  max-width: 76ch;
}
.style-coach-plan {
  margin-top: var(--sp-4);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-md);
  border: 1px solid var(--accent-line);
  background: var(--accent-soft);
}
.style-coach-plan-head {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wider);
  color: var(--txt-3);
  margin-bottom: var(--sp-2);
}
.style-coach-plan p {
  font-size: var(--fs-sm);
  color: var(--txt-1);
  line-height: var(--lh-snug);
  max-width: 76ch;
}

/* -- Style split -----------------------------------------------------------
   The three-style breakdown inside the "Your style" tile: name, share bar and
   count per row. Built from the same tokens as every other bar on the site, with
   the dominant row picked out in the accent the tile already carries. */
/* One grid for the whole table, rows contributing cells via display: contents.
   The name column is max-content so the longest label — "Day trading (intraday)"
   — sets the width for every row and can never slide under its bar, which is
   exactly what happened when this column was a fixed 82px. */
.style-split {
  margin-top: var(--sp-3);
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr) max-content;
  column-gap: var(--sp-3);
  row-gap: var(--sp-2);
  align-items: center;
  font-size: var(--fs-xs);
  color: var(--txt-3);
}
.style-split-row { display: contents; }
.style-split-row.is-on > * { color: var(--txt-1); }
.style-split-name { white-space: nowrap; }
.style-split-bar {
  display: block;
  height: 5px;
  border-radius: var(--r-full);
  background: var(--bg-1);
  overflow: hidden;
}
.style-split-bar span {
  display: block;
  height: 100%;
  border-radius: var(--r-full);
  background: var(--line-3);
  /* The fill grows in on arrival. Transform, not width: scaleX animates on the
     compositor, so no layout work per frame. The inline width is the resting
     truth; the keyframe only sweeps up to it. */
  transform-origin: left center;
  animation: split-fill 700ms var(--ease) backwards;
}
.style-split-row.is-on .style-split-bar span { background: var(--accent); }
@keyframes split-fill { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@media (prefers-reduced-motion: reduce) {
  .style-split-bar span { animation: none; }
}
.style-split-n {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* -- Hold-time facts -------------------------------------------------------
   The rows under each hold-time tile in "How you actually trade": label left,
   figure right, same mono numerals as every other stat on the site. */
.hold-facts {
  margin-top: var(--sp-3);
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
}
.hold-facts > div {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-3);
  font-size: var(--fs-xs);
  color: var(--txt-3);
}
.hold-facts strong {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-weight: var(--fw-reg);
  color: var(--txt-2);
}

/* -- The full read-back ----------------------------------------------------
   Strengths and growth under the behaviour card in "How you actually trade".
   Two columns on wide screens, stacked on narrow; each item is a small plain
   statement — the number, why it matters, and the keep/fix line. */
.assess { margin-top: var(--sp-4); }
.assess-verdict {
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-md);
  border: 1px solid var(--line-1);
  background: var(--bg-2);
  font-size: var(--fs-sm);
  color: var(--txt-2);
  line-height: var(--lh-base);
}
.assess-verdict.tone-commend { border-color: var(--gain-line, var(--line-1)); color: var(--txt-1); }
.assess-cols {
  margin-top: var(--sp-3);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: var(--sp-4);
  align-items: start;
}
.assess-col-head {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wider);
  color: var(--txt-3);
  margin-bottom: var(--sp-2);
}
.assess-col-head.gain { color: var(--gain); }
.assess-col-head.loss { color: var(--loss); }
.assess-item {
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--line-1);
  border-radius: var(--r-md);
  background: var(--bg-1);
}
.assess-item + .assess-item { margin-top: var(--sp-2); }
.assess-item-head {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-sm);
  color: var(--txt-1);
}
.assess-item-head svg { width: 14px; height: 14px; flex: none; }
.assess-item.is-strength .assess-item-head svg { color: var(--gain); }
.assess-item.is-growth .assess-item-head svg { color: var(--accent); }
.assess-item p {
  margin-top: var(--sp-2);
  font-size: var(--fs-xs);
  color: var(--txt-3);
  line-height: 1.55;
}
.assess-item-next { color: var(--txt-2); }
.assess-item-next span {
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wider);
  color: var(--txt-4);
  margin-right: var(--sp-2);
}

/* Arrival: the read-back and coach card ease up as they enter, staggered so the
   section reads top to bottom. Transform + opacity only — compositor work. */
.style-coach, .assess-verdict, .assess-item {
  animation: assess-in 480ms var(--ease) backwards;
}
.assess-verdict { animation-delay: 80ms; }
.assess-item:nth-child(2) { animation-delay: 140ms; }
.assess-item:nth-child(3) { animation-delay: 200ms; }
.assess-item:nth-child(4) { animation-delay: 260ms; }
.assess-item:nth-child(5) { animation-delay: 320ms; }
@keyframes assess-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .style-coach, .assess-verdict, .assess-item { animation: none; }
}

/* The read-back icons breathe. A strength's check glows its green softly; a
   growth item's crosshair pulses toward the accent — alive, but quiet enough to
   sit beside the numbers without competing. Compositor-only properties. */
.assess-item.is-strength .assess-item-head svg {
  animation: assess-glow-gain 2.6s ease-in-out infinite;
}
.assess-item.is-growth .assess-item-head svg {
  animation: assess-glow-focus 2.6s ease-in-out infinite;
}
@keyframes assess-glow-gain {
  0%, 100% { opacity: 0.75; filter: drop-shadow(0 0 0 rgba(47, 191, 113, 0)); transform: scale(1); }
  50%      { opacity: 1;    filter: drop-shadow(0 0 6px rgba(47, 191, 113, 0.55)); transform: scale(1.12); }
}
@keyframes assess-glow-focus {
  0%, 100% { opacity: 0.75; filter: drop-shadow(0 0 0 rgba(122, 184, 255, 0)); transform: scale(1) rotate(0deg); }
  50%      { opacity: 1;    filter: drop-shadow(0 0 6px rgba(122, 184, 255, 0.5)); transform: scale(1.12) rotate(90deg); }
}
@media (prefers-reduced-motion: reduce) {
  .assess-item.is-strength .assess-item-head svg,
  .assess-item.is-growth .assess-item-head svg { animation: none; }
}

/* -- My trading rules ------------------------------------------------------
   The trader's own written plan, entered beside the "I followed my plan"
   toggle it gives meaning to. */
.own-rules { margin-top: var(--sp-3); }
.own-rules-list { display: flex; flex-direction: column; gap: var(--sp-1); margin-bottom: var(--sp-3); }
.own-rule {
  display: flex; align-items: baseline; gap: var(--sp-3);
  font-size: var(--fs-xs); color: var(--txt-2); line-height: 1.5;
}
.own-rule .num { color: var(--txt-4); font-size: var(--fs-2xs); min-width: 14px; text-align: right; }

/* The invitation to write a plan, shown only while none exists. */
.assess-plan-prompt {
  display: flex; gap: var(--sp-3); align-items: flex-start;
  margin-top: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border: 0;
  border-radius: var(--r-md);
  background: color-mix(in srgb, var(--accent) 7%, transparent);
}
.assess-plan-prompt svg { width: 15px; height: 15px; flex: none; color: var(--accent); margin-top: 2px; }
.assess-plan-prompt p { font-size: var(--fs-xs); color: var(--txt-2); line-height: 1.55; max-width: 80ch; }
.assess-plan-prompt a { color: var(--accent); text-decoration: none; white-space: nowrap; }
.assess-plan-prompt a:hover { text-decoration: underline; }

/* -- Currencies-to-alert dropdown ------------------------------------------ */
.ccy-dd { position: relative; }
.ccy-dd-btn {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-3); width: 100%; cursor: pointer; text-align: left;
  font-family: var(--font-mono);
}
.ccy-dd-btn svg { width: 14px; height: 14px; color: var(--txt-3); flex: none; }
.ccy-dd-panel {
  position: absolute; z-index: 30; top: calc(100% + 4px); left: 0; right: 0;
  padding: var(--sp-3);
  background: var(--bg-3);
  border: 1px solid var(--line-3);
  border-radius: var(--r-md);
  box-shadow: var(--sh-2);
  display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--sp-1) var(--sp-3);
}
.ccy-dd-panel .is-all, .ccy-dd-panel hr { grid-column: 1 / -1; }
.ccy-opt {
  display: flex; align-items: center; gap: var(--sp-2);
  font-size: var(--fs-xs); font-family: var(--font-mono); color: var(--txt-2);
  cursor: pointer; padding: 3px 2px; border-radius: var(--r-sm);
}
.ccy-opt:hover { color: var(--txt-1); background: var(--bg-2); }

/* Methodology tables: long explanation cells wrap safely inside the card and
   never run into its edge. */
[data-collapse] .table td { white-space: normal; overflow-wrap: break-word; }
[data-collapse] .table td:first-child { white-space: nowrap; padding-right: var(--sp-3); }
[data-collapse] .table { table-layout: auto; width: 100%; }

/* One-line plain-language explainer under a metric tile — what the number
   measures, said briefly, so no figure sits on screen unexplained. */
.kpi-help {
  margin-top: var(--sp-2);
  font-size: var(--fs-xs);
  color: var(--txt-2);        /* readable against the wash, colorblind-safe contrast */
  line-height: 1.5;
  max-width: 36ch;
  position: relative; z-index: 1;
}

/* The tile's tone wash breathes: a soft pool of the tile's own colour drifting
   slowly behind the numbers. Transform-only, so it costs nothing, and it sits
   under the text (z-index 0 vs 1) so legibility never pays for the motion. */
.kpi::after {
  content: '';
  position: absolute;
  inset: -40%;
  background: radial-gradient(closest-side, var(--kpi-wash, transparent), transparent 70%);
  animation: kpi-breathe 9s ease-in-out infinite alternate;
  pointer-events: none;
  z-index: 0;
}
.kpi.tone-gain::after   { animation-delay: -3s; }
.kpi.tone-loss::after   { animation-delay: -6s; }
.kpi-label, .kpi-value, .kpi-foot { position: relative; z-index: 1; }
@keyframes kpi-breathe {
  from { transform: translate(-8%, -6%) scale(0.9); opacity: 0.55; }
  to   { transform: translate(8%, 6%) scale(1.15); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) { .kpi::after { animation: none; } }

.debt-tile.is-world { grid-column: 1 / -1; }
.debt-tile.is-world 
.wdc-row {
  display: grid;
  grid-template-columns: minmax(160px, 1.2fr) 1.4fr 0.8fr 1.2fr;
  gap: var(--sp-3);
  align-items: center;
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--line-1);
  border-radius: var(--r-md);
  background: var(--bg-1);
}
.wdc-row + .wdc-row { margin-top: var(--sp-2); }
.wdc-row.is-world { border-color: var(--accent-line); background: var(--bg-2); }
.wdc-name { font-weight: var(--fw-bold); letter-spacing: 0.04em; font-size: var(--fs-md); }
.wdc-pop {
  font-family: var(--font-mono); font-size: var(--fs-2xs);
  color: var(--txt-3); margin-top: 2px; letter-spacing: 0.05em;
}
.wdc-pop .num { color: var(--txt-2); }
.wdc-cell { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.wdc-pill {
  align-self: flex-start;
  font-family: var(--font-mono); font-size: 9px; font-weight: var(--fw-bold);
  text-transform: uppercase; letter-spacing: 0.08em;
  padding: 1px 8px; border-radius: var(--r-full);
  background: var(--bg-3); color: var(--txt-3);
  border: 1px solid var(--line-2);
}
.wdc-pill.is-debt { background: rgba(229, 72, 77, 0.18); color: #ff8b8f; border-color: rgba(229, 72, 77, 0.4); }
.wdc-pill.is-gdp  { background: rgba(47, 191, 113, 0.15); color: #6fdfa3; border-color: rgba(47, 191, 113, 0.35); }
.wdc-num {
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-md);
  color: var(--txt-1);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.wdc-num.is-debt { color: #ff6b70; text-shadow: 0 0 14px rgba(229, 72, 77, 0.35); }
.wdc-num.is-gdp  { color: #4fd08a; text-shadow: 0 0 14px rgba(47, 191, 113, 0.3); }
.wdc-row.is-world .wdc-num { font-size: var(--fs-lg); }
@media (max-width: 860px) {
  .wdc-row { grid-template-columns: 1fr 1fr; }
  .wdc-who { grid-column: 1 / -1; }
}

/* Per-nation clock wall — the usdebtclock main-page anatomy on our surface. */
.wdc-detail-head { font-size: var(--fs-md); margin-bottom: var(--sp-3); color: var(--txt-1); }
.wdc-wall {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--sp-3);
}
.wdc-tile {
  border: 1px solid var(--line-1);
  border-radius: var(--r-md);
  background: var(--bg-1);
  padding: var(--sp-3) var(--sp-4);
  display: flex; flex-direction: column; gap: 4px;
}
.wdc-big {
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-xl);
  color: var(--txt-1);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.wdc-tile.is-debtT .wdc-big { color: #ff6b70; text-shadow: 0 0 16px rgba(229, 72, 77, 0.35); }
.wdc-tile.is-gdpT .wdc-big  { color: #4fd08a; text-shadow: 0 0 16px rgba(47, 191, 113, 0.3); }
#wdc-nations button { font-size: var(--fs-2xs); }

/* Why a metric is blank, and what to do about it. Deliberately distinct from the
   ordinary explainer: a blank number is a question the user is already asking. */
.kpi-needs {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
  margin-top: var(--sp-2);
  padding: var(--sp-2) var(--sp-3);
  /* No dashed frame. A dashed box reads as a placeholder or a drop target, which
     is wrong for a permanent explanation — and it draws more attention than the
     number it is explaining. A soft tint carries the same grouping quietly. */
  border: 0;
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--accent) 7%, transparent);
  font-size: var(--fs-2xs);
  color: var(--txt-2);
  line-height: 1.5;
  max-width: 38ch;
}
.kpi-needs svg { width: 12px; height: 12px; flex: none; color: var(--accent); margin-top: 2px; }

/* Habits card. Its own card so it can be folded away — the page is long enough
   without it. Two things matter here: nothing may touch (each habit is a separate
   reading and needs its own air), and the card carries a slow ambient glow so it
   reads as the living, watching part of the page rather than another table. The
   glow is behind the content at low alpha and never sits under text. */
.habit-card { position: relative; overflow: hidden; isolation: isolate; }
.habit-card::before {
  content: '';
  position: absolute;
  inset: -40%;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(38% 44% at 18% 16%, color-mix(in srgb, var(--accent) 22%, transparent), transparent 70%),
    radial-gradient(34% 40% at 82% 78%, color-mix(in srgb, var(--gain) 18%, transparent), transparent 70%),
    radial-gradient(30% 36% at 62% 24%, color-mix(in srgb, var(--loss) 12%, transparent), transparent 72%);
  filter: blur(46px);
  opacity: .5;
  animation: habit-drift 18s ease-in-out infinite alternate;
}
@keyframes habit-drift {
  0%   { transform: translate3d(0, 0, 0) scale(1); opacity: .38; }
  50%  { transform: translate3d(2%, -3%, 0) scale(1.08); opacity: .6; }
  100% { transform: translate3d(-2%, 2%, 0) scale(1.03); opacity: .44; }
}

/* Breathing room. The two columns and every item inside them were sitting flush
   against the card edge and against each other; each habit is its own statement. */
.habit-card .card-body,
.habit-card .assess-cols { padding-top: var(--sp-2); }
.habit-card .assess-cols {
  gap: var(--sp-5);
  align-items: start;
}
.habit-card .assess-col { display: flex; flex-direction: column; gap: var(--sp-3); }
.habit-card .assess-col-head { margin-bottom: var(--sp-1); }
.habit-card .assess-item {
  padding: var(--sp-4);
  border-radius: var(--r-lg);
  background: color-mix(in srgb, var(--bg-1) 88%, transparent);
  backdrop-filter: blur(2px);
  animation: habit-rise .38s cubic-bezier(.22,.61,.36,1) both;
  transition: transform .18s ease, box-shadow .18s ease;
}
.habit-card .assess-item:hover {
  transform: translateY(-1px);
  box-shadow: 0 6px 22px -12px color-mix(in srgb, var(--accent) 55%, transparent);
}
.habit-card .assess-item + .assess-item { margin-top: 0; }
.habit-card .assess-col:nth-child(2) .assess-item { animation-delay: .06s; }
.habit-card .assess-item:nth-child(3) { animation-delay: .05s; }
.habit-card .assess-item:nth-child(4) { animation-delay: .09s; }
.habit-card .assess-item:nth-child(5) { animation-delay: .12s; }
.habit-card .assess-item:nth-child(6) { animation-delay: .15s; }
.habit-card .assess-item:nth-child(7) { animation-delay: .18s; }
.habit-card .assess-item p {
  font-size: var(--fs-sm);
  line-height: 1.62;
  color: var(--txt-2);
  margin: 0 0 var(--sp-2);
}
.habit-card .assess-item p:last-child { margin-bottom: 0; }
.habit-card .assess-item-head { margin-bottom: var(--sp-2); gap: var(--sp-2); }
.habit-card .assess-item-head strong { font-size: var(--fs-sm); line-height: 1.42; }
.habit-card .assess-item-next { color: var(--txt-2); padding-top: var(--sp-2); }
.habit-card .field-hint { margin-top: var(--sp-4); }
@keyframes habit-rise {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
@media (max-width: 760px) {
  .habit-card .assess-cols { gap: var(--sp-4); }
}
@media (prefers-reduced-motion: reduce) {
  .habit-card .assess-item { animation: none; transition: none; }
  .habit-card::before { animation: none; }
}

/* Dashboard Discipline card ONLY. An earlier version of this block restyled the
   bare .dl class, which silently changed entry, trade and journal-edit pages that
   share it — pages nobody asked to change. Scoped to .dl-disc now: the base .dl
   above stays exactly as it always was everywhere else. Bold labels with a short
   caption under each, numbers right-aligned in tabular figures, real gutters so
   nothing touches. */
.dl.dl-disc {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--sp-3) var(--sp-5);
  align-items: baseline;
}
.dl.dl-disc dt {
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-family: var(--font-sans);
  font-size: var(--fs-sm);
  font-weight: var(--fw-semi);
  text-transform: none;
  letter-spacing: normal;
  color: var(--txt-1);
}
.dl.dl-disc dd {
  margin: 0;
  text-align: right;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  padding-left: var(--sp-4);
}
.dl.dl-disc .dl-note {
  font-size: var(--fs-xs);
  color: var(--txt-3);
  font-weight: 400;
  line-height: 1.4;
}

/* Habit items carry their verdict in the colour, not just the icon: green for a
   habit worth keeping, red for one worth breaking, on BOTH edges so the shape
   reads as a symmetric card rather than a one-sided accent tab. The fill is a
   very low-alpha wash of the same hue — enough to separate the two columns at a
   glance, light enough that the text keeps its contrast. */
.habit-card .assess-item.is-strength {
  border-left: 2px solid color-mix(in srgb, var(--gain) 60%, transparent);
  border-right: 2px solid color-mix(in srgb, var(--gain) 60%, transparent);
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--gain) 9%, transparent),
      color-mix(in srgb, var(--gain) 3%, transparent)),
    color-mix(in srgb, var(--bg-1) 92%, transparent);
}
.habit-card .assess-item.is-growth {
  border-left: 2px solid color-mix(in srgb, var(--loss) 60%, transparent);
  border-right: 2px solid color-mix(in srgb, var(--loss) 60%, transparent);
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--loss) 9%, transparent),
      color-mix(in srgb, var(--loss) 3%, transparent)),
    color-mix(in srgb, var(--bg-1) 92%, transparent);
}
.habit-card .assess-item.is-growth .assess-item-head svg { color: var(--loss); }
.habit-card .assess-item.is-strength:hover {
  box-shadow: 0 6px 22px -12px color-mix(in srgb, var(--gain) 65%, transparent);
}
.habit-card .assess-item.is-growth:hover {
  box-shadow: 0 6px 22px -12px color-mix(in srgb, var(--loss) 65%, transparent);
}

/* The card body was missing, so the item rectangles ran to the card edges.
   These give the grid real gutters on every side and keep the last item clear
   of the closing note. */
.habit-card .card-body { padding: var(--sp-4) var(--sp-5) var(--sp-5); }
.habit-card .assess-cols { margin-top: var(--sp-3); padding: 0; }
.habit-card .assess-col:last-child { margin-bottom: 0; }
@media (max-width: 760px) {
  .habit-card .card-body { padding: var(--sp-3) var(--sp-4) var(--sp-4); }
}

/* Bad habits are shown as a pair: the trigger, then the substitute. The two rows
   are labelled like the "Keep it" row on the good side so the card stays visually
   consistent, but the wording is deliberately not the journal's IF/THEN — that
   frame belongs to a forward commitment, this one to a routine already running. */
.habit-card .habit-cue,
.habit-card .habit-instead {
  font-size: var(--fs-sm);
  color: var(--txt-2);
  line-height: 1.6;
  margin: 0;
  padding-top: var(--sp-2);
}
.habit-card .habit-cue span,
.habit-card .habit-instead span {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wider);
  color: var(--txt-3);
  margin-bottom: 2px;
}
/* The prescription is green, not red. Red is an avoidance signal — it tells the
   reader to move away from what it marks, which is exactly wrong on the one line
   asking them to move toward something. Green also makes the colour mean one
   consistent thing on this card: the behaviour we want. It marks "Keep it" in the
   good column, and here it marks the way out of the bad one. The red container
   still carries the diagnosis; only the way forward is green. */
.habit-card .habit-instead span { color: var(--gain); }
.habit-card .habit-instead {
  border-top: 1px solid color-mix(in srgb, var(--gain) 22%, transparent);
  margin-top: var(--sp-2);
}

/* Embedded video. Capped rather than full-bleed: it should be clearly watchable
   without pushing the meeting dates and the rest of the card off the screen. */
.video-embed {
  position: relative;
  width: 100%;
  max-width: 640px;
  aspect-ratio: 16 / 9;
  border: 1px solid var(--line-2);
  border-radius: var(--r-lg);
  overflow: hidden;
  background: #000;
}
.video-embed iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}

/* Event titles link out to the Forex Factory calendar for that day. Styled as
   text, not as a button — the row is a data row first; the link is there when
   wanted and invisible when not. */
.cal-link {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.cal-link:hover,
.cal-link:focus-visible {
  color: var(--accent);
  border-bottom-color: color-mix(in srgb, var(--accent) 45%, transparent);
}

/* Settings: the privacy/session switches carry long explanations, and stacked in
   one column they turned the page into a scroll. Two columns use the empty right
   side and roughly halve the height. Collapses to one column on narrow screens,
   where side-by-side would squeeze the text instead of helping it. */
.embed-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: var(--sp-4) var(--sp-6);
  align-items: start;
}
.embed-grid > .field { margin-top: 0; }
@media (max-width: 720px) {
  .embed-grid { grid-template-columns: 1fr; gap: var(--sp-4); }
}

/* The mark: a bull candle and a bear candle, both alive. They breathe against
   each other — one pushing up while the other presses down — which is the whole
   idea of a market in two shapes. Movement is deliberately small: the SVG box,
   the 34px tile and the text beside it never move, so nothing reflows and the
   logo stays exactly where it sits. Transforms only (no width/height/y changes),
   so this composites on the GPU and cannot distort the artwork. */
.brand-candles .bull,
.brand-candles .bear {
  transform-box: fill-box;
  transform-origin: center;
  will-change: transform;
}
.brand-candles .bull { animation: candle-bull 3.6s ease-in-out infinite; }
.brand-candles .bear { animation: candle-bear 3.6s ease-in-out infinite; }

/* Opposed phases: when the bull is at its high the bear is at its low. Kept
   inside ±1.1 units so neither candle can clip the 18x18 viewBox. */
@keyframes candle-bull {
  0%, 100% { transform: translateY(0.5px) scaleY(0.94); }
  50%      { transform: translateY(-1.1px) scaleY(1.1); }
}
@keyframes candle-bear {
  0%, 100% { transform: translateY(-0.5px) scaleY(1.08); }
  50%      { transform: translateY(1.1px) scaleY(0.92); }
}

/* The tile itself gets the faintest lift as the pair crosses, so the mark reads
   as one living object rather than two separate animations. */
.brand-mark { transition: border-color var(--dur-base) var(--ease); }
.brand:hover .brand-mark { border-color: var(--line-3); }
.brand:hover .brand-candles .bull,
.brand:hover .brand-candles .bear { animation-duration: 1.8s; }

@media (prefers-reduced-motion: reduce) {
  .brand-candles .bull,
  .brand-candles .bear { animation: none; }
}

/* Trading-plan rules: one box per rule. Scrolls past roughly seven rows so a
   trader with twenty rules does not push everything below it off the screen —
   the section keeps the same height whether five rules are written or twenty. */
.rules-list {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  max-height: 320px;
  overflow-y: auto;
  padding-right: var(--sp-2);
}
.rule-row { display: flex; align-items: center; gap: var(--sp-2); }
.rule-row .input { flex: 1; min-width: 0; }
.rule-num {
  flex: none;
  width: 1.5rem;
  text-align: right;
  font-family: var(--font-mono);
  font-size: var(--fs-2xs);
  color: var(--txt-3);
}
.rule-del {
  flex: none;
  width: 26px; height: 26px;
  border: 0; border-radius: var(--r-sm);
  background: transparent;
  color: var(--txt-3);
  cursor: pointer;
  line-height: 1;
}
.rule-del:hover { color: var(--loss); background: color-mix(in srgb, var(--loss) 10%, transparent); }

/* Rule-by-rule plan adherence on the entry form. The class (not inline style)
   carries display so the hidden attribute still works. */
.rules-check {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  margin-top: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-md);
  background: color-mix(in srgb, var(--accent) 5%, transparent);
}
.rules-check[hidden] { display: none; }
.rules-check .switch span:last-child { font-size: var(--fs-sm); line-height: 1.45; }

/* A rule earns its check the moment it is written. Gain-green on purpose — the
   same colour that marks a kept habit and a winning row everywhere else, so the
   animation speaks the site's own language. One soft glow on the transition from
   empty to written, then it settles; opening a saved plan shows calm checks with
   no fireworks. */
.rule-row .rule-ok {
  flex: none;
  width: 20px;
  text-align: center;
  font-weight: var(--fw-semi);
  color: var(--gain);
  opacity: 0;
  transform: scale(0.6);
  transition: opacity 0.25s var(--ease), transform 0.25s var(--ease);
}
.rule-row.is-filled .rule-ok { opacity: 1; transform: scale(1); }
.rule-row.glow-once .rule-ok { animation: rule-glow 0.9s ease-out; }
@keyframes rule-glow {
  0%   { text-shadow: none; transform: scale(0.6); }
  40%  { text-shadow: 0 0 12px color-mix(in srgb, var(--gain) 85%, transparent); transform: scale(1.25); }
  100% { text-shadow: none; transform: scale(1); }
}
#plan-starters .chip {
  font-size: var(--fs-xs);
  max-width: 100%;
  white-space: normal;
  text-align: left;
  line-height: 1.35;
}
@media (prefers-reduced-motion: reduce) {
  .rule-row .rule-ok { transition: none; }
  .rule-row.glow-once .rule-ok { animation: none; }
}

/* Sub-dropdowns inside "More of my plan": each area opens on click and nothing
   shows until asked for. Same chips, same look — only the reveal changed. */
.plan-sub { margin: var(--sp-2) 0 var(--sp-4); padding-left: var(--sp-2); }
.plan-sub[hidden] { display: none; }

/* The plan's dropdown headers read as buttons now. They existed before but
   looked like captions, and a control nobody recognises as a control might as
   well not exist — the owner reported the sections "missing" while every one of
   them was live behind these toggles. */
#plan-card .collapse-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: var(--sp-3) var(--sp-4);
  margin-top: var(--sp-2);
  border: 1px solid var(--line-2);
  border-radius: var(--r-md);
  background: var(--bg-1);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
#plan-card .collapse-head:hover { border-color: var(--line-3); background: var(--bg-2); }
#plan-card .collapse-head svg { width: 14px; height: 14px; flex: none; }

/* Coaching cards speak one colour language in both directions: a small green
   check for what is working, a small red cross for what costs — beside a bolded
   header, sized like text, never like an icon that shouts. Faint washes mirror
   the habit card so the direction reads before the words do. */
.coach-pattern > .collapse-head { font-weight: var(--fw-semi); }
.coach-pattern.is-good > .collapse-head::before,
.coach-pattern.is-focus > .collapse-head::before {
  font-size: var(--fs-xs);
  font-weight: 700;
  margin-right: var(--sp-2);
  flex: none;
}
.coach-pattern.is-good > .collapse-head::before { content: '\2713'; color: var(--gain); }
.coach-pattern.is-focus > .collapse-head::before { content: '\2715'; color: var(--loss); }
.coach-pattern.is-good {
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--gain) 5%, transparent),
      color-mix(in srgb, var(--gain) 2%, transparent)),
    var(--bg-1);
}
.coach-pattern.is-focus {
  background:
    linear-gradient(180deg,
      color-mix(in srgb, var(--loss) 5%, transparent),
      color-mix(in srgb, var(--loss) 2%, transparent)),
    var(--bg-1);
}

/* First run: one instruction, nothing else. Every card except the start panel
   and the price ticker is hidden until the account holds a trade — the tool is
   not simpler, it is just quiet until it has something true to say. */
body.is-firstrun .page > .card:not(.is-firstrun),
body.is-firstrun .page > section.card:not(.is-firstrun),
body.is-firstrun .page > .grid,
body.is-firstrun .page > .row:not([data-no-rearrange]) { display: none; }
body.is-firstrun .page > #ticker { display: block; }

.firstrun-steps {
  margin: var(--sp-4) 0 0;
  padding-left: var(--sp-5);
  color: var(--txt-2);
  font-size: var(--fs-sm);
  line-height: 1.9;
  max-width: 74ch;
}
.firstrun-steps strong { color: var(--txt-1); }

/* Undo toast. Wider than a notice because it carries an action, and it does not
   fade on a timer alone — the countdown is visible so nobody is surprised. */
.toast.has-undo {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  padding-right: var(--sp-3);
}
.toast-undo {
  flex: none;
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  background: transparent;
  color: var(--accent);
  border-radius: var(--r-sm);
  padding: 4px 10px;
  font-size: var(--fs-xs);
  font-family: var(--font-mono);
  cursor: pointer;
}
.toast-undo:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
.toast-undo:disabled { opacity: 0.5; cursor: default; }

/* The bin, when you arrive from the header button: a brief ring so it is obvious
   which card the page just jumped to. */
.bin-flash { animation: bin-flash 1.1s ease-out; }
@keyframes bin-flash {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 55%, transparent); }
  60%  { box-shadow: 0 0 0 6px color-mix(in srgb, var(--accent) 22%, transparent); }
  100% { box-shadow: 0 0 0 0 transparent; }
}
@media (prefers-reduced-motion: reduce) { .bin-flash { animation: none; } }

/* Rule starters: one suggestion visible per area, the rest behind the header.
   The card stays the same height whether a trader is browsing one idea or all
   twenty, so opening an area never shoves the boxes above it off the screen. */
.starter-area { margin-bottom: var(--sp-2); }
.starter-area > .collapse-head {
  display: flex; align-items: center; justify-content: space-between;
  width: 100%; padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--line-1); border-radius: var(--r-sm);
  background: var(--bg-1); cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease);
}
.starter-area > .collapse-head:hover { border-color: var(--line-3); }
.starter-area > .collapse-head svg { width: 13px; height: 13px; flex: none; }
.starter-peek { padding: var(--sp-2) 0 var(--sp-1) var(--sp-3); }
.starter-all  { padding: var(--sp-2) 0 var(--sp-2) var(--sp-3); max-height: 260px; overflow-y: auto; }
.starter-one  { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.starter-one .chip { white-space: normal; text-align: left; line-height: 1.35; max-width: 100%; }
.starter-skip {
  flex: none; width: 22px; height: 22px; line-height: 1;
  border: 0; border-radius: var(--r-sm);
  background: transparent; color: var(--txt-3); cursor: pointer;
}
.starter-skip:hover { color: var(--loss); background: color-mix(in srgb, var(--loss) 10%, transparent); }
/* The replacement arrives rather than blinking — it should read as a deck being
   flicked, not as the page re-rendering under the trader. */
.starter-in { animation: starter-in 0.36s var(--ease); }
@keyframes starter-in {
  from { opacity: 0; transform: translateX(10px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) { .starter-in { animation: none; } }

/* One rule per block: the name, a verdict word, a bar you can read at a glance,
   and a sentence saying what the number means. A bare "19 of 28 · 68%" told a
   trader nothing about whether that was good. */
.rule-score { margin-bottom: var(--sp-4); }
.rule-score-head { display: flex; align-items: baseline; gap: var(--sp-3); }
.rule-score-name { flex: 1; min-width: 0; font-weight: var(--fw-semi); color: var(--txt-1); }
.rule-score-verdict {
  flex: none; font-family: var(--font-mono); font-size: var(--fs-2xs);
  text-transform: uppercase; letter-spacing: var(--tracking-wide);
}
.rule-score-verdict.gain { color: var(--gain); }
.rule-score-verdict.warn { color: var(--warn, var(--txt-2)); }
.rule-score-verdict.loss { color: var(--loss); }
.rule-score-bar {
  height: 6px; border-radius: 3px; background: var(--bg-2);
  margin: var(--sp-2) 0 var(--sp-2); overflow: hidden;
}
.rule-score-bar span { display: block; height: 100%; border-radius: 3px; }
.rule-score-bar .is-good { background: var(--gain); }
.rule-score-bar .is-mid  { background: var(--txt-3); }
.rule-score-bar .is-bad  { background: var(--loss); }
.rule-score-note { font-size: var(--fs-xs); color: var(--txt-3); line-height: 1.5; }

/* Expand/Collapse all: the page background was scrolling behind these and eating
   the labels. Give them the page surface and lift them above it. */
/* The entries header. It sticks, so it needs its own surface — but a flat fill of
   the page colour read as a black strip laid over the page rather than part of it.
   It now carries the same surface, border, radius and edge highlight as a card
   head, and frosts what scrolls under it instead of blanking it out. */
.jl-tools {
  position: sticky; top: 0; z-index: 5;
  padding: var(--sp-4) var(--sp-5);
  margin-bottom: var(--sp-4);
  background:
    linear-gradient(180deg, var(--accent-soft) 0%, transparent 62%),
    color-mix(in srgb, var(--bg-1) 88%, transparent);
  -webkit-backdrop-filter: blur(14px) saturate(1.25);
          backdrop-filter: blur(14px) saturate(1.25);
  border: 1px solid var(--accent-line);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-1), inset 0 1px 0 var(--edge-highlight),
              0 0 0 0 transparent;
  animation: jl-breathe 5.5s var(--ease) infinite;
  transition: box-shadow var(--dur-base) var(--ease),
              border-color var(--dur-base) var(--ease);
}
/* A slow breath rather than a pulse. It marks the header as the live thing on the
   page without ever competing with the writing underneath it. */
@keyframes jl-breathe {
  0%, 100% { box-shadow: var(--sh-1), inset 0 1px 0 var(--edge-highlight),
                         0 0 18px -6px var(--accent-glow); }
  50%      { box-shadow: var(--sh-1), inset 0 1px 0 var(--edge-highlight),
                         0 0 34px -4px var(--accent-glow); }
}
/* Lifts once it is actually stuck, so the shadow is a consequence of scrolling
   rather than permanent decoration. */
.jl-tools.is-stuck {
  border-color: var(--accent);
  box-shadow: var(--sh-2), inset 0 1px 0 var(--edge-highlight),
              0 0 30px -4px var(--accent-glow);
  animation: none;
}
.jl-tools h2 { color: var(--accent-hi); }
@media (prefers-reduced-motion: reduce) { .jl-tools { animation: none; } }

/* The key for the three dots, said once in the header rather than on every card. */
.emo-key {
  display: flex; align-items: baseline; gap: var(--sp-2);
  flex-wrap: wrap; margin: var(--sp-3) 0 0;
  font-size: var(--fs-xs); color: var(--txt-3); line-height: var(--lh-normal);
}
.emo-key .emo-strip { transform: translateY(1px); }
.emo-legend {
  display: inline-flex; align-items: center; gap: 4px;
  margin: 0 var(--sp-1); color: var(--txt-2); white-space: nowrap;
}
@media (prefers-reduced-motion: reduce) { .jl-tools { transition: none; } }

/* Browsers without color-mix still get a solid, correct surface. */
@supports not (background: color-mix(in srgb, red 50%, transparent)) {
  .jl-tools { background: var(--bg-1); }
}

/* Delete straight from an entry card — no need to open it first. Quiet until
   the card is hovered, so a list of writing does not read as a row of bins. */
.entry-del {
  flex: none; width: 26px; height: 26px; padding: 0;
  border: 0; border-radius: var(--r-sm);
  background: transparent; color: var(--txt-4);
  cursor: pointer; opacity: 0;
  transition: opacity var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.entry-del svg { width: 14px; height: 14px; }
.entry-card:hover .entry-del, .entry-del:focus-visible { opacity: 1; }
.entry-del:hover { color: var(--loss); background: color-mix(in srgb, var(--loss) 10%, transparent); }
@media (hover: none) { .entry-del { opacity: 1; } }

/* Grades come from two places now — the trader and the app. The pill carries a
   small "You" so a bare letter is never ambiguous, and our read is always
   labelled "Ours" beside it. */
.grade.is-yours { display: inline-flex; align-items: center; gap: 4px; }
/* Whose grade this is, said in words. "You" alone read as a shout rather than a
   label; "Your grade" mirrors "Our grade" so the pair is legible at a glance. */
.grade-who {
  font-family: var(--font-mono); font-size: var(--fs-2xs);
  text-transform: uppercase; letter-spacing: var(--tracking-wide);
  opacity: 0.75; font-weight: 400; margin-right: 0.35em; white-space: nowrap;
}
.tag .grade-who { margin-right: 0.3em; }

/* The account lens sits beside the page title: it changes what every number on
   the page means, so it belongs where the page announces itself. Hidden for a
   trader with a single account. */
.acct-lens { display: flex; align-items: center; gap: var(--sp-2); margin-left: var(--sp-4); }
.acct-lens[hidden] { display: none; }
.acct-lens-label {
  font-family: var(--font-mono); font-size: var(--fs-2xs);
  text-transform: uppercase; letter-spacing: var(--tracking-wide); color: var(--txt-3);
}
.acct-lens .select { width: auto; min-width: 150px; max-width: 230px; padding: 4px 26px 4px 10px; font-size: var(--fs-sm); }
@media (max-width: 860px) { .acct-lens-label { display: none; } .acct-lens .select { min-width: 120px; } }


/* Folded explanations -------------------------------------------------------
   Settings explains every control it offers. Left fully open the page reads as
   a wall of prose and the controls disappear into it, so a long explanation is
   clamped to its opening lines with the rest one click away. Clamping keeps the
   original markup intact — no sentence splitting, no lost emphasis or links. */
/* Height-based, not line-clamp: several of these blocks contain their own
   <p> and <strong> children, and -webkit-line-clamp silently does nothing on a
   container with block-level children — the long ones stayed fully open. A
   max-height with a fade clamps any structure, and the fade signals that there
   is more rather than cutting a sentence dead. */
.is-clamped {
  max-height: 3.1em; overflow: hidden; position: relative;
  -webkit-mask-image: linear-gradient(to bottom, #000 55%, transparent 100%);
          mask-image: linear-gradient(to bottom, #000 55%, transparent 100%);
}
.hint-more {
  display: inline-block; margin-top: var(--sp-1); padding: 0;
  background: none; border: 0; cursor: pointer;
  font-family: var(--font-mono); font-size: var(--fs-2xs);
  letter-spacing: var(--tracking-wide); text-transform: uppercase;
  color: var(--accent); opacity: 0.85;
}
.hint-more:hover { opacity: 1; text-decoration: underline; }
.hint-more:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px; }


/* Touch targets ------------------------------------------------------------
   Controls are sized for a mouse: .btn-sm is 28px tall, which is precise with a
   cursor and fiddly with a thumb. Apple's HIG asks for 44pt and WCAG 2.5.5 for
   44x44 CSS px on touch.

   Scoped to `pointer: coarse` rather than to a screen width, so this is decided
   by how the device is being POINTED AT, not how wide it is — a phone gets the
   bigger targets, a small desktop window does not, and the desktop design is
   left exactly as it was. Padding does the growing so nothing reflows. */
@media (pointer: coarse) {
  .btn, .btn-sm, .btn-icon,
  .seg button, .chip, .tag[role="button"] {
    min-height: 44px;
    display: inline-flex; align-items: center; justify-content: center;
  }
  .btn-sm { height: auto; padding-top: 8px; padding-bottom: 8px; }
  .btn-icon, .btn-icon.btn-sm { min-width: 44px; width: auto; height: auto; }
  .input, .select, .textarea { min-height: 44px; font-size: 16px; }
  /* 16px on inputs is not a style choice: iOS Safari zooms the whole page in on
     focus for anything smaller, and the trader then has to pinch back out. */
  input[type="checkbox"], input[type="radio"], .checkbox { min-width: 22px; min-height: 22px; }
  .entry-del, .row-del, [data-entry-del], [data-del] { min-width: 44px; min-height: 44px; }
  /* Row checkboxes need room around them or the row opens instead of ticking. */
  td .bulk-pick { transform: scale(1.3); margin: var(--sp-2); }
}


/* Selection check boxes -------------------------------------------------------
   A browser-default checkbox is the one control on the Trades page that does not
   belong to this design — square, bright, and drawn by the operating system. This
   is the same shape language as the rest of the app: the card radius, the accent
   for the on state, the same easing everything else animates on. The tick is drawn
   as a rotated border rather than a glyph so it scales with the box and inherits
   the accent colour. */
/* NOTE: deliberately NOT called .tick — that class already belongs to the price
   ticker's items, and reusing it sized every quote on the dashboard to 17px and
   piled them on top of each other. */
.checkbox {
  -webkit-appearance: none; appearance: none;
  position: relative; flex: none;
  width: 17px; height: 17px; margin: 0;
  border: 1px solid var(--border-2);
  border-radius: var(--r-sm);
  background: var(--bg-inset);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease),
              background-color var(--dur-fast) var(--ease),
              box-shadow var(--dur-fast) var(--ease);
}
.checkbox:hover { border-color: var(--accent-line); background: var(--bg-2); }

.checkbox:checked {
  background: var(--accent);
  border-color: var(--accent);
  box-shadow: var(--sh-glow-accent);
}
.checkbox:checked::after {
  content: ''; position: absolute;
  left: 5px; top: 1px; width: 5px; height: 10px;
  border: solid var(--on-accent);
  border-width: 0 2px 2px 0;
  transform: rotate(42deg);
}
/* Partial selection on the header box: a dash, not a tick, because "some" is not
   "all" and a tick there would misreport what is selected. */
.checkbox:indeterminate {
  background: var(--accent-soft);
  border-color: var(--accent-line);
}
.checkbox:indeterminate::after {
  content: ''; position: absolute;
  left: 3px; top: 7px; width: 9px; height: 2px;
  background: var(--accent-hi); border-radius: 1px;
}
.checkbox:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* The column needs a name, or a bare box at the head of a table explains nothing. */
.th-sub {
  display: block; margin-top: 2px;
  font-family: var(--font-mono); font-size: var(--fs-2xs);
  letter-spacing: var(--tracking-wide); text-transform: uppercase;
  color: var(--txt-4); font-weight: var(--fw-normal);
}
/* A selected row reads as selected, so it is obvious what an action will hit. */
tr:has(.bulk-pick:checked) { background: var(--accent-soft); }
@media (prefers-reduced-motion: reduce) { .checkbox { transition: none; } }


/* The two grades on an entry card: the trader's and the app's, read as one unit.
   They wrap together rather than separately, so a narrow card never leaves one
   grade stranded on a line of its own. */
.grade-pair {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  flex-wrap: wrap; margin-left: var(--sp-2);
}
/* The app's grade is deliberately quieter than the trader's own. Theirs is the
   judgement they made; ours is a second opinion, and it should not shout over it. */
.grade.is-ours { opacity: 0.82; font-weight: var(--fw-med); }
.grade.is-ours:hover { opacity: 1; }


/* Whether one specific coaching finding is moving. Colour carries the direction so
   it reads before it is read: fading green, newly appeared red, unchanged neutral. */
.coach-trend {
  margin: var(--sp-2) 0 0;
  font-family: var(--font-mono); font-size: var(--fs-xs);
  color: var(--txt-3);
  padding-left: var(--sp-3); border-left: 2px solid var(--line-2);
}
.coach-trend.is-better { color: var(--gain); border-left-color: var(--gain-line); }
.coach-trend.is-worse  { color: var(--loss); border-left-color: var(--loss-line); }
.coach-trend.is-flat   { color: var(--txt-3); border-left-color: var(--line-2); }


/* A folded KPI explanation sits in a tight tile, so it clamps shorter than a page
   paragraph and its toggle stays out of the number's way. */
.kpi-help.is-clamped { max-height: 2.6em; }
.kpi-help + .hint-more { margin-top: 2px; font-size: var(--fs-2xs); }


/* "Are you getting better?" rows -------------------------------------------
   The label used to be flex:1, which stretched it across the whole card and
   threw the numbers against the far right edge — a stripe of dead space in the
   middle of every row, and the eye had to travel to connect a measure to its
   figures.

   One grid owns all the rows in a group, so the columns size to their widest
   content and every row still lines up. justify-content:start keeps the block
   from stretching, so label and numbers stay next to each other however wide
   the screen gets. */
.prog-rows {
  display: grid;
  /* A fixed label track rather than auto: "Moving the right way" and "Slipping"
     are two separate grids, so an auto column sized each group to its own longest
     label and the two lists did not line up with each other. One shared width
     keeps every row in the card on the same vertical rhythm. */
  grid-template-columns: 1.1rem minmax(0, auto) max-content;
  justify-content: start;
  align-items: baseline;
  column-gap: var(--sp-3);
  row-gap: var(--sp-2);
}
.prog-row { display: contents; }
.prog-label { min-width: 0; }
/* The arrow stays tight against its label; the figures get their own breathing
   room. A single column-gap could not do both — one value made the arrow float
   away from the word or the numbers crowd it. This separates the two. */
.prog-nums { white-space: nowrap; padding-left: var(--sp-5); }

@media (max-width: 900px) { .prog-nums { padding-left: var(--sp-4); } }
@media (max-width: 640px) { .prog-nums { padding-left: var(--sp-3); } }

/* Narrow screens: the block goes full width so long labels can use the room,
   and the numbers still sit immediately after their label rather than adrift. */
/* Only on genuinely narrow screens does the label take the remaining room; above
   that the columns stay sized to their content so the figures sit near the words. */
@media (max-width: 620px) {
  .prog-rows { grid-template-columns: 1.1rem minmax(0, 1fr) max-content; justify-content: stretch; }
}
