/* ─────────────────────────────────────────────────────────────────────────
   milo
   Terminal-leaning, but the top of the page is a cat, not a console.

   Contrast note: every muted ratio below is measured against --panel
   (#131316), not --bg. Checking muted text against the darker page bg
   flatters it by ~0.3 and lands under the 4.5 AA floor.
   ───────────────────────────────────────────────────────────────────────── */

:root {
  /* Neutral dark. The previous palette was blue-purple tinted with a coloured
     wash over the whole page, which read as "neon terminal" rather than a dark
     theme. Surfaces are now grey; colour only appears where it carries meaning
     — a number that is up or down, a state that matters. */
  --bg:      #0A0A0C;
  --bg-2:    #0E0E11;
  --panel:   #131316;
  --panel-2: #191920;
  --line:    #26262D;
  --line-hi: #38383F;

  /* Ratios measured against --panel (#131316), where most text sits. */
  --fg:      #E9E9ED;   /* 15.1:1 */
  --fg-dim:  #A8A8B2;   /*  7.6:1 */
  --fg-mut:  #82828E;   /*  4.6:1 — the AA floor for body text */

  /* Desaturated a step. The old values were fine on contrast but glowed. */
  --green:  #3ECF8E;
  --purple: #9B7BE8;
  --amber:  #E0A94A;
  --red:    #E8716E;
  --cyan:   #56B6C2;

  --r: 4px;
  --pad: clamp(.85rem, 2.6vw, 1.7rem);
  --maxw: 1180px;
  --ease: cubic-bezier(.2,.7,.3,1);
  --spring: cubic-bezier(.34,1.4,.64,1);
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0; background: var(--bg); color: var(--fg);
  font: 400 14px/1.6 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  overflow-x: hidden; min-height: 100vh;
}
@media (min-width: 720px) { body { font-size: 14.5px; } }

.dim { color: var(--fg-mut); }
.mono { font-variant-numeric: tabular-nums; }
a { color: var(--cyan); text-decoration: none; }
a:hover { text-decoration: underline; }
:where(a, button, [tabindex]):focus-visible { outline: 2px solid var(--green); outline-offset: 2px; }

.sr {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
.skip {
  position: fixed; top: -80px; left: 8px; z-index: 300;
  background: var(--green); color: #08150F; padding: .7rem 1rem; font-weight: 700;
  transition: top .15s var(--ease);
}
.skip:focus { top: 6px; }

/* Scanlines, dialled well back — texture, not an effect. */
.scan {
  position: fixed; inset: 0; z-index: 60; pointer-events: none;
  background: repeating-linear-gradient(180deg, rgba(255,255,255,.010) 0 1px, transparent 1px 3px);
  mix-blend-mode: overlay;
}
/* Was a purple/green radial wash across the whole page — that colour cast is
   what stopped it reading as a dark theme. Neutral lift at the top only. */
.glow {
  position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background: radial-gradient(80% 50% at 50% 0%, rgba(255,255,255,.035), transparent 72%);
}

/* ═══ INTERACTION ════════════════════════════════════════════════════
   Everything here is transform / opacity / box-shadow only — no property
   that triggers layout, so none of it can cost a frame. All of it dies
   under prefers-reduced-motion or the [motion] toggle.

   The glow follows the pointer via two CSS vars written from rAF; the
   element itself never re-renders. */

.lit { position: relative; isolation: isolate; }
.lit::before {
  content: ''; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  border-radius: inherit; opacity: 0;
  background: radial-gradient(340px circle at var(--mx, 50%) var(--my, 50%),
              color-mix(in oklab, var(--accent, var(--green)) 13%, transparent), transparent 62%);
  transition: opacity .35s var(--ease);
}
.lit:hover::before, .lit:focus-within::before { opacity: 1; }

/* Edge light — a thin accent line that wipes in from the left on hover. */
.lit::after {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 2px;
  background: var(--accent, var(--green)); border-radius: inherit;
  transform: scaleY(0); transform-origin: 50% 0;
  transition: transform .32s var(--ease);
  pointer-events: none;
}
.lit:hover::after, .lit:focus-within::after { transform: scaleY(1); }

/* Lift + a soft accent shadow. */
.rise { transition: transform .25s var(--ease), box-shadow .25s var(--ease), border-color .25s var(--ease); }
.rise:hover {
  transform: translateY(-2px);
  border-color: color-mix(in oklab, var(--accent, var(--line-hi)) 45%, var(--line));
  box-shadow: 0 6px 24px -10px color-mix(in oklab, var(--accent, #000) 40%, transparent),
              0 2px 8px -4px rgba(0,0,0,.6);
}

/* A value that just changed. Flashes its own accent, then settles. */
@keyframes tick-flash {
  0%   { color: var(--accent, var(--green)); text-shadow: 0 0 14px color-mix(in oklab, var(--accent, var(--green)) 55%, transparent); }
  100% { text-shadow: 0 0 0 transparent; }
}
.flash { animation: tick-flash 1.1s var(--ease); }

/* A row that just arrived. */
@keyframes row-in {
  from { opacity: 0; transform: translateX(-8px); }
  to   { opacity: 1; transform: none; }
}
.fresh-row { animation: row-in .4s var(--ease); }

/* Sections fade up as they come into view, once. */
.reveal { opacity: 0; transform: translateY(14px); transition: opacity .6s var(--ease), transform .6s var(--ease); }
.reveal.in { opacity: 1; transform: none; }

/* Press feedback on anything clickable. */
button:active:not(:disabled) { transform: scale(.97); }
.btn, .sq, .dots button { transition: color .2s var(--ease), border-color .2s var(--ease), transform .1s var(--ease), background .2s var(--ease); }

/* Focus is a glow, not just an outline. */
:where(a, button, [tabindex]):focus-visible {
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--green) 28%, transparent);
}

/* The status dot breathes a halo while he is awake. */
@keyframes halo {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in oklab, currentColor 55%, transparent); }
  50%      { box-shadow: 0 0 0 5px transparent; }
}
.led.live { color: inherit; animation: blip 2.2s steps(1) infinite, halo 2.2s var(--ease) infinite; }

/* Nothing moves when motion is off. */
html[data-motion="off"] .lit::before,
html[data-motion="off"] .lit::after { display: none; }
html[data-motion="off"] .rise:hover { transform: none; box-shadow: none; }
html[data-motion="off"] .flash,
html[data-motion="off"] .fresh-row,
html[data-motion="off"] .led { animation: none !important; }
html[data-motion="off"] .reveal { opacity: 1; transform: none; transition: none; }

/* ── bar ─────────────────────────────────────────────────────────────── */
/* One sticky block for the bar and the tape. --top-h is measured in JS (the
   bar wraps) and feeds scroll-padding, so an anchor never lands underneath. */
.top { position: sticky; top: 0; z-index: 50; }
html { scroll-padding-top: calc(var(--top-h, 84px) + .75rem); }

.bar {
  display: flex; align-items: center; flex-wrap: wrap; gap: .5rem;
  padding: 0 var(--pad); min-height: 46px; font-size: 12.5px;
  background: color-mix(in oklab, var(--bg) 90%, transparent);
  backdrop-filter: blur(10px); border-bottom: 1px solid var(--line);
}
.bar-id { color: var(--green); font-weight: 700; }
.sep { color: var(--line-hi); }
.hide-sm { display: none; }
@media (min-width: 620px) { .hide-sm { display: inline; } }
.pill { display: inline-flex; align-items: center; gap: .4rem; color: var(--fg-dim); }
.led { width: 7px; height: 7px; border-radius: 50%; background: var(--fg-mut); flex: 0 0 auto; }
/* The light reports the LINK, not a mood. "asleep" and "unreachable" looked
   identical to a visitor, and only one of them is a problem. */
