/* poll
   Split from the single v22 stylesheet. Everything here is scoped to
   .card.poll and this form's own class prefix (rule 14: a change made for one
   card form is scoped to that form, by class). */
/* ------------------------------------------------------------------ *
   The Poll
 * ------------------------------------------------------------------ */
.card.poll .vizsec{padding:4px 0}
/* .pollviz's base layout (display, width, flex-direction, height, min-height,
   gap, padding-top) is one rule now, back at its first declaration. */
/* The one exception, and it is an override of that rule, not a second
   declaration of it (rule 17's own distinction: a deliberate, more-specific
   selector beats a duplicate name) -- shell.css's shared `gap:10px` is one
   number for every prompt_first form's stack, and this form's own stack has
   nothing worth 10px of air twice over: the chart is the one thing on this
   panel worth the room, and every px this row does not spend is a px
   `.pl-spark`'s `flex:1` (setup slot, css/shell.css) spends instead.
   Reviewer, on the Simulator: "graph can get bigger and move the buttons
   down" -- both come from the same cut, since `.pl-opts` sits right after
   `.pl-spark` in the column and a shorter gap between them is a chart that
   reaches further down before the buttons start. 6px, not 0: the tally
   reserve below `.pl-opts` (`.pl-gap.g-tally`) needs ONE gap of daylight
   from the last button so the two do not visually fuse, same reasoning as
   every other join in this file that keeps rather than removes its gap. */
.card.poll .pollviz{gap:6px}

/* the day the call is about.

   **The Move's own chart (session 37 -> lane-shared-replay), not a second
   implementation that resembled it.** The reviewer: "this needs to be the
   day's animation, should be similar to the reaction graph. They should be
   the same underlying thing." A first cut (session 37, `initPollReplay`
   driving `--pl-p` on a `pl`-namespaced `lineChart`) copied The Move's
   *clock* but drew a second chart; this draws The Move's chart itself --
   `js/replay.js`'s `chartPrice` for the running $ price and change line, and
   `js/viz.js`'s `lineChart(ns:"rp", ...)`, the identical call `reaction.js`
   makes. `.rp-line`, `.rp-ref` (the prior-close rule, static and legible
   from the first frame -- see css/replay.css) and `.rp-dot` are that shared
   stylesheet's now, not this file's.

   Two things The Move's chart carries that this one does not, both because
   of what the stored card carries rather than by choice: no ticker+clock
   header (`visual` has no `symbol` field, and the spark carries no per-point
   time to build a clock from -- inventing either is AGENTS.md rule 4's
   plausible wrong fact, not a read one), and no volume strip (the stored
   `spark` is 80 raw prices with no per-bar share count). Both are the
   shared block's own opt-outs -- `chartTop` simply is not called, and no
   `.rp-vol` markup exists here to style.

   `.pl-spark` is now a flex COLUMN carrying the price/change rows and the
   plot, mirroring The Move's own `.rpviz`: `.pl-plot` (below) is the one
   flexible child, so the two fixed-height text rows above it cost the panel
   the same whether the chart is tall or squeezed to its floor. */
/* lane-poll-panel, 2026-09-04: the chart is decoration relative to the vote
   buttons, the result bars and the footer -- it carries no number or label
   the card depends on (the move badge and "Prev close" text are the same
   words either way, just drawn over a shorter plot) -- so it is what gives
   first when the panel is short, same principle as the Close's own gaps
   (forms/close.css), applied to a form whose stage carries a flex:1 chart
   rather than plain content blocks. min-height was 70px, tuned against a
   roomy panel and never revisited for a short one: foldcheck measured the
   panel clipped 55-89px on stored Poll cards carrying a spark (2026-09-02-
   feed-fixcheck-003, 2026-09-02-feed-s29-004) because 70px was a floor
   nothing below it could go, and every other block in the stack had no give
   of its own. 18px keeps the ruled prior-close line and its label
   (11px text at top:5px) fully legible while giving back up to 52px design
   px of the 70; flex-grow:1 (set by the shell's own `[data-slot="setup"]`
   rule for this layout, not duplicated here) still lets it grow past 18 on
   a roomy panel exactly as before. The bottom margin drops entirely rather
   than becoming a flex item: unlike Close's `.closeviz`, `.pollviz` is
   declared in css/shell.css with a fixed `gap:10px` this file cannot
   remove, so a spacer element inserted between two already-adjacent
   siblings would add a SECOND 10px gap rather than replace the first --
   double the fixed cost for no shrink benefit. A margin the shell's gap
   already sits next to has nothing to gain from becoming a spacer; it is
   just made smaller. */
