/* swings
   Split from the single v22 stylesheet. Everything here is scoped to
   .card.swings and this form's own class prefix (rule 14: a change made for one
   card form is scoped to that form, by class).

   lane-face-fit-width: not evaluated at 430x743/375x667 this lane -- left at the shell default --faceh-min:var(--faceh) (852), which is byte-identical to today and safe. See NEXT.md / the coordinator for who picks this up. */
/* ------------------------------------------------------------------ *
   The Swings — the day's five biggest jumps, or its five biggest drops.

   Named in the header as well as the selectors. The block that used to sit
   here still called itself "The Close" from an earlier naming, and a later
   edit anchored on that comment and deleted this entire section -- the rows
   lost their grid and the sparklines lost `fill:none`, so they rendered as
   black blobs. A stale section name is a live hazard, not a typo.
 * ------------------------------------------------------------------ */
.card.swings .vizsec{padding:20px 0}

/* The Swings' own reduced-motion rules. They used to sit in the shared block
   at the end of The Poll's section: correct selectors, wrong neighbourhood.
   A rule for one form living where another form's markup happens to be is
   what AGENTS.md rule 14 is about, and it is how this block was deleted once
   already. */
@media (prefers-reduced-motion:reduce){
  .mv-spark{clip-path:none;transition:none}
  /* .mv-row carries no CSS transition of its own since session 37b (the
     glide moved into swings.js's rAF loop) -- this rule is now a no-op, left
     so a future reader does not wonder whether reduced motion still needs
     one; swings.js's own `reduce` branch never enters the rAF loop at all. */
  .mv-row{transition:none}
}
.swings{display:flex;flex-direction:column;width:100%;height:100%;min-height:0}
.mv-head{padding:0 15px;font-size:12px;font-weight:600;color:var(--muted);
  letter-spacing:0;text-transform:none}
.mv-cap{margin:6px 15px 0;padding:12px 0 0;font-size:12px;color:var(--dim);
  letter-spacing:0;text-transform:none;border-top:1px solid var(--line)}

/* space-between, not space-evenly. Evenly puts a gap above the first row and
   below the last as well as between them, so the panel had dead bands at both
   ends and the five rows sat in a floating block. */
.mv-rows{flex:1;min-height:0;display:flex;flex-direction:column;justify-content:space-between;
  gap:18px;padding:0 15px;margin:0}

/* Fixed columns, not `auto`. Every row is its own grid, so an auto-sized
   figure column took its width from that row's own number -- -23.07% is wider
   than -9.47% -- and the chart column started at a different x on each row.
   Five plots that do not share a left edge cannot be read against each other,
   which is the entire point of stacking them. */
/* The rows do not animate in, and the stagger they used to arrive on is gone.

   Each row carried `--d: .26 + i*.13s` and faded up from `translateY(9px)`, so
   the card was read top to bottom: five rows appearing one after another, each
   dragging its own line and figure behind it. That made the ENTRANCE the
   animation and the price movement a detail inside it.

   The five names are not a sequence -- they are one list, ranked, and the
   reader is meant to compare them. So the list is simply there, and the only
   thing that moves is the thing worth watching: five sessions drawing at once,
   left to right, each figure tracking its own line. `--d` is no longer set on
   the row at all, so nothing here reads it. */
/* The real-time leaderboard: as the running figure in swings.js moves a row
   ahead of or behind its neighbours, the row is repositioned by
   `transform:translateY` (js/forms/swings.js) rather than moved in the DOM,
   so the reorder reads as five names swapping places rather than a list
   quietly re-rendering.

   Session 37b: no CSS transition here any more. A `.2s ease` transition
   retargets from scratch on every flip, and when two rows sit close (SIG /
   ASO / NET early in the jumps card, TBBK / BRZE on drops) the raw ranking
   flips back and forth several times a second -- each flip restarted the
   easing, so the row never finished a move before reversing and the reviewer
   read it as jitter ("hmmm there's no way to make this smoother?"). The glide
   is now per-frame exponential easing in swings.js's own rAF loop (`curY +=
   (target - curY) * alpha`, ~130ms time constant, continuous in the current
   position rather than restarted by CSS), and the flips it eases toward are
   filtered by hysteresis in the same file -- so a row only actually changes
   rank when the lead is real. Nothing here overrides transform any more; the
   inline style swings.js writes is the only thing moving a row. */
.mv-row{display:grid;grid-template-columns:minmax(0,1fr) 96px 84px;gap:11px;align-items:center}

/* Originally shipped as identity in ink, not in the issuer's colour: five
   brand hexes do not survive the Session card's treatment -- TSLA and MSTR
   are red, and a red ticker beside a green line on an up day is the
   identity/direction collision. Overruled by the reviewer, session 33,
   2026-09-03: each row's ticker now carries its issuer's colour when one
   resolves (session/swings.mjs), ink when it does not. `--row-brand` is set
   per row rather than reusing `--brand` -- see the comment on `rowPalette()`
   in forms/swings.js for why a second name was necessary. */