.led.live  { background: var(--green); animation: blip 2.2s steps(1) infinite; }
.led.stale { background: var(--amber); }
.led.down, .led.broken { background: var(--red); }
@keyframes blip { 0%,70%{opacity:1} 71%,100%{opacity:.25} }

/* On narrow screens the nav wraps; pushing it right with margin-left:auto
   then stranded [motion] on a third row. Let them share a line instead. */
.bar-nav { display: flex; flex-wrap: wrap; }
@media (min-width: 620px) { .bar-nav { margin-left: auto; } }
.bar-nav a {
  color: var(--fg-mut); display: inline-flex; align-items: center;
  min-height: 44px; padding: 0 .38rem;
}
@media (min-width: 620px) { .bar-nav a { padding: 0 .5rem; } }
.bar-nav a::before { content: '['; color: var(--line-hi); }
.bar-nav a::after  { content: ']'; color: var(--line-hi); }
.bar-nav a:hover, .bar-nav a[aria-current] { color: var(--green); text-decoration: none; }
.btn {
  font: inherit; font-size: 12.5px; cursor: pointer; color: var(--fg-mut);
  background: none; border: 0; display: inline-flex; align-items: center;
  min-height: 44px; padding: 0 .5rem;
}
.btn:hover { color: var(--fg); }
.btn[aria-pressed="true"] { color: var(--amber); }

/* ── layout ──────────────────────────────────────────────────────────── */
main { max-width: var(--maxw); margin: 0 auto; padding: var(--pad); display: grid; gap: clamp(1.4rem,4vw,2.8rem); }
/* Grid items default to min-width:auto, so the horizontally-scrolling feed
   rail refuses to shrink below its content and drags `main` out to ~1700px,
   taking every other section with it. min-width:0 is what makes overflow-x
   actually scroll instead of expand. */
.sec { scroll-margin-top: 58px; min-width: 0; }
.sec-hd { display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; margin-bottom: .7rem; flex-wrap: wrap; }
.eyebrow {
  font-size: 12.5px; font-weight: 700; letter-spacing: .16em; text-transform: uppercase;
  color: var(--green); margin: 0;
}
.eyebrow::before { content: '// '; color: var(--line-hi); }

.panel { background: var(--panel); border: 1px solid var(--line); border-radius: var(--r); }
.panel-hd {
  display: flex; align-items: baseline; justify-content: space-between;
  flex-wrap: wrap; gap: .1rem .8rem;
  padding: .55rem .8rem; border-bottom: 1px solid var(--line);
  background: var(--panel-2); font-size: 12.5px; color: var(--fg-dim);
}
.panel-hd.sub { border-top: 1px solid var(--line); }

/* Where the numbers come from. Quiet, but always there. */
.wallet-bar {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: .1rem .6rem;
  padding: .4rem .8rem; border-bottom: 1px solid var(--line);
  background: var(--bg-2); font-size: 11px;
}
.wal { color: var(--fg-dim); font-variant-numeric: tabular-nums; word-break: break-all; }
.wal[href]:hover { color: var(--cyan); text-decoration: underline; }
/* No href means no wallet configured — it must not look clickable. */
.wal:not([href]) { color: var(--fg-mut); cursor: default; }
/* The title must never break; the meta may wrap under it on a narrow screen.
   Without this the two interleave and read as one garbled line. */
.panel-hd > span:first-child { color: var(--fg); flex: 0 0 auto; white-space: nowrap; }
.panel-hd > span + span { flex: 1 1 auto; text-align: right; min-width: 0; }

/* ═══ 1. NOW ═════════════════════════════════════════════════════════ */
/* Two columns, two rows, and the second row is where the fix actually lives.
   The portrait spans both rows and carries its own height (4:5 of a 400px
   column = 500px), so it is the thing that sets the stage's height. Row 1 is
   the text at its natural size; row 2 is `1fr`, which means it receives
   whatever the portrait's height leaves over — no more, no less.
   That is what makes the feed END level with the portrait instead of merely
   starting near it. `align-items: start` had to go: it was telling every item
   to keep its own height and ignore the row. */
.now-stage {
  display: grid;
  grid-template-columns: minmax(0, 400px) minmax(0, 1fr);
  grid-template-rows: auto 1fr;
  gap: clamp(1rem, 3vw, 2rem);
}
.portrait { grid-column: 1; grid-row: 1 / span 2; align-self: start; }
.now-body { grid-column: 2; grid-row: 1; align-self: start; }
.now-feed {
  grid-column: 2; grid-row: 2;
  min-width: 0; min-height: 0;
  display: grid; grid-template-rows: auto 1fr; gap: .55rem;
}

/* Not wide enough for two boxes beside a 400px portrait. Rather than let them
   stack into a tower taller than the portrait, the feed drops to full width
   below the stage — where two across fits comfortably again. */
@media (max-width: 1100px) {
  .portrait { grid-row: 1; }
  .now-feed { grid-column: 1 / -1; grid-row: 2; margin-top: .4rem; }
}

@media (max-width: 780px) {
  .now-stage { grid-template-columns: 1fr; }
  /* grid-ROW has to be reset too, not just the column. Clearing only the column
     left .now-body pinned to row 1 and .now-feed to row 2, so the portrait —
     the one element set to `auto` — was placed after both of them and the page
     opened with the text, then the feed, then the cat at the bottom. */
  .portrait, .now-body, .now-feed { grid-column: 1; grid-row: auto; }
  /* Stacked, the portrait would otherwise take the column's full width — 716px
     wide and 895px tall on a 773px screen, which is the entire viewport before
     you reach a single number. Capped, it reads as a character card. */
  .portrait { max-width: 300px; margin-inline: auto; }
}

/* Cap it so the hero can never eat the whole screen on a tall narrow phone —
   it hit 142% of viewport height before this. */
/* Portrait, not landscape, and contain rather than cover.
   The 4/3 + cover pairing was built for photographs of a cat lying on things.
   A full-body character render is 2:3, so cover cropped it to a horizontal
   slice through the middle — all hoodie, no face, no shoes. Contain shows the
   whole character, and because the PNG has an alpha channel the space either
   side is just the frame, which reads as a character card. */
.portrait {
  margin: 0; position: relative; overflow: hidden;
  border: 1px solid var(--line); border-radius: var(--r);
  aspect-ratio: 4/5;
  background:
    radial-gradient(85% 65% at 50% 42%, color-mix(in oklab, var(--amber) 12%, transparent), transparent 70%),
    linear-gradient(165deg, var(--panel-2), var(--bg));
}
.portrait img, .portrait video {
  display: block; width: 100%; height: 100%; object-fit: contain;
}
/* The incoming portrait is stacked over the outgoing one and faded up, so the
   two illustrations dissolve instead of cutting. Absolute, or prepending it
   would shove the old one out of the frame for a beat. */
.portrait img.pfade {
  position: absolute; inset: 0; opacity: 0;
  transition: opacity .4s var(--ease);
}
.portrait img.pfade.in { opacity: 1; }
html[data-motion="off"] .portrait img.pfade { transition: none; opacity: 1; }
/* Just enough vignette to seat the figure. The old 60%-black radial was tuned
   for photos and swallowed the character's feet. */