/* min-height 18px is lane-poll-panel's own floor (2026-09-04): foldcheck
   measured the panel clipped 55-89px on stored Poll cards carrying a spark
   before it, because 70px was a fixed floor nothing below it could give.
   The running price/change row now lives in this same flex column, ahead of
   `.pl-plot` -- see forms/poll.css's own note above for why it is a column
   rather than the bare relative box it used to be. */
/* No explicit min-height of its own any more: the column's real floor is the
   SUM of its children's -- the price/change rows' own natural height (they
   are `.rdprice`/`.rdchg`, sized by css/shell.css's `.card.solo` rule) plus
   `.pl-plot`'s own 40px below -- and an explicit min-height smaller than
   that sum does not shrink the content to fit it, it lets the content
   overflow the box while the box itself obeys the smaller number: the
   header row and the top of the plot painted UNDER the option buttons
   until this was measured on a frame. flex's own `min-height:auto`
   (content-based) is the correct floor here, not a second number that can
   disagree with it. */
.pl-spark{position:relative;flex:1;width:auto;margin:0 15px;
  display:flex;flex-direction:column;gap:0}
/* The plot alone -- `.rp-svg`/`.rp-ref`/`.rp-dot`/`.rp-pill` (css/replay.css)
   need a positioned box scoped to just the chart, not one that also
   contains the price/change rows above it, or their height would be
   counted into every percentage those rules place. `.rp-pill` (the move
   badge, `lineChart`'s `o.endPill` option, moved from this file's own
   `.pl-move`) reads the same box for the same reason: it is `top:<n>%` of
   the plot's own height, computed from the identical `sparkScale` the plot
   itself uses. 40px is this lane's own floor, chosen so the line and its
   prior-close rule stay legible at the panel's shortest -- see the fit
   measurement in the commit message for the batch this was checked
   against. */
.pl-plot{position:relative;flex:1;min-height:22px}

/* The question, and the button that explains how it settles. No type of its
   own: it is the stack's `question` slot and the layout block sets it. It was
   13px here and 15px on the other three forms, which is the difference
   between a deadline label and a question -- and it was a deadline label,
   because the question itself was in the headline. */
.pl-when{display:flex;align-items:center;gap:7px;padding:0 15px 1px}
.pl-info{appearance:none;-webkit-appearance:none;width:16px;height:16px;flex:none;
  border:1px solid var(--edge);border-radius:50%;background:none;color:var(--dim);
  font:600 10px/1 "IBM Plex Sans",system-ui,sans-serif;cursor:pointer;padding:0}
.pl-info:hover,.pl-info:focus-visible{color:var(--ink);border-color:var(--muted)}
.pl-note{padding:0 15px 10px;font-size:var(--fs-12);line-height:1.45;color:var(--dim)}

.pl-opts{display:flex;flex-direction:column;gap:2px;padding:0 15px}
/* The divider between the two options, not a third one -- the reviewer:
   "could we also put a creative Or in between the two?" A hairline the word
   sits on, in the shell's own dim ink rather than a new colour, quiet enough
   not to read as a control: no border, no background of its own, no hover or
   focus state, and `aria-hidden` on the wrapper with no button or link
   anywhere in it, so neither a screen reader nor formcheck's "reader can
   reach a 44px control" probe finds a third thing to press between two real
   ones. js/forms/poll.js's `Array.join` only ever places it BETWEEN entries,
   so it costs nothing when there is only one option and nothing extra when
   there are more than two. */
