/* reaction
   Split from the single v22 stylesheet. Everything here is scoped to
   .card.reaction and this form's own class prefix (rule 14: a change made for one
   card form is scoped to that form, by class). */
.card.open .dropviz{gap:8px}
.card.open .dropnums b{font-size:var(--fs-22)}
.card.open .dropnums strong{font-size:var(--fs-14)}

.dropviz{width:100%;height:100%;align-self:stretch;display:flex;flex-direction:column;gap:10px;justify-content:flex-start}
.dropnums{display:flex;justify-content:space-between;align-items:flex-end}
.dropnums b{font-size:35px;line-height:1.02;color:var(--ink)}
.dropnums strong{font-size:20px;color:var(--down)}

/* ---- v14 rule fixes ------------------------------------------------ */
/* C3: a continuous path is a line. C24: bounded to its own range. */
/* The band is symmetric around the anchor, so the anchor is always the exact
   middle of the plot -- a static rule, nothing for the JS to place. Without it
   a fixed scale is an empty box: the reader cannot see that the blank half
   above the line IS the statement (the day never traded above yesterday). */
.pathwrap{width:100%;flex:1;min-height:120px;display:block;position:relative}
/* Findings 07/01, s10 - the prior-close reference. Neutral, because a level is
   not a direction (C52), and --muted rather than --dim because it is a price a
   reader has to read. The dashes are a gradient, not stroke-dasharray: see the
   .pathviz note on what non-scaling-stroke does to dashes in this file. It
   arrives just ahead of the stroke, since it is what the stroke is measured
   against. */
.priceref{position:absolute;left:0;right:0;display:flex;align-items:center;gap:8px;
  transform:translateY(-50%);pointer-events:none;opacity:0;transition:opacity .34s ease-out}
.card.in .priceref{opacity:1;transition-delay:.18s}
.priceref i{flex:1;height:1px;
  background:repeating-linear-gradient(to right,var(--dim) 0 3px,transparent 3px 7px)}
/* The label is the first item in the rule's flex row, so the dashes begin
   AFTER it and the stroke has nothing to strike through. Dropping it below the
   rule instead put it in the path's first dip, which is the same collision the
   old start tag had. */
/* font-size and letter-spacing are what the v19 label pass in css/shell.css
   already computed here (.pathends span,.priceref span). Same fold as .runzero
   in forms/streak.css. */
.priceref span{font-size:var(--fs-12-5);letter-spacing:0;color:var(--muted);white-space:nowrap;flex:none}
/* The visual's own figure stack is the price a second time, now that the
   readout carries it. Lost in the consolidation and it printed twice. */
/* Was `.card.solo .X` (fe-009, docs/reviews/frontend-2026-09-03.md): the
   shell-wide solo layout, not this form. .dropnums and .pathwrap are inert
   elsewhere today because only reaction.js emits them, but .priceref comes
   from the shared, exported `viz.js priceRef()` -- the second solo form to
   call it would have silently inherited this padding. `:where()` adds the
   archetype check at zero specificity, so the compound selector is still
   (.card + .solo), same as before this rescoping, and the cascade against
   `.card.replay .pathwrap{cursor:default}` below (equal specificity, source
   order) is unchanged. `drop` is the pre-rename archetype (FORM_ALIASES in
   index.html); no live batch carries it, but a card that does still gets the
   same rules a `reaction` card does. */
.card.solo:where([data-archetype="reaction"],[data-archetype="drop"]) .dropnums{display:none}
.card.solo:where([data-archetype="reaction"],[data-archetype="drop"]) .priceref{padding-left:15px}
.card.solo:where([data-archetype="reaction"],[data-archetype="drop"]) .pathwrap{touch-action:none;cursor:crosshair}
.scrub{position:absolute;inset:0;pointer-events:none;opacity:0;transition:opacity .12s ease-out}
.scrub.on{opacity:1}
.scrub i{position:absolute;top:0;bottom:0;width:1px;background:var(--edge)}
.scrub b{position:absolute;width:11px;height:11px;border-radius:50%;background:var(--stance);
  transform:translate(-50%,-50%);box-shadow:0 0 0 4px var(--stance-wash)}

/* Click to skip. Three chevrons in the bottom-right of the plot -- a player's
   fast-forward, not a labelled button competing with the transport control at
   the top of the panel. On screen only while there is playback left to jump
   past: gone once the day is done, and never shown to a reduced-motion reader
   who was given the finished chart in the first place.
   The tap target is padded well past the marks; the marks themselves are 32px
   wide and would be a miss on a thumb. */