.portrait::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(130% 105% at 50% 45%, transparent 62%, rgba(0,0,0,.42));
}
.ph {
  height: 100%; display: grid; place-content: center; gap: .5rem; text-align: center;
  background: repeating-linear-gradient(115deg, rgba(255,255,255,.014) 0 2px, transparent 2px 5px);
}
/* --line-hi is a border colour; using it for text gave 1.55:1 and the
   placeholder was effectively invisible. Text colours only, here. */
.ph-note {
  font-size: 12px; color: var(--fg-mut); text-align: center;
  padding: 0 1.2rem; line-height: 1.55; max-width: 32ch;
}
.portrait-cap {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 1;
  display: flex; align-items: center; gap: .5rem;
  padding: .45rem .65rem; font-size: 11.5px; color: var(--fg-dim);
  background: linear-gradient(0deg, rgba(4,6,10,.92), transparent);
}
.rec { width: 7px; height: 7px; border-radius: 50%; background: var(--red); flex: 0 0 auto; animation: blip 2s steps(1) infinite; }

.now-body { display: grid; gap: .55rem; align-content: start; }
.now-act { margin: 0; font-size: clamp(1.15rem, 3.2vw, 1.6rem); line-height: 1.35; color: var(--fg); }
.now-note { margin: 0; font-size: 13px; }

/* reach */
.stats {
  list-style: none; margin: .5rem 0 0; padding: 0;
  display: grid; gap: .5rem; grid-template-columns: repeat(3, minmax(0, 1fr));
}
@media (max-width: 460px) { .stats { grid-template-columns: repeat(2, minmax(0,1fr)); } }
.stats li {
  background: var(--panel); border: 1px solid var(--line);
  border-top: 2px solid var(--c, var(--line-hi));
  border-radius: var(--r); padding: .6rem .7rem; min-width: 0;
}
.stats .n {
  display: block; font-size: 11px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--fg-mut); margin-bottom: .15rem;
}
.stats .v {
  display: block; font-size: clamp(1.15rem, 3.4vw, 1.55rem); font-weight: 700;
  color: var(--fg); font-variant-numeric: tabular-nums; line-height: 1.15;
}
.stats .s { display: block; font-size: 11px; color: var(--fg-mut); margin-top: .1rem; }
.stats li.unknown .v { color: var(--fg-mut); }
/* "coming soon" is words, not a figure. At the number's size it dominates the
   row and reads as a value; at label size it reads as the state it is. */
.stats li.soon-cell .v {
  font-size: 13px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
  color: var(--fg-mut); line-height: 1.9;
}
.sync { margin: .5rem 0 0; font-size: 11.5px; }

/* ═══ 2. FEED ════════════════════════════════════════════════════════ */
.slide-nav { display: flex; gap: .3rem; }
.sq {
  font: inherit; cursor: pointer; color: var(--fg-dim);
  background: var(--panel); border: 1px solid var(--line-hi); border-radius: var(--r);
  width: 44px; height: 44px; display: grid; place-items: center;
}
.sq:hover { color: var(--green); border-color: var(--green); }
.sq.wide { width: auto; padding: 0 .75rem; font-size: 12.5px; }
.sq.wide[aria-pressed="false"] { color: var(--amber); border-color: color-mix(in oklab, var(--amber) 55%, transparent); }

.rail {
  display: grid; grid-auto-flow: column; grid-auto-columns: min(72vw, 260px);
  gap: .6rem; overflow-x: auto; scroll-snap-type: x mandatory;
  padding-bottom: .5rem; scrollbar-width: thin;
  min-width: 0;   /* see .sec — without this the rail expands instead of scrolling */
}
.rail::-webkit-scrollbar { height: 8px; }
.rail::-webkit-scrollbar-thumb { background: var(--line-hi); border-radius: 4px; }
.card {
  scroll-snap-align: start; position: relative;
  border: 1px solid var(--line); border-radius: var(--r); overflow: hidden;
  background: var(--panel); transition: border-color .2s var(--ease), transform .2s var(--ease);
}
.card:hover { border-color: var(--line-hi); transform: translateY(-2px); }
.card .media { position: relative; aspect-ratio: 3/4; background: var(--bg-2); }
.card .media img, .card .media video { width: 100%; height: 100%; object-fit: cover; display: block; }
.card .ph { aspect-ratio: 3/4; }
.card figcaption { padding: .5rem .6rem; font-size: 12px; color: var(--fg-dim); }
.card .meta { display: block; margin-top: .15rem; color: var(--fg-mut); font-size: 11px; }
.badge {
  position: absolute; top: .45rem; left: .45rem; z-index: 1;
  font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase;
  padding: .12rem .4rem; border-radius: 2px;
  background: rgba(4,6,10,.8); border: 1px solid var(--line-hi); color: var(--fg-dim);
}
.badge.video { color: var(--amber); border-color: color-mix(in oklab, var(--amber) 50%, transparent); }
.play {
  position: absolute; inset: 0; display: grid; place-items: center; z-index: 1;
  background: none; border: 0; cursor: pointer; color: var(--fg);
  font: inherit; font-size: 1.5rem;
}
.play span {
  display: grid; place-items: center; width: 52px; height: 52px; border-radius: 50%;
  background: rgba(4,6,10,.72); border: 1px solid var(--line-hi);
  transition: transform .15s var(--spring), border-color .2s var(--ease);
}
.play:hover span { transform: scale(1.08); border-color: var(--green); }

/* ═══ 2. THE THREE BOXES ═════════════════════════════════════════════ */
/* Two boxes, not three — TikTok is gone until there is something in it, and a
   "coming soon" panel taking a third of the row was paying full rent for no
   information.
   Stays two across all the way down to 620px, because below 1100 the feed has
   already escaped to full width (see .now-feed) and has the room. */
.boxes {
  display: grid; gap: .7rem; grid-template-columns: repeat(2, minmax(0, 1fr));
  /* min-height:0 lets the 1fr row above shrink this below its content height;
     the box body clips. Without it the grid floors at content size and the
     overhang comes straight back. */
  min-height: 0;
}
@media (max-width: 620px) { .boxes { grid-template-columns: 1fr; } }

.boxes-hd {
  display: flex; align-items: center; justify-content: space-between;
  gap: 1rem; margin-top: .5rem;
}

.box {
  display: flex; flex-direction: column; min-width: 0; min-height: 0;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--r);
  transition: border-color .2s var(--ease);
}
.box:hover, .box:focus-within { border-color: var(--line-hi); }
.box:focus-visible { outline: 2px solid var(--green); outline-offset: 2px; }

.box-hd {
  display: flex; align-items: center; justify-content: space-between; gap: .6rem;
  padding: .5rem .7rem; border-bottom: 1px solid var(--line);
  background: var(--panel-2); font-size: 12px;
}
.box-t { color: var(--fg); font-weight: 700; }
.box-t::before { content: '// '; color: var(--line-hi); font-weight: 400; }
/* Grows to fill whatever the box is given, rather than being a fixed 208px.
   The fixed height was there to stop a short slide and a long one making the
   row jump every few seconds — but the slides are absolutely positioned, so
   what actually prevents the jump is the body having a height that does not
   depend on its content. Filling a definite grid row does that just as well,
   and lets the box end level with the portrait.
   min-height is the floor for the stacked layouts, where there is no row to
   fill and the flex-grow has nothing to work with. */