/* margin is 2px, not the roomier value tried first: this row's cost comes
   straight out of `.pl-plot`'s own flex:1 -- the plot gave back ~7px of its
   grown height for this at the 739 window (measured, greybox-driver) before
   the margin was cut, and the reserve row below still overflowed `.vizsec`'s
   own bottom edge (clipped by its `overflow:hidden`) by a couple of px. The
   flex column's shrink phase does not fully return this divider's cost to
   the plot the way growth freely gives it (a `flex-basis:0%` item -- this
   form's `setup` slot, css/shell.css -- carries zero shrink WEIGHT by the
   spec formula, so only some of a negative delta lands back on the chart),
   so the honest fix is spending less here rather than assuming the chart
   absorbs it either direction. */
.pl-or{position:relative;flex:none;height:1px;background:var(--line);
  margin:2px 16px;pointer-events:none}
.pl-or span{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
  background:var(--panel);padding:0 7px;font-size:var(--fs-11);font-style:italic;
  font-weight:500;color:var(--dim);white-space:nowrap}
/* lane-poll-panel: the space `.pl-tally`'s eventual 26px used to reserve as
   a fixed `margin-bottom:36px` on this row (36 = 26 plus the two 10px flex
   gaps either side of it) is now a real flex item instead -- `.pl-gap.g-
   tally` below, sharing the `commit` slot `.pl-tally` itself carries -- so
   it can flex-shrink under a short panel exactly the way a margin never
   could (same argument as forms/close.css's gaps). formcheck's
   prompt_first stack check fails any rendered row with no `data-slot` at
   all, so an untagged spacer was not an option; sharing `commit` is safe
   for the OTHER thing that name has to stay clear of -- giving `.pl-tally`
   permanent layout via `visibility:hidden` was tried once already and
   failed the "reader can reach a 44px control" probe, because that probe
   treats any `[data-slot="commit"]` match that is a `<button>` or
   contains one as a touch target, and found a real, small one. The
   spacer is a bare `<i>`, matches neither test, and the two never paint
   at once (one is always `display:none` while the other shows), so the
   probe still finds nothing pressable until the reader has actually
   picked. */
/* `position:relative` makes this row the containing block for `.pl-track`
   below (rule: a change made for the reveal is scoped to the box it reveals
   in, not spent on a taller box). padding-bottom is 32px in BOTH states --
   14px of margin plus the 10px gap and 8px bar the fill used to gain only at
   the call, back when `.pl-track` was a flow child. Reserving it here instead
   of animating it in is what stops the option rows growing when the poll is
   answered (see `.pl-track` below). */
/* No `text-align` of its own any more -- the label was never plain text of
   this element's, it is `.pl-head`'s flex child, so a browser's own default
   button centring (Reveal's `.rv-lock` relies on the same default) was
   always the wrong lever here; `.pl-head`'s own `justify-content` below is
   the one declaration that actually moves it.

   `align-items:stretch` is an explicit override, not a restatement of the
   default: a `<button>` carries `align-items:flex-start` in the UA
   stylesheet's own form-control reset, which a plain `display:flex` here
   does not clear. Left at that default, `.pl-head` shrank to the width of
   its own content (label plus the reserved, invisible `.pl-fig`) rather
   than stretching across the row -- found on the rendered frame, once
   answered, as a "Your pick" mark and border that never appeared: `.pl-fig`
   (below) pins to `.pl-head`'s right edge, and a `.pl-head` narrower than
   the button put that edge well short of it. */
.pl-opt{appearance:none;-webkit-appearance:none;width:100%;
  display:flex;flex-direction:column;align-items:stretch;gap:3px;position:relative;
  background:#141517;border:1px solid transparent;border-radius:var(--radius-md);
  padding:4px 16px 16px;color:var(--ink);font:inherit;cursor:pointer;
  transition:background .18s ease-out,border-color .18s ease-out,transform .12s ease-out}