.rp-skip{position:absolute;right:0;bottom:2px;z-index:3;display:none;
  appearance:none;-webkit-appearance:none;
  width:30px;height:30px;flex:none;border-radius:50%;
  border:1px solid var(--edge);background:transparent;color:var(--muted);
  align-items:center;justify-content:center;cursor:pointer;
  transition:color .16s ease-out,border-color .16s ease-out}
.rp-skip:hover,.rp-skip:focus-visible{color:var(--ink);border-color:var(--muted)}
.rp-skip svg{width:15px;height:9px;display:block;fill:none;stroke:currentColor;
  stroke-width:2.1;stroke-linecap:round;stroke-linejoin:round}
.card[data-rp="armed"] .rp-skip,
.card[data-rp="playing"] .rp-skip,
.card[data-rp="paused"] .rp-skip{display:flex}

/* --- the plot ----------------------------------------------------- */
.rpviz{width:100%;height:100%;align-self:stretch;
  display:flex;flex-direction:column;gap:10px;justify-content:flex-start}

/* One wipe, two consumers. The price line and the volume beneath it are
   revealed by the same custom property, which is what makes "volume appears
   alongside its bar" true by construction rather than by two timers that
   agree until one of them drifts. */
.rp-svg,.rp-vol{clip-path:inset(0 calc(100% - var(--rp-p,0%)) 0 0)}
.rp-svg{width:100%;height:100%;overflow:visible;display:block}

/* The plot is inset to the same 15px as every caption in the panel, so the
   whole card has one left edge -- and so the close has somewhere to be. Run
   edge to edge, the last minutes of the session and the marker on them were
   cut off by the panel's own rounded corner, which is a chart losing the one
   point the card is about. The volume takes the identical inset because the
   two are read against each other by x position.

   .pathwrap is width:100% for the static Move, and 100% plus a margin is
   30px of overflow -- which is how the close ran off the panel in the first
   place. */
.rp-wrap,.rp-vol{width:auto;margin-left:15px;margin-right:15px}

/* Two copies of one path, clipped at the previous close: above it is green,
   below it is red, and the crossing is exact because it is a geometric cut
   rather than a colour chosen per segment. A gradient was tried first and
   rejected -- it fades through a dead grey at the one price the card is
   asking the reader to watch. */
.rp-line{fill:none;stroke-width:2.1;stroke-linecap:round;stroke-linejoin:round;
  vector-effect:non-scaling-stroke}
.rp-line.up{stroke:var(--up)}
.rp-line.dn{stroke:var(--down)}

/* The previous close, ruled and stationary. It is the only fixed thing on the
   plot and it is drawn before the first bar, so "above or below yesterday" is
   readable from the first second. */
.rp-ref{position:absolute;left:0;right:0;border-top:1px dashed var(--edge);
  transform:translateY(-50%);pointer-events:none}
.rp-ref span{position:absolute;left:0;top:5px;font-size:var(--fs-11);color:var(--dim);
  letter-spacing:0;text-transform:none;white-space:nowrap}

/* The leading edge. It sits on the interpolated line rather than on the last
   bar, because it is a position and not a price -- the printed price snaps to
   real prints (see initReplay). */
/* --stance is set live by initReplay through the same two classes the rest of
   the renderer uses, so the marker, the volume and the readout all turn at the
   same instant and no colour literal is written twice. */
.rp-dot{position:absolute;width:9px;height:9px;border-radius:50%;
  background:var(--stance);transform:translate(-50%,-50%);
  box-shadow:0 0 0 4px var(--stance-wash);opacity:0;
  transition:opacity .2s ease-out}
.card[data-rp="playing"] .rp-dot,
.card[data-rp="paused"] .rp-dot,
.card[data-rp="done"] .rp-dot,
.card[data-rp="reduced"] .rp-dot{opacity:1}

/* --- volume ------------------------------------------------------- */
/* Absolutely positioned by time, not laid out by index. A flex row divides
   the width by the number of bars, which is only the same thing when no
   interval is missing -- and a session with a hole would silently stretch the
   bars either side of it to cover ground nothing traded in. */
/* Heights stay linear in shares. An intraday session really is a U -- SPY's
   median 5m bar is 4% of its busiest one -- and a percentile scale or a square
   root would make the middle of the day legible by making the bars stop being
   proportional to volume, which is the C53 mistake with a different axis. The
   strip's job is where the volume was, and it says that truthfully. */
.rp-vol{position:relative;height:32px;flex:none}
.rp-vol i{position:absolute;bottom:0;height:var(--h);min-height:1px;
  background:var(--stance-dim,var(--edge));display:block}