.box-body { position: relative; flex: 1 1 auto; min-height: 190px; overflow: hidden; }
/* Side by side, the PORTRAIT flexes to the content — not the other way round.
 *
 * Forcing the feed into the leftover space did align the two columns exactly,
 * and left the box body at 80px, which clips the post text. The gap beside a
 * 500px portrait is ~233px and a readable feed box needs ~280px, so that space
 * was never going to be enough. Squeezing the content to fit a decorative
 * frame is the wrong trade.
 *
 * Instead the frame stretches: aspect-ratio is dropped, the figure fills the
 * column, and because the portrait is a transparent PNG drawn with
 * object-fit: contain, the cat simply centres in a taller box. Both columns end
 * level and nothing is clipped. */
@media (min-width: 1101px) {
  .portrait { align-self: stretch; aspect-ratio: auto; }
}
.slide {
  position: absolute; inset: 0; padding: .7rem;
  display: flex; flex-direction: column; gap: .35rem; justify-content: center;
  opacity: 0; transform: translateY(6px); pointer-events: none;
  transition: opacity .45s var(--ease), transform .45s var(--ease);
}
.slide.on { opacity: 1; transform: none; pointer-events: auto; }
html[data-motion="off"] .slide { transition: none; }

/* — x posts — */
.slide-kind { font-size: 11px; color: var(--kind, var(--fg-mut)); }
.slide-text { margin: 0; font-size: 13.5px; line-height: 1.5; color: var(--fg); }
.slide-text.quiet { color: var(--fg-mut); }
.slide-when { font-size: 11px; color: var(--fg-mut); margin-top: auto; }
.slide-shot {
  width: 100%; max-height: 92px; object-fit: cover;
  border: 1px solid var(--line); border-radius: 2px; display: block;
}

/* — best trades — */
.pnl-sym { font-size: 13px; font-weight: 700; }
.pnl-big {
  font-size: clamp(1.3rem, 4vw, 1.75rem); font-weight: 700;
  font-variant-numeric: tabular-nums; line-height: 1.15;
}
.pnl-sub { font-size: 11.5px; color: var(--fg-mut); }
.pnl-bar { height: 4px; background: var(--bg-2); border: 1px solid var(--line); margin-top: .2rem; }
.pnl-bar i { display: block; height: 100%; background: var(--green); }


/* — dots — */
.box-ft { padding: .3rem .5rem; border-top: 1px solid var(--line); }
.dots { display: flex; gap: 2px; flex-wrap: wrap; }
.dots button {
  /* 6px dot, 24×44 hit area — the target is the padding, not the mark.
     24 is the WCAG 2.5.8 minimum; 18 looked fine and failed it. */
  appearance: none; background: none; border: 0; cursor: pointer;
  width: 24px; height: 44px; padding: 0; display: grid; place-items: center;
}
.dots button::before {
  content: ''; width: 6px; height: 6px; border-radius: 50%;
  background: var(--line-hi); transition: background .2s var(--ease), transform .2s var(--ease);
}
.dots button:hover::before { background: var(--fg-mut); }
.dots button[aria-current="true"]::before { background: var(--green); transform: scale(1.35); }
.dots button:focus-visible { outline: 2px solid var(--green); outline-offset: -6px; border-radius: 3px; }

/* ═══ TICKER ═════════════════════════════════════════════════════════
   One long rail, duplicated, translated by exactly -50%. Because the two
   halves are identical the wrap is invisible, and because it is a single
   transform the browser can put the whole thing on the compositor — it costs
   nothing per frame regardless of how many items are in it. */
.ticker {
  position: relative; overflow: hidden;
  border-bottom: 1px solid var(--line);
  background: var(--panel);
  height: 38px;
}
.ticker-rail {
  display: flex; width: max-content; height: 100%; align-items: center;
  animation: ticker-run var(--dur, 60s) linear infinite;
  will-change: transform;
}
.ticker-half { display: flex; align-items: center; height: 100%; }
@keyframes ticker-run { to { transform: translateX(-50%); } }
/* Pause on hover, on focus, and while off-screen. The last one matters: an
   animation nobody can see still costs a composite every frame. */
.ticker:hover .ticker-rail,
.ticker:focus-within .ticker-rail,
.ticker.idle .ticker-rail { animation-play-state: paused; }

.tick {
  display: inline-flex; align-items: baseline; gap: .45rem;
  padding: 0 1.05rem; white-space: nowrap; font-size: 12.5px;
  border-right: 1px solid var(--line);
}
.tick-sym { color: var(--fg); font-weight: 700; }
.tick-x   { color: var(--fg-mut); font-variant-numeric: tabular-nums; }
.tick-v   { font-variant-numeric: tabular-nums; font-weight: 700; }
.tick.up   .tick-v { color: var(--green); }
.tick.down .tick-v { color: var(--red); }
.tick-tag {
  font-size: 9.5px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--fg-mut); border: 1px solid var(--line-hi); border-radius: 2px;
  padding: 0 .28rem;
}
.tick.is-open .tick-tag { color: var(--cyan); border-color: color-mix(in oklab, var(--cyan) 45%, transparent); }
/* A newly-arrived tick flashes in its own direction — a red trade lighting up
   green is a small lie told 30 times a minute. */
.tick.fresh { animation: tick-arrive 2.4s var(--ease); }
.tick.up   { --flash: var(--green); }
.tick.down { --flash: var(--red); }
@keyframes tick-arrive {
  0%   { background: color-mix(in oklab, var(--flash, var(--green)) 22%, transparent); }
  100% { background: transparent; }
}

/* Fade the ends so items enter and leave instead of being chopped. */
.ticker-edge {
  position: absolute; top: 0; bottom: 0; width: 56px; z-index: 2; pointer-events: none;
}
/* The fade must resolve to the ticker's OWN background, not the page's, or the
   ends read as a smudge instead of items leaving the frame. */
.ticker-edge.left  { left: 0;  background: linear-gradient(90deg, var(--panel), transparent); }
.ticker-edge.right { right: 0; background: linear-gradient(270deg, var(--panel), transparent); }

/* Motion off: no animation, scroll it by hand instead — and drop the duplicate
   half, or you would scroll through the same trades twice. */
html[data-motion="off"] .ticker-rail { animation: none; }
html[data-motion="off"] .ticker { overflow-x: auto; }
html[data-motion="off"] .ticker-half + .ticker-half { display: none; }

/* ═══ HOLDINGS ═══════════════════════════════════════════════════════
   The chart is the hero of the desk: full width, its own header, its own
   controls. Everything below it is supporting detail. */
.holdings { border-top: 1px solid var(--line); }
.hold-hd {
  display: flex; align-items: center; flex-wrap: wrap; gap: .55rem;
  padding: .8rem .85rem .2rem;
}
.hold-hd h4 {
  margin: 0; font-size: 15px; font-weight: 700; color: var(--fg);
  letter-spacing: .01em; margin-right: auto;
}

/* Segmented controls. Same component, two weights: the range picker is quiet
   because you set it once, the view picker is loud because it changes what
   the panel IS. */
.segs { display: flex; gap: 2px; padding: 2px; border-radius: 5px; background: var(--bg-2); }
.segs[hidden] { display: none; }
.segs button {
  appearance: none; border: 0; cursor: pointer; font: inherit;
  font-size: 11.5px; line-height: 1; color: var(--fg-mut);
  background: transparent; border-radius: 4px;
  min-height: 26px; padding: 0 .6rem;
  transition: color .15s var(--ease), background .15s var(--ease);
}
/* The hit area is 26px tall for looks and 44px tall for thumbs (WCAG 2.5.8).
   The pseudo-element does the second job without inflating the pill. */