.pl-opt:hover{background:#1B1C20;border-color:#26272B}
.pl-opt:focus-visible{outline:2px solid var(--muted);outline-offset:2px}
.pl-opt:active{transform:scale(.985);background:#202126}
/* Centred like the other prompt forms' commit buttons (session lane-poll-
   panel, reviewer: "maybe center the buttons like we do with other
   buttons?") -- The Reveal's `.rv-lock` and The Price/Price Gap's `.pr-
   prompt`/`.pg-prompt` all centre their single line of text; this form's
   button carries a second thing too, the pick mark and total in `.pl-fig`,
   so copying `justify-content:space-between` -> `center` on this row alone
   would have centred the LABEL-PLUS-FIGURE as one group -- and `.pl-fig`
   carries real width even while invisible (the "Your pick" tag fades in by
   opacity rather than mounting late, and `.pl-total` is reserved even empty
   so a later fill never resizes the button, both existing rules below), so
   that group's true centre sits well left of the button's, dragging the
   readable label off centre with it. `.pl-fig` stays nested here rather
   than moving to a sibling of `.pl-head` -- position:absolute below takes
   it out of flex layout entirely (CSS Flexbox 4.1: an out-of-flow child is
   not a flex item), so `justify-content:center` now sees only the `<b>`
   label and centres that alone, while `.pl-head` staying its containing
   block keeps `.pl-fig` pinned to the label's own row rather than the
   button's full padded height. */
.pl-head{position:relative;display:flex;align-items:center;justify-content:center}
.pl-head b{font-size:var(--fs-19);font-weight:700;letter-spacing:var(--ls-n01)}
/* Anchored to `.pl-head`'s row, not `.pl-opt`'s full height: a hasCrowd
   card's padding-bottom reserves room below for `.pl-track`, so `.pl-head`
   sits at the TOP of the button rather than its true centre -- pinning here
   keeps the mark level with the label on that shape as well as the
   no-crowd one, where the two boxes agree. */
.pl-fig{position:absolute;right:0;top:50%;transform:translateY(-50%);
  display:flex;align-items:center;gap:9px}

/* none of the result exists until the call is made */
.pl-fig strong,.pl-track,.pl-you{opacity:0}
/* Absolutely positioned inside the padding-bottom `.pl-opt` reserves above --
   drawn where the reveal used to grow the row into, rather than growing it.
   Fading from empty to filled costs the row nothing: this is the same fix
   forms/price.css uses for the guess marks on `.pr-track`, applied to a bar
   instead of two points. Never `max-height`, which was the animated growth
   that shrank the chart above -- `.pr-shot`'s 153.9px -> 72.2px on The Price
   is `.pl-spark`'s own `flex:1` problem here, one level along. */
.pl-track{position:absolute;left:16px;right:16px;bottom:7px;height:7px;
  border-radius:var(--radius-xs);background:var(--line);overflow:hidden;
  transition:opacity .3s ease-out}
.pl-track i{display:block;height:100%;width:0;border-radius:var(--radius-xs);background:var(--dim)}
.pl-fig strong{font-size:var(--fs-19);font-weight:700;letter-spacing:var(--ls-n01);
  font-variant-numeric:tabular-nums;transition:opacity .3s ease-out .1s}
.pl-you{display:inline-flex;align-items:center;gap:4px;font-style:normal;
  font-size:var(--fs-11);font-weight:600;letter-spacing:.05em;text-transform:uppercase;
  color:var(--muted);transition:opacity .3s ease-out .1s}
/* Read, not shown -- js/forms/poll.js's own comment on why: a centred label
   and a same-row "Your pick" tag pinned to the button's right edge measured
   real overlap on the frame. Off-screen rather than `display:none`/width:0
   so a screen reader still gets the words the checkmark alone cannot carry;
   `.pl-you`'s own `gap:4px` collapses to nothing once this contributes zero
   width, so no stray space sits after the icon. */
.pl-you-txt{position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
.pl-tick{width:13px;height:13px;fill:none;stroke:currentColor;stroke-width:2.2;
  stroke-linecap:round;stroke-linejoin:round}

.pollviz.called .pl-opt{cursor:default}
.pollviz.called .pl-opt:hover{background:#141517;border-color:transparent}
.pollviz.called .pl-opt:active{transform:none}
.pollviz.called .pl-fig strong{opacity:1}
.pollviz.called .pl-track{opacity:1}
.pollviz.called .pl-track i{width:var(--w);transition:width .78s cubic-bezier(.16,1,.3,1) calc(.1s + var(--d))}
/* Neither direction nor identity belongs in a poll bar.
   --up/--down were wrong because a call is not a day: "Keeps running" is not
   an up move, it is a guess about one. --brand was wrong for the same reason
   one level along -- it is the issuer's colour, and TSLA's issuer colour is
   red, so the bullish option drew in the down palette. The leader is simply
   the brighter bar. */
.pollviz.called .pl-opt.lead .pl-track i{background:var(--ink)}
.pollviz.called .pl-opt.lead .pl-fig strong{color:var(--ink)}
.pollviz.called .pl-opt:not(.lead){background:#111214}
.pollviz.called .pl-opt:not(.lead) .pl-fig strong{color:var(--muted)}
.pollviz.called .pl-opt.picked{border-color:var(--edge)}
.pollviz.called .pl-opt.picked .pl-you{opacity:1}

/* No crowd, no vote store (session 38): a card built after 2026-09-11 carries
   no `pct` on its options, so js/forms/poll.js draws neither a `.pl-track`
   bar nor a percentage -- and never assigns `.lead` to either button, because
   there is no share to compare. Left alone, `.pollviz.called .pl-opt:not(
   .lead)` above would then match BOTH options (neither carries `.lead`) and
   dim both to the losing-bar colour, reading as two greyed-out buttons rather
   than an honest tie. This rule is scoped one level narrower (four classes
   against that rule's four -- rule 17: the later declaration at equal
   specificity wins, so it has to come after, which it does here) and puts
   both options back at the ordinary, unanswered background; `.picked`'s own
   border and `.pl-you`'s "Your pick" tag (both above, unaffected) are what
   marks the reader's own call. */
.pollviz.called.no-crowd .pl-opt{background:#141517}
/* The slot a per-option total will land in the day there is a real count --
   reserved and empty rather than absent, so that day does not resize the
   button (js/forms/poll.js's own comment on `.pl-total`). min-width is a
   guess at a plausible count's width ("1,234"), not a measurement against a
   real one, because no real one exists yet to measure. */
.pl-total{display:inline-block;min-width:46px;text-align:right}

/* the count, and the way back out. Changing a call is a deliberate act, not
   something a second tap on the other bar should do by accident.

   Fourth instance of the same bug (AGENTS.md rule 18): this row was
   `display:none` until `.called`, which is what The Price's result block did
   before it was fixed, and what The Price Gap and The Reveal each fixed
   separately afterward. `display:none` removes the row -- and the flex gap
   above it -- from the stack entirely, so committing an answer added a whole
   row's height where none had been. The space is reserved instead, on
   `.pl-opts` above, rather than on this element: `.pl-tally` itself stays
   `display:none` here so a probe for "can the reader reach a big enough
   commit control" still finds none on the unanswered card, which is the
   correct answer -- the commit on this form is the option buttons, not this
   row (`data-slot="commit"` here is about stack ORDER, not about being a
   pressable target). */
/* lane-shared-replay: padding-top 11px -> 4px. The chart above now costs
   this panel more (the running price/change row it did not carry before),
   so the tally row -- fixed height, unlike `.pl-gap.g-tally`'s own
   flex-shrink -- gives back what it can too: "Crowd leans bullish" was
   measured clipped by the panel's own rounded bottom edge on the answered
   frame before this. */
.pl-tally{display:none;align-items:baseline;justify-content:space-between;gap:12px;
  padding:4px 15px 0;font-size:var(--fs-12);color:var(--dim)}
.pollviz.called .pl-tally{display:flex}
/* Stands in for `.pl-tally` before a call is made -- js/forms/poll.js gives
   it `data-slot="commit"` too, the same slot `.pl-tally` carries, so the
   shell's own `[data-slot="commit"]{order:4}` rule (css/shell.css) places
   it without a second declaration of `order` here (rule 17: prefer a
   distinct name over a duplicate one, and this is the same case run the
   other way -- no need to redeclare what the shell already sets for this
   attribute). Removed outright once `.pl-tally` takes its place, and never
   a `<button>` or a button's container, so it does not add a second match
   to the "reader can reach a 44px control" probe the way `.pl-tally` itself
   once did (comment above).

   `flex:0 20 26px` used to let it give back that 26px under a short panel
   (session before this one). Session 37's Poll layout-shift fix found the
   real reason: 26 was never `.pl-tally`'s real height -- its own computed
   `height` (unscaled design px, `offsetHeight`; the card face is laid out
   at a fixed width and CSS-scaled to the viewport, AGENTS rule 8, so this
   is NOT what `getBoundingClientRect` reports) is 19px, not 26 -- close
   enough that the gap was rarely noticed, until `.pl-plot`'s own `flex:1`
   chart got hungrier once `.pl-foot`'s two lines came out (same session),
   and this spacer's `shrink:20` gave the difference straight to the chart
   on every single card, answered or not. The reviewer, on the real
   Simulator: "the poll's layout must not shift" -- so nothing here shrinks
   any more, and cannot give anything back under a short panel. That is a
   real cost (this form no longer has this 19px to spend when the panel is
   tight) traded for the one the reviewer asked for: answering changes what
   is drawn in this row, never whether the row exists or how tall it is.

   `min-height`, not `flex-basis` -- css/shell.css's prompt_first stack
   (`.stage > * > [data-slot]{flex-grow:0;flex-basis:auto}`, deliberately
   with no `min-height`, "a shared floor would be this block reaching a
   level down") outranks anything this file sets on `flex-basis` by
   specificity, shell loading first or not (rule 14's trap run in reverse --
   the SHELL out-specifies the form here, not the other way round). A
   `flex:0 0 <n>px` in this file was silently overridden back to
   `flex-basis:auto` and rendered at its empty content height, 0 -- measured
   on the real Simulator before this comment existed. `min-height` is the
   one sizing property the shell's rule leaves to the form, exactly as its
   own comment says, so it is the one that actually reserves this row's
   height. `flex-shrink:0` stays alongside it: the shell rule never sets
   shrink, so this form's own `0` still holds. */
.pl-gap.g-tally{flex-shrink:0;min-height:19px}
/* 19px was measured against a `.pl-tally` that always prints something --
   the sample-crowd count, or "You called it." on a no-crowd card. Session
   38/39 pulled "You called it." with nothing to replace it (this lane's own
   brief: "'You called it.' was removed from the no-crowd tally"), so a
   no-crowd `.pl-tally` now holds an empty `.pl-count` and a `.pl-change`
   hidden outside `?dev=1` -- nothing left to give either child a line-box,
   and its real height collapsed to its own 4px top padding. Measured on the
   rendered frame answering a no-crowd poll: the chart grew ~16px design the
   instant the reserve gap (19px) gave way to the real, now-empty row (4px)
   -- the exact "layout must not shift" defect this spacer exists to
   prevent, just running in the direction nobody had reserved against. This
   scopes the reservation to what a no-crowd card's tally row actually is
   now rather than what it used to print; a hasCrowd card's real count
   still measures near 19px, so its reservation is untouched. */
.pollviz.no-crowd .pl-gap.g-tally{min-height:4px}
.pollviz.called .pl-gap.g-tally{display:none}
/* A call is final -- one tap, no revision (session 38, reviewer: "remove the
   option to change call"). Same treatment forms/reveal.css already gives
   `.rv-again`: the control stays in the markup (a prototype-only reset) but
   is invisible unless the renderer is opened with `?dev=1` -- so the
   production card has no way to un-answer itself, on any poll, sample-crowd
   or no-crowd alike. */
.pl-change{display:none;appearance:none;-webkit-appearance:none;background:none;border:0;padding:0;
  color:var(--muted);font:600 12px/1 "IBM Plex Sans",system-ui,sans-serif;
  text-decoration:underline;text-underline-offset:3px;cursor:pointer}
body.devtools .pollviz.called .pl-change{display:inline-block}
.pl-change:hover,.pl-change:focus-visible{color:var(--ink)}

/* No margin-top:auto -- the stack's `note` slot sets it (rule 17). Top
   padding was 13px, tightened to 8px (lane-poll-panel) as one more small,
   fixed, un-shrinkable saving alongside the chart's floor and the tally
   spacer above -- not made a flex item itself, the same call close.css
   makes for its own small content-adjacent gaps.

   Session 37, reviewer: "we can earn some space here by removing this" --
   the unanswered face's two lines ("Make your call to reveal the crowd.",
   the resolve time) are gone. There is no `.pl-cap` any more; the resolve
   time is not reprinted here.

   First cut of that made this row `display:none` until `.pollviz.called`,
   which earned the chart the row's height back -- and then TOOK it back the
   instant a call was made, because `display:none` -> `display:flex` is a
   real box arriving in the flex column, and `.pl-plot`'s `flex:1` chart is
   what gives when a sibling grows. Caught on the real Simulator: the chart
   visibly shrank and the panel's bottom dropped on every answer (session
   24's rule -- answering never shrinks the chart; reviewer, same night:
   "the poll's layout must not shift"). `visibility`, not `display`, is the
   fix: this row is laid out at its real, final height from the very first
   paint -- `visualPoll` writes the crowd-lean sentence into `.pl-prompt`
   before any card is answered, the same computation that decides the
   `.lead` class, so the two can never disagree -- and only PAINT turns on
   with `.called`. The chart's height and the panel's bottom are now the
   same box before and after answering; only what is drawn inside this row
   changes, not whether the row exists. */
.pl-foot{visibility:hidden;padding:1px 15px 0;margin-left:15px;margin-right:15px;
  border-top:1px solid var(--line);padding-left:0;padding-right:0;
  display:flex;flex-direction:column;gap:3px}
.pollviz.called .pl-foot{visibility:visible}
.pl-prompt{font-size:var(--fs-13);font-weight:600;color:var(--ink)}

/* No crowd, no lean sentence to reserve room for (session 38, on top of the
   removed bar above): a no-crowd card's `.pl-prompt` is always the empty
   string (js/forms/poll.js), so unlike the sample-crowd card this row is
   never going to paint anything, in either state -- `display:none`
   unconditionally rather than `visibility:hidden`'s "reserve it, might paint
   later" is honest about that, and costs nothing on the "answering must not
   shift the layout" rule because a box that is absent before AND after
   cannot move between them.
   The reviewer, on the freed space: "we should take up more space with the
   buttons" -- so it goes to `.pl-opt` below, a fixed size increase rather
   than a flex-grow one. `checkStack` in review/formcheck.mjs allows only the
   `setup` slot (the chart above) to carry `flex-grow`; growing `.pl-opts`
   (slot `control`) instead would trip that guard, so the extra room is
   spent as literal padding here, not as a claim on the panel's slack.

   10px top and bottom, not a first cut's 20: measured on the real Simulator
   (session 38) at the automation driver's own 430x739 -- toolbar up, the
   shortest window this renderer is asked to fit -- `.stage`'s own bottom sat
   at 585.1px while `.pl-opts` alone already ran to 589.2 and `.pl-tally`
   (once called) to 623.1, both past it: `.pl-spark`'s `flex:1` has a floor
   (its own min-height plus the price/change rows above the plot) and cannot
   shrink below it to make room, so a fixed increase spent here that outruns
   the window's actual give does not "come out of the chart" as intended --
   it overflows the panel outright, and the row below painted over the top
   of it, which a still probe of the CLOSED card cannot show (rule 1: the
   defect was only visible once answered, on the frame, not in the DOM). 10px
   keeps `.pl-opts` and `.pl-tally` inside `.stage` on that same window with
   room to spare, confirmed the same way. */
.pollviz.no-crowd .pl-foot{display:none}
.pollviz.no-crowd .pl-opt{padding:10px 16px 10px}

/* The chart replay (session 37) skips straight to the finished day: JS's
   `createReplayClock` checks `reduce` before starting a frame and paints the
   completed state once, never opening a wipe to stop. `.rp-svg`'s own
   defence-in-depth reduced-motion rule (css/replay.css) covers the belt half
   of that already, shared with The Move; the option bars below still animate
   on the pick, so that half stays this form's own. */
@media (prefers-reduced-motion:reduce){
  .pl-track i,.pl-track,.pl-count{transition:none}
}