/* The plot and its volume run the full width of the panel; every caption is
   inset to the same 15px the readout above them uses. That is the static
   Move's arrangement (.card.solo .volcap/.tickrow) and there is no reason for
   the replay to hold a different left edge to the card it replaced -- without
   it "30.5m shares" and "4:00 PM" sat hard against the rounded corner. */
/* --- the figures ---------------------------------------------------
   Four facts the readout above the plot cannot carry, each on its own row.

   They were a caption, an axis and a three-column strip, all set at 12px in
   --dim: three different arrangements of small grey text stacked under the
   chart, and the four numbers the card is actually counting were the least
   legible things on it. Then they were four rows of one weight with an
   identical hairline under each -- which is a settings screen. Nothing on it
   was more important than anything else, so the payoff, the one figure the
   card exists to deliver, read as the fourth preference in a list.

   So: the payoff first, alone, because it is the chart's conclusion; then the
   other three as a strip divided by rules *between the columns* rather than
   under the rows. Equal thirds, because those three really are peers. 107px
   where the list was 164px, and the 57px goes back to the plot.

   The four <b> counters stay in the payload's order -- volume, high, low,
   stake -- and `order` does the moving, so the payoff leads the block without
   the DOM saying so and `.rp-figs b` still reads the same four values in the
   same sequence.

   The 9:30 AM / 4:00 PM axis is gone with them. The clock in the readout runs
   the session the whole way through and names the bell at both ends, so the
   axis was restating in static type what the card spends eight seconds
   saying. `visual.replay.ticks` is still in the payload and still drawn by
   the static Move's .tickrow; this form no longer uses it.

   Tabular figures AND a min-width on every box that is rewritten as the day
   plays. The previous pass claimed "fixed tabular figures throughout" and was
   wrong: the values had them, the clocks in the <em> never did, so
   "9:40 AM" -> "12:35 PM" moved its own box by 9.5px and the volume total and
   the stake swung 11px each on HOOD as they gained a digit. A comment
   asserting a property the CSS did not have. Measured now, not asserted:
   one distinct width per box across the whole session, on all four cards. */
.rp-figs{display:flex;flex-direction:column;margin:6px 15px 0}
.rp-bar{order:0;display:flex;justify-content:space-between;align-items:baseline;
  gap:12px;padding-top:12px;border-top:1px solid var(--line);
  font-size:var(--fs-14);font-weight:600;color:var(--muted)}
.rp-bar b{font-size:var(--fs-29);font-weight:700;color:var(--ink);line-height:1;
  font-variant-numeric:tabular-nums;letter-spacing:var(--ls-n02);
  min-width:120px;text-align:right}
/* Equal thirds, so a figure that gains a digit grows inside its own column
   and nothing beside it moves. */
.rp-cols{order:1;display:grid;grid-template-columns:repeat(3,1fr);
  margin-top:16px;padding-top:13px;border-top:1px solid var(--line)}
.rp-col{display:flex;flex-direction:column;gap:3px;padding-left:11px}
.rp-col:first-child{padding-left:0}
.rp-col + .rp-col{border-left:1px solid var(--line);margin-left:1px}
.rp-clab{font-size:var(--fs-11);font-weight:600;color:var(--dim);white-space:nowrap}
.rp-clab em{font-style:normal;font-weight:400;font-variant-numeric:tabular-nums;
  display:inline-block;min-width:60px;margin-left:5px}
.rp-col b{font-size:16px;font-weight:700;color:var(--ink);line-height:1.15;
  font-variant-numeric:tabular-nums;letter-spacing:var(--ls-n01)}

/* Inspection is a post-playback affordance. Offering the crosshair while the
   line is still being drawn invites a drag that fights the animation. */
.card.replay .pathwrap{cursor:default}
.card.replay[data-rp="done"] .pathwrap,
.card.replay[data-rp="reduced"] .pathwrap{cursor:crosshair}
.card.replay .pathwrap:focus-visible{outline:2px solid var(--muted);outline-offset:3px}

/* --- the honest failure ------------------------------------------- */
.rp-invalid{display:flex;flex-direction:column;width:100%;min-height:0}
/* A replay block the renderer cannot trust draws the static session path and
   says so, rather than animating a session it could not verify. */
.rp-fail{margin-top:8px;padding:8px 11px;border:1px solid var(--edge);border-radius:8px;
  font-size:var(--fs-12);color:var(--dim);letter-spacing:0;text-transform:none}

/* Reduced motion gets the finished day, immediately and without a wipe: the
   card's whole content is the session, so skipping the animation must not
   skip the chart. initReplay paints the completed state and never starts a
   frame loop; these rules stop the transitions that would still fire. */
@media (prefers-reduced-motion:reduce){
  .rp-svg,.rp-vol{clip-path:none}
  .rp-dot{transition:none}
}