.segs button { position: relative; }
/* Vertical only. Extending it sideways would push each button's hit area into
   its neighbour's 2px gap, so a click near the seam would land on whichever
   happened to come later in the DOM. */
.segs button::after { content: ''; position: absolute; inset: -9px 0; }
.segs button:hover { color: var(--fg-dim); }
.segs button[aria-pressed="true"] { color: var(--fg); background: var(--panel-2); }
.segs.solid button[aria-pressed="true"] {
  color: var(--bg); background: var(--green); font-weight: 700;
}
.segs button:focus-visible { outline: 1px solid var(--cyan); outline-offset: 1px; }
/* A range with no history behind it still works — it just says so.
   The signal is a dotted underline, NOT a dimmer colour: --line-hi is a border
   token at 1.66:1 on this background, and these buttons are live controls, not
   decoration. On a freshly started agent every range but 24h is thin, so this
   is the normal case on day one, not an edge case. */
.segs button.thin {
  text-decoration: underline dotted var(--line-hi);
  text-underline-offset: 3px;
}

.hold-meta {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: .1rem .6rem;
  padding: .1rem .85rem .3rem;
}
.hold-v { font-size: clamp(1.3rem, 3.4vw, 1.75rem); font-weight: 700; color: var(--fg); font-variant-numeric: tabular-nums; }
.hold-d { font-size: 12.5px; font-weight: 700; font-variant-numeric: tabular-nums; }
.hold-d.up { color: var(--green); } .hold-d.down { color: var(--red); }
.hold-meta .mono { margin-left: auto; font-size: 11px; }
.hold-meta.bare .hold-d, .hold-meta.bare .mono { display: none; }

/* ═══ CHARTS ═════════════════════════════════════════════════════════ */
.chart-wrap { position: relative; margin: 0; padding: .4rem .85rem .7rem; }
.chart-wrap[hidden] { display: none; }
.chart-wrap { touch-action: pan-y; }   /* scrub sideways, still scroll the page */
.chart-wrap:focus-visible { outline: 1px solid var(--cyan); outline-offset: -1px; }
.chart-host { position: relative; }

/* Plot and axes are one grid. The label gutters are real tracks, so a label
   can never sit on top of the data and the plot never has to guess how much
   room the widest "$1,900" needs. */
.cframe {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--yw, 58px);
  grid-template-rows: var(--ph, clamp(180px, 26vw, 280px)) var(--xh, 20px);
}
.cplot { grid-area: 1 / 1; position: relative; min-width: 0; }
.cyaxis { grid-area: 1 / 2; position: relative; }
.cxaxis { grid-area: 2 / 1; position: relative; }

.chart { display: block; width: 100%; height: 100%; overflow: visible; }
.chart .grid line { stroke: var(--line); stroke-width: 1; vector-effect: non-scaling-stroke; }
.chart .baseline { stroke: var(--line-hi); stroke-width: 1; stroke-dasharray: 3 4; vector-effect: non-scaling-stroke; }
.chart .line {
  fill: none; stroke: var(--green); stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
.cframe.down .chart .line { stroke: var(--red); }

/* --c must live on the <svg>, not on .area.
   The gradient stops are inside <defs>, which is NOT a descendant of the path
   that uses the gradient — so a custom property set on .area never reaches
   them, stop-color falls back, and the fill silently disappears. Setting it on
   the root of the SVG is the only place both the path and the defs inherit it. */
.chart { --c: var(--green); }
.cframe.down .chart { --c: var(--red); }
.cframe.up   .chart { --c: var(--green); }

/* The live end of the line. An HTML dot, not an <svg> circle: the plot is
   stretched with preserveAspectRatio="none", which would squash a circle into
   an ellipse by whatever the container's aspect ratio happens to be. */
.chart-head {
  position: absolute; left: 0; bottom: 0; width: 7px; height: 7px;
  border-radius: 50%; background: var(--green); transform: translate(-50%, 50%);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--green) 22%, transparent);
}
.cframe.down ~ * .chart-head, .cframe.down .chart-head {
  background: var(--red); box-shadow: 0 0 0 3px color-mix(in oklab, var(--red) 22%, transparent);
}
.chart-wrap.drawing .chart-head { animation: pop .4s .5s var(--ease) backwards; }
@keyframes pop { from { opacity: 0; transform: translate(-50%, 50%) scale(.2); } }

/* Axis labels. HTML, positioned by percentage against the same box the plot
   stretches into, so they stay pinned to their gridlines at every width. */
.ax-y, .ax-x {
  position: absolute; font-size: 10.5px; color: var(--fg-mut);
  font-variant-numeric: tabular-nums; white-space: nowrap; pointer-events: none;
}
.ax-y { right: 0; padding-left: .5rem; transform: translateY(50%); }
.ax-x { top: 3px; transform: translateX(-50%); }
.ax-x.at-start { transform: translateX(0); }
.ax-x.at-end   { transform: translateX(-100%); }

/* The whole plot wipes in from the left. A stroke-dashoffset draw looks
   wonderful on a smooth 48-point curve and terrible on a 600-point sawtooth,
   where it reads as the line being erased rather than drawn. */
@keyframes wipe { from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0 0 0 0); } }
.chart-wrap.drawing .cplot { animation: wipe .85s var(--ease); }

.chart-empty { padding: 2.5rem .8rem; color: var(--fg-mut); font-size: 12.5px; text-align: center; }