.mv-id{display:flex;flex-direction:column;gap:3px;min-width:0}
.mv-sym{font-size:18px;font-weight:700;letter-spacing:-.01em;
  color:var(--row-brand,var(--ink));
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* On the real gainers list a ticker is not the identity -- STDN, ASST and CHA
   identify nothing to anyone. */
.mv-nm{font-size:12.5px;font-weight:400;color:var(--dim);letter-spacing:0;
  text-transform:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.mv-num{display:flex;flex-direction:column;align-items:flex-end;gap:3px}
.mv-px{font-size:12px;font-weight:400;color:var(--dim);letter-spacing:0;
  font-variant-numeric:tabular-nums;text-transform:none}

/* The session, drawn. A clip wipe, not a dash offset -- see the .pathviz note
   above: non-scaling-stroke makes a browser measure dashes in screen pixels,
   so a dash reveal silently drops the end of a choppy path. */
.mv-field{position:relative;height:40px}
/* .8s read as "draws quickly" rather than "the day happening" -- every card
   animation in this product holds 3-5s (AGENTS.md/memory, supersedes
   archetypes.md's 1.5s note), and the reviewer asked for this one at 5
   (session 37, Sep 9 jumps card at 430x743: "let's try to make this 5
   seconds"). swings.js reads this duration off the computed style to drive
   the running-percentage figure, so changing it here is the only edit --
   the figure follows the wipe with no JS change.

   Session 37b: `cubic-bezier(.22,.9,.3,1)` front-loads the draw -- y'(0) is
   already 2.7, so roughly 70% of each path is revealed in the first 1.5s and
   the last 3.5s barely move. Most of the re-rank happened early and the last
   third of the clock read as dead time. Replaced with `linear`: the wipe
   advances at a constant rate for its own full 5s, so the ranking a reader
   is meant to be tracking keeps evolving right up to the settle rather than
   finishing early and coasting.

   Captured both at 2.5s in
   (review/frames/2026-09-09-jumps-001-mid-2_5s-linear-430x821.png vs
   -oldeasing-430x821.png, same card, same instant): under the old curve
   every figure already reads within ~1pp of its final value (SIG +22.84%
   against a final +23.96%, ASO +14.02% against +14.40%) and the rank has
   already reached its settled order (SIG, ASO, PLSE, SSL, NET) -- almost
   the entire re-rank has happened by a fifth of the way through the last
   half of the clock. Under linear at the same instant every figure sits
   roughly half its final value (SIG +18.23%, ASO +10.60%) and the ranking
   is still visibly unsettled (SIG, ASO, PLSE, SSL, NET only three of five
   rows separated by a clearly readable gap). A gentler symmetric ease was
   also tried (`cubic-bezier(.37,0,.63,1)`) and discarded without being
   worth a frame: at the sampled instant, 2.5s of 5s, ANY symmetric
   ease-in-out is by construction closest to linear exactly at its own
   midpoint, so it added a third curve without adding a third answer. A
   future reviewer who wants an eased-but-not-front-loaded option should
   compare somewhere off-centre, not at 2.5s, or the two will look alike by
   construction.

   `.2s` of transition-delay is new: the reviewer asked the card "sit on
   zero for like a fifth of a second to prep the user" before the wipe
   starts (session 37b) -- every spark fully clipped, every figure at zero,
   every row in its stored slot. swings.js already reads `transitionDelay`
   off this element's computed style to offset the figure's own timeline, so
   the delay traveling through the existing read is the entire fix; nothing
   in swings.js names ".2s" itself. The wipe's own clock starts after the
   hold rather than at `.card.in`, same as before -- only its OWN duration
   moved, next paragraph.

   5s -> 4s, same session, minutes after the smoothing fix above landed:
   "also it's a bit long." The hold stays .2s (the reviewer asked for that
   separately and did not say it read as part of the length). swings.js
   reads this element's `transitionDuration` off computed style the same as
   it always has, so the figure and the re-rank follow the shorter clock
   with no change on that side either. */
.mv-spark{display:block;width:100%;height:100%;overflow:visible;
  clip-path:inset(0 100% 0 0);transition:clip-path 4s linear .2s}
.card.in .mv-spark{clip-path:inset(0 0 0 0)}
.mv-spark path{fill:none;stroke:var(--up);stroke-width:1.7;
  stroke-linecap:round;stroke-linejoin:round;vector-effect:non-scaling-stroke}
.mv-row.dn .mv-spark path{stroke:var(--down)}

/* Sized so the longest real figure -- a -23.07% with its sign -- still leaves
   the name column room for "Edison International" without an ellipsis. */
.mv-pct{font-size:17px;font-weight:700;letter-spacing:-.015em;text-align:right;
  font-variant-numeric:tabular-nums;color:var(--up);white-space:nowrap}
.mv-row.dn .mv-pct{color:var(--down)}
/* A non-numeric pct (fe-002) draws neither -- both this and the spark stroke
   above defaulted to --up with no third colour, so a stored "n/a" read as a
   real gain. Never reachable from a real payload: direction() maps every
   finite pct, including zero, to up or dn. */
.mv-row.na .mv-pct{color:var(--muted)}
.mv-row.na .mv-spark path{stroke:var(--muted)}