/* The Move's replay, moved out of the shell sheet. It hangs off
   `.card.replay` and the `rp-` prefix rather than `.card.reaction`, which is
   why it did not follow the form automatically -- but it is this form's CSS
   and a form's CSS is one file you can find. */
/* ------------------------------------------------------------------ *
   The Move — the session, replayed.

   Everything here hangs off `.card.replay` or a `.rp-` class, per AGENTS.md
   rule 14. `.card.replay` is set only when the payload carries a validated
   `visual.replay` block, so the eleven frozen batches that hold a `reaction`
   or `drop` card are drawn by the static path below it and are untouched by
   every rule in this section.

   The form is a claim with its ending withheld: an empty plot anchored at
   9:30 with the previous close already ruled on it, then about eight seconds
   of the real session, then the numbers. The restraint is the point — one
   line, one rule, one strip of volume and a clock. Nothing here counts down,
   pulses or instructs.
 * ------------------------------------------------------------------ */

/* One title, and it does not move. An earlier build swapped a suspense line
   for a factual one at 16:00, in the same frame that four hidden figures
   appeared -- five changes at the instant the reader was watching the chart
   finish, which reads as a snap rather than as an ending. The title says what
   the card is; every number on it says what happened, continuously.

   So there is nothing here holding the headline or the payoff back. The
   figures below are counters, and a counter that is invisible until it stops
   is not a counter. */

/* --- the readout -------------------------------------------------- */
/* Reuses .rdout's column so the panel keeps one left edge; the rows inside
   it are this form's own. */
.rp-top{display:flex;align-items:center;gap:9px;min-height:20px}
.rp-sym{font-size:var(--fs-13);font-weight:700;letter-spacing:var(--ls-01);color:var(--muted)}
/* The market clock. Tabular and min-width'd because it is the one number on
   the card that changes every frame: proportional digits make "11:11 AM" and
   "10:00 AM" different widths and the whole row twitches for eight seconds. */
.rp-clock{font-size:var(--fs-13);font-weight:600;color:var(--dim);
  font-variant-numeric:tabular-nums;min-width:74px}
.rp-ctl{margin-left:auto;appearance:none;-webkit-appearance:none;
  width:30px;height:30px;flex:none;border-radius:50%;
  border:1px solid var(--edge);background:transparent;color:var(--muted);
  display:flex;align-items:center;justify-content:center;cursor:pointer;
  transition:color .16s ease-out,border-color .16s ease-out}
.rp-ctl:hover,.rp-ctl:focus-visible{color:var(--ink);border-color:var(--muted)}
.rp-ctl svg{width:13px;height:13px;fill:currentColor;stroke:none;display:block}
.rp-ctl .rp-i{display:none}
.card[data-rp="playing"] .rp-i-pause,
.card[data-rp="paused"] .rp-i-play,
.card[data-rp="armed"] .rp-i-play,
.card[data-rp="loading"] .rp-i-play,
.card[data-rp="invalid"] .rp-i-play,
.card[data-rp="done"] .rp-i-replay,
.card[data-rp="reduced"] .rp-i-replay{display:block}
/* Nothing to drive until the state machine is wired, and nothing to drive
   ever if the payload failed the model. Both read as disabled rather than as
   a control that does nothing. */
.card[data-rp="loading"] .rp-ctl,
.card[data-rp="invalid"] .rp-ctl{opacity:.35;pointer-events:none}

/* Fixed baselines. The price and the change are rewritten ~79 times during a
   replay and must not move a pixel while they do -- tabular figures, and a
   line-height set here rather than inherited so the row's height cannot
   depend on which glyphs are in it. */
.rp-price{font-variant-numeric:tabular-nums;line-height:1;min-height:29px}
.rp-chg{font-variant-numeric:tabular-nums;line-height:1;min-height:14px;
  align-items:baseline}
.rp-chg em{font-style:normal;font-weight:400;color:var(--dim)}

/* The stake lives in the panel now, on its own row with the other three
   counters. The payload keeps `sub`; this form no longer draws it, and an
   empty payoff slot would otherwise reserve its margin above Read more.

   `.card.replay .hook .sub{display:none}` moved to shell.css, consolidated
   with The Habit's and The Insider's copies of the same rule -- see shell.css
   for the declaration. This card's real deck ("$10,000 became $9,931", the
   SPY replay) wraps to 2 lines and costs 73.17px over the pinned bottom if
   shown; a one-line deck costs 37.09px. Nothing here changed what this card
   renders. Note: the archetype is `reaction`, styled here, and its display
   name is "The Move" (docs/archetypes.md) -- the `.replay` class comes from
   the opt-in replay visual, not from a different form. */