/* crosshair — inside .cplot, so its percentages are the plot's percentages */
.crosshair { position: absolute; inset: 0; pointer-events: none; }
.crosshair[hidden] { display: none; }
.cross-line {
  position: absolute; top: 0; bottom: 0; width: 1px;
  background: var(--line-hi); left: var(--cx, 0);
}
.cross-dot {
  position: absolute; width: 8px; height: 8px; border-radius: 50%;
  background: var(--bg); border: 2px solid var(--green);
  left: var(--cx, 0); top: var(--cy, 0); transform: translate(-50%, -50%);
}
.cframe.down .cross-dot { border-color: var(--red); }
.cross-label {
  position: absolute; left: var(--cx, 0); top: var(--cy, 0);
  transform: translate(-50%, -170%);
  background: var(--panel-2); border: 1px solid var(--line-hi); border-radius: 3px;
  padding: .2rem .45rem; font-size: 11px; color: var(--fg); white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
/* Near either edge the label would hang off the panel — pin it inside. */
.crosshair.flip .cross-label { transform: translate(-100%, -170%); }
.crosshair.flip-start .cross-label { transform: translate(0, -170%); }

@media (max-width: 560px) {
  .cframe { grid-template-columns: minmax(0, 1fr) 48px; --ph: 190px; }
  .ax-y, .ax-x { font-size: 9.5px; }
  .hold-hd { gap: .4rem; }
  .segs button { padding: 0 .45rem; }
}

/* ── donuts ────────────────────────────────────────────────────────── */
.dwrap { padding: .9rem .8rem 1.2rem; display: flex; flex-direction: column; align-items: center; gap: .7rem; flex: 1; }
/* [hidden] is display:none in the UA sheet, but this rule's display wins on
   specificity — the view toggle would show every pane at once without it. */
.dwrap[hidden] { display: none; }
.donut { position: relative; width: min(148px, 62%); aspect-ratio: 1; }
.dsvg { width: 100%; height: 100%; transform: rotate(-90deg); }  /* start at 12 o'clock */
.dtrack { fill: none; stroke: var(--bg-2); stroke-width: 15; }
.dseg {
  fill: none; stroke-width: 15; stroke-linecap: butt;
  transition: stroke-dasharray .7s var(--ease), stroke-dashoffset .7s var(--ease);
  animation: dsweep .75s var(--ease) backwards; animation-delay: var(--delay, 0ms);
}
@keyframes dsweep { from { opacity: 0; stroke-width: 4; } to { opacity: 1; stroke-width: 15; } }
.dseg:hover { stroke-width: 18; }

/* The hole. No counter-rotation needed — this is a sibling box, not inside the
   svg. Width is capped to the hole itself: anything wider is drawn over the
   ring, which reads as a rendering fault rather than a label. */
/* The box IS the hole (inset 18% ≈ the inner radius), so nothing can be drawn
   over the ring. A percentage max-width on the children cannot do this: with
   place-content:center the track is content-sized, so the percentage resolves
   against the text's own width and collapses it. */
.dcenter {
  position: absolute; inset: 18%; display: grid; place-content: center;
  text-align: center; pointer-events: none; line-height: 1.15; overflow: hidden;
}
.dcenter b { display: block; font-size: clamp(1.15rem, 3vw, 1.5rem); font-weight: 700; color: var(--fg); font-variant-numeric: tabular-nums; }
.dcenter em { display: block; font-style: normal; font-size: 10px; letter-spacing: .09em; text-transform: uppercase; color: var(--fg-mut); margin-top: .15rem; }

.dlegend { list-style: none; margin: 0; padding: 0; width: 100%; display: grid; gap: .28rem; }
.dlegend li { display: flex; align-items: center; gap: .4rem; font-size: 11px; min-width: 0; }
.dlegend i { width: 8px; height: 8px; border-radius: 2px; flex: 0 0 auto; }
.dl-name { color: var(--fg-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dl-val { margin-left: auto; color: var(--fg-mut); font-variant-numeric: tabular-nums; flex: 0 0 auto; }
.dfoot {
  width: 100%; margin: 0; padding-top: .5rem; border-top: 1px solid var(--line);
  font-size: 10.5px; color: var(--fg-mut); font-variant-numeric: tabular-nums; text-align: center;
}

/* ── live book ─────────────────────────────────────────────────────── */
.live-wrap { padding: .7rem .8rem; }
/* Not `.live`. That name is also a state modifier on .tick, and `.tick.live`
   would inherit this display:grid — which is exactly what happened: the open
   positions in the tape silently became four-line grids while the closed ones
   stayed inline. Generic state words do not get to be layout classes. */
.livebook { display: grid; gap: .5rem; }
.lrow {
  display: grid; gap: .3rem; padding: .5rem .6rem;
  background: var(--bg-2); border: 1px solid var(--line); border-radius: 3px;
  border-left: 2px solid var(--dot, var(--green));
  transition: border-color .2s var(--ease), background .2s var(--ease);
}
.lrow:hover { background: color-mix(in oklab, var(--bg-2) 80%, var(--panel)); }
.lrow-a { display: flex; align-items: baseline; gap: .4rem; min-width: 0; }
.ldot { width: 7px; height: 7px; border-radius: 50%; background: var(--dot); flex: 0 0 auto; align-self: center; }
.lsym { font-weight: 700; font-size: 12.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lx { margin-left: auto; color: var(--fg-mut); font-size: 12px; font-variant-numeric: tabular-nums; flex: 0 0 auto; }
.ltag {
  font-size: 9px; letter-spacing: .08em; text-transform: uppercase; color: var(--fg-mut);
  border: 1px solid var(--line-hi); border-radius: 2px; padding: 0 .25rem; flex: 0 0 auto;
}
.lrow-b { display: flex; align-items: baseline; gap: .45rem; font-variant-numeric: tabular-nums; }
.lpl { font-size: 15px; font-weight: 700; }
.lpct { font-size: 11.5px; }
.lrow.up .lpl, .lrow.up .lpct { color: var(--green); }
.lrow.down .lpl, .lrow.down .lpct { color: var(--red); }

.lrow-c { display: flex; flex-wrap: wrap; gap: .1rem .85rem; margin: .15rem 0 0; }
.lrow-c div { display: flex; gap: .28rem; align-items: baseline; }
.lrow-c dt { font-size: 9.5px; letter-spacing: .07em; text-transform: uppercase; color: var(--fg-mut); }
.lrow-c dd { margin: 0; font-size: 11px; color: var(--fg-dim); font-variant-numeric: tabular-nums; }

/* ── position bars ─────────────────────────────────────────────────── */
.split { display: grid; grid-template-columns: 1fr 1fr; border-top: 1px solid var(--line); }
.split-col { min-width: 0; }
.split-col + .split-col { border-left: 1px solid var(--line); }
@media (max-width: 780px) {
  .split { grid-template-columns: 1fr; }
  .split-col + .split-col { border-left: 0; border-top: 1px solid var(--line); }
}

.pbars { display: grid; gap: .55rem; padding: .7rem .8rem; }
.pbar { --c: var(--green); }
.pbar.down { --c: var(--red); }
.pbar-hd { display: flex; align-items: baseline; gap: .5rem; font-size: 12.5px; margin-bottom: .25rem; }
.pbar-sym { font-weight: 700; }
.pbar-x { color: var(--fg-mut); font-variant-numeric: tabular-nums; }
.pbar-pl { margin-left: auto; font-weight: 700; font-variant-numeric: tabular-nums; }
.pbar-pl.up { color: var(--green); } .pbar-pl.down { color: var(--red); }

/* Two bars, stacked: what it cost underneath, what it is worth on top. The
   overhang is the profit — no legend needed. */
.pbar-track {
  position: relative; height: 9px; background: var(--bg-2);
  border: 1px solid var(--line); border-radius: 2px; overflow: hidden;
}
.pbar-track i { position: absolute; inset: 0 auto 0 0; display: block; }
.pbar-mark {
  width: var(--mark); background: color-mix(in oklab, var(--c) 55%, transparent);
  transition: width .8s var(--ease);
}
.pbar-cost {
  width: var(--cost); background: var(--c); opacity: .95;
  transition: width .8s var(--ease);
  box-shadow: 0 0 12px -2px var(--c);
}
/* Under water the two swap roles: cost is now the LONGER bar and would paint
   over the mark entirely, hiding the very thing the chart is for. So the ghost
   is always the bigger one and the solid bar is always "what it is worth now",
   whichever direction the trade is going. */
.pbar.down .pbar-mark { z-index: 1; background: var(--c); box-shadow: 0 0 12px -2px var(--c); }
.pbar.down .pbar-cost { background: color-mix(in oklab, var(--c) 30%, transparent); box-shadow: none; }
.pbar-ft { display: flex; justify-content: space-between; font-size: 10.5px; color: var(--fg-mut); margin-top: .2rem; }

/* ── fill histogram ────────────────────────────────────────────────── */
.hist-wrap { padding: .7rem .8rem; }
.hist {
  position: relative; display: flex; align-items: center; justify-content: center; gap: 4px;
  height: var(--h, 108px); padding: .3rem 0;
}
.hist::before {
  content: ''; position: absolute; left: 0; right: 0; top: 50%;
  height: 1px; background: var(--line-hi);
}
/* The bar is a full-height column; ::before is the visible part, hung off the
   zero line. It has to be done this way: a percentage margin resolves against
   the container's WIDTH, not its height, so `margin-bottom: 50%` to reach the
   midline is off by however wide the column happens to be. */
/* Capped, and the row is centred. Four trades stretched to a quarter of the
   panel each look like a bar chart of nothing; capped they read as four
   trades, and the zero line stays visible in the gaps between them. */
.hbar {
  position: relative; flex: 1 1 0; min-width: 5px; max-width: 26px;
  align-self: stretch; cursor: default;
  transition: transform .18s var(--ease), filter .18s var(--ease);
}
.hbar::before {
  content: ''; position: absolute; left: 0; right: 0;
  height: var(--mag); border-radius: 2px;
}
.hbar.up::before   { bottom: 50%; background: linear-gradient(180deg, var(--green), color-mix(in oklab, var(--green) 40%, transparent)); }
.hbar.down::before { top: 50%;    background: linear-gradient(0deg, var(--red), color-mix(in oklab, var(--red) 40%, transparent)); }
.hbar:hover, .hbar:focus-visible {
  transform: scaleX(1.35); filter: brightness(1.4) drop-shadow(0 0 6px currentColor);
}
.hbar.up:hover, .hbar.up:focus-visible { color: var(--green); }
.hbar.down:hover, .hbar.down:focus-visible { color: var(--red); }

/* Which trade was that? The bar already carries the answer in data-tip. */
/* The tooltip goes on the empty side of the zero line — under a winner, over a
   loser. It is always adjacent to its bar and always inside the box, which no
   amount of "above the bar" arithmetic can promise for a tall one. */
.hbar::after {
  content: attr(data-tip);
  position: absolute; left: 50%;
  transform: translateX(-50%) scale(.94); transform-origin: center;
  background: var(--panel-2); border: 1px solid var(--line-hi); border-radius: 3px;
  padding: .22rem .45rem; font-size: 11px; color: var(--fg); white-space: nowrap;
  font-variant-numeric: tabular-nums; pointer-events: none;
  opacity: 0; transition: opacity .14s var(--ease), transform .14s var(--ease);
  z-index: 3;
}
.hbar.up::after   { top: calc(50% + 9px); }
.hbar.down::after { bottom: calc(50% + 9px); }
.hbar:hover::after, .hbar:focus-visible::after { opacity: 1; transform: translateX(-50%) scale(1); }
/* The row is centred, so the first and last bars are the ones that can clip. */
.hbar:first-child::after  { left: 0; transform: translateX(0) scale(.94); }
.hbar:first-child:hover::after, .hbar:first-child:focus-visible::after { transform: translateX(0) scale(1); }
.hbar:last-child::after   { left: auto; right: 0; transform: translateX(0) scale(.94); }
.hbar:last-child:hover::after, .hbar:last-child:focus-visible::after { transform: translateX(0) scale(1); }

.hist-foot {
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  gap: .5rem; border-top: 1px solid var(--line); padding-top: .55rem; margin-top: .2rem;
}
.hf-cell { display: flex; flex-direction: column; gap: .1rem; min-width: 0; }
.hf-cell em { font-style: normal; font-size: 9.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--fg-mut); }
.hf-cell b { font-size: 13px; font-variant-numeric: tabular-nums; }
.hf-cell b.up { color: var(--green); } .hf-cell b.down { color: var(--red); }
.hf-cell i { font-style: normal; font-size: 10.5px; color: var(--fg-mut); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ── ratio + sparkline ─────────────────────────────────────────────── */
.ratio { display: flex; height: 3px; margin-top: .3rem; border-radius: 2px; overflow: hidden; background: var(--bg-2); }
.ratio i { display: block; height: 100%; transition: width .8s var(--ease); }
.ratio-w { background: var(--green); }
.ratio-l { background: var(--red); opacity: .75; }

.spark { display: block; width: 100%; max-width: 150px; height: 22px; margin-top: .3rem; overflow: visible; }
.kpis .x { display: block; min-height: 22px; margin-top: .1rem; }
.spark path { fill: none; stroke-width: 1.5; stroke-linecap: round; vector-effect: non-scaling-stroke; }
.spark.up path   { stroke: color-mix(in oklab, var(--green) 70%, transparent); }
.spark.down path { stroke: color-mix(in oklab, var(--red) 70%, transparent); }

html[data-motion="off"] .chart-wrap.drawing .cplot,
html[data-motion="off"] .chart-wrap.drawing .chart-head { animation: none; clip-path: none; }
html[data-motion="off"] .dseg { animation: none; transition: none; }
html[data-motion="off"] .pbar-mark,
html[data-motion="off"] .pbar-cost,
html[data-motion="off"] .ratio i { transition: none; }
html[data-motion="off"] .hbar:hover { transform: none; }

/* ── tilt + ripple ─────────────────────────────────────────────────── */
/* A couple of degrees of parallax under the cursor. Anything more reads as a
   gimmick; this just makes the surface feel like it has depth. */
.tilt { transform: perspective(700px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)); }

.ripple {
  position: absolute; border-radius: 50%; pointer-events: none; z-index: 3;
  background: radial-gradient(circle, color-mix(in oklab, var(--accent, var(--green)) 40%, transparent), transparent 70%);
  transform: translate(-50%, -50%) scale(0); opacity: .8;
  animation: ripple-out .55s var(--ease) forwards;
}
@keyframes ripple-out {
  to { transform: translate(-50%, -50%) scale(1); opacity: 0; }
}
html[data-motion="off"] .tilt { transform: none; }
html[data-motion="off"] .ripple { display: none; }

/* ═══ 3. DESK ════════════════════════════════════════════════════════ */
.up   { color: var(--green); }
.down { color: var(--red); }
.halted {
  color: var(--red); font-weight: 700;
  border: 1px solid var(--red); border-radius: 2px; padding: .05rem .4rem;
}

.kpis {
  list-style: none; margin: 0; padding: .7rem .8rem;
  display: grid; gap: .55rem; grid-template-columns: repeat(6, minmax(0,1fr));
  border-bottom: 1px solid var(--line);
}
@media (max-width: 900px) { .kpis { grid-template-columns: repeat(3, minmax(0,1fr)); } }
@media (max-width: 460px) { .kpis { grid-template-columns: repeat(2, minmax(0,1fr)); } }
.kpis li { min-width: 0; }
.kpis .n { display: block; font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase; color: var(--fg-mut); }
.kpis .v { display: block; font-size: clamp(1rem, 2.6vw, 1.3rem); font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1.25; }
.kpis .s { display: block; font-size: 10.5px; color: var(--fg-mut); }
.kpis li.up .v { color: var(--green); }
.kpis li.down .v { color: var(--red); }

.pos { display: grid; gap: .5rem; padding: .7rem .8rem; border-bottom: 1px solid var(--line); }
@media (min-width: 760px) { .pos { grid-template-columns: 1fr 1fr; } }
.poscard {
  border: 1px solid var(--line); border-left: 2px solid var(--line-hi);
  border-radius: var(--r); padding: .6rem .7rem; background: var(--panel-2); min-width: 0;
}
.poscard.up   { border-left-color: var(--green); }
.poscard.down { border-left-color: var(--red); }
.poscard header { display: flex; align-items: baseline; gap: .6rem; margin-bottom: .45rem; flex-wrap: wrap; }
.poscard .sym { font-weight: 700; }
.poscard .x { font-variant-numeric: tabular-nums; font-weight: 700; }
.poscard .mk { margin-left: auto; font-variant-numeric: tabular-nums; font-size: 13px; white-space: nowrap; }
.poscard .mk em { font-style: normal; }

.rungs { display: flex; flex-wrap: wrap; gap: .1rem .8rem; font-size: 11.5px; color: var(--fg-mut); margin-bottom: .4rem; }
.rung.hit { color: var(--green); }

.posmeta { display: flex; flex-wrap: wrap; gap: .1rem .8rem; font-size: 11px; color: var(--fg-mut); }
.posmeta .stop { color: var(--amber); }
.posmeta .stop.be { color: var(--green); }
.posmeta em { font-style: normal; }

.calls { list-style: none; margin: 0; padding: .3rem 0; }
.calls li {
  display: grid; grid-template-columns: 8ch 6ch 10ch 6ch 1fr; gap: .6rem; align-items: baseline;
  padding: .35rem .8rem; border-bottom: 1px dashed var(--line); font-size: 12.5px;
}
.calls li:last-child { border-bottom: 0; }
.calls li:hover { background: var(--panel-2); }
.calls .t { color: var(--fg-mut); font-size: 11.5px; }
.calls .verdict { font-weight: 700; }
.calls li.took .verdict { color: var(--green); }
.calls li.passed .verdict { color: var(--fg-mut); }
.calls .sym { color: var(--fg); }
.calls .tiers { color: var(--amber); letter-spacing: .1em; }
.calls .why { color: var(--fg-mut); min-width: 0; }
.calls li.passed .sym { color: var(--fg-mut); }
@media (max-width: 700px) {
  .calls li { grid-template-columns: 6ch 1fr auto; }
  .calls .t { grid-column: 1; }
  .calls .tiers { grid-column: 3; }
  .calls .why { grid-column: 1 / -1; }
}

/* ═══ (legacy ledger bits, still used by the fills tape) ═════════════ */
.led-top {
  display: grid; grid-template-columns: minmax(0,auto) minmax(0,1fr);
  gap: clamp(.8rem,3vw,2rem); padding: .9rem .8rem; border-bottom: 1px solid var(--line);
  align-items: center;
}
@media (max-width: 620px) { .led-top { grid-template-columns: 1fr; } }
.big { display: grid; gap: .1rem; }
.big .dim { font-size: 12px; }
.big b { font-size: clamp(1.5rem, 4.5vw, 2.1rem); font-variant-numeric: tabular-nums; }

.bond-hd { display: flex; justify-content: space-between; font-size: 12px; margin-bottom: .3rem; }
.bond-bar { height: 8px; background: var(--bg-2); border: 1px solid var(--line); }
.bond-bar i { display: block; height: 100%; width: 0; background: linear-gradient(90deg, var(--purple), var(--green)); transition: width 1s var(--ease); }
.bond-note { margin: .35rem 0 0; font-size: 11.5px; }

.alloc { list-style: none; margin: 0; padding: .5rem 0; }
.alloc li { display: grid; grid-template-columns: 12ch 1fr auto; gap: .8rem; align-items: center; padding: .4rem .8rem; }
.alloc .n { color: var(--c, var(--fg)); font-size: 13px; }
.alloc .track { height: 6px; background: var(--bg-2); border: 1px solid var(--line); }
.alloc .track i { display: block; height: 100%; background: var(--c, var(--green)); width: 0; transition: width .9s var(--ease); }
.alloc .v { font-size: 13px; font-variant-numeric: tabular-nums; white-space: nowrap; }
.alloc li.idle .n, .alloc li.idle .v { color: var(--fg-mut); }
@media (max-width: 560px) { .alloc li { grid-template-columns: 1fr auto; } .alloc .track { grid-column: 1/-1; } }

.log { list-style: none; margin: 0; padding: .3rem 0; }
.log li {
  display: grid; grid-template-columns: 8ch 1fr auto; gap: .8rem; align-items: baseline;
  padding: .42rem .8rem; border-bottom: 1px dashed var(--line); font-size: 13px;
}
.log li:last-child { border-bottom: 0; }
.log li:hover { background: var(--panel-2); }
.log .t { color: var(--fg-mut); font-size: 12px; }
.log .what { color: var(--act, var(--fg)); font-weight: 700; }
.log .why { color: var(--fg-dim); }
.log .why::before { content: '— '; color: var(--line-hi); }
.log .amt { font-variant-numeric: tabular-nums; white-space: nowrap; }
.log .amt.zero { color: var(--fg-mut); }
.log .flags { display: block; margin-top: .15rem; font-size: 11.5px; color: var(--fg-mut); }
.flag.sim { color: var(--cyan); }
@media (max-width: 620px) { .log li { grid-template-columns: 1fr auto; } .log .t { grid-column: 1/-1; } }

/* ═══ 4. ROADMAP ═════════════════════════════════════════════════════ */
.road { list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem; }
.road li {
  display: grid; grid-template-columns: auto 1fr; gap: .8rem;
  background: var(--panel); border: 1px solid var(--line); border-left: 2px solid var(--s, var(--line-hi));
  border-radius: var(--r); padding: .7rem .85rem;
}
.road .mark { color: var(--s, var(--fg-mut)); font-size: 13px; }
.road h3 { margin: 0 0 .2rem; font-size: 14px; font-weight: 700; color: var(--fg); }
.road p { margin: 0; font-size: 12.5px; color: var(--fg-dim); line-height: 1.55; }
.road .tags { margin-top: .35rem; display: flex; gap: .5rem; flex-wrap: wrap; font-size: 11px; color: var(--fg-mut); }
.road .tag { border: 1px solid var(--line-hi); border-radius: 2px; padding: .05rem .35rem; }
.road li.active { --s: var(--green); }
.road li.next { --s: var(--amber); }
.road li.planned { --s: var(--purple); }
/* --s is both the left border AND the marker glyph colour, so it has to clear
   the text contrast floor. --line-hi gave 1.59:1 on the marker. */
.road li.someday { --s: var(--fg-mut); }
.road li.someday h3 { color: var(--fg-dim); }

/* ── notice / errors ─────────────────────────────────────────────────── */
.notes { background: var(--bg-2); }
.notice { margin: 0; padding: .8rem; white-space: pre-wrap; color: var(--fg-mut); font-size: 12.5px; line-height: 1.65; }
.foot { padding: 0 .8rem .8rem; margin: 0; font-size: 12px; }
.err {
  display: flex; align-items: center; gap: .8rem; flex-wrap: wrap;
  padding: .6rem .8rem; border: 1px solid var(--red); border-radius: var(--r);
  background: color-mix(in oklab, var(--red) 10%, var(--panel)); font-size: 13px;
}
.err button {
  font: inherit; font-size: 12.5px; cursor: pointer; margin-left: auto;
  background: none; color: var(--red); border: 1px solid var(--red); border-radius: 2px;
  display: inline-flex; align-items: center; min-height: 44px; padding: 0 .9rem;
}
.empty { padding: .8rem; color: var(--fg-mut); font-size: 12.5px; }

/* ── motion ──────────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; }
}
html[data-motion="off"] .scan { display: none; }
html[data-motion="off"] .led, html[data-motion="off"] .rec { animation: none; }
html[data-motion="off"] * { transition: none !important; }
html[data-motion="off"] .rail { scroll-behavior: auto; }
